@agent-native/core 0.115.4 → 0.117.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 +1 -0
- package/corpus/README.md +3 -3
- package/corpus/core/CHANGELOG.md +43 -0
- package/corpus/core/agent-native.eject.json +1 -0
- package/corpus/core/docs/AGENTS.md +1 -1
- package/corpus/core/docs/content/agent-mentions.mdx +2 -2
- package/corpus/core/docs/content/{workspace.mdx → agent-resources.mdx} +1 -7
- package/corpus/core/docs/content/agent-teams.mdx +4 -4
- package/corpus/core/docs/content/dispatch.mdx +10 -10
- package/corpus/core/docs/content/drop-in-agent.mdx +4 -4
- package/corpus/core/docs/content/external-agents.mdx +1 -1
- package/corpus/core/docs/content/frames.mdx +1 -1
- package/corpus/core/docs/content/integrations.mdx +13 -2
- package/corpus/core/docs/content/key-concepts.mdx +2 -2
- package/corpus/core/docs/content/mcp-clients.mdx +7 -1
- package/corpus/core/docs/content/multi-app-workspace.mdx +3 -3
- package/corpus/core/docs/content/real-time-collaboration.mdx +33 -15
- package/corpus/core/docs/content/recurring-jobs.mdx +7 -7
- package/corpus/core/docs/content/skills-guide.mdx +4 -4
- package/corpus/core/docs/content/template-brain.mdx +1 -1
- package/corpus/core/docs/content/toolkit-collaboration.mdx +1 -1
- package/corpus/core/docs/content/using-your-agent.mdx +1 -1
- package/corpus/core/docs/content/what-is-agent-native.mdx +4 -4
- package/corpus/core/docs/content/workspace-management.mdx +4 -4
- package/corpus/core/package.json +4 -1
- package/corpus/core/src/agent/production-agent.ts +13 -3
- package/corpus/core/src/cli/doctor.ts +6 -1
- package/corpus/core/src/client/AgentPanel.tsx +79 -41
- package/corpus/core/src/client/AssistantChat.tsx +71 -25
- package/corpus/core/src/client/FeedbackButton.tsx +33 -7
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +6 -8
- package/corpus/core/src/client/agent-chat-adapter.ts +11 -2
- package/corpus/core/src/client/agent-page/AgentTabFrame.tsx +22 -3
- package/corpus/core/src/client/agent-page/AgentTabsPage.tsx +34 -19
- package/corpus/core/src/client/analytics.ts +7 -0
- package/corpus/core/src/client/chat/message-components.tsx +43 -16
- package/corpus/core/src/client/chat/tool-call-display.tsx +30 -18
- package/corpus/core/src/client/chat/tool-render-registry.tsx +2 -0
- package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +19 -2
- package/corpus/core/src/client/resources/ResourceTree.tsx +7 -10
- package/corpus/core/src/client/resources/ResourcesPanel.tsx +33 -70
- package/corpus/core/src/client/resources/mcp-connection-resume.ts +107 -0
- package/corpus/core/src/client/resources/mcp-integration-catalog.ts +65 -12
- package/corpus/core/src/client/resources/mcp-integration-logos.ts +4 -0
- package/corpus/core/src/client/sse-event-processor.ts +50 -14
- package/corpus/core/src/client/use-db-sync.ts +370 -21
- package/corpus/core/src/collab/client.ts +26 -2
- package/corpus/core/src/connections/catalog.ts +7 -1
- package/corpus/core/src/db/ddl-guard.ts +49 -10
- package/corpus/core/src/deploy/build.ts +25 -2
- package/corpus/core/src/eject/remote-mcp-presets.ts +37 -5
- package/corpus/core/src/guards/explicit-collab-access.ts +229 -0
- package/corpus/core/src/guards/index.ts +1 -0
- package/corpus/core/src/localization/default-messages.ts +54 -5
- package/corpus/core/src/mcp/connect-route.ts +34 -51
- package/corpus/core/src/provider-api/index.ts +1 -1
- package/corpus/core/src/realtime-protocol.ts +92 -0
- package/corpus/core/src/server/collab-plugin.ts +137 -18
- package/corpus/core/src/server/core-routes-plugin.ts +7 -0
- package/corpus/core/src/server/index.ts +2 -0
- package/corpus/core/src/server/poll-events.ts +29 -8
- package/corpus/core/src/server/poll.ts +1123 -865
- package/corpus/core/src/server/realtime-token.ts +106 -0
- package/corpus/core/src/server/sentry-config.ts +40 -0
- package/corpus/core/src/server/short-lived-token.ts +156 -0
- package/corpus/core/src/server/sse.ts +15 -0
- package/corpus/core/src/server/ssr-handler.ts +9 -3
- package/corpus/core/src/styles/agent-native.css +40 -0
- package/corpus/core/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/core/src/templates/default/.agents/skills/real-time-collab/SKILL.md +35 -13
- package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/core/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +35 -13
- package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +12 -8
- package/corpus/templates/analytics/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/analytics/AGENTS.md +7 -2
- package/corpus/templates/analytics/actions/export-dashboard-panel-to-google-sheet.ts +144 -0
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +7 -88
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +34 -37
- package/corpus/templates/analytics/app/global.css +0 -4
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +2 -7
- package/corpus/templates/analytics/app/i18n-data.ts +107 -0
- package/corpus/templates/analytics/app/lib/data-source-status.ts +9 -0
- package/corpus/templates/analytics/app/lib/sql-query.ts +1 -2
- package/corpus/templates/analytics/app/pages/Ask.tsx +2 -7
- package/corpus/templates/analytics/app/pages/DataSources.tsx +48 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +59 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +2 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/report-panel-window.ts +3 -1
- package/corpus/templates/analytics/changelog/2026-07-22-clear-background-chat-progress.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-22-daily-dashboard-email-query-batches.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-22-export-table-panels-directly-to-google-sheets.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-22-recent-chats-can-expand-in-the-sidebar.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +8 -8
- package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +31 -0
- package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +3 -5
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +12 -0
- package/corpus/templates/analytics/server/lib/first-party-dashboard-repair.ts +3 -5
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +150 -21
- package/corpus/templates/analytics/server/lib/google-sheets-export.ts +188 -0
- package/corpus/templates/analytics/server/lib/provider-api.ts +7 -2
- package/corpus/templates/analytics/server/plugins/collab.ts +6 -3
- package/corpus/templates/analytics/shared/sql-query-limits.ts +1 -0
- package/corpus/templates/assets/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/templates/assets/.agents/skills/real-time-collab/SKILL.md +35 -13
- package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/assets/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +54 -62
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +2 -6
- package/corpus/templates/assets/app/routes/_index.tsx +8 -5
- package/corpus/templates/assets/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-22-recent-chats-expand-from-a-compact-list.md +6 -0
- package/corpus/templates/brain/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/brain/actions/rotate-source-ingest-token.ts +77 -0
- package/corpus/templates/brain/app/components/layout/Layout.tsx +5 -1
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +38 -51
- package/corpus/templates/brain/app/hooks/use-navigation-state.ts +2 -9
- package/corpus/templates/brain/app/i18n-data.ts +120 -0
- package/corpus/templates/brain/app/routes/sources.tsx +156 -5
- package/corpus/templates/brain/changelog/2026-07-22-chat-runs-always-end-with-an-answer.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-22-clear-background-chat-progress.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-22-long-chat-titles-stay-inside-the-sidebar.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-22-new-clips-and-webhook-sources-now-show-their-secure-connecti.md +6 -0
- package/corpus/templates/brain/jobs/emit-netlify-brain-queue-cron.ts +125 -0
- package/corpus/templates/brain/netlify.toml +5 -1
- package/corpus/templates/brain/scripts/configure-prod-clips-source.ts +50 -0
- package/corpus/templates/brain/server/lib/capture-sanitization.ts +64 -6
- package/corpus/templates/brain/server/plugins/agent-chat.ts +4 -0
- package/corpus/templates/brain/server/plugins/brain-jobs.ts +1 -0
- package/corpus/templates/brain/server/routes/api/brain/queue/run.post.ts +55 -0
- package/corpus/templates/calendar/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -0
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +43 -67
- package/corpus/templates/chat/app/hooks/use-navigation-state.ts +2 -7
- package/corpus/templates/chat/app/routes/_index.tsx +8 -5
- package/corpus/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
- package/corpus/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
- package/corpus/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
- package/corpus/templates/clips/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/templates/clips/AGENTS.md +9 -1
- package/corpus/templates/clips/actions/export-to-brain.ts +82 -6
- package/corpus/templates/clips/actions/request-transcript.ts +25 -14
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +12 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
- package/corpus/templates/clips/changelog/2026-07-22-clips-transcripts-now-retry-brain-export.md +5 -0
- package/corpus/templates/clips/changelog/2026-07-22-german-umlauts-and-other-non-ascii-characters-now-paste-corr.md +6 -0
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +26 -3
- package/corpus/templates/clips/jobs/emit-netlify-brain-export-cron.ts +118 -0
- package/corpus/templates/clips/netlify.toml +4 -1
- package/corpus/templates/clips/scripts/configure-prod-brain-export.ts +92 -0
- package/corpus/templates/clips/server/jobs/brain-export.ts +182 -0
- package/corpus/templates/clips/server/lib/brain-export-state.ts +42 -0
- package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +5 -1
- package/corpus/templates/clips/server/plugins/calendar-jobs.ts +2 -0
- package/corpus/templates/clips/server/register-secrets.ts +42 -0
- package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +10 -1
- package/corpus/templates/clips/server/routes/api/clips/brain-export/run.post.ts +45 -0
- package/corpus/templates/content/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +566 -299
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +14 -0
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +1 -1
- package/corpus/templates/content/app/i18n-data.ts +90 -0
- package/corpus/templates/content/server/plugins/collab.ts +1 -1
- package/corpus/templates/crm/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/templates/crm/actions/_crm-data-program-actions.ts +15 -0
- package/corpus/templates/crm/actions/get-crm-dashboard-panel.ts +2 -0
- package/corpus/templates/crm/actions/run.ts +2 -6
- package/corpus/templates/crm/actions/update-crm-record.ts +4 -2
- package/corpus/templates/crm/app/components/layout/CrmLayout.tsx +6 -0
- package/corpus/templates/crm/changelog/2026-07-22-crm-field-validation-now-explains-which-fields-cannot-be-edi.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-22-fixed-hubspot-contact-syncs-missing-records-updated-after-th.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-22-fixed-pipeline-dashboard-panels-so-their-opportunity-data-lo.md +6 -0
- package/corpus/templates/crm/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
- package/corpus/templates/crm/server/crm/hubspot-adapter.ts +4 -1
- package/corpus/templates/design/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/design/server/plugins/collab.ts +11 -8
- package/corpus/templates/dispatch/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/templates/forms/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/forms/app/components/layout/Layout.tsx +1 -0
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/forms/app/hooks/use-navigation-state.ts +2 -9
- package/corpus/templates/forms/app/root.tsx +1 -6
- package/corpus/templates/forms/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
- package/corpus/templates/macros/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/templates/mail/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +2 -2
- package/corpus/templates/plan/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/plan/app/components/layout/Layout.tsx +5 -1
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +57 -72
- package/corpus/templates/plan/app/hooks/use-navigation-state.ts +2 -9
- package/corpus/templates/plan/app/lib/create-plan-routing.ts +2 -2
- package/corpus/templates/plan/app/pages/PlanChatPage.tsx +0 -1
- package/corpus/templates/plan/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
- package/corpus/templates/plan/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
- package/corpus/templates/plan/changelog/2026-07-22-recent-chats-and-sidebar-controls-are-more-compact.md +6 -0
- package/corpus/templates/plan/server/plugins/collab.ts +5 -2
- package/corpus/templates/slides/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/slides/server/plugins/collab.ts +11 -8
- package/corpus/templates/tasks/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/corpus/templates/tasks/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +5 -22
- package/corpus/templates/tasks/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
- package/corpus/toolkit/CHANGELOG.md +7 -0
- package/corpus/toolkit/README.md +12 -3
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/src/chat-history/ChatHistoryRail.tsx +89 -0
- package/corpus/toolkit/src/chat-history/index.ts +5 -0
- package/corpus/toolkit/src/chat-history.css +59 -0
- package/corpus/toolkit/src/composer/RealtimeVoiceMode.tsx +106 -36
- package/corpus/toolkit/src/composer/VoiceButton.tsx +49 -0
- package/corpus/toolkit/src/composer/realtime-voice-audio-level.ts +2 -1
- package/corpus/toolkit/src/composer/useRealtimeVoiceMode.tsx +5 -3
- package/corpus/toolkit/src/styles.css +35 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +13 -3
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/doctor.d.ts +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +5 -2
- package/dist/cli/doctor.js.map +1 -1
- package/dist/client/AgentPanel.d.ts +1 -0
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +38 -18
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +55 -20
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/FeedbackButton.d.ts +4 -2
- package/dist/client/FeedbackButton.d.ts.map +1 -1
- package/dist/client/FeedbackButton.js +18 -2
- package/dist/client/FeedbackButton.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +2 -3
- 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 +11 -3
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/agent-page/AgentTabFrame.d.ts +3 -1
- package/dist/client/agent-page/AgentTabFrame.d.ts.map +1 -1
- package/dist/client/agent-page/AgentTabFrame.js +3 -2
- package/dist/client/agent-page/AgentTabFrame.js.map +1 -1
- package/dist/client/agent-page/AgentTabsPage.d.ts +2 -0
- package/dist/client/agent-page/AgentTabsPage.d.ts.map +1 -1
- package/dist/client/agent-page/AgentTabsPage.js +26 -15
- package/dist/client/agent-page/AgentTabsPage.js.map +1 -1
- package/dist/client/analytics.d.ts +10 -0
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/chat/message-components.d.ts +6 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +17 -11
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +11 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +16 -17
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/chat/tool-render-registry.d.ts +2 -0
- package/dist/client/chat/tool-render-registry.d.ts.map +1 -1
- package/dist/client/chat/tool-render-registry.js.map +1 -1
- package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -1
- package/dist/client/resources/McpConnectionSuggestion.js +15 -1
- package/dist/client/resources/McpConnectionSuggestion.js.map +1 -1
- package/dist/client/resources/ResourceTree.d.ts.map +1 -1
- package/dist/client/resources/ResourceTree.js +3 -3
- package/dist/client/resources/ResourceTree.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.d.ts +1 -0
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +34 -20
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/client/resources/mcp-connection-resume.d.ts +11 -0
- package/dist/client/resources/mcp-connection-resume.d.ts.map +1 -0
- package/dist/client/resources/mcp-connection-resume.js +95 -0
- package/dist/client/resources/mcp-connection-resume.js.map +1 -0
- package/dist/client/resources/mcp-integration-catalog.d.ts +1 -0
- package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.js +63 -8
- package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
- package/dist/client/resources/mcp-integration-logos.d.ts.map +1 -1
- package/dist/client/resources/mcp-integration-logos.js +4 -0
- package/dist/client/resources/mcp-integration-logos.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +5 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +39 -13
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-db-sync.d.ts +4 -3
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +332 -21
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/collab/client.d.ts.map +1 -1
- package/dist/collab/client.js +26 -2
- package/dist/collab/client.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/connections/catalog.d.ts +1 -1
- package/dist/connections/catalog.d.ts.map +1 -1
- package/dist/connections/catalog.js +7 -1
- package/dist/connections/catalog.js.map +1 -1
- package/dist/db/ddl-guard.d.ts +7 -2
- package/dist/db/ddl-guard.d.ts.map +1 -1
- package/dist/db/ddl-guard.js +21 -9
- package/dist/db/ddl-guard.js.map +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +25 -2
- package/dist/deploy/build.js.map +1 -1
- package/dist/eject/remote-mcp-presets.d.ts.map +1 -1
- package/dist/eject/remote-mcp-presets.js +33 -3
- package/dist/eject/remote-mcp-presets.js.map +1 -1
- package/dist/guards/explicit-collab-access.d.ts +8 -0
- package/dist/guards/explicit-collab-access.d.ts.map +1 -0
- package/dist/guards/explicit-collab-access.js +214 -0
- package/dist/guards/explicit-collab-access.js.map +1 -0
- package/dist/guards/index.d.ts +1 -0
- package/dist/guards/index.d.ts.map +1 -1
- package/dist/guards/index.js +1 -0
- package/dist/guards/index.js.map +1 -1
- package/dist/localization/default-messages.d.ts +43 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +48 -5
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/mcp/connect-route.d.ts.map +1 -1
- package/dist/mcp/connect-route.js +34 -48
- package/dist/mcp/connect-route.js.map +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
- package/dist/provider-api/actions/provider-api.d.ts +7 -7
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/provider-api/index.js +1 -1
- package/dist/provider-api/index.js.map +1 -1
- package/dist/realtime-protocol.d.ts +40 -0
- package/dist/realtime-protocol.d.ts.map +1 -0
- package/dist/realtime-protocol.js +69 -0
- package/dist/realtime-protocol.js.map +1 -0
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/collab-plugin.d.ts +34 -3
- package/dist/server/collab-plugin.d.ts.map +1 -1
- package/dist/server/collab-plugin.js +65 -12
- package/dist/server/collab-plugin.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +3 -0
- package/dist/server/core-routes-plugin.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.map +1 -1
- package/dist/server/poll-events.d.ts +7 -1
- package/dist/server/poll-events.d.ts.map +1 -1
- package/dist/server/poll-events.js +24 -7
- package/dist/server/poll-events.js.map +1 -1
- package/dist/server/poll.d.ts +223 -42
- package/dist/server/poll.d.ts.map +1 -1
- package/dist/server/poll.js +849 -737
- package/dist/server/poll.js.map +1 -1
- package/dist/server/realtime-token.d.ts +34 -0
- package/dist/server/realtime-token.d.ts.map +1 -0
- package/dist/server/realtime-token.js +85 -0
- package/dist/server/realtime-token.js.map +1 -0
- package/dist/server/sentry-config.d.ts +12 -0
- package/dist/server/sentry-config.d.ts.map +1 -1
- package/dist/server/sentry-config.js +34 -0
- package/dist/server/sentry-config.js.map +1 -1
- package/dist/server/short-lived-token.d.ts +46 -0
- package/dist/server/short-lived-token.d.ts.map +1 -1
- package/dist/server/short-lived-token.js +104 -0
- package/dist/server/short-lived-token.js.map +1 -1
- package/dist/server/sse.d.ts +1 -1
- package/dist/server/sse.d.ts.map +1 -1
- package/dist/server/sse.js +4 -0
- package/dist/server/sse.js.map +1 -1
- package/dist/server/ssr-handler.d.ts.map +1 -1
- package/dist/server/ssr-handler.js +5 -3
- package/dist/server/ssr-handler.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/styles/agent-native.css +40 -0
- package/dist/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/dist/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/dist/templates/chat/app/components/layout/Layout.tsx +1 -0
- package/dist/templates/chat/app/components/layout/Sidebar.tsx +43 -67
- package/dist/templates/chat/app/hooks/use-navigation-state.ts +2 -7
- package/dist/templates/chat/app/routes/_index.tsx +8 -5
- package/dist/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
- package/dist/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
- package/dist/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
- package/dist/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/dist/templates/default/.agents/skills/real-time-collab/SKILL.md +35 -13
- package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/dist/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/dist/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +35 -13
- package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +12 -8
- package/docs/AGENTS.md +1 -1
- package/docs/content/agent-mentions.mdx +2 -2
- package/docs/content/{workspace.mdx → agent-resources.mdx} +1 -7
- package/docs/content/agent-teams.mdx +4 -4
- package/docs/content/dispatch.mdx +10 -10
- package/docs/content/drop-in-agent.mdx +4 -4
- package/docs/content/external-agents.mdx +1 -1
- package/docs/content/frames.mdx +1 -1
- package/docs/content/integrations.mdx +13 -2
- package/docs/content/key-concepts.mdx +2 -2
- package/docs/content/mcp-clients.mdx +7 -1
- package/docs/content/multi-app-workspace.mdx +3 -3
- package/docs/content/real-time-collaboration.mdx +33 -15
- package/docs/content/recurring-jobs.mdx +7 -7
- package/docs/content/skills-guide.mdx +4 -4
- package/docs/content/template-brain.mdx +1 -1
- package/docs/content/toolkit-collaboration.mdx +1 -1
- package/docs/content/using-your-agent.mdx +1 -1
- package/docs/content/what-is-agent-native.mdx +4 -4
- package/docs/content/workspace-management.mdx +4 -4
- package/package.json +6 -3
- package/src/agent/production-agent.ts +13 -3
- package/src/cli/doctor.ts +6 -1
- package/src/client/AgentPanel.tsx +79 -41
- package/src/client/AssistantChat.tsx +71 -25
- package/src/client/FeedbackButton.tsx +33 -7
- package/src/client/MultiTabAssistantChat.tsx +6 -8
- package/src/client/agent-chat-adapter.ts +11 -2
- package/src/client/agent-page/AgentTabFrame.tsx +22 -3
- package/src/client/agent-page/AgentTabsPage.tsx +34 -19
- package/src/client/analytics.ts +7 -0
- package/src/client/chat/message-components.tsx +43 -16
- package/src/client/chat/tool-call-display.tsx +30 -18
- package/src/client/chat/tool-render-registry.tsx +2 -0
- package/src/client/resources/McpConnectionSuggestion.tsx +19 -2
- package/src/client/resources/ResourceTree.tsx +7 -10
- package/src/client/resources/ResourcesPanel.tsx +33 -70
- package/src/client/resources/mcp-connection-resume.ts +107 -0
- package/src/client/resources/mcp-integration-catalog.ts +65 -12
- package/src/client/resources/mcp-integration-logos.ts +4 -0
- package/src/client/sse-event-processor.ts +50 -14
- package/src/client/use-db-sync.ts +370 -21
- package/src/collab/client.ts +26 -2
- package/src/connections/catalog.ts +7 -1
- package/src/db/ddl-guard.ts +49 -10
- package/src/deploy/build.ts +25 -2
- package/src/eject/remote-mcp-presets.ts +37 -5
- package/src/guards/explicit-collab-access.ts +229 -0
- package/src/guards/index.ts +1 -0
- package/src/localization/default-messages.ts +54 -5
- package/src/mcp/connect-route.ts +34 -51
- package/src/provider-api/index.ts +1 -1
- package/src/realtime-protocol.ts +92 -0
- package/src/server/collab-plugin.ts +137 -18
- package/src/server/core-routes-plugin.ts +7 -0
- package/src/server/index.ts +2 -0
- package/src/server/poll-events.ts +29 -8
- package/src/server/poll.ts +1123 -865
- package/src/server/realtime-token.ts +106 -0
- package/src/server/sentry-config.ts +40 -0
- package/src/server/short-lived-token.ts +156 -0
- package/src/server/sse.ts +15 -0
- package/src/server/ssr-handler.ts +9 -3
- package/src/styles/agent-native.css +40 -0
- package/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/src/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/src/templates/chat/app/components/layout/Layout.tsx +1 -0
- package/src/templates/chat/app/components/layout/Sidebar.tsx +43 -67
- package/src/templates/chat/app/hooks/use-navigation-state.ts +2 -7
- package/src/templates/chat/app/routes/_index.tsx +8 -5
- package/src/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +6 -0
- package/src/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +6 -0
- package/src/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +6 -0
- package/src/templates/default/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/src/templates/default/.agents/skills/real-time-collab/SKILL.md +35 -13
- package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/src/templates/headless/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -1
- package/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +35 -13
- package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
- package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +12 -8
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Realtime subscribe-token mint endpoint.
|
|
3
|
+
*
|
|
4
|
+
* `GET /_agent-native/realtime-token` — the one Netlify request per page load
|
|
5
|
+
* that the hosted Realtime Gateway path needs. The SSR HTML/`.data` shell is a
|
|
6
|
+
* single impersonal, CDN-cached document (`guard:ssr-cache-shell`), so a
|
|
7
|
+
* per-visitor token cannot be baked into the page; the client mints it here
|
|
8
|
+
* after load. Same-origin + session-gated; sessionless requests get 401.
|
|
9
|
+
*
|
|
10
|
+
* The signed token binds this app's Builder project id (the gateway channel)
|
|
11
|
+
* and carries the app's own end-user identity (`owner` = session email, `orgId`
|
|
12
|
+
* = framework org) — the exact tuple `recordChange` stamps onto `sync_events`
|
|
13
|
+
* and the gateway feeds to `canSeeChangeForUser`. It is signed with the app's
|
|
14
|
+
* per-project HMAC secret, injected as a reserved env var at provision time.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
defineEventHandler,
|
|
19
|
+
getMethod,
|
|
20
|
+
type H3Event,
|
|
21
|
+
setResponseHeader,
|
|
22
|
+
setResponseStatus,
|
|
23
|
+
} from "h3";
|
|
24
|
+
|
|
25
|
+
import { getOrgContext } from "../org/context.js";
|
|
26
|
+
import { getSession } from "./auth.js";
|
|
27
|
+
import { resolveBuilderBranchProjectId } from "./builder-browser.js";
|
|
28
|
+
import { runWithRequestContext } from "./request-context.js";
|
|
29
|
+
import { isSameOriginRequest } from "./request-origin.js";
|
|
30
|
+
import { signRealtimeSubscribeToken } from "./short-lived-token.js";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Reserved env var holding the app's per-project HMAC secret. Injected by the
|
|
34
|
+
* Builder provisioning path (`SYSTEM_RESERVED_KEYS` + prod allowlist); see the
|
|
35
|
+
* Agent-Native Realtime Sync tech spec.
|
|
36
|
+
*/
|
|
37
|
+
export const REALTIME_HMAC_SECRET_ENV = "AGENT_NATIVE_REALTIME_HMAC_SECRET";
|
|
38
|
+
|
|
39
|
+
/** Short TTL — validated at connect; the gateway rotates over the stream. */
|
|
40
|
+
const REALTIME_TOKEN_TTL_SECONDS = 600;
|
|
41
|
+
|
|
42
|
+
export function getRealtimeSigningSecret(): string | undefined {
|
|
43
|
+
return process.env[REALTIME_HMAC_SECRET_ENV]?.trim() || undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function createRealtimeTokenHandler() {
|
|
47
|
+
return defineEventHandler(async (event: H3Event) => {
|
|
48
|
+
// Identity-bearing token, valid ~10 min — never cacheable by the browser or
|
|
49
|
+
// any intermediary. Set once up front so every return path carries it.
|
|
50
|
+
setResponseHeader(event, "Cache-Control", "private, no-store");
|
|
51
|
+
|
|
52
|
+
if (getMethod(event) !== "GET") {
|
|
53
|
+
setResponseStatus(event, 405);
|
|
54
|
+
return { error: "Method not allowed" };
|
|
55
|
+
}
|
|
56
|
+
if (!isSameOriginRequest(event)) {
|
|
57
|
+
setResponseStatus(event, 403);
|
|
58
|
+
return { error: "Cross-origin request rejected" };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const session = await getSession(event).catch(() => null);
|
|
62
|
+
if (!session?.email) {
|
|
63
|
+
setResponseStatus(event, 401);
|
|
64
|
+
return { error: "Authentication required" };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const orgCtx = await getOrgContext(event).catch(() => null);
|
|
68
|
+
const requestContext = {
|
|
69
|
+
userEmail: session.email,
|
|
70
|
+
orgId: orgCtx?.orgId ?? session.orgId,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// The scoped-secret fallback inside resolveBuilderBranchProjectId reads the
|
|
74
|
+
// request-context ALS (resolveSecret -> getRequestUserEmail); without it the
|
|
75
|
+
// user/org/workspace scopes silently no-op and only env vars resolve. Wrap
|
|
76
|
+
// the resolution like google-realtime-session.ts does.
|
|
77
|
+
return runWithRequestContext(requestContext, async () => {
|
|
78
|
+
// Async resolver so hosted apps whose project id lives in a
|
|
79
|
+
// request-scoped app/org/workspace secret (not an env var) also work —
|
|
80
|
+
// the sync env-only lookup would 404 them and silently drop the gateway.
|
|
81
|
+
const projectId = await resolveBuilderBranchProjectId();
|
|
82
|
+
const secret = getRealtimeSigningSecret();
|
|
83
|
+
if (!projectId || !secret) {
|
|
84
|
+
// Hosted realtime isn't provisioned for this app. 404 lets the client
|
|
85
|
+
// fall back to the app's own /_agent-native/poll without treating it
|
|
86
|
+
// as an auth failure.
|
|
87
|
+
setResponseStatus(event, 404);
|
|
88
|
+
return { error: "Realtime gateway not configured" };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const token = signRealtimeSubscribeToken(
|
|
92
|
+
{
|
|
93
|
+
projectId,
|
|
94
|
+
owner: session.email,
|
|
95
|
+
orgId: requestContext.orgId,
|
|
96
|
+
ttlSeconds: REALTIME_TOKEN_TTL_SECONDS,
|
|
97
|
+
},
|
|
98
|
+
secret,
|
|
99
|
+
);
|
|
100
|
+
const expiresAt = new Date(
|
|
101
|
+
Date.now() + REALTIME_TOKEN_TTL_SECONDS * 1000,
|
|
102
|
+
).toISOString();
|
|
103
|
+
return { token, expiresAt, ttlSeconds: REALTIME_TOKEN_TTL_SECONDS };
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
}
|
|
@@ -71,3 +71,43 @@ export function getSentryClientConfigScript(): string | null {
|
|
|
71
71
|
"</script>",
|
|
72
72
|
].join("");
|
|
73
73
|
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Hosted Realtime Gateway config for the client, or null for the in-process
|
|
77
|
+
* (local) transport. Values are env-derived and identical for every visitor,
|
|
78
|
+
* so this is safe inside the CDN-cached SSR shell (see `guard:ssr-cache-shell`).
|
|
79
|
+
* The per-user subscribe token is NOT here — it is minted client-side after
|
|
80
|
+
* load from `/_agent-native/realtime-token`.
|
|
81
|
+
*/
|
|
82
|
+
export function resolveRealtimeClientConfig(): {
|
|
83
|
+
transport: "hosted";
|
|
84
|
+
gatewayBaseUrl: string;
|
|
85
|
+
} | null {
|
|
86
|
+
// Fail closed: emit hosted config only when BOTH the transport is hosted AND
|
|
87
|
+
// an explicit gateway URL is set. No production default — this ships into the
|
|
88
|
+
// CDN-cached shell served to every visitor, so a mis-set staging/preview/
|
|
89
|
+
// self-hosted env must stay on the local transport rather than silently
|
|
90
|
+
// point every browser at api.builder.io. This gating is mirrored byte-for-
|
|
91
|
+
// byte in the worker emitter in `deploy/build.ts` (kept in sync deliberately).
|
|
92
|
+
if (firstNonEmpty(process.env.AGENT_NATIVE_REALTIME_TRANSPORT) !== "hosted") {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const gatewayBaseUrl = firstNonEmpty(
|
|
96
|
+
process.env.AGENT_NATIVE_REALTIME_GATEWAY_URL,
|
|
97
|
+
);
|
|
98
|
+
if (!gatewayBaseUrl) return null;
|
|
99
|
+
return { transport: "hosted", gatewayBaseUrl };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function getRealtimeClientConfigScript(): string | null {
|
|
103
|
+
const realtime = resolveRealtimeClientConfig();
|
|
104
|
+
if (!realtime) return null;
|
|
105
|
+
|
|
106
|
+
return [
|
|
107
|
+
"<script data-agent-native-realtime-config>",
|
|
108
|
+
"window.__AGENT_NATIVE_CONFIG__=Object.assign({},window.__AGENT_NATIVE_CONFIG__,",
|
|
109
|
+
JSON.stringify({ realtime }),
|
|
110
|
+
");",
|
|
111
|
+
"</script>",
|
|
112
|
+
].join("");
|
|
113
|
+
}
|
|
@@ -168,3 +168,159 @@ export function verifyShortLivedToken(
|
|
|
168
168
|
|
|
169
169
|
return { ok: true, viewerEmail: claims.viewerEmail };
|
|
170
170
|
}
|
|
171
|
+
|
|
172
|
+
// ── Realtime subscribe tokens ────────────────────────────────────────────────
|
|
173
|
+
//
|
|
174
|
+
// An identity-bearing extension of the same HMAC discipline, used by the hosted
|
|
175
|
+
// Realtime Gateway. Differs from the media token above in three ways the gateway
|
|
176
|
+
// depends on:
|
|
177
|
+
// 1. It is signed with a caller-supplied PER-PROJECT key (the app's HMAC
|
|
178
|
+
// secret), not the single deployment-wide `getSigningKey()`. The app server
|
|
179
|
+
// mints with it; the gateway (a second issuer for stream rotation) verifies
|
|
180
|
+
// and re-mints with the same per-project key. A token minted for project A
|
|
181
|
+
// fails signature verification against project B's key.
|
|
182
|
+
// 2. It carries authorization-bearing identity (`owner`/`orgId`) that the
|
|
183
|
+
// gateway feeds to `canSeeChangeForUser` — the media token's `viewerEmail`
|
|
184
|
+
// is audit-only by contract, so a new field is required, not repurposed.
|
|
185
|
+
// 3. It binds `projectId` as the channel and stamps a `typ` discriminator, so
|
|
186
|
+
// it is verified against the connect channel and cannot be replayed as a
|
|
187
|
+
// media token (or vice-versa) even if keys ever overlapped.
|
|
188
|
+
|
|
189
|
+
/** Payload `typ` discriminator for realtime subscribe tokens. */
|
|
190
|
+
export const REALTIME_SUBSCRIBE_TOKEN_TYPE = "rt-subscribe";
|
|
191
|
+
const DEFAULT_REALTIME_TTL_SECONDS = 600;
|
|
192
|
+
|
|
193
|
+
/** Inputs for {@link signRealtimeSubscribeToken}. */
|
|
194
|
+
export interface RealtimeSubscribeClaims {
|
|
195
|
+
/** Channel — one Neon project per app. Verified at connect, not just carried. */
|
|
196
|
+
projectId: string;
|
|
197
|
+
/**
|
|
198
|
+
* App end-user session email (NOT a Builder.io account — see the tech spec).
|
|
199
|
+
* Fed to `canSeeChangeForUser`. Present in v0.
|
|
200
|
+
*/
|
|
201
|
+
owner?: string;
|
|
202
|
+
/** Framework org id for the app's end-user. */
|
|
203
|
+
orgId?: string;
|
|
204
|
+
/** Override default TTL (seconds). */
|
|
205
|
+
ttlSeconds?: number;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
interface DecodedRealtimeClaims {
|
|
209
|
+
typ: string;
|
|
210
|
+
projectId: string;
|
|
211
|
+
owner?: string;
|
|
212
|
+
orgId?: string;
|
|
213
|
+
exp: number;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Result of {@link verifyRealtimeSubscribeToken}. On success it returns the
|
|
218
|
+
* identity claims the gateway uses to scope delivery.
|
|
219
|
+
*/
|
|
220
|
+
export type RealtimeVerifyResult =
|
|
221
|
+
| {
|
|
222
|
+
ok: true;
|
|
223
|
+
projectId: string;
|
|
224
|
+
owner?: string;
|
|
225
|
+
orgId?: string;
|
|
226
|
+
exp: number;
|
|
227
|
+
}
|
|
228
|
+
| { ok: false; reason: string };
|
|
229
|
+
|
|
230
|
+
function hmacB64(payloadStr: string, key: string): string {
|
|
231
|
+
return base64UrlEncode(
|
|
232
|
+
crypto.createHmac("sha256", key).update(payloadStr).digest(),
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function timingSafeEqualB64(sig: string, expected: string): boolean {
|
|
237
|
+
const sigBuf = Buffer.from(sig, "utf8");
|
|
238
|
+
const expBuf = Buffer.from(expected, "utf8");
|
|
239
|
+
if (sigBuf.length !== expBuf.length) {
|
|
240
|
+
crypto.timingSafeEqual(expBuf, expBuf); // burn ~equal cycles
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
return crypto.timingSafeEqual(sigBuf, expBuf);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Mint a realtime subscribe token for `claims.projectId`, signed with the
|
|
248
|
+
* app's per-project `key`. Safe to place on the connect query string (short
|
|
249
|
+
* TTL, single-purpose, channel-bound). Verified by
|
|
250
|
+
* {@link verifyRealtimeSubscribeToken} at connect.
|
|
251
|
+
*/
|
|
252
|
+
export function signRealtimeSubscribeToken(
|
|
253
|
+
claims: RealtimeSubscribeClaims,
|
|
254
|
+
key: string,
|
|
255
|
+
): string {
|
|
256
|
+
if (!key) throw new Error("signRealtimeSubscribeToken requires a key");
|
|
257
|
+
// Fail closed: a token with neither owner nor orgId carries no authorization
|
|
258
|
+
// identity, so canSeeChangeForUser would only ever match global/unowned
|
|
259
|
+
// events. Every issuer (mint endpoint + the gateway's rotation re-mint) must
|
|
260
|
+
// supply at least one identity claim.
|
|
261
|
+
if (!claims.owner && !claims.orgId) {
|
|
262
|
+
throw new Error(
|
|
263
|
+
"signRealtimeSubscribeToken requires an owner or orgId claim",
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
const ttl = claims.ttlSeconds ?? DEFAULT_REALTIME_TTL_SECONDS;
|
|
267
|
+
const payload: DecodedRealtimeClaims = {
|
|
268
|
+
typ: REALTIME_SUBSCRIBE_TOKEN_TYPE,
|
|
269
|
+
projectId: claims.projectId,
|
|
270
|
+
exp: Math.floor(Date.now() / 1000) + ttl,
|
|
271
|
+
};
|
|
272
|
+
if (claims.owner) payload.owner = claims.owner;
|
|
273
|
+
if (claims.orgId) payload.orgId = claims.orgId;
|
|
274
|
+
|
|
275
|
+
const payloadStr = base64UrlEncode(JSON.stringify(payload));
|
|
276
|
+
return `${payloadStr}.${hmacB64(payloadStr, key)}`;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Verify a realtime subscribe token against the app's per-project `key` and the
|
|
281
|
+
* connect channel `projectId`. Returns the identity claims only when the shape,
|
|
282
|
+
* signature (constant-time), `typ`, `exp`, and `projectId` binding all hold.
|
|
283
|
+
*/
|
|
284
|
+
export function verifyRealtimeSubscribeToken(
|
|
285
|
+
token: string,
|
|
286
|
+
expected: { projectId: string; key: string },
|
|
287
|
+
): RealtimeVerifyResult {
|
|
288
|
+
if (!expected.key) return { ok: false, reason: "no_key" };
|
|
289
|
+
if (typeof token !== "string" || !token.includes(".")) {
|
|
290
|
+
return { ok: false, reason: "malformed" };
|
|
291
|
+
}
|
|
292
|
+
const [payloadStr, sig] = token.split(".", 2);
|
|
293
|
+
if (!payloadStr || !sig) return { ok: false, reason: "malformed" };
|
|
294
|
+
|
|
295
|
+
if (!timingSafeEqualB64(sig, hmacB64(payloadStr, expected.key))) {
|
|
296
|
+
return { ok: false, reason: "bad_signature" };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
let claims: DecodedRealtimeClaims;
|
|
300
|
+
try {
|
|
301
|
+
claims = JSON.parse(base64UrlDecode(payloadStr).toString("utf8"));
|
|
302
|
+
} catch {
|
|
303
|
+
return { ok: false, reason: "bad_payload" };
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (claims.typ !== REALTIME_SUBSCRIBE_TOKEN_TYPE) {
|
|
307
|
+
return { ok: false, reason: "wrong_type" };
|
|
308
|
+
}
|
|
309
|
+
if (typeof claims.exp !== "number") {
|
|
310
|
+
return { ok: false, reason: "bad_payload" };
|
|
311
|
+
}
|
|
312
|
+
if (claims.exp * 1000 < Date.now()) {
|
|
313
|
+
return { ok: false, reason: "expired" };
|
|
314
|
+
}
|
|
315
|
+
if (claims.projectId !== expected.projectId) {
|
|
316
|
+
return { ok: false, reason: "wrong_project" };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return {
|
|
320
|
+
ok: true,
|
|
321
|
+
projectId: claims.projectId,
|
|
322
|
+
owner: claims.owner,
|
|
323
|
+
orgId: claims.orgId,
|
|
324
|
+
exp: claims.exp,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import { defineEventHandler, createEventStream } from "h3";
|
|
2
2
|
|
|
3
|
+
// Re-export the wire protocol so server consumers (the hosted Realtime Gateway
|
|
4
|
+
// in ai-services) get the frame contract from the same `./server/sse` subpath.
|
|
5
|
+
// The browser client imports it directly from `../realtime-protocol.js`.
|
|
6
|
+
export {
|
|
7
|
+
buildHandshakeFrame,
|
|
8
|
+
parseHandshakeFrame,
|
|
9
|
+
parseTokenFrame,
|
|
10
|
+
REALTIME_CAP_NO_AWARENESS,
|
|
11
|
+
REALTIME_PROTOCOL_VERSION,
|
|
12
|
+
REALTIME_SSE_HANDSHAKE_EVENT,
|
|
13
|
+
REALTIME_SSE_TOKEN_EVENT,
|
|
14
|
+
type RealtimeHandshake,
|
|
15
|
+
type RealtimeTokenFrame,
|
|
16
|
+
} from "../realtime-protocol.js";
|
|
17
|
+
|
|
3
18
|
/** Any object with on/off methods (compatible with EventEmitter, TypedEventEmitter, etc.). */
|
|
4
19
|
interface EventLike {
|
|
5
20
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -37,7 +37,10 @@ import {
|
|
|
37
37
|
} from "./app-base-path.js";
|
|
38
38
|
import { captureError } from "./capture-error.js";
|
|
39
39
|
import { runWithRequestContext } from "./request-context.js";
|
|
40
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
getRealtimeClientConfigScript,
|
|
42
|
+
getSentryClientConfigScript,
|
|
43
|
+
} from "./sentry-config.js";
|
|
41
44
|
|
|
42
45
|
export {
|
|
43
46
|
DEFAULT_SSR_CACHE_HEADERS,
|
|
@@ -342,7 +345,10 @@ async function rewriteMountedResponse(
|
|
|
342
345
|
pathname: string,
|
|
343
346
|
requestUrl: string,
|
|
344
347
|
): Promise<Response> {
|
|
345
|
-
const
|
|
348
|
+
const clientConfigScript =
|
|
349
|
+
[getSentryClientConfigScript(), getRealtimeClientConfigScript()]
|
|
350
|
+
.filter(Boolean)
|
|
351
|
+
.join("") || null;
|
|
346
352
|
const headers = new Headers(response.headers);
|
|
347
353
|
applyDefaultSsrCacheHeader(headers, response.status, pathname);
|
|
348
354
|
applyDefaultSpeculationRulesHeader(headers, response.status, basePath);
|
|
@@ -377,7 +383,7 @@ async function rewriteMountedResponse(
|
|
|
377
383
|
prefixMountedHtml(html, basePath),
|
|
378
384
|
defaultSocialImageUrl(requestUrl, basePath),
|
|
379
385
|
),
|
|
380
|
-
|
|
386
|
+
clientConfigScript,
|
|
381
387
|
),
|
|
382
388
|
{
|
|
383
389
|
status: response.status,
|
|
@@ -927,7 +927,35 @@
|
|
|
927
927
|
display: inline-block;
|
|
928
928
|
}
|
|
929
929
|
|
|
930
|
+
.agent-tool-call {
|
|
931
|
+
position: relative;
|
|
932
|
+
}
|
|
933
|
+
|
|
930
934
|
@media (prefers-reduced-motion: no-preference) {
|
|
935
|
+
.agent-tool-call--entering {
|
|
936
|
+
animation: agent-tool-call-enter 220ms var(--ease-out-strong) both;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
.agent-tool-call[data-active-tail="true"]::after {
|
|
940
|
+
position: absolute;
|
|
941
|
+
z-index: 1;
|
|
942
|
+
inset: 0 0 auto;
|
|
943
|
+
height: 2rem;
|
|
944
|
+
pointer-events: none;
|
|
945
|
+
content: "";
|
|
946
|
+
background: linear-gradient(
|
|
947
|
+
90deg,
|
|
948
|
+
transparent 0%,
|
|
949
|
+
transparent 38%,
|
|
950
|
+
hsl(var(--foreground) / 0.055) 50%,
|
|
951
|
+
transparent 62%,
|
|
952
|
+
transparent 100%
|
|
953
|
+
);
|
|
954
|
+
background-repeat: repeat;
|
|
955
|
+
background-size: 14rem 100%;
|
|
956
|
+
animation: agent-thinking-shine 2.6s linear infinite;
|
|
957
|
+
}
|
|
958
|
+
|
|
931
959
|
@supports ((background-clip: text) or (-webkit-background-clip: text)) {
|
|
932
960
|
.agent-thinking-indicator__text,
|
|
933
961
|
.agent-running-shimmer {
|
|
@@ -960,6 +988,18 @@
|
|
|
960
988
|
}
|
|
961
989
|
}
|
|
962
990
|
|
|
991
|
+
@keyframes agent-tool-call-enter {
|
|
992
|
+
from {
|
|
993
|
+
opacity: 0;
|
|
994
|
+
transform: translateY(0.375rem);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
to {
|
|
998
|
+
opacity: 1;
|
|
999
|
+
transform: translateY(0);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
963
1003
|
@supports (view-transition-name: agent-native-chat) {
|
|
964
1004
|
.agent-native-chat-view-transition {
|
|
965
1005
|
contain: layout;
|
|
@@ -85,7 +85,10 @@ in the moment of agent use.
|
|
|
85
85
|
approval, generative UI, progress, and screen-state exposure.
|
|
86
86
|
- **Chat history kit**: presentational chat lists and recent-chat rails belong
|
|
87
87
|
in Toolkit; Core keeps thread persistence, agent execution, transport, and
|
|
88
|
-
page-to-sidebar handoff.
|
|
88
|
+
page-to-sidebar handoff. Use Toolkit's `ChatHistoryRail` for the standard
|
|
89
|
+
five-item sidebar preview and a footer row with New chat followed by an
|
|
90
|
+
ellipsis disclosure up to fifteen. Apps inject routing, labels, and domain
|
|
91
|
+
actions.
|
|
89
92
|
- **Agent page kit**: the full-page `/agent` surface (`AgentTabsPage` from
|
|
90
93
|
`@agent-native/core/client`) with Context, Files, Connections, Jobs, and
|
|
91
94
|
Access tabs plus a Personal/Organization scope toggle. The canonical home
|
|
@@ -16,7 +16,7 @@ metadata:
|
|
|
16
16
|
|
|
17
17
|
Collaborative editing uses Yjs CRDT via TipTap. The agent and human users are
|
|
18
18
|
equal participants — both edit the same Y.Doc and changes merge cleanly without
|
|
19
|
-
conflicts. Always
|
|
19
|
+
conflicts. Always choose an explicit `access` mode on `createCollabPlugin`.
|
|
20
20
|
|
|
21
21
|
## How It Works
|
|
22
22
|
|
|
@@ -104,7 +104,16 @@ in **different** regions merge fine through the CRDT.
|
|
|
104
104
|
|
|
105
105
|
## Security
|
|
106
106
|
|
|
107
|
-
### Always
|
|
107
|
+
### Always choose an explicit access mode
|
|
108
|
+
|
|
109
|
+
Inspect the schema and sharing registration before configuring collaboration:
|
|
110
|
+
|
|
111
|
+
- For owned or shareable records, use `mode: "resource"` and the resource type
|
|
112
|
+
registered through `registerShareableResource`.
|
|
113
|
+
- For records deliberately visible to every authenticated user on the
|
|
114
|
+
deployment, use `mode: "all-authenticated"`.
|
|
115
|
+
- Never invent a `resourceType` merely to silence a warning. Resource mode is
|
|
116
|
+
correct only when the matching ownership and sharing model exists.
|
|
108
117
|
|
|
109
118
|
```ts
|
|
110
119
|
// server/plugins/collab.ts
|
|
@@ -114,14 +123,23 @@ export default createCollabPlugin({
|
|
|
114
123
|
table: "documents",
|
|
115
124
|
contentColumn: "content",
|
|
116
125
|
idColumn: "id",
|
|
117
|
-
resourceType: "document",
|
|
126
|
+
access: { mode: "resource", resourceType: "document" },
|
|
118
127
|
});
|
|
119
128
|
```
|
|
120
129
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
130
|
+
For intentionally deployment-wide authenticated collaboration:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
createCollabPlugin({
|
|
134
|
+
table: "todos",
|
|
135
|
+
contentColumn: "content",
|
|
136
|
+
access: { mode: "all-authenticated" },
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Omitting `access` is supported only for legacy compatibility and is deprecated.
|
|
141
|
+
It behaves like `all-authenticated`, logs a warning, and is flagged by Doctor.
|
|
142
|
+
Choose the mode explicitly so access intent is reviewable.
|
|
125
143
|
|
|
126
144
|
Non-owner sharees who have explicit access fall back to state-vector catch-up
|
|
127
145
|
(safe, slightly higher latency). Awareness routes require the same viewer
|
|
@@ -133,7 +151,10 @@ Write routes reject payloads exceeding `maxPayloadBytes` (default 2 MB) with
|
|
|
133
151
|
HTTP 413. Override:
|
|
134
152
|
|
|
135
153
|
```ts
|
|
136
|
-
createCollabPlugin({
|
|
154
|
+
createCollabPlugin({
|
|
155
|
+
access: { mode: "resource", resourceType: "document" },
|
|
156
|
+
maxPayloadBytes: 512 * 1024,
|
|
157
|
+
});
|
|
137
158
|
```
|
|
138
159
|
|
|
139
160
|
## Enabling Collaboration
|
|
@@ -144,7 +165,7 @@ createCollabPlugin({ resourceType: "document", maxPayloadBytes: 512 * 1024 });
|
|
|
144
165
|
pnpm add @tiptap/extension-collaboration @tiptap/extension-collaboration-caret @tiptap/y-tiptap @tiptap/core
|
|
145
166
|
```
|
|
146
167
|
|
|
147
|
-
### 2. Add collab server plugin (with
|
|
168
|
+
### 2. Add collab server plugin (with explicit access)
|
|
148
169
|
|
|
149
170
|
```ts
|
|
150
171
|
// server/plugins/collab.ts
|
|
@@ -154,7 +175,7 @@ export default createCollabPlugin({
|
|
|
154
175
|
table: "documents",
|
|
155
176
|
contentColumn: "content",
|
|
156
177
|
idColumn: "id",
|
|
157
|
-
resourceType: "document",
|
|
178
|
+
access: { mode: "resource", resourceType: "document" },
|
|
158
179
|
});
|
|
159
180
|
```
|
|
160
181
|
|
|
@@ -343,9 +364,10 @@ the whole history — external/agent edits must not wipe the user's undo stack.
|
|
|
343
364
|
|
|
344
365
|
## Common Pitfalls
|
|
345
366
|
|
|
346
|
-
- **Missing `
|
|
347
|
-
|
|
348
|
-
|
|
367
|
+
- **Missing `access`** — Legacy omission behaves like `all-authenticated`, logs
|
|
368
|
+
a startup warning, and is flagged by Doctor. Choose `resource` for records
|
|
369
|
+
backed by ownership/sharing, or explicit `all-authenticated` for intentionally
|
|
370
|
+
deployment-wide records. Never invent a resource type to silence the warning.
|
|
349
371
|
- **Don't pass `content` as a TipTap prop** when Collaboration is enabled —
|
|
350
372
|
Yjs owns the content. Seed via `editor.commands.setContent()` only when the
|
|
351
373
|
Y.XmlFragment is empty.
|
|
@@ -72,7 +72,7 @@ The agent modifies data in SQL, but the UI runs in the browser. SSE bridges same
|
|
|
72
72
|
});
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
`useDbSync` and every `subscribeSyncEvents` subscriber share one `EventSource` and one fallback poll loop per tab — this is how collaborative documents receive doc updates and cursor/awareness events.
|
|
75
|
+
`useDbSync` and every `subscribeSyncEvents` subscriber share one `EventSource` and one fallback poll loop per tab — this is how collaborative documents receive doc updates and cursor/awareness events. Hidden tabs keep that shared transport alive by default: SSE stays connected and active fallback polling relaxes to a 10-second floor (idle polling remains once per minute). Pass `pauseWhenHidden: true` only for a consumer that explicitly must stop all hidden-tab sync.
|
|
76
76
|
|
|
77
77
|
## Which sources to depend on
|
|
78
78
|
|
|
@@ -85,7 +85,10 @@ in the moment of agent use.
|
|
|
85
85
|
approval, generative UI, progress, and screen-state exposure.
|
|
86
86
|
- **Chat history kit**: presentational chat lists and recent-chat rails belong
|
|
87
87
|
in Toolkit; Core keeps thread persistence, agent execution, transport, and
|
|
88
|
-
page-to-sidebar handoff.
|
|
88
|
+
page-to-sidebar handoff. Use Toolkit's `ChatHistoryRail` for the standard
|
|
89
|
+
five-item sidebar preview and a footer row with New chat followed by an
|
|
90
|
+
ellipsis disclosure up to fifteen. Apps inject routing, labels, and domain
|
|
91
|
+
actions.
|
|
89
92
|
- **Agent page kit**: the full-page `/agent` surface (`AgentTabsPage` from
|
|
90
93
|
`@agent-native/core/client`) with Context, Files, Connections, Jobs, and
|
|
91
94
|
Access tabs plus a Personal/Organization scope toggle. The canonical home
|
package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md
CHANGED
|
@@ -85,7 +85,10 @@ in the moment of agent use.
|
|
|
85
85
|
approval, generative UI, progress, and screen-state exposure.
|
|
86
86
|
- **Chat history kit**: presentational chat lists and recent-chat rails belong
|
|
87
87
|
in Toolkit; Core keeps thread persistence, agent execution, transport, and
|
|
88
|
-
page-to-sidebar handoff.
|
|
88
|
+
page-to-sidebar handoff. Use Toolkit's `ChatHistoryRail` for the standard
|
|
89
|
+
five-item sidebar preview and a footer row with New chat followed by an
|
|
90
|
+
ellipsis disclosure up to fifteen. Apps inject routing, labels, and domain
|
|
91
|
+
actions.
|
|
89
92
|
- **Agent page kit**: the full-page `/agent` surface (`AgentTabsPage` from
|
|
90
93
|
`@agent-native/core/client`) with Context, Files, Connections, Jobs, and
|
|
91
94
|
Access tabs plus a Personal/Organization scope toggle. The canonical home
|
|
@@ -16,7 +16,7 @@ metadata:
|
|
|
16
16
|
|
|
17
17
|
Collaborative editing uses Yjs CRDT via TipTap. The agent and human users are
|
|
18
18
|
equal participants — both edit the same Y.Doc and changes merge cleanly without
|
|
19
|
-
conflicts. Always
|
|
19
|
+
conflicts. Always choose an explicit `access` mode on `createCollabPlugin`.
|
|
20
20
|
|
|
21
21
|
## How It Works
|
|
22
22
|
|
|
@@ -104,7 +104,16 @@ in **different** regions merge fine through the CRDT.
|
|
|
104
104
|
|
|
105
105
|
## Security
|
|
106
106
|
|
|
107
|
-
### Always
|
|
107
|
+
### Always choose an explicit access mode
|
|
108
|
+
|
|
109
|
+
Inspect the schema and sharing registration before configuring collaboration:
|
|
110
|
+
|
|
111
|
+
- For owned or shareable records, use `mode: "resource"` and the resource type
|
|
112
|
+
registered through `registerShareableResource`.
|
|
113
|
+
- For records deliberately visible to every authenticated user on the
|
|
114
|
+
deployment, use `mode: "all-authenticated"`.
|
|
115
|
+
- Never invent a `resourceType` merely to silence a warning. Resource mode is
|
|
116
|
+
correct only when the matching ownership and sharing model exists.
|
|
108
117
|
|
|
109
118
|
```ts
|
|
110
119
|
// server/plugins/collab.ts
|
|
@@ -114,14 +123,23 @@ export default createCollabPlugin({
|
|
|
114
123
|
table: "documents",
|
|
115
124
|
contentColumn: "content",
|
|
116
125
|
idColumn: "id",
|
|
117
|
-
resourceType: "document",
|
|
126
|
+
access: { mode: "resource", resourceType: "document" },
|
|
118
127
|
});
|
|
119
128
|
```
|
|
120
129
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
130
|
+
For intentionally deployment-wide authenticated collaboration:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
createCollabPlugin({
|
|
134
|
+
table: "todos",
|
|
135
|
+
contentColumn: "content",
|
|
136
|
+
access: { mode: "all-authenticated" },
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Omitting `access` is supported only for legacy compatibility and is deprecated.
|
|
141
|
+
It behaves like `all-authenticated`, logs a warning, and is flagged by Doctor.
|
|
142
|
+
Choose the mode explicitly so access intent is reviewable.
|
|
125
143
|
|
|
126
144
|
Non-owner sharees who have explicit access fall back to state-vector catch-up
|
|
127
145
|
(safe, slightly higher latency). Awareness routes require the same viewer
|
|
@@ -133,7 +151,10 @@ Write routes reject payloads exceeding `maxPayloadBytes` (default 2 MB) with
|
|
|
133
151
|
HTTP 413. Override:
|
|
134
152
|
|
|
135
153
|
```ts
|
|
136
|
-
createCollabPlugin({
|
|
154
|
+
createCollabPlugin({
|
|
155
|
+
access: { mode: "resource", resourceType: "document" },
|
|
156
|
+
maxPayloadBytes: 512 * 1024,
|
|
157
|
+
});
|
|
137
158
|
```
|
|
138
159
|
|
|
139
160
|
## Enabling Collaboration
|
|
@@ -144,7 +165,7 @@ createCollabPlugin({ resourceType: "document", maxPayloadBytes: 512 * 1024 });
|
|
|
144
165
|
pnpm add @tiptap/extension-collaboration @tiptap/extension-collaboration-caret @tiptap/y-tiptap @tiptap/core
|
|
145
166
|
```
|
|
146
167
|
|
|
147
|
-
### 2. Add collab server plugin (with
|
|
168
|
+
### 2. Add collab server plugin (with explicit access)
|
|
148
169
|
|
|
149
170
|
```ts
|
|
150
171
|
// server/plugins/collab.ts
|
|
@@ -154,7 +175,7 @@ export default createCollabPlugin({
|
|
|
154
175
|
table: "documents",
|
|
155
176
|
contentColumn: "content",
|
|
156
177
|
idColumn: "id",
|
|
157
|
-
resourceType: "document",
|
|
178
|
+
access: { mode: "resource", resourceType: "document" },
|
|
158
179
|
});
|
|
159
180
|
```
|
|
160
181
|
|
|
@@ -343,9 +364,10 @@ the whole history — external/agent edits must not wipe the user's undo stack.
|
|
|
343
364
|
|
|
344
365
|
## Common Pitfalls
|
|
345
366
|
|
|
346
|
-
- **Missing `
|
|
347
|
-
|
|
348
|
-
|
|
367
|
+
- **Missing `access`** — Legacy omission behaves like `all-authenticated`, logs
|
|
368
|
+
a startup warning, and is flagged by Doctor. Choose `resource` for records
|
|
369
|
+
backed by ownership/sharing, or explicit `all-authenticated` for intentionally
|
|
370
|
+
deployment-wide records. Never invent a resource type to silence the warning.
|
|
349
371
|
- **Don't pass `content` as a TipTap prop** when Collaboration is enabled —
|
|
350
372
|
Yjs owns the content. Seed via `editor.commands.setContent()` only when the
|
|
351
373
|
Y.XmlFragment is empty.
|