@agent-native/core 0.112.0 → 0.114.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-native.eject.json +164 -0
- package/corpus/README.md +4 -2
- package/corpus/core/CHANGELOG.md +33 -0
- package/corpus/core/README.md +61 -174
- package/corpus/core/agent-native.eject.json +164 -0
- package/corpus/core/docs/AGENTS.md +7 -0
- package/corpus/core/docs/content/agent-native-toolkit.mdx +38 -0
- package/corpus/core/docs/content/integrations.mdx +63 -2
- package/corpus/core/docs/content/package-lifecycle.mdx +54 -4
- package/corpus/core/docs/design/provider-integration-authority.md +117 -0
- package/corpus/core/package.json +20 -2
- package/corpus/core/scripts/materialize-source-corpus.mjs +9 -2
- package/corpus/core/src/cli/atomic-json-file.ts +324 -0
- package/corpus/core/src/cli/claude-code-participant.ts +515 -0
- package/corpus/core/src/cli/code-agent-executor.ts +18 -10
- package/corpus/core/src/cli/code-agent-runs.concurrent-worker.ts +33 -0
- package/corpus/core/src/cli/code-agent-runs.ts +55 -62
- package/corpus/core/src/cli/codex-cli-participant.ts +496 -0
- package/corpus/core/src/cli/eject.ts +1594 -0
- package/corpus/core/src/cli/index.ts +16 -0
- package/corpus/core/src/cli/multi-frontier-runs.ts +1246 -135
- package/corpus/core/src/client/chat/ChatHistoryList.tsx +7 -404
- package/corpus/core/src/client/index.ts +4 -0
- package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +8 -2
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +7 -2
- package/corpus/core/src/client/resources/ResourcesPanel.tsx +17 -3
- package/corpus/core/src/client/resources/index.ts +19 -0
- package/corpus/core/src/client/resources/mcp-integration-catalog.ts +9 -1
- package/corpus/core/src/client/resources/runtime.ts +1 -0
- package/corpus/core/src/client/setup-connections/catalog.ts +6 -0
- package/corpus/core/src/client/setup-connections/index.ts +1 -0
- package/corpus/core/src/client/setup-connections/runtime.ts +1 -0
- package/corpus/core/src/connections/catalog.ts +41 -24
- package/corpus/core/src/connections/index.ts +1 -0
- package/corpus/core/src/connections/runtime.ts +1 -0
- package/corpus/core/src/eject/provider-api-definitions.ts +193 -0
- package/corpus/core/src/eject/remote-mcp-presets.ts +137 -0
- package/corpus/core/src/eject/setup-readiness-ui.tsx +29 -0
- package/corpus/core/src/eject/workspace-connections.ts +92 -0
- package/corpus/core/src/index.ts +1 -0
- package/corpus/core/src/integrations/adapter-overrides.ts +12 -0
- package/corpus/core/src/integrations/eject/messaging-adapters.ts +85 -0
- package/corpus/core/src/integrations/index.ts +15 -1
- package/corpus/core/src/integrations/plugin.ts +34 -12
- package/corpus/core/src/integrations/runtime.ts +1 -0
- package/corpus/core/src/integrations/slack-oauth.ts +1 -0
- package/corpus/core/src/integrations/types.ts +3 -1
- package/corpus/core/src/integrations/webhook-delivery.ts +55 -0
- package/corpus/core/src/notifications/channels.ts +4 -10
- package/corpus/core/src/package-lifecycle/eject-manifest.ts +345 -0
- package/corpus/core/src/package-lifecycle/index.ts +1 -0
- package/corpus/core/src/provider-api/actions/custom-provider-registration.ts +197 -0
- package/corpus/core/src/provider-api/actions/delete-staged-dataset.ts +3 -42
- package/corpus/core/src/provider-api/actions/list-staged-datasets.ts +3 -45
- package/corpus/core/src/provider-api/actions/provider-api-audit.ts +88 -0
- package/corpus/core/src/provider-api/actions/provider-api.ts +460 -0
- package/corpus/core/src/provider-api/actions/query-staged-dataset.ts +3 -140
- package/corpus/core/src/provider-api/actions/staged-datasets.ts +359 -0
- package/corpus/core/src/provider-api/gong.ts +108 -0
- package/corpus/core/src/provider-api/index.ts +46 -12
- package/corpus/core/src/provider-api/runtime.ts +1 -0
- package/corpus/core/src/scripts/docs/source-search.ts +2 -1
- package/corpus/core/src/server/google-oauth-credentials.ts +80 -3
- package/corpus/core/src/server/index.ts +6 -0
- package/corpus/core/src/setup-connections/index.ts +1 -0
- package/corpus/core/src/shared/merge-by-id.ts +14 -0
- package/corpus/core/src/styles/chat-history-list.css +1 -313
- package/corpus/core/src/templates/default/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/core/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +21 -9
- package/corpus/core/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/core/src/templates/default/.agents/skills/integration-webhooks/SKILL.md +323 -0
- package/corpus/core/src/templates/default/.agents/skills/onboarding/SKILL.md +56 -0
- package/corpus/core/src/templates/default/.agents/skills/secrets/SKILL.md +273 -0
- package/corpus/core/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/corpus/core/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/core/src/templates/default/AGENTS.md +26 -18
- package/corpus/core/src/templates/headless/.agents/skills/actions/SKILL.md +512 -0
- package/corpus/core/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +21 -9
- package/corpus/core/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/core/src/templates/headless/.agents/skills/integration-webhooks/SKILL.md +323 -0
- package/corpus/core/src/templates/headless/.agents/skills/secrets/SKILL.md +273 -0
- package/corpus/core/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/core/src/templates/headless/AGENTS.md +8 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +21 -9
- package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +7 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/core/src/templates/workspace-core/AGENTS.md +4 -0
- package/corpus/core/src/templates/workspace-root/AGENTS.md +4 -0
- package/corpus/core/src/vite/client.ts +25 -2
- package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/analytics/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/templates/analytics/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/corpus/templates/analytics/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/analytics/actions/delete-staged-dataset.ts +3 -23
- package/corpus/templates/analytics/actions/list-staged-datasets.ts +3 -27
- package/corpus/templates/analytics/actions/provider-api-catalog.ts +16 -19
- package/corpus/templates/analytics/actions/provider-api-docs.ts +3 -5
- package/corpus/templates/analytics/actions/provider-api-request.ts +162 -185
- package/corpus/templates/analytics/actions/query-staged-dataset.ts +3 -47
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +52 -161
- package/corpus/templates/assets/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/assets/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/templates/assets/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/corpus/templates/assets/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +63 -160
- package/corpus/templates/brain/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/brain/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/templates/brain/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/corpus/templates/brain/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/brain/actions/_schemas.ts +13 -7
- package/corpus/templates/brain/actions/delete-staged-dataset.ts +3 -24
- package/corpus/templates/brain/actions/list-staged-datasets.ts +3 -28
- package/corpus/templates/brain/actions/provider-api-catalog.ts +5 -11
- package/corpus/templates/brain/actions/provider-api-docs.ts +3 -5
- package/corpus/templates/brain/actions/provider-api-request.ts +3 -37
- package/corpus/templates/brain/actions/query-staged-dataset.ts +3 -48
- package/corpus/templates/brain/actions/remediate-legacy-capture-audiences.ts +167 -0
- package/corpus/templates/brain/actions/update-source.ts +5 -1
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +48 -154
- package/corpus/templates/brain/app/lib/brain-chat-readiness.ts +6 -0
- package/corpus/templates/brain/app/lib/brain.ts +55 -0
- package/corpus/templates/brain/app/lib/ingest-handoff.ts +35 -0
- package/corpus/templates/brain/app/routes/_index.tsx +8 -0
- package/corpus/templates/brain/app/routes.ts +1 -0
- package/corpus/templates/brain/changelog/2026-07-19-ask-starts-immediately-when-builder-s-managed-ai-connection-.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-19-slack-and-granola-backfills-now-process-captures-concurrentl.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-19-slack-backfills-can-now-join-configured-public-channels-befo.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-19-slack-backfills-now-continue-across-the-configured-number-of.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-19-slack-backfills-now-start-with-a-safe-four-week-window-and-r.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-19-source-review-settings-now-control-knowledge-approval.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-19-the-manage-agent-page-now-opens-correctly-from-brain-navigat.md +6 -0
- package/corpus/templates/brain/server/db/schema.ts +3 -0
- package/corpus/templates/brain/server/lib/brain.ts +41 -2
- package/corpus/templates/brain/server/lib/connectors.ts +432 -115
- package/corpus/templates/brain/server/lib/meeting-audience.ts +38 -0
- package/corpus/templates/brain/server/lib/slack-channel-membership.ts +44 -0
- package/corpus/templates/brain/server/plugins/db.ts +10 -0
- package/corpus/templates/brain/server/routes/api/_agent-native/brain/ingest.post.ts +6 -4
- package/corpus/templates/brain/shared/slack-source-config.ts +60 -0
- package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/calendar/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/templates/calendar/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/corpus/templates/calendar/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/calendar/actions/delete-staged-dataset.ts +3 -24
- package/corpus/templates/calendar/actions/list-staged-datasets.ts +3 -28
- package/corpus/templates/calendar/actions/provider-api-catalog.ts +5 -11
- package/corpus/templates/calendar/actions/provider-api-docs.ts +3 -5
- package/corpus/templates/calendar/actions/provider-api-request.ts +3 -37
- package/corpus/templates/calendar/actions/query-staged-dataset.ts +3 -48
- package/corpus/templates/calendar/server/handlers/gong.ts +6 -61
- package/corpus/templates/calendar/server/handlers/google-auth.ts +10 -8
- package/corpus/templates/calendar/server/lib/google-calendar.ts +7 -45
- package/corpus/templates/chat/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/chat/.agents/skills/agent-native-docs/SKILL.md +21 -9
- package/corpus/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/templates/chat/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/corpus/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/chat/AGENTS.md +6 -2
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +51 -157
- package/corpus/templates/clips/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/clips/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/templates/clips/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/corpus/templates/clips/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/clips/AGENTS.md +11 -0
- package/corpus/templates/clips/actions/export-to-brain.ts +505 -171
- package/corpus/templates/clips/changelog/2026-07-19-ready-clips-transcripts-can-be-backfilled-to-brain-for-a-bou.md +6 -0
- package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +1 -1
- package/corpus/templates/clips/server/lib/google-calendar-client.ts +12 -22
- package/corpus/templates/clips/server/lib/google-calendar-oauth.ts +9 -6
- package/corpus/templates/clips/server/lib/slack-oauth.ts +12 -47
- package/corpus/templates/clips/server/routes/_agent-native/google/auth-url.get.ts +2 -6
- package/corpus/templates/clips/server/routes/api/auth/google-calendar.get.ts +4 -4
- package/corpus/templates/content/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/content/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/templates/content/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/corpus/templates/content/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/content/actions/delete-staged-dataset.ts +3 -24
- package/corpus/templates/content/actions/list-staged-datasets.ts +3 -28
- package/corpus/templates/content/actions/provider-api-catalog.ts +5 -11
- package/corpus/templates/content/actions/provider-api-docs.ts +3 -5
- package/corpus/templates/content/actions/provider-api-request.ts +3 -37
- package/corpus/templates/content/actions/query-staged-dataset.ts +3 -48
- package/corpus/templates/design/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/design/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/templates/design/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/corpus/templates/design/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/design/actions/delete-staged-dataset.ts +4 -23
- package/corpus/templates/design/actions/list-staged-datasets.ts +4 -27
- package/corpus/templates/design/actions/provider-api-catalog.ts +15 -18
- package/corpus/templates/design/actions/provider-api-docs.ts +57 -48
- package/corpus/templates/design/actions/provider-api-request.ts +140 -164
- package/corpus/templates/design/actions/query-staged-dataset.ts +4 -47
- package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/dispatch/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/templates/dispatch/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/corpus/templates/dispatch/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/dispatch/actions/delete-staged-dataset.ts +4 -23
- package/corpus/templates/dispatch/actions/list-staged-datasets.ts +4 -30
- package/corpus/templates/dispatch/actions/provider-api-catalog.ts +15 -18
- package/corpus/templates/dispatch/actions/provider-api-docs.ts +57 -48
- package/corpus/templates/dispatch/actions/provider-api-request.ts +138 -163
- package/corpus/templates/dispatch/actions/query-staged-dataset.ts +4 -47
- package/corpus/templates/forms/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/forms/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/templates/forms/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/corpus/templates/forms/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/forms/server/lib/integrations.ts +23 -22
- package/corpus/templates/macros/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/templates/macros/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/mail/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/mail/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/templates/mail/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/corpus/templates/mail/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/mail/actions/delete-staged-dataset.ts +4 -23
- package/corpus/templates/mail/actions/list-staged-datasets.ts +4 -27
- package/corpus/templates/mail/actions/provider-api-catalog.ts +15 -18
- package/corpus/templates/mail/actions/provider-api-docs.ts +57 -48
- package/corpus/templates/mail/actions/provider-api-request.ts +146 -173
- package/corpus/templates/mail/actions/query-staged-dataset.ts +4 -47
- package/corpus/templates/mail/server/handlers/gong.ts +6 -61
- package/corpus/templates/mail/server/lib/google-auth.ts +10 -13
- package/corpus/templates/plan/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/plan/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/templates/plan/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/corpus/templates/plan/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +47 -156
- package/corpus/templates/slides/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/slides/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/templates/slides/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/corpus/templates/slides/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/templates/slides/actions/delete-staged-dataset.ts +4 -23
- package/corpus/templates/slides/actions/list-staged-datasets.ts +4 -27
- package/corpus/templates/slides/actions/provider-api-catalog.ts +15 -18
- package/corpus/templates/slides/actions/provider-api-docs.ts +57 -48
- package/corpus/templates/slides/actions/provider-api-request.ts +145 -172
- package/corpus/templates/slides/actions/query-staged-dataset.ts +4 -47
- package/corpus/templates/slides/server/lib/google-docs-oauth.ts +11 -36
- package/corpus/templates/tasks/.agents/skills/actions/SKILL.md +32 -12
- package/corpus/templates/tasks/.agents/skills/agent-native-docs/SKILL.md +21 -9
- package/corpus/templates/tasks/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/corpus/templates/tasks/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/corpus/templates/tasks/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/corpus/toolkit/CHANGELOG.md +131 -0
- package/corpus/toolkit/README.md +60 -0
- package/corpus/toolkit/agent-native.eject.json +411 -0
- package/corpus/toolkit/export-snapshot.json +35 -0
- package/corpus/toolkit/migration-manifest.json +4 -0
- package/corpus/toolkit/package.json +284 -0
- package/corpus/toolkit/scripts/finalize-build.mjs +55 -0
- package/corpus/toolkit/src/app-shell/header-actions.tsx +84 -0
- package/corpus/toolkit/src/app-shell/index.ts +1 -0
- package/corpus/toolkit/src/chat-history/ChatHistoryList.tsx +469 -0
- package/corpus/toolkit/src/chat-history/index.ts +7 -0
- package/corpus/toolkit/src/chat-history.css +314 -0
- package/corpus/toolkit/src/collab-ui/AgentPresenceChip.tsx +69 -0
- package/corpus/toolkit/src/collab-ui/LiveCursorOverlay.tsx +241 -0
- package/corpus/toolkit/src/collab-ui/PresenceBar.tsx +346 -0
- package/corpus/toolkit/src/collab-ui/RecentEditHighlights.tsx +225 -0
- package/corpus/toolkit/src/collab-ui/RemoteSelectionRings.tsx +227 -0
- package/corpus/toolkit/src/collab-ui/agent-identity.ts +16 -0
- package/corpus/toolkit/src/collab-ui/index.ts +39 -0
- package/corpus/toolkit/src/collab-ui/lead-client.ts +32 -0
- package/corpus/toolkit/src/collab-ui/types.ts +96 -0
- package/corpus/toolkit/src/composer/AgentComposerFrame.tsx +59 -0
- package/corpus/toolkit/src/composer/ComposerPlusMenu.tsx +983 -0
- package/corpus/toolkit/src/composer/MentionPopover.tsx +412 -0
- package/corpus/toolkit/src/composer/PastedTextChip.tsx +150 -0
- package/corpus/toolkit/src/composer/PromptComposer.tsx +718 -0
- package/corpus/toolkit/src/composer/RealtimeVoiceMode.tsx +856 -0
- package/corpus/toolkit/src/composer/TiptapComposer.tsx +2936 -0
- package/corpus/toolkit/src/composer/VoiceButton.tsx +255 -0
- package/corpus/toolkit/src/composer/asset-picker-url.ts +80 -0
- package/corpus/toolkit/src/composer/attachment-accept.ts +71 -0
- package/corpus/toolkit/src/composer/draft-key.ts +7 -0
- package/corpus/toolkit/src/composer/extensions/FileReference.tsx +55 -0
- package/corpus/toolkit/src/composer/extensions/MentionReference.tsx +93 -0
- package/corpus/toolkit/src/composer/extensions/SkillReference.tsx +56 -0
- package/corpus/toolkit/src/composer/index.ts +79 -0
- package/corpus/toolkit/src/composer/pasted-text.ts +160 -0
- package/corpus/toolkit/src/composer/prompt-attachments.ts +93 -0
- package/corpus/toolkit/src/composer/realtime-voice-audio-level.ts +64 -0
- package/corpus/toolkit/src/composer/realtime-voice-transcript.ts +134 -0
- package/corpus/toolkit/src/composer/runtime-adapters.tsx +343 -0
- package/corpus/toolkit/src/composer/types.ts +66 -0
- package/corpus/toolkit/src/composer/use-file-search.ts +64 -0
- package/corpus/toolkit/src/composer/use-mention-search.ts +90 -0
- package/corpus/toolkit/src/composer/use-skills.ts +45 -0
- package/corpus/toolkit/src/composer/useRealtimeVoiceMode.tsx +2117 -0
- package/corpus/toolkit/src/composer/useVoiceDictation.ts +1191 -0
- package/corpus/toolkit/src/context-ui/ContextMeter.tsx +101 -0
- package/corpus/toolkit/src/context-ui/ContextSegmentRow.tsx +184 -0
- package/corpus/toolkit/src/context-ui/ContextTreemap.tsx +125 -0
- package/corpus/toolkit/src/context-ui/ContextXRayPanel.tsx +377 -0
- package/corpus/toolkit/src/context-ui/format.ts +42 -0
- package/corpus/toolkit/src/context-ui/index.ts +15 -0
- package/corpus/toolkit/src/context-ui/types.ts +43 -0
- package/corpus/toolkit/src/design-tweaks/index.ts +1 -0
- package/corpus/toolkit/src/design-tweaks/visual-style-controls.tsx +1084 -0
- package/corpus/toolkit/src/editor/BubbleToolbar.tsx +253 -0
- package/corpus/toolkit/src/editor/CodeBlockNode.tsx +254 -0
- package/corpus/toolkit/src/editor/DragHandle.ts +1437 -0
- package/corpus/toolkit/src/editor/ImageExtension.ts +306 -0
- package/corpus/toolkit/src/editor/RegistryBlockContext.tsx +107 -0
- package/corpus/toolkit/src/editor/RegistryBlockNode.tsx +652 -0
- package/corpus/toolkit/src/editor/RichMarkdownEditor.tsx +81 -0
- package/corpus/toolkit/src/editor/RunId.ts +61 -0
- package/corpus/toolkit/src/editor/SharedRichEditor.tsx +321 -0
- package/corpus/toolkit/src/editor/SlashCommandMenu.tsx +310 -0
- package/corpus/toolkit/src/editor/extensions.ts +402 -0
- package/corpus/toolkit/src/editor/gfmDoc.ts +92 -0
- package/corpus/toolkit/src/editor/index.ts +93 -0
- package/corpus/toolkit/src/editor/registrySlashCommands.ts +131 -0
- package/corpus/toolkit/src/editor/surgical-apply.ts +192 -0
- package/corpus/toolkit/src/editor/useCollabReconcile.ts +589 -0
- package/corpus/toolkit/src/editor.css +510 -0
- package/corpus/toolkit/src/hooks/index.ts +2 -0
- package/corpus/toolkit/src/hooks/use-mobile.tsx +21 -0
- package/corpus/toolkit/src/hooks/use-toast.ts +188 -0
- package/corpus/toolkit/src/index.ts +8 -0
- package/corpus/toolkit/src/onboarding/index.ts +1 -0
- package/corpus/toolkit/src/provider.tsx +47 -0
- package/corpus/toolkit/src/sharing/VisibilityBadge.tsx +37 -0
- package/corpus/toolkit/src/sharing/index.ts +4 -0
- package/corpus/toolkit/src/styles.css +21 -0
- package/corpus/toolkit/src/ui/accordion.tsx +56 -0
- package/corpus/toolkit/src/ui/alert-dialog.tsx +141 -0
- package/corpus/toolkit/src/ui/alert.tsx +59 -0
- package/corpus/toolkit/src/ui/aspect-ratio.tsx +5 -0
- package/corpus/toolkit/src/ui/avatar.tsx +48 -0
- package/corpus/toolkit/src/ui/badge.tsx +37 -0
- package/corpus/toolkit/src/ui/breadcrumb.tsx +115 -0
- package/corpus/toolkit/src/ui/button.tsx +78 -0
- package/corpus/toolkit/src/ui/calendar.tsx +214 -0
- package/corpus/toolkit/src/ui/card.tsx +86 -0
- package/corpus/toolkit/src/ui/carousel.tsx +260 -0
- package/corpus/toolkit/src/ui/chart.tsx +375 -0
- package/corpus/toolkit/src/ui/checkbox.tsx +28 -0
- package/corpus/toolkit/src/ui/collapsible.tsx +9 -0
- package/corpus/toolkit/src/ui/command.tsx +174 -0
- package/corpus/toolkit/src/ui/context-menu.tsx +202 -0
- package/corpus/toolkit/src/ui/date-picker.tsx +64 -0
- package/corpus/toolkit/src/ui/dialog.tsx +146 -0
- package/corpus/toolkit/src/ui/drawer.tsx +126 -0
- package/corpus/toolkit/src/ui/dropdown-menu.tsx +218 -0
- package/corpus/toolkit/src/ui/form.tsx +177 -0
- package/corpus/toolkit/src/ui/hover-card.tsx +27 -0
- package/corpus/toolkit/src/ui/index.ts +52 -0
- package/corpus/toolkit/src/ui/input-otp.tsx +69 -0
- package/corpus/toolkit/src/ui/input.tsx +22 -0
- package/corpus/toolkit/src/ui/label.tsx +24 -0
- package/corpus/toolkit/src/ui/logo.tsx +36 -0
- package/corpus/toolkit/src/ui/menubar.tsx +235 -0
- package/corpus/toolkit/src/ui/navigation-menu.tsx +128 -0
- package/corpus/toolkit/src/ui/pagination.tsx +121 -0
- package/corpus/toolkit/src/ui/popover.tsx +44 -0
- package/corpus/toolkit/src/ui/progress.tsx +26 -0
- package/corpus/toolkit/src/ui/radio-group.tsx +42 -0
- package/corpus/toolkit/src/ui/resizable.tsx +43 -0
- package/corpus/toolkit/src/ui/scroll-area.tsx +46 -0
- package/corpus/toolkit/src/ui/select.tsx +158 -0
- package/corpus/toolkit/src/ui/separator.tsx +29 -0
- package/corpus/toolkit/src/ui/sheet.tsx +160 -0
- package/corpus/toolkit/src/ui/sidebar.tsx +777 -0
- package/corpus/toolkit/src/ui/skeleton.tsx +18 -0
- package/corpus/toolkit/src/ui/slider.tsx +26 -0
- package/corpus/toolkit/src/ui/sonner.tsx +58 -0
- package/corpus/toolkit/src/ui/spinner.tsx +17 -0
- package/corpus/toolkit/src/ui/switch.tsx +27 -0
- package/corpus/toolkit/src/ui/table.tsx +117 -0
- package/corpus/toolkit/src/ui/tabs.tsx +53 -0
- package/corpus/toolkit/src/ui/textarea.tsx +23 -0
- package/corpus/toolkit/src/ui/toast.tsx +127 -0
- package/corpus/toolkit/src/ui/toaster.tsx +33 -0
- package/corpus/toolkit/src/ui/toggle-group.tsx +59 -0
- package/corpus/toolkit/src/ui/toggle.tsx +43 -0
- package/corpus/toolkit/src/ui/tooltip.tsx +53 -0
- package/corpus/toolkit/src/ui/use-toast.ts +3 -0
- package/corpus/toolkit/src/utils.ts +8 -0
- package/corpus/toolkit/tsconfig.json +23 -0
- package/dist/cli/atomic-json-file.d.ts +30 -0
- package/dist/cli/atomic-json-file.d.ts.map +1 -0
- package/dist/cli/atomic-json-file.js +240 -0
- package/dist/cli/atomic-json-file.js.map +1 -0
- package/dist/cli/claude-code-participant.d.ts +77 -0
- package/dist/cli/claude-code-participant.d.ts.map +1 -0
- package/dist/cli/claude-code-participant.js +355 -0
- package/dist/cli/claude-code-participant.js.map +1 -0
- package/dist/cli/code-agent-executor.d.ts.map +1 -1
- package/dist/cli/code-agent-executor.js +18 -10
- package/dist/cli/code-agent-executor.js.map +1 -1
- package/dist/cli/code-agent-runs.concurrent-worker.d.ts +2 -0
- package/dist/cli/code-agent-runs.concurrent-worker.d.ts.map +1 -0
- package/dist/cli/code-agent-runs.concurrent-worker.js +32 -0
- package/dist/cli/code-agent-runs.concurrent-worker.js.map +1 -0
- package/dist/cli/code-agent-runs.d.ts.map +1 -1
- package/dist/cli/code-agent-runs.js +43 -56
- package/dist/cli/code-agent-runs.js.map +1 -1
- package/dist/cli/codex-cli-participant.d.ts +77 -0
- package/dist/cli/codex-cli-participant.d.ts.map +1 -0
- package/dist/cli/codex-cli-participant.js +354 -0
- package/dist/cli/codex-cli-participant.js.map +1 -0
- package/dist/cli/eject.d.ts +21 -0
- package/dist/cli/eject.d.ts.map +1 -0
- package/dist/cli/eject.js +1145 -0
- package/dist/cli/eject.js.map +1 -0
- package/dist/cli/index.js +15 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/multi-frontier-runs.d.ts +115 -0
- package/dist/cli/multi-frontier-runs.d.ts.map +1 -1
- package/dist/cli/multi-frontier-runs.js +831 -113
- package/dist/cli/multi-frontier-runs.js.map +1 -1
- package/dist/client/chat/ChatHistoryList.d.ts +1 -87
- package/dist/client/chat/ChatHistoryList.d.ts.map +1 -1
- package/dist/client/chat/ChatHistoryList.js +1 -82
- package/dist/client/chat/ChatHistoryList.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/resources/McpConnectionSuggestion.d.ts +4 -3
- package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -1
- package/dist/client/resources/McpConnectionSuggestion.js +3 -3
- package/dist/client/resources/McpConnectionSuggestion.js.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.d.ts +4 -3
- package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +2 -2
- package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.d.ts +4 -1
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +9 -7
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/client/resources/index.d.ts +3 -0
- package/dist/client/resources/index.d.ts.map +1 -1
- package/dist/client/resources/index.js +3 -0
- package/dist/client/resources/index.js.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.d.ts +2 -1
- package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.js +6 -2
- package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
- package/dist/client/resources/runtime.d.ts +2 -0
- package/dist/client/resources/runtime.d.ts.map +1 -0
- package/dist/client/resources/runtime.js +2 -0
- package/dist/client/resources/runtime.js.map +1 -0
- package/dist/client/setup-connections/catalog.d.ts +2 -0
- package/dist/client/setup-connections/catalog.d.ts.map +1 -0
- package/dist/client/setup-connections/catalog.js +7 -0
- package/dist/client/setup-connections/catalog.js.map +1 -0
- package/dist/client/setup-connections/index.d.ts +1 -0
- package/dist/client/setup-connections/index.d.ts.map +1 -1
- package/dist/client/setup-connections/index.js +1 -0
- package/dist/client/setup-connections/index.js.map +1 -1
- package/dist/client/setup-connections/runtime.d.ts +2 -0
- package/dist/client/setup-connections/runtime.d.ts.map +1 -0
- package/dist/client/setup-connections/runtime.js +2 -0
- package/dist/client/setup-connections/runtime.js.map +1 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/connections/catalog.d.ts +5 -3
- package/dist/connections/catalog.d.ts.map +1 -1
- package/dist/connections/catalog.js +16 -8
- package/dist/connections/catalog.js.map +1 -1
- package/dist/connections/index.d.ts +1 -1
- package/dist/connections/index.d.ts.map +1 -1
- package/dist/connections/index.js +1 -1
- package/dist/connections/index.js.map +1 -1
- package/dist/connections/runtime.d.ts +2 -0
- package/dist/connections/runtime.d.ts.map +1 -0
- package/dist/connections/runtime.js +2 -0
- package/dist/connections/runtime.js.map +1 -0
- package/dist/eject/provider-api-definitions.d.ts +34 -0
- package/dist/eject/provider-api-definitions.d.ts.map +1 -0
- package/dist/eject/provider-api-definitions.js +88 -0
- package/dist/eject/provider-api-definitions.js.map +1 -0
- package/dist/eject/remote-mcp-presets.d.ts +17 -0
- package/dist/eject/remote-mcp-presets.d.ts.map +1 -0
- package/dist/eject/remote-mcp-presets.js +81 -0
- package/dist/eject/remote-mcp-presets.js.map +1 -0
- package/dist/eject/setup-readiness-ui.d.ts +7 -0
- package/dist/eject/setup-readiness-ui.d.ts.map +1 -0
- package/dist/eject/setup-readiness-ui.js +13 -0
- package/dist/eject/setup-readiness-ui.js.map +1 -0
- package/dist/eject/workspace-connections.d.ts +11 -0
- package/dist/eject/workspace-connections.d.ts.map +1 -0
- package/dist/eject/workspace-connections.js +47 -0
- package/dist/eject/workspace-connections.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/adapter-overrides.d.ts +3 -0
- package/dist/integrations/adapter-overrides.d.ts.map +1 -0
- package/dist/integrations/adapter-overrides.js +7 -0
- package/dist/integrations/adapter-overrides.js.map +1 -0
- package/dist/integrations/eject/messaging-adapters.d.ts +12 -0
- package/dist/integrations/eject/messaging-adapters.d.ts.map +1 -0
- package/dist/integrations/eject/messaging-adapters.js +49 -0
- package/dist/integrations/eject/messaging-adapters.js.map +1 -0
- package/dist/integrations/index.d.ts +5 -3
- package/dist/integrations/index.d.ts.map +1 -1
- package/dist/integrations/index.js +4 -2
- package/dist/integrations/index.js.map +1 -1
- package/dist/integrations/plugin.d.ts +32 -1
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +19 -12
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/integrations/runtime.d.ts +2 -0
- package/dist/integrations/runtime.d.ts.map +1 -0
- package/dist/integrations/runtime.js +2 -0
- package/dist/integrations/runtime.js.map +1 -0
- package/dist/integrations/slack-manifest.d.ts +1 -1
- package/dist/integrations/slack-oauth.d.ts +1 -1
- package/dist/integrations/slack-oauth.d.ts.map +1 -1
- package/dist/integrations/slack-oauth.js +1 -0
- package/dist/integrations/slack-oauth.js.map +1 -1
- package/dist/integrations/types.d.ts +3 -1
- package/dist/integrations/types.d.ts.map +1 -1
- package/dist/integrations/types.js.map +1 -1
- package/dist/integrations/webhook-delivery.d.ts +23 -0
- package/dist/integrations/webhook-delivery.d.ts.map +1 -0
- package/dist/integrations/webhook-delivery.js +32 -0
- package/dist/integrations/webhook-delivery.js.map +1 -0
- package/dist/notifications/channels.d.ts.map +1 -1
- package/dist/notifications/channels.js +4 -9
- package/dist/notifications/channels.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/package-lifecycle/eject-manifest.d.ts +43 -0
- package/dist/package-lifecycle/eject-manifest.d.ts.map +1 -0
- package/dist/package-lifecycle/eject-manifest.js +219 -0
- package/dist/package-lifecycle/eject-manifest.js.map +1 -0
- package/dist/package-lifecycle/index.d.ts +1 -0
- package/dist/package-lifecycle/index.d.ts.map +1 -1
- package/dist/package-lifecycle/index.js +1 -0
- package/dist/package-lifecycle/index.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +136 -0
- package/dist/provider-api/actions/custom-provider-registration.d.ts.map +1 -0
- package/dist/provider-api/actions/custom-provider-registration.js +154 -0
- package/dist/provider-api/actions/custom-provider-registration.js.map +1 -0
- package/dist/provider-api/actions/delete-staged-dataset.d.ts +1 -1
- package/dist/provider-api/actions/delete-staged-dataset.d.ts.map +1 -1
- package/dist/provider-api/actions/delete-staged-dataset.js +3 -34
- package/dist/provider-api/actions/delete-staged-dataset.js.map +1 -1
- package/dist/provider-api/actions/list-staged-datasets.d.ts +2 -3
- package/dist/provider-api/actions/list-staged-datasets.d.ts.map +1 -1
- package/dist/provider-api/actions/list-staged-datasets.js +3 -40
- package/dist/provider-api/actions/list-staged-datasets.js.map +1 -1
- package/dist/provider-api/actions/provider-api-audit.d.ts +7 -0
- package/dist/provider-api/actions/provider-api-audit.d.ts.map +1 -0
- package/dist/provider-api/actions/provider-api-audit.js +74 -0
- package/dist/provider-api/actions/provider-api-audit.js.map +1 -0
- package/dist/provider-api/actions/provider-api.d.ts +370 -0
- package/dist/provider-api/actions/provider-api.d.ts.map +1 -0
- package/dist/provider-api/actions/provider-api.js +280 -0
- package/dist/provider-api/actions/provider-api.js.map +1 -0
- package/dist/provider-api/actions/query-staged-dataset.d.ts +1 -1
- package/dist/provider-api/actions/query-staged-dataset.d.ts.map +1 -1
- package/dist/provider-api/actions/query-staged-dataset.js +3 -116
- package/dist/provider-api/actions/query-staged-dataset.js.map +1 -1
- package/dist/provider-api/actions/staged-datasets.d.ts +190 -0
- package/dist/provider-api/actions/staged-datasets.d.ts.map +1 -0
- package/dist/provider-api/actions/staged-datasets.js +210 -0
- package/dist/provider-api/actions/staged-datasets.js.map +1 -0
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/provider-api/gong.d.ts +44 -0
- package/dist/provider-api/gong.d.ts.map +1 -0
- package/dist/provider-api/gong.js +56 -0
- package/dist/provider-api/gong.js.map +1 -0
- package/dist/provider-api/index.d.ts +6 -2
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/provider-api/index.js +25 -12
- package/dist/provider-api/index.js.map +1 -1
- package/dist/provider-api/runtime.d.ts +2 -0
- package/dist/provider-api/runtime.d.ts.map +1 -0
- package/dist/provider-api/runtime.js +2 -0
- package/dist/provider-api/runtime.js.map +1 -0
- package/dist/scripts/docs/source-search.d.ts +2 -1
- package/dist/scripts/docs/source-search.d.ts.map +1 -1
- package/dist/scripts/docs/source-search.js +2 -1
- package/dist/scripts/docs/source-search.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/google-oauth-credentials.d.ts +35 -0
- package/dist/server/google-oauth-credentials.d.ts.map +1 -1
- package/dist/server/google-oauth-credentials.js +48 -2
- package/dist/server/google-oauth-credentials.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/setup-connections/index.d.ts +1 -1
- package/dist/setup-connections/index.d.ts.map +1 -1
- package/dist/setup-connections/index.js +1 -1
- package/dist/setup-connections/index.js.map +1 -1
- package/dist/shared/merge-by-id.d.ts +5 -0
- package/dist/shared/merge-by-id.d.ts.map +1 -0
- package/dist/shared/merge-by-id.js +7 -0
- package/dist/shared/merge-by-id.js.map +1 -0
- package/dist/styles/chat-history-list.css +1 -313
- package/dist/templates/chat/.agents/skills/actions/SKILL.md +32 -12
- package/dist/templates/chat/.agents/skills/agent-native-docs/SKILL.md +21 -9
- package/dist/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/dist/templates/chat/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/dist/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/dist/templates/chat/AGENTS.md +6 -2
- package/dist/templates/chat/app/components/layout/Sidebar.tsx +51 -157
- package/dist/templates/default/.agents/skills/actions/SKILL.md +32 -12
- package/dist/templates/default/.agents/skills/agent-native-docs/SKILL.md +21 -9
- package/dist/templates/default/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/dist/templates/default/.agents/skills/integration-webhooks/SKILL.md +323 -0
- package/dist/templates/default/.agents/skills/onboarding/SKILL.md +56 -0
- package/dist/templates/default/.agents/skills/secrets/SKILL.md +273 -0
- package/dist/templates/default/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/dist/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/dist/templates/default/AGENTS.md +26 -18
- package/dist/templates/headless/.agents/skills/actions/SKILL.md +512 -0
- package/dist/templates/headless/.agents/skills/agent-native-docs/SKILL.md +21 -9
- package/dist/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/dist/templates/headless/.agents/skills/integration-webhooks/SKILL.md +323 -0
- package/dist/templates/headless/.agents/skills/secrets/SKILL.md +273 -0
- package/dist/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/dist/templates/headless/AGENTS.md +8 -0
- package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -12
- package/dist/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +21 -9
- package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +7 -0
- package/dist/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/dist/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/dist/templates/workspace-core/AGENTS.md +4 -0
- package/dist/templates/workspace-root/AGENTS.md +4 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +22 -2
- package/dist/vite/client.js.map +1 -1
- package/docs/AGENTS.md +7 -0
- package/docs/content/agent-native-toolkit.mdx +38 -0
- package/docs/content/integrations.mdx +63 -2
- package/docs/content/package-lifecycle.mdx +54 -4
- package/docs/design/provider-integration-authority.md +117 -0
- package/package.json +20 -2
- package/src/a2a/agent-card.ts +83 -0
- package/src/a2a/artifact-response.ts +1648 -0
- package/src/a2a/auth-policy.ts +66 -0
- package/src/a2a/caller-auth.ts +102 -0
- package/src/a2a/client.ts +876 -0
- package/src/a2a/correlation.ts +50 -0
- package/src/a2a/handlers.ts +1285 -0
- package/src/a2a/index.ts +50 -0
- package/src/a2a/invoke.ts +348 -0
- package/src/a2a/response-text.ts +53 -0
- package/src/a2a/server.ts +586 -0
- package/src/a2a/task-store.ts +917 -0
- package/src/a2a/types.ts +221 -0
- package/src/a2a-claims.ts +52 -0
- package/src/action-change-marker.ts +74 -0
- package/src/action-ui.ts +38 -0
- package/src/action.ts +1406 -0
- package/src/adapters/cli/index.ts +6 -0
- package/src/adapters/cli/registry.ts +73 -0
- package/src/adapters/cli/shell-adapter.ts +92 -0
- package/src/adapters/cli/types.ts +27 -0
- package/src/agent/action-continuation-guidance.ts +38 -0
- package/src/agent/app-model-defaults.ts +201 -0
- package/src/agent/context-xray/actions/context-evict.ts +56 -0
- package/src/agent/context-xray/actions/context-manifest-get.ts +75 -0
- package/src/agent/context-xray/actions/context-pin.ts +56 -0
- package/src/agent/context-xray/actions/context-preview-get.ts +134 -0
- package/src/agent/context-xray/actions/context-report.ts +75 -0
- package/src/agent/context-xray/actions/context-restore.ts +51 -0
- package/src/agent/context-xray/actions/errors.ts +40 -0
- package/src/agent/context-xray/apply-directives.ts +137 -0
- package/src/agent/context-xray/directives-store.ts +178 -0
- package/src/agent/context-xray/identity.ts +37 -0
- package/src/agent/context-xray/manifest.ts +284 -0
- package/src/agent/context-xray/migrations.ts +32 -0
- package/src/agent/context-xray/plugin.ts +23 -0
- package/src/agent/context-xray/schema.ts +17 -0
- package/src/agent/context-xray/segments.ts +194 -0
- package/src/agent/context-xray/tokenize.ts +93 -0
- package/src/agent/default-model.ts +5 -0
- package/src/agent/durable-background.ts +547 -0
- package/src/agent/engine/ai-sdk-engine.ts +565 -0
- package/src/agent/engine/anthropic-engine.ts +320 -0
- package/src/agent/engine/builder-engine.ts +1110 -0
- package/src/agent/engine/builder-gateway-headers.ts +44 -0
- package/src/agent/engine/builtin.ts +122 -0
- package/src/agent/engine/context-directives-transform.ts +73 -0
- package/src/agent/engine/continuation-dispatch-retry.ts +168 -0
- package/src/agent/engine/credential-errors.ts +69 -0
- package/src/agent/engine/index.ts +54 -0
- package/src/agent/engine/openai-compatible-endpoint.ts +25 -0
- package/src/agent/engine/output-tokens.ts +181 -0
- package/src/agent/engine/provider-env-vars.ts +69 -0
- package/src/agent/engine/registry.ts +853 -0
- package/src/agent/engine/tool-call-journal-seed.ts +85 -0
- package/src/agent/engine/translate-ai-sdk.ts +407 -0
- package/src/agent/engine/translate-anthropic.ts +611 -0
- package/src/agent/engine/types.ts +310 -0
- package/src/agent/harness/acp-adapter.ts +893 -0
- package/src/agent/harness/acp-builtin.ts +70 -0
- package/src/agent/harness/ai-sdk-adapter.ts +509 -0
- package/src/agent/harness/background.ts +365 -0
- package/src/agent/harness/builtin.ts +50 -0
- package/src/agent/harness/index.ts +86 -0
- package/src/agent/harness/lifecycle.ts +498 -0
- package/src/agent/harness/registry.ts +97 -0
- package/src/agent/harness/runner.ts +182 -0
- package/src/agent/harness/store.ts +466 -0
- package/src/agent/harness/translate.ts +130 -0
- package/src/agent/harness/types.ts +129 -0
- package/src/agent/index.ts +32 -0
- package/src/agent/loop-settings.ts +186 -0
- package/src/agent/model-config.ts +363 -0
- package/src/agent/observational-memory/compactor.ts +78 -0
- package/src/agent/observational-memory/config.ts +78 -0
- package/src/agent/observational-memory/index.ts +79 -0
- package/src/agent/observational-memory/internal-run.ts +89 -0
- package/src/agent/observational-memory/message-text.ts +49 -0
- package/src/agent/observational-memory/migrations.ts +44 -0
- package/src/agent/observational-memory/observer.ts +128 -0
- package/src/agent/observational-memory/plugin.ts +35 -0
- package/src/agent/observational-memory/prompts.ts +53 -0
- package/src/agent/observational-memory/read.ts +131 -0
- package/src/agent/observational-memory/reflector.ts +119 -0
- package/src/agent/observational-memory/schema.ts +48 -0
- package/src/agent/observational-memory/store.ts +275 -0
- package/src/agent/observational-memory/types.ts +65 -0
- package/src/agent/processors.ts +209 -0
- package/src/agent/production-agent.ts +8254 -0
- package/src/agent/run-loop-with-resume.ts +394 -0
- package/src/agent/run-manager.ts +1825 -0
- package/src/agent/run-ownership.ts +75 -0
- package/src/agent/run-store.ts +2601 -0
- package/src/agent/runtime-context.ts +169 -0
- package/src/agent/thread-data-builder.ts +1584 -0
- package/src/agent/tool-call-journal.ts +368 -0
- package/src/agent/tool-error-redaction.ts +52 -0
- package/src/agent/tool-result-images.ts +178 -0
- package/src/agent/tool-search.ts +376 -0
- package/src/agent/types.ts +372 -0
- package/src/agent-native/index.ts +198 -0
- package/src/agent-web/config.ts +253 -0
- package/src/agent-web/generator.ts +382 -0
- package/src/agent-web/index.ts +36 -0
- package/src/appearance/actions/change-appearance.ts +35 -0
- package/src/application-state/emitter.ts +81 -0
- package/src/application-state/handlers.ts +121 -0
- package/src/application-state/index.ts +44 -0
- package/src/application-state/script-helpers.ts +155 -0
- package/src/application-state/store.ts +274 -0
- package/src/assets/branding/agent-native.icon/Assets/background.svg +3 -0
- package/src/assets/branding/agent-native.icon/Assets/foreground.svg +12 -0
- package/src/assets/branding/agent-native.icon/icon.json +49 -0
- package/src/assets/branding/favicon.png +0 -0
- package/src/assets/branding/favicon.svg +1 -0
- package/src/assets/branding/icon-on-dark.svg +10 -0
- package/src/assets/branding/icon-on-light.svg +10 -0
- package/src/assets/branding/mac-app-icon.svg +1 -0
- package/src/assets/branding/slack-bot/agent-native-1024.png +0 -0
- package/src/assets/branding/slack-bot/agent-native-512.png +0 -0
- package/src/assets/branding/tray-icon.svg +6 -0
- package/src/assets/branding/wordmark-on-dark.svg +21 -0
- package/src/assets/branding/wordmark-on-light.svg +21 -0
- package/src/assets/fonts/LICENSE_LIBERATION +96 -0
- package/src/assets/fonts/LICENSE_NOTO_NASKH_ARABIC +93 -0
- package/src/assets/fonts/LiberationSans-Bold.ttf +0 -0
- package/src/assets/fonts/LiberationSans-Regular.ttf +0 -0
- package/src/assets/fonts/NotoNaskhArabic-Variable.ttf +0 -0
- package/src/audit/actions/export-audit-events.ts +162 -0
- package/src/audit/actions/get-audit-event.ts +24 -0
- package/src/audit/actions/list-audit-events.ts +66 -0
- package/src/audit/cleanup-job.ts +100 -0
- package/src/audit/config.ts +90 -0
- package/src/audit/index.ts +43 -0
- package/src/audit/record.ts +180 -0
- package/src/audit/redact.ts +109 -0
- package/src/audit/store.ts +346 -0
- package/src/audit/types.ts +141 -0
- package/src/automation/index.ts +802 -0
- package/src/brand-kit/brand-signals.ts +124 -0
- package/src/brand-kit/fig/index.ts +64 -0
- package/src/brand-kit/index.ts +72 -0
- package/src/brand-kit/types.ts +113 -0
- package/src/browser-sessions/actions.ts +302 -0
- package/src/browser-sessions/routes.ts +234 -0
- package/src/browser-sessions/store.ts +679 -0
- package/src/browser-sessions/types.ts +84 -0
- package/src/changelog/parse.ts +328 -0
- package/src/chat-threads/emitter.ts +22 -0
- package/src/chat-threads/schema.ts +43 -0
- package/src/chat-threads/store.ts +1311 -0
- package/src/checkpoints/index.ts +15 -0
- package/src/checkpoints/service.ts +142 -0
- package/src/checkpoints/store.ts +153 -0
- package/src/cli/add.ts +425 -0
- package/src/cli/agent.ts +424 -0
- package/src/cli/agents.ts +122 -0
- package/src/cli/app-skill.ts +1455 -0
- package/src/cli/atomic-json-file.ts +324 -0
- package/src/cli/audit-agent-web.ts +324 -0
- package/src/cli/changelog.ts +199 -0
- package/src/cli/claude-code-participant.ts +515 -0
- package/src/cli/code-agent-commands.ts +270 -0
- package/src/cli/code-agent-connector.ts +1109 -0
- package/src/cli/code-agent-executor.ts +1970 -0
- package/src/cli/code-agent-output-smoother.ts +140 -0
- package/src/cli/code-agent-runs.concurrent-worker.ts +33 -0
- package/src/cli/code-agent-runs.ts +548 -0
- package/src/cli/code.ts +1805 -0
- package/src/cli/codex-cli-participant.ts +496 -0
- package/src/cli/connect.ts +2714 -0
- package/src/cli/content-local.ts +442 -0
- package/src/cli/context-xray-local.ts +2193 -0
- package/src/cli/create-workspace.ts +30 -0
- package/src/cli/create.ts +2247 -0
- package/src/cli/deploy-build.ts +50 -0
- package/src/cli/design-connect.ts +3187 -0
- package/src/cli/doctor.ts +472 -0
- package/src/cli/eject.ts +1594 -0
- package/src/cli/eval.ts +134 -0
- package/src/cli/gateway-helpers.ts +70 -0
- package/src/cli/index.ts +1217 -0
- package/src/cli/info.ts +105 -0
- package/src/cli/invoke.ts +288 -0
- package/src/cli/mcp-config-writers.ts +857 -0
- package/src/cli/mcp.ts +594 -0
- package/src/cli/migrate.ts +1813 -0
- package/src/cli/migration-codemod.ts +646 -0
- package/src/cli/multi-frontier-runs.ts +1952 -0
- package/src/cli/package-lifecycle.ts +823 -0
- package/src/cli/plan-blocks.ts +143 -0
- package/src/cli/plan-local.ts +3189 -0
- package/src/cli/plan-publish-store.ts +138 -0
- package/src/cli/pr-visual-recap-workflow.ts +2 -0
- package/src/cli/recap.ts +5 -0
- package/src/cli/setup-agents.ts +120 -0
- package/src/cli/skills-content/assets-skill.ts +83 -0
- package/src/cli/skills-content/canvas.ts +153 -0
- package/src/cli/skills-content/connection.ts +52 -0
- package/src/cli/skills-content/content-skill.ts +120 -0
- package/src/cli/skills-content/design-exploration-skill.ts +118 -0
- package/src/cli/skills-content/design-visual-edit-skill.ts +228 -0
- package/src/cli/skills-content/document-quality.ts +196 -0
- package/src/cli/skills-content/exemplar.ts +69 -0
- package/src/cli/skills-content/help.ts +77 -0
- package/src/cli/skills-content/index.ts +22 -0
- package/src/cli/skills-content/local-files.ts +100 -0
- package/src/cli/skills-content/plan-setup-auth.ts +80 -0
- package/src/cli/skills-content/visual-plan-skill.ts +493 -0
- package/src/cli/skills-content/visual-recap-skill.ts +553 -0
- package/src/cli/skills-content/visualize-repo-skill.ts +93 -0
- package/src/cli/skills-content/wireframe.ts +349 -0
- package/src/cli/skills.ts +4340 -0
- package/src/cli/sync-builder-starter-manifest.ts +631 -0
- package/src/cli/telemetry.ts +136 -0
- package/src/cli/templates-meta.ts +269 -0
- package/src/cli/upgrade.ts +956 -0
- package/src/cli/visualize-repo.ts +715 -0
- package/src/cli/workspace-dev.ts +1486 -0
- package/src/cli/workspacify.ts +258 -0
- package/src/client/AgentAskPopover.tsx +89 -0
- package/src/client/AgentChatHome.tsx +66 -0
- package/src/client/AgentNative.tsx +161 -0
- package/src/client/AgentNativeEmbedded.tsx +288 -0
- package/src/client/AgentNativeFrame.tsx +151 -0
- package/src/client/AgentPanel.tsx +3437 -0
- package/src/client/AgentTaskCard.tsx +273 -0
- package/src/client/AppearancePicker.tsx +79 -0
- package/src/client/AssistantChat.tsx +5793 -0
- package/src/client/ClientOnly.tsx +22 -0
- package/src/client/CommandMenu.tsx +726 -0
- package/src/client/ConnectBuilderCard.tsx +390 -0
- package/src/client/DefaultSpinner.tsx +47 -0
- package/src/client/ErrorBoundary.tsx +359 -0
- package/src/client/ErrorReportActions.tsx +69 -0
- package/src/client/FeedbackButton.tsx +553 -0
- package/src/client/HighlightedCodeBlock.tsx +173 -0
- package/src/client/IframeEmbed.tsx +151 -0
- package/src/client/KeepTabOpenNotice.tsx +181 -0
- package/src/client/MultiTabAssistantChat.tsx +2576 -0
- package/src/client/NewWorkspaceAppFlow.tsx +525 -0
- package/src/client/PoweredByBadge.tsx +284 -0
- package/src/client/RunStuckBanner.tsx +431 -0
- package/src/client/StarfieldBackground.tsx +466 -0
- package/src/client/Turnstile.tsx +125 -0
- package/src/client/active-run-state.ts +139 -0
- package/src/client/agent-chat/index.ts +279 -0
- package/src/client/agent-chat-adapter.ts +3692 -0
- package/src/client/agent-chat.ts +1233 -0
- package/src/client/agent-engine-key.ts +122 -0
- package/src/client/agent-page/AgentContextTab.tsx +487 -0
- package/src/client/agent-page/AgentEmptyState.tsx +44 -0
- package/src/client/agent-page/AgentJobsTab.tsx +441 -0
- package/src/client/agent-page/AgentTabFrame.tsx +39 -0
- package/src/client/agent-page/AgentTabsPage.tsx +1096 -0
- package/src/client/agent-page/types.ts +18 -0
- package/src/client/agent-page/use-jobs.ts +150 -0
- package/src/client/agent-sidebar-state.ts +166 -0
- package/src/client/analytics/index.ts +40 -0
- package/src/client/analytics-session.ts +70 -0
- package/src/client/analytics.ts +1757 -0
- package/src/client/api-path.ts +124 -0
- package/src/client/app-providers.tsx +230 -0
- package/src/client/appearance.ts +130 -0
- package/src/client/application-state.ts +154 -0
- package/src/client/assistant-ui-recovery.tsx +205 -0
- package/src/client/automation/index.ts +4 -0
- package/src/client/automation.ts +22 -0
- package/src/client/blocks/AiEditableField.tsx +30 -0
- package/src/client/blocks/BlockView.tsx +145 -0
- package/src/client/blocks/RegistryBlockDataProvider.tsx +121 -0
- package/src/client/blocks/SchemaBlockEditor.tsx +457 -0
- package/src/client/blocks/agent.ts +95 -0
- package/src/client/blocks/code-block-direction.ts +21 -0
- package/src/client/blocks/index.ts +308 -0
- package/src/client/blocks/library/AnnotatedCodeBlock.tsx +710 -0
- package/src/client/blocks/library/ApiEndpointBlock.tsx +1120 -0
- package/src/client/blocks/library/DataModelBlock.tsx +775 -0
- package/src/client/blocks/library/DiffBlock.tsx +1854 -0
- package/src/client/blocks/library/FileTreeBlock.tsx +868 -0
- package/src/client/blocks/library/HighlightedCode.tsx +260 -0
- package/src/client/blocks/library/JsonExplorerBlock.tsx +608 -0
- package/src/client/blocks/library/MermaidBlock.tsx +362 -0
- package/src/client/blocks/library/OpenApiSpecBlock.tsx +998 -0
- package/src/client/blocks/library/annotated-code.config.ts +101 -0
- package/src/client/blocks/library/annotation-rail.tsx +1020 -0
- package/src/client/blocks/library/api-endpoint.config.ts +207 -0
- package/src/client/blocks/library/block-copy.ts +399 -0
- package/src/client/blocks/library/callout.config.ts +57 -0
- package/src/client/blocks/library/callout.tsx +149 -0
- package/src/client/blocks/library/checklist.config.ts +56 -0
- package/src/client/blocks/library/checklist.tsx +208 -0
- package/src/client/blocks/library/code-filename-label.tsx +59 -0
- package/src/client/blocks/library/code-highlight.tsx +178 -0
- package/src/client/blocks/library/code-tabs.config.ts +70 -0
- package/src/client/blocks/library/code-tabs.tsx +606 -0
- package/src/client/blocks/library/code.config.ts +69 -0
- package/src/client/blocks/library/code.tsx +440 -0
- package/src/client/blocks/library/columns.config.ts +85 -0
- package/src/client/blocks/library/columns.tsx +334 -0
- package/src/client/blocks/library/data-model.config.ts +152 -0
- package/src/client/blocks/library/dev-doc-ui.tsx +191 -0
- package/src/client/blocks/library/diagram.config.ts +222 -0
- package/src/client/blocks/library/diagram.tsx +958 -0
- package/src/client/blocks/library/diff.config.ts +115 -0
- package/src/client/blocks/library/file-tree.config.ts +92 -0
- package/src/client/blocks/library/html.config.ts +79 -0
- package/src/client/blocks/library/html.tsx +258 -0
- package/src/client/blocks/library/json-explorer.config.ts +74 -0
- package/src/client/blocks/library/mermaid.config.ts +49 -0
- package/src/client/blocks/library/narrow-container.tsx +37 -0
- package/src/client/blocks/library/openapi-spec.config.ts +66 -0
- package/src/client/blocks/library/question-form.config.ts +127 -0
- package/src/client/blocks/library/question-form.tsx +944 -0
- package/src/client/blocks/library/sanitize-html.ts +343 -0
- package/src/client/blocks/library/server-specs.ts +338 -0
- package/src/client/blocks/library/specs.tsx +308 -0
- package/src/client/blocks/library/table.config.ts +61 -0
- package/src/client/blocks/library/table.tsx +449 -0
- package/src/client/blocks/library/tabs.config.ts +96 -0
- package/src/client/blocks/library/tabs.tsx +665 -0
- package/src/client/blocks/library/wireframe-icons.ts +210 -0
- package/src/client/blocks/library/wireframe-kit.tsx +1850 -0
- package/src/client/blocks/library/wireframe.config.ts +539 -0
- package/src/client/blocks/library/wireframe.tsx +535 -0
- package/src/client/blocks/mdx.ts +364 -0
- package/src/client/blocks/provider.tsx +51 -0
- package/src/client/blocks/registry.ts +83 -0
- package/src/client/blocks/schema-form/introspect.ts +201 -0
- package/src/client/blocks/server.ts +170 -0
- package/src/client/blocks/types.ts +415 -0
- package/src/client/browser-session-bridge.ts +548 -0
- package/src/client/browser-tab-id.ts +36 -0
- package/src/client/builder-frame.ts +194 -0
- package/src/client/builder-mark.tsx +21 -0
- package/src/client/changelog/Changelog.tsx +333 -0
- package/src/client/changelog/index.ts +1 -0
- package/src/client/chat/ChatHistoryList.tsx +7 -0
- package/src/client/chat/attachment-adapters.ts +426 -0
- package/src/client/chat/connectors.ts +1579 -0
- package/src/client/chat/index.ts +160 -0
- package/src/client/chat/markdown-renderer.tsx +693 -0
- package/src/client/chat/message-components.tsx +1326 -0
- package/src/client/chat/repo-helpers.ts +281 -0
- package/src/client/chat/run-recovery.tsx +994 -0
- package/src/client/chat/runtime.ts +1810 -0
- package/src/client/chat/tool-call-display.tsx +1270 -0
- package/src/client/chat/tool-render-registry.tsx +144 -0
- package/src/client/chat/use-agent-chat-lifecycle-tracking.ts +74 -0
- package/src/client/chat/use-reconnect-reader-owner.ts +28 -0
- package/src/client/chat/widgets/DataChartRenderer.tsx +242 -0
- package/src/client/chat/widgets/DataChartWidget.tsx +51 -0
- package/src/client/chat/widgets/DataInsightsWidget.tsx +59 -0
- package/src/client/chat/widgets/DataTableWidget.tsx +240 -0
- package/src/client/chat/widgets/InlineExtensionWidget.tsx +60 -0
- package/src/client/chat/widgets/builtin-tool-renderers.tsx +213 -0
- package/src/client/chat/widgets/data-widget-types.ts +1 -0
- package/src/client/chat/widgets/inline-extension-result.ts +86 -0
- package/src/client/chat-model-groups.ts +171 -0
- package/src/client/chat-thread-url.ts +11 -0
- package/src/client/chat-view-transition.ts +213 -0
- package/src/client/client-action.ts +21 -0
- package/src/client/client-bootstrap.ts +7 -0
- package/src/client/client-surface.ts +41 -0
- package/src/client/clipboard.ts +89 -0
- package/src/client/code-agent-chat-adapter.ts +537 -0
- package/src/client/collab/index.ts +60 -0
- package/src/client/command-navigation/index.ts +15 -0
- package/src/client/comments-review/index.ts +1 -0
- package/src/client/components/AgentPresenceChip.tsx +4 -0
- package/src/client/components/ApiKeySettings.tsx +309 -0
- package/src/client/components/CodeAgentIndicator.tsx +59 -0
- package/src/client/components/CodeRequiredDialog.tsx +470 -0
- package/src/client/components/LiveCursorOverlay.tsx +5 -0
- package/src/client/components/MissingKeyCard.tsx +61 -0
- package/src/client/components/PresenceBar.tsx +4 -0
- package/src/client/components/RecentEditHighlights.tsx +4 -0
- package/src/client/components/RemoteSelectionRings.tsx +5 -0
- package/src/client/components/icons/AgentNativeIcon.tsx +46 -0
- package/src/client/components/ui/dialog.tsx +1 -0
- package/src/client/components/ui/dropdown-menu.tsx +1 -0
- package/src/client/components/ui/hover-card.tsx +1 -0
- package/src/client/components/ui/message-scroller.tsx +131 -0
- package/src/client/components/ui/popover.tsx +1 -0
- package/src/client/components/ui/sheet.tsx +1 -0
- package/src/client/components/ui/tooltip.tsx +1 -0
- package/src/client/composer/index.ts +11 -0
- package/src/client/composer/runtime-adapters.tsx +123 -0
- package/src/client/composer/use-mention-search.ts +7 -0
- package/src/client/composer/wired-components.tsx +53 -0
- package/src/client/context-xray/ContextMeter.tsx +124 -0
- package/src/client/context-xray/ContextSegmentRow.tsx +27 -0
- package/src/client/context-xray/ContextTreemap.tsx +24 -0
- package/src/client/context-xray/ContextXRayPanel.tsx +59 -0
- package/src/client/context-xray/SegmentProvenancePopover.tsx +18 -0
- package/src/client/context-xray/format.ts +27 -0
- package/src/client/conversation/AgentConversation.tsx +555 -0
- package/src/client/conversation/code-agent-transcript.ts +283 -0
- package/src/client/conversation/index.ts +22 -0
- package/src/client/conversation/types.ts +85 -0
- package/src/client/conversation/use-near-bottom-autoscroll.ts +278 -0
- package/src/client/create-query-client.ts +86 -0
- package/src/client/db-admin/DataGrid.tsx +551 -0
- package/src/client/db-admin/DbAdminPage.tsx +218 -0
- package/src/client/db-admin/DevDatabaseLink.tsx +16 -0
- package/src/client/db-admin/EditableCell.tsx +451 -0
- package/src/client/db-admin/FilterBar.tsx +203 -0
- package/src/client/db-admin/ResultsGrid.tsx +114 -0
- package/src/client/db-admin/RowSidePanel.tsx +337 -0
- package/src/client/db-admin/SqlEditor.tsx +840 -0
- package/src/client/db-admin/TableBrowser.tsx +212 -0
- package/src/client/db-admin/TableEditor.tsx +760 -0
- package/src/client/db-admin/cell-format.ts +246 -0
- package/src/client/db-admin/changeset.ts +296 -0
- package/src/client/db-admin/export-utils.ts +78 -0
- package/src/client/db-admin/index.ts +33 -0
- package/src/client/db-admin/sql-storage.ts +134 -0
- package/src/client/db-admin/storage.ts +68 -0
- package/src/client/db-admin/useAgentSync.ts +226 -0
- package/src/client/db-admin/useDbAdmin.ts +294 -0
- package/src/client/dev-overlay/DevOverlay.tsx +576 -0
- package/src/client/dev-overlay/builtins.ts +40 -0
- package/src/client/dev-overlay/index.ts +23 -0
- package/src/client/dev-overlay/registry.ts +67 -0
- package/src/client/dev-overlay/types.ts +64 -0
- package/src/client/dev-overlay/use-dev-option.ts +78 -0
- package/src/client/dev-overlay/use-dev-overlay-shortcut.ts +31 -0
- package/src/client/dynamic-suggestions.ts +455 -0
- package/src/client/embed-auth.ts +544 -0
- package/src/client/embed.ts +54 -0
- package/src/client/error-capture.ts +690 -0
- package/src/client/error-format.ts +211 -0
- package/src/client/error-reporting.ts +100 -0
- package/src/client/extensions/AgentNativeExtensionFrame.e2e-host.tsx +136 -0
- package/src/client/extensions/AgentNativeExtensionFrame.tsx +708 -0
- package/src/client/extensions/EmbeddedExtension.tsx +604 -0
- package/src/client/extensions/ExtensionEditor.tsx +450 -0
- package/src/client/extensions/ExtensionQueryErrorState.tsx +47 -0
- package/src/client/extensions/ExtensionSlot.tsx +345 -0
- package/src/client/extensions/ExtensionViewer.tsx +1502 -0
- package/src/client/extensions/ExtensionViewerPage.tsx +32 -0
- package/src/client/extensions/ExtensionsListPage.tsx +432 -0
- package/src/client/extensions/ExtensionsSidebarSection.tsx +1259 -0
- package/src/client/extensions/InlineExtensionFrame.tsx +660 -0
- package/src/client/extensions/agent-native-extension-runtime.ts +495 -0
- package/src/client/extensions/delete-extension.ts +76 -0
- package/src/client/extensions/extension-load-error.ts +26 -0
- package/src/client/extensions/extension-order.ts +49 -0
- package/src/client/extensions/extension-popularity.ts +61 -0
- package/src/client/extensions/iframe-bridge.ts +377 -0
- package/src/client/extensions/index.ts +84 -0
- package/src/client/extensions/portable-extension.ts +712 -0
- package/src/client/feature-flags/FeatureFlagsPanel.tsx +519 -0
- package/src/client/feature-flags/helpers.ts +77 -0
- package/src/client/feature-flags/index.ts +13 -0
- package/src/client/feature-flags/types.ts +29 -0
- package/src/client/feature-flags/use-feature-flag.ts +26 -0
- package/src/client/feedback-context.ts +90 -0
- package/src/client/frame-protocol.ts +168 -0
- package/src/client/frame.ts +329 -0
- package/src/client/guided-questions.tsx +1152 -0
- package/src/client/history/VersionHistoryPanel.tsx +175 -0
- package/src/client/history/index.ts +21 -0
- package/src/client/history/use-history.ts +114 -0
- package/src/client/hooks/index.ts +46 -0
- package/src/client/host/index.ts +170 -0
- package/src/client/host-bridge.ts +1272 -0
- package/src/client/host-tools.ts +190 -0
- package/src/client/i18n.tsx +798 -0
- package/src/client/index.ts +370 -0
- package/src/client/integrations/IntegrationCard.tsx +186 -0
- package/src/client/integrations/IntegrationsPanel.tsx +633 -0
- package/src/client/integrations/api.ts +295 -0
- package/src/client/integrations/index.ts +29 -0
- package/src/client/integrations/useIntegrationStatus.ts +52 -0
- package/src/client/mcp-app-host.ts +720 -0
- package/src/client/mcp-apps/McpAppRenderer.tsx +760 -0
- package/src/client/navigation/index.ts +54 -0
- package/src/client/notifications/NotificationsBell.tsx +383 -0
- package/src/client/notifications/index.ts +1 -0
- package/src/client/observability/ObservabilityDashboard.tsx +890 -0
- package/src/client/observability/ThumbsFeedback.tsx +160 -0
- package/src/client/observability/index.ts +20 -0
- package/src/client/observability/useObservability.ts +266 -0
- package/src/client/onboarding/OnboardingBanner.tsx +73 -0
- package/src/client/onboarding/OnboardingPanel.tsx +1036 -0
- package/src/client/onboarding/SetupButton.tsx +64 -0
- package/src/client/onboarding/index.ts +25 -0
- package/src/client/onboarding/use-onboarding.ts +164 -0
- package/src/client/onboarding/use-preview-mode.ts +40 -0
- package/src/client/org/InvitationBanner.tsx +148 -0
- package/src/client/org/OrgSwitcher.tsx +754 -0
- package/src/client/org/RequireActiveOrg.tsx +337 -0
- package/src/client/org/TeamPage.tsx +1432 -0
- package/src/client/org/hooks.ts +359 -0
- package/src/client/org/index.ts +70 -0
- package/src/client/org/workspace-app-links.ts +367 -0
- package/src/client/org-team/index.ts +1 -0
- package/src/client/progress/RunsTray.tsx +680 -0
- package/src/client/progress/index.ts +1 -0
- package/src/client/require-session.tsx +142 -0
- package/src/client/resources/BuiltinCapabilityDetail.tsx +258 -0
- package/src/client/resources/McpConnectionSuggestion.tsx +276 -0
- package/src/client/resources/McpIntegrationDialog.tsx +808 -0
- package/src/client/resources/McpServerDetail.tsx +230 -0
- package/src/client/resources/ResourceEditor.tsx +872 -0
- package/src/client/resources/ResourceTree.tsx +860 -0
- package/src/client/resources/ResourcesPanel.tsx +1960 -0
- package/src/client/resources/index.ts +25 -0
- package/src/client/resources/mcp-integration-catalog.ts +771 -0
- package/src/client/resources/mcp-integration-logos.ts +108 -0
- package/src/client/resources/runtime.ts +1 -0
- package/src/client/resources/use-builtin-capabilities.ts +113 -0
- package/src/client/resources/use-mcp-servers.ts +217 -0
- package/src/client/resources/use-resources.ts +402 -0
- package/src/client/review/ReviewCommentComposer.tsx +123 -0
- package/src/client/review/ReviewStatusBadge.tsx +39 -0
- package/src/client/review/ReviewThreadPanel.tsx +625 -0
- package/src/client/review/index.ts +38 -0
- package/src/client/review/use-review.ts +186 -0
- package/src/client/route-chunk-recovery/index.ts +1 -0
- package/src/client/route-chunk-recovery.ts +331 -0
- package/src/client/route-state.ts +455 -0
- package/src/client/route-warmup.tsx +543 -0
- package/src/client/session-replay-iframe.e2e-host.tsx +100 -0
- package/src/client/session-replay.ts +3417 -0
- package/src/client/settings/AgentsSection.tsx +441 -0
- package/src/client/settings/AutomationsSection.tsx +516 -0
- package/src/client/settings/BackgroundAgentSection.tsx +189 -0
- package/src/client/settings/BrowserSection.tsx +110 -0
- package/src/client/settings/DemoModeSection.tsx +48 -0
- package/src/client/settings/SecretsSection.tsx +875 -0
- package/src/client/settings/SettingsPanel.tsx +3162 -0
- package/src/client/settings/SettingsSection.tsx +283 -0
- package/src/client/settings/SettingsTabsPage.tsx +592 -0
- package/src/client/settings/UsageSection.tsx +401 -0
- package/src/client/settings/VoiceTranscriptionSection.tsx +960 -0
- package/src/client/settings/agent-settings-search.ts +220 -0
- package/src/client/settings/index.ts +27 -0
- package/src/client/settings/useBuilderStatus.ts +1055 -0
- package/src/client/setup-connections/BuilderConnectCard.tsx +89 -0
- package/src/client/setup-connections/ProviderReadinessBadge.tsx +73 -0
- package/src/client/setup-connections/SetupConnectionsPage.tsx +94 -0
- package/src/client/setup-connections/catalog.ts +6 -0
- package/src/client/setup-connections/index.ts +47 -0
- package/src/client/setup-connections/runtime.ts +1 -0
- package/src/client/sharing/ShareButton.tsx +1748 -0
- package/src/client/sharing/ShareDialog.tsx +846 -0
- package/src/client/sharing/VisibilityBadge.tsx +4 -0
- package/src/client/sharing/index.ts +2 -0
- package/src/client/sse-event-processor.ts +2002 -0
- package/src/client/terminal/AgentTerminal.tsx +488 -0
- package/src/client/terminal/index.ts +7 -0
- package/src/client/theme.ts +27 -0
- package/src/client/tombstone/agent-presence-chip.ts +17 -0
- package/src/client/tombstone/editor.ts +265 -0
- package/src/client/tombstone/live-cursor-overlay.ts +21 -0
- package/src/client/tombstone/presence-bar.ts +17 -0
- package/src/client/tombstone/recent-edit-highlights.ts +17 -0
- package/src/client/tombstone/remote-selection-rings.ts +21 -0
- package/src/client/tombstone/rich-markdown-editor.ts +265 -0
- package/src/client/tombstone/ui-dialog.ts +49 -0
- package/src/client/tombstone/ui-dropdown-menu.ts +69 -0
- package/src/client/tombstone/ui-hover-card.ts +21 -0
- package/src/client/tombstone/ui-popover.ts +25 -0
- package/src/client/tombstone/ui-sheet.ts +49 -0
- package/src/client/tombstone/ui-tooltip.ts +29 -0
- package/src/client/tombstone/visual-style-controls.ts +61 -0
- package/src/client/tool-cells/BashCell.tsx +164 -0
- package/src/client/tool-cells/EditCell.tsx +303 -0
- package/src/client/tool-cells/FilesChangedSummary.tsx +208 -0
- package/src/client/tool-cells/WriteCell.tsx +145 -0
- package/src/client/tool-cells/index.ts +7 -0
- package/src/client/tool-display.ts +41 -0
- package/src/client/track.ts +57 -0
- package/src/client/transcription/BuilderTranscriptionCta.tsx +120 -0
- package/src/client/transcription/use-live-transcription.ts +292 -0
- package/src/client/ui/index.ts +55 -0
- package/src/client/uploads/index.ts +6 -0
- package/src/client/uploads/upload-editor-image.ts +79 -0
- package/src/client/uploads/use-upload-resource.ts +24 -0
- package/src/client/url-scrub.ts +51 -0
- package/src/client/use-action.ts +766 -0
- package/src/client/use-agent-chat-context.ts +63 -0
- package/src/client/use-agent-chat-home-handoff.ts +207 -0
- package/src/client/use-agent-chat.ts +53 -0
- package/src/client/use-agent-engine-configured.ts +211 -0
- package/src/client/use-avatar.ts +127 -0
- package/src/client/use-change-version.ts +150 -0
- package/src/client/use-chat-models.ts +252 -0
- package/src/client/use-chat-threads.ts +1262 -0
- package/src/client/use-db-sync.ts +1112 -0
- package/src/client/use-demo-mode-status.ts +30 -0
- package/src/client/use-dev-mode.ts +177 -0
- package/src/client/use-external-value.ts +40 -0
- package/src/client/use-pausing-interval.ts +65 -0
- package/src/client/use-pinch-zoom.ts +202 -0
- package/src/client/use-run-stuck-detection.ts +427 -0
- package/src/client/use-send-to-agent-chat.ts +85 -0
- package/src/client/use-session.ts +114 -0
- package/src/client/utils.ts +7 -0
- package/src/client/visual-style-controls.tsx +1 -0
- package/src/client/vite-dev-recovery-script.ts +226 -0
- package/src/client/voice-provider-status.ts +44 -0
- package/src/client/widgets/index.ts +41 -0
- package/src/code-agents/background-controller.ts +398 -0
- package/src/code-agents/background-run.ts +156 -0
- package/src/code-agents/index.ts +95 -0
- package/src/code-agents/prompt-attachments.ts +39 -0
- package/src/code-agents/transcript-normalizer.ts +750 -0
- package/src/code-agents/transcript-order.ts +73 -0
- package/src/coding-tools/index.ts +742 -0
- package/src/coding-tools/run-code.ts +1662 -0
- package/src/coding-tools/sandbox/adapter.ts +82 -0
- package/src/coding-tools/sandbox/background.ts +406 -0
- package/src/coding-tools/sandbox/executions-store.ts +517 -0
- package/src/coding-tools/sandbox/index.ts +132 -0
- package/src/coding-tools/sandbox/local-child-process-adapter.ts +217 -0
- package/src/collab/agent-identity.ts +5 -0
- package/src/collab/agent-presence.ts +341 -0
- package/src/collab/awareness-store.ts +196 -0
- package/src/collab/awareness.ts +317 -0
- package/src/collab/client-struct.ts +366 -0
- package/src/collab/client.ts +1205 -0
- package/src/collab/emitter.ts +31 -0
- package/src/collab/follow-mode.ts +107 -0
- package/src/collab/index.ts +151 -0
- package/src/collab/json-to-yjs.ts +504 -0
- package/src/collab/presence.ts +207 -0
- package/src/collab/recent-edits.ts +215 -0
- package/src/collab/routes.ts +191 -0
- package/src/collab/storage.ts +228 -0
- package/src/collab/struct-routes.ts +133 -0
- package/src/collab/text-to-yjs.ts +79 -0
- package/src/collab/undo.ts +432 -0
- package/src/collab/xml-ops.ts +68 -0
- package/src/collab/ydoc-manager.ts +544 -0
- package/src/command-navigation/actions.ts +100 -0
- package/src/command-navigation/index.ts +12 -0
- package/src/comments-review/index.ts +1 -0
- package/src/connections/catalog.ts +451 -0
- package/src/connections/index.ts +48 -0
- package/src/connections/reader.ts +911 -0
- package/src/connections/runtime.ts +1 -0
- package/src/credentials/index.ts +187 -0
- package/src/data-programs/actions.ts +335 -0
- package/src/data-programs/contract.ts +284 -0
- package/src/data-programs/execute.ts +693 -0
- package/src/data-programs/index.ts +72 -0
- package/src/data-programs/schema.ts +148 -0
- package/src/data-programs/store.ts +755 -0
- package/src/data-widgets/index.ts +376 -0
- package/src/db/client.ts +1627 -0
- package/src/db/create-get-db.ts +579 -0
- package/src/db/ddl-guard.ts +302 -0
- package/src/db/drizzle-config.ts +190 -0
- package/src/db/ensure-additive-columns.ts +430 -0
- package/src/db/index.ts +57 -0
- package/src/db/migrations.ts +691 -0
- package/src/db/request-telemetry.ts +154 -0
- package/src/db/runtime-diagnostics.ts +331 -0
- package/src/db/schema.ts +121 -0
- package/src/db/widen-columns.ts +75 -0
- package/src/db-admin/agent-tools.ts +202 -0
- package/src/db-admin/index.ts +26 -0
- package/src/db-admin/operations.ts +803 -0
- package/src/db-admin/routes.ts +174 -0
- package/src/db-admin/types.ts +110 -0
- package/src/demo/browser-state.ts +47 -0
- package/src/demo/fetch-interceptor.ts +175 -0
- package/src/demo/redact.ts +575 -0
- package/src/deploy/build.ts +3935 -0
- package/src/deploy/immutable-assets.ts +65 -0
- package/src/deploy/route-discovery.ts +458 -0
- package/src/deploy/workspace-core.ts +282 -0
- package/src/deploy/workspace-deploy.ts +1488 -0
- package/src/eject/provider-api-definitions.ts +193 -0
- package/src/eject/remote-mcp-presets.ts +137 -0
- package/src/eject/setup-readiness-ui.tsx +29 -0
- package/src/eject/workspace-connections.ts +92 -0
- package/src/embedding/agent.ts +159 -0
- package/src/embedding/bridge.ts +297 -0
- package/src/embedding/index.ts +43 -0
- package/src/embedding/protocol.ts +185 -0
- package/src/embedding/react.tsx +319 -0
- package/src/embeddings/index.ts +233 -0
- package/src/eval/agent-runner.ts +210 -0
- package/src/eval/define-eval.ts +55 -0
- package/src/eval/index.ts +49 -0
- package/src/eval/report.ts +75 -0
- package/src/eval/runner.ts +369 -0
- package/src/eval/scorer.ts +244 -0
- package/src/eval/types.ts +188 -0
- package/src/event-bus/bus.ts +140 -0
- package/src/event-bus/index.ts +3 -0
- package/src/event-bus/registry.ts +79 -0
- package/src/event-bus/types.ts +29 -0
- package/src/extensions/actions.ts +1636 -0
- package/src/extensions/change-marker.ts +54 -0
- package/src/extensions/content-patch.ts +468 -0
- package/src/extensions/fetch-tool.ts +461 -0
- package/src/extensions/html-shell.ts +803 -0
- package/src/extensions/local.ts +462 -0
- package/src/extensions/path.ts +40 -0
- package/src/extensions/proxy-security.ts +171 -0
- package/src/extensions/routes.ts +1080 -0
- package/src/extensions/schema.ts +269 -0
- package/src/extensions/session-replay-iframe.ts +131 -0
- package/src/extensions/slots/routes.ts +126 -0
- package/src/extensions/slots/schema.ts +87 -0
- package/src/extensions/slots/store.ts +450 -0
- package/src/extensions/store.ts +1347 -0
- package/src/extensions/theme.ts +114 -0
- package/src/extensions/url-safety.ts +316 -0
- package/src/extensions/web-content.ts +663 -0
- package/src/extensions/web-search-tool.ts +437 -0
- package/src/feature-flags/a2a-action-route.ts +56 -0
- package/src/feature-flags/actions/get-feature-flags.ts +24 -0
- package/src/feature-flags/actions/list-feature-flags.ts +69 -0
- package/src/feature-flags/actions/set-feature-flag.ts +89 -0
- package/src/feature-flags/index.ts +21 -0
- package/src/feature-flags/permissions.ts +53 -0
- package/src/feature-flags/plugin.ts +44 -0
- package/src/feature-flags/registry.ts +96 -0
- package/src/feature-flags/store.ts +176 -0
- package/src/file-upload/actions/upload-image.ts +213 -0
- package/src/file-upload/builder.ts +431 -0
- package/src/file-upload/index.ts +24 -0
- package/src/file-upload/pre-upload-attachments.ts +305 -0
- package/src/file-upload/registry.ts +125 -0
- package/src/file-upload/types.ts +89 -0
- package/src/guards/db-tool-scoping.ts +129 -0
- package/src/guards/index.ts +23 -0
- package/src/guards/no-drizzle-push.ts +116 -0
- package/src/guards/no-env-credentials.ts +229 -0
- package/src/guards/no-env-mutation.ts +114 -0
- package/src/guards/no-localhost-fallback.ts +124 -0
- package/src/guards/no-unscoped-credentials.ts +227 -0
- package/src/guards/no-unscoped-queries.ts +834 -0
- package/src/guards/scan-utils.ts +105 -0
- package/src/guards/types.ts +30 -0
- package/src/history/actions/create-resource-version.ts +92 -0
- package/src/history/actions/get-resource-version.ts +60 -0
- package/src/history/actions/list-resource-history.ts +53 -0
- package/src/history/actions/list-resource-versions.ts +43 -0
- package/src/history/actions/restore-resource-version.ts +96 -0
- package/src/history/index.ts +26 -0
- package/src/history/registry.ts +111 -0
- package/src/history/store.ts +379 -0
- package/src/history/types.ts +69 -0
- package/src/index.browser.ts +155 -0
- package/src/index.ts +408 -0
- package/src/ingestion/docx.ts +158 -0
- package/src/ingestion/figma-node-to-html.ts +1860 -0
- package/src/ingestion/figma.ts +548 -0
- package/src/ingestion/index.ts +107 -0
- package/src/ingestion/media.ts +264 -0
- package/src/ingestion/notion.ts +121 -0
- package/src/ingestion/office.ts +213 -0
- package/src/ingestion/orchestration.ts +160 -0
- package/src/ingestion/pptx.ts +331 -0
- package/src/ingestion/selection.ts +81 -0
- package/src/ingestion/website.ts +151 -0
- package/src/integrations/a2a-continuation-marker.ts +2 -0
- package/src/integrations/a2a-continuation-processor.ts +888 -0
- package/src/integrations/a2a-continuations-store.ts +643 -0
- package/src/integrations/adapter-overrides.ts +12 -0
- package/src/integrations/adapters/discord.ts +364 -0
- package/src/integrations/adapters/email.ts +1164 -0
- package/src/integrations/adapters/google-docs.ts +371 -0
- package/src/integrations/adapters/index.ts +29 -0
- package/src/integrations/adapters/microsoft-teams.ts +413 -0
- package/src/integrations/adapters/slack.ts +1914 -0
- package/src/integrations/adapters/telegram.ts +386 -0
- package/src/integrations/adapters/whatsapp.ts +358 -0
- package/src/integrations/awaiting-input-store.ts +130 -0
- package/src/integrations/catalog.ts +571 -0
- package/src/integrations/computer-supervision-store.ts +423 -0
- package/src/integrations/computer-supervision.ts +213 -0
- package/src/integrations/config-store.ts +131 -0
- package/src/integrations/controls-store.ts +187 -0
- package/src/integrations/eject/messaging-adapters.ts +85 -0
- package/src/integrations/google-docs-poller.ts +671 -0
- package/src/integrations/identity-links-store.ts +210 -0
- package/src/integrations/identity.ts +196 -0
- package/src/integrations/index.ts +220 -0
- package/src/integrations/installations-store.ts +677 -0
- package/src/integrations/integration-memory.ts +187 -0
- package/src/integrations/internal-token.ts +106 -0
- package/src/integrations/pending-tasks-retry-job.ts +286 -0
- package/src/integrations/pending-tasks-store.ts +468 -0
- package/src/integrations/plugin.ts +3048 -0
- package/src/integrations/remote-commands-store.ts +690 -0
- package/src/integrations/remote-devices-store.ts +492 -0
- package/src/integrations/remote-json-safety.ts +127 -0
- package/src/integrations/remote-push-delivery-job.ts +48 -0
- package/src/integrations/remote-push-delivery.ts +370 -0
- package/src/integrations/remote-push-store.ts +664 -0
- package/src/integrations/remote-retry-job.ts +55 -0
- package/src/integrations/remote-run-events-store.ts +222 -0
- package/src/integrations/remote-types.ts +186 -0
- package/src/integrations/runtime.ts +1 -0
- package/src/integrations/scope-store.ts +498 -0
- package/src/integrations/slack-manifest.ts +78 -0
- package/src/integrations/slack-oauth.ts +344 -0
- package/src/integrations/task-queue-stats.ts +144 -0
- package/src/integrations/thread-mapping-store.ts +134 -0
- package/src/integrations/types.ts +485 -0
- package/src/integrations/usage-budget-store.ts +906 -0
- package/src/integrations/webhook-delivery.ts +55 -0
- package/src/integrations/webhook-handler.ts +1939 -0
- package/src/jobs/actions/list-recurring-jobs.ts +105 -0
- package/src/jobs/actions/manage-recurring-job.ts +85 -0
- package/src/jobs/cron.ts +126 -0
- package/src/jobs/index.ts +9 -0
- package/src/jobs/scheduler.ts +703 -0
- package/src/jobs/tools.ts +367 -0
- package/src/local-artifacts/index.ts +1372 -0
- package/src/localization/actions/get-localization-preference.ts +24 -0
- package/src/localization/actions/set-localization-preference.ts +32 -0
- package/src/localization/default-messages.ts +1009 -0
- package/src/localization/index.ts +2 -0
- package/src/localization/server.ts +131 -0
- package/src/localization/shared.ts +208 -0
- package/src/mcp/actions/call-mcp-tool.ts +19 -0
- package/src/mcp/actions/create-org-service-token.ts +87 -0
- package/src/mcp/actions/list-mcp-tools.ts +18 -0
- package/src/mcp/actions/list-org-service-tokens.ts +44 -0
- package/src/mcp/actions/revoke-org-service-token.ts +30 -0
- package/src/mcp/actions/service-token-access.ts +121 -0
- package/src/mcp/ask-app-inline-tasks.ts +125 -0
- package/src/mcp/build-server.ts +2356 -0
- package/src/mcp/builtin-tools.ts +1506 -0
- package/src/mcp/connect-route.ts +1460 -0
- package/src/mcp/connect-store.ts +680 -0
- package/src/mcp/embed-app.ts +2051 -0
- package/src/mcp/embed-route.ts +84 -0
- package/src/mcp/external-agent-policy.ts +18 -0
- package/src/mcp/index.ts +47 -0
- package/src/mcp/oauth-route.ts +1016 -0
- package/src/mcp/oauth-store.ts +581 -0
- package/src/mcp/oauth-token.ts +227 -0
- package/src/mcp/org-directory.ts +343 -0
- package/src/mcp/route-paths.ts +34 -0
- package/src/mcp/screen-memory-stdio.ts +290 -0
- package/src/mcp/server.ts +507 -0
- package/src/mcp/stdio.ts +328 -0
- package/src/mcp/workspace-resolve.ts +261 -0
- package/src/mcp-client/app-api.ts +136 -0
- package/src/mcp-client/app-result.ts +64 -0
- package/src/mcp-client/builtin-capabilities.ts +146 -0
- package/src/mcp-client/builtin-store.ts +93 -0
- package/src/mcp-client/config.ts +281 -0
- package/src/mcp-client/errors.ts +54 -0
- package/src/mcp-client/hub-client.ts +172 -0
- package/src/mcp-client/hub-routes.ts +198 -0
- package/src/mcp-client/index.ts +504 -0
- package/src/mcp-client/manager.ts +968 -0
- package/src/mcp-client/oauth-client.ts +462 -0
- package/src/mcp-client/oauth-routes.ts +307 -0
- package/src/mcp-client/remote-store.ts +674 -0
- package/src/mcp-client/remote-url.ts +97 -0
- package/src/mcp-client/routes.ts +1093 -0
- package/src/mcp-client/screen-memory-local.ts +461 -0
- package/src/mcp-client/tool-policy.ts +182 -0
- package/src/mcp-client/visibility.ts +56 -0
- package/src/mcp-client/workspace-servers.ts +379 -0
- package/src/navigation/index.ts +170 -0
- package/src/notifications/actions.ts +151 -0
- package/src/notifications/channels.ts +410 -0
- package/src/notifications/index.ts +22 -0
- package/src/notifications/registry.ts +216 -0
- package/src/notifications/routes.ts +106 -0
- package/src/notifications/store.ts +236 -0
- package/src/notifications/types.ts +52 -0
- package/src/oauth-tokens/google-refresh.ts +175 -0
- package/src/oauth-tokens/index.ts +15 -0
- package/src/oauth-tokens/store.ts +384 -0
- package/src/observability/cleanup-job.ts +124 -0
- package/src/observability/evals.ts +507 -0
- package/src/observability/experiments.ts +359 -0
- package/src/observability/feedback.ts +332 -0
- package/src/observability/hosted-model-experiment.ts +118 -0
- package/src/observability/index.ts +62 -0
- package/src/observability/plugin.ts +31 -0
- package/src/observability/routes.ts +431 -0
- package/src/observability/sentiment.ts +261 -0
- package/src/observability/store.ts +1347 -0
- package/src/observability/traces.ts +789 -0
- package/src/observability/tracing.ts +157 -0
- package/src/observability/tracking-identity.ts +55 -0
- package/src/observability/types.ts +211 -0
- package/src/onboarding/default-steps.ts +428 -0
- package/src/onboarding/index.ts +22 -0
- package/src/onboarding/plugin.ts +262 -0
- package/src/onboarding/registry.ts +47 -0
- package/src/onboarding/types.ts +96 -0
- package/src/org/accept-pending.ts +98 -0
- package/src/org/auto-join-domain.ts +113 -0
- package/src/org/context.ts +614 -0
- package/src/org/free-email-providers.ts +124 -0
- package/src/org/handlers.ts +1153 -0
- package/src/org/index.ts +63 -0
- package/src/org/migrations.ts +105 -0
- package/src/org/permissions.ts +30 -0
- package/src/org/plugin.ts +255 -0
- package/src/org/schema.ts +28 -0
- package/src/org/types.ts +57 -0
- package/src/org-team/index.ts +1 -0
- package/src/package-lifecycle/deprecated-imports.ts +179 -0
- package/src/package-lifecycle/eject-manifest.ts +345 -0
- package/src/package-lifecycle/index.ts +7 -0
- package/src/package-lifecycle/manifest.ts +120 -0
- package/src/package-lifecycle/migration-manifest.ts +159 -0
- package/src/package-lifecycle/migration-message.ts +6 -0
- package/src/package-lifecycle/tombstone.ts +58 -0
- package/src/package-lifecycle/upgrade-error.ts +37 -0
- package/src/private-blob/index.ts +18 -0
- package/src/private-blob/registry.ts +242 -0
- package/src/private-blob/types.ts +55 -0
- package/src/progress/actions.ts +179 -0
- package/src/progress/index.ts +16 -0
- package/src/progress/registry.ts +127 -0
- package/src/progress/routes.ts +89 -0
- package/src/progress/store.ts +353 -0
- package/src/progress/types.ts +59 -0
- package/src/provider-api/actions/custom-provider-registration.ts +197 -0
- package/src/provider-api/actions/delete-staged-dataset.ts +5 -0
- package/src/provider-api/actions/github-repo-files.ts +266 -0
- package/src/provider-api/actions/list-staged-datasets.ts +5 -0
- package/src/provider-api/actions/provider-api-audit.ts +88 -0
- package/src/provider-api/actions/provider-api.ts +460 -0
- package/src/provider-api/actions/query-staged-dataset.ts +5 -0
- package/src/provider-api/actions/staged-datasets.ts +359 -0
- package/src/provider-api/corpus-jobs-store.ts +522 -0
- package/src/provider-api/corpus-jobs.ts +1817 -0
- package/src/provider-api/custom-registry.ts +456 -0
- package/src/provider-api/github-repo.ts +675 -0
- package/src/provider-api/gong.ts +108 -0
- package/src/provider-api/index.ts +5444 -0
- package/src/provider-api/quota-governor.ts +562 -0
- package/src/provider-api/runtime.ts +1 -0
- package/src/provider-api/staged-datasets-aggregate.ts +304 -0
- package/src/provider-api/staged-datasets-store.ts +441 -0
- package/src/provider-api/staging.ts +483 -0
- package/src/resources/agents.ts +65 -0
- package/src/resources/emitter.ts +54 -0
- package/src/resources/handlers.ts +728 -0
- package/src/resources/index.ts +4 -0
- package/src/resources/metadata.ts +252 -0
- package/src/resources/script-helpers.ts +160 -0
- package/src/resources/store.ts +1618 -0
- package/src/review/actions/consume-review-feedback.ts +48 -0
- package/src/review/actions/create-review-comment.ts +108 -0
- package/src/review/actions/delete-review-comment.ts +67 -0
- package/src/review/actions/get-review-feedback.ts +49 -0
- package/src/review/actions/list-review-comments.ts +87 -0
- package/src/review/actions/reply-review-comment.ts +118 -0
- package/src/review/actions/resolve-review-thread.ts +94 -0
- package/src/review/actions/send-review-thread-to-agent.ts +62 -0
- package/src/review/actions/set-review-status.ts +58 -0
- package/src/review/identity.ts +101 -0
- package/src/review/index.ts +45 -0
- package/src/review/mentions.ts +44 -0
- package/src/review/registry.ts +111 -0
- package/src/review/store.ts +997 -0
- package/src/review/types.ts +101 -0
- package/src/router/index.ts +24 -0
- package/src/scripts/agent-engines/list-agent-engines.ts +130 -0
- package/src/scripts/agent-engines/manage-agent-engine.ts +197 -0
- package/src/scripts/agent-engines/set-agent-engine.ts +84 -0
- package/src/scripts/agent-engines/test-agent-engine.ts +174 -0
- package/src/scripts/call-agent.ts +792 -0
- package/src/scripts/chat/index.ts +8 -0
- package/src/scripts/chat/open-chat.ts +56 -0
- package/src/scripts/chat/search-chats.ts +121 -0
- package/src/scripts/core-scripts.ts +22 -0
- package/src/scripts/db/check-scoping.ts +212 -0
- package/src/scripts/db/exec.ts +791 -0
- package/src/scripts/db/index.ts +19 -0
- package/src/scripts/db/migrate-encrypt-credentials.ts +223 -0
- package/src/scripts/db/migrate-encrypt-oauth-tokens.ts +213 -0
- package/src/scripts/db/migrate-user-api-keys.ts +257 -0
- package/src/scripts/db/patch.ts +820 -0
- package/src/scripts/db/query.ts +299 -0
- package/src/scripts/db/reset-dev-owner.ts +285 -0
- package/src/scripts/db/safety.ts +286 -0
- package/src/scripts/db/schema.ts +364 -0
- package/src/scripts/db/scoping.ts +341 -0
- package/src/scripts/db/sqlite-client.ts +78 -0
- package/src/scripts/db/tool-mode.ts +23 -0
- package/src/scripts/db/tool-schemas.ts +31 -0
- package/src/scripts/db/wipe-leaked-builder-keys.ts +201 -0
- package/src/scripts/dev/index.ts +296 -0
- package/src/scripts/dev/list-files.ts +119 -0
- package/src/scripts/dev/read-file.ts +77 -0
- package/src/scripts/dev/search-files.ts +151 -0
- package/src/scripts/dev/shell.ts +111 -0
- package/src/scripts/dev/write-file.ts +56 -0
- package/src/scripts/dev-session.ts +96 -0
- package/src/scripts/docs/index.ts +8 -0
- package/src/scripts/docs/search.ts +274 -0
- package/src/scripts/docs/source-search.ts +363 -0
- package/src/scripts/index.ts +15 -0
- package/src/scripts/manage-agent-loop-settings.ts +84 -0
- package/src/scripts/parse-args.ts +46 -0
- package/src/scripts/resources/delete-memory.ts +67 -0
- package/src/scripts/resources/delete.ts +69 -0
- package/src/scripts/resources/effective.ts +71 -0
- package/src/scripts/resources/index.ts +18 -0
- package/src/scripts/resources/list.ts +103 -0
- package/src/scripts/resources/migrate-learnings.ts +40 -0
- package/src/scripts/resources/read.ts +107 -0
- package/src/scripts/resources/save-memory.ts +99 -0
- package/src/scripts/resources/write.ts +158 -0
- package/src/scripts/runner.ts +339 -0
- package/src/scripts/utils.ts +130 -0
- package/src/search/index.ts +413 -0
- package/src/search-utils/index.ts +112 -0
- package/src/secrets/crypto.ts +228 -0
- package/src/secrets/index.ts +65 -0
- package/src/secrets/onboarding.ts +101 -0
- package/src/secrets/register-framework-secrets.ts +228 -0
- package/src/secrets/register.ts +129 -0
- package/src/secrets/routes.ts +697 -0
- package/src/secrets/schema.ts +52 -0
- package/src/secrets/storage.ts +561 -0
- package/src/secrets/substitution.ts +305 -0
- package/src/server/__snapshots__/agent-chat-plugin.surface.spec.ts.snap +150 -0
- package/src/server/action-change.ts +66 -0
- package/src/server/action-discovery.ts +774 -0
- package/src/server/action-routes.ts +608 -0
- package/src/server/agent-access.ts +72 -0
- package/src/server/agent-chat/action-filters-a2a.ts +339 -0
- package/src/server/agent-chat/browser-team-tools.ts +510 -0
- package/src/server/agent-chat/context-tools.ts +599 -0
- package/src/server/agent-chat/framework-prompts.ts +467 -0
- package/src/server/agent-chat/lazy-fs.ts +11 -0
- package/src/server/agent-chat/mcp-glue.ts +91 -0
- package/src/server/agent-chat/plugin-options.ts +379 -0
- package/src/server/agent-chat/process-run-failure.ts +62 -0
- package/src/server/agent-chat/prompt-resources.ts +1004 -0
- package/src/server/agent-chat/recurring-jobs-runtime.ts +80 -0
- package/src/server/agent-chat/request-surface.ts +71 -0
- package/src/server/agent-chat/run-code-tools.ts +67 -0
- package/src/server/agent-chat/script-entries.ts +796 -0
- package/src/server/agent-chat/shared-thread.ts +301 -0
- package/src/server/agent-chat/skill-frontmatter.ts +41 -0
- package/src/server/agent-chat-plugin.ts +5930 -0
- package/src/server/agent-discovery.ts +706 -0
- package/src/server/agent-engine-api-key-route.ts +236 -0
- package/src/server/agent-run-context.ts +204 -0
- package/src/server/agent-teams-run-queue.ts +344 -0
- package/src/server/agent-teams.ts +2257 -0
- package/src/server/agents-bundle.ts +464 -0
- package/src/server/analytics.ts +105 -0
- package/src/server/app-base-path.ts +79 -0
- package/src/server/app-name.ts +53 -0
- package/src/server/app-url.ts +184 -0
- package/src/server/attachment-actions.ts +276 -0
- package/src/server/attribution.ts +214 -0
- package/src/server/auth-marketing.ts +260 -0
- package/src/server/auth-plugin.ts +27 -0
- package/src/server/auth.ts +3803 -0
- package/src/server/better-auth-instance.ts +1197 -0
- package/src/server/builder-browser.ts +1701 -0
- package/src/server/builder-design-systems.ts +668 -0
- package/src/server/builder-space.ts +176 -0
- package/src/server/captcha.ts +63 -0
- package/src/server/capture-error.ts +64 -0
- package/src/server/cli-capture.ts +141 -0
- package/src/server/collab-plugin.ts +391 -0
- package/src/server/complete-text.ts +231 -0
- package/src/server/cookie-namespace.ts +159 -0
- package/src/server/core-routes-plugin.ts +4122 -0
- package/src/server/cors-origins.ts +60 -0
- package/src/server/create-server.ts +268 -0
- package/src/server/credential-provider.ts +1202 -0
- package/src/server/csrf.ts +229 -0
- package/src/server/date-utils.ts +42 -0
- package/src/server/deep-link.ts +119 -0
- package/src/server/derived-secret.ts +32 -0
- package/src/server/design-token-utils.ts +1032 -0
- package/src/server/desktop-sso.ts +83 -0
- package/src/server/edge.ts +69 -0
- package/src/server/email-actions.ts +218 -0
- package/src/server/email-template.ts +192 -0
- package/src/server/email-templates.ts +148 -0
- package/src/server/email.ts +219 -0
- package/src/server/embed-route.ts +283 -0
- package/src/server/embed-session.ts +805 -0
- package/src/server/embedded.ts +252 -0
- package/src/server/entry-server.tsx +154 -0
- package/src/server/framework-request-handler.ts +848 -0
- package/src/server/fusion-app.ts +447 -0
- package/src/server/google-auth-mode.ts +34 -0
- package/src/server/google-auth-plugin.ts +496 -0
- package/src/server/google-oauth-credentials.ts +128 -0
- package/src/server/google-oauth.ts +972 -0
- package/src/server/google-realtime-session.ts +182 -0
- package/src/server/h3-helpers.ts +174 -0
- package/src/server/http-response-telemetry.ts +396 -0
- package/src/server/identity-sso-store.ts +304 -0
- package/src/server/identity-sso.ts +514 -0
- package/src/server/index.ts +593 -0
- package/src/server/missing-key.ts +36 -0
- package/src/server/oauth-helpers.ts +44 -0
- package/src/server/oauth-public-origin.ts +44 -0
- package/src/server/oauth-return-url.ts +120 -0
- package/src/server/og-fonts-data.ts +15 -0
- package/src/server/og-fonts.ts +75 -0
- package/src/server/onboarding-html.ts +4040 -0
- package/src/server/open-route.ts +311 -0
- package/src/server/org-admin.ts +28 -0
- package/src/server/poll-events.ts +88 -0
- package/src/server/poll.ts +1224 -0
- package/src/server/prompts/framework-core-compact.ts +107 -0
- package/src/server/prompts/framework-core.ts +143 -0
- package/src/server/prompts/index.ts +16 -0
- package/src/server/prompts/model-overlays.ts +54 -0
- package/src/server/prompts/shared-rules.ts +104 -0
- package/src/server/realtime-voice.ts +967 -0
- package/src/server/recap-image-route.ts +242 -0
- package/src/server/recap-image-store.ts +185 -0
- package/src/server/request-context.ts +416 -0
- package/src/server/request-origin.ts +67 -0
- package/src/server/resources-plugin.ts +119 -0
- package/src/server/schema-prompt.ts +429 -0
- package/src/server/scoped-key-storage.ts +223 -0
- package/src/server/security-headers.ts +170 -0
- package/src/server/self-dispatch.ts +224 -0
- package/src/server/sentry-config.ts +73 -0
- package/src/server/sentry-plugin.ts +138 -0
- package/src/server/sentry.ts +444 -0
- package/src/server/short-lived-token.ts +170 -0
- package/src/server/social-og-image.ts +454 -0
- package/src/server/sse.ts +102 -0
- package/src/server/ssr-handler.ts +466 -0
- package/src/server/transcribe-voice.ts +1076 -0
- package/src/server/voice-providers-status.ts +118 -0
- package/src/server/workspace-oauth.ts +31 -0
- package/src/server/workspace-provider-oauth.ts +970 -0
- package/src/session-replay-iframe-protocol.ts +58 -0
- package/src/settings/handlers.ts +44 -0
- package/src/settings/index.ts +34 -0
- package/src/settings/org-settings.ts +83 -0
- package/src/settings/script-helpers.ts +24 -0
- package/src/settings/store.ts +247 -0
- package/src/settings/user-settings.ts +47 -0
- package/src/setup-connections/index.ts +90 -0
- package/src/setup-connections/onboarding.ts +47 -0
- package/src/shared/agent-access.ts +112 -0
- package/src/shared/agent-chat.ts +124 -0
- package/src/shared/agent-env.ts +46 -0
- package/src/shared/agent-readable-resource.ts +111 -0
- package/src/shared/agent-sidebar-url.ts +39 -0
- package/src/shared/cache-control.ts +25 -0
- package/src/shared/cloudflare-globals.d.ts +34 -0
- package/src/shared/context-xray.ts +172 -0
- package/src/shared/embed-auth.ts +6 -0
- package/src/shared/index.ts +83 -0
- package/src/shared/llm-connection.ts +42 -0
- package/src/shared/markdown-block-split.ts +129 -0
- package/src/shared/mcp-connect-content.ts +130 -0
- package/src/shared/mcp-embed-headers.ts +175 -0
- package/src/shared/mcp-integration-config.ts +101 -0
- package/src/shared/merge-by-id.ts +14 -0
- package/src/shared/oauth-state.ts +39 -0
- package/src/shared/reasoning-effort.ts +228 -0
- package/src/shared/route-warmup-config.ts +128 -0
- package/src/shared/runtime.ts +31 -0
- package/src/shared/social-meta.ts +82 -0
- package/src/shared/streaming-text-smoothing.ts +184 -0
- package/src/shared/truncate.ts +13 -0
- package/src/shared/workspace-app-audience.ts +177 -0
- package/src/shared/workspace-app-id.ts +54 -0
- package/src/sharing/access.ts +585 -0
- package/src/sharing/actions/create-agent-resource-link.ts +91 -0
- package/src/sharing/actions/extension-change.ts +22 -0
- package/src/sharing/actions/list-resource-shares.ts +93 -0
- package/src/sharing/actions/set-resource-visibility.ts +91 -0
- package/src/sharing/actions/share-resource.ts +322 -0
- package/src/sharing/actions/unshare-resource.ts +72 -0
- package/src/sharing/index.ts +35 -0
- package/src/sharing/registry.ts +201 -0
- package/src/sharing/schema.ts +99 -0
- package/src/styles/agent-conversation.css +766 -0
- package/src/styles/agent-native.css +938 -0
- package/src/styles/blocks.css +1717 -0
- package/src/styles/chat-history-list.css +1 -0
- package/src/styles/rich-markdown-editor.css +1 -0
- package/src/tailwind.preset.ts +140 -0
- package/src/templates/chat/.agents/skills/actions/SKILL.md +32 -12
- package/src/templates/chat/.agents/skills/agent-native-docs/SKILL.md +21 -9
- package/src/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/src/templates/chat/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/src/templates/chat/AGENTS.md +6 -2
- package/src/templates/chat/app/components/layout/Sidebar.tsx +51 -157
- package/src/templates/default/.agents/skills/actions/SKILL.md +32 -12
- package/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +21 -9
- package/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/src/templates/default/.agents/skills/integration-webhooks/SKILL.md +323 -0
- package/src/templates/default/.agents/skills/onboarding/SKILL.md +56 -0
- package/src/templates/default/.agents/skills/secrets/SKILL.md +273 -0
- package/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/src/templates/default/AGENTS.md +26 -18
- package/src/templates/headless/.agents/skills/actions/SKILL.md +512 -0
- package/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +21 -9
- package/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/src/templates/headless/.agents/skills/integration-webhooks/SKILL.md +323 -0
- package/src/templates/headless/.agents/skills/secrets/SKILL.md +273 -0
- package/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/src/templates/headless/AGENTS.md +8 -0
- package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -12
- package/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +21 -9
- package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +7 -0
- package/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +145 -0
- package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +14 -2
- package/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +6 -0
- package/src/templates/workspace-core/AGENTS.md +4 -0
- package/src/templates/workspace-root/AGENTS.md +4 -0
- package/src/terminal/cli-registry.ts +57 -0
- package/src/terminal/index.ts +22 -0
- package/src/terminal/pty-server.ts +389 -0
- package/src/terminal/terminal-plugin.ts +265 -0
- package/src/testing.ts +23 -0
- package/src/tracking/index.ts +10 -0
- package/src/tracking/providers.ts +422 -0
- package/src/tracking/registry.ts +102 -0
- package/src/tracking/route.ts +108 -0
- package/src/tracking/types.ts +16 -0
- package/src/transcription/builder-transcription.ts +118 -0
- package/src/triggers/actions/list-automations.ts +99 -0
- package/src/triggers/actions/manage-automation.ts +78 -0
- package/src/triggers/actions.ts +333 -0
- package/src/triggers/condition-evaluator.ts +163 -0
- package/src/triggers/dispatcher.ts +536 -0
- package/src/triggers/index.ts +13 -0
- package/src/triggers/routes.ts +322 -0
- package/src/triggers/types.ts +42 -0
- package/src/types/pglite.d.ts +26 -0
- package/src/usage/store.ts +698 -0
- package/src/vite/action-types-plugin.ts +481 -0
- package/src/vite/agent-web-plugin.ts +78 -0
- package/src/vite/agents-bundle-plugin.ts +185 -0
- package/src/vite/client.ts +3039 -0
- package/src/vite/index.ts +21 -0
- package/src/voice/index.ts +18 -0
- package/src/voice/voice-cleanup-prompt.ts +38 -0
- package/src/voice/voice-context.ts +217 -0
- package/src/voice/voice-replacements.ts +75 -0
- package/src/workspace-connections/credentials.ts +603 -0
- package/src/workspace-connections/index.ts +72 -0
- package/src/workspace-connections/lifecycle.ts +43 -0
- package/src/workspace-connections/store.ts +1943 -0
- package/src/workspace-files/index.ts +18 -0
- package/src/workspace-files/store.ts +339 -0
- package/src/workspace-files/tool.ts +266 -0
|
@@ -0,0 +1,2356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared MCP server builder.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `server.ts` so the stateless Streamable-HTTP mount
|
|
5
|
+
* (`mountMCP`) and the stdio transport (`runMCPStdio --standalone`) build the
|
|
6
|
+
* *same* MCP server from the *same* `ActionEntry` registry. Both surfaces:
|
|
7
|
+
*
|
|
8
|
+
* - expose every action as an MCP tool (+ the `ask-agent` meta-tool),
|
|
9
|
+
* - append the framework deep-link block / `_meta` to every tool result,
|
|
10
|
+
* - wrap `run()` / `askAgent()` in `runWithRequestContext` so per-user /
|
|
11
|
+
* per-org scoping (accessFilter, resolveCredential, MCP visibility) is
|
|
12
|
+
* honoured.
|
|
13
|
+
*
|
|
14
|
+
* `server.ts` re-exports `createMCPServerForRequest` and the auth helpers so
|
|
15
|
+
* any (future) external importer of `@agent-native/core/mcp` keeps resolving.
|
|
16
|
+
*
|
|
17
|
+
* Node-only at the SDK level, but this module itself has no Node-only imports
|
|
18
|
+
* — it can be bundled into the serverless function alongside `mountMCP`.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
MCP_APP_EXTENSION_ID,
|
|
23
|
+
MCP_APP_MIME_TYPE,
|
|
24
|
+
MCP_APP_RESOURCE_URI_META_KEY,
|
|
25
|
+
type ActionMcpAppCsp,
|
|
26
|
+
type ActionMcpAppResourceConfig,
|
|
27
|
+
} from "../action.js";
|
|
28
|
+
import type { ActionEntry } from "../agent/production-agent.js";
|
|
29
|
+
import { isMcpActionResult } from "../mcp-client/app-result.js";
|
|
30
|
+
import { getConfiguredAppBasePath } from "../server/app-base-path.js";
|
|
31
|
+
import {
|
|
32
|
+
buildDeepLink,
|
|
33
|
+
toAbsoluteOpenUrl,
|
|
34
|
+
toDesktopOpenUrl,
|
|
35
|
+
toVsCodeOpenUrl,
|
|
36
|
+
} from "../server/deep-link.js";
|
|
37
|
+
import {
|
|
38
|
+
getRequestContext,
|
|
39
|
+
getRequestOrgId,
|
|
40
|
+
getRequestUserEmail,
|
|
41
|
+
runWithRequestContext,
|
|
42
|
+
} from "../server/request-context.js";
|
|
43
|
+
import {
|
|
44
|
+
isAgentNativeOpenDeepLink,
|
|
45
|
+
withCollapsedAgentSidebarParam,
|
|
46
|
+
} from "../shared/agent-sidebar-url.js";
|
|
47
|
+
import { MCP_APP_CHAT_BRIDGE_QUERY_PARAM } from "../shared/embed-auth.js";
|
|
48
|
+
import { getBuiltinCrossAppTools } from "./builtin-tools.js";
|
|
49
|
+
import {
|
|
50
|
+
MCP_CONNECT_OAUTH_CLIENT_ID,
|
|
51
|
+
MCP_CONNECT_SCOPE,
|
|
52
|
+
} from "./connect-store.js";
|
|
53
|
+
import { MCP_APP_REQUEST_ORIGIN_CSP_SOURCE } from "./embed-app.js";
|
|
54
|
+
import type { ExternalAgentPolicy } from "./external-agent-policy.js";
|
|
55
|
+
import {
|
|
56
|
+
MCP_OAUTH_SCOPES,
|
|
57
|
+
hasMcpOAuthScope,
|
|
58
|
+
verifyMcpOAuthAccessToken,
|
|
59
|
+
} from "./oauth-token.js";
|
|
60
|
+
|
|
61
|
+
export interface MCPConfig {
|
|
62
|
+
/** App name shown in MCP server info */
|
|
63
|
+
name: string;
|
|
64
|
+
/** Optional human-facing app title shown by MCP hosts that support titles. */
|
|
65
|
+
title?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Canonical app id (directory under `apps/`, e.g. `mail`) this MCP server
|
|
68
|
+
* is mounted for. Optional & back-compat: when omitted the builtin
|
|
69
|
+
* cross-app tools fall back to lowercasing `name`. Used by `open_app` /
|
|
70
|
+
* `ask_app` / `create_workspace_app` to tell "this app" from a cross-app
|
|
71
|
+
* target so they resolve the *target* app's origin rather than echoing the
|
|
72
|
+
* current request origin.
|
|
73
|
+
*/
|
|
74
|
+
appId?: string;
|
|
75
|
+
/** App description */
|
|
76
|
+
description: string;
|
|
77
|
+
/** Optional canonical website URL for hosts that surface MCP app details. */
|
|
78
|
+
websiteUrl?: string;
|
|
79
|
+
/** Optional app icons for MCP hosts that render server branding. */
|
|
80
|
+
icons?: Array<{
|
|
81
|
+
src: string;
|
|
82
|
+
mimeType?: string;
|
|
83
|
+
sizes?: string[];
|
|
84
|
+
theme?: "light" | "dark";
|
|
85
|
+
}>;
|
|
86
|
+
/** Version string (default "1.0.0") */
|
|
87
|
+
version?: string;
|
|
88
|
+
/** Action registry — same as agent chat and A2A */
|
|
89
|
+
actions: Record<string, ActionEntry>;
|
|
90
|
+
/**
|
|
91
|
+
* Full ("production") action surface served to an **authenticated real
|
|
92
|
+
* caller** — a connect-minted token, an `agent-native mcp install` stdio
|
|
93
|
+
* proxy (owner-email header / `AGENT_NATIVE_OWNER_EMAIL`), or a deployed /
|
|
94
|
+
* `AGENT_MODE=production` app. In local dev `actions` is intentionally the
|
|
95
|
+
* sparse, dev-toggled surface (builtins + read-only public-agent actions)
|
|
96
|
+
* so the local agent chat and unauthenticated dev probes don't see every
|
|
97
|
+
* mutating tool; but per the external-agents contract a real caller that
|
|
98
|
+
* connected with a token MUST get the full surface even in dev. When unset
|
|
99
|
+
* (production, where `actions` already IS the full set) the swap is a
|
|
100
|
+
* no-op. See `external-agents` skill, "Dev vs production tool surface".
|
|
101
|
+
*/
|
|
102
|
+
productionActions?: Record<string, ActionEntry>;
|
|
103
|
+
/** Handler for the ask-agent meta-tool — runs the full agent loop */
|
|
104
|
+
askAgent?: (message: string) => Promise<string>;
|
|
105
|
+
/**
|
|
106
|
+
* Disable the generic cross-app builtin tools (`list_apps`, `open_app`,
|
|
107
|
+
* `ask_app`, `create_workspace_app`, `list_templates`). They are merged in
|
|
108
|
+
* by default so external agents get a stable verb set; a template action of
|
|
109
|
+
* the same name always wins (template precedence). Set to `false` only for
|
|
110
|
+
* a constrained / locked-down mount.
|
|
111
|
+
*/
|
|
112
|
+
builtinCrossAppTools?: boolean;
|
|
113
|
+
/**
|
|
114
|
+
* Curated allow-list of action names served to **external connector** clients
|
|
115
|
+
* on a hosted multi-tenant deployment.
|
|
116
|
+
*
|
|
117
|
+
* Whenever this list is non-empty it is active by default for **every**
|
|
118
|
+
* caller — hosted connectors, code/stdio clients, and the local CLI alike.
|
|
119
|
+
* The MCP server trims both the advertised tool list *and* the callable
|
|
120
|
+
* surface to exactly these names (plus any builtin cross-app tools such as
|
|
121
|
+
* `list_apps` / `open_app`). Any tool call for a name **not** in the list is
|
|
122
|
+
* rejected — it is not merely hidden. This prevents the ~105-tool full
|
|
123
|
+
* catalog from landing in every external agent's context window and removes
|
|
124
|
+
* footguns (db-exec, seed-*, extension tools, browser-session tools, etc.)
|
|
125
|
+
* from connectors. It is no longer gated behind an environment variable, and
|
|
126
|
+
* the catalog is never inferred from the client name/user-agent.
|
|
127
|
+
*
|
|
128
|
+
* `tool-search` stays available in the compact catalog for discovery. A
|
|
129
|
+
* searched action still needs the connector catalog or authenticated-read
|
|
130
|
+
* policy before `tools/call`; callers who need the full surface up front opt in
|
|
131
|
+
* explicitly with `agent-native connect --full-catalog` (embeds a
|
|
132
|
+
* `catalog_scope: "full"` claim in the connect-minted JWT) or the
|
|
133
|
+
* deployment-wide `AGENT_NATIVE_MCP_FULL_CATALOG=1` env override.
|
|
134
|
+
*
|
|
135
|
+
* Declare this in your template's `createAgentChatPlugin` options rather than
|
|
136
|
+
* setting it on `MCPConfig` directly; the plugin copies it through.
|
|
137
|
+
*/
|
|
138
|
+
connectorCatalog?: string[];
|
|
139
|
+
/**
|
|
140
|
+
* Optional policy for automatically exposing explicitly annotated,
|
|
141
|
+
* authenticated read actions to external MCP callers.
|
|
142
|
+
*/
|
|
143
|
+
externalAgents?: ExternalAgentPolicy;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Identity extracted from a verified MCP bearer token / JWT. Used to wrap
|
|
148
|
+
* `entry.run()` and `config.askAgent()` calls in `runWithRequestContext`
|
|
149
|
+
* so downstream tools (db-query, accessFilter, resolveCredential) honour
|
|
150
|
+
* per-user / per-org scoping. Without this wrap the MCP endpoint would
|
|
151
|
+
* silently bypass tenant isolation. See finding #6 in
|
|
152
|
+
* /tmp/security-audit/12-mcp-a2a-agent.md.
|
|
153
|
+
*/
|
|
154
|
+
export interface MCPCallerIdentity {
|
|
155
|
+
userEmail: string | undefined;
|
|
156
|
+
orgId?: string | undefined;
|
|
157
|
+
orgDomain: string | undefined;
|
|
158
|
+
/** Present only for standard remote MCP OAuth access tokens. */
|
|
159
|
+
oauthScopes?: string[];
|
|
160
|
+
/** Present only for standard remote MCP OAuth access tokens. */
|
|
161
|
+
oauthClientId?: string;
|
|
162
|
+
/** Present only for framework-minted first-party MCP client tokens. */
|
|
163
|
+
firstPartyMcp?: boolean;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Per-request context used to turn an action's relative deep link into the
|
|
167
|
+
* absolute web URL (and desktop `agentnative://` URL) the external agent
|
|
168
|
+
* surfaces. Derived from the inbound request headers in `mountMCP`, or from
|
|
169
|
+
* the resolved local app origin in the stdio standalone path. */
|
|
170
|
+
export interface MCPRequestMeta {
|
|
171
|
+
/** Origin of the running app, e.g. `http://localhost:8100`. */
|
|
172
|
+
origin?: string;
|
|
173
|
+
/** Optional mount prefix for path-mounted apps, e.g. `/mail`. */
|
|
174
|
+
basePath?: string;
|
|
175
|
+
/** Optional client preference for which URL the *markdown* link uses. */
|
|
176
|
+
target?: "browser" | "desktop" | "terminal";
|
|
177
|
+
/**
|
|
178
|
+
* Best-effort caller label derived from MCP transport headers. Chat-style
|
|
179
|
+
* remote hosts should stay on the compact catalog; code/stdio clients can
|
|
180
|
+
* explicitly identify themselves to keep the full action surface.
|
|
181
|
+
*/
|
|
182
|
+
clientName?: string;
|
|
183
|
+
/** Explicit framework client hint from `x-agent-native-mcp-client`. */
|
|
184
|
+
clientHint?: string;
|
|
185
|
+
/** Explicit opt-in to the full tool catalog for code/stdio style clients. */
|
|
186
|
+
fullCatalog?: boolean;
|
|
187
|
+
/**
|
|
188
|
+
* The caller authenticated with a real credential (verified A2A/connect
|
|
189
|
+
* JWT, matching ACCESS_TOKEN, or a forwarded owner-email header from
|
|
190
|
+
* `agent-native mcp install`) — not the unauthenticated local dev-open
|
|
191
|
+
* path. When true, `createMCPServerForRequest` serves
|
|
192
|
+
* `config.productionActions` (the full surface) instead of the sparse dev
|
|
193
|
+
* `config.actions`. Set by `mountMCP` from `verifyAuth`.
|
|
194
|
+
*/
|
|
195
|
+
fullSurface?: boolean;
|
|
196
|
+
/**
|
|
197
|
+
* Whether this request may receive inline MCP App embeds (the `ui://`
|
|
198
|
+
* resource reference hosts render in an iframe). Resolved once per request by
|
|
199
|
+
* `createMCPServerForRequest` from `isMcpAppsInlineEnabled(identity)` — the
|
|
200
|
+
* deploy-toggleable kill switch. When `false`, no MCP App resource is
|
|
201
|
+
* advertised or referenced and tool results fall back to their deep-link
|
|
202
|
+
* text. Defaults to disabled when unset.
|
|
203
|
+
*/
|
|
204
|
+
inlineMcpApps?: boolean;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const ASK_AGENT_DEFAULT_INLINE_WAIT_MS = 20_000;
|
|
208
|
+
const ASK_AGENT_MAX_INLINE_WAIT_MS = 25_000;
|
|
209
|
+
|
|
210
|
+
function boundedAskAgentWaitMs(raw: unknown): number {
|
|
211
|
+
if (raw == null || raw === "") return ASK_AGENT_DEFAULT_INLINE_WAIT_MS;
|
|
212
|
+
const parsed = Number(raw);
|
|
213
|
+
if (!Number.isFinite(parsed)) return ASK_AGENT_DEFAULT_INLINE_WAIT_MS;
|
|
214
|
+
return Math.max(
|
|
215
|
+
0,
|
|
216
|
+
Math.min(ASK_AGENT_MAX_INLINE_WAIT_MS, Math.trunc(parsed)),
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function isExplicitAsyncAskAgent(raw: unknown): boolean {
|
|
221
|
+
return raw === true || raw === "true" || raw === 1 || raw === "1";
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function formatAskAgentResult(result: unknown): string {
|
|
225
|
+
if (typeof result === "string") return result;
|
|
226
|
+
if (result && typeof result === "object") {
|
|
227
|
+
const record = result as Record<string, unknown>;
|
|
228
|
+
if (record.status === "completed" && typeof record.response === "string") {
|
|
229
|
+
return record.response;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const serialized = JSON.stringify(result);
|
|
233
|
+
return serialized === undefined ? String(result) : serialized;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Deploy-toggleable kill switch for inline MCP App embeds — the `ui://`
|
|
238
|
+
* resource reference hosts like Codex / Cursor / ChatGPT render in a sandboxed
|
|
239
|
+
* iframe. **Off by default**, so a not-yet-verified inline embed never reaches
|
|
240
|
+
* normal users; flip it on per environment with `AGENT_NATIVE_MCP_APPS_INLINE=1`
|
|
241
|
+
* and a redeploy. While the global switch is off, accounts listed in
|
|
242
|
+
* `AGENT_NATIVE_MCP_APPS_INLINE_ALLOW_EMAILS` (comma/space separated) still get
|
|
243
|
+
* inline embeds, so you can keep verifying a fix in production before enabling
|
|
244
|
+
* it for everyone. Requires no skills/instructions change — when disabled, tool
|
|
245
|
+
* results simply fall back to their deep-link text.
|
|
246
|
+
*/
|
|
247
|
+
export function isMcpAppsInlineEnabled(
|
|
248
|
+
identity: MCPCallerIdentity | undefined,
|
|
249
|
+
): boolean {
|
|
250
|
+
const flag = process.env.AGENT_NATIVE_MCP_APPS_INLINE?.trim().toLowerCase();
|
|
251
|
+
if (flag === "1" || flag === "true" || flag === "yes" || flag === "on") {
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
const email = identity?.userEmail?.trim().toLowerCase();
|
|
255
|
+
if (email) {
|
|
256
|
+
const allowed = (
|
|
257
|
+
process.env.AGENT_NATIVE_MCP_APPS_INLINE_ALLOW_EMAILS ?? ""
|
|
258
|
+
)
|
|
259
|
+
.split(/[\s,]+/)
|
|
260
|
+
.map((value) => value.trim().toLowerCase())
|
|
261
|
+
.filter(Boolean);
|
|
262
|
+
if (allowed.includes(email)) return true;
|
|
263
|
+
}
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
type McpOAuthScope = (typeof MCP_OAUTH_SCOPES)[number];
|
|
268
|
+
|
|
269
|
+
function isActionVisibleForOAuthScope(
|
|
270
|
+
entry: ActionEntry,
|
|
271
|
+
scopes: string[] | undefined,
|
|
272
|
+
): boolean {
|
|
273
|
+
if (!scopes) return true;
|
|
274
|
+
const required: McpOAuthScope =
|
|
275
|
+
entry.readOnly === true ? "mcp:read" : "mcp:write";
|
|
276
|
+
return hasMcpOAuthScope(scopes, required);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const COMPACT_MCP_APP_CATALOG_BUILTINS = new Set([
|
|
280
|
+
"list_apps",
|
|
281
|
+
"open_app",
|
|
282
|
+
"ask_app",
|
|
283
|
+
"ask_app_status",
|
|
284
|
+
"create_embed_session",
|
|
285
|
+
// `tool-search` MUST stay in every compact/connector surface: it is how a
|
|
286
|
+
// compacted client discovers and loads any action on demand, which is what
|
|
287
|
+
// makes "small catalog by default" safe instead of limiting.
|
|
288
|
+
"tool-search",
|
|
289
|
+
]);
|
|
290
|
+
|
|
291
|
+
function isActionAdvertisedInCompactMcpAppCatalog(
|
|
292
|
+
name: string,
|
|
293
|
+
entry: ActionEntry,
|
|
294
|
+
config: MCPConfig,
|
|
295
|
+
): boolean {
|
|
296
|
+
if (COMPACT_MCP_APP_CATALOG_BUILTINS.has(name)) return true;
|
|
297
|
+
if (
|
|
298
|
+
(entry.mcpApp as { compactCatalog?: unknown } | undefined)
|
|
299
|
+
?.compactCatalog === true
|
|
300
|
+
) {
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
if (config.builtinCrossAppTools === false && entry.mcpApp?.resource) {
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function explicitlyRequestsFullMcpCatalog(
|
|
310
|
+
requestMeta: MCPRequestMeta | undefined,
|
|
311
|
+
): boolean {
|
|
312
|
+
// Full catalog is a deliberate, rare opt-in — NEVER a default, and NEVER
|
|
313
|
+
// inferred from the client name / user-agent. It is reached only by an
|
|
314
|
+
// explicit deployment env or a token minted with
|
|
315
|
+
// `agent-native connect --full-catalog` (which embeds `catalog_scope: "full"`,
|
|
316
|
+
// surfaced here as requestMeta.fullCatalog). Dumping ~105 tool schemas
|
|
317
|
+
// (100k+ tokens) into a context window just because a client called itself
|
|
318
|
+
// "code"/"cursor"/"codex" was a recurring footgun. Everything else gets the
|
|
319
|
+
// connector/compact catalog plus `tool-search`, which keeps every tool
|
|
320
|
+
// discoverable; only permitted actions are callable without full opt-in.
|
|
321
|
+
if (process.env.AGENT_NATIVE_MCP_FULL_CATALOG === "1") return true;
|
|
322
|
+
return requestMeta?.fullCatalog === true;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const warnedFullCatalogKeys = new Set<string>();
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Loud, deduped warning emitted whenever the full MCP catalog is actually
|
|
329
|
+
* served. Full catalog is a deliberate, rare opt-in (env or a `--full-catalog`
|
|
330
|
+
* token claim); logging it makes an accidental ~100k-token tool dump visible
|
|
331
|
+
* instead of silent, so a regression can't quietly reintroduce the footgun.
|
|
332
|
+
*/
|
|
333
|
+
function warnFullCatalogServed(toolCount: number): void {
|
|
334
|
+
const source =
|
|
335
|
+
process.env.AGENT_NATIVE_MCP_FULL_CATALOG === "1"
|
|
336
|
+
? "AGENT_NATIVE_MCP_FULL_CATALOG=1"
|
|
337
|
+
: "a token minted with --full-catalog (catalog_scope:full)";
|
|
338
|
+
const key = `${source}:${toolCount}`;
|
|
339
|
+
if (warnedFullCatalogKeys.has(key)) return;
|
|
340
|
+
warnedFullCatalogKeys.add(key);
|
|
341
|
+
console.warn(
|
|
342
|
+
`[agent-native] Serving the FULL MCP tool catalog (${toolCount} tools) via ${source}. ` +
|
|
343
|
+
`This is a large context payload meant to be a rare, explicit opt-in — most ` +
|
|
344
|
+
`clients should use the default compact/connector catalog + tool-search instead.`,
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export function isAuthenticatedReadAction(entry: ActionEntry): boolean {
|
|
349
|
+
return (
|
|
350
|
+
entry.http !== false &&
|
|
351
|
+
entry.http?.method === "GET" &&
|
|
352
|
+
entry.readOnly === true &&
|
|
353
|
+
entry.publicAgent?.expose === true &&
|
|
354
|
+
entry.publicAgent.readOnly === true &&
|
|
355
|
+
entry.publicAgent.requiresAuth === true
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Hard exclusion list for the `authenticatedReads: "auto"` derivation ONLY
|
|
361
|
+
* (see `autoAuthenticatedReadNames` below). Explicit `connectorCatalog`
|
|
362
|
+
* entries are a deliberate, reviewed choice made by the app and are NOT
|
|
363
|
+
* affected by this list — an app can still list any of these names in
|
|
364
|
+
* `connectorCatalog` on purpose.
|
|
365
|
+
*
|
|
366
|
+
* These are the footgun families this file's other comments already call
|
|
367
|
+
* out ("removes footguns (db-exec, seed-*, extension tools, browser-session
|
|
368
|
+
* tools, etc.)" above, and "keeps db-exec / seed-* / extension /
|
|
369
|
+
* browser-session footguns off the external surface" near the connector
|
|
370
|
+
* tier below): generic core SQL access, template demo/seed data, the
|
|
371
|
+
* extension-management suite, live browser-session control, and Context
|
|
372
|
+
* X-Ray internals. `isAuthenticatedReadAction` only inspects action
|
|
373
|
+
* metadata (http/readOnly/publicAgent flags) — nothing stops a future
|
|
374
|
+
* change from mis-annotating one of these with that exact flag set again,
|
|
375
|
+
* the way `db-query`/`db-schema` were briefly (and accidentally) annotated
|
|
376
|
+
* before it was caught in review. These names can never be auto-derived
|
|
377
|
+
* from metadata alone; exposing one to external callers requires an
|
|
378
|
+
* explicit `connectorCatalog` entry.
|
|
379
|
+
*/
|
|
380
|
+
const AUTO_READ_EXCLUDED_ACTION_NAMES = new Set([
|
|
381
|
+
"db-query",
|
|
382
|
+
"db-schema",
|
|
383
|
+
"db-exec",
|
|
384
|
+
"db-patch",
|
|
385
|
+
"context-manifest-get",
|
|
386
|
+
"context-preview-get",
|
|
387
|
+
"context-pin",
|
|
388
|
+
"context-evict",
|
|
389
|
+
"context-restore",
|
|
390
|
+
"context-report",
|
|
391
|
+
]);
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Substring/prefix patterns for excluded name *families* that aren't a
|
|
395
|
+
* fixed, enumerable set: `seed-*` varies per app/template, and the
|
|
396
|
+
* extension-management and browser-session tool suites use varying verb
|
|
397
|
+
* prefixes around a shared noun (e.g. `list-extensions`, `create-extension`,
|
|
398
|
+
* `hide-extension`; `list-browser-sessions`, `run-browser-session-action`) —
|
|
399
|
+
* so a leading-prefix match alone would miss most of them.
|
|
400
|
+
*/
|
|
401
|
+
const AUTO_READ_EXCLUDED_ACTION_PATTERNS: RegExp[] = [
|
|
402
|
+
/^seed-/,
|
|
403
|
+
/extension/,
|
|
404
|
+
/browser-session/,
|
|
405
|
+
];
|
|
406
|
+
|
|
407
|
+
export function isAutoReadExcludedActionName(name: string): boolean {
|
|
408
|
+
return (
|
|
409
|
+
AUTO_READ_EXCLUDED_ACTION_NAMES.has(name) ||
|
|
410
|
+
AUTO_READ_EXCLUDED_ACTION_PATTERNS.some((pattern) => pattern.test(name))
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function autoAuthenticatedReadNames(
|
|
415
|
+
actions: Record<string, ActionEntry>,
|
|
416
|
+
config: MCPConfig,
|
|
417
|
+
): Set<string> {
|
|
418
|
+
if (config.externalAgents?.authenticatedReads !== "auto") return new Set();
|
|
419
|
+
return new Set(
|
|
420
|
+
Object.entries(actions)
|
|
421
|
+
.filter(
|
|
422
|
+
([name, entry]) =>
|
|
423
|
+
isAuthenticatedReadAction(entry) &&
|
|
424
|
+
!isAutoReadExcludedActionName(name),
|
|
425
|
+
)
|
|
426
|
+
.map(([name]) => name),
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function externalAgentDenySet(config: MCPConfig): Set<string> {
|
|
431
|
+
return new Set(
|
|
432
|
+
(config.externalAgents?.denyActions ?? [])
|
|
433
|
+
.map((name) => name.trim())
|
|
434
|
+
.filter(Boolean),
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function externalAgentWritesAreAskAppOnly(config: MCPConfig): boolean {
|
|
439
|
+
return (
|
|
440
|
+
config.externalAgents?.writes === "ask_app_only" ||
|
|
441
|
+
(config.externalAgents?.authenticatedReads === "auto" &&
|
|
442
|
+
config.externalAgents?.writes !== "allowlisted")
|
|
443
|
+
);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
interface ResolvedMcpAppResource {
|
|
447
|
+
uri: string;
|
|
448
|
+
legacyUris?: string[];
|
|
449
|
+
name: string;
|
|
450
|
+
title?: string;
|
|
451
|
+
description?: string;
|
|
452
|
+
html: ActionMcpAppResourceConfig["html"];
|
|
453
|
+
mimeType: typeof MCP_APP_MIME_TYPE;
|
|
454
|
+
_meta?: Record<string, unknown>;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
interface McpAppResourceContext {
|
|
458
|
+
actionName: string;
|
|
459
|
+
appId?: string;
|
|
460
|
+
requestOrigin?: string;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
interface VersionedMcpAppResourceUri {
|
|
464
|
+
uri: string;
|
|
465
|
+
legacyUris?: string[];
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function metadataObject(value: unknown): Record<string, unknown> {
|
|
469
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
470
|
+
? (value as Record<string, unknown>)
|
|
471
|
+
: {};
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function originString(value: unknown): string | undefined {
|
|
475
|
+
if (typeof value !== "string" || !value.trim()) return undefined;
|
|
476
|
+
try {
|
|
477
|
+
return new URL(value).origin;
|
|
478
|
+
} catch {
|
|
479
|
+
return undefined;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function hostSpecificDomainString(value: unknown): string | undefined {
|
|
484
|
+
if (typeof value !== "string" || !value.trim()) return undefined;
|
|
485
|
+
const trimmed = value.trim();
|
|
486
|
+
try {
|
|
487
|
+
new URL(trimmed);
|
|
488
|
+
return undefined;
|
|
489
|
+
} catch {
|
|
490
|
+
return trimmed;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function withMcpChatBridgeParam(urlOrPath: string): string {
|
|
495
|
+
try {
|
|
496
|
+
const base = "http://agent-native.invalid";
|
|
497
|
+
const url = urlOrPath.startsWith("/")
|
|
498
|
+
? new URL(urlOrPath, base)
|
|
499
|
+
: new URL(urlOrPath);
|
|
500
|
+
url.searchParams.set(MCP_APP_CHAT_BRIDGE_QUERY_PARAM, "1");
|
|
501
|
+
return urlOrPath.startsWith("/")
|
|
502
|
+
? `${url.pathname}${url.search}${url.hash}`
|
|
503
|
+
: url.toString();
|
|
504
|
+
} catch {
|
|
505
|
+
return urlOrPath;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function isEmbedStartUrl(value: string): boolean {
|
|
510
|
+
try {
|
|
511
|
+
const base = "http://agent-native.invalid";
|
|
512
|
+
const url = value.startsWith("/") ? new URL(value, base) : new URL(value);
|
|
513
|
+
return url.pathname.includes("/_agent-native/embed/start");
|
|
514
|
+
} catch {
|
|
515
|
+
return value.includes("/_agent-native/embed/start");
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function routePathFromOpenUrl(value: string): string | null {
|
|
520
|
+
try {
|
|
521
|
+
const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(value);
|
|
522
|
+
const url = hasScheme
|
|
523
|
+
? new URL(value)
|
|
524
|
+
: new URL(value, "http://agent-native.invalid");
|
|
525
|
+
const route = `${url.pathname}${url.search}${url.hash}`;
|
|
526
|
+
if (!route.startsWith("/") || route.startsWith("//")) return null;
|
|
527
|
+
if (route.startsWith("/\\")) return null;
|
|
528
|
+
if (/^\/[a-z][a-z0-9+.-]*:/i.test(route)) return null;
|
|
529
|
+
return route;
|
|
530
|
+
} catch {
|
|
531
|
+
return null;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Recursively redact embed-ticket-bearing URLs from any value before it gets
|
|
537
|
+
* serialized into a model-visible text payload. Embed start URLs carry a
|
|
538
|
+
* single-use ticket that grants iframe access to the user's session — they
|
|
539
|
+
* MUST stay in `_meta` (where the embed runtime can consume them) and never
|
|
540
|
+
* appear in `content[].text` for the LLM. This is the generic safety net for
|
|
541
|
+
* actions that return `{ embedStartUrl, ... }` without declaring
|
|
542
|
+
* `mcpApp.resource` (the resource path already strips them via
|
|
543
|
+
* `mcpAppStructuredContent`).
|
|
544
|
+
*
|
|
545
|
+
* Circular structures are replaced with a marker. Strings that embed an
|
|
546
|
+
* `isEmbedStartUrl` substring (e.g. a longer message that includes the URL)
|
|
547
|
+
* are replaced with `[hidden embed URL]`. Credential-like `ticket` fields are
|
|
548
|
+
* removed only inside an embed-signaled object/branch, so ordinary business
|
|
549
|
+
* fields from unrelated read actions remain faithful.
|
|
550
|
+
*/
|
|
551
|
+
const EMBED_RESULT_SENSITIVE_KEYS = new Set([
|
|
552
|
+
"embedTargetPath",
|
|
553
|
+
"embedExpiresAt",
|
|
554
|
+
"embedTicket",
|
|
555
|
+
]);
|
|
556
|
+
|
|
557
|
+
function isEmbedCredentialKey(key: string): boolean {
|
|
558
|
+
return key === "ticket" || /Ticket$/.test(key);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function containsEmbedRoutingSignal(
|
|
562
|
+
value: unknown,
|
|
563
|
+
seen = new WeakSet<object>(),
|
|
564
|
+
): boolean {
|
|
565
|
+
if (typeof value === "string") return isEmbedStartUrl(value);
|
|
566
|
+
if (!value || typeof value !== "object") return false;
|
|
567
|
+
if (seen.has(value)) return false;
|
|
568
|
+
seen.add(value);
|
|
569
|
+
if (Array.isArray(value)) {
|
|
570
|
+
const result = value.some((item) => containsEmbedRoutingSignal(item, seen));
|
|
571
|
+
seen.delete(value);
|
|
572
|
+
return result;
|
|
573
|
+
}
|
|
574
|
+
for (const [key, val] of Object.entries(value)) {
|
|
575
|
+
if (EMBED_RESULT_SENSITIVE_KEYS.has(key)) {
|
|
576
|
+
seen.delete(value);
|
|
577
|
+
return true;
|
|
578
|
+
}
|
|
579
|
+
if (containsEmbedRoutingSignal(val, seen)) {
|
|
580
|
+
seen.delete(value);
|
|
581
|
+
return true;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
seen.delete(value);
|
|
585
|
+
return false;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function purgeEmbedStartUrls(
|
|
589
|
+
value: unknown,
|
|
590
|
+
seen = new WeakSet<object>(),
|
|
591
|
+
embedContext = false,
|
|
592
|
+
): unknown {
|
|
593
|
+
if (typeof value === "string") {
|
|
594
|
+
return isEmbedStartUrl(value) ? "[hidden embed URL]" : value;
|
|
595
|
+
}
|
|
596
|
+
if (Array.isArray(value)) {
|
|
597
|
+
if (seen.has(value)) return "[circular result]";
|
|
598
|
+
seen.add(value);
|
|
599
|
+
// An embed marker in one array item puts the whole result in the embed
|
|
600
|
+
// routing context. Credential fields in sibling items must not survive
|
|
601
|
+
// just because the marker lives elsewhere in the array.
|
|
602
|
+
const arrayEmbedContext = embedContext || containsEmbedRoutingSignal(value);
|
|
603
|
+
const out = value.map((item) =>
|
|
604
|
+
purgeEmbedStartUrls(item, seen, arrayEmbedContext),
|
|
605
|
+
);
|
|
606
|
+
seen.delete(value);
|
|
607
|
+
return out;
|
|
608
|
+
}
|
|
609
|
+
if (value && typeof value === "object") {
|
|
610
|
+
if (seen.has(value)) return "[circular result]";
|
|
611
|
+
seen.add(value);
|
|
612
|
+
const entries = Object.entries(value as Record<string, unknown>);
|
|
613
|
+
const localEmbedContext = embedContext || containsEmbedRoutingSignal(value);
|
|
614
|
+
const out: Record<string, unknown> = {};
|
|
615
|
+
for (const [key, val] of entries) {
|
|
616
|
+
if (
|
|
617
|
+
EMBED_RESULT_SENSITIVE_KEYS.has(key) ||
|
|
618
|
+
(localEmbedContext && isEmbedCredentialKey(key))
|
|
619
|
+
) {
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
if (typeof val === "string" && isEmbedStartUrl(val)) {
|
|
623
|
+
// Drop the key entirely for object-typed inputs so a tool result like
|
|
624
|
+
// `{ embedStartUrl: "..." }` does not appear at all in the LLM text.
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
out[key] = purgeEmbedStartUrls(val, seen, localEmbedContext);
|
|
628
|
+
}
|
|
629
|
+
seen.delete(value);
|
|
630
|
+
return out;
|
|
631
|
+
}
|
|
632
|
+
return value;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// True when a tool result carries SOME content. An errored/no-plan result comes
|
|
636
|
+
// back empty (`{}` / null) and would render an empty embed box. We gate on "has
|
|
637
|
+
// content", not "has a URL" — valid embeds often carry data but no URL (the
|
|
638
|
+
// shell mints the embed-start itself).
|
|
639
|
+
function mcpResultHasContent(result: unknown): boolean {
|
|
640
|
+
if (result == null) return false;
|
|
641
|
+
if (typeof result === "string") return result.trim().length > 0;
|
|
642
|
+
if (Array.isArray(result)) return result.length > 0;
|
|
643
|
+
if (typeof result === "object") return Object.keys(result).length > 0;
|
|
644
|
+
return Boolean(result);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function mcpAppEmbedOpenLinkMeta(
|
|
648
|
+
result: unknown,
|
|
649
|
+
resource: ResolvedMcpAppResource,
|
|
650
|
+
meta: MCPRequestMeta | undefined,
|
|
651
|
+
): Record<string, unknown> {
|
|
652
|
+
const out = metadataObject(result);
|
|
653
|
+
const embedStartUrl =
|
|
654
|
+
typeof out.embedStartUrl === "string"
|
|
655
|
+
? out.embedStartUrl
|
|
656
|
+
: out.embed === true &&
|
|
657
|
+
typeof out.url === "string" &&
|
|
658
|
+
out.url.includes("/_agent-native/embed/start")
|
|
659
|
+
? out.url
|
|
660
|
+
: null;
|
|
661
|
+
if (!embedStartUrl) return {};
|
|
662
|
+
|
|
663
|
+
const webUrl = toAbsoluteOpenUrl(
|
|
664
|
+
withMcpChatBridgeParam(embedStartUrl),
|
|
665
|
+
meta?.origin,
|
|
666
|
+
);
|
|
667
|
+
const deepLinkUrl =
|
|
668
|
+
typeof out.deepLinkUrl === "string" ? out.deepLinkUrl : null;
|
|
669
|
+
const fallbackLabel = resource.title ?? resource.name ?? "app";
|
|
670
|
+
const label =
|
|
671
|
+
typeof out.app === "string" && out.app.trim()
|
|
672
|
+
? `Open ${out.app.trim()}`
|
|
673
|
+
: fallbackLabel;
|
|
674
|
+
const view =
|
|
675
|
+
typeof out.view === "string" && out.view.trim()
|
|
676
|
+
? out.view.trim()
|
|
677
|
+
: typeof out.path === "string" && out.path.trim()
|
|
678
|
+
? out.path.trim()
|
|
679
|
+
: undefined;
|
|
680
|
+
// Only fabricate an open URL when there is a real path-like value: an
|
|
681
|
+
// explicit deepLinkUrl, or a non-embed `out.url`, or a leading-slash
|
|
682
|
+
// `view`/`path` that's already a route. Bare view-name strings like
|
|
683
|
+
// "inbox" or "deck" must NOT be turned into `${origin}/inbox` — apps
|
|
684
|
+
// route views at app-specific paths (e.g. slides routes `view: "deck"`
|
|
685
|
+
// at `/deck/:id`), so a synthesized origin-relative URL is just a 404.
|
|
686
|
+
// In that case omit `openLink` entirely; the embedStart meta carries
|
|
687
|
+
// the actual launch reference.
|
|
688
|
+
const pathFromRouteLike =
|
|
689
|
+
view && view.startsWith("/")
|
|
690
|
+
? view
|
|
691
|
+
: typeof out.path === "string" && out.path.trim().startsWith("/")
|
|
692
|
+
? out.path.trim()
|
|
693
|
+
: undefined;
|
|
694
|
+
const explicitOpenUrl = deepLinkUrl
|
|
695
|
+
? deepLinkUrl
|
|
696
|
+
: typeof out.url === "string" && !isEmbedStartUrl(out.url)
|
|
697
|
+
? out.url
|
|
698
|
+
: pathFromRouteLike;
|
|
699
|
+
const safeOpenUrl = explicitOpenUrl
|
|
700
|
+
? toAbsoluteOpenUrl(explicitOpenUrl, meta?.origin)
|
|
701
|
+
: null;
|
|
702
|
+
// Embed open links expose the safe browser target in `webUrl`, but the
|
|
703
|
+
// desktop URL must enter the app through the registered scheme so Electron
|
|
704
|
+
// can focus the right webview. Preserve the full route/query in the `to`
|
|
705
|
+
// param; focus ids are often only present on `url`, not `out.params`.
|
|
706
|
+
const desktopDeepLinkUrl = (() => {
|
|
707
|
+
if (!safeOpenUrl) return null;
|
|
708
|
+
const app =
|
|
709
|
+
typeof out.app === "string" && out.app.trim()
|
|
710
|
+
? out.app.trim()
|
|
711
|
+
: undefined;
|
|
712
|
+
if (!app) return safeOpenUrl;
|
|
713
|
+
if (isAgentNativeOpenDeepLink(safeOpenUrl)) {
|
|
714
|
+
return toDesktopOpenUrl(safeOpenUrl);
|
|
715
|
+
}
|
|
716
|
+
const targetRoute = routePathFromOpenUrl(safeOpenUrl);
|
|
717
|
+
if (!targetRoute) return safeOpenUrl;
|
|
718
|
+
const viewParam =
|
|
719
|
+
typeof out.view === "string" && out.view.trim() ? out.view.trim() : "";
|
|
720
|
+
const params =
|
|
721
|
+
out.params && typeof out.params === "object" && !Array.isArray(out.params)
|
|
722
|
+
? (out.params as Record<
|
|
723
|
+
string,
|
|
724
|
+
string | number | boolean | null | undefined
|
|
725
|
+
>)
|
|
726
|
+
: undefined;
|
|
727
|
+
return toDesktopOpenUrl(
|
|
728
|
+
buildDeepLink({
|
|
729
|
+
app,
|
|
730
|
+
view: viewParam,
|
|
731
|
+
to: targetRoute,
|
|
732
|
+
...(params ? { params } : {}),
|
|
733
|
+
}),
|
|
734
|
+
);
|
|
735
|
+
})();
|
|
736
|
+
|
|
737
|
+
return {
|
|
738
|
+
"agent-native/embedStart": {
|
|
739
|
+
startUrl: webUrl,
|
|
740
|
+
...(typeof out.embedExpiresAt === "number"
|
|
741
|
+
? { expiresAt: out.embedExpiresAt }
|
|
742
|
+
: {}),
|
|
743
|
+
},
|
|
744
|
+
...(safeOpenUrl
|
|
745
|
+
? {
|
|
746
|
+
"agent-native/openLink": {
|
|
747
|
+
label,
|
|
748
|
+
...(view ? { view } : {}),
|
|
749
|
+
webUrl: safeOpenUrl,
|
|
750
|
+
desktopUrl: desktopDeepLinkUrl ?? safeOpenUrl,
|
|
751
|
+
vscodeUrl: toVsCodeOpenUrl(safeOpenUrl),
|
|
752
|
+
},
|
|
753
|
+
}
|
|
754
|
+
: {}),
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
async function withServerMintedMcpAppEmbedStart(
|
|
759
|
+
result: unknown,
|
|
760
|
+
meta: MCPRequestMeta | undefined,
|
|
761
|
+
): Promise<unknown> {
|
|
762
|
+
if (!result || typeof result !== "object" || Array.isArray(result)) {
|
|
763
|
+
return result;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
const out = result as Record<string, unknown>;
|
|
767
|
+
if (out.embed !== true) return result;
|
|
768
|
+
if (typeof out.embedStartUrl === "string" && out.embedStartUrl.trim()) {
|
|
769
|
+
return result;
|
|
770
|
+
}
|
|
771
|
+
if (
|
|
772
|
+
typeof out.url === "string" &&
|
|
773
|
+
out.url.trim() &&
|
|
774
|
+
isEmbedStartUrl(out.url)
|
|
775
|
+
) {
|
|
776
|
+
return result;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
const candidate = [out.url, out.path, out.deepLinkUrl].find(
|
|
780
|
+
(value): value is string =>
|
|
781
|
+
typeof value === "string" && value.trim().length > 0,
|
|
782
|
+
);
|
|
783
|
+
if (!candidate) return result;
|
|
784
|
+
|
|
785
|
+
const trimmed = candidate.trim();
|
|
786
|
+
const isPath = trimmed.startsWith("/") && !trimmed.startsWith("//");
|
|
787
|
+
const isAbsoluteHttp = /^https?:\/\//i.test(trimmed);
|
|
788
|
+
if (!isPath && !isAbsoluteHttp) return result;
|
|
789
|
+
if (isAbsoluteHttp && !meta?.origin) return result;
|
|
790
|
+
|
|
791
|
+
const ctx = getRequestContext();
|
|
792
|
+
const ownerEmail = ctx?.userEmail?.trim();
|
|
793
|
+
if (!ownerEmail) return result;
|
|
794
|
+
|
|
795
|
+
const { normalizeEmbedTargetPath, createEmbedSessionTicket } =
|
|
796
|
+
await import("../server/embed-session.js");
|
|
797
|
+
const { buildEmbedStartPath } = await import("../server/embed-route.js");
|
|
798
|
+
const targetPath = normalizeEmbedTargetPath(
|
|
799
|
+
withMcpChatBridgeParam(trimmed),
|
|
800
|
+
meta?.origin,
|
|
801
|
+
);
|
|
802
|
+
if (!targetPath) return result;
|
|
803
|
+
|
|
804
|
+
const ticket = await createEmbedSessionTicket({
|
|
805
|
+
ownerEmail,
|
|
806
|
+
orgId: ctx?.orgId,
|
|
807
|
+
targetPath,
|
|
808
|
+
scope: typeof out.chrome === "string" ? out.chrome : null,
|
|
809
|
+
});
|
|
810
|
+
const startPath = buildEmbedStartPath(ticket.ticket);
|
|
811
|
+
const embedStartUrl = meta?.origin
|
|
812
|
+
? new URL(startPath, meta.origin).toString()
|
|
813
|
+
: startPath;
|
|
814
|
+
|
|
815
|
+
return {
|
|
816
|
+
...out,
|
|
817
|
+
embedStartUrl,
|
|
818
|
+
embedTargetPath: targetPath,
|
|
819
|
+
embedExpiresAt: ticket.expiresAt,
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* Build the deep-link content block + structured `_meta` for a tool result.
|
|
825
|
+
* Best-effort: any throw / nullish link is swallowed so a bad `link` builder
|
|
826
|
+
* never fails the tool call.
|
|
827
|
+
*/
|
|
828
|
+
export function buildLinkArtifacts(
|
|
829
|
+
entry: ActionEntry,
|
|
830
|
+
args: Record<string, any>,
|
|
831
|
+
result: any,
|
|
832
|
+
meta: MCPRequestMeta | undefined,
|
|
833
|
+
): {
|
|
834
|
+
block?: { type: "text"; text: string };
|
|
835
|
+
_meta?: Record<string, unknown>;
|
|
836
|
+
} {
|
|
837
|
+
if (typeof entry.link !== "function") return {};
|
|
838
|
+
try {
|
|
839
|
+
const lk = entry.link({ args: args ?? {}, result });
|
|
840
|
+
if (!lk?.url) return {};
|
|
841
|
+
const linkUrl = isAgentNativeOpenDeepLink(lk.url)
|
|
842
|
+
? withCollapsedAgentSidebarParam(lk.url)
|
|
843
|
+
: lk.url;
|
|
844
|
+
const webUrl = toAbsoluteOpenUrl(linkUrl, meta?.origin);
|
|
845
|
+
const desktopUrl = toDesktopOpenUrl(linkUrl);
|
|
846
|
+
const vscodeUrl = toVsCodeOpenUrl(webUrl);
|
|
847
|
+
const markdownUrl = meta?.target === "desktop" ? desktopUrl : webUrl;
|
|
848
|
+
return {
|
|
849
|
+
block: { type: "text", text: `\n\n[${lk.label} →](${markdownUrl})` },
|
|
850
|
+
_meta: {
|
|
851
|
+
"agent-native/openLink": {
|
|
852
|
+
label: lk.label,
|
|
853
|
+
view: lk.view,
|
|
854
|
+
webUrl,
|
|
855
|
+
desktopUrl,
|
|
856
|
+
vscodeUrl,
|
|
857
|
+
},
|
|
858
|
+
},
|
|
859
|
+
};
|
|
860
|
+
} catch {
|
|
861
|
+
return {};
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/**
|
|
866
|
+
* Merge the generic cross-app builtin tools into the config's action
|
|
867
|
+
* registry. **Template actions take precedence**: if a template defines an
|
|
868
|
+
* action with the same name as a builtin (e.g. its own `list_apps`), the
|
|
869
|
+
* template entry wins and the builtin is dropped. This mirrors the
|
|
870
|
+
* template-over-workspace-core precedence in `autoDiscoverActions`.
|
|
871
|
+
*
|
|
872
|
+
* The builtins are pure-ish navigators / scaffolders; they call back into the
|
|
873
|
+
* same `config.actions` / `config.askAgent` so there is no second agent loop.
|
|
874
|
+
*/
|
|
875
|
+
function mergeBuiltinTools(
|
|
876
|
+
config: MCPConfig,
|
|
877
|
+
baseActions: Record<string, ActionEntry>,
|
|
878
|
+
requestMeta?: MCPRequestMeta,
|
|
879
|
+
): Record<string, ActionEntry> {
|
|
880
|
+
if (config.builtinCrossAppTools === false) return baseActions;
|
|
881
|
+
const builtins = getBuiltinCrossAppTools(config, requestMeta);
|
|
882
|
+
const merged: Record<string, ActionEntry> = { ...builtins };
|
|
883
|
+
// Template / app actions overwrite same-named builtins.
|
|
884
|
+
for (const [name, entry] of Object.entries(baseActions)) {
|
|
885
|
+
merged[name] = entry;
|
|
886
|
+
}
|
|
887
|
+
return merged;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
function absoluteMetadataUrl(
|
|
891
|
+
value: string | undefined,
|
|
892
|
+
requestMeta?: MCPRequestMeta,
|
|
893
|
+
): string | undefined {
|
|
894
|
+
const trimmed = value?.trim();
|
|
895
|
+
if (!trimmed) return undefined;
|
|
896
|
+
try {
|
|
897
|
+
if (requestMeta?.origin) {
|
|
898
|
+
const basePath = requestMeta.basePath ?? getConfiguredAppBasePath();
|
|
899
|
+
const appBase = `${requestMeta.origin.replace(/\/+$/, "")}${basePath}/`;
|
|
900
|
+
const appLocalValue =
|
|
901
|
+
trimmed.startsWith("/") && !trimmed.startsWith("//")
|
|
902
|
+
? trimmed.replace(/^\/+/, "")
|
|
903
|
+
: trimmed;
|
|
904
|
+
return new URL(appLocalValue, appBase).href;
|
|
905
|
+
}
|
|
906
|
+
return new URL(trimmed).href;
|
|
907
|
+
} catch {
|
|
908
|
+
return trimmed;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
function mcpServerInfo(config: MCPConfig, requestMeta?: MCPRequestMeta) {
|
|
913
|
+
const websiteUrl = absoluteMetadataUrl(config.websiteUrl, requestMeta);
|
|
914
|
+
const icons = config.icons
|
|
915
|
+
?.map((icon) => {
|
|
916
|
+
const src = absoluteMetadataUrl(icon.src, requestMeta);
|
|
917
|
+
if (!src) return null;
|
|
918
|
+
return {
|
|
919
|
+
src,
|
|
920
|
+
...(icon.mimeType ? { mimeType: icon.mimeType } : {}),
|
|
921
|
+
...(icon.sizes?.length ? { sizes: icon.sizes } : {}),
|
|
922
|
+
...(icon.theme ? { theme: icon.theme } : {}),
|
|
923
|
+
};
|
|
924
|
+
})
|
|
925
|
+
.filter((icon): icon is NonNullable<typeof icon> => Boolean(icon));
|
|
926
|
+
return {
|
|
927
|
+
name: config.name,
|
|
928
|
+
version: config.version ?? "1.0.0",
|
|
929
|
+
...(config.title?.trim() ? { title: config.title.trim() } : {}),
|
|
930
|
+
...(config.description?.trim()
|
|
931
|
+
? { description: config.description.trim() }
|
|
932
|
+
: {}),
|
|
933
|
+
...(websiteUrl ? { websiteUrl } : {}),
|
|
934
|
+
...(icons?.length ? { icons } : {}),
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
function safeUiSegment(value: string | undefined, fallback: string): string {
|
|
939
|
+
const normalized = (value || fallback)
|
|
940
|
+
.trim()
|
|
941
|
+
.toLowerCase()
|
|
942
|
+
.replace(/[^a-z0-9._-]+/g, "-")
|
|
943
|
+
.replace(/^-+|-+$/g, "");
|
|
944
|
+
return normalized || fallback;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// ChatGPT and Claude cache MCP App resource HTML by `ui://` URI. Bump this
|
|
948
|
+
// when the shared shell changes in a way that must invalidate host caches.
|
|
949
|
+
const MCP_APP_RESOURCE_SHELL_VERSION = "shell-v64";
|
|
950
|
+
|
|
951
|
+
function legacyDefaultMcpAppUri(config: MCPConfig, actionName: string): string {
|
|
952
|
+
const app = safeUiSegment(config.appId ?? config.name, "agent-native");
|
|
953
|
+
const action = safeUiSegment(actionName, "tool");
|
|
954
|
+
return `ui://${app}/${action}`;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
function versionMcpAppResourceUri(
|
|
958
|
+
rawUri: string,
|
|
959
|
+
): VersionedMcpAppResourceUri | null {
|
|
960
|
+
const uri = rawUri.trim();
|
|
961
|
+
if (!uri.startsWith("ui://")) return null;
|
|
962
|
+
const versionSuffix = `/${MCP_APP_RESOURCE_SHELL_VERSION}`;
|
|
963
|
+
let versionedUri: string;
|
|
964
|
+
try {
|
|
965
|
+
const parsed = new URL(uri);
|
|
966
|
+
const path = parsed.pathname.replace(/\/+$/g, "");
|
|
967
|
+
parsed.pathname = /\/shell-v\d+$/.test(path)
|
|
968
|
+
? path.replace(/\/shell-v\d+$/, versionSuffix)
|
|
969
|
+
: `${path}${versionSuffix}`;
|
|
970
|
+
versionedUri = parsed.toString();
|
|
971
|
+
} catch {
|
|
972
|
+
return null;
|
|
973
|
+
}
|
|
974
|
+
return {
|
|
975
|
+
uri: versionedUri,
|
|
976
|
+
...(versionedUri !== uri ? { legacyUris: [uri] } : {}),
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
function unversionMcpAppResourceUri(uri: string): string | null {
|
|
981
|
+
if (!uri.startsWith("ui://")) return null;
|
|
982
|
+
try {
|
|
983
|
+
const parsed = new URL(uri);
|
|
984
|
+
parsed.pathname = parsed.pathname
|
|
985
|
+
.replace(/\/+$/g, "")
|
|
986
|
+
.replace(/\/shell-v\d+$/g, "");
|
|
987
|
+
return parsed.toString();
|
|
988
|
+
} catch {
|
|
989
|
+
return null;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
function normalizeMcpAppResourceUriForMatch(uri: unknown): string | null {
|
|
994
|
+
if (typeof uri !== "string") return null;
|
|
995
|
+
const trimmed = uri.trim();
|
|
996
|
+
if (!trimmed.startsWith("ui://")) return null;
|
|
997
|
+
return (
|
|
998
|
+
unversionMcpAppResourceUri(trimmed) ??
|
|
999
|
+
trimmed.replace(/\/+$/g, "").replace(/\/shell-v\d+(?=([?#]|$))/g, "")
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
function matchesMcpAppResourceUri(
|
|
1004
|
+
resourceUri: VersionedMcpAppResourceUri,
|
|
1005
|
+
requestedUri: unknown,
|
|
1006
|
+
): boolean {
|
|
1007
|
+
if (typeof requestedUri !== "string") return false;
|
|
1008
|
+
const requested = requestedUri.trim();
|
|
1009
|
+
if (resourceUri.uri === requested) return true;
|
|
1010
|
+
if (resourceUri.legacyUris?.includes(requested)) return true;
|
|
1011
|
+
const requestedBase = normalizeMcpAppResourceUriForMatch(requested);
|
|
1012
|
+
const currentBase = normalizeMcpAppResourceUriForMatch(resourceUri.uri);
|
|
1013
|
+
return Boolean(requestedBase && currentBase && requestedBase === currentBase);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
function getMcpAppResourceUri(
|
|
1017
|
+
config: MCPConfig,
|
|
1018
|
+
actionName: string,
|
|
1019
|
+
entry: ActionEntry,
|
|
1020
|
+
): VersionedMcpAppResourceUri | null {
|
|
1021
|
+
const resource = entry.mcpApp?.resource;
|
|
1022
|
+
if (!resource) return null;
|
|
1023
|
+
const baseUri =
|
|
1024
|
+
resource.uri?.trim() || legacyDefaultMcpAppUri(config, actionName);
|
|
1025
|
+
return versionMcpAppResourceUri(baseUri);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
function expandRequestOriginSources(
|
|
1029
|
+
sources: string[] | undefined,
|
|
1030
|
+
requestMeta?: MCPRequestMeta,
|
|
1031
|
+
): string[] | undefined {
|
|
1032
|
+
if (!sources) return undefined;
|
|
1033
|
+
const origin = requestMeta?.origin;
|
|
1034
|
+
return sources.flatMap((source) =>
|
|
1035
|
+
source === MCP_APP_REQUEST_ORIGIN_CSP_SOURCE && origin
|
|
1036
|
+
? [origin]
|
|
1037
|
+
: [source],
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
function openAiWidgetCsp(
|
|
1042
|
+
cspConfig: ActionMcpAppCsp | undefined,
|
|
1043
|
+
requestMeta?: MCPRequestMeta,
|
|
1044
|
+
): Record<string, string[]> | undefined {
|
|
1045
|
+
if (!cspConfig) return undefined;
|
|
1046
|
+
const csp: Record<string, string[]> = {};
|
|
1047
|
+
const connectDomains = expandRequestOriginSources(
|
|
1048
|
+
cspConfig.connectDomains,
|
|
1049
|
+
requestMeta,
|
|
1050
|
+
);
|
|
1051
|
+
const resourceDomains = expandRequestOriginSources(
|
|
1052
|
+
cspConfig.resourceDomains,
|
|
1053
|
+
requestMeta,
|
|
1054
|
+
);
|
|
1055
|
+
const frameDomains = expandRequestOriginSources(
|
|
1056
|
+
cspConfig.frameDomains,
|
|
1057
|
+
requestMeta,
|
|
1058
|
+
);
|
|
1059
|
+
if (connectDomains?.length) csp.connect_domains = connectDomains;
|
|
1060
|
+
if (resourceDomains?.length) csp.resource_domains = resourceDomains;
|
|
1061
|
+
if (frameDomains?.length) csp.frame_domains = frameDomains;
|
|
1062
|
+
return Object.keys(csp).length > 0 ? csp : undefined;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
function mcpAppUiMeta(
|
|
1066
|
+
resource: ActionMcpAppResourceConfig,
|
|
1067
|
+
resolvedCsp: ActionMcpAppCsp | undefined,
|
|
1068
|
+
requestMeta?: MCPRequestMeta,
|
|
1069
|
+
description?: string,
|
|
1070
|
+
): Record<string, unknown> | undefined {
|
|
1071
|
+
const base =
|
|
1072
|
+
resource._meta && typeof resource._meta === "object"
|
|
1073
|
+
? { ...resource._meta }
|
|
1074
|
+
: {};
|
|
1075
|
+
const existingUi =
|
|
1076
|
+
base.ui && typeof base.ui === "object" && !Array.isArray(base.ui)
|
|
1077
|
+
? (base.ui as Record<string, unknown>)
|
|
1078
|
+
: {};
|
|
1079
|
+
const ui: Record<string, unknown> = { ...existingUi };
|
|
1080
|
+
delete ui.domain;
|
|
1081
|
+
if (resolvedCsp) {
|
|
1082
|
+
ui.csp = {
|
|
1083
|
+
...resolvedCsp,
|
|
1084
|
+
connectDomains: expandRequestOriginSources(
|
|
1085
|
+
resolvedCsp.connectDomains,
|
|
1086
|
+
requestMeta,
|
|
1087
|
+
),
|
|
1088
|
+
resourceDomains: expandRequestOriginSources(
|
|
1089
|
+
resolvedCsp.resourceDomains,
|
|
1090
|
+
requestMeta,
|
|
1091
|
+
),
|
|
1092
|
+
frameDomains: expandRequestOriginSources(
|
|
1093
|
+
resolvedCsp.frameDomains,
|
|
1094
|
+
requestMeta,
|
|
1095
|
+
),
|
|
1096
|
+
baseUriDomains: expandRequestOriginSources(
|
|
1097
|
+
resolvedCsp.baseUriDomains,
|
|
1098
|
+
requestMeta,
|
|
1099
|
+
),
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
if (resource.permissions) ui.permissions = resource.permissions;
|
|
1103
|
+
const hostSpecificDomain =
|
|
1104
|
+
hostSpecificDomainString(resource.domain) ??
|
|
1105
|
+
hostSpecificDomainString(existingUi.domain);
|
|
1106
|
+
if (hostSpecificDomain) ui.domain = hostSpecificDomain;
|
|
1107
|
+
const openAiWidgetDomain =
|
|
1108
|
+
originString(resource.domain) ??
|
|
1109
|
+
originString(ui.domain) ??
|
|
1110
|
+
originString(existingUi.domain) ??
|
|
1111
|
+
originString(requestMeta?.origin);
|
|
1112
|
+
if (typeof resource.prefersBorder === "boolean") {
|
|
1113
|
+
ui.prefersBorder = resource.prefersBorder;
|
|
1114
|
+
}
|
|
1115
|
+
if (Object.keys(ui).length > 0) base.ui = ui;
|
|
1116
|
+
if (description && base["openai/widgetDescription"] == null) {
|
|
1117
|
+
base["openai/widgetDescription"] = description;
|
|
1118
|
+
}
|
|
1119
|
+
if (
|
|
1120
|
+
typeof resource.prefersBorder === "boolean" &&
|
|
1121
|
+
base["openai/widgetPrefersBorder"] == null
|
|
1122
|
+
) {
|
|
1123
|
+
base["openai/widgetPrefersBorder"] = resource.prefersBorder;
|
|
1124
|
+
}
|
|
1125
|
+
const openAiCsp = openAiWidgetCsp(resolvedCsp, requestMeta);
|
|
1126
|
+
if (openAiCsp && base["openai/widgetCSP"] == null) {
|
|
1127
|
+
base["openai/widgetCSP"] = openAiCsp;
|
|
1128
|
+
}
|
|
1129
|
+
if (openAiWidgetDomain && base["openai/widgetDomain"] == null) {
|
|
1130
|
+
base["openai/widgetDomain"] = openAiWidgetDomain;
|
|
1131
|
+
}
|
|
1132
|
+
return Object.keys(base).length > 0 ? base : undefined;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
async function resolveMcpAppCsp(
|
|
1136
|
+
resource: ActionMcpAppResourceConfig,
|
|
1137
|
+
ctx: McpAppResourceContext,
|
|
1138
|
+
): Promise<ActionMcpAppCsp | undefined> {
|
|
1139
|
+
if (!resource.csp) return undefined;
|
|
1140
|
+
return typeof resource.csp === "function"
|
|
1141
|
+
? await resource.csp(ctx)
|
|
1142
|
+
: resource.csp;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
async function resolveMcpAppResource(
|
|
1146
|
+
config: MCPConfig,
|
|
1147
|
+
actionName: string,
|
|
1148
|
+
entry: ActionEntry,
|
|
1149
|
+
requestMeta?: MCPRequestMeta,
|
|
1150
|
+
): Promise<ResolvedMcpAppResource | null> {
|
|
1151
|
+
const resource = entry.mcpApp?.resource;
|
|
1152
|
+
if (!resource) return null;
|
|
1153
|
+
// NB: the inline kill switch is intentionally NOT enforced here. This
|
|
1154
|
+
// resolver also backs `resources/read`, which must keep serving the shell
|
|
1155
|
+
// for a URI the host already holds (e.g. a cached descriptor) so it degrades
|
|
1156
|
+
// gracefully instead of throwing a hard `-32603`. The switch is enforced at
|
|
1157
|
+
// the *advertisement/render* sites (`tools/list` descriptor meta,
|
|
1158
|
+
// `tools/call` result meta, `resources/list`) so disabled embeds never get
|
|
1159
|
+
// advertised in the first place.
|
|
1160
|
+
const resolvedUri = getMcpAppResourceUri(config, actionName, entry);
|
|
1161
|
+
if (!resolvedUri) return null;
|
|
1162
|
+
const description = resource.description ?? entry.tool.description;
|
|
1163
|
+
const resolvedCsp = await resolveMcpAppCsp(resource, {
|
|
1164
|
+
actionName,
|
|
1165
|
+
appId: config.appId,
|
|
1166
|
+
requestOrigin: requestMeta?.origin,
|
|
1167
|
+
});
|
|
1168
|
+
const resourceMeta = mcpAppUiMeta(
|
|
1169
|
+
resource,
|
|
1170
|
+
resolvedCsp,
|
|
1171
|
+
requestMeta,
|
|
1172
|
+
description,
|
|
1173
|
+
);
|
|
1174
|
+
return {
|
|
1175
|
+
uri: resolvedUri.uri,
|
|
1176
|
+
...(resolvedUri.legacyUris ? { legacyUris: resolvedUri.legacyUris } : {}),
|
|
1177
|
+
name: resource.name?.trim() || actionName,
|
|
1178
|
+
...(resource.title ? { title: resource.title } : {}),
|
|
1179
|
+
...(description ? { description } : {}),
|
|
1180
|
+
html: resource.html,
|
|
1181
|
+
mimeType: resource.mimeType ?? MCP_APP_MIME_TYPE,
|
|
1182
|
+
...(resourceMeta ? { _meta: resourceMeta } : {}),
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
async function resolveMcpAppResourceSafely(
|
|
1187
|
+
config: MCPConfig,
|
|
1188
|
+
actionName: string,
|
|
1189
|
+
entry: ActionEntry,
|
|
1190
|
+
requestMeta?: MCPRequestMeta,
|
|
1191
|
+
): Promise<ResolvedMcpAppResource | null> {
|
|
1192
|
+
try {
|
|
1193
|
+
return await resolveMcpAppResource(config, actionName, entry, requestMeta);
|
|
1194
|
+
} catch (error) {
|
|
1195
|
+
console.warn(
|
|
1196
|
+
`[mcp] Skipping MCP App resource for action "${actionName}" because its metadata could not be resolved.`,
|
|
1197
|
+
error,
|
|
1198
|
+
);
|
|
1199
|
+
return null;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
async function getMcpAppResources(
|
|
1204
|
+
config: MCPConfig,
|
|
1205
|
+
actions: Record<string, ActionEntry>,
|
|
1206
|
+
requestMeta?: MCPRequestMeta,
|
|
1207
|
+
): Promise<ResolvedMcpAppResource[]> {
|
|
1208
|
+
// Advertisement path (resources/list + resources/templates/list): suppressed
|
|
1209
|
+
// by the inline kill switch so disabled embeds are never listed.
|
|
1210
|
+
if (!requestMeta?.inlineMcpApps) return [];
|
|
1211
|
+
const resources = await Promise.all(
|
|
1212
|
+
Object.entries(actions).map(([name, entry]) =>
|
|
1213
|
+
resolveMcpAppResourceSafely(config, name, entry, requestMeta),
|
|
1214
|
+
),
|
|
1215
|
+
);
|
|
1216
|
+
return resources.filter((resource): resource is ResolvedMcpAppResource =>
|
|
1217
|
+
Boolean(resource),
|
|
1218
|
+
);
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
function renderMcpAppHtml(
|
|
1222
|
+
resource: ResolvedMcpAppResource,
|
|
1223
|
+
actionName: string,
|
|
1224
|
+
config: MCPConfig,
|
|
1225
|
+
requestMeta?: MCPRequestMeta,
|
|
1226
|
+
): string {
|
|
1227
|
+
if (typeof resource.html === "function") {
|
|
1228
|
+
return resource.html({
|
|
1229
|
+
actionName,
|
|
1230
|
+
appId: config.appId,
|
|
1231
|
+
requestOrigin: requestMeta?.origin,
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
return resource.html;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
function openAiToolDescriptorMeta(
|
|
1238
|
+
resource: ResolvedMcpAppResource,
|
|
1239
|
+
): Record<string, unknown> {
|
|
1240
|
+
const label = resource.title ?? resource.name;
|
|
1241
|
+
const widgetCsp = metadataObject(resource._meta?.["openai/widgetCSP"]);
|
|
1242
|
+
return {
|
|
1243
|
+
"openai/outputTemplate": resource.uri,
|
|
1244
|
+
"openai/toolInvocation/invoking": `Opening ${label}`,
|
|
1245
|
+
"openai/toolInvocation/invoked": `${label} ready`,
|
|
1246
|
+
"openai/widgetAccessible": true,
|
|
1247
|
+
...(Object.keys(widgetCsp).length > 0
|
|
1248
|
+
? { "openai/widgetCSP": widgetCsp }
|
|
1249
|
+
: {}),
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
function openAiToolResultMeta(
|
|
1254
|
+
resource: ResolvedMcpAppResource,
|
|
1255
|
+
): Record<string, unknown> {
|
|
1256
|
+
const label = resource.title ?? resource.name;
|
|
1257
|
+
const widgetCsp = metadataObject(resource._meta?.["openai/widgetCSP"]);
|
|
1258
|
+
return {
|
|
1259
|
+
"openai/outputTemplate": resource.uri,
|
|
1260
|
+
"openai/toolInvocation/invoking": `Opening ${label}`,
|
|
1261
|
+
"openai/toolInvocation/invoked": `${label} ready`,
|
|
1262
|
+
"openai/widgetAccessible": true,
|
|
1263
|
+
...(Object.keys(widgetCsp).length > 0
|
|
1264
|
+
? { "openai/widgetCSP": widgetCsp }
|
|
1265
|
+
: {}),
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
function mcpAppToolUiMeta(
|
|
1270
|
+
resource: ResolvedMcpAppResource,
|
|
1271
|
+
visibility: unknown,
|
|
1272
|
+
): Record<string, unknown> {
|
|
1273
|
+
return {
|
|
1274
|
+
resourceUri: resource.uri,
|
|
1275
|
+
visibility: Array.isArray(visibility) ? visibility : ["model", "app"],
|
|
1276
|
+
};
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
function primitiveValue(value: unknown): value is string | number | boolean {
|
|
1280
|
+
return (
|
|
1281
|
+
typeof value === "string" ||
|
|
1282
|
+
typeof value === "number" ||
|
|
1283
|
+
typeof value === "boolean"
|
|
1284
|
+
);
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
function mcpAppStructuredContent(
|
|
1288
|
+
result: unknown,
|
|
1289
|
+
meta: Record<string, unknown> | undefined,
|
|
1290
|
+
): Record<string, unknown> {
|
|
1291
|
+
const purged = purgeEmbedStartUrls(result);
|
|
1292
|
+
const out: Record<string, unknown> =
|
|
1293
|
+
purged && typeof purged === "object" && !Array.isArray(purged)
|
|
1294
|
+
? { ...(purged as Record<string, unknown>) }
|
|
1295
|
+
: primitiveValue(purged)
|
|
1296
|
+
? { result: purged }
|
|
1297
|
+
: {};
|
|
1298
|
+
for (const key of ["embedStartUrl", "startUrl"]) {
|
|
1299
|
+
const value = out[key];
|
|
1300
|
+
if (typeof value === "string" && isEmbedStartUrl(value)) delete out[key];
|
|
1301
|
+
}
|
|
1302
|
+
if (typeof out.url === "string" && isEmbedStartUrl(out.url)) {
|
|
1303
|
+
delete out.url;
|
|
1304
|
+
}
|
|
1305
|
+
// Internal embed-routing fields belong in `_meta["agent-native/embedStart"]`
|
|
1306
|
+
// (consumed by the embed runtime), not in `structuredContent` (read by the
|
|
1307
|
+
// LLM). `embedTargetPath` reveals the exact route + thread/draft id the user
|
|
1308
|
+
// is looking at; `embedExpiresAt` is an unintended timestamp; ticket-bearing
|
|
1309
|
+
// fields are single-use credentials. Drop all of them unconditionally.
|
|
1310
|
+
const openLink = meta?.["agent-native/openLink"];
|
|
1311
|
+
if (openLink && typeof openLink === "object" && !Array.isArray(openLink)) {
|
|
1312
|
+
const webUrl = (openLink as Record<string, unknown>).webUrl;
|
|
1313
|
+
if (typeof webUrl === "string" && isEmbedStartUrl(webUrl)) {
|
|
1314
|
+
return Object.keys(out).length > 0 ? out : { status: "ok" };
|
|
1315
|
+
}
|
|
1316
|
+
out.openLink = openLink;
|
|
1317
|
+
if (typeof webUrl === "string" && !out.url) out.url = webUrl;
|
|
1318
|
+
}
|
|
1319
|
+
return Object.keys(out).length > 0 ? out : { status: "ok" };
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
function truncateToolText(value: string, max = 2000): string {
|
|
1323
|
+
if (value.length <= max) return value;
|
|
1324
|
+
return `${value.slice(0, max - 1)}…`;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
function conciseMcpAppToolText(
|
|
1328
|
+
name: string,
|
|
1329
|
+
result: unknown,
|
|
1330
|
+
structuredContent: Record<string, unknown>,
|
|
1331
|
+
): string {
|
|
1332
|
+
if (typeof result === "string") return truncateToolText(result);
|
|
1333
|
+
const message = structuredContent.message;
|
|
1334
|
+
if (typeof message === "string" && message.trim()) {
|
|
1335
|
+
return truncateToolText(message.trim());
|
|
1336
|
+
}
|
|
1337
|
+
const title = structuredContent.title ?? structuredContent.name;
|
|
1338
|
+
if (typeof title === "string" && title.trim()) {
|
|
1339
|
+
return `${title.trim()} is ready.`;
|
|
1340
|
+
}
|
|
1341
|
+
const id = structuredContent.id;
|
|
1342
|
+
if (typeof id === "string" && id.trim()) {
|
|
1343
|
+
return `${name} completed for ${id.trim()}.`;
|
|
1344
|
+
}
|
|
1345
|
+
return `${name} completed.`;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
function isSuccessOnlyResult(value: Record<string, unknown>): boolean {
|
|
1349
|
+
const keys = Object.keys(value);
|
|
1350
|
+
if (keys.length === 0) return true;
|
|
1351
|
+
return keys.every((key) => {
|
|
1352
|
+
const item = value[key];
|
|
1353
|
+
if (key === "ok" || key === "success") return item === true;
|
|
1354
|
+
if (key === "status") {
|
|
1355
|
+
return item === "ok" || item === "success" || item === "completed";
|
|
1356
|
+
}
|
|
1357
|
+
return false;
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
function conciseToolResultText(
|
|
1362
|
+
name: string,
|
|
1363
|
+
result: unknown,
|
|
1364
|
+
options?: { preserveObjectResult?: boolean },
|
|
1365
|
+
): string {
|
|
1366
|
+
const purged = purgeEmbedStartUrls(result);
|
|
1367
|
+
if (typeof purged === "string") return truncateToolText(purged);
|
|
1368
|
+
if (purged === true || purged == null) return `${name} completed.`;
|
|
1369
|
+
if (purged && typeof purged === "object" && !Array.isArray(purged)) {
|
|
1370
|
+
const record = purged as Record<string, unknown>;
|
|
1371
|
+
// Read-only actions are data reads, not mutations. Keep their object
|
|
1372
|
+
// payload available to MCP clients in the text fallback too; the
|
|
1373
|
+
// structuredContent branch below is the lossless path for clients that
|
|
1374
|
+
// support it. Mutating/action-style results retain the concise status
|
|
1375
|
+
// text so we do not unexpectedly dump write results into conversations.
|
|
1376
|
+
if (options?.preserveObjectResult) {
|
|
1377
|
+
const text = JSON.stringify(purged);
|
|
1378
|
+
return text === undefined ? `${name} completed.` : truncateToolText(text);
|
|
1379
|
+
}
|
|
1380
|
+
const message = record.message ?? record.summary;
|
|
1381
|
+
if (typeof message === "string" && message.trim()) {
|
|
1382
|
+
return truncateToolText(message.trim());
|
|
1383
|
+
}
|
|
1384
|
+
const id = record.id ?? record.planId ?? record.commentId;
|
|
1385
|
+
const title = record.title ?? record.name;
|
|
1386
|
+
if (typeof title === "string" && title.trim()) {
|
|
1387
|
+
const titleText = title.trim();
|
|
1388
|
+
return typeof id === "string" && id.trim()
|
|
1389
|
+
? `${titleText} (${id.trim()}) is ready.`
|
|
1390
|
+
: `${titleText} is ready.`;
|
|
1391
|
+
}
|
|
1392
|
+
if (typeof id === "string" && id.trim()) {
|
|
1393
|
+
return `${name} completed for ${id.trim()}.`;
|
|
1394
|
+
}
|
|
1395
|
+
const link = record.url ?? record.webUrl ?? record.path;
|
|
1396
|
+
if (typeof link === "string" && link.trim()) {
|
|
1397
|
+
return `${name} completed: ${truncateToolText(link.trim(), 500)}`;
|
|
1398
|
+
}
|
|
1399
|
+
if (isSuccessOnlyResult(record)) return `${name} completed.`;
|
|
1400
|
+
}
|
|
1401
|
+
const text = JSON.stringify(purged);
|
|
1402
|
+
return text === undefined ? `${name} completed.` : truncateToolText(text);
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
// ---------------------------------------------------------------------------
|
|
1406
|
+
// MCP Server creation — converts ActionEntry registry to MCP tools
|
|
1407
|
+
// ---------------------------------------------------------------------------
|
|
1408
|
+
|
|
1409
|
+
/**
|
|
1410
|
+
* Build a fully-wired MCP `Server` for a single request / session.
|
|
1411
|
+
*
|
|
1412
|
+
* Shared by the stateless Streamable-HTTP mount (`mountMCP`) and the stdio
|
|
1413
|
+
* standalone transport. The HTTP mount passes the per-request origin via
|
|
1414
|
+
* `requestMeta`; the stdio standalone path passes the resolved local app
|
|
1415
|
+
* origin so deep links still become absolute URLs.
|
|
1416
|
+
*/
|
|
1417
|
+
export async function createMCPServerForRequest(
|
|
1418
|
+
config: MCPConfig,
|
|
1419
|
+
identity: MCPCallerIdentity | undefined,
|
|
1420
|
+
requestMeta?: MCPRequestMeta,
|
|
1421
|
+
) {
|
|
1422
|
+
const { Server } = await import("@modelcontextprotocol/sdk/server/index.js");
|
|
1423
|
+
const {
|
|
1424
|
+
ListToolsRequestSchema,
|
|
1425
|
+
CallToolRequestSchema,
|
|
1426
|
+
ListResourcesRequestSchema,
|
|
1427
|
+
ReadResourceRequestSchema,
|
|
1428
|
+
ListResourceTemplatesRequestSchema,
|
|
1429
|
+
} = await import("@modelcontextprotocol/sdk/types.js");
|
|
1430
|
+
|
|
1431
|
+
// Resolve the effective caller identity. JWT / header-derived identity
|
|
1432
|
+
// (passed by `mountMCP` via `verifyAuth`) wins. When the caller passed no
|
|
1433
|
+
// identity — the stdio **standalone** path — fall back to the
|
|
1434
|
+
// `AGENT_NATIVE_OWNER_EMAIL` env the `agent-native mcp install` flow writes
|
|
1435
|
+
// into the `agent-native mcp serve` process env, so standalone tool runs are
|
|
1436
|
+
// tenant-scoped to the configured owner instead of running unscoped. Stays
|
|
1437
|
+
// undefined for true dev-open (no token, no secret, no owner) — behavior
|
|
1438
|
+
// there is unchanged.
|
|
1439
|
+
const ownerFromEnv = process.env.AGENT_NATIVE_OWNER_EMAIL?.trim();
|
|
1440
|
+
const effectiveIdentity: MCPCallerIdentity | undefined =
|
|
1441
|
+
identity ??
|
|
1442
|
+
(ownerFromEnv
|
|
1443
|
+
? { userEmail: ownerFromEnv, orgDomain: undefined }
|
|
1444
|
+
: undefined);
|
|
1445
|
+
|
|
1446
|
+
// Resolve the inline-MCP-App kill switch once per request from the effective
|
|
1447
|
+
// identity + environment, then thread it through `requestMeta` so every
|
|
1448
|
+
// resource/tool handler below honors the same decision. An explicit value on
|
|
1449
|
+
// the incoming meta (tests / embedded callers) wins.
|
|
1450
|
+
requestMeta = {
|
|
1451
|
+
...(requestMeta ?? {}),
|
|
1452
|
+
inlineMcpApps:
|
|
1453
|
+
requestMeta?.inlineMcpApps ?? isMcpAppsInlineEnabled(effectiveIdentity),
|
|
1454
|
+
};
|
|
1455
|
+
|
|
1456
|
+
// The action set the request handlers operate on = base actions + generic
|
|
1457
|
+
// cross-app builtins (template wins on name collision). An authenticated
|
|
1458
|
+
// real caller (connect-minted token / `mcp install` owner / production —
|
|
1459
|
+
// `requestMeta.fullSurface`, or the stdio standalone path identified by
|
|
1460
|
+
// `AGENT_NATIVE_OWNER_EMAIL`) gets the full `productionActions` surface
|
|
1461
|
+
// even in local dev; the unauthenticated dev-open path keeps the sparse
|
|
1462
|
+
// `config.actions`. See `external-agents` skill, "Dev vs production tool
|
|
1463
|
+
// surface".
|
|
1464
|
+
const useFullSurface = requestMeta?.fullSurface === true || !!ownerFromEnv;
|
|
1465
|
+
const baseActions =
|
|
1466
|
+
useFullSurface && config.productionActions
|
|
1467
|
+
? config.productionActions
|
|
1468
|
+
: config.actions;
|
|
1469
|
+
const actions = mergeBuiltinTools(config, baseActions, requestMeta);
|
|
1470
|
+
const visibleActions = Object.fromEntries(
|
|
1471
|
+
Object.entries(actions).filter(([, entry]) =>
|
|
1472
|
+
isActionVisibleForOAuthScope(entry, effectiveIdentity?.oauthScopes),
|
|
1473
|
+
),
|
|
1474
|
+
);
|
|
1475
|
+
const fullCatalogRequested = explicitlyRequestsFullMcpCatalog(requestMeta);
|
|
1476
|
+
// Compact/connector is the DEFAULT for every caller — hosted connectors,
|
|
1477
|
+
// code clients (Claude Code / Cursor / Codex), and the local CLI alike. The
|
|
1478
|
+
// full ~105-tool catalog is served only on the explicit opt-in above, so a
|
|
1479
|
+
// host can never dump every action schema into one giant tool card. The
|
|
1480
|
+
// `mcp:apps` scope still lands on this compact MCP-Apps surface; with no
|
|
1481
|
+
// opt-in, everyone else does too.
|
|
1482
|
+
const compactMcpAppCatalog = !fullCatalogRequested;
|
|
1483
|
+
const advertisedActionsBeforeConnector = compactMcpAppCatalog
|
|
1484
|
+
? Object.fromEntries(
|
|
1485
|
+
Object.entries(visibleActions).filter(([name, entry]) =>
|
|
1486
|
+
isActionAdvertisedInCompactMcpAppCatalog(name, entry, config),
|
|
1487
|
+
),
|
|
1488
|
+
)
|
|
1489
|
+
: visibleActions;
|
|
1490
|
+
const autoReadNames = autoAuthenticatedReadNames(visibleActions, config);
|
|
1491
|
+
const connectorNames = new Set([
|
|
1492
|
+
...(config.connectorCatalog ?? []),
|
|
1493
|
+
...autoReadNames,
|
|
1494
|
+
]);
|
|
1495
|
+
const denyNames = externalAgentDenySet(config);
|
|
1496
|
+
const automaticConnectorPolicyActive =
|
|
1497
|
+
config.externalAgents?.authenticatedReads === "auto";
|
|
1498
|
+
// Connector-catalog tier: when a template declares a connector allow-list,
|
|
1499
|
+
// serve exactly that curated surface plus any explicitly annotated
|
|
1500
|
+
// authenticated reads from `externalAgents.authenticatedReads: "auto"`.
|
|
1501
|
+
// This stays compact by default and keeps db-exec / seed-* / extension /
|
|
1502
|
+
// browser-session footguns off the external surface.
|
|
1503
|
+
const connectorCatalogActive =
|
|
1504
|
+
(connectorNames.size > 0 || automaticConnectorPolicyActive) &&
|
|
1505
|
+
!fullCatalogRequested;
|
|
1506
|
+
// When the connector catalog is active, filter directly from visibleActions
|
|
1507
|
+
// rather than advertisedActionsBeforeConnector. This ensures the connector
|
|
1508
|
+
// tier is an independent, template-declared surface that doesn't accidentally
|
|
1509
|
+
// narrow to just the compact-catalog builtins when shouldUseCompactMcpCatalogByDefault
|
|
1510
|
+
// would have activated the compact catalog for the same caller.
|
|
1511
|
+
const advertisedActions = connectorCatalogActive
|
|
1512
|
+
? Object.fromEntries(
|
|
1513
|
+
Object.entries(visibleActions).filter(([name, entry]) => {
|
|
1514
|
+
if (denyNames.has(name)) return false;
|
|
1515
|
+
if (COMPACT_MCP_APP_CATALOG_BUILTINS.has(name)) return true;
|
|
1516
|
+
if (!connectorNames.has(name)) return false;
|
|
1517
|
+
if (
|
|
1518
|
+
externalAgentWritesAreAskAppOnly(config) &&
|
|
1519
|
+
entry.readOnly !== true
|
|
1520
|
+
) {
|
|
1521
|
+
return false;
|
|
1522
|
+
}
|
|
1523
|
+
return true;
|
|
1524
|
+
}),
|
|
1525
|
+
)
|
|
1526
|
+
: advertisedActionsBeforeConnector;
|
|
1527
|
+
if (fullCatalogRequested) {
|
|
1528
|
+
warnFullCatalogServed(Object.keys(advertisedActions).length);
|
|
1529
|
+
}
|
|
1530
|
+
const supportsMcpApps =
|
|
1531
|
+
compactMcpAppCatalog ||
|
|
1532
|
+
Object.values(advertisedActions).some((entry) =>
|
|
1533
|
+
Boolean(entry.mcpApp?.resource),
|
|
1534
|
+
);
|
|
1535
|
+
const server = new Server(mcpServerInfo(config, requestMeta), {
|
|
1536
|
+
capabilities: {
|
|
1537
|
+
tools: {},
|
|
1538
|
+
...(supportsMcpApps
|
|
1539
|
+
? {
|
|
1540
|
+
resources: {},
|
|
1541
|
+
extensions: {
|
|
1542
|
+
[MCP_APP_EXTENSION_ID]: {
|
|
1543
|
+
mimeTypes: [MCP_APP_MIME_TYPE],
|
|
1544
|
+
},
|
|
1545
|
+
},
|
|
1546
|
+
}
|
|
1547
|
+
: {}),
|
|
1548
|
+
},
|
|
1549
|
+
});
|
|
1550
|
+
|
|
1551
|
+
// Resolve orgId once per request (DB lookup) so subsequent wraps are
|
|
1552
|
+
// synchronous. The caller identity may be undefined for true dev-open —
|
|
1553
|
+
// in that case we run with no userEmail/orgId, which makes downstream
|
|
1554
|
+
// tools that require per-user scope return empty results rather than
|
|
1555
|
+
// cross-tenant data (the safe default).
|
|
1556
|
+
const orgIdPromise = resolveMcpIdentityOrgId(effectiveIdentity);
|
|
1557
|
+
|
|
1558
|
+
/**
|
|
1559
|
+
* Wrap a callback in
|
|
1560
|
+
* `runWithRequestContext({ userEmail, orgId, requestOrigin }, fn)`.
|
|
1561
|
+
* Both the tools/list and tools/call handlers go through this so
|
|
1562
|
+
* downstream `accessFilter`, `resolveCredential`, and per-user MCP
|
|
1563
|
+
* visibility checks see the verified caller's identity. `requestOrigin`
|
|
1564
|
+
* is the live server origin derived from the inbound request (same value
|
|
1565
|
+
* used to absolutize deep links) so actions that build fetchable URLs
|
|
1566
|
+
* (e.g. design `export-coding-handoff`'s signed raw-code URL) resolve the
|
|
1567
|
+
* correct local-workspace origin instead of a prod/localhost fallback.
|
|
1568
|
+
*/
|
|
1569
|
+
async function withCallerContext<T>(fn: () => Promise<T>): Promise<T> {
|
|
1570
|
+
const orgId = await orgIdPromise;
|
|
1571
|
+
return runWithRequestContext(
|
|
1572
|
+
{
|
|
1573
|
+
userEmail: effectiveIdentity?.userEmail,
|
|
1574
|
+
orgId,
|
|
1575
|
+
...(requestMeta?.origin ? { requestOrigin: requestMeta.origin } : {}),
|
|
1576
|
+
},
|
|
1577
|
+
fn,
|
|
1578
|
+
) as Promise<T>;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
// tools/list — return all actions + ask-agent meta-tool. Wrapped in the
|
|
1582
|
+
// request context so per-user MCP visibility (mcp-client/visibility.ts)
|
|
1583
|
+
// applies to the listing too.
|
|
1584
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
1585
|
+
return withCallerContext(async () => {
|
|
1586
|
+
const tools = await Promise.all(
|
|
1587
|
+
Object.entries(advertisedActions).map(async ([name, entry]) => {
|
|
1588
|
+
const hasLink = typeof entry.link === "function";
|
|
1589
|
+
const mcpAppResource = await resolveMcpAppResourceSafely(
|
|
1590
|
+
config,
|
|
1591
|
+
name,
|
|
1592
|
+
entry,
|
|
1593
|
+
requestMeta,
|
|
1594
|
+
);
|
|
1595
|
+
const rawToolMeta =
|
|
1596
|
+
(entry.tool as any)._meta &&
|
|
1597
|
+
typeof (entry.tool as any)._meta === "object" &&
|
|
1598
|
+
!Array.isArray((entry.tool as any)._meta)
|
|
1599
|
+
? { ...((entry.tool as any)._meta as Record<string, unknown>) }
|
|
1600
|
+
: {};
|
|
1601
|
+
const toolMeta = {
|
|
1602
|
+
...rawToolMeta,
|
|
1603
|
+
// Advertisement path: only tag the tool with its inline-embed
|
|
1604
|
+
// descriptor when the kill switch is on, so disabled embeds never
|
|
1605
|
+
// prompt a host to render/read the `ui://` resource.
|
|
1606
|
+
...(mcpAppResource && requestMeta?.inlineMcpApps
|
|
1607
|
+
? {
|
|
1608
|
+
...openAiToolDescriptorMeta(mcpAppResource),
|
|
1609
|
+
[MCP_APP_RESOURCE_URI_META_KEY]: mcpAppResource.uri,
|
|
1610
|
+
ui: mcpAppToolUiMeta(
|
|
1611
|
+
mcpAppResource,
|
|
1612
|
+
entry.mcpApp?.visibility ??
|
|
1613
|
+
metadataObject(rawToolMeta.ui).visibility,
|
|
1614
|
+
),
|
|
1615
|
+
}
|
|
1616
|
+
: {}),
|
|
1617
|
+
};
|
|
1618
|
+
const baseDescription = entry.tool.description ?? name;
|
|
1619
|
+
const annotations: Record<string, unknown> = {
|
|
1620
|
+
readOnlyHint: entry.readOnly === true,
|
|
1621
|
+
destructiveHint: entry.publicAgent?.isConsequential === true,
|
|
1622
|
+
openWorldHint: false,
|
|
1623
|
+
};
|
|
1624
|
+
if (hasLink) annotations["agent-native/producesOpenLink"] = true;
|
|
1625
|
+
return {
|
|
1626
|
+
name,
|
|
1627
|
+
description: hasLink
|
|
1628
|
+
? `${baseDescription} After calling, surface the returned "Open in … →" link to the user.`
|
|
1629
|
+
: baseDescription,
|
|
1630
|
+
inputSchema: entry.tool.parameters ?? {
|
|
1631
|
+
type: "object" as const,
|
|
1632
|
+
properties: {},
|
|
1633
|
+
},
|
|
1634
|
+
...(Object.keys(toolMeta).length > 0 ? { _meta: toolMeta } : {}),
|
|
1635
|
+
annotations,
|
|
1636
|
+
};
|
|
1637
|
+
}),
|
|
1638
|
+
);
|
|
1639
|
+
|
|
1640
|
+
if (
|
|
1641
|
+
!compactMcpAppCatalog &&
|
|
1642
|
+
!connectorCatalogActive &&
|
|
1643
|
+
config.askAgent &&
|
|
1644
|
+
hasMcpOAuthScope(effectiveIdentity?.oauthScopes, "mcp:write")
|
|
1645
|
+
) {
|
|
1646
|
+
tools.push({
|
|
1647
|
+
name: "ask-agent",
|
|
1648
|
+
description:
|
|
1649
|
+
"Send a natural-language message to the app's AI agent and get a response. " +
|
|
1650
|
+
"Use this for complex, multi-step tasks that require the agent's reasoning " +
|
|
1651
|
+
"and full context about the app. On hosted MCP, the server waits briefly " +
|
|
1652
|
+
"for fast completions and returns a taskId plus ask_app_status polling " +
|
|
1653
|
+
"instructions when the agent needs longer.",
|
|
1654
|
+
inputSchema: {
|
|
1655
|
+
type: "object" as const,
|
|
1656
|
+
properties: {
|
|
1657
|
+
message: {
|
|
1658
|
+
type: "string",
|
|
1659
|
+
description: "The message to send to the agent",
|
|
1660
|
+
},
|
|
1661
|
+
async: {
|
|
1662
|
+
type: "boolean",
|
|
1663
|
+
description:
|
|
1664
|
+
"Start a durable task and return immediately with a taskId.",
|
|
1665
|
+
},
|
|
1666
|
+
maxWaitMs: {
|
|
1667
|
+
type: "number",
|
|
1668
|
+
description:
|
|
1669
|
+
"Maximum inline wait in milliseconds. Hosted MCP clamps this to 25000ms.",
|
|
1670
|
+
},
|
|
1671
|
+
},
|
|
1672
|
+
required: ["message"],
|
|
1673
|
+
},
|
|
1674
|
+
annotations: {
|
|
1675
|
+
readOnlyHint: false,
|
|
1676
|
+
destructiveHint: false,
|
|
1677
|
+
openWorldHint: false,
|
|
1678
|
+
},
|
|
1679
|
+
});
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
return { tools };
|
|
1683
|
+
});
|
|
1684
|
+
});
|
|
1685
|
+
|
|
1686
|
+
// tools/call — dispatch to action registry or ask-agent. Wrapped in the
|
|
1687
|
+
// request context so the action's `run(args)` and `askAgent()` execute
|
|
1688
|
+
// with the verified caller's identity, not the platform default.
|
|
1689
|
+
server.setRequestHandler(CallToolRequestSchema, async (request: any) => {
|
|
1690
|
+
return withCallerContext(async () => {
|
|
1691
|
+
const { name, arguments: args } = request.params;
|
|
1692
|
+
|
|
1693
|
+
if (name === "ask-agent" && config.askAgent) {
|
|
1694
|
+
if (compactMcpAppCatalog || connectorCatalogActive) {
|
|
1695
|
+
return {
|
|
1696
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
1697
|
+
isError: true,
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1700
|
+
if (!hasMcpOAuthScope(effectiveIdentity?.oauthScopes, "mcp:write")) {
|
|
1701
|
+
return {
|
|
1702
|
+
content: [
|
|
1703
|
+
{
|
|
1704
|
+
type: "text",
|
|
1705
|
+
text: "Forbidden: OAuth scope does not allow ask-agent",
|
|
1706
|
+
},
|
|
1707
|
+
],
|
|
1708
|
+
isError: true,
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
const message = args?.message ?? "";
|
|
1712
|
+
try {
|
|
1713
|
+
// Keep the legacy meta-tool compatible for local callers, but use
|
|
1714
|
+
// the same durable A2A submission path as ask_app whenever this is
|
|
1715
|
+
// an HTTP/hosted request. A full agent loop must never be held open
|
|
1716
|
+
// for minutes behind one MCP tools/call request. Always route
|
|
1717
|
+
// through ask_app's own run() — even with no request origin, since
|
|
1718
|
+
// it now bounds that case too via its process-local inline-task
|
|
1719
|
+
// fallback (ask-app-inline-tasks.ts) instead of us awaiting
|
|
1720
|
+
// config.askAgent() here unbounded. This is the shared helper: no
|
|
1721
|
+
// second task map to keep in sync.
|
|
1722
|
+
const hostedAskApp = getBuiltinCrossAppTools(
|
|
1723
|
+
config,
|
|
1724
|
+
requestMeta,
|
|
1725
|
+
).ask_app;
|
|
1726
|
+
const result = await hostedAskApp.run({
|
|
1727
|
+
message,
|
|
1728
|
+
async: isExplicitAsyncAskAgent(args?.async),
|
|
1729
|
+
maxWaitMs: isExplicitAsyncAskAgent(args?.async)
|
|
1730
|
+
? 0
|
|
1731
|
+
: boundedAskAgentWaitMs(args?.maxWaitMs),
|
|
1732
|
+
});
|
|
1733
|
+
return {
|
|
1734
|
+
content: [{ type: "text", text: formatAskAgentResult(result) }],
|
|
1735
|
+
};
|
|
1736
|
+
} catch (err: any) {
|
|
1737
|
+
return {
|
|
1738
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
1739
|
+
isError: true,
|
|
1740
|
+
};
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
// Connector-catalog tier: when active, callableActions === advertisedActions
|
|
1745
|
+
// (the filtered set). Non-listed tools are not callable — mirroring how
|
|
1746
|
+
// compactMcpAppCatalog gates calls on advertisedActions.
|
|
1747
|
+
const callableActions =
|
|
1748
|
+
compactMcpAppCatalog || connectorCatalogActive
|
|
1749
|
+
? advertisedActions
|
|
1750
|
+
: actions;
|
|
1751
|
+
const entry = callableActions[name];
|
|
1752
|
+
if (!entry) {
|
|
1753
|
+
return {
|
|
1754
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
1755
|
+
isError: true,
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
if (
|
|
1759
|
+
!isActionVisibleForOAuthScope(entry, effectiveIdentity?.oauthScopes)
|
|
1760
|
+
) {
|
|
1761
|
+
return {
|
|
1762
|
+
content: [
|
|
1763
|
+
{
|
|
1764
|
+
type: "text",
|
|
1765
|
+
text: `Forbidden: OAuth scope does not allow tool ${name}`,
|
|
1766
|
+
},
|
|
1767
|
+
],
|
|
1768
|
+
isError: true,
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
try {
|
|
1773
|
+
// We're inside `withCallerContext`, so the request-context getters
|
|
1774
|
+
// resolve the verified MCP caller's identity (do NOT inject a dev
|
|
1775
|
+
// fallback). Tag the call as an external-agent MCP dispatch.
|
|
1776
|
+
const result = await entry.run((args as Record<string, string>) ?? {}, {
|
|
1777
|
+
userEmail: getRequestUserEmail(),
|
|
1778
|
+
orgId: getRequestOrgId() ?? null,
|
|
1779
|
+
caller: "mcp",
|
|
1780
|
+
actionName: name,
|
|
1781
|
+
});
|
|
1782
|
+
const mcpResult = isMcpActionResult(result) ? result : null;
|
|
1783
|
+
const rawResult = mcpResult ? mcpResult.raw : result;
|
|
1784
|
+
const resultForClient = mcpResult ? mcpResult.text : result;
|
|
1785
|
+
const mcpResultIsError =
|
|
1786
|
+
!!mcpResult &&
|
|
1787
|
+
!!mcpResult.raw &&
|
|
1788
|
+
typeof mcpResult.raw === "object" &&
|
|
1789
|
+
(mcpResult.raw as Record<string, unknown>).isError === true;
|
|
1790
|
+
// Render path: only treat the result as an inline embed when the kill
|
|
1791
|
+
// switch is on. When off, `mcpAppResource` is null so every embed
|
|
1792
|
+
// branch below degrades to the plain deep-link artifacts the tool would
|
|
1793
|
+
// otherwise return — no `openai/outputTemplate`, no minted embed-start,
|
|
1794
|
+
// no embed structuredContent — so the host shows a link, not an iframe.
|
|
1795
|
+
const mcpAppResourceCandidate = requestMeta?.inlineMcpApps
|
|
1796
|
+
? await resolveMcpAppResourceSafely(config, name, entry, requestMeta)
|
|
1797
|
+
: null;
|
|
1798
|
+
const rawResultForClient = mcpAppResourceCandidate
|
|
1799
|
+
? await withServerMintedMcpAppEmbedStart(rawResult, requestMeta)
|
|
1800
|
+
: rawResult;
|
|
1801
|
+
// Only attach the embed widget for a non-error result that has content.
|
|
1802
|
+
const embedHasContent = mcpResultHasContent(rawResultForClient);
|
|
1803
|
+
const mcpAppResource =
|
|
1804
|
+
mcpAppResourceCandidate && !mcpResultIsError && embedHasContent
|
|
1805
|
+
? mcpAppResourceCandidate
|
|
1806
|
+
: null;
|
|
1807
|
+
// `openai/outputTemplate` is declared at the tool level, so the host
|
|
1808
|
+
// renders a widget for every call regardless of result _meta. The only
|
|
1809
|
+
// per-result signal it honors is `isError` (shows error text, no widget),
|
|
1810
|
+
// so treat an embed tool that produced nothing as an error.
|
|
1811
|
+
const embedProducedNothing =
|
|
1812
|
+
!!mcpAppResourceCandidate && !mcpResultIsError && !embedHasContent;
|
|
1813
|
+
const { block, _meta } = buildLinkArtifacts(
|
|
1814
|
+
entry,
|
|
1815
|
+
(args as Record<string, any>) ?? {},
|
|
1816
|
+
rawResultForClient,
|
|
1817
|
+
requestMeta,
|
|
1818
|
+
);
|
|
1819
|
+
const responseMeta: Record<string, unknown> = {
|
|
1820
|
+
...(_meta ?? {}),
|
|
1821
|
+
...(mcpAppResource
|
|
1822
|
+
? mcpAppEmbedOpenLinkMeta(
|
|
1823
|
+
rawResultForClient,
|
|
1824
|
+
mcpAppResource,
|
|
1825
|
+
requestMeta,
|
|
1826
|
+
)
|
|
1827
|
+
: {}),
|
|
1828
|
+
...(mcpAppResource ? openAiToolResultMeta(mcpAppResource) : {}),
|
|
1829
|
+
};
|
|
1830
|
+
const toolUiMeta = metadataObject((entry.tool as any)._meta?.ui);
|
|
1831
|
+
const toolVisibility = toolUiMeta.visibility;
|
|
1832
|
+
const isAppOnlyVisibility =
|
|
1833
|
+
Array.isArray(toolVisibility) &&
|
|
1834
|
+
toolVisibility.length > 0 &&
|
|
1835
|
+
toolVisibility.every((v) => v === "app");
|
|
1836
|
+
const readOnlyStructuredResult =
|
|
1837
|
+
entry.readOnly === true &&
|
|
1838
|
+
rawResultForClient &&
|
|
1839
|
+
typeof rawResultForClient === "object"
|
|
1840
|
+
? Array.isArray(rawResultForClient)
|
|
1841
|
+
? { items: rawResultForClient }
|
|
1842
|
+
: rawResultForClient
|
|
1843
|
+
: undefined;
|
|
1844
|
+
const structuredContent = mcpAppResource
|
|
1845
|
+
? mcpAppStructuredContent(rawResultForClient, responseMeta)
|
|
1846
|
+
: isAppOnlyVisibility &&
|
|
1847
|
+
rawResult &&
|
|
1848
|
+
typeof rawResult === "object" &&
|
|
1849
|
+
!Array.isArray(rawResult)
|
|
1850
|
+
? (rawResult as Record<string, unknown>)
|
|
1851
|
+
: readOnlyStructuredResult
|
|
1852
|
+
? mcpAppStructuredContent(readOnlyStructuredResult, responseMeta)
|
|
1853
|
+
: undefined;
|
|
1854
|
+
const text = mcpAppResource
|
|
1855
|
+
? conciseMcpAppToolText(name, resultForClient, structuredContent!)
|
|
1856
|
+
: conciseToolResultText(name, resultForClient, {
|
|
1857
|
+
preserveObjectResult: entry.readOnly === true,
|
|
1858
|
+
});
|
|
1859
|
+
const content: any[] = [{ type: "text", text }];
|
|
1860
|
+
if (block) content.push(block);
|
|
1861
|
+
return {
|
|
1862
|
+
content,
|
|
1863
|
+
...(mcpResultIsError || embedProducedNothing
|
|
1864
|
+
? { isError: true }
|
|
1865
|
+
: {}),
|
|
1866
|
+
...(structuredContent ? { structuredContent } : {}),
|
|
1867
|
+
...(Object.keys(responseMeta).length > 0
|
|
1868
|
+
? { _meta: responseMeta }
|
|
1869
|
+
: {}),
|
|
1870
|
+
};
|
|
1871
|
+
} catch (err: any) {
|
|
1872
|
+
return {
|
|
1873
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
1874
|
+
isError: true,
|
|
1875
|
+
};
|
|
1876
|
+
}
|
|
1877
|
+
});
|
|
1878
|
+
});
|
|
1879
|
+
|
|
1880
|
+
if (supportsMcpApps) {
|
|
1881
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
1882
|
+
return withCallerContext(async () => {
|
|
1883
|
+
const mcpAppResources = await getMcpAppResources(
|
|
1884
|
+
config,
|
|
1885
|
+
advertisedActions,
|
|
1886
|
+
requestMeta,
|
|
1887
|
+
);
|
|
1888
|
+
return {
|
|
1889
|
+
resources: mcpAppResources.map((resource) => ({
|
|
1890
|
+
uri: resource.uri,
|
|
1891
|
+
name: resource.name,
|
|
1892
|
+
...(resource.title ? { title: resource.title } : {}),
|
|
1893
|
+
...(resource.description
|
|
1894
|
+
? { description: resource.description }
|
|
1895
|
+
: {}),
|
|
1896
|
+
mimeType: resource.mimeType,
|
|
1897
|
+
...(resource._meta ? { _meta: resource._meta } : {}),
|
|
1898
|
+
})),
|
|
1899
|
+
};
|
|
1900
|
+
});
|
|
1901
|
+
});
|
|
1902
|
+
|
|
1903
|
+
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
|
|
1904
|
+
return withCallerContext(async () => {
|
|
1905
|
+
const mcpAppResources = await getMcpAppResources(
|
|
1906
|
+
config,
|
|
1907
|
+
advertisedActions,
|
|
1908
|
+
requestMeta,
|
|
1909
|
+
);
|
|
1910
|
+
return {
|
|
1911
|
+
resourceTemplates: mcpAppResources.map((resource) => ({
|
|
1912
|
+
uriTemplate: resource.uri,
|
|
1913
|
+
name: resource.name,
|
|
1914
|
+
...(resource.title ? { title: resource.title } : {}),
|
|
1915
|
+
...(resource.description
|
|
1916
|
+
? { description: resource.description }
|
|
1917
|
+
: {}),
|
|
1918
|
+
mimeType: resource.mimeType,
|
|
1919
|
+
...(resource._meta ? { _meta: resource._meta } : {}),
|
|
1920
|
+
})),
|
|
1921
|
+
};
|
|
1922
|
+
});
|
|
1923
|
+
});
|
|
1924
|
+
|
|
1925
|
+
server.setRequestHandler(
|
|
1926
|
+
ReadResourceRequestSchema,
|
|
1927
|
+
async (request: any) => {
|
|
1928
|
+
return withCallerContext(async () => {
|
|
1929
|
+
const uri = request.params?.uri;
|
|
1930
|
+
let found: {
|
|
1931
|
+
actionName: string;
|
|
1932
|
+
resource: ResolvedMcpAppResource;
|
|
1933
|
+
} | null = null;
|
|
1934
|
+
for (const [name, entry] of Object.entries(advertisedActions)) {
|
|
1935
|
+
const resourceUri = getMcpAppResourceUri(config, name, entry);
|
|
1936
|
+
if (!resourceUri || !matchesMcpAppResourceUri(resourceUri, uri)) {
|
|
1937
|
+
continue;
|
|
1938
|
+
}
|
|
1939
|
+
const resource = await resolveMcpAppResourceSafely(
|
|
1940
|
+
config,
|
|
1941
|
+
name,
|
|
1942
|
+
entry,
|
|
1943
|
+
requestMeta,
|
|
1944
|
+
);
|
|
1945
|
+
if (resource) {
|
|
1946
|
+
found = { actionName: name, resource };
|
|
1947
|
+
break;
|
|
1948
|
+
}
|
|
1949
|
+
// resolveMcpAppResourceSafely returned null (e.g. an async resolver
|
|
1950
|
+
// threw) — keep scanning the remaining candidates rather than
|
|
1951
|
+
// aborting and reporting the resource as missing.
|
|
1952
|
+
}
|
|
1953
|
+
if (!found) {
|
|
1954
|
+
throw new Error(`MCP App resource not found: ${uri}`);
|
|
1955
|
+
}
|
|
1956
|
+
return {
|
|
1957
|
+
contents: [
|
|
1958
|
+
{
|
|
1959
|
+
uri,
|
|
1960
|
+
mimeType: found.resource.mimeType,
|
|
1961
|
+
text: renderMcpAppHtml(
|
|
1962
|
+
found.resource,
|
|
1963
|
+
found.actionName,
|
|
1964
|
+
config,
|
|
1965
|
+
requestMeta,
|
|
1966
|
+
),
|
|
1967
|
+
...(found.resource._meta
|
|
1968
|
+
? { _meta: found.resource._meta }
|
|
1969
|
+
: {}),
|
|
1970
|
+
},
|
|
1971
|
+
],
|
|
1972
|
+
};
|
|
1973
|
+
});
|
|
1974
|
+
},
|
|
1975
|
+
);
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
return server;
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
// ---------------------------------------------------------------------------
|
|
1982
|
+
// Auth — reuses the same pattern as A2A (Bearer token or JWT). Shared so the
|
|
1983
|
+
// HTTP mount and any stdio-side auth-aware helper resolve identity identically.
|
|
1984
|
+
// ---------------------------------------------------------------------------
|
|
1985
|
+
|
|
1986
|
+
export function getAccessTokens(): string[] {
|
|
1987
|
+
const single = process.env.ACCESS_TOKEN;
|
|
1988
|
+
const multi = process.env.ACCESS_TOKENS;
|
|
1989
|
+
const tokens: string[] = [];
|
|
1990
|
+
if (single) tokens.push(single);
|
|
1991
|
+
if (multi) {
|
|
1992
|
+
tokens.push(
|
|
1993
|
+
...multi
|
|
1994
|
+
.split(",")
|
|
1995
|
+
.map((t) => t.trim())
|
|
1996
|
+
.filter(Boolean),
|
|
1997
|
+
);
|
|
1998
|
+
}
|
|
1999
|
+
return tokens;
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
/**
|
|
2003
|
+
* Resolve the caller identity for a static-token (or dev-open) auth path.
|
|
2004
|
+
*
|
|
2005
|
+
* Static `ACCESS_TOKEN` / `ACCESS_TOKENS` auth carries no per-caller claims,
|
|
2006
|
+
* so without this the MCP endpoint would run every tool with
|
|
2007
|
+
* `userEmail === undefined` and per-user / per-org scoped actions
|
|
2008
|
+
* (`accessFilter`, `resolveAccess`, `resolveCredential`) would return
|
|
2009
|
+
* empty / wrong data. The `agent-native mcp install` flow writes
|
|
2010
|
+
* `AGENT_NATIVE_OWNER_EMAIL` into the client config env and the stdio proxy
|
|
2011
|
+
* forwards it as the `X-Agent-Native-Owner-Email` request header (see
|
|
2012
|
+
* `mcp/stdio.ts#authHeaders`). We trust that owner hint *only* on the
|
|
2013
|
+
* static-token path — JWT auth already carries a cryptographically verified
|
|
2014
|
+
* `sub`, so the header is ignored there and never widens JWT scope.
|
|
2015
|
+
*
|
|
2016
|
+
* Precedence is server-trusted-first: the server process's
|
|
2017
|
+
* `AGENT_NATIVE_OWNER_EMAIL` env (set out-of-band by the operator / deploy)
|
|
2018
|
+
* ALWAYS wins, and a client-supplied `X-Agent-Native-Owner-Email` header is
|
|
2019
|
+
* honored *only as a fallback when that env is unset*. A static `ACCESS_TOKEN`
|
|
2020
|
+
* is a shared bearer secret; letting a request header override a
|
|
2021
|
+
* server-configured owner would let anyone holding a leaked token act as any
|
|
2022
|
+
* user. The header path remains for the single-tenant local-dev install flow
|
|
2023
|
+
* where the app server process has no owner env and the token *is* the
|
|
2024
|
+
* workspace secret; multi-tenant deployments must use A2A JWT (verified `sub`),
|
|
2025
|
+
* not a static token, for per-user scope.
|
|
2026
|
+
*
|
|
2027
|
+
* Returns `undefined` when no owner email is available (true dev-open: no
|
|
2028
|
+
* token, no secret, no owner) so behavior there stays unchanged.
|
|
2029
|
+
*/
|
|
2030
|
+
function deriveStaticTokenIdentity(
|
|
2031
|
+
ownerEmailHeader: string | undefined,
|
|
2032
|
+
): MCPCallerIdentity | undefined {
|
|
2033
|
+
const owner =
|
|
2034
|
+
process.env.AGENT_NATIVE_OWNER_EMAIL?.trim() ||
|
|
2035
|
+
(typeof ownerEmailHeader === "string" && ownerEmailHeader.trim()) ||
|
|
2036
|
+
"";
|
|
2037
|
+
if (!owner) return undefined;
|
|
2038
|
+
return { userEmail: owner, orgDomain: undefined };
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
export function getBearerToken(
|
|
2042
|
+
authHeader: string | undefined,
|
|
2043
|
+
): string | undefined {
|
|
2044
|
+
if (!authHeader) return undefined;
|
|
2045
|
+
const match = /^Bearer\s+(.+)$/i.exec(authHeader.trim());
|
|
2046
|
+
return match?.[1]?.trim() || undefined;
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
function addSecretCandidate(
|
|
2050
|
+
candidates: string[],
|
|
2051
|
+
secret: string | null | undefined,
|
|
2052
|
+
): void {
|
|
2053
|
+
const trimmed = secret?.trim();
|
|
2054
|
+
if (!trimmed || candidates.includes(trimmed)) return;
|
|
2055
|
+
candidates.push(trimmed);
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
async function verifyA2AJwtForMcp(
|
|
2059
|
+
token: string,
|
|
2060
|
+
resourceUrl?: string | string[],
|
|
2061
|
+
): Promise<Record<string, unknown> | null> {
|
|
2062
|
+
const jose = await import("jose");
|
|
2063
|
+
let unverifiedPayload: Record<string, unknown> | null = null;
|
|
2064
|
+
try {
|
|
2065
|
+
unverifiedPayload = jose.decodeJwt(token) as Record<string, unknown>;
|
|
2066
|
+
} catch {
|
|
2067
|
+
return null;
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
const candidateSecrets: string[] = [];
|
|
2071
|
+
addSecretCandidate(candidateSecrets, process.env.A2A_SECRET);
|
|
2072
|
+
|
|
2073
|
+
const orgDomain =
|
|
2074
|
+
typeof unverifiedPayload.org_domain === "string"
|
|
2075
|
+
? unverifiedPayload.org_domain
|
|
2076
|
+
: undefined;
|
|
2077
|
+
if (orgDomain) {
|
|
2078
|
+
try {
|
|
2079
|
+
const { getA2ASecretByDomain } = await import("../org/context.js");
|
|
2080
|
+
addSecretCandidate(
|
|
2081
|
+
candidateSecrets,
|
|
2082
|
+
await getA2ASecretByDomain(orgDomain),
|
|
2083
|
+
);
|
|
2084
|
+
} catch {
|
|
2085
|
+
// DB not ready or org lookup unavailable — fall back to other candidates.
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
const firstPartyMcp = unverifiedPayload.agent_native_first_party_mcp === true;
|
|
2090
|
+
const audiences = firstPartyMcp ? mcpAudienceList(resourceUrl) : null;
|
|
2091
|
+
if (firstPartyMcp && !audiences?.length) return null;
|
|
2092
|
+
|
|
2093
|
+
for (const secret of candidateSecrets) {
|
|
2094
|
+
const encodedSecret = new TextEncoder().encode(secret);
|
|
2095
|
+
for (const audience of audiences ?? [undefined]) {
|
|
2096
|
+
try {
|
|
2097
|
+
const { payload } = await jose.jwtVerify(
|
|
2098
|
+
token,
|
|
2099
|
+
encodedSecret,
|
|
2100
|
+
audience ? { audience } : undefined,
|
|
2101
|
+
);
|
|
2102
|
+
return payload as Record<string, unknown>;
|
|
2103
|
+
} catch {
|
|
2104
|
+
// Try the next candidate without exposing which secret matched.
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
return null;
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
function mcpAudienceList(resource: string | string[] | undefined): string[] {
|
|
2113
|
+
const raw = Array.isArray(resource) ? resource : resource ? [resource] : [];
|
|
2114
|
+
const seen = new Set<string>();
|
|
2115
|
+
const out: string[] = [];
|
|
2116
|
+
for (const value of raw) {
|
|
2117
|
+
const normalized = value.replace(/\/+$/, "");
|
|
2118
|
+
if (normalized && !seen.has(normalized)) {
|
|
2119
|
+
seen.add(normalized);
|
|
2120
|
+
out.push(normalized);
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
return out;
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
async function isConnectTokenAllowed(
|
|
2127
|
+
jti: string | undefined,
|
|
2128
|
+
): Promise<boolean> {
|
|
2129
|
+
if (!jti) return false;
|
|
2130
|
+
try {
|
|
2131
|
+
const { isJtiRevoked, touchTokenUsed } = await import("./connect-store.js");
|
|
2132
|
+
if (await isJtiRevoked(jti)) return false;
|
|
2133
|
+
// Best-effort usage telemetry — never blocks / throws.
|
|
2134
|
+
void touchTokenUsed(jti);
|
|
2135
|
+
} catch {
|
|
2136
|
+
// Store import / lookup failed — fail open. Signature verification already
|
|
2137
|
+
// passed; this only gates explicit revokes.
|
|
2138
|
+
}
|
|
2139
|
+
return true;
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
/**
|
|
2143
|
+
* Verify the inbound auth header. Returns:
|
|
2144
|
+
* - { authed: true, identity } when verified — `identity` is derived from
|
|
2145
|
+
* the JWT (`sub` / `org_domain`) for JWT auth, or from the
|
|
2146
|
+
* `AGENT_NATIVE_OWNER_EMAIL` env / `X-Agent-Native-Owner-Email` header
|
|
2147
|
+
* for static-token auth (the `agent-native mcp install` flow). `identity`
|
|
2148
|
+
* is undefined only for true dev-open with no owner hint.
|
|
2149
|
+
* - { authed: false } on rejection.
|
|
2150
|
+
*
|
|
2151
|
+
* When A2A_SECRET is set we extract the JWT's `sub` (caller email) and
|
|
2152
|
+
* `org_domain` claims so the MCP endpoint can wrap tool runs in
|
|
2153
|
+
* `runWithRequestContext({ userEmail, orgId })`. Without that wrap, the
|
|
2154
|
+
* MCP endpoint loses tenant identity and downstream `accessFilter` /
|
|
2155
|
+
* `resolveCredential` calls fall back to platform-wide defaults.
|
|
2156
|
+
*
|
|
2157
|
+
* `ownerEmailHeader` is the forwarded `X-Agent-Native-Owner-Email` value; it
|
|
2158
|
+
* is consulted ONLY on the static-token / dev-open path (never to influence
|
|
2159
|
+
* verified JWT identity), so the install flow runs tools as the configured
|
|
2160
|
+
* owner instead of an unscoped anonymous caller.
|
|
2161
|
+
*/
|
|
2162
|
+
export async function verifyAuth(
|
|
2163
|
+
authHeader: string | undefined,
|
|
2164
|
+
ownerEmailHeader?: string,
|
|
2165
|
+
options: { allowDevOpen?: boolean; resourceUrl?: string | string[] } = {},
|
|
2166
|
+
): Promise<{
|
|
2167
|
+
authed: boolean;
|
|
2168
|
+
identity?: MCPCallerIdentity;
|
|
2169
|
+
/**
|
|
2170
|
+
* The caller presented a real credential — a verified A2A/connect JWT, a
|
|
2171
|
+
* matching ACCESS_TOKEN, or (on the no-auth-configured path) a forwarded
|
|
2172
|
+
* owner-email header from `agent-native mcp install`. Drives the full vs
|
|
2173
|
+
* sparse MCP tool surface in local dev. The pure unauthenticated dev-open
|
|
2174
|
+
* path (no secret, no token, no owner header) is `false`.
|
|
2175
|
+
*/
|
|
2176
|
+
fullSurface?: boolean;
|
|
2177
|
+
/**
|
|
2178
|
+
* The caller explicitly opted up to the full connector catalog by minting
|
|
2179
|
+
* their token with `--full-catalog` (or equivalent). When `true`, the
|
|
2180
|
+
* compact/connector-catalog tier filter (active by default whenever a
|
|
2181
|
+
* `connectorCatalog` is declared) is bypassed for this caller. Derived from a
|
|
2182
|
+
* `catalog_scope: "full"` claim in the verified A2A/connect JWT.
|
|
2183
|
+
*/
|
|
2184
|
+
fullCatalog?: boolean;
|
|
2185
|
+
}> {
|
|
2186
|
+
// No auth configured → allow only when the route caller has already
|
|
2187
|
+
// established that this is a loopback/local dev request. Still honour an
|
|
2188
|
+
// owner hint there so the local install/connect flow stays tenant-scoped.
|
|
2189
|
+
const accessTokens = getAccessTokens();
|
|
2190
|
+
const hasA2ASecret = !!process.env.A2A_SECRET?.trim();
|
|
2191
|
+
const token = getBearerToken(authHeader);
|
|
2192
|
+
if (token) {
|
|
2193
|
+
const oauthIdentity = await verifyMcpOAuthAccessToken(
|
|
2194
|
+
token,
|
|
2195
|
+
options.resourceUrl,
|
|
2196
|
+
);
|
|
2197
|
+
if (oauthIdentity) {
|
|
2198
|
+
if (
|
|
2199
|
+
oauthIdentity.clientId === MCP_CONNECT_OAUTH_CLIENT_ID &&
|
|
2200
|
+
!(await isConnectTokenAllowed(oauthIdentity.jti))
|
|
2201
|
+
) {
|
|
2202
|
+
return { authed: false };
|
|
2203
|
+
}
|
|
2204
|
+
return {
|
|
2205
|
+
authed: true,
|
|
2206
|
+
identity: {
|
|
2207
|
+
userEmail: oauthIdentity.userEmail,
|
|
2208
|
+
...(oauthIdentity.orgId ? { orgId: oauthIdentity.orgId } : {}),
|
|
2209
|
+
orgDomain: oauthIdentity.orgDomain,
|
|
2210
|
+
oauthScopes: oauthIdentity.scopes,
|
|
2211
|
+
oauthClientId: oauthIdentity.clientId,
|
|
2212
|
+
},
|
|
2213
|
+
fullSurface: true,
|
|
2214
|
+
// Per-token opt-up: `catalog_scope: "full"` in the OAuth token
|
|
2215
|
+
// bypasses the connector-catalog tier filter on hosted deployments.
|
|
2216
|
+
fullCatalog: oauthIdentity.catalogScope === "full",
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
if (accessTokens.length === 0 && !hasA2ASecret && !token) {
|
|
2221
|
+
if (options.allowDevOpen === false) {
|
|
2222
|
+
return { authed: false };
|
|
2223
|
+
}
|
|
2224
|
+
return {
|
|
2225
|
+
authed: true,
|
|
2226
|
+
identity: deriveStaticTokenIdentity(ownerEmailHeader),
|
|
2227
|
+
// `mcp install`'s stdio proxy forwards an owner-email header even when
|
|
2228
|
+
// the local app has no secret configured — that is a real, identified
|
|
2229
|
+
// caller and gets the full surface. A bare browser/curl dev probe with
|
|
2230
|
+
// no owner hint stays on the sparse dev surface.
|
|
2231
|
+
fullSurface: !!(ownerEmailHeader && ownerEmailHeader.trim()),
|
|
2232
|
+
};
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
if (!token) return { authed: false };
|
|
2236
|
+
|
|
2237
|
+
// Try an A2A JWT via the shared A2A_SECRET first, then the caller org's
|
|
2238
|
+
// synced A2A secret when the token carries org_domain.
|
|
2239
|
+
const payload = await verifyA2AJwtForMcp(token, options.resourceUrl);
|
|
2240
|
+
if (payload) {
|
|
2241
|
+
const tokenScope =
|
|
2242
|
+
typeof payload.scope === "string" ? payload.scope : undefined;
|
|
2243
|
+
if (tokenScope && tokenScope !== MCP_CONNECT_SCOPE) {
|
|
2244
|
+
return { authed: false };
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
// Connect-minted tokens (scope === "mcp-connect") carry a random `jti`
|
|
2248
|
+
// and are individually revocable. Only these tokens hit the revoke
|
|
2249
|
+
// store — ordinary A2A delegation JWTs skip the DB lookup entirely so
|
|
2250
|
+
// the hot path is unchanged. The signature was already
|
|
2251
|
+
// cryptographically verified, so failing open here only widens the
|
|
2252
|
+
// explicit-revoke gate, never the trust boundary.
|
|
2253
|
+
if (tokenScope === MCP_CONNECT_SCOPE) {
|
|
2254
|
+
if (!(await isConnectTokenAllowed(payload.jti as string | undefined))) {
|
|
2255
|
+
return { authed: false };
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
return {
|
|
2260
|
+
authed: true,
|
|
2261
|
+
identity: {
|
|
2262
|
+
userEmail: typeof payload.sub === "string" ? payload.sub : undefined,
|
|
2263
|
+
// Org SERVICE tokens (connect-minted, synthetic `svc-*@service.<org>`
|
|
2264
|
+
// subject) carry the org id directly as an `org_id` claim so the
|
|
2265
|
+
// resolved identity is org-scoped even when the org has no domain
|
|
2266
|
+
// mapping. Personal/delegation JWTs don't set the claim — unchanged.
|
|
2267
|
+
...(typeof payload.org_id === "string" && payload.org_id
|
|
2268
|
+
? { orgId: payload.org_id as string }
|
|
2269
|
+
: {}),
|
|
2270
|
+
orgDomain:
|
|
2271
|
+
typeof payload.org_domain === "string"
|
|
2272
|
+
? (payload.org_domain as string)
|
|
2273
|
+
: undefined,
|
|
2274
|
+
...(payload.agent_native_first_party_mcp === true
|
|
2275
|
+
? { firstPartyMcp: true }
|
|
2276
|
+
: {}),
|
|
2277
|
+
},
|
|
2278
|
+
// Verified JWT (connect-minted or A2A delegation) — a real caller.
|
|
2279
|
+
fullSurface: true,
|
|
2280
|
+
// Per-token opt-up: `catalog_scope: "full"` embedded at mint time via
|
|
2281
|
+
// `agent-native connect --full-catalog` bypasses the connector-catalog
|
|
2282
|
+
// tier filter on hosted multi-tenant deployments.
|
|
2283
|
+
fullCatalog: payload.catalog_scope === "full",
|
|
2284
|
+
};
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
if (accessTokens.length === 0 && !hasA2ASecret) {
|
|
2288
|
+
if (options.allowDevOpen === false) {
|
|
2289
|
+
return { authed: false };
|
|
2290
|
+
}
|
|
2291
|
+
return {
|
|
2292
|
+
authed: true,
|
|
2293
|
+
identity: deriveStaticTokenIdentity(ownerEmailHeader),
|
|
2294
|
+
fullSurface: !!(ownerEmailHeader && ownerEmailHeader.trim()),
|
|
2295
|
+
};
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
// Try ACCESS_TOKEN / ACCESS_TOKENS exact match. Static tokens carry no
|
|
2299
|
+
// per-caller claims, so derive identity from the forwarded owner-email
|
|
2300
|
+
// hint (install flow) — otherwise tools would run unscoped. Compare in
|
|
2301
|
+
// constant time (matching the rest of this subsystem's secret-comparison
|
|
2302
|
+
// discipline); node:crypto is imported dynamically because this module is
|
|
2303
|
+
// bundled into the serverless function and avoids static Node-only imports.
|
|
2304
|
+
if (accessTokens.length > 0) {
|
|
2305
|
+
const { timingSafeEqual } = await import("node:crypto");
|
|
2306
|
+
const candidate = Buffer.from(token, "utf8");
|
|
2307
|
+
const matched = accessTokens.some((configured) => {
|
|
2308
|
+
const expected = Buffer.from(configured, "utf8");
|
|
2309
|
+
return (
|
|
2310
|
+
expected.length === candidate.length &&
|
|
2311
|
+
timingSafeEqual(expected, candidate)
|
|
2312
|
+
);
|
|
2313
|
+
});
|
|
2314
|
+
if (matched) {
|
|
2315
|
+
return {
|
|
2316
|
+
authed: true,
|
|
2317
|
+
identity: deriveStaticTokenIdentity(ownerEmailHeader),
|
|
2318
|
+
// Matched a configured ACCESS_TOKEN — a real caller.
|
|
2319
|
+
fullSurface: true,
|
|
2320
|
+
};
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
return { authed: false };
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
export async function resolveOrgIdFromDomain(
|
|
2328
|
+
orgDomain: string | undefined,
|
|
2329
|
+
): Promise<string | undefined> {
|
|
2330
|
+
if (!orgDomain) return undefined;
|
|
2331
|
+
try {
|
|
2332
|
+
const { resolveOrgByDomain } = await import("../org/context.js");
|
|
2333
|
+
const org = await resolveOrgByDomain(orgDomain);
|
|
2334
|
+
return org?.orgId ?? undefined;
|
|
2335
|
+
} catch {
|
|
2336
|
+
return undefined;
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
export async function resolveMcpIdentityOrgId(
|
|
2341
|
+
identity: MCPCallerIdentity | undefined,
|
|
2342
|
+
): Promise<string | undefined> {
|
|
2343
|
+
if (identity?.orgId) return identity.orgId;
|
|
2344
|
+
|
|
2345
|
+
const orgIdFromDomain = await resolveOrgIdFromDomain(identity?.orgDomain);
|
|
2346
|
+
if (orgIdFromDomain) return orgIdFromDomain;
|
|
2347
|
+
|
|
2348
|
+
const userEmail = identity?.userEmail?.trim();
|
|
2349
|
+
if (!userEmail) return undefined;
|
|
2350
|
+
try {
|
|
2351
|
+
const { resolveOrgIdForEmail } = await import("../org/context.js");
|
|
2352
|
+
return (await resolveOrgIdForEmail(userEmail)) ?? undefined;
|
|
2353
|
+
} catch {
|
|
2354
|
+
return undefined;
|
|
2355
|
+
}
|
|
2356
|
+
}
|