@agent-native/core 0.47.1 → 0.48.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/agent-native.js +41 -0
- package/dist/a2a/handlers.js +2 -2
- package/dist/a2a/handlers.js.map +1 -1
- package/dist/a2a/server.js +2 -2
- package/dist/a2a/server.js.map +1 -1
- package/dist/action.d.ts +43 -2
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js.map +1 -1
- package/dist/agent/context-xray/actions/context-evict.d.ts +7 -1
- package/dist/agent/context-xray/actions/context-evict.d.ts.map +1 -1
- package/dist/agent/context-xray/actions/context-manifest-get.d.ts +4 -1
- package/dist/agent/context-xray/actions/context-manifest-get.d.ts.map +1 -1
- package/dist/agent/context-xray/actions/context-pin.d.ts +7 -1
- package/dist/agent/context-xray/actions/context-pin.d.ts.map +1 -1
- package/dist/agent/context-xray/actions/context-report.d.ts +12 -1
- package/dist/agent/context-xray/actions/context-report.d.ts.map +1 -1
- package/dist/agent/context-xray/actions/context-restore.d.ts +7 -1
- package/dist/agent/context-xray/actions/context-restore.d.ts.map +1 -1
- package/dist/agent/context-xray/apply-directives.d.ts.map +1 -1
- package/dist/agent/context-xray/apply-directives.js.map +1 -1
- package/dist/agent/context-xray/schema.d.ts +10 -10
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +26 -3
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/engine/anthropic-engine.d.ts +1 -1
- package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +47 -8
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/engine/builtin.js +1 -1
- package/dist/agent/engine/builtin.js.map +1 -1
- package/dist/agent/engine/output-tokens.d.ts +1 -1
- package/dist/agent/engine/output-tokens.d.ts.map +1 -1
- package/dist/agent/engine/output-tokens.js +6 -2
- package/dist/agent/engine/output-tokens.js.map +1 -1
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +7 -4
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/engine/types.d.ts +19 -0
- package/dist/agent/engine/types.d.ts.map +1 -1
- package/dist/agent/engine/types.js +6 -0
- package/dist/agent/engine/types.js.map +1 -1
- package/dist/agent/model-config.d.ts +22 -14
- package/dist/agent/model-config.d.ts.map +1 -1
- package/dist/agent/model-config.js +113 -8
- package/dist/agent/model-config.js.map +1 -1
- package/dist/agent/production-agent.d.ts +19 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +253 -39
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +10 -0
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/agent/run-manager.d.ts +1 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +36 -9
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +47 -4
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +154 -4
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +57 -2
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/agent/types.d.ts +3 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/agent-web/generator.d.ts +3 -3
- package/dist/appearance/actions/change-appearance.d.ts +6 -1
- package/dist/appearance/actions/change-appearance.d.ts.map +1 -1
- package/dist/application-state/handlers.d.ts +2 -2
- package/dist/application-state/handlers.d.ts.map +1 -1
- package/dist/application-state/store.d.ts.map +1 -1
- package/dist/application-state/store.js +17 -0
- package/dist/application-state/store.js.map +1 -1
- package/dist/catalog.json +2 -1
- package/dist/cli/code-agent-commands.d.ts.map +1 -1
- package/dist/cli/code-agent-commands.js +2 -0
- package/dist/cli/code-agent-commands.js.map +1 -1
- package/dist/cli/code-agent-connector.js +7 -13
- package/dist/cli/code-agent-connector.js.map +1 -1
- package/dist/cli/code-agent-executor.d.ts +54 -2
- package/dist/cli/code-agent-executor.d.ts.map +1 -1
- package/dist/cli/code-agent-executor.js +504 -48
- package/dist/cli/code-agent-executor.js.map +1 -1
- package/dist/cli/code-agent-runs.d.ts +13 -0
- package/dist/cli/code-agent-runs.d.ts.map +1 -1
- package/dist/cli/code-agent-runs.js +36 -0
- package/dist/cli/code-agent-runs.js.map +1 -1
- package/dist/cli/code.js +59 -5
- package/dist/cli/code.js.map +1 -1
- package/dist/cli/connect.js +141 -3
- package/dist/cli/connect.js.map +1 -1
- package/dist/cli/index.js +0 -0
- package/dist/cli/pr-visual-recap-workflow.js +1 -1
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/cli/recap.js +476 -46
- package/dist/cli/recap.js.map +1 -1
- package/dist/cli/skills.js +298 -179
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +29 -2
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AgentTaskCard.d.ts.map +1 -1
- package/dist/client/AgentTaskCard.js +17 -2
- package/dist/client/AgentTaskCard.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +310 -1732
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/CommandMenu.d.ts +1 -1
- package/dist/client/CommandMenu.d.ts.map +1 -1
- package/dist/client/CommandMenu.js +1 -1
- package/dist/client/CommandMenu.js.map +1 -1
- package/dist/client/HighlightedCodeBlock.d.ts +40 -0
- package/dist/client/HighlightedCodeBlock.d.ts.map +1 -0
- package/dist/client/HighlightedCodeBlock.js +110 -0
- package/dist/client/HighlightedCodeBlock.js.map +1 -0
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +8 -1
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/PoweredByBadge.d.ts +2 -2
- package/dist/client/PoweredByBadge.d.ts.map +1 -1
- package/dist/client/RunStuckBanner.d.ts +1 -1
- package/dist/client/RunStuckBanner.d.ts.map +1 -1
- package/dist/client/StarfieldBackground.d.ts.map +1 -1
- package/dist/client/StarfieldBackground.js +10 -5
- package/dist/client/StarfieldBackground.js.map +1 -1
- package/dist/client/Turnstile.d.ts +1 -1
- package/dist/client/Turnstile.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts +3 -2
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +13 -9
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/app-providers.d.ts +99 -0
- package/dist/client/app-providers.d.ts.map +1 -0
- package/dist/client/app-providers.js +19 -0
- package/dist/client/app-providers.js.map +1 -0
- package/dist/client/assistant-ui-recovery.d.ts +1 -1
- package/dist/client/auth-redirect-url.d.ts +1 -1
- package/dist/client/auth-redirect-url.d.ts.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts +0 -19
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.js +141 -55
- package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
- package/dist/client/blocks/library/DiffBlock.js +1 -1
- package/dist/client/blocks/library/DiffBlock.js.map +1 -1
- package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/FileTreeBlock.js +1 -1
- package/dist/client/blocks/library/FileTreeBlock.js.map +1 -1
- package/dist/client/blocks/library/HighlightedCode.d.ts.map +1 -1
- package/dist/client/blocks/library/HighlightedCode.js +5 -3
- package/dist/client/blocks/library/HighlightedCode.js.map +1 -1
- package/dist/client/blocks/library/annotation-rail.d.ts +5 -4
- package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -1
- package/dist/client/blocks/library/annotation-rail.js +22 -3
- package/dist/client/blocks/library/annotation-rail.js.map +1 -1
- package/dist/client/blocks/library/diagram.js +1 -1
- package/dist/client/blocks/library/diagram.js.map +1 -1
- package/dist/client/blocks/library/diff.config.d.ts +3 -2
- package/dist/client/blocks/library/diff.config.d.ts.map +1 -1
- package/dist/client/blocks/library/diff.config.js +4 -3
- package/dist/client/blocks/library/diff.config.js.map +1 -1
- package/dist/client/blocks/library/question-form.d.ts.map +1 -1
- package/dist/client/blocks/library/question-form.js +2 -1
- package/dist/client/blocks/library/question-form.js.map +1 -1
- package/dist/client/blocks/library/wireframe-kit.d.ts +1 -1
- package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -1
- package/dist/client/blocks/library/wireframe.js +1 -1
- package/dist/client/blocks/library/wireframe.js.map +1 -1
- package/dist/client/chat/attachment-adapters.d.ts +58 -0
- package/dist/client/chat/attachment-adapters.d.ts.map +1 -0
- package/dist/client/chat/attachment-adapters.js +331 -0
- package/dist/client/chat/attachment-adapters.js.map +1 -0
- package/dist/client/chat/index.d.ts +13 -0
- package/dist/client/chat/index.d.ts.map +1 -0
- package/dist/client/chat/index.js +13 -0
- package/dist/client/chat/index.js.map +1 -0
- package/dist/client/chat/markdown-renderer.d.ts +49 -0
- package/dist/client/chat/markdown-renderer.d.ts.map +1 -0
- package/dist/client/chat/markdown-renderer.js +391 -0
- package/dist/client/chat/markdown-renderer.js.map +1 -0
- package/dist/client/chat/message-components.d.ts +35 -0
- package/dist/client/chat/message-components.d.ts.map +1 -0
- package/dist/client/chat/message-components.js +452 -0
- package/dist/client/chat/message-components.js.map +1 -0
- package/dist/client/chat/repo-helpers.d.ts +41 -0
- package/dist/client/chat/repo-helpers.d.ts.map +1 -0
- package/dist/client/chat/repo-helpers.js +61 -0
- package/dist/client/chat/repo-helpers.js.map +1 -0
- package/dist/client/chat/run-recovery.d.ts +41 -0
- package/dist/client/chat/run-recovery.d.ts.map +1 -0
- package/dist/client/chat/run-recovery.js +348 -0
- package/dist/client/chat/run-recovery.js.map +1 -0
- package/dist/client/chat/tool-call-display.d.ts +34 -0
- package/dist/client/chat/tool-call-display.d.ts.map +1 -0
- package/dist/client/chat/tool-call-display.js +284 -0
- package/dist/client/chat/tool-call-display.js.map +1 -0
- package/dist/client/code-agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/code-agent-chat-adapter.js +20 -0
- package/dist/client/code-agent-chat-adapter.js.map +1 -1
- package/dist/client/collab/index.d.ts +10 -0
- package/dist/client/collab/index.d.ts.map +1 -0
- package/dist/client/collab/index.js +10 -0
- package/dist/client/collab/index.js.map +1 -0
- package/dist/client/components/AgentPresenceChip.d.ts +1 -1
- package/dist/client/components/AgentPresenceChip.d.ts.map +1 -1
- package/dist/client/components/ApiKeySettings.d.ts +1 -1
- package/dist/client/components/ApiKeySettings.d.ts.map +1 -1
- package/dist/client/components/CodeAgentIndicator.d.ts +1 -1
- package/dist/client/components/CodeAgentIndicator.d.ts.map +1 -1
- package/dist/client/components/CodeRequiredDialog.d.ts +1 -1
- package/dist/client/components/CodeRequiredDialog.d.ts.map +1 -1
- package/dist/client/components/LiveCursorOverlay.d.ts.map +1 -1
- package/dist/client/components/LiveCursorOverlay.js.map +1 -1
- package/dist/client/components/PresenceBar.d.ts +1 -1
- package/dist/client/components/PresenceBar.d.ts.map +1 -1
- package/dist/client/composer/PromptComposer.d.ts.map +1 -1
- package/dist/client/composer/PromptComposer.js +6 -26
- package/dist/client/composer/PromptComposer.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +8 -2
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +21 -9
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/VoiceButton.d.ts +2 -2
- package/dist/client/composer/VoiceButton.d.ts.map +1 -1
- package/dist/client/composer/index.d.ts +1 -1
- package/dist/client/composer/index.d.ts.map +1 -1
- package/dist/client/composer/index.js +1 -1
- package/dist/client/composer/index.js.map +1 -1
- package/dist/client/composer/use-skills.d.ts +1 -1
- package/dist/client/context-xray/ContextMeter.d.ts +1 -1
- package/dist/client/context-xray/ContextMeter.d.ts.map +1 -1
- package/dist/client/context-xray/ContextMeter.js +3 -3
- package/dist/client/context-xray/ContextMeter.js.map +1 -1
- package/dist/client/context-xray/ContextXRayPanel.d.ts.map +1 -1
- package/dist/client/context-xray/ContextXRayPanel.js +4 -3
- package/dist/client/context-xray/ContextXRayPanel.js.map +1 -1
- package/dist/client/context-xray/format.d.ts +11 -0
- package/dist/client/context-xray/format.d.ts.map +1 -1
- package/dist/client/context-xray/format.js +16 -0
- package/dist/client/context-xray/format.js.map +1 -1
- package/dist/client/conversation/AgentConversation.d.ts.map +1 -1
- package/dist/client/conversation/AgentConversation.js +8 -53
- package/dist/client/conversation/AgentConversation.js.map +1 -1
- package/dist/client/conversation/use-near-bottom-autoscroll.d.ts +1 -1
- package/dist/client/conversation/use-near-bottom-autoscroll.d.ts.map +1 -1
- package/dist/client/conversation/use-near-bottom-autoscroll.js +14 -1
- package/dist/client/conversation/use-near-bottom-autoscroll.js.map +1 -1
- package/dist/client/create-query-client.d.ts +28 -0
- package/dist/client/create-query-client.d.ts.map +1 -0
- package/dist/client/create-query-client.js +78 -0
- package/dist/client/create-query-client.js.map +1 -0
- package/dist/client/db-admin/DevDatabaseLink.d.ts +1 -1
- package/dist/client/db-admin/DevDatabaseLink.d.ts.map +1 -1
- package/dist/client/db-admin/RowSidePanel.d.ts +1 -1
- package/dist/client/db-admin/RowSidePanel.d.ts.map +1 -1
- package/dist/client/db-admin/RowSidePanel.js +2 -2
- package/dist/client/db-admin/RowSidePanel.js.map +1 -1
- package/dist/client/db-admin/TableEditor.d.ts +1 -1
- package/dist/client/db-admin/TableEditor.d.ts.map +1 -1
- package/dist/client/db-admin/TableEditor.js +1 -1
- package/dist/client/db-admin/TableEditor.js.map +1 -1
- package/dist/client/db-admin/cell-format.d.ts +1 -1
- package/dist/client/db-admin/cell-format.d.ts.map +1 -1
- package/dist/client/dev-overlay/DevOverlay.d.ts +1 -1
- package/dist/client/dev-overlay/DevOverlay.d.ts.map +1 -1
- package/dist/client/editor/index.d.ts +2 -0
- package/dist/client/editor/index.d.ts.map +1 -0
- package/dist/client/editor/index.js +2 -0
- package/dist/client/editor/index.js.map +1 -0
- package/dist/client/error-format.d.ts.map +1 -1
- package/dist/client/error-format.js +4 -0
- package/dist/client/error-format.js.map +1 -1
- package/dist/client/extensions/AgentNativeExtensionFrame.d.ts +1 -1
- package/dist/client/extensions/AgentNativeExtensionFrame.d.ts.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionSlot.d.ts +1 -1
- package/dist/client/extensions/ExtensionSlot.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewerPage.d.ts +1 -1
- package/dist/client/extensions/ExtensionViewerPage.d.ts.map +1 -1
- package/dist/client/guided-questions.d.ts +6 -6
- package/dist/client/host-bridge.d.ts.map +1 -1
- package/dist/client/host-bridge.js +2 -0
- package/dist/client/host-bridge.js.map +1 -1
- package/dist/client/index.d.ts +7 -6
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +5 -3
- package/dist/client/index.js.map +1 -1
- package/dist/client/onboarding/OnboardingBanner.d.ts +1 -1
- package/dist/client/onboarding/OnboardingBanner.d.ts.map +1 -1
- package/dist/client/onboarding/OnboardingPanel.d.ts +1 -1
- package/dist/client/onboarding/OnboardingPanel.d.ts.map +1 -1
- package/dist/client/onboarding/SetupButton.d.ts +1 -1
- package/dist/client/onboarding/SetupButton.d.ts.map +1 -1
- package/dist/client/org/InvitationBanner.d.ts +1 -1
- package/dist/client/org/InvitationBanner.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts +1 -1
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/RequireActiveOrg.d.ts +1 -1
- package/dist/client/org/RequireActiveOrg.d.ts.map +1 -1
- package/dist/client/org/hooks.d.ts +3 -3
- package/dist/client/org/hooks.d.ts.map +1 -1
- package/dist/client/progress/RunsTray.d.ts +2 -2
- package/dist/client/progress/RunsTray.d.ts.map +1 -1
- package/dist/client/progress/RunsTray.js +34 -9
- package/dist/client/progress/RunsTray.js.map +1 -1
- package/dist/client/resources/ResourceEditor.d.ts.map +1 -1
- package/dist/client/resources/ResourceEditor.js +1 -1
- package/dist/client/resources/ResourceEditor.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +2 -0
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/client/rich-markdown-editor/BubbleToolbar.d.ts +1 -1
- package/dist/client/rich-markdown-editor/BubbleToolbar.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/CodeBlockNode.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/CodeBlockNode.js +2 -1
- package/dist/client/rich-markdown-editor/CodeBlockNode.js.map +1 -1
- package/dist/client/rich-markdown-editor/ImageExtension.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/ImageExtension.js +2 -1
- package/dist/client/rich-markdown-editor/ImageExtension.js.map +1 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts +1 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js +1 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -1
- package/dist/client/rich-markdown-editor/RichMarkdownEditor.d.ts +1 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.js +2 -3
- package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
- package/dist/client/rich-markdown-editor/SlashCommandMenu.d.ts +1 -1
- package/dist/client/rich-markdown-editor/SlashCommandMenu.d.ts.map +1 -1
- package/dist/client/route-state.d.ts +12 -2
- package/dist/client/route-state.d.ts.map +1 -1
- package/dist/client/route-state.js +1 -1
- package/dist/client/route-state.js.map +1 -1
- package/dist/client/route-warmup.d.ts +1 -1
- package/dist/client/route-warmup.d.ts.map +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts +2 -2
- package/dist/client/sharing/ShareDialog.d.ts +1 -1
- package/dist/client/sharing/ShareDialog.d.ts.map +1 -1
- package/dist/client/sse-event-processor.d.ts +8 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +33 -10
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/terminal/AgentTerminal.d.ts +1 -1
- package/dist/client/terminal/AgentTerminal.d.ts.map +1 -1
- package/dist/client/terminal/AgentTerminal.js +4 -2
- package/dist/client/terminal/AgentTerminal.js.map +1 -1
- package/dist/client/tool-cells/BashCell.d.ts +25 -0
- package/dist/client/tool-cells/BashCell.d.ts.map +1 -0
- package/dist/client/tool-cells/BashCell.js +49 -0
- package/dist/client/tool-cells/BashCell.js.map +1 -0
- package/dist/client/tool-cells/EditCell.d.ts +24 -0
- package/dist/client/tool-cells/EditCell.d.ts.map +1 -0
- package/dist/client/tool-cells/EditCell.js +126 -0
- package/dist/client/tool-cells/EditCell.js.map +1 -0
- package/dist/client/tool-cells/FilesChangedSummary.d.ts +13 -0
- package/dist/client/tool-cells/FilesChangedSummary.d.ts.map +1 -0
- package/dist/client/tool-cells/FilesChangedSummary.js +98 -0
- package/dist/client/tool-cells/FilesChangedSummary.js.map +1 -0
- package/dist/client/tool-cells/WriteCell.d.ts +17 -0
- package/dist/client/tool-cells/WriteCell.d.ts.map +1 -0
- package/dist/client/tool-cells/WriteCell.js +26 -0
- package/dist/client/tool-cells/WriteCell.js.map +1 -0
- package/dist/client/tool-cells/index.d.ts +8 -0
- package/dist/client/tool-cells/index.d.ts.map +1 -0
- package/dist/client/tool-cells/index.js +5 -0
- package/dist/client/tool-cells/index.js.map +1 -0
- package/dist/client/transcription/BuilderTranscriptionCta.d.ts +1 -1
- package/dist/client/transcription/BuilderTranscriptionCta.d.ts.map +1 -1
- package/dist/client/use-chat-threads.d.ts +1 -1
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +11 -8
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/client/use-db-sync.d.ts +2 -0
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +329 -302
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/code-agents/transcript-normalizer.d.ts +15 -1
- package/dist/code-agents/transcript-normalizer.d.ts.map +1 -1
- package/dist/code-agents/transcript-normalizer.js +47 -0
- package/dist/code-agents/transcript-normalizer.js.map +1 -1
- package/dist/coding-tools/index.d.ts +75 -0
- package/dist/coding-tools/index.d.ts.map +1 -1
- package/dist/coding-tools/index.js +137 -10
- package/dist/coding-tools/index.js.map +1 -1
- package/dist/collab/client.d.ts.map +1 -1
- package/dist/collab/client.js +15 -9
- package/dist/collab/client.js.map +1 -1
- package/dist/collab/ydoc-manager.d.ts +1 -1
- package/dist/collab/ydoc-manager.d.ts.map +1 -1
- package/dist/collab/ydoc-manager.js +1 -1
- package/dist/collab/ydoc-manager.js.map +1 -1
- package/dist/db/client.d.ts +9 -1
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +204 -48
- package/dist/db/client.js.map +1 -1
- package/dist/db/create-get-db.d.ts +38 -0
- package/dist/db/create-get-db.d.ts.map +1 -1
- package/dist/db/create-get-db.js +204 -4
- package/dist/db/create-get-db.js.map +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +159 -67
- package/dist/db/migrations.js.map +1 -1
- package/dist/demo/actions/toggle-demo-mode.d.ts +6 -1
- package/dist/demo/actions/toggle-demo-mode.d.ts.map +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +80 -39
- package/dist/deploy/build.js.map +1 -1
- package/dist/deploy/workspace-deploy.js +20 -10
- package/dist/deploy/workspace-deploy.js.map +1 -1
- package/dist/extensions/schema.d.ts +51 -51
- package/dist/extensions/slots/schema.d.ts +13 -13
- package/dist/file-upload/actions/upload-image.d.ts +26 -1
- package/dist/file-upload/actions/upload-image.d.ts.map +1 -1
- package/dist/file-upload/index.d.ts +1 -1
- package/dist/file-upload/index.d.ts.map +1 -1
- package/dist/file-upload/index.js +1 -1
- package/dist/file-upload/index.js.map +1 -1
- package/dist/file-upload/pre-upload-attachments.d.ts +37 -0
- package/dist/file-upload/pre-upload-attachments.d.ts.map +1 -1
- package/dist/file-upload/pre-upload-attachments.js +79 -19
- package/dist/file-upload/pre-upload-attachments.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/adapters/slack.js +1 -1
- package/dist/integrations/adapters/slack.js.map +1 -1
- package/dist/integrations/plugin.js +1 -1
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/jobs/scheduler.js +70 -21
- package/dist/jobs/scheduler.js.map +1 -1
- package/dist/mcp/actions/create-org-service-token.d.ts +14 -0
- package/dist/mcp/actions/create-org-service-token.d.ts.map +1 -0
- package/dist/mcp/actions/create-org-service-token.js +74 -0
- package/dist/mcp/actions/create-org-service-token.js.map +1 -0
- package/dist/mcp/actions/list-org-service-tokens.d.ts +17 -0
- package/dist/mcp/actions/list-org-service-tokens.d.ts.map +1 -0
- package/dist/mcp/actions/list-org-service-tokens.js +42 -0
- package/dist/mcp/actions/list-org-service-tokens.js.map +1 -0
- package/dist/mcp/actions/revoke-org-service-token.d.ts +7 -0
- package/dist/mcp/actions/revoke-org-service-token.d.ts.map +1 -0
- package/dist/mcp/actions/revoke-org-service-token.js +28 -0
- package/dist/mcp/actions/revoke-org-service-token.js.map +1 -0
- package/dist/mcp/actions/service-token-access.d.ts +24 -0
- package/dist/mcp/actions/service-token-access.d.ts.map +1 -0
- package/dist/mcp/actions/service-token-access.js +63 -0
- package/dist/mcp/actions/service-token-access.js.map +1 -0
- package/dist/mcp/build-server.d.ts +42 -11
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +53 -3
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/connect-route.d.ts +35 -0
- package/dist/mcp/connect-route.d.ts.map +1 -1
- package/dist/mcp/connect-route.js +57 -2
- package/dist/mcp/connect-route.js.map +1 -1
- package/dist/mcp/connect-store.d.ts +43 -0
- package/dist/mcp/connect-store.d.ts.map +1 -1
- package/dist/mcp/connect-store.js +129 -12
- package/dist/mcp/connect-store.js.map +1 -1
- package/dist/mcp/oauth-token.d.ts +10 -0
- package/dist/mcp/oauth-token.d.ts.map +1 -1
- package/dist/mcp/oauth-token.js +2 -0
- package/dist/mcp/oauth-token.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +3 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp-client/routes.js +1 -1
- package/dist/mcp-client/routes.js.map +1 -1
- package/dist/org/context.d.ts +4 -0
- package/dist/org/context.d.ts.map +1 -1
- package/dist/org/context.js +10 -0
- package/dist/org/context.js.map +1 -1
- package/dist/org/handlers.d.ts +11 -7
- package/dist/org/handlers.d.ts.map +1 -1
- package/dist/org/handlers.js +0 -8
- package/dist/org/handlers.js.map +1 -1
- package/dist/org/migrations.d.ts.map +1 -1
- package/dist/org/migrations.js +8 -0
- package/dist/org/migrations.js.map +1 -1
- package/dist/org/schema.d.ts +15 -15
- package/dist/progress/actions.d.ts.map +1 -1
- package/dist/progress/actions.js +13 -5
- package/dist/progress/actions.js.map +1 -1
- package/dist/provider-api/actions/delete-staged-dataset.d.ts +9 -0
- package/dist/provider-api/actions/delete-staged-dataset.d.ts.map +1 -0
- package/dist/provider-api/actions/delete-staged-dataset.js +35 -0
- package/dist/provider-api/actions/delete-staged-dataset.js.map +1 -0
- package/dist/provider-api/actions/list-staged-datasets.d.ts +15 -0
- package/dist/provider-api/actions/list-staged-datasets.d.ts.map +1 -0
- package/dist/provider-api/actions/list-staged-datasets.js +41 -0
- package/dist/provider-api/actions/list-staged-datasets.js.map +1 -0
- package/dist/provider-api/actions/query-staged-dataset.d.ts +29 -0
- package/dist/provider-api/actions/query-staged-dataset.d.ts.map +1 -0
- package/dist/provider-api/actions/query-staged-dataset.js +116 -0
- package/dist/provider-api/actions/query-staged-dataset.js.map +1 -0
- package/dist/provider-api/custom-registry.d.ts.map +1 -1
- package/dist/provider-api/custom-registry.js.map +1 -1
- package/dist/provider-api/index.d.ts +10 -10
- package/dist/provider-api/index.js +0 -5
- package/dist/provider-api/index.js.map +1 -1
- package/dist/provider-api/staged-datasets-aggregate.d.ts +46 -0
- package/dist/provider-api/staged-datasets-aggregate.d.ts.map +1 -0
- package/dist/provider-api/staged-datasets-aggregate.js +209 -0
- package/dist/provider-api/staged-datasets-aggregate.js.map +1 -0
- package/dist/provider-api/staged-datasets-store.d.ts +76 -0
- package/dist/provider-api/staged-datasets-store.d.ts.map +1 -0
- package/dist/provider-api/staged-datasets-store.js +319 -0
- package/dist/provider-api/staged-datasets-store.js.map +1 -0
- package/dist/provider-api/staging.d.ts +100 -0
- package/dist/provider-api/staging.d.ts.map +1 -0
- package/dist/provider-api/staging.js +281 -0
- package/dist/provider-api/staging.js.map +1 -0
- package/dist/resources/handlers.d.ts.map +1 -1
- package/dist/resources/handlers.js +13 -1
- package/dist/resources/handlers.js.map +1 -1
- package/dist/scripts/call-agent.d.ts.map +1 -1
- package/dist/scripts/call-agent.js +1 -2
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/scripts/resources/migrate-learnings.d.ts +1 -1
- package/dist/scripts/resources/migrate-learnings.d.ts.map +1 -1
- package/dist/scripts/resources/migrate-learnings.js +1 -1
- package/dist/scripts/resources/migrate-learnings.js.map +1 -1
- package/dist/secrets/schema.d.ts +7 -7
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +14 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +3 -2
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +33 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +251 -180
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-discovery.d.ts.map +1 -1
- package/dist/server/agent-discovery.js +13 -16
- package/dist/server/agent-discovery.js.map +1 -1
- package/dist/server/agent-teams-run-queue.d.ts +31 -8
- package/dist/server/agent-teams-run-queue.d.ts.map +1 -1
- package/dist/server/agent-teams-run-queue.js +61 -18
- package/dist/server/agent-teams-run-queue.js.map +1 -1
- package/dist/server/agent-teams.d.ts +27 -1
- package/dist/server/agent-teams.d.ts.map +1 -1
- package/dist/server/agent-teams.js +214 -14
- package/dist/server/agent-teams.js.map +1 -1
- package/dist/server/app-base-path.d.ts +20 -0
- package/dist/server/app-base-path.d.ts.map +1 -1
- package/dist/server/app-base-path.js +36 -0
- package/dist/server/app-base-path.js.map +1 -1
- package/dist/server/attachment-actions.d.ts +43 -0
- package/dist/server/attachment-actions.d.ts.map +1 -0
- package/dist/server/attachment-actions.js +214 -0
- package/dist/server/attachment-actions.js.map +1 -0
- package/dist/server/auth.js +1 -1
- package/dist/server/auth.js.map +1 -1
- package/dist/server/complete-text.d.ts +56 -0
- package/dist/server/complete-text.d.ts.map +1 -0
- package/dist/server/complete-text.js +147 -0
- package/dist/server/complete-text.js.map +1 -0
- package/dist/server/core-routes-plugin.d.ts +1 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +37 -27
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/cors-origins.d.ts.map +1 -1
- package/dist/server/cors-origins.js +6 -1
- package/dist/server/cors-origins.js.map +1 -1
- package/dist/server/create-server.d.ts.map +1 -1
- package/dist/server/create-server.js +2 -1
- package/dist/server/create-server.js.map +1 -1
- package/dist/server/csrf.d.ts +1 -1
- package/dist/server/csrf.d.ts.map +1 -1
- package/dist/server/email-actions.d.ts +19 -0
- package/dist/server/email-actions.d.ts.map +1 -0
- package/dist/server/email-actions.js +191 -0
- package/dist/server/email-actions.js.map +1 -0
- package/dist/server/embed-route.js +1 -1
- package/dist/server/embed-route.js.map +1 -1
- package/dist/server/embed-session.d.ts.map +1 -1
- package/dist/server/embed-session.js +5 -1
- package/dist/server/embed-session.js.map +1 -1
- package/dist/server/entry-server.d.ts +24 -0
- package/dist/server/entry-server.d.ts.map +1 -0
- package/dist/server/entry-server.js +54 -0
- package/dist/server/entry-server.js.map +1 -0
- package/dist/server/framework-request-handler.d.ts.map +1 -1
- package/dist/server/framework-request-handler.js +2 -10
- package/dist/server/framework-request-handler.js.map +1 -1
- package/dist/server/google-oauth.d.ts.map +1 -1
- package/dist/server/google-oauth.js +2 -9
- package/dist/server/google-oauth.js.map +1 -1
- package/dist/server/google-realtime-session.d.ts.map +1 -1
- package/dist/server/google-realtime-session.js +6 -4
- package/dist/server/google-realtime-session.js.map +1 -1
- package/dist/server/h3-helpers.d.ts +39 -0
- package/dist/server/h3-helpers.d.ts.map +1 -1
- package/dist/server/h3-helpers.js +104 -1
- package/dist/server/h3-helpers.js.map +1 -1
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +1 -8
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/server/open-route.d.ts.map +1 -1
- package/dist/server/open-route.js +1 -0
- package/dist/server/open-route.js.map +1 -1
- package/dist/server/prompts/framework-core-compact.d.ts +19 -0
- package/dist/server/prompts/framework-core-compact.d.ts.map +1 -0
- package/dist/server/prompts/framework-core-compact.js +69 -0
- package/dist/server/prompts/framework-core-compact.js.map +1 -0
- package/dist/server/prompts/framework-core.d.ts +26 -0
- package/dist/server/prompts/framework-core.d.ts.map +1 -0
- package/dist/server/prompts/framework-core.js +130 -0
- package/dist/server/prompts/framework-core.js.map +1 -0
- package/dist/server/prompts/index.d.ts +9 -0
- package/dist/server/prompts/index.d.ts.map +1 -0
- package/dist/server/prompts/index.js +9 -0
- package/dist/server/prompts/index.js.map +1 -0
- package/dist/server/prompts/model-overlays.d.ts +18 -0
- package/dist/server/prompts/model-overlays.d.ts.map +1 -0
- package/dist/server/prompts/model-overlays.js +46 -0
- package/dist/server/prompts/model-overlays.js.map +1 -0
- package/dist/server/prompts/shared-rules.d.ts +29 -0
- package/dist/server/prompts/shared-rules.d.ts.map +1 -0
- package/dist/server/prompts/shared-rules.js +54 -0
- package/dist/server/prompts/shared-rules.js.map +1 -0
- package/dist/server/security-headers.d.ts +7 -1
- package/dist/server/security-headers.d.ts.map +1 -1
- package/dist/server/security-headers.js +11 -0
- package/dist/server/security-headers.js.map +1 -1
- package/dist/server/ssr-handler.d.ts.map +1 -1
- package/dist/server/ssr-handler.js +135 -46
- package/dist/server/ssr-handler.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts.map +1 -1
- package/dist/server/transcribe-voice.js +7 -4
- package/dist/server/transcribe-voice.js.map +1 -1
- package/dist/settings/store.d.ts.map +1 -1
- package/dist/settings/store.js +9 -0
- package/dist/settings/store.js.map +1 -1
- package/dist/shared/markdown-block-split.d.ts +39 -0
- package/dist/shared/markdown-block-split.d.ts.map +1 -0
- package/dist/shared/markdown-block-split.js +97 -0
- package/dist/shared/markdown-block-split.js.map +1 -0
- package/dist/shared/reasoning-effort.js +13 -1
- package/dist/shared/reasoning-effort.js.map +1 -1
- package/dist/shared/streaming-text-smoothing.d.ts +18 -0
- package/dist/shared/streaming-text-smoothing.d.ts.map +1 -1
- package/dist/shared/streaming-text-smoothing.js +70 -4
- package/dist/shared/streaming-text-smoothing.js.map +1 -1
- package/dist/sharing/actions/list-resource-shares.d.ts +24 -1
- package/dist/sharing/actions/list-resource-shares.d.ts.map +1 -1
- package/dist/sharing/actions/set-resource-visibility.d.ts +8 -1
- package/dist/sharing/actions/set-resource-visibility.d.ts.map +1 -1
- package/dist/sharing/actions/share-resource.d.ts +12 -1
- package/dist/sharing/actions/share-resource.d.ts.map +1 -1
- package/dist/sharing/actions/unshare-resource.d.ts +8 -1
- package/dist/sharing/actions/unshare-resource.d.ts.map +1 -1
- package/dist/sharing/schema.d.ts +10 -10
- package/dist/styles/agent-conversation.css +239 -0
- package/dist/templates/default/.agents/skills/delegate-to-agent/SKILL.md +50 -2
- package/dist/templates/default/AGENTS.md +1 -1
- package/dist/templates/default/DEVELOPING.md +19 -0
- package/dist/templates/default/app/entry.client.tsx +4 -1
- package/dist/templates/default/app/entry.server.tsx +4 -56
- package/dist/templates/default/app/global.css +3 -2
- package/dist/templates/default/app/root.tsx +8 -24
- package/dist/templates/default/app/routes/_index.tsx +0 -13
- package/dist/templates/default/package.json +6 -5
- package/dist/templates/default/tsconfig.json +2 -1
- package/dist/templates/starter-shell-sync.spec.ts +118 -0
- package/dist/templates/ui-primitives-sync.spec.ts +399 -0
- package/dist/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +50 -2
- package/dist/terminal/pty-server.js +1 -1
- package/dist/terminal/pty-server.js.map +1 -1
- package/dist/triggers/dispatcher.js +1 -1
- package/dist/triggers/dispatcher.js.map +1 -1
- package/dist/usage/store.d.ts.map +1 -1
- package/dist/usage/store.js +60 -7
- package/dist/usage/store.js.map +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +44 -12
- package/dist/vite/client.js.map +1 -1
- package/dist/workspace-files/schema.d.ts +8 -8
- package/dist/workspace-files/tool.d.ts.map +1 -1
- package/dist/workspace-files/tool.js +0 -1
- package/dist/workspace-files/tool.js.map +1 -1
- package/docs/content/a2a-protocol.md +18 -12
- package/docs/content/actions.md +42 -10
- package/docs/content/agent-mentions.md +7 -8
- package/docs/content/agent-teams.md +23 -37
- package/docs/content/agent-web-surfaces.md +18 -9
- package/docs/content/authentication.md +6 -17
- package/docs/content/automations.md +43 -15
- package/docs/content/cli-adapters.md +25 -24
- package/docs/content/client.md +66 -17
- package/docs/content/cloneable-saas.md +19 -23
- package/docs/content/code-agents-ui.md +3 -31
- package/docs/content/components.md +308 -0
- package/docs/content/context-awareness.md +4 -0
- package/docs/content/creating-templates.md +4 -2
- package/docs/content/cross-app-sso.md +45 -19
- package/docs/content/database.md +26 -1
- package/docs/content/deployment.md +3 -1
- package/docs/content/dispatch.md +9 -37
- package/docs/content/drop-in-agent.md +123 -2
- package/docs/content/embedding-sdk.md +35 -0
- package/docs/content/extensions.md +2 -2
- package/docs/content/external-agents.md +86 -171
- package/docs/content/faq.md +6 -27
- package/docs/content/frames.md +9 -12
- package/docs/content/getting-started.md +80 -77
- package/docs/content/key-concepts.md +29 -19
- package/docs/content/mcp-apps.md +103 -0
- package/docs/content/mcp-clients.md +2 -2
- package/docs/content/mcp-protocol.md +40 -17
- package/docs/content/messaging.md +11 -4
- package/docs/content/migration-workbench.md +4 -47
- package/docs/content/multi-app-workspace.md +48 -17
- package/docs/content/multi-tenancy.md +1 -1
- package/docs/content/notifications.md +8 -6
- package/docs/content/observability.md +26 -15
- package/docs/content/onboarding.md +7 -1
- package/docs/content/pr-visual-recap.md +203 -23
- package/docs/content/progress.md +5 -5
- package/docs/content/pure-agent-apps.md +3 -1
- package/docs/content/real-time-collaboration.md +106 -0
- package/docs/content/recurring-jobs.md +17 -1
- package/docs/content/security.md +17 -3
- package/docs/content/server.md +39 -3
- package/docs/content/sharing.md +20 -1
- package/docs/content/skills-guide.md +151 -125
- package/docs/content/template-analytics.md +8 -0
- package/docs/content/template-assets.md +2 -0
- package/docs/content/template-brain.md +59 -3
- package/docs/content/template-calendar.md +8 -0
- package/docs/content/template-clips.md +11 -2
- package/docs/content/template-content.md +24 -4
- package/docs/content/template-design.md +19 -17
- package/docs/content/template-dispatch.md +2 -0
- package/docs/content/template-forms.md +28 -1
- package/docs/content/template-mail.md +17 -0
- package/docs/content/template-plan.md +177 -10
- package/docs/content/template-slides.md +51 -12
- package/docs/content/template-videos.md +17 -0
- package/docs/content/tracking.md +17 -13
- package/docs/content/using-your-agent.md +15 -5
- package/docs/content/voice-input.md +1 -1
- package/docs/content/what-is-agent-native.md +5 -6
- package/docs/content/workspace-connections.md +138 -424
- package/docs/content/workspace-management.md +12 -128
- package/docs/content/workspace.md +125 -199
- package/docs/content/writing-agent-instructions.md +17 -1
- package/package.json +25 -6
- package/src/templates/default/.agents/skills/delegate-to-agent/SKILL.md +50 -2
- package/src/templates/default/AGENTS.md +1 -1
- package/src/templates/default/DEVELOPING.md +19 -0
- package/src/templates/default/app/entry.client.tsx +4 -1
- package/src/templates/default/app/entry.server.tsx +4 -56
- package/src/templates/default/app/global.css +3 -2
- package/src/templates/default/app/root.tsx +8 -24
- package/src/templates/default/app/routes/_index.tsx +0 -13
- package/src/templates/default/package.json +6 -5
- package/src/templates/default/tsconfig.json +2 -1
- package/src/templates/starter-shell-sync.spec.ts +118 -0
- package/src/templates/ui-primitives-sync.spec.ts +399 -0
- package/src/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +50 -2
- package/tsconfig.base.json +2 -10
- package/dist/cli/app-skill.d.ts +0 -157
- package/dist/cli/app-skill.d.ts.map +0 -1
- package/dist/cli/audit-agent-web.d.ts +0 -2
- package/dist/cli/audit-agent-web.d.ts.map +0 -1
- package/dist/cli/code-agent-connector.d.ts +0 -17
- package/dist/cli/code-agent-connector.d.ts.map +0 -1
- package/dist/cli/code.d.ts +0 -66
- package/dist/cli/code.d.ts.map +0 -1
- package/dist/cli/connect.d.ts +0 -140
- package/dist/cli/connect.d.ts.map +0 -1
- package/dist/cli/context-xray-local.d.ts +0 -16
- package/dist/cli/context-xray-local.d.ts.map +0 -1
- package/dist/cli/create-workspace.d.ts +0 -8
- package/dist/cli/create-workspace.d.ts.map +0 -1
- package/dist/cli/index.d.ts +0 -3
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/info.d.ts +0 -2
- package/dist/cli/info.d.ts.map +0 -1
- package/dist/cli/mcp-config-writers.d.ts +0 -82
- package/dist/cli/mcp-config-writers.d.ts.map +0 -1
- package/dist/cli/mcp.d.ts +0 -16
- package/dist/cli/mcp.d.ts.map +0 -1
- package/dist/cli/migrate.d.ts +0 -38
- package/dist/cli/migrate.d.ts.map +0 -1
- package/dist/cli/plan-local.d.ts +0 -43
- package/dist/cli/plan-local.d.ts.map +0 -1
- package/dist/cli/plan-publish-store.d.ts +0 -62
- package/dist/cli/plan-publish-store.d.ts.map +0 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts +0 -11
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +0 -1
- package/dist/cli/recap.d.ts +0 -297
- package/dist/cli/recap.d.ts.map +0 -1
- package/dist/cli/skills.d.ts +0 -162
- package/dist/cli/skills.d.ts.map +0 -1
- package/dist/cli/workspace-dev.d.ts +0 -96
- package/dist/cli/workspace-dev.d.ts.map +0 -1
|
@@ -1,191 +1,37 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
2
|
-
import
|
|
3
|
-
import { AssistantRuntimeProvider, useLocalRuntime, useThreadRuntime, useThread, useAui, useComposer, useComposerRuntime,
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useRef, useEffect, useCallback, useMemo, forwardRef, useImperativeHandle, } from "react";
|
|
3
|
+
import { AssistantRuntimeProvider, useLocalRuntime, useThreadRuntime, useThread, useAui, useComposer, useComposerRuntime, ThreadPrimitive, } from "@assistant-ui/react";
|
|
4
4
|
import { CompositeAttachmentAdapter } from "@assistant-ui/react";
|
|
5
|
-
import ReactMarkdown, { defaultUrlTransform } from "react-markdown";
|
|
6
|
-
import remarkGfm from "remark-gfm";
|
|
7
5
|
import { createAgentChatAdapter, } from "./agent-chat-adapter.js";
|
|
8
6
|
import { appendAgentChatContextToMessage, formatAgentChatContextItemsForPrompt, normalizeAgentChatContextItem, publishAgentChatContextItems, refreshAgentChatContext, } from "./agent-chat.js";
|
|
9
7
|
import { useAgentDynamicSuggestionsResult, } from "./dynamic-suggestions.js";
|
|
10
|
-
import {
|
|
8
|
+
import { PROVIDER_ENV_VARS } from "../agent/engine/provider-env-vars.js";
|
|
11
9
|
import { getActiveRun } from "./active-run-state.js";
|
|
12
10
|
import { AgentAutoContinueSignal, readSSEStreamRaw, } from "./sse-event-processor.js";
|
|
13
11
|
import { captureError } from "./analytics.js";
|
|
14
12
|
import { AssistantMessageListErrorBoundary, AssistantUiStaleIndexErrorBoundary, } from "./assistant-ui-recovery.js";
|
|
15
13
|
import { cn } from "./utils.js";
|
|
16
|
-
import { writeClipboardText } from "./clipboard.js";
|
|
17
14
|
import { useNearBottomAutoscroll } from "./conversation/index.js";
|
|
18
15
|
import { TextAttachmentAdapter } from "./composer/attachment-accept.js";
|
|
19
|
-
import { AgentTaskCard } from "./AgentTaskCard.js";
|
|
20
|
-
import { ConnectBuilderCard } from "./ConnectBuilderCard.js";
|
|
21
|
-
import { McpAppRenderer } from "./mcp-apps/McpAppRenderer.js";
|
|
22
|
-
import { humanizeToolName } from "./tool-display.js";
|
|
23
|
-
import { useBuilderConnectFlow } from "./settings/useBuilderStatus.js";
|
|
24
16
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./components/ui/tooltip.js";
|
|
25
|
-
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "./components/ui/dropdown-menu.js";
|
|
26
|
-
import { IframeEmbed, parseEmbedBody } from "./IframeEmbed.js";
|
|
27
17
|
import { GuidedQuestionFlow, useGuidedQuestionFlow, } from "./guided-questions.js";
|
|
28
18
|
import { useDevMode } from "./use-dev-mode.js";
|
|
29
19
|
import { agentNativePath } from "./api-path.js";
|
|
30
|
-
import { saveAgentEngineApiKey, } from "./agent-engine-key.js";
|
|
31
|
-
import { BUILDER_SPACE_SETTINGS_URL, NEW_CHAT_ACTION_HREF, } from "./error-format.js";
|
|
32
|
-
import { ThumbsFeedback } from "./observability/ThumbsFeedback.js";
|
|
33
20
|
import { TiptapComposer, } from "./composer/TiptapComposer.js";
|
|
34
21
|
import { AgentComposerFrame } from "./composer/AgentComposerFrame.js";
|
|
35
22
|
import { isPastedTextAttachmentName } from "./composer/pasted-text.js";
|
|
36
23
|
import { PastedTextChip } from "./composer/PastedTextChip.js";
|
|
37
24
|
import { ContextMeter } from "./context-xray/ContextMeter.js";
|
|
38
|
-
import { IconMessage, IconX, IconPlayerStop,
|
|
25
|
+
import { IconMessage, IconX, IconPlayerStop, IconChevronDown, IconTerminal, IconClock, IconAlertTriangle, IconRefresh, } from "@tabler/icons-react";
|
|
26
|
+
// ─── chat/ module imports ─────────────────────────────────────────────────────
|
|
27
|
+
import { DownscalingImageAttachmentAdapter, BinaryDocumentAttachmentAdapter, MAX_ESTIMATED_BODY_BYTES, AGGRESSIVE_MAX_IMAGE_DIMENSION, AGGRESSIVE_JPEG_QUALITY, transcodeImageToDataURL, createAgentImageAttachments, serializeQueuedAttachments, estimateAttachmentBodyBytes, } from "./chat/attachment-adapters.js";
|
|
28
|
+
import { TextStreamingContext } from "./chat/markdown-renderer.js";
|
|
29
|
+
import { ChatRunningContext, ReconnectStreamMessage, } from "./chat/tool-call-display.js";
|
|
30
|
+
import { CheckpointContext, MessageActionsContext, UserMessage, AssistantMessage, SelectionAttachedPill, RunningActivityStatus, } from "./chat/message-components.js";
|
|
31
|
+
import { BuilderSetupCard, LoopLimitContinueCard, RunErrorRecoveryCard, PlanModeCallout, getLoopLimitMetadata, getRunErrorMetadata, getRequestModeMetadata, } from "./chat/run-recovery.js";
|
|
32
|
+
import { repoHasAssistantMessage, getRepoMessages, getRepoMessage, shouldImportServerThreadData, } from "./chat/repo-helpers.js";
|
|
39
33
|
export { AssistantMessageListErrorBoundary, AssistantUiStaleIndexErrorBoundary, assistantUiRecoverableRenderErrorKind, isAssistantUiRecoverableRenderError, isAssistantUiStaleIndexError, } from "./assistant-ui-recovery.js";
|
|
40
|
-
|
|
41
|
-
accept = "image/*";
|
|
42
|
-
async add(state) {
|
|
43
|
-
return {
|
|
44
|
-
id: state.file.name,
|
|
45
|
-
type: "image",
|
|
46
|
-
name: state.file.name,
|
|
47
|
-
contentType: state.file.type,
|
|
48
|
-
file: state.file,
|
|
49
|
-
status: { type: "requires-action", reason: "composer-send" },
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
async send(attachment) {
|
|
53
|
-
return {
|
|
54
|
-
...attachment,
|
|
55
|
-
status: { type: "complete" },
|
|
56
|
-
content: [
|
|
57
|
-
{
|
|
58
|
-
type: "image",
|
|
59
|
-
image: await getImageFileDataURL(attachment.file),
|
|
60
|
-
},
|
|
61
|
-
],
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
async remove() {
|
|
65
|
-
// noop
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
class BinaryDocumentAttachmentAdapter {
|
|
69
|
-
accept = "application/pdf,.pdf";
|
|
70
|
-
async add(state) {
|
|
71
|
-
return {
|
|
72
|
-
id: state.file.name,
|
|
73
|
-
type: "document",
|
|
74
|
-
name: state.file.name,
|
|
75
|
-
contentType: inferDocumentContentType(state.file),
|
|
76
|
-
file: state.file,
|
|
77
|
-
status: { type: "requires-action", reason: "composer-send" },
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
async send(attachment) {
|
|
81
|
-
return {
|
|
82
|
-
...attachment,
|
|
83
|
-
status: { type: "complete" },
|
|
84
|
-
content: [
|
|
85
|
-
{
|
|
86
|
-
type: "file",
|
|
87
|
-
filename: attachment.name,
|
|
88
|
-
data: await getFileDataURL(attachment.file),
|
|
89
|
-
mimeType: inferDocumentContentType(attachment.file),
|
|
90
|
-
},
|
|
91
|
-
],
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
async remove() {
|
|
95
|
-
// noop
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
function inferDocumentContentType(file) {
|
|
99
|
-
if (file.type)
|
|
100
|
-
return file.type;
|
|
101
|
-
if (file.name.toLowerCase().endsWith(".pdf"))
|
|
102
|
-
return "application/pdf";
|
|
103
|
-
return "application/octet-stream";
|
|
104
|
-
}
|
|
105
|
-
function getFileDataURL(file) {
|
|
106
|
-
return new Promise((resolve, reject) => {
|
|
107
|
-
const reader = new FileReader();
|
|
108
|
-
reader.onload = () => resolve(reader.result);
|
|
109
|
-
reader.onerror = (error) => reject(error);
|
|
110
|
-
reader.readAsDataURL(file);
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
// Anthropic / OpenAI vision inputs choke on multi-megabyte images, and
|
|
114
|
-
// base64-encoding a raw screenshot eats enough heap to crash the composer
|
|
115
|
-
// (PayloadTooLarge / "Maximum call stack" in serializers). Downscale large
|
|
116
|
-
// images on the client before we ever serialize them.
|
|
117
|
-
const MAX_IMAGE_BYTES = 4 * 1024 * 1024;
|
|
118
|
-
const MAX_IMAGE_DIMENSION = 2048;
|
|
119
|
-
function loadImage(url) {
|
|
120
|
-
return new Promise((resolve, reject) => {
|
|
121
|
-
const img = new Image();
|
|
122
|
-
img.onload = () => resolve(img);
|
|
123
|
-
img.onerror = () => reject(new Error("Failed to decode pasted image"));
|
|
124
|
-
img.src = url;
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
async function getImageFileDataURL(file) {
|
|
128
|
-
if (file.size <= MAX_IMAGE_BYTES) {
|
|
129
|
-
return getFileDataURL(file);
|
|
130
|
-
}
|
|
131
|
-
if (typeof document === "undefined" || typeof Image === "undefined") {
|
|
132
|
-
return getFileDataURL(file);
|
|
133
|
-
}
|
|
134
|
-
const objectUrl = URL.createObjectURL(file);
|
|
135
|
-
try {
|
|
136
|
-
const img = await loadImage(objectUrl);
|
|
137
|
-
const ratio = Math.min(MAX_IMAGE_DIMENSION / img.naturalWidth, MAX_IMAGE_DIMENSION / img.naturalHeight, 1);
|
|
138
|
-
const width = Math.max(1, Math.round(img.naturalWidth * ratio));
|
|
139
|
-
const height = Math.max(1, Math.round(img.naturalHeight * ratio));
|
|
140
|
-
const canvas = document.createElement("canvas");
|
|
141
|
-
canvas.width = width;
|
|
142
|
-
canvas.height = height;
|
|
143
|
-
const ctx = canvas.getContext("2d");
|
|
144
|
-
if (!ctx) {
|
|
145
|
-
return getFileDataURL(file);
|
|
146
|
-
}
|
|
147
|
-
ctx.drawImage(img, 0, 0, width, height);
|
|
148
|
-
const useJpeg = file.type !== "image/png" || file.size > MAX_IMAGE_BYTES * 2;
|
|
149
|
-
return canvas.toDataURL(useJpeg ? "image/jpeg" : "image/png", 0.85);
|
|
150
|
-
}
|
|
151
|
-
catch {
|
|
152
|
-
return getFileDataURL(file);
|
|
153
|
-
}
|
|
154
|
-
finally {
|
|
155
|
-
URL.revokeObjectURL(objectUrl);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
function imageContentTypeFromDataUrl(dataUrl) {
|
|
159
|
-
const match = /^data:([^;,]+)/.exec(dataUrl);
|
|
160
|
-
return match?.[1] || "image/jpeg";
|
|
161
|
-
}
|
|
162
|
-
function imageExtensionFromContentType(contentType) {
|
|
163
|
-
if (contentType === "image/png")
|
|
164
|
-
return "png";
|
|
165
|
-
if (contentType === "image/webp")
|
|
166
|
-
return "webp";
|
|
167
|
-
if (contentType === "image/gif")
|
|
168
|
-
return "gif";
|
|
169
|
-
return "jpg";
|
|
170
|
-
}
|
|
171
|
-
function createAgentImageAttachments(images) {
|
|
172
|
-
const validImages = (images ?? []).filter((image) => image.trim().length > 0);
|
|
173
|
-
if (validImages.length === 0)
|
|
174
|
-
return undefined;
|
|
175
|
-
return validImages.map((image, index) => {
|
|
176
|
-
const contentType = imageContentTypeFromDataUrl(image);
|
|
177
|
-
const extension = imageExtensionFromContentType(contentType);
|
|
178
|
-
const name = `image-${index + 1}.${extension}`;
|
|
179
|
-
return {
|
|
180
|
-
id: `agent-chat-image-${index + 1}`,
|
|
181
|
-
type: "image",
|
|
182
|
-
name,
|
|
183
|
-
contentType,
|
|
184
|
-
status: { type: "complete" },
|
|
185
|
-
content: [{ type: "image", image }],
|
|
186
|
-
};
|
|
187
|
-
});
|
|
188
|
-
}
|
|
34
|
+
export { displayableUserMessageText } from "./chat/message-components.js";
|
|
189
35
|
function createUserMessageRunConfig(references, requestMode, recoveryAction, trackInRunsTray) {
|
|
190
36
|
const custom = {};
|
|
191
37
|
if (references && references.length > 0) {
|
|
@@ -208,156 +54,6 @@ function createUserMessageRunConfig(references, requestMode, recoveryAction, tra
|
|
|
208
54
|
}
|
|
209
55
|
return options;
|
|
210
56
|
}
|
|
211
|
-
function escapeQueuedAttachmentAttribute(value) {
|
|
212
|
-
return value.replace(/&/g, "&").replace(/"/g, """);
|
|
213
|
-
}
|
|
214
|
-
function isTextLikeFile(file) {
|
|
215
|
-
if (file.type.startsWith("text/"))
|
|
216
|
-
return true;
|
|
217
|
-
if (file.type === "application/json")
|
|
218
|
-
return true;
|
|
219
|
-
return /\.(txt|md|markdown|csv|json|yaml|yml)$/i.test(file.name);
|
|
220
|
-
}
|
|
221
|
-
function textFileAttachmentEnvelope(file, text) {
|
|
222
|
-
const contentType = file.type || "text/plain";
|
|
223
|
-
return `<attachment name="${escapeQueuedAttachmentAttribute(file.name)}" contentType="${escapeQueuedAttachmentAttribute(contentType)}">\n${text}\n</attachment>`;
|
|
224
|
-
}
|
|
225
|
-
function serializeAttachmentContentPart(part) {
|
|
226
|
-
if (part.type === "image" && typeof part.image === "string") {
|
|
227
|
-
return { type: "image", image: part.image };
|
|
228
|
-
}
|
|
229
|
-
if (part.type === "text" && typeof part.text === "string") {
|
|
230
|
-
return { type: "text", text: part.text };
|
|
231
|
-
}
|
|
232
|
-
if (part.type === "file" && typeof part.data === "string") {
|
|
233
|
-
return {
|
|
234
|
-
type: "file",
|
|
235
|
-
data: part.data,
|
|
236
|
-
mimeType: typeof part.mimeType === "string"
|
|
237
|
-
? part.mimeType
|
|
238
|
-
: "application/octet-stream",
|
|
239
|
-
...(typeof part.filename === "string" ? { filename: part.filename } : {}),
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
return null;
|
|
243
|
-
}
|
|
244
|
-
async function serializeQueuedAttachments(attachments) {
|
|
245
|
-
const queued = [];
|
|
246
|
-
for (const raw of attachments ?? []) {
|
|
247
|
-
const attachment = raw;
|
|
248
|
-
const name = attachment.name || attachment.file?.name || "attachment";
|
|
249
|
-
const id = attachment.id || name;
|
|
250
|
-
const type = attachment.type || "file";
|
|
251
|
-
const contentType = attachment.contentType || attachment.file?.type;
|
|
252
|
-
if (Array.isArray(attachment.content) && attachment.content.length > 0) {
|
|
253
|
-
const content = attachment.content
|
|
254
|
-
.map((part) => serializeAttachmentContentPart(part))
|
|
255
|
-
.filter((part) => !!part);
|
|
256
|
-
if (content.length > 0) {
|
|
257
|
-
queued.push({
|
|
258
|
-
id,
|
|
259
|
-
type,
|
|
260
|
-
name,
|
|
261
|
-
contentType,
|
|
262
|
-
status: { type: "complete" },
|
|
263
|
-
content,
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
continue;
|
|
267
|
-
}
|
|
268
|
-
if (typeof File !== "undefined" && attachment.file instanceof File) {
|
|
269
|
-
const file = attachment.file;
|
|
270
|
-
if (file.type.startsWith("image/")) {
|
|
271
|
-
queued.push({
|
|
272
|
-
id,
|
|
273
|
-
type: "image",
|
|
274
|
-
name,
|
|
275
|
-
contentType: file.type,
|
|
276
|
-
status: { type: "complete" },
|
|
277
|
-
content: [{ type: "image", image: await getImageFileDataURL(file) }],
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
else if (isTextLikeFile(file)) {
|
|
281
|
-
queued.push({
|
|
282
|
-
id,
|
|
283
|
-
type: "file",
|
|
284
|
-
name,
|
|
285
|
-
contentType: file.type || "text/plain",
|
|
286
|
-
status: { type: "complete" },
|
|
287
|
-
content: [
|
|
288
|
-
{
|
|
289
|
-
type: "text",
|
|
290
|
-
text: textFileAttachmentEnvelope(file, await file.text()),
|
|
291
|
-
},
|
|
292
|
-
],
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
else {
|
|
296
|
-
queued.push({
|
|
297
|
-
id,
|
|
298
|
-
type: "document",
|
|
299
|
-
name,
|
|
300
|
-
contentType: inferDocumentContentType(file),
|
|
301
|
-
status: { type: "complete" },
|
|
302
|
-
content: [
|
|
303
|
-
{
|
|
304
|
-
type: "file",
|
|
305
|
-
filename: file.name,
|
|
306
|
-
data: await getFileDataURL(file),
|
|
307
|
-
mimeType: inferDocumentContentType(file),
|
|
308
|
-
},
|
|
309
|
-
],
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
return queued.length > 0 ? queued : undefined;
|
|
315
|
-
}
|
|
316
|
-
// ─── Markdown Text ──────────────────────────────────────────────────────────
|
|
317
|
-
const markdownStyles = `
|
|
318
|
-
.agent-markdown > :first-child { margin-top: 0; }
|
|
319
|
-
.agent-markdown > :last-child { margin-bottom: 0; }
|
|
320
|
-
.agent-markdown p { margin: 0.5em 0; }
|
|
321
|
-
.agent-markdown ul, .agent-markdown ol { margin: 0.5em 0; padding-left: 1.5em; }
|
|
322
|
-
.agent-markdown li { margin: 0.2em 0; }
|
|
323
|
-
.agent-markdown li > p { margin: 0; }
|
|
324
|
-
.agent-markdown h1 { font-size: 1.25em; font-weight: 600; margin: 0.75em 0 0.25em; }
|
|
325
|
-
.agent-markdown h2 { font-size: 1.125em; font-weight: 600; margin: 0.75em 0 0.25em; }
|
|
326
|
-
.agent-markdown h3 { font-size: 1em; font-weight: 600; margin: 0.75em 0 0.25em; }
|
|
327
|
-
.agent-markdown strong { font-weight: 600; }
|
|
328
|
-
.agent-markdown em { font-style: italic; }
|
|
329
|
-
.agent-markdown code { font-size: 0.875em; padding: 0.15em 0.35em; border-radius: 0.25em; background: hsl(var(--muted, 0 0% 15%)); color: hsl(var(--foreground, 0 0% 90%)); border: 1px solid hsl(var(--border, 0 0% 80%)); }
|
|
330
|
-
.agent-markdown pre { margin: 0.5em 0; padding: 0.75em 1em; border-radius: 0.375em; background: hsl(var(--muted, 0 0% 15%)); color: hsl(var(--foreground, 0 0% 90%)); overflow-x: auto; border: 1px solid hsl(var(--border, 0 0% 80%)); }
|
|
331
|
-
.agent-markdown pre code { padding: 0; background: transparent; font-size: 0.8125em; color: inherit; border: none; }
|
|
332
|
-
.agent-markdown-shiki { margin: 0.5em 0; border-radius: 0.375em; overflow: hidden; font-size: 0.8125em; }
|
|
333
|
-
.agent-markdown-shiki pre { margin: 0; padding: 0.75em 1em; overflow-x: auto; background: var(--shiki-light-bg); color: var(--shiki-light); }
|
|
334
|
-
.agent-markdown-shiki pre code { background: transparent; padding: 0; font-size: inherit; color: inherit; }
|
|
335
|
-
.agent-markdown-shiki pre span { color: var(--shiki-light); background: var(--shiki-light-bg); }
|
|
336
|
-
.dark .agent-markdown-shiki pre { background: var(--shiki-dark-bg); color: var(--shiki-dark); }
|
|
337
|
-
.dark .agent-markdown-shiki pre span { color: var(--shiki-dark); background: var(--shiki-dark-bg); }
|
|
338
|
-
@media (prefers-color-scheme: dark) { :root:not(.light) .agent-markdown-shiki pre { background: var(--shiki-dark-bg); color: var(--shiki-dark); } :root:not(.light) .agent-markdown-shiki pre span { color: var(--shiki-dark); background: var(--shiki-dark-bg); } }
|
|
339
|
-
.agent-tool-code .agent-markdown-shiki { margin: 0; border-radius: 0; min-width: max-content; }
|
|
340
|
-
.agent-tool-code .agent-markdown-shiki pre { padding: 0.75rem; border: 0; background: transparent; }
|
|
341
|
-
.agent-tool-code .agent-markdown-shiki pre span { background: transparent; }
|
|
342
|
-
.agent-tool-code pre { margin: 0; min-width: max-content; padding: 0.75rem; background: transparent; color: inherit; }
|
|
343
|
-
.agent-tool-code mark { border-radius: 0.1875rem; background: rgba(245, 158, 11, 0.25); color: inherit; }
|
|
344
|
-
.agent-markdown hr { border: none; border-top: 1px solid hsl(var(--border, 0 0% 20%)); margin: 0.75em 0 1em; }
|
|
345
|
-
.agent-markdown a { text-decoration: underline; text-underline-offset: 2px; }
|
|
346
|
-
.agent-markdown a.agent-markdown-cta { text-decoration: none; }
|
|
347
|
-
.agent-markdown blockquote { border-left: 2px solid hsl(var(--border, 0 0% 20%)); padding-left: 0.75em; margin: 0.5em 0; opacity: 0.8; }
|
|
348
|
-
.agent-markdown table { border-collapse: collapse; margin: 0.5em 0; font-size: 0.875em; }
|
|
349
|
-
.agent-markdown th, .agent-markdown td { border: 1px solid hsl(var(--border, 0 0% 20%)); padding: 0.35em 0.65em; text-align: left; }
|
|
350
|
-
.agent-markdown th { font-weight: 600; background: hsl(var(--muted, 0 0% 15%)); color: hsl(var(--foreground, 0 0% 90%)); }
|
|
351
|
-
.agent-markdown[data-streaming="true"] > :last-child:not(pre):not(table)::after { content: ""; display: inline-block; width: 0.42em; height: 1em; margin-left: 0.12em; border-radius: 999px; background: currentColor; opacity: 0.35; transform: translateY(0.16em); animation: agent-markdown-stream-caret 1.15s ease-in-out infinite; }
|
|
352
|
-
@keyframes agent-markdown-stream-caret { 0%, 100% { opacity: 0.2; } 50% { opacity: 0.58; } }
|
|
353
|
-
@media (prefers-reduced-motion: reduce) { .agent-markdown[data-streaming="true"] > :last-child:not(pre):not(table)::after { animation: none; opacity: 0.28; } }
|
|
354
|
-
`;
|
|
355
|
-
/**
|
|
356
|
-
* Pending selection context — written to application_state when the user
|
|
357
|
-
* presses Cmd+I with text selected on the page. The agent's next turn picks
|
|
358
|
-
* it up via the `selectionContextPromise` in production-agent. The pill
|
|
359
|
-
* below tells the user the context is attached and lets them clear it.
|
|
360
|
-
*/
|
|
361
57
|
const PENDING_SELECTION_KEY = "pending-selection-context";
|
|
362
58
|
const ACTIVE_RUN_CLEAR_TIMEOUT_MS = 5_000;
|
|
363
59
|
const ACTIVE_RUN_POLL_INTERVAL_MS = 150;
|
|
@@ -367,67 +63,6 @@ function activeRunLooksStale(runInfo) {
|
|
|
367
63
|
heartbeatAt != null &&
|
|
368
64
|
Date.now() - heartbeatAt > 5000);
|
|
369
65
|
}
|
|
370
|
-
function repoHasAssistantMessage(repo) {
|
|
371
|
-
return repo?.messages?.some((m) => (m.message?.role ?? m.role) === "assistant");
|
|
372
|
-
}
|
|
373
|
-
function getRepoMessages(repo) {
|
|
374
|
-
return Array.isArray(repo?.messages) ? repo.messages : [];
|
|
375
|
-
}
|
|
376
|
-
function getRepoMessage(entry) {
|
|
377
|
-
return entry?.message ?? entry;
|
|
378
|
-
}
|
|
379
|
-
function isAssistantMessageTerminal(message) {
|
|
380
|
-
const statusType = message?.status && typeof message.status === "object"
|
|
381
|
-
? message.status.type
|
|
382
|
-
: undefined;
|
|
383
|
-
return statusType === "complete" || statusType === "incomplete";
|
|
384
|
-
}
|
|
385
|
-
function repoTextLength(repo) {
|
|
386
|
-
let length = 0;
|
|
387
|
-
for (const entry of getRepoMessages(repo)) {
|
|
388
|
-
const message = getRepoMessage(entry);
|
|
389
|
-
const content = message?.content;
|
|
390
|
-
if (typeof content === "string") {
|
|
391
|
-
length += content.length;
|
|
392
|
-
}
|
|
393
|
-
else if (Array.isArray(content)) {
|
|
394
|
-
for (const part of content) {
|
|
395
|
-
if (part?.type === "text" && typeof part.text === "string") {
|
|
396
|
-
length += part.text.length;
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
return length;
|
|
402
|
-
}
|
|
403
|
-
function repoTerminalAssistantCount(repo) {
|
|
404
|
-
return getRepoMessages(repo).filter((entry) => {
|
|
405
|
-
const message = getRepoMessage(entry);
|
|
406
|
-
return message?.role === "assistant" && isAssistantMessageTerminal(message);
|
|
407
|
-
}).length;
|
|
408
|
-
}
|
|
409
|
-
function shouldImportServerThreadData(currentRepo, incomingRepo) {
|
|
410
|
-
const incomingCount = getRepoMessages(incomingRepo).length;
|
|
411
|
-
if (incomingCount === 0)
|
|
412
|
-
return false;
|
|
413
|
-
const currentCount = getRepoMessages(currentRepo).length;
|
|
414
|
-
if (currentCount === 0)
|
|
415
|
-
return true;
|
|
416
|
-
if (incomingCount < currentCount)
|
|
417
|
-
return false;
|
|
418
|
-
if (incomingCount === currentCount) {
|
|
419
|
-
const currentTerminalAssistants = repoTerminalAssistantCount(currentRepo);
|
|
420
|
-
const incomingTerminalAssistants = repoTerminalAssistantCount(incomingRepo);
|
|
421
|
-
if (incomingTerminalAssistants < currentTerminalAssistants) {
|
|
422
|
-
return false;
|
|
423
|
-
}
|
|
424
|
-
if (incomingTerminalAssistants <= currentTerminalAssistants &&
|
|
425
|
-
repoTextLength(incomingRepo) < repoTextLength(currentRepo)) {
|
|
426
|
-
return false;
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
return true;
|
|
430
|
-
}
|
|
431
66
|
function cloneContentParts(content) {
|
|
432
67
|
return content.map((part) => part.type === "text"
|
|
433
68
|
? { ...part }
|
|
@@ -470,452 +105,15 @@ async function waitForThreadRunToClear(apiUrl, threadId) {
|
|
|
470
105
|
await new Promise((resolve) => window.setTimeout(resolve, ACTIVE_RUN_POLL_INTERVAL_MS));
|
|
471
106
|
}
|
|
472
107
|
}
|
|
473
|
-
function coerceMessageDate(value) {
|
|
474
|
-
if (value instanceof Date) {
|
|
475
|
-
return Number.isNaN(value.getTime()) ? null : value;
|
|
476
|
-
}
|
|
477
|
-
if (typeof value === "string" || typeof value === "number") {
|
|
478
|
-
const date = new Date(value);
|
|
479
|
-
return Number.isNaN(date.getTime()) ? null : date;
|
|
480
|
-
}
|
|
481
|
-
return null;
|
|
482
|
-
}
|
|
483
|
-
function isSameCalendarDay(a, b) {
|
|
484
|
-
return (a.getFullYear() === b.getFullYear() &&
|
|
485
|
-
a.getMonth() === b.getMonth() &&
|
|
486
|
-
a.getDate() === b.getDate());
|
|
487
|
-
}
|
|
488
|
-
function formatMessageTimestamp(value) {
|
|
489
|
-
const date = coerceMessageDate(value);
|
|
490
|
-
if (!date)
|
|
491
|
-
return null;
|
|
492
|
-
const now = new Date();
|
|
493
|
-
const yesterday = new Date(now);
|
|
494
|
-
yesterday.setDate(now.getDate() - 1);
|
|
495
|
-
const time = new Intl.DateTimeFormat(undefined, {
|
|
496
|
-
hour: "numeric",
|
|
497
|
-
minute: "2-digit",
|
|
498
|
-
}).format(date);
|
|
499
|
-
let short;
|
|
500
|
-
if (isSameCalendarDay(date, now)) {
|
|
501
|
-
short = time;
|
|
502
|
-
}
|
|
503
|
-
else if (isSameCalendarDay(date, yesterday)) {
|
|
504
|
-
short = `Yesterday ${time}`;
|
|
505
|
-
}
|
|
506
|
-
else if (date.getFullYear() === now.getFullYear()) {
|
|
507
|
-
short = `${new Intl.DateTimeFormat(undefined, {
|
|
508
|
-
month: "short",
|
|
509
|
-
day: "numeric",
|
|
510
|
-
}).format(date)}, ${time}`;
|
|
511
|
-
}
|
|
512
|
-
else {
|
|
513
|
-
short = `${new Intl.DateTimeFormat(undefined, {
|
|
514
|
-
month: "short",
|
|
515
|
-
day: "numeric",
|
|
516
|
-
year: "numeric",
|
|
517
|
-
}).format(date)}, ${time}`;
|
|
518
|
-
}
|
|
519
|
-
return {
|
|
520
|
-
short,
|
|
521
|
-
full: new Intl.DateTimeFormat(undefined, {
|
|
522
|
-
weekday: "short",
|
|
523
|
-
month: "short",
|
|
524
|
-
day: "numeric",
|
|
525
|
-
year: "numeric",
|
|
526
|
-
hour: "numeric",
|
|
527
|
-
minute: "2-digit",
|
|
528
|
-
}).format(date),
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
function MessageTimestamp({ timestamp, className, }) {
|
|
532
|
-
return (_jsx("span", { className: cn("text-[11px] leading-none text-muted-foreground", className), title: timestamp.full, children: timestamp.short }));
|
|
533
|
-
}
|
|
534
|
-
function SelectionAttachedPill() {
|
|
535
|
-
const [length, setLength] = useState(null);
|
|
536
|
-
useEffect(() => {
|
|
537
|
-
let cancelled = false;
|
|
538
|
-
fetch(agentNativePath(`/_agent-native/application-state/${PENDING_SELECTION_KEY}`))
|
|
539
|
-
.then((r) => (r.ok && r.status !== 204 ? r.json() : null))
|
|
540
|
-
.then((data) => {
|
|
541
|
-
if (cancelled)
|
|
542
|
-
return;
|
|
543
|
-
const text = data?.value?.text ??
|
|
544
|
-
data?.text;
|
|
545
|
-
if (text)
|
|
546
|
-
setLength(text.length);
|
|
547
|
-
})
|
|
548
|
-
.catch(() => { });
|
|
549
|
-
return () => {
|
|
550
|
-
cancelled = true;
|
|
551
|
-
};
|
|
552
|
-
}, []);
|
|
553
|
-
useEffect(() => {
|
|
554
|
-
function onAttached(e) {
|
|
555
|
-
const detail = e.detail;
|
|
556
|
-
if (typeof detail?.length === "number")
|
|
557
|
-
setLength(detail.length);
|
|
558
|
-
}
|
|
559
|
-
function onCleared() {
|
|
560
|
-
setLength(null);
|
|
561
|
-
}
|
|
562
|
-
window.addEventListener("agent-panel:selection-attached", onAttached);
|
|
563
|
-
window.addEventListener("agent-panel:selection-cleared", onCleared);
|
|
564
|
-
return () => {
|
|
565
|
-
window.removeEventListener("agent-panel:selection-attached", onAttached);
|
|
566
|
-
window.removeEventListener("agent-panel:selection-cleared", onCleared);
|
|
567
|
-
};
|
|
568
|
-
}, []);
|
|
569
|
-
if (length === null || length === 0)
|
|
570
|
-
return null;
|
|
571
|
-
return (_jsx("div", { className: "shrink-0 px-3 pt-1.5 -mb-1", children: _jsxs("div", { className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-muted/50 px-2 py-0.5 text-[11px] text-muted-foreground", children: [_jsx(IconQuote, { size: 11 }), _jsxs("span", { children: [length.toLocaleString(), " chars of selection attached"] }), _jsx("button", { type: "button", "aria-label": "Clear selection context", onClick: () => {
|
|
572
|
-
setLength(null);
|
|
573
|
-
clearPendingSelection();
|
|
574
|
-
}, className: "flex h-4 w-4 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/60", children: _jsx(IconX, { size: 11 }) })] }) }));
|
|
575
|
-
}
|
|
576
|
-
let stylesInjected = false;
|
|
577
|
-
function injectMarkdownStyles() {
|
|
578
|
-
if (stylesInjected || typeof document === "undefined")
|
|
579
|
-
return;
|
|
580
|
-
stylesInjected = true;
|
|
581
|
-
const style = document.createElement("style");
|
|
582
|
-
style.textContent = markdownStyles;
|
|
583
|
-
document.head.appendChild(style);
|
|
584
|
-
}
|
|
585
|
-
function extractCodeText(child) {
|
|
586
|
-
if (typeof child === "string")
|
|
587
|
-
return child;
|
|
588
|
-
if (Array.isArray(child))
|
|
589
|
-
return child.map(extractCodeText).join("");
|
|
590
|
-
if (React.isValidElement(child)) {
|
|
591
|
-
const props = child.props;
|
|
592
|
-
return extractCodeText(props.children);
|
|
593
|
-
}
|
|
594
|
-
return "";
|
|
595
|
-
}
|
|
596
|
-
let highlighterLoader = null;
|
|
597
|
-
function loadHighlighter() {
|
|
598
|
-
if (!highlighterLoader) {
|
|
599
|
-
highlighterLoader = (async () => {
|
|
600
|
-
const [{ createHighlighterCore }, { createOnigurumaEngine }] = await Promise.all([
|
|
601
|
-
import("shiki/core"),
|
|
602
|
-
import("shiki/engine/oniguruma"),
|
|
603
|
-
]);
|
|
604
|
-
return createHighlighterCore({
|
|
605
|
-
themes: [
|
|
606
|
-
import("shiki/themes/github-light-default.mjs"),
|
|
607
|
-
import("shiki/themes/github-dark-default.mjs"),
|
|
608
|
-
],
|
|
609
|
-
langs: [
|
|
610
|
-
import("shiki/langs/javascript.mjs"),
|
|
611
|
-
import("shiki/langs/typescript.mjs"),
|
|
612
|
-
import("shiki/langs/jsx.mjs"),
|
|
613
|
-
import("shiki/langs/tsx.mjs"),
|
|
614
|
-
import("shiki/langs/json.mjs"),
|
|
615
|
-
import("shiki/langs/css.mjs"),
|
|
616
|
-
import("shiki/langs/html.mjs"),
|
|
617
|
-
import("shiki/langs/markdown.mjs"),
|
|
618
|
-
import("shiki/langs/bash.mjs"),
|
|
619
|
-
import("shiki/langs/shellscript.mjs"),
|
|
620
|
-
import("shiki/langs/python.mjs"),
|
|
621
|
-
import("shiki/langs/yaml.mjs"),
|
|
622
|
-
import("shiki/langs/sql.mjs"),
|
|
623
|
-
],
|
|
624
|
-
engine: createOnigurumaEngine(import("shiki/wasm")),
|
|
625
|
-
});
|
|
626
|
-
})().catch((error) => {
|
|
627
|
-
// Reset on failure so a future code block can retry instead of
|
|
628
|
-
// silently failing forever on a stale chunk / network blip.
|
|
629
|
-
highlighterLoader = null;
|
|
630
|
-
throw error;
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
return highlighterLoader;
|
|
634
|
-
}
|
|
635
|
-
import { PROVIDER_ENV_VARS } from "../agent/engine/provider-env-vars.js";
|
|
636
108
|
const PROVIDER_ENV_VAR_SET = new Set(PROVIDER_ENV_VARS);
|
|
637
|
-
// Map a few common aliases to languages we bundled above.
|
|
638
|
-
const LANG_ALIASES = {
|
|
639
|
-
js: "javascript",
|
|
640
|
-
ts: "typescript",
|
|
641
|
-
sh: "bash",
|
|
642
|
-
shell: "bash",
|
|
643
|
-
zsh: "bash",
|
|
644
|
-
py: "python",
|
|
645
|
-
yml: "yaml",
|
|
646
|
-
md: "markdown",
|
|
647
|
-
bq: "sql",
|
|
648
|
-
bigquery: "sql",
|
|
649
|
-
};
|
|
650
|
-
function HighlightedCodeBlock({ code, lang }) {
|
|
651
|
-
const [html, setHtml] = useState(null);
|
|
652
|
-
useEffect(() => {
|
|
653
|
-
let cancelled = false;
|
|
654
|
-
loadHighlighter()
|
|
655
|
-
.then((highlighter) => {
|
|
656
|
-
const requested = (lang || "text").toLowerCase();
|
|
657
|
-
const resolved = LANG_ALIASES[requested] ?? requested;
|
|
658
|
-
const loaded = highlighter.getLoadedLanguages();
|
|
659
|
-
const finalLang = loaded.includes(resolved) ? resolved : "text";
|
|
660
|
-
return highlighter.codeToHtml(code, {
|
|
661
|
-
lang: finalLang,
|
|
662
|
-
themes: {
|
|
663
|
-
light: "github-light-default",
|
|
664
|
-
dark: "github-dark-default",
|
|
665
|
-
},
|
|
666
|
-
defaultColor: false,
|
|
667
|
-
});
|
|
668
|
-
})
|
|
669
|
-
.then((out) => {
|
|
670
|
-
if (!cancelled)
|
|
671
|
-
setHtml(out);
|
|
672
|
-
})
|
|
673
|
-
.catch(() => {
|
|
674
|
-
// Unknown language or other shiki failure — fall back to plain pre.
|
|
675
|
-
if (!cancelled)
|
|
676
|
-
setHtml(null);
|
|
677
|
-
});
|
|
678
|
-
return () => {
|
|
679
|
-
cancelled = true;
|
|
680
|
-
};
|
|
681
|
-
}, [code, lang]);
|
|
682
|
-
if (html) {
|
|
683
|
-
return (_jsx("div", { className: "agent-markdown-shiki", dangerouslySetInnerHTML: { __html: html } }));
|
|
684
|
-
}
|
|
685
|
-
return (_jsx("pre", { children: _jsx("code", { className: lang ? `language-${lang}` : undefined, children: code }) }));
|
|
686
|
-
}
|
|
687
|
-
const CTA_BUTTON_CLASSES = "agent-markdown-cta mt-1 inline-flex items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs font-medium text-background no-underline shadow-sm transition-colors hover:bg-foreground/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background cursor-pointer";
|
|
688
|
-
const markdownComponents = {
|
|
689
|
-
a(props) {
|
|
690
|
-
const { href, children, className, rel: _rel, target: _target, ...rest } = props;
|
|
691
|
-
if (href === NEW_CHAT_ACTION_HREF) {
|
|
692
|
-
// In-app action: dispatch a CustomEvent that MultiTabAssistantChat
|
|
693
|
-
// listens for and opens a new chat tab. Not an external navigation.
|
|
694
|
-
return (_jsxs("button", { type: "button", onClick: (e) => {
|
|
695
|
-
e.preventDefault();
|
|
696
|
-
window.dispatchEvent(new CustomEvent("agent-chat:new-chat"));
|
|
697
|
-
}, className: cn(CTA_BUTTON_CLASSES, className), children: [_jsx(IconPlus, { size: 13, strokeWidth: 2, "aria-hidden": "true" }), _jsx("span", { children: children })] }));
|
|
698
|
-
}
|
|
699
|
-
const isBuilderCta = isBuilderErrorCtaHref(href);
|
|
700
|
-
if (!isBuilderCta) {
|
|
701
|
-
return (_jsx("a", { href: href, className: className, ...rest, children: children }));
|
|
702
|
-
}
|
|
703
|
-
return (_jsxs("a", { href: href, target: "_blank", rel: "noreferrer", className: cn(CTA_BUTTON_CLASSES, className), ...rest, children: [_jsx("span", { children: children }), _jsx(IconExternalLink, { size: 13, strokeWidth: 2, "aria-hidden": "true" })] }));
|
|
704
|
-
},
|
|
705
|
-
pre(props) {
|
|
706
|
-
const { children, ...rest } = props;
|
|
707
|
-
if (React.isValidElement(children)) {
|
|
708
|
-
const childProps = children.props;
|
|
709
|
-
const className = childProps.className || "";
|
|
710
|
-
if (/\blanguage-embed\b/.test(className)) {
|
|
711
|
-
const body = extractCodeText(childProps.children);
|
|
712
|
-
const parsed = parseEmbedBody(body);
|
|
713
|
-
return (_jsx(IframeEmbed, { ...parsed }));
|
|
714
|
-
}
|
|
715
|
-
const langMatch = className.match(/\blanguage-([\w+-]+)\b/);
|
|
716
|
-
if (langMatch) {
|
|
717
|
-
const code = extractCodeText(childProps.children).replace(/\n$/, "");
|
|
718
|
-
return _jsx(HighlightedCodeBlock, { code: code, lang: langMatch[1] });
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
return _jsx("pre", { ...rest, children: children });
|
|
722
|
-
},
|
|
723
|
-
};
|
|
724
|
-
function isBuilderErrorCtaHref(href) {
|
|
725
|
-
if (!href)
|
|
726
|
-
return false;
|
|
727
|
-
try {
|
|
728
|
-
const url = new URL(href);
|
|
729
|
-
if (url.protocol !== "https:" || url.hostname !== "builder.io") {
|
|
730
|
-
return false;
|
|
731
|
-
}
|
|
732
|
-
return (url.href === BUILDER_SPACE_SETTINGS_URL ||
|
|
733
|
-
url.pathname === "/account/billing" ||
|
|
734
|
-
/^\/app\/organizations\/[^/]+\/billing$/.test(url.pathname));
|
|
735
|
-
}
|
|
736
|
-
catch {
|
|
737
|
-
return false;
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
// react-markdown's defaultUrlTransform strips href values whose protocol
|
|
741
|
-
// isn't on its safe list (https, mailto, etc.). Our in-app pseudo-href
|
|
742
|
-
// `agent-native:new-chat` would be blanked out by that, so let it through
|
|
743
|
-
// while delegating every other URL to the default transform for sanitization.
|
|
744
|
-
function markdownUrlTransform(value) {
|
|
745
|
-
if (value === NEW_CHAT_ACTION_HREF)
|
|
746
|
-
return value;
|
|
747
|
-
return defaultUrlTransform(value);
|
|
748
|
-
}
|
|
749
|
-
const TextStreamingContext = React.createContext(false);
|
|
750
|
-
function usePrefersReducedMotion() {
|
|
751
|
-
const [prefersReducedMotion, setPrefersReducedMotion] = useState(() => typeof window !== "undefined" && window.matchMedia
|
|
752
|
-
? window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
|
753
|
-
: false);
|
|
754
|
-
useEffect(() => {
|
|
755
|
-
if (typeof window === "undefined" || !window.matchMedia)
|
|
756
|
-
return undefined;
|
|
757
|
-
const media = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
758
|
-
const handleChange = () => setPrefersReducedMotion(media.matches);
|
|
759
|
-
handleChange();
|
|
760
|
-
if (typeof media.addEventListener === "function") {
|
|
761
|
-
media.addEventListener("change", handleChange);
|
|
762
|
-
return () => media.removeEventListener("change", handleChange);
|
|
763
|
-
}
|
|
764
|
-
media.addListener(handleChange);
|
|
765
|
-
return () => media.removeListener(handleChange);
|
|
766
|
-
}, []);
|
|
767
|
-
return prefersReducedMotion;
|
|
768
|
-
}
|
|
769
|
-
function sliceGraphemes(targetText, graphemes, count) {
|
|
770
|
-
if (count >= graphemes.length)
|
|
771
|
-
return targetText;
|
|
772
|
-
if (count <= 0)
|
|
773
|
-
return "";
|
|
774
|
-
return graphemes.slice(0, count).join("");
|
|
775
|
-
}
|
|
776
|
-
function useSmoothStreamingText(targetText, streaming, resetKey) {
|
|
777
|
-
const prefersReducedMotion = usePrefersReducedMotion();
|
|
778
|
-
const [visibleText, setVisibleText] = useState(() => {
|
|
779
|
-
if (!streaming || prefersReducedMotion)
|
|
780
|
-
return targetText;
|
|
781
|
-
const graphemes = splitStreamingTextGraphemes(targetText);
|
|
782
|
-
return sliceGraphemes(targetText, graphemes, initialSmoothStreamingGraphemeCount(graphemes));
|
|
783
|
-
});
|
|
784
|
-
const visibleTextRef = useRef(visibleText);
|
|
785
|
-
const visibleCountRef = useRef(splitStreamingTextGraphemes(visibleText).length);
|
|
786
|
-
const targetTextRef = useRef(targetText);
|
|
787
|
-
const targetGraphemesRef = useRef(splitStreamingTextGraphemes(targetText));
|
|
788
|
-
const frameRef = useRef(null);
|
|
789
|
-
const lastCommitAtRef = useRef(0);
|
|
790
|
-
const pauseUntilRef = useRef(0);
|
|
791
|
-
const resetKeyRef = useRef(resetKey);
|
|
792
|
-
const stepRef = useRef(() => { });
|
|
793
|
-
const commitVisibleCount = useCallback((nextCount) => {
|
|
794
|
-
const graphemes = targetGraphemesRef.current;
|
|
795
|
-
const boundedCount = Math.max(0, Math.min(nextCount, graphemes.length));
|
|
796
|
-
const nextText = sliceGraphemes(targetTextRef.current, graphemes, boundedCount);
|
|
797
|
-
visibleCountRef.current = boundedCount;
|
|
798
|
-
if (visibleTextRef.current !== nextText) {
|
|
799
|
-
visibleTextRef.current = nextText;
|
|
800
|
-
setVisibleText(nextText);
|
|
801
|
-
}
|
|
802
|
-
}, []);
|
|
803
|
-
const cancelFrame = useCallback(() => {
|
|
804
|
-
if (frameRef.current != null &&
|
|
805
|
-
typeof window !== "undefined" &&
|
|
806
|
-
typeof window.cancelAnimationFrame === "function") {
|
|
807
|
-
window.cancelAnimationFrame(frameRef.current);
|
|
808
|
-
}
|
|
809
|
-
frameRef.current = null;
|
|
810
|
-
pauseUntilRef.current = 0;
|
|
811
|
-
}, []);
|
|
812
|
-
const scheduleFrame = useCallback(() => {
|
|
813
|
-
if (frameRef.current != null)
|
|
814
|
-
return;
|
|
815
|
-
if (typeof window === "undefined" ||
|
|
816
|
-
typeof window.requestAnimationFrame !== "function") {
|
|
817
|
-
commitVisibleCount(targetGraphemesRef.current.length);
|
|
818
|
-
return;
|
|
819
|
-
}
|
|
820
|
-
frameRef.current = window.requestAnimationFrame((time) => {
|
|
821
|
-
frameRef.current = null;
|
|
822
|
-
stepRef.current(time);
|
|
823
|
-
});
|
|
824
|
-
}, [commitVisibleCount]);
|
|
825
|
-
stepRef.current = (time) => {
|
|
826
|
-
const targetGraphemes = targetGraphemesRef.current;
|
|
827
|
-
const backlog = targetGraphemes.length - visibleCountRef.current;
|
|
828
|
-
if (backlog <= 0) {
|
|
829
|
-
pauseUntilRef.current = 0;
|
|
830
|
-
return;
|
|
831
|
-
}
|
|
832
|
-
if (pauseUntilRef.current > time) {
|
|
833
|
-
scheduleFrame();
|
|
834
|
-
return;
|
|
835
|
-
}
|
|
836
|
-
const lastCommitAt = lastCommitAtRef.current || time - SMOOTH_STREAMING_COMMIT_INTERVAL_MS;
|
|
837
|
-
if (time - lastCommitAt < SMOOTH_STREAMING_COMMIT_INTERVAL_MS &&
|
|
838
|
-
backlog > 1) {
|
|
839
|
-
scheduleFrame();
|
|
840
|
-
return;
|
|
841
|
-
}
|
|
842
|
-
const revealCount = smoothStreamingRevealCount({
|
|
843
|
-
backlog,
|
|
844
|
-
elapsedMs: Math.min(120, Math.max(8, time - lastCommitAt)),
|
|
845
|
-
});
|
|
846
|
-
if (revealCount > 0) {
|
|
847
|
-
const nextCount = visibleCountRef.current + revealCount;
|
|
848
|
-
commitVisibleCount(nextCount);
|
|
849
|
-
lastCommitAtRef.current = time;
|
|
850
|
-
const nextBacklog = targetGraphemes.length - visibleCountRef.current;
|
|
851
|
-
const pauseMs = smoothStreamingPunctuationDelayMs(targetGraphemes[visibleCountRef.current - 1], nextBacklog);
|
|
852
|
-
pauseUntilRef.current = pauseMs > 0 ? time + pauseMs : 0;
|
|
853
|
-
}
|
|
854
|
-
if (visibleCountRef.current < targetGraphemes.length) {
|
|
855
|
-
scheduleFrame();
|
|
856
|
-
}
|
|
857
|
-
else {
|
|
858
|
-
pauseUntilRef.current = 0;
|
|
859
|
-
}
|
|
860
|
-
};
|
|
861
|
-
useEffect(() => {
|
|
862
|
-
const targetGraphemes = splitStreamingTextGraphemes(targetText);
|
|
863
|
-
targetTextRef.current = targetText;
|
|
864
|
-
targetGraphemesRef.current = targetGraphemes;
|
|
865
|
-
const keyChanged = resetKeyRef.current !== resetKey;
|
|
866
|
-
resetKeyRef.current = resetKey;
|
|
867
|
-
if (!streaming || prefersReducedMotion) {
|
|
868
|
-
cancelFrame();
|
|
869
|
-
commitVisibleCount(targetGraphemes.length);
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
|
-
const visibleNoLongerMatchesTarget = visibleTextRef.current.length > 0 &&
|
|
873
|
-
!targetText.startsWith(visibleTextRef.current);
|
|
874
|
-
if (visibleNoLongerMatchesTarget ||
|
|
875
|
-
visibleCountRef.current > targetGraphemes.length ||
|
|
876
|
-
(keyChanged && visibleTextRef.current.length === 0)) {
|
|
877
|
-
commitVisibleCount(initialSmoothStreamingGraphemeCount(targetGraphemes));
|
|
878
|
-
lastCommitAtRef.current = 0;
|
|
879
|
-
pauseUntilRef.current = 0;
|
|
880
|
-
}
|
|
881
|
-
if (visibleCountRef.current < targetGraphemes.length) {
|
|
882
|
-
scheduleFrame();
|
|
883
|
-
}
|
|
884
|
-
}, [
|
|
885
|
-
targetText,
|
|
886
|
-
streaming,
|
|
887
|
-
prefersReducedMotion,
|
|
888
|
-
resetKey,
|
|
889
|
-
cancelFrame,
|
|
890
|
-
commitVisibleCount,
|
|
891
|
-
scheduleFrame,
|
|
892
|
-
]);
|
|
893
|
-
useEffect(() => cancelFrame, [cancelFrame]);
|
|
894
|
-
return visibleText;
|
|
895
|
-
}
|
|
896
|
-
function SmoothMarkdownText({ text, streaming, resetKey, statusType = "complete", }) {
|
|
897
|
-
useEffect(() => {
|
|
898
|
-
injectMarkdownStyles();
|
|
899
|
-
}, []);
|
|
900
|
-
const visibleText = useSmoothStreamingText(text, streaming, resetKey);
|
|
901
|
-
const isVisuallyStreaming = streaming && visibleText !== text;
|
|
902
|
-
return (_jsx("div", { className: "agent-markdown break-words", "data-status": statusType, "data-streaming": isVisuallyStreaming ? "true" : undefined, children: _jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], components: markdownComponents, urlTransform: markdownUrlTransform, children: visibleText }) }));
|
|
903
|
-
}
|
|
904
|
-
function MarkdownText() {
|
|
905
|
-
const textPart = useMessagePartText();
|
|
906
|
-
const messageRuntime = useMessageRuntime();
|
|
907
|
-
const message = messageRuntime.getState();
|
|
908
|
-
const thread = useThread();
|
|
909
|
-
const textStreaming = React.useContext(TextStreamingContext);
|
|
910
|
-
const lastMessage = thread.messages[thread.messages.length - 1];
|
|
911
|
-
const isLastAssistantMessage = message.role === "assistant" && lastMessage?.id === message.id;
|
|
912
|
-
const statusType = textPart.status?.type ?? message.status?.type ?? "complete";
|
|
913
|
-
return (_jsx(SmoothMarkdownText, { text: textPart.text, streaming: textStreaming && isLastAssistantMessage, resetKey: `${message.id}:${statusType}`, statusType: statusType }));
|
|
914
|
-
}
|
|
915
109
|
// ─── Composer Attachment Preview ─────────────────────────────────────────────
|
|
916
110
|
function getImageAttachmentSrc(attachment) {
|
|
917
111
|
if (attachment.type !== "image")
|
|
918
112
|
return null;
|
|
113
|
+
// Prefer the hosted URL when the server already uploaded this attachment.
|
|
114
|
+
const uploadUrl = attachment.metadata?.uploadUrl;
|
|
115
|
+
if (uploadUrl)
|
|
116
|
+
return uploadUrl;
|
|
919
117
|
if ("file" in attachment && attachment.file) {
|
|
920
118
|
return URL.createObjectURL(attachment.file);
|
|
921
119
|
}
|
|
@@ -953,716 +151,6 @@ function ComposerAttachmentPreviewStrip() {
|
|
|
953
151
|
return null;
|
|
954
152
|
return (_jsx("div", { className: "flex flex-wrap gap-2 px-2 pt-2", children: attachments.map((attachment) => (_jsx(ComposerAttachmentPreviewCard, { attachment: attachment, onRemove: handleRemove }, attachment.id))) }));
|
|
955
153
|
}
|
|
956
|
-
// Provides the parent's combined running state to tool-call renderers so they
|
|
957
|
-
// can stop spinning when the user clicks stop. `thread.isRunning` alone misses
|
|
958
|
-
// the force-stopped case; `part.result === undefined` alone ignores stop.
|
|
959
|
-
const ChatRunningContext = React.createContext(false);
|
|
960
|
-
function stringifyToolValue(value, pretty = false) {
|
|
961
|
-
if (typeof value === "string")
|
|
962
|
-
return value;
|
|
963
|
-
try {
|
|
964
|
-
return JSON.stringify(value, null, pretty ? 2 : 0);
|
|
965
|
-
}
|
|
966
|
-
catch {
|
|
967
|
-
return String(value ?? "");
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
function looksLikeSql(text) {
|
|
971
|
-
return /^\s*(select|with|insert|update|delete|merge|create|alter|drop|explain|declare|begin)\b/i.test(text);
|
|
972
|
-
}
|
|
973
|
-
function parseJsonText(text) {
|
|
974
|
-
const trimmed = text.trim();
|
|
975
|
-
if (!trimmed || !/^[{[]/.test(trimmed))
|
|
976
|
-
return null;
|
|
977
|
-
try {
|
|
978
|
-
return JSON.parse(trimmed);
|
|
979
|
-
}
|
|
980
|
-
catch {
|
|
981
|
-
return null;
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
function inferToolTextLanguage(text, key, toolName) {
|
|
985
|
-
const keyName = (key ?? "").toLowerCase();
|
|
986
|
-
const tool = (toolName ?? "").toLowerCase();
|
|
987
|
-
if (keyName === "sql" ||
|
|
988
|
-
keyName.endsWith("sql") ||
|
|
989
|
-
keyName === "query" ||
|
|
990
|
-
tool.includes("bigquery") ||
|
|
991
|
-
tool.includes("db-query") ||
|
|
992
|
-
looksLikeSql(text)) {
|
|
993
|
-
return "sql";
|
|
994
|
-
}
|
|
995
|
-
return parseJsonText(text) ? "json" : "text";
|
|
996
|
-
}
|
|
997
|
-
function formatToolTextValue(value, key, toolName) {
|
|
998
|
-
if (typeof value === "string") {
|
|
999
|
-
const parsed = parseJsonText(value);
|
|
1000
|
-
if (parsed) {
|
|
1001
|
-
return { text: JSON.stringify(parsed, null, 2), lang: "json" };
|
|
1002
|
-
}
|
|
1003
|
-
return {
|
|
1004
|
-
text: value,
|
|
1005
|
-
lang: inferToolTextLanguage(value, key, toolName),
|
|
1006
|
-
};
|
|
1007
|
-
}
|
|
1008
|
-
return { text: stringifyToolValue(value, true), lang: "json" };
|
|
1009
|
-
}
|
|
1010
|
-
function toolInputPayload(toolName, args) {
|
|
1011
|
-
const entries = Object.entries(args);
|
|
1012
|
-
if (entries.length === 0)
|
|
1013
|
-
return null;
|
|
1014
|
-
if (entries.length === 1) {
|
|
1015
|
-
const [key, value] = entries[0];
|
|
1016
|
-
const formatted = formatToolTextValue(value, key, toolName);
|
|
1017
|
-
const normalizedKey = key.toLowerCase();
|
|
1018
|
-
const keyLabel = normalizedKey === "sql" || normalizedKey.endsWith("sql") ? "SQL" : key;
|
|
1019
|
-
return {
|
|
1020
|
-
section: "input",
|
|
1021
|
-
title: `Input - ${keyLabel}`,
|
|
1022
|
-
text: formatted.text,
|
|
1023
|
-
copyText: typeof value === "string" ? value : stringifyToolValue(value, true),
|
|
1024
|
-
lang: formatted.lang,
|
|
1025
|
-
};
|
|
1026
|
-
}
|
|
1027
|
-
return {
|
|
1028
|
-
section: "input",
|
|
1029
|
-
title: "Input",
|
|
1030
|
-
text: JSON.stringify(args, null, 2),
|
|
1031
|
-
copyText: JSON.stringify(args, null, 2),
|
|
1032
|
-
lang: "json",
|
|
1033
|
-
};
|
|
1034
|
-
}
|
|
1035
|
-
function toolResultPayload(result) {
|
|
1036
|
-
if (result === undefined)
|
|
1037
|
-
return null;
|
|
1038
|
-
const formatted = formatToolTextValue(result);
|
|
1039
|
-
return {
|
|
1040
|
-
section: "result",
|
|
1041
|
-
title: "Result",
|
|
1042
|
-
text: formatted.text,
|
|
1043
|
-
copyText: result,
|
|
1044
|
-
lang: formatted.lang,
|
|
1045
|
-
};
|
|
1046
|
-
}
|
|
1047
|
-
function escapeRegExp(value) {
|
|
1048
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1049
|
-
}
|
|
1050
|
-
function countTextMatches(text, query) {
|
|
1051
|
-
const needle = query.trim();
|
|
1052
|
-
if (!needle)
|
|
1053
|
-
return 0;
|
|
1054
|
-
return Array.from(text.matchAll(new RegExp(escapeRegExp(needle), "gi")))
|
|
1055
|
-
.length;
|
|
1056
|
-
}
|
|
1057
|
-
function renderHighlightedSearchText(text, query) {
|
|
1058
|
-
const needle = query.trim();
|
|
1059
|
-
if (!needle)
|
|
1060
|
-
return text;
|
|
1061
|
-
const regex = new RegExp(escapeRegExp(needle), "gi");
|
|
1062
|
-
const parts = [];
|
|
1063
|
-
let lastIndex = 0;
|
|
1064
|
-
let match;
|
|
1065
|
-
while ((match = regex.exec(text))) {
|
|
1066
|
-
if (match.index > lastIndex) {
|
|
1067
|
-
parts.push(text.slice(lastIndex, match.index));
|
|
1068
|
-
}
|
|
1069
|
-
parts.push(_jsx("mark", { children: match[0] }, `${match.index}-${match[0]}`));
|
|
1070
|
-
lastIndex = match.index + match[0].length;
|
|
1071
|
-
if (match[0].length === 0)
|
|
1072
|
-
regex.lastIndex += 1;
|
|
1073
|
-
}
|
|
1074
|
-
if (lastIndex < text.length)
|
|
1075
|
-
parts.push(text.slice(lastIndex));
|
|
1076
|
-
return parts;
|
|
1077
|
-
}
|
|
1078
|
-
function ToolDetailViewer({ payload }) {
|
|
1079
|
-
const [expanded, setExpanded] = useState(false);
|
|
1080
|
-
const [searchOpen, setSearchOpen] = useState(false);
|
|
1081
|
-
const [search, setSearch] = useState("");
|
|
1082
|
-
const [copied, setCopied] = useState(false);
|
|
1083
|
-
const copyResetRef = useRef(null);
|
|
1084
|
-
const matchCount = useMemo(() => countTextMatches(payload.text, search), [payload.text, search]);
|
|
1085
|
-
useEffect(() => {
|
|
1086
|
-
return () => {
|
|
1087
|
-
if (copyResetRef.current)
|
|
1088
|
-
clearTimeout(copyResetRef.current);
|
|
1089
|
-
};
|
|
1090
|
-
}, []);
|
|
1091
|
-
const copyValue = useCallback(async () => {
|
|
1092
|
-
try {
|
|
1093
|
-
if (await writeClipboardText(payload.copyText)) {
|
|
1094
|
-
setCopied(true);
|
|
1095
|
-
if (copyResetRef.current)
|
|
1096
|
-
clearTimeout(copyResetRef.current);
|
|
1097
|
-
copyResetRef.current = setTimeout(() => setCopied(false), 1200);
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
catch {
|
|
1101
|
-
// Clipboard failures should not interrupt chat rendering.
|
|
1102
|
-
}
|
|
1103
|
-
}, [payload.copyText]);
|
|
1104
|
-
return (_jsxs("div", { className: "rounded-md border border-border/50 bg-background/60", children: [_jsxs("div", { className: "flex min-h-9 flex-wrap items-center gap-2 border-b border-border/50 px-2.5 py-1.5", children: [_jsx("div", { className: "min-w-0 flex-1", children: _jsxs("div", { className: "flex min-w-0 items-center gap-1.5", children: [_jsx("span", { className: "truncate text-[11px] font-medium text-foreground/85", children: payload.title }), payload.lang !== "text" && (_jsx("span", { className: "shrink-0 rounded border border-border/60 px-1 py-0.5 font-mono text-[9px] uppercase leading-none text-muted-foreground", children: payload.lang }))] }) }), _jsx("button", { type: "button", onClick: () => setSearchOpen((v) => !v), "aria-label": `Search ${payload.title.toLowerCase()}`, "aria-pressed": searchOpen, className: cn("inline-flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground", searchOpen && "bg-accent text-foreground"), children: _jsx(IconSearch, { size: 12 }) }), _jsx("button", { type: "button", onClick: () => setExpanded((v) => !v), "aria-label": expanded ? "Shrink code viewer" : "Expand code viewer", "aria-pressed": expanded, className: "inline-flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground", children: expanded ? (_jsx(IconArrowsMinimize, { size: 12 })) : (_jsx(IconArrowsMaximize, { size: 12 })) }), _jsxs("button", { type: "button", onClick: copyValue, className: "inline-flex h-6 items-center gap-1 rounded-md px-1.5 font-sans text-[11px] text-muted-foreground hover:bg-accent hover:text-foreground", children: [copied ? _jsx(IconCheck, { size: 12 }) : _jsx(IconCopy, { size: 12 }), copied ? "Copied" : "Copy"] })] }), searchOpen && (_jsxs("div", { className: "flex items-center gap-2 border-b border-border/50 px-2.5 py-2", children: [_jsx("input", { value: search, onChange: (e) => setSearch(e.target.value), placeholder: "Find", className: "h-7 min-w-0 flex-1 rounded-md border border-border bg-background px-2 text-xs text-foreground outline-none placeholder:text-muted-foreground focus:ring-1 focus:ring-ring" }), _jsx("span", { className: "shrink-0 text-[11px] text-muted-foreground", children: search.trim() ? matchCount : "" })] })), _jsx("div", { className: cn("agent-tool-code overflow-auto font-mono text-[11px] leading-relaxed text-foreground", expanded ? "max-h-[70vh]" : "max-h-72"), children: search.trim() ? (_jsx("pre", { children: _jsx("code", { children: renderHighlightedSearchText(payload.text, search) }) })) : (_jsx(HighlightedCodeBlock, { code: payload.text, lang: payload.lang })) })] }));
|
|
1105
|
-
}
|
|
1106
|
-
function ToolCallDisplay({ toolName, argsText, args, result, mcpApp, isRunning, }) {
|
|
1107
|
-
const streamRef = useRef(null);
|
|
1108
|
-
const isAgentCall = toolName.startsWith("agent:");
|
|
1109
|
-
const [expanded, setExpanded] = useState(isAgentCall);
|
|
1110
|
-
const agentName = isAgentCall ? toolName.slice(6) : null;
|
|
1111
|
-
const isAgentError = isAgentCall && result === "Error calling agent";
|
|
1112
|
-
const agentStreamText = isAgentCall ? (argsText ?? "") : "";
|
|
1113
|
-
const hasStreamText = agentStreamText.length > 0;
|
|
1114
|
-
const hasArgs = !isAgentCall && Object.keys(args).length > 0;
|
|
1115
|
-
// NOTE: All hooks must be above any conditional returns
|
|
1116
|
-
useEffect(() => {
|
|
1117
|
-
if (isAgentCall && isRunning && streamRef.current) {
|
|
1118
|
-
streamRef.current.scrollTop = streamRef.current.scrollHeight;
|
|
1119
|
-
}
|
|
1120
|
-
}, [agentStreamText, isAgentCall, isRunning]);
|
|
1121
|
-
// Render connect-builder as ConnectBuilderCard once the result is available
|
|
1122
|
-
if (toolName === "connect-builder" && result) {
|
|
1123
|
-
try {
|
|
1124
|
-
const parsed = JSON.parse(result);
|
|
1125
|
-
if (parsed?.kind === "connect-builder-card") {
|
|
1126
|
-
return (_jsx(ConnectBuilderCard, { configured: !!parsed.configured, builderEnabled: parsed.builderEnabled !== false,
|
|
1127
|
-
// Ignore saved cliAuthUrl values from older tool results. They
|
|
1128
|
-
// contain signed callback state and can expire while a chat sits
|
|
1129
|
-
// open; the card's hook fetches a fresh signed URL on mount/click.
|
|
1130
|
-
connectUrl: parsed.connectUrl || "", orgName: parsed.orgName ?? null, prompt: typeof parsed.prompt === "string" ? parsed.prompt : "" }));
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
catch {
|
|
1134
|
-
// fall through to default pill rendering
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
// Render agent-teams spawn as AgentTaskCard once the result is available
|
|
1138
|
-
if (toolName === "agent-teams" &&
|
|
1139
|
-
args?.action === "spawn" &&
|
|
1140
|
-
result) {
|
|
1141
|
-
try {
|
|
1142
|
-
const parsed = JSON.parse(result);
|
|
1143
|
-
if (parsed.taskId && parsed.threadId) {
|
|
1144
|
-
return (_jsx(AgentTaskCard, { taskId: parsed.taskId, threadId: parsed.threadId, description: parsed.description ||
|
|
1145
|
-
args?.task ||
|
|
1146
|
-
"Sub-agent task", onOpen: (tid) => {
|
|
1147
|
-
window.dispatchEvent(new CustomEvent("agent-task-open", {
|
|
1148
|
-
detail: {
|
|
1149
|
-
threadId: tid,
|
|
1150
|
-
description: parsed.description ||
|
|
1151
|
-
args?.task ||
|
|
1152
|
-
"",
|
|
1153
|
-
name: parsed.name || "",
|
|
1154
|
-
},
|
|
1155
|
-
}));
|
|
1156
|
-
} }));
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
catch {
|
|
1160
|
-
// Fall through to default pill rendering
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
const inputPayload = hasArgs ? toolInputPayload(toolName, args) : null;
|
|
1164
|
-
const resultPayload = toolResultPayload(result);
|
|
1165
|
-
const displayName = isAgentCall
|
|
1166
|
-
? isRunning
|
|
1167
|
-
? `Asking ${agentName}...`
|
|
1168
|
-
: isAgentError
|
|
1169
|
-
? `Error asking ${agentName}`
|
|
1170
|
-
: `Asked ${agentName}`
|
|
1171
|
-
: humanizeToolName(toolName);
|
|
1172
|
-
const canExpand = isAgentCall
|
|
1173
|
-
? hasStreamText
|
|
1174
|
-
: hasArgs || result !== undefined;
|
|
1175
|
-
const isExpanded = isAgentCall ? hasStreamText && expanded : expanded;
|
|
1176
|
-
return (_jsxs("div", { className: "my-1 overflow-hidden", children: [mcpApp && _jsx(McpAppRenderer, { app: mcpApp, className: "mb-1.5" }), _jsxs("button", { onClick: () => canExpand && setExpanded(!isExpanded), "aria-expanded": canExpand ? isExpanded : undefined, className: cn("flex items-center gap-2 rounded-md px-2.5 py-1.5 text-xs font-mono w-full text-left overflow-hidden", isRunning
|
|
1177
|
-
? "bg-muted text-muted-foreground"
|
|
1178
|
-
: "bg-muted text-muted-foreground hover:bg-accent"), children: [_jsx("span", { className: "shrink-0", children: isRunning ? (_jsx(IconLoader2, { className: "h-3 w-3 animate-spin" })) : isAgentError ? (_jsx(IconCircleX, { className: "h-3 w-3 text-destructive" })) : result !== undefined ? (_jsx(IconCheck, { className: "h-3 w-3 text-emerald-500" })) : (_jsx(IconSquareFilled, { className: "h-3 w-3 text-muted-foreground" })) }), _jsx("span", { className: "truncate min-w-0", children: _jsx("span", { className: "font-medium", children: displayName }) }), canExpand && (_jsx(IconChevronDown, { className: cn("ml-auto h-3 w-3 shrink-0 opacity-40", isExpanded && "rotate-180") }))] }), isExpanded && isAgentCall && hasStreamText && (_jsx("div", { ref: streamRef, className: "mt-1 rounded-md bg-muted/50 px-3 py-2 text-xs text-muted-foreground break-words max-h-48 overflow-y-auto agent-markdown prose prose-sm prose-invert max-w-none", children: _jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], components: markdownComponents, urlTransform: markdownUrlTransform, children: agentStreamText }) })), isExpanded && !isAgentCall && (hasArgs || result !== undefined) && (_jsxs("div", { className: "mt-1 space-y-2 rounded-md bg-muted/50 px-3 py-2 text-xs text-muted-foreground", children: [inputPayload && _jsx(ToolDetailViewer, { payload: inputPayload }), resultPayload && _jsx(ToolDetailViewer, { payload: resultPayload })] }))] }));
|
|
1179
|
-
}
|
|
1180
|
-
function ToolCallFallback({ toolName, args, argsText, result, ...rest }) {
|
|
1181
|
-
const chatRunning = React.useContext(ChatRunningContext);
|
|
1182
|
-
const isRunning = result === undefined && chatRunning;
|
|
1183
|
-
return (_jsx(ToolCallDisplay, { toolName: toolName, args: args, argsText: argsText, result: typeof result === "string"
|
|
1184
|
-
? result
|
|
1185
|
-
: result !== undefined
|
|
1186
|
-
? JSON.stringify(result)
|
|
1187
|
-
: undefined, mcpApp: rest.mcpApp, isRunning: isRunning }));
|
|
1188
|
-
}
|
|
1189
|
-
// ─── Reconnect Stream Message ───────────────────────────────────────────────
|
|
1190
|
-
// Renders the agent's in-progress response during reconnection (outside
|
|
1191
|
-
// assistant-ui's runtime). Uses the same visual styling as normal messages.
|
|
1192
|
-
function ReconnectStreamMessage({ content }) {
|
|
1193
|
-
const chatRunning = React.useContext(ChatRunningContext);
|
|
1194
|
-
return (_jsx("div", { className: "flex justify-start", children: _jsx("div", { className: "max-w-[95%] text-sm leading-relaxed text-foreground space-y-1", children: content.map((part, i) => {
|
|
1195
|
-
if (part.type === "text") {
|
|
1196
|
-
return (_jsx(SmoothMarkdownText, { text: part.text, streaming: chatRunning, resetKey: `reconnect-text-${i}`, statusType: chatRunning ? "running" : "complete" }, `reconnect-text-${i}`));
|
|
1197
|
-
}
|
|
1198
|
-
if (part.type === "tool-call") {
|
|
1199
|
-
return (_jsx(ToolCallDisplay, { toolName: part.toolName, argsText: part.argsText, args: part.args, result: part.result, mcpApp: part.mcpApp, isRunning: part.result === undefined && chatRunning }, `reconnect-tool-${i}`));
|
|
1200
|
-
}
|
|
1201
|
-
return null;
|
|
1202
|
-
}) }) }));
|
|
1203
|
-
}
|
|
1204
|
-
// ─── Message Components ─────────────────────────────────────────────────────
|
|
1205
|
-
const mentionIconProps = {
|
|
1206
|
-
size: 14,
|
|
1207
|
-
className: "shrink-0 text-muted-foreground",
|
|
1208
|
-
};
|
|
1209
|
-
function MentionChipIcon({ icon }) {
|
|
1210
|
-
switch (icon) {
|
|
1211
|
-
case "folder":
|
|
1212
|
-
return _jsx(IconFolder, { ...mentionIconProps });
|
|
1213
|
-
case "document":
|
|
1214
|
-
return _jsx(IconFileText, { ...mentionIconProps });
|
|
1215
|
-
case "form":
|
|
1216
|
-
return _jsx(IconCheckbox, { ...mentionIconProps });
|
|
1217
|
-
case "email":
|
|
1218
|
-
return _jsx(IconMail, { ...mentionIconProps });
|
|
1219
|
-
case "user":
|
|
1220
|
-
return _jsx(IconUser, { ...mentionIconProps });
|
|
1221
|
-
case "deck":
|
|
1222
|
-
return _jsx(IconPresentation, { ...mentionIconProps });
|
|
1223
|
-
case "agent":
|
|
1224
|
-
return _jsx(IconMessageChatbot, { ...mentionIconProps });
|
|
1225
|
-
case "file":
|
|
1226
|
-
return _jsx(IconFile, { ...mentionIconProps });
|
|
1227
|
-
default:
|
|
1228
|
-
return _jsx(IconStack2, { ...mentionIconProps });
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
// Matches rich mention format: @[label|icon] or plain @word
|
|
1232
|
-
const richMentionPattern = /@\[([^\]|]+)\|([^\]]+)\]/g;
|
|
1233
|
-
const plainMentionPattern = /((?:^|(?<=\s))@(\w+))/g;
|
|
1234
|
-
function UserMessageText({ text }) {
|
|
1235
|
-
// Strip injected <context>...</context> blocks before display
|
|
1236
|
-
const displayText = displayableUserMessageText(text);
|
|
1237
|
-
const parts = [];
|
|
1238
|
-
let lastIndex = 0;
|
|
1239
|
-
let match;
|
|
1240
|
-
let hasRichMentions = false;
|
|
1241
|
-
// First try rich mentions (@[label|icon])
|
|
1242
|
-
richMentionPattern.lastIndex = 0;
|
|
1243
|
-
while ((match = richMentionPattern.exec(displayText)) !== null) {
|
|
1244
|
-
hasRichMentions = true;
|
|
1245
|
-
const matchStart = match.index;
|
|
1246
|
-
if (matchStart > lastIndex) {
|
|
1247
|
-
parts.push(displayText.slice(lastIndex, matchStart));
|
|
1248
|
-
}
|
|
1249
|
-
const label = match[1];
|
|
1250
|
-
const icon = match[2];
|
|
1251
|
-
parts.push(_jsxs("span", { className: "inline-flex items-center gap-1 rounded-md border border-input bg-muted/50 px-1.5 py-0.5 text-xs font-medium text-foreground align-middle mx-0.5 max-w-[200px] select-all", "data-mention-label": label, children: [_jsx(MentionChipIcon, { icon: icon }), _jsx("span", { className: "truncate", children: label })] }, matchStart));
|
|
1252
|
-
lastIndex = matchStart + match[0].length;
|
|
1253
|
-
}
|
|
1254
|
-
if (hasRichMentions) {
|
|
1255
|
-
if (lastIndex < displayText.length) {
|
|
1256
|
-
parts.push(displayText.slice(lastIndex));
|
|
1257
|
-
}
|
|
1258
|
-
return _jsx(_Fragment, { children: parts });
|
|
1259
|
-
}
|
|
1260
|
-
// Fallback: plain @word mentions (for older messages)
|
|
1261
|
-
plainMentionPattern.lastIndex = 0;
|
|
1262
|
-
while ((match = plainMentionPattern.exec(displayText)) !== null) {
|
|
1263
|
-
const matchStart = match.index;
|
|
1264
|
-
if (matchStart > lastIndex) {
|
|
1265
|
-
parts.push(displayText.slice(lastIndex, matchStart));
|
|
1266
|
-
}
|
|
1267
|
-
const mentionName = match[2];
|
|
1268
|
-
parts.push(_jsxs("span", { className: "inline-flex items-center gap-1 rounded-md border border-input bg-muted/50 px-1.5 py-0.5 text-xs font-medium text-foreground align-middle mx-0.5 select-all", "data-mention-label": mentionName, children: ["@", mentionName] }, matchStart));
|
|
1269
|
-
lastIndex = matchStart + match[0].length;
|
|
1270
|
-
}
|
|
1271
|
-
if (lastIndex < displayText.length) {
|
|
1272
|
-
parts.push(displayText.slice(lastIndex));
|
|
1273
|
-
}
|
|
1274
|
-
return _jsx(_Fragment, { children: parts.length > 0 ? parts : displayText });
|
|
1275
|
-
}
|
|
1276
|
-
export function displayableUserMessageText(text) {
|
|
1277
|
-
return text.replace(/<context>[\s\S]*?<\/context>\n?/g, "").trim();
|
|
1278
|
-
}
|
|
1279
|
-
function UserMessageAttachments() {
|
|
1280
|
-
const messageRuntime = useMessageRuntime();
|
|
1281
|
-
const msg = messageRuntime.getState();
|
|
1282
|
-
// assistant-ui stores user attachments on msg.attachments (separate from content).
|
|
1283
|
-
// Each attachment has: { id, type, name, contentType?, content: MessagePart[] }.
|
|
1284
|
-
// Image adapters put a {type:"image", image:"data:..."} part in content; text
|
|
1285
|
-
// adapters put a {type:"text", text:"<attachment>..."} part. Fall back to a
|
|
1286
|
-
// file chip when there's no inline image.
|
|
1287
|
-
const attachments = msg
|
|
1288
|
-
.attachments;
|
|
1289
|
-
if (!attachments || attachments.length === 0)
|
|
1290
|
-
return null;
|
|
1291
|
-
return (_jsx("div", { className: "flex flex-wrap justify-end gap-1.5 mb-1.5", children: attachments.map((att) => {
|
|
1292
|
-
if (isPastedTextAttachmentName(att.name)) {
|
|
1293
|
-
return _jsx(PastedTextChip, { attachment: att, compact: true }, att.id);
|
|
1294
|
-
}
|
|
1295
|
-
const imagePart = att.content?.find((p) => p.type === "image" && "image" in p && !!p.image);
|
|
1296
|
-
if (imagePart) {
|
|
1297
|
-
return (_jsx("div", { className: "h-16 w-16 overflow-hidden rounded-lg border border-border/70 bg-muted/50", title: att.name, children: _jsx("img", { src: imagePart.image, alt: att.name, className: "h-full w-full object-cover" }) }, att.id));
|
|
1298
|
-
}
|
|
1299
|
-
return (_jsxs("div", { className: "flex items-center gap-1.5 rounded-lg border border-border/70 bg-muted/50 px-2 py-1.5 text-xs text-muted-foreground", title: att.name, children: [_jsx(IconFile, { className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "truncate max-w-[120px]", children: att.name || "file" })] }, att.id));
|
|
1300
|
-
}) }));
|
|
1301
|
-
}
|
|
1302
|
-
function UserMessage() {
|
|
1303
|
-
const [expanded, setExpanded] = useState(false);
|
|
1304
|
-
const [isExpandable, setIsExpandable] = useState(false);
|
|
1305
|
-
const contentRef = useRef(null);
|
|
1306
|
-
const messageRuntime = useMessageRuntime();
|
|
1307
|
-
const message = messageRuntime.getState();
|
|
1308
|
-
const timestamp = formatMessageTimestamp(message.createdAt);
|
|
1309
|
-
const hasDisplayableText = message.content
|
|
1310
|
-
?.filter((part) => {
|
|
1311
|
-
return part.type === "text" && typeof part.text === "string";
|
|
1312
|
-
})
|
|
1313
|
-
.some((part) => displayableUserMessageText(part.text).length > 0) ??
|
|
1314
|
-
false;
|
|
1315
|
-
useEffect(() => {
|
|
1316
|
-
const el = contentRef.current;
|
|
1317
|
-
if (!el || !hasDisplayableText)
|
|
1318
|
-
return;
|
|
1319
|
-
const measure = () => {
|
|
1320
|
-
setIsExpandable(el.scrollHeight > 200);
|
|
1321
|
-
};
|
|
1322
|
-
measure();
|
|
1323
|
-
const observer = new ResizeObserver(measure);
|
|
1324
|
-
observer.observe(el);
|
|
1325
|
-
return () => observer.disconnect();
|
|
1326
|
-
}, [hasDisplayableText]);
|
|
1327
|
-
return (_jsx("div", { className: "group flex justify-end", style: { contentVisibility: "auto" }, children: _jsxs("div", { className: "max-w-[85%]", children: [_jsx(UserMessageAttachments, {}), hasDisplayableText && (_jsxs("div", { className: "relative rounded-lg bg-accent px-3 py-2 text-sm leading-relaxed text-foreground", onCopy: (e) => {
|
|
1328
|
-
const selection = window.getSelection();
|
|
1329
|
-
if (!selection || selection.rangeCount === 0)
|
|
1330
|
-
return;
|
|
1331
|
-
const fragment = selection.getRangeAt(0).cloneContents();
|
|
1332
|
-
const mentions = fragment.querySelectorAll("[data-mention-label]");
|
|
1333
|
-
if (mentions.length === 0)
|
|
1334
|
-
return;
|
|
1335
|
-
e.preventDefault();
|
|
1336
|
-
mentions.forEach((el) => {
|
|
1337
|
-
el.textContent = `@${el.getAttribute("data-mention-label")}`;
|
|
1338
|
-
});
|
|
1339
|
-
const div = document.createElement("div");
|
|
1340
|
-
div.appendChild(fragment);
|
|
1341
|
-
e.clipboardData.setData("text/plain", div.textContent || "");
|
|
1342
|
-
}, children: [_jsx("div", { ref: contentRef, className: cn("whitespace-pre-wrap break-words", !expanded && isExpandable && "max-h-[200px] overflow-hidden"), children: _jsx(MessagePrimitive.Parts, { components: {
|
|
1343
|
-
Text: UserMessageText,
|
|
1344
|
-
} }) }), !expanded && isExpandable && (_jsx("div", { className: "pointer-events-none absolute inset-x-0 bottom-0 h-14 rounded-b-lg bg-gradient-to-t from-accent via-accent/90 to-transparent" }))] })), hasDisplayableText && isExpandable && (_jsxs("button", { type: "button", onClick: () => setExpanded((prev) => !prev), className: "mt-1 inline-flex items-center gap-1 rounded-md px-1.5 py-1 text-[11px] font-medium text-muted-foreground hover:text-foreground", children: [_jsx(IconChevronDown, { className: cn("h-3.5 w-3.5 transition-transform", expanded && "rotate-180") }), expanded ? "Collapse" : "Expand"] })), timestamp && (_jsx("div", { className: "mt-1 flex justify-end", children: _jsx(MessageTimestamp, { timestamp: timestamp, className: "opacity-0 transition-opacity duration-150 group-hover:opacity-100 group-focus-within:opacity-100" }) }))] }) }));
|
|
1345
|
-
}
|
|
1346
|
-
const CheckpointContext = React.createContext(null);
|
|
1347
|
-
const MessageActionsContext = React.createContext(null);
|
|
1348
|
-
function MessageActionsMenu({ showRevert, onRevert, } = {}) {
|
|
1349
|
-
const [open, setOpen] = useState(false);
|
|
1350
|
-
const [copied, setCopied] = useState(null);
|
|
1351
|
-
const messageRuntime = useMessageRuntime();
|
|
1352
|
-
const actionsCtx = React.useContext(MessageActionsContext);
|
|
1353
|
-
const timestamp = formatMessageTimestamp(messageRuntime.getState().createdAt);
|
|
1354
|
-
const handleCopyMessage = useCallback(() => {
|
|
1355
|
-
const m = messageRuntime.getState();
|
|
1356
|
-
const text = m.content
|
|
1357
|
-
.filter((p) => p.type === "text")
|
|
1358
|
-
.map((p) => p.text)
|
|
1359
|
-
.join("\n");
|
|
1360
|
-
void writeClipboardText(text).then((ok) => {
|
|
1361
|
-
if (!ok)
|
|
1362
|
-
return;
|
|
1363
|
-
setCopied("message");
|
|
1364
|
-
setTimeout(() => {
|
|
1365
|
-
setCopied(null);
|
|
1366
|
-
setOpen(false);
|
|
1367
|
-
}, 1000);
|
|
1368
|
-
});
|
|
1369
|
-
}, [messageRuntime]);
|
|
1370
|
-
const handleCopyRequestId = useCallback(() => {
|
|
1371
|
-
const m = messageRuntime.getState();
|
|
1372
|
-
const meta = m.metadata;
|
|
1373
|
-
// Live yields put the trace ID at metadata.custom.runId; server-persisted
|
|
1374
|
-
// messages put it at metadata.runId. If neither is present (e.g. the run
|
|
1375
|
-
// is still in flight and this is the first message), fall back to the
|
|
1376
|
-
// active-run state so a hung / mid-stream chat still surfaces a usable
|
|
1377
|
-
// trace ID. Last resort is the assistant-ui local message id.
|
|
1378
|
-
const runId = (typeof meta?.custom?.runId === "string" && meta.custom.runId) ||
|
|
1379
|
-
(typeof meta?.runId === "string" && meta.runId) ||
|
|
1380
|
-
(typeof window !== "undefined" ? getActiveRun()?.runId : null) ||
|
|
1381
|
-
m.id ||
|
|
1382
|
-
"";
|
|
1383
|
-
void writeClipboardText(runId).then((ok) => {
|
|
1384
|
-
if (!ok)
|
|
1385
|
-
return;
|
|
1386
|
-
setCopied("id");
|
|
1387
|
-
setTimeout(() => {
|
|
1388
|
-
setCopied(null);
|
|
1389
|
-
setOpen(false);
|
|
1390
|
-
}, 1000);
|
|
1391
|
-
});
|
|
1392
|
-
}, [messageRuntime]);
|
|
1393
|
-
const handleForkChat = useCallback(() => {
|
|
1394
|
-
setOpen(false);
|
|
1395
|
-
actionsCtx?.onForkChat?.();
|
|
1396
|
-
}, [actionsCtx]);
|
|
1397
|
-
const handleRevert = useCallback(() => {
|
|
1398
|
-
setOpen(false);
|
|
1399
|
-
onRevert?.();
|
|
1400
|
-
}, [onRevert]);
|
|
1401
|
-
return (_jsxs(DropdownMenu, { open: open, onOpenChange: setOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { "aria-label": "Message actions", className: cn("flex h-6 w-6 items-center justify-center rounded-md text-muted-foreground/70 transition-colors duration-150 hover:bg-accent hover:text-foreground", open && "bg-accent text-foreground"), children: _jsx(IconDots, { className: "h-3.5 w-3.5" }) }) }), _jsxs(DropdownMenuContent, { align: "start", sideOffset: 6, className: "w-48 rounded-lg border-border p-1.5 shadow-xl", children: [actionsCtx?.onForkChat && (_jsxs(DropdownMenuItem, { onSelect: handleForkChat, children: [_jsx(IconGitFork, { className: "h-3.5 w-3.5" }), "Fork Chat"] })), _jsxs(DropdownMenuItem, { onSelect: (e) => {
|
|
1402
|
-
e.preventDefault();
|
|
1403
|
-
handleCopyMessage();
|
|
1404
|
-
}, children: [copied === "message" ? (_jsx(IconCheck, { className: "h-3.5 w-3.5" })) : (_jsx(IconCopy, { className: "h-3.5 w-3.5" })), copied === "message" ? "Copied!" : "Copy Message"] }), _jsxs(DropdownMenuItem, { onSelect: (e) => {
|
|
1405
|
-
e.preventDefault();
|
|
1406
|
-
handleCopyRequestId();
|
|
1407
|
-
}, children: [copied === "id" ? (_jsx(IconCheck, { className: "h-3.5 w-3.5" })) : (_jsx(IconId, { className: "h-3.5 w-3.5" })), copied === "id" ? "Copied!" : "Copy Request ID"] }), showRevert && (_jsxs(DropdownMenuItem, { onSelect: handleRevert, children: [_jsx(IconArrowBackUp, { className: "h-3.5 w-3.5" }), "Revert to here"] })), timestamp && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuLabel, { className: "px-2 py-1 text-[11px] font-normal text-muted-foreground", children: ["Sent ", timestamp.short] })] }))] })] }));
|
|
1408
|
-
}
|
|
1409
|
-
function AssistantMessage() {
|
|
1410
|
-
const [restoreState, setRestoreState] = useState("idle");
|
|
1411
|
-
const messageRuntime = useMessageRuntime();
|
|
1412
|
-
const thread = useThread();
|
|
1413
|
-
const chatRunning = React.useContext(ChatRunningContext);
|
|
1414
|
-
const msg = messageRuntime.getState();
|
|
1415
|
-
const timestamp = formatMessageTimestamp(msg.createdAt);
|
|
1416
|
-
const isLast = thread.messages.length > 0 &&
|
|
1417
|
-
thread.messages[thread.messages.length - 1].id === msg.id;
|
|
1418
|
-
const isComplete = !isLast || !chatRunning;
|
|
1419
|
-
const cpCtx = React.useContext(CheckpointContext);
|
|
1420
|
-
const handleRestore = useCallback(async () => {
|
|
1421
|
-
if (restoreState === "idle") {
|
|
1422
|
-
setRestoreState("confirming");
|
|
1423
|
-
return;
|
|
1424
|
-
}
|
|
1425
|
-
if (restoreState !== "confirming" || !cpCtx)
|
|
1426
|
-
return;
|
|
1427
|
-
setRestoreState("restoring");
|
|
1428
|
-
try {
|
|
1429
|
-
const m = messageRuntime.getState();
|
|
1430
|
-
const meta = m.metadata;
|
|
1431
|
-
const runId = (typeof meta?.custom?.runId === "string" && meta.custom.runId) ||
|
|
1432
|
-
(typeof meta?.runId === "string" && meta.runId) ||
|
|
1433
|
-
null;
|
|
1434
|
-
if (!runId) {
|
|
1435
|
-
setRestoreState("idle");
|
|
1436
|
-
return;
|
|
1437
|
-
}
|
|
1438
|
-
const tid = cpCtx.threadId || "";
|
|
1439
|
-
const res = await fetch(`${cpCtx.apiUrl}/checkpoints?threadId=${encodeURIComponent(tid)}`);
|
|
1440
|
-
const checkpoints = res.ok ? await res.json() : [];
|
|
1441
|
-
const checkpoint = checkpoints.find((cp) => cp.runId === runId);
|
|
1442
|
-
if (!checkpoint) {
|
|
1443
|
-
setRestoreState("idle");
|
|
1444
|
-
return;
|
|
1445
|
-
}
|
|
1446
|
-
const restoreRes = await fetch(`${cpCtx.apiUrl}/checkpoints/restore`, {
|
|
1447
|
-
method: "POST",
|
|
1448
|
-
headers: { "Content-Type": "application/json" },
|
|
1449
|
-
body: JSON.stringify({ checkpointId: checkpoint.id }),
|
|
1450
|
-
});
|
|
1451
|
-
if (restoreRes.ok) {
|
|
1452
|
-
window.location.reload();
|
|
1453
|
-
}
|
|
1454
|
-
else {
|
|
1455
|
-
setRestoreState("idle");
|
|
1456
|
-
}
|
|
1457
|
-
}
|
|
1458
|
-
catch {
|
|
1459
|
-
setRestoreState("idle");
|
|
1460
|
-
}
|
|
1461
|
-
}, [restoreState, cpCtx, messageRuntime]);
|
|
1462
|
-
const cancelRestore = useCallback(() => {
|
|
1463
|
-
setRestoreState("idle");
|
|
1464
|
-
}, []);
|
|
1465
|
-
const showRestore = cpCtx?.devMode && isComplete && !isLast;
|
|
1466
|
-
return (_jsxs("div", { className: "group relative", style: { contentVisibility: isComplete ? "auto" : "visible" }, children: [_jsx("div", { className: "max-w-[95%] text-sm leading-relaxed text-foreground", children: _jsx(MessagePrimitive.Parts, { components: {
|
|
1467
|
-
Text: MarkdownText,
|
|
1468
|
-
tools: {
|
|
1469
|
-
Fallback: ToolCallFallback,
|
|
1470
|
-
},
|
|
1471
|
-
} }) }), isComplete && (_jsxs("div", { className: "mt-1 flex items-center justify-between", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx(MessageActionsMenu, { showRevert: showRestore && restoreState === "idle", onRevert: handleRestore }), timestamp && (_jsx(MessageTimestamp, { timestamp: timestamp, className: "opacity-0 transition-opacity duration-150 group-hover:opacity-100 group-focus-within:opacity-100" }))] }), showRestore && restoreState === "confirming" ? (_jsxs("div", { className: "flex items-center gap-1 text-xs", children: [_jsx("button", { onClick: handleRestore, className: "rounded-md bg-destructive px-1.5 py-0.5 text-destructive-foreground hover:bg-destructive/90", children: "Restore to here?" }), _jsx("button", { onClick: cancelRestore, className: "rounded-md px-1.5 py-0.5 text-muted-foreground hover:bg-accent", children: "Cancel" })] })) : showRestore && restoreState === "restoring" ? (_jsxs("span", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(IconLoader2, { className: "h-3 w-3 animate-spin" }), "Restoring..."] })) : (_jsx(ThumbsFeedback, { threadId: cpCtx?.threadId ?? "", runId: (() => {
|
|
1472
|
-
const meta = messageRuntime.getState().metadata;
|
|
1473
|
-
return ((typeof meta?.custom?.runId === "string" &&
|
|
1474
|
-
meta.custom.runId) ||
|
|
1475
|
-
(typeof meta?.runId === "string" && meta.runId) ||
|
|
1476
|
-
"");
|
|
1477
|
-
})(), messageSeq: thread.messages.findIndex((m) => m.id === msg.id) }))] }))] }));
|
|
1478
|
-
}
|
|
1479
|
-
// ─── Thinking Indicator ─────────────────────────────────────────────────────
|
|
1480
|
-
function RunningActivityStatus({ label }) {
|
|
1481
|
-
return (_jsx("div", { className: "agent-running-activity shrink-0 px-4 pb-2", children: _jsx(ThinkingIndicator, { label: label }) }));
|
|
1482
|
-
}
|
|
1483
|
-
function ThinkingIndicator({ label = "Thinking" } = {}) {
|
|
1484
|
-
const [dots, setDots] = useState(0);
|
|
1485
|
-
useEffect(() => {
|
|
1486
|
-
const interval = setInterval(() => {
|
|
1487
|
-
setDots((d) => (d + 1) % 4);
|
|
1488
|
-
}, 400);
|
|
1489
|
-
return () => clearInterval(interval);
|
|
1490
|
-
}, []);
|
|
1491
|
-
return (_jsx("div", { className: "flex items-center text-muted-foreground", children: _jsxs("span", { className: "text-xs", children: [label, ".".repeat(dots)] }) }));
|
|
1492
|
-
}
|
|
1493
|
-
// ─── Builder.io Connect CTA (shared by setup + usage-limit cards) ───────────
|
|
1494
|
-
//
|
|
1495
|
-
// Renders a single row with left-aligned copy and a right-aligned action.
|
|
1496
|
-
// Click opens the Builder CLI-auth popup via the shared
|
|
1497
|
-
// `useBuilderConnectFlow` hook (which owns the synchronous window.open,
|
|
1498
|
-
// the 2s status poll, and the focus-refresh). On success the hook broadcasts
|
|
1499
|
-
// a config-change event and this card clears its local `missingApiKey` gate
|
|
1500
|
-
// so the user can start chatting without a full-page reload.
|
|
1501
|
-
//
|
|
1502
|
-
// Desktop note: when this component runs inside the Electron shell, the
|
|
1503
|
-
// window.open call is intercepted by the main process's webview popup handler,
|
|
1504
|
-
// which opens the flow in an Electron BrowserWindow that shares the webview's
|
|
1505
|
-
// session. See packages/desktop-app/src/main/index.ts.
|
|
1506
|
-
function BuilderConnectCta({ variant = "primary", onConnected, }) {
|
|
1507
|
-
const { configured, orgName, connecting, error, start } = useBuilderConnectFlow({
|
|
1508
|
-
trackingSource: "assistant_chat_builder_cta",
|
|
1509
|
-
onConnected,
|
|
1510
|
-
});
|
|
1511
|
-
const containerClass = variant === "compact"
|
|
1512
|
-
? "rounded-md border border-border px-3 py-2.5"
|
|
1513
|
-
: "flex items-center gap-3 rounded-md border border-border px-3 py-3";
|
|
1514
|
-
if (configured) {
|
|
1515
|
-
return (_jsxs("div", { className: containerClass, children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-xs font-medium text-foreground", children: "Builder.io" }), _jsx("p", { className: "text-[11px] text-muted-foreground mt-0.5", children: orgName ? `Connected — ${orgName}` : "Connected" })] }), _jsxs("span", { className: "ml-auto inline-flex items-center gap-1 shrink-0 rounded-md bg-emerald-500/10 px-2 py-0.5 text-[10px] font-medium text-emerald-500", children: [_jsx(IconCheck, { size: 10 }), "Connected"] })] }));
|
|
1516
|
-
}
|
|
1517
|
-
return (_jsxs("div", { className: containerClass, children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "text-xs font-medium text-foreground", children: "Connect Builder.io" }), _jsx("p", { className: "text-[11px] text-muted-foreground mt-0.5 max-w-[220px]", children: "Free credits for LLM, hosting, and more \u2014 no API key needed" }), error && _jsx("p", { className: "mt-1 text-[10px] text-destructive", children: error })] }), _jsx("button", { type: "button", onClick: () => start(), disabled: connecting, className: "ml-auto inline-flex items-center gap-1 shrink-0 rounded-md bg-foreground px-3 py-1.5 text-[11px] font-medium no-underline text-background hover:opacity-90 disabled:opacity-60 disabled:cursor-wait", "aria-busy": connecting, children: connecting ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { size: 10, className: "animate-spin" }), "Waiting\u2026"] })) : (_jsxs(_Fragment, { children: ["Connect", _jsx(IconExternalLink, { size: 10 })] })) })] }));
|
|
1518
|
-
}
|
|
1519
|
-
// ─── Builder Setup Card ─────────────────────────────────────────────────────
|
|
1520
|
-
function BuilderSetupCard({ onConnected, bouncePulse, }) {
|
|
1521
|
-
// Progressive disclosure: the card leads with the one-click Builder connect.
|
|
1522
|
-
// The bring-your-own-key path stays tucked behind a single link so the chat
|
|
1523
|
-
// stays clean for people who connect Builder or never use the side chat at
|
|
1524
|
-
// all (they can keep driving the plan from their own coding agent).
|
|
1525
|
-
const [keyOpen, setKeyOpen] = useState(false);
|
|
1526
|
-
const cardRef = useRef(null);
|
|
1527
|
-
// Replay the bounce keyframe each time bouncePulse increments. Toggling the
|
|
1528
|
-
// class off-then-on (with a forced reflow) restarts the animation even when
|
|
1529
|
-
// the value changes back-to-back.
|
|
1530
|
-
useEffect(() => {
|
|
1531
|
-
if (!bouncePulse)
|
|
1532
|
-
return;
|
|
1533
|
-
const el = cardRef.current;
|
|
1534
|
-
if (!el)
|
|
1535
|
-
return;
|
|
1536
|
-
el.classList.remove("animate-bounce-once");
|
|
1537
|
-
void el.offsetWidth;
|
|
1538
|
-
el.classList.add("animate-bounce-once");
|
|
1539
|
-
}, [bouncePulse]);
|
|
1540
|
-
return (_jsxs("div", { ref: cardRef, className: "mx-4 my-6 rounded-lg border border-border bg-card p-5", children: [_jsxs("div", { className: "flex items-center gap-3 mb-3", children: [_jsx("div", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted", children: _jsx(IconMessage, { className: "h-4.5 w-4.5 text-muted-foreground" }) }), _jsxs("div", { children: [_jsx("h3", { className: "text-sm font-medium text-foreground", children: "Turn on the side chat" }), _jsx("p", { className: "mt-0.5 text-[11px] text-muted-foreground", children: "One click to connect Builder for free hosted access \u2014 no API key or account needed." })] })] }), _jsxs("div", { className: "space-y-3", children: [_jsx(BuilderConnectCta, { onConnected: onConnected }), keyOpen ? (_jsx(ApiKeyConnect, { onConnected: onConnected })) : (_jsx("div", { className: "text-center", children: _jsx("button", { type: "button", onClick: () => setKeyOpen(true), className: "text-[11px] text-muted-foreground underline-offset-2 hover:text-foreground hover:underline", children: "Or paste your own Anthropic or OpenAI key" }) })), _jsx("p", { className: "text-center text-[11px] leading-relaxed text-muted-foreground", children: "You can skip this and keep editing the plan with your own coding agent." })] })] }));
|
|
1541
|
-
}
|
|
1542
|
-
// ─── Inline BYOK (Anthropic / OpenAI) ───────────────────────────────────────
|
|
1543
|
-
const API_KEY_PROVIDERS = [
|
|
1544
|
-
{ value: "anthropic", label: "Anthropic", placeholder: "sk-ant-…" },
|
|
1545
|
-
{ value: "openai", label: "OpenAI", placeholder: "sk-…" },
|
|
1546
|
-
];
|
|
1547
|
-
function ApiKeyConnect({ onConnected }) {
|
|
1548
|
-
const [provider, setProvider] = useState("anthropic");
|
|
1549
|
-
const [apiKey, setApiKey] = useState("");
|
|
1550
|
-
const [saving, setSaving] = useState(false);
|
|
1551
|
-
const [error, setError] = useState(null);
|
|
1552
|
-
const active = API_KEY_PROVIDERS.find((p) => p.value === provider);
|
|
1553
|
-
const handleSave = useCallback(async () => {
|
|
1554
|
-
if (!apiKey.trim() || saving)
|
|
1555
|
-
return;
|
|
1556
|
-
setSaving(true);
|
|
1557
|
-
setError(null);
|
|
1558
|
-
try {
|
|
1559
|
-
await saveAgentEngineApiKey({ provider, apiKey });
|
|
1560
|
-
setApiKey("");
|
|
1561
|
-
onConnected?.();
|
|
1562
|
-
}
|
|
1563
|
-
catch (err) {
|
|
1564
|
-
setError(err instanceof Error ? err.message : "Could not save the key.");
|
|
1565
|
-
}
|
|
1566
|
-
finally {
|
|
1567
|
-
setSaving(false);
|
|
1568
|
-
}
|
|
1569
|
-
}, [apiKey, onConnected, provider, saving]);
|
|
1570
|
-
return (_jsxs("div", { className: "rounded-md border border-border bg-background/60 p-3", children: [_jsxs("div", { className: "mb-2 flex items-center gap-1.5 text-[11px] font-medium text-foreground", children: [_jsx(IconKey, { size: 12, strokeWidth: 1.9 }), "Use your own API key"] }), _jsx("p", { className: "mb-2.5 text-[11px] leading-relaxed text-muted-foreground", children: "Stored locally for this app only \u2014 no account required." }), _jsx("div", { role: "tablist", "aria-label": "API key provider", className: "mb-2 inline-flex rounded-md border border-border bg-muted/40 p-0.5", children: API_KEY_PROVIDERS.map((option) => {
|
|
1571
|
-
const selected = option.value === provider;
|
|
1572
|
-
return (_jsx("button", { type: "button", role: "tab", "aria-selected": selected, onClick: () => {
|
|
1573
|
-
setProvider(option.value);
|
|
1574
|
-
setError(null);
|
|
1575
|
-
}, className: cn("rounded px-2.5 py-1 text-[11px] font-medium transition-colors", selected
|
|
1576
|
-
? "bg-background text-foreground shadow-sm"
|
|
1577
|
-
: "text-muted-foreground hover:text-foreground"), children: option.label }, option.value));
|
|
1578
|
-
}) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { type: "password", value: apiKey, autoComplete: "off", spellCheck: false, placeholder: active.placeholder, onChange: (e) => {
|
|
1579
|
-
setApiKey(e.target.value);
|
|
1580
|
-
if (error)
|
|
1581
|
-
setError(null);
|
|
1582
|
-
}, onKeyDown: (e) => {
|
|
1583
|
-
if (e.key === "Enter") {
|
|
1584
|
-
e.preventDefault();
|
|
1585
|
-
void handleSave();
|
|
1586
|
-
}
|
|
1587
|
-
}, className: "h-8 min-w-0 flex-1 rounded-md border border-input bg-background px-2.5 text-[12px] text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-1 focus:ring-offset-background" }), _jsx("button", { type: "button", onClick: handleSave, disabled: !apiKey.trim() || saving, className: "inline-flex h-8 shrink-0 items-center gap-1 rounded-md bg-foreground px-3 text-[11px] font-medium text-background hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-60", children: saving ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { size: 11, className: "animate-spin" }), "Saving\u2026"] })) : ("Save") })] }), error ? (_jsx("p", { className: "mt-2 text-[11px] text-destructive", children: error })) : null] }));
|
|
1588
|
-
}
|
|
1589
|
-
function getLoopLimitMetadata(message) {
|
|
1590
|
-
const meta = message?.metadata;
|
|
1591
|
-
const loopLimit = meta?.custom?.loopLimit ?? meta?.loopLimit;
|
|
1592
|
-
if (!loopLimit || typeof loopLimit !== "object")
|
|
1593
|
-
return null;
|
|
1594
|
-
return {
|
|
1595
|
-
...(typeof loopLimit.maxIterations === "number"
|
|
1596
|
-
? { maxIterations: loopLimit.maxIterations }
|
|
1597
|
-
: {}),
|
|
1598
|
-
};
|
|
1599
|
-
}
|
|
1600
|
-
function getRunErrorMetadata(message) {
|
|
1601
|
-
const meta = message?.metadata;
|
|
1602
|
-
const runError = meta?.custom?.runError ?? meta?.runError;
|
|
1603
|
-
if (!runError || typeof runError !== "object")
|
|
1604
|
-
return null;
|
|
1605
|
-
const messageText = typeof runError.message === "string" ? runError.message : "";
|
|
1606
|
-
if (!messageText)
|
|
1607
|
-
return null;
|
|
1608
|
-
const runId = typeof runError.runId === "string"
|
|
1609
|
-
? runError.runId
|
|
1610
|
-
: typeof meta?.custom?.runId === "string"
|
|
1611
|
-
? meta.custom.runId
|
|
1612
|
-
: typeof meta?.runId === "string"
|
|
1613
|
-
? meta.runId
|
|
1614
|
-
: undefined;
|
|
1615
|
-
return {
|
|
1616
|
-
message: messageText,
|
|
1617
|
-
...(typeof runError.details === "string"
|
|
1618
|
-
? { details: runError.details }
|
|
1619
|
-
: {}),
|
|
1620
|
-
...(typeof runError.errorCode === "string"
|
|
1621
|
-
? { errorCode: runError.errorCode }
|
|
1622
|
-
: {}),
|
|
1623
|
-
...(runId ? { runId } : {}),
|
|
1624
|
-
...(runError.recoverable ? { recoverable: true } : {}),
|
|
1625
|
-
};
|
|
1626
|
-
}
|
|
1627
|
-
function getRequestModeMetadata(message) {
|
|
1628
|
-
const meta = message?.metadata;
|
|
1629
|
-
const requestMode = meta?.custom?.requestMode ?? meta?.requestMode;
|
|
1630
|
-
return requestMode === "act" || requestMode === "plan" ? requestMode : null;
|
|
1631
|
-
}
|
|
1632
|
-
function isBuilderReconnectRunError(info) {
|
|
1633
|
-
const code = (info.errorCode ?? "").toLowerCase();
|
|
1634
|
-
const message = info.message.toLowerCase();
|
|
1635
|
-
const isAuthCode = code === "authentication_error" ||
|
|
1636
|
-
code === "unauthorized" ||
|
|
1637
|
-
code === "http_401" ||
|
|
1638
|
-
code === "http_403";
|
|
1639
|
-
return (code === "builder_auth_error" ||
|
|
1640
|
-
message.includes("builder authentication failed") ||
|
|
1641
|
-
(isAuthCode &&
|
|
1642
|
-
(message.includes("invalid token") ||
|
|
1643
|
-
message.includes("personal access token"))));
|
|
1644
|
-
}
|
|
1645
|
-
function isProviderQueryRunError(info) {
|
|
1646
|
-
const text = [info.errorCode, info.message, info.details]
|
|
1647
|
-
.filter(Boolean)
|
|
1648
|
-
.join("\n")
|
|
1649
|
-
.toLowerCase();
|
|
1650
|
-
return (text.includes("bigquery") ||
|
|
1651
|
-
text.includes("sql") ||
|
|
1652
|
-
text.includes("query") ||
|
|
1653
|
-
text.includes("schema") ||
|
|
1654
|
-
text.includes("syntax") ||
|
|
1655
|
-
text.includes("unknown column") ||
|
|
1656
|
-
text.includes("unknown table") ||
|
|
1657
|
-
text.includes("type mismatch"));
|
|
1658
|
-
}
|
|
1659
|
-
function isConnectionRecoveryRunError(info) {
|
|
1660
|
-
const code = (info.errorCode ?? "").toLowerCase();
|
|
1661
|
-
const message = info.message.toLowerCase();
|
|
1662
|
-
return (code === "connection_error" ||
|
|
1663
|
-
message.includes("connection kept failing") ||
|
|
1664
|
-
message.includes("automatic recovery attempts"));
|
|
1665
|
-
}
|
|
1666
154
|
function getMessageText(message) {
|
|
1667
155
|
const msg = message?.message ?? message;
|
|
1668
156
|
const content = msg?.content;
|
|
@@ -1754,165 +242,6 @@ export function latestNonRecoveryUserMessageText(messages) {
|
|
|
1754
242
|
}
|
|
1755
243
|
return "";
|
|
1756
244
|
}
|
|
1757
|
-
function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDismiss, }) {
|
|
1758
|
-
const [detailsOpen, setDetailsOpen] = useState(false);
|
|
1759
|
-
const [copied, setCopied] = useState(false);
|
|
1760
|
-
const [forking, setForking] = useState(false);
|
|
1761
|
-
const [forkError, setForkError] = useState(null);
|
|
1762
|
-
const builderReconnect = useBuilderConnectFlow({
|
|
1763
|
-
trackingSource: "assistant_chat_reconnect_error",
|
|
1764
|
-
});
|
|
1765
|
-
const canRecover = info.recoverable === true;
|
|
1766
|
-
const shouldShowBuilderReconnect = isBuilderReconnectRunError(info);
|
|
1767
|
-
const builderReconnectResolved = shouldShowBuilderReconnect &&
|
|
1768
|
-
builderReconnect.hasFetchedStatus &&
|
|
1769
|
-
builderReconnect.configured;
|
|
1770
|
-
const isQueryError = isProviderQueryRunError(info);
|
|
1771
|
-
const isConnectionRecoveryError = isConnectionRecoveryRunError(info);
|
|
1772
|
-
const copyLabel = info.runId || info.errorCode || info.details ? "Copy debug" : "Copy";
|
|
1773
|
-
const copyDetails = useCallback(() => {
|
|
1774
|
-
const text = [
|
|
1775
|
-
info.message,
|
|
1776
|
-
info.errorCode ? `Code: ${info.errorCode}` : "",
|
|
1777
|
-
info.runId ? `Run: ${info.runId}` : "",
|
|
1778
|
-
info.details ? `Details:\n${info.details}` : "",
|
|
1779
|
-
]
|
|
1780
|
-
.filter(Boolean)
|
|
1781
|
-
.join("\n\n");
|
|
1782
|
-
void writeClipboardText(text).then((ok) => {
|
|
1783
|
-
if (!ok)
|
|
1784
|
-
return;
|
|
1785
|
-
setCopied(true);
|
|
1786
|
-
setTimeout(() => setCopied(false), 1200);
|
|
1787
|
-
});
|
|
1788
|
-
}, [info]);
|
|
1789
|
-
const startNewChat = useCallback(() => {
|
|
1790
|
-
window.dispatchEvent(new CustomEvent("agent-chat:new-chat"));
|
|
1791
|
-
onDismiss();
|
|
1792
|
-
}, [onDismiss]);
|
|
1793
|
-
const handleFork = useCallback(async () => {
|
|
1794
|
-
if (!onFork || forking)
|
|
1795
|
-
return;
|
|
1796
|
-
setForking(true);
|
|
1797
|
-
setForkError(null);
|
|
1798
|
-
try {
|
|
1799
|
-
const result = await onFork();
|
|
1800
|
-
if (result === false) {
|
|
1801
|
-
setForkError("Could not fork this chat. Try starting a new chat.");
|
|
1802
|
-
}
|
|
1803
|
-
}
|
|
1804
|
-
catch {
|
|
1805
|
-
setForkError("Could not fork this chat. Try starting a new chat.");
|
|
1806
|
-
}
|
|
1807
|
-
finally {
|
|
1808
|
-
setForking(false);
|
|
1809
|
-
}
|
|
1810
|
-
}, [forking, onFork]);
|
|
1811
|
-
useEffect(() => {
|
|
1812
|
-
if (builderReconnectResolved) {
|
|
1813
|
-
onDismiss();
|
|
1814
|
-
}
|
|
1815
|
-
}, [builderReconnectResolved, onDismiss]);
|
|
1816
|
-
return (_jsxs("div", { className: "rounded-lg border border-amber-500/25 bg-amber-500/[0.06] p-3 text-sm", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-amber-500/10 text-amber-700 dark:text-amber-300", children: _jsx(IconAlertTriangle, { size: 14 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "font-medium text-foreground", children: canRecover
|
|
1817
|
-
? "The agent stopped before finishing"
|
|
1818
|
-
: "The agent hit an error" }), _jsx("p", { className: "mt-1 text-xs leading-relaxed text-muted-foreground", children: info.message }), shouldShowBuilderReconnect && !builderReconnectResolved && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: "The current Builder.io or model-provider credential was rejected. Reconnect Builder.io, then retry this message." })), isConnectionRecoveryError && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: "If retry lands on the same error, start a new chat session and continue from what already changed." })), (info.runId || info.errorCode || info.details) && (_jsxs("button", { type: "button", onClick: () => setDetailsOpen((v) => !v), className: "mt-2 inline-flex items-center gap-1 text-[11px] font-medium text-muted-foreground hover:text-foreground", children: [_jsx(IconChevronDown, { size: 12, className: cn("transition-transform", detailsOpen && "rotate-180") }), "Details"] })), detailsOpen && (_jsxs("div", { className: "mt-2 rounded-md border border-border/60 bg-background/70 p-2 font-mono text-[11px] leading-relaxed text-muted-foreground", children: [info.runId && _jsxs("div", { children: ["run: ", info.runId] }), info.errorCode && _jsxs("div", { children: ["code: ", info.errorCode] }), info.details && (_jsx("pre", { className: "mt-2 max-h-28 overflow-auto whitespace-pre-wrap break-words font-mono", children: info.details }))] }))] }), _jsx("button", { type: "button", onClick: onDismiss, "aria-label": "Dismiss", className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-background/80 hover:text-foreground", children: _jsx(IconX, { size: 14 }) })] }), _jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2", children: [shouldShowBuilderReconnect && !builderReconnectResolved && (_jsxs("button", { type: "button", onClick: () => builderReconnect.start(), disabled: builderReconnect.connecting, className: "inline-flex h-8 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90 disabled:cursor-wait disabled:opacity-70", children: [builderReconnect.connecting ? (_jsx(IconLoader2, { size: 13, className: "animate-spin" })) : (_jsx(IconExternalLink, { size: 13 })), builderReconnect.connecting
|
|
1819
|
-
? "Connecting Builder.io"
|
|
1820
|
-
: "Reconnect Builder.io"] })), canRecover && (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", onClick: onContinue, className: "inline-flex h-8 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90", children: [_jsx(IconPlayerPlay, { size: 13 }), "Continue"] }), _jsxs("button", { type: "button", onClick: onRetry, className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent", children: [_jsx(IconRefresh, { size: 13 }), isQueryError ? "Diagnose and retry" : "Retry"] })] })), canRecover && isConnectionRecoveryError && (_jsxs("button", { type: "button", onClick: startNewChat, className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent", children: [_jsx(IconPlus, { size: 13 }), "New chat"] })), canRecover && onFork && !isConnectionRecoveryError && (_jsxs("button", { type: "button", onClick: handleFork, disabled: forking, title: "Fork this conversation into a separate chat thread.", "aria-label": "Fork this conversation into a separate chat thread", className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent disabled:cursor-wait disabled:opacity-70", children: [forking ? (_jsx(IconLoader2, { size: 13, className: "animate-spin" })) : (_jsx(IconGitFork, { size: 13 })), forking ? "Forking..." : "Fork chat"] })), _jsxs("button", { type: "button", onClick: copyDetails, className: "ml-auto inline-flex h-8 items-center gap-1.5 rounded-md px-2.5 text-xs font-medium text-muted-foreground hover:bg-background/80 hover:text-foreground", children: [copied ? _jsx(IconCheck, { size: 13 }) : _jsx(IconCopy, { size: 13 }), copied ? "Copied" : copyLabel] })] }), shouldShowBuilderReconnect && builderReconnect.error && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-red-500", children: builderReconnect.error })), forkError && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-red-500", children: forkError }))] }));
|
|
1821
|
-
}
|
|
1822
|
-
function LoopLimitContinueCard({ info, onContinue, }) {
|
|
1823
|
-
const [settings, setSettings] = useState(null);
|
|
1824
|
-
const [value, setValue] = useState("");
|
|
1825
|
-
const [saving, setSaving] = useState(false);
|
|
1826
|
-
const [saved, setSaved] = useState(false);
|
|
1827
|
-
const [error, setError] = useState(null);
|
|
1828
|
-
const load = useCallback(() => {
|
|
1829
|
-
let cancelled = false;
|
|
1830
|
-
fetch(agentNativePath("/_agent-native/agent-loop-settings"))
|
|
1831
|
-
.then((r) => (r.ok ? r.json() : null))
|
|
1832
|
-
.then((data) => {
|
|
1833
|
-
if (cancelled || !data)
|
|
1834
|
-
return;
|
|
1835
|
-
setSettings(data);
|
|
1836
|
-
setValue(String(data.maxIterations));
|
|
1837
|
-
})
|
|
1838
|
-
.catch(() => {
|
|
1839
|
-
if (!cancelled)
|
|
1840
|
-
setValue(String(info.maxIterations ?? ""));
|
|
1841
|
-
});
|
|
1842
|
-
return () => {
|
|
1843
|
-
cancelled = true;
|
|
1844
|
-
};
|
|
1845
|
-
}, [info.maxIterations]);
|
|
1846
|
-
useEffect(() => load(), [load]);
|
|
1847
|
-
const currentLimit = settings?.maxIterations ?? info.maxIterations;
|
|
1848
|
-
const numericValue = Number(value);
|
|
1849
|
-
const hasPendingChange = !!settings &&
|
|
1850
|
-
settings.canUpdate &&
|
|
1851
|
-
Number.isInteger(numericValue) &&
|
|
1852
|
-
numericValue !== settings.maxIterations;
|
|
1853
|
-
const scopeLabel = settings?.scope === "org"
|
|
1854
|
-
? settings.orgName
|
|
1855
|
-
? `${settings.orgName} org`
|
|
1856
|
-
: "org"
|
|
1857
|
-
: "your account";
|
|
1858
|
-
const saveLimit = useCallback(async () => {
|
|
1859
|
-
if (!settings?.canUpdate)
|
|
1860
|
-
return false;
|
|
1861
|
-
setSaving(true);
|
|
1862
|
-
setSaved(false);
|
|
1863
|
-
setError(null);
|
|
1864
|
-
try {
|
|
1865
|
-
const res = await fetch(agentNativePath("/_agent-native/agent-loop-settings"), {
|
|
1866
|
-
method: "PUT",
|
|
1867
|
-
headers: { "Content-Type": "application/json" },
|
|
1868
|
-
body: JSON.stringify({ maxIterations: numericValue }),
|
|
1869
|
-
});
|
|
1870
|
-
const body = await res.json().catch(() => ({}));
|
|
1871
|
-
if (!res.ok) {
|
|
1872
|
-
throw new Error(body?.error ?? `Save failed (${res.status})`);
|
|
1873
|
-
}
|
|
1874
|
-
setSettings(body);
|
|
1875
|
-
setValue(String(body.maxIterations));
|
|
1876
|
-
setSaved(true);
|
|
1877
|
-
window.dispatchEvent(new CustomEvent("agent-loop-settings:changed", { detail: body }));
|
|
1878
|
-
setTimeout(() => setSaved(false), 2000);
|
|
1879
|
-
return true;
|
|
1880
|
-
}
|
|
1881
|
-
catch (err) {
|
|
1882
|
-
setError(err instanceof Error ? err.message : "Save failed");
|
|
1883
|
-
return false;
|
|
1884
|
-
}
|
|
1885
|
-
finally {
|
|
1886
|
-
setSaving(false);
|
|
1887
|
-
}
|
|
1888
|
-
}, [numericValue, settings?.canUpdate]);
|
|
1889
|
-
const handleContinue = useCallback(async () => {
|
|
1890
|
-
if (hasPendingChange) {
|
|
1891
|
-
const ok = await saveLimit();
|
|
1892
|
-
if (!ok)
|
|
1893
|
-
return;
|
|
1894
|
-
}
|
|
1895
|
-
onContinue();
|
|
1896
|
-
}, [hasPendingChange, onContinue, saveLimit]);
|
|
1897
|
-
const openSettings = useCallback(() => {
|
|
1898
|
-
try {
|
|
1899
|
-
window.location.hash = "agent-limits";
|
|
1900
|
-
}
|
|
1901
|
-
catch { }
|
|
1902
|
-
window.dispatchEvent(new CustomEvent("agent-panel:open-settings"));
|
|
1903
|
-
}, []);
|
|
1904
|
-
return (_jsxs("div", { className: "rounded-lg border border-amber-500/25 bg-amber-500/[0.06] px-3 py-3 shadow-sm", children: [_jsxs("div", { className: "flex items-start gap-2.5", children: [_jsx("span", { className: "mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-amber-500/10 text-amber-600 dark:text-amber-400", children: _jsx(IconGauge, { size: 14 }) }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-sm font-medium text-foreground", children: "Step limit reached" }), _jsxs("p", { className: "mt-0.5 text-xs leading-relaxed text-muted-foreground", children: ["The agent used", " ", currentLimit
|
|
1905
|
-
? `${currentLimit.toLocaleString()} steps`
|
|
1906
|
-
: "all available steps", ". Keep going in a fresh turn, or raise the ", scopeLabel, " limit first."] })] })] }), _jsxs("div", { className: "mt-3 flex flex-wrap items-end gap-2", children: [_jsxs("label", { className: "min-w-[116px] flex-1 space-y-1", children: [_jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: "Max steps" }), _jsx("input", { type: "number", min: settings?.minMaxIterations ?? 1, max: settings?.maxMaxIterations ?? 1000, value: value, disabled: !settings?.canUpdate || saving, onChange: (e) => {
|
|
1907
|
-
setValue(e.target.value);
|
|
1908
|
-
setError(null);
|
|
1909
|
-
}, className: "h-8 w-full rounded-md border border-border bg-background px-2 text-xs text-foreground outline-none focus:ring-1 focus:ring-ring disabled:opacity-60" })] }), _jsx("button", { type: "button", onClick: saveLimit, disabled: !hasPendingChange || saving, className: "inline-flex h-8 items-center gap-1 rounded-md border border-border px-2.5 text-xs font-medium text-foreground hover:bg-accent disabled:opacity-50", children: saving ? (_jsx(IconLoader2, { size: 12, className: "animate-spin" })) : saved ? (_jsx(IconCheck, { size: 12 })) : ("Save") }), _jsxs("button", { type: "button", onClick: openSettings, className: "inline-flex h-8 items-center gap-1 rounded-md border border-border px-2.5 text-xs font-medium text-muted-foreground hover:bg-accent hover:text-foreground", children: [_jsx(IconSettings, { size: 12 }), "Settings"] }), _jsxs("button", { type: "button", onClick: handleContinue, disabled: saving, className: "ml-auto inline-flex h-8 items-center gap-1 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90 disabled:opacity-60", children: [hasPendingChange ? "Save and keep going" : "Keep going", _jsx(IconArrowRight, { size: 12 })] })] }), settings && !settings.canUpdate && (_jsx("p", { className: "mt-2 text-[11px] text-muted-foreground", children: "Only organization owners and admins can change this limit." })), error && _jsx("p", { className: "mt-2 text-[11px] text-destructive", children: error })] }));
|
|
1910
|
-
}
|
|
1911
|
-
function PlanModeCallout({ canImplementPlan, onImplementPlan, onSwitchToAct, }) {
|
|
1912
|
-
return (_jsx("div", { className: "shrink-0 px-3 pt-2", children: _jsx("div", { className: "rounded-lg border border-blue-500/25 bg-blue-500/[0.06] px-3 py-2.5 shadow-sm", children: _jsxs("div", { className: "flex items-center gap-2.5", children: [_jsx("span", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-blue-500/10 text-blue-600 dark:text-blue-300", children: _jsx(IconClipboardList, { size: 15 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-sm font-medium text-foreground", children: canImplementPlan ? "Plan ready" : "Plan mode is on" }), _jsx("p", { className: "mt-0.5 text-xs leading-relaxed text-muted-foreground", children: canImplementPlan
|
|
1913
|
-
? "Switch to Act and run the proposed plan."
|
|
1914
|
-
: "The next turn will stay read-only until you switch to Act." })] }), canImplementPlan ? (_jsxs("button", { type: "button", onClick: onImplementPlan, className: "inline-flex h-8 shrink-0 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90", children: [_jsx(IconPlayerPlay, { size: 13 }), "Implement Plan"] })) : (_jsxs("button", { type: "button", onClick: onSwitchToAct, className: "inline-flex h-8 shrink-0 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent", children: ["Act", _jsx(IconArrowRight, { size: 13 })] }))] }) }) }));
|
|
1915
|
-
}
|
|
1916
245
|
export const CHAT_STORAGE_PREFIX = "agent-chat:";
|
|
1917
246
|
/** Remove persisted chat for a given tabId (or "default"). */
|
|
1918
247
|
export function clearChatStorage(tabId) {
|
|
@@ -1956,7 +285,73 @@ function ensureMessageMetadata(repo) {
|
|
|
1956
285
|
// Re-export for backwards compatibility
|
|
1957
286
|
import { extractThreadMeta, normalizeThreadRepository, } from "../agent/thread-data-builder.js";
|
|
1958
287
|
export { extractThreadMeta };
|
|
1959
|
-
|
|
288
|
+
/**
|
|
289
|
+
* Strip raw base64 payload from attachment content parts when a hosted URL
|
|
290
|
+
* already exists in the same content entry. This keeps the periodic thread
|
|
291
|
+
* save payload compact — the server already stored the URL reference when it
|
|
292
|
+
* processed the POST, and re-shipping multi-megabyte base64 strings on every
|
|
293
|
+
* 5-second poll save balloons the SQL thread_data column unnecessarily.
|
|
294
|
+
*
|
|
295
|
+
* Only strips the raw base64 data-URL string from `content[].image` / `content[].data`
|
|
296
|
+
* when a `metadata.uploadUrl` reference is present on the same attachment object,
|
|
297
|
+
* so the transcript can still render from the hosted URL after hydration.
|
|
298
|
+
*/
|
|
299
|
+
function stripBase64FromRepo(repo) {
|
|
300
|
+
if (!repo || typeof repo !== "object")
|
|
301
|
+
return repo;
|
|
302
|
+
const r = repo;
|
|
303
|
+
if (!Array.isArray(r.messages))
|
|
304
|
+
return repo;
|
|
305
|
+
const messages = r.messages.map((entry) => {
|
|
306
|
+
if (!entry || typeof entry !== "object")
|
|
307
|
+
return entry;
|
|
308
|
+
const e = entry;
|
|
309
|
+
const msg = (e.message ?? e);
|
|
310
|
+
if (!msg || typeof msg !== "object")
|
|
311
|
+
return entry;
|
|
312
|
+
const attachments = msg.attachments;
|
|
313
|
+
if (!Array.isArray(attachments))
|
|
314
|
+
return entry;
|
|
315
|
+
const strippedAttachments = attachments.map((att) => {
|
|
316
|
+
if (!att || typeof att !== "object")
|
|
317
|
+
return att;
|
|
318
|
+
const a = att;
|
|
319
|
+
const meta = a.metadata;
|
|
320
|
+
// Only strip when we have a hosted upload URL confirmed by the server.
|
|
321
|
+
if (!meta?.uploadUrl)
|
|
322
|
+
return att;
|
|
323
|
+
if (!Array.isArray(a.content))
|
|
324
|
+
return att;
|
|
325
|
+
const strippedContent = a.content.map((part) => {
|
|
326
|
+
if (!part || typeof part !== "object")
|
|
327
|
+
return part;
|
|
328
|
+
const p = part;
|
|
329
|
+
// Replace the raw base64 image data-URL with the hosted URL.
|
|
330
|
+
if (p.type === "image" &&
|
|
331
|
+
typeof p.image === "string" &&
|
|
332
|
+
p.image.startsWith("data:")) {
|
|
333
|
+
return { ...p, image: meta.uploadUrl };
|
|
334
|
+
}
|
|
335
|
+
// Replace the raw base64 file data with a stripped marker.
|
|
336
|
+
if (p.type === "file" &&
|
|
337
|
+
typeof p.data === "string" &&
|
|
338
|
+
p.data.startsWith("data:")) {
|
|
339
|
+
const { data: _d, ...rest } = p;
|
|
340
|
+
return { ...rest, url: meta.uploadUrl };
|
|
341
|
+
}
|
|
342
|
+
return part;
|
|
343
|
+
});
|
|
344
|
+
return { ...a, content: strippedContent };
|
|
345
|
+
});
|
|
346
|
+
const strippedMsg = { ...msg, attachments: strippedAttachments };
|
|
347
|
+
if (e.message !== undefined) {
|
|
348
|
+
return { ...e, message: strippedMsg };
|
|
349
|
+
}
|
|
350
|
+
return strippedMsg;
|
|
351
|
+
});
|
|
352
|
+
return { ...r, messages };
|
|
353
|
+
}
|
|
354
|
+
const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateText, suggestions, dynamicSuggestions, emptyStateAddon, showHeader = true, onSwitchToCli, className, apiUrl, tabId, browserTabId, threadId, contextScope, isActiveComposer = true, onMessageCountChange, onSaveThread, onGenerateTitle, composerSlot, composerAreaClassName, composerPlaceholder, composerLayoutVariant = "default", centerComposerWhenEmpty = false, emptyStateDisplay = "default", composerToolbarSlot, composerExtraActionButton, composerDisabled = false, composerDisabledPlaceholder, isNewThread, onSlashCommand, execMode, onExecModeChange, planModeDisabled, planModeDisabledReason, selectedModel, defaultModel, selectedEffort, availableModels, onModelChange, onEffortChange, onForkChat, onConnectProvider, plusMenuMode = "full", providerStatusChecksEnabled = true, loadHistoryRepository, historyReloadKey, externalStreaming = false, }, ref) {
|
|
1960
355
|
const thread = useThread();
|
|
1961
356
|
const threadRuntime = useThreadRuntime();
|
|
1962
357
|
const composerRuntime = useComposerRuntime();
|
|
@@ -1977,6 +372,10 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
1977
372
|
// attachment strip otherwise navigate to the file (browser default), which
|
|
1978
373
|
// is why "upload does nothing" — the chat refreshes to the dropped image.
|
|
1979
374
|
const [dropActive, setDropActive] = useState(false);
|
|
375
|
+
// Inline error shown just above the composer for attachment failures
|
|
376
|
+
// (unsupported format, size cap, body-size rejection, drop errors).
|
|
377
|
+
// Cleared on the next message send.
|
|
378
|
+
const [composerError, setComposerError] = useState(null);
|
|
1980
379
|
const dropDepthRef = useRef(0);
|
|
1981
380
|
const handleChatDragEnter = useCallback((e) => {
|
|
1982
381
|
if (!Array.from(e.dataTransfer?.types ?? []).includes("Files"))
|
|
@@ -2025,9 +424,12 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
2025
424
|
return new File([file], uniqueName, { type: file.type });
|
|
2026
425
|
});
|
|
2027
426
|
void Promise.all(attachments.map((file) => composerRuntime.addAttachment(file))).catch((error) => {
|
|
2028
|
-
|
|
427
|
+
const msg = error instanceof Error
|
|
428
|
+
? error.message
|
|
429
|
+
: "Could not add the dropped file. Try a different format.";
|
|
430
|
+
setComposerError(msg);
|
|
2029
431
|
});
|
|
2030
|
-
}, [composerRuntime]);
|
|
432
|
+
}, [composerRuntime, setComposerError]);
|
|
2031
433
|
// Patch the underlying assistant-ui MessageRepository so addOrUpdateMessage
|
|
2032
434
|
// can't throw "Parent message not found" mid-run. assistant-ui calls
|
|
2033
435
|
// `repository.clear()` from `runtime.import()` and from `resetHead(null)`,
|
|
@@ -2153,6 +555,11 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
2153
555
|
const [dismissedRunErrorKey, setDismissedRunErrorKey] = useState(null);
|
|
2154
556
|
const userStoppedRunRef = useRef(null);
|
|
2155
557
|
const [isReconnecting, setIsReconnecting] = useState(false);
|
|
558
|
+
// Last activity label emitted by agent-chat:activity events (tool name / step label).
|
|
559
|
+
const [activityLabel, setActivityLabel] = useState(null);
|
|
560
|
+
// True during the 250ms continuation window and startup of the next chunk
|
|
561
|
+
// (adapter's auto-continue delay before POSTing the next chunk).
|
|
562
|
+
const [isAutoResuming, setIsAutoResuming] = useState(false);
|
|
2156
563
|
const [reconnectContent, setReconnectContent] = useState([]);
|
|
2157
564
|
// When stop is clicked during reconnect, keep content visible (don't wipe it)
|
|
2158
565
|
const [reconnectFrozen, setReconnectFrozen] = useState(false);
|
|
@@ -2173,6 +580,9 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
2173
580
|
const wasRunningRef = useRef(false);
|
|
2174
581
|
const lastBroadcastRunningRef = useRef(isRunning);
|
|
2175
582
|
const tiptapRef = useRef(null);
|
|
583
|
+
// Stable ref to the "stop active run" action so addToQueue can abort
|
|
584
|
+
// a running turn without adding many unstable closure deps to its dep list.
|
|
585
|
+
const stopActiveRunRef = useRef(() => { });
|
|
2176
586
|
useEffect(() => {
|
|
2177
587
|
if (lastBroadcastRunningRef.current === isRunning)
|
|
2178
588
|
return;
|
|
@@ -2656,7 +1066,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
2656
1066
|
lastSaveTimeRef.current = now;
|
|
2657
1067
|
savedTitleRef.current = title;
|
|
2658
1068
|
onSaveThreadRef.current(threadId, {
|
|
2659
|
-
threadData: JSON.stringify(repo),
|
|
1069
|
+
threadData: JSON.stringify(stripBase64FromRepo(repo)),
|
|
2660
1070
|
title,
|
|
2661
1071
|
preview,
|
|
2662
1072
|
messageCount: messages.length,
|
|
@@ -2676,7 +1086,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
2676
1086
|
const { title, preview } = extractThreadMeta(repo);
|
|
2677
1087
|
savedTitleRef.current = title;
|
|
2678
1088
|
onSaveThreadRef.current(threadId, {
|
|
2679
|
-
threadData: JSON.stringify(repo),
|
|
1089
|
+
threadData: JSON.stringify(stripBase64FromRepo(repo)),
|
|
2680
1090
|
title,
|
|
2681
1091
|
preview,
|
|
2682
1092
|
messageCount: messages.length,
|
|
@@ -2884,6 +1294,52 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
2884
1294
|
window.addEventListener("agent-chat:run-error", handler);
|
|
2885
1295
|
return () => window.removeEventListener("agent-chat:run-error", handler);
|
|
2886
1296
|
}, [tabId]);
|
|
1297
|
+
// Track the most recent activity label for the running indicator.
|
|
1298
|
+
useEffect(() => {
|
|
1299
|
+
const handler = (e) => {
|
|
1300
|
+
const detail = e.detail;
|
|
1301
|
+
if (tabId && detail?.tabId && detail.tabId !== tabId)
|
|
1302
|
+
return;
|
|
1303
|
+
if (typeof detail?.label === "string" && detail.label.trim()) {
|
|
1304
|
+
setActivityLabel(detail.label.trim());
|
|
1305
|
+
setIsAutoResuming(false);
|
|
1306
|
+
}
|
|
1307
|
+
};
|
|
1308
|
+
window.addEventListener("agent-chat:activity", handler);
|
|
1309
|
+
return () => window.removeEventListener("agent-chat:activity", handler);
|
|
1310
|
+
}, [tabId]);
|
|
1311
|
+
// Clear the activity label when the server clears a corrective draft.
|
|
1312
|
+
useEffect(() => {
|
|
1313
|
+
const handler = (e) => {
|
|
1314
|
+
const detail = e.detail;
|
|
1315
|
+
if (tabId && detail?.tabId && detail.tabId !== tabId)
|
|
1316
|
+
return;
|
|
1317
|
+
setActivityLabel(null);
|
|
1318
|
+
};
|
|
1319
|
+
window.addEventListener("agent-chat:activity-clear", handler);
|
|
1320
|
+
return () => window.removeEventListener("agent-chat:activity-clear", handler);
|
|
1321
|
+
}, [tabId]);
|
|
1322
|
+
// Show "Resuming…" during the adapter's auto-continuation window (the
|
|
1323
|
+
// ~250ms gap between the end of one serverless chunk and the POST for the
|
|
1324
|
+
// next). The adapter dispatches `agent-chat:auto-continue` at that moment.
|
|
1325
|
+
useEffect(() => {
|
|
1326
|
+
const handler = (e) => {
|
|
1327
|
+
const detail = e.detail;
|
|
1328
|
+
if (tabId && detail?.tabId && detail.tabId !== tabId)
|
|
1329
|
+
return;
|
|
1330
|
+
setIsAutoResuming(true);
|
|
1331
|
+
setActivityLabel(null);
|
|
1332
|
+
};
|
|
1333
|
+
window.addEventListener("agent-chat:auto-continue", handler);
|
|
1334
|
+
return () => window.removeEventListener("agent-chat:auto-continue", handler);
|
|
1335
|
+
}, [tabId]);
|
|
1336
|
+
// Clear auto-resume / activity label when the run stops.
|
|
1337
|
+
useEffect(() => {
|
|
1338
|
+
if (!isRunning) {
|
|
1339
|
+
setIsAutoResuming(false);
|
|
1340
|
+
setActivityLabel(null);
|
|
1341
|
+
}
|
|
1342
|
+
}, [isRunning]);
|
|
2887
1343
|
// Auto-dequeue: when agent finishes running, send the next queued message
|
|
2888
1344
|
useEffect(() => {
|
|
2889
1345
|
if (wasRunningRef.current && !isRunning && queuedMessages.length > 0) {
|
|
@@ -3003,12 +1459,55 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
3003
1459
|
threadId,
|
|
3004
1460
|
threadRuntime,
|
|
3005
1461
|
]);
|
|
1462
|
+
// Abort the active server run (identical to what the Stop button does) so
|
|
1463
|
+
// an immediate-while-running send can proceed cleanly without a 409 race.
|
|
1464
|
+
// Captured in a stable ref so addToQueue can call it without listing
|
|
1465
|
+
// all the stop-related state in its own dep array.
|
|
1466
|
+
const stopActiveRun = useCallback(() => {
|
|
1467
|
+
setForceStopped(true);
|
|
1468
|
+
const activeRun = getActiveRun();
|
|
1469
|
+
const runIdToAbort = reconnectRunIdRef.current ?? activeRun?.runId;
|
|
1470
|
+
userStoppedRunRef.current = {
|
|
1471
|
+
at: Date.now(),
|
|
1472
|
+
...(runIdToAbort ? { runId: runIdToAbort } : {}),
|
|
1473
|
+
};
|
|
1474
|
+
setRunErrorInfo(null);
|
|
1475
|
+
setDismissedRunErrorKey(null);
|
|
1476
|
+
if (runIdToAbort) {
|
|
1477
|
+
fetch(`${apiUrl}/runs/${encodeURIComponent(runIdToAbort)}/abort`, {
|
|
1478
|
+
method: "POST",
|
|
1479
|
+
}).catch(() => { });
|
|
1480
|
+
}
|
|
1481
|
+
if (isReconnecting) {
|
|
1482
|
+
reconnectAbortRef.current?.abort();
|
|
1483
|
+
reconnectAbortRef.current = null;
|
|
1484
|
+
reconnectRunIdRef.current = null;
|
|
1485
|
+
setIsReconnecting(false);
|
|
1486
|
+
setReconnectFrozen(reconnectContent.length > 0);
|
|
1487
|
+
}
|
|
1488
|
+
threadRuntime.cancelRun();
|
|
1489
|
+
if (typeof window !== "undefined") {
|
|
1490
|
+
window.dispatchEvent(new CustomEvent("agentNative.chatRunning", {
|
|
1491
|
+
detail: { isRunning: false, tabId: tabId || threadId },
|
|
1492
|
+
}));
|
|
1493
|
+
}
|
|
1494
|
+
}, [
|
|
1495
|
+
apiUrl,
|
|
1496
|
+
isReconnecting,
|
|
1497
|
+
reconnectContent.length,
|
|
1498
|
+
tabId,
|
|
1499
|
+
threadId,
|
|
1500
|
+
threadRuntime,
|
|
1501
|
+
]);
|
|
1502
|
+
// Keep the ref current so addToQueue can call it without a stale closure.
|
|
1503
|
+
stopActiveRunRef.current = stopActiveRun;
|
|
3006
1504
|
const addToQueue = useCallback(async (text, images, references, attachments, requestMode, intent = "queued", recoveryAction, includeComposerContext = false, trackInRunsTray = false) => {
|
|
3007
1505
|
materializeFrozenReconnectContent();
|
|
3008
1506
|
setShowContinue(false);
|
|
3009
1507
|
setLoopLimitInfo(null);
|
|
3010
1508
|
setRunErrorInfo(null);
|
|
3011
1509
|
setDismissedRunErrorKey(null);
|
|
1510
|
+
setComposerError(null);
|
|
3012
1511
|
userStoppedRunRef.current = null;
|
|
3013
1512
|
// Selection context attached via Cmd+I is one-shot — clear it as soon
|
|
3014
1513
|
// as the user actually sends a message so it can't be re-used.
|
|
@@ -3023,12 +1522,92 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
3023
1522
|
? buildComposerContextSubmission(text)
|
|
3024
1523
|
: { text, includesContext: false };
|
|
3025
1524
|
const submittedText = submitted.text;
|
|
3026
|
-
|
|
1525
|
+
let queuedAttachments;
|
|
1526
|
+
try {
|
|
1527
|
+
queuedAttachments = await serializeQueuedAttachments(attachments);
|
|
1528
|
+
}
|
|
1529
|
+
catch (err) {
|
|
1530
|
+
const msg = err instanceof Error
|
|
1531
|
+
? err.message
|
|
1532
|
+
: "Attachment could not be processed.";
|
|
1533
|
+
setComposerError(msg);
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
3027
1536
|
const imageAttachments = createAgentImageAttachments(images);
|
|
3028
|
-
const
|
|
1537
|
+
const allAttachments = [
|
|
3029
1538
|
...(queuedAttachments ?? []),
|
|
3030
1539
|
...(imageAttachments ?? []),
|
|
3031
1540
|
];
|
|
1541
|
+
// ── Body-size guard (Fix 3) ─────────────────────────────────────
|
|
1542
|
+
// Estimate the total serialized attachment payload. If it exceeds the
|
|
1543
|
+
// Vercel/Netlify body limit, progressively re-compress images until
|
|
1544
|
+
// the payload fits, then reject the largest remaining file if still over.
|
|
1545
|
+
let messageAttachments = allAttachments;
|
|
1546
|
+
{
|
|
1547
|
+
const allDataUrls = allAttachments.flatMap((a) => a.content
|
|
1548
|
+
.filter((c) => c.type === "image")
|
|
1549
|
+
.map((c) => c.image));
|
|
1550
|
+
if (estimateAttachmentBodyBytes(allDataUrls) > MAX_ESTIMATED_BODY_BYTES) {
|
|
1551
|
+
// Re-compress image attachments more aggressively.
|
|
1552
|
+
const recompressed = [];
|
|
1553
|
+
let stillOver = false;
|
|
1554
|
+
for (const att of allAttachments) {
|
|
1555
|
+
if (att.type === "image" &&
|
|
1556
|
+
att.content.length === 1 &&
|
|
1557
|
+
att.content[0].type === "image") {
|
|
1558
|
+
// Find the original File from the queued attachments input.
|
|
1559
|
+
const rawAtt = (attachments ?? []).find((r) => r.id === att.id);
|
|
1560
|
+
const rawFile = rawAtt?.file;
|
|
1561
|
+
if (rawFile && typeof document !== "undefined") {
|
|
1562
|
+
try {
|
|
1563
|
+
const recompressedUrl = await transcodeImageToDataURL(rawFile, {
|
|
1564
|
+
maxDimension: AGGRESSIVE_MAX_IMAGE_DIMENSION,
|
|
1565
|
+
jpegQuality: AGGRESSIVE_JPEG_QUALITY,
|
|
1566
|
+
});
|
|
1567
|
+
recompressed.push({
|
|
1568
|
+
...att,
|
|
1569
|
+
content: [{ type: "image", image: recompressedUrl }],
|
|
1570
|
+
});
|
|
1571
|
+
continue;
|
|
1572
|
+
}
|
|
1573
|
+
catch {
|
|
1574
|
+
// Could not recompress — keep original and flag overflow
|
|
1575
|
+
stillOver = true;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
else {
|
|
1579
|
+
stillOver = true;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
recompressed.push(att);
|
|
1583
|
+
}
|
|
1584
|
+
// Re-estimate after recompression.
|
|
1585
|
+
const recompressedUrls = recompressed.flatMap((a) => a.content
|
|
1586
|
+
.filter((c) => c.type === "image")
|
|
1587
|
+
.map((c) => c.image));
|
|
1588
|
+
if (stillOver ||
|
|
1589
|
+
estimateAttachmentBodyBytes(recompressedUrls) >
|
|
1590
|
+
MAX_ESTIMATED_BODY_BYTES) {
|
|
1591
|
+
// Find the largest attachment and reject it.
|
|
1592
|
+
let largestIdx = -1;
|
|
1593
|
+
let largestSize = 0;
|
|
1594
|
+
for (let i = 0; i < recompressed.length; i++) {
|
|
1595
|
+
const url = recompressed[i].content.find((c) => c.type === "image")?.image ?? "";
|
|
1596
|
+
if (url.length > largestSize) {
|
|
1597
|
+
largestSize = url.length;
|
|
1598
|
+
largestIdx = i;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
if (largestIdx >= 0) {
|
|
1602
|
+
const rejected = recompressed[largestIdx];
|
|
1603
|
+
setComposerError(`"${rejected.name}" makes the message too large to send (combined attachments must be under ${Math.round(MAX_ESTIMATED_BODY_BYTES / 1024 / 1024)} MB). Remove it or use a smaller image.`);
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
messageAttachments = recompressed;
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
// ── End body-size guard ──────────────────────────────────────────
|
|
3032
1611
|
// Snapshot the exec mode at enqueue time when the caller didn't
|
|
3033
1612
|
// pass an explicit override. Without this, a plan-mode message that
|
|
3034
1613
|
// sits in the queue runs as 'act' if the user flips the global toggle
|
|
@@ -3039,7 +1618,35 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
3039
1618
|
: execMode === "build"
|
|
3040
1619
|
? "act"
|
|
3041
1620
|
: undefined);
|
|
3042
|
-
if (isRunning && intent === "
|
|
1621
|
+
if (isRunning && intent === "immediate") {
|
|
1622
|
+
// Mid-run Enter race fix: immediately abort the active server run,
|
|
1623
|
+
// wait for it to clear, then send — mirroring what the auto-dequeue
|
|
1624
|
+
// path already does safely. Without this, assistant-ui's append()
|
|
1625
|
+
// would cancel the adapter run locally but the server run would keep
|
|
1626
|
+
// going; the new POST would then 409, reconnect to the OLD run, and
|
|
1627
|
+
// replay the old answer under the new prompt.
|
|
1628
|
+
setQueuedMessages((prev) => [
|
|
1629
|
+
...prev,
|
|
1630
|
+
{
|
|
1631
|
+
id: typeof crypto !== "undefined" && crypto.randomUUID
|
|
1632
|
+
? crypto.randomUUID()
|
|
1633
|
+
: `q-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
1634
|
+
text: submittedText,
|
|
1635
|
+
images,
|
|
1636
|
+
attachments: messageAttachments.length > 0 ? messageAttachments : undefined,
|
|
1637
|
+
references,
|
|
1638
|
+
requestMode: effectiveRequestMode,
|
|
1639
|
+
recoveryAction,
|
|
1640
|
+
trackInRunsTray,
|
|
1641
|
+
},
|
|
1642
|
+
]);
|
|
1643
|
+
// Abort the server run (same as Stop button). This flips forceStopped
|
|
1644
|
+
// → isRunning=false → auto-dequeue fires → waitForThreadRunToClear →
|
|
1645
|
+
// append. The abort is fire-and-forget; waitForThreadRunToClear does
|
|
1646
|
+
// the actual wait.
|
|
1647
|
+
stopActiveRunRef.current();
|
|
1648
|
+
}
|
|
1649
|
+
else if (isRunning && intent === "queued") {
|
|
3043
1650
|
setQueuedMessages((prev) => [
|
|
3044
1651
|
...prev,
|
|
3045
1652
|
{
|
|
@@ -3305,7 +1912,11 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
3305
1912
|
? { skipLabel: guidedQuestionsSkipLabel }
|
|
3306
1913
|
: {}), ...(guidedQuestionsSubmitLabel
|
|
3307
1914
|
? { submitLabel: guidedQuestionsSubmitLabel }
|
|
3308
|
-
: {}), className: "h-auto items-stretch justify-stretch bg-transparent" }) }) })), showPlanModeCallout && (_jsx(PlanModeCallout, { canImplementPlan: canImplementPlan, onImplementPlan: handleImplementPlan, onSwitchToAct: handleSwitchToAct })), _jsx(SelectionAttachedPill, {}), showRunningInUI && (_jsx(RunningActivityStatus, { label: isReconnecting
|
|
1915
|
+
: {}), className: "h-auto items-stretch justify-stretch bg-transparent" }) }) })), showPlanModeCallout && (_jsx(PlanModeCallout, { canImplementPlan: canImplementPlan, onImplementPlan: handleImplementPlan, onSwitchToAct: handleSwitchToAct })), _jsx(SelectionAttachedPill, {}), showRunningInUI && (_jsx(RunningActivityStatus, { label: isReconnecting
|
|
1916
|
+
? "Reconnecting"
|
|
1917
|
+
: isAutoResuming
|
|
1918
|
+
? "Resuming"
|
|
1919
|
+
: (activityLabel ?? "Thinking") })), composerError && (_jsxs("div", { role: "alert", className: "shrink-0 mx-3 mb-1.5 flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: [_jsx(IconAlertTriangle, { className: "h-3.5 w-3.5 mt-0.5 shrink-0" }), _jsx("span", { className: "flex-1 leading-snug", children: composerError }), _jsx("button", { type: "button", "aria-label": "Dismiss error", onClick: () => setComposerError(null), className: "shrink-0 opacity-70 hover:opacity-100", children: _jsx(IconX, { className: "h-3 w-3" }) })] })), _jsxs(AgentComposerFrame, { layoutVariant: composerLayoutVariant, className: cn(composerAreaClassName, missingApiKey && "cursor-pointer", isComposerDisabled && "opacity-70"), onClick: missingApiKey
|
|
3309
1920
|
? () => setMissingKeyBouncePulse((p) => p + 1)
|
|
3310
1921
|
: undefined, children: [_jsx(ComposerAttachmentPreviewStrip, {}), _jsx(TiptapComposer, { focusRef: tiptapRef, disabled: isComposerDisabled, placeholder: missingApiKey
|
|
3311
1922
|
? "Connect an AI engine above to start chatting…"
|
|
@@ -3318,42 +1929,9 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
3318
1929
|
: "Send a follow-up..."
|
|
3319
1930
|
: composerPlaceholder, onSubmit: isRunning || composerContextItems.length > 0
|
|
3320
1931
|
? (text, references, attachments, options) => void addToQueue(text, undefined, references.length > 0 ? references : undefined, attachments, undefined, options?.intent ?? "immediate", undefined, true)
|
|
3321
|
-
: undefined, onSlashCommand: onSlashCommand, execMode: execMode, onExecModeChange: onExecModeChange, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason, selectedModel: selectedModel ?? defaultModel, selectedEffort: selectedEffort, availableModels: availableModels, onModelChange: onModelChange, onEffortChange: onEffortChange, onConnectProvider: onConnectProvider, toolbarSlot: composerToolbarSlot, contextItems: composerContextItems, onRemoveContextItem: removeComposerContextItem, plusMenuMode: plusMenuMode, layoutVariant: composerLayoutVariant, providerConnectStatusEnabled: providerStatusChecksEnabled, draftScope: threadId || tabId, interceptBuildRequestsForBuilder: true, extraActionButton: contextXRayEnabled ||
|
|
1932
|
+
: undefined, onSlashCommand: onSlashCommand, execMode: execMode, onExecModeChange: onExecModeChange, planModeDisabled: planModeDisabled, planModeDisabledReason: planModeDisabledReason, selectedModel: selectedModel ?? defaultModel, selectedEffort: selectedEffort, availableModels: availableModels, onModelChange: onModelChange, onEffortChange: onEffortChange, onConnectProvider: onConnectProvider, toolbarSlot: composerToolbarSlot, contextItems: composerContextItems, onRemoveContextItem: removeComposerContextItem, plusMenuMode: plusMenuMode, layoutVariant: composerLayoutVariant, providerConnectStatusEnabled: providerStatusChecksEnabled, draftScope: threadId || tabId, interceptBuildRequestsForBuilder: true, onAttachmentError: setComposerError, extraActionButton: contextXRayEnabled ||
|
|
3322
1933
|
composerExtraActionButton ||
|
|
3323
|
-
showRunningInUI ? (_jsxs(_Fragment, { children: [contextXRayEnabled && (_jsx(ContextMeter, { threadId: threadId })), composerExtraActionButton, showRunningInUI && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: ()
|
|
3324
|
-
// Nuclear stop: flip forceStopped so isRunning is false
|
|
3325
|
-
// immediately. This unblocks submission even if the
|
|
3326
|
-
// runtime or reconnect state is stuck.
|
|
3327
|
-
setForceStopped(true);
|
|
3328
|
-
const activeRun = getActiveRun();
|
|
3329
|
-
const runIdToAbort = reconnectRunIdRef.current ??
|
|
3330
|
-
activeRun?.runId;
|
|
3331
|
-
userStoppedRunRef.current = {
|
|
3332
|
-
at: Date.now(),
|
|
3333
|
-
...(runIdToAbort
|
|
3334
|
-
? { runId: runIdToAbort }
|
|
3335
|
-
: {}),
|
|
3336
|
-
};
|
|
3337
|
-
setRunErrorInfo(null);
|
|
3338
|
-
setDismissedRunErrorKey(null);
|
|
3339
|
-
if (runIdToAbort) {
|
|
3340
|
-
fetch(`${apiUrl}/runs/${encodeURIComponent(runIdToAbort)}/abort`, { method: "POST" }).catch(() => { });
|
|
3341
|
-
}
|
|
3342
|
-
if (isReconnecting) {
|
|
3343
|
-
reconnectAbortRef.current?.abort();
|
|
3344
|
-
reconnectAbortRef.current = null;
|
|
3345
|
-
reconnectRunIdRef.current = null;
|
|
3346
|
-
setIsReconnecting(false);
|
|
3347
|
-
setReconnectFrozen(reconnectContent.length > 0);
|
|
3348
|
-
}
|
|
3349
|
-
threadRuntime.cancelRun();
|
|
3350
|
-
window.dispatchEvent(new CustomEvent("agentNative.chatRunning", {
|
|
3351
|
-
detail: {
|
|
3352
|
-
isRunning: false,
|
|
3353
|
-
tabId: tabId || threadId,
|
|
3354
|
-
},
|
|
3355
|
-
}));
|
|
3356
|
-
}, className: "shrink-0 flex h-7 w-7 items-center justify-center rounded-md bg-muted text-foreground hover:bg-muted/80", children: _jsx(IconPlayerStop, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Stop generating" })] }))] })) : undefined })] })] }) }) }) }) }));
|
|
1934
|
+
showRunningInUI ? (_jsxs(_Fragment, { children: [contextXRayEnabled && (_jsx(ContextMeter, { threadId: threadId })), composerExtraActionButton, showRunningInUI && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: stopActiveRun, className: "shrink-0 flex h-7 w-7 items-center justify-center rounded-md bg-muted text-foreground hover:bg-muted/80", children: _jsx(IconPlayerStop, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Stop generating" })] }))] })) : undefined })] })] }) }) }) }) }));
|
|
3357
1935
|
});
|
|
3358
1936
|
export const AssistantChat = forwardRef(function AssistantChat({ apiUrl = agentNativePath("/_agent-native/agent-chat"), tabId, browserTabId, threadId, contextScope, isActiveComposer, ...props }, ref) {
|
|
3359
1937
|
const modelRef = useRef(props.selectedModel);
|