@agent-native/core 0.98.17 → 0.99.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +25 -0
- package/corpus/core/docs/content/actions.mdx +1 -1
- package/corpus/core/docs/content/authentication.mdx +1 -1
- package/corpus/core/docs/content/dispatch.mdx +3 -3
- package/corpus/core/docs/content/external-agents.mdx +30 -19
- package/corpus/core/docs/content/locales/ar-SA/actions.mdx +1 -1
- package/corpus/core/docs/content/locales/ar-SA/authentication.mdx +1 -1
- package/corpus/core/docs/content/locales/ar-SA/dispatch.mdx +3 -3
- package/corpus/core/docs/content/locales/ar-SA/external-agents.mdx +23 -19
- package/corpus/core/docs/content/locales/ar-SA/mcp-clients.mdx +31 -3
- package/corpus/core/docs/content/locales/ar-SA/mcp-protocol.mdx +29 -15
- package/corpus/core/docs/content/locales/ar-SA/plan-plugin.mdx +3 -3
- package/corpus/core/docs/content/locales/ar-SA/server.mdx +2 -2
- package/corpus/core/docs/content/locales/ar-SA/skills-guide.mdx +1 -1
- package/corpus/core/docs/content/locales/ar-SA/template-assets.mdx +1 -1
- package/corpus/core/docs/content/locales/ar-SA/template-dispatch.mdx +2 -2
- package/corpus/core/docs/content/locales/ar-SA/template-plan.mdx +3 -3
- package/corpus/core/docs/content/locales/de-DE/actions.mdx +1 -1
- package/corpus/core/docs/content/locales/de-DE/authentication.mdx +1 -1
- package/corpus/core/docs/content/locales/de-DE/dispatch.mdx +3 -3
- package/corpus/core/docs/content/locales/de-DE/external-agents.mdx +23 -19
- package/corpus/core/docs/content/locales/de-DE/mcp-clients.mdx +37 -3
- package/corpus/core/docs/content/locales/de-DE/mcp-protocol.mdx +29 -15
- package/corpus/core/docs/content/locales/de-DE/plan-plugin.mdx +3 -3
- package/corpus/core/docs/content/locales/de-DE/server.mdx +2 -2
- package/corpus/core/docs/content/locales/de-DE/skills-guide.mdx +1 -1
- package/corpus/core/docs/content/locales/de-DE/template-assets.mdx +1 -1
- package/corpus/core/docs/content/locales/de-DE/template-dispatch.mdx +2 -2
- package/corpus/core/docs/content/locales/de-DE/template-plan.mdx +3 -3
- package/corpus/core/docs/content/locales/es-ES/actions.mdx +1 -1
- package/corpus/core/docs/content/locales/es-ES/authentication.mdx +1 -1
- package/corpus/core/docs/content/locales/es-ES/dispatch.mdx +3 -3
- package/corpus/core/docs/content/locales/es-ES/external-agents.mdx +32 -19
- package/corpus/core/docs/content/locales/es-ES/mcp-clients.mdx +17 -3
- package/corpus/core/docs/content/locales/es-ES/mcp-protocol.mdx +38 -15
- package/corpus/core/docs/content/locales/es-ES/plan-plugin.mdx +3 -3
- package/corpus/core/docs/content/locales/es-ES/server.mdx +2 -2
- package/corpus/core/docs/content/locales/es-ES/skills-guide.mdx +1 -1
- package/corpus/core/docs/content/locales/es-ES/template-assets.mdx +1 -1
- package/corpus/core/docs/content/locales/es-ES/template-dispatch.mdx +2 -2
- package/corpus/core/docs/content/locales/es-ES/template-plan.mdx +3 -3
- package/corpus/core/docs/content/locales/fr-FR/actions.mdx +1 -1
- package/corpus/core/docs/content/locales/fr-FR/authentication.mdx +1 -1
- package/corpus/core/docs/content/locales/fr-FR/dispatch.mdx +3 -3
- package/corpus/core/docs/content/locales/fr-FR/external-agents.mdx +23 -19
- package/corpus/core/docs/content/locales/fr-FR/mcp-clients.mdx +17 -3
- package/corpus/core/docs/content/locales/fr-FR/mcp-protocol.mdx +29 -15
- package/corpus/core/docs/content/locales/fr-FR/plan-plugin.mdx +3 -3
- package/corpus/core/docs/content/locales/fr-FR/server.mdx +2 -2
- package/corpus/core/docs/content/locales/fr-FR/skills-guide.mdx +1 -1
- package/corpus/core/docs/content/locales/fr-FR/template-assets.mdx +1 -1
- package/corpus/core/docs/content/locales/fr-FR/template-dispatch.mdx +2 -2
- package/corpus/core/docs/content/locales/fr-FR/template-plan.mdx +3 -3
- package/corpus/core/docs/content/locales/hi-IN/actions.mdx +1 -1
- package/corpus/core/docs/content/locales/hi-IN/authentication.mdx +1 -1
- package/corpus/core/docs/content/locales/hi-IN/dispatch.mdx +3 -3
- package/corpus/core/docs/content/locales/hi-IN/external-agents.mdx +31 -19
- package/corpus/core/docs/content/locales/hi-IN/mcp-clients.mdx +17 -3
- package/corpus/core/docs/content/locales/hi-IN/mcp-protocol.mdx +36 -15
- package/corpus/core/docs/content/locales/hi-IN/plan-plugin.mdx +3 -3
- package/corpus/core/docs/content/locales/hi-IN/server.mdx +2 -2
- package/corpus/core/docs/content/locales/hi-IN/skills-guide.mdx +1 -1
- package/corpus/core/docs/content/locales/hi-IN/template-assets.mdx +1 -1
- package/corpus/core/docs/content/locales/hi-IN/template-dispatch.mdx +2 -2
- package/corpus/core/docs/content/locales/hi-IN/template-plan.mdx +3 -3
- package/corpus/core/docs/content/locales/ja-JP/actions.mdx +1 -1
- package/corpus/core/docs/content/locales/ja-JP/authentication.mdx +1 -1
- package/corpus/core/docs/content/locales/ja-JP/dispatch.mdx +3 -3
- package/corpus/core/docs/content/locales/ja-JP/external-agents.mdx +23 -19
- package/corpus/core/docs/content/locales/ja-JP/mcp-clients.mdx +34 -3
- package/corpus/core/docs/content/locales/ja-JP/mcp-protocol.mdx +29 -15
- package/corpus/core/docs/content/locales/ja-JP/plan-plugin.mdx +3 -3
- package/corpus/core/docs/content/locales/ja-JP/server.mdx +2 -2
- package/corpus/core/docs/content/locales/ja-JP/skills-guide.mdx +1 -1
- package/corpus/core/docs/content/locales/ja-JP/template-assets.mdx +1 -1
- package/corpus/core/docs/content/locales/ja-JP/template-dispatch.mdx +2 -2
- package/corpus/core/docs/content/locales/ja-JP/template-plan.mdx +3 -3
- package/corpus/core/docs/content/locales/ko-KR/actions.mdx +1 -1
- package/corpus/core/docs/content/locales/ko-KR/authentication.mdx +1 -1
- package/corpus/core/docs/content/locales/ko-KR/dispatch.mdx +3 -3
- package/corpus/core/docs/content/locales/ko-KR/external-agents.mdx +30 -19
- package/corpus/core/docs/content/locales/ko-KR/mcp-clients.mdx +33 -3
- package/corpus/core/docs/content/locales/ko-KR/mcp-protocol.mdx +36 -15
- package/corpus/core/docs/content/locales/ko-KR/plan-plugin.mdx +3 -3
- package/corpus/core/docs/content/locales/ko-KR/server.mdx +2 -2
- package/corpus/core/docs/content/locales/ko-KR/skills-guide.mdx +1 -1
- package/corpus/core/docs/content/locales/ko-KR/template-assets.mdx +1 -1
- package/corpus/core/docs/content/locales/ko-KR/template-dispatch.mdx +2 -2
- package/corpus/core/docs/content/locales/ko-KR/template-plan.mdx +3 -3
- package/corpus/core/docs/content/locales/pt-BR/actions.mdx +1 -1
- package/corpus/core/docs/content/locales/pt-BR/authentication.mdx +1 -1
- package/corpus/core/docs/content/locales/pt-BR/dispatch.mdx +3 -3
- package/corpus/core/docs/content/locales/pt-BR/external-agents.mdx +32 -19
- package/corpus/core/docs/content/locales/pt-BR/mcp-clients.mdx +34 -3
- package/corpus/core/docs/content/locales/pt-BR/mcp-protocol.mdx +32 -15
- package/corpus/core/docs/content/locales/pt-BR/plan-plugin.mdx +3 -3
- package/corpus/core/docs/content/locales/pt-BR/server.mdx +2 -2
- package/corpus/core/docs/content/locales/pt-BR/skills-guide.mdx +1 -1
- package/corpus/core/docs/content/locales/pt-BR/template-assets.mdx +1 -1
- package/corpus/core/docs/content/locales/pt-BR/template-dispatch.mdx +2 -2
- package/corpus/core/docs/content/locales/pt-BR/template-plan.mdx +3 -3
- package/corpus/core/docs/content/locales/zh-CN/actions.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-CN/authentication.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-CN/dispatch.mdx +3 -3
- package/corpus/core/docs/content/locales/zh-CN/external-agents.mdx +23 -19
- package/corpus/core/docs/content/locales/zh-CN/mcp-clients.mdx +28 -3
- package/corpus/core/docs/content/locales/zh-CN/mcp-protocol.mdx +29 -15
- package/corpus/core/docs/content/locales/zh-CN/plan-plugin.mdx +3 -3
- package/corpus/core/docs/content/locales/zh-CN/server.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-CN/skills-guide.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-CN/template-assets.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-CN/template-dispatch.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-CN/template-plan.mdx +3 -3
- package/corpus/core/docs/content/locales/zh-TW/actions.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-TW/authentication.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-TW/dispatch.mdx +3 -3
- package/corpus/core/docs/content/locales/zh-TW/external-agents.mdx +23 -19
- package/corpus/core/docs/content/locales/zh-TW/mcp-clients.mdx +28 -3
- package/corpus/core/docs/content/locales/zh-TW/mcp-protocol.mdx +30 -15
- package/corpus/core/docs/content/locales/zh-TW/plan-plugin.mdx +3 -3
- package/corpus/core/docs/content/locales/zh-TW/server.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-TW/skills-guide.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-TW/template-assets.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-TW/template-dispatch.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-TW/template-plan.mdx +3 -3
- package/corpus/core/docs/content/mcp-clients.mdx +31 -4
- package/corpus/core/docs/content/mcp-protocol.mdx +35 -15
- package/corpus/core/docs/content/plan-plugin.mdx +3 -3
- package/corpus/core/docs/content/server.mdx +2 -2
- package/corpus/core/docs/content/skills-guide.mdx +1 -1
- package/corpus/core/docs/content/template-assets.mdx +1 -1
- package/corpus/core/docs/content/template-dispatch.mdx +2 -2
- package/corpus/core/docs/content/template-plan.mdx +3 -3
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/client.ts +3 -0
- package/corpus/core/src/a2a/handlers.ts +95 -7
- package/corpus/core/src/agent/production-agent.ts +21 -0
- package/corpus/core/src/cli/connect.ts +50 -24
- package/corpus/core/src/cli/mcp-config-writers.ts +10 -0
- package/corpus/core/src/cli/mcp.ts +5 -5
- package/corpus/core/src/cli/pr-visual-recap-workflow.ts +1 -1
- package/corpus/core/src/cli/skills-content/assets-skill.ts +1 -1
- package/corpus/core/src/cli/skills-content/design-exploration-skill.ts +1 -1
- package/corpus/core/src/cli/skills-content/help.ts +1 -1
- package/corpus/core/src/cli/skills-content/plan-setup-auth.ts +1 -1
- package/corpus/core/src/cli/skills-content/visual-plan-skill.ts +1 -1
- package/corpus/core/src/cli/skills-content/visual-recap-skill.ts +2 -2
- package/corpus/core/src/cli/skills.ts +23 -12
- package/corpus/core/src/client/analytics.ts +13 -0
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +5 -0
- package/corpus/core/src/client/resources/mcp-integration-catalog.ts +17 -0
- package/corpus/core/src/embedding/agent.ts +8 -6
- package/corpus/core/src/localization/default-messages.ts +7 -0
- package/corpus/core/src/mcp/ask-app-inline-tasks.ts +125 -0
- package/corpus/core/src/mcp/build-server.ts +65 -3
- package/corpus/core/src/mcp/builtin-tools.ts +186 -22
- package/corpus/core/src/mcp/connect-route.ts +17 -14
- package/corpus/core/src/mcp/oauth-route.ts +38 -13
- package/corpus/core/src/mcp/route-paths.ts +34 -0
- package/corpus/core/src/mcp/server.ts +21 -10
- package/corpus/core/src/mcp/stdio.ts +34 -16
- package/corpus/core/src/mcp/workspace-resolve.ts +2 -1
- package/corpus/core/src/mcp-client/manager.ts +5 -3
- package/corpus/core/src/mcp-client/remote-store.ts +10 -10
- package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +13 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +42 -9
- package/corpus/core/src/server/agent-teams.ts +17 -0
- package/corpus/core/src/server/auth.ts +22 -6
- package/corpus/core/src/server/core-routes-plugin.ts +30 -24
- package/corpus/core/src/server/csrf.ts +17 -0
- package/corpus/core/src/server/framework-request-handler.ts +8 -2
- package/corpus/core/src/server/google-oauth.ts +12 -2
- package/corpus/core/src/server/http-response-telemetry.ts +6 -1
- package/corpus/core/src/server/recap-image-route.ts +3 -3
- package/corpus/core/src/server/security-headers.ts +7 -1
- package/corpus/core/src/server/ssr-handler.ts +2 -0
- package/corpus/core/src/shared/cache-control.ts +6 -4
- package/corpus/core/src/templates/default/.agents/skills/security/SKILL.md +10 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +15 -6
- package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +16 -10
- package/corpus/core/src/templates/workspace-core/.agents/skills/performance/SKILL.md +20 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/security/SKILL.md +10 -0
- package/corpus/templates/analytics/.agents/skills/security/SKILL.md +10 -0
- package/corpus/templates/analytics/app/assets/replay-cursor.svg +17 -0
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +4 -3
- package/corpus/templates/analytics/app/global.css +71 -4
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +32 -4
- package/corpus/templates/analytics/app/pages/monitoring/errors/IssueDetail.tsx +1 -1
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +49 -11
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +4 -6
- package/corpus/templates/analytics/changelog/2026-07-13-all-time-dashboard-filters-no-longer-send-invalid-dates-to-b.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-13-daily-dashboard-emails-recover-more-reliably-from-serverless.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-13-error-issue-titles-wrap-instead-of-overflowing.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-13-saving-a-new-dashboard-view-no-longer-fails-with-a-server-er.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-13-session-replay-cursors-stay-hidden-until-their-first-recorde.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-13-session-replay-links-now-support-cmd-click-and-middle-click-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-13-session-replays-keep-a-visible-mac-style-cursor-over-the-vis.md +6 -0
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +21 -2
- package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +57 -1
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +266 -28
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +43 -4
- package/corpus/templates/analytics/server/lib/error-capture.ts +24 -5
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +5 -0
- package/corpus/templates/analytics/server/routes/[...page].get.ts +0 -2
- package/corpus/templates/assets/.agents/skills/security/SKILL.md +10 -0
- package/corpus/templates/assets/agent-native.app-skill.json +1 -1
- package/corpus/templates/brain/.agents/skills/security/SKILL.md +10 -0
- package/corpus/templates/calendar/.agents/skills/security/SKILL.md +10 -0
- package/corpus/templates/calendar/AGENTS.md +2 -1
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +0 -19
- package/corpus/templates/calendar/app/lib/attendee-local-time.ts +14 -5
- package/corpus/templates/calendar/changelog/2026-07-13-your-local-time-now-follows-your-browser-timezone-and-event-.md +6 -0
- package/corpus/templates/chat/.agents/skills/security/SKILL.md +10 -0
- package/corpus/templates/clips/.agents/skills/recording/SKILL.md +7 -2
- package/corpus/templates/clips/.agents/skills/security/SKILL.md +10 -0
- package/corpus/templates/clips/AGENTS.md +7 -0
- package/corpus/templates/clips/actions/list-meetings.ts +19 -4
- package/corpus/templates/clips/actions/start-meeting-recording.ts +7 -1
- package/corpus/templates/clips/app/components/library/recording-card.tsx +27 -26
- package/corpus/templates/clips/app/components/library/space-card.tsx +4 -6
- package/corpus/templates/clips/app/components/meetings/transcript-bubbles.tsx +75 -78
- package/corpus/templates/clips/app/components/recorder/confetti-canvas.tsx +18 -1
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +6 -12
- package/corpus/templates/clips/app/components/recorder/recording-toolbar.tsx +19 -2
- package/corpus/templates/clips/app/lib/camera-composite.ts +70 -10
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +1 -1
- package/corpus/templates/clips/app/routes/record.tsx +5 -25
- package/corpus/templates/clips/changelog/2026-07-13-clips-desktop-now-requires-macos-13-for-native-capture.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-13-clips-hide-solo-meeting-reminders.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-13-clips-now-removes-duplicate-mic-and-system-transcript-turns-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-13-desktop-recordings-run-cooler-and-quieter-live-transcription.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-13-meeting-recordings-now-stop-after-a-calendar-meeting-ends-an.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-13-meeting-transcripts-now-use-a-clean-agent-chat-style-reading.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-13-screen-recordings-use-less-processing-power-during-longer-ca.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/background.ts +5 -3
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +17 -14
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +8 -2
- package/corpus/templates/clips/desktop/README.md +3 -0
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +9 -0
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -2
- package/corpus/templates/clips/desktop/src/lib/silence-events.ts +1 -0
- package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +5 -0
- package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +134 -5
- package/corpus/templates/clips/desktop/src/lib/upload-video-stream.ts +28 -0
- package/corpus/templates/clips/desktop/src/overlays/flow-bar.tsx +10 -3
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +42 -9
- package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +10 -2
- package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +6 -1
- package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +77 -1
- package/corpus/templates/clips/desktop/src-tauri/src/silence_detector.rs +104 -25
- package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +4 -0
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +287 -32
- package/corpus/templates/clips/desktop/src-tauri/tauri.conf.json +1 -1
- package/corpus/templates/clips/server/lib/calendar-event-classification.ts +28 -0
- package/corpus/templates/clips/server/routes/[...page].get.ts +1 -59
- package/corpus/templates/clips/shared/recording-capture.ts +37 -0
- package/corpus/templates/content/.agents/skills/security/SKILL.md +10 -0
- package/corpus/templates/content/agent-native.app-skill.json +1 -1
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +18 -16
- package/corpus/templates/content/server/routes/[...page].get.ts +1 -15
- package/corpus/templates/design/.agents/skills/security/SKILL.md +10 -0
- package/corpus/templates/design/actions/connect-assets-mcp.ts +1 -1
- package/corpus/templates/design/agent-native.app-skill.json +1 -1
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +88 -12
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1 -0
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +4 -1
- package/corpus/templates/design/changelog/2026-07-13-adding-shapes-preserves-canvas-background.md +6 -0
- package/corpus/templates/design/changelog/2026-07-13-clicking-quickly-between-overlapping-elements-no-longer-bounc.md +6 -0
- package/corpus/templates/design/server/plugins/assets-mcp.ts +1 -1
- package/corpus/templates/design/server/routes/[...page].get.ts +0 -2
- package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +10 -0
- package/corpus/templates/forms/.agents/skills/security/SKILL.md +10 -0
- package/corpus/templates/macros/.agents/skills/security/SKILL.md +10 -0
- package/corpus/templates/mail/.agents/skills/security/SKILL.md +10 -0
- package/corpus/templates/plan/.agents/skills/security/SKILL.md +10 -0
- package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +1 -1
- package/corpus/templates/plan/.agents/skills/visual-recap/SKILL.md +2 -2
- package/corpus/templates/plan/agent-native.app-skill.json +1 -1
- package/corpus/templates/plan/app/lib/plan-local-bridge.ts +6 -1
- package/corpus/templates/plan/app/pages/PlansPage.tsx +15 -6
- package/corpus/templates/plan/changelog/2026-07-13-fixed-local-plan-previews-continuously-flickering-while-revi.md +6 -0
- package/corpus/templates/plan/server/routes/[...page].get.ts +0 -2
- package/corpus/templates/slides/.agents/skills/security/SKILL.md +10 -0
- package/dist/a2a/client.d.ts +2 -0
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +2 -0
- package/dist/a2a/client.js.map +1 -1
- package/dist/a2a/handlers.d.ts.map +1 -1
- package/dist/a2a/handlers.js +84 -7
- package/dist/a2a/handlers.js.map +1 -1
- package/dist/agent/production-agent.d.ts +9 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +12 -0
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/connect.d.ts +2 -2
- package/dist/cli/connect.d.ts.map +1 -1
- package/dist/cli/connect.js +45 -23
- package/dist/cli/connect.js.map +1 -1
- package/dist/cli/mcp-config-writers.d.ts.map +1 -1
- package/dist/cli/mcp-config-writers.js +7 -0
- package/dist/cli/mcp-config-writers.js.map +1 -1
- package/dist/cli/mcp.js +5 -3
- package/dist/cli/mcp.js.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.js +1 -1
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/cli/skills-content/assets-skill.d.ts +1 -1
- package/dist/cli/skills-content/assets-skill.d.ts.map +1 -1
- package/dist/cli/skills-content/assets-skill.js +1 -1
- package/dist/cli/skills-content/assets-skill.js.map +1 -1
- package/dist/cli/skills-content/design-exploration-skill.d.ts +1 -1
- package/dist/cli/skills-content/design-exploration-skill.d.ts.map +1 -1
- package/dist/cli/skills-content/design-exploration-skill.js +1 -1
- package/dist/cli/skills-content/design-exploration-skill.js.map +1 -1
- package/dist/cli/skills-content/help.d.ts +1 -1
- package/dist/cli/skills-content/help.d.ts.map +1 -1
- package/dist/cli/skills-content/help.js +1 -1
- package/dist/cli/skills-content/help.js.map +1 -1
- package/dist/cli/skills-content/plan-setup-auth.d.ts +1 -1
- package/dist/cli/skills-content/plan-setup-auth.d.ts.map +1 -1
- package/dist/cli/skills-content/plan-setup-auth.js +1 -1
- package/dist/cli/skills-content/plan-setup-auth.js.map +1 -1
- package/dist/cli/skills-content/visual-plan-skill.d.ts +1 -1
- package/dist/cli/skills-content/visual-plan-skill.d.ts.map +1 -1
- package/dist/cli/skills-content/visual-plan-skill.js +1 -1
- package/dist/cli/skills-content/visual-plan-skill.js.map +1 -1
- package/dist/cli/skills-content/visual-recap-skill.d.ts +1 -1
- package/dist/cli/skills-content/visual-recap-skill.d.ts.map +1 -1
- package/dist/cli/skills-content/visual-recap-skill.js +2 -2
- package/dist/cli/skills-content/visual-recap-skill.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +16 -12
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +11 -0
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +1 -1
- package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.d.ts +1 -0
- package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.js +14 -0
- package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/embedding/agent.d.ts.map +1 -1
- package/dist/embedding/agent.js +6 -7
- package/dist/embedding/agent.js.map +1 -1
- package/dist/localization/default-messages.d.ts +5 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +5 -0
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/mcp/ask-app-inline-tasks.d.ts +47 -0
- package/dist/mcp/ask-app-inline-tasks.d.ts.map +1 -0
- package/dist/mcp/ask-app-inline-tasks.js +87 -0
- package/dist/mcp/ask-app-inline-tasks.js.map +1 -0
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +56 -3
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/builtin-tools.d.ts.map +1 -1
- package/dist/mcp/builtin-tools.js +141 -21
- package/dist/mcp/builtin-tools.js.map +1 -1
- package/dist/mcp/connect-route.d.ts +12 -10
- package/dist/mcp/connect-route.d.ts.map +1 -1
- package/dist/mcp/connect-route.js +17 -14
- package/dist/mcp/connect-route.js.map +1 -1
- package/dist/mcp/oauth-route.d.ts +1 -1
- package/dist/mcp/oauth-route.d.ts.map +1 -1
- package/dist/mcp/oauth-route.js +30 -13
- package/dist/mcp/oauth-route.js.map +1 -1
- package/dist/mcp/route-paths.d.ts +13 -0
- package/dist/mcp/route-paths.d.ts.map +1 -0
- package/dist/mcp/route-paths.js +27 -0
- package/dist/mcp/route-paths.js.map +1 -0
- package/dist/mcp/server.d.ts +2 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +14 -6
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/stdio.d.ts +7 -6
- package/dist/mcp/stdio.d.ts.map +1 -1
- package/dist/mcp/stdio.js +27 -15
- package/dist/mcp/stdio.js.map +1 -1
- package/dist/mcp/workspace-resolve.d.ts +2 -1
- package/dist/mcp/workspace-resolve.d.ts.map +1 -1
- package/dist/mcp/workspace-resolve.js +2 -1
- package/dist/mcp/workspace-resolve.js.map +1 -1
- package/dist/mcp-client/manager.js +6 -3
- package/dist/mcp-client/manager.js.map +1 -1
- package/dist/mcp-client/remote-store.js +5 -7
- package/dist/mcp-client/remote-store.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
- package/dist/server/agent-chat/action-filters-a2a.js +11 -1
- package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +43 -11
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-teams.d.ts.map +1 -1
- package/dist/server/agent-teams.js +15 -1
- package/dist/server/agent-teams.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +16 -6
- package/dist/server/auth.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +4 -3
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +20 -15
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/csrf.d.ts.map +1 -1
- package/dist/server/csrf.js +11 -0
- package/dist/server/csrf.js.map +1 -1
- package/dist/server/framework-request-handler.d.ts.map +1 -1
- package/dist/server/framework-request-handler.js +8 -2
- package/dist/server/framework-request-handler.js.map +1 -1
- package/dist/server/google-oauth.d.ts +2 -0
- package/dist/server/google-oauth.d.ts.map +1 -1
- package/dist/server/google-oauth.js +11 -2
- package/dist/server/google-oauth.js.map +1 -1
- package/dist/server/http-response-telemetry.d.ts.map +1 -1
- package/dist/server/http-response-telemetry.js +4 -1
- package/dist/server/http-response-telemetry.js.map +1 -1
- package/dist/server/recap-image-route.js +3 -3
- package/dist/server/recap-image-route.js.map +1 -1
- package/dist/server/security-headers.d.ts.map +1 -1
- package/dist/server/security-headers.js +4 -1
- package/dist/server/security-headers.js.map +1 -1
- package/dist/server/ssr-handler.d.ts.map +1 -1
- package/dist/server/ssr-handler.js +3 -1
- package/dist/server/ssr-handler.js.map +1 -1
- package/dist/shared/cache-control.d.ts +7 -6
- package/dist/shared/cache-control.d.ts.map +1 -1
- package/dist/shared/cache-control.js +4 -3
- package/dist/shared/cache-control.js.map +1 -1
- package/dist/templates/default/.agents/skills/security/SKILL.md +10 -0
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +15 -6
- package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +16 -10
- package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +20 -1
- package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +10 -0
- package/docs/content/actions.mdx +1 -1
- package/docs/content/authentication.mdx +1 -1
- package/docs/content/dispatch.mdx +3 -3
- package/docs/content/external-agents.mdx +30 -19
- package/docs/content/locales/ar-SA/actions.mdx +1 -1
- package/docs/content/locales/ar-SA/authentication.mdx +1 -1
- package/docs/content/locales/ar-SA/dispatch.mdx +3 -3
- package/docs/content/locales/ar-SA/external-agents.mdx +23 -19
- package/docs/content/locales/ar-SA/mcp-clients.mdx +31 -3
- package/docs/content/locales/ar-SA/mcp-protocol.mdx +29 -15
- package/docs/content/locales/ar-SA/plan-plugin.mdx +3 -3
- package/docs/content/locales/ar-SA/server.mdx +2 -2
- package/docs/content/locales/ar-SA/skills-guide.mdx +1 -1
- package/docs/content/locales/ar-SA/template-assets.mdx +1 -1
- package/docs/content/locales/ar-SA/template-dispatch.mdx +2 -2
- package/docs/content/locales/ar-SA/template-plan.mdx +3 -3
- package/docs/content/locales/de-DE/actions.mdx +1 -1
- package/docs/content/locales/de-DE/authentication.mdx +1 -1
- package/docs/content/locales/de-DE/dispatch.mdx +3 -3
- package/docs/content/locales/de-DE/external-agents.mdx +23 -19
- package/docs/content/locales/de-DE/mcp-clients.mdx +37 -3
- package/docs/content/locales/de-DE/mcp-protocol.mdx +29 -15
- package/docs/content/locales/de-DE/plan-plugin.mdx +3 -3
- package/docs/content/locales/de-DE/server.mdx +2 -2
- package/docs/content/locales/de-DE/skills-guide.mdx +1 -1
- package/docs/content/locales/de-DE/template-assets.mdx +1 -1
- package/docs/content/locales/de-DE/template-dispatch.mdx +2 -2
- package/docs/content/locales/de-DE/template-plan.mdx +3 -3
- package/docs/content/locales/es-ES/actions.mdx +1 -1
- package/docs/content/locales/es-ES/authentication.mdx +1 -1
- package/docs/content/locales/es-ES/dispatch.mdx +3 -3
- package/docs/content/locales/es-ES/external-agents.mdx +32 -19
- package/docs/content/locales/es-ES/mcp-clients.mdx +17 -3
- package/docs/content/locales/es-ES/mcp-protocol.mdx +38 -15
- package/docs/content/locales/es-ES/plan-plugin.mdx +3 -3
- package/docs/content/locales/es-ES/server.mdx +2 -2
- package/docs/content/locales/es-ES/skills-guide.mdx +1 -1
- package/docs/content/locales/es-ES/template-assets.mdx +1 -1
- package/docs/content/locales/es-ES/template-dispatch.mdx +2 -2
- package/docs/content/locales/es-ES/template-plan.mdx +3 -3
- package/docs/content/locales/fr-FR/actions.mdx +1 -1
- package/docs/content/locales/fr-FR/authentication.mdx +1 -1
- package/docs/content/locales/fr-FR/dispatch.mdx +3 -3
- package/docs/content/locales/fr-FR/external-agents.mdx +23 -19
- package/docs/content/locales/fr-FR/mcp-clients.mdx +17 -3
- package/docs/content/locales/fr-FR/mcp-protocol.mdx +29 -15
- package/docs/content/locales/fr-FR/plan-plugin.mdx +3 -3
- package/docs/content/locales/fr-FR/server.mdx +2 -2
- package/docs/content/locales/fr-FR/skills-guide.mdx +1 -1
- package/docs/content/locales/fr-FR/template-assets.mdx +1 -1
- package/docs/content/locales/fr-FR/template-dispatch.mdx +2 -2
- package/docs/content/locales/fr-FR/template-plan.mdx +3 -3
- package/docs/content/locales/hi-IN/actions.mdx +1 -1
- package/docs/content/locales/hi-IN/authentication.mdx +1 -1
- package/docs/content/locales/hi-IN/dispatch.mdx +3 -3
- package/docs/content/locales/hi-IN/external-agents.mdx +31 -19
- package/docs/content/locales/hi-IN/mcp-clients.mdx +17 -3
- package/docs/content/locales/hi-IN/mcp-protocol.mdx +36 -15
- package/docs/content/locales/hi-IN/plan-plugin.mdx +3 -3
- package/docs/content/locales/hi-IN/server.mdx +2 -2
- package/docs/content/locales/hi-IN/skills-guide.mdx +1 -1
- package/docs/content/locales/hi-IN/template-assets.mdx +1 -1
- package/docs/content/locales/hi-IN/template-dispatch.mdx +2 -2
- package/docs/content/locales/hi-IN/template-plan.mdx +3 -3
- package/docs/content/locales/ja-JP/actions.mdx +1 -1
- package/docs/content/locales/ja-JP/authentication.mdx +1 -1
- package/docs/content/locales/ja-JP/dispatch.mdx +3 -3
- package/docs/content/locales/ja-JP/external-agents.mdx +23 -19
- package/docs/content/locales/ja-JP/mcp-clients.mdx +34 -3
- package/docs/content/locales/ja-JP/mcp-protocol.mdx +29 -15
- package/docs/content/locales/ja-JP/plan-plugin.mdx +3 -3
- package/docs/content/locales/ja-JP/server.mdx +2 -2
- package/docs/content/locales/ja-JP/skills-guide.mdx +1 -1
- package/docs/content/locales/ja-JP/template-assets.mdx +1 -1
- package/docs/content/locales/ja-JP/template-dispatch.mdx +2 -2
- package/docs/content/locales/ja-JP/template-plan.mdx +3 -3
- package/docs/content/locales/ko-KR/actions.mdx +1 -1
- package/docs/content/locales/ko-KR/authentication.mdx +1 -1
- package/docs/content/locales/ko-KR/dispatch.mdx +3 -3
- package/docs/content/locales/ko-KR/external-agents.mdx +30 -19
- package/docs/content/locales/ko-KR/mcp-clients.mdx +33 -3
- package/docs/content/locales/ko-KR/mcp-protocol.mdx +36 -15
- package/docs/content/locales/ko-KR/plan-plugin.mdx +3 -3
- package/docs/content/locales/ko-KR/server.mdx +2 -2
- package/docs/content/locales/ko-KR/skills-guide.mdx +1 -1
- package/docs/content/locales/ko-KR/template-assets.mdx +1 -1
- package/docs/content/locales/ko-KR/template-dispatch.mdx +2 -2
- package/docs/content/locales/ko-KR/template-plan.mdx +3 -3
- package/docs/content/locales/pt-BR/actions.mdx +1 -1
- package/docs/content/locales/pt-BR/authentication.mdx +1 -1
- package/docs/content/locales/pt-BR/dispatch.mdx +3 -3
- package/docs/content/locales/pt-BR/external-agents.mdx +32 -19
- package/docs/content/locales/pt-BR/mcp-clients.mdx +34 -3
- package/docs/content/locales/pt-BR/mcp-protocol.mdx +32 -15
- package/docs/content/locales/pt-BR/plan-plugin.mdx +3 -3
- package/docs/content/locales/pt-BR/server.mdx +2 -2
- package/docs/content/locales/pt-BR/skills-guide.mdx +1 -1
- package/docs/content/locales/pt-BR/template-assets.mdx +1 -1
- package/docs/content/locales/pt-BR/template-dispatch.mdx +2 -2
- package/docs/content/locales/pt-BR/template-plan.mdx +3 -3
- package/docs/content/locales/zh-CN/actions.mdx +1 -1
- package/docs/content/locales/zh-CN/authentication.mdx +1 -1
- package/docs/content/locales/zh-CN/dispatch.mdx +3 -3
- package/docs/content/locales/zh-CN/external-agents.mdx +23 -19
- package/docs/content/locales/zh-CN/mcp-clients.mdx +28 -3
- package/docs/content/locales/zh-CN/mcp-protocol.mdx +29 -15
- package/docs/content/locales/zh-CN/plan-plugin.mdx +3 -3
- package/docs/content/locales/zh-CN/server.mdx +2 -2
- package/docs/content/locales/zh-CN/skills-guide.mdx +1 -1
- package/docs/content/locales/zh-CN/template-assets.mdx +1 -1
- package/docs/content/locales/zh-CN/template-dispatch.mdx +2 -2
- package/docs/content/locales/zh-CN/template-plan.mdx +3 -3
- package/docs/content/locales/zh-TW/actions.mdx +1 -1
- package/docs/content/locales/zh-TW/authentication.mdx +1 -1
- package/docs/content/locales/zh-TW/dispatch.mdx +3 -3
- package/docs/content/locales/zh-TW/external-agents.mdx +23 -19
- package/docs/content/locales/zh-TW/mcp-clients.mdx +28 -3
- package/docs/content/locales/zh-TW/mcp-protocol.mdx +30 -15
- package/docs/content/locales/zh-TW/plan-plugin.mdx +3 -3
- package/docs/content/locales/zh-TW/server.mdx +2 -2
- package/docs/content/locales/zh-TW/skills-guide.mdx +1 -1
- package/docs/content/locales/zh-TW/template-assets.mdx +1 -1
- package/docs/content/locales/zh-TW/template-dispatch.mdx +2 -2
- package/docs/content/locales/zh-TW/template-plan.mdx +3 -3
- package/docs/content/mcp-clients.mdx +31 -4
- package/docs/content/mcp-protocol.mdx +35 -15
- package/docs/content/plan-plugin.mdx +3 -3
- package/docs/content/server.mdx +2 -2
- package/docs/content/skills-guide.mdx +1 -1
- package/docs/content/template-assets.mdx +1 -1
- package/docs/content/template-dispatch.mdx +2 -2
- package/docs/content/template-plan.mdx +3 -3
- package/package.json +2 -2
- package/src/templates/default/.agents/skills/security/SKILL.md +10 -0
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +15 -6
- package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +16 -10
- package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +20 -1
- package/src/templates/workspace-core/.agents/skills/security/SKILL.md +10 -0
|
@@ -3,6 +3,7 @@ import { runWithRequestContext } from "@agent-native/core/server/request-context
|
|
|
3
3
|
import { sendDashboardReportSubscription } from "../lib/dashboard-report";
|
|
4
4
|
import {
|
|
5
5
|
claimDueDashboardReportSubscriptions,
|
|
6
|
+
dashboardReportRetryAt,
|
|
6
7
|
markDashboardReportResult,
|
|
7
8
|
} from "../lib/dashboard-report-subscriptions";
|
|
8
9
|
|
|
@@ -41,22 +42,40 @@ export async function runDashboardReportsOnce(): Promise<{
|
|
|
41
42
|
for (const sub of batch) {
|
|
42
43
|
processed++;
|
|
43
44
|
try {
|
|
45
|
+
const retryAt = dashboardReportRetryAt(sub);
|
|
44
46
|
const result = await runWithRequestContext(
|
|
45
47
|
{
|
|
46
48
|
userEmail: sub.ownerEmail,
|
|
47
49
|
orgId: sub.orgId ?? undefined,
|
|
48
50
|
},
|
|
49
|
-
() =>
|
|
51
|
+
() =>
|
|
52
|
+
sendDashboardReportSubscription(sub, {
|
|
53
|
+
skipEmailWithoutScreenshot: retryAt !== null,
|
|
54
|
+
}),
|
|
50
55
|
);
|
|
51
56
|
if (!result.screenshotAttached) {
|
|
52
57
|
const message = result.screenshotError
|
|
53
58
|
? `Dashboard screenshot unavailable: ${result.screenshotError}`
|
|
54
59
|
: "Dashboard screenshot unavailable";
|
|
60
|
+
if (retryAt && !result.emailsSent) {
|
|
61
|
+
console.error(
|
|
62
|
+
`[dashboard-report] Subscription ${sub.id} skipped sending without a screenshot, will retry:`,
|
|
63
|
+
message,
|
|
64
|
+
);
|
|
65
|
+
await markDashboardReportResult(
|
|
66
|
+
sub,
|
|
67
|
+
"error",
|
|
68
|
+
`${message} (retry scheduled)`,
|
|
69
|
+
{ nextRunAt: retryAt },
|
|
70
|
+
);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
failed++;
|
|
55
74
|
console.error(
|
|
56
75
|
`[dashboard-report] Subscription ${sub.id} sent without a screenshot:`,
|
|
57
76
|
message,
|
|
58
77
|
);
|
|
59
|
-
await markDashboardReportResult(sub, "
|
|
78
|
+
await markDashboardReportResult(sub, "error", message);
|
|
60
79
|
continue;
|
|
61
80
|
}
|
|
62
81
|
await markDashboardReportResult(sub, "success");
|
|
@@ -158,6 +158,19 @@ function calendarDayAfter(parts: { year: number; month: number; day: number }) {
|
|
|
158
158
|
};
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
function calendarDayBefore(parts: {
|
|
162
|
+
year: number;
|
|
163
|
+
month: number;
|
|
164
|
+
day: number;
|
|
165
|
+
}) {
|
|
166
|
+
const prev = new Date(Date.UTC(parts.year, parts.month - 1, parts.day - 1));
|
|
167
|
+
return {
|
|
168
|
+
year: prev.getUTCFullYear(),
|
|
169
|
+
month: prev.getUTCMonth() + 1,
|
|
170
|
+
day: prev.getUTCDate(),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
161
174
|
function zonedTimeToUtc(
|
|
162
175
|
parts: { year: number; month: number; day: number },
|
|
163
176
|
hour: number,
|
|
@@ -205,6 +218,47 @@ export function nextDailyRunAt(
|
|
|
205
218
|
return candidate.toISOString();
|
|
206
219
|
}
|
|
207
220
|
|
|
221
|
+
export function lastDailyRunAt(
|
|
222
|
+
timeOfDay: string,
|
|
223
|
+
timeZone: string,
|
|
224
|
+
from: Date = new Date(),
|
|
225
|
+
): string {
|
|
226
|
+
const [hour, minute] = assertTimeOfDay(timeOfDay).split(":").map(Number);
|
|
227
|
+
const timezone = assertTimezone(timeZone);
|
|
228
|
+
const today = getZonedParts(from, timezone);
|
|
229
|
+
let candidate = zonedTimeToUtc(today, hour, minute, timezone);
|
|
230
|
+
if (candidate.getTime() > from.getTime()) {
|
|
231
|
+
candidate = zonedTimeToUtc(
|
|
232
|
+
calendarDayBefore(today),
|
|
233
|
+
hour,
|
|
234
|
+
minute,
|
|
235
|
+
timezone,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
return candidate.toISOString();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const DASHBOARD_REPORT_RETRY_WINDOW_MS = 60 * 60 * 1000;
|
|
242
|
+
const DASHBOARD_REPORT_RETRY_DELAY_MS = 10 * 60 * 1000;
|
|
243
|
+
|
|
244
|
+
export function dashboardReportRetryAt(
|
|
245
|
+
sub: DashboardReportSubscription,
|
|
246
|
+
now: Date = new Date(),
|
|
247
|
+
): string | null {
|
|
248
|
+
if (!sub.enabled) return null;
|
|
249
|
+
try {
|
|
250
|
+
const anchor = Date.parse(lastDailyRunAt(sub.timeOfDay, sub.timezone, now));
|
|
251
|
+
if (now.getTime() - anchor < DASHBOARD_REPORT_RETRY_WINDOW_MS) {
|
|
252
|
+
return new Date(
|
|
253
|
+
now.getTime() + DASHBOARD_REPORT_RETRY_DELAY_MS,
|
|
254
|
+
).toISOString();
|
|
255
|
+
}
|
|
256
|
+
return null;
|
|
257
|
+
} catch {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
208
262
|
function rowToSubscription(row: any): DashboardReportSubscription {
|
|
209
263
|
return {
|
|
210
264
|
id: row.id,
|
|
@@ -477,6 +531,7 @@ export async function markDashboardReportResult(
|
|
|
477
531
|
sub: DashboardReportSubscription,
|
|
478
532
|
status: "success" | "error",
|
|
479
533
|
error?: string,
|
|
534
|
+
options?: { nextRunAt?: string },
|
|
480
535
|
): Promise<void> {
|
|
481
536
|
const now = nowIso();
|
|
482
537
|
const db = getDb() as any;
|
|
@@ -486,7 +541,8 @@ export async function markDashboardReportResult(
|
|
|
486
541
|
lastStatus: status,
|
|
487
542
|
lastError: error ? error.slice(0, 500) : null,
|
|
488
543
|
nextRunAt: sub.enabled
|
|
489
|
-
?
|
|
544
|
+
? (options?.nextRunAt ??
|
|
545
|
+
nextDailyRunAt(sub.timeOfDay, sub.timezone, new Date()))
|
|
490
546
|
: null,
|
|
491
547
|
updatedAt: now,
|
|
492
548
|
})
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { existsSync } from "node:fs";
|
|
3
|
+
import { readdir, rm, stat } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
2
6
|
|
|
3
7
|
import {
|
|
4
8
|
getAppProductionUrl,
|
|
@@ -44,11 +48,24 @@ const DASHBOARD_REPORT_CID = "dashboard-report-snapshot";
|
|
|
44
48
|
const LOCAL_SCREENSHOT_TIMEOUT_MS = 90_000;
|
|
45
49
|
const SERVERLESS_SCREENSHOT_TIMEOUT_MS = 90_000;
|
|
46
50
|
const SERVERLESS_SECOND_READY_TIMEOUT_MS = 45_000;
|
|
51
|
+
const SERVERLESS_FULL_ATTEMPT_TIMEOUT_MS = 125_000;
|
|
52
|
+
const SERVERLESS_LIGHTWEIGHT_ATTEMPT_TIMEOUT_MS = 85_000;
|
|
53
|
+
const BROWSER_CLEANUP_TIMEOUT_MS = 10_000;
|
|
47
54
|
const SCREENSHOT_VIEWPORT_PADDING = 64;
|
|
48
55
|
|
|
49
56
|
type DashboardScreenshotAttempt = {
|
|
50
57
|
label: "full" | "full-lightweight";
|
|
51
58
|
viewport: { width: number; height: number };
|
|
59
|
+
captureScale?: number;
|
|
60
|
+
readyTimeout?: number;
|
|
61
|
+
secondReadyTimeout?: number;
|
|
62
|
+
totalTimeout?: number;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
type LaunchedScreenshotBrowser = {
|
|
66
|
+
browser: any;
|
|
67
|
+
cleanup: () => Promise<void>;
|
|
68
|
+
newPage: () => Promise<any>;
|
|
52
69
|
};
|
|
53
70
|
|
|
54
71
|
function daysAgo(n: number): string {
|
|
@@ -216,14 +233,44 @@ function localChromiumExecutablePath(): string | null {
|
|
|
216
233
|
return candidates.find((candidate) => existsSync(candidate)) ?? null;
|
|
217
234
|
}
|
|
218
235
|
|
|
219
|
-
async function
|
|
236
|
+
async function sweepStaleScreenshotProfiles(): Promise<void> {
|
|
237
|
+
let entries: string[];
|
|
238
|
+
try {
|
|
239
|
+
entries = await readdir(tmpdir());
|
|
240
|
+
} catch {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const stale = entries
|
|
244
|
+
.filter((entry) => entry.startsWith("dashboard-report-playwright-"))
|
|
245
|
+
.slice(0, 8);
|
|
246
|
+
for (const entry of stale) {
|
|
247
|
+
const full = join(tmpdir(), entry);
|
|
248
|
+
const info = await stat(full).catch(() => null);
|
|
249
|
+
if (info && Date.now() - info.mtimeMs > 30 * 60_000) {
|
|
250
|
+
await rm(full, { recursive: true, force: true }).catch(() => {});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function launchScreenshotBrowser(
|
|
256
|
+
viewport: DashboardScreenshotAttempt["viewport"],
|
|
257
|
+
): Promise<LaunchedScreenshotBrowser> {
|
|
220
258
|
const { chromium: playwright } = await import("playwright-core");
|
|
221
259
|
const localExecutablePath = localChromiumExecutablePath();
|
|
222
260
|
if (localExecutablePath) {
|
|
223
|
-
|
|
261
|
+
const browser = await playwright.launch({
|
|
224
262
|
executablePath: localExecutablePath,
|
|
225
263
|
headless: true,
|
|
226
264
|
});
|
|
265
|
+
return {
|
|
266
|
+
browser,
|
|
267
|
+
cleanup: async () => {},
|
|
268
|
+
newPage: () =>
|
|
269
|
+
browser.newPage({
|
|
270
|
+
viewport,
|
|
271
|
+
deviceScaleFactor: 1,
|
|
272
|
+
}),
|
|
273
|
+
};
|
|
227
274
|
}
|
|
228
275
|
|
|
229
276
|
if (isServerlessBrowserRuntime()) {
|
|
@@ -232,19 +279,54 @@ async function launchScreenshotBrowser() {
|
|
|
232
279
|
const packUrl =
|
|
233
280
|
process.env.DASHBOARD_REPORT_CHROMIUM_PACK_URL?.trim() ||
|
|
234
281
|
DEFAULT_SERVERLESS_CHROMIUM_PACK_URL;
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
282
|
+
await sweepStaleScreenshotProfiles();
|
|
283
|
+
const userDataDir = join(
|
|
284
|
+
tmpdir(),
|
|
285
|
+
`dashboard-report-playwright-${randomUUID()}`,
|
|
286
|
+
);
|
|
287
|
+
const cleanup = async () => {
|
|
288
|
+
await rm(userDataDir, { recursive: true, force: true }).catch((err) => {
|
|
289
|
+
console.error(
|
|
290
|
+
"[dashboard-report] Failed to clean Chromium profile:",
|
|
291
|
+
errorMessage(err),
|
|
292
|
+
);
|
|
293
|
+
});
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
try {
|
|
297
|
+
const browser = await playwright.launchPersistentContext(userDataDir, {
|
|
298
|
+
args: [
|
|
299
|
+
...chromium.args,
|
|
300
|
+
"--disable-dev-shm-usage",
|
|
301
|
+
"--disable-gpu",
|
|
302
|
+
"--hide-scrollbars",
|
|
303
|
+
],
|
|
304
|
+
deviceScaleFactor: 1,
|
|
305
|
+
executablePath: await chromium.executablePath(packUrl),
|
|
306
|
+
headless: true,
|
|
307
|
+
viewport,
|
|
308
|
+
});
|
|
309
|
+
return {
|
|
310
|
+
browser,
|
|
311
|
+
cleanup,
|
|
312
|
+
newPage: () => browser.newPage(),
|
|
313
|
+
};
|
|
314
|
+
} catch (err) {
|
|
315
|
+
await cleanup();
|
|
316
|
+
throw err;
|
|
317
|
+
}
|
|
245
318
|
}
|
|
246
319
|
|
|
247
|
-
|
|
320
|
+
const browser = await playwright.launch({ headless: true });
|
|
321
|
+
return {
|
|
322
|
+
browser,
|
|
323
|
+
cleanup: async () => {},
|
|
324
|
+
newPage: () =>
|
|
325
|
+
browser.newPage({
|
|
326
|
+
viewport,
|
|
327
|
+
deviceScaleFactor: 1,
|
|
328
|
+
}),
|
|
329
|
+
};
|
|
248
330
|
}
|
|
249
331
|
|
|
250
332
|
function screenshotTimeoutMs(): number {
|
|
@@ -350,6 +432,40 @@ async function fitViewportWidthToDashboardCapture(
|
|
|
350
432
|
await page.waitForTimeout(250);
|
|
351
433
|
}
|
|
352
434
|
|
|
435
|
+
async function scaleDashboardCapture(
|
|
436
|
+
page: any,
|
|
437
|
+
scale: number | undefined,
|
|
438
|
+
): Promise<void> {
|
|
439
|
+
if (!scale || scale >= 1) return;
|
|
440
|
+
await page.evaluate(`(() => {
|
|
441
|
+
const root = document.querySelector("[data-dashboard-report-capture]");
|
|
442
|
+
if (root instanceof HTMLElement) root.style.zoom = "${scale}";
|
|
443
|
+
})()`);
|
|
444
|
+
await page.waitForTimeout(250);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
async function runBoundedBrowserCleanup(
|
|
448
|
+
label: string,
|
|
449
|
+
operation: () => Promise<void>,
|
|
450
|
+
): Promise<void> {
|
|
451
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
452
|
+
try {
|
|
453
|
+
await Promise.race([
|
|
454
|
+
operation(),
|
|
455
|
+
new Promise<never>((_, reject) => {
|
|
456
|
+
timeout = setTimeout(
|
|
457
|
+
() => reject(new Error(`${label} exceeded cleanup timeout`)),
|
|
458
|
+
BROWSER_CLEANUP_TIMEOUT_MS,
|
|
459
|
+
);
|
|
460
|
+
}),
|
|
461
|
+
]);
|
|
462
|
+
} catch (err) {
|
|
463
|
+
console.error(`[dashboard-report] ${label}:`, errorMessage(err));
|
|
464
|
+
} finally {
|
|
465
|
+
if (timeout) clearTimeout(timeout);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
353
469
|
async function captureDashboardPng(
|
|
354
470
|
sub: DashboardReportSubscription,
|
|
355
471
|
snapshot: ReportSnapshot,
|
|
@@ -373,13 +489,47 @@ async function captureDashboardPng(
|
|
|
373
489
|
screenshotUrl.searchParams.set(EMBED_MODE_QUERY_PARAM, "1");
|
|
374
490
|
screenshotUrl.searchParams.set(EMBED_TOKEN_QUERY_PARAM, token);
|
|
375
491
|
|
|
376
|
-
|
|
492
|
+
let browser: any;
|
|
493
|
+
let cleanup = async () => {};
|
|
494
|
+
let newPage = async (): Promise<any> => {
|
|
495
|
+
throw new Error("Screenshot browser did not provide a page factory");
|
|
496
|
+
};
|
|
497
|
+
let launchPromise: Promise<LaunchedScreenshotBrowser> | undefined;
|
|
498
|
+
let launchTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
499
|
+
let captureStage = "launching the screenshot browser";
|
|
500
|
+
let attemptTimedOut = false;
|
|
501
|
+
const attemptTimeout = attempt.totalTimeout
|
|
502
|
+
? setTimeout(() => {
|
|
503
|
+
attemptTimedOut = true;
|
|
504
|
+
if (browser) void browser.close().catch(() => {});
|
|
505
|
+
}, attempt.totalTimeout)
|
|
506
|
+
: null;
|
|
377
507
|
try {
|
|
508
|
+
launchPromise = launchScreenshotBrowser(attempt.viewport);
|
|
509
|
+
const launched = attempt.totalTimeout
|
|
510
|
+
? await Promise.race([
|
|
511
|
+
launchPromise,
|
|
512
|
+
new Promise<never>((_, reject) => {
|
|
513
|
+
launchTimeout = setTimeout(() => {
|
|
514
|
+
attemptTimedOut = true;
|
|
515
|
+
reject(
|
|
516
|
+
new Error(
|
|
517
|
+
`${attempt.label} browser launch exceeded ${attempt.totalTimeout}ms`,
|
|
518
|
+
),
|
|
519
|
+
);
|
|
520
|
+
}, attempt.totalTimeout);
|
|
521
|
+
}),
|
|
522
|
+
])
|
|
523
|
+
: await launchPromise;
|
|
524
|
+
browser = launched.browser;
|
|
525
|
+
cleanup = launched.cleanup;
|
|
526
|
+
newPage = launched.newPage;
|
|
527
|
+
if (attemptTimedOut) {
|
|
528
|
+
throw new Error("Screenshot browser launch exceeded attempt timeout");
|
|
529
|
+
}
|
|
530
|
+
|
|
378
531
|
const timeout = screenshotTimeoutMs();
|
|
379
|
-
const page = await
|
|
380
|
-
viewport: attempt.viewport,
|
|
381
|
-
deviceScaleFactor: 1,
|
|
382
|
-
});
|
|
532
|
+
const page = await newPage();
|
|
383
533
|
page.setDefaultTimeout(timeout);
|
|
384
534
|
await page.emulateMedia({ media: "screen", colorScheme: "light" });
|
|
385
535
|
await page.addInitScript(() => {
|
|
@@ -390,14 +540,24 @@ async function captureDashboardPng(
|
|
|
390
540
|
timeout,
|
|
391
541
|
});
|
|
392
542
|
|
|
543
|
+
captureStage = "waiting for the report surface";
|
|
393
544
|
const capture = page.locator("[data-dashboard-report-capture]");
|
|
394
545
|
await capture.waitFor({ state: "visible", timeout });
|
|
395
|
-
|
|
546
|
+
captureStage = "waiting for dashboard queries";
|
|
547
|
+
await waitForDashboardReportReady(page, attempt.readyTimeout ?? timeout);
|
|
548
|
+
captureStage = "rendering lazy dashboard panels";
|
|
396
549
|
await scrollDashboardForLazyRendering(page);
|
|
397
|
-
|
|
550
|
+
captureStage = "waiting for lazy dashboard panels";
|
|
551
|
+
await waitForDashboardReportReady(
|
|
552
|
+
page,
|
|
553
|
+
attempt.secondReadyTimeout ?? secondReadyTimeoutMs(),
|
|
554
|
+
);
|
|
398
555
|
|
|
556
|
+
captureStage = "sizing the dashboard capture";
|
|
399
557
|
await fitViewportWidthToDashboardCapture(page, capture, attempt.viewport);
|
|
558
|
+
await scaleDashboardCapture(page, attempt.captureScale);
|
|
400
559
|
await capture.scrollIntoViewIfNeeded();
|
|
560
|
+
captureStage = "rasterizing the dashboard PNG";
|
|
401
561
|
const image = await capture.screenshot({
|
|
402
562
|
type: "png",
|
|
403
563
|
animations: "disabled",
|
|
@@ -406,8 +566,39 @@ async function captureDashboardPng(
|
|
|
406
566
|
throw new Error("Dashboard screenshot was empty");
|
|
407
567
|
}
|
|
408
568
|
return Buffer.from(image);
|
|
569
|
+
} catch (err) {
|
|
570
|
+
if (attemptTimedOut) {
|
|
571
|
+
throw new Error(
|
|
572
|
+
`${attempt.label} capture exceeded ${attempt.totalTimeout}ms while ${captureStage}`,
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
throw new Error(`${captureStage}: ${errorMessage(err)}`);
|
|
409
576
|
} finally {
|
|
410
|
-
|
|
577
|
+
if (attemptTimeout) clearTimeout(attemptTimeout);
|
|
578
|
+
if (launchTimeout) clearTimeout(launchTimeout);
|
|
579
|
+
if (!browser && launchPromise) {
|
|
580
|
+
void launchPromise.then(
|
|
581
|
+
async (lateBrowser) => {
|
|
582
|
+
await runBoundedBrowserCleanup(
|
|
583
|
+
"Failed to close late screenshot browser",
|
|
584
|
+
() => lateBrowser.browser.close(),
|
|
585
|
+
);
|
|
586
|
+
await runBoundedBrowserCleanup(
|
|
587
|
+
"Failed to clean late Chromium profile",
|
|
588
|
+
lateBrowser.cleanup,
|
|
589
|
+
);
|
|
590
|
+
},
|
|
591
|
+
() => {
|
|
592
|
+
// The launch rejection was already handled by the race above.
|
|
593
|
+
},
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
if (browser) {
|
|
597
|
+
await runBoundedBrowserCleanup("Failed to close screenshot browser", () =>
|
|
598
|
+
browser.close(),
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
await runBoundedBrowserCleanup("Failed to clean Chromium profile", cleanup);
|
|
411
602
|
}
|
|
412
603
|
}
|
|
413
604
|
|
|
@@ -419,6 +610,11 @@ function errorMessage(err: unknown): string {
|
|
|
419
610
|
);
|
|
420
611
|
}
|
|
421
612
|
|
|
613
|
+
function storedAttemptError(message: string): string {
|
|
614
|
+
const normalized = message.replace(/\s+/g, " ").trim();
|
|
615
|
+
return normalized.length > 220 ? `${normalized.slice(0, 219)}…` : normalized;
|
|
616
|
+
}
|
|
617
|
+
|
|
422
618
|
async function captureDashboardPngWithFallback(
|
|
423
619
|
sub: DashboardReportSubscription,
|
|
424
620
|
snapshot: ReportSnapshot,
|
|
@@ -427,11 +623,33 @@ async function captureDashboardPngWithFallback(
|
|
|
427
623
|
mode: "full" | "full-lightweight" | "none";
|
|
428
624
|
error?: string;
|
|
429
625
|
}> {
|
|
626
|
+
const serverless = isServerlessBrowserRuntime();
|
|
430
627
|
const attempts: DashboardScreenshotAttempt[] = [
|
|
431
|
-
{
|
|
432
|
-
|
|
628
|
+
{
|
|
629
|
+
label: "full",
|
|
630
|
+
viewport: { width: 1440, height: 1800 },
|
|
631
|
+
captureScale: 0.85,
|
|
632
|
+
...(serverless
|
|
633
|
+
? {
|
|
634
|
+
secondReadyTimeout: 25_000,
|
|
635
|
+
totalTimeout: SERVERLESS_FULL_ATTEMPT_TIMEOUT_MS,
|
|
636
|
+
}
|
|
637
|
+
: {}),
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
label: "full-lightweight",
|
|
641
|
+
viewport: { width: 1200, height: 1400 },
|
|
642
|
+
captureScale: 0.7,
|
|
643
|
+
...(serverless
|
|
644
|
+
? {
|
|
645
|
+
readyTimeout: 55_000,
|
|
646
|
+
secondReadyTimeout: 15_000,
|
|
647
|
+
totalTimeout: SERVERLESS_LIGHTWEIGHT_ATTEMPT_TIMEOUT_MS,
|
|
648
|
+
}
|
|
649
|
+
: {}),
|
|
650
|
+
},
|
|
433
651
|
];
|
|
434
|
-
|
|
652
|
+
const errors: string[] = [];
|
|
435
653
|
|
|
436
654
|
for (const attempt of attempts) {
|
|
437
655
|
try {
|
|
@@ -440,15 +658,20 @@ async function captureDashboardPngWithFallback(
|
|
|
440
658
|
mode: attempt.label,
|
|
441
659
|
};
|
|
442
660
|
} catch (err) {
|
|
443
|
-
|
|
661
|
+
const attemptError = errorMessage(err);
|
|
662
|
+
errors.push(`${attempt.label}: ${storedAttemptError(attemptError)}`);
|
|
444
663
|
console.error(
|
|
445
664
|
`[dashboard-report] ${attempt.label} screenshot failed for subscription ${sub.id}:`,
|
|
446
|
-
|
|
665
|
+
attemptError,
|
|
447
666
|
);
|
|
448
667
|
}
|
|
449
668
|
}
|
|
450
669
|
|
|
451
|
-
return {
|
|
670
|
+
return {
|
|
671
|
+
png: null,
|
|
672
|
+
mode: "none",
|
|
673
|
+
...(errors.length ? { error: errors.join(" | ") } : {}),
|
|
674
|
+
};
|
|
452
675
|
}
|
|
453
676
|
|
|
454
677
|
function reportDate(snapshot: ReportSnapshot): string {
|
|
@@ -518,13 +741,17 @@ function reportFilename(title: string): string {
|
|
|
518
741
|
|
|
519
742
|
export async function sendDashboardReportSubscription(
|
|
520
743
|
sub: DashboardReportSubscription,
|
|
521
|
-
options: {
|
|
744
|
+
options: {
|
|
745
|
+
requireScreenshot?: boolean;
|
|
746
|
+
skipEmailWithoutScreenshot?: boolean;
|
|
747
|
+
} = {},
|
|
522
748
|
): Promise<{
|
|
523
749
|
dashboardUrl: string;
|
|
524
750
|
recipientCount: number;
|
|
525
751
|
screenshotAttached: boolean;
|
|
526
752
|
screenshotMode: "full" | "full-lightweight" | "none";
|
|
527
753
|
screenshotError?: string;
|
|
754
|
+
emailsSent: boolean;
|
|
528
755
|
}> {
|
|
529
756
|
const snapshot = await collectReportSnapshot(sub);
|
|
530
757
|
const capture = await captureDashboardPngWithFallback(sub, snapshot);
|
|
@@ -535,6 +762,16 @@ export async function sendDashboardReportSubscription(
|
|
|
535
762
|
: "Dashboard screenshot unavailable",
|
|
536
763
|
);
|
|
537
764
|
}
|
|
765
|
+
if (!capture.png && options.skipEmailWithoutScreenshot) {
|
|
766
|
+
return {
|
|
767
|
+
dashboardUrl: snapshot.dashboardUrl,
|
|
768
|
+
recipientCount: sub.recipients.length,
|
|
769
|
+
screenshotAttached: false,
|
|
770
|
+
screenshotMode: capture.mode,
|
|
771
|
+
emailsSent: false,
|
|
772
|
+
...(capture.error ? { screenshotError: capture.error } : {}),
|
|
773
|
+
};
|
|
774
|
+
}
|
|
538
775
|
const screenshotAttached = Boolean(capture.png);
|
|
539
776
|
const html = renderReportEmailHtml(snapshot, { screenshotAttached });
|
|
540
777
|
const text = renderReportText(snapshot, { screenshotAttached });
|
|
@@ -565,6 +802,7 @@ export async function sendDashboardReportSubscription(
|
|
|
565
802
|
recipientCount: sub.recipients.length,
|
|
566
803
|
screenshotAttached,
|
|
567
804
|
screenshotMode: capture.mode,
|
|
805
|
+
emailsSent: true,
|
|
568
806
|
...(capture.error ? { screenshotError: capture.error } : {}),
|
|
569
807
|
};
|
|
570
808
|
}
|
|
@@ -1846,12 +1846,38 @@ export async function saveDashboardView(
|
|
|
1846
1846
|
orgId: ctx.orgId ?? undefined,
|
|
1847
1847
|
});
|
|
1848
1848
|
const db = getDb() as any;
|
|
1849
|
-
|
|
1849
|
+
let id = view.id ?? nanoidFallback();
|
|
1850
|
+
let existing = false;
|
|
1850
1851
|
if (view.id) {
|
|
1852
|
+
const [existingRow] = await db
|
|
1853
|
+
.select({
|
|
1854
|
+
id: schema.dashboardViews.id,
|
|
1855
|
+
dashboardId: schema.dashboardViews.dashboardId,
|
|
1856
|
+
})
|
|
1857
|
+
.from(schema.dashboardViews)
|
|
1858
|
+
.where(eq(schema.dashboardViews.id, view.id))
|
|
1859
|
+
.limit(1);
|
|
1860
|
+
if (existingRow?.dashboardId === dashboardId) {
|
|
1861
|
+
existing = true;
|
|
1862
|
+
} else if (existingRow) {
|
|
1863
|
+
// View ids are generated from names in the browser but remain globally
|
|
1864
|
+
// primary-keyed for backwards compatibility. Avoid colliding with a
|
|
1865
|
+
// same-named view on another dashboard instead of updating that row or
|
|
1866
|
+
// returning a raw unique-constraint 500.
|
|
1867
|
+
id = nanoidFallback();
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
if (existing) {
|
|
1851
1872
|
await db
|
|
1852
1873
|
.update(schema.dashboardViews)
|
|
1853
1874
|
.set({ name: view.name, filters: JSON.stringify(view.filters) })
|
|
1854
|
-
.where(
|
|
1875
|
+
.where(
|
|
1876
|
+
and(
|
|
1877
|
+
eq(schema.dashboardViews.id, id),
|
|
1878
|
+
eq(schema.dashboardViews.dashboardId, dashboardId),
|
|
1879
|
+
),
|
|
1880
|
+
);
|
|
1855
1881
|
} else {
|
|
1856
1882
|
await db.insert(schema.dashboardViews).values({
|
|
1857
1883
|
id,
|
|
@@ -1864,7 +1890,15 @@ export async function saveDashboardView(
|
|
|
1864
1890
|
const [row] = await db
|
|
1865
1891
|
.select()
|
|
1866
1892
|
.from(schema.dashboardViews)
|
|
1867
|
-
.where(
|
|
1893
|
+
.where(
|
|
1894
|
+
and(
|
|
1895
|
+
eq(schema.dashboardViews.id, id),
|
|
1896
|
+
eq(schema.dashboardViews.dashboardId, dashboardId),
|
|
1897
|
+
),
|
|
1898
|
+
);
|
|
1899
|
+
if (!row) {
|
|
1900
|
+
throw new Error("Dashboard view was not persisted");
|
|
1901
|
+
}
|
|
1868
1902
|
const dash = await getDashboard(dashboardId, ctx);
|
|
1869
1903
|
if (dash) {
|
|
1870
1904
|
recordScopedChange(
|
|
@@ -1891,7 +1925,12 @@ export async function deleteDashboardView(
|
|
|
1891
1925
|
const db = getDb() as any;
|
|
1892
1926
|
await db
|
|
1893
1927
|
.delete(schema.dashboardViews)
|
|
1894
|
-
.where(
|
|
1928
|
+
.where(
|
|
1929
|
+
and(
|
|
1930
|
+
eq(schema.dashboardViews.id, viewId),
|
|
1931
|
+
eq(schema.dashboardViews.dashboardId, dashboardId),
|
|
1932
|
+
),
|
|
1933
|
+
);
|
|
1895
1934
|
const dash = await getDashboard(dashboardId, ctx);
|
|
1896
1935
|
if (dash) {
|
|
1897
1936
|
recordScopedChange(
|
|
@@ -547,6 +547,27 @@ export interface RawExceptionInput {
|
|
|
547
547
|
breadcrumbs: unknown[];
|
|
548
548
|
}
|
|
549
549
|
|
|
550
|
+
/**
|
|
551
|
+
* Browser request cancellation is expected during navigation and query
|
|
552
|
+
* invalidation. Keep the first-party issue store aligned with the client
|
|
553
|
+
* Sentry filter, while preserving other AbortError failures for triage.
|
|
554
|
+
*/
|
|
555
|
+
export function isBenignBrowserAbortException(
|
|
556
|
+
input: Pick<RawExceptionInput, "type" | "message">,
|
|
557
|
+
): boolean {
|
|
558
|
+
const exceptionType = input.type.trim().toLowerCase();
|
|
559
|
+
const exceptionValue = input.message.trim().toLowerCase();
|
|
560
|
+
return (
|
|
561
|
+
exceptionValue === "the user aborted a request." ||
|
|
562
|
+
exceptionValue === "signal is aborted without reason" ||
|
|
563
|
+
exceptionValue === "aborterror: the user aborted a request." ||
|
|
564
|
+
exceptionValue === "aborterror: signal is aborted without reason" ||
|
|
565
|
+
(exceptionType === "aborterror" &&
|
|
566
|
+
(exceptionValue.includes("the user aborted a request") ||
|
|
567
|
+
exceptionValue.includes("signal is aborted without reason")))
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
|
|
550
571
|
function nowIso(): string {
|
|
551
572
|
return new Date().toISOString();
|
|
552
573
|
}
|
|
@@ -930,11 +951,9 @@ export async function ingestAnalyticsExceptionEvents(
|
|
|
930
951
|
let ingested = 0;
|
|
931
952
|
for (const item of events) {
|
|
932
953
|
try {
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
item.derived,
|
|
937
|
-
);
|
|
954
|
+
const raw = extractExceptionInput(item.properties);
|
|
955
|
+
if (isBenignBrowserAbortException(raw)) continue;
|
|
956
|
+
await ingestException(scope, raw, item.derived);
|
|
938
957
|
ingested += 1;
|
|
939
958
|
} catch (error) {
|
|
940
959
|
console.warn("[error-capture] Failed to ingest exception event:", error);
|
|
@@ -239,6 +239,11 @@ export function realDataFinalGuard(
|
|
|
239
239
|
// actual query. One corrective turn was enough for Sonnet but caused Luna
|
|
240
240
|
// to hit the fallback before it reached the query.
|
|
241
241
|
maxRetries: 2,
|
|
242
|
+
// The first request may use the compact starter catalog. A corrective
|
|
243
|
+
// retry must be able to reach the real source action directly; otherwise
|
|
244
|
+
// some model families spend the retry on tool-search narration and hit
|
|
245
|
+
// the canned fallback without ever running a query.
|
|
246
|
+
expandToolSurface: true,
|
|
242
247
|
};
|
|
243
248
|
}
|
|
244
249
|
|