@agent-native/core 0.30.6 → 0.31.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/dist/a2a/client.d.ts +2 -0
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +7 -5
- package/dist/a2a/client.js.map +1 -1
- package/dist/a2a/handlers.d.ts.map +1 -1
- package/dist/a2a/handlers.js +3 -0
- package/dist/a2a/handlers.js.map +1 -1
- package/dist/a2a/server.d.ts.map +1 -1
- package/dist/a2a/server.js.map +1 -1
- package/dist/a2a/task-store.d.ts.map +1 -1
- package/dist/a2a/task-store.js +5 -1
- package/dist/a2a/task-store.js.map +1 -1
- package/dist/action.js +22 -4
- package/dist/action.js.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +5 -0
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
- package/dist/agent/engine/anthropic-engine.js +0 -7
- package/dist/agent/engine/anthropic-engine.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +4 -0
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/engine/translate-ai-sdk.d.ts.map +1 -1
- package/dist/agent/engine/translate-ai-sdk.js +5 -3
- package/dist/agent/engine/translate-ai-sdk.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +31 -4
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +21 -8
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +5 -1
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/tool-search.js.map +1 -1
- package/dist/application-state/store.d.ts.map +1 -1
- package/dist/application-state/store.js +18 -7
- package/dist/application-state/store.js.map +1 -1
- package/dist/brand-kit/brand-signals.d.ts +31 -0
- package/dist/brand-kit/brand-signals.d.ts.map +1 -0
- package/dist/brand-kit/brand-signals.js +101 -0
- package/dist/brand-kit/brand-signals.js.map +1 -0
- package/dist/brand-kit/index.d.ts +21 -0
- package/dist/brand-kit/index.d.ts.map +1 -0
- package/dist/brand-kit/index.js +34 -0
- package/dist/brand-kit/index.js.map +1 -0
- package/dist/brand-kit/types.d.ts +103 -0
- package/dist/brand-kit/types.d.ts.map +1 -0
- package/dist/brand-kit/types.js +17 -0
- package/dist/brand-kit/types.js.map +1 -0
- package/dist/browser-sessions/store.d.ts.map +1 -1
- package/dist/browser-sessions/store.js +6 -1
- package/dist/browser-sessions/store.js.map +1 -1
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +6 -2
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/checkpoints/store.d.ts.map +1 -1
- package/dist/checkpoints/store.js +5 -1
- package/dist/checkpoints/store.js.map +1 -1
- package/dist/cli/code-agent-executor.d.ts.map +1 -1
- package/dist/cli/code-agent-executor.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +0 -1
- package/dist/cli/create.js.map +1 -1
- package/dist/client/AgentNative.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +18 -20
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +69 -17
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/IframeEmbed.d.ts.map +1 -1
- package/dist/client/IframeEmbed.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +1 -1
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/RunStuckBanner.js.map +1 -1
- package/dist/client/agent-chat.d.ts +0 -3
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +0 -3
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/builder-mark.d.ts.map +1 -1
- package/dist/client/builder-mark.js.map +1 -1
- package/dist/client/components/CodeRequiredDialog.js +0 -7
- package/dist/client/components/CodeRequiredDialog.js.map +1 -1
- package/dist/client/components/MissingKeyCard.d.ts.map +1 -1
- package/dist/client/components/MissingKeyCard.js.map +1 -1
- package/dist/client/composer/PromptComposer.d.ts.map +1 -1
- package/dist/client/composer/PromptComposer.js +6 -3
- package/dist/client/composer/PromptComposer.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +5 -0
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/VoiceButton.d.ts.map +1 -1
- package/dist/client/composer/VoiceButton.js +9 -0
- package/dist/client/composer/VoiceButton.js.map +1 -1
- package/dist/client/composer/extensions/FileReference.d.ts.map +1 -1
- package/dist/client/composer/extensions/FileReference.js.map +1 -1
- package/dist/client/composer/extensions/MentionReference.d.ts.map +1 -1
- package/dist/client/composer/extensions/MentionReference.js.map +1 -1
- package/dist/client/composer/extensions/SkillReference.d.ts.map +1 -1
- package/dist/client/composer/extensions/SkillReference.js.map +1 -1
- package/dist/client/composer/use-file-search.d.ts.map +1 -1
- package/dist/client/composer/use-file-search.js +14 -3
- package/dist/client/composer/use-file-search.js.map +1 -1
- package/dist/client/conversation/AgentConversation.js +8 -6
- package/dist/client/conversation/AgentConversation.js.map +1 -1
- package/dist/client/conversation/use-near-bottom-autoscroll.d.ts.map +1 -1
- package/dist/client/conversation/use-near-bottom-autoscroll.js +133 -35
- package/dist/client/conversation/use-near-bottom-autoscroll.js.map +1 -1
- package/dist/client/db-admin/DbAdminPage.js.map +1 -1
- package/dist/client/db-admin/EditableCell.js +1 -1
- package/dist/client/db-admin/EditableCell.js.map +1 -1
- package/dist/client/dev-overlay/DevOverlay.d.ts +0 -2
- package/dist/client/dev-overlay/DevOverlay.d.ts.map +1 -1
- package/dist/client/dev-overlay/DevOverlay.js +1 -2
- package/dist/client/dev-overlay/DevOverlay.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +19 -0
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +11 -3
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/integrations/IntegrationsPanel.d.ts.map +1 -1
- package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
- package/dist/client/mcp-app-host.d.ts.map +1 -1
- package/dist/client/mcp-app-host.js +25 -3
- package/dist/client/mcp-app-host.js.map +1 -1
- package/dist/client/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/client/mcp-apps/McpAppRenderer.js +1 -1
- package/dist/client/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/client/notifications/NotificationsBell.js.map +1 -1
- package/dist/client/onboarding/SetupButton.d.ts.map +1 -1
- package/dist/client/onboarding/SetupButton.js +6 -0
- package/dist/client/onboarding/SetupButton.js.map +1 -1
- package/dist/client/progress/RunsTray.js.map +1 -1
- package/dist/client/resources/McpServerDetail.d.ts.map +1 -1
- package/dist/client/resources/McpServerDetail.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +0 -1
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/client/settings/AgentsSection.d.ts.map +1 -1
- package/dist/client/settings/AgentsSection.js +1 -1
- package/dist/client/settings/AgentsSection.js.map +1 -1
- package/dist/client/settings/AutomationsSection.js.map +1 -1
- package/dist/client/settings/SettingsPanel.js +2 -2
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +0 -4
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +13 -3
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/terminal/AgentTerminal.d.ts.map +1 -1
- package/dist/client/terminal/AgentTerminal.js +1 -1
- package/dist/client/terminal/AgentTerminal.js.map +1 -1
- package/dist/client/use-agent-chat.d.ts.map +1 -1
- package/dist/client/use-agent-chat.js +20 -4
- package/dist/client/use-agent-chat.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +39 -25
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +24 -0
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/client/use-dev-mode.d.ts.map +1 -1
- package/dist/client/use-dev-mode.js +25 -9
- package/dist/client/use-dev-mode.js.map +1 -1
- package/dist/client/use-run-stuck-detection.d.ts.map +1 -1
- package/dist/client/use-run-stuck-detection.js +7 -1
- package/dist/client/use-run-stuck-detection.js.map +1 -1
- package/dist/client/useProductionAgent.d.ts.map +1 -1
- package/dist/client/useProductionAgent.js +6 -2
- package/dist/client/useProductionAgent.js.map +1 -1
- package/dist/collab/agent-presence.d.ts +0 -3
- package/dist/collab/agent-presence.d.ts.map +1 -1
- package/dist/collab/agent-presence.js +3 -5
- package/dist/collab/agent-presence.js.map +1 -1
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/awareness.js +11 -1
- package/dist/collab/awareness.js.map +1 -1
- package/dist/collab/client-struct.js.map +1 -1
- package/dist/collab/storage.d.ts.map +1 -1
- package/dist/collab/storage.js +5 -1
- package/dist/collab/storage.js.map +1 -1
- package/dist/collab/ydoc-manager.d.ts.map +1 -1
- package/dist/collab/ydoc-manager.js +35 -8
- package/dist/collab/ydoc-manager.js.map +1 -1
- package/dist/deploy/build.js +0 -5
- package/dist/deploy/build.js.map +1 -1
- package/dist/extensions/content-patch.js +1 -1
- package/dist/extensions/content-patch.js.map +1 -1
- package/dist/extensions/fetch-tool.d.ts.map +1 -1
- package/dist/extensions/fetch-tool.js +4 -1
- package/dist/extensions/fetch-tool.js.map +1 -1
- package/dist/extensions/routes.js +12 -12
- package/dist/extensions/routes.js.map +1 -1
- package/dist/extensions/slots/store.d.ts.map +1 -1
- package/dist/extensions/slots/store.js +5 -1
- package/dist/extensions/slots/store.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts.map +1 -1
- package/dist/file-upload/actions/upload-image.js +39 -4
- package/dist/file-upload/actions/upload-image.js.map +1 -1
- package/dist/integrations/a2a-continuations-store.d.ts.map +1 -1
- package/dist/integrations/a2a-continuations-store.js +5 -1
- package/dist/integrations/a2a-continuations-store.js.map +1 -1
- package/dist/integrations/adapters/email.d.ts.map +1 -1
- package/dist/integrations/adapters/email.js +5 -2
- package/dist/integrations/adapters/email.js.map +1 -1
- package/dist/integrations/adapters/slack.d.ts.map +1 -1
- package/dist/integrations/adapters/slack.js.map +1 -1
- package/dist/integrations/google-docs-poller.d.ts.map +1 -1
- package/dist/integrations/google-docs-poller.js +16 -5
- package/dist/integrations/google-docs-poller.js.map +1 -1
- package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-retry-job.js +6 -2
- package/dist/integrations/pending-tasks-retry-job.js.map +1 -1
- package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-store.js +5 -1
- package/dist/integrations/pending-tasks-store.js.map +1 -1
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +14 -3
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/integrations/remote-commands-store.d.ts.map +1 -1
- package/dist/integrations/remote-commands-store.js +5 -1
- package/dist/integrations/remote-commands-store.js.map +1 -1
- package/dist/integrations/remote-devices-store.d.ts.map +1 -1
- package/dist/integrations/remote-devices-store.js +5 -1
- package/dist/integrations/remote-devices-store.js.map +1 -1
- package/dist/integrations/remote-push-store.d.ts.map +1 -1
- package/dist/integrations/remote-push-store.js +5 -1
- package/dist/integrations/remote-push-store.js.map +1 -1
- package/dist/integrations/remote-retry-job.js +1 -1
- package/dist/integrations/remote-retry-job.js.map +1 -1
- package/dist/integrations/remote-run-events-store.d.ts.map +1 -1
- package/dist/integrations/remote-run-events-store.js +5 -1
- package/dist/integrations/remote-run-events-store.js.map +1 -1
- package/dist/integrations/thread-mapping-store.d.ts.map +1 -1
- package/dist/integrations/thread-mapping-store.js +5 -1
- package/dist/integrations/thread-mapping-store.js.map +1 -1
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +10 -1
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/jobs/scheduler.d.ts.map +1 -1
- package/dist/jobs/scheduler.js +31 -15
- package/dist/jobs/scheduler.js.map +1 -1
- package/dist/jobs/tools.d.ts.map +1 -1
- package/dist/jobs/tools.js +4 -1
- package/dist/jobs/tools.js.map +1 -1
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +24 -9
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/connect-store.d.ts +3 -4
- package/dist/mcp/connect-store.d.ts.map +1 -1
- package/dist/mcp/connect-store.js +5 -5
- package/dist/mcp/connect-store.js.map +1 -1
- package/dist/mcp-client/routes.js +6 -1
- package/dist/mcp-client/routes.js.map +1 -1
- package/dist/notifications/channels.d.ts.map +1 -1
- package/dist/notifications/channels.js +3 -2
- package/dist/notifications/channels.js.map +1 -1
- package/dist/oauth-tokens/store.d.ts.map +1 -1
- package/dist/oauth-tokens/store.js +5 -1
- package/dist/oauth-tokens/store.js.map +1 -1
- package/dist/observability/evals.d.ts.map +1 -1
- package/dist/observability/evals.js +7 -7
- package/dist/observability/evals.js.map +1 -1
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +15 -5
- package/dist/observability/traces.js.map +1 -1
- package/dist/org/accept-pending.js +1 -1
- package/dist/org/accept-pending.js.map +1 -1
- package/dist/org/handlers.d.ts.map +1 -1
- package/dist/org/handlers.js +3 -2
- package/dist/org/handlers.js.map +1 -1
- package/dist/progress/store.d.ts.map +1 -1
- package/dist/progress/store.js +11 -1
- package/dist/progress/store.js.map +1 -1
- package/dist/resources/handlers.d.ts +5 -5
- package/dist/resources/handlers.d.ts.map +1 -1
- package/dist/resources/handlers.js +0 -2
- package/dist/resources/handlers.js.map +1 -1
- package/dist/resources/store.d.ts.map +1 -1
- package/dist/resources/store.js +23 -13
- package/dist/resources/store.js.map +1 -1
- package/dist/scripts/db/query.d.ts.map +1 -1
- package/dist/scripts/db/query.js +1 -2
- package/dist/scripts/db/query.js.map +1 -1
- package/dist/scripts/db/schema.js.map +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +10 -3
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +3 -6
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +13 -9
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +0 -3
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +1 -2
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/create-server.d.ts.map +1 -1
- package/dist/server/create-server.js +0 -23
- package/dist/server/create-server.js.map +1 -1
- package/dist/server/google-oauth.d.ts.map +1 -1
- package/dist/server/google-oauth.js +0 -3
- package/dist/server/google-oauth.js.map +1 -1
- package/dist/server/identity-sso-store.d.ts.map +1 -1
- package/dist/server/identity-sso-store.js +14 -3
- package/dist/server/identity-sso-store.js.map +1 -1
- package/dist/server/poll.d.ts.map +1 -1
- package/dist/server/poll.js +67 -18
- package/dist/server/poll.js.map +1 -1
- package/dist/server/schema-prompt.js +1 -1
- package/dist/server/schema-prompt.js.map +1 -1
- package/dist/server/security-headers.d.ts +5 -4
- package/dist/server/security-headers.d.ts.map +1 -1
- package/dist/server/security-headers.js +5 -4
- package/dist/server/security-headers.js.map +1 -1
- package/dist/settings/store.d.ts.map +1 -1
- package/dist/settings/store.js +5 -1
- package/dist/settings/store.js.map +1 -1
- package/dist/sharing/access.d.ts.map +1 -1
- package/dist/sharing/access.js +25 -4
- package/dist/sharing/access.js.map +1 -1
- package/dist/sharing/actions/set-resource-visibility.d.ts.map +1 -1
- package/dist/sharing/actions/set-resource-visibility.js +8 -1
- package/dist/sharing/actions/set-resource-visibility.js.map +1 -1
- package/dist/triggers/actions.d.ts.map +1 -1
- package/dist/triggers/actions.js +1 -2
- package/dist/triggers/actions.js.map +1 -1
- package/dist/triggers/dispatcher.d.ts.map +1 -1
- package/dist/triggers/dispatcher.js +36 -8
- package/dist/triggers/dispatcher.js.map +1 -1
- package/dist/usage/store.d.ts.map +1 -1
- package/dist/usage/store.js +5 -1
- package/dist/usage/store.js.map +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +7 -5
- package/dist/vite/client.js.map +1 -1
- package/package.json +3 -2
- package/dist/client/conversation/AgentConversation.spec.d.ts +0 -2
- package/dist/client/conversation/AgentConversation.spec.d.ts.map +0 -1
- package/dist/client/conversation/AgentConversation.spec.js +0 -69
- package/dist/client/conversation/AgentConversation.spec.js.map +0 -1
- package/dist/client/extensions/AgentNativeExtensionFrame.e2e-host.d.ts +0 -2
- package/dist/client/extensions/AgentNativeExtensionFrame.e2e-host.d.ts.map +0 -1
- package/dist/client/extensions/AgentNativeExtensionFrame.e2e-host.js +0 -110
- package/dist/client/extensions/AgentNativeExtensionFrame.e2e-host.js.map +0 -1
- package/dist/client/extensions/AgentNativeExtensionFrame.spec.d.ts +0 -2
- package/dist/client/extensions/AgentNativeExtensionFrame.spec.d.ts.map +0 -1
- package/dist/client/extensions/AgentNativeExtensionFrame.spec.js +0 -68
- package/dist/client/extensions/AgentNativeExtensionFrame.spec.js.map +0 -1
- package/dist/client/extensions/ExtensionViewer.spec.d.ts +0 -2
- package/dist/client/extensions/ExtensionViewer.spec.d.ts.map +0 -1
- package/dist/client/extensions/ExtensionViewer.spec.js +0 -94
- package/dist/client/extensions/ExtensionViewer.spec.js.map +0 -1
- package/dist/client/guided-questions.flow.spec.d.ts +0 -2
- package/dist/client/guided-questions.flow.spec.d.ts.map +0 -1
- package/dist/client/guided-questions.flow.spec.js +0 -147
- package/dist/client/guided-questions.flow.spec.js.map +0 -1
- package/dist/client/settings/useBuilderStatus.spec.d.ts +0 -2
- package/dist/client/settings/useBuilderStatus.spec.d.ts.map +0 -1
- package/dist/client/settings/useBuilderStatus.spec.js +0 -487
- package/dist/client/settings/useBuilderStatus.spec.js.map +0 -1
- package/dist/client/sharing/ShareButton.spec.d.ts +0 -2
- package/dist/client/sharing/ShareButton.spec.d.ts.map +0 -1
- package/dist/client/sharing/ShareButton.spec.js +0 -196
- package/dist/client/sharing/ShareButton.spec.js.map +0 -1
- package/dist/client/use-chat-models.spec.d.ts +0 -2
- package/dist/client/use-chat-models.spec.d.ts.map +0 -1
- package/dist/client/use-chat-models.spec.js +0 -39
- package/dist/client/use-chat-models.spec.js.map +0 -1
- package/dist/client/use-chat-threads.spec.d.ts +0 -2
- package/dist/client/use-chat-threads.spec.d.ts.map +0 -1
- package/dist/client/use-chat-threads.spec.js +0 -760
- package/dist/client/use-chat-threads.spec.js.map +0 -1
- package/dist/client/use-db-sync.spec.d.ts +0 -2
- package/dist/client/use-db-sync.spec.d.ts.map +0 -1
- package/dist/client/use-db-sync.spec.js +0 -107
- package/dist/client/use-db-sync.spec.js.map +0 -1
- package/dist/server/script-discovery.d.ts +0 -6
- package/dist/server/script-discovery.d.ts.map +0 -1
- package/dist/server/script-discovery.js +0 -6
- package/dist/server/script-discovery.js.map +0 -1
package/dist/server/poll.js
CHANGED
|
@@ -37,6 +37,10 @@ _pollEmitter.setMaxListeners(0);
|
|
|
37
37
|
let _versionSeeded = false;
|
|
38
38
|
/** Tracks the latest updated_at we've seen from the DB, per table. */
|
|
39
39
|
let _lastDbCheck = 0;
|
|
40
|
+
// Coalesces concurrent checkExternalDbChanges runs. The 1s throttle alone does
|
|
41
|
+
// not prevent overlap when a single check takes longer than 1s — two overlapping
|
|
42
|
+
// runs would each read+advance the shared watermarks and double-emit events.
|
|
43
|
+
let _checkPromise = null;
|
|
40
44
|
let _lastAppStateTs = 0;
|
|
41
45
|
let _lastSettingsTs = 0;
|
|
42
46
|
let _lastExtensionsTs = 0;
|
|
@@ -55,6 +59,10 @@ let _lastActionMarkerTs = 0;
|
|
|
55
59
|
*/
|
|
56
60
|
let _lastScreenRefreshTs = 0;
|
|
57
61
|
let _screenRefreshInitialized = false;
|
|
62
|
+
// Per-session high-water marks for `__screen_refresh__`. Each user's row is
|
|
63
|
+
// tracked independently so a refresh triggered by one user only remounts that
|
|
64
|
+
// user's screen (owner-scoped), never every authenticated poller.
|
|
65
|
+
const _lastScreenRefreshTsBySession = new Map();
|
|
58
66
|
const SCREEN_REFRESH_KEY = "__screen_refresh__";
|
|
59
67
|
let _localEmittersWired = false;
|
|
60
68
|
function wireLocalEmitters() {
|
|
@@ -286,13 +294,16 @@ async function seedVersionFromDb() {
|
|
|
286
294
|
readActionMarkerMaxUpdatedAt(db),
|
|
287
295
|
db
|
|
288
296
|
.execute({
|
|
289
|
-
sql: "SELECT updated_at FROM application_state WHERE key = ?",
|
|
297
|
+
sql: "SELECT session_id, updated_at FROM application_state WHERE key = ?",
|
|
290
298
|
args: [SCREEN_REFRESH_KEY],
|
|
291
299
|
})
|
|
292
300
|
.catch(() => ({ rows: [] })),
|
|
293
301
|
]);
|
|
294
302
|
const extensionsTs = timestampValue(extensionsMaxUpdatedAt);
|
|
295
|
-
|
|
303
|
+
let refreshTs = 0;
|
|
304
|
+
for (const row of refreshResult.rows) {
|
|
305
|
+
refreshTs = Math.max(refreshTs, timestampValue(row.updated_at));
|
|
306
|
+
}
|
|
296
307
|
// Seed version — never decrease an already-set value
|
|
297
308
|
_version = Math.max(_version, appTs, settingsTs, extensionsTs, extensionMarkerTs, actionMarkerTs);
|
|
298
309
|
// Set baselines so checkExternalDbChanges detects future writes
|
|
@@ -306,7 +317,16 @@ async function seedVersionFromDb() {
|
|
|
306
317
|
// marker on cold start, or the first poll after the action will miss it.
|
|
307
318
|
_lastActionMarkerTs = 0;
|
|
308
319
|
_lastScreenRefreshTs = refreshTs;
|
|
320
|
+
_lastScreenRefreshTsBySession.clear();
|
|
321
|
+
for (const row of refreshResult.rows) {
|
|
322
|
+
if (typeof row.session_id === "string") {
|
|
323
|
+
_lastScreenRefreshTsBySession.set(row.session_id, timestampValue(row.updated_at));
|
|
324
|
+
}
|
|
325
|
+
}
|
|
309
326
|
_screenRefreshInitialized = true;
|
|
327
|
+
// Skip the redundant cold-start recheck unless there is an existing durable
|
|
328
|
+
// action marker that the first poll still needs to emit.
|
|
329
|
+
_lastDbCheck = actionMarkerTs > 0 ? 0 : Date.now();
|
|
310
330
|
}
|
|
311
331
|
catch {
|
|
312
332
|
// Tables may not exist yet — ignore
|
|
@@ -320,7 +340,18 @@ async function checkExternalDbChanges() {
|
|
|
320
340
|
const now = Date.now();
|
|
321
341
|
if (now - _lastDbCheck < 1000)
|
|
322
342
|
return;
|
|
343
|
+
// Coalesce: if a check is already running, await it instead of starting a
|
|
344
|
+
// second overlapping run that would double-advance the shared watermarks
|
|
345
|
+
// (and double-emit change events).
|
|
346
|
+
if (_checkPromise)
|
|
347
|
+
return _checkPromise;
|
|
323
348
|
_lastDbCheck = now;
|
|
349
|
+
_checkPromise = doCheckExternalDbChanges().finally(() => {
|
|
350
|
+
_checkPromise = null;
|
|
351
|
+
});
|
|
352
|
+
return _checkPromise;
|
|
353
|
+
}
|
|
354
|
+
async function doCheckExternalDbChanges() {
|
|
324
355
|
try {
|
|
325
356
|
const db = getDbExec();
|
|
326
357
|
// Check application_state for external writes. Preserve the changed key so
|
|
@@ -372,31 +403,49 @@ async function checkExternalDbChanges() {
|
|
|
372
403
|
// updated_at bumps, emit a distinct event so the client invalidates
|
|
373
404
|
// all queries (not just the ones matching its default queryKey prefix).
|
|
374
405
|
const refreshResult = await db.execute({
|
|
375
|
-
sql: "SELECT updated_at, value FROM application_state WHERE key = ?",
|
|
406
|
+
sql: "SELECT session_id, updated_at, value FROM application_state WHERE key = ?",
|
|
376
407
|
args: [SCREEN_REFRESH_KEY],
|
|
377
408
|
});
|
|
378
|
-
const refreshTs = timestampValue(
|
|
409
|
+
const refreshTs = refreshResult.rows.reduce((max, row) => Math.max(max, timestampValue(row.updated_at)), 0);
|
|
379
410
|
if (!_screenRefreshInitialized) {
|
|
380
411
|
_lastScreenRefreshTs = refreshTs;
|
|
412
|
+
for (const row of refreshResult.rows) {
|
|
413
|
+
if (typeof row.session_id === "string") {
|
|
414
|
+
_lastScreenRefreshTsBySession.set(row.session_id, timestampValue(row.updated_at));
|
|
415
|
+
}
|
|
416
|
+
}
|
|
381
417
|
_screenRefreshInitialized = true;
|
|
382
418
|
}
|
|
383
419
|
else if (refreshTs > _lastScreenRefreshTs) {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
420
|
+
// Emit a per-user event only for the session(s) whose row actually
|
|
421
|
+
// advanced, scoped with `owner` so canSeeChangeForUser delivers it only
|
|
422
|
+
// to that user — not every authenticated poller.
|
|
423
|
+
for (const row of refreshResult.rows) {
|
|
424
|
+
const owner = typeof row.session_id === "string" ? row.session_id : undefined;
|
|
425
|
+
if (!owner)
|
|
426
|
+
continue;
|
|
427
|
+
const rowTs = timestampValue(row.updated_at);
|
|
428
|
+
if (rowTs <= (_lastScreenRefreshTsBySession.get(owner) ?? 0))
|
|
429
|
+
continue;
|
|
430
|
+
let scope;
|
|
431
|
+
try {
|
|
432
|
+
const raw = row.value;
|
|
433
|
+
if (typeof raw === "string") {
|
|
434
|
+
const parsed = JSON.parse(raw);
|
|
435
|
+
if (typeof parsed?.scope === "string")
|
|
436
|
+
scope = parsed.scope;
|
|
437
|
+
}
|
|
391
438
|
}
|
|
439
|
+
catch { }
|
|
440
|
+
recordChange({
|
|
441
|
+
source: "screen-refresh",
|
|
442
|
+
type: "change",
|
|
443
|
+
key: SCREEN_REFRESH_KEY,
|
|
444
|
+
owner,
|
|
445
|
+
...(scope ? { scope } : {}),
|
|
446
|
+
});
|
|
447
|
+
_lastScreenRefreshTsBySession.set(owner, rowTs);
|
|
392
448
|
}
|
|
393
|
-
catch { }
|
|
394
|
-
recordChange({
|
|
395
|
-
source: "screen-refresh",
|
|
396
|
-
type: "change",
|
|
397
|
-
key: SCREEN_REFRESH_KEY,
|
|
398
|
-
...(scope ? { scope } : {}),
|
|
399
|
-
});
|
|
400
449
|
_lastScreenRefreshTs = refreshTs;
|
|
401
450
|
}
|
|
402
451
|
// Extension mutations write a durable marker row so delete and hide/unhide
|
package/dist/server/poll.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"poll.js","sourceRoot":"","sources":["../../src/server/poll.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Polling-based change notification.\n *\n * Replaces SSE with a simple version counter. Each DB mutation (app-state,\n * settings, resources) increments the version. Clients poll `/_agent-native/poll?since=N`\n * and receive any events that occurred after version N.\n *\n * Works in all deployment environments (serverless, edge, long-lived).\n *\n * Also detects cross-process DB writes by periodically checking the\n * application_state and settings tables' updated_at timestamps. This ensures\n * that changes made by external processes (e.g., CLI actions, cron jobs)\n * are picked up even though they don't call recordChange() in this process.\n */\n\nimport { EventEmitter } from \"node:events\";\nimport { defineEventHandler, getQuery, setResponseStatus } from \"h3\";\nimport {\n ACTION_CHANGE_MARKER_KEY,\n parseActionChangeMarker,\n type ActionChangeTarget,\n} from \"../action-change-marker.js\";\nimport { getAppStateEmitter } from \"../application-state/emitter.js\";\nimport { getDbExec } from \"../db/client.js\";\nimport {\n EXTENSION_CHANGE_MARKER_KEY,\n parseExtensionChangeMarker,\n type ExtensionChangeTarget,\n} from \"../extensions/change-marker.js\";\nimport { getSettingsEmitter } from \"../settings/store.js\";\nimport { getSession } from \"./auth.js\";\n\nexport interface ChangeEvent {\n version: number;\n source: string;\n type: string;\n key?: string;\n /**\n * Owner email for tenant-scoped events. When absent, the event is treated\n * as deployment-global (e.g. table-level \"something changed\" pings) and\n * delivered to every authenticated poller. Specific events that should\n * only fan out to one user MUST set this — otherwise polling clients\n * across tenants see each other's signals.\n */\n owner?: string;\n /** Optional org ID for org-scoped events. */\n orgId?: string;\n [k: string]: unknown;\n}\n\n// In-memory ring buffer of recent changes. Kept small since clients\n// poll frequently (every 2-3s) and only need events since their last poll.\nconst MAX_BUFFER = 200;\nlet _version = 0;\nconst _buffer: ChangeEvent[] = [];\nexport const POLL_CHANGE_EVENT = \"poll-change\";\nconst _pollEmitter = new EventEmitter();\n_pollEmitter.setMaxListeners(0);\n\n/**\n * Whether we've seeded _version from the DB. In serverless (Netlify,\n * Vercel, etc.) each invocation starts fresh — without seeding, _version\n * resets to 0 and polling clients see the version jump backwards, causing\n * duplicate events and stuck UI.\n */\nlet _versionSeeded = false;\n\n/** Tracks the latest updated_at we've seen from the DB, per table. */\nlet _lastDbCheck = 0;\nlet _lastAppStateTs = 0;\nlet _lastSettingsTs = 0;\nlet _lastExtensionsTs = 0;\nlet _lastExtensionsUpdatedAt: string | number | undefined;\nlet _lastExtensionMarkerTs = 0;\nlet _lastActionMarkerTs = 0;\n\n/**\n * Tracks the latest updated_at seen on the `__screen_refresh__` key in\n * application_state. Bumped when the agent calls the `refresh-screen` tool,\n * and surfaced as a distinct `screen-refresh` event so clients can remount\n * the main content subtree via React key.\n *\n * `_screenRefreshInitialized` guards against spurious emits on the first\n * poll after a restart (where an existing row would look like a fresh bump).\n * Once we've taken a baseline reading, any subsequent increase emits.\n */\nlet _lastScreenRefreshTs = 0;\nlet _screenRefreshInitialized = false;\nconst SCREEN_REFRESH_KEY = \"__screen_refresh__\";\nlet _localEmittersWired = false;\n\nfunction wireLocalEmitters(): void {\n if (_localEmittersWired) return;\n _localEmittersWired = true;\n getAppStateEmitter().on(\"app-state\", (event) => {\n if (\n event.key === EXTENSION_CHANGE_MARKER_KEY ||\n event.key === ACTION_CHANGE_MARKER_KEY\n ) {\n return;\n }\n recordChange(event);\n });\n getSettingsEmitter().on(\"settings\", (event) => {\n recordChange(event);\n });\n}\n\nfunction timestampValue(value: unknown): number {\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value !== \"string\") return 0;\n const numeric = Number(value);\n if (Number.isFinite(numeric)) return numeric;\n const parsed = Date.parse(value);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction sqlWatermarkValue(value: unknown): string | number | undefined {\n if (typeof value === \"string\" && value.length > 0) return value;\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n return undefined;\n}\n\nasync function readMaxUpdatedAtRaw(\n db: {\n execute: (\n query: string | { sql: string; args?: unknown[] },\n ) => Promise<{ rows: Array<Record<string, unknown>> }>;\n },\n table: \"application_state\" | \"settings\" | \"tools\",\n): Promise<unknown> {\n try {\n const result = await db.execute(\n `SELECT MAX(updated_at) as max_ts FROM ${table}`,\n );\n return result.rows[0]?.max_ts;\n } catch {\n // Optional framework tables may not exist in every app yet.\n return undefined;\n }\n}\n\nasync function readMaxUpdatedAt(\n db: {\n execute: (\n query: string | { sql: string; args?: unknown[] },\n ) => Promise<{ rows: Array<Record<string, unknown>> }>;\n },\n table: \"application_state\" | \"settings\" | \"tools\",\n): Promise<number> {\n return timestampValue(await readMaxUpdatedAtRaw(db, table));\n}\n\nasync function readExtensionMarkerMaxUpdatedAt(db: {\n execute: (\n query: string | { sql: string; args?: unknown[] },\n ) => Promise<{ rows: Array<Record<string, unknown>> }>;\n}): Promise<number> {\n try {\n const result = await db.execute({\n sql: \"SELECT MAX(updated_at) as max_ts FROM application_state WHERE key = ?\",\n args: [EXTENSION_CHANGE_MARKER_KEY],\n });\n return timestampValue(result.rows[0]?.max_ts);\n } catch {\n return 0;\n }\n}\n\nasync function readActionMarkerMaxUpdatedAt(db: {\n execute: (\n query: string | { sql: string; args?: unknown[] },\n ) => Promise<{ rows: Array<Record<string, unknown>> }>;\n}): Promise<number> {\n try {\n const result = await db.execute({\n sql: \"SELECT MAX(updated_at) as max_ts FROM application_state WHERE key = ?\",\n args: [ACTION_CHANGE_MARKER_KEY],\n });\n return timestampValue(result.rows[0]?.max_ts);\n } catch {\n return 0;\n }\n}\n\n/** Get the current global version counter. */\nexport function getVersion(): number {\n return _version;\n}\n\nexport function getPollEmitter(): EventEmitter {\n return _pollEmitter;\n}\n\nexport function canSeeChangeForUser(\n event: ChangeEvent,\n userEmail: string,\n orgId: string | undefined,\n): boolean {\n // Global / unowned events: every authenticated user gets them.\n if (!event.owner && !event.orgId) return true;\n if (event.owner && event.owner === userEmail) return true;\n if (event.orgId && orgId && event.orgId === orgId) return true;\n return false;\n}\n\n/** Record a change event. Called by emitter listeners. */\nexport function recordChange(event: {\n source: string;\n type: string;\n key?: string;\n [k: string]: unknown;\n}): void {\n // Use timestamp-aligned versions so all serverless instances produce\n // values in the same range (seeded from DB, then incremented via\n // Date.now). Plain ++counter diverges across cold starts.\n _version = Math.max(_version + 1, Date.now());\n const entry: ChangeEvent = { ...event, version: _version };\n _buffer.push(entry);\n if (_buffer.length > MAX_BUFFER) {\n _buffer.splice(0, _buffer.length - MAX_BUFFER);\n }\n _pollEmitter.emit(POLL_CHANGE_EVENT, entry);\n}\n\nfunction extensionTargetKey(target: ExtensionChangeTarget): string | null {\n if (target.owner) return `owner:${target.owner}`;\n if (target.orgId) return `org:${target.orgId}`;\n return null;\n}\n\nfunction addExtensionTarget(\n targets: Map<string, ExtensionChangeTarget>,\n target: ExtensionChangeTarget,\n): void {\n const key = extensionTargetKey(target);\n if (key) targets.set(key, target);\n}\n\nfunction recordExtensionChanges(targets: ExtensionChangeTarget[]): void {\n const uniqueTargets = new Map<string, ExtensionChangeTarget>();\n for (const target of targets) addExtensionTarget(uniqueTargets, target);\n for (const target of uniqueTargets.values()) {\n recordChange({\n source: \"extensions\",\n type: \"change\",\n key: \"*\",\n ...(target.owner ? { owner: target.owner } : {}),\n ...(target.orgId ? { orgId: target.orgId } : {}),\n });\n }\n}\n\nfunction recordActionChanges(targets: ActionChangeTarget[]): void {\n for (const target of targets) {\n recordChange({\n source: \"action\",\n type: \"change\",\n key: target.actionName ?? \"*\",\n ...(target.owner ? { owner: target.owner } : {}),\n ...(target.orgId ? { orgId: target.orgId } : {}),\n });\n }\n}\n\nfunction extensionTargetsForRow(\n row: Record<string, unknown>,\n shareRows: Array<Record<string, unknown>>,\n): ExtensionChangeTarget[] {\n const targets = new Map<string, ExtensionChangeTarget>();\n const owner = typeof row.owner_email === \"string\" ? row.owner_email : \"\";\n const orgId = typeof row.org_id === \"string\" ? row.org_id : \"\";\n const visibility =\n typeof row.visibility === \"string\" ? row.visibility : \"private\";\n\n if (owner) addExtensionTarget(targets, { owner });\n if (visibility === \"org\" && orgId) addExtensionTarget(targets, { orgId });\n\n for (const share of shareRows) {\n const principalType =\n typeof share.principal_type === \"string\" ? share.principal_type : \"\";\n const principalId =\n typeof share.principal_id === \"string\" ? share.principal_id : \"\";\n if (principalType === \"user\" && principalId) {\n addExtensionTarget(targets, { owner: principalId });\n } else if (principalType === \"org\" && principalId) {\n addExtensionTarget(targets, { orgId: principalId });\n }\n }\n\n return Array.from(targets.values());\n}\n\nasync function readExtensionTargetsForRows(\n db: {\n execute: (\n query: string | { sql: string; args?: unknown[] },\n ) => Promise<{ rows: Array<Record<string, unknown>> }>;\n },\n rows: Array<Record<string, unknown>>,\n): Promise<ExtensionChangeTarget[][]> {\n const ids = rows\n .map((row) => (typeof row.id === \"string\" ? row.id : \"\"))\n .filter(Boolean);\n const sharesByResourceId = new Map<string, Array<Record<string, unknown>>>();\n\n if (ids.length > 0) {\n try {\n const placeholders = ids.map(() => \"?\").join(\", \");\n const shareResult = await db.execute({\n sql: `SELECT resource_id, principal_type, principal_id FROM tool_shares WHERE resource_id IN (${placeholders})`,\n args: ids,\n });\n for (const share of shareResult.rows) {\n const resourceId =\n typeof share.resource_id === \"string\" ? share.resource_id : \"\";\n if (!resourceId) continue;\n const bucket = sharesByResourceId.get(resourceId) ?? [];\n bucket.push(share);\n sharesByResourceId.set(resourceId, bucket);\n }\n } catch {\n // Sharing tables are optional during early app initialization.\n }\n }\n\n return rows.map((row) =>\n extensionTargetsForRow(\n row,\n sharesByResourceId.get(typeof row.id === \"string\" ? row.id : \"\") ?? [],\n ),\n );\n}\n\n/** Get all changes after a given version. */\nexport function getChangesSince(since: number): {\n version: number;\n events: ChangeEvent[];\n} {\n if (since >= _version) {\n return { version: _version, events: [] };\n }\n const events = _buffer.filter((e) => e.version > since);\n return { version: _version, events };\n}\n\n/**\n * Get changes after a given version, filtered to events the caller is\n * allowed to see.\n *\n * Filtering rules:\n * - Events without an `owner` are deployment-global (table-level pings,\n * screen-refresh, etc.) and visible to every authenticated user.\n * - Events with `owner === userEmail` go to that user.\n * - Events with `orgId === orgId` go to anyone in that org.\n * - All other owned events are filtered out.\n */\nexport function getChangesSinceForUser(\n since: number,\n userEmail: string,\n orgId: string | undefined,\n): { version: number; events: ChangeEvent[] } {\n if (since >= _version) {\n return { version: _version, events: [] };\n }\n const events = _buffer.filter(\n (e) => e.version > since && canSeeChangeForUser(e, userEmail, orgId),\n );\n return { version: _version, events };\n}\n\n/**\n * Seed _version from DB timestamps on the first call so serverless\n * cold starts don't return version 0 and confuse polling clients.\n */\nasync function seedVersionFromDb(): Promise<void> {\n if (_versionSeeded) return;\n _versionSeeded = true;\n\n try {\n const db = getDbExec();\n\n const [\n appTs,\n settingsTs,\n extensionsMaxUpdatedAt,\n extensionMarkerTs,\n actionMarkerTs,\n refreshResult,\n ] = await Promise.all([\n readMaxUpdatedAt(db, \"application_state\"),\n readMaxUpdatedAt(db, \"settings\"),\n readMaxUpdatedAtRaw(db, \"tools\"),\n readExtensionMarkerMaxUpdatedAt(db),\n readActionMarkerMaxUpdatedAt(db),\n db\n .execute({\n sql: \"SELECT updated_at FROM application_state WHERE key = ?\",\n args: [SCREEN_REFRESH_KEY],\n })\n .catch(() => ({ rows: [] })),\n ]);\n\n const extensionsTs = timestampValue(extensionsMaxUpdatedAt);\n const refreshTs = timestampValue(refreshResult.rows[0]?.updated_at);\n\n // Seed version — never decrease an already-set value\n _version = Math.max(\n _version,\n appTs,\n settingsTs,\n extensionsTs,\n extensionMarkerTs,\n actionMarkerTs,\n );\n\n // Set baselines so checkExternalDbChanges detects future writes\n _lastAppStateTs = appTs;\n _lastSettingsTs = settingsTs;\n _lastExtensionsTs = extensionsTs;\n _lastExtensionsUpdatedAt = sqlWatermarkValue(extensionsMaxUpdatedAt);\n _lastExtensionMarkerTs = extensionMarkerTs;\n // Action markers are durable specifically so a web server can observe work\n // performed by a separate action process. Do not baseline past an existing\n // marker on cold start, or the first poll after the action will miss it.\n _lastActionMarkerTs = 0;\n _lastScreenRefreshTs = refreshTs;\n _screenRefreshInitialized = true;\n } catch {\n // Tables may not exist yet — ignore\n }\n}\n\n/**\n * Check for cross-process DB writes by comparing updated_at timestamps.\n * Runs at most once per second to avoid excessive queries.\n */\nasync function checkExternalDbChanges(): Promise<void> {\n const now = Date.now();\n if (now - _lastDbCheck < 1000) return;\n _lastDbCheck = now;\n\n try {\n const db = getDbExec();\n\n // Check application_state for external writes. Preserve the changed key so\n // clients can invalidate one-shot command queries (`navigate`, `__set_url__`)\n // only when those command rows actually change; noisy keys such as\n // `slide-fit-check` should not wake navigation readers.\n const appResult = await db.execute({\n sql: \"SELECT session_id, key, updated_at FROM application_state WHERE updated_at > ? ORDER BY updated_at ASC\",\n args: [_lastAppStateTs],\n });\n if (appResult.rows.length > 0) {\n const appTs = appResult.rows.reduce(\n (max, row) => Math.max(max, timestampValue(row.updated_at)),\n _lastAppStateTs,\n );\n if (_lastAppStateTs > 0) {\n for (const row of appResult.rows) {\n const key = typeof row.key === \"string\" ? row.key : \"*\";\n if (\n key === EXTENSION_CHANGE_MARKER_KEY ||\n key === ACTION_CHANGE_MARKER_KEY\n ) {\n continue;\n }\n const owner =\n typeof row.session_id === \"string\" ? row.session_id : undefined;\n recordChange({\n source: \"app-state\",\n type: \"change\",\n key,\n ...(owner ? { owner } : {}),\n });\n }\n }\n _lastAppStateTs = appTs;\n }\n\n // Mutating actions write a durable marker in addition to the in-process\n // event. This lets dev-mode `pnpm action ...` child processes and\n // serverless action invocations wake the web server's SSE/poll loop as a\n // first-class source:\"action\" event rather than a generic app-state bump.\n const actionMarkerTs = await readActionMarkerMaxUpdatedAt(db);\n if (actionMarkerTs > _lastActionMarkerTs) {\n const actionMarkerResult = await db.execute({\n sql: \"SELECT session_id, value, updated_at FROM application_state WHERE key = ? ORDER BY updated_at ASC\",\n args: [ACTION_CHANGE_MARKER_KEY],\n });\n const changedActionMarkers = actionMarkerResult.rows.filter(\n (row) => timestampValue(row.updated_at) > _lastActionMarkerTs,\n );\n recordActionChanges(\n changedActionMarkers\n .map((row) => parseActionChangeMarker(row.session_id, row.value))\n .filter((target): target is ActionChangeTarget => !!target),\n );\n _lastActionMarkerTs = actionMarkerTs;\n }\n\n // Check for screen-refresh requests from the agent. The `refresh-screen`\n // tool writes to application_state under a well-known key; when its\n // updated_at bumps, emit a distinct event so the client invalidates\n // all queries (not just the ones matching its default queryKey prefix).\n const refreshResult = await db.execute({\n sql: \"SELECT updated_at, value FROM application_state WHERE key = ?\",\n args: [SCREEN_REFRESH_KEY],\n });\n const refreshTs = timestampValue(refreshResult.rows[0]?.updated_at);\n if (!_screenRefreshInitialized) {\n _lastScreenRefreshTs = refreshTs;\n _screenRefreshInitialized = true;\n } else if (refreshTs > _lastScreenRefreshTs) {\n let scope: string | undefined;\n try {\n const raw = refreshResult.rows[0]?.value;\n if (typeof raw === \"string\") {\n const parsed = JSON.parse(raw);\n if (typeof parsed?.scope === \"string\") scope = parsed.scope;\n }\n } catch {}\n recordChange({\n source: \"screen-refresh\",\n type: \"change\",\n key: SCREEN_REFRESH_KEY,\n ...(scope ? { scope } : {}),\n });\n _lastScreenRefreshTs = refreshTs;\n }\n\n // Extension mutations write a durable marker row so delete and hide/unhide\n // operations are visible across serverless invocations. Translate those\n // marker rows back into extension-source events for targeted client\n // invalidation while preserving user/org scope.\n const extensionMarkerTs = await readExtensionMarkerMaxUpdatedAt(db);\n if (extensionMarkerTs > _lastExtensionMarkerTs) {\n const extensionMarkerResult = await db.execute({\n sql: \"SELECT session_id, value, updated_at FROM application_state WHERE key = ? ORDER BY updated_at ASC\",\n args: [EXTENSION_CHANGE_MARKER_KEY],\n });\n const changedExtensionMarkers = extensionMarkerResult.rows.filter(\n (row) => timestampValue(row.updated_at) > _lastExtensionMarkerTs,\n );\n if (_lastExtensionMarkerTs > 0) {\n recordExtensionChanges(\n changedExtensionMarkers\n .map((row) => parseExtensionChangeMarker(row.session_id, row.value))\n .filter((target): target is ExtensionChangeTarget => !!target),\n );\n }\n _lastExtensionMarkerTs = extensionMarkerTs;\n }\n\n // Check settings for external writes\n const settingsTs = await readMaxUpdatedAt(db, \"settings\");\n if (settingsTs > _lastSettingsTs) {\n if (_lastSettingsTs > 0) {\n recordChange({ source: \"settings\", type: \"change\", key: \"*\" });\n }\n _lastSettingsTs = settingsTs;\n }\n\n // Extension rows live in the legacy physical `tools` table. Keep this as a\n // compatibility fallback for direct table writes, but scope events to the\n // resource owner/share targets instead of broadcasting deployment-wide.\n const extensionsMaxUpdatedAt = await readMaxUpdatedAtRaw(db, \"tools\");\n const extensionsTs = timestampValue(extensionsMaxUpdatedAt);\n if (extensionsTs > _lastExtensionsTs) {\n const since = _lastExtensionsUpdatedAt;\n const extensionResult =\n since === undefined\n ? await db.execute({\n sql: \"SELECT id, owner_email, org_id, visibility, updated_at FROM tools ORDER BY updated_at ASC\",\n args: [],\n })\n : await db.execute({\n sql: \"SELECT id, owner_email, org_id, visibility, updated_at FROM tools WHERE updated_at > ? ORDER BY updated_at ASC\",\n args: [since],\n });\n const changedExtensionRows = extensionResult.rows.filter(\n (row) => timestampValue(row.updated_at) > _lastExtensionsTs,\n );\n if (_lastExtensionsTs > 0) {\n const targetsByRow = await readExtensionTargetsForRows(\n db,\n changedExtensionRows,\n );\n for (const targets of targetsByRow) recordExtensionChanges(targets);\n }\n _lastExtensionsTs = extensionsTs;\n _lastExtensionsUpdatedAt = sqlWatermarkValue(extensionsMaxUpdatedAt);\n }\n } catch {\n // Tables may not exist yet — ignore\n }\n}\n\n/**\n * Create an H3 handler for the poll endpoint.\n *\n * GET /_agent-native/poll?since=N → { version, events[] }\n *\n * Requires an authenticated session. Events are filtered to the caller's\n * tenant — global events (owner-less, table-level pings) reach every\n * authenticated caller; owned events reach only the matching user/org.\n * Without auth + filtering, an anonymous attacker could poll the deployment\n * and infer cross-tenant activity from the global event stream.\n */\nexport function createPollHandler() {\n wireLocalEmitters();\n return defineEventHandler(async (event) => {\n const session = await getSession(event).catch(() => null);\n if (!session?.email) {\n setResponseStatus(event, 401);\n return { error: \"Unauthenticated\" };\n }\n // On cold start, seed _version from DB so we don't return version: 0\n await seedVersionFromDb();\n // Check for cross-process writes before responding\n await checkExternalDbChanges();\n\n const query = getQuery(event);\n const since = parseInt(String(query.since ?? \"0\"), 10) || 0;\n return getChangesSinceForUser(since, session.email, session.orgId);\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"poll.js","sourceRoot":"","sources":["../../src/server/poll.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Polling-based change notification.\n *\n * Replaces SSE with a simple version counter. Each DB mutation (app-state,\n * settings, resources) increments the version. Clients poll `/_agent-native/poll?since=N`\n * and receive any events that occurred after version N.\n *\n * Works in all deployment environments (serverless, edge, long-lived).\n *\n * Also detects cross-process DB writes by periodically checking the\n * application_state and settings tables' updated_at timestamps. This ensures\n * that changes made by external processes (e.g., CLI actions, cron jobs)\n * are picked up even though they don't call recordChange() in this process.\n */\n\nimport { EventEmitter } from \"node:events\";\nimport { defineEventHandler, getQuery, setResponseStatus } from \"h3\";\nimport {\n ACTION_CHANGE_MARKER_KEY,\n parseActionChangeMarker,\n type ActionChangeTarget,\n} from \"../action-change-marker.js\";\nimport { getAppStateEmitter } from \"../application-state/emitter.js\";\nimport { getDbExec } from \"../db/client.js\";\nimport {\n EXTENSION_CHANGE_MARKER_KEY,\n parseExtensionChangeMarker,\n type ExtensionChangeTarget,\n} from \"../extensions/change-marker.js\";\nimport { getSettingsEmitter } from \"../settings/store.js\";\nimport { getSession } from \"./auth.js\";\n\nexport interface ChangeEvent {\n version: number;\n source: string;\n type: string;\n key?: string;\n /**\n * Owner email for tenant-scoped events. When absent, the event is treated\n * as deployment-global (e.g. table-level \"something changed\" pings) and\n * delivered to every authenticated poller. Specific events that should\n * only fan out to one user MUST set this — otherwise polling clients\n * across tenants see each other's signals.\n */\n owner?: string;\n /** Optional org ID for org-scoped events. */\n orgId?: string;\n [k: string]: unknown;\n}\n\n// In-memory ring buffer of recent changes. Kept small since clients\n// poll frequently (every 2-3s) and only need events since their last poll.\nconst MAX_BUFFER = 200;\nlet _version = 0;\nconst _buffer: ChangeEvent[] = [];\nexport const POLL_CHANGE_EVENT = \"poll-change\";\nconst _pollEmitter = new EventEmitter();\n_pollEmitter.setMaxListeners(0);\n\n/**\n * Whether we've seeded _version from the DB. In serverless (Netlify,\n * Vercel, etc.) each invocation starts fresh — without seeding, _version\n * resets to 0 and polling clients see the version jump backwards, causing\n * duplicate events and stuck UI.\n */\nlet _versionSeeded = false;\n\n/** Tracks the latest updated_at we've seen from the DB, per table. */\nlet _lastDbCheck = 0;\n// Coalesces concurrent checkExternalDbChanges runs. The 1s throttle alone does\n// not prevent overlap when a single check takes longer than 1s — two overlapping\n// runs would each read+advance the shared watermarks and double-emit events.\nlet _checkPromise: Promise<void> | null = null;\nlet _lastAppStateTs = 0;\nlet _lastSettingsTs = 0;\nlet _lastExtensionsTs = 0;\nlet _lastExtensionsUpdatedAt: string | number | undefined;\nlet _lastExtensionMarkerTs = 0;\nlet _lastActionMarkerTs = 0;\n\n/**\n * Tracks the latest updated_at seen on the `__screen_refresh__` key in\n * application_state. Bumped when the agent calls the `refresh-screen` tool,\n * and surfaced as a distinct `screen-refresh` event so clients can remount\n * the main content subtree via React key.\n *\n * `_screenRefreshInitialized` guards against spurious emits on the first\n * poll after a restart (where an existing row would look like a fresh bump).\n * Once we've taken a baseline reading, any subsequent increase emits.\n */\nlet _lastScreenRefreshTs = 0;\nlet _screenRefreshInitialized = false;\n// Per-session high-water marks for `__screen_refresh__`. Each user's row is\n// tracked independently so a refresh triggered by one user only remounts that\n// user's screen (owner-scoped), never every authenticated poller.\nconst _lastScreenRefreshTsBySession = new Map<string, number>();\nconst SCREEN_REFRESH_KEY = \"__screen_refresh__\";\nlet _localEmittersWired = false;\n\nfunction wireLocalEmitters(): void {\n if (_localEmittersWired) return;\n _localEmittersWired = true;\n getAppStateEmitter().on(\"app-state\", (event) => {\n if (\n event.key === EXTENSION_CHANGE_MARKER_KEY ||\n event.key === ACTION_CHANGE_MARKER_KEY\n ) {\n return;\n }\n recordChange(event);\n });\n getSettingsEmitter().on(\"settings\", (event) => {\n recordChange(event);\n });\n}\n\nfunction timestampValue(value: unknown): number {\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value !== \"string\") return 0;\n const numeric = Number(value);\n if (Number.isFinite(numeric)) return numeric;\n const parsed = Date.parse(value);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction sqlWatermarkValue(value: unknown): string | number | undefined {\n if (typeof value === \"string\" && value.length > 0) return value;\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n return undefined;\n}\n\nasync function readMaxUpdatedAtRaw(\n db: {\n execute: (\n query: string | { sql: string; args?: unknown[] },\n ) => Promise<{ rows: Array<Record<string, unknown>> }>;\n },\n table: \"application_state\" | \"settings\" | \"tools\",\n): Promise<unknown> {\n try {\n const result = await db.execute(\n `SELECT MAX(updated_at) as max_ts FROM ${table}`,\n );\n return result.rows[0]?.max_ts;\n } catch {\n // Optional framework tables may not exist in every app yet.\n return undefined;\n }\n}\n\nasync function readMaxUpdatedAt(\n db: {\n execute: (\n query: string | { sql: string; args?: unknown[] },\n ) => Promise<{ rows: Array<Record<string, unknown>> }>;\n },\n table: \"application_state\" | \"settings\" | \"tools\",\n): Promise<number> {\n return timestampValue(await readMaxUpdatedAtRaw(db, table));\n}\n\nasync function readExtensionMarkerMaxUpdatedAt(db: {\n execute: (\n query: string | { sql: string; args?: unknown[] },\n ) => Promise<{ rows: Array<Record<string, unknown>> }>;\n}): Promise<number> {\n try {\n const result = await db.execute({\n sql: \"SELECT MAX(updated_at) as max_ts FROM application_state WHERE key = ?\",\n args: [EXTENSION_CHANGE_MARKER_KEY],\n });\n return timestampValue(result.rows[0]?.max_ts);\n } catch {\n return 0;\n }\n}\n\nasync function readActionMarkerMaxUpdatedAt(db: {\n execute: (\n query: string | { sql: string; args?: unknown[] },\n ) => Promise<{ rows: Array<Record<string, unknown>> }>;\n}): Promise<number> {\n try {\n const result = await db.execute({\n sql: \"SELECT MAX(updated_at) as max_ts FROM application_state WHERE key = ?\",\n args: [ACTION_CHANGE_MARKER_KEY],\n });\n return timestampValue(result.rows[0]?.max_ts);\n } catch {\n return 0;\n }\n}\n\n/** Get the current global version counter. */\nexport function getVersion(): number {\n return _version;\n}\n\nexport function getPollEmitter(): EventEmitter {\n return _pollEmitter;\n}\n\nexport function canSeeChangeForUser(\n event: ChangeEvent,\n userEmail: string,\n orgId: string | undefined,\n): boolean {\n // Global / unowned events: every authenticated user gets them.\n if (!event.owner && !event.orgId) return true;\n if (event.owner && event.owner === userEmail) return true;\n if (event.orgId && orgId && event.orgId === orgId) return true;\n return false;\n}\n\n/** Record a change event. Called by emitter listeners. */\nexport function recordChange(event: {\n source: string;\n type: string;\n key?: string;\n [k: string]: unknown;\n}): void {\n // Use timestamp-aligned versions so all serverless instances produce\n // values in the same range (seeded from DB, then incremented via\n // Date.now). Plain ++counter diverges across cold starts.\n _version = Math.max(_version + 1, Date.now());\n const entry: ChangeEvent = { ...event, version: _version };\n _buffer.push(entry);\n if (_buffer.length > MAX_BUFFER) {\n _buffer.splice(0, _buffer.length - MAX_BUFFER);\n }\n _pollEmitter.emit(POLL_CHANGE_EVENT, entry);\n}\n\nfunction extensionTargetKey(target: ExtensionChangeTarget): string | null {\n if (target.owner) return `owner:${target.owner}`;\n if (target.orgId) return `org:${target.orgId}`;\n return null;\n}\n\nfunction addExtensionTarget(\n targets: Map<string, ExtensionChangeTarget>,\n target: ExtensionChangeTarget,\n): void {\n const key = extensionTargetKey(target);\n if (key) targets.set(key, target);\n}\n\nfunction recordExtensionChanges(targets: ExtensionChangeTarget[]): void {\n const uniqueTargets = new Map<string, ExtensionChangeTarget>();\n for (const target of targets) addExtensionTarget(uniqueTargets, target);\n for (const target of uniqueTargets.values()) {\n recordChange({\n source: \"extensions\",\n type: \"change\",\n key: \"*\",\n ...(target.owner ? { owner: target.owner } : {}),\n ...(target.orgId ? { orgId: target.orgId } : {}),\n });\n }\n}\n\nfunction recordActionChanges(targets: ActionChangeTarget[]): void {\n for (const target of targets) {\n recordChange({\n source: \"action\",\n type: \"change\",\n key: target.actionName ?? \"*\",\n ...(target.owner ? { owner: target.owner } : {}),\n ...(target.orgId ? { orgId: target.orgId } : {}),\n });\n }\n}\n\nfunction extensionTargetsForRow(\n row: Record<string, unknown>,\n shareRows: Array<Record<string, unknown>>,\n): ExtensionChangeTarget[] {\n const targets = new Map<string, ExtensionChangeTarget>();\n const owner = typeof row.owner_email === \"string\" ? row.owner_email : \"\";\n const orgId = typeof row.org_id === \"string\" ? row.org_id : \"\";\n const visibility =\n typeof row.visibility === \"string\" ? row.visibility : \"private\";\n\n if (owner) addExtensionTarget(targets, { owner });\n if (visibility === \"org\" && orgId) addExtensionTarget(targets, { orgId });\n\n for (const share of shareRows) {\n const principalType =\n typeof share.principal_type === \"string\" ? share.principal_type : \"\";\n const principalId =\n typeof share.principal_id === \"string\" ? share.principal_id : \"\";\n if (principalType === \"user\" && principalId) {\n addExtensionTarget(targets, { owner: principalId });\n } else if (principalType === \"org\" && principalId) {\n addExtensionTarget(targets, { orgId: principalId });\n }\n }\n\n return Array.from(targets.values());\n}\n\nasync function readExtensionTargetsForRows(\n db: {\n execute: (\n query: string | { sql: string; args?: unknown[] },\n ) => Promise<{ rows: Array<Record<string, unknown>> }>;\n },\n rows: Array<Record<string, unknown>>,\n): Promise<ExtensionChangeTarget[][]> {\n const ids = rows\n .map((row) => (typeof row.id === \"string\" ? row.id : \"\"))\n .filter(Boolean);\n const sharesByResourceId = new Map<string, Array<Record<string, unknown>>>();\n\n if (ids.length > 0) {\n try {\n const placeholders = ids.map(() => \"?\").join(\", \");\n const shareResult = await db.execute({\n sql: `SELECT resource_id, principal_type, principal_id FROM tool_shares WHERE resource_id IN (${placeholders})`,\n args: ids,\n });\n for (const share of shareResult.rows) {\n const resourceId =\n typeof share.resource_id === \"string\" ? share.resource_id : \"\";\n if (!resourceId) continue;\n const bucket = sharesByResourceId.get(resourceId) ?? [];\n bucket.push(share);\n sharesByResourceId.set(resourceId, bucket);\n }\n } catch {\n // Sharing tables are optional during early app initialization.\n }\n }\n\n return rows.map((row) =>\n extensionTargetsForRow(\n row,\n sharesByResourceId.get(typeof row.id === \"string\" ? row.id : \"\") ?? [],\n ),\n );\n}\n\n/** Get all changes after a given version. */\nexport function getChangesSince(since: number): {\n version: number;\n events: ChangeEvent[];\n} {\n if (since >= _version) {\n return { version: _version, events: [] };\n }\n const events = _buffer.filter((e) => e.version > since);\n return { version: _version, events };\n}\n\n/**\n * Get changes after a given version, filtered to events the caller is\n * allowed to see.\n *\n * Filtering rules:\n * - Events without an `owner` are deployment-global (table-level pings,\n * screen-refresh, etc.) and visible to every authenticated user.\n * - Events with `owner === userEmail` go to that user.\n * - Events with `orgId === orgId` go to anyone in that org.\n * - All other owned events are filtered out.\n */\nexport function getChangesSinceForUser(\n since: number,\n userEmail: string,\n orgId: string | undefined,\n): { version: number; events: ChangeEvent[] } {\n if (since >= _version) {\n return { version: _version, events: [] };\n }\n const events = _buffer.filter(\n (e) => e.version > since && canSeeChangeForUser(e, userEmail, orgId),\n );\n return { version: _version, events };\n}\n\n/**\n * Seed _version from DB timestamps on the first call so serverless\n * cold starts don't return version 0 and confuse polling clients.\n */\nasync function seedVersionFromDb(): Promise<void> {\n if (_versionSeeded) return;\n _versionSeeded = true;\n\n try {\n const db = getDbExec();\n\n const [\n appTs,\n settingsTs,\n extensionsMaxUpdatedAt,\n extensionMarkerTs,\n actionMarkerTs,\n refreshResult,\n ] = await Promise.all([\n readMaxUpdatedAt(db, \"application_state\"),\n readMaxUpdatedAt(db, \"settings\"),\n readMaxUpdatedAtRaw(db, \"tools\"),\n readExtensionMarkerMaxUpdatedAt(db),\n readActionMarkerMaxUpdatedAt(db),\n db\n .execute({\n sql: \"SELECT session_id, updated_at FROM application_state WHERE key = ?\",\n args: [SCREEN_REFRESH_KEY],\n })\n .catch(() => ({ rows: [] as Record<string, unknown>[] })),\n ]);\n\n const extensionsTs = timestampValue(extensionsMaxUpdatedAt);\n let refreshTs = 0;\n for (const row of refreshResult.rows) {\n refreshTs = Math.max(refreshTs, timestampValue(row.updated_at));\n }\n\n // Seed version — never decrease an already-set value\n _version = Math.max(\n _version,\n appTs,\n settingsTs,\n extensionsTs,\n extensionMarkerTs,\n actionMarkerTs,\n );\n\n // Set baselines so checkExternalDbChanges detects future writes\n _lastAppStateTs = appTs;\n _lastSettingsTs = settingsTs;\n _lastExtensionsTs = extensionsTs;\n _lastExtensionsUpdatedAt = sqlWatermarkValue(extensionsMaxUpdatedAt);\n _lastExtensionMarkerTs = extensionMarkerTs;\n // Action markers are durable specifically so a web server can observe work\n // performed by a separate action process. Do not baseline past an existing\n // marker on cold start, or the first poll after the action will miss it.\n _lastActionMarkerTs = 0;\n _lastScreenRefreshTs = refreshTs;\n _lastScreenRefreshTsBySession.clear();\n for (const row of refreshResult.rows) {\n if (typeof row.session_id === \"string\") {\n _lastScreenRefreshTsBySession.set(\n row.session_id,\n timestampValue(row.updated_at),\n );\n }\n }\n _screenRefreshInitialized = true;\n // Skip the redundant cold-start recheck unless there is an existing durable\n // action marker that the first poll still needs to emit.\n _lastDbCheck = actionMarkerTs > 0 ? 0 : Date.now();\n } catch {\n // Tables may not exist yet — ignore\n }\n}\n\n/**\n * Check for cross-process DB writes by comparing updated_at timestamps.\n * Runs at most once per second to avoid excessive queries.\n */\nasync function checkExternalDbChanges(): Promise<void> {\n const now = Date.now();\n if (now - _lastDbCheck < 1000) return;\n // Coalesce: if a check is already running, await it instead of starting a\n // second overlapping run that would double-advance the shared watermarks\n // (and double-emit change events).\n if (_checkPromise) return _checkPromise;\n _lastDbCheck = now;\n _checkPromise = doCheckExternalDbChanges().finally(() => {\n _checkPromise = null;\n });\n return _checkPromise;\n}\n\nasync function doCheckExternalDbChanges(): Promise<void> {\n try {\n const db = getDbExec();\n\n // Check application_state for external writes. Preserve the changed key so\n // clients can invalidate one-shot command queries (`navigate`, `__set_url__`)\n // only when those command rows actually change; noisy keys such as\n // `slide-fit-check` should not wake navigation readers.\n const appResult = await db.execute({\n sql: \"SELECT session_id, key, updated_at FROM application_state WHERE updated_at > ? ORDER BY updated_at ASC\",\n args: [_lastAppStateTs],\n });\n if (appResult.rows.length > 0) {\n const appTs = appResult.rows.reduce(\n (max, row) => Math.max(max, timestampValue(row.updated_at)),\n _lastAppStateTs,\n );\n if (_lastAppStateTs > 0) {\n for (const row of appResult.rows) {\n const key = typeof row.key === \"string\" ? row.key : \"*\";\n if (\n key === EXTENSION_CHANGE_MARKER_KEY ||\n key === ACTION_CHANGE_MARKER_KEY\n ) {\n continue;\n }\n const owner =\n typeof row.session_id === \"string\" ? row.session_id : undefined;\n recordChange({\n source: \"app-state\",\n type: \"change\",\n key,\n ...(owner ? { owner } : {}),\n });\n }\n }\n _lastAppStateTs = appTs;\n }\n\n // Mutating actions write a durable marker in addition to the in-process\n // event. This lets dev-mode `pnpm action ...` child processes and\n // serverless action invocations wake the web server's SSE/poll loop as a\n // first-class source:\"action\" event rather than a generic app-state bump.\n const actionMarkerTs = await readActionMarkerMaxUpdatedAt(db);\n if (actionMarkerTs > _lastActionMarkerTs) {\n const actionMarkerResult = await db.execute({\n sql: \"SELECT session_id, value, updated_at FROM application_state WHERE key = ? ORDER BY updated_at ASC\",\n args: [ACTION_CHANGE_MARKER_KEY],\n });\n const changedActionMarkers = actionMarkerResult.rows.filter(\n (row) => timestampValue(row.updated_at) > _lastActionMarkerTs,\n );\n recordActionChanges(\n changedActionMarkers\n .map((row) => parseActionChangeMarker(row.session_id, row.value))\n .filter((target): target is ActionChangeTarget => !!target),\n );\n _lastActionMarkerTs = actionMarkerTs;\n }\n\n // Check for screen-refresh requests from the agent. The `refresh-screen`\n // tool writes to application_state under a well-known key; when its\n // updated_at bumps, emit a distinct event so the client invalidates\n // all queries (not just the ones matching its default queryKey prefix).\n const refreshResult = await db.execute({\n sql: \"SELECT session_id, updated_at, value FROM application_state WHERE key = ?\",\n args: [SCREEN_REFRESH_KEY],\n });\n const refreshTs = refreshResult.rows.reduce(\n (max, row) => Math.max(max, timestampValue(row.updated_at)),\n 0,\n );\n if (!_screenRefreshInitialized) {\n _lastScreenRefreshTs = refreshTs;\n for (const row of refreshResult.rows) {\n if (typeof row.session_id === \"string\") {\n _lastScreenRefreshTsBySession.set(\n row.session_id,\n timestampValue(row.updated_at),\n );\n }\n }\n _screenRefreshInitialized = true;\n } else if (refreshTs > _lastScreenRefreshTs) {\n // Emit a per-user event only for the session(s) whose row actually\n // advanced, scoped with `owner` so canSeeChangeForUser delivers it only\n // to that user — not every authenticated poller.\n for (const row of refreshResult.rows) {\n const owner =\n typeof row.session_id === \"string\" ? row.session_id : undefined;\n if (!owner) continue;\n const rowTs = timestampValue(row.updated_at);\n if (rowTs <= (_lastScreenRefreshTsBySession.get(owner) ?? 0)) continue;\n let scope: string | undefined;\n try {\n const raw = row.value;\n if (typeof raw === \"string\") {\n const parsed = JSON.parse(raw);\n if (typeof parsed?.scope === \"string\") scope = parsed.scope;\n }\n } catch {}\n recordChange({\n source: \"screen-refresh\",\n type: \"change\",\n key: SCREEN_REFRESH_KEY,\n owner,\n ...(scope ? { scope } : {}),\n });\n _lastScreenRefreshTsBySession.set(owner, rowTs);\n }\n _lastScreenRefreshTs = refreshTs;\n }\n\n // Extension mutations write a durable marker row so delete and hide/unhide\n // operations are visible across serverless invocations. Translate those\n // marker rows back into extension-source events for targeted client\n // invalidation while preserving user/org scope.\n const extensionMarkerTs = await readExtensionMarkerMaxUpdatedAt(db);\n if (extensionMarkerTs > _lastExtensionMarkerTs) {\n const extensionMarkerResult = await db.execute({\n sql: \"SELECT session_id, value, updated_at FROM application_state WHERE key = ? ORDER BY updated_at ASC\",\n args: [EXTENSION_CHANGE_MARKER_KEY],\n });\n const changedExtensionMarkers = extensionMarkerResult.rows.filter(\n (row) => timestampValue(row.updated_at) > _lastExtensionMarkerTs,\n );\n if (_lastExtensionMarkerTs > 0) {\n recordExtensionChanges(\n changedExtensionMarkers\n .map((row) => parseExtensionChangeMarker(row.session_id, row.value))\n .filter((target): target is ExtensionChangeTarget => !!target),\n );\n }\n _lastExtensionMarkerTs = extensionMarkerTs;\n }\n\n // Check settings for external writes\n const settingsTs = await readMaxUpdatedAt(db, \"settings\");\n if (settingsTs > _lastSettingsTs) {\n if (_lastSettingsTs > 0) {\n recordChange({ source: \"settings\", type: \"change\", key: \"*\" });\n }\n _lastSettingsTs = settingsTs;\n }\n\n // Extension rows live in the legacy physical `tools` table. Keep this as a\n // compatibility fallback for direct table writes, but scope events to the\n // resource owner/share targets instead of broadcasting deployment-wide.\n const extensionsMaxUpdatedAt = await readMaxUpdatedAtRaw(db, \"tools\");\n const extensionsTs = timestampValue(extensionsMaxUpdatedAt);\n if (extensionsTs > _lastExtensionsTs) {\n const since = _lastExtensionsUpdatedAt;\n const extensionResult =\n since === undefined\n ? await db.execute({\n sql: \"SELECT id, owner_email, org_id, visibility, updated_at FROM tools ORDER BY updated_at ASC\",\n args: [],\n })\n : await db.execute({\n sql: \"SELECT id, owner_email, org_id, visibility, updated_at FROM tools WHERE updated_at > ? ORDER BY updated_at ASC\",\n args: [since],\n });\n const changedExtensionRows = extensionResult.rows.filter(\n (row) => timestampValue(row.updated_at) > _lastExtensionsTs,\n );\n if (_lastExtensionsTs > 0) {\n const targetsByRow = await readExtensionTargetsForRows(\n db,\n changedExtensionRows,\n );\n for (const targets of targetsByRow) recordExtensionChanges(targets);\n }\n _lastExtensionsTs = extensionsTs;\n _lastExtensionsUpdatedAt = sqlWatermarkValue(extensionsMaxUpdatedAt);\n }\n } catch {\n // Tables may not exist yet — ignore\n }\n}\n\n/**\n * Create an H3 handler for the poll endpoint.\n *\n * GET /_agent-native/poll?since=N → { version, events[] }\n *\n * Requires an authenticated session. Events are filtered to the caller's\n * tenant — global events (owner-less, table-level pings) reach every\n * authenticated caller; owned events reach only the matching user/org.\n * Without auth + filtering, an anonymous attacker could poll the deployment\n * and infer cross-tenant activity from the global event stream.\n */\nexport function createPollHandler() {\n wireLocalEmitters();\n return defineEventHandler(async (event) => {\n const session = await getSession(event).catch(() => null);\n if (!session?.email) {\n setResponseStatus(event, 401);\n return { error: \"Unauthenticated\" };\n }\n // On cold start, seed _version from DB so we don't return version: 0\n await seedVersionFromDb();\n // Check for cross-process writes before responding\n await checkExternalDbChanges();\n\n const query = getQuery(event);\n const since = parseInt(String(query.since ?? \"0\"), 10) || 0;\n return getChangesSinceForUser(since, session.email, session.orgId);\n });\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-prompt.js","sourceRoot":"","sources":["../../src/server/schema-prompt.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Auto-introspected SQL schema context block for the agent's system prompt.\n *\n * On every chat turn, the framework appends a compact, always-fresh summary\n * of the app's SQL database — every table, every column, every foreign key —\n * so the agent knows exactly what data model it's working with. The schema\n * is pulled live from `information_schema` (Postgres) or `PRAGMA table_info`\n * (SQLite), cached briefly to keep latency down but never hard-coded.\n *\n * The block also:\n * - points at the db-query / db-exec / db-patch / db-schema tools for runtime access\n * - lists Postgres column descriptions (`COMMENT ON COLUMN ...`) if present\n * - explains the current user/org data scoping so the agent doesn't re-filter\n * by hand (which would be redundant and easy to get wrong)\n */\nimport {\n getDbExec,\n getDatabaseUrl,\n isPostgres,\n type DbExec,\n} from \"../db/client.js\";\n\ninterface ColumnSchema {\n name: string;\n type: string;\n notnull: boolean;\n pk: boolean;\n comment: string | null;\n}\n\ninterface ForeignKey {\n from: string;\n table: string;\n to: string;\n}\n\ninterface TableSchema {\n name: string;\n columns: ColumnSchema[];\n foreignKeys: ForeignKey[];\n comment: string | null;\n}\n\n// Short-lived in-memory cache — schema rarely changes between messages, but\n// we want new tables to show up within a few seconds during active dev.\nconst CACHE_TTL_MS = 15_000;\nlet _cache: {\n key: string;\n expires: number;\n tables: TableSchema[];\n dialect: \"postgres\" | \"sqlite\";\n} | null = null;\n\nfunction cacheKey(): string {\n return (isPostgres() ? \"pg:\" : \"lite:\") + (getDatabaseUrl() || \"\");\n}\n\n// ─── Postgres introspection ─────────────────────────────────────────────────\n\nasync function introspectPostgres(db: DbExec): Promise<TableSchema[]> {\n const tablesRes = await db.execute({\n sql: `SELECT table_name AS name,\n obj_description((quote_ident(table_schema) || '.' || quote_ident(table_name))::regclass, 'pg_class') AS comment\n FROM information_schema.tables\n WHERE table_schema = 'public' AND table_type = 'BASE TABLE'\n ORDER BY table_name`,\n args: [],\n });\n\n const tables: TableSchema[] = [];\n\n for (const t of tablesRes.rows as any[]) {\n const name = t.name as string;\n\n const colsRes = await db.execute({\n sql: `SELECT c.column_name AS name,\n c.data_type AS type,\n CASE WHEN c.is_nullable = 'NO' THEN 1 ELSE 0 END AS notnull,\n col_description((quote_ident(c.table_schema) || '.' || quote_ident(c.table_name))::regclass, c.ordinal_position) AS comment\n FROM information_schema.columns c\n WHERE c.table_name = ? AND c.table_schema = 'public'\n ORDER BY c.ordinal_position`,\n args: [name],\n });\n\n const pksRes = await db.execute({\n sql: `SELECT kcu.column_name AS name\n FROM information_schema.table_constraints tc\n JOIN information_schema.key_column_usage kcu\n ON tc.constraint_name = kcu.constraint_name\n AND tc.table_schema = kcu.table_schema\n WHERE tc.table_name = ? AND tc.constraint_type = 'PRIMARY KEY'`,\n args: [name],\n });\n const pkSet = new Set((pksRes.rows as any[]).map((r) => r.name as string));\n\n const fksRes = await db.execute({\n sql: `SELECT kcu.column_name AS col_from,\n ccu.table_name AS ref_table,\n ccu.column_name AS ref_col\n FROM information_schema.table_constraints tc\n JOIN information_schema.key_column_usage kcu\n ON tc.constraint_name = kcu.constraint_name\n JOIN information_schema.constraint_column_usage ccu\n ON tc.constraint_name = ccu.constraint_name\n WHERE tc.table_name = ? AND tc.constraint_type = 'FOREIGN KEY'`,\n args: [name],\n });\n\n tables.push({\n name,\n comment: (t.comment as string | null) ?? null,\n columns: (colsRes.rows as any[]).map((c) => ({\n name: c.name as string,\n type: (c.type as string) || \"any\",\n notnull: Number(c.notnull) === 1,\n pk: pkSet.has(c.name as string),\n comment: (c.comment as string | null) ?? null,\n })),\n foreignKeys: (fksRes.rows as any[]).map((f) => ({\n from: f.col_from as string,\n table: f.ref_table as string,\n to: f.ref_col as string,\n })),\n });\n }\n\n return tables;\n}\n\n// ─── SQLite / libSQL / D1 introspection ────────────────────────────────────\n\nasync function introspectSqlite(db: DbExec): Promise<TableSchema[]> {\n const tablesRes = await db.execute(\n `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`,\n );\n\n const tables: TableSchema[] = [];\n\n for (const row of tablesRes.rows as any[]) {\n const name = row.name as string;\n if (!name) continue;\n // Quote the identifier for PRAGMA calls; SQLite requires doubling embedded quotes.\n const escaped = name.replace(/\"/g, '\"\"');\n\n const colsRes = await db.execute(`PRAGMA table_info(\"${escaped}\")`);\n const fksRes = await db.execute(`PRAGMA foreign_key_list(\"${escaped}\")`);\n\n tables.push({\n name,\n comment: null, // SQLite has no column/table comments\n columns: (colsRes.rows as any[]).map((c) => ({\n name: c.name as string,\n type: ((c.type as string) || \"\").toLowerCase() || \"any\",\n notnull: Number(c.notnull) === 1,\n pk: Number(c.pk) === 1,\n comment: null,\n })),\n foreignKeys: (fksRes.rows as any[]).map((f) => ({\n from: f.from as string,\n table: f.table as string,\n to: f.to as string,\n })),\n });\n }\n\n return tables;\n}\n\n// ─── Cached entry point ─────────────────────────────────────────────────────\n\nasync function getSchema(): Promise<{\n tables: TableSchema[];\n dialect: \"postgres\" | \"sqlite\";\n}> {\n const key = cacheKey();\n const now = Date.now();\n if (_cache && _cache.key === key && _cache.expires > now) {\n return { tables: _cache.tables, dialect: _cache.dialect };\n }\n\n const db = getDbExec();\n const dialect: \"postgres\" | \"sqlite\" = isPostgres() ? \"postgres\" : \"sqlite\";\n const tables =\n dialect === \"postgres\"\n ? await introspectPostgres(db)\n : await introspectSqlite(db);\n\n _cache = { key, expires: now + CACHE_TTL_MS, tables, dialect };\n return { tables, dialect };\n}\n\n/** Manually drop the cache — useful from tests or after running a migration. */\nexport function invalidateSchemaPromptCache(): void {\n _cache = null;\n}\n\n// ─── Formatting ─────────────────────────────────────────────────────────────\n\nfunction shortType(type: string): string {\n // Trim verbose Postgres type names for compactness in the prompt.\n const t = type.toLowerCase();\n if (t === \"character varying\") return \"varchar\";\n if (t === \"timestamp without time zone\") return \"timestamp\";\n if (t === \"timestamp with time zone\") return \"timestamptz\";\n if (t === \"double precision\") return \"double\";\n return t;\n}\n\nfunction formatTable(table: TableSchema): string {\n const fkByCol = new Map<string, string>();\n for (const fk of table.foreignKeys) {\n fkByCol.set(fk.from, `${fk.table}.${fk.to}`);\n }\n\n const cols = table.columns.map((c) => {\n const flags: string[] = [];\n if (c.pk) flags.push(\"pk\");\n if (!c.notnull && !c.pk) flags.push(\"null\");\n const fk = fkByCol.get(c.name);\n if (fk) flags.push(`→${fk}`);\n\n // Flag scoping columns so the agent understands per-user/per-org filtering.\n if (c.name === \"owner_email\") flags.push(\"user-scope\");\n if (c.name === \"org_id\") flags.push(\"org-scope\");\n\n const flagStr = flags.length ? ` [${flags.join(\", \")}]` : \"\";\n const commentStr = c.comment ? ` -- ${c.comment.replace(/\\s+/g, \" \")}` : \"\";\n return ` ${c.name} ${shortType(c.type)}${flagStr}${commentStr}`;\n });\n\n const header = table.comment\n ? ` ${table.name} -- ${table.comment.replace(/\\s+/g, \" \")}`\n : ` ${table.name}`;\n\n return [header, ...cols].join(\"\\n\");\n}\n\n// ─── Public API ─────────────────────────────────────────────────────────────\n\n/**\n * Build the `<sql-database>` block appended to the system prompt on every turn.\n *\n * `owner` and `orgId` come from the per-request context (AGENT_USER_EMAIL /\n * AGENT_ORG_ID) and are surfaced so the agent knows who it is acting on behalf\n * of — and understands that rows are already filtered for that identity.\n */\nexport async function loadSchemaPromptBlock(opts: {\n owner?: string | null;\n orgId?: string | null;\n /** If true, mention db-query/db-exec/db-patch/db-schema as available tools. */\n hasRawDbTools?: boolean;\n}): Promise<string> {\n let tables: TableSchema[];\n let dialect: \"postgres\" | \"sqlite\";\n try {\n const res = await getSchema();\n tables = res.tables;\n dialect = res.dialect;\n } catch (err) {\n // DB not ready, or introspection blew up — don't take the chat down.\n return \"\";\n }\n\n if (tables.length === 0) return \"\";\n\n // Partition framework-internal tables from template tables so the agent\n // focuses on the data model it's most likely to touch.\n const CORE_TABLES = new Set([\n \"application_state\",\n \"settings\",\n \"oauth_tokens\",\n \"sessions\",\n \"resources\",\n \"chat_threads\",\n \"_collab_docs\",\n \"usage_events\",\n \"usage_totals\",\n \"user\",\n \"account\",\n \"verification\",\n \"organization\",\n \"member\",\n \"invitation\",\n ]);\n\n const templateTables = tables.filter((t) => !CORE_TABLES.has(t.name));\n const coreTables = tables.filter((t) => CORE_TABLES.has(t.name));\n\n const lines: string[] = [];\n lines.push(\"<sql-database>\");\n lines.push(\n `The app's state lives in a SQL database (${dialect}). The schema below is auto-introspected fresh each turn — treat it as authoritative.`,\n );\n lines.push(\"\");\n\n if (templateTables.length > 0) {\n lines.push(\"## Template tables\");\n lines.push(\"\");\n for (const t of templateTables) {\n lines.push(formatTable(t));\n lines.push(\"\");\n }\n }\n\n if (coreTables.length > 0) {\n lines.push(\n \"## Framework tables (auth, resources, chat threads, app-state, etc.) — usually read/written via dedicated tools, not raw SQL\",\n );\n lines.push(\"\");\n for (const t of coreTables) {\n lines.push(formatTable(t));\n lines.push(\"\");\n }\n }\n\n // Tooling references.\n if (opts.hasRawDbTools) {\n lines.push(\"## SQL tools\");\n lines.push(\n \"- `db-schema` — refresh the full schema with indexes and foreign keys\",\n );\n lines.push(\n \"- `db-query` — run a SELECT (read-only; results already filtered to the current user/org)\",\n );\n lines.push(\n \"- `db-exec` — run INSERT / UPDATE / DELETE / REPLACE (writes already scoped; owner_email and org_id are auto-injected on INSERT). For multiple related writes, pass `statements` so they run in one transaction instead of separate tool calls. Schema changes are blocked.\",\n );\n lines.push(\n \"- `db-patch` — surgical search-and-replace on a large text column. Send `{find, replace}` pairs instead of the full new value. Use this for edits to large fields (documents, slide HTML, dashboard/form JSON) — it avoids re-sending multi-kilobyte strings and saves tokens. Targets exactly one row (narrow `--where` by primary key). Uses the same per-user/per-org scoping as db-exec.\",\n );\n lines.push(\"\");\n lines.push(\"### When to pick which SQL tool\");\n lines.push(\n \"- Set a short column outright, update multiple columns, or do computed updates (`calories = calories + 50`) → `db-exec UPDATE`.\",\n );\n lines.push(\n '- Insert/update several rows as one logical operation → `db-exec` with `statements: \\'[{\"sql\":\"...\",\"args\":[...]}]\\'` so the batch commits or rolls back together.',\n );\n lines.push(\n \"- Change a small slice of a large text/JSON column → `db-patch`. Much cheaper token-wise than re-sending the whole column.\",\n );\n lines.push(\n \"- A template-specific action exists for the table (`edit-document`, `update-slide`, etc.) → use that action. It encodes business rules and pushes live Yjs updates to any open collaborative editor; raw SQL does neither.\",\n );\n lines.push(\n \"- Read data → `db-query`. Never re-add `WHERE owner_email = ...` — scoping already applies it.\",\n );\n lines.push(\"\");\n lines.push(\"### External data sources vs the app database\");\n lines.push(\n \"The `db-*` tools ONLY query the app's own SQL database (the tables listed above). They do NOT reach external data warehouses, analytics platforms, or third-party services.\",\n );\n lines.push(\n \"If the user asks about tables that are NOT in the schema above, use the appropriate template action instead — for example `bigquery` for BigQuery warehouse tables, `ga4-report` for Google Analytics, `hubspot-deals` for HubSpot, etc. Check your available actions for the right data-source-specific tool.\",\n );\n lines.push(\n \"**Never use `db-query` for external data.** It will fail because those tables don't exist in the app database.\",\n );\n lines.push(\"\");\n } else {\n lines.push(\n \"SQL is accessed through the template actions listed above. The schema is shown for context — so you understand the data model those actions operate on.\",\n );\n lines.push(\"\");\n }\n\n // Data scoping context.\n const ownerLine = opts.owner ? opts.owner : \"(unresolved)\";\n const orgLine = opts.orgId ? opts.orgId : \"(none)\";\n lines.push(\"## Data scoping (enforced at the SQL layer)\");\n lines.push(`- Current user: \\`${ownerLine}\\``);\n lines.push(`- Current org: \\`${orgLine}\\``);\n lines.push(\n \"- Tables with an `owner_email` column are automatically filtered to the current user via temporary views before every query.\",\n );\n lines.push(\n \"- Tables with an `org_id` column are automatically filtered to the current org as well.\",\n );\n lines.push(\n \"- On INSERT, `owner_email` and `org_id` are auto-injected — do NOT set them manually.\",\n );\n lines.push(\n \"- Do NOT add `WHERE owner_email = ...` or `WHERE org_id = ...` to your queries — the filter is already applied, and re-adding it will confuse the scoped view.\",\n );\n lines.push(\"</sql-database>\");\n\n return \"\\n\\n\" + lines.join(\"\\n\");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"schema-prompt.js","sourceRoot":"","sources":["../../src/server/schema-prompt.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Auto-introspected SQL schema context block for the agent's system prompt.\n *\n * On every chat turn, the framework appends a compact, always-fresh summary\n * of the app's SQL database — every table, every column, every foreign key —\n * so the agent knows exactly what data model it's working with. The schema\n * is pulled live from `information_schema` (Postgres) or `PRAGMA table_info`\n * (SQLite), cached briefly to keep latency down but never hard-coded.\n *\n * The block also:\n * - points at the db-query / db-exec / db-patch / db-schema tools for runtime access\n * - lists Postgres column descriptions (`COMMENT ON COLUMN ...`) if present\n * - explains the current user/org data scoping so the agent doesn't re-filter\n * by hand (which would be redundant and easy to get wrong)\n */\nimport {\n getDbExec,\n getDatabaseUrl,\n isPostgres,\n type DbExec,\n} from \"../db/client.js\";\n\ninterface ColumnSchema {\n name: string;\n type: string;\n notnull: boolean;\n pk: boolean;\n comment: string | null;\n}\n\ninterface ForeignKey {\n from: string;\n table: string;\n to: string;\n}\n\ninterface TableSchema {\n name: string;\n columns: ColumnSchema[];\n foreignKeys: ForeignKey[];\n comment: string | null;\n}\n\n// Short-lived in-memory cache — schema rarely changes between messages, but\n// we want new tables to show up within a few seconds during active dev.\nconst CACHE_TTL_MS = 15_000;\nlet _cache: {\n key: string;\n expires: number;\n tables: TableSchema[];\n dialect: \"postgres\" | \"sqlite\";\n} | null = null;\n\nfunction cacheKey(): string {\n return (isPostgres() ? \"pg:\" : \"lite:\") + (getDatabaseUrl() || \"\");\n}\n\n// ─── Postgres introspection ─────────────────────────────────────────────────\n\nasync function introspectPostgres(db: DbExec): Promise<TableSchema[]> {\n const tablesRes = await db.execute({\n sql: `SELECT table_name AS name,\n obj_description((quote_ident(table_schema) || '.' || quote_ident(table_name))::regclass, 'pg_class') AS comment\n FROM information_schema.tables\n WHERE table_schema = 'public' AND table_type = 'BASE TABLE'\n ORDER BY table_name`,\n args: [],\n });\n\n const tables: TableSchema[] = [];\n\n for (const t of tablesRes.rows as any[]) {\n const name = t.name as string;\n\n const colsRes = await db.execute({\n sql: `SELECT c.column_name AS name,\n c.data_type AS type,\n CASE WHEN c.is_nullable = 'NO' THEN 1 ELSE 0 END AS notnull,\n col_description((quote_ident(c.table_schema) || '.' || quote_ident(c.table_name))::regclass, c.ordinal_position) AS comment\n FROM information_schema.columns c\n WHERE c.table_name = ? AND c.table_schema = 'public'\n ORDER BY c.ordinal_position`,\n args: [name],\n });\n\n const pksRes = await db.execute({\n sql: `SELECT kcu.column_name AS name\n FROM information_schema.table_constraints tc\n JOIN information_schema.key_column_usage kcu\n ON tc.constraint_name = kcu.constraint_name\n AND tc.table_schema = kcu.table_schema\n WHERE tc.table_name = ? AND tc.constraint_type = 'PRIMARY KEY'`,\n args: [name],\n });\n const pkSet = new Set((pksRes.rows as any[]).map((r) => r.name as string));\n\n const fksRes = await db.execute({\n sql: `SELECT kcu.column_name AS col_from,\n ccu.table_name AS ref_table,\n ccu.column_name AS ref_col\n FROM information_schema.table_constraints tc\n JOIN information_schema.key_column_usage kcu\n ON tc.constraint_name = kcu.constraint_name\n JOIN information_schema.constraint_column_usage ccu\n ON tc.constraint_name = ccu.constraint_name\n WHERE tc.table_name = ? AND tc.constraint_type = 'FOREIGN KEY'`,\n args: [name],\n });\n\n tables.push({\n name,\n comment: (t.comment as string | null) ?? null,\n columns: (colsRes.rows as any[]).map((c) => ({\n name: c.name as string,\n type: (c.type as string) || \"any\",\n notnull: Number(c.notnull) === 1,\n pk: pkSet.has(c.name as string),\n comment: (c.comment as string | null) ?? null,\n })),\n foreignKeys: (fksRes.rows as any[]).map((f) => ({\n from: f.col_from as string,\n table: f.ref_table as string,\n to: f.ref_col as string,\n })),\n });\n }\n\n return tables;\n}\n\n// ─── SQLite / libSQL / D1 introspection ────────────────────────────────────\n\nasync function introspectSqlite(db: DbExec): Promise<TableSchema[]> {\n const tablesRes = await db.execute(\n `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`,\n );\n\n const tables: TableSchema[] = [];\n\n for (const row of tablesRes.rows as any[]) {\n const name = row.name as string;\n if (!name) continue;\n // Quote the identifier for PRAGMA calls; SQLite requires doubling embedded quotes.\n const escaped = name.replace(/\"/g, '\"\"');\n\n const colsRes = await db.execute(`PRAGMA table_info(\"${escaped}\")`);\n const fksRes = await db.execute(`PRAGMA foreign_key_list(\"${escaped}\")`);\n\n tables.push({\n name,\n comment: null, // SQLite has no column/table comments\n columns: (colsRes.rows as any[]).map((c) => ({\n name: c.name as string,\n type: ((c.type as string) || \"\").toLowerCase() || \"any\",\n notnull: Number(c.notnull) === 1,\n pk: Number(c.pk) === 1,\n comment: null,\n })),\n foreignKeys: (fksRes.rows as any[]).map((f) => ({\n from: f.from as string,\n table: f.table as string,\n to: f.to as string,\n })),\n });\n }\n\n return tables;\n}\n\n// ─── Cached entry point ─────────────────────────────────────────────────────\n\nasync function getSchema(): Promise<{\n tables: TableSchema[];\n dialect: \"postgres\" | \"sqlite\";\n}> {\n const key = cacheKey();\n const now = Date.now();\n if (_cache && _cache.key === key && _cache.expires > now) {\n return { tables: _cache.tables, dialect: _cache.dialect };\n }\n\n const db = getDbExec();\n const dialect: \"postgres\" | \"sqlite\" = isPostgres() ? \"postgres\" : \"sqlite\";\n const tables =\n dialect === \"postgres\"\n ? await introspectPostgres(db)\n : await introspectSqlite(db);\n\n _cache = { key, expires: now + CACHE_TTL_MS, tables, dialect };\n return { tables, dialect };\n}\n\n/** Manually drop the cache — useful from tests or after running a migration. */\nexport function invalidateSchemaPromptCache(): void {\n _cache = null;\n}\n\n// ─── Formatting ─────────────────────────────────────────────────────────────\n\nfunction shortType(type: string): string {\n // Trim verbose Postgres type names for compactness in the prompt.\n const t = type.toLowerCase();\n if (t === \"character varying\") return \"varchar\";\n if (t === \"timestamp without time zone\") return \"timestamp\";\n if (t === \"timestamp with time zone\") return \"timestamptz\";\n if (t === \"double precision\") return \"double\";\n return t;\n}\n\nfunction formatTable(table: TableSchema): string {\n const fkByCol = new Map<string, string>();\n for (const fk of table.foreignKeys) {\n fkByCol.set(fk.from, `${fk.table}.${fk.to}`);\n }\n\n const cols = table.columns.map((c) => {\n const flags: string[] = [];\n if (c.pk) flags.push(\"pk\");\n if (!c.notnull && !c.pk) flags.push(\"null\");\n const fk = fkByCol.get(c.name);\n if (fk) flags.push(`→${fk}`);\n\n // Flag scoping columns so the agent understands per-user/per-org filtering.\n if (c.name === \"owner_email\") flags.push(\"user-scope\");\n if (c.name === \"org_id\") flags.push(\"org-scope\");\n\n const flagStr = flags.length ? ` [${flags.join(\", \")}]` : \"\";\n const commentStr = c.comment ? ` -- ${c.comment.replace(/\\s+/g, \" \")}` : \"\";\n return ` ${c.name} ${shortType(c.type)}${flagStr}${commentStr}`;\n });\n\n const header = table.comment\n ? ` ${table.name} -- ${table.comment.replace(/\\s+/g, \" \")}`\n : ` ${table.name}`;\n\n return [header, ...cols].join(\"\\n\");\n}\n\n// ─── Public API ─────────────────────────────────────────────────────────────\n\n/**\n * Build the `<sql-database>` block appended to the system prompt on every turn.\n *\n * `owner` and `orgId` come from the per-request context (AGENT_USER_EMAIL /\n * AGENT_ORG_ID) and are surfaced so the agent knows who it is acting on behalf\n * of — and understands that rows are already filtered for that identity.\n */\nexport async function loadSchemaPromptBlock(opts: {\n owner?: string | null;\n orgId?: string | null;\n /** If true, mention db-query/db-exec/db-patch/db-schema as available tools. */\n hasRawDbTools?: boolean;\n}): Promise<string> {\n let tables: TableSchema[];\n let dialect: \"postgres\" | \"sqlite\";\n try {\n const res = await getSchema();\n tables = res.tables;\n dialect = res.dialect;\n } catch {\n // DB not ready, or introspection blew up — don't take the chat down.\n return \"\";\n }\n\n if (tables.length === 0) return \"\";\n\n // Partition framework-internal tables from template tables so the agent\n // focuses on the data model it's most likely to touch.\n const CORE_TABLES = new Set([\n \"application_state\",\n \"settings\",\n \"oauth_tokens\",\n \"sessions\",\n \"resources\",\n \"chat_threads\",\n \"_collab_docs\",\n \"usage_events\",\n \"usage_totals\",\n \"user\",\n \"account\",\n \"verification\",\n \"organization\",\n \"member\",\n \"invitation\",\n ]);\n\n const templateTables = tables.filter((t) => !CORE_TABLES.has(t.name));\n const coreTables = tables.filter((t) => CORE_TABLES.has(t.name));\n\n const lines: string[] = [];\n lines.push(\"<sql-database>\");\n lines.push(\n `The app's state lives in a SQL database (${dialect}). The schema below is auto-introspected fresh each turn — treat it as authoritative.`,\n );\n lines.push(\"\");\n\n if (templateTables.length > 0) {\n lines.push(\"## Template tables\");\n lines.push(\"\");\n for (const t of templateTables) {\n lines.push(formatTable(t));\n lines.push(\"\");\n }\n }\n\n if (coreTables.length > 0) {\n lines.push(\n \"## Framework tables (auth, resources, chat threads, app-state, etc.) — usually read/written via dedicated tools, not raw SQL\",\n );\n lines.push(\"\");\n for (const t of coreTables) {\n lines.push(formatTable(t));\n lines.push(\"\");\n }\n }\n\n // Tooling references.\n if (opts.hasRawDbTools) {\n lines.push(\"## SQL tools\");\n lines.push(\n \"- `db-schema` — refresh the full schema with indexes and foreign keys\",\n );\n lines.push(\n \"- `db-query` — run a SELECT (read-only; results already filtered to the current user/org)\",\n );\n lines.push(\n \"- `db-exec` — run INSERT / UPDATE / DELETE / REPLACE (writes already scoped; owner_email and org_id are auto-injected on INSERT). For multiple related writes, pass `statements` so they run in one transaction instead of separate tool calls. Schema changes are blocked.\",\n );\n lines.push(\n \"- `db-patch` — surgical search-and-replace on a large text column. Send `{find, replace}` pairs instead of the full new value. Use this for edits to large fields (documents, slide HTML, dashboard/form JSON) — it avoids re-sending multi-kilobyte strings and saves tokens. Targets exactly one row (narrow `--where` by primary key). Uses the same per-user/per-org scoping as db-exec.\",\n );\n lines.push(\"\");\n lines.push(\"### When to pick which SQL tool\");\n lines.push(\n \"- Set a short column outright, update multiple columns, or do computed updates (`calories = calories + 50`) → `db-exec UPDATE`.\",\n );\n lines.push(\n '- Insert/update several rows as one logical operation → `db-exec` with `statements: \\'[{\"sql\":\"...\",\"args\":[...]}]\\'` so the batch commits or rolls back together.',\n );\n lines.push(\n \"- Change a small slice of a large text/JSON column → `db-patch`. Much cheaper token-wise than re-sending the whole column.\",\n );\n lines.push(\n \"- A template-specific action exists for the table (`edit-document`, `update-slide`, etc.) → use that action. It encodes business rules and pushes live Yjs updates to any open collaborative editor; raw SQL does neither.\",\n );\n lines.push(\n \"- Read data → `db-query`. Never re-add `WHERE owner_email = ...` — scoping already applies it.\",\n );\n lines.push(\"\");\n lines.push(\"### External data sources vs the app database\");\n lines.push(\n \"The `db-*` tools ONLY query the app's own SQL database (the tables listed above). They do NOT reach external data warehouses, analytics platforms, or third-party services.\",\n );\n lines.push(\n \"If the user asks about tables that are NOT in the schema above, use the appropriate template action instead — for example `bigquery` for BigQuery warehouse tables, `ga4-report` for Google Analytics, `hubspot-deals` for HubSpot, etc. Check your available actions for the right data-source-specific tool.\",\n );\n lines.push(\n \"**Never use `db-query` for external data.** It will fail because those tables don't exist in the app database.\",\n );\n lines.push(\"\");\n } else {\n lines.push(\n \"SQL is accessed through the template actions listed above. The schema is shown for context — so you understand the data model those actions operate on.\",\n );\n lines.push(\"\");\n }\n\n // Data scoping context.\n const ownerLine = opts.owner ? opts.owner : \"(unresolved)\";\n const orgLine = opts.orgId ? opts.orgId : \"(none)\";\n lines.push(\"## Data scoping (enforced at the SQL layer)\");\n lines.push(`- Current user: \\`${ownerLine}\\``);\n lines.push(`- Current org: \\`${orgLine}\\``);\n lines.push(\n \"- Tables with an `owner_email` column are automatically filtered to the current user via temporary views before every query.\",\n );\n lines.push(\n \"- Tables with an `org_id` column are automatically filtered to the current org as well.\",\n );\n lines.push(\n \"- On INSERT, `owner_email` and `org_id` are auto-injected — do NOT set them manually.\",\n );\n lines.push(\n \"- Do NOT add `WHERE owner_email = ...` or `WHERE org_id = ...` to your queries — the filter is already applied, and re-adding it will confuse the scoped view.\",\n );\n lines.push(\"</sql-database>\");\n\n return \"\\n\\n\" + lines.join(\"\\n\");\n}\n"]}
|
|
@@ -44,10 +44,11 @@
|
|
|
44
44
|
* MCP embed-session page loads use `cross-origin` so COEP hosts such as
|
|
45
45
|
* Claude's MCP Apps proxy can frame the short-lived app document.
|
|
46
46
|
*
|
|
47
|
-
* NOTE:
|
|
48
|
-
* embedded subresource to opt in via CORP/CORS, which would
|
|
49
|
-
* iframe editor and template embed use cases. COOP + CORP
|
|
50
|
-
* us most of the protection
|
|
47
|
+
* NOTE: `Cross-Origin-Embedder-Policy` is NOT set by default because it
|
|
48
|
+
* requires every embedded subresource to opt in via CORP/CORS, which would
|
|
49
|
+
* break Builder's iframe editor and template embed use cases. COOP + CORP
|
|
50
|
+
* without COEP gives us most of the protection on normal responses; COEP is
|
|
51
|
+
* only added for validated MCP embed-session page loads (see above).
|
|
51
52
|
*/
|
|
52
53
|
/**
|
|
53
54
|
* Create the security-headers h3 middleware. Mount this BEFORE other route
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security-headers.d.ts","sourceRoot":"","sources":["../../src/server/security-headers.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"security-headers.d.ts","sourceRoot":"","sources":["../../src/server/security-headers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AA2CH;;;;;;GAMG;AACH,wBAAgB,+BAA+B,8EA6C9C"}
|
|
@@ -44,10 +44,11 @@
|
|
|
44
44
|
* MCP embed-session page loads use `cross-origin` so COEP hosts such as
|
|
45
45
|
* Claude's MCP Apps proxy can frame the short-lived app document.
|
|
46
46
|
*
|
|
47
|
-
* NOTE:
|
|
48
|
-
* embedded subresource to opt in via CORP/CORS, which would
|
|
49
|
-
* iframe editor and template embed use cases. COOP + CORP
|
|
50
|
-
* us most of the protection
|
|
47
|
+
* NOTE: `Cross-Origin-Embedder-Policy` is NOT set by default because it
|
|
48
|
+
* requires every embedded subresource to opt in via CORP/CORS, which would
|
|
49
|
+
* break Builder's iframe editor and template embed use cases. COOP + CORP
|
|
50
|
+
* without COEP gives us most of the protection on normal responses; COEP is
|
|
51
|
+
* only added for validated MCP embed-session page loads (see above).
|
|
51
52
|
*/
|
|
52
53
|
import { defineEventHandler, getHeader, setResponseHeader } from "h3";
|
|
53
54
|
import { requestHasEmbedAuthMarker } from "./embed-session.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security-headers.js","sourceRoot":"","sources":["../../src/server/security-headers.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"security-headers.js","sourceRoot":"","sources":["../../src/server/security-headers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Security response headers middleware.\n *\n * Sets a baseline set of \"no-brainer\" security headers on every framework HTTP\n * response. These headers are layered defenses: each one mitigates a specific\n * class of attack, and together they harden the surface against clickjacking,\n * MIME-sniffing, referrer leakage, mixed-content downgrades, and cross-origin\n * window/embed access.\n *\n * The headers we emit:\n *\n * - `Strict-Transport-Security` — forces HTTPS for the browser's lifetime\n * of the cached value, preventing SSL-strip MITM. Only emitted when the\n * request scheme is `https` (we don't want to break local-dev HTTP, and\n * emitting HSTS over HTTP is a no-op per the spec but causes confusion).\n * - `X-Content-Type-Options: nosniff` — disables browser MIME sniffing so\n * a tool /render route serving user-authored HTML can't be misinterpreted\n * as some other content type by a clever Accept header.\n * - `X-Frame-Options: DENY` — prevents the entire app from being iframed by\n * other origins (clickjacking the agent chat, booking pages, etc.). The\n * tool /render endpoint and any other route that legitimately needs to be\n * embedded in the same-origin app shell can opt out by setting its own\n * header inside the route handler — h3's `setResponseHeader` overwrites,\n * so a route emitting `SAMEORIGIN` wins over our middleware default.\n * We skip this header entirely in dev (NODE_ENV !== \"production\") so the\n * desktop app's local dev frame (localhost:3334) can iframe templates\n * running on other localhost ports (e.g. mail at 8085).\n * - `Referrer-Policy: strict-origin-when-cross-origin` — strips path/query\n * from outbound Referer headers when the request crosses origin, so a\n * public-share viewer's outbound link clicks never leak the share token.\n * - `Permissions-Policy: camera=(), microphone=(self), geolocation=(),\n * screen-wake-lock=()` — allows the app shell to request microphone access\n * for composer dictation while keeping camera/location/wake-lock blocked\n * by default. Templates that need broader media capture for recording UI\n * override this on their own routes.\n * - `Cross-Origin-Opener-Policy: same-origin` — isolates window.opener so\n * a popup-window opener reference can't read or modify our document.\n * - `Cross-Origin-Embedder-Policy: require-corp` — emitted only for\n * validated MCP embed-session page loads. COEP hosts such as Claude's MCP\n * Apps proxy require framed cross-origin documents to opt in explicitly.\n * - `Cross-Origin-Resource-Policy: same-site` — prevents other origins from\n * embedding our endpoints as `<img>` / `<script>` / `<audio>`, blocking\n * the simplest data-leak chain when combined with auth cookies. Validated\n * MCP embed-session page loads use `cross-origin` so COEP hosts such as\n * Claude's MCP Apps proxy can frame the short-lived app document.\n *\n * NOTE: `Cross-Origin-Embedder-Policy` is NOT set by default because it\n * requires every embedded subresource to opt in via CORP/CORS, which would\n * break Builder's iframe editor and template embed use cases. COOP + CORP\n * without COEP gives us most of the protection on normal responses; COEP is\n * only added for validated MCP embed-session page loads (see above).\n */\n\nimport { defineEventHandler, getHeader, setResponseHeader } from \"h3\";\nimport { requestHasEmbedAuthMarker } from \"./embed-session.js\";\nimport {\n isMcpEmbedCorsOrigin,\n MCP_EMBED_CORS_ALLOW_HEADERS,\n} from \"../shared/mcp-embed-headers.js\";\n\nconst HSTS = \"max-age=31536000; includeSubDomains; preload\";\nconst PERMISSIONS_POLICY =\n \"camera=(), microphone=(self), geolocation=(), screen-wake-lock=()\";\n\n/**\n * Returns true when the request was received over HTTPS. We trust both the\n * underlying connection (when the server is terminating TLS itself) and the\n * `x-forwarded-proto` header (set by Netlify, Vercel, Cloudflare, and any\n * other reverse proxy that fronts the framework).\n */\nfunction isHttpsRequest(event: any): boolean {\n const xfp =\n event?.node?.req?.headers?.[\"x-forwarded-proto\"] ??\n event?.headers?.get?.(\"x-forwarded-proto\");\n if (typeof xfp === \"string\" && xfp.split(\",\")[0].trim() === \"https\")\n return true;\n if (Array.isArray(xfp) && xfp[0] === \"https\") return true;\n // h3 sets `event.url.protocol` to \"http:\" or \"https:\".\n const proto = event?.url?.protocol;\n if (proto === \"https:\") return true;\n // Direct Node `req.connection.encrypted` (older runtimes).\n if (event?.node?.req?.connection?.encrypted) return true;\n return false;\n}\n\nfunction isMcpEndpointRequest(event: any): boolean {\n const pathname =\n event?.url?.pathname ??\n String(event?.node?.req?.url ?? event?.path ?? \"/\").split(\"?\")[0];\n return (\n pathname === \"/_agent-native/mcp\" || pathname.endsWith(\"/_agent-native/mcp\")\n );\n}\n\n/**\n * Create the security-headers h3 middleware. Mount this BEFORE other route\n * handlers so the headers are present on every response (including 4xx/5xx\n * error pages). Route handlers that need to relax a specific header (e.g.\n * `X-Frame-Options: SAMEORIGIN` on the tool render route) can call\n * `setResponseHeader` after this runs — the latest write wins.\n */\nexport function createSecurityHeadersMiddleware() {\n const isProduction = process.env.NODE_ENV === \"production\";\n return defineEventHandler((event) => {\n const embedFrameRequest = requestHasEmbedAuthMarker(event);\n const mcpEndpointRequest = isMcpEndpointRequest(event);\n const requestOrigin = getHeader(event, \"origin\");\n setResponseHeader(event, \"X-Content-Type-Options\", \"nosniff\");\n if (isProduction && !embedFrameRequest) {\n setResponseHeader(event, \"X-Frame-Options\", \"DENY\");\n }\n setResponseHeader(\n event,\n \"Referrer-Policy\",\n embedFrameRequest ? \"no-referrer\" : \"strict-origin-when-cross-origin\",\n );\n setResponseHeader(event, \"Permissions-Policy\", PERMISSIONS_POLICY);\n setResponseHeader(event, \"Cross-Origin-Opener-Policy\", \"same-origin\");\n if (embedFrameRequest) {\n setResponseHeader(event, \"Cross-Origin-Embedder-Policy\", \"require-corp\");\n }\n setResponseHeader(\n event,\n \"Cross-Origin-Resource-Policy\",\n embedFrameRequest || mcpEndpointRequest ? \"cross-origin\" : \"same-site\",\n );\n if (embedFrameRequest && isMcpEmbedCorsOrigin(requestOrigin)) {\n setResponseHeader(event, \"Access-Control-Allow-Origin\", requestOrigin);\n setResponseHeader(event, \"Vary\", \"Origin\");\n setResponseHeader(\n event,\n \"Access-Control-Allow-Methods\",\n \"GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS\",\n );\n setResponseHeader(\n event,\n \"Access-Control-Allow-Headers\",\n MCP_EMBED_CORS_ALLOW_HEADERS,\n );\n }\n if (isHttpsRequest(event)) {\n setResponseHeader(event, \"Strict-Transport-Security\", HSTS);\n }\n // Continue to the next handler — we only set headers, don't return a body.\n return undefined;\n });\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/settings/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAOtC,wBAAgB,kBAAkB,IAAI,YAAY,CAEjD;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/settings/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAOtC,wBAAgB,kBAAkB,IAAI,YAAY,CAEjD;AA2BD,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAUzC;AAED,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAkBlB;AAED,wBAAsB,cAAc,IAAI,OAAO,CAC7C,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CACxC,CAUA"}
|
package/dist/settings/store.js
CHANGED
|
@@ -20,7 +20,11 @@ async function ensureTable() {
|
|
|
20
20
|
updated_at ${intType()} NOT NULL
|
|
21
21
|
)
|
|
22
22
|
`);
|
|
23
|
-
})()
|
|
23
|
+
})().catch((err) => {
|
|
24
|
+
// Retry init on the next call after a failed startup.
|
|
25
|
+
_initPromise = undefined;
|
|
26
|
+
throw err;
|
|
27
|
+
});
|
|
24
28
|
}
|
|
25
29
|
return _initPromise;
|
|
26
30
|
}
|