@agent-native/core 0.121.2 → 0.122.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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +139 -0
- package/corpus/core/docs/content/actions.mdx +2 -2
- package/corpus/core/docs/content/agent-surfaces.mdx +1 -1
- package/corpus/core/docs/content/authentication.mdx +2 -0
- package/corpus/core/docs/content/deployment.mdx +36 -0
- package/corpus/core/docs/content/http-api.mdx +266 -0
- package/corpus/core/docs/content/syncing-template-changes.mdx +159 -0
- package/corpus/core/package.json +2 -1
- package/corpus/core/src/a2a/activity.ts +38 -27
- package/corpus/core/src/agent/engine/builder-engine.ts +35 -4
- package/corpus/core/src/agent/run-manager.ts +20 -1
- package/corpus/core/src/agent/run-store.ts +4 -1
- package/corpus/core/src/agent/types.ts +28 -8
- package/corpus/core/src/chat-threads/store.ts +45 -0
- package/corpus/core/src/checkpoints/index.ts +2 -0
- package/corpus/core/src/checkpoints/route-match.ts +13 -0
- package/corpus/core/src/cli/create.ts +105 -17
- package/corpus/core/src/cli/index.ts +20 -0
- package/corpus/core/src/cli/template-baseline.ts +410 -0
- package/corpus/core/src/cli/template-sync.ts +1004 -0
- package/corpus/core/src/cli/templates-meta.ts +1 -0
- package/corpus/core/src/client/AssistantChat.tsx +38 -12
- package/corpus/core/src/client/NewWorkspaceAppFlow.tsx +75 -10
- package/corpus/core/src/client/chat/message-components.tsx +127 -66
- package/corpus/core/src/client/chat/tool-call-display.tsx +29 -5
- package/corpus/core/src/client/error-format.ts +18 -0
- package/corpus/core/src/client/org/OrgSwitcher.tsx +30 -8
- package/corpus/core/src/client/org/TeamPage.tsx +7 -6
- package/corpus/core/src/client/sse-event-processor.ts +3 -0
- package/corpus/core/src/data-widgets/index.ts +41 -0
- package/corpus/core/src/deploy/build.ts +11 -9
- package/corpus/core/src/integrations/webhook-handler.ts +63 -9
- package/corpus/core/src/org/auto-join-domain.ts +4 -3
- package/corpus/core/src/org/context.ts +108 -39
- package/corpus/core/src/org/index.ts +5 -0
- package/corpus/core/src/org/service-identity.ts +60 -0
- package/corpus/core/src/provider-api/index.ts +22 -1
- package/corpus/core/src/scripts/docs/search.ts +12 -1
- package/corpus/core/src/server/agent-chat/context-tools.ts +9 -4
- package/corpus/core/src/server/agent-chat/framework-prompts.ts +38 -66
- package/corpus/core/src/server/agent-chat-plugin.ts +17 -10
- package/corpus/core/src/server/agents-bundle.ts +84 -5
- package/corpus/core/src/server/auth.ts +4 -3
- package/corpus/core/src/server/builder-browser.ts +47 -19
- package/corpus/core/src/server/core-routes-plugin.ts +2 -2
- package/corpus/core/src/server/credential-provider.ts +209 -68
- package/corpus/core/src/server/index.ts +3 -0
- package/corpus/core/src/server/prompts/framework-core-compact.ts +6 -13
- package/corpus/core/src/server/prompts/framework-core.ts +9 -21
- package/corpus/core/src/server/prompts/index.ts +0 -1
- package/corpus/core/src/server/prompts/shared-rules.ts +19 -11
- package/corpus/core/src/server/ssr-handler.ts +13 -2
- package/corpus/core/src/settings/user-settings.ts +13 -1
- package/corpus/core/src/shared/cache-control.ts +141 -0
- package/corpus/core/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/core/src/templates/chat/_gitignore +1 -0
- package/corpus/core/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/core/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/performance/SKILL.md +19 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/visual-answer/SKILL.md +1 -0
- package/corpus/core/src/vite/agents-bundle-plugin.ts +7 -6
- package/corpus/core/src/vite/client.ts +5 -0
- package/corpus/templates/analytics/.agents/skills/adhoc-analysis/SKILL.md +4 -5
- package/corpus/templates/analytics/.agents/skills/admin-surfaces/SKILL.md +55 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +10 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/references/template-catalog-and-demo.md +62 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-ops/SKILL.md +85 -0
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +27 -2
- package/corpus/templates/analytics/.agents/skills/data-querying/references/inline-chart-embeds.md +139 -0
- package/corpus/templates/analytics/.agents/skills/monitoring/SKILL.md +77 -0
- package/corpus/templates/analytics/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/templates/analytics/AGENTS.md +91 -563
- package/corpus/templates/analytics/_gitignore +1 -0
- package/corpus/templates/analytics/actions/bigquery.ts +12 -4
- package/corpus/templates/analytics/actions/generate-chart.ts +5 -5
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +30 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +3 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +1 -0
- package/corpus/templates/analytics/changelog/2026-07-24-analytics-finds-saved-dashboards-and-queries-far-more-reliab.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-24-notion-access-errors-now-explain-that-a-page-needs-sharing-w.md +6 -0
- package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +7 -0
- package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +191 -30
- package/corpus/templates/analytics/server/lib/bigquery.ts +11 -1
- package/corpus/templates/analytics/server/lib/notion.ts +15 -4
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +10 -0
- package/corpus/templates/assets/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/templates/brain/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/templates/brain/_gitignore +1 -0
- package/corpus/templates/calendar/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/templates/calendar/_gitignore +1 -0
- package/corpus/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/templates/chat/_gitignore +1 -0
- package/corpus/templates/clips/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +34 -0
- package/corpus/templates/clips/AGENTS.md +5 -4
- package/corpus/templates/clips/_gitignore +1 -0
- package/corpus/templates/clips/actions/cleanup-transcript.ts +5 -0
- package/corpus/templates/clips/actions/get-recording-insights.ts +44 -8
- package/corpus/templates/clips/actions/get-recording-player-data.ts +39 -70
- package/corpus/templates/clips/actions/list-clip-views.ts +2 -4
- package/corpus/templates/clips/actions/list-recordings.ts +69 -19
- package/corpus/templates/clips/actions/list-viewers.ts +5 -2
- package/corpus/templates/clips/actions/regenerate-summary.ts +5 -1
- package/corpus/templates/clips/actions/request-transcript.ts +28 -1
- package/corpus/templates/clips/app/components/editor/stitch-manager.tsx +21 -1
- package/corpus/templates/clips/app/components/meetings/quick-ask-sidebar.tsx +14 -1
- package/corpus/templates/clips/app/components/player/insights-panel.tsx +8 -20
- package/corpus/templates/clips/app/components/player/recording-views-badge.tsx +335 -0
- package/corpus/templates/clips/app/components/player/video-player.tsx +13 -8
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +2 -1
- package/corpus/templates/clips/app/i18n/en-US.ts +13 -2
- package/corpus/templates/clips/app/lib/capture-permissions.ts +110 -0
- package/corpus/templates/clips/app/lib/recording-link.ts +32 -0
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +27 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +46 -15
- package/corpus/templates/clips/app/routes/record.tsx +77 -31
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +42 -31
- package/corpus/templates/clips/changelog/2026-07-24-clip-pages-now-show-a-view-count-with-viewer-avatars-next-to.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-24-clip-summaries-read-like-a-description-you-d-write-yourself-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-24-clip-view-counts-now-separate-human-views-from-agent-views-s.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-24-clips-actions-can-now-be-called-by-org-service-tokens-so-ext.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-24-clips-no-longer-briefly-shows-a-dock-icon-while-launching.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-24-fixed-owner-and-editor-share-links-redirecting-to-the-record.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-24-meeting-and-note-recordings-now-start-as-a-compact-pill-on-t.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-24-new-clips-now-copy-a-shareable-link-instead-of-the-owner-onl.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-24-opening-a-share-link-as-the-clip-s-owner-now-goes-straight-t.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-24-permissions-and-rewind-controls-now-use-compact-consistent-h.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-24-quitting-the-desktop-app-no-longer-shows-a-false-macos-crash.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-24-screen-recording-permission-errors-now-name-the-exact-app-to.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-24-the-desktop-app-now-copies-a-new-clip-s-share-link-to-your-c.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-24-the-recording-toolbar-now-stays-above-other-apps-including-o.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-24-transcript-captions-now-use-real-speech-timings-instead-of-a.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-24-video-playback-no-longer-stutters-and-replays-the-last-momen.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/background.ts +17 -1
- package/corpus/templates/clips/desktop/package.json +2 -0
- package/corpus/templates/clips/desktop/src/app.tsx +89 -0
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +9 -3
- package/corpus/templates/clips/desktop/src/lib/audio-meter.ts +51 -0
- package/corpus/templates/clips/desktop/src/lib/native-notification.ts +44 -0
- package/corpus/templates/clips/desktop/src/lib/recording-link.ts +67 -0
- package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +35 -35
- package/corpus/templates/clips/desktop/src/overlays/live-transcript.tsx +3 -2
- package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +47 -46
- package/corpus/templates/clips/desktop/src/styles.css +127 -78
- package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +7 -0
- package/corpus/templates/clips/desktop/src-tauri/Info.plist +6 -0
- package/corpus/templates/clips/desktop/src-tauri/capabilities/default.json +1 -0
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +7 -3
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +4 -1
- package/corpus/templates/clips/desktop/src-tauri/src/main.rs +8 -0
- package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +21 -10
- package/corpus/templates/clips/desktop/src-tauri/src/util.rs +40 -0
- package/corpus/templates/clips/server/db/schema.ts +25 -0
- package/corpus/templates/clips/server/lib/agent-views.ts +145 -0
- package/corpus/templates/clips/server/lib/public-agent-context.ts +7 -0
- package/corpus/templates/clips/server/lib/recording-share-grant.ts +79 -0
- package/corpus/templates/clips/server/lib/recordings.ts +51 -3
- package/corpus/templates/clips/server/plugins/db.ts +23 -0
- package/corpus/templates/clips/server/routes/api/public-recording.get.ts +49 -17
- package/corpus/templates/clips/shared/recording-link.ts +43 -0
- package/corpus/templates/clips/shared/share-attribution.ts +4 -0
- package/corpus/templates/clips/shared/share-dashboard-redirect.ts +42 -0
- package/corpus/templates/clips/shared/view-analytics.ts +26 -0
- package/corpus/templates/content/.agents/skills/content/SKILL.md +5 -0
- package/corpus/templates/content/.agents/skills/content/references/local-file-mode.md +135 -0
- package/corpus/templates/content/.agents/skills/document-editing/SKILL.md +12 -0
- package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +238 -0
- package/corpus/templates/content/.agents/skills/document-editing/references/document-behavior.md +91 -0
- package/corpus/templates/content/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/templates/content/AGENTS.md +62 -583
- package/corpus/templates/content/_gitignore +1 -0
- package/corpus/templates/design/.agents/skills/creative-context/SKILL.md +11 -0
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +50 -8
- package/corpus/templates/design/.agents/skills/design-generation/references/code-layers.md +81 -0
- package/corpus/templates/design/.agents/skills/design-generation/references/code-workspace.md +42 -0
- package/corpus/templates/design/.agents/skills/design-generation/references/editor-extensions.md +34 -0
- package/corpus/templates/design/.agents/skills/responsive-breakpoints/SKILL.md +4 -1
- package/corpus/templates/design/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +17 -0
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +8196 -0
- package/corpus/templates/design/.generated/bridge/embedded-wheel.generated.ts +265 -0
- package/corpus/templates/design/.generated/bridge/hit-test.generated.ts +642 -0
- package/corpus/templates/design/.generated/bridge/motion-preview.generated.ts +571 -0
- package/corpus/templates/design/.generated/bridge/nav.generated.ts +91 -0
- package/corpus/templates/design/.generated/bridge/sample.generated.ts +23 -0
- package/corpus/templates/design/.generated/bridge/shader-fill-preview.generated.ts +263 -0
- package/corpus/templates/design/.generated/bridge/shader-runtime.generated.ts +518 -0
- package/corpus/templates/design/.generated/bridge/tweak.generated.ts +20 -0
- package/corpus/templates/design/.generated/bridge/zoom.generated.ts +32 -0
- package/corpus/templates/design/AGENTS.md +88 -503
- package/corpus/templates/design/_gitignore +1 -0
- package/corpus/templates/dispatch/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/templates/dispatch/_gitignore +1 -0
- package/corpus/templates/dispatch/changelog/2026-07-24-signing-in-now-puts-you-in-your-companys-workspace.md +6 -0
- package/corpus/templates/forms/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/templates/forms/_gitignore +1 -0
- package/corpus/templates/forms/server/lib/public-form-ssr.ts +2 -2
- package/corpus/templates/macros/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/templates/macros/_gitignore +1 -0
- package/corpus/templates/mail/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/templates/mail/_gitignore +1 -0
- package/corpus/templates/plan/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/templates/plan/_gitignore +1 -0
- package/corpus/templates/slides/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/templates/slides/_gitignore +1 -0
- package/corpus/templates/slides/server/plugins/db.ts +4 -0
- package/corpus/templates/tasks/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/corpus/templates/tasks/AGENTS.md +1 -1
- package/corpus/templates/tasks/_gitignore +1 -0
- package/dist/a2a/activity.d.ts +4 -1
- package/dist/a2a/activity.d.ts.map +1 -1
- package/dist/a2a/activity.js +27 -16
- package/dist/a2a/activity.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts +1 -0
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +31 -4
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +18 -1
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +1 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +2 -1
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/types.d.ts +12 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js +21 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/chat-threads/store.d.ts +10 -0
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +41 -0
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/checkpoints/index.d.ts +1 -0
- package/dist/checkpoints/index.d.ts.map +1 -1
- package/dist/checkpoints/index.js +1 -0
- package/dist/checkpoints/index.js.map +1 -1
- package/dist/checkpoints/route-match.d.ts +8 -0
- package/dist/checkpoints/route-match.d.ts.map +1 -0
- package/dist/checkpoints/route-match.js +13 -0
- package/dist/checkpoints/route-match.js.map +1 -0
- package/dist/cli/create.d.ts +35 -4
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +69 -19
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/index.js +19 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/template-baseline.d.ts +54 -0
- package/dist/cli/template-baseline.d.ts.map +1 -0
- package/dist/cli/template-baseline.js +334 -0
- package/dist/cli/template-baseline.js.map +1 -0
- package/dist/cli/template-sync.d.ts +57 -0
- package/dist/cli/template-sync.d.ts.map +1 -0
- package/dist/cli/template-sync.js +787 -0
- package/dist/cli/template-sync.js.map +1 -0
- package/dist/cli/templates-meta.d.ts.map +1 -1
- package/dist/cli/templates-meta.js +1 -0
- package/dist/cli/templates-meta.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +2 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +38 -10
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.d.ts.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.js +32 -2
- package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
- package/dist/client/chat/message-components.d.ts +20 -0
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +81 -46
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +14 -6
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/error-format.d.ts.map +1 -1
- package/dist/client/error-format.js +13 -0
- package/dist/client/error-format.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts +8 -1
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +3 -2
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/org/TeamPage.d.ts.map +1 -1
- package/dist/client/org/TeamPage.js +1 -1
- package/dist/client/org/TeamPage.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +3 -0
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/data-widgets/index.d.ts +3 -0
- package/dist/data-widgets/index.d.ts.map +1 -1
- package/dist/data-widgets/index.js +33 -0
- package/dist/data-widgets/index.js.map +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +11 -7
- package/dist/deploy/build.js.map +1 -1
- package/dist/eject/provider-api-definitions.d.ts +1 -0
- package/dist/eject/provider-api-definitions.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +46 -9
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/org/auto-join-domain.d.ts +3 -2
- package/dist/org/auto-join-domain.d.ts.map +1 -1
- package/dist/org/auto-join-domain.js +1 -1
- package/dist/org/auto-join-domain.js.map +1 -1
- package/dist/org/context.d.ts.map +1 -1
- package/dist/org/context.js +84 -28
- package/dist/org/context.js.map +1 -1
- package/dist/org/index.d.ts +1 -0
- package/dist/org/index.d.ts.map +1 -1
- package/dist/org/index.js +1 -0
- package/dist/org/index.js.map +1 -1
- package/dist/org/service-identity.d.ts +43 -0
- package/dist/org/service-identity.d.ts.map +1 -0
- package/dist/org/service-identity.js +34 -0
- package/dist/org/service-identity.js.map +1 -0
- package/dist/provider-api/actions/custom-provider-registration.d.ts +14 -14
- package/dist/provider-api/actions/provider-api.d.ts +13 -11
- package/dist/provider-api/actions/provider-api.d.ts.map +1 -1
- package/dist/provider-api/index.d.ts +4 -0
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/provider-api/index.js +17 -1
- package/dist/provider-api/index.js.map +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/docs/search.d.ts.map +1 -1
- package/dist/scripts/docs/search.js +12 -1
- package/dist/scripts/docs/search.js.map +1 -1
- package/dist/server/agent-chat/context-tools.d.ts.map +1 -1
- package/dist/server/agent-chat/context-tools.js +3 -3
- package/dist/server/agent-chat/context-tools.js.map +1 -1
- package/dist/server/agent-chat/framework-prompts.d.ts +8 -3
- package/dist/server/agent-chat/framework-prompts.d.ts.map +1 -1
- package/dist/server/agent-chat/framework-prompts.js +33 -62
- package/dist/server/agent-chat/framework-prompts.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +16 -10
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agents-bundle.d.ts +18 -0
- package/dist/server/agents-bundle.d.ts.map +1 -1
- package/dist/server/agents-bundle.js +70 -4
- package/dist/server/agents-bundle.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +4 -3
- package/dist/server/auth.js.map +1 -1
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +43 -19
- package/dist/server/builder-browser.js.map +1 -1
- package/dist/server/core-routes-plugin.js +2 -2
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/credential-provider.d.ts +38 -2
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +142 -64
- package/dist/server/credential-provider.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 +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/prompts/framework-core-compact.d.ts +1 -1
- package/dist/server/prompts/framework-core-compact.d.ts.map +1 -1
- package/dist/server/prompts/framework-core-compact.js +7 -13
- package/dist/server/prompts/framework-core-compact.js.map +1 -1
- package/dist/server/prompts/framework-core.d.ts +1 -1
- package/dist/server/prompts/framework-core.d.ts.map +1 -1
- package/dist/server/prompts/framework-core.js +10 -21
- package/dist/server/prompts/framework-core.js.map +1 -1
- package/dist/server/prompts/index.d.ts +1 -1
- package/dist/server/prompts/index.d.ts.map +1 -1
- package/dist/server/prompts/index.js +1 -1
- package/dist/server/prompts/index.js.map +1 -1
- package/dist/server/prompts/shared-rules.d.ts +16 -10
- package/dist/server/prompts/shared-rules.d.ts.map +1 -1
- package/dist/server/prompts/shared-rules.js +19 -10
- package/dist/server/prompts/shared-rules.js.map +1 -1
- package/dist/server/ssr-handler.d.ts +1 -1
- package/dist/server/ssr-handler.d.ts.map +1 -1
- package/dist/server/ssr-handler.js +10 -3
- package/dist/server/ssr-handler.js.map +1 -1
- package/dist/settings/user-settings.d.ts.map +1 -1
- package/dist/settings/user-settings.js +13 -1
- package/dist/settings/user-settings.js.map +1 -1
- package/dist/shared/cache-control.d.ts +52 -0
- package/dist/shared/cache-control.d.ts.map +1 -1
- package/dist/shared/cache-control.js +107 -0
- package/dist/shared/cache-control.js.map +1 -1
- package/dist/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/dist/templates/chat/_gitignore +1 -0
- package/dist/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/dist/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +19 -0
- package/dist/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/dist/templates/workspace-core/.agents/skills/visual-answer/SKILL.md +1 -0
- package/dist/vite/agents-bundle-plugin.d.ts.map +1 -1
- package/dist/vite/agents-bundle-plugin.js +8 -7
- package/dist/vite/agents-bundle-plugin.js.map +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +2 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/actions.mdx +2 -2
- package/docs/content/agent-surfaces.mdx +1 -1
- package/docs/content/authentication.mdx +2 -0
- package/docs/content/deployment.mdx +36 -0
- package/docs/content/http-api.mdx +266 -0
- package/docs/content/syncing-template-changes.mdx +159 -0
- package/package.json +4 -3
- package/src/a2a/activity.ts +38 -27
- package/src/agent/engine/builder-engine.ts +35 -4
- package/src/agent/run-manager.ts +20 -1
- package/src/agent/run-store.ts +4 -1
- package/src/agent/types.ts +28 -8
- package/src/chat-threads/store.ts +45 -0
- package/src/checkpoints/index.ts +2 -0
- package/src/checkpoints/route-match.ts +13 -0
- package/src/cli/create.ts +105 -17
- package/src/cli/index.ts +20 -0
- package/src/cli/template-baseline.ts +410 -0
- package/src/cli/template-sync.ts +1004 -0
- package/src/cli/templates-meta.ts +1 -0
- package/src/client/AssistantChat.tsx +38 -12
- package/src/client/NewWorkspaceAppFlow.tsx +75 -10
- package/src/client/chat/message-components.tsx +127 -66
- package/src/client/chat/tool-call-display.tsx +29 -5
- package/src/client/error-format.ts +18 -0
- package/src/client/org/OrgSwitcher.tsx +30 -8
- package/src/client/org/TeamPage.tsx +7 -6
- package/src/client/sse-event-processor.ts +3 -0
- package/src/data-widgets/index.ts +41 -0
- package/src/deploy/build.ts +11 -9
- package/src/integrations/webhook-handler.ts +63 -9
- package/src/org/auto-join-domain.ts +4 -3
- package/src/org/context.ts +108 -39
- package/src/org/index.ts +5 -0
- package/src/org/service-identity.ts +60 -0
- package/src/provider-api/index.ts +22 -1
- package/src/scripts/docs/search.ts +12 -1
- package/src/server/__snapshots__/agent-chat-plugin.surface.spec.ts.snap +25 -36
- package/src/server/agent-chat/context-tools.ts +9 -4
- package/src/server/agent-chat/framework-prompts.ts +38 -66
- package/src/server/agent-chat-plugin.ts +17 -10
- package/src/server/agents-bundle.ts +84 -5
- package/src/server/auth.ts +4 -3
- package/src/server/builder-browser.ts +47 -19
- package/src/server/core-routes-plugin.ts +2 -2
- package/src/server/credential-provider.ts +209 -68
- package/src/server/index.ts +3 -0
- package/src/server/prompts/framework-core-compact.ts +6 -13
- package/src/server/prompts/framework-core.ts +9 -21
- package/src/server/prompts/index.ts +0 -1
- package/src/server/prompts/shared-rules.ts +19 -11
- package/src/server/ssr-handler.ts +13 -2
- package/src/settings/user-settings.ts +13 -1
- package/src/shared/cache-control.ts +141 -0
- package/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/src/templates/chat/_gitignore +1 -0
- package/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +19 -0
- package/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
- package/src/templates/workspace-core/.agents/skills/visual-answer/SKILL.md +1 -0
- package/src/vite/agents-bundle-plugin.ts +7 -6
- package/src/vite/client.ts +5 -0
|
@@ -382,6 +382,31 @@ export const recordingViews = table("recording_views", {
|
|
|
382
382
|
viewedAt: text("viewed_at").notNull().default(now()),
|
|
383
383
|
});
|
|
384
384
|
|
|
385
|
+
// Agent views — one row per (clip, agent, time bucket). Deliberately separate
|
|
386
|
+
// from `recording_viewers` / `recording_views` so no human-view count can ever
|
|
387
|
+
// pick agents up by forgetting a filter: the human tables stay agent-free.
|
|
388
|
+
export const recordingAgentViews = table(
|
|
389
|
+
"recording_agent_views",
|
|
390
|
+
{
|
|
391
|
+
id: text("id").primaryKey(),
|
|
392
|
+
recordingId: text("recording_id").notNull(),
|
|
393
|
+
// sha256 of user-agent + request IP. Never stores the raw IP.
|
|
394
|
+
agentKey: text("agent_key").notNull(),
|
|
395
|
+
agentLabel: text("agent_label"),
|
|
396
|
+
// Time bucket that collapses one agent's burst of context/transcript/frame
|
|
397
|
+
// polls into a single view.
|
|
398
|
+
viewSessionId: text("view_session_id").notNull(),
|
|
399
|
+
firstSeenAt: text("first_seen_at").notNull().default(now()),
|
|
400
|
+
lastSeenAt: text("last_seen_at").notNull().default(now()),
|
|
401
|
+
requestCount: integer("request_count").notNull().default(1),
|
|
402
|
+
},
|
|
403
|
+
(view) => ({
|
|
404
|
+
recordingAgentViewSessionUnique: uniqueIndex(
|
|
405
|
+
"recording_agent_views_session_unique_idx",
|
|
406
|
+
).on(view.recordingId, view.agentKey, view.viewSessionId),
|
|
407
|
+
}),
|
|
408
|
+
);
|
|
409
|
+
|
|
385
410
|
// -----------------------------------------------------------------------------
|
|
386
411
|
// Meetings (Granola-style) — recording + transcript + AI notes anchored to
|
|
387
412
|
// a calendar event or an ad-hoc meeting block. Composes with the existing
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent views — counting when an *outside agent* reads a clip through the
|
|
3
|
+
* public agent APIs (`/api/agent-context.json`, `/api/agent-transcript.json`,
|
|
4
|
+
* `/api/agent-frame.jpg`), as opposed to a human opening the player.
|
|
5
|
+
*
|
|
6
|
+
* Those routes are agent-only surfaces: a human watching a clip never hits
|
|
7
|
+
* them, so a request on one is the signal. Counts live in their own table so
|
|
8
|
+
* every existing human-view query stays agent-free by construction.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
12
|
+
|
|
13
|
+
import { count, desc, eq, sql } from "drizzle-orm";
|
|
14
|
+
import { getRequestHeader, getRequestIP, type H3Event } from "h3";
|
|
15
|
+
|
|
16
|
+
import { getDb, schema } from "../db/index.js";
|
|
17
|
+
import { nanoid } from "./recordings.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* One agent's burst of context + transcript + frame polls is one view.
|
|
21
|
+
* Fixed windows mean a burst straddling a boundary counts twice; switch to
|
|
22
|
+
* last-seen-gap dedup if that ever shows up in the numbers.
|
|
23
|
+
*/
|
|
24
|
+
export const AGENT_VIEW_SESSION_MS = 30 * 60 * 1000;
|
|
25
|
+
|
|
26
|
+
const AGENT_LABELS: [RegExp, string][] = [
|
|
27
|
+
[/claude|anthropic/i, "Claude"],
|
|
28
|
+
[/chatgpt|gptbot|oai-searchbot|openai/i, "ChatGPT"],
|
|
29
|
+
[/perplexity/i, "Perplexity"],
|
|
30
|
+
[/gemini|google-extended|googleother/i, "Gemini"],
|
|
31
|
+
[/copilot/i, "Copilot"],
|
|
32
|
+
[/cursor/i, "Cursor"],
|
|
33
|
+
[/bingbot|bingpreview/i, "Bing"],
|
|
34
|
+
[/applebot/i, "Apple Intelligence"],
|
|
35
|
+
[/meta-external|facebookbot/i, "Meta AI"],
|
|
36
|
+
[/bytespider|amazonbot|ccbot|diffbot|youbot/i, "Crawler"],
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
export const UNKNOWN_AGENT_LABEL = "Agent";
|
|
40
|
+
|
|
41
|
+
export function agentLabelFromUserAgent(userAgent: string): string {
|
|
42
|
+
for (const [pattern, label] of AGENT_LABELS) {
|
|
43
|
+
if (pattern.test(userAgent)) return label;
|
|
44
|
+
}
|
|
45
|
+
return UNKNOWN_AGENT_LABEL;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function agentViewSessionId(
|
|
49
|
+
now: number,
|
|
50
|
+
bucketMs = AGENT_VIEW_SESSION_MS,
|
|
51
|
+
): string {
|
|
52
|
+
return String(Math.floor(now / bucketMs));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Hashed so an agent is distinguishable across polls without storing its IP. */
|
|
56
|
+
export function agentKeyFor(userAgent: string, ip: string): string {
|
|
57
|
+
return createHash("sha256")
|
|
58
|
+
.update(`${userAgent}|${ip}`)
|
|
59
|
+
.digest("hex")
|
|
60
|
+
.slice(0, 32);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Best-effort: never let view accounting fail an agent's read of a clip.
|
|
65
|
+
*/
|
|
66
|
+
export async function recordAgentView(
|
|
67
|
+
event: H3Event,
|
|
68
|
+
recordingId: string,
|
|
69
|
+
nowMs: number = Date.now(),
|
|
70
|
+
): Promise<void> {
|
|
71
|
+
try {
|
|
72
|
+
const userAgent = (getRequestHeader(event, "user-agent") ?? "").slice(
|
|
73
|
+
0,
|
|
74
|
+
512,
|
|
75
|
+
);
|
|
76
|
+
const ip = getRequestIP(event) || "unknown";
|
|
77
|
+
const now = new Date(nowMs).toISOString();
|
|
78
|
+
|
|
79
|
+
await getDb()
|
|
80
|
+
.insert(schema.recordingAgentViews)
|
|
81
|
+
.values({
|
|
82
|
+
id: nanoid(),
|
|
83
|
+
recordingId,
|
|
84
|
+
agentKey: agentKeyFor(userAgent, ip),
|
|
85
|
+
agentLabel: agentLabelFromUserAgent(userAgent),
|
|
86
|
+
viewSessionId: agentViewSessionId(nowMs),
|
|
87
|
+
firstSeenAt: now,
|
|
88
|
+
lastSeenAt: now,
|
|
89
|
+
requestCount: 1,
|
|
90
|
+
})
|
|
91
|
+
.onConflictDoUpdate({
|
|
92
|
+
target: [
|
|
93
|
+
schema.recordingAgentViews.recordingId,
|
|
94
|
+
schema.recordingAgentViews.agentKey,
|
|
95
|
+
schema.recordingAgentViews.viewSessionId,
|
|
96
|
+
],
|
|
97
|
+
set: {
|
|
98
|
+
lastSeenAt: now,
|
|
99
|
+
requestCount: sql`${schema.recordingAgentViews.requestCount} + 1`,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.warn("[agent-views] failed to record agent view:", err);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function countRecordingAgentViews(
|
|
108
|
+
recordingId: string,
|
|
109
|
+
): Promise<number> {
|
|
110
|
+
const [row] = await getDb()
|
|
111
|
+
.select({ value: count() })
|
|
112
|
+
.from(schema.recordingAgentViews)
|
|
113
|
+
.where(eq(schema.recordingAgentViews.recordingId, recordingId));
|
|
114
|
+
return Number(row?.value ?? 0);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export interface AgentViewerSummary {
|
|
118
|
+
agentLabel: string;
|
|
119
|
+
views: number;
|
|
120
|
+
lastSeenAt: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Agents that read this clip, most recent first, grouped by product label. */
|
|
124
|
+
export async function listRecordingAgentViewers(
|
|
125
|
+
recordingId: string,
|
|
126
|
+
limit = 8,
|
|
127
|
+
): Promise<AgentViewerSummary[]> {
|
|
128
|
+
const rows = await getDb()
|
|
129
|
+
.select({
|
|
130
|
+
agentLabel: schema.recordingAgentViews.agentLabel,
|
|
131
|
+
views: count(),
|
|
132
|
+
lastSeenAt: sql<string>`MAX(${schema.recordingAgentViews.lastSeenAt})`,
|
|
133
|
+
})
|
|
134
|
+
.from(schema.recordingAgentViews)
|
|
135
|
+
.where(eq(schema.recordingAgentViews.recordingId, recordingId))
|
|
136
|
+
.groupBy(schema.recordingAgentViews.agentLabel)
|
|
137
|
+
.orderBy(desc(sql`MAX(${schema.recordingAgentViews.lastSeenAt})`))
|
|
138
|
+
.limit(limit);
|
|
139
|
+
|
|
140
|
+
return rows.map((r) => ({
|
|
141
|
+
agentLabel: r.agentLabel || UNKNOWN_AGENT_LABEL,
|
|
142
|
+
views: Number(r.views ?? 0),
|
|
143
|
+
lastSeenAt: r.lastSeenAt,
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
} from "../../shared/transcript-segments.js";
|
|
32
32
|
import { resolveTranscriptPresentation } from "../../shared/transcript-status.js";
|
|
33
33
|
import { getDb, schema } from "../db/index.js";
|
|
34
|
+
import { recordAgentView } from "./agent-views.js";
|
|
34
35
|
import { verifySharePassword } from "./share-password.js";
|
|
35
36
|
|
|
36
37
|
export type PublicAgentRecording = typeof schema.recordings._.inferSelect;
|
|
@@ -290,6 +291,12 @@ export async function loadPublicAgentAccess(
|
|
|
290
291
|
}
|
|
291
292
|
}
|
|
292
293
|
|
|
294
|
+
// Every agent API route funnels through here, so this is the one place that
|
|
295
|
+
// sees an outside agent read a clip. Owner requests are previews, not views.
|
|
296
|
+
if (!viewerIsOwner) {
|
|
297
|
+
await recordAgentView(event, recording.id);
|
|
298
|
+
}
|
|
299
|
+
|
|
293
300
|
return {
|
|
294
301
|
ok: true,
|
|
295
302
|
access: {
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Explicit share-grant lookup for the direct `/r/:id` recording page.
|
|
3
|
+
*
|
|
4
|
+
* Lives beside `recording-page-access.ts` rather than inside it so that module
|
|
5
|
+
* stays free of database imports and their registration side effects. Every
|
|
6
|
+
* surface that decides whether a viewer may open the authenticated recording
|
|
7
|
+
* page — the player action and the public share endpoint that auto-redirects
|
|
8
|
+
* into it — must use this helper, or the share page and the page it redirects
|
|
9
|
+
* to will disagree and ping-pong.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
getRequestOrgId,
|
|
14
|
+
getRequestUserEmail,
|
|
15
|
+
} from "@agent-native/core/server/request-context";
|
|
16
|
+
import { and, eq, or, sql } from "drizzle-orm";
|
|
17
|
+
|
|
18
|
+
import { getDb, schema } from "../db/index.js";
|
|
19
|
+
import type { RecordingPageAccessRole } from "./recording-page-access.js";
|
|
20
|
+
import type { RecordingVisibility } from "./recordings.js";
|
|
21
|
+
|
|
22
|
+
export interface RecordingShareGrantInput {
|
|
23
|
+
recordingId: string;
|
|
24
|
+
role: RecordingPageAccessRole;
|
|
25
|
+
visibility: RecordingVisibility;
|
|
26
|
+
hasPassword: boolean;
|
|
27
|
+
/** Agent/MCP/A2A callers, which may open password-less public clips. */
|
|
28
|
+
isAgentCaller?: boolean;
|
|
29
|
+
/** Omit to read the ambient request context. */
|
|
30
|
+
userEmail?: string | null;
|
|
31
|
+
/** Omit to read the ambient request context. */
|
|
32
|
+
orgId?: string | null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function hasExplicitRecordingShare(
|
|
36
|
+
input: RecordingShareGrantInput,
|
|
37
|
+
): Promise<boolean> {
|
|
38
|
+
if (input.role === "owner") return true;
|
|
39
|
+
if (input.visibility !== "public") return false;
|
|
40
|
+
if (!input.hasPassword && input.isAgentCaller) return true;
|
|
41
|
+
|
|
42
|
+
const userEmail = (
|
|
43
|
+
input.userEmail === undefined ? getRequestUserEmail() : input.userEmail
|
|
44
|
+
)
|
|
45
|
+
?.trim()
|
|
46
|
+
.toLowerCase();
|
|
47
|
+
const orgId = input.orgId === undefined ? getRequestOrgId() : input.orgId;
|
|
48
|
+
|
|
49
|
+
const principals = [];
|
|
50
|
+
if (userEmail) {
|
|
51
|
+
principals.push(
|
|
52
|
+
and(
|
|
53
|
+
eq(schema.recordingShares.principalType, "user"),
|
|
54
|
+
sql`lower(${schema.recordingShares.principalId}) = ${userEmail}`,
|
|
55
|
+
),
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
if (orgId) {
|
|
59
|
+
principals.push(
|
|
60
|
+
and(
|
|
61
|
+
eq(schema.recordingShares.principalType, "org"),
|
|
62
|
+
eq(schema.recordingShares.principalId, orgId),
|
|
63
|
+
),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
if (principals.length === 0) return false;
|
|
67
|
+
|
|
68
|
+
const [share] = await getDb()
|
|
69
|
+
.select({ id: schema.recordingShares.id })
|
|
70
|
+
.from(schema.recordingShares)
|
|
71
|
+
.where(
|
|
72
|
+
and(
|
|
73
|
+
eq(schema.recordingShares.resourceId, input.recordingId),
|
|
74
|
+
or(...principals),
|
|
75
|
+
),
|
|
76
|
+
)
|
|
77
|
+
.limit(1);
|
|
78
|
+
return Boolean(share);
|
|
79
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { readAppState } from "@agent-native/core/application-state";
|
|
2
|
-
import { orgMembers } from "@agent-native/core/org";
|
|
2
|
+
import { implicitServiceOrgRole, orgMembers } from "@agent-native/core/org";
|
|
3
3
|
import { getSession } from "@agent-native/core/server";
|
|
4
4
|
import {
|
|
5
5
|
getRequestUserEmail,
|
|
6
6
|
getRequestOrgId,
|
|
7
7
|
} from "@agent-native/core/server/request-context";
|
|
8
|
-
import { and, desc, eq, sql } from "drizzle-orm";
|
|
8
|
+
import { and, count, desc, eq, sql } from "drizzle-orm";
|
|
9
9
|
import { HTTPError, type H3Event } from "h3";
|
|
10
10
|
|
|
11
11
|
import { getDb, schema } from "../db/index.js";
|
|
@@ -142,7 +142,11 @@ export async function getOrganizationRoleForEmail(
|
|
|
142
142
|
// org_members table may not exist yet on first boot before migrations finish.
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
return
|
|
145
|
+
return implicitServiceOrgRole({
|
|
146
|
+
email,
|
|
147
|
+
orgId: organizationId,
|
|
148
|
+
requestOrgId: getRequestOrgId(),
|
|
149
|
+
});
|
|
146
150
|
}
|
|
147
151
|
|
|
148
152
|
export async function requireOrganizationAccess(
|
|
@@ -362,3 +366,47 @@ export function shouldCountView(
|
|
|
362
366
|
): boolean {
|
|
363
367
|
return totalWatchMs >= 5000 || completedPct >= 75 || scrubbedToEnd;
|
|
364
368
|
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* The single definition of a counted *viewer*: one `recording_viewers` row
|
|
372
|
+
* whose `countedView` flag is set. That is one row per person, so it answers
|
|
373
|
+
* "how many distinct viewers", not "how many views" — use
|
|
374
|
+
* `countRecordingViews` for the total. The in-memory twin is
|
|
375
|
+
* `isCountedViewerRow` in `shared/view-analytics.ts`.
|
|
376
|
+
*/
|
|
377
|
+
export function countedViewCondition() {
|
|
378
|
+
return eq(schema.recordingViewers.countedView, true);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Total views for a recording: one per counted view *session*, so a returning
|
|
383
|
+
* viewer's second visit counts again. Every surface that reports a view count
|
|
384
|
+
* (library list, insights, player, public share page) goes through this.
|
|
385
|
+
*/
|
|
386
|
+
export async function countRecordingViews(
|
|
387
|
+
recordingId: string,
|
|
388
|
+
): Promise<number> {
|
|
389
|
+
const db = getDb();
|
|
390
|
+
const [viewerRow] = await db
|
|
391
|
+
.select({ value: count() })
|
|
392
|
+
.from(schema.recordingViewers)
|
|
393
|
+
.where(
|
|
394
|
+
and(
|
|
395
|
+
eq(schema.recordingViewers.recordingId, recordingId),
|
|
396
|
+
countedViewCondition(),
|
|
397
|
+
),
|
|
398
|
+
);
|
|
399
|
+
const [viewLogRow] = await db
|
|
400
|
+
.select({ value: count() })
|
|
401
|
+
.from(schema.recordingViews)
|
|
402
|
+
.where(eq(schema.recordingViews.recordingId, recordingId));
|
|
403
|
+
|
|
404
|
+
// `recording_views` only exists from migration v46, so clips recorded before
|
|
405
|
+
// it have zero log rows. Floor the total at the counted-viewer count so those
|
|
406
|
+
// clips keep reporting a real number instead of dropping to 0, and so the
|
|
407
|
+
// total can never read below the unique-viewer count beside it.
|
|
408
|
+
return Math.max(
|
|
409
|
+
Number(viewLogRow?.value ?? 0),
|
|
410
|
+
Number(viewerRow?.value ?? 0),
|
|
411
|
+
);
|
|
412
|
+
}
|
|
@@ -850,6 +850,29 @@ const migrations = runMigrations(
|
|
|
850
850
|
`UPDATE organization_settings SET default_visibility = 'public' WHERE default_visibility = 'private' AND updated_at = created_at`,
|
|
851
851
|
].join("; "),
|
|
852
852
|
},
|
|
853
|
+
// -------------------------------------------------------------------------
|
|
854
|
+
// Agent views — external agents polling a public clip's agent context,
|
|
855
|
+
// transcript, or frame APIs. Kept in its own table so human view counts
|
|
856
|
+
// cannot accidentally include agents.
|
|
857
|
+
// -------------------------------------------------------------------------
|
|
858
|
+
{
|
|
859
|
+
version: 51,
|
|
860
|
+
name: "recording-agent-views",
|
|
861
|
+
sql: [
|
|
862
|
+
`CREATE TABLE IF NOT EXISTS recording_agent_views (
|
|
863
|
+
id TEXT PRIMARY KEY,
|
|
864
|
+
recording_id TEXT NOT NULL,
|
|
865
|
+
agent_key TEXT NOT NULL,
|
|
866
|
+
agent_label TEXT,
|
|
867
|
+
view_session_id TEXT NOT NULL,
|
|
868
|
+
first_seen_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
869
|
+
last_seen_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
870
|
+
request_count INTEGER NOT NULL DEFAULT 1
|
|
871
|
+
)`,
|
|
872
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS recording_agent_views_session_unique_idx ON recording_agent_views (recording_id, agent_key, view_session_id)`,
|
|
873
|
+
`CREATE INDEX IF NOT EXISTS recording_agent_views_recording_idx ON recording_agent_views (recording_id, last_seen_at)`,
|
|
874
|
+
].join("; "),
|
|
875
|
+
},
|
|
853
876
|
],
|
|
854
877
|
{ table: "clips_migrations" },
|
|
855
878
|
);
|
|
@@ -46,8 +46,16 @@ import { isMediaVerificationPending } from "../../lib/media-verification-state.j
|
|
|
46
46
|
import { resolvePlayerThumbnailUrl } from "../../lib/player-thumbnail-url.js";
|
|
47
47
|
import { resolvePlayerVideoUrl } from "../../lib/player-video-url.js";
|
|
48
48
|
import {
|
|
49
|
+
canOpenDirectRecordingPage,
|
|
50
|
+
isRecordingExpired,
|
|
51
|
+
type RecordingPageAccessRole,
|
|
52
|
+
} from "../../lib/recording-page-access.js";
|
|
53
|
+
import { hasExplicitRecordingShare } from "../../lib/recording-share-grant.js";
|
|
54
|
+
import {
|
|
55
|
+
countRecordingViews,
|
|
49
56
|
getOrganizationRoleForEmail,
|
|
50
57
|
parseSpaceIds,
|
|
58
|
+
type RecordingVisibility,
|
|
51
59
|
} from "../../lib/recordings.js";
|
|
52
60
|
import { verifySharePassword } from "../../lib/share-password.js";
|
|
53
61
|
|
|
@@ -258,12 +266,10 @@ export default defineEventHandler(async (event) => {
|
|
|
258
266
|
}
|
|
259
267
|
|
|
260
268
|
// Expiry check
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
return { error: "Recording has expired", expired: true };
|
|
266
|
-
}
|
|
269
|
+
const recordingExpired = isRecordingExpired(rec.expiresAt);
|
|
270
|
+
if (recordingExpired) {
|
|
271
|
+
setResponseStatus(event, 410);
|
|
272
|
+
return { error: "Recording has expired", expired: true };
|
|
267
273
|
}
|
|
268
274
|
|
|
269
275
|
// Password check
|
|
@@ -405,6 +411,32 @@ export default defineEventHandler(async (event) => {
|
|
|
405
411
|
recordingStatus: rec.status,
|
|
406
412
|
});
|
|
407
413
|
|
|
414
|
+
const viewCount = await countRecordingViews(recordingId);
|
|
415
|
+
|
|
416
|
+
const viewerRole =
|
|
417
|
+
viewerAccess?.role ?? (viewerIsOrgMember ? "viewer" : null);
|
|
418
|
+
// Mirrors the gate in `get-recording-player-data` exactly: the share page
|
|
419
|
+
// auto-redirects on this flag, so a false positive bounces the viewer
|
|
420
|
+
// between /share/:id and /r/:id forever. Only a resolved access role can
|
|
421
|
+
// open the direct page — the org-member fallback above is a display role,
|
|
422
|
+
// not access the player action would grant.
|
|
423
|
+
const canOpenDashboard =
|
|
424
|
+
Boolean(session?.email) && viewerAccess && !recordingExpired
|
|
425
|
+
? canOpenDirectRecordingPage({
|
|
426
|
+
role: viewerAccess.role as RecordingPageAccessRole,
|
|
427
|
+
visibility: rec.visibility as RecordingVisibility,
|
|
428
|
+
hasPassword: Boolean(rec.password),
|
|
429
|
+
hasExplicitShare: await hasExplicitRecordingShare({
|
|
430
|
+
recordingId,
|
|
431
|
+
role: viewerAccess.role as RecordingPageAccessRole,
|
|
432
|
+
visibility: rec.visibility as RecordingVisibility,
|
|
433
|
+
hasPassword: Boolean(rec.password),
|
|
434
|
+
userEmail: session?.email ?? null,
|
|
435
|
+
orgId: session?.orgId ?? null,
|
|
436
|
+
}),
|
|
437
|
+
})
|
|
438
|
+
: false;
|
|
439
|
+
|
|
408
440
|
return {
|
|
409
441
|
recording: {
|
|
410
442
|
id: rec.id,
|
|
@@ -439,6 +471,8 @@ export default defineEventHandler(async (event) => {
|
|
|
439
471
|
updatedAt: rec.updatedAt,
|
|
440
472
|
},
|
|
441
473
|
agentContextUrl,
|
|
474
|
+
// Aggregate count only — never viewer identities on this public payload.
|
|
475
|
+
viewCount,
|
|
442
476
|
transcript: transcript
|
|
443
477
|
? {
|
|
444
478
|
status: transcriptPresentation.status,
|
|
@@ -479,17 +513,15 @@ export default defineEventHandler(async (event) => {
|
|
|
479
513
|
placement: c.placement,
|
|
480
514
|
})),
|
|
481
515
|
viewer: session?.email
|
|
482
|
-
?
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
};
|
|
492
|
-
})()
|
|
516
|
+
? {
|
|
517
|
+
canEdit:
|
|
518
|
+
viewerRole === "owner" ||
|
|
519
|
+
viewerRole === "admin" ||
|
|
520
|
+
viewerRole === "editor",
|
|
521
|
+
isOwner: viewerRole === "owner",
|
|
522
|
+
role: viewerRole ?? "viewer",
|
|
523
|
+
canOpenDashboard,
|
|
524
|
+
}
|
|
493
525
|
: null,
|
|
494
526
|
};
|
|
495
527
|
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical share URL for a recording, shared by every surface that hands a
|
|
3
|
+
* user a link to paste: the web recorder, the desktop app, and the Chrome
|
|
4
|
+
* extension.
|
|
5
|
+
*
|
|
6
|
+
* `/share/<id>` is the public viewer page — SSR-rendered, password/expiry
|
|
7
|
+
* aware, and the shape Slack unfurls. `/r/<id>` is the owner dashboard: it
|
|
8
|
+
* renders client-side only and shows a sign-in prompt to a recipient. Copying
|
|
9
|
+
* `/r/<id>` produces a link that works for the author and looks broken to
|
|
10
|
+
* everyone they send it to, so share flows must build URLs from here rather
|
|
11
|
+
* than hand-rolling a path.
|
|
12
|
+
*/
|
|
13
|
+
import { withShareAttribution } from "./share-attribution";
|
|
14
|
+
|
|
15
|
+
/** Public share path for a recording, relative to the app base path. */
|
|
16
|
+
export function recordingSharePath(recordingId: string): string {
|
|
17
|
+
return `/share/${encodeURIComponent(recordingId)}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface RecordingShareUrlParams {
|
|
21
|
+
recordingId: string;
|
|
22
|
+
/** Absolute origin, e.g. `https://clips.example.com` or a desktop serverUrl. */
|
|
23
|
+
origin: string;
|
|
24
|
+
/** App base path when the app is mounted under a subpath. */
|
|
25
|
+
basePath?: string;
|
|
26
|
+
/** Non-PII owner id for viral attribution. Omitted when unknown. */
|
|
27
|
+
ownerId?: string | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Absolute, ready-to-paste share URL for a recording, carrying the same
|
|
32
|
+
* attribution params the Share dialog mints so auto-copied links measure the
|
|
33
|
+
* signup funnel identically.
|
|
34
|
+
*/
|
|
35
|
+
export function buildRecordingShareUrl(
|
|
36
|
+
params: RecordingShareUrlParams,
|
|
37
|
+
): string {
|
|
38
|
+
const { recordingId, origin, basePath = "", ownerId } = params;
|
|
39
|
+
const trimmedOrigin = origin.trim().replace(/\/+$/, "");
|
|
40
|
+
const trimmedBase = basePath.trim().replace(/\/+$/, "");
|
|
41
|
+
const url = `${trimmedOrigin}${trimmedBase}${recordingSharePath(recordingId)}`;
|
|
42
|
+
return withShareAttribution(url, ownerId);
|
|
43
|
+
}
|
|
@@ -21,6 +21,10 @@ export const CLIP_SHARE_REF = "clip_share";
|
|
|
21
21
|
export const REF_PARAM = "ref";
|
|
22
22
|
export const VIA_PARAM = "via";
|
|
23
23
|
|
|
24
|
+
/** Marker used only when `/r/:id` falls back to the public share route. */
|
|
25
|
+
export const DASHBOARD_REDIRECT_PARAM = "dashboard_redirect";
|
|
26
|
+
export const DASHBOARD_REDIRECT_VALUE = "1";
|
|
27
|
+
|
|
24
28
|
/**
|
|
25
29
|
* Append `ref=clip_share` (and `via=<ownerId>` when a non-PII owner id is
|
|
26
30
|
* known) to an absolute share/embed URL, preserving any existing query params.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DASHBOARD_REDIRECT_PARAM,
|
|
3
|
+
DASHBOARD_REDIRECT_VALUE,
|
|
4
|
+
} from "./share-attribution.js";
|
|
5
|
+
|
|
6
|
+
/** Query params worth carrying from /share/:id over to /r/:id. */
|
|
7
|
+
const FORWARDED_PARAMS = ["t", "panel"] as const;
|
|
8
|
+
|
|
9
|
+
export interface DashboardRedirectInput {
|
|
10
|
+
recordingId: string | null | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* The server's `canOpenDirectRecordingPage` verdict, not a display role.
|
|
13
|
+
* /r sends anyone it rejects back to /share, so a looser predicate here
|
|
14
|
+
* bounces the viewer between the two routes forever.
|
|
15
|
+
*/
|
|
16
|
+
canOpenDashboard: boolean;
|
|
17
|
+
/** Current location search string, with or without the leading `?`. */
|
|
18
|
+
search: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Where a share-link visitor should be sent instead of rendering /share/:id,
|
|
23
|
+
* or null to stay put.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveDashboardRedirect(
|
|
26
|
+
input: DashboardRedirectInput,
|
|
27
|
+
): string | null {
|
|
28
|
+
if (!input.canOpenDashboard || !input.recordingId) return null;
|
|
29
|
+
|
|
30
|
+
const params = new URLSearchParams(input.search);
|
|
31
|
+
if (params.get(DASHBOARD_REDIRECT_PARAM) === DASHBOARD_REDIRECT_VALUE)
|
|
32
|
+
return null;
|
|
33
|
+
|
|
34
|
+
const forwarded = new URLSearchParams();
|
|
35
|
+
for (const key of FORWARDED_PARAMS) {
|
|
36
|
+
const value = params.get(key);
|
|
37
|
+
if (value) forwarded.set(key, value);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const query = forwarded.toString();
|
|
41
|
+
return `/r/${encodeURIComponent(input.recordingId)}${query ? `?${query}` : ""}`;
|
|
42
|
+
}
|
|
@@ -2,3 +2,29 @@ export function clampCompletionPct(value: number | null | undefined): number {
|
|
|
2
2
|
if (typeof value !== "number" || !Number.isFinite(value)) return 0;
|
|
3
3
|
return Math.min(100, Math.max(0, value));
|
|
4
4
|
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* In-memory twin of `countedViewCondition` / `countRecordingViews` in
|
|
8
|
+
* `server/lib/recordings.ts`: a view is one `recording_viewers` row whose
|
|
9
|
+
* `countedView` flag is set. Use it whenever the rows are already loaded so
|
|
10
|
+
* every surface reports the same number.
|
|
11
|
+
*/
|
|
12
|
+
export function isCountedViewerRow(row: { countedView?: unknown }): boolean {
|
|
13
|
+
return Boolean(row.countedView);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const ANONYMOUS_VIEWER_NAME_PREFIX = "anon:";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Anonymous rows keep their `anon:<sessionId>` dedup key in `viewer_name`.
|
|
20
|
+
* That key is storage plumbing, never a display name — return null so callers
|
|
21
|
+
* render their own "Someone" placeholder instead of leaking the session key.
|
|
22
|
+
*/
|
|
23
|
+
export function displayViewerName(
|
|
24
|
+
viewerName: string | null | undefined,
|
|
25
|
+
): string | null {
|
|
26
|
+
if (viewerName == null) return null;
|
|
27
|
+
return viewerName.startsWith(ANONYMOUS_VIEWER_NAME_PREFIX)
|
|
28
|
+
? null
|
|
29
|
+
: viewerName;
|
|
30
|
+
}
|
|
@@ -237,6 +237,11 @@ File access still requires a local Content app, Agent Native Desktop, or another
|
|
|
237
237
|
trusted bridge. Disconnecting a folder leaves the SQL pages and disk files in
|
|
238
238
|
place. Concurrent edits and missing source files require explicit review.
|
|
239
239
|
|
|
240
|
+
See **`references/local-file-mode.md`** for the full folder-source Pull/Check/Push
|
|
241
|
+
workflow, the manifest/CLI launch commands, Builder Symbols and Builder
|
|
242
|
+
source-component preservation rules, picked-folder/Desktop component
|
|
243
|
+
previews, and the agent component-edit workflow.
|
|
244
|
+
|
|
240
245
|
## MDX And Components
|
|
241
246
|
|
|
242
247
|
- Preserve frontmatter keys you do not understand. Preserve MDX imports,
|