@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/settings/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/settings/store.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventEmitter } from \"events\";\nimport { getDbExec, isPostgres, intType } from \"../db/client.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\nconst _emitter = new EventEmitter();\n\nexport function getSettingsEmitter(): EventEmitter {\n return _emitter;\n}\n\nfunction settingsTable(): string {\n return isPostgres() ? \"public.settings\" : \"settings\";\n}\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const table = settingsTable();\n await client.execute(`\n CREATE TABLE IF NOT EXISTS ${table} (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL,\n updated_at ${intType()} NOT NULL\n )\n `);\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nexport async function getSetting(\n key: string,\n): Promise<Record<string, unknown> | null> {\n await ensureTable();\n const client = getDbExec();\n const table = settingsTable();\n const { rows } = await client.execute({\n sql: `SELECT value FROM ${table} WHERE key = ?`,\n args: [key],\n });\n if (rows.length === 0) return null;\n return JSON.parse(rows[0].value as string);\n}\n\nexport interface StoreWriteOptions {\n /** Tag identifying who initiated this write (e.g. a tab ID). */\n requestSource?: string;\n}\n\nexport async function putSetting(\n key: string,\n value: Record<string, unknown>,\n options?: StoreWriteOptions,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const table = settingsTable();\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO ${table} (key, value, updated_at) VALUES (?, ?, ?) ON CONFLICT (key) DO UPDATE SET value=EXCLUDED.value, updated_at=EXCLUDED.updated_at`\n : `INSERT OR REPLACE INTO ${table} (key, value, updated_at) VALUES (?, ?, ?)`,\n args: [key, JSON.stringify(value), Date.now()],\n });\n _emitter.emit(\"settings\", {\n source: \"settings\",\n type: \"change\",\n key,\n ...(options?.requestSource && { requestSource: options.requestSource }),\n });\n}\n\nexport async function deleteSetting(\n key: string,\n options?: StoreWriteOptions,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const table = settingsTable();\n const result = await client.execute({\n sql: `DELETE FROM ${table} WHERE key = ?`,\n args: [key],\n });\n if (result.rowsAffected > 0) {\n _emitter.emit(\"settings\", {\n source: \"settings\",\n type: \"delete\",\n key,\n ...(options?.requestSource && { requestSource: options.requestSource }),\n });\n return true;\n }\n return false;\n}\n\nexport async function getAllSettings(): Promise<\n Record<string, Record<string, unknown>>\n> {\n await ensureTable();\n const client = getDbExec();\n const table = settingsTable();\n const { rows } = await client.execute(`SELECT key, value FROM ${table}`);\n const result: Record<string, Record<string, unknown>> = {};\n for (const row of rows) {\n result[row.key as string] = JSON.parse(row.value as string);\n }\n return result;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/sharing/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAoB,KAAK,GAAG,EAAE,MAAM,aAAa,CAAC;AAUzD,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAmBxD,qBAAa,cAAe,SAAQ,KAAK;IACvC,UAAU,SAAO;gBACL,OAAO,SAAc;CAIlC;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,wBAAgB,aAAa,IAAI,aAAa,CAK7C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAC1B,aAAa,EAAE,GAAG,EAClB,WAAW,EAAE,GAAG,EAChB,GAAG,GAAE,aAA+B,EACpC,OAAO,GAAE,SAAoB,EAC7B,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,GAAG,
|
|
1
|
+
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/sharing/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAoB,KAAK,GAAG,EAAE,MAAM,aAAa,CAAC;AAUzD,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAmBxD,qBAAa,cAAe,SAAQ,KAAK;IACvC,UAAU,SAAO;gBACL,OAAO,SAAc;CAIlC;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,wBAAgB,aAAa,IAAI,aAAa,CAK7C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAC1B,aAAa,EAAE,GAAG,EAClB,WAAW,EAAE,GAAG,EAChB,GAAG,GAAE,aAA+B,EACpC,OAAO,GAAE,SAAoB,EAC7B,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,GAAG,CAwDL;AAsDD,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1B,yCAAyC;IACzC,QAAQ,EAAE,GAAG,CAAC;CACf;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,GAAG,GAAE,aAA+B,GACnC,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAkChC;AA8CD;;;GAGG;AACH,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,SAAS,GAAG,OAAkB,EACvC,GAAG,GAAE,aAA+B,GACnC,OAAO,CAAC,cAAc,CAAC,CAWzB"}
|
package/dist/sharing/access.js
CHANGED
|
@@ -85,17 +85,21 @@ export function accessFilter(resourceTable, sharesTable, ctx = currentAccess(),
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
if (userEmail) {
|
|
88
|
+
const shareScope = restrictedShareScopeSql(reg, resourceTable, ctx);
|
|
88
89
|
clauses.push(sql `exists (select 1 from ${sharesTable}
|
|
89
90
|
where ${sharesTable.resourceId} = ${resourceTable.id}
|
|
90
91
|
and ${sharesTable.principalType} = 'user'
|
|
91
92
|
and ${sharesTable.principalId} = ${userEmail}
|
|
93
|
+
and ${shareScope}
|
|
92
94
|
and ${minRoleSql(minRole)})`);
|
|
93
95
|
}
|
|
94
96
|
if (orgId) {
|
|
97
|
+
const shareScope = restrictedShareScopeSql(reg, resourceTable, ctx);
|
|
95
98
|
clauses.push(sql `exists (select 1 from ${sharesTable}
|
|
96
99
|
where ${sharesTable.resourceId} = ${resourceTable.id}
|
|
97
100
|
and ${sharesTable.principalType} = 'org'
|
|
98
101
|
and ${sharesTable.principalId} = ${orgId}
|
|
102
|
+
and ${shareScope}
|
|
99
103
|
and ${minRoleSql(minRole)})`);
|
|
100
104
|
}
|
|
101
105
|
return or(...clauses) ?? sql `1=0`;
|
|
@@ -125,6 +129,21 @@ function minRoleSql(minRole) {
|
|
|
125
129
|
}
|
|
126
130
|
return sql `role = 'admin'`;
|
|
127
131
|
}
|
|
132
|
+
function restrictedShareScopeSql(reg, resourceTable, ctx) {
|
|
133
|
+
// Restricted resources (extensions) must stay inside their resource org even
|
|
134
|
+
// if stale cross-org share rows already exist from older code or bad data.
|
|
135
|
+
if (reg?.requireOrgMemberForUserShares !== true)
|
|
136
|
+
return sql `1=1`;
|
|
137
|
+
if (!ctx.orgId)
|
|
138
|
+
return sql `1=0`;
|
|
139
|
+
return eq(resourceTable.orgId, ctx.orgId);
|
|
140
|
+
}
|
|
141
|
+
function explicitSharesAllowedForResource(reg, resource, ctx) {
|
|
142
|
+
if (reg.requireOrgMemberForUserShares !== true)
|
|
143
|
+
return true;
|
|
144
|
+
const resourceOrgId = resource?.orgId ?? null;
|
|
145
|
+
return !!resourceOrgId && !!ctx.orgId && resourceOrgId === ctx.orgId;
|
|
146
|
+
}
|
|
128
147
|
/**
|
|
129
148
|
* Return the effective role the current user has on a specific resource, or
|
|
130
149
|
* null if they have no access. Loads the resource and relevant share rows.
|
|
@@ -146,25 +165,27 @@ export async function resolveAccess(resourceType, resourceId, ctx = currentAcces
|
|
|
146
165
|
}
|
|
147
166
|
if (resource.visibility === "public" && reg.allowPublic !== false) {
|
|
148
167
|
// No share row needed; default viewer unless upgraded below.
|
|
149
|
-
const role = await highestShareRole(reg, resourceId, ctx);
|
|
168
|
+
const role = await highestShareRole(reg, resourceId, ctx, resource);
|
|
150
169
|
return { role: role ?? "viewer", resource };
|
|
151
170
|
}
|
|
152
171
|
// `visibility === "public"` on an `allowPublic: false` resource is treated
|
|
153
172
|
// as private: only owner + explicit shares grant access. Falls through to
|
|
154
173
|
// the explicit-share lookup below.
|
|
155
174
|
if (resource.visibility === "org" && orgId && resource.orgId === orgId) {
|
|
156
|
-
const role = await highestShareRole(reg, resourceId, ctx);
|
|
175
|
+
const role = await highestShareRole(reg, resourceId, ctx, resource);
|
|
157
176
|
return { role: role ?? "viewer", resource };
|
|
158
177
|
}
|
|
159
|
-
const role = await highestShareRole(reg, resourceId, ctx);
|
|
178
|
+
const role = await highestShareRole(reg, resourceId, ctx, resource);
|
|
160
179
|
if (role)
|
|
161
180
|
return { role, resource };
|
|
162
181
|
return null;
|
|
163
182
|
}
|
|
164
|
-
async function highestShareRole(reg, resourceId, ctx) {
|
|
183
|
+
async function highestShareRole(reg, resourceId, ctx, resource) {
|
|
165
184
|
const { userEmail, orgId } = ctx;
|
|
166
185
|
if (!userEmail && !orgId)
|
|
167
186
|
return null;
|
|
187
|
+
if (!explicitSharesAllowedForResource(reg, resource, ctx))
|
|
188
|
+
return null;
|
|
168
189
|
const db = reg.getDb();
|
|
169
190
|
const principalClauses = [];
|
|
170
191
|
if (userEmail) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/sharing/access.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Access-control helpers for shareable resources.\n *\n * The access model combines:\n * 1. Direct ownership — `owner_email = currentUser`.\n * 2. Visibility — `'private' | 'org' | 'public'`. `org` grants read to anyone\n * in the same org; `public` grants read to any authenticated user.\n * 3. Share rows — per-user or per-org grants in the `{type}_shares` table\n * with a role (`viewer | editor | admin`).\n *\n * Use `applyAccessFilter()` on list/read queries to filter rows the current\n * user can see. Use `assertAccess()` at the top of write actions to reject\n * callers who lack the required role.\n */\n\nimport { and, eq, or, sql, type SQL } from \"drizzle-orm\";\nimport {\n getRequestUserEmail,\n getRequestOrgId,\n} from \"../server/request-context.js\";\nimport {\n listShareableResources,\n requireShareableResource,\n type ShareableResourceRegistration,\n} from \"./registry.js\";\nimport { ROLE_RANK, type ShareRole } from \"./schema.js\";\n\n/**\n * Find a registration by its drizzle table identity. Used to look up\n * per-resource policy flags (e.g. `allowPublic`) inside `accessFilter`,\n * which receives only the table — not the resource-type name.\n *\n * Identity is stable within a single bundle (Vite dedupes module instances);\n * the SSR/server side is the only caller, so per-bundle identity is fine.\n */\nfunction findRegistrationByTable(\n resourceTable: any,\n): ShareableResourceRegistration | undefined {\n for (const reg of listShareableResources()) {\n if (reg.resourceTable === resourceTable) return reg;\n }\n return undefined;\n}\n\nexport class ForbiddenError extends Error {\n statusCode = 403;\n constructor(message = \"Forbidden\") {\n super(message);\n this.name = \"ForbiddenError\";\n }\n}\n\nexport interface AccessContext {\n userEmail?: string;\n orgId?: string;\n}\n\n/** Current request's access context. Pulls from request-context ALS. */\nexport function currentAccess(): AccessContext {\n return {\n userEmail: getRequestUserEmail(),\n orgId: getRequestOrgId(),\n };\n}\n\n/**\n * Build a Drizzle `WHERE` clause that admits rows the current user can see.\n * Pass the ownable resource table and its shares table; optional min role\n * (defaults to 'viewer') gates which share rows count.\n *\n * `visibility = 'public'` is intentionally NOT admitted by default. Public\n * means \"anyone with the link can view\" (still honoured by `resolveAccess`\n * for read-by-id), not \"appears in every signed-in user's list/sidebar.\"\n * Pass `{ includePublic: true }` for the rare list endpoint that wants\n * cross-user public discovery (a public template gallery, for example).\n *\n * Example:\n *\n * const rows = await db\n * .select()\n * .from(schema.documents)\n * .where(accessFilter(schema.documents, schema.documentShares));\n */\nexport function accessFilter(\n resourceTable: any,\n sharesTable: any,\n ctx: AccessContext = currentAccess(),\n minRole: ShareRole = \"viewer\",\n options: { includePublic?: boolean } = {},\n): SQL {\n const { userEmail, orgId } = ctx;\n // Defense in depth — resources registered with `allowPublic: false` must\n // never participate in cross-user \"public\" discovery, even if a caller\n // accidentally passes `includePublic: true` or if a stale public row sits\n // in the DB.\n const reg = findRegistrationByTable(resourceTable);\n const publicAllowed = reg?.allowPublic !== false;\n const includePublic = (options.includePublic ?? false) && publicAllowed;\n const clauses: SQL[] = [];\n\n if (userEmail) {\n clauses.push(\n and(\n eq(resourceTable.ownerEmail, userEmail),\n ownerScopeFilter(resourceTable, ctx),\n )!,\n );\n }\n if (minRole === \"viewer\") {\n if (includePublic) {\n clauses.push(eq(resourceTable.visibility, \"public\"));\n }\n if (orgId) {\n clauses.push(\n and(\n eq(resourceTable.visibility, \"org\"),\n eq(resourceTable.orgId, orgId),\n )!,\n );\n }\n }\n if (userEmail) {\n clauses.push(\n sql`exists (select 1 from ${sharesTable}\n where ${sharesTable.resourceId} = ${resourceTable.id}\n and ${sharesTable.principalType} = 'user'\n and ${sharesTable.principalId} = ${userEmail}\n and ${minRoleSql(minRole)})`,\n );\n }\n if (orgId) {\n clauses.push(\n sql`exists (select 1 from ${sharesTable}\n where ${sharesTable.resourceId} = ${resourceTable.id}\n and ${sharesTable.principalType} = 'org'\n and ${sharesTable.principalId} = ${orgId}\n and ${minRoleSql(minRole)})`,\n );\n }\n\n return or(...clauses) ?? sql`1=0`;\n}\n\nfunction ownerScopeFilter(resourceTable: any, ctx: AccessContext): SQL {\n if (ctx.orgId) {\n // Rows created before org-scoping, or in solo mode, have no org_id. Keep\n // them manageable by their owner after the owner joins or switches into an\n // organization, while still keeping rows from other orgs out of scope.\n return or(\n eq(resourceTable.orgId, ctx.orgId),\n sql`${resourceTable.orgId} IS NULL`,\n )!;\n }\n return sql`${resourceTable.orgId} IS NULL`;\n}\n\nfunction ownerMatchesActiveScope(resource: any, ctx: AccessContext): boolean {\n const resourceOrgId = resource?.orgId ?? null;\n if (!resourceOrgId) return true;\n return ctx.orgId === resourceOrgId;\n}\n\nfunction minRoleSql(minRole: ShareRole): SQL {\n if (minRole === \"viewer\") {\n // any role satisfies viewer\n return sql`1=1`;\n }\n if (minRole === \"editor\") {\n return sql`role in ('editor','admin')`;\n }\n return sql`role = 'admin'`;\n}\n\nexport interface ResolvedAccess {\n /** Effective role: 'owner' for the resource owner, or the share role. */\n role: \"owner\" | ShareRole;\n /** The resource row (already loaded). */\n resource: any;\n}\n\n/**\n * Return the effective role the current user has on a specific resource, or\n * null if they have no access. Loads the resource and relevant share rows.\n */\nexport async function resolveAccess(\n resourceType: string,\n resourceId: string,\n ctx: AccessContext = currentAccess(),\n): Promise<ResolvedAccess | null> {\n const reg = requireShareableResource(resourceType);\n const db = reg.getDb() as any;\n\n const [resource] = await db\n .select()\n .from(reg.resourceTable)\n .where(eq(reg.resourceTable.id, resourceId));\n if (!resource) return null;\n\n const { userEmail, orgId } = ctx;\n\n if (\n userEmail &&\n resource.ownerEmail === userEmail &&\n ownerMatchesActiveScope(resource, ctx)\n ) {\n return { role: \"owner\", resource };\n }\n if (resource.visibility === \"public\" && reg.allowPublic !== false) {\n // No share row needed; default viewer unless upgraded below.\n const role = await highestShareRole(reg, resourceId, ctx);\n return { role: role ?? \"viewer\", resource };\n }\n // `visibility === \"public\"` on an `allowPublic: false` resource is treated\n // as private: only owner + explicit shares grant access. Falls through to\n // the explicit-share lookup below.\n if (resource.visibility === \"org\" && orgId && resource.orgId === orgId) {\n const role = await highestShareRole(reg, resourceId, ctx);\n return { role: role ?? \"viewer\", resource };\n }\n const role = await highestShareRole(reg, resourceId, ctx);\n if (role) return { role, resource };\n return null;\n}\n\nasync function highestShareRole(\n reg: ShareableResourceRegistration,\n resourceId: string,\n ctx: AccessContext,\n): Promise<ShareRole | null> {\n const { userEmail, orgId } = ctx;\n if (!userEmail && !orgId) return null;\n const db = reg.getDb() as any;\n\n const principalClauses: ReturnType<typeof and>[] = [];\n if (userEmail) {\n principalClauses.push(\n and(\n eq(reg.sharesTable.principalType, \"user\"),\n eq(reg.sharesTable.principalId, userEmail),\n ),\n );\n }\n if (orgId) {\n principalClauses.push(\n and(\n eq(reg.sharesTable.principalType, \"org\"),\n eq(reg.sharesTable.principalId, orgId),\n ),\n );\n }\n\n const rows = await db\n .select({ role: reg.sharesTable.role })\n .from(reg.sharesTable)\n .where(\n and(eq(reg.sharesTable.resourceId, resourceId), or(...principalClauses)),\n )\n .limit(10);\n\n let best: ShareRole | null = null;\n for (const r of rows as Array<{ role: ShareRole }>) {\n if (!best || ROLE_RANK[r.role] > ROLE_RANK[best]) best = r.role;\n }\n return best;\n}\n\n/**\n * Throw ForbiddenError if the current user can't act on this resource with at\n * least the given role. Used at the top of update/delete actions.\n */\nexport async function assertAccess(\n resourceType: string,\n resourceId: string,\n minRole: ShareRole | \"owner\" = \"viewer\",\n ctx: AccessContext = currentAccess(),\n): Promise<ResolvedAccess> {\n const access = await resolveAccess(resourceType, resourceId, ctx);\n if (!access) {\n throw new ForbiddenError(`No access to ${resourceType} ${resourceId}`);\n }\n if (ROLE_RANK[access.role] < ROLE_RANK[minRole]) {\n throw new ForbiddenError(\n `Requires ${minRole} role on ${resourceType} ${resourceId} (have ${access.role})`,\n );\n }\n return access;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/sharing/access.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Access-control helpers for shareable resources.\n *\n * The access model combines:\n * 1. Direct ownership — `owner_email = currentUser`.\n * 2. Visibility — `'private' | 'org' | 'public'`. `org` grants read to anyone\n * in the same org; `public` grants read to any authenticated user.\n * 3. Share rows — per-user or per-org grants in the `{type}_shares` table\n * with a role (`viewer | editor | admin`).\n *\n * Use `applyAccessFilter()` on list/read queries to filter rows the current\n * user can see. Use `assertAccess()` at the top of write actions to reject\n * callers who lack the required role.\n */\n\nimport { and, eq, or, sql, type SQL } from \"drizzle-orm\";\nimport {\n getRequestUserEmail,\n getRequestOrgId,\n} from \"../server/request-context.js\";\nimport {\n listShareableResources,\n requireShareableResource,\n type ShareableResourceRegistration,\n} from \"./registry.js\";\nimport { ROLE_RANK, type ShareRole } from \"./schema.js\";\n\n/**\n * Find a registration by its drizzle table identity. Used to look up\n * per-resource policy flags (e.g. `allowPublic`) inside `accessFilter`,\n * which receives only the table — not the resource-type name.\n *\n * Identity is stable within a single bundle (Vite dedupes module instances);\n * the SSR/server side is the only caller, so per-bundle identity is fine.\n */\nfunction findRegistrationByTable(\n resourceTable: any,\n): ShareableResourceRegistration | undefined {\n for (const reg of listShareableResources()) {\n if (reg.resourceTable === resourceTable) return reg;\n }\n return undefined;\n}\n\nexport class ForbiddenError extends Error {\n statusCode = 403;\n constructor(message = \"Forbidden\") {\n super(message);\n this.name = \"ForbiddenError\";\n }\n}\n\nexport interface AccessContext {\n userEmail?: string;\n orgId?: string;\n}\n\n/** Current request's access context. Pulls from request-context ALS. */\nexport function currentAccess(): AccessContext {\n return {\n userEmail: getRequestUserEmail(),\n orgId: getRequestOrgId(),\n };\n}\n\n/**\n * Build a Drizzle `WHERE` clause that admits rows the current user can see.\n * Pass the ownable resource table and its shares table; optional min role\n * (defaults to 'viewer') gates which share rows count.\n *\n * `visibility = 'public'` is intentionally NOT admitted by default. Public\n * means \"anyone with the link can view\" (still honoured by `resolveAccess`\n * for read-by-id), not \"appears in every signed-in user's list/sidebar.\"\n * Pass `{ includePublic: true }` for the rare list endpoint that wants\n * cross-user public discovery (a public template gallery, for example).\n *\n * Example:\n *\n * const rows = await db\n * .select()\n * .from(schema.documents)\n * .where(accessFilter(schema.documents, schema.documentShares));\n */\nexport function accessFilter(\n resourceTable: any,\n sharesTable: any,\n ctx: AccessContext = currentAccess(),\n minRole: ShareRole = \"viewer\",\n options: { includePublic?: boolean } = {},\n): SQL {\n const { userEmail, orgId } = ctx;\n // Defense in depth — resources registered with `allowPublic: false` must\n // never participate in cross-user \"public\" discovery, even if a caller\n // accidentally passes `includePublic: true` or if a stale public row sits\n // in the DB.\n const reg = findRegistrationByTable(resourceTable);\n const publicAllowed = reg?.allowPublic !== false;\n const includePublic = (options.includePublic ?? false) && publicAllowed;\n const clauses: SQL[] = [];\n\n if (userEmail) {\n clauses.push(\n and(\n eq(resourceTable.ownerEmail, userEmail),\n ownerScopeFilter(resourceTable, ctx),\n )!,\n );\n }\n if (minRole === \"viewer\") {\n if (includePublic) {\n clauses.push(eq(resourceTable.visibility, \"public\"));\n }\n if (orgId) {\n clauses.push(\n and(\n eq(resourceTable.visibility, \"org\"),\n eq(resourceTable.orgId, orgId),\n )!,\n );\n }\n }\n if (userEmail) {\n const shareScope = restrictedShareScopeSql(reg, resourceTable, ctx);\n clauses.push(\n sql`exists (select 1 from ${sharesTable}\n where ${sharesTable.resourceId} = ${resourceTable.id}\n and ${sharesTable.principalType} = 'user'\n and ${sharesTable.principalId} = ${userEmail}\n and ${shareScope}\n and ${minRoleSql(minRole)})`,\n );\n }\n if (orgId) {\n const shareScope = restrictedShareScopeSql(reg, resourceTable, ctx);\n clauses.push(\n sql`exists (select 1 from ${sharesTable}\n where ${sharesTable.resourceId} = ${resourceTable.id}\n and ${sharesTable.principalType} = 'org'\n and ${sharesTable.principalId} = ${orgId}\n and ${shareScope}\n and ${minRoleSql(minRole)})`,\n );\n }\n\n return or(...clauses) ?? sql`1=0`;\n}\n\nfunction ownerScopeFilter(resourceTable: any, ctx: AccessContext): SQL {\n if (ctx.orgId) {\n // Rows created before org-scoping, or in solo mode, have no org_id. Keep\n // them manageable by their owner after the owner joins or switches into an\n // organization, while still keeping rows from other orgs out of scope.\n return or(\n eq(resourceTable.orgId, ctx.orgId),\n sql`${resourceTable.orgId} IS NULL`,\n )!;\n }\n return sql`${resourceTable.orgId} IS NULL`;\n}\n\nfunction ownerMatchesActiveScope(resource: any, ctx: AccessContext): boolean {\n const resourceOrgId = resource?.orgId ?? null;\n if (!resourceOrgId) return true;\n return ctx.orgId === resourceOrgId;\n}\n\nfunction minRoleSql(minRole: ShareRole): SQL {\n if (minRole === \"viewer\") {\n // any role satisfies viewer\n return sql`1=1`;\n }\n if (minRole === \"editor\") {\n return sql`role in ('editor','admin')`;\n }\n return sql`role = 'admin'`;\n}\n\nfunction restrictedShareScopeSql(\n reg: ShareableResourceRegistration | undefined,\n resourceTable: any,\n ctx: AccessContext,\n): SQL {\n // Restricted resources (extensions) must stay inside their resource org even\n // if stale cross-org share rows already exist from older code or bad data.\n if (reg?.requireOrgMemberForUserShares !== true) return sql`1=1`;\n if (!ctx.orgId) return sql`1=0`;\n return eq(resourceTable.orgId, ctx.orgId);\n}\n\nfunction explicitSharesAllowedForResource(\n reg: ShareableResourceRegistration,\n resource: any,\n ctx: AccessContext,\n): boolean {\n if (reg.requireOrgMemberForUserShares !== true) return true;\n const resourceOrgId = resource?.orgId ?? null;\n return !!resourceOrgId && !!ctx.orgId && resourceOrgId === ctx.orgId;\n}\n\nexport interface ResolvedAccess {\n /** Effective role: 'owner' for the resource owner, or the share role. */\n role: \"owner\" | ShareRole;\n /** The resource row (already loaded). */\n resource: any;\n}\n\n/**\n * Return the effective role the current user has on a specific resource, or\n * null if they have no access. Loads the resource and relevant share rows.\n */\nexport async function resolveAccess(\n resourceType: string,\n resourceId: string,\n ctx: AccessContext = currentAccess(),\n): Promise<ResolvedAccess | null> {\n const reg = requireShareableResource(resourceType);\n const db = reg.getDb() as any;\n\n const [resource] = await db\n .select()\n .from(reg.resourceTable)\n .where(eq(reg.resourceTable.id, resourceId));\n if (!resource) return null;\n\n const { userEmail, orgId } = ctx;\n\n if (\n userEmail &&\n resource.ownerEmail === userEmail &&\n ownerMatchesActiveScope(resource, ctx)\n ) {\n return { role: \"owner\", resource };\n }\n if (resource.visibility === \"public\" && reg.allowPublic !== false) {\n // No share row needed; default viewer unless upgraded below.\n const role = await highestShareRole(reg, resourceId, ctx, resource);\n return { role: role ?? \"viewer\", resource };\n }\n // `visibility === \"public\"` on an `allowPublic: false` resource is treated\n // as private: only owner + explicit shares grant access. Falls through to\n // the explicit-share lookup below.\n if (resource.visibility === \"org\" && orgId && resource.orgId === orgId) {\n const role = await highestShareRole(reg, resourceId, ctx, resource);\n return { role: role ?? \"viewer\", resource };\n }\n const role = await highestShareRole(reg, resourceId, ctx, resource);\n if (role) return { role, resource };\n return null;\n}\n\nasync function highestShareRole(\n reg: ShareableResourceRegistration,\n resourceId: string,\n ctx: AccessContext,\n resource: any,\n): Promise<ShareRole | null> {\n const { userEmail, orgId } = ctx;\n if (!userEmail && !orgId) return null;\n if (!explicitSharesAllowedForResource(reg, resource, ctx)) return null;\n const db = reg.getDb() as any;\n\n const principalClauses: ReturnType<typeof and>[] = [];\n if (userEmail) {\n principalClauses.push(\n and(\n eq(reg.sharesTable.principalType, \"user\"),\n eq(reg.sharesTable.principalId, userEmail),\n ),\n );\n }\n if (orgId) {\n principalClauses.push(\n and(\n eq(reg.sharesTable.principalType, \"org\"),\n eq(reg.sharesTable.principalId, orgId),\n ),\n );\n }\n\n const rows = await db\n .select({ role: reg.sharesTable.role })\n .from(reg.sharesTable)\n .where(\n and(eq(reg.sharesTable.resourceId, resourceId), or(...principalClauses)),\n )\n .limit(10);\n\n let best: ShareRole | null = null;\n for (const r of rows as Array<{ role: ShareRole }>) {\n if (!best || ROLE_RANK[r.role] > ROLE_RANK[best]) best = r.role;\n }\n return best;\n}\n\n/**\n * Throw ForbiddenError if the current user can't act on this resource with at\n * least the given role. Used at the top of update/delete actions.\n */\nexport async function assertAccess(\n resourceType: string,\n resourceId: string,\n minRole: ShareRole | \"owner\" = \"viewer\",\n ctx: AccessContext = currentAccess(),\n): Promise<ResolvedAccess> {\n const access = await resolveAccess(resourceType, resourceId, ctx);\n if (!access) {\n throw new ForbiddenError(`No access to ${resourceType} ${resourceId}`);\n }\n if (ROLE_RANK[access.role] < ROLE_RANK[minRole]) {\n throw new ForbiddenError(\n `Requires ${minRole} role on ${resourceType} ${resourceId} (have ${access.role})`,\n );\n }\n return access;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set-resource-visibility.d.ts","sourceRoot":"","sources":["../../../src/sharing/actions/set-resource-visibility.ts"],"names":[],"mappings":";AAWA,
|
|
1
|
+
{"version":3,"file":"set-resource-visibility.d.ts","sourceRoot":"","sources":["../../../src/sharing/actions/set-resource-visibility.ts"],"names":[],"mappings":";AAWA,wBAqDG"}
|
|
@@ -25,7 +25,14 @@ export default defineAction({
|
|
|
25
25
|
const db = reg.getDb();
|
|
26
26
|
const update = { visibility: args.visibility };
|
|
27
27
|
const currentOrgId = getRequestOrgId();
|
|
28
|
-
|
|
28
|
+
// Only the resource owner may bind an org to a previously unscoped resource.
|
|
29
|
+
// If a non-owner admin did this, the resource would adopt the admin's org
|
|
30
|
+
// and ownerMatchesActiveScope would then lock the real owner out of their
|
|
31
|
+
// own resource. Non-owner admins can still flip visibility once orgId is set.
|
|
32
|
+
if (args.visibility === "org" &&
|
|
33
|
+
currentOrgId &&
|
|
34
|
+
!access.resource?.orgId &&
|
|
35
|
+
access.role === "owner") {
|
|
29
36
|
update.orgId = currentOrgId;
|
|
30
37
|
}
|
|
31
38
|
await db
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set-resource-visibility.js","sourceRoot":"","sources":["../../../src/sharing/actions/set-resource-visibility.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EACL,8BAA8B,EAC9B,2BAA2B,GAC5B,MAAM,uBAAuB,CAAC;AAE/B,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,mHAAmH;IACrH,sEAAsE;IACtE,oEAAoE;IACpE,YAAY,EAAE,KAAK;IACnB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;QACxB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;QACtB,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;KACjD,CAAC;IACF,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAClB,MAAM,GAAG,GAAG,wBAAwB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACxD,IAAI,IAAI,CAAC,UAAU,KAAK,QAAQ,IAAI,GAAG,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;YAC9D,MAAM,IAAI,cAAc,CACtB,GAAG,GAAG,CAAC,WAAW,mFAAmF,CACtG,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,YAAY,CAC/B,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,UAAU,EACf,OAAO,CACR,CAAC;QACF,MAAM,sBAAsB,GAAG,MAAM,8BAA8B,CACjE,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,UAAU,CAChB,CAAC;QACF,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,EAAS,CAAC;QAC9B,MAAM,MAAM,GAA4B,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;QACxE,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;QACvC,
|
|
1
|
+
{"version":3,"file":"set-resource-visibility.js","sourceRoot":"","sources":["../../../src/sharing/actions/set-resource-visibility.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EACL,8BAA8B,EAC9B,2BAA2B,GAC5B,MAAM,uBAAuB,CAAC;AAE/B,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,mHAAmH;IACrH,sEAAsE;IACtE,oEAAoE;IACpE,YAAY,EAAE,KAAK;IACnB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;QACxB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;QACtB,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;KACjD,CAAC;IACF,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAClB,MAAM,GAAG,GAAG,wBAAwB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACxD,IAAI,IAAI,CAAC,UAAU,KAAK,QAAQ,IAAI,GAAG,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;YAC9D,MAAM,IAAI,cAAc,CACtB,GAAG,GAAG,CAAC,WAAW,mFAAmF,CACtG,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,YAAY,CAC/B,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,UAAU,EACf,OAAO,CACR,CAAC;QACF,MAAM,sBAAsB,GAAG,MAAM,8BAA8B,CACjE,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,UAAU,CAChB,CAAC;QACF,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,EAAS,CAAC;QAC9B,MAAM,MAAM,GAA4B,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;QACxE,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;QACvC,6EAA6E;QAC7E,0EAA0E;QAC1E,0EAA0E;QAC1E,8EAA8E;QAC9E,IACE,IAAI,CAAC,UAAU,KAAK,KAAK;YACzB,YAAY;YACZ,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK;YACvB,MAAM,CAAC,IAAI,KAAK,OAAO,EACvB,CAAC;YACD,MAAM,CAAC,KAAK,GAAG,YAAY,CAAC;QAC9B,CAAC;QACD,MAAM,EAAE;aACL,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC;aACzB,GAAG,CAAC,MAAM,CAAC;aACX,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QACpD,MAAM,2BAA2B,CAC/B,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,UAAU,EACf,sBAAsB,CACvB,CAAC;QACF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;IACnD,CAAC;CACF,CAAC,CAAC","sourcesContent":["import { eq } from \"drizzle-orm\";\nimport { z } from \"zod\";\nimport { defineAction } from \"../../action.js\";\nimport { getRequestOrgId } from \"../../server/request-context.js\";\nimport { assertAccess, ForbiddenError } from \"../access.js\";\nimport { requireShareableResource } from \"../registry.js\";\nimport {\n getExtensionShareChangeTargets,\n notifyExtensionShareChanged,\n} from \"./extension-change.js\";\n\nexport default defineAction({\n description:\n \"Change the coarse visibility of a shareable resource: 'private' | 'org' | 'public'. Owner or admin role required.\",\n // (audit H5) Visibility changes are admin-tier and can flip a private\n // resource org-wide or public. Refuse from the tools iframe bridge.\n toolCallable: false,\n schema: z.object({\n resourceType: z.string(),\n resourceId: z.string(),\n visibility: z.enum([\"private\", \"org\", \"public\"]),\n }),\n run: async (args) => {\n const reg = requireShareableResource(args.resourceType);\n if (args.visibility === \"public\" && reg.allowPublic === false) {\n throw new ForbiddenError(\n `${reg.displayName} cannot be made public — share with specific people or your organization instead.`,\n );\n }\n const access = await assertAccess(\n args.resourceType,\n args.resourceId,\n \"admin\",\n );\n const beforeExtensionTargets = await getExtensionShareChangeTargets(\n args.resourceType,\n args.resourceId,\n );\n const db = reg.getDb() as any;\n const update: Record<string, unknown> = { visibility: args.visibility };\n const currentOrgId = getRequestOrgId();\n // Only the resource owner may bind an org to a previously unscoped resource.\n // If a non-owner admin did this, the resource would adopt the admin's org\n // and ownerMatchesActiveScope would then lock the real owner out of their\n // own resource. Non-owner admins can still flip visibility once orgId is set.\n if (\n args.visibility === \"org\" &&\n currentOrgId &&\n !access.resource?.orgId &&\n access.role === \"owner\"\n ) {\n update.orgId = currentOrgId;\n }\n await db\n .update(reg.resourceTable)\n .set(update)\n .where(eq(reg.resourceTable.id, args.resourceId));\n await notifyExtensionShareChanged(\n args.resourceType,\n args.resourceId,\n beforeExtensionTargets,\n );\n return { ok: true, visibility: args.visibility };\n },\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/triggers/actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/triggers/actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAgNhE,wBAAgB,2BAA2B,CACzC,cAAc,EAAE,MAAM,MAAM,GAC3B,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAuG7B"}
|
package/dist/triggers/actions.js
CHANGED
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { listEvents } from "../event-bus/index.js";
|
|
12
12
|
import { resourceListAllOwners, resourcePut, resourceDelete, resourceGetByPath, SHARED_OWNER, } from "../resources/store.js";
|
|
13
|
-
import { parseTriggerFrontmatter, buildTriggerContent } from "./dispatcher.js";
|
|
14
|
-
import { refreshEventSubscriptions } from "./dispatcher.js";
|
|
13
|
+
import { parseTriggerFrontmatter, buildTriggerContent, refreshEventSubscriptions, } from "./dispatcher.js";
|
|
15
14
|
/* ------------------------------------------------------------------ */
|
|
16
15
|
/* Individual action handlers */
|
|
17
16
|
/* ------------------------------------------------------------------ */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/triggers/actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EACL,qBAAqB,EACrB,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,YAAY,GACb,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAG5D,wEAAwE;AACxE,wEAAwE;AACxE,wEAAwE;AAExE,KAAK,UAAU,gBAAgB;IAC7B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,gGAAgG,CAAC;IAC1G,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAC7B,IAAI,SAAS,GAAG,EAAE,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,CAAC,CAAC,aAAoB,CAAC;YACjC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;gBACxB,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBAC7C,SAAS,GAAG,YAAY,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9C,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO;YACvB,CAAC,CAAC,gBAAgB,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE;YAC7C,CAAC,CAAC,EAAE,CAAC;QACP,OAAO,OAAO,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,WAAW,GAAG,SAAS,GAAG,OAAO,EAAE,CAAC;IACnE,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,KAAK,UAAU,UAAU,CACvB,IAA4B,EAC5B,cAA4B;IAE5B,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAC/B,MAAM,SAAS,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,SAAS;SACvB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,IAAI,CAAC,CAAC,KAAK,KAAK,YAAY,CAAC;SAC5D,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;SACrC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,uBAAuB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC1D,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAChE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;IACxD,CAAC,CAAC;SACD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACZ,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC/D,IAAI,IAAI,CAAC,YAAY,KAAK,MAAM,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAClE,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;IAEL,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,uBAAuB,CAAC;IAE1D,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAC/B,MAAM,IAAI,GACR,CAAC,CAAC,IAAI,CAAC,WAAW,KAAK,OAAO;YAC5B,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,GAAG,EAAE;YAC7B,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC;QACvD,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5E,MAAM,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS;YAChC,CAAC,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG;YACxC,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1D,OAAO,OAAO,CAAC,CAAC,IAAI,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,GAAG,UAAU,IAAI,SAAS,aAAa,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACvK,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED,KAAK,UAAU,YAAY,CACzB,IAA4B,EAC5B,cAA4B;IAE5B,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IAC3D,IAAI,CAAC,IAAI;QAAE,OAAO,+CAA+C,CAAC;IAElE,MAAM,IAAI,GAAG,QAAQ,IAAI,KAAK,CAAC;IAE/B,6BAA6B;IAC7B,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACtD,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,+BAA+B,IAAI,0EAA0E,CAAC;IACvH,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC;IAC5E,MAAM,IAAI,GAAuB;QAC/B,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE;QAC7B,OAAO,EAAE,IAAI;QACb,WAAW;QACX,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,SAAS;QAC9B,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,SAAS;QACtC,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS;QACjE,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,SAAS;QAChC,SAAS,EAAE,KAAK;QAChB,KAAK,EAAE,SAAS;KACjB,CAAC;IAEF,MAAM,OAAO,GAAG,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAC3D,MAAM,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAExC,uEAAuE;IACvE,MAAM,yBAAyB,EAAE,CAAC;IAElC,MAAM,OAAO,GACX,WAAW,KAAK,OAAO;QACrB,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAC/E,CAAC,CAAC,gBAAgB,IAAI,CAAC,QAAQ,GAAG,CAAC;IAEvC,OAAO,eAAe,IAAI,oBAAoB,OAAO,OAAO,IAAI,CAAC,IAAI,QAAQ,CAAC;AAChF,CAAC;AAED,KAAK,UAAU,YAAY,CACzB,IAA4B,EAC5B,cAA4B;IAE5B,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACvB,MAAM,IAAI,GAAG,QAAQ,IAAI,KAAK,CAAC;IAE/B,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACtD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,eAAe,IAAI,oCAAoC,CAAC;IACjE,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,uBAAuB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAEjE,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAC/B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,KAAK,OAAO,CAAC;IAC1C,CAAC;IACD,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QACjC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC;IAC/C,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;IAElC,MAAM,WAAW,CACf,QAAQ,CAAC,KAAK,EACd,QAAQ,CAAC,IAAI,EACb,mBAAmB,CAAC,IAAI,EAAE,OAAO,CAAC,CACnC,CAAC;IACF,MAAM,yBAAyB,EAAE,CAAC;IAElC,OAAO,eAAe,IAAI,YAAY,CAAC;AACzC,CAAC;AAED,KAAK,UAAU,YAAY,CACzB,IAA4B,EAC5B,cAA4B;IAE5B,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,QAAQ,IAAI,CAAC,IAAI,KAAK,CAAC;IAEpC,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACtD,IAAI,CAAC,QAAQ;QAAE,OAAO,eAAe,IAAI,CAAC,IAAI,cAAc,CAAC;IAE7D,MAAM,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAClC,OAAO,eAAe,IAAI,CAAC,IAAI,YAAY,CAAC;AAC9C,CAAC;AAED,KAAK,UAAU,cAAc,CAC3B,IAA4B,EAC5B,cAA4B;IAE5B,kEAAkE;IAClE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,uBAAuB,CAAC,CAAC;IAEvD,IAAI,IAAI,GAA4B,EAAE,CAAC;IACvC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,IAAI,CAAC;YACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,wCAAwC,CAAC;QAClD,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,4DAA4D;IAC5D,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAC/B,IAAI,CAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IAC9C,OAAO,kCAAkC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,CAAC,uEAAuE,CAAC;AAC3I,CAAC;AAED,wEAAwE;AACxE,wEAAwE;AACxE,wEAAwE;AAExE,MAAM,aAAa,GAAG;IACpB,aAAa;IACb,MAAM;IACN,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,WAAW;CACH,CAAC;AAEX,MAAM,UAAU,2BAA2B,CACzC,cAA4B;IAE5B,OAAO;QACL,oBAAoB,EAAE;YACpB,IAAI,EAAE;gBACJ,WAAW,EAAE;;;;;;;gIAO2G;gBACxH,UAAU,EAAE;oBACV,IAAI,EAAE,QAAiB;oBACvB,UAAU,EAAE;wBACV,MAAM,EAAE;4BACN,IAAI,EAAE,QAAQ;4BACd,WAAW,EACT,oFAAoF;4BACtF,IAAI,EAAE,CAAC,GAAG,aAAa,CAAC;yBACzB;wBACD,IAAI,EAAE;4BACJ,IAAI,EAAE,QAAQ;4BACd,WAAW,EACT,wFAAwF;yBAC3F;wBACD,YAAY,EAAE;4BACZ,IAAI,EAAE,QAAQ;4BACd,WAAW,EAAE,6CAA6C;4BAC1D,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC;yBAC5B;wBACD,KAAK,EAAE;4BACL,IAAI,EAAE,QAAQ;4BACd,WAAW,EACT,iHAAiH;yBACpH;wBACD,QAAQ,EAAE;4BACR,IAAI,EAAE,QAAQ;4BACd,WAAW,EACT,gFAAgF;yBACnF;wBACD,SAAS,EAAE;4BACT,IAAI,EAAE,QAAQ;4BACd,WAAW,EACT,wIAAwI;yBAC3I;wBACD,IAAI,EAAE;4BACJ,IAAI,EAAE,QAAQ;4BACd,WAAW,EACT,qGAAqG;4BACvG,IAAI,EAAE,CAAC,SAAS,EAAE,eAAe,CAAC;yBACnC;wBACD,MAAM,EAAE;4BACN,IAAI,EAAE,QAAQ;4BACd,WAAW,EACT,iFAAiF;yBACpF;wBACD,IAAI,EAAE;4BACJ,IAAI,EAAE,QAAQ;4BACd,WAAW,EACT,yJAAyJ;yBAC5J;wBACD,OAAO,EAAE;4BACP,IAAI,EAAE,QAAQ;4BACd,WAAW,EACT,sDAAsD;yBACzD;wBACD,YAAY,EAAE;4BACZ,IAAI,EAAE,QAAQ;4BACd,WAAW,EACT,wDAAwD;yBAC3D;wBACD,IAAI,EAAE;4BACJ,IAAI,EAAE,QAAQ;4BACd,WAAW,EACT,gHAAgH;yBACnH;qBACF;oBACD,QAAQ,EAAE,CAAC,QAAQ,CAAC;iBACrB;aACF;YACD,GAAG,EAAE,KAAK,EAAE,IAA4B,EAAE,EAAE;gBAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;gBAE3B,QAAQ,MAAM,EAAE,CAAC;oBACf,KAAK,aAAa;wBAChB,OAAO,gBAAgB,EAAE,CAAC;oBAC5B,KAAK,MAAM;wBACT,OAAO,UAAU,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;oBAC1C,KAAK,QAAQ;wBACX,OAAO,YAAY,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;oBAC5C,KAAK,QAAQ;wBACX,OAAO,YAAY,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;oBAC5C,KAAK,QAAQ;wBACX,OAAO,YAAY,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;oBAC5C,KAAK,WAAW;wBACd,OAAO,cAAc,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;oBAC9C;wBACE,OAAO,0BAA0B,MAAM,qBAAqB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;gBAC5F,CAAC;YACH,CAAC;SACF;KACF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Framework-level agent actions for the automations system.\n *\n * These are registered as native tools (not template actions) so they're\n * available in every template. The agent uses them to create, list, and\n * manage automations from chat.\n *\n * All six operations are consolidated into a single `manage-automations` tool\n * with an `action` discriminator to keep the tool registry compact.\n */\n\nimport type { ActionEntry } from \"../agent/production-agent.js\";\nimport { listEvents } from \"../event-bus/index.js\";\nimport {\n resourceListAllOwners,\n resourcePut,\n resourceDelete,\n resourceGetByPath,\n SHARED_OWNER,\n} from \"../resources/store.js\";\nimport { parseTriggerFrontmatter, buildTriggerContent } from \"./dispatcher.js\";\nimport { refreshEventSubscriptions } from \"./dispatcher.js\";\nimport type { TriggerFrontmatter } from \"./types.js\";\n\n/* ------------------------------------------------------------------ */\n/* Individual action handlers */\n/* ------------------------------------------------------------------ */\n\nasync function handleListEvents(): Promise<string> {\n const events = listEvents();\n if (events.length === 0) {\n return \"No events registered yet. Events are registered by integrations (mail, calendar, clips, etc.).\";\n }\n const lines = events.map((e) => {\n let schemaStr = \"\";\n try {\n const s = e.payloadSchema as any;\n if (s?._zod?.def?.shape) {\n const fields = Object.keys(s._zod.def.shape);\n schemaStr = ` Fields: ${fields.join(\", \")}`;\n }\n } catch {\n // ignore\n }\n const example = e.example\n ? `\\n Example: ${JSON.stringify(e.example)}`\n : \"\";\n return `- **${e.name}**: ${e.description}${schemaStr}${example}`;\n });\n return lines.join(\"\\n\");\n}\n\nasync function handleList(\n args: Record<string, string>,\n getCurrentUser: () => string,\n): Promise<string> {\n const owner = getCurrentUser();\n const resources = await resourceListAllOwners(\"jobs/\");\n const triggers = resources\n .filter((r) => r.owner === owner || r.owner === SHARED_OWNER)\n .filter((r) => r.path.endsWith(\".md\"))\n .map((r) => {\n const { meta, body } = parseTriggerFrontmatter(r.content);\n const name = r.path.replace(/^jobs\\//, \"\").replace(/\\.md$/, \"\");\n return { name, meta, body, owner: r.owner, id: r.id };\n })\n .filter((t) => {\n if (args.domain && t.meta.domain !== args.domain) return false;\n if (args.enabled_only === \"true\" && !t.meta.enabled) return false;\n return true;\n });\n\n if (triggers.length === 0) return \"No automations found.\";\n\n const lines = triggers.map((t) => {\n const type =\n t.meta.triggerType === \"event\"\n ? `on ${t.meta.event || \"?\"}`\n : `cron: ${t.meta.schedule}`;\n const status = t.meta.enabled ? \"enabled\" : \"disabled\";\n const lastStatus = t.meta.lastStatus ? ` (last: ${t.meta.lastStatus})` : \"\";\n const condition = t.meta.condition\n ? `\\n Condition: \"${t.meta.condition}\"`\n : \"\";\n const domain = t.meta.domain ? ` [${t.meta.domain}]` : \"\";\n return `- **${t.name}**${domain}: ${type} → ${t.meta.mode} (${status}${lastStatus})${condition}\\n Body: ${t.body.slice(0, 100)}${t.body.length > 100 ? \"...\" : \"\"}`;\n });\n return lines.join(\"\\n\\n\");\n}\n\nasync function handleDefine(\n args: Record<string, string>,\n getCurrentUser: () => string,\n): Promise<string> {\n const owner = getCurrentUser();\n const name = (args.name || \"\").replace(/[^a-z0-9-]/g, \"-\");\n if (!name) return \"Error: name is required (lowercase, hyphens).\";\n\n const path = `jobs/${name}.md`;\n\n // Check if it already exists\n const existing = await resourceGetByPath(owner, path);\n if (existing) {\n return `Error: An automation named \"${name}\" already exists. Use a different name or delete the existing one first.`;\n }\n\n const triggerType = args.trigger_type === \"schedule\" ? \"schedule\" : \"event\";\n const meta: TriggerFrontmatter = {\n schedule: args.schedule || \"\",\n enabled: true,\n triggerType,\n event: args.event || undefined,\n condition: args.condition || undefined,\n mode: args.mode === \"deterministic\" ? \"deterministic\" : \"agentic\",\n domain: args.domain || undefined,\n createdBy: owner,\n runAs: \"creator\",\n };\n\n const content = buildTriggerContent(meta, args.body || \"\");\n await resourcePut(owner, path, content);\n\n // Refresh event subscriptions so the new trigger is active immediately\n await refreshEventSubscriptions();\n\n const summary =\n triggerType === \"event\"\n ? `on ${meta.event || \"?\"}${meta.condition ? ` when \"${meta.condition}\"` : \"\"}`\n : `on schedule \"${meta.schedule}\"`;\n\n return `Automation \"${name}\" created. Fires ${summary} in ${meta.mode} mode.`;\n}\n\nasync function handleUpdate(\n args: Record<string, string>,\n getCurrentUser: () => string,\n): Promise<string> {\n const owner = getCurrentUser();\n const name = args.name;\n const path = `jobs/${name}.md`;\n\n const resource = await resourceGetByPath(owner, path);\n if (!resource) {\n return `Automation \"${name}\" not found (or you don't own it).`;\n }\n\n const { meta, body } = parseTriggerFrontmatter(resource.content);\n\n if (args.enabled !== undefined) {\n meta.enabled = args.enabled !== \"false\";\n }\n if (args.condition !== undefined) {\n meta.condition = args.condition || undefined;\n }\n const newBody = args.body ?? body;\n\n await resourcePut(\n resource.owner,\n resource.path,\n buildTriggerContent(meta, newBody),\n );\n await refreshEventSubscriptions();\n\n return `Automation \"${name}\" updated.`;\n}\n\nasync function handleDelete(\n args: Record<string, string>,\n getCurrentUser: () => string,\n): Promise<string> {\n const owner = getCurrentUser();\n const path = `jobs/${args.name}.md`;\n\n const resource = await resourceGetByPath(owner, path);\n if (!resource) return `Automation \"${args.name}\" not found.`;\n\n await resourceDelete(resource.id);\n return `Automation \"${args.name}\" deleted.`;\n}\n\nasync function handleFireTest(\n args: Record<string, string>,\n getCurrentUser: () => string,\n): Promise<string> {\n // Dynamic import to avoid circular dependency at module load time\n const { emit } = await import(\"../event-bus/index.js\");\n\n let data: Record<string, unknown> = {};\n if (args.data) {\n try {\n data = JSON.parse(args.data);\n } catch {\n return \"Error: invalid JSON in data parameter.\";\n }\n }\n\n // Scope the test event to the current user so only their automations fire,\n // not automations owned by other users in the same process.\n const owner = getCurrentUser();\n emit(\"test.event.fired\", { data }, { owner });\n return `Test event fired with payload: ${JSON.stringify({ data })}. Any automations subscribed to \"test.event.fired\" will be evaluated.`;\n}\n\n/* ------------------------------------------------------------------ */\n/* Consolidated tool entry */\n/* ------------------------------------------------------------------ */\n\nconst VALID_ACTIONS = [\n \"list-events\",\n \"list\",\n \"define\",\n \"update\",\n \"delete\",\n \"fire-test\",\n] as const;\n\nexport function createAutomationToolEntries(\n getCurrentUser: () => string,\n): Record<string, ActionEntry> {\n return {\n \"manage-automations\": {\n tool: {\n description: `Manage automations (event-triggered and scheduled tasks). Use the \"action\" parameter to choose an operation:\n\n- **list-events**: List all registered event types that automations can subscribe to. Returns event names, descriptions, and payload schemas. Call this BEFORE defining an automation to discover available events.\n- **list**: List all automations (triggers). Shows name, event, condition, mode, status, and domain. Optional params: domain, enabled_only.\n- **define**: Create a new automation. IMPORTANT: Always confirm with the user before calling — show them a summary of what will be created. Required params: name, trigger_type, body. Optional: event, schedule, condition, mode, domain.\n- **update**: Update an existing automation's settings (enabled, condition, body, etc.). Required param: name. Optional: enabled, condition, body.\n- **delete**: Delete an automation. Always confirm with the user first. Required param: name.\n- **fire-test**: Fire a test event to validate automations. Emits a test.event.fired event. Optional param: data (JSON string).`,\n parameters: {\n type: \"object\" as const,\n properties: {\n action: {\n type: \"string\",\n description:\n \"The operation to perform: list-events, list, define, update, delete, or fire-test.\",\n enum: [...VALID_ACTIONS],\n },\n name: {\n type: \"string\",\n description:\n \"Slug name for the automation (lowercase, hyphens). Used by define, update, and delete.\",\n },\n trigger_type: {\n type: \"string\",\n description: '\"event\" or \"schedule\". Required for define.',\n enum: [\"event\", \"schedule\"],\n },\n event: {\n type: \"string\",\n description:\n \"For event triggers: the event name to subscribe to. Call with action=list-events first to see available events.\",\n },\n schedule: {\n type: \"string\",\n description:\n 'For schedule triggers: cron expression. Example: \"0 9 * * 1-5\" (9am weekdays).',\n },\n condition: {\n type: \"string\",\n description:\n 'Natural-language condition. Example: \"attendee email ends with @builder.io\". Leave empty for unconditional. Used by define and update.',\n },\n mode: {\n type: \"string\",\n description:\n '\"agentic\" (full agent loop, can use tools) or \"deterministic\" (fixed actions only). Used by define.',\n enum: [\"agentic\", \"deterministic\"],\n },\n domain: {\n type: \"string\",\n description:\n \"Domain tag for grouping (mail, calendar, clips, etc.). Used by define and list.\",\n },\n body: {\n type: \"string\",\n description:\n \"The natural-language instructions for what to do when the automation fires. This becomes the agent's prompt in agentic mode. Used by define and update.\",\n },\n enabled: {\n type: \"string\",\n description:\n '\"true\" or \"false\" to enable/disable. Used by update.',\n },\n enabled_only: {\n type: \"string\",\n description:\n '\"true\" to show only enabled automations. Used by list.',\n },\n data: {\n type: \"string\",\n description:\n 'JSON data to include as the test event payload. Used by fire-test. Example: \\'{\"email\": \"test@example.com\"}\\'.',\n },\n },\n required: [\"action\"],\n },\n },\n run: async (args: Record<string, string>) => {\n const action = args.action;\n\n switch (action) {\n case \"list-events\":\n return handleListEvents();\n case \"list\":\n return handleList(args, getCurrentUser);\n case \"define\":\n return handleDefine(args, getCurrentUser);\n case \"update\":\n return handleUpdate(args, getCurrentUser);\n case \"delete\":\n return handleDelete(args, getCurrentUser);\n case \"fire-test\":\n return handleFireTest(args, getCurrentUser);\n default:\n return `Error: unknown action \"${action}\". Valid actions: ${VALID_ACTIONS.join(\", \")}.`;\n }\n },\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/triggers/actions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Framework-level agent actions for the automations system.\n *\n * These are registered as native tools (not template actions) so they're\n * available in every template. The agent uses them to create, list, and\n * manage automations from chat.\n *\n * All six operations are consolidated into a single `manage-automations` tool\n * with an `action` discriminator to keep the tool registry compact.\n */\n\nimport type { ActionEntry } from \"../agent/production-agent.js\";\nimport { listEvents } from \"../event-bus/index.js\";\nimport {\n resourceListAllOwners,\n resourcePut,\n resourceDelete,\n resourceGetByPath,\n SHARED_OWNER,\n} from \"../resources/store.js\";\nimport {\n parseTriggerFrontmatter,\n buildTriggerContent,\n refreshEventSubscriptions,\n} from \"./dispatcher.js\";\nimport type { TriggerFrontmatter } from \"./types.js\";\n\n/* ------------------------------------------------------------------ */\n/* Individual action handlers */\n/* ------------------------------------------------------------------ */\n\nasync function handleListEvents(): Promise<string> {\n const events = listEvents();\n if (events.length === 0) {\n return \"No events registered yet. Events are registered by integrations (mail, calendar, clips, etc.).\";\n }\n const lines = events.map((e) => {\n let schemaStr = \"\";\n try {\n const s = e.payloadSchema as any;\n if (s?._zod?.def?.shape) {\n const fields = Object.keys(s._zod.def.shape);\n schemaStr = ` Fields: ${fields.join(\", \")}`;\n }\n } catch {\n // ignore\n }\n const example = e.example\n ? `\\n Example: ${JSON.stringify(e.example)}`\n : \"\";\n return `- **${e.name}**: ${e.description}${schemaStr}${example}`;\n });\n return lines.join(\"\\n\");\n}\n\nasync function handleList(\n args: Record<string, string>,\n getCurrentUser: () => string,\n): Promise<string> {\n const owner = getCurrentUser();\n const resources = await resourceListAllOwners(\"jobs/\");\n const triggers = resources\n .filter((r) => r.owner === owner || r.owner === SHARED_OWNER)\n .filter((r) => r.path.endsWith(\".md\"))\n .map((r) => {\n const { meta, body } = parseTriggerFrontmatter(r.content);\n const name = r.path.replace(/^jobs\\//, \"\").replace(/\\.md$/, \"\");\n return { name, meta, body, owner: r.owner, id: r.id };\n })\n .filter((t) => {\n if (args.domain && t.meta.domain !== args.domain) return false;\n if (args.enabled_only === \"true\" && !t.meta.enabled) return false;\n return true;\n });\n\n if (triggers.length === 0) return \"No automations found.\";\n\n const lines = triggers.map((t) => {\n const type =\n t.meta.triggerType === \"event\"\n ? `on ${t.meta.event || \"?\"}`\n : `cron: ${t.meta.schedule}`;\n const status = t.meta.enabled ? \"enabled\" : \"disabled\";\n const lastStatus = t.meta.lastStatus ? ` (last: ${t.meta.lastStatus})` : \"\";\n const condition = t.meta.condition\n ? `\\n Condition: \"${t.meta.condition}\"`\n : \"\";\n const domain = t.meta.domain ? ` [${t.meta.domain}]` : \"\";\n return `- **${t.name}**${domain}: ${type} → ${t.meta.mode} (${status}${lastStatus})${condition}\\n Body: ${t.body.slice(0, 100)}${t.body.length > 100 ? \"...\" : \"\"}`;\n });\n return lines.join(\"\\n\\n\");\n}\n\nasync function handleDefine(\n args: Record<string, string>,\n getCurrentUser: () => string,\n): Promise<string> {\n const owner = getCurrentUser();\n const name = (args.name || \"\").replace(/[^a-z0-9-]/g, \"-\");\n if (!name) return \"Error: name is required (lowercase, hyphens).\";\n\n const path = `jobs/${name}.md`;\n\n // Check if it already exists\n const existing = await resourceGetByPath(owner, path);\n if (existing) {\n return `Error: An automation named \"${name}\" already exists. Use a different name or delete the existing one first.`;\n }\n\n const triggerType = args.trigger_type === \"schedule\" ? \"schedule\" : \"event\";\n const meta: TriggerFrontmatter = {\n schedule: args.schedule || \"\",\n enabled: true,\n triggerType,\n event: args.event || undefined,\n condition: args.condition || undefined,\n mode: args.mode === \"deterministic\" ? \"deterministic\" : \"agentic\",\n domain: args.domain || undefined,\n createdBy: owner,\n runAs: \"creator\",\n };\n\n const content = buildTriggerContent(meta, args.body || \"\");\n await resourcePut(owner, path, content);\n\n // Refresh event subscriptions so the new trigger is active immediately\n await refreshEventSubscriptions();\n\n const summary =\n triggerType === \"event\"\n ? `on ${meta.event || \"?\"}${meta.condition ? ` when \"${meta.condition}\"` : \"\"}`\n : `on schedule \"${meta.schedule}\"`;\n\n return `Automation \"${name}\" created. Fires ${summary} in ${meta.mode} mode.`;\n}\n\nasync function handleUpdate(\n args: Record<string, string>,\n getCurrentUser: () => string,\n): Promise<string> {\n const owner = getCurrentUser();\n const name = args.name;\n const path = `jobs/${name}.md`;\n\n const resource = await resourceGetByPath(owner, path);\n if (!resource) {\n return `Automation \"${name}\" not found (or you don't own it).`;\n }\n\n const { meta, body } = parseTriggerFrontmatter(resource.content);\n\n if (args.enabled !== undefined) {\n meta.enabled = args.enabled !== \"false\";\n }\n if (args.condition !== undefined) {\n meta.condition = args.condition || undefined;\n }\n const newBody = args.body ?? body;\n\n await resourcePut(\n resource.owner,\n resource.path,\n buildTriggerContent(meta, newBody),\n );\n await refreshEventSubscriptions();\n\n return `Automation \"${name}\" updated.`;\n}\n\nasync function handleDelete(\n args: Record<string, string>,\n getCurrentUser: () => string,\n): Promise<string> {\n const owner = getCurrentUser();\n const path = `jobs/${args.name}.md`;\n\n const resource = await resourceGetByPath(owner, path);\n if (!resource) return `Automation \"${args.name}\" not found.`;\n\n await resourceDelete(resource.id);\n return `Automation \"${args.name}\" deleted.`;\n}\n\nasync function handleFireTest(\n args: Record<string, string>,\n getCurrentUser: () => string,\n): Promise<string> {\n // Dynamic import to avoid circular dependency at module load time\n const { emit } = await import(\"../event-bus/index.js\");\n\n let data: Record<string, unknown> = {};\n if (args.data) {\n try {\n data = JSON.parse(args.data);\n } catch {\n return \"Error: invalid JSON in data parameter.\";\n }\n }\n\n // Scope the test event to the current user so only their automations fire,\n // not automations owned by other users in the same process.\n const owner = getCurrentUser();\n emit(\"test.event.fired\", { data }, { owner });\n return `Test event fired with payload: ${JSON.stringify({ data })}. Any automations subscribed to \"test.event.fired\" will be evaluated.`;\n}\n\n/* ------------------------------------------------------------------ */\n/* Consolidated tool entry */\n/* ------------------------------------------------------------------ */\n\nconst VALID_ACTIONS = [\n \"list-events\",\n \"list\",\n \"define\",\n \"update\",\n \"delete\",\n \"fire-test\",\n] as const;\n\nexport function createAutomationToolEntries(\n getCurrentUser: () => string,\n): Record<string, ActionEntry> {\n return {\n \"manage-automations\": {\n tool: {\n description: `Manage automations (event-triggered and scheduled tasks). Use the \"action\" parameter to choose an operation:\n\n- **list-events**: List all registered event types that automations can subscribe to. Returns event names, descriptions, and payload schemas. Call this BEFORE defining an automation to discover available events.\n- **list**: List all automations (triggers). Shows name, event, condition, mode, status, and domain. Optional params: domain, enabled_only.\n- **define**: Create a new automation. IMPORTANT: Always confirm with the user before calling — show them a summary of what will be created. Required params: name, trigger_type, body. Optional: event, schedule, condition, mode, domain.\n- **update**: Update an existing automation's settings (enabled, condition, body, etc.). Required param: name. Optional: enabled, condition, body.\n- **delete**: Delete an automation. Always confirm with the user first. Required param: name.\n- **fire-test**: Fire a test event to validate automations. Emits a test.event.fired event. Optional param: data (JSON string).`,\n parameters: {\n type: \"object\" as const,\n properties: {\n action: {\n type: \"string\",\n description:\n \"The operation to perform: list-events, list, define, update, delete, or fire-test.\",\n enum: [...VALID_ACTIONS],\n },\n name: {\n type: \"string\",\n description:\n \"Slug name for the automation (lowercase, hyphens). Used by define, update, and delete.\",\n },\n trigger_type: {\n type: \"string\",\n description: '\"event\" or \"schedule\". Required for define.',\n enum: [\"event\", \"schedule\"],\n },\n event: {\n type: \"string\",\n description:\n \"For event triggers: the event name to subscribe to. Call with action=list-events first to see available events.\",\n },\n schedule: {\n type: \"string\",\n description:\n 'For schedule triggers: cron expression. Example: \"0 9 * * 1-5\" (9am weekdays).',\n },\n condition: {\n type: \"string\",\n description:\n 'Natural-language condition. Example: \"attendee email ends with @builder.io\". Leave empty for unconditional. Used by define and update.',\n },\n mode: {\n type: \"string\",\n description:\n '\"agentic\" (full agent loop, can use tools) or \"deterministic\" (fixed actions only). Used by define.',\n enum: [\"agentic\", \"deterministic\"],\n },\n domain: {\n type: \"string\",\n description:\n \"Domain tag for grouping (mail, calendar, clips, etc.). Used by define and list.\",\n },\n body: {\n type: \"string\",\n description:\n \"The natural-language instructions for what to do when the automation fires. This becomes the agent's prompt in agentic mode. Used by define and update.\",\n },\n enabled: {\n type: \"string\",\n description:\n '\"true\" or \"false\" to enable/disable. Used by update.',\n },\n enabled_only: {\n type: \"string\",\n description:\n '\"true\" to show only enabled automations. Used by list.',\n },\n data: {\n type: \"string\",\n description:\n 'JSON data to include as the test event payload. Used by fire-test. Example: \\'{\"email\": \"test@example.com\"}\\'.',\n },\n },\n required: [\"action\"],\n },\n },\n run: async (args: Record<string, string>) => {\n const action = args.action;\n\n switch (action) {\n case \"list-events\":\n return handleListEvents();\n case \"list\":\n return handleList(args, getCurrentUser);\n case \"define\":\n return handleDefine(args, getCurrentUser);\n case \"update\":\n return handleUpdate(args, getCurrentUser);\n case \"delete\":\n return handleDelete(args, getCurrentUser);\n case \"fire-test\":\n return handleFireTest(args, getCurrentUser);\n default:\n return `Error: unknown action \"${action}\". Valid actions: ${VALID_ACTIONS.join(\", \")}.`;\n }\n },\n },\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/triggers/dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/triggers/dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,8BAA8B,CAAC;AAQtC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAKrD,iBAAS,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG;IACjD,IAAI,EAAE,kBAAkB,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd,CAsFA;AAED,iBAAS,mBAAmB,CAAC,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAsB3E;AAID,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC9C,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAgBD;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,qBAAqB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAGf;AAED;;;GAGG;AACH,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC,CAgC/D;AA2QD,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,CAAC"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* subscribes to their events, and dispatches them (condition eval → agent
|
|
6
6
|
* loop) when matching events fire.
|
|
7
7
|
*/
|
|
8
|
-
import { subscribe } from "../event-bus/index.js";
|
|
8
|
+
import { subscribe, unsubscribe } from "../event-bus/index.js";
|
|
9
9
|
import { resourceListAllOwners, resourcePut } from "../resources/store.js";
|
|
10
10
|
import { runWithRequestContext } from "../server/request-context.js";
|
|
11
11
|
import { runAgentLoop, actionsToEngineTools, getOwnerActiveApiKey, } from "../agent/production-agent.js";
|
|
@@ -126,8 +126,18 @@ function buildTriggerContent(meta, body) {
|
|
|
126
126
|
lines.push(body);
|
|
127
127
|
return lines.join("\n");
|
|
128
128
|
}
|
|
129
|
-
// Track active subscriptions to avoid
|
|
130
|
-
|
|
129
|
+
// Track active subscriptions (eventName -> subscription id) to avoid
|
|
130
|
+
// double-subscribing AND so subscriptions for events that no longer have any
|
|
131
|
+
// enabled trigger can be torn down — otherwise deleted/disabled triggers leave
|
|
132
|
+
// phantom bus listeners that fire handleEvent forever.
|
|
133
|
+
const _eventSubscriptions = new Map();
|
|
134
|
+
// In-flight agentic dispatches keyed by `${owner}:${path}`. Guards against the
|
|
135
|
+
// check-then-write TOCTOU window in handleEvent: two near-simultaneous fires of
|
|
136
|
+
// the same event both pass the `lastStatus !== "running"` check (which has
|
|
137
|
+
// several awaits before the DB is marked running) and would otherwise launch
|
|
138
|
+
// two concurrent agent runs for one trigger. Sufficient for single-process
|
|
139
|
+
// deployments; multi-instance would need a conditional DB update.
|
|
140
|
+
const _dispatchingTriggers = new Set();
|
|
131
141
|
let _deps = null;
|
|
132
142
|
/**
|
|
133
143
|
* Initialize the trigger dispatcher. Call once at server startup.
|
|
@@ -153,10 +163,17 @@ export async function refreshEventSubscriptions() {
|
|
|
153
163
|
eventNames.add(meta.event);
|
|
154
164
|
}
|
|
155
165
|
}
|
|
166
|
+
// Tear down subscriptions whose event no longer has any enabled trigger.
|
|
167
|
+
for (const [eventName, subId] of [..._eventSubscriptions]) {
|
|
168
|
+
if (!eventNames.has(eventName)) {
|
|
169
|
+
unsubscribe(subId);
|
|
170
|
+
_eventSubscriptions.delete(eventName);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
156
173
|
for (const eventName of eventNames) {
|
|
157
|
-
if (!
|
|
158
|
-
subscribe(eventName, (payload, eventMeta) => handleEvent(eventName, payload, eventMeta));
|
|
159
|
-
|
|
174
|
+
if (!_eventSubscriptions.has(eventName)) {
|
|
175
|
+
const subId = subscribe(eventName, (payload, eventMeta) => handleEvent(eventName, payload, eventMeta));
|
|
176
|
+
_eventSubscriptions.set(eventName, subId);
|
|
160
177
|
}
|
|
161
178
|
}
|
|
162
179
|
}
|
|
@@ -201,9 +218,20 @@ async function handleEvent(eventName, payload, eventMeta) {
|
|
|
201
218
|
const matches = await evaluateCondition(meta.condition, payload, apiKey);
|
|
202
219
|
if (!matches)
|
|
203
220
|
continue;
|
|
204
|
-
// Dispatch
|
|
221
|
+
// Dispatch. Guard against concurrent duplicate dispatch of the same
|
|
222
|
+
// trigger (TOCTOU on lastStatus) with an in-process lock keyed on the
|
|
223
|
+
// trigger's identity.
|
|
224
|
+
const dispatchKey = `${resource.owner}:${resource.path}`;
|
|
225
|
+
if (_dispatchingTriggers.has(dispatchKey))
|
|
226
|
+
continue;
|
|
205
227
|
if (meta.mode === "agentic") {
|
|
206
|
-
|
|
228
|
+
_dispatchingTriggers.add(dispatchKey);
|
|
229
|
+
try {
|
|
230
|
+
await dispatchAgentic(resource, meta, body, payload, eventMeta, apiKey);
|
|
231
|
+
}
|
|
232
|
+
finally {
|
|
233
|
+
_dispatchingTriggers.delete(dispatchKey);
|
|
234
|
+
}
|
|
207
235
|
}
|
|
208
236
|
else {
|
|
209
237
|
console.warn(`[triggers] Deterministic mode not yet implemented for "${resource.path}" — skipping`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../../src/triggers/dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,oBAAoB,GAErB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,uBAAuB,EACvB,aAAa,GACd,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,uEAAuE;AACvE,MAAM,cAAc,GAAG,oCAAoC,CAAC;AAE5D,SAAS,uBAAuB,CAAC,OAAe;IAI9C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC5C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO;YACL,IAAI,EAAE;gBACJ,QAAQ,EAAE,EAAE;gBACZ,OAAO,EAAE,KAAK;gBACd,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;aAChB;YACD,IAAI,EAAE,OAAO;SACd,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAE7B,MAAM,IAAI,GAAuB;QAC/B,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,UAAU;QACvB,IAAI,EAAE,SAAS;KAChB,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,QAAQ,KAAK,CAAC,CAAC;YAAE,SAAS;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3C,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5C,IACE,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC9C,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAC9C,CAAC;YACD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;QAED,QAAQ,GAAG,EAAE,CAAC;YACZ,KAAK,UAAU;gBACb,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;gBACtB,MAAM;YACR,KAAK,SAAS;gBACZ,IAAI,CAAC,OAAO,GAAG,KAAK,KAAK,OAAO,CAAC;gBACjC,MAAM;YACR,KAAK,aAAa;gBAChB,IAAI,CAAC,WAAW;oBACd,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC;gBACjE,MAAM;YACR,KAAK,OAAO;gBACV,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;gBACnB,MAAM;YACR,KAAK,WAAW;gBACd,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;gBACvB,MAAM;YACR,KAAK,MAAM;gBACT,IAAI,CAAC,IAAI;oBACP,KAAK,KAAK,eAAe,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;gBACvE,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;gBACpB,MAAM;YACR,KAAK,WAAW;gBACd,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;gBACvB,MAAM;YACR,KAAK,OAAO;gBACV,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;gBACnB,MAAM;YACR,KAAK,OAAO;gBACV,IAAI,CAAC,KAAK;oBACR,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;gBAChE,MAAM;YACR,KAAK,SAAS;gBACZ,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;gBACrB,MAAM;YACR,KAAK,YAAY;gBACf,IAAI,CAAC,UAAU,GAAG,KAAyC,CAAC;gBAC5D,MAAM;YACR,KAAK,WAAW;gBACd,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;gBACvB,MAAM;YACR,KAAK,SAAS;gBACZ,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;gBACrB,MAAM;QACV,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxB,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAwB,EAAE,IAAY;IACjE,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC;IACtB,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;IAC3C,KAAK,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IACvC,KAAK,CAAC,IAAI,CAAC,gBAAgB,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAC/C,IAAI,IAAI,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACnD,IAAI,IAAI,CAAC,SAAS;QAChB,KAAK,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IACpE,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACjC,IAAI,IAAI,CAAC,MAAM;QAAE,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IACtD,IAAI,IAAI,CAAC,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;IAC/D,IAAI,IAAI,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACnD,IAAI,IAAI,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACnD,IAAI,IAAI,CAAC,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IACzD,IAAI,IAAI,CAAC,UAAU;QAAE,KAAK,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IAClE,IAAI,IAAI,CAAC,SAAS;QAChB,KAAK,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IACpE,IAAI,IAAI,CAAC,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IACzD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAaD,yDAAyD;AACzD,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAU,CAAC;AAC5C,IAAI,KAAK,GAAiC,IAAI,CAAC;AAE/C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,IAA2B;IAE3B,KAAK,GAAG,IAAI,CAAC;IACb,MAAM,yBAAyB,EAAE,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB;IAC7C,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;QAErC,KAAK,MAAM,QAAQ,IAAI,YAAY,EAAE,CAAC;YACpC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAAE,SAAS;YAC7C,MAAM,EAAE,IAAI,EAAE,GAAG,uBAAuB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC3D,IAAI,IAAI,CAAC,WAAW,KAAK,OAAO,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC/D,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;QAED,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;gBACtC,SAAS,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,CAC1C,WAAW,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,CAAC,CAC3C,CAAC;gBACF,iBAAiB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,mDAAmD,EAAE,GAAG,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,SAAiB,EACjB,OAAgB,EAChB,SAAoB;IAEpB,IAAI,CAAC,KAAK;QAAE,OAAO;IAEnB,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;QAC1D,MAAM,gBAAgB,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;YACjD,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAC;YAC1C,MAAM,EAAE,IAAI,EAAE,GAAG,uBAAuB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YACpD,4DAA4D;YAC5D,+DAA+D;YAC/D,IACE,SAAS,CAAC,KAAK;gBACf,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,KAAK;gBAC3B,CAAC,CAAC,KAAK,KAAK,YAAY,EACxB,CAAC;gBACD,OAAO,KAAK,CAAC;YACf,CAAC;YACD,OAAO,CACL,IAAI,CAAC,WAAW,KAAK,OAAO;gBAC5B,IAAI,CAAC,KAAK,KAAK,SAAS;gBACxB,IAAI,CAAC,OAAO;gBACZ,IAAI,CAAC,UAAU,KAAK,SAAS,CAC9B,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,KAAK,MAAM,QAAQ,IAAI,gBAAgB,EAAE,CAAC;YACxC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,uBAAuB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YACjE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;gBAAE,SAAS;YAE3B,2CAA2C;YAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,IAAI,QAAQ,CAAC,KAAK,CAAC;YAC/C,MAAM,UAAU,GAAG,MAAM,oBAAoB,CAAC,KAAK,CAAC,CAAC;YACrD,MAAM,MAAM,GACV,UAAU,IAAI,KAAK,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;YAC9D,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,OAAO,CAAC,IAAI,CACV,sCAAsC,QAAQ,CAAC,IAAI,cAAc,CAClE,CAAC;gBACF,SAAS;YACX,CAAC;YAED,qBAAqB;YACrB,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;YACzE,IAAI,CAAC,OAAO;gBAAE,SAAS;YAEvB,WAAW;YACX,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC5B,MAAM,eAAe,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;YAC1E,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CACV,0DAA0D,QAAQ,CAAC,IAAI,cAAc,CACtF,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,oCAAoC,SAAS,IAAI,EAAE,GAAG,CAAC,CAAC;IACxE,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,wBAAwB,CACrC,YAAoB,EACpB,QAA4B;IAE5B,IAAI,YAAY,KAAK,YAAY;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACvD,IAAI,CAAC;QACH,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;QACtD,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;QACvB,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC;YAClC,GAAG,EAAE,8CAA8C;YACnD,IAAI,EAAE,CAAC,YAAY,CAAC;SACrB,CAAC,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,YAAY,oBAAoB,EAAE,CAAC;QAC1E,CAAC;QACD,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC;gBACpC,GAAG,EAAE,gFAAgF;gBACrF,IAAI,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC;aAC/B,CAAC,CAAC;YACH,IAAI,CAAC,YAAY,CAAC,IAAI,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzD,OAAO;oBACL,EAAE,EAAE,KAAK;oBACT,MAAM,EAAE,SAAS,YAAY,mCAAmC,QAAQ,GAAG;iBAC5E,CAAC;YACJ,CAAC;QACH,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,MAAM,GAAG,GAAG,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;QAC9C,IACE,GAAG,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YAC9B,GAAG,CAAC,QAAQ,CAAC,eAAe,CAAC;YAC7B,GAAG,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAC/B,CAAC;YACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;QACtB,CAAC;QACD,OAAO,CAAC,IAAI,CACV,qDAAqD,YAAY,IAAI,EACrE,GAAG,EAAE,OAAO,CACb,CAAC;QACF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,eAAe,CAC5B,QAA0D,EAC1D,IAAwB,EACxB,IAAY,EACZ,OAAgB,EAChB,SAAoB,EACpB,MAAc;IAEd,IAAI,CAAC,KAAK;QAAE,OAAO;IAEnB,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC9E,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IAEvB,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,IAAI,QAAQ,CAAC,KAAK,CAAC;IACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,IAAI,SAAS,CAAC;IAEzC,qEAAqE;IACrE,oEAAoE;IACpE,oEAAoE;IACpE,uEAAuE;IACvE,MAAM,QAAQ,GAAG,MAAM,wBAAwB,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;IACxE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,OAAO,CAAC,IAAI,CACV,gCAAgC,WAAW,MAAM,QAAQ,CAAC,MAAM,IAAI;YAClE,mEAAmE,CACtE,CAAC;QACF,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC;QACjC,MAAM,WAAW,CACf,QAAQ,CAAC,KAAK,EACd,QAAQ,CAAC,IAAI,EACb,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,CAChC,CAAC;QACF,OAAO;IACT,CAAC;IAED,kBAAkB;IAClB,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IACjC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;IAC5B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC3B,MAAM,WAAW,CACf,QAAQ,CAAC,KAAK,EACd,QAAQ,CAAC,IAAI,EACb,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,CAChC,CAAC;IAEF,MAAM,qBAAqB,CACzB,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,EAC5C,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,KAAM,CAAC,UAAU,EAAE,CAAC;YACpC,MAAM,YAAY,GAAG,MAAM,KAAM,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;YAChE,MAAM,KAAK,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;YAE5C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC;gBACjC,MAAM;gBACN,KAAK,EAAE,KAAM,CAAC,KAAK;aACpB,CAAC,CAAC;YACH,MAAM,KAAK,GACT,KAAM,CAAC,KAAK;gBACZ,CAAC,MAAM,uBAAuB,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,KAAM,CAAC,KAAK,EAAE,CAAC,CAAC;gBAChE,MAAM,CAAC,YAAY,CAAC;YACtB,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,YAAY,EAAE;gBAC9C,KAAK,EAAE,YAAY,WAAW,MAAM,GAAG,CAAC,kBAAkB,EAAE,EAAE;aAC/D,CAAC,CAAC;YAEH,IAAI,UAAkB,CAAC;YACvB,IAAI,CAAC;gBACH,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAChD,CAAC;YAAC,MAAM,CAAC;gBACP,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;YAC/B,CAAC;YAED,MAAM,WAAW,GAAG,wBAAwB,WAAW;SACtD,IAAI,CAAC,KAAK;YACP,SAAS,CAAC,OAAO;YACjB,SAAS,CAAC,SAAS;;;EAG7B,UAAU;;;;EAIV,IAAI,EAAE,CAAC;YAED,MAAM,QAAQ,GAAG;gBACf;oBACE,IAAI,EAAE,MAAe;oBACrB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;iBACxD;aACF,CAAC;YAEF,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;YACzC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;YAEpE,MAAM,MAAM,GAAqB,EAAE,CAAC;YAEpC,IAAI,CAAC;gBACH,MAAM,YAAY,CAAC;oBACjB,MAAM;oBACN,KAAK;oBACL,YAAY;oBACZ,KAAK;oBACL,QAAQ;oBACR,OAAO;oBACP,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;oBACnC,MAAM,EAAE,UAAU,CAAC,MAAM;iBAC1B,CAAC,CAAC;YACL,CAAC;oBAAS,CAAC;gBACT,YAAY,CAAC,OAAO,CAAC,CAAC;YACxB,CAAC;YAED,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;YAC5B,MAAM,WAAW,CACf,QAAQ,CAAC,KAAK,EACd,QAAQ,CAAC,IAAI,EACb,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,CAChC,CAAC;YAEF,OAAO,CAAC,GAAG,CAAC,eAAe,WAAW,0BAA0B,CAAC,CAAC;QACpE,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC;YAC1B,IAAI,CAAC,SAAS,GAAG,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,eAAe,CAAC;YAChE,MAAM,WAAW,CACf,QAAQ,CAAC,KAAK,EACd,QAAQ,CAAC,IAAI,EACb,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,CAChC,CAAC;YACF,OAAO,CAAC,KAAK,CAAC,eAAe,WAAW,WAAW,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QACrE,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,CAAC","sourcesContent":["/**\n * Trigger dispatcher — bridges the event bus to the automation system.\n *\n * On startup, loads all event-triggered jobs from the resources store,\n * subscribes to their events, and dispatches them (condition eval → agent\n * loop) when matching events fire.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { subscribe } from \"../event-bus/index.js\";\nimport type { EventMeta } from \"../event-bus/types.js\";\nimport { resourceListAllOwners, resourcePut } from \"../resources/store.js\";\nimport { runWithRequestContext } from \"../server/request-context.js\";\nimport {\n runAgentLoop,\n actionsToEngineTools,\n getOwnerActiveApiKey,\n type ActionEntry,\n} from \"../agent/production-agent.js\";\nimport {\n getStoredModelForEngine,\n resolveEngine,\n} from \"../agent/engine/index.js\";\nimport { createThread } from \"../chat-threads/store.js\";\nimport type { AgentChatEvent } from \"../agent/types.js\";\nimport { evaluateCondition } from \"./condition-evaluator.js\";\nimport type { TriggerFrontmatter } from \"./types.js\";\n\n// Re-use the job frontmatter parser — triggers extend the same format.\nconst FRONTMATTER_RE = /^---\\n([\\s\\S]*?)\\n---\\n?([\\s\\S]*)$/;\n\nfunction parseTriggerFrontmatter(content: string): {\n meta: TriggerFrontmatter;\n body: string;\n} {\n const match = content.match(FRONTMATTER_RE);\n if (!match) {\n return {\n meta: {\n schedule: \"\",\n enabled: false,\n triggerType: \"schedule\",\n mode: \"agentic\",\n },\n body: content,\n };\n }\n\n const yamlBlock = match[1];\n const body = match[2].trim();\n\n const meta: TriggerFrontmatter = {\n schedule: \"\",\n enabled: true,\n triggerType: \"schedule\",\n mode: \"agentic\",\n };\n\n for (const line of yamlBlock.split(\"\\n\")) {\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n const key = line.slice(0, colonIdx).trim();\n let value = line.slice(colonIdx + 1).trim();\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n\n switch (key) {\n case \"schedule\":\n meta.schedule = value;\n break;\n case \"enabled\":\n meta.enabled = value !== \"false\";\n break;\n case \"triggerType\":\n meta.triggerType =\n value === \"event\" || value === \"schedule\" ? value : \"schedule\";\n break;\n case \"event\":\n meta.event = value;\n break;\n case \"condition\":\n meta.condition = value;\n break;\n case \"mode\":\n meta.mode =\n value === \"deterministic\" || value === \"agentic\" ? value : \"agentic\";\n break;\n case \"domain\":\n meta.domain = value;\n break;\n case \"createdBy\":\n meta.createdBy = value;\n break;\n case \"orgId\":\n meta.orgId = value;\n break;\n case \"runAs\":\n meta.runAs =\n value === \"shared\" || value === \"creator\" ? value : undefined;\n break;\n case \"lastRun\":\n meta.lastRun = value;\n break;\n case \"lastStatus\":\n meta.lastStatus = value as TriggerFrontmatter[\"lastStatus\"];\n break;\n case \"lastError\":\n meta.lastError = value;\n break;\n case \"nextRun\":\n meta.nextRun = value;\n break;\n }\n }\n\n return { meta, body };\n}\n\nfunction buildTriggerContent(meta: TriggerFrontmatter, body: string): string {\n const lines = [\"---\"];\n lines.push(`schedule: \"${meta.schedule}\"`);\n lines.push(`enabled: ${meta.enabled}`);\n lines.push(`triggerType: ${meta.triggerType}`);\n if (meta.event) lines.push(`event: ${meta.event}`);\n if (meta.condition)\n lines.push(`condition: \"${meta.condition.replace(/\"/g, '\\\\\"')}\"`);\n lines.push(`mode: ${meta.mode}`);\n if (meta.domain) lines.push(`domain: ${meta.domain}`);\n if (meta.createdBy) lines.push(`createdBy: ${meta.createdBy}`);\n if (meta.orgId) lines.push(`orgId: ${meta.orgId}`);\n if (meta.runAs) lines.push(`runAs: ${meta.runAs}`);\n if (meta.lastRun) lines.push(`lastRun: ${meta.lastRun}`);\n if (meta.lastStatus) lines.push(`lastStatus: ${meta.lastStatus}`);\n if (meta.lastError)\n lines.push(`lastError: \"${meta.lastError.replace(/\"/g, '\\\\\"')}\"`);\n if (meta.nextRun) lines.push(`nextRun: ${meta.nextRun}`);\n lines.push(\"---\");\n lines.push(\"\");\n lines.push(body);\n return lines.join(\"\\n\");\n}\n\n// ─── Dispatcher deps (same pattern as SchedulerDeps) ────────────────────────\n\nexport interface TriggerDispatcherDeps {\n getActions: () => Record<string, ActionEntry>;\n getSystemPrompt: (owner: string) => Promise<string>;\n apiKey?: string;\n model?: string;\n /** App/template id used for org-scoped per-app model defaults. */\n appId?: string;\n}\n\n// Track active subscriptions to avoid double-subscribing\nconst _subscribedEvents = new Set<string>();\nlet _deps: TriggerDispatcherDeps | null = null;\n\n/**\n * Initialize the trigger dispatcher. Call once at server startup.\n * Loads all event-triggered jobs and subscribes to their events.\n */\nexport async function initTriggerDispatcher(\n deps: TriggerDispatcherDeps,\n): Promise<void> {\n _deps = deps;\n await refreshEventSubscriptions();\n}\n\n/**\n * Refresh event subscriptions from the resource store.\n * Call after creating/updating triggers.\n */\nexport async function refreshEventSubscriptions(): Promise<void> {\n try {\n const jobResources = await resourceListAllOwners(\"jobs/\");\n const eventNames = new Set<string>();\n\n for (const resource of jobResources) {\n if (!resource.path.endsWith(\".md\")) continue;\n const { meta } = parseTriggerFrontmatter(resource.content);\n if (meta.triggerType === \"event\" && meta.event && meta.enabled) {\n eventNames.add(meta.event);\n }\n }\n\n for (const eventName of eventNames) {\n if (!_subscribedEvents.has(eventName)) {\n subscribe(eventName, (payload, eventMeta) =>\n handleEvent(eventName, payload, eventMeta),\n );\n _subscribedEvents.add(eventName);\n }\n }\n } catch (err) {\n console.error(\"[triggers] Failed to refresh event subscriptions:\", err);\n }\n}\n\nasync function handleEvent(\n eventName: string,\n payload: unknown,\n eventMeta: EventMeta,\n): Promise<void> {\n if (!_deps) return;\n\n try {\n const jobResources = await resourceListAllOwners(\"jobs/\");\n const matchingTriggers = jobResources.filter((r) => {\n if (!r.path.endsWith(\".md\")) return false;\n const { meta } = parseTriggerFrontmatter(r.content);\n // Scope: only dispatch triggers owned by the event's owner,\n // or shared triggers. Prevents cross-tenant trigger execution.\n if (\n eventMeta.owner &&\n r.owner !== eventMeta.owner &&\n r.owner !== \"__shared__\"\n ) {\n return false;\n }\n return (\n meta.triggerType === \"event\" &&\n meta.event === eventName &&\n meta.enabled &&\n meta.lastStatus !== \"running\"\n );\n });\n\n for (const resource of matchingTriggers) {\n const { meta, body } = parseTriggerFrontmatter(resource.content);\n if (!body.trim()) continue;\n\n // Resolve API key for condition evaluation\n const owner = meta.createdBy || resource.owner;\n const userApiKey = await getOwnerActiveApiKey(owner);\n const apiKey =\n userApiKey || _deps.apiKey || process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n console.warn(\n `[triggers] No API key for trigger \"${resource.path}\" — skipping`,\n );\n continue;\n }\n\n // Evaluate condition\n const matches = await evaluateCondition(meta.condition, payload, apiKey);\n if (!matches) continue;\n\n // Dispatch\n if (meta.mode === \"agentic\") {\n await dispatchAgentic(resource, meta, body, payload, eventMeta, apiKey);\n } else {\n console.warn(\n `[triggers] Deterministic mode not yet implemented for \"${resource.path}\" — skipping`,\n );\n }\n }\n } catch (err) {\n console.error(`[triggers] Error handling event \"${eventName}\":`, err);\n }\n}\n\n/**\n * Validate that the run-as user still exists and (if scoped to an org) is\n * still a member of that org. Mirrors the recurring-jobs scheduler check\n * (audit 12 #10): event-triggered automations must stop firing when the\n * creator is removed/demoted.\n */\nasync function isTriggerRunAsStillValid(\n jobUserEmail: string,\n jobOrgId: string | undefined,\n): Promise<{ ok: boolean; reason?: string }> {\n if (jobUserEmail === \"__shared__\") return { ok: true };\n try {\n const { getDbExec } = await import(\"../db/client.js\");\n const db = getDbExec();\n const userResult = await db.execute({\n sql: `SELECT 1 FROM \"user\" WHERE email = ? LIMIT 1`,\n args: [jobUserEmail],\n });\n if (!userResult.rows || userResult.rows.length === 0) {\n return { ok: false, reason: `user \"${jobUserEmail}\" no longer exists` };\n }\n if (jobOrgId) {\n const memberResult = await db.execute({\n sql: `SELECT 1 FROM org_members WHERE org_id = ? AND LOWER(email) = LOWER(?) LIMIT 1`,\n args: [jobOrgId, jobUserEmail],\n });\n if (!memberResult.rows || memberResult.rows.length === 0) {\n return {\n ok: false,\n reason: `user \"${jobUserEmail}\" is no longer a member of org \"${jobOrgId}\"`,\n };\n }\n }\n return { ok: true };\n } catch (err: any) {\n const msg = err?.message?.toLowerCase() ?? \"\";\n if (\n msg.includes(\"does not exist\") ||\n msg.includes(\"no such table\") ||\n msg.includes(\"undefined table\")\n ) {\n return { ok: true };\n }\n console.warn(\n `[triggers] User/membership validation failed for \"${jobUserEmail}\":`,\n err?.message,\n );\n return { ok: true };\n }\n}\n\nasync function dispatchAgentic(\n resource: { path: string; owner: string; content: string },\n meta: TriggerFrontmatter,\n body: string,\n payload: unknown,\n eventMeta: EventMeta,\n apiKey: string,\n): Promise<void> {\n if (!_deps) return;\n\n const triggerName = resource.path.replace(/^jobs\\//, \"\").replace(/\\.md$/, \"\");\n const now = new Date();\n\n const jobUserEmail = meta.createdBy || resource.owner;\n const jobOrgId = meta.orgId ?? undefined;\n\n // SECURITY (audit 12 #10): re-validate the run-as user/membership on\n // every dispatch. Sharing revocation, user deletion, and org-member\n // removal must take effect for already-scheduled triggers. Skip the\n // dispatch on failure; leave the trigger entry alone for admin review.\n const validity = await isTriggerRunAsStillValid(jobUserEmail, jobOrgId);\n if (!validity.ok) {\n console.warn(\n `[triggers] Skipping trigger \"${triggerName}\": ${validity.reason}. ` +\n `User/membership no longer valid — leaving entry for admin review.`,\n );\n meta.lastRun = now.toISOString();\n meta.lastStatus = \"skipped\";\n meta.lastError = validity.reason;\n await resourcePut(\n resource.owner,\n resource.path,\n buildTriggerContent(meta, body),\n );\n return;\n }\n\n // Mark as running\n meta.lastRun = now.toISOString();\n meta.lastStatus = \"running\";\n meta.lastError = undefined;\n await resourcePut(\n resource.owner,\n resource.path,\n buildTriggerContent(meta, body),\n );\n\n await runWithRequestContext(\n { userEmail: jobUserEmail, orgId: jobOrgId },\n async () => {\n try {\n const actions = _deps!.getActions();\n const systemPrompt = await _deps!.getSystemPrompt(jobUserEmail);\n const tools = actionsToEngineTools(actions);\n\n const engine = await resolveEngine({\n apiKey,\n appId: _deps!.appId,\n });\n const model =\n _deps!.model ??\n (await getStoredModelForEngine(engine, { appId: _deps!.appId })) ??\n engine.defaultModel;\n const thread = await createThread(jobUserEmail, {\n title: `Trigger: ${triggerName} — ${now.toLocaleDateString()}`,\n });\n\n let payloadStr: string;\n try {\n payloadStr = JSON.stringify(payload, null, 2);\n } catch {\n payloadStr = String(payload);\n }\n\n const triggerText = `[Automation Trigger: ${triggerName}]\nEvent: ${meta.event}\nEvent ID: ${eventMeta.eventId}\nFired at: ${eventMeta.emittedAt}\n\nEvent payload:\n${payloadStr}\n\nExecute the following automation instructions:\n\n${body}`;\n\n const messages = [\n {\n role: \"user\" as const,\n content: [{ type: \"text\" as const, text: triggerText }],\n },\n ];\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 5 * 60 * 1000);\n\n const events: AgentChatEvent[] = [];\n\n try {\n await runAgentLoop({\n engine,\n model,\n systemPrompt,\n tools,\n messages,\n actions,\n send: (event) => events.push(event),\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timeout);\n }\n\n meta.lastStatus = \"success\";\n await resourcePut(\n resource.owner,\n resource.path,\n buildTriggerContent(meta, body),\n );\n\n console.log(`[triggers] \"${triggerName}\" completed successfully`);\n } catch (err: any) {\n meta.lastStatus = \"error\";\n meta.lastError = err?.message?.slice(0, 200) || \"Unknown error\";\n await resourcePut(\n resource.owner,\n resource.path,\n buildTriggerContent(meta, body),\n );\n console.error(`[triggers] \"${triggerName}\" failed:`, err?.message);\n }\n },\n );\n}\n\nexport { parseTriggerFrontmatter, buildTriggerContent };\n"]}
|
|
1
|
+
{"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../../src/triggers/dispatcher.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Trigger dispatcher — bridges the event bus to the automation system.\n *\n * On startup, loads all event-triggered jobs from the resources store,\n * subscribes to their events, and dispatches them (condition eval → agent\n * loop) when matching events fire.\n */\n\nimport { subscribe, unsubscribe } from \"../event-bus/index.js\";\nimport type { EventMeta } from \"../event-bus/types.js\";\nimport { resourceListAllOwners, resourcePut } from \"../resources/store.js\";\nimport { runWithRequestContext } from \"../server/request-context.js\";\nimport {\n runAgentLoop,\n actionsToEngineTools,\n getOwnerActiveApiKey,\n type ActionEntry,\n} from \"../agent/production-agent.js\";\nimport {\n getStoredModelForEngine,\n resolveEngine,\n} from \"../agent/engine/index.js\";\nimport { createThread } from \"../chat-threads/store.js\";\nimport type { AgentChatEvent } from \"../agent/types.js\";\nimport { evaluateCondition } from \"./condition-evaluator.js\";\nimport type { TriggerFrontmatter } from \"./types.js\";\n\n// Re-use the job frontmatter parser — triggers extend the same format.\nconst FRONTMATTER_RE = /^---\\n([\\s\\S]*?)\\n---\\n?([\\s\\S]*)$/;\n\nfunction parseTriggerFrontmatter(content: string): {\n meta: TriggerFrontmatter;\n body: string;\n} {\n const match = content.match(FRONTMATTER_RE);\n if (!match) {\n return {\n meta: {\n schedule: \"\",\n enabled: false,\n triggerType: \"schedule\",\n mode: \"agentic\",\n },\n body: content,\n };\n }\n\n const yamlBlock = match[1];\n const body = match[2].trim();\n\n const meta: TriggerFrontmatter = {\n schedule: \"\",\n enabled: true,\n triggerType: \"schedule\",\n mode: \"agentic\",\n };\n\n for (const line of yamlBlock.split(\"\\n\")) {\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n const key = line.slice(0, colonIdx).trim();\n let value = line.slice(colonIdx + 1).trim();\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n\n switch (key) {\n case \"schedule\":\n meta.schedule = value;\n break;\n case \"enabled\":\n meta.enabled = value !== \"false\";\n break;\n case \"triggerType\":\n meta.triggerType =\n value === \"event\" || value === \"schedule\" ? value : \"schedule\";\n break;\n case \"event\":\n meta.event = value;\n break;\n case \"condition\":\n meta.condition = value;\n break;\n case \"mode\":\n meta.mode =\n value === \"deterministic\" || value === \"agentic\" ? value : \"agentic\";\n break;\n case \"domain\":\n meta.domain = value;\n break;\n case \"createdBy\":\n meta.createdBy = value;\n break;\n case \"orgId\":\n meta.orgId = value;\n break;\n case \"runAs\":\n meta.runAs =\n value === \"shared\" || value === \"creator\" ? value : undefined;\n break;\n case \"lastRun\":\n meta.lastRun = value;\n break;\n case \"lastStatus\":\n meta.lastStatus = value as TriggerFrontmatter[\"lastStatus\"];\n break;\n case \"lastError\":\n meta.lastError = value;\n break;\n case \"nextRun\":\n meta.nextRun = value;\n break;\n }\n }\n\n return { meta, body };\n}\n\nfunction buildTriggerContent(meta: TriggerFrontmatter, body: string): string {\n const lines = [\"---\"];\n lines.push(`schedule: \"${meta.schedule}\"`);\n lines.push(`enabled: ${meta.enabled}`);\n lines.push(`triggerType: ${meta.triggerType}`);\n if (meta.event) lines.push(`event: ${meta.event}`);\n if (meta.condition)\n lines.push(`condition: \"${meta.condition.replace(/\"/g, '\\\\\"')}\"`);\n lines.push(`mode: ${meta.mode}`);\n if (meta.domain) lines.push(`domain: ${meta.domain}`);\n if (meta.createdBy) lines.push(`createdBy: ${meta.createdBy}`);\n if (meta.orgId) lines.push(`orgId: ${meta.orgId}`);\n if (meta.runAs) lines.push(`runAs: ${meta.runAs}`);\n if (meta.lastRun) lines.push(`lastRun: ${meta.lastRun}`);\n if (meta.lastStatus) lines.push(`lastStatus: ${meta.lastStatus}`);\n if (meta.lastError)\n lines.push(`lastError: \"${meta.lastError.replace(/\"/g, '\\\\\"')}\"`);\n if (meta.nextRun) lines.push(`nextRun: ${meta.nextRun}`);\n lines.push(\"---\");\n lines.push(\"\");\n lines.push(body);\n return lines.join(\"\\n\");\n}\n\n// ─── Dispatcher deps (same pattern as SchedulerDeps) ────────────────────────\n\nexport interface TriggerDispatcherDeps {\n getActions: () => Record<string, ActionEntry>;\n getSystemPrompt: (owner: string) => Promise<string>;\n apiKey?: string;\n model?: string;\n /** App/template id used for org-scoped per-app model defaults. */\n appId?: string;\n}\n\n// Track active subscriptions (eventName -> subscription id) to avoid\n// double-subscribing AND so subscriptions for events that no longer have any\n// enabled trigger can be torn down — otherwise deleted/disabled triggers leave\n// phantom bus listeners that fire handleEvent forever.\nconst _eventSubscriptions = new Map<string, string>();\n// In-flight agentic dispatches keyed by `${owner}:${path}`. Guards against the\n// check-then-write TOCTOU window in handleEvent: two near-simultaneous fires of\n// the same event both pass the `lastStatus !== \"running\"` check (which has\n// several awaits before the DB is marked running) and would otherwise launch\n// two concurrent agent runs for one trigger. Sufficient for single-process\n// deployments; multi-instance would need a conditional DB update.\nconst _dispatchingTriggers = new Set<string>();\nlet _deps: TriggerDispatcherDeps | null = null;\n\n/**\n * Initialize the trigger dispatcher. Call once at server startup.\n * Loads all event-triggered jobs and subscribes to their events.\n */\nexport async function initTriggerDispatcher(\n deps: TriggerDispatcherDeps,\n): Promise<void> {\n _deps = deps;\n await refreshEventSubscriptions();\n}\n\n/**\n * Refresh event subscriptions from the resource store.\n * Call after creating/updating triggers.\n */\nexport async function refreshEventSubscriptions(): Promise<void> {\n try {\n const jobResources = await resourceListAllOwners(\"jobs/\");\n const eventNames = new Set<string>();\n\n for (const resource of jobResources) {\n if (!resource.path.endsWith(\".md\")) continue;\n const { meta } = parseTriggerFrontmatter(resource.content);\n if (meta.triggerType === \"event\" && meta.event && meta.enabled) {\n eventNames.add(meta.event);\n }\n }\n\n // Tear down subscriptions whose event no longer has any enabled trigger.\n for (const [eventName, subId] of [..._eventSubscriptions]) {\n if (!eventNames.has(eventName)) {\n unsubscribe(subId);\n _eventSubscriptions.delete(eventName);\n }\n }\n\n for (const eventName of eventNames) {\n if (!_eventSubscriptions.has(eventName)) {\n const subId = subscribe(eventName, (payload, eventMeta) =>\n handleEvent(eventName, payload, eventMeta),\n );\n _eventSubscriptions.set(eventName, subId);\n }\n }\n } catch (err) {\n console.error(\"[triggers] Failed to refresh event subscriptions:\", err);\n }\n}\n\nasync function handleEvent(\n eventName: string,\n payload: unknown,\n eventMeta: EventMeta,\n): Promise<void> {\n if (!_deps) return;\n\n try {\n const jobResources = await resourceListAllOwners(\"jobs/\");\n const matchingTriggers = jobResources.filter((r) => {\n if (!r.path.endsWith(\".md\")) return false;\n const { meta } = parseTriggerFrontmatter(r.content);\n // Scope: only dispatch triggers owned by the event's owner,\n // or shared triggers. Prevents cross-tenant trigger execution.\n if (\n eventMeta.owner &&\n r.owner !== eventMeta.owner &&\n r.owner !== \"__shared__\"\n ) {\n return false;\n }\n return (\n meta.triggerType === \"event\" &&\n meta.event === eventName &&\n meta.enabled &&\n meta.lastStatus !== \"running\"\n );\n });\n\n for (const resource of matchingTriggers) {\n const { meta, body } = parseTriggerFrontmatter(resource.content);\n if (!body.trim()) continue;\n\n // Resolve API key for condition evaluation\n const owner = meta.createdBy || resource.owner;\n const userApiKey = await getOwnerActiveApiKey(owner);\n const apiKey =\n userApiKey || _deps.apiKey || process.env.ANTHROPIC_API_KEY;\n if (!apiKey) {\n console.warn(\n `[triggers] No API key for trigger \"${resource.path}\" — skipping`,\n );\n continue;\n }\n\n // Evaluate condition\n const matches = await evaluateCondition(meta.condition, payload, apiKey);\n if (!matches) continue;\n\n // Dispatch. Guard against concurrent duplicate dispatch of the same\n // trigger (TOCTOU on lastStatus) with an in-process lock keyed on the\n // trigger's identity.\n const dispatchKey = `${resource.owner}:${resource.path}`;\n if (_dispatchingTriggers.has(dispatchKey)) continue;\n if (meta.mode === \"agentic\") {\n _dispatchingTriggers.add(dispatchKey);\n try {\n await dispatchAgentic(\n resource,\n meta,\n body,\n payload,\n eventMeta,\n apiKey,\n );\n } finally {\n _dispatchingTriggers.delete(dispatchKey);\n }\n } else {\n console.warn(\n `[triggers] Deterministic mode not yet implemented for \"${resource.path}\" — skipping`,\n );\n }\n }\n } catch (err) {\n console.error(`[triggers] Error handling event \"${eventName}\":`, err);\n }\n}\n\n/**\n * Validate that the run-as user still exists and (if scoped to an org) is\n * still a member of that org. Mirrors the recurring-jobs scheduler check\n * (audit 12 #10): event-triggered automations must stop firing when the\n * creator is removed/demoted.\n */\nasync function isTriggerRunAsStillValid(\n jobUserEmail: string,\n jobOrgId: string | undefined,\n): Promise<{ ok: boolean; reason?: string }> {\n if (jobUserEmail === \"__shared__\") return { ok: true };\n try {\n const { getDbExec } = await import(\"../db/client.js\");\n const db = getDbExec();\n const userResult = await db.execute({\n sql: `SELECT 1 FROM \"user\" WHERE email = ? LIMIT 1`,\n args: [jobUserEmail],\n });\n if (!userResult.rows || userResult.rows.length === 0) {\n return { ok: false, reason: `user \"${jobUserEmail}\" no longer exists` };\n }\n if (jobOrgId) {\n const memberResult = await db.execute({\n sql: `SELECT 1 FROM org_members WHERE org_id = ? AND LOWER(email) = LOWER(?) LIMIT 1`,\n args: [jobOrgId, jobUserEmail],\n });\n if (!memberResult.rows || memberResult.rows.length === 0) {\n return {\n ok: false,\n reason: `user \"${jobUserEmail}\" is no longer a member of org \"${jobOrgId}\"`,\n };\n }\n }\n return { ok: true };\n } catch (err: any) {\n const msg = err?.message?.toLowerCase() ?? \"\";\n if (\n msg.includes(\"does not exist\") ||\n msg.includes(\"no such table\") ||\n msg.includes(\"undefined table\")\n ) {\n return { ok: true };\n }\n console.warn(\n `[triggers] User/membership validation failed for \"${jobUserEmail}\":`,\n err?.message,\n );\n return { ok: true };\n }\n}\n\nasync function dispatchAgentic(\n resource: { path: string; owner: string; content: string },\n meta: TriggerFrontmatter,\n body: string,\n payload: unknown,\n eventMeta: EventMeta,\n apiKey: string,\n): Promise<void> {\n if (!_deps) return;\n\n const triggerName = resource.path.replace(/^jobs\\//, \"\").replace(/\\.md$/, \"\");\n const now = new Date();\n\n const jobUserEmail = meta.createdBy || resource.owner;\n const jobOrgId = meta.orgId ?? undefined;\n\n // SECURITY (audit 12 #10): re-validate the run-as user/membership on\n // every dispatch. Sharing revocation, user deletion, and org-member\n // removal must take effect for already-scheduled triggers. Skip the\n // dispatch on failure; leave the trigger entry alone for admin review.\n const validity = await isTriggerRunAsStillValid(jobUserEmail, jobOrgId);\n if (!validity.ok) {\n console.warn(\n `[triggers] Skipping trigger \"${triggerName}\": ${validity.reason}. ` +\n `User/membership no longer valid — leaving entry for admin review.`,\n );\n meta.lastRun = now.toISOString();\n meta.lastStatus = \"skipped\";\n meta.lastError = validity.reason;\n await resourcePut(\n resource.owner,\n resource.path,\n buildTriggerContent(meta, body),\n );\n return;\n }\n\n // Mark as running\n meta.lastRun = now.toISOString();\n meta.lastStatus = \"running\";\n meta.lastError = undefined;\n await resourcePut(\n resource.owner,\n resource.path,\n buildTriggerContent(meta, body),\n );\n\n await runWithRequestContext(\n { userEmail: jobUserEmail, orgId: jobOrgId },\n async () => {\n try {\n const actions = _deps!.getActions();\n const systemPrompt = await _deps!.getSystemPrompt(jobUserEmail);\n const tools = actionsToEngineTools(actions);\n\n const engine = await resolveEngine({\n apiKey,\n appId: _deps!.appId,\n });\n const model =\n _deps!.model ??\n (await getStoredModelForEngine(engine, { appId: _deps!.appId })) ??\n engine.defaultModel;\n const thread = await createThread(jobUserEmail, {\n title: `Trigger: ${triggerName} — ${now.toLocaleDateString()}`,\n });\n\n let payloadStr: string;\n try {\n payloadStr = JSON.stringify(payload, null, 2);\n } catch {\n payloadStr = String(payload);\n }\n\n const triggerText = `[Automation Trigger: ${triggerName}]\nEvent: ${meta.event}\nEvent ID: ${eventMeta.eventId}\nFired at: ${eventMeta.emittedAt}\n\nEvent payload:\n${payloadStr}\n\nExecute the following automation instructions:\n\n${body}`;\n\n const messages = [\n {\n role: \"user\" as const,\n content: [{ type: \"text\" as const, text: triggerText }],\n },\n ];\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 5 * 60 * 1000);\n\n const events: AgentChatEvent[] = [];\n\n try {\n await runAgentLoop({\n engine,\n model,\n systemPrompt,\n tools,\n messages,\n actions,\n send: (event) => events.push(event),\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timeout);\n }\n\n meta.lastStatus = \"success\";\n await resourcePut(\n resource.owner,\n resource.path,\n buildTriggerContent(meta, body),\n );\n\n console.log(`[triggers] \"${triggerName}\" completed successfully`);\n } catch (err: any) {\n meta.lastStatus = \"error\";\n meta.lastError = err?.message?.slice(0, 200) || \"Unknown error\";\n await resourcePut(\n resource.owner,\n resource.path,\n buildTriggerContent(meta, body),\n );\n console.error(`[triggers] \"${triggerName}\" failed:`, err?.message);\n }\n },\n );\n}\n\nexport { parseTriggerFrontmatter, buildTriggerContent };\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/usage/store.ts"],"names":[],"mappings":"AAuBA,eAAO,MAAM,sCAAsC,OAAO,CAAC;AAC3D,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAEhD,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,iBAAiB,CAAC;AAEzD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,yBAAyB,GAAG,uBAAuB,CAAC;IAC5D,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,iBAAiB,EAAE,gBAK/B,CAAC;AAEF,eAAO,MAAM,4BAA4B,EAAE,gBAO1C,CAAC;AAEF,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACpC,gBAAgB,CAIlB;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQjE;AAyBD,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wFAAwF;IACxF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/usage/store.ts"],"names":[],"mappings":"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"}
|