@agent-native/core 0.169.1 → 0.171.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-native.eject.json +4 -1
- package/bin/agent-native.js +23 -5
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +1 -1
- package/corpus/templates/analytics/app/i18n-data.ts +28 -0
- package/corpus/templates/analytics/app/pages/Agents.tsx +25 -1
- package/corpus/templates/analytics/app/routes/$.tsx +10 -0
- package/corpus/templates/analytics/app/routes.ts +3 -1
- package/corpus/templates/analytics/changelog/2026-08-22-fixed-the-agent-native-templates-first-party-dashboard-panel.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-22-org-admin-panels-no-longer-report-a-database-error-as-a-per.md +9 -0
- package/corpus/templates/analytics/changelog/2026-08-22-slack-requests-report-invalid-inputs.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-22-unknown-analytics-links-now-return-a-real-not-found-status.md +6 -0
- package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +2 -1
- package/corpus/templates/analytics/docs/schemas/tracked-events.md +2 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +29 -7
- package/corpus/templates/analytics/server/handlers/slack.ts +59 -5
- package/corpus/templates/analytics/server/lib/db-admin-connections.ts +11 -6
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +132 -2
- package/corpus/templates/analytics/server/lib/slack.ts +4 -2
- package/corpus/templates/analytics/vitest.config.ts +6 -1
- package/corpus/templates/assets/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/assets/app/routes/library.tsx +5 -3
- package/corpus/templates/assets/app/routes.ts +3 -1
- package/corpus/templates/assets/changelog/2026-08-22-library-page-loads-again-instead-of-crashing-on-open.md +6 -0
- package/corpus/templates/assets/vitest.config.ts +6 -1
- package/corpus/templates/brain/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/brain/app/components/ui/pointer-lock.ts +1 -0
- package/corpus/templates/brain/app/routes/sources.tsx +8 -3
- package/corpus/templates/brain/changelog/2026-08-22-tuning-or-adding-a-source-no-longer-leaves-the-sources-page.md +6 -0
- package/corpus/templates/brain/vitest.config.ts +1 -0
- package/corpus/templates/calendar/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/calendar/actions/list-booking-links.ts +83 -4
- package/corpus/templates/calendar/actions/update-booking-link.ts +4 -3
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +11 -3
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +1 -1
- package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +103 -44
- package/corpus/templates/calendar/app/components/calendar/OutOfOfficeEvent.tsx +28 -24
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +9 -2
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +47 -58
- package/corpus/templates/calendar/app/hooks/use-booking-links.ts +5 -1
- package/corpus/templates/calendar/app/lib/event-layout.ts +52 -26
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +126 -98
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +1 -0
- package/corpus/templates/calendar/app/pages/Settings.tsx +88 -81
- package/corpus/templates/calendar/app/routes.ts +3 -1
- package/corpus/templates/calendar/changelog/2026-08-22-booking-link-visibility-respects-shared-access.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-22-clicking-an-out-of-office-event-s-marker-no-longer-creates-a.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-22-event-detail-views-now-show-your-calendar-timezone.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-22-google-calendar-can-now-connect-with-the-shared-google-oauth.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-22-overlapping-events-now-show-each-others-boundaries.md +6 -0
- package/corpus/templates/calendar/changelog/2026-08-22-public-booking-pages-now-offer-time-slots-from-the-host-s-we.md +6 -0
- package/corpus/templates/calendar/server/handlers/bookings.ts +5 -7
- package/corpus/templates/calendar/server/handlers/google-auth.ts +10 -2
- package/corpus/templates/calendar/server/lib/google-calendar.ts +139 -14
- package/corpus/templates/calendar/server/onboarding.ts +85 -0
- package/corpus/templates/calendar/server/plugins/agent-chat.ts +2 -0
- package/corpus/templates/calendar/shared/api.ts +9 -1
- package/corpus/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/chat/app/routes.ts +3 -1
- package/corpus/templates/chat/vitest.config.ts +8 -0
- package/corpus/templates/clips/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/clips/actions/get-meeting.ts +9 -4
- package/corpus/templates/clips/actions/list-recordings.ts +16 -3
- package/corpus/templates/clips/actions/search-meetings.ts +1 -0
- package/corpus/templates/clips/actions/view-screen.ts +8 -1
- package/corpus/templates/clips/app/components/meetings/meeting-history-row.tsx +32 -1
- package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +1 -1
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +29 -1
- package/corpus/templates/clips/app/i18n/en-US.ts +5 -0
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +33 -4
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +1 -0
- package/corpus/templates/clips/app/routes/access-request.approve.tsx +16 -9
- package/corpus/templates/clips/app/routes/embed.$shareId.tsx +17 -7
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +17 -7
- package/corpus/templates/clips/changelog/2026-08-21-camera-bubble-black-circle.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-22-screen-recordings-respect-the-microphone-toggle.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-22-the-recordings-library-no-longer-fails-right-after-server-start.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-23-the-agent-can-see-your-library-right-after-server-start.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/background.ts +1 -2
- package/corpus/templates/clips/chrome-extension/src/native-transcription.ts +39 -7
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +21 -25
- package/corpus/templates/clips/desktop/src/app.tsx +53 -24
- package/corpus/templates/clips/desktop/src/lib/bubble-playback.ts +68 -0
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +3 -0
- package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +72 -14
- package/corpus/templates/clips/desktop/src/lib/updater.ts +8 -3
- package/corpus/templates/clips/desktop/src/overlays/bubble.tsx +162 -20
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +43 -6
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +150 -13
- package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +12 -0
- package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +28 -3
- package/corpus/templates/clips/server/lib/emails.ts +17 -33
- package/corpus/templates/content/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +140 -15
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +12 -14
- package/corpus/templates/content/app/components/ui/pointer-lock.ts +1 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +18 -2
- package/corpus/templates/content/app/hooks/use-local-storage.ts +38 -22
- package/corpus/templates/content/app/i18n-data.ts +25 -0
- package/corpus/templates/content/app/routes.ts +3 -1
- package/corpus/templates/content/changelog/2026-08-22-code-blocks-inserted-from-the-slash-menu-no-longer-get-stuck.md +6 -0
- package/corpus/templates/content/changelog/2026-08-22-the-sidebar-page-tree-no-longer-flashes-empty-after-creating-a-page.md +6 -0
- package/corpus/templates/content/docs/solutions/2026-08-21-content-mermaid-rendering-shape.md +233 -0
- package/corpus/templates/content/server/plugins/agent-chat.ts +9 -2
- package/corpus/templates/content/vitest.config.ts +7 -1
- package/corpus/templates/crm/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/design/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +1383 -88
- package/corpus/templates/design/actions/generate-design.ts +98 -47
- package/corpus/templates/design/actions/get-design-system.ts +6 -1
- package/corpus/templates/design/actions/refresh-design-system-with-builder.ts +146 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +70 -26
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +284 -136
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +2057 -141
- package/corpus/templates/design/app/components/design/editor/DesignBottomToolbar.tsx +6 -2
- package/corpus/templates/design/app/components/design/editor/DesignWorkspaceRail.tsx +4 -4
- package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +10 -1
- package/corpus/templates/design/app/components/design/multi-screen/alt-hover-measurement.ts +19 -2
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +9 -2
- package/corpus/templates/design/app/components/layout/Header.tsx +15 -0
- package/corpus/templates/design/app/components/layout/Layout.tsx +7 -2
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +11 -4
- package/corpus/templates/design/app/i18n-data.ts +1 -0
- package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +12 -0
- package/corpus/templates/design/app/lib/design-system-data.ts +68 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +77 -35
- package/corpus/templates/design/app/pages/DesignSystems.tsx +136 -42
- package/corpus/templates/design/app/pages/Index.tsx +2 -0
- package/corpus/templates/design/app/pages/Templates.tsx +0 -10
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +10 -0
- package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +50 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-design-editor-command.ts +1 -1
- package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +10 -8
- package/corpus/templates/design/app/pages/design-editor/commands/apply-local-content-update.ts +6 -6
- package/corpus/templates/design/app/pages/design-editor/commands/apply-pending-visual-styles-with-agent.ts +1 -1
- package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +18 -7
- package/corpus/templates/design/app/pages/design-editor/commands/escape-hotkey.ts +4 -90
- package/corpus/templates/design/app/pages/design-editor/commands/layer-marquee-selection-change.ts +2 -1
- package/corpus/templates/design/app/pages/design-editor/commands/layer-selection-change.ts +4 -3
- package/corpus/templates/design/app/pages/design-editor/commands/screen-element-select.ts +12 -2
- package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-style-change.ts +9 -2
- package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-move-semantic-handoff.ts +1 -1
- package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-semantic-handoff.ts +1 -1
- package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-state-semantic-handoff.ts +1 -1
- package/corpus/templates/design/app/pages/design-editor/commands/start-sidebar-resize.ts +1 -1
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +11 -1
- package/corpus/templates/design/app/pages/design-editor/effects/adopt-db-file-content.ts +11 -19
- package/corpus/templates/design/app/pages/design-editor/effects/observe-collab-text.ts +6 -6
- package/corpus/templates/design/app/pages/design-editor/effects/publish-agent-selection-context.ts +1 -1
- package/corpus/templates/design/app/pages/design-editor/effects/seed-collab-content.ts +12 -15
- package/corpus/templates/design/app/pages/design-editor/selection-state.ts +5 -59
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +8 -0
- package/corpus/templates/design/app/pages/design-editor/types.ts +3 -3
- package/corpus/templates/design/app/routes.ts +3 -1
- package/corpus/templates/design/changelog/2026-08-20-canvas-edits-patch-the-frame-instead-of-rebuilding-it.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-builder-design-system-imports-reconcile-after-indexing.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-draw-mode-needs-shift-y.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-eyedropper-shortcut.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-figma-style-alignment-guides-and-snapping.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-group-transforms-keep-the-selection.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-inspector-fields-keep-focus-on-enter.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-pen-alt-keeps-the-incoming-handle.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-pen-on-the-board-creates-one-vector.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-scale-a-multi-selection-from-the-group-handles.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-scale-resizes-text-with-the-object.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-selecting-another-object-keeps-the-scale-tool-armed.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-the-shape-tool-remembers-the-shape-you-picked.md +6 -0
- package/corpus/templates/design/changelog/2026-08-21-vectors-drawn-in-a-frame-land-where-you-drew-them.md +6 -0
- package/corpus/templates/design/changelog/2026-08-22-multi-screen-generation-no-longer-loses-a-saved-screens-layo.md +6 -0
- package/corpus/templates/design/changelog/2026-08-22-rename-and-search-fields-are-labeled-for-screen-readers.md +6 -0
- package/corpus/templates/design/package.json +2 -0
- package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +311 -0
- package/corpus/templates/design/shared/canvas-math.ts +612 -206
- package/corpus/templates/design/shared/pen-path.ts +37 -0
- package/corpus/templates/design/vitest.config.ts +7 -1
- package/corpus/templates/dispatch/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/dispatch/actions/list-workspace-connections.ts +34 -3
- package/corpus/templates/dispatch/app/routes/integrations.tsx +55 -21
- package/corpus/templates/dispatch/app/routes.ts +3 -1
- package/corpus/templates/factory/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/factory/.env.example +2 -7
- package/corpus/templates/factory/AGENTS.md +3 -0
- package/corpus/templates/factory/README.md +5 -3
- package/corpus/templates/factory/actions/create-factory.ts +16 -3
- package/corpus/templates/factory/actions/get-slack-feedback-context.ts +2 -0
- package/corpus/templates/factory/actions/get-triage-item.ts +2 -0
- package/corpus/templates/factory/actions/list-factory-audit.ts +116 -36
- package/corpus/templates/factory/actions/list-factory-automations.ts +2 -1
- package/corpus/templates/factory/actions/list-triage-items.ts +22 -19
- package/corpus/templates/factory/actions/run-factory-automation.ts +7 -3
- package/corpus/templates/factory/actions/save-factory-automation.ts +5 -2
- package/corpus/templates/factory/actions/save-triage-config.ts +2 -8
- package/corpus/templates/factory/actions/start-builder-for-item.ts +21 -0
- package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +229 -297
- package/corpus/templates/factory/app/components/factory/FactorySettingsView.tsx +397 -247
- package/corpus/templates/factory/app/components/factory/FactorySourceSettingsGroup.tsx +33 -0
- package/corpus/templates/factory/app/components/factory/NewFactoryForm.tsx +202 -162
- package/corpus/templates/factory/app/i18n/en-US.ts +32 -4
- package/corpus/templates/factory/app/routes/factory.tsx +84 -11
- package/corpus/templates/factory/app/routes.ts +3 -1
- package/corpus/templates/factory/changelog/2026-08-21-activity-runs-use-automation-display-names.md +6 -0
- package/corpus/templates/factory/changelog/2026-08-21-create-and-settings-source-cards.md +6 -0
- package/corpus/templates/factory/changelog/2026-08-21-keep-automations-visible-while-running.md +6 -0
- package/corpus/templates/factory/changelog/2026-08-21-nested-factory-automations-can-poll.md +6 -0
- package/corpus/templates/factory/changelog/2026-08-21-settings-sticky-unsaved-bar.md +6 -0
- package/corpus/templates/factory/server/connectors/credentials.ts +10 -5
- package/corpus/templates/factory/server/lib/factory-audit-report.ts +412 -0
- package/corpus/templates/factory/server/lib/factory-config-reconcile.ts +321 -0
- package/corpus/templates/factory/server/lib/factory-scope.ts +25 -2
- package/corpus/templates/factory/server/lib/require-factory-automation.ts +13 -4
- package/corpus/templates/factory/server/plugins/factory-migrations.ts +148 -4
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +4 -6
- package/corpus/templates/factory/vitest.config.ts +17 -2
- package/corpus/templates/forms/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/forms/app/routes/_app.settings.tsx +4 -1
- package/corpus/templates/forms/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
- package/corpus/templates/forms/server/lib/public-form-ssr.ts +11 -1
- package/corpus/templates/forms/vitest.config.ts +1 -0
- package/corpus/templates/macros/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/macros/app/routes.ts +3 -1
- package/corpus/templates/macros/vitest.config.ts +6 -1
- package/corpus/templates/mail/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/mail/actions/manage-gmail-filters.ts +10 -2
- package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +100 -37
- package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +14 -2
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +66 -36
- package/corpus/templates/mail/app/hooks/use-google-auth.ts +2 -0
- package/corpus/templates/mail/app/lib/google-oauth-setup.ts +17 -0
- package/corpus/templates/mail/app/pages/InboxPage.tsx +69 -9
- package/corpus/templates/mail/app/routes/$.tsx +9 -0
- package/corpus/templates/mail/app/routes/$view.$threadId.tsx +23 -1
- package/corpus/templates/mail/app/routes/$view.tsx +47 -1
- package/corpus/templates/mail/app/routes.ts +3 -1
- package/corpus/templates/mail/changelog/2026-08-22-gmail-can-now-connect-with-the-shared-google-oauth.md +6 -0
- package/corpus/templates/mail/changelog/2026-08-22-gmail-filters-now-shows-the-real-error.md +6 -0
- package/corpus/templates/mail/changelog/2026-08-22-unknown-mail-urls-now-show-the-404-page.md +6 -0
- package/corpus/templates/mail/server/handlers/google-auth.ts +11 -2
- package/corpus/templates/mail/server/lib/google-auth.ts +92 -12
- package/corpus/templates/mail/server/onboarding.ts +66 -18
- package/corpus/templates/mail/vitest.config.ts +6 -1
- package/corpus/templates/plan/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/plan/app/routes/settings.tsx +4 -1
- package/corpus/templates/plan/app/routes.ts +3 -1
- package/corpus/templates/plan/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
- package/corpus/templates/plan/netlify.toml +1 -1
- package/corpus/templates/slides/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/slides/actions/patch-deck.ts +79 -10
- package/corpus/templates/slides/actions/update-slide.ts +11 -1
- package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +14 -4
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +10 -52
- package/corpus/templates/slides/app/components/editor/GoogleDocImportHint.tsx +8 -53
- package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +9 -75
- package/corpus/templates/slides/app/components/editor/GoogleSlidesReferenceImport.tsx +8 -60
- package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +10 -2
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +19 -7
- package/corpus/templates/slides/app/lib/sanitize-slide-html.ts +49 -36
- package/corpus/templates/slides/app/routes/access-request.approve.tsx +16 -9
- package/corpus/templates/slides/app/routes.ts +3 -1
- package/corpus/templates/slides/changelog/2026-08-22-already-uploaded-chat-images-are-no-longer-dropped.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-22-google-drive-and-slides-can-now-connect-with-the-shared-google-oauth.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-22-multi-slide-repairs-now-report-slides-that-still-overflow.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-22-update-slide-now-reports-which-batched-edit-was-skipped.md +6 -0
- package/corpus/templates/slides/server/handlers/google-docs-auth.ts +52 -1
- package/corpus/templates/slides/server/lib/chat-attachments.ts +29 -3
- package/corpus/templates/slides/server/lib/google-docs-oauth.ts +66 -16
- package/corpus/templates/slides/server/lib/slide-content-patch.ts +13 -2
- package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -2
- package/corpus/templates/slides/tsconfig.json +5 -0
- package/corpus/templates/slides/vitest.config.ts +6 -1
- package/corpus/templates/tasks/.agents/skills/secrets/SKILL.md +11 -4
- package/corpus/templates/tasks/app/routes/settings.tsx +4 -1
- package/corpus/templates/tasks/app/routes.ts +3 -1
- package/corpus/templates/tasks/changelog/2026-08-22-the-extensions-link-now-opens-the-extensions-tab-instead-o.md +6 -0
- package/corpus/templates/tasks/vitest.config.ts +7 -1
- package/dist/action.d.ts +91 -0
- package/dist/action.js +50 -0
- package/dist/agent/abort-reasons.d.ts +38 -0
- package/dist/agent/abort-reasons.js +49 -0
- package/dist/agent/engine/ai-sdk-engine.js +8 -5
- package/dist/agent/engine/anthropic-engine.js +7 -5
- package/dist/agent/engine/first-event-timeout.d.ts +42 -17
- package/dist/agent/engine/first-event-timeout.js +67 -25
- package/dist/agent/production-agent.d.ts +23 -0
- package/dist/agent/production-agent.js +107 -102
- package/dist/agent/run-loop-with-resume.d.ts +2 -28
- package/dist/agent/run-loop-with-resume.js +25 -40
- package/dist/agent/run-manager.js +5 -4
- package/dist/agent/run-store.d.ts +55 -0
- package/dist/agent/run-store.js +124 -1
- package/dist/agent/tool-search.js +2 -1
- package/dist/agent/types.d.ts +9 -4
- package/dist/app-config/auth.d.ts +1 -0
- package/dist/app-config/auth.js +7 -0
- package/dist/app-config/configuration-error.d.ts +11 -0
- package/dist/app-config/configuration-error.js +14 -0
- package/dist/app-config/index.d.ts +1 -0
- package/dist/app-config/index.js +1 -0
- package/dist/app-config/integrations.d.ts +1 -0
- package/dist/app-config/integrations.js +10 -0
- package/dist/app-config/plugins.d.ts +34 -0
- package/dist/app-config/plugins.js +39 -0
- package/dist/app-config/run-lifecycle-invariants.d.ts +18 -9
- package/dist/app-config/run-lifecycle-invariants.js +18 -25
- package/dist/app-config/schema.d.ts +17 -0
- package/dist/app-config/schema.js +2 -0
- package/dist/chat-threads/schema-migrations.js +20 -0
- package/dist/chat-threads/store.js +21 -4
- package/dist/cli/native-dependencies.d.ts +4 -0
- package/dist/cli/native-dependencies.js +15 -0
- package/dist/client/EnvironmentBadge.d.ts +1 -1
- package/dist/client/EnvironmentBadge.js +40 -5
- package/dist/client/MultiTabAssistantChat.d.ts +1 -1
- package/dist/client/MultiTabAssistantChat.js +35 -14
- package/dist/client/agent-chat/index.d.ts +1 -1
- package/dist/client/agent-chat/index.js +1 -1
- package/dist/client/agent-page/use-jobs.d.ts +2 -1
- package/dist/client/agent-page/use-jobs.js +3 -5
- package/dist/client/agent-sidebar-state.js +3 -1
- package/dist/client/blocks/library/code-tabs.js +5 -0
- package/dist/client/blocks/library/code.js +1 -0
- package/dist/client/chat-first/apps-rail.js +2 -2
- package/dist/client/chat-model-groups.d.ts +12 -0
- package/dist/client/chat-model-groups.js +3 -2
- package/dist/client/integrations/GoogleProductLogo.d.ts +5 -0
- package/dist/client/integrations/GoogleProductLogo.js +45 -0
- package/dist/client/integrations/index.d.ts +2 -0
- package/dist/client/integrations/index.js +2 -0
- package/dist/client/integrations/workspace-provider-oauth.d.ts +8 -0
- package/dist/client/integrations/workspace-provider-oauth.js +13 -0
- package/dist/client/resources/mcp-integration-catalog.js +0 -29
- package/dist/client/settings/SettingsTabsPage.js +10 -6
- package/dist/client/sharing/ShareButton.js +11 -2
- package/dist/client/use-chat-models.d.ts +6 -0
- package/dist/client/use-chat-models.js +10 -0
- package/dist/client/use-db-sync.js +27 -9
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/connections/catalog.d.ts +111 -9
- package/dist/connections/catalog.js +86 -30
- package/dist/db/create-get-db.js +18 -0
- package/dist/db/ddl-guard.d.ts +0 -4
- package/dist/db/ddl-guard.js +46 -0
- package/dist/deploy/build.d.ts +19 -0
- package/dist/deploy/build.js +190 -16
- package/dist/deploy/function-bundle.d.ts +45 -0
- package/dist/deploy/function-bundle.js +337 -0
- package/dist/deploy/workspace-deploy.js +19 -1
- package/dist/eject/workspace-connections.js +4 -0
- package/dist/email-catalog/registry.d.ts +4 -0
- package/dist/email-catalog/registry.js +79 -14
- package/dist/extensions/content-patch.js +14 -2
- package/dist/integrations/plugin.d.ts +10 -0
- package/dist/integrations/plugin.js +40 -6
- package/dist/jobs/actions/run-automation-now.d.ts +2 -1
- package/dist/jobs/actions/run-automation-now.js +11 -5
- package/dist/jobs/frontmatter.js +53 -2
- package/dist/jobs/run-now.d.ts +11 -1
- package/dist/jobs/run-now.js +42 -7
- package/dist/jobs/scheduler.d.ts +1 -0
- package/dist/jobs/scheduler.js +2 -1
- package/dist/localization/core-messages/ar-SA.js +3 -0
- package/dist/localization/core-messages/de-DE.js +3 -0
- package/dist/localization/core-messages/en-US.d.ts +3 -0
- package/dist/localization/core-messages/en-US.js +3 -0
- package/dist/localization/core-messages/es-ES.js +3 -0
- package/dist/localization/core-messages/fr-FR.js +3 -0
- package/dist/localization/core-messages/hi-IN.js +3 -0
- package/dist/localization/core-messages/ja-JP.js +3 -0
- package/dist/localization/core-messages/ko-KR.js +3 -0
- package/dist/localization/core-messages/pt-BR.js +3 -0
- package/dist/localization/core-messages/zh-CN.js +3 -0
- package/dist/localization/core-messages/zh-TW.js +3 -0
- package/dist/localization/core-messages.d.ts +3 -0
- package/dist/localization/default-messages.d.ts +2 -5
- package/dist/localization/default-messages.js +2 -5
- package/dist/mcp/build-server.d.ts +9 -0
- package/dist/mcp/build-server.js +37 -8
- package/dist/mcp-client/oauth-client.d.ts +7 -0
- package/dist/mcp-client/oauth-client.js +12 -1
- package/dist/oauth-tokens/index.d.ts +1 -1
- package/dist/oauth-tokens/index.js +1 -1
- package/dist/oauth-tokens/lifecycle.d.ts +11 -0
- package/dist/oauth-tokens/lifecycle.js +17 -0
- package/dist/oauth-tokens/store.js +7 -0
- package/dist/observability/posthog-ai.d.ts +15 -1
- package/dist/observability/posthog-ai.js +24 -12
- package/dist/observability/traces.js +158 -28
- package/dist/onboarding/app-profile.js +2 -2
- package/dist/onboarding/plugin.js +10 -2
- package/dist/onboarding/types.d.ts +2 -0
- package/dist/provider-api/actions/custom-provider-registration.d.ts +1 -1
- package/dist/provider-api/actions/provider-api.d.ts +2 -2
- package/dist/provider-api/index.js +2 -0
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/resources/store.js +13 -4
- package/dist/scripts/agent-engines/list-agent-engines.js +29 -3
- package/dist/secrets/register-framework-secrets.js +1 -1
- package/dist/server/action-discovery.js +5 -0
- package/dist/server/agent-chat/action-filters-a2a.d.ts +27 -3
- package/dist/server/agent-chat/action-filters-a2a.js +48 -9
- package/dist/server/agent-chat-plugin.js +36 -9
- package/dist/server/agent-discovery.js +43 -20
- package/dist/server/auth.js +18 -16
- package/dist/server/beta-opt-out-html.js +45 -4
- package/dist/server/better-auth-instance.d.ts +2 -1
- package/dist/server/better-auth-instance.js +37 -7
- package/dist/server/builder-design-systems.d.ts +4 -1
- package/dist/server/builder-design-systems.js +87 -6
- package/dist/server/builder-oauth.d.ts +1 -7
- package/dist/server/builder-oauth.js +80 -163
- package/dist/server/core-routes-plugin.d.ts +29 -0
- package/dist/server/core-routes-plugin.js +100 -20
- package/dist/server/default-plugins.d.ts +18 -0
- package/dist/server/default-plugins.js +22 -0
- package/dist/server/edge.d.ts +1 -0
- package/dist/server/edge.js +1 -0
- package/dist/server/framework-request-handler.js +31 -3
- package/dist/server/google-oauth.d.ts +3 -0
- package/dist/server/google-oauth.js +3 -0
- package/dist/server/index.d.ts +4 -4
- package/dist/server/index.js +3 -3
- package/dist/server/onboarding-html.js +37 -3
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/workspace-provider-oauth.d.ts +6 -1
- package/dist/server/workspace-provider-oauth.js +72 -20
- package/dist/shared/agent-sidebar-url.d.ts +1 -0
- package/dist/shared/agent-sidebar-url.js +1 -0
- package/dist/shared/environment-lanes.d.ts +2 -0
- package/dist/shared/environment-lanes.js +3 -1
- package/dist/shared/index.d.ts +3 -1
- package/dist/shared/index.js +3 -1
- package/dist/shared/surface-visibility.d.ts +31 -0
- package/dist/shared/surface-visibility.js +57 -0
- package/dist/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
- package/dist/templates/chat/app/routes.ts +3 -1
- package/dist/templates/chat/vitest.config.ts +8 -0
- package/dist/templates/default/.agents/skills/secrets/SKILL.md +11 -4
- package/dist/templates/default/app/routes.ts +3 -1
- package/dist/templates/headless/.agents/skills/secrets/SKILL.md +11 -4
- package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +11 -4
- package/dist/tracking/registry.d.ts +11 -0
- package/dist/tracking/registry.js +5 -2
- package/dist/triggers/actions.js +10 -3
- package/dist/usage/store.js +23 -3
- package/dist/user-profile/actions/get-auth-methods.js +18 -5
- package/dist/vite/client.js +33 -7
- package/docs/content/actions-access-control.mdx +43 -4
- package/docs/content/actions-defining.mdx +16 -0
- package/docs/content/authentication.mdx +40 -21
- package/docs/content/environment-variables.mdx +1 -0
- package/docs/content/template-clips-capture-everywhere.mdx +29 -30
- package/docs/content/template-clips-developers.mdx +4 -4
- package/docs/content/template-clips.mdx +3 -1
- package/package.json +2 -2
- package/src/eject/workspace-connections.ts +4 -0
- package/src/templates/chat/.agents/skills/secrets/SKILL.md +11 -4
- package/src/templates/chat/app/routes.ts +3 -1
- package/src/templates/chat/vitest.config.ts +8 -0
- package/src/templates/default/.agents/skills/secrets/SKILL.md +11 -4
- package/src/templates/default/app/routes.ts +3 -1
- package/src/templates/headless/.agents/skills/secrets/SKILL.md +11 -4
- package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +11 -4
|
@@ -5,13 +5,14 @@ import {
|
|
|
5
5
|
listOAuthAccountsByOwner,
|
|
6
6
|
} from "@agent-native/core/oauth-tokens";
|
|
7
7
|
import {
|
|
8
|
-
isOAuthConnected,
|
|
9
8
|
getOAuthAccounts,
|
|
9
|
+
getCredentialContext,
|
|
10
10
|
getRequestOrgId,
|
|
11
11
|
resolveSecret,
|
|
12
12
|
runWithRequestContext,
|
|
13
13
|
resolveGoogleProviderCredentialCandidatesWithReader,
|
|
14
14
|
} from "@agent-native/core/server";
|
|
15
|
+
import { resolveWorkspaceConnectionForApp } from "@agent-native/core/workspace-connections";
|
|
15
16
|
|
|
16
17
|
import type {
|
|
17
18
|
CalendarEvent,
|
|
@@ -31,11 +32,37 @@ import {
|
|
|
31
32
|
calendarUpdateEvent,
|
|
32
33
|
peopleGetProfile,
|
|
33
34
|
} from "./google-api.js";
|
|
35
|
+
import { getCalendarProviderApiRuntime } from "./provider-api.js";
|
|
34
36
|
import {
|
|
35
37
|
alignSeriesRecurrenceToStart,
|
|
36
38
|
shiftSeriesDateValue,
|
|
37
39
|
} from "./series-recurrence.js";
|
|
38
40
|
|
|
41
|
+
type ManagedCalendarClient = {
|
|
42
|
+
email: string;
|
|
43
|
+
accessToken: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
async function resolveManagedCalendarClient(): Promise<ManagedCalendarClient | null> {
|
|
47
|
+
if (!getCredentialContext()) return null;
|
|
48
|
+
const connection = await resolveWorkspaceConnectionForApp({
|
|
49
|
+
appId: "calendar",
|
|
50
|
+
provider: "google_calendar",
|
|
51
|
+
requireConnected: true,
|
|
52
|
+
});
|
|
53
|
+
if (!connection.available) return null;
|
|
54
|
+
const credential =
|
|
55
|
+
await getCalendarProviderApiRuntime().resolveOAuthAccessToken({
|
|
56
|
+
provider: "google_calendar",
|
|
57
|
+
});
|
|
58
|
+
if (!credential.accountId) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
"The connected Google Calendar workspace account has no account id.",
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
return { email: credential.accountId, accessToken: credential.accessToken };
|
|
64
|
+
}
|
|
65
|
+
|
|
39
66
|
const SCOPES = [
|
|
40
67
|
"https://www.googleapis.com/auth/calendar.readonly",
|
|
41
68
|
"https://www.googleapis.com/auth/calendar.events",
|
|
@@ -55,6 +82,16 @@ interface GoogleTokens {
|
|
|
55
82
|
photoUrl?: string;
|
|
56
83
|
}
|
|
57
84
|
|
|
85
|
+
const CALENDAR_SCOPE_PREFIX = "https://www.googleapis.com/auth/calendar.";
|
|
86
|
+
|
|
87
|
+
function hasCalendarScope(tokens: Record<string, unknown>): boolean {
|
|
88
|
+
const scope = tokens.scope;
|
|
89
|
+
if (typeof scope !== "string" || !scope.trim()) return true;
|
|
90
|
+
return scope
|
|
91
|
+
.split(/[\s,]+/)
|
|
92
|
+
.some((value) => value.startsWith(CALENDAR_SCOPE_PREFIX));
|
|
93
|
+
}
|
|
94
|
+
|
|
58
95
|
function optionalString(value: unknown): string | undefined {
|
|
59
96
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
60
97
|
}
|
|
@@ -562,8 +599,10 @@ export async function getClient(
|
|
|
562
599
|
email: string | undefined,
|
|
563
600
|
): Promise<{ accessToken: string } | null> {
|
|
564
601
|
if (!email) return null;
|
|
565
|
-
const accounts = await listOAuthAccountsByOwner("google", email)
|
|
566
|
-
|
|
602
|
+
const accounts = (await listOAuthAccountsByOwner("google", email)).filter(
|
|
603
|
+
(account) => hasCalendarScope(account.tokens),
|
|
604
|
+
);
|
|
605
|
+
if (accounts.length === 0) return resolveManagedCalendarClient();
|
|
567
606
|
|
|
568
607
|
const account = accounts.find((a) => a.accountId === email) ?? accounts[0];
|
|
569
608
|
|
|
@@ -584,7 +623,9 @@ export interface GoogleAccountSelection {
|
|
|
584
623
|
export async function getDefaultAccountSelection(
|
|
585
624
|
ownerEmail: string,
|
|
586
625
|
): Promise<GoogleAccountSelection> {
|
|
587
|
-
const accounts =
|
|
626
|
+
const accounts = (
|
|
627
|
+
await listOAuthAccountsByOwner("google", ownerEmail)
|
|
628
|
+
).filter((account) => hasCalendarScope(account.tokens));
|
|
588
629
|
const account =
|
|
589
630
|
accounts.find(
|
|
590
631
|
(candidate) =>
|
|
@@ -592,6 +633,10 @@ export async function getDefaultAccountSelection(
|
|
|
592
633
|
ownerEmail.trim().toLowerCase(),
|
|
593
634
|
) ?? accounts[0];
|
|
594
635
|
if (!account) {
|
|
636
|
+
const managed = await resolveManagedCalendarClient();
|
|
637
|
+
if (managed) {
|
|
638
|
+
return { ownerEmail, accountEmail: managed.email };
|
|
639
|
+
}
|
|
595
640
|
throw new Error(
|
|
596
641
|
"Google Calendar not connected. Connect via Settings first.",
|
|
597
642
|
);
|
|
@@ -605,12 +650,21 @@ export async function getClientForAccount({
|
|
|
605
650
|
accountEmail,
|
|
606
651
|
}: GoogleAccountSelection): Promise<{ accessToken: string }> {
|
|
607
652
|
const normalizedAccountEmail = accountEmail.trim().toLowerCase();
|
|
608
|
-
const accounts =
|
|
653
|
+
const accounts = (
|
|
654
|
+
await listOAuthAccountsByOwner("google", ownerEmail)
|
|
655
|
+
).filter((account) => hasCalendarScope(account.tokens));
|
|
609
656
|
const account = accounts.find(
|
|
610
657
|
(candidate) =>
|
|
611
658
|
candidate.accountId.trim().toLowerCase() === normalizedAccountEmail,
|
|
612
659
|
);
|
|
613
660
|
if (!account) {
|
|
661
|
+
const managed = await resolveManagedCalendarClient();
|
|
662
|
+
if (
|
|
663
|
+
managed &&
|
|
664
|
+
managed.email.trim().toLowerCase() === normalizedAccountEmail
|
|
665
|
+
) {
|
|
666
|
+
return { accessToken: managed.accessToken };
|
|
667
|
+
}
|
|
614
668
|
throw new Error(
|
|
615
669
|
`Google Calendar account not connected for this user: ${accountEmail}`,
|
|
616
670
|
);
|
|
@@ -654,7 +708,9 @@ export async function getClientsWithErrors(forEmail?: string): Promise<{
|
|
|
654
708
|
errors: Array<{ email: string; error: string }>;
|
|
655
709
|
}> {
|
|
656
710
|
if (!forEmail) return { clients: [], errors: [] };
|
|
657
|
-
const accounts = await listOAuthAccountsByOwner("google", forEmail)
|
|
711
|
+
const accounts = (await listOAuthAccountsByOwner("google", forEmail)).filter(
|
|
712
|
+
(account) => hasCalendarScope(account.tokens),
|
|
713
|
+
);
|
|
658
714
|
|
|
659
715
|
const clients: Array<{ email: string; accessToken: string }> = [];
|
|
660
716
|
const errors: Array<{ email: string; error: string }> = [];
|
|
@@ -682,6 +738,18 @@ export async function getClientsWithErrors(forEmail?: string): Promise<{
|
|
|
682
738
|
}
|
|
683
739
|
}
|
|
684
740
|
|
|
741
|
+
if (clients.length === 0) {
|
|
742
|
+
try {
|
|
743
|
+
const managed = await resolveManagedCalendarClient();
|
|
744
|
+
if (managed) clients.push(managed);
|
|
745
|
+
} catch (err: any) {
|
|
746
|
+
errors.push({
|
|
747
|
+
email: "workspace",
|
|
748
|
+
error: err?.message || "Workspace Google Calendar connection failed",
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
685
753
|
return { clients, errors };
|
|
686
754
|
}
|
|
687
755
|
|
|
@@ -695,7 +763,9 @@ export async function getOwnedAccountEmails(
|
|
|
695
763
|
forEmail?: string,
|
|
696
764
|
): Promise<string[]> {
|
|
697
765
|
if (!forEmail) return [];
|
|
698
|
-
const accounts = await listOAuthAccountsByOwner("google", forEmail)
|
|
766
|
+
const accounts = (await listOAuthAccountsByOwner("google", forEmail)).filter(
|
|
767
|
+
(account) => hasCalendarScope(account.tokens),
|
|
768
|
+
);
|
|
699
769
|
return accounts.map((account) => account.accountId);
|
|
700
770
|
}
|
|
701
771
|
|
|
@@ -716,7 +786,47 @@ export async function getClientsForAccountsWithErrors(
|
|
|
716
786
|
resolvedAccounts: [],
|
|
717
787
|
};
|
|
718
788
|
}
|
|
719
|
-
const accounts = await listOAuthAccountsByOwner("google", forEmail)
|
|
789
|
+
const accounts = (await listOAuthAccountsByOwner("google", forEmail)).filter(
|
|
790
|
+
(account) => hasCalendarScope(account.tokens),
|
|
791
|
+
);
|
|
792
|
+
if (accounts.length === 0) {
|
|
793
|
+
const managed = await resolveManagedCalendarClient();
|
|
794
|
+
if (!managed) {
|
|
795
|
+
if (accountEmails?.length) {
|
|
796
|
+
throw new Error(
|
|
797
|
+
`Google Calendar account not connected for this user: ${accountEmails.join(", ")}`,
|
|
798
|
+
);
|
|
799
|
+
}
|
|
800
|
+
return {
|
|
801
|
+
clients: [],
|
|
802
|
+
errors: [],
|
|
803
|
+
requestedAccounts: [],
|
|
804
|
+
resolvedAccounts: [],
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
const requestedAccounts = Array.from(
|
|
808
|
+
new Set(
|
|
809
|
+
(accountEmails ?? [managed.email])
|
|
810
|
+
.map((email) => email.trim().toLowerCase())
|
|
811
|
+
.filter(Boolean),
|
|
812
|
+
),
|
|
813
|
+
);
|
|
814
|
+
if (
|
|
815
|
+
requestedAccounts.some(
|
|
816
|
+
(email) => email !== managed.email.trim().toLowerCase(),
|
|
817
|
+
)
|
|
818
|
+
) {
|
|
819
|
+
throw new Error(
|
|
820
|
+
`Google Calendar account not connected for this user: ${requestedAccounts.join(", ")}`,
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
return {
|
|
824
|
+
clients: [managed],
|
|
825
|
+
errors: [],
|
|
826
|
+
requestedAccounts,
|
|
827
|
+
resolvedAccounts: [managed.email],
|
|
828
|
+
};
|
|
829
|
+
}
|
|
720
830
|
const byNormalized = new Map(
|
|
721
831
|
accounts.map((account) => [
|
|
722
832
|
account.accountId.trim().toLowerCase(),
|
|
@@ -765,15 +875,22 @@ export async function getClientsForAccountsWithErrors(
|
|
|
765
875
|
}
|
|
766
876
|
|
|
767
877
|
export async function isConnected(forEmail?: string): Promise<boolean> {
|
|
768
|
-
|
|
878
|
+
if (!forEmail) return false;
|
|
879
|
+
const accounts = await listOAuthAccountsByOwner("google", forEmail);
|
|
880
|
+
if (accounts.some((account) => hasCalendarScope(account.tokens))) return true;
|
|
881
|
+
return Boolean(await resolveManagedCalendarClient());
|
|
769
882
|
}
|
|
770
883
|
|
|
771
884
|
export async function getConnectedAccounts(
|
|
772
885
|
forEmail?: string,
|
|
773
886
|
): Promise<string[]> {
|
|
774
887
|
if (!forEmail) return [];
|
|
775
|
-
const accounts = await listOAuthAccountsByOwner("google", forEmail)
|
|
776
|
-
|
|
888
|
+
const accounts = (await listOAuthAccountsByOwner("google", forEmail)).filter(
|
|
889
|
+
(account) => hasCalendarScope(account.tokens),
|
|
890
|
+
);
|
|
891
|
+
if (accounts.length > 0) return accounts.map((a) => a.accountId);
|
|
892
|
+
const managed = await resolveManagedCalendarClient();
|
|
893
|
+
return managed ? [managed.email] : [];
|
|
777
894
|
}
|
|
778
895
|
|
|
779
896
|
export async function getPrimaryAccountPhotoUrl(
|
|
@@ -781,7 +898,9 @@ export async function getPrimaryAccountPhotoUrl(
|
|
|
781
898
|
): Promise<string | undefined> {
|
|
782
899
|
if (!forEmail) return undefined;
|
|
783
900
|
const fallbackUserImage = await getBetterAuthUserImage(forEmail);
|
|
784
|
-
const accounts = await listOAuthAccountsByOwner("google", forEmail)
|
|
901
|
+
const accounts = (await listOAuthAccountsByOwner("google", forEmail)).filter(
|
|
902
|
+
(account) => hasCalendarScope(account.tokens),
|
|
903
|
+
);
|
|
785
904
|
const account = accounts.find((a) => a.accountId === forEmail) ?? accounts[0];
|
|
786
905
|
if (!account) return fallbackUserImage;
|
|
787
906
|
|
|
@@ -805,16 +924,22 @@ export async function getAuthStatus(
|
|
|
805
924
|
forEmail?: string,
|
|
806
925
|
orgId?: string,
|
|
807
926
|
): Promise<GoogleAuthStatus> {
|
|
808
|
-
const oauthAccounts = await getOAuthAccounts("google", forEmail)
|
|
927
|
+
const oauthAccounts = (await getOAuthAccounts("google", forEmail)).filter(
|
|
928
|
+
(account) => hasCalendarScope(account.tokens),
|
|
929
|
+
);
|
|
809
930
|
|
|
810
931
|
if (oauthAccounts.length === 0) {
|
|
811
|
-
|
|
932
|
+
const managed = await resolveManagedCalendarClient();
|
|
933
|
+
return managed
|
|
934
|
+
? { connected: true, accounts: [{ email: managed.email, shared: true }] }
|
|
935
|
+
: { connected: false, accounts: [] };
|
|
812
936
|
}
|
|
813
937
|
|
|
814
938
|
const result: Array<{
|
|
815
939
|
email: string;
|
|
816
940
|
expiresAt?: string;
|
|
817
941
|
photoUrl?: string;
|
|
942
|
+
shared?: boolean;
|
|
818
943
|
}> = [];
|
|
819
944
|
for (const account of oauthAccounts) {
|
|
820
945
|
const tokens = account.tokens as unknown as GoogleTokens;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { listOAuthAccountsByOwner } from "@agent-native/core/oauth-tokens";
|
|
2
|
+
import { registerOnboardingStep } from "@agent-native/core/onboarding";
|
|
3
|
+
import { hasWorkspaceProviderOAuthCredentials } from "@agent-native/core/server";
|
|
4
|
+
import { resolveWorkspaceConnectionForApp } from "@agent-native/core/workspace-connections";
|
|
5
|
+
|
|
6
|
+
const CALENDAR_SCOPES = [
|
|
7
|
+
"https://www.googleapis.com/auth/calendar.readonly",
|
|
8
|
+
"https://www.googleapis.com/auth/calendar.events",
|
|
9
|
+
] as const;
|
|
10
|
+
|
|
11
|
+
function hasRequiredScope(tokens: unknown): boolean {
|
|
12
|
+
if (!tokens || typeof tokens !== "object" || Array.isArray(tokens)) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
const scope = (tokens as { scope?: unknown }).scope;
|
|
16
|
+
if (typeof scope !== "string" || !scope.trim()) return true;
|
|
17
|
+
const granted = new Set(scope.split(/[\s,]+/).filter(Boolean));
|
|
18
|
+
return CALENDAR_SCOPES.some((requiredScope) => granted.has(requiredScope));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
registerOnboardingStep({
|
|
22
|
+
id: "google-calendar",
|
|
23
|
+
order: 100,
|
|
24
|
+
required: false,
|
|
25
|
+
title: "Connect Google Calendar",
|
|
26
|
+
description: "Manage events and availability with your Google account.",
|
|
27
|
+
isAvailable: () => hasWorkspaceProviderOAuthCredentials("google_calendar"),
|
|
28
|
+
methods: [
|
|
29
|
+
{
|
|
30
|
+
id: "oauth",
|
|
31
|
+
kind: "link",
|
|
32
|
+
primary: true,
|
|
33
|
+
label: "Connect Google Calendar for me",
|
|
34
|
+
description:
|
|
35
|
+
"One-click Google sign-in using the workspace's managed OAuth connection. Only you can use this connection.",
|
|
36
|
+
payload: {
|
|
37
|
+
url: "/_agent-native/connections/oauth/google_calendar/start?scope=user&appId=calendar&return=/settings",
|
|
38
|
+
external: false,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: "oauth-workspace",
|
|
43
|
+
kind: "link",
|
|
44
|
+
label: "Connect Google Calendar for my workspace",
|
|
45
|
+
description:
|
|
46
|
+
"Workspace admins can connect once and make this Calendar connection available to selected apps or everyone in the workspace.",
|
|
47
|
+
payload: {
|
|
48
|
+
url: "/_agent-native/connections/oauth/google_calendar/start?scope=organization&appId=calendar&return=/settings",
|
|
49
|
+
external: false,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "agent-task",
|
|
54
|
+
kind: "agent-task",
|
|
55
|
+
badge: "beta",
|
|
56
|
+
label: "Have the agent connect it",
|
|
57
|
+
payload: {
|
|
58
|
+
prompt:
|
|
59
|
+
"Help me connect Google Calendar through the managed Google OAuth connection. Start the one-click Calendar connection flow, explain the Google permission screen, and confirm whether I want the connection available only to me or to the workspace. Do not ask me to create Google Cloud credentials or paste keys.",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
isComplete: async (context) => {
|
|
64
|
+
if (!context?.userEmail) return false;
|
|
65
|
+
try {
|
|
66
|
+
const accounts = await listOAuthAccountsByOwner(
|
|
67
|
+
"google",
|
|
68
|
+
context.userEmail,
|
|
69
|
+
);
|
|
70
|
+
if (accounts.some((account) => hasRequiredScope(account.tokens))) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
return (
|
|
74
|
+
await resolveWorkspaceConnectionForApp({
|
|
75
|
+
appId: "calendar",
|
|
76
|
+
provider: "google_calendar",
|
|
77
|
+
requireConnected: true,
|
|
78
|
+
})
|
|
79
|
+
).available;
|
|
80
|
+
} catch {
|
|
81
|
+
// coercion-ok: a failed lookup must leave onboarding incomplete, never completed.
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
});
|
|
@@ -310,13 +310,21 @@ export interface BookingLink {
|
|
|
310
310
|
isActive: boolean;
|
|
311
311
|
/** Sharing visibility: private (default), org, or public */
|
|
312
312
|
visibility?: "private" | "org" | "public";
|
|
313
|
+
/** Effective management role for the current caller, when loaded from a list. */
|
|
314
|
+
accessRole?: "owner" | "admin" | "editor" | "commenter" | "viewer";
|
|
313
315
|
createdAt: string;
|
|
314
316
|
updatedAt: string;
|
|
315
317
|
}
|
|
316
318
|
|
|
317
319
|
export interface GoogleAuthStatus {
|
|
320
|
+
configured?: boolean;
|
|
318
321
|
connected: boolean;
|
|
319
|
-
accounts: Array<{
|
|
322
|
+
accounts: Array<{
|
|
323
|
+
email: string;
|
|
324
|
+
expiresAt?: string;
|
|
325
|
+
photoUrl?: string;
|
|
326
|
+
shared?: boolean;
|
|
327
|
+
}>;
|
|
320
328
|
}
|
|
321
329
|
|
|
322
330
|
export interface ExternalCalendar {
|
|
@@ -19,10 +19,17 @@ data, and generated extension/app content may mention credential **names** such
|
|
|
19
19
|
as `OPENAI_API_KEY`, but must not contain real API keys, tokens, webhook URLs,
|
|
20
20
|
signing secrets, OAuth refresh tokens, or private Builder/customer data.
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
`${keys.NAME}` substitution.
|
|
25
|
-
|
|
22
|
+
Provider secret values are supplied at runtime through the encrypted
|
|
23
|
+
`app_secrets` vault, `saveCredential` / `resolveCredential`, OAuth, or
|
|
24
|
+
`${keys.NAME}` substitution. Deployment configuration is reserved for
|
|
25
|
+
deploy-level secrets and non-provider configuration. Examples must use obvious
|
|
26
|
+
placeholders such as `<OPENAI_API_KEY>` or `${keys.SLACK_WEBHOOK}`, not
|
|
27
|
+
real-looking copied values.
|
|
28
|
+
|
|
29
|
+
Provider credentials and provider account identifiers are workspace data. Use
|
|
30
|
+
standard workspace connections and org/workspace vault scopes; never put them
|
|
31
|
+
in `.env` or deployment environment variables, and never add a provider-specific
|
|
32
|
+
action or startup bootstrap just to write a credential for one organization.
|
|
26
33
|
|
|
27
34
|
## Credential Modeling Preflight
|
|
28
35
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { type RouteConfig } from "@react-router/dev/routes";
|
|
2
2
|
import { flatRoutes } from "@react-router/fs-routes";
|
|
3
3
|
|
|
4
|
-
export default flatRoutes(
|
|
4
|
+
export default flatRoutes({
|
|
5
|
+
ignoredRouteFiles: ["**/*.test.{ts,tsx}", "**/*.spec.{ts,tsx}"],
|
|
6
|
+
}) satisfies RouteConfig;
|
|
@@ -19,10 +19,17 @@ data, and generated extension/app content may mention credential **names** such
|
|
|
19
19
|
as `OPENAI_API_KEY`, but must not contain real API keys, tokens, webhook URLs,
|
|
20
20
|
signing secrets, OAuth refresh tokens, or private Builder/customer data.
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
`${keys.NAME}` substitution.
|
|
25
|
-
|
|
22
|
+
Provider secret values are supplied at runtime through the encrypted
|
|
23
|
+
`app_secrets` vault, `saveCredential` / `resolveCredential`, OAuth, or
|
|
24
|
+
`${keys.NAME}` substitution. Deployment configuration is reserved for
|
|
25
|
+
deploy-level secrets and non-provider configuration. Examples must use obvious
|
|
26
|
+
placeholders such as `<OPENAI_API_KEY>` or `${keys.SLACK_WEBHOOK}`, not
|
|
27
|
+
real-looking copied values.
|
|
28
|
+
|
|
29
|
+
Provider credentials and provider account identifiers are workspace data. Use
|
|
30
|
+
standard workspace connections and org/workspace vault scopes; never put them
|
|
31
|
+
in `.env` or deployment environment variables, and never add a provider-specific
|
|
32
|
+
action or startup bootstrap just to write a credential for one organization.
|
|
26
33
|
|
|
27
34
|
## Credential Modeling Preflight
|
|
28
35
|
|
|
@@ -48,7 +48,7 @@ function safeParseArray<T>(
|
|
|
48
48
|
|
|
49
49
|
export default defineAction({
|
|
50
50
|
description:
|
|
51
|
-
|
|
51
|
+
'Get a meeting by id with its participants, action items, and a reference to its recording (if any). When no meeting is returned, `meeting` is null and `reason` is "unavailable" — it does not distinguish missing from inaccessible. A read failure throws instead.',
|
|
52
52
|
schema: z.object({
|
|
53
53
|
id: z.string().describe("Meeting id"),
|
|
54
54
|
}),
|
|
@@ -59,12 +59,17 @@ export default defineAction({
|
|
|
59
59
|
const materialized = await materializeCalendarMeetingFromVirtualId(
|
|
60
60
|
args.id,
|
|
61
61
|
);
|
|
62
|
-
if (!materialized?.meeting?.id)
|
|
62
|
+
if (!materialized?.meeting?.id)
|
|
63
|
+
return { meeting: null, reason: "unavailable" as const };
|
|
63
64
|
meetingId = materialized.meeting.id;
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
// "cannot see it" and "does not exist" deliberately share one reason: a
|
|
68
|
+
// caller that could tell them apart could probe ids to learn which
|
|
69
|
+
// meetings exist. The distinction callers actually need is this vs a
|
|
70
|
+
// thrown read failure, which stays separate.
|
|
66
71
|
const access = await resolveAccess("meeting", meetingId);
|
|
67
|
-
if (!access) return { meeting: null };
|
|
72
|
+
if (!access) return { meeting: null, reason: "unavailable" as const };
|
|
68
73
|
|
|
69
74
|
const db = getDb();
|
|
70
75
|
const [row] = await db
|
|
@@ -77,7 +82,7 @@ export default defineAction({
|
|
|
77
82
|
),
|
|
78
83
|
)
|
|
79
84
|
.limit(1);
|
|
80
|
-
if (!row) return { meeting: null };
|
|
85
|
+
if (!row) return { meeting: null, reason: "unavailable" as const };
|
|
81
86
|
|
|
82
87
|
// Server-side JSON parse — clients see structured arrays, not raw TEXT.
|
|
83
88
|
const bullets = safeParseArray<Bullet>(
|
|
@@ -188,9 +188,19 @@ export default defineAction({
|
|
|
188
188
|
// Meeting recordings are transcript-only (no playable media) and live on
|
|
189
189
|
// the /meetings surface, so keep them out of clip library views. The link
|
|
190
190
|
// is meetings.recordingId (no meetingId column on recordings), so exclude
|
|
191
|
-
// any recording referenced by a meeting.
|
|
192
|
-
//
|
|
193
|
-
|
|
191
|
+
// any recording referenced by a meeting. Keep the exclusion database-side
|
|
192
|
+
// as a NOT IN (SELECT ...) subquery instead of pulling every meeting's
|
|
193
|
+
// recording id into memory — this table only grows. `await db` with no
|
|
194
|
+
// chain resolves the lazy proxy from create-get-db.ts to the real db
|
|
195
|
+
// instance without issuing a query; only *then* build the subquery off
|
|
196
|
+
// that resolved instance. Building it off `db` directly and handing the
|
|
197
|
+
// still-unresolved chain straight to notInArray() is the cold-start bug
|
|
198
|
+
// this used to have — drizzle reads `.getSQL()` on it synchronously, and
|
|
199
|
+
// the proxy throws rather than silently building wrong SQL. The subquery
|
|
200
|
+
// filters out NULLs so NOT IN doesn't collapse to an empty result under
|
|
201
|
+
// SQL NULL semantics.
|
|
202
|
+
const resolvedDb = await db;
|
|
203
|
+
const meetingRecordingIds = resolvedDb
|
|
194
204
|
.select({ id: schema.meetings.recordingId })
|
|
195
205
|
.from(schema.meetings)
|
|
196
206
|
.where(isNotNull(schema.meetings.recordingId));
|
|
@@ -200,6 +210,9 @@ export default defineAction({
|
|
|
200
210
|
// Lifecycle view filters
|
|
201
211
|
if (args.view === "trash") {
|
|
202
212
|
whereClauses.push(isNotNull(schema.recordings.trashedAt));
|
|
213
|
+
if (orgId) {
|
|
214
|
+
whereClauses.push(eq(schema.recordings.organizationId, orgId));
|
|
215
|
+
}
|
|
203
216
|
} else {
|
|
204
217
|
whereClauses.push(isNull(schema.recordings.trashedAt));
|
|
205
218
|
if (args.view === "archive") {
|
|
@@ -210,7 +210,14 @@ async function fetchLibrary({
|
|
|
210
210
|
conditions.push(eq(schema.recordings.organizationId, organizationId));
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
|
-
|
|
213
|
+
// Meeting recordings are transcript-only and live on /meetings, so keep them
|
|
214
|
+
// out of clip library views. `await db` with no chain resolves the lazy proxy
|
|
215
|
+
// from create-get-db.ts to the real instance without issuing a query; the
|
|
216
|
+
// subquery must be built off *that*. Building it off `db` hands an unresolved
|
|
217
|
+
// chain to notInArray(), which reads `.getSQL()` synchronously on a cold-start
|
|
218
|
+
// proxy. The subquery filters NULLs so NOT IN doesn't collapse to empty.
|
|
219
|
+
const resolvedDb = await db;
|
|
220
|
+
const meetingRecordingIds = resolvedDb
|
|
214
221
|
.select({ id: schema.meetings.recordingId })
|
|
215
222
|
.from(schema.meetings)
|
|
216
223
|
.where(isNotNull(schema.meetings.recordingId));
|
|
@@ -26,6 +26,7 @@ export interface MeetingHistoryItem {
|
|
|
26
26
|
actualEnd?: string | null;
|
|
27
27
|
createdAt?: string | null;
|
|
28
28
|
participants?: AttendeeStackParticipant[];
|
|
29
|
+
ownerEmail?: string | null;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
function formatTime(iso?: string | null): string {
|
|
@@ -56,6 +57,28 @@ export function formatParticipantNames(
|
|
|
56
57
|
return `${names.slice(0, 2).join(", ")} & ${names.length - 2} others`;
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
/**
|
|
61
|
+
* A meeting's owner — who actually recorded it in Clips — isn't necessarily
|
|
62
|
+
* on the attendee list (an ad-hoc note has none at all), and two attendees on
|
|
63
|
+
* the same call can each hold their own copy. Unlike the attendee subtitle,
|
|
64
|
+
* this is shown unconditionally, including the viewer's own meetings, so
|
|
65
|
+
* ownership is never ambiguous once a meeting is shared.
|
|
66
|
+
*/
|
|
67
|
+
export function formatOwnerHint(
|
|
68
|
+
ownerEmail: string | null | undefined,
|
|
69
|
+
viewerEmail: string | null | undefined,
|
|
70
|
+
t: ReturnType<typeof useT>,
|
|
71
|
+
): string {
|
|
72
|
+
const owner = ownerEmail?.trim();
|
|
73
|
+
if (!owner) return "";
|
|
74
|
+
const viewer = viewerEmail?.trim().toLowerCase();
|
|
75
|
+
const name =
|
|
76
|
+
viewer && owner.toLowerCase() === viewer
|
|
77
|
+
? t("meetingDetail.me")
|
|
78
|
+
: owner.replace(/@.*$/, "");
|
|
79
|
+
return t("meetingDetail.recordedBy", { name });
|
|
80
|
+
}
|
|
81
|
+
|
|
59
82
|
export function MeetingHistoryRow({
|
|
60
83
|
meeting,
|
|
61
84
|
snippet,
|
|
@@ -66,11 +89,17 @@ export function MeetingHistoryRow({
|
|
|
66
89
|
const t = useT();
|
|
67
90
|
const { session } = useSession();
|
|
68
91
|
const participants = meeting.participants ?? [];
|
|
69
|
-
const
|
|
92
|
+
const ownerHint = formatOwnerHint(meeting.ownerEmail, session?.email, t);
|
|
93
|
+
const primaryText =
|
|
70
94
|
snippet?.trim() || formatParticipantNames(participants, session?.email);
|
|
95
|
+
const subtitle = [primaryText, ownerHint].filter(Boolean).join(" · ");
|
|
71
96
|
const time = formatTime(
|
|
72
97
|
meeting.actualStart ?? meeting.scheduledStart ?? meeting.createdAt,
|
|
73
98
|
);
|
|
99
|
+
const soloOwnerAvatar: AttendeeStackParticipant[] =
|
|
100
|
+
participants.length === 0 && ownerHint && meeting.ownerEmail
|
|
101
|
+
? [{ email: meeting.ownerEmail }]
|
|
102
|
+
: [];
|
|
74
103
|
|
|
75
104
|
return (
|
|
76
105
|
<NavLink
|
|
@@ -79,6 +108,8 @@ export function MeetingHistoryRow({
|
|
|
79
108
|
>
|
|
80
109
|
{participants.length > 0 ? (
|
|
81
110
|
<AttendeeStack participants={participants} size="md" max={2} />
|
|
111
|
+
) : soloOwnerAvatar.length > 0 ? (
|
|
112
|
+
<AttendeeStack participants={soloOwnerAvatar} size="md" max={1} />
|
|
82
113
|
) : (
|
|
83
114
|
<span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground">
|
|
84
115
|
<IconFileText className="h-3.5 w-3.5" />
|
|
@@ -94,7 +94,7 @@ export function RecordingOptionsMenu({
|
|
|
94
94
|
<Button
|
|
95
95
|
variant="ghost"
|
|
96
96
|
size="icon"
|
|
97
|
-
className="
|
|
97
|
+
className="h-auto w-auto shrink-0 px-0.5 py-1.5"
|
|
98
98
|
aria-label={t("deleteRecordingMenu.clipOptions")}
|
|
99
99
|
>
|
|
100
100
|
<IconDotsVertical className="h-4 w-4" />
|