@agent-native/core 0.106.3 → 0.107.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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +23 -0
- package/corpus/core/docs/content/integrations.mdx +597 -0
- package/corpus/core/docs/content/locales/ar-SA/mcp-clients.mdx +19 -1
- package/corpus/core/docs/content/locales/de-DE/mcp-clients.mdx +22 -1
- package/corpus/core/docs/content/locales/es-ES/mcp-clients.mdx +23 -1
- package/corpus/core/docs/content/locales/fr-FR/mcp-clients.mdx +23 -1
- package/corpus/core/docs/content/locales/hi-IN/mcp-clients.mdx +22 -1
- package/corpus/core/docs/content/locales/ja-JP/mcp-clients.mdx +21 -1
- package/corpus/core/docs/content/locales/ko-KR/mcp-clients.mdx +20 -1
- package/corpus/core/docs/content/locales/pt-BR/mcp-clients.mdx +22 -1
- package/corpus/core/docs/content/locales/zh-CN/mcp-clients.mdx +17 -1
- package/corpus/core/docs/content/locales/zh-TW/mcp-clients.mdx +17 -1
- package/corpus/core/docs/content/mcp-clients.mdx +51 -1
- package/corpus/core/docs/content/organizations-teams-permissions.mdx +413 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +19 -0
- package/corpus/core/src/agent/run-store.ts +98 -2
- package/corpus/core/src/client/AssistantChat.tsx +26 -1
- package/corpus/core/src/client/CommandMenu.tsx +5 -0
- package/corpus/core/src/client/active-run-state.ts +34 -0
- package/corpus/core/src/client/agent-chat-adapter.ts +4 -0
- package/corpus/core/src/client/chat/message-components.tsx +41 -0
- package/corpus/core/src/client/composer/TiptapComposer.tsx +45 -8
- package/corpus/core/src/client/integrations/IntegrationsPanel.tsx +30 -11
- package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +270 -0
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +177 -40
- package/corpus/core/src/client/resources/mcp-integration-catalog.ts +478 -4
- package/corpus/core/src/client/resources/mcp-integration-logos.ts +108 -0
- package/corpus/core/src/client/sharing/ShareButton.tsx +30 -9
- package/corpus/core/src/client/use-agent-chat-home-handoff.ts +2 -2
- package/corpus/core/src/deploy/build.ts +32 -0
- package/corpus/core/src/extensions/html-shell.ts +33 -1
- package/corpus/core/src/ingestion/office.ts +1 -2
- package/corpus/core/src/ingestion/pptx.ts +2 -4
- package/corpus/core/src/localization/default-messages.ts +129 -1
- package/corpus/core/src/mcp/actions/call-mcp-tool.ts +1 -0
- package/corpus/core/src/secrets/register-framework-secrets.ts +28 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +29 -1
- package/corpus/core/src/server/index.ts +4 -0
- package/corpus/core/src/server/org-admin.ts +28 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +9 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/references/api.md +31 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +9 -0
- package/corpus/core/src/vite/client.ts +24 -0
- package/corpus/templates/analytics/.agents/skills/creative-context/SKILL.md +64 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +29 -21
- package/corpus/templates/analytics/AGENTS.md +13 -0
- package/corpus/templates/analytics/actions/clone-creative-context-dashboard.ts +86 -0
- package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +1 -1
- package/corpus/templates/analytics/actions/update-dashboard.ts +6 -2
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +35 -7
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +3 -3
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/analytics/app/i18n-data.ts +1 -0
- package/corpus/templates/analytics/app/pages/Ask.tsx +8 -4
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +11 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/extension-slot.ts +11 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +86 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +6 -0
- package/corpus/templates/analytics/app/routes/agent.tsx +7 -1
- package/corpus/templates/analytics/changelog/2026-07-17-dashboard-extension-boxes.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-17-library-now-flags-published-dashboards-with-newer-versions-a.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-17-resources-can-be-added-to-creative-context-from-their-action.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
- package/corpus/templates/analytics/package.json +1 -0
- package/corpus/templates/analytics/server/lib/agent-readable-resource-context.ts +8 -0
- package/corpus/templates/analytics/server/lib/native-creative-context.ts +158 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/analytics/server/plugins/creative-context.ts +10 -0
- package/corpus/templates/assets/.agents/skills/creative-context/SKILL.md +12 -3
- package/corpus/templates/assets/AGENTS.md +5 -0
- package/corpus/templates/assets/actions/clone-creative-context-asset.ts +74 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +2 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +308 -178
- package/corpus/templates/assets/app/routes/library.tsx +5 -11
- package/corpus/templates/assets/changelog/2026-07-17-library-now-flags-published-assets-with-newer-versions-and-l.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
- package/corpus/templates/assets/server/lib/native-creative-context.ts +198 -0
- package/corpus/templates/assets/server/plugins/creative-context.ts +4 -0
- package/corpus/templates/brain/actions/provider-api-request.ts +1 -0
- package/corpus/templates/brain/app/hooks/use-navigation-state.ts +6 -1
- package/corpus/templates/brain/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/calendar/actions/provider-api-request.ts +1 -0
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +1 -1
- package/corpus/templates/calendar/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/chat/app/hooks/use-navigation-state.ts +4 -1
- package/corpus/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/clips/actions/get-recording-insights.ts +7 -4
- package/corpus/templates/clips/actions/list-recordings.ts +48 -0
- package/corpus/templates/clips/actions/list-viewers.ts +2 -1
- package/corpus/templates/clips/app/components/editor/stitch-manager.tsx +3 -1
- package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +1 -3
- package/corpus/templates/clips/app/hooks/use-desktop-promo.ts +11 -6
- package/corpus/templates/clips/app/hooks/use-view-tracking.ts +6 -1
- package/corpus/templates/clips/app/lib/ffmpeg-export.ts +4 -2
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +21 -3
- package/corpus/templates/clips/changelog/2026-07-17-dictation-keeps-sentence-spacing-and-no-longer-crashes-when-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-17-shared-clips-now-preserve-editor-access-and-show-editor-insi.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-17-stitching-clips-no-longer-fails-when-editor-media-metadata-i.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/clips/server/routes/api/public-recording.get.ts +14 -8
- package/corpus/templates/clips/shared/view-analytics.ts +4 -0
- package/corpus/templates/content/.agents/skills/creative-context/SKILL.md +12 -3
- package/corpus/templates/content/AGENTS.md +5 -0
- package/corpus/templates/content/actions/_content-space-access.ts +45 -2
- package/corpus/templates/content/actions/clone-creative-context-document.ts +61 -0
- package/corpus/templates/content/actions/create-content-database.ts +41 -7
- package/corpus/templates/content/actions/provider-api-request.ts +1 -0
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +21 -0
- package/corpus/templates/content/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +29 -0
- package/corpus/templates/content/app/root.tsx +1 -10
- package/corpus/templates/content/changelog/2026-07-17-creating-a-database-now-completes-reliably-in-hosted-workspa.md +6 -0
- package/corpus/templates/content/changelog/2026-07-17-library-now-flags-published-documents-with-newer-versions-an.md +6 -0
- package/corpus/templates/content/changelog/2026-07-17-resources-can-be-added-to-creative-context-from-their-action.md +6 -0
- package/corpus/templates/content/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/content/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
- package/corpus/templates/content/parity/matrix.md +1 -1
- package/corpus/templates/content/parity/matrix.ts +1 -0
- package/corpus/templates/content/server/lib/native-creative-context.ts +191 -0
- package/corpus/templates/content/server/plugins/creative-context.ts +8 -1
- package/corpus/templates/design/.agents/skills/creative-context/SKILL.md +11 -3
- package/corpus/templates/design/AGENTS.md +5 -0
- package/corpus/templates/design/actions/clone-creative-context-design-native.ts +111 -0
- package/corpus/templates/design/actions/provider-api-request.ts +1 -0
- package/corpus/templates/design/app/pages/Index.tsx +44 -0
- package/corpus/templates/design/app/pages/design-editor/types.ts +1 -0
- package/corpus/templates/design/changelog/2026-07-17-library-now-flags-published-designs-with-newer-versions-and-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-17-resources-can-be-added-to-creative-context-from-their-action.md +6 -0
- package/corpus/templates/design/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
- package/corpus/templates/design/server/lib/native-creative-context.ts +265 -0
- package/corpus/templates/design/server/plugins/creative-context.ts +8 -1
- package/corpus/templates/dispatch/actions/provider-api-request.ts +1 -0
- package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +4 -1
- package/corpus/templates/dispatch/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/forms/app/components/layout/Layout.tsx +2 -4
- package/corpus/templates/forms/app/components/ui/popover.tsx +25 -1
- package/corpus/templates/forms/app/hooks/use-navigation-state.ts +6 -1
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +13 -10
- package/corpus/templates/forms/app/root.tsx +6 -1
- package/corpus/templates/forms/changelog/2026-07-17-form-builder-popovers-stay-within-the-viewport.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-17-form-editor-loading-matches-current-sidebar-free-layout.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/mail/actions/provider-api-request.ts +1 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +1 -1
- package/corpus/templates/mail/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +14 -3
- package/corpus/templates/plan/app/hooks/use-navigation-state.ts +8 -1
- package/corpus/templates/plan/app/root.tsx +0 -2
- package/corpus/templates/plan/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/slides/.agents/skills/creative-context/SKILL.md +11 -3
- package/corpus/templates/slides/AGENTS.md +5 -0
- package/corpus/templates/slides/actions/clone-creative-context-deck.ts +96 -0
- package/corpus/templates/slides/actions/extract-pdf.ts +3 -2
- package/corpus/templates/slides/actions/import-file.ts +3 -2
- package/corpus/templates/slides/actions/provider-api-request.ts +1 -0
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +27 -0
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +21 -0
- package/corpus/templates/slides/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +23 -23
- package/corpus/templates/slides/changelog/2026-07-17-duplicated-slides-now-stay-in-the-deck.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-17-library-now-flags-published-decks-with-newer-versions-and-le.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-17-pdf-and-powerpoint-imports-now-work-reliably-in-hosted-decks.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-17-resources-can-be-added-to-creative-context-from-their-action.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-17-you-can-add-exact-approved-artifact-versions-to-governed-cre.md +6 -0
- package/corpus/templates/slides/server/lib/native-creative-context.ts +288 -0
- package/corpus/templates/slides/server/plugins/creative-context.ts +4 -0
- package/corpus/templates/tasks/app/components/layout/Layout.tsx +2 -13
- package/corpus/templates/tasks/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it.md +6 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +13 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-store.d.ts +17 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +84 -2
- package/dist/agent/run-store.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +18 -3
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/CommandMenu.d.ts.map +1 -1
- package/dist/client/CommandMenu.js +4 -0
- package/dist/client/CommandMenu.js.map +1 -1
- package/dist/client/active-run-state.d.ts +7 -0
- package/dist/client/active-run-state.d.ts.map +1 -1
- package/dist/client/active-run-state.js +28 -0
- package/dist/client/active-run-state.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +4 -1
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +35 -1
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +7 -0
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +37 -8
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/integrations/IntegrationsPanel.d.ts.map +1 -1
- package/dist/client/integrations/IntegrationsPanel.js +3 -2
- package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
- package/dist/client/resources/McpConnectionSuggestion.d.ts +9 -0
- package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -0
- package/dist/client/resources/McpConnectionSuggestion.js +134 -0
- package/dist/client/resources/McpConnectionSuggestion.js.map +1 -0
- package/dist/client/resources/McpIntegrationDialog.d.ts +2 -1
- package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +51 -21
- package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.d.ts +15 -0
- package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.js +420 -4
- package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
- package/dist/client/resources/mcp-integration-logos.d.ts +2 -0
- package/dist/client/resources/mcp-integration-logos.d.ts.map +1 -0
- package/dist/client/resources/mcp-integration-logos.js +109 -0
- package/dist/client/resources/mcp-integration-logos.js.map +1 -0
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +15 -12
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.js +1 -1
- package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +23 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/extensions/html-shell.d.ts.map +1 -1
- package/dist/extensions/html-shell.js +33 -1
- package/dist/extensions/html-shell.js.map +1 -1
- package/dist/ingestion/office.d.ts.map +1 -1
- package/dist/ingestion/office.js +1 -2
- package/dist/ingestion/office.js.map +1 -1
- package/dist/ingestion/pptx.js +2 -4
- package/dist/ingestion/pptx.js.map +1 -1
- package/dist/localization/default-messages.d.ts +100 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +101 -1
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/mcp/actions/call-mcp-tool.js +1 -0
- package/dist/mcp/actions/call-mcp-tool.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/register-framework-secrets.d.ts.map +1 -1
- package/dist/secrets/register-framework-secrets.js +25 -0
- package/dist/secrets/register-framework-secrets.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +23 -1
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/org-admin.d.ts +3 -0
- package/dist/server/org-admin.d.ts.map +1 -0
- package/dist/server/org-admin.js +24 -0
- package/dist/server/org-admin.js.map +1 -0
- package/dist/templates/chat/app/hooks/use-navigation-state.ts +4 -1
- package/dist/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +9 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/references/api.md +31 -0
- package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +9 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +20 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/integrations.mdx +597 -0
- package/docs/content/locales/ar-SA/mcp-clients.mdx +19 -1
- package/docs/content/locales/de-DE/mcp-clients.mdx +22 -1
- package/docs/content/locales/es-ES/mcp-clients.mdx +23 -1
- package/docs/content/locales/fr-FR/mcp-clients.mdx +23 -1
- package/docs/content/locales/hi-IN/mcp-clients.mdx +22 -1
- package/docs/content/locales/ja-JP/mcp-clients.mdx +21 -1
- package/docs/content/locales/ko-KR/mcp-clients.mdx +20 -1
- package/docs/content/locales/pt-BR/mcp-clients.mdx +22 -1
- package/docs/content/locales/zh-CN/mcp-clients.mdx +17 -1
- package/docs/content/locales/zh-TW/mcp-clients.mdx +17 -1
- package/docs/content/mcp-clients.mdx +51 -1
- package/docs/content/organizations-teams-permissions.mdx +413 -0
- package/package.json +1 -1
- package/src/templates/chat/app/hooks/use-navigation-state.ts +4 -1
- package/src/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +6 -0
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +9 -0
- package/src/templates/workspace-core/.agents/skills/extensions/references/api.md +31 -0
- package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +9 -0
|
@@ -38,7 +38,9 @@ import React, {
|
|
|
38
38
|
import type { ReasoningEffort } from "../shared/reasoning-effort.js";
|
|
39
39
|
import {
|
|
40
40
|
ACTIVE_RUN_STATE_EVENT,
|
|
41
|
+
clearPendingTurnIfMatches,
|
|
41
42
|
clearActiveRunIfMatches,
|
|
43
|
+
getPendingTurn,
|
|
42
44
|
getActiveRunActivityTool,
|
|
43
45
|
getActiveRun,
|
|
44
46
|
resolveReconnectAfterSeq,
|
|
@@ -172,6 +174,7 @@ import {
|
|
|
172
174
|
useGuidedQuestionFlow,
|
|
173
175
|
} from "./guided-questions.js";
|
|
174
176
|
import { useT } from "./i18n.js";
|
|
177
|
+
import { McpConnectionSuggestion } from "./resources/McpConnectionSuggestion.js";
|
|
175
178
|
import {
|
|
176
179
|
AgentAutoContinueSignal,
|
|
177
180
|
type ContentPart,
|
|
@@ -2239,6 +2242,14 @@ const AssistantChatInner = forwardRef<
|
|
|
2239
2242
|
// (unsupported format, size cap, body-size rejection, drop errors).
|
|
2240
2243
|
// Cleared on the next message send.
|
|
2241
2244
|
const [composerError, setComposerError] = useState<string | null>(null);
|
|
2245
|
+
const [composerText, setComposerText] = useState("");
|
|
2246
|
+
const handleComposerTextChange = useCallback(
|
|
2247
|
+
(text: string) => {
|
|
2248
|
+
setComposerText(text);
|
|
2249
|
+
onComposerTextChange?.(text);
|
|
2250
|
+
},
|
|
2251
|
+
[onComposerTextChange],
|
|
2252
|
+
);
|
|
2242
2253
|
const dropDepthRef = useRef(0);
|
|
2243
2254
|
const handleChatDragEnter = useCallback((e: React.DragEvent) => {
|
|
2244
2255
|
if (!Array.from(e.dataTransfer?.types ?? []).includes("Files")) return;
|
|
@@ -4272,6 +4283,7 @@ const AssistantChatInner = forwardRef<
|
|
|
4272
4283
|
}
|
|
4273
4284
|
const activeRun = getActiveRun();
|
|
4274
4285
|
const runIdToAbort = reconnectRunIdRef.current ?? activeRun?.runId;
|
|
4286
|
+
const pendingTurn = threadId ? getPendingTurn(threadId) : null;
|
|
4275
4287
|
userStoppedRunRef.current = {
|
|
4276
4288
|
at: Date.now(),
|
|
4277
4289
|
...(runIdToAbort ? { runId: runIdToAbort } : {}),
|
|
@@ -4283,6 +4295,16 @@ const AssistantChatInner = forwardRef<
|
|
|
4283
4295
|
fetch(`${apiUrl}/runs/${encodeURIComponent(runIdToAbort)}/abort`, {
|
|
4284
4296
|
method: "POST",
|
|
4285
4297
|
}).catch(() => {});
|
|
4298
|
+
} else if (pendingTurn && threadId) {
|
|
4299
|
+
clearPendingTurnIfMatches(threadId, pendingTurn.turnId);
|
|
4300
|
+
fetch(
|
|
4301
|
+
`${apiUrl}/runs/turn/${encodeURIComponent(pendingTurn.turnId)}/abort`,
|
|
4302
|
+
{
|
|
4303
|
+
method: "POST",
|
|
4304
|
+
headers: { "Content-Type": "application/json" },
|
|
4305
|
+
body: JSON.stringify({ threadId }),
|
|
4306
|
+
},
|
|
4307
|
+
).catch(() => {});
|
|
4286
4308
|
}
|
|
4287
4309
|
if (isReconnecting) {
|
|
4288
4310
|
reconnectAbortRef.current?.abort();
|
|
@@ -5382,6 +5404,9 @@ const AssistantChatInner = forwardRef<
|
|
|
5382
5404
|
</MessageScrollerProvider>
|
|
5383
5405
|
|
|
5384
5406
|
{showComposerSlot ? composerSlot : null}
|
|
5407
|
+
{isActiveComposer && (
|
|
5408
|
+
<McpConnectionSuggestion text={composerText} />
|
|
5409
|
+
)}
|
|
5385
5410
|
{showCenteredEmptyThreadFooterSlot ? (
|
|
5386
5411
|
<div className="agent-thread-footer-slot agent-thread-footer-slot--centered-empty">
|
|
5387
5412
|
{resolvedThreadFooterSlot}
|
|
@@ -5488,7 +5513,7 @@ const AssistantChatInner = forwardRef<
|
|
|
5488
5513
|
focusRef={tiptapRef}
|
|
5489
5514
|
onTextChange={
|
|
5490
5515
|
isActiveComposer
|
|
5491
|
-
?
|
|
5516
|
+
? handleComposerTextChange
|
|
5492
5517
|
: undefined
|
|
5493
5518
|
}
|
|
5494
5519
|
disabled={isComposerDisabled}
|
|
@@ -72,6 +72,11 @@ export function openAgentSettings(
|
|
|
72
72
|
const normalizedSection =
|
|
73
73
|
typeof section === "string" ? section : section?.section;
|
|
74
74
|
|
|
75
|
+
const secretHash = normalizedSection?.replace(/^#/, "");
|
|
76
|
+
if (secretHash?.toLowerCase().startsWith("secrets:")) {
|
|
77
|
+
window.location.hash = `#${secretHash}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
75
80
|
openAgentSidebar();
|
|
76
81
|
// Voice mode unmounts the chat surface while its dock is collapsed, so its
|
|
77
82
|
// settings listener does not exist until opening the sidebar remounts it.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const STORAGE_KEY = "agent-chat-active-run";
|
|
2
|
+
const PENDING_TURN_STORAGE_KEY = "agent-chat-pending-turn";
|
|
2
3
|
export const ACTIVE_RUN_STATE_EVENT = "agent-chat:active-run-state-change";
|
|
3
4
|
|
|
4
5
|
export interface ActiveRunState {
|
|
@@ -8,6 +9,11 @@ export interface ActiveRunState {
|
|
|
8
9
|
activityTool?: string | null;
|
|
9
10
|
}
|
|
10
11
|
|
|
12
|
+
export interface PendingTurnState {
|
|
13
|
+
threadId: string;
|
|
14
|
+
turnId: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
11
17
|
function notifyActiveRunStateChanged(state: ActiveRunState | null): void {
|
|
12
18
|
if (
|
|
13
19
|
typeof window === "undefined" ||
|
|
@@ -88,6 +94,34 @@ export function clearActiveRunIfMatches(threadId: string, runId: string): void {
|
|
|
88
94
|
clearActiveRun();
|
|
89
95
|
}
|
|
90
96
|
|
|
97
|
+
export function setPendingTurn(state: PendingTurnState): void {
|
|
98
|
+
try {
|
|
99
|
+
sessionStorage.setItem(PENDING_TURN_STORAGE_KEY, JSON.stringify(state));
|
|
100
|
+
} catch {}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function getPendingTurn(threadId: string): PendingTurnState | null {
|
|
104
|
+
try {
|
|
105
|
+
const raw = sessionStorage.getItem(PENDING_TURN_STORAGE_KEY);
|
|
106
|
+
if (!raw) return null;
|
|
107
|
+
const state = JSON.parse(raw) as PendingTurnState;
|
|
108
|
+
return state.threadId === threadId && state.turnId ? state : null;
|
|
109
|
+
} catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function clearPendingTurnIfMatches(
|
|
115
|
+
threadId: string,
|
|
116
|
+
turnId?: string,
|
|
117
|
+
): void {
|
|
118
|
+
const state = getPendingTurn(threadId);
|
|
119
|
+
if (!state || (turnId && state.turnId !== turnId)) return;
|
|
120
|
+
try {
|
|
121
|
+
sessionStorage.removeItem(PENDING_TURN_STORAGE_KEY);
|
|
122
|
+
} catch {}
|
|
123
|
+
}
|
|
124
|
+
|
|
91
125
|
/** Resume reconnect SSE after the last seen event (0 = replay from the start). */
|
|
92
126
|
export function resolveReconnectAfterSeq(
|
|
93
127
|
threadId: string,
|
|
@@ -11,9 +11,11 @@ import type {
|
|
|
11
11
|
} from "../agent/types.js";
|
|
12
12
|
import type { ReasoningEffort } from "../shared/reasoning-effort.js";
|
|
13
13
|
import {
|
|
14
|
+
clearPendingTurnIfMatches,
|
|
14
15
|
setActiveRun,
|
|
15
16
|
updateActiveRunSeq,
|
|
16
17
|
clearActiveRun,
|
|
18
|
+
setPendingTurn,
|
|
17
19
|
} from "./active-run-state.js";
|
|
18
20
|
import { captureError } from "./analytics.js";
|
|
19
21
|
import { agentNativePath } from "./api-path.js";
|
|
@@ -1634,6 +1636,7 @@ export function createAgentChatAdapter(
|
|
|
1634
1636
|
const content: ContentPart[] = [];
|
|
1635
1637
|
const toolCallCounter = { value: 0 };
|
|
1636
1638
|
const turnId = generateTurnId();
|
|
1639
|
+
if (threadId) setPendingTurn({ threadId, turnId });
|
|
1637
1640
|
let runId: string | null = null;
|
|
1638
1641
|
let lastSeq = -1;
|
|
1639
1642
|
const seenRunSeqs = new Map<string, number>();
|
|
@@ -3279,6 +3282,7 @@ export function createAgentChatAdapter(
|
|
|
3279
3282
|
attemptedRunIds.push(runId);
|
|
3280
3283
|
}
|
|
3281
3284
|
if (runId && threadId) {
|
|
3285
|
+
clearPendingTurnIfMatches(threadId, turnId);
|
|
3282
3286
|
setActiveRun({ threadId, runId, lastSeq: -1 });
|
|
3283
3287
|
}
|
|
3284
3288
|
|
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
import { isPastedTextAttachmentName } from "../composer/pasted-text.js";
|
|
63
63
|
import { PastedTextChip } from "../composer/PastedTextChip.js";
|
|
64
64
|
import { ThumbsFeedback } from "../observability/ThumbsFeedback.js";
|
|
65
|
+
import { McpConnectionSuggestion } from "../resources/McpConnectionSuggestion.js";
|
|
65
66
|
import type { ContentPart } from "../sse-event-processor.js";
|
|
66
67
|
import { cn } from "../utils.js";
|
|
67
68
|
import {
|
|
@@ -819,6 +820,37 @@ function assistantMessageStatusIsTerminal(message: {
|
|
|
819
820
|
return statusType === "complete" || statusType === "incomplete";
|
|
820
821
|
}
|
|
821
822
|
|
|
823
|
+
function messageTextFromContent(content: unknown): string {
|
|
824
|
+
if (typeof content === "string") return content;
|
|
825
|
+
if (!Array.isArray(content)) return "";
|
|
826
|
+
return content
|
|
827
|
+
.flatMap((part) => {
|
|
828
|
+
if (!part || typeof part !== "object") return [];
|
|
829
|
+
const record = part as {
|
|
830
|
+
type?: unknown;
|
|
831
|
+
text?: unknown;
|
|
832
|
+
result?: unknown;
|
|
833
|
+
};
|
|
834
|
+
const values: string[] = [];
|
|
835
|
+
if (typeof record.text === "string") values.push(record.text);
|
|
836
|
+
if (record.type === "tool-call" && typeof record.result === "string") {
|
|
837
|
+
values.push(record.result);
|
|
838
|
+
}
|
|
839
|
+
return values;
|
|
840
|
+
})
|
|
841
|
+
.join("\n");
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
function latestUserMessageText(messages: readonly unknown[]): string {
|
|
845
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
846
|
+
const message = messages[index];
|
|
847
|
+
if (!message || typeof message !== "object") continue;
|
|
848
|
+
const record = message as { role?: unknown; content?: unknown };
|
|
849
|
+
if (record.role === "user") return messageTextFromContent(record.content);
|
|
850
|
+
}
|
|
851
|
+
return "";
|
|
852
|
+
}
|
|
853
|
+
|
|
822
854
|
export function assistantMessageHasUnresolvedTool(content: unknown): boolean {
|
|
823
855
|
if (!Array.isArray(content)) return false;
|
|
824
856
|
return content.some((part): boolean => {
|
|
@@ -946,6 +978,8 @@ export function AssistantMessage() {
|
|
|
946
978
|
thread.messages[thread.messages.length - 1].id === msg.id;
|
|
947
979
|
const hasRenderableContent = assistantMessageHasRenderableContent(msg);
|
|
948
980
|
const hasUnresolvedTool = assistantMessageHasUnresolvedTool(msg.content);
|
|
981
|
+
const responseConnectionText = messageTextFromContent(msg.content);
|
|
982
|
+
const responseConnectionContext = latestUserMessageText(thread.messages);
|
|
949
983
|
const isComplete = shouldShowAssistantMessageFooter({
|
|
950
984
|
isLast,
|
|
951
985
|
chatRunning,
|
|
@@ -1104,6 +1138,13 @@ export function AssistantMessage() {
|
|
|
1104
1138
|
{isComplete && hasCodeAgentTools && msgContent && (
|
|
1105
1139
|
<FilesChangedSummary parts={msgContent} />
|
|
1106
1140
|
)}
|
|
1141
|
+
{isComplete && isLast && (
|
|
1142
|
+
<McpConnectionSuggestion
|
|
1143
|
+
text={responseConnectionText}
|
|
1144
|
+
contextText={responseConnectionContext}
|
|
1145
|
+
variant="response"
|
|
1146
|
+
/>
|
|
1147
|
+
)}
|
|
1107
1148
|
{isLast && hasUnresolvedTool && !chatRunning && (
|
|
1108
1149
|
<RunningActivityStatus label="Thinking" />
|
|
1109
1150
|
)}
|
|
@@ -110,6 +110,28 @@ export function canSubmitComposerContent(options: {
|
|
|
110
110
|
);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
const VOICE_TERMINAL_PUNCTUATION = /[.!?…。!?:;](?:["'”’»)\]}]*)$/;
|
|
114
|
+
|
|
115
|
+
export function formatVoiceTranscriptForComposer(text: string): string {
|
|
116
|
+
const trimmed = text.trim();
|
|
117
|
+
if (!trimmed) return "";
|
|
118
|
+
return `${trimmed}${VOICE_TERMINAL_PUNCTUATION.test(trimmed) ? "" : "."} `;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function canRemoveVoicePreview(options: {
|
|
122
|
+
documentSize: number;
|
|
123
|
+
anchor: number;
|
|
124
|
+
previewText: string;
|
|
125
|
+
currentText: string;
|
|
126
|
+
}): boolean {
|
|
127
|
+
if (!options.previewText) return false;
|
|
128
|
+
if (options.anchor < 1) return false;
|
|
129
|
+
if (options.anchor + options.previewText.length > options.documentSize) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
return options.currentText === options.previewText;
|
|
133
|
+
}
|
|
134
|
+
|
|
113
135
|
export function resolveComposerPrimaryAction(options: {
|
|
114
136
|
canSubmit: boolean;
|
|
115
137
|
hasStopButton: boolean;
|
|
@@ -2082,15 +2104,16 @@ export function TiptapComposer({
|
|
|
2082
2104
|
(text: string) => {
|
|
2083
2105
|
const ed = editor;
|
|
2084
2106
|
if (!isComposerEditorUsable(ed)) return;
|
|
2107
|
+
const formattedText = formatVoiceTranscriptForComposer(text);
|
|
2085
2108
|
|
|
2086
2109
|
const anchor = voiceAnchorRef.current;
|
|
2087
2110
|
if (anchor != null) {
|
|
2088
2111
|
const prevLen = prevVoiceInsertRef.current.length;
|
|
2089
|
-
if (
|
|
2112
|
+
if (formattedText) {
|
|
2090
2113
|
ed.chain()
|
|
2091
2114
|
.focus()
|
|
2092
2115
|
.deleteRange({ from: anchor, to: anchor + prevLen })
|
|
2093
|
-
.insertContentAt(anchor,
|
|
2116
|
+
.insertContentAt(anchor, formattedText)
|
|
2094
2117
|
.run();
|
|
2095
2118
|
} else if (prevLen > 0) {
|
|
2096
2119
|
ed.chain()
|
|
@@ -2099,14 +2122,14 @@ export function TiptapComposer({
|
|
|
2099
2122
|
}
|
|
2100
2123
|
voiceAnchorRef.current = null;
|
|
2101
2124
|
prevVoiceInsertRef.current = "";
|
|
2102
|
-
} else if (
|
|
2125
|
+
} else if (formattedText) {
|
|
2103
2126
|
const { from } = ed.state.selection;
|
|
2104
2127
|
const prevChar =
|
|
2105
2128
|
from > 1 ? ed.state.doc.textBetween(from - 1, from) : "";
|
|
2106
2129
|
const needsLead = prevChar && !/\s/.test(prevChar);
|
|
2107
2130
|
ed.chain()
|
|
2108
2131
|
.focus()
|
|
2109
|
-
.insertContent((needsLead ? " " : "") +
|
|
2132
|
+
.insertContent((needsLead ? " " : "") + formattedText)
|
|
2110
2133
|
.run();
|
|
2111
2134
|
}
|
|
2112
2135
|
},
|
|
@@ -2172,10 +2195,24 @@ export function TiptapComposer({
|
|
|
2172
2195
|
const anchor = voiceAnchorRef.current;
|
|
2173
2196
|
const prevLen = prevVoiceInsertRef.current.length;
|
|
2174
2197
|
if (isComposerEditorUsable(editor) && prevLen > 0) {
|
|
2175
|
-
editor
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2198
|
+
const documentSize = editor.state.doc.content.size;
|
|
2199
|
+
const currentText =
|
|
2200
|
+
anchor >= 1 && anchor + prevLen <= documentSize
|
|
2201
|
+
? editor.state.doc.textBetween(anchor, anchor + prevLen, "", "\n")
|
|
2202
|
+
: "";
|
|
2203
|
+
if (
|
|
2204
|
+
canRemoveVoicePreview({
|
|
2205
|
+
documentSize,
|
|
2206
|
+
anchor,
|
|
2207
|
+
previewText: prevVoiceInsertRef.current,
|
|
2208
|
+
currentText,
|
|
2209
|
+
})
|
|
2210
|
+
) {
|
|
2211
|
+
editor
|
|
2212
|
+
.chain()
|
|
2213
|
+
.deleteRange({ from: anchor, to: anchor + prevLen })
|
|
2214
|
+
.run();
|
|
2215
|
+
}
|
|
2179
2216
|
}
|
|
2180
2217
|
voiceAnchorRef.current = null;
|
|
2181
2218
|
prevVoiceInsertRef.current = "";
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from "@tabler/icons-react";
|
|
15
15
|
import React, { useState, useCallback, useEffect } from "react";
|
|
16
16
|
|
|
17
|
+
import { AgentAskPopover } from "../AgentAskPopover.js";
|
|
17
18
|
import { agentNativePath } from "../api-path.js";
|
|
18
19
|
import {
|
|
19
20
|
Tooltip,
|
|
@@ -511,6 +512,14 @@ export function IntegrationsPanel() {
|
|
|
511
512
|
<div className="text-[10px] font-medium text-muted-foreground mb-1.5">
|
|
512
513
|
{t("integrations.addChatIntegration")}
|
|
513
514
|
</div>
|
|
515
|
+
<AgentAskPopover
|
|
516
|
+
label={t("integrations.addSomething")}
|
|
517
|
+
title={t("integrations.addSomethingTitle")}
|
|
518
|
+
placeholder={t("integrations.addSomethingPlaceholder")}
|
|
519
|
+
prompt=""
|
|
520
|
+
context="The user wants to add a chat or workspace integration that is not in the current directory. Research the provider's official OAuth or MCP setup, explain any app registration or allowlist requirements, and add a reusable integration preset when it is supported. Never ask the user to paste credentials into a prompt."
|
|
521
|
+
className="mb-2 h-7 w-full justify-center border-dashed px-2 text-[10px]"
|
|
522
|
+
/>
|
|
514
523
|
<AddIntegrationPicker
|
|
515
524
|
connectedIds={connectedIds}
|
|
516
525
|
onSelect={(p) => {
|
|
@@ -533,17 +542,27 @@ export function IntegrationsPanel() {
|
|
|
533
542
|
{t("integrations.chatIntegrationsDescription")}
|
|
534
543
|
</div>
|
|
535
544
|
</div>
|
|
536
|
-
<
|
|
537
|
-
<
|
|
538
|
-
<
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
545
|
+
<div className="flex items-center gap-1">
|
|
546
|
+
<Tooltip>
|
|
547
|
+
<TooltipTrigger asChild>
|
|
548
|
+
<button
|
|
549
|
+
onClick={() => setShowPicker(true)}
|
|
550
|
+
className="flex h-5 w-5 items-center justify-center rounded text-muted-foreground hover:bg-accent/50 hover:text-foreground"
|
|
551
|
+
>
|
|
552
|
+
<IconPlus size={12} />
|
|
553
|
+
</button>
|
|
554
|
+
</TooltipTrigger>
|
|
555
|
+
<TooltipContent>{t("integrations.addIntegration")}</TooltipContent>
|
|
556
|
+
</Tooltip>
|
|
557
|
+
<AgentAskPopover
|
|
558
|
+
label={t("integrations.addSomething")}
|
|
559
|
+
title={t("integrations.addSomethingTitle")}
|
|
560
|
+
placeholder={t("integrations.addSomethingPlaceholder")}
|
|
561
|
+
prompt=""
|
|
562
|
+
context="The user wants to add a chat or workspace integration that is not in the current directory. Research the provider's official OAuth or MCP setup, explain any app registration or allowlist requirements, and add a reusable integration preset when it is supported. Never ask the user to paste credentials into a prompt."
|
|
563
|
+
className="h-7 px-2 text-[10px]"
|
|
564
|
+
/>
|
|
565
|
+
</div>
|
|
547
566
|
</div>
|
|
548
567
|
|
|
549
568
|
{loading ? (
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IconArrowUpRight,
|
|
3
|
+
IconLoader2,
|
|
4
|
+
IconPlugConnected,
|
|
5
|
+
IconX,
|
|
6
|
+
} from "@tabler/icons-react";
|
|
7
|
+
import { useEffect, useMemo, useState } from "react";
|
|
8
|
+
|
|
9
|
+
import { agentNativePath } from "../api-path.js";
|
|
10
|
+
import { openAgentSettings } from "../CommandMenu.js";
|
|
11
|
+
import { useT } from "../i18n.js";
|
|
12
|
+
import {
|
|
13
|
+
buildMcpOAuthStartUrl,
|
|
14
|
+
findMcpIntegrationForText,
|
|
15
|
+
getMcpIntegrationApiFallback,
|
|
16
|
+
getDefaultMcpIntegrations,
|
|
17
|
+
isMcpConnectionFailureText,
|
|
18
|
+
type DefaultMcpIntegration,
|
|
19
|
+
} from "./mcp-integration-catalog.js";
|
|
20
|
+
import { McpIntegrationDialog } from "./McpIntegrationDialog.js";
|
|
21
|
+
import {
|
|
22
|
+
useCreateMcpServer,
|
|
23
|
+
useMcpServers,
|
|
24
|
+
type McpServer,
|
|
25
|
+
} from "./use-mcp-servers.js";
|
|
26
|
+
|
|
27
|
+
export type McpConnectionSuggestionVariant = "composer" | "response";
|
|
28
|
+
|
|
29
|
+
interface McpConnectionSuggestionProps {
|
|
30
|
+
text: string;
|
|
31
|
+
contextText?: string;
|
|
32
|
+
variant?: McpConnectionSuggestionVariant;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function compareUrl(value: string): string {
|
|
36
|
+
try {
|
|
37
|
+
const url = new URL(value.trim());
|
|
38
|
+
url.hash = "";
|
|
39
|
+
return url.toString().replace(/\/+$/, "");
|
|
40
|
+
} catch {
|
|
41
|
+
return value.trim().replace(/\/+$/, "");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isConnected(
|
|
46
|
+
integration: DefaultMcpIntegration,
|
|
47
|
+
servers: McpServer[],
|
|
48
|
+
): boolean {
|
|
49
|
+
const targetUrl = compareUrl(integration.url);
|
|
50
|
+
return servers.some(
|
|
51
|
+
(server) =>
|
|
52
|
+
server.status.state === "connected" &&
|
|
53
|
+
compareUrl(server.url) === targetUrl,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function returnUrl(): string {
|
|
58
|
+
if (typeof window === "undefined") return "/";
|
|
59
|
+
return (
|
|
60
|
+
window.location.pathname + window.location.search + window.location.hash
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function canStartOAuth(integration: DefaultMcpIntegration): boolean {
|
|
65
|
+
return (
|
|
66
|
+
integration.authMode === "oauth" &&
|
|
67
|
+
integration.connectionMode === "oauth" &&
|
|
68
|
+
integration.availability === "ready"
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function hasApiFallback(
|
|
73
|
+
apiFallback: DefaultMcpIntegration["apiFallback"] | null,
|
|
74
|
+
): boolean {
|
|
75
|
+
return Boolean(apiFallback);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function McpConnectionSuggestion({
|
|
79
|
+
text,
|
|
80
|
+
contextText = "",
|
|
81
|
+
variant = "composer",
|
|
82
|
+
}: McpConnectionSuggestionProps) {
|
|
83
|
+
const t = useT();
|
|
84
|
+
const mcpServersQuery = useMcpServers();
|
|
85
|
+
const createMcpServer = useCreateMcpServer();
|
|
86
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
87
|
+
const [dismissedId, setDismissedId] = useState<string | null>(null);
|
|
88
|
+
const [connecting, setConnecting] = useState(false);
|
|
89
|
+
const [error, setError] = useState<string | null>(null);
|
|
90
|
+
const integrations = useMemo(() => getDefaultMcpIntegrations(), []);
|
|
91
|
+
const textIntegration = useMemo(
|
|
92
|
+
() => findMcpIntegrationForText(text, integrations),
|
|
93
|
+
[integrations, text],
|
|
94
|
+
);
|
|
95
|
+
const contextIntegration = useMemo(
|
|
96
|
+
() =>
|
|
97
|
+
contextText ? findMcpIntegrationForText(contextText, integrations) : null,
|
|
98
|
+
[contextText, integrations],
|
|
99
|
+
);
|
|
100
|
+
const integration =
|
|
101
|
+
variant === "response" &&
|
|
102
|
+
textIntegration &&
|
|
103
|
+
contextIntegration &&
|
|
104
|
+
textIntegration.id !== contextIntegration.id
|
|
105
|
+
? null
|
|
106
|
+
: (textIntegration ?? contextIntegration);
|
|
107
|
+
const apiFallback = integration
|
|
108
|
+
? getMcpIntegrationApiFallback(integration)
|
|
109
|
+
: null;
|
|
110
|
+
const servers = useMemo(
|
|
111
|
+
() => [
|
|
112
|
+
...(mcpServersQuery.data?.user ?? []),
|
|
113
|
+
...(mcpServersQuery.data?.org ?? []),
|
|
114
|
+
],
|
|
115
|
+
[mcpServersQuery.data],
|
|
116
|
+
);
|
|
117
|
+
const connected = integration ? isConnected(integration, servers) : false;
|
|
118
|
+
const shouldSuggest =
|
|
119
|
+
mcpServersQuery.isSuccess &&
|
|
120
|
+
integration &&
|
|
121
|
+
!connected &&
|
|
122
|
+
dismissedId !== integration.id &&
|
|
123
|
+
(variant === "composer" || isMcpConnectionFailureText(text));
|
|
124
|
+
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
setError(null);
|
|
127
|
+
setConnecting(false);
|
|
128
|
+
}, [integration?.id, variant]);
|
|
129
|
+
|
|
130
|
+
if (!shouldSuggest) return null;
|
|
131
|
+
|
|
132
|
+
const connect = async () => {
|
|
133
|
+
if (!integration || connecting) return;
|
|
134
|
+
setError(null);
|
|
135
|
+
|
|
136
|
+
if (apiFallback) {
|
|
137
|
+
openAgentSettings(`secrets:${apiFallback.secretKey}`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (canStartOAuth(integration)) {
|
|
142
|
+
window.location.assign(
|
|
143
|
+
agentNativePath(
|
|
144
|
+
buildMcpOAuthStartUrl({
|
|
145
|
+
name: integration.name,
|
|
146
|
+
url: integration.url,
|
|
147
|
+
description: integration.description,
|
|
148
|
+
scope: "user",
|
|
149
|
+
returnUrl: returnUrl(),
|
|
150
|
+
}),
|
|
151
|
+
),
|
|
152
|
+
);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (
|
|
157
|
+
integration.authMode === "none" &&
|
|
158
|
+
integration.connectionMode === "direct"
|
|
159
|
+
) {
|
|
160
|
+
setConnecting(true);
|
|
161
|
+
try {
|
|
162
|
+
await createMcpServer.mutateAsync({
|
|
163
|
+
scope: "user",
|
|
164
|
+
name: integration.name,
|
|
165
|
+
url: integration.url,
|
|
166
|
+
description: integration.description,
|
|
167
|
+
});
|
|
168
|
+
setDismissedId(integration.id);
|
|
169
|
+
} catch (cause) {
|
|
170
|
+
setError(
|
|
171
|
+
cause instanceof Error ? cause.message : t("mcpIntegrations.failed"),
|
|
172
|
+
);
|
|
173
|
+
} finally {
|
|
174
|
+
setConnecting(false);
|
|
175
|
+
}
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
setDialogOpen(true);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const actionLabel = hasApiFallback(apiFallback)
|
|
183
|
+
? t("mcpIntegrations.useApiToken")
|
|
184
|
+
: canStartOAuth(integration)
|
|
185
|
+
? t("mcpIntegrations.connectWithOAuth")
|
|
186
|
+
: integration.availability === "client-restricted" ||
|
|
187
|
+
integration.availability === "provider-setup"
|
|
188
|
+
? t("mcpIntegrations.viewSetup")
|
|
189
|
+
: t("mcpIntegrations.connect");
|
|
190
|
+
|
|
191
|
+
return (
|
|
192
|
+
<>
|
|
193
|
+
<div
|
|
194
|
+
className={
|
|
195
|
+
variant === "response"
|
|
196
|
+
? "mt-3 flex max-w-[520px] items-center gap-2 rounded-lg border border-primary/20 bg-primary/5 px-3 py-2 text-[12px]"
|
|
197
|
+
: "mx-auto mb-2 flex w-[min(100%,680px)] items-center gap-2 rounded-lg border border-primary/20 bg-primary/5 px-3 py-2 text-[12px]"
|
|
198
|
+
}
|
|
199
|
+
data-mcp-connection-suggestion={integration.id}
|
|
200
|
+
>
|
|
201
|
+
<div className="relative flex h-6 w-6 shrink-0 items-center justify-center overflow-hidden rounded-md border border-border bg-background text-[10px] font-semibold text-muted-foreground">
|
|
202
|
+
<span aria-hidden="true">{integration.name.slice(0, 1)}</span>
|
|
203
|
+
{integration.logoUrl && (
|
|
204
|
+
<img
|
|
205
|
+
src={integration.logoUrl}
|
|
206
|
+
alt=""
|
|
207
|
+
className="absolute h-5 w-5 object-contain"
|
|
208
|
+
onError={(event) => {
|
|
209
|
+
event.currentTarget.hidden = true;
|
|
210
|
+
}}
|
|
211
|
+
/>
|
|
212
|
+
)}
|
|
213
|
+
</div>
|
|
214
|
+
<span className="min-w-0 flex-1 leading-snug text-foreground">
|
|
215
|
+
{t(
|
|
216
|
+
hasApiFallback(apiFallback)
|
|
217
|
+
? "mcpIntegrations.connectSuggestionWithApiToken"
|
|
218
|
+
: "mcpIntegrations.connectSuggestion",
|
|
219
|
+
{ name: integration.name },
|
|
220
|
+
)}
|
|
221
|
+
</span>
|
|
222
|
+
<button
|
|
223
|
+
type="button"
|
|
224
|
+
onClick={() => void connect()}
|
|
225
|
+
disabled={connecting}
|
|
226
|
+
className="inline-flex shrink-0 items-center gap-1 rounded-md bg-primary px-2.5 py-1.5 text-[11px] font-medium text-primary-foreground hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-60"
|
|
227
|
+
>
|
|
228
|
+
{connecting && <IconLoader2 className="h-3 w-3 animate-spin" />}
|
|
229
|
+
{!connecting &&
|
|
230
|
+
canStartOAuth(integration) &&
|
|
231
|
+
!hasApiFallback(apiFallback) && (
|
|
232
|
+
<IconPlugConnected className="h-3 w-3" />
|
|
233
|
+
)}
|
|
234
|
+
{!connecting && hasApiFallback(apiFallback) && (
|
|
235
|
+
<IconPlugConnected className="h-3 w-3" />
|
|
236
|
+
)}
|
|
237
|
+
{actionLabel}
|
|
238
|
+
{!connecting &&
|
|
239
|
+
!canStartOAuth(integration) &&
|
|
240
|
+
!hasApiFallback(apiFallback) && (
|
|
241
|
+
<IconArrowUpRight className="h-3 w-3" />
|
|
242
|
+
)}
|
|
243
|
+
</button>
|
|
244
|
+
<button
|
|
245
|
+
type="button"
|
|
246
|
+
onClick={() => setDismissedId(integration.id)}
|
|
247
|
+
className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-background hover:text-foreground"
|
|
248
|
+
aria-label={t("mcpIntegrations.dismissSuggestion")}
|
|
249
|
+
>
|
|
250
|
+
<IconX className="h-3.5 w-3.5" />
|
|
251
|
+
</button>
|
|
252
|
+
</div>
|
|
253
|
+
{error && (
|
|
254
|
+
<div className="mx-auto mb-2 w-[min(100%,680px)] text-[11px] text-destructive">
|
|
255
|
+
{error}
|
|
256
|
+
</div>
|
|
257
|
+
)}
|
|
258
|
+
<McpIntegrationDialog
|
|
259
|
+
open={dialogOpen}
|
|
260
|
+
onOpenChange={setDialogOpen}
|
|
261
|
+
initialIntegrationId={integration.id}
|
|
262
|
+
defaultScope="user"
|
|
263
|
+
canCreateOrgMcp={false}
|
|
264
|
+
hasOrg={Boolean(mcpServersQuery.data?.orgId)}
|
|
265
|
+
onCreateMcpServer={(args) => createMcpServer.mutateAsync(args)}
|
|
266
|
+
onCreated={() => setDismissedId(integration.id)}
|
|
267
|
+
/>
|
|
268
|
+
</>
|
|
269
|
+
);
|
|
270
|
+
}
|