@agent-native/core 0.90.11 → 0.91.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +34 -0
- package/corpus/core/docs/content/toolkit-settings.mdx +56 -3
- package/corpus/core/package.json +2 -1
- package/corpus/core/src/agent/run-ownership.ts +29 -1
- package/corpus/core/src/changelog/parse.ts +78 -0
- package/corpus/core/src/chat-threads/schema.ts +43 -0
- package/corpus/core/src/chat-threads/store.ts +144 -12
- package/corpus/core/src/cli/design-connect.ts +53 -9
- package/corpus/core/src/cli/workspace-dev.ts +25 -5
- package/corpus/core/src/client/AgentPanel.tsx +79 -14
- package/corpus/core/src/client/AssistantChat.tsx +359 -425
- package/corpus/core/src/client/analytics-session.ts +70 -0
- package/corpus/core/src/client/analytics.ts +175 -49
- package/corpus/core/src/client/chat/message-components.tsx +22 -2
- package/corpus/core/src/client/components/ui/message-scroller.tsx +131 -0
- package/corpus/core/src/client/conversation/AgentConversation.tsx +63 -41
- package/corpus/core/src/client/error-capture.ts +616 -0
- package/corpus/core/src/client/index.ts +12 -0
- package/corpus/core/src/client/session-replay.ts +57 -7
- package/corpus/core/src/client/settings/SettingsPanel.tsx +123 -1
- package/corpus/core/src/client/settings/SettingsTabsPage.tsx +292 -45
- package/corpus/core/src/client/settings/index.ts +1 -0
- package/corpus/core/src/client/sharing/ShareButton.tsx +14 -11
- package/corpus/core/src/client/sharing/ShareDialog.tsx +13 -10
- package/corpus/core/src/mcp/builtin-tools.ts +29 -2
- package/corpus/core/src/server/agent-chat-plugin.ts +174 -79
- package/corpus/core/src/server/onboarding-html.ts +28 -16
- package/corpus/core/src/sharing/actions/list-resource-shares.ts +45 -1
- package/corpus/core/src/sharing/actions/share-resource.ts +9 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +9 -6
- package/corpus/core/src/vite/client.ts +217 -35
- package/corpus/templates/analytics/.agents/skills/adhoc-analysis/SKILL.md +8 -7
- package/corpus/templates/analytics/.agents/skills/analysis-workspace/SKILL.md +14 -10
- package/corpus/templates/analytics/.agents/skills/bigquery/SKILL.md +15 -15
- package/corpus/templates/analytics/.agents/skills/cross-source-analysis/SKILL.md +1 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +38 -22
- package/corpus/templates/analytics/.agents/skills/data-programs/SKILL.md +114 -61
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +1 -0
- package/corpus/templates/analytics/.agents/skills/gong/SKILL.md +7 -7
- package/corpus/templates/analytics/.agents/skills/hubspot/SKILL.md +1 -0
- package/corpus/templates/analytics/.agents/skills/prometheus/SKILL.md +1 -1
- package/corpus/templates/analytics/.builder/skills/bigquery/SKILL.md +17 -17
- package/corpus/templates/analytics/.builder/skills/gcloud/SKILL.md +5 -5
- package/corpus/templates/analytics/.builder/skills/github/SKILL.md +7 -7
- package/corpus/templates/analytics/.builder/skills/gong/SKILL.md +12 -12
- package/corpus/templates/analytics/.builder/skills/grafana/SKILL.md +7 -7
- package/corpus/templates/analytics/.builder/skills/sentry/SKILL.md +7 -7
- package/corpus/templates/analytics/.builder/skills/slack/SKILL.md +6 -6
- package/corpus/templates/analytics/.builder/skills/stripe/SKILL.md +7 -7
- package/corpus/templates/analytics/AGENTS.md +47 -2
- package/corpus/templates/analytics/actions/add-status-page-monitor.ts +36 -0
- package/corpus/templates/analytics/actions/capture-test-error.ts +33 -0
- package/corpus/templates/analytics/actions/delete-monitor.ts +24 -0
- package/corpus/templates/analytics/actions/delete-status-page.ts +23 -0
- package/corpus/templates/analytics/actions/get-error-issue.ts +36 -0
- package/corpus/templates/analytics/actions/get-monitor-stats.ts +55 -0
- package/corpus/templates/analytics/actions/get-monitor.ts +27 -0
- package/corpus/templates/analytics/actions/get-public-status-page.ts +41 -0
- package/corpus/templates/analytics/actions/get-status-page.ts +29 -0
- package/corpus/templates/analytics/actions/list-error-issues.ts +40 -0
- package/corpus/templates/analytics/actions/list-monitors.ts +21 -0
- package/corpus/templates/analytics/actions/list-status-pages.ts +21 -0
- package/corpus/templates/analytics/actions/match-error-issues.ts +51 -0
- package/corpus/templates/analytics/actions/navigate.ts +58 -4
- package/corpus/templates/analytics/actions/remove-status-page-monitor.ts +22 -0
- package/corpus/templates/analytics/actions/reorder-status-page-monitors.ts +25 -0
- package/corpus/templates/analytics/actions/resolve-error-issue.ts +38 -0
- package/corpus/templates/analytics/actions/run-monitor-check.ts +23 -0
- package/corpus/templates/analytics/actions/save-analysis.ts +1 -0
- package/corpus/templates/analytics/actions/save-monitor.ts +145 -0
- package/corpus/templates/analytics/actions/save-status-page.ts +47 -0
- package/corpus/templates/analytics/actions/view-screen.ts +212 -0
- package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -0
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +10 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +134 -39
- package/corpus/templates/analytics/app/components/monitoring/PublicStatusView.tsx +351 -0
- package/corpus/templates/analytics/app/components/monitoring/ResponseTimeChart.tsx +174 -0
- package/corpus/templates/analytics/app/components/monitoring/UptimeStatCards.tsx +85 -0
- package/corpus/templates/analytics/app/components/monitoring/UptimeTimelineBars.tsx +106 -0
- package/corpus/templates/analytics/app/components/monitoring/chart-utils.ts +101 -0
- package/corpus/templates/analytics/app/components/monitoring/index.ts +28 -0
- package/corpus/templates/analytics/app/components/monitoring/types.ts +42 -0
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +34 -0
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +6 -0
- package/corpus/templates/analytics/app/i18n-data.ts +33 -0
- package/corpus/templates/analytics/app/pages/DataSources.tsx +28 -0
- package/corpus/templates/analytics/app/pages/Settings.tsx +77 -9
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +3 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +7 -0
- package/corpus/templates/analytics/app/pages/monitoring/ErrorsPanel.tsx +205 -0
- package/corpus/templates/analytics/app/pages/monitoring/MonitoringPage.tsx +100 -0
- package/corpus/templates/analytics/app/pages/monitoring/UptimePanel.tsx +475 -0
- package/corpus/templates/analytics/app/pages/monitoring/errors/IssueDetail.tsx +372 -0
- package/corpus/templates/analytics/app/pages/monitoring/errors/IssueList.tsx +360 -0
- package/corpus/templates/analytics/app/pages/monitoring/errors/Sparkline.tsx +55 -0
- package/corpus/templates/analytics/app/pages/monitoring/errors/i18n.ts +106 -0
- package/corpus/templates/analytics/app/pages/monitoring/errors/types.ts +75 -0
- package/corpus/templates/analytics/app/pages/monitoring/errors/utils.ts +132 -0
- package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorDetail.tsx +535 -0
- package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorFormPage.tsx +1026 -0
- package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorList.tsx +311 -0
- package/corpus/templates/analytics/app/pages/monitoring/uptime/i18n.ts +235 -0
- package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/StatusPageEditor.tsx +661 -0
- package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/StatusPagesView.tsx +355 -0
- package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/i18n.ts +106 -0
- package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/slug.ts +24 -0
- package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/types.ts +20 -0
- package/corpus/templates/analytics/app/pages/monitoring/uptime/status-summary.ts +87 -0
- package/corpus/templates/analytics/app/pages/monitoring/uptime/types.ts +161 -0
- package/corpus/templates/analytics/app/pages/monitoring/uptime/utils.ts +210 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +76 -1
- package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +74 -9
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +197 -20
- package/corpus/templates/analytics/app/root.tsx +33 -1
- package/corpus/templates/analytics/app/routes/monitoring._index.tsx +10 -0
- package/corpus/templates/analytics/app/routes/status.$slug.tsx +118 -0
- package/corpus/templates/analytics/changelog/2026-07-08-add-uptime-monitors-that-ping-your-urls-and-alert-you-when-t.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-analytics-chats-keep-large-data-dictionaries-compact-so-prov.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-create-public-status-pages-that-share-the-live-health-of-cho.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-dashboard-extension-panels-use-clearer-open-copy.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-error-capture.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-fixed-single-panel-dashboard-drags-so-they-no-longer-rewrite.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-jump-straight-from-an-error-in-a-session-recording-to-its-fu.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-monitor-detail-and-list-now-show-colorful-uptime-timelines-r.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-pinned-ask-chats-now-show-a-pin-icon-in-the-sidebar.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-publish-a-public-uptime-status-page-with-colorful-uptime-tim.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-sessions-now-hide-visitor-emails-in-demo-mode-and-show-clean.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-settings-are-cleaner-searchable-and-alerts-have-their-own-tab.md +5 -0
- package/corpus/templates/analytics/changelog/2026-07-08-sidebar-sections-stay-collapsed-until-opened.md +5 -0
- package/corpus/templates/analytics/changelog/2026-07-08-the-uptime-monitor-list-now-has-a-current-status-overview-wi.md +6 -0
- package/corpus/templates/analytics/docs/error-capture.md +172 -0
- package/corpus/templates/analytics/docs/uptime-monitoring.md +150 -0
- package/corpus/templates/analytics/server/db/schema-errors.ts +150 -0
- package/corpus/templates/analytics/server/db/schema-monitoring.ts +177 -0
- package/corpus/templates/analytics/server/db/schema.ts +6 -0
- package/corpus/templates/analytics/server/jobs/uptime-monitors.ts +123 -0
- package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +41 -0
- package/corpus/templates/analytics/server/lib/data-dictionary-context.ts +55 -12
- package/corpus/templates/analytics/server/lib/error-capture.ts +1331 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +47 -1
- package/corpus/templates/analytics/server/lib/monitor-stats.ts +494 -0
- package/corpus/templates/analytics/server/lib/status-pages.ts +798 -0
- package/corpus/templates/analytics/server/lib/uptime-monitors.ts +1665 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +7 -37
- package/corpus/templates/analytics/server/plugins/auth.ts +11 -1
- package/corpus/templates/analytics/server/plugins/db.ts +408 -0
- package/corpus/templates/analytics/server/plugins/uptime-monitor-jobs.ts +63 -0
- package/corpus/templates/assets/app/routes/_index.tsx +1 -1
- package/corpus/templates/assets/app/routes/settings.tsx +22 -1
- package/corpus/templates/assets/changelog/2026-07-08-recent-drafts-keep-comfortable-side-padding.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
- package/corpus/templates/brain/app/routes/settings.tsx +43 -5
- package/corpus/templates/brain/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
- package/corpus/templates/calendar/app/pages/Settings.tsx +44 -6
- package/corpus/templates/calendar/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
- package/corpus/templates/chat/app/routes/settings.tsx +16 -1
- package/corpus/templates/chat/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
- package/corpus/templates/clips/actions/lib/audio-only-transcription.ts +79 -37
- package/corpus/templates/clips/app/components/player/player-controls.tsx +35 -0
- package/corpus/templates/clips/app/components/player/video-player.tsx +162 -18
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +63 -6
- package/corpus/templates/clips/app/routes/record.tsx +18 -1
- package/corpus/templates/clips/changelog/2026-07-07-shared-clips-start-playback-reliably-from-the-first-play-click.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-08-chrome-extension-camera-and-microphone-menus-now-show-which-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-08-chrome-extension-copies-new-clip-links-after-saving.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-08-chrome-extension-recordings-now-continue-after-granting-came.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-08-mobile-shared-clips-now-have-loom-style-tap-and-skip-controls.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-08-recordings-and-uploads-copy-links-after-saving.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-08-settings-are-cleaner-searchable-and-collapse-s3-with-builder.md +5 -0
- package/corpus/templates/clips/changelog/2026-07-08-transcripts-are-more-reliable-for-longer-desktop-recordings-.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -0
- package/corpus/templates/clips/chrome-extension/src/background.ts +162 -13
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +14 -0
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +36 -0
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +99 -25
- package/corpus/templates/clips/chrome-extension/src/permission.ts +63 -14
- package/corpus/templates/clips/chrome-extension/src/popup.ts +123 -23
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +47 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +33 -4
- package/corpus/templates/content/actions/add-content-database-source-field-property.ts +214 -29
- package/corpus/templates/content/actions/attach-content-database-source.ts +3 -0
- package/corpus/templates/content/actions/change-content-database-source-role.ts +1 -0
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +42 -32
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +352 -81
- package/corpus/templates/content/app/components/editor/database/settings.tsx +49 -24
- package/corpus/templates/content/app/routes/_app.settings.tsx +19 -1
- package/corpus/templates/content/changelog/2026-07-07-builder-source-tag-fields-now-import-as-multi-select.md +6 -0
- package/corpus/templates/content/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
- package/corpus/templates/content/shared/api.ts +3 -0
- package/corpus/templates/content/shared/properties.ts +42 -0
- package/corpus/templates/design/AGENTS.md +9 -0
- package/corpus/templates/design/actions/generate-design.ts +3 -0
- package/corpus/templates/design/actions/get-design-system.ts +147 -1
- package/corpus/templates/design/actions/request-localhost-write-consent.ts +141 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +13 -19
- package/corpus/templates/design/app/routes/settings.tsx +16 -1
- package/corpus/templates/design/changelog/2026-07-08-design-generation-now-follows-hydrated-builder-design-system.md +6 -0
- package/corpus/templates/design/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
- package/corpus/templates/design/server/lib/verify-write-grant.ts +4 -2
- package/corpus/templates/dispatch/app/routes/settings.tsx +23 -2
- package/corpus/templates/dispatch/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
- package/corpus/templates/forms/app/routes/_app.settings.tsx +16 -1
- package/corpus/templates/forms/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
- package/corpus/templates/macros/app/routes/settings.tsx +16 -1
- package/corpus/templates/macros/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
- package/corpus/templates/mail/app/components/settings/GmailFiltersSection.tsx +1 -1
- package/corpus/templates/mail/app/components/settings/SnippetsSection.tsx +1 -1
- package/corpus/templates/mail/app/pages/SettingsPage.tsx +123 -105
- package/corpus/templates/mail/changelog/2026-07-08-settings-redesigned-with-a-consistent-searchable-layout.md +5 -0
- package/corpus/templates/plan/app/routes/settings.tsx +23 -2
- package/corpus/templates/plan/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
- package/corpus/templates/slides/actions/add-slide.ts +1 -0
- package/corpus/templates/slides/actions/get-design-system.ts +147 -1
- package/corpus/templates/slides/app/pages/Index.tsx +46 -1
- package/corpus/templates/slides/app/routes/settings.tsx +16 -1
- package/corpus/templates/slides/changelog/2026-07-08-deck-generation-now-follows-hydrated-builder-design-system-g.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
- package/dist/agent/run-ownership.d.ts +6 -0
- package/dist/agent/run-ownership.d.ts.map +1 -1
- package/dist/agent/run-ownership.js +11 -1
- package/dist/agent/run-ownership.js.map +1 -1
- package/dist/changelog/parse.d.ts +7 -0
- package/dist/changelog/parse.d.ts.map +1 -1
- package/dist/changelog/parse.js +64 -0
- package/dist/changelog/parse.js.map +1 -1
- package/dist/chat-threads/schema.d.ts +445 -0
- package/dist/chat-threads/schema.d.ts.map +1 -0
- package/dist/chat-threads/schema.js +39 -0
- package/dist/chat-threads/schema.js.map +1 -0
- package/dist/chat-threads/store.d.ts +12 -0
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +99 -10
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +40 -10
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/workspace-dev.d.ts.map +1 -1
- package/dist/cli/workspace-dev.js +20 -5
- package/dist/cli/workspace-dev.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +35 -7
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +105 -183
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/analytics-session.d.ts +3 -0
- package/dist/client/analytics-session.d.ts.map +1 -0
- package/dist/client/analytics-session.js +66 -0
- package/dist/client/analytics-session.js.map +1 -0
- package/dist/client/analytics.d.ts +32 -0
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +116 -46
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/chat/message-components.d.ts +2 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +16 -6
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/components/ui/message-scroller.d.ts +16 -0
- package/dist/client/components/ui/message-scroller.d.ts.map +1 -0
- package/dist/client/components/ui/message-scroller.js +25 -0
- package/dist/client/components/ui/message-scroller.js.map +1 -0
- package/dist/client/conversation/AgentConversation.d.ts +1 -1
- package/dist/client/conversation/AgentConversation.d.ts.map +1 -1
- package/dist/client/conversation/AgentConversation.js +3 -8
- package/dist/client/conversation/AgentConversation.js.map +1 -1
- package/dist/client/error-capture.d.ts +99 -0
- package/dist/client/error-capture.d.ts.map +1 -0
- package/dist/client/error-capture.js +437 -0
- package/dist/client/error-capture.js.map +1 -0
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +3 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/session-replay.d.ts +22 -0
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +45 -4
- package/dist/client/session-replay.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +100 -5
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/SettingsTabsPage.d.ts +49 -1
- package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
- package/dist/client/settings/SettingsTabsPage.js +131 -19
- package/dist/client/settings/SettingsTabsPage.js.map +1 -1
- package/dist/client/settings/index.d.ts +1 -1
- package/dist/client/settings/index.d.ts.map +1 -1
- package/dist/client/settings/index.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +13 -8
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/sharing/ShareDialog.d.ts.map +1 -1
- package/dist/client/sharing/ShareDialog.js +12 -7
- package/dist/client/sharing/ShareDialog.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/mcp/builtin-tools.d.ts.map +1 -1
- package/dist/mcp/builtin-tools.js +21 -2
- package/dist/mcp/builtin-tools.js.map +1 -1
- package/dist/observability/routes.d.ts +1 -1
- package/dist/org/schema.d.ts +15 -15
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +109 -73
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +28 -16
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/sharing/actions/list-resource-shares.js +30 -1
- package/dist/sharing/actions/list-resource-shares.js.map +1 -1
- package/dist/sharing/actions/share-resource.js +6 -0
- package/dist/sharing/actions/share-resource.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -2
- package/dist/templates/workspace-core/.agents/skills/changelog/SKILL.md +9 -6
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +192 -30
- package/dist/vite/client.js.map +1 -1
- package/docs/content/toolkit-settings.mdx +56 -3
- package/package.json +2 -1
- package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -2
- package/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +9 -6
|
@@ -6,13 +6,25 @@ import {
|
|
|
6
6
|
normalizeThreadTitle,
|
|
7
7
|
} from "../agent/thread-data-builder.js";
|
|
8
8
|
import { getDbExec, intType, isPostgres } from "../db/client.js";
|
|
9
|
+
import { createGetDb } from "../db/create-get-db.js";
|
|
9
10
|
import {
|
|
10
11
|
ensureColumnExists,
|
|
11
12
|
ensureIndexExists,
|
|
12
13
|
ensureTableExists,
|
|
13
14
|
} from "../db/ddl-guard.js";
|
|
14
15
|
import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
|
|
16
|
+
import { getRequestOrgId } from "../server/request-context.js";
|
|
17
|
+
import { resolveAccess, type AccessContext } from "../sharing/access.js";
|
|
18
|
+
import { registerShareableResource } from "../sharing/registry.js";
|
|
19
|
+
import { roleSatisfies, type ShareRole } from "../sharing/schema.js";
|
|
15
20
|
import { emitChatThreadChange } from "./emitter.js";
|
|
21
|
+
import {
|
|
22
|
+
chatThreads,
|
|
23
|
+
chatThreadShares,
|
|
24
|
+
CHAT_THREAD_SHARES_CREATE_SQL,
|
|
25
|
+
CHAT_THREAD_SHARES_CREATE_SQL_PG,
|
|
26
|
+
CHAT_THREAD_SHARES_RESOURCE_INDEX_SQL,
|
|
27
|
+
} from "./schema.js";
|
|
16
28
|
|
|
17
29
|
let _initPromise: Promise<void> | undefined;
|
|
18
30
|
|
|
@@ -33,6 +45,7 @@ let _initPromise: Promise<void> | undefined;
|
|
|
33
45
|
const _threadDataLocks = new Map<string, Promise<unknown>>();
|
|
34
46
|
const DEFAULT_THREAD_DATA_UPDATE_ATTEMPTS = 12;
|
|
35
47
|
const THREAD_DATA_CONFLICT_BACKOFF_MS = 25;
|
|
48
|
+
const getChatThreadsDb = createGetDb({ chatThreads, chatThreadShares });
|
|
36
49
|
|
|
37
50
|
export function withThreadDataLock<T>(
|
|
38
51
|
threadId: string,
|
|
@@ -73,7 +86,9 @@ async function ensureTable(): Promise<void> {
|
|
|
73
86
|
scope_label TEXT,
|
|
74
87
|
pinned_at ${intType()},
|
|
75
88
|
archived_at ${intType()},
|
|
76
|
-
share_token_hash TEXT
|
|
89
|
+
share_token_hash TEXT,
|
|
90
|
+
org_id TEXT,
|
|
91
|
+
visibility TEXT NOT NULL DEFAULT 'private'
|
|
77
92
|
)
|
|
78
93
|
`;
|
|
79
94
|
|
|
@@ -100,6 +115,8 @@ async function ensureTable(): Promise<void> {
|
|
|
100
115
|
["pinned_at", intType()],
|
|
101
116
|
["archived_at", intType()],
|
|
102
117
|
["share_token_hash", "TEXT"],
|
|
118
|
+
["org_id", "TEXT"],
|
|
119
|
+
["visibility", "TEXT NOT NULL DEFAULT 'private'"],
|
|
103
120
|
] as const) {
|
|
104
121
|
await ensureColumnExists(
|
|
105
122
|
"chat_threads",
|
|
@@ -107,6 +124,10 @@ async function ensureTable(): Promise<void> {
|
|
|
107
124
|
`ALTER TABLE chat_threads ADD COLUMN IF NOT EXISTS ${col} ${type}`,
|
|
108
125
|
);
|
|
109
126
|
}
|
|
127
|
+
await ensureTableExists(
|
|
128
|
+
"chat_thread_shares",
|
|
129
|
+
CHAT_THREAD_SHARES_CREATE_SQL_PG,
|
|
130
|
+
);
|
|
110
131
|
// Widen millisecond-timestamp columns that older deployments created as
|
|
111
132
|
// 32-bit `INTEGER`; on Postgres the `Date.now()` written on every turn
|
|
112
133
|
// overflows int4. No-op once widened / on fresh BIGINT databases.
|
|
@@ -134,6 +155,10 @@ async function ensureTable(): Promise<void> {
|
|
|
134
155
|
"chat_threads_share_token_idx",
|
|
135
156
|
`CREATE INDEX IF NOT EXISTS chat_threads_share_token_idx ON chat_threads (share_token_hash)`,
|
|
136
157
|
);
|
|
158
|
+
await ensureIndexExists(
|
|
159
|
+
"chat_thread_shares_resource_idx",
|
|
160
|
+
CHAT_THREAD_SHARES_RESOURCE_INDEX_SQL,
|
|
161
|
+
);
|
|
137
162
|
// One-time backfill of message_count for legacy rows written before
|
|
138
163
|
// the column was maintained.
|
|
139
164
|
await backfillLegacyMessageCounts(client);
|
|
@@ -153,6 +178,8 @@ async function ensureTable(): Promise<void> {
|
|
|
153
178
|
["pinned_at", intType()],
|
|
154
179
|
["archived_at", intType()],
|
|
155
180
|
["share_token_hash", "TEXT"],
|
|
181
|
+
["org_id", "TEXT"],
|
|
182
|
+
["visibility", "TEXT NOT NULL DEFAULT 'private'"],
|
|
156
183
|
] as const) {
|
|
157
184
|
try {
|
|
158
185
|
await client.execute(
|
|
@@ -162,6 +189,11 @@ async function ensureTable(): Promise<void> {
|
|
|
162
189
|
// Column already exists.
|
|
163
190
|
}
|
|
164
191
|
}
|
|
192
|
+
try {
|
|
193
|
+
await client.execute(CHAT_THREAD_SHARES_CREATE_SQL);
|
|
194
|
+
} catch {
|
|
195
|
+
// Table already exists.
|
|
196
|
+
}
|
|
165
197
|
// Widen millisecond-timestamp columns that older deployments created as
|
|
166
198
|
// 32-bit `INTEGER`; on Postgres the `Date.now()` written on every turn
|
|
167
199
|
// overflows int4. No-op once widened / on fresh BIGINT databases.
|
|
@@ -181,6 +213,7 @@ async function ensureTable(): Promise<void> {
|
|
|
181
213
|
`CREATE INDEX IF NOT EXISTS chat_threads_owner_updated_idx ON chat_threads (owner_email, updated_at)`,
|
|
182
214
|
`CREATE INDEX IF NOT EXISTS chat_threads_scope_updated_idx ON chat_threads (scope_type, scope_id, updated_at)`,
|
|
183
215
|
`CREATE INDEX IF NOT EXISTS chat_threads_share_token_idx ON chat_threads (share_token_hash)`,
|
|
216
|
+
CHAT_THREAD_SHARES_RESOURCE_INDEX_SQL,
|
|
184
217
|
]) {
|
|
185
218
|
try {
|
|
186
219
|
await client.execute(ddl);
|
|
@@ -261,6 +294,8 @@ export interface ChatThread {
|
|
|
261
294
|
scope: ChatThreadScope | null;
|
|
262
295
|
pinnedAt: number | null;
|
|
263
296
|
archivedAt: number | null;
|
|
297
|
+
orgId: string | null;
|
|
298
|
+
visibility: "private" | "org" | "public";
|
|
264
299
|
}
|
|
265
300
|
|
|
266
301
|
export interface ChatThreadSummary {
|
|
@@ -273,6 +308,8 @@ export interface ChatThreadSummary {
|
|
|
273
308
|
scope: ChatThreadScope | null;
|
|
274
309
|
pinnedAt: number | null;
|
|
275
310
|
archivedAt: number | null;
|
|
311
|
+
orgId: string | null;
|
|
312
|
+
visibility: "private" | "org" | "public";
|
|
276
313
|
}
|
|
277
314
|
|
|
278
315
|
export interface ForkThreadSourceSnapshot {
|
|
@@ -297,6 +334,10 @@ function readNullableNumber(value: unknown): number | null {
|
|
|
297
334
|
return Number.isFinite(n) && n > 0 ? n : null;
|
|
298
335
|
}
|
|
299
336
|
|
|
337
|
+
function readVisibility(value: unknown): "private" | "org" | "public" {
|
|
338
|
+
return value === "org" || value === "public" ? value : "private";
|
|
339
|
+
}
|
|
340
|
+
|
|
300
341
|
function normalizeForkSourceSnapshot(
|
|
301
342
|
source: ForkThreadSourceSnapshot | null | undefined,
|
|
302
343
|
): {
|
|
@@ -359,6 +400,8 @@ function rowToThread(r: Record<string, unknown>): ChatThread {
|
|
|
359
400
|
scope: readScope(r),
|
|
360
401
|
pinnedAt: readNullableNumber(r.pinned_at),
|
|
361
402
|
archivedAt: readNullableNumber(r.archived_at),
|
|
403
|
+
orgId: (r.org_id as string | null | undefined) ?? null,
|
|
404
|
+
visibility: readVisibility(r.visibility),
|
|
362
405
|
};
|
|
363
406
|
}
|
|
364
407
|
|
|
@@ -378,6 +421,8 @@ function rowToSummary(r: Record<string, unknown>): ChatThreadSummary | null {
|
|
|
378
421
|
scope: readScope(r),
|
|
379
422
|
pinnedAt: readNullableNumber(r.pinned_at),
|
|
380
423
|
archivedAt: readNullableNumber(r.archived_at),
|
|
424
|
+
orgId: (r.org_id as string | null | undefined) ?? null,
|
|
425
|
+
visibility: readVisibility(r.visibility),
|
|
381
426
|
};
|
|
382
427
|
}
|
|
383
428
|
|
|
@@ -391,9 +436,10 @@ export async function createThread(
|
|
|
391
436
|
const now = Date.now();
|
|
392
437
|
const title = opts?.title ?? "";
|
|
393
438
|
const scope = opts?.scope ?? null;
|
|
439
|
+
const orgId = getRequestOrgId() ?? null;
|
|
394
440
|
|
|
395
441
|
await client.execute({
|
|
396
|
-
sql: `INSERT INTO chat_threads (id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label) VALUES (?, ?, ?, '', '{}', 0, ?, ?, ?, ?,
|
|
442
|
+
sql: `INSERT INTO chat_threads (id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label, org_id, visibility) VALUES (?, ?, ?, '', '{}', 0, ?, ?, ?, ?, ?, ?, 'private')`,
|
|
397
443
|
args: [
|
|
398
444
|
id,
|
|
399
445
|
ownerEmail,
|
|
@@ -403,6 +449,7 @@ export async function createThread(
|
|
|
403
449
|
scope?.type ?? null,
|
|
404
450
|
scope?.id ?? null,
|
|
405
451
|
scope?.label ?? null,
|
|
452
|
+
orgId,
|
|
406
453
|
],
|
|
407
454
|
});
|
|
408
455
|
|
|
@@ -418,10 +465,12 @@ export async function createThread(
|
|
|
418
465
|
scope,
|
|
419
466
|
pinnedAt: null,
|
|
420
467
|
archivedAt: null,
|
|
468
|
+
orgId,
|
|
469
|
+
visibility: "private",
|
|
421
470
|
};
|
|
422
471
|
}
|
|
423
472
|
|
|
424
|
-
const THREAD_COLUMNS = `id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label, pinned_at, archived_at`;
|
|
473
|
+
const THREAD_COLUMNS = `id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label, pinned_at, archived_at, org_id, visibility`;
|
|
425
474
|
// The list/summary path deliberately omits `thread_data`: it is the full
|
|
426
475
|
// message-history JSON blob and selecting it for every row turns "open the
|
|
427
476
|
// sidebar" into "download every conversation". The summary derives nothing
|
|
@@ -429,7 +478,41 @@ const THREAD_COLUMNS = `id, owner_email, title, preview, thread_data, message_co
|
|
|
429
478
|
// (message_count is maintained on write and backfilled for legacy rows at
|
|
430
479
|
// bootstrap). The detail path (`THREAD_COLUMNS` / `getThread`) still returns
|
|
431
480
|
// the full blob.
|
|
432
|
-
const SUMMARY_COLUMNS = `id, title, preview, message_count, created_at, updated_at, scope_type, scope_id, scope_label, pinned_at, archived_at`;
|
|
481
|
+
const SUMMARY_COLUMNS = `id, title, preview, message_count, created_at, updated_at, scope_type, scope_id, scope_label, pinned_at, archived_at, org_id, visibility`;
|
|
482
|
+
|
|
483
|
+
export function registerChatThreadsShareable(): void {
|
|
484
|
+
registerShareableResource({
|
|
485
|
+
type: "chat_thread",
|
|
486
|
+
resourceTable: chatThreads,
|
|
487
|
+
sharesTable: chatThreadShares,
|
|
488
|
+
displayName: "Chat",
|
|
489
|
+
titleColumn: "title",
|
|
490
|
+
getResourcePath: (thread) =>
|
|
491
|
+
`/?thread=${encodeURIComponent(String(thread.id ?? ""))}`,
|
|
492
|
+
getDb: () => getChatThreadsDb(),
|
|
493
|
+
allowPublic: false,
|
|
494
|
+
ownerAccessIgnoresOrg: true,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
export async function ensureChatThreadTables(): Promise<void> {
|
|
499
|
+
await ensureTable();
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
export async function resolveThreadAccess(
|
|
503
|
+
userEmail: string | null | undefined,
|
|
504
|
+
threadId: string | null | undefined,
|
|
505
|
+
minRole: ShareRole | "owner" = "viewer",
|
|
506
|
+
ctx: Omit<AccessContext, "userEmail"> = {},
|
|
507
|
+
): Promise<ChatThread | null> {
|
|
508
|
+
if (!userEmail || !threadId) return null;
|
|
509
|
+
const access = await resolveAccess("chat_thread", threadId, {
|
|
510
|
+
userEmail,
|
|
511
|
+
orgId: ctx.orgId,
|
|
512
|
+
});
|
|
513
|
+
if (!access || !roleSatisfies(access.role, minRole)) return null;
|
|
514
|
+
return await getThread(threadId);
|
|
515
|
+
}
|
|
433
516
|
|
|
434
517
|
export async function getThread(id: string): Promise<ChatThread | null> {
|
|
435
518
|
await ensureTable();
|
|
@@ -445,7 +528,11 @@ export async function getThread(id: string): Promise<ChatThread | null> {
|
|
|
445
528
|
export async function forkThread(
|
|
446
529
|
sourceId: string,
|
|
447
530
|
ownerEmail: string,
|
|
448
|
-
opts?: {
|
|
531
|
+
opts?: {
|
|
532
|
+
id?: string;
|
|
533
|
+
source?: ForkThreadSourceSnapshot | null;
|
|
534
|
+
sourceAccessGranted?: boolean;
|
|
535
|
+
},
|
|
449
536
|
): Promise<ChatThread | null> {
|
|
450
537
|
const snapshot = normalizeForkSourceSnapshot(opts?.source);
|
|
451
538
|
let source = await getThread(sourceId);
|
|
@@ -494,13 +581,19 @@ export async function forkThread(
|
|
|
494
581
|
messageCount: snapshot.messageCount,
|
|
495
582
|
};
|
|
496
583
|
}
|
|
497
|
-
if (
|
|
584
|
+
if (
|
|
585
|
+
!source ||
|
|
586
|
+
(!opts?.sourceAccessGranted && source.ownerEmail !== ownerEmail)
|
|
587
|
+
) {
|
|
588
|
+
return null;
|
|
589
|
+
}
|
|
498
590
|
const id = opts?.id ?? generateId();
|
|
499
591
|
const now = Date.now();
|
|
500
592
|
const title = source.title ? `${source.title} (fork)` : "";
|
|
501
593
|
const client = getDbExec();
|
|
594
|
+
const orgId = getRequestOrgId() ?? null;
|
|
502
595
|
await client.execute({
|
|
503
|
-
sql: `INSERT INTO chat_threads (id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
596
|
+
sql: `INSERT INTO chat_threads (id, owner_email, title, preview, thread_data, message_count, created_at, updated_at, scope_type, scope_id, scope_label, org_id, visibility) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'private')`,
|
|
504
597
|
args: [
|
|
505
598
|
id,
|
|
506
599
|
ownerEmail,
|
|
@@ -513,6 +606,7 @@ export async function forkThread(
|
|
|
513
606
|
source.scope?.type ?? null,
|
|
514
607
|
source.scope?.id ?? null,
|
|
515
608
|
source.scope?.label ?? null,
|
|
609
|
+
orgId,
|
|
516
610
|
],
|
|
517
611
|
});
|
|
518
612
|
return {
|
|
@@ -527,6 +621,8 @@ export async function forkThread(
|
|
|
527
621
|
scope: source.scope,
|
|
528
622
|
pinnedAt: null,
|
|
529
623
|
archivedAt: null,
|
|
624
|
+
orgId,
|
|
625
|
+
visibility: "private",
|
|
530
626
|
};
|
|
531
627
|
}
|
|
532
628
|
|
|
@@ -542,6 +638,28 @@ export interface ListThreadsOptions {
|
|
|
542
638
|
scope?: { type: string; id: string };
|
|
543
639
|
/** When true, returns only threads with no scope (general chats). */
|
|
544
640
|
unscopedOnly?: boolean;
|
|
641
|
+
orgId?: string | null;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function chatThreadAccessSql(
|
|
645
|
+
userEmail: string,
|
|
646
|
+
orgId: string | null | undefined,
|
|
647
|
+
): { sql: string; args: (string | number)[] } {
|
|
648
|
+
const normalizedEmail = userEmail.trim().toLowerCase();
|
|
649
|
+
const clauses = [
|
|
650
|
+
`LOWER(owner_email) = ?`,
|
|
651
|
+
`EXISTS (SELECT 1 FROM chat_thread_shares WHERE chat_thread_shares.resource_id = chat_threads.id AND chat_thread_shares.principal_type = 'user' AND LOWER(chat_thread_shares.principal_id) = ?)`,
|
|
652
|
+
];
|
|
653
|
+
const args: (string | number)[] = [normalizedEmail, normalizedEmail];
|
|
654
|
+
if (orgId) {
|
|
655
|
+
clauses.push(`(visibility = 'org' AND org_id = ?)`);
|
|
656
|
+
args.push(orgId);
|
|
657
|
+
clauses.push(
|
|
658
|
+
`EXISTS (SELECT 1 FROM chat_thread_shares WHERE chat_thread_shares.resource_id = chat_threads.id AND chat_thread_shares.principal_type = 'org' AND chat_thread_shares.principal_id = ?)`,
|
|
659
|
+
);
|
|
660
|
+
args.push(orgId);
|
|
661
|
+
}
|
|
662
|
+
return { sql: `(${clauses.join(" OR ")})`, args };
|
|
545
663
|
}
|
|
546
664
|
|
|
547
665
|
export async function listThreads(
|
|
@@ -562,8 +680,12 @@ export async function listThreads(
|
|
|
562
680
|
// maintained on every write and backfilled for legacy rows at bootstrap,
|
|
563
681
|
// so the old `OR thread_data LIKE '%"messages"%'` substring scan over the
|
|
564
682
|
// full blob is no longer needed here.
|
|
565
|
-
const
|
|
566
|
-
|
|
683
|
+
const access = chatThreadAccessSql(
|
|
684
|
+
ownerEmail,
|
|
685
|
+
opts.orgId ?? getRequestOrgId(),
|
|
686
|
+
);
|
|
687
|
+
const filters: string[] = [access.sql, `message_count > 0`];
|
|
688
|
+
const args: (string | number)[] = [...access.args];
|
|
567
689
|
if (opts.scope) {
|
|
568
690
|
filters.push(`scope_type = ? AND scope_id = ?`);
|
|
569
691
|
args.push(opts.scope.type, opts.scope.id);
|
|
@@ -588,7 +710,7 @@ export async function searchThreads(
|
|
|
588
710
|
ownerEmail: string,
|
|
589
711
|
query: string,
|
|
590
712
|
limit = 50,
|
|
591
|
-
options: { scope?: { type: string; id: string } } = {},
|
|
713
|
+
options: { scope?: { type: string; id: string }; orgId?: string | null } = {},
|
|
592
714
|
): Promise<ChatThreadSummary[]> {
|
|
593
715
|
await ensureTable();
|
|
594
716
|
const client = getDbExec();
|
|
@@ -596,12 +718,16 @@ export async function searchThreads(
|
|
|
596
718
|
// The count-guard uses the maintained/backfilled `message_count` column
|
|
597
719
|
// (same as listThreads). The content match still scans `thread_data` —
|
|
598
720
|
// search legitimately needs to look inside message history.
|
|
721
|
+
const access = chatThreadAccessSql(
|
|
722
|
+
ownerEmail,
|
|
723
|
+
options.orgId ?? getRequestOrgId(),
|
|
724
|
+
);
|
|
599
725
|
const filters: string[] = [
|
|
600
|
-
|
|
726
|
+
access.sql,
|
|
601
727
|
`message_count > 0`,
|
|
602
728
|
`(title LIKE ? ESCAPE '!' OR preview LIKE ? ESCAPE '!' OR thread_data LIKE ? ESCAPE '!')`,
|
|
603
729
|
];
|
|
604
|
-
const args: (string | number)[] = [
|
|
730
|
+
const args: (string | number)[] = [...access.args, pattern, pattern, pattern];
|
|
605
731
|
if (options.scope) {
|
|
606
732
|
filters.push(`scope_type = ? AND scope_id = ?`);
|
|
607
733
|
args.push(options.scope.type, options.scope.id);
|
|
@@ -1137,6 +1263,12 @@ export async function deleteThread(id: string): Promise<boolean> {
|
|
|
1137
1263
|
args: [id],
|
|
1138
1264
|
});
|
|
1139
1265
|
if (result.rowsAffected > 0) {
|
|
1266
|
+
await client
|
|
1267
|
+
.execute({
|
|
1268
|
+
sql: `DELETE FROM chat_thread_shares WHERE resource_id = ?`,
|
|
1269
|
+
args: [id],
|
|
1270
|
+
})
|
|
1271
|
+
.catch(() => {});
|
|
1140
1272
|
emitChatThreadChange(id);
|
|
1141
1273
|
return true;
|
|
1142
1274
|
}
|
|
@@ -708,8 +708,42 @@ function addLiveEditBaseHref(html: string, href: string): string {
|
|
|
708
708
|
return `<!DOCTYPE html><html><head>${baseTag}<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head><body>${html}</body></html>`;
|
|
709
709
|
}
|
|
710
710
|
|
|
711
|
-
|
|
712
|
-
|
|
711
|
+
/**
|
|
712
|
+
* Rewrite the iframe path to the real target route before the proxied app's
|
|
713
|
+
* bundle runs. The bridge serves snapshots from its own `/live-edit` path, so a
|
|
714
|
+
* client-side-routed SPA would otherwise boot at "/live-edit", match no route,
|
|
715
|
+
* and render its 404. A synchronous inline script in `<head>` runs during parse
|
|
716
|
+
* (before deferred module bundles), so `history.replaceState` lands the SPA on
|
|
717
|
+
* the intended route. Assets still resolve via the injected `<base href>`.
|
|
718
|
+
*/
|
|
719
|
+
function injectPreBootLocationShim(html: string, targetPath: string): string {
|
|
720
|
+
const path = targetPath.trim();
|
|
721
|
+
if (!path || path === "/live-edit") return html;
|
|
722
|
+
const shim = `<script data-agent-native-live-edit-location>
|
|
723
|
+
(function(){try{var p=${JSON.stringify(path)};if(p&&(location.pathname+location.search)!==p){history.replaceState(null,"",p);}}catch(e){}})();
|
|
724
|
+
</script>`;
|
|
725
|
+
if (/<head\b[^>]*>/i.test(html)) {
|
|
726
|
+
return html.replace(/<head\b[^>]*>/i, (match) => `${match}${shim}`);
|
|
727
|
+
}
|
|
728
|
+
if (/<html\b[^>]*>/i.test(html)) {
|
|
729
|
+
return html.replace(
|
|
730
|
+
/<html\b[^>]*>/i,
|
|
731
|
+
(match) => `${match}<head>${shim}</head>`,
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
return `${shim}${html}`;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function injectLiveEditBridge(
|
|
738
|
+
html: string,
|
|
739
|
+
baseHref: string,
|
|
740
|
+
script: string,
|
|
741
|
+
targetPath: string,
|
|
742
|
+
) {
|
|
743
|
+
const withBase = injectPreBootLocationShim(
|
|
744
|
+
addLiveEditBaseHref(html, baseHref),
|
|
745
|
+
targetPath,
|
|
746
|
+
);
|
|
713
747
|
if (!script) return withBase;
|
|
714
748
|
if (withBase.includes("</body>")) {
|
|
715
749
|
return withBase.replace("</body>", `${script}</body>`);
|
|
@@ -1288,10 +1322,17 @@ export async function startDesignConnectBridge(
|
|
|
1288
1322
|
);
|
|
1289
1323
|
const includeEditorBridge =
|
|
1290
1324
|
requestUrl.searchParams.get("bridge") !== "0";
|
|
1325
|
+
// The dev server route the SPA must boot on (e.g. "/todo"), taken
|
|
1326
|
+
// from the resolved snapshot target rather than the bridge's own
|
|
1327
|
+
// "/live-edit" request path.
|
|
1328
|
+
const targetParsed = new URL(targetUrl);
|
|
1329
|
+
const targetPath =
|
|
1330
|
+
`${targetParsed.pathname}${targetParsed.search}` || "/";
|
|
1291
1331
|
const html = injectLiveEditBridge(
|
|
1292
1332
|
snapshot.html,
|
|
1293
1333
|
new URL("/", manifest.bridgeUrl).toString(),
|
|
1294
1334
|
includeEditorBridge ? liveEditBridgeScript : "",
|
|
1335
|
+
targetPath,
|
|
1295
1336
|
);
|
|
1296
1337
|
sendText(
|
|
1297
1338
|
res,
|
|
@@ -1897,15 +1938,18 @@ export async function runDesign(argv: string[]) {
|
|
|
1897
1938
|
console.error(`Routes: ${manifest.routeCount}`);
|
|
1898
1939
|
console.error(`Dev URL: ${manifest.devServerUrl}`);
|
|
1899
1940
|
|
|
1900
|
-
// Self-register with the design app server (best-effort)
|
|
1901
|
-
//
|
|
1902
|
-
//
|
|
1903
|
-
// step grant-localhost-write-consent would mint a different token, causing
|
|
1904
|
-
// every bridge write to return 401.
|
|
1941
|
+
// Self-register with the design app server (best-effort): POST the bridge
|
|
1942
|
+
// token to connect-localhost so the server row stores the real token. Without
|
|
1943
|
+
// it, the bridge and server tokens diverge and every write returns 401.
|
|
1905
1944
|
const appUrl = resolveAppUrl(parsed.appUrl);
|
|
1906
1945
|
if (appUrl) {
|
|
1907
|
-
|
|
1908
|
-
|
|
1946
|
+
void registerConnectionWithServer(appUrl, bridge, resolveAuthToken());
|
|
1947
|
+
} else {
|
|
1948
|
+
// No app URL means the token never reaches the DB — surface it instead of
|
|
1949
|
+
// silently skipping, since live-edit will then 401.
|
|
1950
|
+
console.error(
|
|
1951
|
+
"[design connect] No app URL resolved (pass --app-url or set AGENT_NATIVE_URL); skipping self-registration — live-edit will fail to authorize.",
|
|
1952
|
+
);
|
|
1909
1953
|
}
|
|
1910
1954
|
|
|
1911
1955
|
return await new Promise<number>((resolve) => {
|
|
@@ -83,6 +83,7 @@ const DEFAULT_PROXY_RESPONSE_TIMEOUT_MS = 5_000;
|
|
|
83
83
|
const DEFAULT_PROXY_NON_HTML_RESPONSE_TIMEOUT_MS = 120_000;
|
|
84
84
|
const APP_OUTPUT_TAIL_BYTES = 8_000;
|
|
85
85
|
const POLLING_WATCH_INTERVAL_MS = "1000";
|
|
86
|
+
const reportedDiscoverAppsReadFailures = new Set<string>();
|
|
86
87
|
const STARTING_APP_RESPONSE_HEADERS: http.OutgoingHttpHeaders = {
|
|
87
88
|
"content-type": "text/html; charset=utf-8",
|
|
88
89
|
"cache-control": "no-store, no-cache, max-age=0, must-revalidate",
|
|
@@ -287,6 +288,23 @@ function readJson(file: string): any {
|
|
|
287
288
|
}
|
|
288
289
|
}
|
|
289
290
|
|
|
291
|
+
function shouldReportDiscoverAppsReadFailure(
|
|
292
|
+
appsDir: string,
|
|
293
|
+
code: string | undefined,
|
|
294
|
+
): boolean {
|
|
295
|
+
if (code === "ENOENT") return false;
|
|
296
|
+
const key = `${appsDir}:${code ?? "unknown"}`;
|
|
297
|
+
if (reportedDiscoverAppsReadFailures.has(key)) return false;
|
|
298
|
+
reportedDiscoverAppsReadFailures.add(key);
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function shouldCaptureDiscoverAppsReadFailure(
|
|
303
|
+
code: string | undefined,
|
|
304
|
+
): boolean {
|
|
305
|
+
return code !== "EACCES" && code !== "EPERM";
|
|
306
|
+
}
|
|
307
|
+
|
|
290
308
|
function discoverApps(appsDir: string, appPortStart: number): WorkspaceApp[] {
|
|
291
309
|
if (!fs.existsSync(appsDir)) return [];
|
|
292
310
|
// existsSync -> readdirSync is a TOCTOU race. Treat ENOENT as "no apps
|
|
@@ -296,15 +314,17 @@ function discoverApps(appsDir: string, appPortStart: number): WorkspaceApp[] {
|
|
|
296
314
|
entries = fs.readdirSync(appsDir, { withFileTypes: true });
|
|
297
315
|
} catch (err) {
|
|
298
316
|
const code = (err as NodeJS.ErrnoException).code;
|
|
299
|
-
if (code
|
|
317
|
+
if (shouldReportDiscoverAppsReadFailure(appsDir, code)) {
|
|
300
318
|
console.warn(
|
|
301
319
|
`[workspace] Could not read ${appsDir} (${code ?? "unknown"}): ` +
|
|
302
320
|
`${(err as Error).message}`,
|
|
303
321
|
);
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
322
|
+
if (shouldCaptureDiscoverAppsReadFailure(code)) {
|
|
323
|
+
Sentry.captureException(err, {
|
|
324
|
+
tags: { handled: "dev-discover-readdir" },
|
|
325
|
+
level: "warning",
|
|
326
|
+
});
|
|
327
|
+
}
|
|
308
328
|
}
|
|
309
329
|
return [];
|
|
310
330
|
}
|
|
@@ -68,6 +68,7 @@ import {
|
|
|
68
68
|
import { ErrorReportActions } from "./ErrorReportActions.js";
|
|
69
69
|
import { FeedbackButton } from "./FeedbackButton.js";
|
|
70
70
|
import { RunsTrayMenuItem } from "./progress/RunsTray.js";
|
|
71
|
+
import { ShareButton } from "./sharing/ShareButton.js";
|
|
71
72
|
// Lazy-load the full assistant-ui chat stack (tiptap composer + react-markdown +
|
|
72
73
|
// assistant-ui + zod block schemas) so it is NOT in the static import closure of
|
|
73
74
|
// every page. The header/tab chrome renders immediately; chat streams in once the
|
|
@@ -90,7 +91,7 @@ import {
|
|
|
90
91
|
type AgentSidebarStateChangeDetail,
|
|
91
92
|
} from "./agent-sidebar-state.js";
|
|
92
93
|
import { trackEvent } from "./analytics.js";
|
|
93
|
-
import { agentNativePath } from "./api-path.js";
|
|
94
|
+
import { agentNativePath, appPath } from "./api-path.js";
|
|
94
95
|
import { assistantUiRecoverableRenderErrorKind } from "./assistant-ui-recovery.js";
|
|
95
96
|
import type { AssistantChatProps } from "./AssistantChat.js";
|
|
96
97
|
import { shouldParentFrameOwnAgentPanel } from "./builder-frame.js";
|
|
@@ -1080,10 +1081,31 @@ function AgentPanelInner({
|
|
|
1080
1081
|
const [headerMenuOpen, setHeaderMenuOpen] = useState(false);
|
|
1081
1082
|
const [feedbackOpen, setFeedbackOpen] = useState(false);
|
|
1082
1083
|
|
|
1084
|
+
const getChatThreadShareUrl = useCallback(
|
|
1085
|
+
(threadId: string) => {
|
|
1086
|
+
if (typeof window === "undefined") return undefined;
|
|
1087
|
+
if (
|
|
1088
|
+
threadUrlSync &&
|
|
1089
|
+
typeof threadUrlSync === "object" &&
|
|
1090
|
+
typeof threadUrlSync.getPath === "function"
|
|
1091
|
+
) {
|
|
1092
|
+
return new URL(
|
|
1093
|
+
appPath(threadUrlSync.getPath(threadId)),
|
|
1094
|
+
window.location.origin,
|
|
1095
|
+
).toString();
|
|
1096
|
+
}
|
|
1097
|
+
const url = new URL(window.location.href);
|
|
1098
|
+
url.searchParams.set("thread", threadId);
|
|
1099
|
+
return url.toString();
|
|
1100
|
+
},
|
|
1101
|
+
[threadUrlSync],
|
|
1102
|
+
);
|
|
1103
|
+
|
|
1083
1104
|
const renderHeaderActions = useCallback(
|
|
1084
1105
|
({
|
|
1085
1106
|
activeChatSessionId,
|
|
1086
1107
|
activeTabId,
|
|
1108
|
+
activeTabMessageCount,
|
|
1087
1109
|
addTab,
|
|
1088
1110
|
clearActiveTab,
|
|
1089
1111
|
closeAllTabs,
|
|
@@ -1095,6 +1117,7 @@ function AgentPanelInner({
|
|
|
1095
1117
|
}: Pick<
|
|
1096
1118
|
MultiTabAssistantChatHeaderProps,
|
|
1097
1119
|
| "activeTabId"
|
|
1120
|
+
| "activeTabMessageCount"
|
|
1098
1121
|
| "addTab"
|
|
1099
1122
|
| "clearActiveTab"
|
|
1100
1123
|
| "closeAllTabs"
|
|
@@ -1110,6 +1133,28 @@ function AgentPanelInner({
|
|
|
1110
1133
|
<SetupButton />
|
|
1111
1134
|
</Suspense>
|
|
1112
1135
|
)}
|
|
1136
|
+
{(() => {
|
|
1137
|
+
const activeTab =
|
|
1138
|
+
mode === "chat" && activeChatSessionId
|
|
1139
|
+
? tabs.find((tab) => tab.id === activeChatSessionId)
|
|
1140
|
+
: undefined;
|
|
1141
|
+
if (
|
|
1142
|
+
!activeTab ||
|
|
1143
|
+
(activeTabMessageCount <= 0 && activeTab.status === "idle")
|
|
1144
|
+
) {
|
|
1145
|
+
return null;
|
|
1146
|
+
}
|
|
1147
|
+
return (
|
|
1148
|
+
<ShareButton
|
|
1149
|
+
resourceType="chat_thread"
|
|
1150
|
+
resourceId={activeTab.id}
|
|
1151
|
+
resourceTitle={activeTab.label || "Chat"}
|
|
1152
|
+
shareUrl={getChatThreadShareUrl(activeTab.id)}
|
|
1153
|
+
trigger="icon"
|
|
1154
|
+
triggerClassName="h-7 w-7"
|
|
1155
|
+
/>
|
|
1156
|
+
);
|
|
1157
|
+
})()}
|
|
1113
1158
|
<FeedbackButton
|
|
1114
1159
|
variant="icon"
|
|
1115
1160
|
side="bottom"
|
|
@@ -1331,6 +1376,7 @@ function AgentPanelInner({
|
|
|
1331
1376
|
closeOtherCliTabs,
|
|
1332
1377
|
closeTabHint,
|
|
1333
1378
|
feedbackOpen,
|
|
1379
|
+
getChatThreadShareUrl,
|
|
1334
1380
|
headerMenuOpen,
|
|
1335
1381
|
isFullscreen,
|
|
1336
1382
|
mode,
|
|
@@ -1361,25 +1407,42 @@ function AgentPanelInner({
|
|
|
1361
1407
|
) {
|
|
1362
1408
|
return null;
|
|
1363
1409
|
}
|
|
1410
|
+
const activeTab = activeTabId
|
|
1411
|
+
? tabs.find((tab) => tab.id === activeTabId)
|
|
1412
|
+
: undefined;
|
|
1413
|
+
const canShareActiveTab =
|
|
1414
|
+
activeTab && (activeTabMessageCount > 0 || activeTab.status !== "idle");
|
|
1364
1415
|
|
|
1365
1416
|
return (
|
|
1366
1417
|
<div className="pointer-events-none absolute inset-x-0 top-3 z-[60] flex justify-end px-3 sm:top-4 sm:px-4">
|
|
1367
|
-
<
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1418
|
+
<div className="pointer-events-auto flex items-center gap-1">
|
|
1419
|
+
{canShareActiveTab ? (
|
|
1420
|
+
<ShareButton
|
|
1421
|
+
resourceType="chat_thread"
|
|
1422
|
+
resourceId={activeTab.id}
|
|
1423
|
+
resourceTitle={activeTab.label || "Chat"}
|
|
1424
|
+
shareUrl={getChatThreadShareUrl(activeTab.id)}
|
|
1425
|
+
trigger="icon"
|
|
1426
|
+
triggerClassName="h-8 w-8 border border-border bg-background/95 shadow-sm backdrop-blur hover:bg-accent"
|
|
1427
|
+
/>
|
|
1428
|
+
) : null}
|
|
1429
|
+
<button
|
|
1430
|
+
type="button"
|
|
1431
|
+
data-agent-page-new-chat=""
|
|
1432
|
+
aria-label={t("agentPanel.newChat")}
|
|
1433
|
+
onClick={() => {
|
|
1434
|
+
addTab();
|
|
1435
|
+
}}
|
|
1436
|
+
className="inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background/95 px-2.5 text-xs font-medium text-foreground shadow-sm backdrop-blur transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
1437
|
+
>
|
|
1438
|
+
<IconPlus size={14} />
|
|
1439
|
+
<span>{t("agentPanel.newChat")}</span>
|
|
1440
|
+
</button>
|
|
1441
|
+
</div>
|
|
1379
1442
|
</div>
|
|
1380
1443
|
);
|
|
1381
1444
|
},
|
|
1382
|
-
[t],
|
|
1445
|
+
[getChatThreadShareUrl, t],
|
|
1383
1446
|
);
|
|
1384
1447
|
|
|
1385
1448
|
// Ref callback: scroll the active tab into view in the overflow container.
|
|
@@ -1404,6 +1467,7 @@ function AgentPanelInner({
|
|
|
1404
1467
|
({
|
|
1405
1468
|
tabs,
|
|
1406
1469
|
activeTabId,
|
|
1470
|
+
activeTabMessageCount,
|
|
1407
1471
|
setActiveTabId,
|
|
1408
1472
|
addTab,
|
|
1409
1473
|
clearActiveTab,
|
|
@@ -1432,6 +1496,7 @@ function AgentPanelInner({
|
|
|
1432
1496
|
{renderHeaderActions({
|
|
1433
1497
|
activeChatSessionId: activeTabId,
|
|
1434
1498
|
activeTabId,
|
|
1499
|
+
activeTabMessageCount,
|
|
1435
1500
|
addTab,
|
|
1436
1501
|
clearActiveTab,
|
|
1437
1502
|
closeAllTabs,
|