@agent-native/core 0.146.8 → 0.148.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/corpus/templates/analytics/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/corpus/templates/analytics/DEVELOPING.md +50 -3
- package/corpus/templates/analytics/actions/purge-first-party-analytics-postgres.ts +190 -0
- package/corpus/templates/analytics/app/i18n/index.ts +14 -29
- package/corpus/templates/analytics/app/i18n-data.ts +10 -10
- package/corpus/templates/analytics/app/pages/Settings.tsx +5 -21
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -3
- package/corpus/templates/analytics/app/routes/status.$slug.tsx +4 -2
- package/corpus/templates/analytics/scripts/first-party-analytics-bigquery-backfill.ts +253 -24
- package/corpus/templates/analytics/server/db/schema.ts +23 -0
- package/corpus/templates/analytics/server/handlers/first-party-analytics.ts +3 -1
- package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +1 -1
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +113 -13
- package/corpus/templates/analytics/server/lib/first-party-analytics-purge.ts +140 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-volume.ts +207 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +40 -9
- package/corpus/templates/analytics/server/lib/gcloud.ts +148 -20
- package/corpus/templates/analytics/server/plugins/db.ts +42 -0
- package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/corpus/templates/assets/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/corpus/templates/assets/app/i18n/index.ts +14 -29
- package/corpus/templates/brain/.agents/skills/adding-a-feature/SKILL.md +9 -0
- package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/corpus/templates/brain/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/corpus/templates/brain/app/i18n/index.ts +14 -29
- package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/corpus/templates/calendar/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/corpus/templates/chat/.agents/skills/adding-a-feature/SKILL.md +9 -0
- package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/corpus/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/corpus/templates/chat/app/i18n/index.ts +14 -29
- package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/corpus/templates/clips/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +2 -7
- package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +18 -13
- package/corpus/templates/clips/app/components/settings/ai-setup-section.tsx +87 -370
- package/corpus/templates/clips/app/components/settings/slack-section.tsx +59 -101
- package/corpus/templates/clips/app/components/settings/video-storage-section.tsx +136 -213
- package/corpus/templates/clips/app/i18n/en-US.ts +11 -8
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +16 -4
- package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +14 -5
- package/corpus/templates/clips/desktop/src/app.tsx +1 -1
- package/corpus/templates/clips/package.json +1 -0
- package/corpus/templates/clips/react-router.config.ts +7 -0
- package/corpus/templates/clips/server/plugins/auth.ts +6 -1
- package/corpus/templates/clips/shared/prerendered-public-paths.ts +21 -0
- package/corpus/templates/clips/shared/share-meta.ts +8 -3
- package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/corpus/templates/content/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/corpus/templates/content/app/i18n/index.ts +14 -29
- package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/corpus/templates/crm/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/corpus/templates/crm/app/i18n/index.ts +14 -29
- package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/corpus/templates/design/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/corpus/templates/design/actions/analyze-brand-assets.ts +15 -15
- package/corpus/templates/design/actions/import-code.ts +2 -43
- package/corpus/templates/design/app/i18n/index.ts +14 -29
- package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/corpus/templates/dispatch/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/corpus/templates/dispatch/app/chat-first-pages.ts +747 -0
- package/corpus/templates/dispatch/app/i18n-data.ts +182 -1
- package/corpus/templates/dispatch/app/routes/settings.tsx +62 -1
- package/corpus/templates/dispatch/changelog/2026-08-08-chat-first-navigation-keeps-conversations-central-while-open.md +6 -0
- package/corpus/templates/factory/.agents/skills/adding-a-feature/SKILL.md +9 -0
- package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/corpus/templates/factory/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/corpus/templates/factory/AGENTS.md +40 -49
- package/corpus/templates/factory/actions/babysit-agent-native-pull-request.ts +66 -0
- package/corpus/templates/factory/actions/evaluate-triage-item.ts +37 -0
- package/corpus/templates/factory/actions/get-slack-feedback-context.ts +20 -0
- package/corpus/templates/factory/actions/get-triage-item.ts +25 -4
- package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +55 -0
- package/corpus/templates/factory/actions/ingest-github-observation.ts +21 -0
- package/corpus/templates/factory/actions/list-factory-audit.ts +118 -0
- package/corpus/templates/factory/actions/list-factory-automations.ts +2 -1
- package/corpus/templates/factory/actions/list-triage-items.ts +24 -2
- package/corpus/templates/factory/actions/poll-github-sources.ts +60 -0
- package/corpus/templates/factory/actions/poll-sentry-errors.ts +39 -0
- package/corpus/templates/factory/actions/poll-slack-channel.ts +45 -1
- package/corpus/templates/factory/actions/run-factory-automation.ts +1 -1
- package/corpus/templates/factory/actions/save-factory-automation.ts +2 -2
- package/corpus/templates/factory/actions/start-builder-for-item.ts +51 -0
- package/corpus/templates/factory/app/components/factory/FactoryAuditView.tsx +685 -0
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +6 -0
- package/corpus/templates/factory/app/i18n/en-US.ts +25 -0
- package/corpus/templates/factory/app/routes/factory.tsx +203 -118
- package/corpus/templates/factory/changelog/2026-08-08-scheduler-health-moves-to-settings.md +6 -0
- package/corpus/templates/factory/server/db/schema.ts +37 -0
- package/corpus/templates/factory/server/lib/require-factory-automation.ts +5 -1
- package/corpus/templates/factory/server/plugins/factory-migrations.ts +29 -0
- package/corpus/templates/factory/server/triage/audit.ts +79 -0
- package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/corpus/templates/forms/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/corpus/templates/forms/app/i18n/index.ts +14 -29
- package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/corpus/templates/macros/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/corpus/templates/macros/app/i18n/index.ts +14 -29
- package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/corpus/templates/mail/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/corpus/templates/mail/app/i18n/index.ts +14 -29
- package/corpus/templates/plan/.agents/skills/adding-a-feature/SKILL.md +9 -0
- package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/corpus/templates/plan/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/corpus/templates/plan/app/components/plan/wireframe/Wireframe.tsx +7 -7
- package/corpus/templates/plan/app/i18n/index.ts +14 -29
- package/corpus/templates/plan/app/lib/plan-document-title.ts +3 -1
- package/corpus/templates/plan/app/root.tsx +1 -0
- package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/corpus/templates/slides/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/corpus/templates/slides/actions/analyze-brand-assets.ts +15 -15
- package/corpus/templates/slides/actions/import-code.ts +2 -43
- package/corpus/templates/slides/app/i18n/index.ts +14 -29
- package/corpus/templates/slides/server/routes/api/preview-images.get.ts +4 -1
- package/corpus/templates/tasks/.agents/skills/adding-a-feature/SKILL.md +9 -0
- package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +115 -68
- package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/corpus/templates/tasks/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/corpus/templates/tasks/app/components/layout/HeaderActions.tsx +1 -84
- package/corpus/templates/tasks/app/hooks/use-mobile.tsx +1 -21
- package/corpus/templates/tasks/app/i18n/index.ts +14 -29
- package/dist/agent/engine/ai-sdk-engine.js +2 -1
- package/dist/agent/engine/builder-engine.d.ts +11 -1
- package/dist/agent/engine/builder-engine.js +11 -4
- package/dist/agent/engine/openai-compatible-endpoint.d.ts +2 -0
- package/dist/agent/engine/openai-compatible-endpoint.js +5 -1
- package/dist/agent/engine/provider-endpoint-validation.d.ts +9 -0
- package/dist/agent/engine/provider-endpoint-validation.js +26 -0
- package/dist/agent/engine/provider-env-vars.d.ts +1 -1
- package/dist/agent/engine/provider-env-vars.js +1 -1
- package/dist/agent/engine/registry.d.ts +8 -6
- package/dist/agent/engine/registry.js +37 -24
- package/dist/agent/tool-search.d.ts +1 -0
- package/dist/agent/tool-search.js +17 -1
- package/dist/automations/service.js +4 -10
- package/dist/brand-kit/brand-signals.d.ts +13 -0
- package/dist/brand-kit/brand-signals.js +15 -0
- package/dist/brand-kit/index.d.ts +2 -2
- package/dist/brand-kit/index.js +1 -1
- package/dist/chat-threads/store.d.ts +2 -0
- package/dist/chat-threads/store.js +1 -1
- package/dist/cli/agent-plugin.d.ts +5 -2
- package/dist/cli/agent-plugin.js +17 -9
- package/dist/cli/code-agent-executor.js +25 -5
- package/dist/cli/code-agent-mcp-config.d.ts +21 -0
- package/dist/cli/code-agent-mcp-config.js +103 -0
- package/dist/cli/plan-blocks.d.ts +3 -18
- package/dist/cli/plan-blocks.js +2 -95
- package/dist/cli/plan-publish-store.d.ts +2 -61
- package/dist/cli/plan-publish-store.js +2 -127
- package/dist/cli/skills-content/connection.d.ts +1 -1
- package/dist/cli/skills-content/connection.js +1 -52
- package/dist/cli/skills-content/visual-recap-skill.d.ts +1 -1
- package/dist/cli/skills-content/visual-recap-skill.js +1 -553
- package/dist/client/NewWorkspaceAppFlow.js +4 -1
- package/dist/client/agent-chat/index.d.ts +4 -1
- package/dist/client/agent-chat/index.js +4 -1
- package/dist/client/agent-engine-key.d.ts +14 -4
- package/dist/client/agent-engine-key.js +51 -5
- package/dist/client/agent-page/AgentTabsPage.d.ts +1 -0
- package/dist/client/agent-page/AgentTabsPage.js +4 -3
- package/dist/client/agent-provider-catalog.d.ts +20 -0
- package/dist/client/agent-provider-catalog.js +100 -0
- package/dist/client/app-providers.d.ts +3 -1
- package/dist/client/app-providers.js +50 -5
- package/dist/client/blocks/index.d.ts +1 -0
- package/dist/client/blocks/index.js +1 -0
- package/dist/client/chat/run-recovery.js +4 -48
- package/dist/client/chat/runtime.js +63 -0
- package/dist/client/chat-first/agents-pane.d.ts +2 -0
- package/dist/client/chat-first/agents-pane.js +6 -0
- package/dist/client/chat-first/app-pane.d.ts +2 -0
- package/dist/client/chat-first/app-pane.js +6 -0
- package/dist/client/chat-first/apps-rail.d.ts +2 -0
- package/dist/client/chat-first/apps-rail.js +103 -0
- package/dist/client/chat-first/browser-pane.d.ts +2 -0
- package/dist/client/chat-first/browser-pane.js +64 -0
- package/dist/client/chat-first/chat-history.d.ts +12 -0
- package/dist/client/chat-first/chat-history.js +10 -0
- package/dist/client/chat-first/copy.d.ts +2 -0
- package/dist/client/chat-first/copy.js +117 -0
- package/dist/client/chat-first/index.d.ts +13 -0
- package/dist/client/chat-first/index.js +12 -0
- package/dist/client/chat-first/primary-nav.d.ts +8 -0
- package/dist/client/chat-first/primary-nav.js +6 -0
- package/dist/client/chat-first/session-watch-pane.d.ts +2 -0
- package/dist/client/chat-first/session-watch-pane.js +9 -0
- package/dist/client/chat-first/surface-panel.d.ts +9 -0
- package/dist/client/chat-first/surface-panel.js +9 -0
- package/dist/client/chat-first/surface-tabs.d.ts +9 -0
- package/dist/client/chat-first/surface-tabs.js +74 -0
- package/dist/client/chat-first/types.d.ts +74 -0
- package/dist/client/chat-first/types.js +1 -0
- package/dist/client/chat-first-agent-activity.d.ts +11 -0
- package/dist/client/chat-first-agent-activity.js +51 -0
- package/dist/client/chat-first-surface-panel-toggle.d.ts +5 -0
- package/dist/client/chat-first-surface-panel-toggle.js +6 -0
- package/dist/client/chat-first.d.ts +208 -0
- package/dist/client/chat-first.js +817 -0
- package/dist/client/extensions/ExtensionViewer.js +8 -1
- package/dist/client/i18n.d.ts +14 -0
- package/dist/client/i18n.js +18 -0
- package/dist/client/resources/McpIntegrationDialog.js +3 -2
- package/dist/client/resources/McpServerDetail.js +6 -11
- package/dist/client/resources/index.d.ts +1 -0
- package/dist/client/resources/index.js +1 -0
- package/dist/client/resources/use-mcp-servers.d.ts +20 -0
- package/dist/client/resources/use-mcp-servers.js +82 -46
- package/dist/client/settings/ProviderSetupForm.d.ts +21 -0
- package/dist/client/settings/ProviderSetupForm.js +145 -0
- package/dist/client/settings/SettingsPanel.js +52 -49
- package/dist/client/settings/index.d.ts +3 -0
- package/dist/client/settings/index.js +3 -0
- package/dist/coding-tools/run-code.d.ts +5 -0
- package/dist/coding-tools/run-code.js +196 -36
- package/dist/coding-tools/tool-orchestration.d.ts +10 -0
- package/dist/coding-tools/tool-orchestration.js +107 -0
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/jobs/background-automation-runner.js +19 -2
- package/dist/jobs/frontmatter.d.ts +9 -0
- package/dist/jobs/frontmatter.js +16 -0
- package/dist/jobs/scheduler-health.d.ts +20 -0
- package/dist/jobs/scheduler-health.js +86 -1
- package/dist/jobs/scheduler.js +46 -9
- package/dist/jobs/tools.d.ts +1 -1
- package/dist/jobs/tools.js +2 -7
- package/dist/localization/default-messages.d.ts +20 -0
- package/dist/localization/default-messages.js +25 -5
- package/dist/mcp-client/routes.d.ts +3 -0
- package/dist/mcp-client/routes.js +14 -0
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/onboarding/app-profile.js +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +8 -8
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/scripts/agent-engines/test-agent-engine.js +34 -10
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/agent-chat/run-code-tools.d.ts +1 -1
- package/dist/server/agent-chat/run-code-tools.js +3 -1
- package/dist/server/agent-chat-plugin.js +6 -6
- package/dist/server/agent-engine-api-key-route.js +43 -12
- package/dist/server/agents-bundle.d.ts +2 -0
- package/dist/server/agents-bundle.js +32 -2
- package/dist/server/auth.js +21 -5
- package/dist/server/credential-provider.d.ts +8 -2
- package/dist/server/credential-provider.js +14 -9
- package/dist/server/design-token-utils.d.ts +18 -0
- package/dist/server/design-token-utils.js +31 -0
- package/dist/server/onboarding-html.js +22 -2
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/chat-first-app-creation.d.ts +19 -0
- package/dist/shared/chat-first-app-creation.js +75 -0
- package/dist/shared/document-title.d.ts +7 -0
- package/dist/shared/document-title.js +29 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.js +2 -0
- package/dist/styles/chat-history-list.css +16 -0
- package/dist/templates/chat/.agents/skills/adding-a-feature/SKILL.md +9 -0
- package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
- package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/dist/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/dist/templates/chat/app/i18n/index.ts +14 -29
- package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +9 -0
- package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +115 -68
- package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/dist/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/dist/templates/default/app/i18n/index.ts +14 -29
- package/dist/templates/default/react-router.config.ts +18 -0
- package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/dist/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +9 -0
- package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +22 -9
- package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +115 -68
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/dist/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +7 -0
- package/dist/triggers/dispatcher.js +1 -8
- package/docs/content/environment-variables.mdx +39 -38
- package/package.json +5 -4
- package/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +9 -0
- package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +115 -68
- package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/src/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/src/templates/chat/app/i18n/index.ts +14 -29
- package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +9 -0
- package/src/templates/default/.agents/skills/frontend-design/SKILL.md +115 -68
- package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/src/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/src/templates/default/app/i18n/index.ts +14 -29
- package/src/templates/default/react-router.config.ts +18 -0
- package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/src/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +9 -0
- package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +22 -9
- package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +115 -68
- package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +18 -6
- package/src/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +8 -1
- package/src/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +7 -0
- package/corpus/templates/plan/app/components/plan/wireframe/sanitize-html.ts +0 -343
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { executeSandboxCode, } from "./run-code.js";
|
|
2
|
+
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
3
|
+
const MAX_TIMEOUT_MS = 120_000;
|
|
4
|
+
const DEFAULT_MAX_OUTPUT_CHARS = 50_000;
|
|
5
|
+
const MAX_OUTPUT_CHARS = 100_000;
|
|
6
|
+
const DEFAULT_MAX_TOOL_CALLS = 32;
|
|
7
|
+
const MAX_TOOL_CALLS = 128;
|
|
8
|
+
/**
|
|
9
|
+
* Create the bounded read-only orchestration tool.
|
|
10
|
+
*
|
|
11
|
+
* This deliberately has a separate registry entry from run-code. The latter
|
|
12
|
+
* retains its existing workspace staging and background-execution contract;
|
|
13
|
+
* this entry is for short fan-out, reduction, and aggregation over tools that
|
|
14
|
+
* the host can prove are read-only for the supplied arguments.
|
|
15
|
+
*/
|
|
16
|
+
export function createToolOrchestrationEntry(getActions) {
|
|
17
|
+
return {
|
|
18
|
+
readOnly: true,
|
|
19
|
+
allowInPlanMode: false,
|
|
20
|
+
timeoutMs: MAX_TIMEOUT_MS,
|
|
21
|
+
maxResultChars: MAX_OUTPUT_CHARS,
|
|
22
|
+
tool: {
|
|
23
|
+
description: [
|
|
24
|
+
"Run a bounded JavaScript orchestration script in the existing sandbox.",
|
|
25
|
+
"Use toolSearch(query) to discover read-only or conditionally read-only tools, then toolCall(name, args) to fan out, join, filter, and aggregate their results without putting every intermediate payload into chat. The host rechecks conditional policies against each argument object.",
|
|
26
|
+
"The host rechecks every child call: mutating actions, hidden actions, provider writes, non-GET/HEAD web requests, staging, file saves, and workspace writes are rejected. Call mutations directly as native tools.",
|
|
27
|
+
"The orchestration sandbox has no background mode, a strict child-tool-call budget, and a 20-page cap on each provider fetchAllPages call. It is a bounded orchestration path, not a general-purpose replacement for run-code.",
|
|
28
|
+
"Available globals: toolSearch(query?, options?), toolCall(name, args?), providerFetch(provider, path, init?), providerFetchAll(...), providerSearchAll(...), webFetch(url, init?), webRead(url, init?), workspaceRead(path, opts?), workspaceReadMeta(path, opts?), workspaceList(prefix?), and appAction(name, args?) for eligible read-only actions.",
|
|
29
|
+
"Print only the compact result needed by the conversation with console.log().",
|
|
30
|
+
].join(" "),
|
|
31
|
+
parameters: {
|
|
32
|
+
type: "object",
|
|
33
|
+
properties: {
|
|
34
|
+
code: {
|
|
35
|
+
type: "string",
|
|
36
|
+
description: "JavaScript source to execute. ESM syntax and top-level await are supported.",
|
|
37
|
+
},
|
|
38
|
+
timeoutMs: {
|
|
39
|
+
type: "number",
|
|
40
|
+
description: `Execution timeout in milliseconds. Default: ${DEFAULT_TIMEOUT_MS}; max: ${MAX_TIMEOUT_MS}.`,
|
|
41
|
+
},
|
|
42
|
+
maxOutputChars: {
|
|
43
|
+
type: "number",
|
|
44
|
+
description: `Maximum combined stdout+stderr characters. Default: ${DEFAULT_MAX_OUTPUT_CHARS}; max: ${MAX_OUTPUT_CHARS}.`,
|
|
45
|
+
},
|
|
46
|
+
maxToolCalls: {
|
|
47
|
+
type: "number",
|
|
48
|
+
description: `Maximum bridged child-tool calls. Default: ${DEFAULT_MAX_TOOL_CALLS}; max: ${MAX_TOOL_CALLS}.`,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
required: ["code"],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
run: async (args, context) => {
|
|
55
|
+
if (args.background || args.executionId) {
|
|
56
|
+
return "Error: tool-orchestration is foreground-only and does not support background or polling arguments. Use run-code for durable long-running execution.";
|
|
57
|
+
}
|
|
58
|
+
const code = typeof args.code === "string" ? args.code : "";
|
|
59
|
+
if (!code.trim())
|
|
60
|
+
return "Error: code is required.";
|
|
61
|
+
const requestedTimeout = Number(args.timeoutMs);
|
|
62
|
+
const timeoutMs = Number.isFinite(requestedTimeout) && requestedTimeout > 0
|
|
63
|
+
? Math.min(requestedTimeout, MAX_TIMEOUT_MS)
|
|
64
|
+
: DEFAULT_TIMEOUT_MS;
|
|
65
|
+
const requestedOutput = Number(args.maxOutputChars);
|
|
66
|
+
const maxOutputChars = Number.isFinite(requestedOutput) && requestedOutput > 0
|
|
67
|
+
? Math.min(requestedOutput, MAX_OUTPUT_CHARS)
|
|
68
|
+
: DEFAULT_MAX_OUTPUT_CHARS;
|
|
69
|
+
const requestedCalls = Number(args.maxToolCalls);
|
|
70
|
+
const maxToolCalls = Number.isFinite(requestedCalls) && requestedCalls > 0
|
|
71
|
+
? Math.min(Math.floor(requestedCalls), MAX_TOOL_CALLS)
|
|
72
|
+
: DEFAULT_MAX_TOOL_CALLS;
|
|
73
|
+
const result = await executeSandboxCode({
|
|
74
|
+
code,
|
|
75
|
+
timeoutMs,
|
|
76
|
+
getActions,
|
|
77
|
+
mode: "tool-orchestration",
|
|
78
|
+
maxToolCalls,
|
|
79
|
+
context,
|
|
80
|
+
});
|
|
81
|
+
return formatResult(result, timeoutMs, maxOutputChars);
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function formatResult(result, timeoutMs, maxOutputChars) {
|
|
86
|
+
const combined = [
|
|
87
|
+
result.stdout ? `stdout:\n${result.stdout}` : "",
|
|
88
|
+
result.stderr ? `stderr:\n${result.stderr}` : "",
|
|
89
|
+
]
|
|
90
|
+
.filter(Boolean)
|
|
91
|
+
.join("\n\n") || "(no output)";
|
|
92
|
+
const lines = [];
|
|
93
|
+
if (result.timedOut)
|
|
94
|
+
lines.push(`timedOut: true (${timeoutMs}ms)`);
|
|
95
|
+
if (result.exitCode !== 0 && result.exitCode !== null) {
|
|
96
|
+
lines.push(`exitCode: ${result.exitCode}`);
|
|
97
|
+
}
|
|
98
|
+
if (result.bridgeToolsUsed.length) {
|
|
99
|
+
lines.push(`bridgeToolsUsed: ${result.bridgeToolsUsed.join(", ")}`);
|
|
100
|
+
}
|
|
101
|
+
lines.push(combined);
|
|
102
|
+
const full = lines.join("\n\n");
|
|
103
|
+
if (full.length <= maxOutputChars)
|
|
104
|
+
return full;
|
|
105
|
+
const truncated = full.slice(0, maxOutputChars);
|
|
106
|
+
return `${truncated}\n\n...[truncated ${(full.length - maxOutputChars).toLocaleString()} chars]`;
|
|
107
|
+
}
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
|
|
|
26
26
|
* Body: { update: string (base64), requestSource?: string }
|
|
27
27
|
*/
|
|
28
28
|
export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
29
|
-
error: string;
|
|
30
29
|
ok?: undefined;
|
|
30
|
+
error: string;
|
|
31
31
|
} | {
|
|
32
32
|
error?: undefined;
|
|
33
33
|
ok: boolean;
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
|
|
14
14
|
*/
|
|
15
15
|
export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
16
|
-
error: string;
|
|
17
16
|
ok?: undefined;
|
|
17
|
+
error: string;
|
|
18
18
|
} | {
|
|
19
19
|
error?: undefined;
|
|
20
20
|
ok: boolean;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { collectFinalResponseTextFromAgentEvents } from "../a2a/response-text.js";
|
|
2
|
+
import { createBuilderEngine } from "../agent/engine/builder-engine.js";
|
|
2
3
|
import { getStoredModelForEngine, normalizeModelForEngine, resolveEngine, } from "../agent/engine/index.js";
|
|
3
4
|
import { actionsToEngineTools, filterInitialEngineTools, getOwnerActiveApiKey, } from "../agent/production-agent.js";
|
|
4
5
|
import { runAgentLoopDirectWithSoftTimeout } from "../agent/run-loop-with-resume.js";
|
|
@@ -9,6 +10,7 @@ import { resolveAutomationExecutionIdentity, } from "../automations/service.js";
|
|
|
9
10
|
import { createThread } from "../chat-threads/store.js";
|
|
10
11
|
import { queryOrgMembers } from "../org/context.js";
|
|
11
12
|
import { organizationIdFromResourceOwner, organizationResourceOwner, } from "../resources/store.js";
|
|
13
|
+
import { resolveBuilderCredentialsDetailed } from "../server/credential-provider.js";
|
|
12
14
|
import { runWithRequestContext, } from "../server/request-context.js";
|
|
13
15
|
import { attachAutomationRunThread, finishAutomationRun, startAutomationRun, } from "./run-history.js";
|
|
14
16
|
const BACKGROUND_RUN_STUCK_MS = 10 * 60_000;
|
|
@@ -236,18 +238,33 @@ async function executeBackgroundAutomation(options, deps, historyId) {
|
|
|
236
238
|
const availableTools = actionsToEngineTools(actions);
|
|
237
239
|
const tools = filterInitialEngineTools(availableTools, initialToolNames);
|
|
238
240
|
const userApiKey = await getOwnerActiveApiKey(ownerEmail);
|
|
239
|
-
const
|
|
241
|
+
const resolvedEngine = deps.engine ??
|
|
240
242
|
(await resolveEngine({
|
|
241
243
|
apiKey: userApiKey ?? deps.apiKey,
|
|
242
244
|
appId: deps.appId,
|
|
243
245
|
}));
|
|
246
|
+
// The run manager invokes its detached callback after the scheduler's
|
|
247
|
+
// setup stack has yielded. Capture the verified Builder pair while the
|
|
248
|
+
// owner/org identity is explicit, then keep it on the engine so a
|
|
249
|
+
// concurrent serverless run cannot make this call look unauthenticated.
|
|
250
|
+
const engine = !deps.engine && resolvedEngine.name === "builder"
|
|
251
|
+
? createBuilderEngine({
|
|
252
|
+
credentials: await resolveBuilderCredentialsDetailed({
|
|
253
|
+
userEmail: ownerEmail,
|
|
254
|
+
orgId,
|
|
255
|
+
}),
|
|
256
|
+
})
|
|
257
|
+
: resolvedEngine;
|
|
244
258
|
const modelCandidate = automation.meta.model ??
|
|
245
259
|
deps.model ??
|
|
246
260
|
(await getStoredModelForEngine(engine, { appId: deps.appId })) ??
|
|
247
261
|
engine.defaultModel;
|
|
248
262
|
const model = normalizeModelForEngine(engine, modelCandidate);
|
|
249
263
|
const systemPrompt = await deps.getSystemPrompt(ownerEmail);
|
|
250
|
-
const thread = await createThread(ownerEmail, {
|
|
264
|
+
const thread = await createThread(ownerEmail, {
|
|
265
|
+
title: threadTitle,
|
|
266
|
+
orgId: orgId ?? null,
|
|
267
|
+
});
|
|
251
268
|
const runId = createRunId(options.runIdPrefix);
|
|
252
269
|
await recordRunThread(historyId, thread.id, runId);
|
|
253
270
|
// Scheduled work is background work: it has no synchronous serverless
|
|
@@ -54,6 +54,15 @@ export interface JobFrontmatter {
|
|
|
54
54
|
*/
|
|
55
55
|
delegatedPolicyId?: string;
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Return whether a scheduler or trigger dispatcher may claim this resource.
|
|
59
|
+
*
|
|
60
|
+
* Personal legacy jobs have no app owner and remain compatible with the
|
|
61
|
+
* shared scheduler. An organization-owned resource without an explicit app
|
|
62
|
+
* owner is ambiguous, though: letting every installed app claim it can run
|
|
63
|
+
* the same job multiple times and with the wrong deployment credentials.
|
|
64
|
+
*/
|
|
65
|
+
export declare function jobBelongsToApp(meta: Pick<JobFrontmatter, "appId" | "orgId">, appId: string | null | undefined): boolean;
|
|
57
66
|
export interface JobResourceClassification {
|
|
58
67
|
kind: "job" | "automation";
|
|
59
68
|
hasExplicitTriggerType: boolean;
|
package/dist/jobs/frontmatter.js
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return whether a scheduler or trigger dispatcher may claim this resource.
|
|
3
|
+
*
|
|
4
|
+
* Personal legacy jobs have no app owner and remain compatible with the
|
|
5
|
+
* shared scheduler. An organization-owned resource without an explicit app
|
|
6
|
+
* owner is ambiguous, though: letting every installed app claim it can run
|
|
7
|
+
* the same job multiple times and with the wrong deployment credentials.
|
|
8
|
+
*/
|
|
9
|
+
export function jobBelongsToApp(meta, appId) {
|
|
10
|
+
const ownerAppId = meta.appId?.trim();
|
|
11
|
+
if (ownerAppId) {
|
|
12
|
+
const schedulerAppId = appId?.trim();
|
|
13
|
+
return Boolean(schedulerAppId && ownerAppId === schedulerAppId);
|
|
14
|
+
}
|
|
15
|
+
return !meta.orgId?.trim();
|
|
16
|
+
}
|
|
1
17
|
const MAX_JOB_MCP_TOOLS = 64;
|
|
2
18
|
const JOB_MCP_TOOL_NAME_RE = /^mcp__[^\s]+__[^\s]+$/;
|
|
3
19
|
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n)?([\s\S]*)$/;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { type MigrationEntry } from "../db/migrations.js";
|
|
2
|
+
export declare const AUTOMATION_SCHEDULER_LEASE_MS: number;
|
|
3
|
+
export declare const AUTOMATION_SCHEDULER_LEASE_RENEWAL_MS = 60000;
|
|
2
4
|
/** Authoritative release-time schema for recurring scheduler health. */
|
|
3
5
|
export declare const AUTOMATION_SCHEDULER_HEALTH_MIGRATIONS: MigrationEntry[];
|
|
4
6
|
export declare function runAutomationSchedulerHealthMigrations(nitroApp: unknown): Promise<void>;
|
|
@@ -11,6 +13,24 @@ export interface AutomationSchedulerHealth {
|
|
|
11
13
|
runtime: string | null;
|
|
12
14
|
updatedAt: number | null;
|
|
13
15
|
}
|
|
16
|
+
/** Claim one recurring scheduler across all deployments of an app. */
|
|
17
|
+
export declare function acquireAutomationSchedulerLease(input?: {
|
|
18
|
+
appId?: string;
|
|
19
|
+
now?: number;
|
|
20
|
+
leaseMs?: number;
|
|
21
|
+
}): Promise<string | null>;
|
|
22
|
+
/** Renew a lease while a slow sweep is still executing. */
|
|
23
|
+
export declare function renewAutomationSchedulerLease(input: {
|
|
24
|
+
appId?: string;
|
|
25
|
+
owner: string;
|
|
26
|
+
now?: number;
|
|
27
|
+
leaseMs?: number;
|
|
28
|
+
}): Promise<boolean>;
|
|
29
|
+
/** Release only the lease owned by this invocation. */
|
|
30
|
+
export declare function releaseAutomationSchedulerLease(input: {
|
|
31
|
+
appId?: string;
|
|
32
|
+
owner: string;
|
|
33
|
+
}): Promise<void>;
|
|
14
34
|
/** Record the last scheduler tick without making health bookkeeping fatal. */
|
|
15
35
|
export declare function recordAutomationSchedulerHealth(input: {
|
|
16
36
|
appId?: string;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { getDbExec, intType, isPostgres } from "../db/client.js";
|
|
2
3
|
import { ensureColumnExists, ensureIndexExists, ensureTableExists, } from "../db/ddl-guard.js";
|
|
3
4
|
import { runMigrations } from "../db/migrations.js";
|
|
4
5
|
const TABLE = "automation_scheduler_health";
|
|
5
6
|
const DEFAULT_APP_ID = "default";
|
|
6
7
|
const MAX_ERROR_LENGTH = 500;
|
|
8
|
+
export const AUTOMATION_SCHEDULER_LEASE_MS = 10 * 60_000;
|
|
9
|
+
export const AUTOMATION_SCHEDULER_LEASE_RENEWAL_MS = 60_000;
|
|
7
10
|
/** Authoritative release-time schema for recurring scheduler health. */
|
|
8
11
|
export const AUTOMATION_SCHEDULER_HEALTH_MIGRATIONS = [
|
|
9
12
|
{
|
|
@@ -30,6 +33,14 @@ export const AUTOMATION_SCHEDULER_HEALTH_MIGRATIONS = [
|
|
|
30
33
|
CREATE INDEX IF NOT EXISTS idx_${TABLE}_updated ON ${TABLE} (updated_at)
|
|
31
34
|
`,
|
|
32
35
|
},
|
|
36
|
+
{
|
|
37
|
+
version: 2,
|
|
38
|
+
name: "automation-scheduler-health-lease",
|
|
39
|
+
sql: `
|
|
40
|
+
ALTER TABLE ${TABLE} ADD COLUMN IF NOT EXISTS lease_owner TEXT;
|
|
41
|
+
ALTER TABLE ${TABLE} ADD COLUMN IF NOT EXISTS lease_expires_at INTEGER;
|
|
42
|
+
`,
|
|
43
|
+
},
|
|
33
44
|
];
|
|
34
45
|
export async function runAutomationSchedulerHealthMigrations(nitroApp) {
|
|
35
46
|
await runMigrations(AUTOMATION_SCHEDULER_HEALTH_MIGRATIONS, {
|
|
@@ -56,7 +67,9 @@ async function ensureHealthTable() {
|
|
|
56
67
|
last_dispatched_at ${intType()},
|
|
57
68
|
last_error TEXT,
|
|
58
69
|
runtime TEXT,
|
|
59
|
-
updated_at ${intType()} NOT NULL
|
|
70
|
+
updated_at ${intType()} NOT NULL,
|
|
71
|
+
lease_owner TEXT,
|
|
72
|
+
lease_expires_at ${intType()}
|
|
60
73
|
)
|
|
61
74
|
`;
|
|
62
75
|
const indexSql = `CREATE INDEX IF NOT EXISTS idx_${TABLE}_updated ON ${TABLE} (updated_at)`;
|
|
@@ -69,6 +82,8 @@ async function ensureHealthTable() {
|
|
|
69
82
|
await ensureColumnExists(TABLE, "last_error", `ALTER TABLE ${TABLE} ADD COLUMN IF NOT EXISTS last_error TEXT`);
|
|
70
83
|
await ensureColumnExists(TABLE, "runtime", `ALTER TABLE ${TABLE} ADD COLUMN IF NOT EXISTS runtime TEXT`);
|
|
71
84
|
await ensureColumnExists(TABLE, "updated_at", `ALTER TABLE ${TABLE} ADD COLUMN IF NOT EXISTS updated_at ${intType()}`);
|
|
85
|
+
await ensureColumnExists(TABLE, "lease_owner", `ALTER TABLE ${TABLE} ADD COLUMN IF NOT EXISTS lease_owner TEXT`);
|
|
86
|
+
await ensureColumnExists(TABLE, "lease_expires_at", `ALTER TABLE ${TABLE} ADD COLUMN IF NOT EXISTS lease_expires_at ${intType()}`);
|
|
72
87
|
await ensureIndexExists(`idx_${TABLE}_updated`, indexSql);
|
|
73
88
|
return;
|
|
74
89
|
}
|
|
@@ -84,6 +99,8 @@ async function ensureHealthTable() {
|
|
|
84
99
|
["last_error", "TEXT"],
|
|
85
100
|
["runtime", "TEXT"],
|
|
86
101
|
["updated_at", `${intType()} NOT NULL DEFAULT 0`],
|
|
102
|
+
["lease_owner", "TEXT"],
|
|
103
|
+
["lease_expires_at", `${intType()}`],
|
|
87
104
|
]) {
|
|
88
105
|
if (columns.has(name))
|
|
89
106
|
continue;
|
|
@@ -106,6 +123,74 @@ async function ensureHealthTable() {
|
|
|
106
123
|
}
|
|
107
124
|
return initPromise;
|
|
108
125
|
}
|
|
126
|
+
function isUniqueViolation(error) {
|
|
127
|
+
const candidate = error;
|
|
128
|
+
return (candidate?.code === "23505" ||
|
|
129
|
+
/unique|duplicate|already exists/i.test(String(candidate?.message ?? error)));
|
|
130
|
+
}
|
|
131
|
+
function leaseRowId(appId) {
|
|
132
|
+
return rowIdForScope(appId, null);
|
|
133
|
+
}
|
|
134
|
+
/** Claim one recurring scheduler across all deployments of an app. */
|
|
135
|
+
export async function acquireAutomationSchedulerLease(input = {}) {
|
|
136
|
+
await ensureHealthTable();
|
|
137
|
+
const appId = normalizeAppId(input.appId);
|
|
138
|
+
const id = leaseRowId(appId);
|
|
139
|
+
const now = input.now ?? Date.now();
|
|
140
|
+
const leaseMs = Math.max(input.leaseMs ?? AUTOMATION_SCHEDULER_LEASE_MS, AUTOMATION_SCHEDULER_LEASE_RENEWAL_MS * 2);
|
|
141
|
+
const owner = randomUUID();
|
|
142
|
+
const expiresAt = now + leaseMs;
|
|
143
|
+
const client = getDbExec();
|
|
144
|
+
try {
|
|
145
|
+
await client.execute({
|
|
146
|
+
sql: `INSERT INTO ${TABLE}
|
|
147
|
+
(id, app_id, org_id, last_checked_at, last_dispatched_at, last_error, runtime, updated_at, lease_owner, lease_expires_at)
|
|
148
|
+
VALUES (?, ?, NULL, NULL, NULL, NULL, 'recurring-jobs', ?, ?, ?)`,
|
|
149
|
+
args: [id, appId, now, owner, expiresAt],
|
|
150
|
+
});
|
|
151
|
+
return owner;
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
if (!isUniqueViolation(error))
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
const result = await client.execute({
|
|
158
|
+
sql: `UPDATE ${TABLE}
|
|
159
|
+
SET lease_owner = ?, lease_expires_at = ?, updated_at = ?
|
|
160
|
+
WHERE id = ?
|
|
161
|
+
AND (lease_owner IS NULL OR lease_expires_at IS NULL OR lease_expires_at <= ?)`,
|
|
162
|
+
args: [owner, expiresAt, now, id, now],
|
|
163
|
+
});
|
|
164
|
+
return Number(result.rowsAffected ?? 0) > 0 ? owner : null;
|
|
165
|
+
}
|
|
166
|
+
/** Renew a lease while a slow sweep is still executing. */
|
|
167
|
+
export async function renewAutomationSchedulerLease(input) {
|
|
168
|
+
await ensureHealthTable();
|
|
169
|
+
const now = input.now ?? Date.now();
|
|
170
|
+
const expiresAt = now + Math.max(input.leaseMs ?? AUTOMATION_SCHEDULER_LEASE_MS, 0);
|
|
171
|
+
const result = await getDbExec().execute({
|
|
172
|
+
sql: `UPDATE ${TABLE}
|
|
173
|
+
SET lease_expires_at = ?, updated_at = ?
|
|
174
|
+
WHERE id = ? AND lease_owner = ?`,
|
|
175
|
+
args: [
|
|
176
|
+
expiresAt,
|
|
177
|
+
now,
|
|
178
|
+
leaseRowId(normalizeAppId(input.appId)),
|
|
179
|
+
input.owner,
|
|
180
|
+
],
|
|
181
|
+
});
|
|
182
|
+
return Number(result.rowsAffected ?? 0) > 0;
|
|
183
|
+
}
|
|
184
|
+
/** Release only the lease owned by this invocation. */
|
|
185
|
+
export async function releaseAutomationSchedulerLease(input) {
|
|
186
|
+
await ensureHealthTable();
|
|
187
|
+
await getDbExec().execute({
|
|
188
|
+
sql: `UPDATE ${TABLE}
|
|
189
|
+
SET lease_owner = NULL, lease_expires_at = NULL, updated_at = ?
|
|
190
|
+
WHERE id = ? AND lease_owner = ?`,
|
|
191
|
+
args: [Date.now(), leaseRowId(normalizeAppId(input.appId)), input.owner],
|
|
192
|
+
});
|
|
193
|
+
}
|
|
109
194
|
function fromRow(row) {
|
|
110
195
|
return {
|
|
111
196
|
appId: String(row.app_id ?? DEFAULT_APP_ID),
|
package/dist/jobs/scheduler.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { resourceGetByPath, resourceListAllOwners, resourcePutIfCurrent, } from "../resources/store.js";
|
|
2
2
|
import { backgroundRunCutOffReason, isBackgroundAutomationRunActive, resolveBackgroundAutomationIdentity, runBackgroundAutomation, } from "./background-automation-runner.js";
|
|
3
3
|
import { nextOccurrence, isValidCron, describeCron, effectiveTimezone, } from "./cron.js";
|
|
4
|
-
import { buildJobResourceContent, parseJobResource, } from "./frontmatter.js";
|
|
4
|
+
import { buildJobResourceContent, jobBelongsToApp, parseJobResource, } from "./frontmatter.js";
|
|
5
5
|
import { claimAutomationRun, finishAutomationRun, getAutomationRun, listAutomationRuns, } from "./run-history.js";
|
|
6
|
-
import { recordAutomationSchedulerHealth } from "./scheduler-health.js";
|
|
6
|
+
import { acquireAutomationSchedulerLease, recordAutomationSchedulerHealth, releaseAutomationSchedulerLease, renewAutomationSchedulerLease, AUTOMATION_SCHEDULER_LEASE_RENEWAL_MS, } from "./scheduler-health.js";
|
|
7
7
|
// ─── Frontmatter parsing ────────────────────────────────────────────────────
|
|
8
8
|
export { classifyJobFrontmatter, classifyJobResource, normalizeJobMcpTools, parseJobResource, } from "./frontmatter.js";
|
|
9
9
|
export function parseJobFrontmatter(content) {
|
|
@@ -18,13 +18,6 @@ const MAX_IDENTITY_PREFLIGHTS_PER_TICK = MAX_CONCURRENT_SCHEDULED_JOBS * 4;
|
|
|
18
18
|
const IDENTITY_FAILURE_RETRY_MS = 5 * 60_000;
|
|
19
19
|
const _activeScheduledJobs = new Set();
|
|
20
20
|
const _preflightingScheduledJobs = new Set();
|
|
21
|
-
function jobBelongsToApp(meta, appId) {
|
|
22
|
-
const ownerAppId = meta.appId?.trim();
|
|
23
|
-
if (!ownerAppId)
|
|
24
|
-
return true;
|
|
25
|
-
const schedulerAppId = appId?.trim();
|
|
26
|
-
return Boolean(schedulerAppId && ownerAppId === schedulerAppId);
|
|
27
|
-
}
|
|
28
21
|
// Skip the DB query on every tick if we recently confirmed no jobs exist.
|
|
29
22
|
// `_hasJobsCache` is invalidated whenever a `jobs/*` resource is written or
|
|
30
23
|
// deleted (subscribed below), and refreshed at most every 5 minutes.
|
|
@@ -74,6 +67,43 @@ function subscribeToJobsResourceEvents() {
|
|
|
74
67
|
* one job cannot run twice while leaving other due jobs waiting behind it.
|
|
75
68
|
*/
|
|
76
69
|
export async function processRecurringJobs(deps) {
|
|
70
|
+
const leaseOwner = await acquireAutomationSchedulerLease({
|
|
71
|
+
appId: deps.appId,
|
|
72
|
+
});
|
|
73
|
+
if (!leaseOwner)
|
|
74
|
+
return;
|
|
75
|
+
const leaseRenewal = setInterval(() => {
|
|
76
|
+
void renewAutomationSchedulerLease({
|
|
77
|
+
appId: deps.appId,
|
|
78
|
+
owner: leaseOwner,
|
|
79
|
+
}).catch((error) => {
|
|
80
|
+
console.warn("[recurring-jobs] Scheduler lease renewal failed:", error instanceof Error ? error.message : error);
|
|
81
|
+
});
|
|
82
|
+
}, AUTOMATION_SCHEDULER_LEASE_RENEWAL_MS);
|
|
83
|
+
let primaryFailed = false;
|
|
84
|
+
try {
|
|
85
|
+
await processRecurringJobsWithLease(deps);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
primaryFailed = true;
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
clearInterval(leaseRenewal);
|
|
93
|
+
try {
|
|
94
|
+
await releaseAutomationSchedulerLease({
|
|
95
|
+
appId: deps.appId,
|
|
96
|
+
owner: leaseOwner,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (releaseError) {
|
|
100
|
+
console.warn("[recurring-jobs] Scheduler lease release failed:", releaseError instanceof Error ? releaseError.message : releaseError);
|
|
101
|
+
if (!primaryFailed)
|
|
102
|
+
throw releaseError;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function processRecurringJobsWithLease(deps) {
|
|
77
107
|
subscribeToJobsResourceEvents();
|
|
78
108
|
// Skip if we recently confirmed there are no job resources to run.
|
|
79
109
|
const nowMs = Date.now();
|
|
@@ -392,6 +422,13 @@ async function executeJob(resource, meta, body, deps, now, options = {}) {
|
|
|
392
422
|
requestContext,
|
|
393
423
|
...(options.historyId ? { historyId: options.historyId } : {}),
|
|
394
424
|
actionCaller: "automation",
|
|
425
|
+
actionAutomation: {
|
|
426
|
+
triggerId: resource.id,
|
|
427
|
+
triggerName: jobName,
|
|
428
|
+
...(meta.delegatedPolicyId
|
|
429
|
+
? { policyId: meta.delegatedPolicyId }
|
|
430
|
+
: {}),
|
|
431
|
+
},
|
|
395
432
|
}, deps);
|
|
396
433
|
await recordExecutionOutcome(resource, {
|
|
397
434
|
lastRun: meta.lastRun,
|
package/dist/jobs/tools.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ActionEntry } from "../agent/production-agent.js";
|
|
2
2
|
import { type JobFrontmatter } from "./scheduler.js";
|
|
3
|
-
export
|
|
3
|
+
export { jobBelongsToApp } from "./frontmatter.js";
|
|
4
4
|
/**
|
|
5
5
|
* Authorise a mutation (update / delete) against a job resource. When the
|
|
6
6
|
* job is in the SHARED scope the caller must either be the original
|
package/dist/jobs/tools.js
CHANGED
|
@@ -3,8 +3,9 @@ import { resolveUserSchedulingTimezone } from "../localization/user-timezone.js"
|
|
|
3
3
|
import { resourcePut, resourceGetByPath, resourceList, resourceDelete, organizationIdFromResourceOwner, sharedResourceOwner, SHARED_OWNER, } from "../resources/store.js";
|
|
4
4
|
import { getRequestUserEmail, getRequestOrgId, getIntegrationRequestContext, } from "../server/request-context.js";
|
|
5
5
|
import { isValidCron, nextOccurrence, describeCron, effectiveTimezone, isValidTimezone, } from "./cron.js";
|
|
6
|
-
import { classifyJobResource } from "./frontmatter.js";
|
|
6
|
+
import { classifyJobResource, jobBelongsToApp } from "./frontmatter.js";
|
|
7
7
|
import { parseJobFrontmatter, buildJobContent, normalizeJobMcpTools, } from "./scheduler.js";
|
|
8
|
+
export { jobBelongsToApp } from "./frontmatter.js";
|
|
8
9
|
function getOwner() {
|
|
9
10
|
const email = getRequestUserEmail();
|
|
10
11
|
if (!email)
|
|
@@ -14,12 +15,6 @@ function getOwner() {
|
|
|
14
15
|
function getSharedOwner() {
|
|
15
16
|
return sharedResourceOwner(getRequestOrgId());
|
|
16
17
|
}
|
|
17
|
-
export function jobBelongsToApp(meta, appId) {
|
|
18
|
-
const ownerAppId = meta.appId?.trim();
|
|
19
|
-
if (!ownerAppId)
|
|
20
|
-
return true;
|
|
21
|
-
return ownerAppId === appId?.trim();
|
|
22
|
-
}
|
|
23
18
|
/**
|
|
24
19
|
* Determine if the current request's user is an org owner/admin in the
|
|
25
20
|
* given org. Used to allow privileged users to update or delete shared
|
|
@@ -455,6 +455,26 @@ declare const messages: {
|
|
|
455
455
|
builderModelCredits: string;
|
|
456
456
|
addOwnKeys: string;
|
|
457
457
|
configureProviderKeys: string;
|
|
458
|
+
chooseProvider: string;
|
|
459
|
+
searchProviders: string;
|
|
460
|
+
noProvidersFound: string;
|
|
461
|
+
availableProviders: string;
|
|
462
|
+
configured: string;
|
|
463
|
+
apiKey: string;
|
|
464
|
+
localRuntime: string;
|
|
465
|
+
enterApiKey: string;
|
|
466
|
+
providerSetupFailed: string;
|
|
467
|
+
noApiKeyNeeded: string;
|
|
468
|
+
modelId: string;
|
|
469
|
+
endpointUrl: string;
|
|
470
|
+
optional: string;
|
|
471
|
+
ollamaEndpointHint: string;
|
|
472
|
+
compatibleEndpointHint: string;
|
|
473
|
+
savingProvider: string;
|
|
474
|
+
providerSaved: string;
|
|
475
|
+
useProvider: string;
|
|
476
|
+
saveAndUseProvider: string;
|
|
477
|
+
getApiKey: string;
|
|
458
478
|
checkingAiConnection: string;
|
|
459
479
|
delegatedAgent: {
|
|
460
480
|
asking: string;
|
|
@@ -389,7 +389,7 @@ const messages = {
|
|
|
389
389
|
threadDebugRefreshFailures: "Refresh failed runs",
|
|
390
390
|
threadDebugNoFailures: "No failed runs found.",
|
|
391
391
|
threadDebugCurrentDatabase: "Current Dispatch DB",
|
|
392
|
-
threadDebugSearchPlaceholder: "Search
|
|
392
|
+
threadDebugSearchPlaceholder: "Search threads or email",
|
|
393
393
|
threadDebugSearch: "Search",
|
|
394
394
|
threadDebugLookupPlaceholder: "Paste thread or request/run ID",
|
|
395
395
|
threadDebugInspect: "Inspect",
|
|
@@ -455,12 +455,32 @@ const messages = {
|
|
|
455
455
|
askAgentTitle: "Ask the agent",
|
|
456
456
|
askAgentPlaceholder: "Tell the agent what you want to do…",
|
|
457
457
|
connectAi: "Connect AI",
|
|
458
|
-
builderOrOwnKeys: "
|
|
458
|
+
builderOrOwnKeys: "Choose Builder.io or custom keys.",
|
|
459
459
|
connectBuilderIo: "Connect Builder.io",
|
|
460
460
|
connectingBuilder: "Connecting Builder.io…",
|
|
461
461
|
builderModelCredits: "Free credits for Claude, OpenAI & Gemini",
|
|
462
|
-
addOwnKeys: "
|
|
463
|
-
configureProviderKeys: "
|
|
462
|
+
addOwnKeys: "Custom keys",
|
|
463
|
+
configureProviderKeys: "Choose a provider.",
|
|
464
|
+
chooseProvider: "Choose a provider",
|
|
465
|
+
searchProviders: "Search providers...",
|
|
466
|
+
noProvidersFound: "No providers found.",
|
|
467
|
+
availableProviders: "Available providers",
|
|
468
|
+
configured: "Configured",
|
|
469
|
+
apiKey: "API key",
|
|
470
|
+
localRuntime: "Local",
|
|
471
|
+
enterApiKey: "Enter your {{provider}} API key.",
|
|
472
|
+
providerSetupFailed: "Could not configure this provider.",
|
|
473
|
+
noApiKeyNeeded: "No API key required.",
|
|
474
|
+
modelId: "Model ID",
|
|
475
|
+
endpointUrl: "Endpoint URL",
|
|
476
|
+
optional: "Optional",
|
|
477
|
+
ollamaEndpointHint: "Defaults to Ollama at http://localhost:11434.",
|
|
478
|
+
compatibleEndpointHint: "Use this for LiteLLM or another OpenAI-compatible gateway.",
|
|
479
|
+
savingProvider: "Saving...",
|
|
480
|
+
providerSaved: "Connected",
|
|
481
|
+
useProvider: "Use {{provider}}",
|
|
482
|
+
saveAndUseProvider: "Save and use {{provider}}",
|
|
483
|
+
getApiKey: "Get an API key",
|
|
464
484
|
checkingAiConnection: "Checking AI connection...",
|
|
465
485
|
delegatedAgent: {
|
|
466
486
|
asking: "Asking {{name}}...",
|
|
@@ -475,7 +495,7 @@ const messages = {
|
|
|
475
495
|
setupTitle: "Set up voice mode",
|
|
476
496
|
setupDescription: "Connect Builder.io to use managed voice with free credits, or add your own keys.",
|
|
477
497
|
connectBuilder: "Connect Builder.io",
|
|
478
|
-
useOpenAiKey: "
|
|
498
|
+
useOpenAiKey: "Custom keys",
|
|
479
499
|
startWithOpenAiKey: "Start with OpenAI key",
|
|
480
500
|
start: "Real-time voice",
|
|
481
501
|
keepDictating: "Dictate",
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
* hot-reload the configured server set.
|
|
7
7
|
*
|
|
8
8
|
* GET /_agent-native/mcp/servers list user + org servers
|
|
9
|
+
* GET /_agent-native/mcp/servers/runtime-config
|
|
10
|
+
* reserved; returns 410 until a
|
|
11
|
+
* desktop capability broker exists
|
|
9
12
|
* POST /_agent-native/mcp/servers add a server
|
|
10
13
|
* DELETE /_agent-native/mcp/servers/:id remove a server (scope via ?scope=)
|
|
11
14
|
* POST /_agent-native/mcp/servers/:id/test dry-run connect (no persist)
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
* hot-reload the configured server set.
|
|
7
7
|
*
|
|
8
8
|
* GET /_agent-native/mcp/servers list user + org servers
|
|
9
|
+
* GET /_agent-native/mcp/servers/runtime-config
|
|
10
|
+
* reserved; returns 410 until a
|
|
11
|
+
* desktop capability broker exists
|
|
9
12
|
* POST /_agent-native/mcp/servers add a server
|
|
10
13
|
* DELETE /_agent-native/mcp/servers/:id remove a server (scope via ?scope=)
|
|
11
14
|
* POST /_agent-native/mcp/servers/:id/test dry-run connect (no persist)
|
|
@@ -344,6 +347,11 @@ export function mountMcpServersRoutes(nitroApp, manager, options = {}) {
|
|
|
344
347
|
.replace(/\/+$/, "");
|
|
345
348
|
const parts = pathname ? pathname.split("/") : [];
|
|
346
349
|
setResponseHeader(event, "Content-Type", "application/json");
|
|
350
|
+
if (method === "GET" &&
|
|
351
|
+
parts.length === 1 &&
|
|
352
|
+
parts[0] === "runtime-config") {
|
|
353
|
+
return handleRuntimeConfig(event);
|
|
354
|
+
}
|
|
347
355
|
// POST /servers/test — dry-run a URL+headers before persisting
|
|
348
356
|
if (method === "POST" && parts.length === 1 && parts[0] === "test") {
|
|
349
357
|
return handleTestUrl(event);
|
|
@@ -700,6 +708,12 @@ async function handleList(event, manager) {
|
|
|
700
708
|
role,
|
|
701
709
|
};
|
|
702
710
|
}
|
|
711
|
+
function handleRuntimeConfig(event) {
|
|
712
|
+
setResponseStatus(event, 410);
|
|
713
|
+
return {
|
|
714
|
+
error: "Cleartext MCP runtime config requires a desktop capability broker and is not available over session-authenticated HTTP.",
|
|
715
|
+
};
|
|
716
|
+
}
|
|
703
717
|
async function handleAdd(event, manager) {
|
|
704
718
|
const body = (await readBody(event).catch(() => ({})));
|
|
705
719
|
const scope = body.scope === "org" ? "org" : body.scope === "user" ? "user" : null;
|
|
@@ -41,16 +41,16 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
+
error?: undefined;
|
|
44
45
|
summary: import("./types.js").TraceSummary;
|
|
45
46
|
spans: import("./types.js").TraceSpan[];
|
|
46
47
|
id?: undefined;
|
|
47
|
-
error?: undefined;
|
|
48
48
|
ok?: undefined;
|
|
49
49
|
} | {
|
|
50
|
+
error?: undefined;
|
|
50
51
|
summary?: undefined;
|
|
51
52
|
spans?: undefined;
|
|
52
53
|
id: string;
|
|
53
|
-
error?: undefined;
|
|
54
54
|
ok?: undefined;
|
|
55
55
|
} | {
|
|
56
56
|
summary?: undefined;
|
|
@@ -59,9 +59,9 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
59
59
|
error: any;
|
|
60
60
|
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
|
+
error?: undefined;
|
|
62
63
|
summary?: undefined;
|
|
63
64
|
spans?: undefined;
|
|
64
65
|
id?: undefined;
|
|
65
66
|
ok: boolean;
|
|
66
|
-
error?: undefined;
|
|
67
67
|
}>>;
|