@agent-native/core 0.128.4 → 0.129.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +90 -0
- package/corpus/core/docs/content/template-design-collaboration-and-full-apps.mdx +3 -1
- package/corpus/core/package.json +6 -3
- package/corpus/core/src/a2a/client.ts +18 -4
- package/corpus/core/src/a2a/server.ts +19 -3
- package/corpus/core/src/a2a/types.ts +7 -0
- package/corpus/core/src/action.ts +43 -16
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +14 -10
- package/corpus/core/src/agent/production-agent.ts +41 -3
- package/corpus/core/src/agent/thread-data-builder.ts +5 -0
- package/corpus/core/src/chat-threads/migrations.ts +53 -0
- package/corpus/core/src/cli/create.ts +4 -0
- package/corpus/core/src/cli/skills-content/design-visual-edit-skill.ts +36 -16
- package/corpus/core/src/cli/skills.ts +16 -0
- package/corpus/core/src/client/AgentPanel.tsx +9 -5
- package/corpus/core/src/client/AssistantChat.tsx +138 -46
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +16 -10
- package/corpus/core/src/client/agent-chat-adapter.ts +21 -3
- package/corpus/core/src/client/analytics.ts +159 -102
- package/corpus/core/src/client/chat/message-components.tsx +12 -5
- package/corpus/core/src/client/chat/runtime.ts +2 -1
- package/corpus/core/src/client/chat/tool-call-display.tsx +25 -2
- package/corpus/core/src/client/chat-model-groups.ts +3 -0
- package/corpus/core/src/client/client-status-requests.ts +161 -0
- package/corpus/core/src/client/org/OrgSwitcher.tsx +4 -4
- package/corpus/core/src/client/settings/VoiceTranscriptionSection.tsx +2 -2
- package/corpus/core/src/client/sse-event-processor.ts +15 -3
- package/corpus/core/src/client/use-agent-engine-configured.ts +54 -50
- package/corpus/core/src/client/use-chat-models.ts +16 -11
- package/corpus/core/src/client/use-chat-threads.ts +32 -9
- package/corpus/core/src/client/use-session.ts +4 -5
- package/corpus/core/src/db/client.ts +83 -8
- package/corpus/core/src/db/ensure-additive-columns.ts +31 -20
- package/corpus/core/src/db/migrations.ts +22 -2
- package/corpus/core/src/mcp/approval-store.ts +99 -0
- package/corpus/core/src/mcp/build-server.ts +579 -303
- package/corpus/core/src/mcp/embed-app.ts +1 -1
- package/corpus/core/src/mcp/oauth-client-metadata.ts +380 -0
- package/corpus/core/src/mcp/oauth-route.ts +57 -52
- package/corpus/core/src/mcp/screen-memory-stdio.ts +400 -378
- package/corpus/core/src/mcp/server.ts +29 -145
- package/corpus/core/src/mcp/stdio.ts +72 -80
- package/corpus/core/src/mcp-client/manager.ts +6 -10
- package/corpus/core/src/mcp-client/oauth-client.ts +127 -36
- package/corpus/core/src/mcp-client/oauth-routes.ts +16 -6
- package/corpus/core/src/mcp-client/routes.ts +6 -5
- package/corpus/core/src/server/agent-capabilities.ts +20 -0
- package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +27 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +18 -0
- package/corpus/core/src/server/transcribe-voice.ts +14 -5
- package/corpus/core/src/templates/default/.agents/skills/security/SKILL.md +13 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/analytics/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +1 -10
- package/corpus/templates/analytics/app/global.css +0 -31
- package/corpus/templates/analytics/app/lib/sql-query.ts +46 -20
- package/corpus/templates/analytics/changelog/2026-07-28-analytics-dashboard-loading-states-now-use-a-simpler-standar.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-28-analytics-dashboards-now-load-first-party-charts-reliably-in.md +6 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-cache.ts +42 -16
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +4 -2
- package/corpus/templates/analytics/shared/sql-query-limits.ts +1 -0
- package/corpus/templates/assets/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/brain/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/calendar/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/chat/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/chat/README.md +0 -2
- package/corpus/templates/clips/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/clips/app/components/player/player-controls.tsx +3 -6
- package/corpus/templates/clips/app/components/player/recording-views-badge.tsx +6 -2
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +25 -10
- package/corpus/templates/clips/app/components/sharing/viewed-by-popover.tsx +28 -6
- package/corpus/templates/clips/app/lib/use-visible-avatar-url.ts +34 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +6 -2
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +3 -1
- package/corpus/templates/clips/changelog/2026-07-28-copied-share-links-no-longer-leave-an-unnecessary-confirmati.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-28-playback-skip-controls-no-longer-show-overlapping-numbers.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-28-profile-avatars-now-appear-in-viewer-and-share-access-lists.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-28-public-clips-share-buttons-now-use-a-clean-text-only-label.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +14 -17
- package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +3 -1
- package/corpus/templates/clips/desktop/src/styles.css +13 -10
- package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +2 -2
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +36 -0
- package/corpus/templates/content/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +155 -34
- package/corpus/templates/content/actions/_builder-docs-client.ts +151 -30
- package/corpus/templates/content/actions/stage-builder-source-bulk-update.ts +5 -2
- package/corpus/templates/content/app/root.tsx +35 -14
- package/corpus/templates/content/changelog/2026-07-28-builder-backed-files-media-fields-now-accept-direct-file-url.md +6 -0
- package/corpus/templates/content/changelog/2026-07-28-builder-content-reads-now-negotiate-the-newest-mcp-protocol-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-28-public-content-pages-load-faster-before-the-optional-ask-sid.md +6 -0
- package/corpus/templates/design/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +36 -16
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +42 -3
- package/corpus/templates/design/actions/open-visual-edit.ts +2 -1
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +2 -2
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +14 -15
- package/corpus/templates/design/app/components/design/ResponsiveInteractBar.tsx +314 -0
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +88 -3
- package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +1 -1
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +7 -6
- package/corpus/templates/design/app/i18n-data.ts +56 -11
- package/corpus/templates/design/app/i18n-responsive-interact.ts +183 -0
- package/corpus/templates/design/app/lib/agent-chat.ts +60 -0
- package/corpus/templates/design/app/pages/design-editor/responsive-interact.ts +89 -0
- package/corpus/templates/design/changelog/2026-07-28-design-edits-can-be-applied-through-the-host-coding-agent-fr.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-interact-mode-can-preview-designs-in-responsive-device-sizes.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-interact-mode-now-opens-a-full-screen-responsive-preview-wit.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-show-or-hide-design-editing-chrome-with-cmd-on-apple-devices.md +2 -2
- package/corpus/templates/design/changelog/2026-07-28-visual-edit-now-always-shows-the-live-running-app-in-the-can.md +6 -0
- package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/forms/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/macros/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/mail/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/plan/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/slides/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/tasks/.agents/skills/security/SKILL.md +13 -2
- package/corpus/toolkit/CHANGELOG.md +13 -0
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/src/chat-history.css +1 -2
- package/corpus/toolkit/src/composer/TiptapComposer.tsx +45 -0
- package/dist/a2a/client.d.ts +7 -0
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +8 -3
- package/dist/a2a/client.js.map +1 -1
- package/dist/a2a/server.d.ts.map +1 -1
- package/dist/a2a/server.js +18 -3
- package/dist/a2a/server.js.map +1 -1
- package/dist/a2a/types.d.ts +7 -0
- package/dist/a2a/types.d.ts.map +1 -1
- package/dist/a2a/types.js.map +1 -1
- package/dist/action.d.ts +15 -1
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +39 -16
- package/dist/action.js.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +14 -10
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +35 -7
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts +2 -0
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +3 -0
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/chat-threads/migrations.d.ts +21 -0
- package/dist/chat-threads/migrations.d.ts.map +1 -0
- package/dist/chat-threads/migrations.js +45 -0
- package/dist/chat-threads/migrations.js.map +1 -0
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +4 -0
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.d.ts.map +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.js +36 -16
- package/dist/cli/skills-content/design-visual-edit-skill.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +15 -0
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +6 -5
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +37 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +41 -8
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +11 -10
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +19 -3
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +150 -101
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/chat/message-components.d.ts +2 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +11 -4
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat/runtime.d.ts.map +1 -1
- package/dist/client/chat/runtime.js +2 -1
- package/dist/client/chat/runtime.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +4 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +14 -8
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/chat-model-groups.d.ts.map +1 -1
- package/dist/client/chat-model-groups.js +3 -0
- package/dist/client/chat-model-groups.js.map +1 -1
- package/dist/client/client-status-requests.d.ts +14 -0
- package/dist/client/client-status-requests.d.ts.map +1 -0
- package/dist/client/client-status-requests.js +126 -0
- package/dist/client/client-status-requests.js.map +1 -0
- package/dist/client/org/OrgSwitcher.js +2 -2
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +7 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +8 -3
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
- package/dist/client/use-agent-engine-configured.js +29 -43
- package/dist/client/use-agent-engine-configured.js.map +1 -1
- package/dist/client/use-chat-models.d.ts.map +1 -1
- package/dist/client/use-chat-models.js +11 -11
- package/dist/client/use-chat-models.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +23 -7
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/client/use-session.d.ts.map +1 -1
- package/dist/client/use-session.js +4 -4
- package/dist/client/use-session.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +48 -4
- package/dist/db/client.js.map +1 -1
- package/dist/db/ensure-additive-columns.d.ts.map +1 -1
- package/dist/db/ensure-additive-columns.js +32 -21
- package/dist/db/ensure-additive-columns.js.map +1 -1
- package/dist/db/migrations.d.ts +9 -0
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +14 -3
- package/dist/db/migrations.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/mcp/approval-store.d.ts +16 -0
- package/dist/mcp/approval-store.d.ts.map +1 -0
- package/dist/mcp/approval-store.js +85 -0
- package/dist/mcp/approval-store.js.map +1 -0
- package/dist/mcp/build-server.d.ts +1 -34
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +220 -23
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/embed-app.js +1 -1
- package/dist/mcp/embed-app.js.map +1 -1
- package/dist/mcp/oauth-client-metadata.d.ts +15 -0
- package/dist/mcp/oauth-client-metadata.d.ts.map +1 -0
- package/dist/mcp/oauth-client-metadata.js +297 -0
- package/dist/mcp/oauth-client-metadata.js.map +1 -0
- package/dist/mcp/oauth-route.d.ts.map +1 -1
- package/dist/mcp/oauth-route.js +39 -53
- package/dist/mcp/oauth-route.js.map +1 -1
- package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
- package/dist/mcp/screen-memory-stdio.js +304 -296
- package/dist/mcp/screen-memory-stdio.js.map +1 -1
- package/dist/mcp/server.d.ts +12 -25
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +24 -133
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/stdio.d.ts.map +1 -1
- package/dist/mcp/stdio.js +36 -35
- package/dist/mcp/stdio.js.map +1 -1
- package/dist/mcp-client/manager.d.ts.map +1 -1
- package/dist/mcp-client/manager.js +6 -6
- package/dist/mcp-client/manager.js.map +1 -1
- package/dist/mcp-client/oauth-client.d.ts +13 -17
- package/dist/mcp-client/oauth-client.d.ts.map +1 -1
- package/dist/mcp-client/oauth-client.js +74 -16
- package/dist/mcp-client/oauth-client.js.map +1 -1
- package/dist/mcp-client/oauth-routes.d.ts +2 -2
- package/dist/mcp-client/oauth-routes.d.ts.map +1 -1
- package/dist/mcp-client/oauth-routes.js +16 -6
- package/dist/mcp-client/oauth-routes.js.map +1 -1
- package/dist/mcp-client/routes.js +5 -5
- package/dist/mcp-client/routes.js.map +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +14 -14
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-capabilities.d.ts.map +1 -1
- package/dist/server/agent-capabilities.js +21 -0
- package/dist/server/agent-capabilities.js.map +1 -1
- package/dist/server/agent-chat/action-filters-a2a.d.ts +14 -0
- package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
- package/dist/server/agent-chat/action-filters-a2a.js +16 -0
- package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +11 -1
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/server/transcribe-voice.js +12 -6
- package/dist/server/transcribe-voice.js.map +1 -1
- package/dist/templates/chat/.agents/skills/security/SKILL.md +13 -2
- package/dist/templates/chat/README.md +0 -2
- package/dist/templates/default/.agents/skills/security/SKILL.md +13 -2
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
- package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
- package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
- package/docs/content/template-design-collaboration-and-full-apps.mdx +3 -1
- package/package.json +7 -4
- package/src/a2a/client.ts +18 -4
- package/src/a2a/server.ts +19 -3
- package/src/a2a/types.ts +7 -0
- package/src/action.ts +43 -16
- package/src/agent/engine/ai-sdk-engine.ts +14 -10
- package/src/agent/production-agent.ts +41 -3
- package/src/agent/thread-data-builder.ts +5 -0
- package/src/chat-threads/migrations.ts +53 -0
- package/src/cli/create.ts +4 -0
- package/src/cli/skills-content/design-visual-edit-skill.ts +36 -16
- package/src/cli/skills.ts +16 -0
- package/src/client/AgentPanel.tsx +9 -5
- package/src/client/AssistantChat.tsx +138 -46
- package/src/client/MultiTabAssistantChat.tsx +16 -10
- package/src/client/agent-chat-adapter.ts +21 -3
- package/src/client/analytics.ts +159 -102
- package/src/client/chat/message-components.tsx +12 -5
- package/src/client/chat/runtime.ts +2 -1
- package/src/client/chat/tool-call-display.tsx +25 -2
- package/src/client/chat-model-groups.ts +3 -0
- package/src/client/client-status-requests.ts +161 -0
- package/src/client/org/OrgSwitcher.tsx +4 -4
- package/src/client/settings/VoiceTranscriptionSection.tsx +2 -2
- package/src/client/sse-event-processor.ts +15 -3
- package/src/client/use-agent-engine-configured.ts +54 -50
- package/src/client/use-chat-models.ts +16 -11
- package/src/client/use-chat-threads.ts +32 -9
- package/src/client/use-session.ts +4 -5
- package/src/db/client.ts +83 -8
- package/src/db/ensure-additive-columns.ts +31 -20
- package/src/db/migrations.ts +22 -2
- package/src/mcp/approval-store.ts +99 -0
- package/src/mcp/build-server.ts +579 -303
- package/src/mcp/embed-app.ts +1 -1
- package/src/mcp/oauth-client-metadata.ts +380 -0
- package/src/mcp/oauth-route.ts +57 -52
- package/src/mcp/screen-memory-stdio.ts +400 -378
- package/src/mcp/server.ts +29 -145
- package/src/mcp/stdio.ts +72 -80
- package/src/mcp-client/manager.ts +6 -10
- package/src/mcp-client/oauth-client.ts +127 -36
- package/src/mcp-client/oauth-routes.ts +16 -6
- package/src/mcp-client/routes.ts +6 -5
- package/src/server/agent-capabilities.ts +20 -0
- package/src/server/agent-chat/action-filters-a2a.ts +27 -0
- package/src/server/agent-chat-plugin.ts +18 -0
- package/src/server/transcribe-voice.ts +14 -5
- package/src/templates/chat/.agents/skills/security/SKILL.md +13 -2
- package/src/templates/chat/README.md +0 -2
- package/src/templates/default/.agents/skills/security/SKILL.md +13 -2
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
- package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
- package/src/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
|
@@ -10,15 +10,17 @@
|
|
|
10
10
|
import {
|
|
11
11
|
auth,
|
|
12
12
|
refreshAuthorization,
|
|
13
|
+
validateAuthorizationResponseIssuer,
|
|
14
|
+
type AuthorizationServerMetadata,
|
|
15
|
+
type OAuthClientInformationContext,
|
|
16
|
+
type OAuthClientMetadata,
|
|
13
17
|
type OAuthClientProvider,
|
|
14
|
-
|
|
15
|
-
import type {
|
|
16
|
-
AuthorizationServerMetadata,
|
|
17
|
-
OAuthClientInformationMixed,
|
|
18
|
-
OAuthClientMetadata,
|
|
18
|
+
type OAuthDiscoveryState,
|
|
19
19
|
OAuthProtectedResourceMetadata,
|
|
20
|
+
type StoredOAuthClientInformation,
|
|
21
|
+
type StoredOAuthTokens,
|
|
20
22
|
OAuthTokens,
|
|
21
|
-
} from "@modelcontextprotocol/
|
|
23
|
+
} from "@modelcontextprotocol/client";
|
|
22
24
|
|
|
23
25
|
import {
|
|
24
26
|
deleteOAuthTokens,
|
|
@@ -131,18 +133,13 @@ function credentialOwner(options: { scope: "user" | "org"; scopeId: string }) {
|
|
|
131
133
|
return `${options.scope}:${options.scopeId}`;
|
|
132
134
|
}
|
|
133
135
|
|
|
134
|
-
export
|
|
135
|
-
authorizationServerUrl: string;
|
|
136
|
-
authorizationServerMetadata?: AuthorizationServerMetadata;
|
|
137
|
-
resourceMetadata?: OAuthProtectedResourceMetadata;
|
|
138
|
-
resourceMetadataUrl?: string;
|
|
139
|
-
}
|
|
136
|
+
export type McpOAuthDiscoveryState = OAuthDiscoveryState;
|
|
140
137
|
|
|
141
138
|
export interface McpOAuthCredentialBundle {
|
|
142
139
|
serverUrl: string;
|
|
143
|
-
clientInformation:
|
|
140
|
+
clientInformation: StoredOAuthClientInformation;
|
|
144
141
|
discoveryState?: McpOAuthDiscoveryState;
|
|
145
|
-
tokens:
|
|
142
|
+
tokens: StoredOAuthTokens;
|
|
146
143
|
tokenExpiresAt?: number;
|
|
147
144
|
}
|
|
148
145
|
|
|
@@ -150,7 +147,7 @@ export interface McpOAuthStartResult {
|
|
|
150
147
|
authorizationUrl: URL;
|
|
151
148
|
codeVerifier: string;
|
|
152
149
|
state: string;
|
|
153
|
-
clientInformation:
|
|
150
|
+
clientInformation: StoredOAuthClientInformation;
|
|
154
151
|
discoveryState?: McpOAuthDiscoveryState;
|
|
155
152
|
}
|
|
156
153
|
|
|
@@ -162,11 +159,50 @@ export interface McpOAuthProviderOptions {
|
|
|
162
159
|
serverUrl: string;
|
|
163
160
|
redirectUrl: string;
|
|
164
161
|
state: string;
|
|
165
|
-
clientInformation?:
|
|
162
|
+
clientInformation?: StoredOAuthClientInformation;
|
|
166
163
|
codeVerifier?: string;
|
|
167
164
|
discoveryState?: McpOAuthDiscoveryState;
|
|
168
165
|
}
|
|
169
166
|
|
|
167
|
+
function issuerForDiscovery(
|
|
168
|
+
discovery: McpOAuthDiscoveryState | undefined,
|
|
169
|
+
): string | undefined {
|
|
170
|
+
const issuer = discovery?.authorizationServerMetadata?.issuer;
|
|
171
|
+
return typeof issuer === "string" && issuer ? issuer : undefined;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function withIssuer<T extends { issuer?: string }>(
|
|
175
|
+
value: T,
|
|
176
|
+
context: OAuthClientInformationContext | undefined,
|
|
177
|
+
): T {
|
|
178
|
+
if (value.issuer && context?.issuer && value.issuer !== context.issuer) {
|
|
179
|
+
throw new Error("MCP OAuth credential issuer does not match discovery");
|
|
180
|
+
}
|
|
181
|
+
const issuer = context?.issuer ?? value.issuer;
|
|
182
|
+
return issuer ? { ...value, issuer } : value;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function isBoundToIssuer(
|
|
186
|
+
value: { issuer?: string } | undefined,
|
|
187
|
+
context: OAuthClientInformationContext | undefined,
|
|
188
|
+
): boolean {
|
|
189
|
+
return !context?.issuer || value?.issuer === context.issuer;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function applicationTypeForRedirect(redirectUrl: string): "native" | "web" {
|
|
193
|
+
const url = new URL(redirectUrl);
|
|
194
|
+
if (
|
|
195
|
+
url.protocol !== "https:" ||
|
|
196
|
+
url.hostname === "localhost" ||
|
|
197
|
+
url.hostname === "127.0.0.1" ||
|
|
198
|
+
url.hostname === "::1" ||
|
|
199
|
+
url.hostname === "[::1]"
|
|
200
|
+
) {
|
|
201
|
+
return "native";
|
|
202
|
+
}
|
|
203
|
+
return "web";
|
|
204
|
+
}
|
|
205
|
+
|
|
170
206
|
/**
|
|
171
207
|
* A small adapter around the MCP SDK's OAuth provider interface. The route
|
|
172
208
|
* stores the adapter's state in an encrypted, short-lived browser cookie; the
|
|
@@ -176,8 +212,8 @@ export class McpOAuthClientProvider implements OAuthClientProvider {
|
|
|
176
212
|
private readonly redirectUrlValue: string;
|
|
177
213
|
private readonly stateValue: string;
|
|
178
214
|
private readonly metadata: OAuthClientMetadata;
|
|
179
|
-
private clientInfo?:
|
|
180
|
-
private savedTokens?:
|
|
215
|
+
private clientInfo?: StoredOAuthClientInformation;
|
|
216
|
+
private savedTokens?: StoredOAuthTokens;
|
|
181
217
|
private savedCodeVerifier?: string;
|
|
182
218
|
private savedDiscovery?: McpOAuthDiscoveryState;
|
|
183
219
|
private authorizationUrl?: URL;
|
|
@@ -188,11 +224,24 @@ export class McpOAuthClientProvider implements OAuthClientProvider {
|
|
|
188
224
|
this.clientInfo = options.clientInformation;
|
|
189
225
|
this.savedCodeVerifier = options.codeVerifier;
|
|
190
226
|
this.savedDiscovery = options.discoveryState;
|
|
227
|
+
const recordedIssuer = issuerForDiscovery(this.savedDiscovery);
|
|
228
|
+
if (
|
|
229
|
+
this.clientInfo &&
|
|
230
|
+
!this.clientInfo.issuer &&
|
|
231
|
+
recordedIssuer &&
|
|
232
|
+
this.savedCodeVerifier
|
|
233
|
+
) {
|
|
234
|
+
// A callback flow persists registration, discovery, PKCE, and state in
|
|
235
|
+
// one encrypted cookie. Binding that pre-v2 in-flight registration to
|
|
236
|
+
// its recorded issuer is safe; durable credentials are never inferred.
|
|
237
|
+
this.clientInfo = { ...this.clientInfo, issuer: recordedIssuer };
|
|
238
|
+
}
|
|
191
239
|
this.metadata = {
|
|
192
240
|
redirect_uris: [options.redirectUrl],
|
|
193
241
|
token_endpoint_auth_method: "none",
|
|
194
|
-
grant_types: ["authorization_code"],
|
|
242
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
195
243
|
response_types: ["code"],
|
|
244
|
+
application_type: applicationTypeForRedirect(options.redirectUrl),
|
|
196
245
|
client_name: "Agent Native MCP connector",
|
|
197
246
|
};
|
|
198
247
|
}
|
|
@@ -209,20 +258,34 @@ export class McpOAuthClientProvider implements OAuthClientProvider {
|
|
|
209
258
|
return this.stateValue;
|
|
210
259
|
}
|
|
211
260
|
|
|
212
|
-
clientInformation(
|
|
213
|
-
|
|
261
|
+
clientInformation(
|
|
262
|
+
context?: OAuthClientInformationContext,
|
|
263
|
+
): StoredOAuthClientInformation | undefined {
|
|
264
|
+
return isBoundToIssuer(this.clientInfo, context)
|
|
265
|
+
? this.clientInfo
|
|
266
|
+
: undefined;
|
|
214
267
|
}
|
|
215
268
|
|
|
216
|
-
saveClientInformation(
|
|
217
|
-
|
|
269
|
+
saveClientInformation(
|
|
270
|
+
info: StoredOAuthClientInformation,
|
|
271
|
+
context?: OAuthClientInformationContext,
|
|
272
|
+
): void {
|
|
273
|
+
this.clientInfo = withIssuer(info, context);
|
|
218
274
|
}
|
|
219
275
|
|
|
220
|
-
tokens(
|
|
221
|
-
|
|
276
|
+
tokens(
|
|
277
|
+
context?: OAuthClientInformationContext,
|
|
278
|
+
): StoredOAuthTokens | undefined {
|
|
279
|
+
return isBoundToIssuer(this.savedTokens, context)
|
|
280
|
+
? this.savedTokens
|
|
281
|
+
: undefined;
|
|
222
282
|
}
|
|
223
283
|
|
|
224
|
-
saveTokens(
|
|
225
|
-
|
|
284
|
+
saveTokens(
|
|
285
|
+
tokens: StoredOAuthTokens,
|
|
286
|
+
context?: OAuthClientInformationContext,
|
|
287
|
+
): void {
|
|
288
|
+
this.savedTokens = withIssuer(tokens, context);
|
|
226
289
|
}
|
|
227
290
|
|
|
228
291
|
redirectToAuthorization(authorizationUrl: URL): void {
|
|
@@ -269,7 +332,7 @@ export class McpOAuthClientProvider implements OAuthClientProvider {
|
|
|
269
332
|
return this.savedTokens;
|
|
270
333
|
}
|
|
271
334
|
|
|
272
|
-
get savedClientInformation():
|
|
335
|
+
get savedClientInformation(): StoredOAuthClientInformation | undefined {
|
|
273
336
|
return this.clientInfo;
|
|
274
337
|
}
|
|
275
338
|
}
|
|
@@ -301,13 +364,17 @@ export async function startMcpOAuthAuthorization(
|
|
|
301
364
|
}
|
|
302
365
|
|
|
303
366
|
export async function finishMcpOAuthAuthorization(
|
|
304
|
-
options: McpOAuthProviderOptions & {
|
|
367
|
+
options: McpOAuthProviderOptions & {
|
|
368
|
+
authorizationCode: string;
|
|
369
|
+
iss?: string;
|
|
370
|
+
},
|
|
305
371
|
): Promise<McpOAuthCallbackResult> {
|
|
306
372
|
checkedRemoteUrl(options.serverUrl, "server");
|
|
307
373
|
const provider = new McpOAuthClientProvider(options);
|
|
308
374
|
const result = await auth(provider, {
|
|
309
375
|
serverUrl: options.serverUrl,
|
|
310
376
|
authorizationCode: options.authorizationCode,
|
|
377
|
+
iss: options.iss,
|
|
311
378
|
fetchFn: guardedOAuthFetch(),
|
|
312
379
|
});
|
|
313
380
|
if (result !== "AUTHORIZED" || !provider.savedTokensValue) {
|
|
@@ -325,6 +392,19 @@ export async function finishMcpOAuthAuthorization(
|
|
|
325
392
|
};
|
|
326
393
|
}
|
|
327
394
|
|
|
395
|
+
export function validateMcpOAuthCallbackIssuer(
|
|
396
|
+
discoveryState: McpOAuthDiscoveryState | undefined,
|
|
397
|
+
iss: string | undefined,
|
|
398
|
+
): void {
|
|
399
|
+
validateAuthorizationResponseIssuer({
|
|
400
|
+
iss,
|
|
401
|
+
expectedIssuer: issuerForDiscovery(discoveryState),
|
|
402
|
+
issParameterSupported:
|
|
403
|
+
discoveryState?.authorizationServerMetadata
|
|
404
|
+
?.authorization_response_iss_parameter_supported === true,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
328
408
|
export function tokenExpiresAt(tokens: OAuthTokens): number | undefined {
|
|
329
409
|
const expiresIn = Number(tokens.expires_in);
|
|
330
410
|
if (!Number.isFinite(expiresIn) || expiresIn <= 0) return undefined;
|
|
@@ -406,20 +486,30 @@ export async function getMcpOAuthAccessToken(options: {
|
|
|
406
486
|
if (!credentials || credentials.serverUrl !== options.serverUrl) return null;
|
|
407
487
|
|
|
408
488
|
const accessToken = credentials.tokens.access_token;
|
|
489
|
+
const now = Date.now();
|
|
409
490
|
if (
|
|
410
491
|
typeof credentials.tokenExpiresAt !== "number" ||
|
|
411
|
-
credentials.tokenExpiresAt -
|
|
492
|
+
credentials.tokenExpiresAt - now > TOKEN_EXPIRY_SKEW_MS
|
|
412
493
|
) {
|
|
413
494
|
return accessToken;
|
|
414
495
|
}
|
|
496
|
+
const tokenIsExpired = credentials.tokenExpiresAt <= now;
|
|
415
497
|
|
|
416
498
|
const refreshToken = credentials.tokens.refresh_token;
|
|
417
499
|
const discovery = credentials.discoveryState;
|
|
418
500
|
if (!refreshToken || !discovery?.authorizationServerUrl) {
|
|
419
|
-
return accessToken;
|
|
501
|
+
return tokenIsExpired ? null : accessToken;
|
|
420
502
|
}
|
|
421
503
|
|
|
422
504
|
try {
|
|
505
|
+
const expectedIssuer = issuerForDiscovery(discovery);
|
|
506
|
+
if (
|
|
507
|
+
!expectedIssuer ||
|
|
508
|
+
credentials.clientInformation.issuer !== expectedIssuer ||
|
|
509
|
+
credentials.tokens.issuer !== expectedIssuer
|
|
510
|
+
) {
|
|
511
|
+
return null;
|
|
512
|
+
}
|
|
423
513
|
const resource = discovery.resourceMetadata?.resource
|
|
424
514
|
? checkedRemoteUrl(discovery.resourceMetadata.resource, "resource")
|
|
425
515
|
: undefined;
|
|
@@ -434,9 +524,10 @@ export async function getMcpOAuthAccessToken(options: {
|
|
|
434
524
|
resource,
|
|
435
525
|
fetchFn: guardedOAuthFetch(),
|
|
436
526
|
});
|
|
437
|
-
const nextTokens:
|
|
527
|
+
const nextTokens: StoredOAuthTokens = {
|
|
438
528
|
...credentials.tokens,
|
|
439
529
|
...refreshed,
|
|
530
|
+
issuer: expectedIssuer,
|
|
440
531
|
...(refreshed.refresh_token
|
|
441
532
|
? { refresh_token: refreshed.refresh_token }
|
|
442
533
|
: credentials.tokens.refresh_token
|
|
@@ -454,9 +545,9 @@ export async function getMcpOAuthAccessToken(options: {
|
|
|
454
545
|
});
|
|
455
546
|
return nextTokens.access_token;
|
|
456
547
|
} catch {
|
|
457
|
-
//
|
|
458
|
-
//
|
|
459
|
-
//
|
|
460
|
-
return accessToken;
|
|
548
|
+
// A still-valid token can survive a transient refresh failure. Once it has
|
|
549
|
+
// expired, omit it so callers surface reauthorization instead of retrying
|
|
550
|
+
// a credential that can never authenticate.
|
|
551
|
+
return tokenIsExpired ? null : accessToken;
|
|
461
552
|
}
|
|
462
553
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { StoredOAuthClientInformation } from "@modelcontextprotocol/client";
|
|
4
4
|
import {
|
|
5
5
|
deleteCookie,
|
|
6
6
|
defineEventHandler,
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
finishMcpOAuthAuthorization,
|
|
24
24
|
startMcpOAuthAuthorization,
|
|
25
25
|
type McpOAuthDiscoveryState,
|
|
26
|
+
validateMcpOAuthCallbackIssuer,
|
|
26
27
|
} from "./oauth-client.js";
|
|
27
28
|
import {
|
|
28
29
|
addOAuthRemoteServer,
|
|
@@ -48,7 +49,7 @@ export interface McpOAuthFlow {
|
|
|
48
49
|
redirectUri: string;
|
|
49
50
|
state: string;
|
|
50
51
|
codeVerifier: string;
|
|
51
|
-
clientInformation:
|
|
52
|
+
clientInformation: StoredOAuthClientInformation;
|
|
52
53
|
discoveryState?: McpOAuthDiscoveryState;
|
|
53
54
|
returnUrl?: string;
|
|
54
55
|
expiresAt: number;
|
|
@@ -214,22 +215,30 @@ async function handleMcpOAuthCallback(
|
|
|
214
215
|
const query = getQuery(event);
|
|
215
216
|
const code = text(query.code);
|
|
216
217
|
const state = text(query.state);
|
|
218
|
+
const iss = text(query.iss);
|
|
217
219
|
const providerError = text(query.error);
|
|
218
220
|
const flow = readMcpOAuthFlowCookie(event);
|
|
219
221
|
clearMcpOAuthFlowCookies(event);
|
|
220
|
-
if (providerError || !code || !state) {
|
|
221
|
-
setResponseStatus(event, 400);
|
|
222
|
-
return { error: "MCP OAuth authorization was not completed." };
|
|
223
|
-
}
|
|
224
222
|
const org =
|
|
225
223
|
flow?.scope === "org" ? await getOrgContext(event).catch(() => null) : null;
|
|
226
224
|
if (
|
|
225
|
+
!state ||
|
|
227
226
|
!flow ||
|
|
228
227
|
!isValidMcpOAuthFlow(flow, session.email, org?.orgId ?? undefined, state)
|
|
229
228
|
) {
|
|
230
229
|
setResponseStatus(event, 400);
|
|
231
230
|
return { error: "MCP OAuth state is invalid or expired." };
|
|
232
231
|
}
|
|
232
|
+
try {
|
|
233
|
+
validateMcpOAuthCallbackIssuer(flow.discoveryState, iss);
|
|
234
|
+
} catch {
|
|
235
|
+
setResponseStatus(event, 400);
|
|
236
|
+
return { error: "MCP OAuth authorization response issuer is invalid." };
|
|
237
|
+
}
|
|
238
|
+
if (providerError || !code) {
|
|
239
|
+
setResponseStatus(event, 400);
|
|
240
|
+
return { error: "MCP OAuth authorization was not completed." };
|
|
241
|
+
}
|
|
233
242
|
if (flow.scope === "org" && !isOrgAdmin(org?.role)) {
|
|
234
243
|
setResponseStatus(event, 403);
|
|
235
244
|
return {
|
|
@@ -250,6 +259,7 @@ async function handleMcpOAuthCallback(
|
|
|
250
259
|
codeVerifier: flow.codeVerifier,
|
|
251
260
|
discoveryState: flow.discoveryState,
|
|
252
261
|
authorizationCode: code,
|
|
262
|
+
iss,
|
|
253
263
|
}),
|
|
254
264
|
);
|
|
255
265
|
const result = await addOAuthRemoteServer(flow.scope, flow.scopeId, {
|
package/src/mcp-client/routes.ts
CHANGED
|
@@ -1055,10 +1055,8 @@ async function tryConnect(
|
|
|
1055
1055
|
| { ok: false; error: string }
|
|
1056
1056
|
> {
|
|
1057
1057
|
try {
|
|
1058
|
-
const
|
|
1059
|
-
import("@modelcontextprotocol/
|
|
1060
|
-
import("@modelcontextprotocol/sdk/client/streamableHttp.js"),
|
|
1061
|
-
]);
|
|
1058
|
+
const { Client, StreamableHTTPClientTransport } =
|
|
1059
|
+
await import("@modelcontextprotocol/client");
|
|
1062
1060
|
const requestInit: Record<string, unknown> = {};
|
|
1063
1061
|
if (headers && Object.keys(headers).length > 0) {
|
|
1064
1062
|
requestInit.headers = headers;
|
|
@@ -1068,7 +1066,10 @@ async function tryConnect(
|
|
|
1068
1066
|
});
|
|
1069
1067
|
const client = new Client(
|
|
1070
1068
|
{ name: "agent-native-mcp-client-test", version: "1.0.0" },
|
|
1071
|
-
{
|
|
1069
|
+
{
|
|
1070
|
+
capabilities: {},
|
|
1071
|
+
versionNegotiation: { mode: "auto" },
|
|
1072
|
+
},
|
|
1072
1073
|
);
|
|
1073
1074
|
try {
|
|
1074
1075
|
await client.connect(transport);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { A2AClient } from "../a2a/client.js";
|
|
2
2
|
import type { AgentCard, AgentSkill } from "../a2a/types.js";
|
|
3
3
|
import { type DiscoveredAgent } from "./agent-discovery.js";
|
|
4
|
+
import { getRequestUserEmail } from "./request-context.js";
|
|
4
5
|
|
|
5
6
|
/** Matches the `<available-apps>` prompt block so both surfaces agree. */
|
|
6
7
|
export const MAX_APPS = 30;
|
|
@@ -33,8 +34,27 @@ export async function loadCapabilities(
|
|
|
33
34
|
agent: DiscoveredAgent,
|
|
34
35
|
): Promise<PeerCapabilities> {
|
|
35
36
|
try {
|
|
37
|
+
// Discover as ourselves. An anonymous card lists only publicly-safe
|
|
38
|
+
// actions, which never overlap the set `actions/invoke` accepts, so an
|
|
39
|
+
// unauthenticated probe reports every sibling as having no callable
|
|
40
|
+
// actions and pushes the caller into open-ended delegation.
|
|
41
|
+
let token: string | undefined;
|
|
42
|
+
try {
|
|
43
|
+
const { signA2AToken } = await import("../a2a/client.js");
|
|
44
|
+
const email = getRequestUserEmail();
|
|
45
|
+
if (email) {
|
|
46
|
+
token = await signA2AToken(email, undefined, undefined, {
|
|
47
|
+
preferGlobalSecret: true,
|
|
48
|
+
audience: new URL(agent.url).origin,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
} catch {
|
|
52
|
+
// No signable identity (no secret, no session) — fall back to the
|
|
53
|
+
// anonymous card rather than failing discovery outright.
|
|
54
|
+
}
|
|
36
55
|
const card = await new A2AClient(agent.url).getAgentCard({
|
|
37
56
|
timeoutMs: CARD_TIMEOUT_MS,
|
|
57
|
+
...(token ? { token } : {}),
|
|
38
58
|
});
|
|
39
59
|
return {
|
|
40
60
|
agent,
|
|
@@ -119,6 +119,33 @@ export function buildPublicAgentA2ASkills(
|
|
|
119
119
|
);
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Skills for a caller with a verified A2A identity: exactly what
|
|
124
|
+
* `filterDirectA2AActions` will execute. Kept separate from
|
|
125
|
+
* `buildPublicAgentA2ASkills` because the public card may only name actions
|
|
126
|
+
* with `requiresAuth !== true` while direct invocation requires
|
|
127
|
+
* `requiresAuth === true` — advertising the public set alone tells siblings
|
|
128
|
+
* an app has nothing callable when it does.
|
|
129
|
+
*/
|
|
130
|
+
export function buildAuthenticatedAgentA2ASkills(
|
|
131
|
+
actions: Record<string, ActionEntry>,
|
|
132
|
+
options: Pick<AgentChatPluginOptions, "connectorCatalog" | "externalAgents">,
|
|
133
|
+
): Array<{
|
|
134
|
+
id: string;
|
|
135
|
+
name: string;
|
|
136
|
+
description: string;
|
|
137
|
+
publicAgent: ActionEntry["publicAgent"];
|
|
138
|
+
}> {
|
|
139
|
+
return Object.entries(filterDirectA2AActions(actions, options)).map(
|
|
140
|
+
([name, entry]) => ({
|
|
141
|
+
id: name,
|
|
142
|
+
name,
|
|
143
|
+
description: entry.tool.description,
|
|
144
|
+
publicAgent: entry.publicAgent,
|
|
145
|
+
}),
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
122
149
|
export function resolveArtifactBaseUrl(
|
|
123
150
|
event: any | undefined,
|
|
124
151
|
): string | undefined {
|
|
@@ -102,6 +102,7 @@ import type {
|
|
|
102
102
|
MentionProvider,
|
|
103
103
|
} from "../agent/types.js";
|
|
104
104
|
import { readAppStateForCurrentTab } from "../application-state/script-helpers.js";
|
|
105
|
+
import { runChatThreadDataMigrations } from "../chat-threads/migrations.js";
|
|
105
106
|
import {
|
|
106
107
|
createThread,
|
|
107
108
|
forkThread,
|
|
@@ -280,6 +281,7 @@ import {
|
|
|
280
281
|
runMCPAgentLoop,
|
|
281
282
|
assembleA2AFinalResponse,
|
|
282
283
|
buildPublicAgentA2ASkills,
|
|
284
|
+
buildAuthenticatedAgentA2ASkills,
|
|
283
285
|
resolveArtifactBaseUrl,
|
|
284
286
|
} from "./agent-chat/action-filters-a2a.js";
|
|
285
287
|
import {
|
|
@@ -1318,6 +1320,10 @@ export function createAgentChatPlugin(
|
|
|
1318
1320
|
: "Agent",
|
|
1319
1321
|
description: `Agent-native ${options?.appId ?? "app"} agent`,
|
|
1320
1322
|
skills: buildPublicAgentA2ASkills(allScripts),
|
|
1323
|
+
authenticatedSkills: buildAuthenticatedAgentA2ASkills(
|
|
1324
|
+
mcpFullActions ?? allScripts,
|
|
1325
|
+
options ?? {},
|
|
1326
|
+
),
|
|
1321
1327
|
publicSkillsOnly: true,
|
|
1322
1328
|
streaming: true,
|
|
1323
1329
|
durableBackgroundRuns: options?.durableBackgroundRuns,
|
|
@@ -5952,6 +5958,18 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
5952
5958
|
}, 20_000); // Start 20s after init (after the agent-teams sweep)
|
|
5953
5959
|
})();
|
|
5954
5960
|
|
|
5961
|
+
// ─── Legacy chat-thread message_count repair ──────────────────────
|
|
5962
|
+
// Long-lived processes apply this name-tracked data migration once.
|
|
5963
|
+
// Serverless isolates skip it: launching a full thread_data scan from
|
|
5964
|
+
// every cold start would recreate the connection stampede this repair
|
|
5965
|
+
// was extracted from table bootstrap to prevent.
|
|
5966
|
+
void runChatThreadDataMigrations(nitroApp).catch((err: unknown) => {
|
|
5967
|
+
console.error(
|
|
5968
|
+
"[chat-threads] legacy message_count repair failed — retrying on the next long-lived boot:",
|
|
5969
|
+
err,
|
|
5970
|
+
);
|
|
5971
|
+
});
|
|
5972
|
+
|
|
5955
5973
|
// ─── Trigger Dispatcher (event-based automations) ─────────────────
|
|
5956
5974
|
if (disableRecurringJobsRuntime) {
|
|
5957
5975
|
if (process.env.DEBUG) {
|
|
@@ -49,10 +49,10 @@ const GROQ_URL = "https://api.groq.com/openai/v1/audio/transcriptions";
|
|
|
49
49
|
const GROQ_CHAT_URL = "https://api.groq.com/openai/v1/chat/completions";
|
|
50
50
|
const GROQ_MODEL = "whisper-large-v3-turbo";
|
|
51
51
|
const GROQ_CLEANUP_MODEL = "llama-3.3-70b-versatile";
|
|
52
|
-
const OPENAI_MODEL = "
|
|
52
|
+
const OPENAI_MODEL = "gpt-transcribe";
|
|
53
53
|
const OPENAI_CHAT_URL = "https://api.openai.com/v1/chat/completions";
|
|
54
54
|
const OPENAI_CLEANUP_MODEL = "gpt-5.6-luna";
|
|
55
|
-
const MAX_AUDIO_BYTES = 25 * 1024 * 1024; //
|
|
55
|
+
const MAX_AUDIO_BYTES = 25 * 1024 * 1024; // OpenAI audio upload hard limit.
|
|
56
56
|
// Hard cap for the dictation-cleanup text path (`sanitizeTranscriptText`
|
|
57
57
|
// below). This endpoint has no `task` field — it's always the Hold-Fn
|
|
58
58
|
// dictation cleanup pass — but a long session (many minutes across
|
|
@@ -497,8 +497,8 @@ export function createTranscribeVoiceHandler() {
|
|
|
497
497
|
}
|
|
498
498
|
|
|
499
499
|
/**
|
|
500
|
-
* Posts the audio to
|
|
501
|
-
*
|
|
500
|
+
* Posts the audio to an OpenAI-style transcription endpoint (Groq or OpenAI)
|
|
501
|
+
* and returns `{ text }` / `{ error }` shaped like the
|
|
502
502
|
* other branches in `createTranscribeVoiceHandler`. Hoisted so the
|
|
503
503
|
* strict-Groq preference path and the auto fallback chain share one
|
|
504
504
|
* implementation.
|
|
@@ -538,7 +538,16 @@ async function callWhisperCompat({
|
|
|
538
538
|
);
|
|
539
539
|
form.append("model", provider.model);
|
|
540
540
|
form.append("response_format", "json");
|
|
541
|
-
if (language)
|
|
541
|
+
if (language) {
|
|
542
|
+
if (provider.name === "openai") {
|
|
543
|
+
form.append(
|
|
544
|
+
"languages[]",
|
|
545
|
+
language.split("-")[0]?.toLowerCase() ?? language,
|
|
546
|
+
);
|
|
547
|
+
} else {
|
|
548
|
+
form.append("language", language);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
542
551
|
if (instructions) form.append("prompt", instructions);
|
|
543
552
|
|
|
544
553
|
const controller = new AbortController();
|
|
@@ -163,7 +163,9 @@ private UI after the shell loads. See the `authentication` skill and
|
|
|
163
163
|
proves *someone* is signed in. To restrict an operation to some teammates, set
|
|
164
164
|
`authorize` on the `defineAction`. It wraps `run`, so it applies at every
|
|
165
165
|
dispatch site (agent tool, HTTP, frontend, MCP, A2A, CLI) — unlike
|
|
166
|
-
`needsApproval`, which is
|
|
166
|
+
`needsApproval`, which is honored by the agent loop and MCP 2026 hosts that
|
|
167
|
+
support elicitation, but is not an authorization boundary for every dispatch
|
|
168
|
+
site.
|
|
167
169
|
|
|
168
170
|
```ts
|
|
169
171
|
import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
|
|
@@ -203,7 +205,16 @@ export default defineAction({
|
|
|
203
205
|
});
|
|
204
206
|
```
|
|
205
207
|
|
|
206
|
-
When the gate is truthy and the call is not yet approved, the loop emits
|
|
208
|
+
When the gate is truthy and the call is not yet approved, the agent loop emits
|
|
209
|
+
an `approval_required` event and **stops the turn — `run()` never executes**.
|
|
210
|
+
The human approves via the chat UI's Approve affordance, which re-issues the
|
|
211
|
+
turn with the call's stable `approvalKey`; only then does the action run. MCP
|
|
212
|
+
2026 hosts receive an `input_required` approval request bound to the exact
|
|
213
|
+
caller, action, and arguments. The signed response state is backed by a durable,
|
|
214
|
+
single-use grant, so denial, expiry, replay, missing host support, or invalid
|
|
215
|
+
state fails closed before `run()`. A predicate gates conditionally (e.g. only
|
|
216
|
+
external recipients) and **fails closed** — a throw is treated as "approval
|
|
217
|
+
required".
|
|
207
218
|
|
|
208
219
|
Rules:
|
|
209
220
|
|
|
@@ -163,7 +163,9 @@ private UI after the shell loads. See the `authentication` skill and
|
|
|
163
163
|
proves *someone* is signed in. To restrict an operation to some teammates, set
|
|
164
164
|
`authorize` on the `defineAction`. It wraps `run`, so it applies at every
|
|
165
165
|
dispatch site (agent tool, HTTP, frontend, MCP, A2A, CLI) — unlike
|
|
166
|
-
`needsApproval`, which is
|
|
166
|
+
`needsApproval`, which is honored by the agent loop and MCP 2026 hosts that
|
|
167
|
+
support elicitation, but is not an authorization boundary for every dispatch
|
|
168
|
+
site.
|
|
167
169
|
|
|
168
170
|
```ts
|
|
169
171
|
import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
|
|
@@ -203,7 +205,16 @@ export default defineAction({
|
|
|
203
205
|
});
|
|
204
206
|
```
|
|
205
207
|
|
|
206
|
-
When the gate is truthy and the call is not yet approved, the loop emits
|
|
208
|
+
When the gate is truthy and the call is not yet approved, the agent loop emits
|
|
209
|
+
an `approval_required` event and **stops the turn — `run()` never executes**.
|
|
210
|
+
The human approves via the chat UI's Approve affordance, which re-issues the
|
|
211
|
+
turn with the call's stable `approvalKey`; only then does the action run. MCP
|
|
212
|
+
2026 hosts receive an `input_required` approval request bound to the exact
|
|
213
|
+
caller, action, and arguments. The signed response state is backed by a durable,
|
|
214
|
+
single-use grant, so denial, expiry, replay, missing host support, or invalid
|
|
215
|
+
state fails closed before `run()`. A predicate gates conditionally (e.g. only
|
|
216
|
+
external recipients) and **fails closed** — a throw is treated as "approval
|
|
217
|
+
required".
|
|
207
218
|
|
|
208
219
|
Rules:
|
|
209
220
|
|
|
@@ -39,8 +39,11 @@ Auth is powered by **Better Auth** with account-first design. Every new user cre
|
|
|
39
39
|
Every app's `/mcp` endpoint is also a standard protected MCP
|
|
40
40
|
resource. OAuth-capable hosts connect with the remote MCP URL only, receive a
|
|
41
41
|
`WWW-Authenticate` challenge, discover `/.well-known/oauth-protected-resource`
|
|
42
|
-
and `/.well-known/oauth-authorization-server`,
|
|
43
|
-
client
|
|
42
|
+
and `/.well-known/oauth-authorization-server`, then use a validated Client ID
|
|
43
|
+
Metadata Document when the client id is an HTTPS URL or dynamically register a
|
|
44
|
+
public client otherwise. URL-like client ids never fall back to dynamic
|
|
45
|
+
registration when their metadata is missing or invalid. Clients complete
|
|
46
|
+
authorization-code + PKCE at
|
|
44
47
|
`/mcp/oauth/authorize` / `/mcp/oauth/token`.
|
|
45
48
|
Access tokens are audience-bound to the exact MCP URL and carry user/org
|
|
46
49
|
identity plus `mcp:read`, `mcp:write`, `mcp:apps`, and/or `offline_access`;
|
|
@@ -44,6 +44,24 @@ can still use `npx @agent-native/core connect <url>`, which mints a per-user,
|
|
|
44
44
|
scoped, revocable token from a logged-in browser session; no shared secret is
|
|
45
45
|
copied.
|
|
46
46
|
|
|
47
|
+
The framework serves MCP `2026-07-28` natively over stateless, per-request HTTP
|
|
48
|
+
and keeps the established stateless path for 2025-era clients. New MCP client
|
|
49
|
+
code uses the split `@modelcontextprotocol/client` package with
|
|
50
|
+
`versionNegotiation: { mode: "auto" }`; do not pin a protocol header or
|
|
51
|
+
hand-send `initialize`. OAuth callback handlers preserve `state` and pass the
|
|
52
|
+
RFC 9207 `iss` parameter to the v2 SDK for validation before exchanging a
|
|
53
|
+
code. The authorization server accepts HTTPS Client ID Metadata Documents after
|
|
54
|
+
strict URL, SSRF, size, redirect, schema, and exact callback validation; URL-like
|
|
55
|
+
client ids never fall back to dynamic registration. Traditional non-URL clients
|
|
56
|
+
continue to use public-client dynamic registration.
|
|
57
|
+
|
|
58
|
+
Actions marked `needsApproval` use MCP 2026 `input_required` elicitation. The
|
|
59
|
+
approval is bound to the authenticated caller, exact action, exact arguments,
|
|
60
|
+
and expiry, and its durable grant is consumed atomically before the action runs.
|
|
61
|
+
If the host cannot complete the approval exchange, the predicate throws, the
|
|
62
|
+
state is invalid or replayed, or durable storage is unavailable, the action
|
|
63
|
+
fails closed.
|
|
64
|
+
|
|
47
65
|
Claude and ChatGPT can cache custom connector tool/resource metadata. After
|
|
48
66
|
changing MCP App metadata or the shared `embedApp()` shell, validate with a
|
|
49
67
|
fresh tool call; if the host still behaves like the old descriptor, reconnect
|