@agent-native/core 0.115.4 → 0.116.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 +28 -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 +1 -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/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 +16 -6
- package/corpus/core/src/connections/catalog.ts +7 -1
- 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/server/collab-plugin.ts +137 -18
- package/corpus/core/src/server/index.ts +2 -0
- 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/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/sidebar/DocumentSidebar.tsx +1 -1
- 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/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 +3 -2
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +17 -6
- package/dist/client/use-db-sync.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/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/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- 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/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/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js.map +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 +3 -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/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 +16 -6
- package/src/connections/catalog.ts +7 -1
- 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/server/collab-plugin.ts +137 -18
- package/src/server/index.ts +2 -0
- 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
|
@@ -18,6 +18,7 @@ import { and, asc, eq, gt, gte, isNull, lte, or, sql } from "drizzle-orm";
|
|
|
18
18
|
import { z } from "zod";
|
|
19
19
|
|
|
20
20
|
import { getDb, schema } from "../server/db/index.js";
|
|
21
|
+
import { writeBrainExportState } from "../server/lib/brain-export-state.js";
|
|
21
22
|
import {
|
|
22
23
|
getActiveOrganizationId,
|
|
23
24
|
getCurrentOwnerEmail,
|
|
@@ -232,6 +233,11 @@ async function buildPayload(
|
|
|
232
233
|
.orderBy(asc(schema.recordingTags.tag)),
|
|
233
234
|
]);
|
|
234
235
|
|
|
236
|
+
const summary = meeting?.summaryMd?.trim();
|
|
237
|
+
const captureText = summary
|
|
238
|
+
? `Summary\n${summary}\n\nTranscript\n${transcriptText}`
|
|
239
|
+
: transcriptText;
|
|
240
|
+
|
|
235
241
|
return {
|
|
236
242
|
sourceKey: "clips",
|
|
237
243
|
externalId: `clips:recording:${recording.id}`,
|
|
@@ -246,7 +252,7 @@ async function buildPayload(
|
|
|
246
252
|
meeting?.scheduledStart ||
|
|
247
253
|
recording.createdAt ||
|
|
248
254
|
new Date().toISOString(),
|
|
249
|
-
transcript:
|
|
255
|
+
transcript: captureText,
|
|
250
256
|
segments,
|
|
251
257
|
sourceUrl: resolveSourceUrl(recording.id),
|
|
252
258
|
tags: tags.map((row) => row.tag).filter(Boolean),
|
|
@@ -346,14 +352,17 @@ async function exportRecording(
|
|
|
346
352
|
recording: Recording,
|
|
347
353
|
transcript: RecordingTranscript,
|
|
348
354
|
destination: BrainDestination,
|
|
355
|
+
attempt = 1,
|
|
349
356
|
): Promise<BrainExportResult> {
|
|
350
357
|
const payload = await buildPayload(recording, transcript);
|
|
351
358
|
if (!payload) {
|
|
352
|
-
|
|
359
|
+
const result: BrainExportResult = {
|
|
353
360
|
recordingId: recording.id,
|
|
354
361
|
status: "skipped",
|
|
355
362
|
reason: "empty-transcript",
|
|
356
363
|
};
|
|
364
|
+
await persistBrainExportResult(result, attempt);
|
|
365
|
+
return result;
|
|
357
366
|
}
|
|
358
367
|
|
|
359
368
|
try {
|
|
@@ -370,7 +379,9 @@ async function exportRecording(
|
|
|
370
379
|
},
|
|
371
380
|
{ maxRedirects: 0 },
|
|
372
381
|
);
|
|
373
|
-
|
|
382
|
+
const result = await interpretBrainResponse(recording.id, response);
|
|
383
|
+
await persistBrainExportResult(result, attempt);
|
|
384
|
+
return result;
|
|
374
385
|
} catch (err) {
|
|
375
386
|
const reason =
|
|
376
387
|
(err as Error)?.name === "TimeoutError" ||
|
|
@@ -382,10 +393,65 @@ async function exportRecording(
|
|
|
382
393
|
reason,
|
|
383
394
|
error: (err as Error)?.message ?? String(err),
|
|
384
395
|
});
|
|
385
|
-
|
|
396
|
+
const result: BrainExportResult = {
|
|
397
|
+
recordingId: recording.id,
|
|
398
|
+
status: "failed",
|
|
399
|
+
reason,
|
|
400
|
+
};
|
|
401
|
+
await persistBrainExportResult(result, attempt);
|
|
402
|
+
return result;
|
|
386
403
|
}
|
|
387
404
|
}
|
|
388
405
|
|
|
406
|
+
async function persistBrainExportResult(
|
|
407
|
+
result: BrainExportResult,
|
|
408
|
+
attempts: number,
|
|
409
|
+
): Promise<void> {
|
|
410
|
+
const updatedAt = new Date().toISOString();
|
|
411
|
+
await writeBrainExportState({
|
|
412
|
+
recordingId: result.recordingId,
|
|
413
|
+
status: result.status,
|
|
414
|
+
attempts,
|
|
415
|
+
updatedAt,
|
|
416
|
+
...(result.status === "exported" ? { captureId: result.captureId } : {}),
|
|
417
|
+
...(result.status === "quarantined"
|
|
418
|
+
? {
|
|
419
|
+
sensitivityReceiptId: result.sensitivityReceiptId,
|
|
420
|
+
sensitivityDisposition: result.sensitivityDisposition,
|
|
421
|
+
}
|
|
422
|
+
: {}),
|
|
423
|
+
...(result.status === "failed"
|
|
424
|
+
? {
|
|
425
|
+
reason: result.reason,
|
|
426
|
+
nextAttemptAt: new Date(
|
|
427
|
+
Date.now() +
|
|
428
|
+
Math.min(15 * 60_000, 30_000 * 2 ** Math.max(0, attempts - 1)),
|
|
429
|
+
).toISOString(),
|
|
430
|
+
}
|
|
431
|
+
: result.status === "skipped"
|
|
432
|
+
? { reason: result.reason }
|
|
433
|
+
: {}),
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
async function persistRetryableBrainExportFailure(
|
|
438
|
+
recordingId: string,
|
|
439
|
+
reason: string,
|
|
440
|
+
attempts: number,
|
|
441
|
+
): Promise<void> {
|
|
442
|
+
await writeBrainExportState({
|
|
443
|
+
recordingId,
|
|
444
|
+
status: "failed",
|
|
445
|
+
attempts,
|
|
446
|
+
reason,
|
|
447
|
+
updatedAt: new Date().toISOString(),
|
|
448
|
+
nextAttemptAt: new Date(
|
|
449
|
+
Date.now() +
|
|
450
|
+
Math.min(15 * 60_000, 30_000 * 2 ** Math.max(0, attempts - 1)),
|
|
451
|
+
).toISOString(),
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
|
|
389
455
|
async function mapWithConcurrency<T, R>(
|
|
390
456
|
values: T[],
|
|
391
457
|
concurrency: number,
|
|
@@ -535,6 +601,7 @@ export default defineAction({
|
|
|
535
601
|
.min(1)
|
|
536
602
|
.max(8)
|
|
537
603
|
.default(DEFAULT_CONCURRENCY),
|
|
604
|
+
retryAttempt: z.coerce.number().int().min(1).max(8).optional(),
|
|
538
605
|
cursor: z
|
|
539
606
|
.string()
|
|
540
607
|
.min(1)
|
|
@@ -552,11 +619,17 @@ export default defineAction({
|
|
|
552
619
|
);
|
|
553
620
|
const destinationResult = await resolveBrainDestination();
|
|
554
621
|
if (!destinationResult.destination) {
|
|
555
|
-
|
|
622
|
+
const result: BrainExportResult = {
|
|
556
623
|
recordingId: args.recordingId,
|
|
557
624
|
status: "skipped" as const,
|
|
558
625
|
reason: destinationResult.reason,
|
|
559
626
|
};
|
|
627
|
+
await persistRetryableBrainExportFailure(
|
|
628
|
+
result.recordingId,
|
|
629
|
+
result.reason,
|
|
630
|
+
0,
|
|
631
|
+
);
|
|
632
|
+
return result;
|
|
560
633
|
}
|
|
561
634
|
const [transcript] = await getDb()
|
|
562
635
|
.select()
|
|
@@ -564,16 +637,19 @@ export default defineAction({
|
|
|
564
637
|
.where(eq(schema.recordingTranscripts.recordingId, args.recordingId))
|
|
565
638
|
.limit(1);
|
|
566
639
|
if (!transcript) {
|
|
567
|
-
|
|
640
|
+
const result: BrainExportResult = {
|
|
568
641
|
recordingId: args.recordingId,
|
|
569
642
|
status: "skipped" as const,
|
|
570
643
|
reason: "empty-transcript",
|
|
571
644
|
};
|
|
645
|
+
await persistBrainExportResult(result, args.retryAttempt ?? 0);
|
|
646
|
+
return result;
|
|
572
647
|
}
|
|
573
648
|
return exportRecording(
|
|
574
649
|
access.resource as Recording,
|
|
575
650
|
transcript,
|
|
576
651
|
destinationResult.destination,
|
|
652
|
+
args.retryAttempt ?? 1,
|
|
577
653
|
);
|
|
578
654
|
}
|
|
579
655
|
|
|
@@ -53,6 +53,7 @@ import { and, eq } from "drizzle-orm";
|
|
|
53
53
|
import { z } from "zod";
|
|
54
54
|
|
|
55
55
|
import { getDb, schema } from "../server/db/index.js";
|
|
56
|
+
import { writeBrainExportState } from "../server/lib/brain-export-state.js";
|
|
56
57
|
import { dispatchPostFinalizeJob } from "../server/lib/post-finalize-dispatch.js";
|
|
57
58
|
import {
|
|
58
59
|
getCurrentOwnerEmail,
|
|
@@ -66,7 +67,6 @@ import {
|
|
|
66
67
|
parseTranscriptSegments,
|
|
67
68
|
} from "../shared/transcript-segments.js";
|
|
68
69
|
import cleanupTranscript from "./cleanup-transcript.js";
|
|
69
|
-
import exportToBrain from "./export-to-brain.js";
|
|
70
70
|
import { loadAgentsMdContext } from "./lib/agents-md-context.js";
|
|
71
71
|
import {
|
|
72
72
|
AudioOnlyExtractionError,
|
|
@@ -309,15 +309,26 @@ function scheduleAutoTranscriptRetry({
|
|
|
309
309
|
});
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
-
function queueBrainExport(recordingId: string): void {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
312
|
+
async function queueBrainExport(recordingId: string): Promise<void> {
|
|
313
|
+
await writeBrainExportState({
|
|
314
|
+
recordingId,
|
|
315
|
+
status: "pending",
|
|
316
|
+
attempts: 0,
|
|
317
|
+
updatedAt: new Date().toISOString(),
|
|
318
|
+
nextAttemptAt: new Date(Date.now() + 60_000).toISOString(),
|
|
319
|
+
});
|
|
320
|
+
try {
|
|
321
|
+
await dispatchPostFinalizeJob({
|
|
322
|
+
recordingId,
|
|
323
|
+
kind: "brain-export",
|
|
324
|
+
requireAccepted: true,
|
|
325
|
+
});
|
|
326
|
+
} catch (error) {
|
|
327
|
+
console.warn(
|
|
328
|
+
`[clips] Brain export dispatch failed for ${recordingId}:`,
|
|
329
|
+
error instanceof Error ? error.message : String(error),
|
|
330
|
+
);
|
|
331
|
+
}
|
|
321
332
|
}
|
|
322
333
|
|
|
323
334
|
function verboseTranscriptErrors(): boolean {
|
|
@@ -542,7 +553,7 @@ export async function importLoomTranscriptForRecording({
|
|
|
542
553
|
now,
|
|
543
554
|
});
|
|
544
555
|
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
545
|
-
queueBrainExport(recordingId);
|
|
556
|
+
await queueBrainExport(recordingId);
|
|
546
557
|
return {
|
|
547
558
|
recordingId,
|
|
548
559
|
status: "ready" as const,
|
|
@@ -955,7 +966,7 @@ async function completeReadyTranscript({
|
|
|
955
966
|
// (`transcript-cleanup-${recordingId}`) before its next 2s tick lands —
|
|
956
967
|
// otherwise the "Cleaning up…" badge can lag for one full poll interval.
|
|
957
968
|
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
958
|
-
queueBrainExport(recordingId);
|
|
969
|
+
await queueBrainExport(recordingId);
|
|
959
970
|
|
|
960
971
|
return {
|
|
961
972
|
recordingId,
|
|
@@ -1385,7 +1396,7 @@ const requestTranscriptAction = defineAction({
|
|
|
1385
1396
|
now,
|
|
1386
1397
|
});
|
|
1387
1398
|
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
1388
|
-
queueBrainExport(args.recordingId);
|
|
1399
|
+
await queueBrainExport(args.recordingId);
|
|
1389
1400
|
await clearBuilderCreditsExhausted();
|
|
1390
1401
|
|
|
1391
1402
|
// Re-read title fresh — `rec.title` was fetched before the 30+ s
|
|
@@ -1647,7 +1658,7 @@ const requestTranscriptAction = defineAction({
|
|
|
1647
1658
|
});
|
|
1648
1659
|
|
|
1649
1660
|
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
1650
|
-
queueBrainExport(args.recordingId);
|
|
1661
|
+
await queueBrainExport(args.recordingId);
|
|
1651
1662
|
|
|
1652
1663
|
// Generate transcript-backed metadata without replacing a human title or
|
|
1653
1664
|
// description. The title action keeps any local heuristic replaceable
|
|
@@ -1854,7 +1854,21 @@ mod tests {
|
|
|
1854
1854
|
|
|
1855
1855
|
#[cfg(target_os = "macos")]
|
|
1856
1856
|
mod macos_only {
|
|
1857
|
-
use super::super::chunk_graphemes_by_utf16_units;
|
|
1857
|
+
use super::super::{chunk_graphemes_by_utf16_units, utf8_pasteboard_command};
|
|
1858
|
+
use std::ffi::OsStr;
|
|
1859
|
+
|
|
1860
|
+
#[test]
|
|
1861
|
+
fn pasteboard_commands_force_utf8_for_gui_launches() {
|
|
1862
|
+
let command = utf8_pasteboard_command("pbcopy");
|
|
1863
|
+
let env_value = |key: &str| {
|
|
1864
|
+
command
|
|
1865
|
+
.get_envs()
|
|
1866
|
+
.find(|(name, _)| *name == OsStr::new(key))
|
|
1867
|
+
.and_then(|(_, value)| value)
|
|
1868
|
+
};
|
|
1869
|
+
assert_eq!(env_value("LANG"), Some(OsStr::new("en_US.UTF-8")));
|
|
1870
|
+
assert_eq!(env_value("LC_ALL"), Some(OsStr::new("en_US.UTF-8")));
|
|
1871
|
+
}
|
|
1858
1872
|
|
|
1859
1873
|
#[test]
|
|
1860
1874
|
fn never_splits_a_zwj_family_emoji_across_chunks() {
|
|
@@ -1892,9 +1906,18 @@ mod tests {
|
|
|
1892
1906
|
}
|
|
1893
1907
|
}
|
|
1894
1908
|
|
|
1909
|
+
#[cfg(target_os = "macos")]
|
|
1910
|
+
fn utf8_pasteboard_command(program: &str) -> Command {
|
|
1911
|
+
let mut command = Command::new(program);
|
|
1912
|
+
command
|
|
1913
|
+
.env("LANG", "en_US.UTF-8")
|
|
1914
|
+
.env("LC_ALL", "en_US.UTF-8");
|
|
1915
|
+
command
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1895
1918
|
#[cfg(target_os = "macos")]
|
|
1896
1919
|
fn write_clipboard(text: &str) -> Result<(), String> {
|
|
1897
|
-
let mut child =
|
|
1920
|
+
let mut child = utf8_pasteboard_command("pbcopy")
|
|
1898
1921
|
.stdin(Stdio::piped())
|
|
1899
1922
|
.spawn()
|
|
1900
1923
|
.map_err(|e| format!("pbcopy spawn: {e}"))?;
|
|
@@ -1926,7 +1949,7 @@ fn write_clipboard(_text: &str) -> Result<(), String> {
|
|
|
1926
1949
|
/// content on the clipboard are not preserved by the later restore.
|
|
1927
1950
|
#[cfg(target_os = "macos")]
|
|
1928
1951
|
fn read_clipboard() -> Option<String> {
|
|
1929
|
-
let out =
|
|
1952
|
+
let out = utf8_pasteboard_command("pbpaste").output().ok()?;
|
|
1930
1953
|
if !out.status.success() {
|
|
1931
1954
|
return None;
|
|
1932
1955
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
|
|
3
|
+
import { randomBytes } from "node:crypto";
|
|
4
|
+
import { cpSync, existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
9
|
+
const FUNCTIONS_DIR = path.join(ROOT, ".netlify", "functions-internal");
|
|
10
|
+
const SERVER_DIR = path.join(FUNCTIONS_DIR, "server");
|
|
11
|
+
export const SCHEDULED_NAME = "clips-brain-export-cron";
|
|
12
|
+
export const WORKER_NAME = "clips-brain-export-sweep-background";
|
|
13
|
+
export const ROUTE_PATH = "/api/clips/brain-export/run";
|
|
14
|
+
export const SCHEDULE = "* * * * *";
|
|
15
|
+
|
|
16
|
+
function ensureDir(dir: string) {
|
|
17
|
+
mkdirSync(dir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function scheduledTriggerSource(token: string) {
|
|
21
|
+
return `const WORKER_PATH = "/.netlify/functions/${WORKER_NAME}";
|
|
22
|
+
const CRON_TOKEN = ${JSON.stringify(token)};
|
|
23
|
+
|
|
24
|
+
export default async function handler(request) {
|
|
25
|
+
const response = await fetch(new URL(WORKER_PATH, new URL(request.url).origin), {
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers: { "content-type": "application/json", "x-agent-native-clips-brain-export-cron": CRON_TOKEN },
|
|
28
|
+
body: JSON.stringify({ scheduled: true }),
|
|
29
|
+
});
|
|
30
|
+
if (!response.ok && response.status !== 202) console.error("[clips-brain-export-cron] Background sweep trigger failed:", response.status);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const config = {
|
|
34
|
+
name: "Clips Brain export cron trigger",
|
|
35
|
+
generator: "agent-native clips build",
|
|
36
|
+
schedule: ${JSON.stringify(SCHEDULE)},
|
|
37
|
+
};
|
|
38
|
+
`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function backgroundWorkerSource(token: string) {
|
|
42
|
+
return `globalThis.__AGENT_NATIVE_CLIPS_BRAIN_EXPORT_SCHEDULED_RUNTIME__ = true;
|
|
43
|
+
const CRON_TOKEN = ${JSON.stringify(token)};
|
|
44
|
+
const ROUTE_PATH = ${JSON.stringify(ROUTE_PATH)};
|
|
45
|
+
let cachedHandler;
|
|
46
|
+
|
|
47
|
+
function timingSafeEquals(a, b) {
|
|
48
|
+
if (a.length !== b.length) return false;
|
|
49
|
+
let diff = 0;
|
|
50
|
+
for (let i = 0; i < a.length; i += 1) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
51
|
+
return diff === 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default async function handler(request, context) {
|
|
55
|
+
const token = request.headers.get("x-agent-native-clips-brain-export-cron") || "";
|
|
56
|
+
if (!timingSafeEquals(token, CRON_TOKEN)) return new Response("Unauthorized", { status: 401 });
|
|
57
|
+
cachedHandler ??= (await import("./main.mjs")).default;
|
|
58
|
+
const url = new URL(request.url);
|
|
59
|
+
url.pathname = ROUTE_PATH;
|
|
60
|
+
url.search = "";
|
|
61
|
+
return cachedHandler(new Request(url, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: { "content-type": "application/json" },
|
|
64
|
+
body: JSON.stringify({ scheduled: true }),
|
|
65
|
+
}), context);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const config = {
|
|
69
|
+
name: "Clips Brain export background sweep",
|
|
70
|
+
generator: "agent-native clips build",
|
|
71
|
+
background: true,
|
|
72
|
+
nodeBundler: "none",
|
|
73
|
+
includedFiles: ["**"],
|
|
74
|
+
preferStatic: false,
|
|
75
|
+
};
|
|
76
|
+
`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function emitNetlifyBrainExportCron(
|
|
80
|
+
options: {
|
|
81
|
+
functionsDir?: string;
|
|
82
|
+
serverDir?: string;
|
|
83
|
+
token?: string;
|
|
84
|
+
} = {},
|
|
85
|
+
) {
|
|
86
|
+
const functionsDir = options.functionsDir ?? FUNCTIONS_DIR;
|
|
87
|
+
const serverDir = options.serverDir ?? SERVER_DIR;
|
|
88
|
+
if (!existsSync(serverDir))
|
|
89
|
+
throw new Error(
|
|
90
|
+
"Expected Nitro Netlify server output before emitting Clips Brain export functions.",
|
|
91
|
+
);
|
|
92
|
+
const token = options.token ?? randomBytes(32).toString("hex");
|
|
93
|
+
const scheduledDir = path.join(functionsDir, SCHEDULED_NAME);
|
|
94
|
+
const workerDir = path.join(functionsDir, WORKER_NAME);
|
|
95
|
+
rmSync(scheduledDir, { recursive: true, force: true });
|
|
96
|
+
rmSync(workerDir, { recursive: true, force: true });
|
|
97
|
+
ensureDir(scheduledDir);
|
|
98
|
+
cpSync(serverDir, workerDir, { recursive: true });
|
|
99
|
+
rmSync(path.join(workerDir, "server.mjs"), { force: true });
|
|
100
|
+
writeFileSync(
|
|
101
|
+
path.join(scheduledDir, `${SCHEDULED_NAME}.mjs`),
|
|
102
|
+
scheduledTriggerSource(token),
|
|
103
|
+
);
|
|
104
|
+
writeFileSync(
|
|
105
|
+
path.join(workerDir, `${WORKER_NAME}.mjs`),
|
|
106
|
+
backgroundWorkerSource(token),
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (
|
|
111
|
+
process.argv[1] &&
|
|
112
|
+
path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)
|
|
113
|
+
) {
|
|
114
|
+
emitNetlifyBrainExportCron();
|
|
115
|
+
console.log(
|
|
116
|
+
"[clips-brain-export-cron] Emitted scheduled export sweep and background worker.",
|
|
117
|
+
);
|
|
118
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[build]
|
|
2
2
|
ignore = "node ./scripts/netlify-ignore-build.mjs clips"
|
|
3
|
-
command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter clips build"
|
|
3
|
+
command = "export DATABASE_URL=${NETLIFY_DATABASE_URL:-$DATABASE_URL} && pnpm install && NITRO_PRESET=netlify pnpm --filter clips build && pnpm exec tsx templates/clips/jobs/emit-netlify-brain-export-cron.ts"
|
|
4
4
|
publish = "templates/clips/dist"
|
|
5
5
|
functions = "templates/clips/.netlify/functions-internal"
|
|
6
6
|
|
|
@@ -25,3 +25,6 @@ AGENT_NATIVE_SERVERLESS_FFMPEG_ARCH = "x64"
|
|
|
25
25
|
# auto-continuation path can hand off before Netlify kills the function.
|
|
26
26
|
[functions."*"]
|
|
27
27
|
timeout = 75
|
|
28
|
+
|
|
29
|
+
[functions.clips-brain-export-sweep-background]
|
|
30
|
+
timeout = 120
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
import { writeAppState } from "@agent-native/core/application-state";
|
|
4
|
+
import { saveCredential } from "@agent-native/core/credentials";
|
|
5
|
+
import { getDbExec } from "@agent-native/core/db";
|
|
6
|
+
import { runWithRequestContext } from "@agent-native/core/server";
|
|
7
|
+
|
|
8
|
+
const tokenFile = process.env.BRAIN_CLIPS_TOKEN_FILE;
|
|
9
|
+
if (!tokenFile) throw new Error("BRAIN_CLIPS_TOKEN_FILE is required");
|
|
10
|
+
const token = (await readFile(tokenFile, "utf8")).trim();
|
|
11
|
+
if (!token.startsWith("brain_") || token.length < 32) {
|
|
12
|
+
throw new Error("The Brain ingest token is invalid");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const db = getDbExec();
|
|
16
|
+
const { rows } = await db.execute({
|
|
17
|
+
sql: `SELECT id, owner_email, org_id, created_at
|
|
18
|
+
FROM recordings
|
|
19
|
+
WHERE status = ? AND trashed_at IS NULL AND created_at >= ?
|
|
20
|
+
AND EXISTS (
|
|
21
|
+
SELECT 1 FROM recording_transcripts
|
|
22
|
+
WHERE recording_transcripts.recording_id = recordings.id
|
|
23
|
+
AND recording_transcripts.status = ?
|
|
24
|
+
AND LENGTH(TRIM(COALESCE(recording_transcripts.full_text, ''))) > 0
|
|
25
|
+
)
|
|
26
|
+
ORDER BY created_at DESC
|
|
27
|
+
LIMIT ?`,
|
|
28
|
+
args: [
|
|
29
|
+
"ready",
|
|
30
|
+
new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString(),
|
|
31
|
+
"ready",
|
|
32
|
+
20,
|
|
33
|
+
],
|
|
34
|
+
});
|
|
35
|
+
if (rows.length === 0)
|
|
36
|
+
throw new Error("No recent ready Clips recordings found");
|
|
37
|
+
|
|
38
|
+
const recordings = rows as Array<{
|
|
39
|
+
id: string;
|
|
40
|
+
owner_email: string;
|
|
41
|
+
org_id?: string | null;
|
|
42
|
+
}>;
|
|
43
|
+
const ownerEmail = recordings[0]!.owner_email;
|
|
44
|
+
const orgId = recordings[0]!.org_id?.trim();
|
|
45
|
+
if (!ownerEmail || !orgId) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
"Recent Clips recordings are missing owner or organization scope",
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
if (
|
|
51
|
+
recordings.some(
|
|
52
|
+
(recording) =>
|
|
53
|
+
recording.owner_email !== ownerEmail ||
|
|
54
|
+
recording.org_id?.trim() !== orgId,
|
|
55
|
+
)
|
|
56
|
+
) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
"Recent Clips recordings span multiple owners or organizations",
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
await saveCredential(
|
|
63
|
+
"BRAIN_INGEST_URL",
|
|
64
|
+
"https://brain.agent-native.com/api/_agent-native/brain/ingest",
|
|
65
|
+
{ userEmail: ownerEmail, orgId, scope: "org" },
|
|
66
|
+
);
|
|
67
|
+
await saveCredential("BRAIN_INGEST_TOKEN", token, {
|
|
68
|
+
userEmail: ownerEmail,
|
|
69
|
+
orgId,
|
|
70
|
+
scope: "org",
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
await runWithRequestContext({ userEmail: ownerEmail, orgId }, async () => {
|
|
74
|
+
const now = new Date().toISOString();
|
|
75
|
+
for (const recording of recordings) {
|
|
76
|
+
await writeAppState(`clips-brain-export-${recording.id}`, {
|
|
77
|
+
recordingId: recording.id,
|
|
78
|
+
status: "pending",
|
|
79
|
+
attempts: 0,
|
|
80
|
+
updatedAt: now,
|
|
81
|
+
nextAttemptAt: now,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
console.log(
|
|
87
|
+
JSON.stringify({
|
|
88
|
+
configured: true,
|
|
89
|
+
queued: recordings.length,
|
|
90
|
+
organizationScoped: true,
|
|
91
|
+
}),
|
|
92
|
+
);
|