@agent-native/core 0.131.5 → 0.131.7
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 +3 -3
- package/corpus/core/CHANGELOG.md +70 -0
- package/corpus/core/docs/content/agent-native-toolkit.mdx +13 -6
- package/corpus/core/docs/content/cloneable-saas.mdx +5 -5
- package/corpus/core/docs/content/creating-templates.mdx +9 -0
- package/corpus/core/docs/content/deployment.mdx +18 -12
- package/corpus/core/docs/content/doctor.mdx +8 -1
- package/corpus/core/docs/content/drop-in-agent.mdx +1 -1
- package/corpus/core/docs/content/extensions.mdx +18 -18
- package/corpus/core/docs/content/faq.mdx +4 -4
- package/corpus/core/docs/content/key-concepts.mdx +30 -21
- package/corpus/core/docs/content/security.mdx +2 -2
- package/corpus/core/docs/content/template-brain-developers.mdx +4 -1
- package/corpus/core/docs/content/template-mail-developers.mdx +4 -1
- package/corpus/core/docs/content/what-is-agent-native.mdx +6 -6
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/client.ts +449 -66
- package/corpus/core/src/a2a/correlation.ts +31 -0
- package/corpus/core/src/a2a/invoke.ts +4 -2
- package/corpus/core/src/a2a/types.ts +4 -0
- package/corpus/core/src/action.ts +3 -0
- package/corpus/core/src/agent/action-continuation-guidance.ts +2 -1
- package/corpus/core/src/agent/engine/registry.ts +98 -6
- package/corpus/core/src/agent/production-agent.ts +151 -18
- package/corpus/core/src/agent/run-loop-with-resume.ts +190 -3
- package/corpus/core/src/agent/run-manager.ts +117 -41
- package/corpus/core/src/agent/thread-debug-history.ts +86 -0
- package/corpus/core/src/agent/types.ts +3 -1
- package/corpus/core/src/cli/create.ts +11 -1
- package/corpus/core/src/cli/templates-meta.ts +2 -2
- package/corpus/core/src/client/agent-chat-adapter.ts +91 -13
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +11 -9
- package/corpus/core/src/client/sse-event-processor.ts +17 -3
- package/corpus/core/src/db/client.ts +57 -17
- package/corpus/core/src/guards/no-env-credentials.ts +2 -0
- package/corpus/core/src/integrations/adapters/slack.ts +8 -3
- package/corpus/core/src/localization/default-messages.ts +44 -0
- package/corpus/core/src/observability/traces.ts +78 -5
- package/corpus/core/src/scripts/call-agent.ts +294 -11
- package/corpus/core/src/scripts/describe-workspace-apps.ts +2 -2
- package/corpus/core/src/secrets/crypto.ts +156 -35
- package/corpus/core/src/secrets/storage.ts +64 -26
- package/corpus/core/src/server/agent-capabilities.ts +1 -1
- package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +113 -7
- package/corpus/core/src/server/agent-chat/context-tools.ts +2 -0
- package/corpus/core/src/server/agent-chat/plugin-options.ts +34 -1
- package/corpus/core/src/server/agent-chat/prompt-resources.ts +1 -1
- package/corpus/core/src/server/agent-chat-plugin.ts +91 -59
- package/corpus/core/src/templates/default/.agents/skills/secrets/SKILL.md +19 -7
- package/corpus/core/src/templates/headless/.agents/skills/secrets/SKILL.md +19 -7
- package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
- package/corpus/core/src/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
- package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +19 -7
- package/corpus/core/src/vite/client.ts +1 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-ops/SKILL.md +5 -4
- package/corpus/templates/analytics/.agents/skills/gong/SKILL.md +6 -4
- package/corpus/templates/analytics/AGENTS.md +7 -4
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +4 -3
- package/corpus/templates/analytics/changelog/2026-07-30-delegated-analytics-questions-now-return-a-useful-current-me.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-30-scheduled-dashboard-emails-complete-every-panel-without-pool.md +6 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +28 -1
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +18 -11
- package/corpus/templates/analytics/server/lib/analytics-connector-catalog.ts +4 -4
- package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +9 -5
- package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +6 -2
- package/corpus/templates/analytics/server/lib/dashboard-panel-source-resolver.ts +11 -4
- package/corpus/templates/analytics/server/lib/dashboard-report-render.ts +49 -31
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +16 -8
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +9 -3
- package/corpus/templates/analytics/server/lib/production-serverless-runtime.ts +11 -0
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +10 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +3 -2
- package/corpus/templates/analytics/server/plugins/analytics-alert-jobs.ts +2 -1
- package/corpus/templates/analytics/server/plugins/dashboard-report-jobs.ts +2 -1
- package/corpus/templates/analytics/server/plugins/uptime-monitor-jobs.ts +1 -12
- package/corpus/templates/brain/.agents/skills/brain/RUNBOOK.md +54 -6
- package/corpus/templates/brain/.agents/skills/brain/SKILL.md +20 -9
- package/corpus/templates/brain/.agents/skills/ingestion-and-connectors/SKILL.md +33 -1
- package/corpus/templates/brain/AGENTS.md +3 -1
- package/corpus/templates/brain/README.md +4 -1
- package/corpus/templates/brain/actions/_schemas.ts +30 -8
- package/corpus/templates/brain/actions/ask-brain.ts +124 -14
- package/corpus/templates/brain/actions/create-source.ts +18 -2
- package/corpus/templates/brain/actions/enqueue-captures-distillation.ts +8 -123
- package/corpus/templates/brain/actions/enqueue-distillation.ts +6 -127
- package/corpus/templates/brain/actions/import-capture.ts +17 -5
- package/corpus/templates/brain/actions/import-transcript.ts +17 -5
- package/corpus/templates/brain/actions/update-source.ts +25 -5
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/brain/changelog/2026-07-30-blessed-resources-can-feed-cited-company-answers.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-30-the-left-sidebar-no-longer-shows-a-blank-organization-placeh.md +6 -0
- package/corpus/templates/brain/server/lib/brain.ts +49 -19
- package/corpus/templates/brain/server/lib/distillation-queue.ts +147 -0
- package/corpus/templates/brain/server/lib/source-policy.ts +264 -0
- package/corpus/templates/brain/server/plugins/agent-chat.ts +3 -2
- package/corpus/templates/brain/server/routes/api/_agent-native/brain/ingest.post.ts +116 -19
- package/corpus/templates/clips/app/components/meetings/google-oauth-identity-notice.tsx +51 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +4 -0
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +3 -1
- package/corpus/templates/clips/changelog/2026-07-30-google-calendar-warning.md +6 -0
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +72 -8
- package/corpus/templates/content/changelog/2026-07-30-the-slash-command-hint-now-stays-on-only-the-active-empty-li.md +6 -0
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +506 -11
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +148 -17
- package/corpus/templates/design/app/components/design/canvas-interactions/design-canvas-interactions.ts +357 -0
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +7 -466
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +15 -311
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +19 -997
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +8 -1216
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +31 -38
- package/corpus/templates/design/package.json +0 -1
- package/corpus/templates/dispatch/AGENTS.md +8 -0
- package/corpus/templates/dispatch/DEVELOPING.md +29 -0
- package/corpus/templates/dispatch/actions/view-screen.ts +34 -2
- package/corpus/templates/dispatch/app/i18n-data.ts +457 -0
- package/corpus/templates/dispatch/changelog/2026-07-30-delegated-dispatch-work-continues-reliably-in-the-background.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-07-30-find-failed-runs-across-apps.md +6 -0
- package/corpus/templates/dispatch/netlify.toml +3 -0
- package/corpus/templates/plan/actions/get-visual-plan.ts +10 -6
- package/corpus/templates/plan/actions/patch-visual-plan-source.ts +117 -1
- package/corpus/templates/plan/actions/update-visual-plan.ts +56 -14
- package/corpus/templates/plan/app/pages/PlansPage.tsx +1 -1
- package/corpus/templates/plan/changelog/2026-07-30-plan-agents-recover-with-targeted-writes.md +6 -0
- package/corpus/templates/plan/shared/plan-content.ts +35 -0
- package/corpus/templates/slides/.agents/skills/analytics-data-for-decks/SKILL.md +8 -12
- package/corpus/templates/slides/AGENTS.md +4 -4
- package/corpus/templates/slides/actions/create-deck.ts +1 -1
- package/corpus/templates/slides/actions/duplicate-deck.ts +19 -4
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +2 -10
- package/corpus/templates/slides/app/components/deck/MermaidRenderer.tsx +12 -1
- package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +30 -13
- package/corpus/templates/slides/app/components/editor/DeckEditorSkeleton.tsx +35 -0
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +63 -56
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +21 -76
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1110 -292
- package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +53 -0
- package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +235 -18
- package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +4 -1
- package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +11 -0
- package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +129 -0
- package/corpus/templates/slides/app/components/editor/selection-overlay-measurement.ts +70 -0
- package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +519 -65
- package/corpus/templates/slides/app/components/visual-editor/CanvasCommentPins.tsx +36 -426
- package/corpus/templates/slides/app/components/visual-editor/DrawOverlay.tsx +19 -458
- package/corpus/templates/slides/app/context/DeckContext.tsx +179 -72
- package/corpus/templates/slides/app/global.css +105 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +8 -0
- package/corpus/templates/slides/app/lib/deck-editor-loading.ts +26 -0
- package/corpus/templates/slides/app/lib/mermaid-blocks.ts +27 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +71 -38
- package/corpus/templates/slides/app/pages/Index.tsx +98 -41
- package/corpus/templates/slides/changelog/2026-07-30-deck-loading-no-longer-flashes-an-unavailable-message.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-duplicating-a-deck-from-the-deck-list-now-opens-the-copy-imm.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-layout-overflow-warning-is-readable-and-dismissible.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-new-decks-now-start-with-an-empty-slide-list-when-no-slides-.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-shared-canvas-annotations.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-slide-objects-can-be-moved-as-a-group-copied-and-pasted-with.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-slide-text-editing-and-object-controls-now-match-google-slides.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-style-panel-now-scrolls-with-speaker-notes-open-shows-the-sl.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-top-toolbar-controls-now-use-consistent-sizing-with-undo-and.md +6 -0
- package/corpus/templates/slides/netlify.toml +3 -1
- package/corpus/templates/slides/package.json +0 -1
- package/corpus/templates/slides/server/lib/chat-attachments.ts +18 -0
- package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -0
- package/corpus/templates/slides/vite.config.ts +0 -1
- package/corpus/toolkit/CHANGELOG.md +13 -0
- package/corpus/toolkit/README.md +19 -0
- package/corpus/toolkit/agent-native.eject.json +38 -0
- package/corpus/toolkit/package.json +29 -1
- package/corpus/toolkit/src/canvas-annotations/CanvasCommentPins.tsx +861 -0
- package/corpus/toolkit/src/canvas-annotations/DrawOverlay.tsx +1233 -0
- package/corpus/toolkit/src/canvas-annotations/index.ts +15 -0
- package/corpus/toolkit/src/canvas-annotations/types.ts +14 -0
- package/corpus/toolkit/src/canvas-interactions/canvas-interactions.ts +933 -0
- package/corpus/toolkit/src/canvas-interactions/index.ts +67 -0
- package/corpus/toolkit/src/design-tweaks/scrub-input-utils.ts +311 -0
- package/corpus/toolkit/src/design-tweaks/scrub-input.tsx +491 -0
- package/corpus/toolkit/src/design-tweaks/visual-style-controls.tsx +26 -171
- package/corpus/toolkit/src/index.ts +1 -0
- package/dist/a2a/client.d.ts +23 -0
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +309 -44
- package/dist/a2a/client.js.map +1 -1
- package/dist/a2a/correlation.d.ts +1 -0
- package/dist/a2a/correlation.d.ts.map +1 -1
- package/dist/a2a/correlation.js +27 -0
- package/dist/a2a/correlation.js.map +1 -1
- package/dist/a2a/invoke.d.ts.map +1 -1
- package/dist/a2a/invoke.js +4 -2
- package/dist/a2a/invoke.js.map +1 -1
- package/dist/a2a/types.d.ts +4 -0
- package/dist/a2a/types.d.ts.map +1 -1
- package/dist/a2a/types.js.map +1 -1
- package/dist/action.d.ts +3 -0
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js.map +1 -1
- package/dist/agent/action-continuation-guidance.d.ts.map +1 -1
- package/dist/agent/action-continuation-guidance.js +2 -1
- package/dist/agent/action-continuation-guidance.js.map +1 -1
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +82 -8
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/production-agent.d.ts +40 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +113 -15
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts +21 -0
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +163 -4
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/agent/run-manager.d.ts +16 -9
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +93 -42
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/thread-debug-history.d.ts +10 -0
- package/dist/agent/thread-debug-history.d.ts.map +1 -0
- package/dist/agent/thread-debug-history.js +68 -0
- package/dist/agent/thread-debug-history.js.map +1 -0
- package/dist/agent/types.d.ts +3 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +6 -1
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/templates-meta.js +2 -2
- package/dist/cli/templates-meta.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +76 -13
- package/dist/client/agent-chat-adapter.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/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +12 -3
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +48 -17
- package/dist/db/client.js.map +1 -1
- package/dist/guards/no-env-credentials.d.ts.map +1 -1
- package/dist/guards/no-env-credentials.js +2 -0
- package/dist/guards/no-env-credentials.js.map +1 -1
- package/dist/integrations/adapters/slack.js +7 -3
- package/dist/integrations/adapters/slack.js.map +1 -1
- package/dist/localization/default-messages.d.ts +43 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +43 -0
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/observability/traces.d.ts +3 -1
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +73 -4
- package/dist/observability/traces.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
- package/dist/provider-api/actions/provider-api.d.ts +13 -13
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/scripts/call-agent.d.ts.map +1 -1
- package/dist/scripts/call-agent.js +237 -12
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/scripts/describe-workspace-apps.js +2 -2
- package/dist/scripts/describe-workspace-apps.js.map +1 -1
- package/dist/secrets/crypto.d.ts +36 -21
- package/dist/secrets/crypto.d.ts.map +1 -1
- package/dist/secrets/crypto.js +134 -33
- package/dist/secrets/crypto.js.map +1 -1
- package/dist/secrets/storage.d.ts +6 -6
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +43 -26
- package/dist/secrets/storage.js.map +1 -1
- package/dist/server/agent-capabilities.js +1 -1
- package/dist/server/agent-capabilities.js.map +1 -1
- package/dist/server/agent-chat/action-filters-a2a.d.ts +17 -3
- package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
- package/dist/server/agent-chat/action-filters-a2a.js +94 -3
- package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
- package/dist/server/agent-chat/context-tools.d.ts.map +1 -1
- package/dist/server/agent-chat/context-tools.js +2 -0
- package/dist/server/agent-chat/context-tools.js.map +1 -1
- package/dist/server/agent-chat/plugin-options.d.ts +23 -0
- package/dist/server/agent-chat/plugin-options.d.ts.map +1 -1
- package/dist/server/agent-chat/plugin-options.js +4 -1
- package/dist/server/agent-chat/plugin-options.js.map +1 -1
- package/dist/server/agent-chat/prompt-resources.js +1 -1
- package/dist/server/agent-chat/prompt-resources.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +2 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +79 -56
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/templates/default/.agents/skills/secrets/SKILL.md +19 -7
- package/dist/templates/headless/.agents/skills/secrets/SKILL.md +19 -7
- package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
- package/dist/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
- package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +19 -7
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +1 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/agent-native-toolkit.mdx +13 -6
- package/docs/content/cloneable-saas.mdx +5 -5
- package/docs/content/creating-templates.mdx +9 -0
- package/docs/content/deployment.mdx +18 -12
- package/docs/content/doctor.mdx +8 -1
- package/docs/content/drop-in-agent.mdx +1 -1
- package/docs/content/extensions.mdx +18 -18
- package/docs/content/faq.mdx +4 -4
- package/docs/content/key-concepts.mdx +30 -21
- package/docs/content/security.mdx +2 -2
- package/docs/content/template-brain-developers.mdx +4 -1
- package/docs/content/template-mail-developers.mdx +4 -1
- package/docs/content/what-is-agent-native.mdx +6 -6
- package/package.json +3 -3
- package/src/a2a/client.ts +449 -66
- package/src/a2a/correlation.ts +31 -0
- package/src/a2a/invoke.ts +4 -2
- package/src/a2a/types.ts +4 -0
- package/src/action.ts +3 -0
- package/src/agent/action-continuation-guidance.ts +2 -1
- package/src/agent/engine/registry.ts +98 -6
- package/src/agent/production-agent.ts +151 -18
- package/src/agent/run-loop-with-resume.ts +190 -3
- package/src/agent/run-manager.ts +117 -41
- package/src/agent/thread-debug-history.ts +86 -0
- package/src/agent/types.ts +3 -1
- package/src/cli/create.ts +11 -1
- package/src/cli/templates-meta.ts +2 -2
- package/src/client/agent-chat-adapter.ts +91 -13
- package/src/client/resources/McpIntegrationDialog.tsx +11 -9
- package/src/client/sse-event-processor.ts +17 -3
- package/src/db/client.ts +57 -17
- package/src/guards/no-env-credentials.ts +2 -0
- package/src/integrations/adapters/slack.ts +8 -3
- package/src/localization/default-messages.ts +44 -0
- package/src/observability/traces.ts +78 -5
- package/src/scripts/call-agent.ts +294 -11
- package/src/scripts/describe-workspace-apps.ts +2 -2
- package/src/secrets/crypto.ts +156 -35
- package/src/secrets/storage.ts +64 -26
- package/src/server/agent-capabilities.ts +1 -1
- package/src/server/agent-chat/action-filters-a2a.ts +113 -7
- package/src/server/agent-chat/context-tools.ts +2 -0
- package/src/server/agent-chat/plugin-options.ts +34 -1
- package/src/server/agent-chat/prompt-resources.ts +1 -1
- package/src/server/agent-chat-plugin.ts +91 -59
- package/src/templates/default/.agents/skills/secrets/SKILL.md +19 -7
- package/src/templates/headless/.agents/skills/secrets/SKILL.md +19 -7
- package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
- package/src/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
- package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +19 -7
- package/src/vite/client.ts +1 -0
|
@@ -45,8 +45,10 @@ change the numbers.
|
|
|
45
45
|
- A sibling app asking over A2A sends a question or shaped input, never SQL. It
|
|
46
46
|
lacks this app's schema, data dictionary, dashboards, and dialect knowledge,
|
|
47
47
|
so raw-query actions (`sql`, `code`, `script`, `expression`) are not
|
|
48
|
-
sibling-invocable.
|
|
49
|
-
|
|
48
|
+
sibling-invocable. Prefer natural-language delegation so this app retains its
|
|
49
|
+
instructions, source selection, and tools. Shaped reads are stable contracts,
|
|
50
|
+
not workarounds for delegation; this app still owns the query.
|
|
51
|
+
- For open-ended delegated requests, choose a safe default and label partial.
|
|
50
52
|
- Data integrity first. Never invent numbers, dimensions, filters, or source
|
|
51
53
|
semantics; only present values you actually retrieved, and state uncertainty.
|
|
52
54
|
- Every analytical answer carries audit context: source(s), time window,
|
|
@@ -70,8 +72,9 @@ change the numbers.
|
|
|
70
72
|
only URLs, ids, or handles.
|
|
71
73
|
- Never hardcode API keys, tokens, webhook URLs, secrets, private Builder data,
|
|
72
74
|
or customer data. Use secrets/OAuth and obvious placeholders in examples.
|
|
73
|
-
- External MCP callers default to `ask_app` for
|
|
74
|
-
|
|
75
|
+
- External MCP callers default to `ask_app` for interpretation, source choice,
|
|
76
|
+
analysis, or multi-step work. Direct reads require exact, complete input;
|
|
77
|
+
writes stay `ask_app`-only.
|
|
75
78
|
- Dashboard email reports and analytics alert rules are SQL-backed,
|
|
76
79
|
self-describing action surfaces — don't hand-wire routes around them. Reports
|
|
77
80
|
cap at five recipients. See `dashboard-ops`.
|
|
@@ -33,9 +33,10 @@ export default defineAction({
|
|
|
33
33
|
),
|
|
34
34
|
}),
|
|
35
35
|
readOnly: true,
|
|
36
|
-
// No raw HTTP route:
|
|
37
|
-
//
|
|
38
|
-
//
|
|
36
|
+
// No raw HTTP or connector-catalog route: SQL would land in query strings,
|
|
37
|
+
// logs, or a caller that lacks this app's schema and data dictionary. This
|
|
38
|
+
// remains an internal Analytics-agent tool; sibling agents ask Analytics a
|
|
39
|
+
// natural-language question and Analytics forms the query.
|
|
39
40
|
http: false,
|
|
40
41
|
publicAgent: { expose: true, readOnly: true, requiresAuth: true },
|
|
41
42
|
run: async (args) => {
|
|
@@ -81,6 +81,7 @@ globalThis.__AGENT_NATIVE_DASHBOARD_REPORT_SCHEDULED_RUNTIME__ = true;
|
|
|
81
81
|
|
|
82
82
|
const CRON_TOKEN = ${JSON.stringify(token)};
|
|
83
83
|
const ROUTE_PATH = ${JSON.stringify(ROUTE_PATH)};
|
|
84
|
+
const WORKER_PATH = "/.netlify/functions/${WORKER_NAME}";
|
|
84
85
|
let cachedHandler;
|
|
85
86
|
|
|
86
87
|
function timingSafeEquals(a, b) {
|
|
@@ -92,6 +93,27 @@ function timingSafeEquals(a, b) {
|
|
|
92
93
|
return diff === 0;
|
|
93
94
|
}
|
|
94
95
|
|
|
96
|
+
async function triggerNextSweep(request) {
|
|
97
|
+
const url = new URL(request.url);
|
|
98
|
+
url.pathname = WORKER_PATH;
|
|
99
|
+
url.search = "";
|
|
100
|
+
const response = await fetch(url.toString(), {
|
|
101
|
+
method: "POST",
|
|
102
|
+
headers: {
|
|
103
|
+
"content-type": "application/json",
|
|
104
|
+
"x-agent-native-dashboard-report-cron": CRON_TOKEN,
|
|
105
|
+
},
|
|
106
|
+
body: JSON.stringify({ scheduled: true }),
|
|
107
|
+
});
|
|
108
|
+
if (!response.ok && response.status !== 202) {
|
|
109
|
+
console.error(
|
|
110
|
+
"[dashboard-report-cron] Follow-up sweep trigger failed:",
|
|
111
|
+
response.status,
|
|
112
|
+
await response.text().catch(() => ""),
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
95
117
|
export default async function handler(request, context) {
|
|
96
118
|
const token = request.headers.get("x-agent-native-dashboard-report-cron") || "";
|
|
97
119
|
if (!timingSafeEquals(token, CRON_TOKEN)) {
|
|
@@ -109,7 +131,12 @@ export default async function handler(request, context) {
|
|
|
109
131
|
body: JSON.stringify({ scheduled: true }),
|
|
110
132
|
});
|
|
111
133
|
|
|
112
|
-
|
|
134
|
+
const response = await cachedHandler(rewritten, context);
|
|
135
|
+
const result = await response.clone().json().catch(() => null);
|
|
136
|
+
if (response.ok && result?.remaining > 0) {
|
|
137
|
+
await triggerNextSweep(request);
|
|
138
|
+
}
|
|
139
|
+
return response;
|
|
113
140
|
}
|
|
114
141
|
|
|
115
142
|
export const config = {
|
|
@@ -8,15 +8,21 @@ import {
|
|
|
8
8
|
recordDashboardReportCaptureOutcome,
|
|
9
9
|
} from "../lib/dashboard-report-subscriptions";
|
|
10
10
|
|
|
11
|
+
declare global {
|
|
12
|
+
var __AGENT_NATIVE_DASHBOARD_REPORT_SCHEDULED_RUNTIME__: boolean | undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
11
15
|
let running = false;
|
|
12
16
|
const DEFAULT_MAX_REPORTS_PER_SWEEP = 5;
|
|
13
17
|
const SERVERLESS_REPORT_DELIVERY_BUDGET_MS = 220_000;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
const SERVERLESS_MAX_REPORTS_PER_SWEEP = 1;
|
|
19
|
+
|
|
20
|
+
function serverlessDashboardReportRuntime(): boolean {
|
|
21
|
+
return (
|
|
22
|
+
process.env.NETLIFY === "true" ||
|
|
23
|
+
globalThis.__AGENT_NATIVE_DASHBOARD_REPORT_SCHEDULED_RUNTIME__ === true
|
|
24
|
+
);
|
|
25
|
+
}
|
|
20
26
|
|
|
21
27
|
async function persistDashboardReportResult(
|
|
22
28
|
...args: Parameters<typeof markDashboardReportResult>
|
|
@@ -52,7 +58,9 @@ async function persistDashboardReportCaptureOutcome(
|
|
|
52
58
|
}
|
|
53
59
|
|
|
54
60
|
function maxReportsPerSweep(): number {
|
|
55
|
-
if (
|
|
61
|
+
if (serverlessDashboardReportRuntime()) {
|
|
62
|
+
return SERVERLESS_MAX_REPORTS_PER_SWEEP;
|
|
63
|
+
}
|
|
56
64
|
const raw = process.env.DASHBOARD_REPORT_SWEEP_LIMIT?.trim();
|
|
57
65
|
if (!raw) return DEFAULT_MAX_REPORTS_PER_SWEEP;
|
|
58
66
|
const parsed = Number.parseInt(raw, 10);
|
|
@@ -72,10 +80,6 @@ export async function runDashboardReportsOnce(): Promise<{
|
|
|
72
80
|
}> {
|
|
73
81
|
if (running) return { processed: 0, failed: 0, remaining: 0 };
|
|
74
82
|
running = true;
|
|
75
|
-
const deliveryDeadlineAt =
|
|
76
|
-
process.env.NETLIFY === "true"
|
|
77
|
-
? Date.now() + SERVERLESS_REPORT_DELIVERY_BUDGET_MS
|
|
78
|
-
: undefined;
|
|
79
83
|
let processed = 0;
|
|
80
84
|
let failed = 0;
|
|
81
85
|
let remaining = 0;
|
|
@@ -86,6 +90,9 @@ export async function runDashboardReportsOnce(): Promise<{
|
|
|
86
90
|
remaining = batch.length >= sweepLimit ? 1 : 0;
|
|
87
91
|
for (const sub of batch) {
|
|
88
92
|
processed++;
|
|
93
|
+
const deliveryDeadlineAt = serverlessDashboardReportRuntime()
|
|
94
|
+
? Date.now() + SERVERLESS_REPORT_DELIVERY_BUDGET_MS
|
|
95
|
+
: undefined;
|
|
89
96
|
const retryAt = dashboardReportRetryAt(sub);
|
|
90
97
|
try {
|
|
91
98
|
const result = await runWithRequestContext(
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The deliberately small authenticated MCP surface for Analytics.
|
|
3
3
|
*
|
|
4
|
-
* Keep this list read-only and bounded. It includes
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Keep this list read-only and bounded. It includes optional semantic read
|
|
5
|
+
* contracts for callers that already know the exact operation and complete
|
|
6
|
+
* input. Natural-language ask_app/A2A remains the default. Do not add raw SQL,
|
|
7
|
+
* replay blobs, or dashboard/data mutation actions here.
|
|
7
8
|
*/
|
|
8
9
|
export const ANALYTICS_CONNECTOR_CATALOG = [
|
|
9
10
|
"account-deep-dive",
|
|
@@ -14,7 +15,6 @@ export const ANALYTICS_CONNECTOR_CATALOG = [
|
|
|
14
15
|
"list-session-recordings",
|
|
15
16
|
"get-session-replay-summary",
|
|
16
17
|
"get-session-replay-timeline",
|
|
17
|
-
"query-agent-native-analytics",
|
|
18
18
|
"list-error-issues",
|
|
19
19
|
"get-error-issue",
|
|
20
20
|
] as const;
|
|
@@ -9,14 +9,17 @@ import {
|
|
|
9
9
|
export const LEGACY_NEW_VS_RECURRING_USERS_SQL = `WITH all_users AS (SELECT NULLIF(user_key, '') AS user_key, event_date, user_id FROM analytics_events WHERE event_name = 'session status' AND signed_in = 'true' AND NULLIF(user_key, '') IS NOT NULL AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), 'unknown')) <> 'docs' AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(user_id, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(user_id, '')) LIKE '%@builder.io'))), first_seen AS (SELECT user_key, MIN(event_date) AS first_date FROM all_users GROUP BY user_key), daily AS (SELECT a.event_date AS date, CASE WHEN a.event_date = f.first_date THEN 'New' ELSE 'Recurring' END AS user_type, COUNT(DISTINCT a.user_key) AS users FROM all_users a JOIN first_seen f ON f.user_key = a.user_key WHERE ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD'))) GROUP BY 1, 2) SELECT date, user_type, users FROM daily ORDER BY date, CASE WHEN user_type = 'Recurring' THEN 0 ELSE 1 END`;
|
|
10
10
|
const LEGACY_NEW_VS_RECURRING_USERS_DESCRIPTION =
|
|
11
11
|
"Daily signed-in visitors split by first-ever session (New) vs return visit (Recurring), stacked with Recurring on the bottom and New on top. Docs excluded. A user is New only on their all-time first active day.";
|
|
12
|
-
const
|
|
12
|
+
const BOUNDED_NEW_VS_RECURRING_USERS_SQL = `WITH first_seen AS (SELECT NULLIF(user_key, '') AS user_key, MIN(event_date) AS first_date FROM analytics_events WHERE event_name = 'session status' AND signed_in = 'true' AND NULLIF(user_key, '') IS NOT NULL AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), 'unknown')) <> 'docs' AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(user_id, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(user_id, '')) LIKE '%@builder.io')) AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD') GROUP BY 1), activity AS (SELECT NULLIF(user_key, '') AS user_key, event_date FROM analytics_events WHERE event_name = 'session status' AND signed_in = 'true' AND NULLIF(user_key, '') IS NOT NULL AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), 'unknown')) <> 'docs' AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(user_id, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(user_id, '')) LIKE '%@builder.io')) AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')))), daily AS (SELECT a.event_date AS date, CASE WHEN a.event_date = f.first_date THEN 'New' ELSE 'Recurring' END AS user_type, COUNT(DISTINCT a.user_key) AS users FROM activity a JOIN first_seen f ON f.user_key = a.user_key GROUP BY 1, 2) SELECT date, user_type, users FROM daily ORDER BY date, CASE WHEN user_type = 'Recurring' THEN 0 ELSE 1 END`;
|
|
13
13
|
const MARKETING_SITE_TEMPLATE_FILTER =
|
|
14
14
|
"lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), 'unknown')) <> 'www'";
|
|
15
|
-
const
|
|
16
|
-
|
|
15
|
+
export const DEPLOYED_NEW_VS_RECURRING_USERS_SQL =
|
|
16
|
+
BOUNDED_NEW_VS_RECURRING_USERS_SQL.replace(
|
|
17
17
|
" <> 'docs' AND ",
|
|
18
18
|
` <> 'docs' AND ${MARKETING_SITE_TEMPLATE_FILTER} AND `,
|
|
19
19
|
);
|
|
20
|
+
// Keep cohort classification in one raw-table scan. Separate first-seen and
|
|
21
|
+
// activity scans double the random heap reads on the growing event table.
|
|
22
|
+
const NEW_VS_RECURRING_USERS_SQL = `WITH activity AS (SELECT NULLIF(user_key, '') AS user_key, event_date, MIN(event_date) OVER (PARTITION BY NULLIF(user_key, '')) AS first_date FROM analytics_events WHERE event_name = 'session status' AND signed_in = 'true' AND NULLIF(user_key, '') IS NOT NULL AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), 'unknown')) <> 'docs' AND ${MARKETING_SITE_TEMPLATE_FILTER} AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(user_id, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(user_id, '')) LIKE '%@builder.io')) AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')), daily AS (SELECT event_date AS date, CASE WHEN event_date = first_date THEN 'New' ELSE 'Recurring' END AS user_type, COUNT(DISTINCT user_key) AS users FROM activity WHERE ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD'))) GROUP BY 1, 2) SELECT date, user_type, users FROM daily ORDER BY date, CASE WHEN user_type = 'Recurring' THEN 0 ELSE 1 END`;
|
|
20
23
|
const NEW_VS_RECURRING_USERS_DESCRIPTION =
|
|
21
24
|
"Daily signed-in visitors split by first active day observed in the previous 365 days (New) vs return visit (Recurring), stacked with Recurring on the bottom and New on top. Docs and marketing-site traffic are excluded.";
|
|
22
25
|
|
|
@@ -34,9 +37,10 @@ const CANONICAL_CUSTOM_PANEL_REPLACEMENTS: readonly ExactFirstPartyPanelReplacem
|
|
|
34
37
|
id: "new-vs-recurring-users",
|
|
35
38
|
legacySql: [
|
|
36
39
|
LEGACY_NEW_VS_RECURRING_USERS_SQL,
|
|
37
|
-
|
|
40
|
+
BOUNDED_NEW_VS_RECURRING_USERS_SQL,
|
|
41
|
+
DEPLOYED_NEW_VS_RECURRING_USERS_SQL,
|
|
38
42
|
],
|
|
39
|
-
sql:
|
|
43
|
+
sql: NEW_VS_RECURRING_USERS_SQL,
|
|
40
44
|
legacyDescription: LEGACY_NEW_VS_RECURRING_USERS_DESCRIPTION,
|
|
41
45
|
description: NEW_VS_RECURRING_USERS_DESCRIPTION,
|
|
42
46
|
},
|
|
@@ -400,8 +400,9 @@ export async function runDashboardPanelQuery(args: {
|
|
|
400
400
|
source: DashboardPanelSource;
|
|
401
401
|
query: string;
|
|
402
402
|
ctx: CredentialContext;
|
|
403
|
+
timeoutMs?: number;
|
|
403
404
|
}): Promise<DashboardPanelQueryResult | MissingKeyResponse> {
|
|
404
|
-
const { source, query, ctx } = args;
|
|
405
|
+
const { source, query, ctx, timeoutMs } = args;
|
|
405
406
|
|
|
406
407
|
if (source === "bigquery") {
|
|
407
408
|
const missing = await missingCredential(
|
|
@@ -452,7 +453,10 @@ export async function runDashboardPanelQuery(args: {
|
|
|
452
453
|
userEmail: ctx.userEmail,
|
|
453
454
|
orgId: ctx.orgId ?? null,
|
|
454
455
|
},
|
|
455
|
-
{
|
|
456
|
+
{
|
|
457
|
+
cache: true,
|
|
458
|
+
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
459
|
+
},
|
|
456
460
|
);
|
|
457
461
|
}
|
|
458
462
|
|
|
@@ -19,17 +19,24 @@ type AnalyticsPanelSourceResolver = PanelSourceResolver<
|
|
|
19
19
|
CredentialContext
|
|
20
20
|
>;
|
|
21
21
|
|
|
22
|
+
type AnalyticsPanelSourceRequest = PanelSourceRequest<DashboardPanelSource> & {
|
|
23
|
+
timeoutMs?: number;
|
|
24
|
+
};
|
|
25
|
+
|
|
22
26
|
function createResolver(
|
|
23
27
|
source: DashboardPanelSource,
|
|
24
28
|
): AnalyticsPanelSourceResolver {
|
|
25
29
|
return {
|
|
26
30
|
source,
|
|
27
|
-
resolve: async (request, context) =>
|
|
28
|
-
(
|
|
31
|
+
resolve: async (request, context) => {
|
|
32
|
+
const timeoutMs = (request as AnalyticsPanelSourceRequest).timeoutMs;
|
|
33
|
+
return (await runDashboardPanelQuery({
|
|
29
34
|
source: request.source,
|
|
30
35
|
query: request.query,
|
|
31
36
|
ctx: context,
|
|
32
|
-
|
|
37
|
+
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
38
|
+
})) as DashboardPanelQueryResult | MissingKeyResponse;
|
|
39
|
+
},
|
|
33
40
|
};
|
|
34
41
|
}
|
|
35
42
|
|
|
@@ -42,7 +49,7 @@ const registry = createPanelSourceResolverRegistry<
|
|
|
42
49
|
>({ resolvers: analyticsPanelSourceResolvers });
|
|
43
50
|
|
|
44
51
|
export async function resolveAnalyticsPanelSource(
|
|
45
|
-
request:
|
|
52
|
+
request: AnalyticsPanelSourceRequest,
|
|
46
53
|
context: CredentialContext,
|
|
47
54
|
): Promise<PanelSourceResult | MissingKeyResponse> {
|
|
48
55
|
return registry.resolve(request, context) as Promise<
|
|
@@ -14,7 +14,10 @@ import type {
|
|
|
14
14
|
TableColumnConfig,
|
|
15
15
|
} from "../../app/pages/adhoc/sql-dashboard/types";
|
|
16
16
|
import { DASHBOARD_REPORT_ACTION_TIMEOUT_MS } from "../../shared/dashboard-report-timeouts.js";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
MAX_CONCURRENT_FIRST_PARTY_SQL_QUERIES,
|
|
19
|
+
MAX_CONCURRENT_SQL_QUERIES,
|
|
20
|
+
} from "../../shared/sql-query-limits";
|
|
18
21
|
import {
|
|
19
22
|
normalizeDashboardPanelQuery,
|
|
20
23
|
type DashboardPanelSource,
|
|
@@ -186,7 +189,7 @@ async function fetchOnePanel(
|
|
|
186
189
|
|
|
187
190
|
try {
|
|
188
191
|
const result = await withTimeout(
|
|
189
|
-
resolveAnalyticsPanelSource({ source, query }, ctx),
|
|
192
|
+
resolveAnalyticsPanelSource({ source, query, timeoutMs }, ctx),
|
|
190
193
|
timeoutMs,
|
|
191
194
|
`Panel query timed out after ${Math.round(timeoutMs / 1000)}s`,
|
|
192
195
|
);
|
|
@@ -276,42 +279,57 @@ export async function fetchReportPanelData(args: {
|
|
|
276
279
|
args.perPanelTimeoutMs ?? DEFAULT_PANEL_TIMEOUT_MS,
|
|
277
280
|
);
|
|
278
281
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
282
|
+
const runQueue = async (
|
|
283
|
+
panelQueue: SqlPanel[],
|
|
284
|
+
concurrency: number,
|
|
285
|
+
): Promise<void> => {
|
|
286
|
+
let next = 0;
|
|
287
|
+
const worker = async (): Promise<void> => {
|
|
288
|
+
while (next < panelQueue.length) {
|
|
289
|
+
const panel = panelQueue[next++];
|
|
290
|
+
const remaining = args.deadlineAt
|
|
291
|
+
? args.deadlineAt - Date.now()
|
|
292
|
+
: Infinity;
|
|
293
|
+
if (remaining <= 0) {
|
|
294
|
+
results.set(panel.id, {
|
|
295
|
+
status: "query-failed",
|
|
296
|
+
message:
|
|
297
|
+
"The report delivery deadline passed before this panel could run",
|
|
298
|
+
});
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
results.set(
|
|
302
|
+
panel.id,
|
|
303
|
+
await fetchOnePanel(
|
|
304
|
+
panel,
|
|
305
|
+
vars,
|
|
306
|
+
ctx,
|
|
307
|
+
Math.min(perPanelTimeoutMs, remaining),
|
|
308
|
+
),
|
|
309
|
+
);
|
|
293
310
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
),
|
|
302
|
-
);
|
|
303
|
-
}
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
await Promise.all(
|
|
314
|
+
Array.from({ length: Math.min(concurrency, panelQueue.length) }, () =>
|
|
315
|
+
worker(),
|
|
316
|
+
),
|
|
317
|
+
);
|
|
304
318
|
};
|
|
305
319
|
|
|
306
320
|
await runWithRequestContext(
|
|
307
321
|
{ userEmail: ownerEmail, ...(args.orgId ? { orgId: args.orgId } : {}) },
|
|
308
322
|
async () => {
|
|
309
|
-
await Promise.all(
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
323
|
+
await Promise.all([
|
|
324
|
+
runQueue(
|
|
325
|
+
queue.filter((panel) => panel.source === "first-party"),
|
|
326
|
+
MAX_CONCURRENT_FIRST_PARTY_SQL_QUERIES,
|
|
313
327
|
),
|
|
314
|
-
|
|
328
|
+
runQueue(
|
|
329
|
+
queue.filter((panel) => panel.source !== "first-party"),
|
|
330
|
+
MAX_CONCURRENT_SQL_QUERIES,
|
|
331
|
+
),
|
|
332
|
+
]);
|
|
315
333
|
},
|
|
316
334
|
);
|
|
317
335
|
|
|
@@ -618,19 +618,27 @@ export function validateFirstPartyAnalyticsSql(sql: string): void {
|
|
|
618
618
|
}
|
|
619
619
|
}
|
|
620
620
|
|
|
621
|
-
function
|
|
621
|
+
function scopedTableSource(
|
|
622
|
+
tableName: string,
|
|
623
|
+
scope: AnalyticsScope,
|
|
624
|
+
today: string,
|
|
625
|
+
): {
|
|
622
626
|
sql: string;
|
|
623
627
|
args: Array<string | null>;
|
|
624
628
|
} {
|
|
629
|
+
const freshness = freshnessClause(tableName);
|
|
625
630
|
if (scope.orgId) {
|
|
626
631
|
return {
|
|
627
|
-
|
|
628
|
-
|
|
632
|
+
// Keep the org and personal fallback as separate branches so Postgres can
|
|
633
|
+
// use each branch's composite tenant/date indexes instead of scanning one
|
|
634
|
+
// broad org index for an OR predicate.
|
|
635
|
+
sql: `(SELECT * FROM ${tableName} WHERE org_id = ? AND ${freshness} UNION ALL SELECT * FROM ${tableName} WHERE org_id IS NULL AND owner_email = ? AND ${freshness})`,
|
|
636
|
+
args: [scope.orgId, today, scope.userEmail, today],
|
|
629
637
|
};
|
|
630
638
|
}
|
|
631
639
|
return {
|
|
632
|
-
sql:
|
|
633
|
-
args: [scope.userEmail],
|
|
640
|
+
sql: `(SELECT * FROM ${tableName} WHERE org_id IS NULL AND owner_email = ? AND ${freshness})`,
|
|
641
|
+
args: [scope.userEmail, today],
|
|
634
642
|
};
|
|
635
643
|
}
|
|
636
644
|
|
|
@@ -661,9 +669,9 @@ export function scopedAnalyticsSql(
|
|
|
661
669
|
!RESERVED_ALIAS_WORDS.has(normalizedAlias)
|
|
662
670
|
? aliasPart
|
|
663
671
|
: ` AS ${normalizedTable}`;
|
|
664
|
-
const
|
|
665
|
-
args.push(...
|
|
666
|
-
return `${keyword}
|
|
672
|
+
const scopedSource = scopedTableSource(normalizedTable, scope, today);
|
|
673
|
+
args.push(...scopedSource.args);
|
|
674
|
+
return `${keyword} ${scopedSource.sql}${usableAlias}`;
|
|
667
675
|
},
|
|
668
676
|
);
|
|
669
677
|
return { sql: rewritten, args };
|
|
@@ -298,11 +298,12 @@ export const PRE_MARKETING_SITE_ONE_DAY_RETENTION_BY_TEMPLATE_SQL =
|
|
|
298
298
|
`${KNOWN_PRODUCT_ACTIVITY_TEMPLATE_FILTER}), ranked_first_seen`,
|
|
299
299
|
`${KNOWN_PRODUCT_ACTIVITY_TEMPLATE_FILTER} AND ${OBSERVED_ACTIVITY_LOOKBACK_FILTER}), ranked_first_seen`,
|
|
300
300
|
);
|
|
301
|
-
const
|
|
301
|
+
export const MATERIALIZED_ONE_DAY_RETENTION_BY_TEMPLATE_SQL =
|
|
302
302
|
PRE_MARKETING_SITE_ONE_DAY_RETENTION_BY_TEMPLATE_SQL.replace(
|
|
303
303
|
KNOWN_PRODUCT_ACTIVITY_TEMPLATE_FILTER,
|
|
304
304
|
`${KNOWN_PRODUCT_ACTIVITY_TEMPLATE_FILTER} AND ${MARKETING_SITE_TEMPLATE_FILTER}`,
|
|
305
305
|
);
|
|
306
|
+
const ONE_DAY_RETENTION_BY_TEMPLATE_SQL = `WITH base AS (SELECT ${SIGNED_IN_ACTIVITY_KEY_SQL} AS user_key, ${TEMPLATE_EXPR} AS template, ${EVENT_DATE_SQL} AS event_date FROM analytics_events WHERE ${SIGNED_IN_ACTIVITY_FILTER} AND ${DASHBOARD_EMAIL_FILTER} AND ${KNOWN_PRODUCT_ACTIVITY_TEMPLATE_FILTER} AND ${MARKETING_SITE_TEMPLATE_FILTER} AND ${OBSERVED_ACTIVITY_LOOKBACK_FILTER} GROUP BY 1, 2, 3), observed AS (SELECT user_key, event_date, FIRST_VALUE(template) OVER (PARTITION BY user_key ORDER BY event_date, template) AS starting_template, MIN(event_date) OVER (PARTITION BY user_key ORDER BY event_date, template) AS cohort_date FROM base), cohorts AS (SELECT user_key, starting_template AS template, cohort_date, MAX(CASE WHEN event_date > cohort_date AND event_date <= to_char(cohort_date::date + INTERVAL '7 days', 'YYYY-MM-DD') THEN 1 ELSE 0 END) AS retained FROM observed WHERE cohort_date <= ${daysAgoSql(7)} AND ${dashboardTimeRangeFilter("cohort_date")} GROUP BY user_key, starting_template, cohort_date) SELECT template, SUM(retained) AS retained_users, COUNT(*) AS cohort_users, SUM(retained)::float / NULLIF(COUNT(*), 0) AS rate FROM cohorts GROUP BY template HAVING COUNT(*) >= ${PER_TEMPLATE_RETENTION_MIN_COHORT_SIZE} ORDER BY rate DESC, cohort_users DESC, template`;
|
|
306
307
|
export const PRE_MARKETING_SITE_SEVEN_DAY_RETENTION_BY_TEMPLATE_SQL =
|
|
307
308
|
LEGACY_SEVEN_DAY_RETENTION_BY_TEMPLATE_SQL.replace(
|
|
308
309
|
`${KNOWN_PRODUCT_ACTIVITY_TEMPLATE_FILTER}), ranked_first_seen`,
|
|
@@ -314,17 +315,19 @@ const SEVEN_DAY_RETENTION_BY_TEMPLATE_SQL =
|
|
|
314
315
|
`${KNOWN_PRODUCT_ACTIVITY_TEMPLATE_FILTER} AND ${MARKETING_SITE_TEMPLATE_FILTER}`,
|
|
315
316
|
);
|
|
316
317
|
export const PRE_MARKETING_SITE_RECURRING_USERS_BY_TEMPLATE_SQL = `WITH first_seen AS (SELECT ${SIGNED_IN_ACTIVITY_KEY_SQL} AS user_key, MIN(${EVENT_DATE_SQL}) AS first_date FROM analytics_events WHERE ${SIGNED_IN_PRODUCT_ACTIVITY_FILTER} AND ${DASHBOARD_EMAIL_FILTER} AND ${OBSERVED_ACTIVITY_LOOKBACK_FILTER} GROUP BY 1), activity AS (SELECT ${SIGNED_IN_ACTIVITY_KEY_SQL} AS user_key, ${EVENT_DATE_SQL} AS event_date, ${TEMPLATE_EXPR} AS template FROM analytics_events WHERE ${SIGNED_IN_PRODUCT_ACTIVITY_FILTER} AND ${DASHBOARD_EMAIL_FILTER} AND ${OBSERVED_ACTIVITY_LOOKBACK_FILTER} AND ${DASHBOARD_TIME_RANGE_FILTER}) SELECT a.event_date AS date, a.template AS template, COUNT(DISTINCT a.user_key) AS users FROM activity a JOIN first_seen f ON f.user_key = a.user_key WHERE a.event_date <> f.first_date AND a.template <> 'unknown' GROUP BY 1, 2 ORDER BY date, template`;
|
|
317
|
-
const
|
|
318
|
+
export const DOUBLE_SCAN_RECURRING_USERS_BY_TEMPLATE_SQL =
|
|
318
319
|
PRE_MARKETING_SITE_RECURRING_USERS_BY_TEMPLATE_SQL.replace(
|
|
319
320
|
PRODUCT_ACTIVITY_TEMPLATE_FILTER,
|
|
320
321
|
`${PRODUCT_ACTIVITY_TEMPLATE_FILTER} AND ${MARKETING_SITE_TEMPLATE_FILTER}`,
|
|
321
322
|
);
|
|
323
|
+
const RECURRING_USERS_BY_TEMPLATE_SQL = `WITH activity AS (SELECT ${SIGNED_IN_ACTIVITY_KEY_SQL} AS user_key, ${EVENT_DATE_SQL} AS event_date, ${TEMPLATE_EXPR} AS template, MIN(${EVENT_DATE_SQL}) OVER (PARTITION BY ${SIGNED_IN_ACTIVITY_KEY_SQL}) AS first_date FROM analytics_events WHERE ${SIGNED_IN_PRODUCT_ACTIVITY_FILTER} AND ${MARKETING_SITE_TEMPLATE_FILTER} AND ${DASHBOARD_EMAIL_FILTER} AND ${OBSERVED_ACTIVITY_LOOKBACK_FILTER}) SELECT event_date AS date, template, COUNT(DISTINCT user_key) AS users FROM activity WHERE event_date <> first_date AND template <> 'unknown' AND ${DASHBOARD_TIME_RANGE_FILTER} GROUP BY 1, 2 ORDER BY date, template`;
|
|
322
324
|
export const PRE_MARKETING_SITE_RECURRING_USERS_BY_TEMPLATE_WEEKLY_SQL = `WITH first_seen AS (SELECT ${SIGNED_IN_ACTIVITY_KEY_SQL} AS user_key, MIN(${EVENT_DATE_SQL}) AS first_date FROM analytics_events WHERE ${SIGNED_IN_PRODUCT_ACTIVITY_FILTER} AND ${DASHBOARD_EMAIL_FILTER} AND ${OBSERVED_ACTIVITY_LOOKBACK_FILTER} GROUP BY 1), activity AS (SELECT ${SIGNED_IN_ACTIVITY_KEY_SQL} AS user_key, ${EVENT_DATE_SQL} AS event_date, ${TEMPLATE_EXPR} AS template FROM analytics_events WHERE ${SIGNED_IN_PRODUCT_ACTIVITY_FILTER} AND ${DASHBOARD_EMAIL_FILTER} AND ${OBSERVED_ACTIVITY_LOOKBACK_FILTER} AND ${DASHBOARD_TIME_RANGE_FILTER}) SELECT to_char(date_trunc('week', a.event_date::date), 'YYYY-MM-DD') AS date, a.template AS template, COUNT(DISTINCT a.user_key) AS users FROM activity a JOIN first_seen f ON f.user_key = a.user_key WHERE a.event_date <> f.first_date AND a.template <> 'unknown' GROUP BY 1, 2 ORDER BY date, template`;
|
|
323
|
-
const
|
|
325
|
+
export const DOUBLE_SCAN_RECURRING_USERS_BY_TEMPLATE_WEEKLY_SQL =
|
|
324
326
|
PRE_MARKETING_SITE_RECURRING_USERS_BY_TEMPLATE_WEEKLY_SQL.replace(
|
|
325
327
|
PRODUCT_ACTIVITY_TEMPLATE_FILTER,
|
|
326
328
|
`${PRODUCT_ACTIVITY_TEMPLATE_FILTER} AND ${MARKETING_SITE_TEMPLATE_FILTER}`,
|
|
327
329
|
);
|
|
330
|
+
const RECURRING_USERS_BY_TEMPLATE_WEEKLY_SQL = `WITH activity AS (SELECT ${SIGNED_IN_ACTIVITY_KEY_SQL} AS user_key, ${EVENT_DATE_SQL} AS event_date, ${TEMPLATE_EXPR} AS template, MIN(${EVENT_DATE_SQL}) OVER (PARTITION BY ${SIGNED_IN_ACTIVITY_KEY_SQL}) AS first_date FROM analytics_events WHERE ${SIGNED_IN_PRODUCT_ACTIVITY_FILTER} AND ${MARKETING_SITE_TEMPLATE_FILTER} AND ${DASHBOARD_EMAIL_FILTER} AND ${OBSERVED_ACTIVITY_LOOKBACK_FILTER}) SELECT to_char(date_trunc('week', event_date::date), 'YYYY-MM-DD') AS date, template, COUNT(DISTINCT user_key) AS users FROM activity WHERE event_date <> first_date AND template <> 'unknown' AND ${DASHBOARD_TIME_RANGE_FILTER} GROUP BY 1, 2 ORDER BY date, template`;
|
|
328
331
|
const LEGACY_RECURRING_USERS_DESCRIPTION =
|
|
329
332
|
"Daily signed-in visitors who are NOT on their all-time first active day (Recurring only), stacked by inferred template/app used that day. Docs traffic and unknown template are excluded.";
|
|
330
333
|
const LEGACY_RECURRING_USERS_WEEKLY_DESCRIPTION =
|
|
@@ -382,6 +385,7 @@ export function repairFirstPartyObservedRetentionPanels(
|
|
|
382
385
|
LEGACY_ONE_DAY_RETENTION_BY_TEMPLATE_SQL,
|
|
383
386
|
LEGACY_V0_ONE_DAY_RETENTION_BY_TEMPLATE_SQL,
|
|
384
387
|
PRE_MARKETING_SITE_ONE_DAY_RETENTION_BY_TEMPLATE_SQL,
|
|
388
|
+
MATERIALIZED_ONE_DAY_RETENTION_BY_TEMPLATE_SQL,
|
|
385
389
|
],
|
|
386
390
|
sql: ONE_DAY_RETENTION_BY_TEMPLATE_SQL,
|
|
387
391
|
legacyDescription: LEGACY_ONE_DAY_RETENTION_BY_TEMPLATE_DESCRIPTION,
|
|
@@ -409,6 +413,7 @@ export function repairFirstPartyObservedRetentionPanels(
|
|
|
409
413
|
INTERMEDIATE_RECURRING_USERS_BY_TEMPLATE_SQL,
|
|
410
414
|
DEPLOYED_RECURRING_USERS_BY_TEMPLATE_SQL,
|
|
411
415
|
PRE_MARKETING_SITE_RECURRING_USERS_BY_TEMPLATE_SQL,
|
|
416
|
+
DOUBLE_SCAN_RECURRING_USERS_BY_TEMPLATE_SQL,
|
|
412
417
|
],
|
|
413
418
|
sql: RECURRING_USERS_BY_TEMPLATE_SQL,
|
|
414
419
|
legacyDescription: LEGACY_RECURRING_USERS_DESCRIPTION,
|
|
@@ -422,6 +427,7 @@ export function repairFirstPartyObservedRetentionPanels(
|
|
|
422
427
|
LEGACY_RECURRING_USERS_BY_TEMPLATE_WEEKLY_SQL,
|
|
423
428
|
INTERMEDIATE_RECURRING_USERS_BY_TEMPLATE_WEEKLY_SQL,
|
|
424
429
|
PRE_MARKETING_SITE_RECURRING_USERS_BY_TEMPLATE_WEEKLY_SQL,
|
|
430
|
+
DOUBLE_SCAN_RECURRING_USERS_BY_TEMPLATE_WEEKLY_SQL,
|
|
425
431
|
],
|
|
426
432
|
sql: RECURRING_USERS_BY_TEMPLATE_WEEKLY_SQL,
|
|
427
433
|
legacyDescription: LEGACY_RECURRING_USERS_WEEKLY_DESCRIPTION,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function isProductionServerlessRuntime(): boolean {
|
|
2
|
+
if (process.env.NODE_ENV !== "production") return false;
|
|
3
|
+
return (
|
|
4
|
+
process.env.NETLIFY === "true" ||
|
|
5
|
+
Boolean(process.env.NETLIFY_FUNCTION_NAME) ||
|
|
6
|
+
Boolean(process.env.AWS_LAMBDA_FUNCTION_NAME) ||
|
|
7
|
+
Boolean(process.env.LAMBDA_TASK_ROOT) ||
|
|
8
|
+
process.env.AWS_EXECUTION_ENV?.startsWith("AWS_Lambda") === true ||
|
|
9
|
+
process.env.VERCEL === "1"
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -7,6 +7,7 @@ const INJECTED_CONTEXT_BLOCKS = [
|
|
|
7
7
|
"available-skills",
|
|
8
8
|
"available-agents",
|
|
9
9
|
"available-jobs",
|
|
10
|
+
"a2a-caller-hint",
|
|
10
11
|
"plan-mode-note",
|
|
11
12
|
"non-analytics-retry",
|
|
12
13
|
"response-guard",
|
|
@@ -233,6 +234,15 @@ export function stripInjectedAnalyticsGuardContext(text: string): string {
|
|
|
233
234
|
"",
|
|
234
235
|
);
|
|
235
236
|
}
|
|
237
|
+
// Compatibility with callers deployed before A2A hints were wrapped in the
|
|
238
|
+
// structured block above. The legacy transport note includes words such as
|
|
239
|
+
// "full transcripts" and "create ... dashboard"; allowing those framework
|
|
240
|
+
// words into intent classification falsely triggered corpus and dashboard
|
|
241
|
+
// guards for ordinary delegated metric questions.
|
|
242
|
+
requestText = requestText.replace(
|
|
243
|
+
/\n*\[Note:\s*this request comes from another app via A2A\.[\s\S]*\]\s*$/i,
|
|
244
|
+
"",
|
|
245
|
+
);
|
|
236
246
|
return requestText.trim();
|
|
237
247
|
}
|
|
238
248
|
|
|
@@ -848,8 +848,9 @@ export default createAgentChatPlugin({
|
|
|
848
848
|
connectorCatalog: [...ANALYTICS_CONNECTOR_CATALOG],
|
|
849
849
|
externalAgents: {
|
|
850
850
|
// Keep the direct MCP surface deliberately curated. External agents
|
|
851
|
-
// should use ask_app
|
|
852
|
-
//
|
|
851
|
+
// should use ask_app by default; cataloged actions are optional stable
|
|
852
|
+
// semantic reads for callers with an exact, fully known contract. They are
|
|
853
|
+
// never a fallback for slow or failed delegation.
|
|
853
854
|
authenticatedReads: "off",
|
|
854
855
|
writes: "ask_app_only",
|
|
855
856
|
},
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { runAnalyticsAlertsOnce } from "../jobs/analytics-alerts";
|
|
2
|
+
import { isProductionServerlessRuntime } from "../lib/production-serverless-runtime";
|
|
2
3
|
|
|
3
4
|
const DEFAULT_INTERVAL_MS = 60_000;
|
|
4
5
|
let skippingLogged = false;
|
|
@@ -9,7 +10,7 @@ declare global {
|
|
|
9
10
|
|
|
10
11
|
function platformSchedulerOwnsAlerts(): boolean {
|
|
11
12
|
return (
|
|
12
|
-
|
|
13
|
+
isProductionServerlessRuntime() ||
|
|
13
14
|
globalThis.__AGENT_NATIVE_ANALYTICS_ALERT_SCHEDULED_RUNTIME__ === true
|
|
14
15
|
);
|
|
15
16
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { runDashboardReportsOnce } from "../jobs/dashboard-report";
|
|
2
|
+
import { isProductionServerlessRuntime } from "../lib/production-serverless-runtime";
|
|
2
3
|
|
|
3
4
|
const INTERVAL_MS = 60_000;
|
|
4
5
|
let skippingLogged = false;
|
|
@@ -9,7 +10,7 @@ declare global {
|
|
|
9
10
|
|
|
10
11
|
function platformSchedulerOwnsReports(): boolean {
|
|
11
12
|
return (
|
|
12
|
-
|
|
13
|
+
isProductionServerlessRuntime() ||
|
|
13
14
|
globalThis.__AGENT_NATIVE_DASHBOARD_REPORT_SCHEDULED_RUNTIME__ === true
|
|
14
15
|
);
|
|
15
16
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* the actions/UI have their schema even when in-process sweeps are disabled.
|
|
9
9
|
*/
|
|
10
10
|
import { runDueMonitorsOnce } from "../jobs/uptime-monitors";
|
|
11
|
+
import { isProductionServerlessRuntime } from "../lib/production-serverless-runtime";
|
|
11
12
|
|
|
12
13
|
const DEFAULT_INTERVAL_MS = 30_000;
|
|
13
14
|
let skippingLogged = false;
|
|
@@ -16,18 +17,6 @@ declare global {
|
|
|
16
17
|
var __AGENT_NATIVE_UPTIME_MONITOR_SCHEDULED_RUNTIME__: boolean | undefined;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
function isProductionServerlessRuntime(): boolean {
|
|
20
|
-
if (process.env.NODE_ENV !== "production") return false;
|
|
21
|
-
return (
|
|
22
|
-
process.env.NETLIFY === "true" ||
|
|
23
|
-
Boolean(process.env.NETLIFY_FUNCTION_NAME) ||
|
|
24
|
-
Boolean(process.env.AWS_LAMBDA_FUNCTION_NAME) ||
|
|
25
|
-
Boolean(process.env.LAMBDA_TASK_ROOT) ||
|
|
26
|
-
process.env.AWS_EXECUTION_ENV?.startsWith("AWS_Lambda") === true ||
|
|
27
|
-
process.env.VERCEL === "1"
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
20
|
function platformSchedulerOwnsMonitors(): boolean {
|
|
32
21
|
return (
|
|
33
22
|
isProductionServerlessRuntime() ||
|