@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
|
@@ -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
|
+
);
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { getDbExec } from "@agent-native/core/db";
|
|
2
|
+
import { runWithRequestContext } from "@agent-native/core/server";
|
|
3
|
+
import { and, eq, isNull } from "drizzle-orm";
|
|
4
|
+
|
|
5
|
+
import exportToBrain from "../../actions/export-to-brain.js";
|
|
6
|
+
import { getDb, schema } from "../db/index.js";
|
|
7
|
+
import {
|
|
8
|
+
BRAIN_EXPORT_STATE_PREFIX,
|
|
9
|
+
parseBrainExportState,
|
|
10
|
+
writeBrainExportState,
|
|
11
|
+
} from "../lib/brain-export-state.js";
|
|
12
|
+
import { ownerEmailMatches } from "../lib/recordings.js";
|
|
13
|
+
|
|
14
|
+
const SWEEP_INTERVAL_MS = 60_000;
|
|
15
|
+
const MAX_ATTEMPTS = 8;
|
|
16
|
+
const SWEEP_LIMIT = 4;
|
|
17
|
+
const DISCOVERY_LIMIT = 20;
|
|
18
|
+
const BACKFILL_DAYS = 7;
|
|
19
|
+
let skippingLogged = false;
|
|
20
|
+
|
|
21
|
+
async function seedRecentBrainExports(): Promise<void> {
|
|
22
|
+
const now = new Date();
|
|
23
|
+
const { rows } = await getDbExec().execute({
|
|
24
|
+
sql: `SELECT recordings.id, recordings.owner_email, recordings.org_id
|
|
25
|
+
FROM recordings
|
|
26
|
+
INNER JOIN recording_transcripts
|
|
27
|
+
ON recording_transcripts.recording_id = recordings.id
|
|
28
|
+
WHERE recordings.status = ?
|
|
29
|
+
AND recordings.trashed_at IS NULL
|
|
30
|
+
AND recordings.org_id IS NOT NULL
|
|
31
|
+
AND recordings.created_at >= ?
|
|
32
|
+
AND recording_transcripts.status = ?
|
|
33
|
+
AND LENGTH(TRIM(COALESCE(recording_transcripts.full_text, ''))) > 0
|
|
34
|
+
AND NOT EXISTS (
|
|
35
|
+
SELECT 1 FROM application_state
|
|
36
|
+
WHERE application_state.session_id = recordings.owner_email
|
|
37
|
+
AND application_state.key = ? || recordings.id
|
|
38
|
+
)
|
|
39
|
+
ORDER BY recordings.created_at DESC
|
|
40
|
+
LIMIT ?`,
|
|
41
|
+
args: [
|
|
42
|
+
"ready",
|
|
43
|
+
new Date(
|
|
44
|
+
now.getTime() - BACKFILL_DAYS * 24 * 60 * 60 * 1000,
|
|
45
|
+
).toISOString(),
|
|
46
|
+
"ready",
|
|
47
|
+
BRAIN_EXPORT_STATE_PREFIX,
|
|
48
|
+
DISCOVERY_LIMIT,
|
|
49
|
+
],
|
|
50
|
+
});
|
|
51
|
+
for (const row of rows as Array<{
|
|
52
|
+
id?: unknown;
|
|
53
|
+
owner_email?: unknown;
|
|
54
|
+
org_id?: unknown;
|
|
55
|
+
}>) {
|
|
56
|
+
const recordingId = typeof row.id === "string" ? row.id.trim() : "";
|
|
57
|
+
const ownerEmail =
|
|
58
|
+
typeof row.owner_email === "string" ? row.owner_email.trim() : "";
|
|
59
|
+
const orgId = typeof row.org_id === "string" ? row.org_id.trim() : "";
|
|
60
|
+
if (!recordingId || !ownerEmail || !orgId) continue;
|
|
61
|
+
await runWithRequestContext({ userEmail: ownerEmail, orgId }, () =>
|
|
62
|
+
writeBrainExportState({
|
|
63
|
+
recordingId,
|
|
64
|
+
status: "pending",
|
|
65
|
+
attempts: 0,
|
|
66
|
+
updatedAt: now.toISOString(),
|
|
67
|
+
nextAttemptAt: now.toISOString(),
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function runBrainExportSweepOnce(): Promise<void> {
|
|
74
|
+
await seedRecentBrainExports();
|
|
75
|
+
const { rows } = await getDbExec().execute({
|
|
76
|
+
sql: "SELECT session_id, key, value FROM application_state WHERE key LIKE ? AND (value LIKE ? OR value LIKE ?) ORDER BY updated_at ASC LIMIT ?",
|
|
77
|
+
args: [
|
|
78
|
+
`${BRAIN_EXPORT_STATE_PREFIX}%`,
|
|
79
|
+
'%"status":"pending"%',
|
|
80
|
+
'%"status":"failed"%',
|
|
81
|
+
SWEEP_LIMIT,
|
|
82
|
+
],
|
|
83
|
+
});
|
|
84
|
+
const now = Date.now();
|
|
85
|
+
for (const row of rows as Array<{
|
|
86
|
+
session_id?: unknown;
|
|
87
|
+
key?: unknown;
|
|
88
|
+
value?: unknown;
|
|
89
|
+
}>) {
|
|
90
|
+
const ownerEmail =
|
|
91
|
+
typeof row.session_id === "string" ? row.session_id.trim() : "";
|
|
92
|
+
const key = typeof row.key === "string" ? row.key : "";
|
|
93
|
+
const recordingId = key.slice(BRAIN_EXPORT_STATE_PREFIX.length);
|
|
94
|
+
let raw: unknown;
|
|
95
|
+
try {
|
|
96
|
+
raw = JSON.parse(typeof row.value === "string" ? row.value : "");
|
|
97
|
+
} catch {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const state = parseBrainExportState(raw);
|
|
101
|
+
if (
|
|
102
|
+
!ownerEmail ||
|
|
103
|
+
!recordingId ||
|
|
104
|
+
!state ||
|
|
105
|
+
state.recordingId !== recordingId ||
|
|
106
|
+
!["pending", "failed"].includes(state.status) ||
|
|
107
|
+
state.attempts >= MAX_ATTEMPTS ||
|
|
108
|
+
Date.parse(state.nextAttemptAt ?? "") > now
|
|
109
|
+
)
|
|
110
|
+
continue;
|
|
111
|
+
try {
|
|
112
|
+
const [recording] = await getDb()
|
|
113
|
+
.select({
|
|
114
|
+
ownerEmail: schema.recordings.ownerEmail,
|
|
115
|
+
orgId: schema.recordings.orgId,
|
|
116
|
+
})
|
|
117
|
+
.from(schema.recordings)
|
|
118
|
+
.where(
|
|
119
|
+
and(
|
|
120
|
+
eq(schema.recordings.id, recordingId),
|
|
121
|
+
ownerEmailMatches(schema.recordings.ownerEmail, ownerEmail),
|
|
122
|
+
eq(schema.recordings.status, "ready"),
|
|
123
|
+
isNull(schema.recordings.trashedAt),
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
.limit(1);
|
|
127
|
+
if (!recording) continue;
|
|
128
|
+
await runWithRequestContext(
|
|
129
|
+
{
|
|
130
|
+
userEmail: recording.ownerEmail,
|
|
131
|
+
orgId: recording.orgId ?? undefined,
|
|
132
|
+
},
|
|
133
|
+
() =>
|
|
134
|
+
exportToBrain.run({ recordingId, retryAttempt: state.attempts + 1 }),
|
|
135
|
+
);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
const attempts = state.attempts + 1;
|
|
138
|
+
await runWithRequestContext({ userEmail: ownerEmail }, () =>
|
|
139
|
+
writeBrainExportState({
|
|
140
|
+
recordingId,
|
|
141
|
+
status: "failed",
|
|
142
|
+
attempts,
|
|
143
|
+
reason: "brain-export-worker-failed",
|
|
144
|
+
updatedAt: new Date().toISOString(),
|
|
145
|
+
nextAttemptAt: new Date(
|
|
146
|
+
Date.now() +
|
|
147
|
+
Math.min(15 * 60_000, 30_000 * 2 ** Math.max(0, attempts - 1)),
|
|
148
|
+
).toISOString(),
|
|
149
|
+
}),
|
|
150
|
+
);
|
|
151
|
+
console.warn("[brain-export] sweep item failed", {
|
|
152
|
+
recordingId,
|
|
153
|
+
error: error instanceof Error ? error.message : String(error),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export default function registerBrainExportJob(): void {
|
|
160
|
+
if (process.env.NETLIFY === "true") return;
|
|
161
|
+
const enabled =
|
|
162
|
+
process.env.RUN_BACKGROUND_JOBS === "1" ||
|
|
163
|
+
(process.env.NODE_ENV === "production" &&
|
|
164
|
+
process.env.RUN_BACKGROUND_JOBS !== "0");
|
|
165
|
+
if (!enabled) {
|
|
166
|
+
if (process.env.DEBUG && !skippingLogged) {
|
|
167
|
+
console.log(
|
|
168
|
+
"[brain-export] Skipping background sweep (set RUN_BACKGROUND_JOBS=1 to enable in dev).",
|
|
169
|
+
);
|
|
170
|
+
skippingLogged = true;
|
|
171
|
+
}
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
setInterval(() => {
|
|
175
|
+
runBrainExportSweepOnce().catch((error) =>
|
|
176
|
+
console.error("[brain-export] interval failed:", error),
|
|
177
|
+
);
|
|
178
|
+
}, SWEEP_INTERVAL_MS);
|
|
179
|
+
console.log(
|
|
180
|
+
`[brain-export] Recurring recovery sweep every ${SWEEP_INTERVAL_MS / 1000}s.`,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { writeAppState } from "@agent-native/core/application-state";
|
|
2
|
+
|
|
3
|
+
export const BRAIN_EXPORT_STATE_PREFIX = "clips-brain-export-";
|
|
4
|
+
|
|
5
|
+
export type BrainExportState = {
|
|
6
|
+
recordingId: string;
|
|
7
|
+
status: "pending" | "exported" | "quarantined" | "failed" | "skipped";
|
|
8
|
+
attempts: number;
|
|
9
|
+
updatedAt: string;
|
|
10
|
+
nextAttemptAt?: string;
|
|
11
|
+
captureId?: string;
|
|
12
|
+
sensitivityReceiptId?: string;
|
|
13
|
+
sensitivityDisposition?: string;
|
|
14
|
+
reason?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function brainExportStateKey(recordingId: string): string {
|
|
18
|
+
return `${BRAIN_EXPORT_STATE_PREFIX}${recordingId}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function writeBrainExportState(state: BrainExportState) {
|
|
22
|
+
await writeAppState(brainExportStateKey(state.recordingId), state);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function parseBrainExportState(value: unknown): BrainExportState | null {
|
|
26
|
+
if (!value || typeof value !== "object") return null;
|
|
27
|
+
const state = value as Partial<BrainExportState>;
|
|
28
|
+
if (
|
|
29
|
+
typeof state.recordingId !== "string" ||
|
|
30
|
+
!state.recordingId ||
|
|
31
|
+
!["pending", "exported", "quarantined", "failed", "skipped"].includes(
|
|
32
|
+
String(state.status),
|
|
33
|
+
) ||
|
|
34
|
+
typeof state.attempts !== "number" ||
|
|
35
|
+
!Number.isInteger(state.attempts) ||
|
|
36
|
+
state.attempts < 0 ||
|
|
37
|
+
typeof state.updatedAt !== "string"
|
|
38
|
+
) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
return state as BrainExportState;
|
|
42
|
+
}
|
|
@@ -7,7 +7,11 @@ import {
|
|
|
7
7
|
signScopedAgentAccessToken,
|
|
8
8
|
} from "@agent-native/core/server";
|
|
9
9
|
|
|
10
|
-
export type PostFinalizeJobKind =
|
|
10
|
+
export type PostFinalizeJobKind =
|
|
11
|
+
| "media-ready"
|
|
12
|
+
| "seekable"
|
|
13
|
+
| "transcript"
|
|
14
|
+
| "brain-export";
|
|
11
15
|
|
|
12
16
|
export const POST_FINALIZE_JOB_TOKEN_KIND = "clips-post-finalize-job";
|
|
13
17
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* pattern).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import registerBrainExportJob from "../jobs/brain-export.js";
|
|
8
9
|
import registerBuilderMediaCompressionJob from "../jobs/builder-media-compression.js";
|
|
9
10
|
import registerMediaVerificationJob from "../jobs/media-verification.js";
|
|
10
11
|
import registerMeetingRemindersJob from "../jobs/meeting-reminders.js";
|
|
@@ -17,6 +18,7 @@ export default () => {
|
|
|
17
18
|
// background loop is off.
|
|
18
19
|
registerMeetingRemindersJob();
|
|
19
20
|
registerBuilderMediaCompressionJob();
|
|
21
|
+
registerBrainExportJob();
|
|
20
22
|
registerMediaVerificationJob();
|
|
21
23
|
registerPollCalendarsJob();
|
|
22
24
|
registerStaleMeetingSweeperJob();
|
|
@@ -219,6 +219,48 @@ registerRequiredSecret({
|
|
|
219
219
|
required: false,
|
|
220
220
|
});
|
|
221
221
|
|
|
222
|
+
// ── Brain transcript ingest ──────────────────────────────────────────
|
|
223
|
+
// Both values are workspace-scoped: every clip in the workspace must reach
|
|
224
|
+
// the same Brain source, while the encrypted secret store keeps the token out
|
|
225
|
+
// of action responses, application state, and client bundles.
|
|
226
|
+
|
|
227
|
+
registerRequiredSecret({
|
|
228
|
+
key: "BRAIN_INGEST_URL",
|
|
229
|
+
label: "Brain ingest URL",
|
|
230
|
+
description:
|
|
231
|
+
"Signed Brain generic-ingest endpoint for ready Clips transcripts. Pair with BRAIN_INGEST_TOKEN.",
|
|
232
|
+
scope: "workspace",
|
|
233
|
+
kind: "api-key",
|
|
234
|
+
required: false,
|
|
235
|
+
validator: (value) => {
|
|
236
|
+
if (!value) return true;
|
|
237
|
+
try {
|
|
238
|
+
const url = new URL(value);
|
|
239
|
+
return url.protocol === "http:" || url.protocol === "https:"
|
|
240
|
+
? true
|
|
241
|
+
: { ok: false, error: "Use an HTTP or HTTPS URL." };
|
|
242
|
+
} catch {
|
|
243
|
+
return { ok: false, error: "Enter a valid ingest URL." };
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
registerRequiredSecret({
|
|
249
|
+
key: "BRAIN_INGEST_TOKEN",
|
|
250
|
+
label: "Brain ingest token",
|
|
251
|
+
description:
|
|
252
|
+
"Bearer token for the configured Brain ingest URL. Stored encrypted and never returned to Clips clients or export receipts.",
|
|
253
|
+
scope: "workspace",
|
|
254
|
+
kind: "api-key",
|
|
255
|
+
required: false,
|
|
256
|
+
validator: (value) => {
|
|
257
|
+
if (!value) return true;
|
|
258
|
+
return typeof value === "string" && value.trim().length >= 8
|
|
259
|
+
? true
|
|
260
|
+
: { ok: false, error: "Token looks too short." };
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
|
|
222
264
|
// ── Dark-launched media worker plumbing ──────────────────────────────
|
|
223
265
|
// These are optional until the ai-services worker is deployed. When enabled,
|
|
224
266
|
// Clips enqueues background video compression jobs there instead of using
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from "h3";
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
|
|
14
|
+
import exportToBrain from "../../../../actions/export-to-brain.js";
|
|
14
15
|
import finalizeRecording from "../../../../actions/finalize-recording.js";
|
|
15
16
|
import { ensureRecordingSeekable } from "../../../../actions/lib/ensure-seekable-video.js";
|
|
16
17
|
import requestTranscript from "../../../../actions/request-transcript.js";
|
|
@@ -23,7 +24,7 @@ import {
|
|
|
23
24
|
|
|
24
25
|
const bodySchema = z.object({
|
|
25
26
|
recordingId: z.string().min(1).max(200),
|
|
26
|
-
kind: z.enum(["media-ready", "seekable", "transcript"]),
|
|
27
|
+
kind: z.enum(["media-ready", "seekable", "transcript", "brain-export"]),
|
|
27
28
|
token: z.string().min(1),
|
|
28
29
|
delayMs: z.number().int().min(0).max(30_000).optional(),
|
|
29
30
|
retryAttempt: z.number().int().min(1).max(10).optional(),
|
|
@@ -111,6 +112,14 @@ export default defineEventHandler(async (event: H3Event) => {
|
|
|
111
112
|
return { ok: true, kind, result };
|
|
112
113
|
}
|
|
113
114
|
|
|
115
|
+
if (kind === "brain-export") {
|
|
116
|
+
const result = await exportToBrain.run({
|
|
117
|
+
recordingId,
|
|
118
|
+
retryAttempt,
|
|
119
|
+
});
|
|
120
|
+
return { ok: true, kind, result };
|
|
121
|
+
}
|
|
122
|
+
|
|
114
123
|
const result = await requestTranscript.run({
|
|
115
124
|
recordingId,
|
|
116
125
|
force: true,
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { timingSafeEqual } from "node:crypto";
|
|
2
|
+
|
|
3
|
+
import { createError, defineEventHandler, getHeader } from "h3";
|
|
4
|
+
|
|
5
|
+
import { runBrainExportSweepOnce } from "../../../../jobs/brain-export.js";
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
var __AGENT_NATIVE_CLIPS_BRAIN_EXPORT_SCHEDULED_RUNTIME__:
|
|
9
|
+
| boolean
|
|
10
|
+
| undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function productionLike() {
|
|
14
|
+
return (
|
|
15
|
+
process.env.NODE_ENV === "production" || process.env.NETLIFY === "true"
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function headerMatchesSecret(header: string | undefined, secret: string) {
|
|
20
|
+
const expected = `Bearer ${secret}`;
|
|
21
|
+
const value = header?.trim() ?? "";
|
|
22
|
+
return (
|
|
23
|
+
value.length === expected.length &&
|
|
24
|
+
timingSafeEqual(Buffer.from(value), Buffer.from(expected))
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default defineEventHandler(async (event) => {
|
|
29
|
+
const scheduled =
|
|
30
|
+
globalThis.__AGENT_NATIVE_CLIPS_BRAIN_EXPORT_SCHEDULED_RUNTIME__ === true;
|
|
31
|
+
const secret = process.env.CLIPS_BRAIN_EXPORT_JOBS_SECRET?.trim(); // guard:allow-env-credential — deployment scheduler route secret
|
|
32
|
+
if (!secret && productionLike() && !scheduled)
|
|
33
|
+
throw createError({
|
|
34
|
+
statusCode: 503,
|
|
35
|
+
statusMessage: "CLIPS_BRAIN_EXPORT_JOBS_SECRET is required",
|
|
36
|
+
});
|
|
37
|
+
if (
|
|
38
|
+
secret &&
|
|
39
|
+
!scheduled &&
|
|
40
|
+
!headerMatchesSecret(getHeader(event, "authorization"), secret)
|
|
41
|
+
)
|
|
42
|
+
throw createError({ statusCode: 401, statusMessage: "Unauthorized" });
|
|
43
|
+
await runBrainExportSweepOnce();
|
|
44
|
+
return { ok: true };
|
|
45
|
+
});
|
|
@@ -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
|