@vellumai/assistant 0.8.12 → 0.9.0-staging.2
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/AGENTS.md +0 -14
- package/ARCHITECTURE.md +45 -45
- package/README.md +1 -1
- package/bun.lock +200 -154
- package/docs/architecture/integrations.md +3 -3
- package/docs/architecture/memory.md +2 -2
- package/docs/architecture/security.md +10 -10
- package/docs/runbook-trusted-contacts.md +12 -12
- package/docs/skills.md +6 -6
- package/docs/workflows-testing.md +221 -0
- package/docs/workflows.md +510 -0
- package/examples/plugins/echo/README.md +5 -5
- package/knip.json +2 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +105 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +12 -0
- package/openapi.yaml +7197 -5708
- package/package.json +8 -4
- package/scripts/generate-openapi.ts +66 -114
- package/src/__tests__/access-request-seed-content-blocks.test.ts +213 -0
- package/src/__tests__/adaptive-thinking-repair.test.ts +32 -3
- package/src/__tests__/agent-loop-output-hooks.test.ts +183 -0
- package/src/__tests__/agent-loop-regrowth-guard.test.ts +506 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +77 -0
- package/src/__tests__/app-compiler.test.ts +7 -1
- package/src/__tests__/app-dir-path-guard.test.ts +27 -3
- package/src/__tests__/app-executors.test.ts +43 -0
- package/src/__tests__/approval-cascade.test.ts +0 -5
- package/src/__tests__/approval-routes-http.test.ts +91 -0
- package/src/__tests__/assistant-stream-state.test.ts +107 -0
- package/src/__tests__/browser-fill-credential.test.ts +3 -3
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -1
- package/src/__tests__/compaction-events.test.ts +63 -7
- package/src/__tests__/compaction-trail-store.test.ts +74 -1
- package/src/__tests__/compaction.benchmark.test.ts +63 -41
- package/src/__tests__/compactor-low-watermark-cut.test.ts +349 -0
- package/src/__tests__/context-window-manager-compact-retry.test.ts +64 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -5
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -5
- package/src/__tests__/conversation-history-web-search.test.ts +7 -0
- package/src/__tests__/conversation-process-callsite.test.ts +0 -5
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -5
- package/src/__tests__/conversation-queue.test.ts +0 -5
- package/src/__tests__/conversation-slash-queue.test.ts +0 -5
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -5
- package/src/__tests__/conversation-speed-override.test.ts +0 -5
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +97 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-usage.test.ts +2 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -5
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -5
- package/src/__tests__/credential-broker-browser-fill.test.ts +2 -2
- package/src/__tests__/credential-broker-server-use.test.ts +2 -2
- package/src/__tests__/credential-broker.test.ts +1 -1
- package/src/__tests__/credential-prompt-route.test.ts +417 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault.test.ts +37 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +24 -0
- package/src/__tests__/dynamic-page-surface.test.ts +219 -0
- package/src/__tests__/empty-state-greeting-cache.test.ts +94 -0
- package/src/__tests__/gateway-flag-listener.test.ts +24 -7
- package/src/__tests__/guardian-action-sweep.test.ts +56 -219
- package/src/__tests__/guardian-routing-invariants.test.ts +138 -0
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +99 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +87 -1
- package/src/__tests__/llm-resolver.test.ts +115 -0
- package/src/__tests__/managed-profile-guard.test.ts +6 -5
- package/src/__tests__/max-tokens-continue-hook.test.ts +184 -0
- package/src/__tests__/media-generate-image.test.ts +20 -9
- package/src/__tests__/mock-gateway-ipc.ts +23 -0
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/normalize-onboarding.test.ts +26 -0
- package/src/__tests__/notification-decision-strategy.test.ts +4 -2
- package/src/__tests__/notification-telegram-adapter.test.ts +21 -3
- package/src/__tests__/pending-interactions-resolved-event.test.ts +62 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +72 -18
- package/src/__tests__/require-fresh-approval.test.ts +425 -1
- package/src/__tests__/resolve-app-id.test.ts +56 -0
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -0
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +408 -0
- package/src/__tests__/schedule-routes.test.ts +257 -4
- package/src/__tests__/schedule-store.test.ts +60 -0
- package/src/__tests__/schedule-tools.test.ts +247 -2
- package/src/__tests__/skill-execute-input.test.ts +85 -0
- package/src/__tests__/skill-secret-handling-guard.test.ts +21 -20
- package/src/__tests__/skills.test.ts +3 -3
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +1 -1
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +547 -0
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/task-progress-nudge-hook.test.ts +372 -0
- package/src/__tests__/task-scheduler.test.ts +299 -0
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +209 -0
- package/src/__tests__/tool-result-spool.test.ts +3 -1
- package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +181 -0
- package/src/__tests__/workspace-migration-103-upgrade-quality-profile-to-opus-4-8.test.ts +174 -0
- package/src/agent/compaction-circuit.ts +11 -0
- package/src/agent/loop.ts +181 -12
- package/src/api/constants/call-sites.ts +12 -0
- package/src/api/events/assistant-thinking-delta.ts +10 -0
- package/src/api/events/usage-update.ts +7 -0
- package/src/api/index.ts +4 -1
- package/src/api/responses/memory-v3-selection-log.ts +18 -11
- package/src/approvals/approval-primitive.ts +2 -2
- package/src/background-wake/background-wake-routes.test.ts +5 -2
- package/src/bundler/compiler-tools.ts +1 -1
- package/src/bundler/package-resolver.ts +0 -1
- package/src/calls/call-domain.ts +1 -1
- package/src/calls/guardian-action-sweep.ts +16 -93
- package/src/cli/AGENTS.md +4 -0
- package/src/cli/commands/__tests__/schedules.test.ts +430 -1
- package/src/cli/commands/credentials.ts +28 -24
- package/src/cli/commands/image-generation.ts +23 -9
- package/src/cli/commands/notifications.ts +1 -1
- package/src/cli/commands/plugins.ts +89 -46
- package/src/cli/commands/schedules.ts +384 -11
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +69 -5
- package/src/cli/lib/__tests__/install-from-github.test.ts +15 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +81 -4
- package/src/cli/lib/inspect-plugin.ts +62 -1
- package/src/cli/lib/install-from-github.ts +52 -5
- package/src/cli/lib/upgrade-plugin.ts +18 -0
- package/src/config/__tests__/workflows-schema.test.ts +60 -0
- package/src/config/bundled-skills/acp/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +6 -1
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +7 -1
- package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +7 -1
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +10 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +66 -19
- package/src/config/bundled-skills/image-studio/TOOLS.json +1 -6
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +22 -3
- package/src/config/bundled-skills/personal-page/SKILL.md +57 -0
- package/src/config/bundled-skills/personal-page/TOOLS.json +27 -0
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +17 -0
- package/src/config/bundled-skills/schedule/SKILL.md +7 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +48 -4
- package/src/config/bundled-skills/workflows/SKILL.md +214 -0
- package/src/config/bundled-skills/workflows/TOOLS.json +84 -0
- package/src/config/bundled-skills/workflows/tools/manage-workflows.ts +12 -0
- package/src/config/bundled-skills/workflows/tools/run-workflow.ts +12 -0
- package/src/config/bundled-tool-registry.ts +14 -2
- package/src/config/call-site-defaults.ts +5 -0
- package/src/config/feature-flag-registry.json +12 -4
- package/src/config/llm-context-resolution.ts +8 -0
- package/src/config/llm-resolver.ts +30 -0
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +22 -0
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +16 -0
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +77 -0
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +57 -0
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +28 -0
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +234 -0
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +48 -0
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +16 -0
- package/src/config/preloaded-apps/personal-page/src/index.html +20 -0
- package/src/config/preloaded-apps/personal-page/src/main.tsx +7 -0
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +82 -0
- package/src/config/preloaded-apps/personal-page/src/styles.css +759 -0
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/heartbeat.ts +4 -1
- package/src/config/schemas/llm.ts +33 -26
- package/src/config/schemas/memory-retrospective.ts +19 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/schemas/services.ts +5 -2
- package/src/config/schemas/workflows.ts +42 -0
- package/src/config/skills.ts +3 -3
- package/src/context/compactor.ts +273 -39
- package/src/context/post-turn-tool-result-truncation.ts +23 -6
- package/src/context/tool-result-spool.ts +12 -17
- package/src/credential-execution/executable-discovery.ts +1 -1
- package/src/credential-execution/process-manager.ts +37 -3
- package/src/credential-execution/prompted-credential.ts +205 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +14 -0
- package/src/daemon/conversation-process.ts +11 -2
- package/src/daemon/conversation-surfaces.ts +167 -3
- package/src/daemon/conversation-tool-setup.ts +103 -26
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +115 -11
- package/src/daemon/handlers/shared.ts +26 -14
- package/src/daemon/host-cu-proxy.ts +15 -12
- package/src/daemon/host-file-proxy.ts +15 -12
- package/src/daemon/host-transfer-proxy.ts +30 -24
- package/src/daemon/lifecycle.ts +40 -3
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/messages.ts +2 -10
- package/src/daemon/message-types/workflows.ts +49 -0
- package/src/daemon/parse-actual-tokens-from-error.test.ts +62 -1
- package/src/daemon/parse-actual-tokens-from-error.ts +43 -4
- package/src/daemon/process-message.ts +6 -0
- package/src/daemon/tool-setup-types.ts +57 -0
- package/src/daemon/wake-conversation-ops.ts +18 -0
- package/src/heartbeat/heartbeat-run-store.ts +8 -2
- package/src/home/feed-types.ts +1 -1
- package/src/ipc/gateway-flag-listener.ts +28 -6
- package/src/mcp/mcp-auth-state.ts +8 -20
- package/src/media/__tests__/image-models.test.ts +57 -0
- package/src/media/image-models.ts +66 -0
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +38 -0
- package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +12 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +911 -34
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +227 -5
- package/src/memory/__tests__/memory-retrospective-state.test.ts +195 -0
- package/src/memory/__tests__/preloaded-apps.test.ts +85 -0
- package/src/memory/auto-analysis-enqueue.ts +14 -1
- package/src/memory/compaction-log-store-clickhouse.ts +6 -4
- package/src/memory/conversation-crud.ts +9 -2
- package/src/memory/conversation-disk-view.ts +1 -1
- package/src/memory/conversation-queries.ts +22 -7
- package/src/memory/db-init.ts +20 -0
- package/src/memory/db-maintenance.ts +16 -0
- package/src/memory/embedding-runtime-manager.ts +1 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +112 -14
- package/src/memory/llm-request-log-source-local.ts +19 -1
- package/src/memory/llm-request-log-source.ts +35 -6
- package/src/memory/llm-request-log-store.ts +90 -2
- package/src/memory/memory-retrospective-constants.ts +9 -0
- package/src/memory/memory-retrospective-enqueue.ts +3 -6
- package/src/memory/memory-retrospective-fork-boundary.ts +94 -0
- package/src/memory/memory-retrospective-job.ts +500 -208
- package/src/memory/memory-retrospective-startup-cleanup.ts +97 -19
- package/src/memory/memory-retrospective-state.ts +85 -2
- package/src/memory/migrations/281-memory-retrospective-remembered-log.ts +40 -0
- package/src/memory/migrations/282-schedule-inference-profile.test.ts +77 -0
- package/src/memory/migrations/282-schedule-inference-profile.ts +26 -0
- package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.test.ts +102 -0
- package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.ts +53 -0
- package/src/memory/migrations/284-workflow-runs.ts +51 -0
- package/src/memory/migrations/285-schedule-workflow-mode.ts +26 -0
- package/src/memory/migrations/286-workflow-run-trust.ts +27 -0
- package/src/memory/migrations/287-conversation-origin-channel-index.ts +15 -0
- package/src/memory/migrations/288-backfill-origin-channel-from-bindings.ts +43 -0
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +115 -0
- package/src/memory/migrations/290-schedule-capabilities.test.ts +77 -0
- package/src/memory/migrations/290-schedule-capabilities.ts +25 -0
- package/src/memory/migrations/__tests__/281-memory-retrospective-remembered-log.test.ts +96 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +571 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/preloaded-apps.ts +116 -0
- package/src/memory/schema/infrastructure.ts +4 -0
- package/src/memory/schema/memory-core.ts +4 -0
- package/src/memory/v2/__tests__/concept-page-frontmatter-schema.test.ts +45 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +11 -7
- package/src/memory/v2/__tests__/page-store.test.ts +13 -2
- package/src/memory/v2/__tests__/qdrant.test.ts +24 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -6
- package/src/memory/v2/page-store.ts +4 -3
- package/src/memory/v2/qdrant.ts +42 -3
- package/src/memory/v2/types.ts +16 -10
- package/src/messaging/draft-store.ts +1 -1
- package/src/notifications/access-request-copy.ts +200 -113
- package/src/notifications/adapters/slack.ts +250 -111
- package/src/notifications/adapters/telegram.ts +7 -44
- package/src/notifications/approval-card-builder.ts +93 -0
- package/src/notifications/broadcaster.ts +74 -0
- package/src/notifications/conversation-pairing.ts +8 -6
- package/src/notifications/copy-composer.ts +32 -26
- package/src/notifications/decision-engine.ts +59 -7
- package/src/notifications/guardian-question-mode.ts +145 -155
- package/src/notifications/home-feed-side-effect.ts +28 -11
- package/src/notifications/notification-utils.ts +66 -0
- package/src/notifications/signal.ts +6 -0
- package/src/notifications/tool-approval-copy.ts +142 -0
- package/src/notifications/types.ts +19 -0
- package/src/permissions/threshold.ts +11 -0
- package/src/plugin-api/types.ts +16 -4
- package/src/plugins/defaults/compaction/window-manager.ts +44 -0
- package/src/plugins/defaults/index.ts +46 -0
- package/src/plugins/defaults/max-tokens-continue/continue-state-store.ts +53 -0
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +80 -0
- package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +20 -0
- package/src/plugins/defaults/max-tokens-continue/package.json +14 -0
- package/src/plugins/defaults/memory-retrieval/hooks/__tests__/user-prompt-submit.test.ts +37 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +29 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +8 -3
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +4 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +28 -18
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +67 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +122 -22
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +2 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +63 -1
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +61 -18
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +1 -1
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +39 -10
- package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +34 -1
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +112 -47
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +78 -15
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +206 -0
- package/src/plugins/defaults/task-progress-nudge/package.json +15 -0
- package/src/prompts/__tests__/system-prompt.test.ts +100 -1
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +5 -7
- package/src/prompts/normalize-onboarding.ts +2 -0
- package/src/prompts/persona-resolver.ts +3 -0
- package/src/prompts/system-prompt.ts +51 -2
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +3 -1
- package/src/prompts/templates/system-sections.ts +8 -3
- package/src/providers/call-site-routing.ts +6 -3
- package/src/providers/fireworks/client.ts +3 -0
- package/src/providers/inference/auth.ts +52 -46
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/__tests__/coerce-object-args.test.ts +105 -0
- package/src/providers/openai/chat-completions-provider.ts +47 -9
- package/src/providers/openai/coerce-object-args.ts +104 -0
- package/src/providers/retry.ts +8 -5
- package/src/providers/types.ts +10 -0
- package/src/runtime/__tests__/agent-wake.test.ts +629 -7
- package/src/runtime/access-request-helper.ts +16 -9
- package/src/runtime/agent-wake.ts +302 -51
- package/src/runtime/assistant-stream-state.ts +141 -8
- package/src/runtime/background-job-runner.ts +9 -0
- package/src/runtime/channel-approval-types.ts +1 -0
- package/src/runtime/channel-invite-transports/telegram.ts +6 -5
- package/src/runtime/channel-invite-transports/voice.ts +2 -2
- package/src/runtime/channel-invite-types.ts +4 -2
- package/src/runtime/channel-retry-sweep.ts +19 -41
- package/src/runtime/finalize-event-delivery.ts +72 -0
- package/src/runtime/guardian-action-message-composer.ts +0 -54
- package/src/runtime/http-server.ts +6 -14
- package/src/runtime/http-types.ts +0 -1
- package/src/runtime/message-composer-types.ts +0 -9
- package/src/runtime/middleware/__tests__/rate-limiter.test.ts +63 -0
- package/src/runtime/middleware/auth.ts +27 -3
- package/src/runtime/middleware/rate-limiter.ts +28 -1
- package/src/runtime/migrations/vbundle-builder.ts +6 -5
- package/src/runtime/pending-interactions.ts +20 -1
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +232 -173
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +18 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +436 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +11 -0
- package/src/runtime/routes/approval-routes.ts +35 -8
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -1
- package/src/runtime/routes/btw-routes.ts +37 -1
- package/src/runtime/routes/channel-delivery-routes.ts +11 -7
- package/src/runtime/routes/channel-route-definitions.ts +3 -0
- package/src/runtime/routes/channel-route-shared.ts +3 -1
- package/src/runtime/routes/consolidation-routes.ts +17 -13
- package/src/runtime/routes/conversation-compaction-routes.ts +159 -119
- package/src/runtime/routes/conversation-list-routes.ts +41 -4
- package/src/runtime/routes/conversation-query-routes.ts +196 -11
- package/src/runtime/routes/conversation-routes.ts +15 -1
- package/src/runtime/routes/credential-prompt-routes.ts +39 -17
- package/src/runtime/routes/empty-state-greeting-cache.ts +65 -0
- package/src/runtime/routes/heartbeat-routes.ts +18 -13
- package/src/runtime/routes/image-generation-routes.ts +20 -2
- package/src/runtime/routes/inbound-message-handler.ts +13 -12
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +32 -31
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +7 -5
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +7 -21
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +6 -15
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +1 -1
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/log-export/AGENTS.md +1 -1
- package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
- package/src/runtime/routes/migration-routes.ts +5 -9
- package/src/runtime/routes/plugins-routes.ts +26 -0
- package/src/runtime/routes/ps-routes.ts +10 -8
- package/src/runtime/routes/retrospective-routes.ts +235 -0
- package/src/runtime/routes/runs-pagination.ts +75 -0
- package/src/runtime/routes/schedule-routes.ts +367 -59
- package/src/runtime/routes/sounds-config-routes.ts +239 -0
- package/src/runtime/routes/surface-action-routes.ts +84 -4
- package/src/runtime/routes/workflow-routes.test.ts +372 -0
- package/src/runtime/routes/workflow-routes.ts +363 -0
- package/src/runtime/routes/workspace-routes.test.ts +61 -1
- package/src/runtime/routes/workspace-routes.ts +26 -1
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +38 -0
- package/src/runtime/services/analyze-conversation.ts +26 -13
- package/src/schedule/inference-profile.ts +28 -0
- package/src/schedule/schedule-store.ts +152 -4
- package/src/schedule/scheduler-types.ts +6 -0
- package/src/schedule/scheduler.ts +96 -0
- package/src/security/secret-allowlist.ts +1 -1
- package/src/skills/path-classifier.ts +1 -1
- package/src/tools/apps/executors.ts +23 -0
- package/src/tools/apps/resolve-app-id.ts +42 -0
- package/src/tools/browser/browser-execution.ts +9 -11
- package/src/tools/credentials/broker.ts +4 -4
- package/src/tools/credentials/vault.ts +26 -137
- package/src/tools/executor.ts +69 -0
- package/src/tools/flag-gated-tools.test.ts +76 -0
- package/src/tools/permission-checker.ts +8 -1
- package/src/tools/registry.ts +51 -0
- package/src/tools/schedule/create.ts +77 -1
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +73 -1
- package/src/tools/skills/execute.ts +56 -0
- package/src/tools/terminal/shell.ts +1 -1
- package/src/tools/ui-surface/definitions.ts +91 -2
- package/src/tools/workflows/manage-workflows.ts +183 -0
- package/src/tools/workflows/run-workflow.test.ts +442 -0
- package/src/tools/workflows/run-workflow.ts +88 -0
- package/src/types/onboarding-context.ts +2 -0
- package/src/usage/attribution.ts +24 -0
- package/src/util/canonicalize-identity.ts +12 -3
- package/src/util/platform.ts +17 -17
- package/src/watcher/__tests__/engine.test.ts +24 -0
- package/src/watcher/__tests__/telemetry.test.ts +135 -0
- package/src/watcher/engine.ts +7 -0
- package/src/watcher/telemetry.ts +74 -0
- package/src/workflows/capabilities.test.ts +365 -0
- package/src/workflows/capabilities.ts +359 -0
- package/src/workflows/deterministic-stringify.ts +27 -0
- package/src/workflows/engine-integration.test.ts +656 -0
- package/src/workflows/engine.test.ts +1144 -0
- package/src/workflows/engine.ts +1078 -0
- package/src/workflows/fanout-load.test.ts +168 -0
- package/src/workflows/journal-store.test.ts +369 -0
- package/src/workflows/journal-store.ts +470 -0
- package/src/workflows/leaf-runner.test.ts +704 -0
- package/src/workflows/leaf-runner.ts +589 -0
- package/src/workflows/library.test.ts +134 -0
- package/src/workflows/library.ts +124 -0
- package/src/workflows/run-manager.test.ts +711 -0
- package/src/workflows/run-manager.ts +593 -0
- package/src/workflows/sandbox-escape.test.ts +339 -0
- package/src/workflows/sandbox.test.ts +251 -0
- package/src/workflows/sandbox.ts +447 -0
- package/src/workspace/adaptive-thinking-repair.ts +33 -11
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +1 -1
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +1 -1
- package/src/workspace/migrations/026-backfill-install-meta.ts +1 -1
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +2 -1
- package/src/workspace/migrations/031-drop-user-md.ts +1 -4
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +1 -1
- package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +5 -2
- package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +1 -1
- package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +8 -2
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +41 -14
- package/src/workspace/migrations/102-preserve-heartbeat-enabled-for-existing-workspaces.ts +69 -0
- package/src/workspace/migrations/103-upgrade-quality-profile-to-opus-4-8.ts +83 -0
- package/src/workspace/migrations/104-recheck-adaptive-thinking-model-implied-anthropic.ts +133 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/migrations/runner.ts +1 -1
- package/tsconfig.json +1 -1
- package/src/__tests__/guardian-action-copy-generator.test.ts +0 -200
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -579
- package/src/__tests__/guardian-action-store.test.ts +0 -106
- package/src/daemon/guardian-action-generators.ts +0 -71
- package/src/memory/guardian-action-store.ts +0 -484
- package/src/runtime/guardian-action-grant-minter.ts +0 -150
|
@@ -7,11 +7,14 @@
|
|
|
7
7
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
|
+
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
11
|
+
import { getConfig } from "../../config/loader.js";
|
|
10
12
|
import { getOrCreateConversation } from "../../daemon/conversation-store.js";
|
|
11
13
|
import { INTERNAL_GUARDIAN_TRUST_CONTEXT } from "../../daemon/trust-context.js";
|
|
12
14
|
import { bootstrapConversation } from "../../memory/conversation-bootstrap.js";
|
|
13
15
|
import { getConversation } from "../../memory/conversation-crud.js";
|
|
14
16
|
import { getUsageCostForConversationWindow } from "../../memory/llm-usage-store.js";
|
|
17
|
+
import { validateScheduleInferenceProfile } from "../../schedule/inference-profile.js";
|
|
15
18
|
import {
|
|
16
19
|
describeRRuleExpression,
|
|
17
20
|
isSingleFireRRule,
|
|
@@ -37,10 +40,25 @@ import {
|
|
|
37
40
|
updateSchedule,
|
|
38
41
|
} from "../../schedule/schedule-store.js";
|
|
39
42
|
import { getScheduleUsageSummaries } from "../../schedule/schedule-usage-store.js";
|
|
43
|
+
import { areCoreToolsInitialized } from "../../tools/registry.js";
|
|
40
44
|
import { getLogger } from "../../util/logger.js";
|
|
45
|
+
import { normalizeCapabilityManifest } from "../../workflows/capabilities.js";
|
|
46
|
+
import { getWorkflowRunManager } from "../../workflows/run-manager.js";
|
|
41
47
|
import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
42
48
|
import { parseEpochMillisRange } from "./epoch-millis-range.js";
|
|
43
|
-
import {
|
|
49
|
+
import {
|
|
50
|
+
BadRequestError,
|
|
51
|
+
InternalError,
|
|
52
|
+
NotFoundError,
|
|
53
|
+
ServiceUnavailableError,
|
|
54
|
+
} from "./errors.js";
|
|
55
|
+
import {
|
|
56
|
+
paginateRuns,
|
|
57
|
+
parseRunsBeforeCursor,
|
|
58
|
+
parseRunsLimit,
|
|
59
|
+
RUNS_NEXT_CURSOR_SCHEMA,
|
|
60
|
+
RUNS_PAGINATION_QUERY_PARAMS,
|
|
61
|
+
} from "./runs-pagination.js";
|
|
44
62
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
45
63
|
|
|
46
64
|
const log = getLogger("schedule-routes");
|
|
@@ -66,16 +84,18 @@ const scheduleSchema = z.object({
|
|
|
66
84
|
maxRetries: z.number(),
|
|
67
85
|
retryBackoffMs: z.number(),
|
|
68
86
|
timeoutMs: z.number().nullable(),
|
|
87
|
+
inferenceProfile: z.string().nullable(),
|
|
69
88
|
createdFromConversationId: z.string().nullable(),
|
|
70
89
|
createdFromConversationExists: z.boolean(),
|
|
71
90
|
createdFromConversationArchivedAt: z.number().nullable(),
|
|
72
91
|
description: z.string(),
|
|
73
92
|
cadenceDescription: z.string(),
|
|
74
|
-
mode: z.enum(["notify", "execute", "script", "wake"]),
|
|
93
|
+
mode: z.enum(["notify", "execute", "script", "wake", "workflow"]),
|
|
75
94
|
status: z.enum(["active", "firing", "fired", "cancelled"]),
|
|
76
95
|
routingIntent: z.enum(["single_channel", "multi_channel", "all_channels"]),
|
|
77
96
|
reuseConversation: z.boolean(),
|
|
78
97
|
wakeConversationId: z.string().nullable(),
|
|
98
|
+
workflowName: z.string().nullable(),
|
|
79
99
|
isOneShot: z.boolean(),
|
|
80
100
|
});
|
|
81
101
|
|
|
@@ -156,6 +176,47 @@ function isOneShotForDisplay(
|
|
|
156
176
|
return job.syntax === "rrule" && isSingleFireRRule(job.cronExpression);
|
|
157
177
|
}
|
|
158
178
|
|
|
179
|
+
function serializeSchedule(
|
|
180
|
+
j: ScheduleJob,
|
|
181
|
+
sourceConversationCache: Map<string, CreatedFromConversationState>,
|
|
182
|
+
) {
|
|
183
|
+
const sourceConversation = getCreatedFromConversationState(
|
|
184
|
+
j.createdFromConversationId,
|
|
185
|
+
sourceConversationCache,
|
|
186
|
+
);
|
|
187
|
+
return {
|
|
188
|
+
id: j.id,
|
|
189
|
+
name: j.name,
|
|
190
|
+
enabled: j.enabled,
|
|
191
|
+
syntax: j.syntax,
|
|
192
|
+
expression: j.expression,
|
|
193
|
+
cronExpression: j.cronExpression,
|
|
194
|
+
timezone: j.timezone,
|
|
195
|
+
message: j.message,
|
|
196
|
+
script: j.script,
|
|
197
|
+
nextRunAt: j.nextRunAt,
|
|
198
|
+
lastRunAt: j.lastRunAt,
|
|
199
|
+
lastStatus: j.lastStatus,
|
|
200
|
+
retryCount: j.retryCount,
|
|
201
|
+
maxRetries: j.maxRetries,
|
|
202
|
+
retryBackoffMs: j.retryBackoffMs,
|
|
203
|
+
timeoutMs: j.timeoutMs,
|
|
204
|
+
inferenceProfile: j.inferenceProfile,
|
|
205
|
+
createdFromConversationId: j.createdFromConversationId,
|
|
206
|
+
createdFromConversationExists: sourceConversation.exists,
|
|
207
|
+
createdFromConversationArchivedAt: sourceConversation.archivedAt,
|
|
208
|
+
description: j.description,
|
|
209
|
+
cadenceDescription: getCadenceDescription(j),
|
|
210
|
+
mode: j.mode,
|
|
211
|
+
status: j.status,
|
|
212
|
+
routingIntent: j.routingIntent,
|
|
213
|
+
reuseConversation: j.reuseConversation,
|
|
214
|
+
wakeConversationId: j.wakeConversationId,
|
|
215
|
+
workflowName: j.workflowName,
|
|
216
|
+
isOneShot: isOneShotForDisplay(j),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
159
220
|
function handleListSchedules(queryParams: Record<string, string>) {
|
|
160
221
|
const includeAll = queryParams.include_all === "true";
|
|
161
222
|
const jobs = listSchedules();
|
|
@@ -167,44 +228,20 @@ function handleListSchedules(queryParams: Record<string, string>) {
|
|
|
167
228
|
CreatedFromConversationState
|
|
168
229
|
>();
|
|
169
230
|
return {
|
|
170
|
-
schedules: filtered.map((j) =>
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
sourceConversationCache,
|
|
174
|
-
);
|
|
175
|
-
return {
|
|
176
|
-
id: j.id,
|
|
177
|
-
name: j.name,
|
|
178
|
-
enabled: j.enabled,
|
|
179
|
-
syntax: j.syntax,
|
|
180
|
-
expression: j.expression,
|
|
181
|
-
cronExpression: j.cronExpression,
|
|
182
|
-
timezone: j.timezone,
|
|
183
|
-
message: j.message,
|
|
184
|
-
script: j.script,
|
|
185
|
-
nextRunAt: j.nextRunAt,
|
|
186
|
-
lastRunAt: j.lastRunAt,
|
|
187
|
-
lastStatus: j.lastStatus,
|
|
188
|
-
retryCount: j.retryCount,
|
|
189
|
-
maxRetries: j.maxRetries,
|
|
190
|
-
retryBackoffMs: j.retryBackoffMs,
|
|
191
|
-
timeoutMs: j.timeoutMs,
|
|
192
|
-
createdFromConversationId: j.createdFromConversationId,
|
|
193
|
-
createdFromConversationExists: sourceConversation.exists,
|
|
194
|
-
createdFromConversationArchivedAt: sourceConversation.archivedAt,
|
|
195
|
-
description: j.description,
|
|
196
|
-
cadenceDescription: getCadenceDescription(j),
|
|
197
|
-
mode: j.mode,
|
|
198
|
-
status: j.status,
|
|
199
|
-
routingIntent: j.routingIntent,
|
|
200
|
-
reuseConversation: j.reuseConversation,
|
|
201
|
-
wakeConversationId: j.wakeConversationId,
|
|
202
|
-
isOneShot: isOneShotForDisplay(j),
|
|
203
|
-
};
|
|
204
|
-
}),
|
|
231
|
+
schedules: filtered.map((j) =>
|
|
232
|
+
serializeSchedule(j, sourceConversationCache),
|
|
233
|
+
),
|
|
205
234
|
};
|
|
206
235
|
}
|
|
207
236
|
|
|
237
|
+
function handleGetSchedule(id: string) {
|
|
238
|
+
const job = getSchedule(id);
|
|
239
|
+
if (!job) {
|
|
240
|
+
throw new NotFoundError("Schedule not found");
|
|
241
|
+
}
|
|
242
|
+
return { schedule: serializeSchedule(job, new Map()) };
|
|
243
|
+
}
|
|
244
|
+
|
|
208
245
|
function handleCreateSchedule(body: Record<string, unknown>) {
|
|
209
246
|
const name = typeof body.name === "string" ? body.name.trim() : "";
|
|
210
247
|
const expression =
|
|
@@ -221,19 +258,36 @@ function handleCreateSchedule(body: Record<string, unknown>) {
|
|
|
221
258
|
const timezone = timezoneRaw === "" ? null : timezoneRaw;
|
|
222
259
|
const enabled = body.enabled !== false;
|
|
223
260
|
const mode = (body.mode as string | undefined) ?? "execute";
|
|
261
|
+
const inferenceProfile =
|
|
262
|
+
body.inferenceProfile == null ? null : body.inferenceProfile;
|
|
263
|
+
|
|
264
|
+
if (inferenceProfile !== null) {
|
|
265
|
+
if (typeof inferenceProfile !== "string") {
|
|
266
|
+
throw new BadRequestError("inferenceProfile must be a string or null");
|
|
267
|
+
}
|
|
268
|
+
const profileError = validateScheduleInferenceProfile(inferenceProfile);
|
|
269
|
+
if (profileError) throw new BadRequestError(profileError);
|
|
270
|
+
}
|
|
224
271
|
|
|
225
272
|
if (!name) throw new BadRequestError("name is required");
|
|
226
273
|
if (!expression) throw new BadRequestError("expression is required");
|
|
227
|
-
|
|
274
|
+
// Workflow-mode runs trigger a saved workflow by name and ignore `job.message`
|
|
275
|
+
// entirely (see the workflow branch below), so only require a message for the
|
|
276
|
+
// execute path. Requiring it for workflow mode would force API/UI callers to
|
|
277
|
+
// pass an unused dummy string.
|
|
278
|
+
if (mode !== "workflow" && !message) {
|
|
279
|
+
throw new BadRequestError("message is required");
|
|
280
|
+
}
|
|
228
281
|
if (description === "") {
|
|
229
282
|
throw new BadRequestError("description is required");
|
|
230
283
|
}
|
|
231
284
|
|
|
232
|
-
// The settings UI only exposes execute mode
|
|
233
|
-
//
|
|
234
|
-
|
|
285
|
+
// The settings UI only exposes execute mode; `workflow` mode is reachable
|
|
286
|
+
// here (flag-gated) and via the schedule_create LLM tool. All other modes
|
|
287
|
+
// remain tool-only.
|
|
288
|
+
if (mode !== "execute" && mode !== "workflow") {
|
|
235
289
|
throw new BadRequestError(
|
|
236
|
-
"Only 'execute'
|
|
290
|
+
"Only 'execute' and 'workflow' modes are supported by this endpoint",
|
|
237
291
|
);
|
|
238
292
|
}
|
|
239
293
|
|
|
@@ -244,6 +298,39 @@ function handleCreateSchedule(body: Record<string, unknown>) {
|
|
|
244
298
|
);
|
|
245
299
|
}
|
|
246
300
|
|
|
301
|
+
if (mode === "workflow") {
|
|
302
|
+
assertWorkflowsEnabled();
|
|
303
|
+
const workflowName =
|
|
304
|
+
typeof body.workflowName === "string" ? body.workflowName.trim() : "";
|
|
305
|
+
if (!workflowName) {
|
|
306
|
+
throw new BadRequestError(
|
|
307
|
+
"workflowName is required for workflow-mode schedules",
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
try {
|
|
311
|
+
const job = createSchedule({
|
|
312
|
+
name,
|
|
313
|
+
description,
|
|
314
|
+
message,
|
|
315
|
+
mode: "workflow",
|
|
316
|
+
workflowName,
|
|
317
|
+
workflowArgs: body.workflowArgs,
|
|
318
|
+
enabled,
|
|
319
|
+
timezone,
|
|
320
|
+
expression: normalized.expression,
|
|
321
|
+
syntax: normalized.syntax,
|
|
322
|
+
});
|
|
323
|
+
log.info(
|
|
324
|
+
{ id: job.id, name: job.name, workflowName },
|
|
325
|
+
"Workflow schedule created",
|
|
326
|
+
);
|
|
327
|
+
} catch (err) {
|
|
328
|
+
if (err instanceof Error) throw new BadRequestError(err.message);
|
|
329
|
+
throw err;
|
|
330
|
+
}
|
|
331
|
+
return handleListSchedules({});
|
|
332
|
+
}
|
|
333
|
+
|
|
247
334
|
try {
|
|
248
335
|
const job = createSchedule({
|
|
249
336
|
name,
|
|
@@ -254,6 +341,7 @@ function handleCreateSchedule(body: Record<string, unknown>) {
|
|
|
254
341
|
timezone,
|
|
255
342
|
expression: normalized.expression,
|
|
256
343
|
syntax: normalized.syntax,
|
|
344
|
+
inferenceProfile,
|
|
257
345
|
});
|
|
258
346
|
log.info({ id: job.id, name: job.name }, "Schedule created");
|
|
259
347
|
} catch (err) {
|
|
@@ -263,6 +351,17 @@ function handleCreateSchedule(body: Record<string, unknown>) {
|
|
|
263
351
|
return handleListSchedules({});
|
|
264
352
|
}
|
|
265
353
|
|
|
354
|
+
/**
|
|
355
|
+
* Reject workflow-mode schedule operations when the `workflows` feature flag
|
|
356
|
+
* is off. Scheduled workflow runs would fail at trigger time (the run manager
|
|
357
|
+
* hard-fails with WorkflowsDisabledError), so we fail fast at the route.
|
|
358
|
+
*/
|
|
359
|
+
function assertWorkflowsEnabled(): void {
|
|
360
|
+
if (!isAssistantFeatureFlagEnabled("workflows", getConfig())) {
|
|
361
|
+
throw new BadRequestError("Workflows are not enabled.");
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
266
365
|
function handleToggleSchedule(id: string, body: Record<string, unknown>) {
|
|
267
366
|
const enabled = body.enabled;
|
|
268
367
|
if (typeof enabled !== "boolean") {
|
|
@@ -295,7 +394,13 @@ function handleCancelSchedule(id: string) {
|
|
|
295
394
|
return handleListSchedules({});
|
|
296
395
|
}
|
|
297
396
|
|
|
298
|
-
const VALID_MODES = [
|
|
397
|
+
const VALID_MODES = [
|
|
398
|
+
"notify",
|
|
399
|
+
"execute",
|
|
400
|
+
"script",
|
|
401
|
+
"wake",
|
|
402
|
+
"workflow",
|
|
403
|
+
] as const;
|
|
299
404
|
const VALID_ROUTING_INTENTS = [
|
|
300
405
|
"single_channel",
|
|
301
406
|
"multi_channel",
|
|
@@ -322,6 +427,39 @@ function handleUpdateSchedule(id: string, body: Record<string, unknown>) {
|
|
|
322
427
|
);
|
|
323
428
|
}
|
|
324
429
|
|
|
430
|
+
// Switching a schedule into workflow mode requires the workflows flag.
|
|
431
|
+
if (body.mode === "workflow") {
|
|
432
|
+
assertWorkflowsEnabled();
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Mirror the create-side validation: a schedule whose RESULTING mode is
|
|
436
|
+
// `workflow` must carry a non-empty `workflowName`. Without this, a PATCH can
|
|
437
|
+
// leave a workflow-mode schedule nameless — the scheduler then hits the
|
|
438
|
+
// `!job.workflowName` skip branch and a one-shot job claimed as `firing`
|
|
439
|
+
// never calls completeOneShot/retry, wedging it `firing` forever. We compute
|
|
440
|
+
// the post-update state (the body's value if present, else the persisted one)
|
|
441
|
+
// so both "switch to workflow without a name" and "clear the name on an
|
|
442
|
+
// already-workflow schedule" are rejected. Skip when the schedule does not
|
|
443
|
+
// exist — the updateSchedule call below surfaces that as NotFound.
|
|
444
|
+
const existing = getSchedule(id);
|
|
445
|
+
if (existing) {
|
|
446
|
+
const resultingMode =
|
|
447
|
+
"mode" in body ? (body.mode as string) : existing.mode;
|
|
448
|
+
if (resultingMode === "workflow") {
|
|
449
|
+
const resultingWorkflowName =
|
|
450
|
+
"workflowName" in body
|
|
451
|
+
? typeof body.workflowName === "string"
|
|
452
|
+
? body.workflowName.trim()
|
|
453
|
+
: ""
|
|
454
|
+
: (existing.workflowName ?? "");
|
|
455
|
+
if (!resultingWorkflowName) {
|
|
456
|
+
throw new BadRequestError(
|
|
457
|
+
"workflowName is required for workflow-mode schedules",
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
325
463
|
const updates: Record<string, unknown> = {};
|
|
326
464
|
for (const key of [
|
|
327
465
|
"name",
|
|
@@ -334,6 +472,8 @@ function handleUpdateSchedule(id: string, body: Record<string, unknown>) {
|
|
|
334
472
|
"quiet",
|
|
335
473
|
"reuseConversation",
|
|
336
474
|
"wakeConversationId",
|
|
475
|
+
"workflowName",
|
|
476
|
+
"workflowArgs",
|
|
337
477
|
"maxRetries",
|
|
338
478
|
"retryBackoffMs",
|
|
339
479
|
"timeoutMs",
|
|
@@ -343,6 +483,21 @@ function handleUpdateSchedule(id: string, body: Record<string, unknown>) {
|
|
|
343
483
|
}
|
|
344
484
|
}
|
|
345
485
|
|
|
486
|
+
// Re-derive syntax whenever the expression changes, mirroring the create
|
|
487
|
+
// handler. Without this, switching an expression between cron and rrule
|
|
488
|
+
// would validate the new expression against the schedule's old syntax.
|
|
489
|
+
if (typeof updates.expression === "string") {
|
|
490
|
+
const normalized = normalizeScheduleSyntax({
|
|
491
|
+
expression: updates.expression,
|
|
492
|
+
});
|
|
493
|
+
if (!normalized) {
|
|
494
|
+
throw new BadRequestError(
|
|
495
|
+
"expression could not be parsed as cron or rrule",
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
updates.syntax = normalized.syntax;
|
|
499
|
+
}
|
|
500
|
+
|
|
346
501
|
if ("description" in body) {
|
|
347
502
|
const description =
|
|
348
503
|
typeof body.description === "string" ? body.description.trim() : "";
|
|
@@ -360,6 +515,20 @@ function handleUpdateSchedule(id: string, body: Record<string, unknown>) {
|
|
|
360
515
|
if (timeoutError) throw new BadRequestError(timeoutError);
|
|
361
516
|
}
|
|
362
517
|
|
|
518
|
+
// Inference profile: null clears the override (back to the default
|
|
519
|
+
// main-agent model selection); a string must name a configured profile.
|
|
520
|
+
if ("inferenceProfile" in body) {
|
|
521
|
+
const inferenceProfile = body.inferenceProfile;
|
|
522
|
+
if (inferenceProfile !== null && typeof inferenceProfile !== "string") {
|
|
523
|
+
throw new BadRequestError("inferenceProfile must be a string or null");
|
|
524
|
+
}
|
|
525
|
+
if (typeof inferenceProfile === "string") {
|
|
526
|
+
const profileError = validateScheduleInferenceProfile(inferenceProfile);
|
|
527
|
+
if (profileError) throw new BadRequestError(profileError);
|
|
528
|
+
}
|
|
529
|
+
updates.inferenceProfile = inferenceProfile;
|
|
530
|
+
}
|
|
531
|
+
|
|
363
532
|
try {
|
|
364
533
|
const updated = updateSchedule(id, updates);
|
|
365
534
|
if (!updated) {
|
|
@@ -389,14 +558,17 @@ function handleListScheduleRuns(
|
|
|
389
558
|
if (!schedule) {
|
|
390
559
|
throw new NotFoundError("Schedule not found");
|
|
391
560
|
}
|
|
392
|
-
const
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
561
|
+
const limit = parseRunsLimit(queryParams, 10);
|
|
562
|
+
const before = parseRunsBeforeCursor(queryParams);
|
|
563
|
+
const { rows, nextCursor } = paginateRuns(
|
|
564
|
+
getScheduleRuns(id, limit + 1, before),
|
|
565
|
+
limit,
|
|
566
|
+
(r) => r.createdAt,
|
|
567
|
+
);
|
|
397
568
|
const now = Date.now();
|
|
398
569
|
return {
|
|
399
|
-
|
|
570
|
+
nextCursor,
|
|
571
|
+
runs: rows.map((r) => {
|
|
400
572
|
const conversation = r.conversationId
|
|
401
573
|
? getConversation(r.conversationId)
|
|
402
574
|
: null;
|
|
@@ -494,6 +666,25 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
494
666
|
handler: ({ queryParams }: RouteHandlerArgs) =>
|
|
495
667
|
handleScheduleUsageSummary(queryParams ?? {}),
|
|
496
668
|
},
|
|
669
|
+
// Must stay after literal `schedules/*` GET siblings (e.g. usage-summary):
|
|
670
|
+
// the router matches in declaration order and `:id` would shadow them.
|
|
671
|
+
{
|
|
672
|
+
operationId: "getSchedule",
|
|
673
|
+
endpoint: "schedules/:id",
|
|
674
|
+
method: "GET",
|
|
675
|
+
policy: {
|
|
676
|
+
requiredScopes: ["settings.read"],
|
|
677
|
+
allowedPrincipalTypes: ACTOR_PRINCIPALS,
|
|
678
|
+
},
|
|
679
|
+
summary: "Get schedule",
|
|
680
|
+
description: "Return a single schedule by ID.",
|
|
681
|
+
tags: ["schedules"],
|
|
682
|
+
responseBody: z.object({
|
|
683
|
+
schedule: scheduleSchema.describe("Schedule object"),
|
|
684
|
+
}),
|
|
685
|
+
handler: ({ pathParams }: RouteHandlerArgs) =>
|
|
686
|
+
handleGetSchedule(pathParams!.id),
|
|
687
|
+
},
|
|
497
688
|
{
|
|
498
689
|
operationId: "createSchedule",
|
|
499
690
|
endpoint: "schedules",
|
|
@@ -515,7 +706,12 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
515
706
|
)
|
|
516
707
|
.optional(),
|
|
517
708
|
expression: z.string().describe("Cron or RRULE expression"),
|
|
518
|
-
message: z
|
|
709
|
+
message: z
|
|
710
|
+
.string()
|
|
711
|
+
.describe(
|
|
712
|
+
"Message body to execute on each fire. Required for execute mode; ignored for workflow mode (which triggers workflowName/workflowArgs).",
|
|
713
|
+
)
|
|
714
|
+
.optional(),
|
|
519
715
|
timezone: z
|
|
520
716
|
.string()
|
|
521
717
|
.nullable()
|
|
@@ -525,7 +721,25 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
525
721
|
.boolean()
|
|
526
722
|
.describe("Whether the schedule starts active (default true)")
|
|
527
723
|
.optional(),
|
|
528
|
-
mode: z
|
|
724
|
+
mode: z
|
|
725
|
+
.string()
|
|
726
|
+
.describe("'execute' (default) or 'workflow' (flag-gated)")
|
|
727
|
+
.optional(),
|
|
728
|
+
workflowName: z
|
|
729
|
+
.string()
|
|
730
|
+
.describe("Saved workflow to trigger (required for workflow mode)")
|
|
731
|
+
.optional(),
|
|
732
|
+
workflowArgs: z
|
|
733
|
+
.unknown()
|
|
734
|
+
.describe("Args passed to the workflow run (workflow mode)")
|
|
735
|
+
.optional(),
|
|
736
|
+
inferenceProfile: z
|
|
737
|
+
.string()
|
|
738
|
+
.nullable()
|
|
739
|
+
.describe(
|
|
740
|
+
"Inference profile (llm.profiles key) the schedule's runs use. Omitted or null = default, i.e. the mainAgent call-site model selection.",
|
|
741
|
+
)
|
|
742
|
+
.optional(),
|
|
529
743
|
}),
|
|
530
744
|
responseBody: z.object({
|
|
531
745
|
schedules: z.array(scheduleSchema).describe("Updated schedule list"),
|
|
@@ -543,15 +757,10 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
543
757
|
summary: "List schedule runs",
|
|
544
758
|
description: "Return recent invocation history for a schedule.",
|
|
545
759
|
tags: ["schedules"],
|
|
546
|
-
queryParams:
|
|
547
|
-
{
|
|
548
|
-
name: "limit",
|
|
549
|
-
schema: { type: "integer" },
|
|
550
|
-
description: "Max runs to return (default 10, max 100)",
|
|
551
|
-
},
|
|
552
|
-
],
|
|
760
|
+
queryParams: RUNS_PAGINATION_QUERY_PARAMS(10),
|
|
553
761
|
responseBody: z.object({
|
|
554
762
|
runs: z.array(scheduleRunSchema).describe("Schedule run objects"),
|
|
763
|
+
nextCursor: RUNS_NEXT_CURSOR_SCHEMA,
|
|
555
764
|
}),
|
|
556
765
|
handler: ({ pathParams, queryParams }: RouteHandlerArgs) =>
|
|
557
766
|
handleListScheduleRuns(pathParams!.id, queryParams ?? {}),
|
|
@@ -615,7 +824,19 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
615
824
|
.nullable()
|
|
616
825
|
.describe("Shell command for script mode")
|
|
617
826
|
.optional(),
|
|
618
|
-
mode: z
|
|
827
|
+
mode: z
|
|
828
|
+
.string()
|
|
829
|
+
.describe("notify, execute, script, wake, or workflow")
|
|
830
|
+
.optional(),
|
|
831
|
+
workflowName: z
|
|
832
|
+
.string()
|
|
833
|
+
.nullable()
|
|
834
|
+
.describe("Saved workflow to trigger (workflow mode)")
|
|
835
|
+
.optional(),
|
|
836
|
+
workflowArgs: z
|
|
837
|
+
.unknown()
|
|
838
|
+
.describe("Args passed to the workflow run (workflow mode)")
|
|
839
|
+
.optional(),
|
|
619
840
|
routingIntent: z
|
|
620
841
|
.string()
|
|
621
842
|
.describe("single_channel, multi_channel, or all_channels")
|
|
@@ -632,6 +853,13 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
632
853
|
.nullable()
|
|
633
854
|
.describe("Script-mode execution timeout in ms; null = use default")
|
|
634
855
|
.optional(),
|
|
856
|
+
inferenceProfile: z
|
|
857
|
+
.string()
|
|
858
|
+
.nullable()
|
|
859
|
+
.describe(
|
|
860
|
+
"Inference profile (llm.profiles key) the schedule's runs use; null clears it back to the default mainAgent call-site model selection",
|
|
861
|
+
)
|
|
862
|
+
.optional(),
|
|
635
863
|
}),
|
|
636
864
|
responseBody: z.object({
|
|
637
865
|
schedules: z.array(scheduleSchema).describe("Updated schedule list"),
|
|
@@ -711,6 +939,77 @@ async function handleRunScheduleNow(id: string) {
|
|
|
711
939
|
return handleListSchedules({});
|
|
712
940
|
}
|
|
713
941
|
|
|
942
|
+
// ── Workflow mode (trigger a saved workflow by name) ──────────────
|
|
943
|
+
// Mirrors the scheduler's automatic workflow-firing branch. Without this, a
|
|
944
|
+
// workflow-mode schedule manually triggered via `POST /schedules/:id/run`
|
|
945
|
+
// would fall through to the regular message path below and process
|
|
946
|
+
// `schedule.message` (usually empty — workflow-mode create no longer requires
|
|
947
|
+
// a message), running a no-op normal turn instead of the workflow.
|
|
948
|
+
if (schedule.mode === "workflow") {
|
|
949
|
+
// Fail fast at the route when the flag is off (the run manager would also
|
|
950
|
+
// hard-fail with WorkflowsDisabledError); matches create/update handling.
|
|
951
|
+
assertWorkflowsEnabled();
|
|
952
|
+
if (!schedule.workflowName) {
|
|
953
|
+
throw new BadRequestError("Workflow schedule has no workflowName");
|
|
954
|
+
}
|
|
955
|
+
// Boot race: resolveCapabilities grants the read-only baseline (file_read,
|
|
956
|
+
// web_search, …) from the tool registry, which initializeProvidersAndTools()
|
|
957
|
+
// populates during startup. The scheduler's automatic firing path DEFERS a
|
|
958
|
+
// workflow trigger to a later tick while `!areCoreToolsInitialized()` so a
|
|
959
|
+
// run never launches with an empty baseline. A manual "run now" has no later
|
|
960
|
+
// tick to defer to, so fail fast with a retryable 503 rather than launch a
|
|
961
|
+
// degraded run; the window is just the few seconds of assistant boot.
|
|
962
|
+
if (!areCoreToolsInitialized()) {
|
|
963
|
+
throw new ServiceUnavailableError(
|
|
964
|
+
"The assistant is still starting up and its tools are not ready yet. " +
|
|
965
|
+
"Try running this workflow again in a moment.",
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
const runId = createScheduleRun(schedule.id, `workflow:${schedule.id}`);
|
|
969
|
+
try {
|
|
970
|
+
log.info(
|
|
971
|
+
{
|
|
972
|
+
jobId: schedule.id,
|
|
973
|
+
name: schedule.name,
|
|
974
|
+
workflowName: schedule.workflowName,
|
|
975
|
+
},
|
|
976
|
+
"Triggering workflow schedule manually (run now)",
|
|
977
|
+
);
|
|
978
|
+
const { runId: workflowRunId } = getWorkflowRunManager().start({
|
|
979
|
+
name: schedule.workflowName,
|
|
980
|
+
args: schedule.workflowArgs ?? {},
|
|
981
|
+
// Deliver the completion summary to the schedule's wake target, falling
|
|
982
|
+
// back to the conversation that created it (workflow schedules made via
|
|
983
|
+
// `schedule_create` store `createdFromConversationId` and leave
|
|
984
|
+
// `wakeConversationId` unset) — mirrors the scheduler's firing path.
|
|
985
|
+
conversationId:
|
|
986
|
+
schedule.wakeConversationId ??
|
|
987
|
+
schedule.createdFromConversationId ??
|
|
988
|
+
undefined,
|
|
989
|
+
// The schedule's persisted capability manifest scopes the run, mirroring
|
|
990
|
+
// the scheduler's firing path; a legacy schedule with null
|
|
991
|
+
// `capabilities` normalizes to the read-only baseline.
|
|
992
|
+
manifest: normalizeCapabilityManifest(schedule.capabilities),
|
|
993
|
+
trustContext: INTERNAL_GUARDIAN_TRUST_CONTEXT,
|
|
994
|
+
});
|
|
995
|
+
// `start` launches the run fire-and-forget and returns synchronously;
|
|
996
|
+
// a successful trigger is recorded as ok. Completion/failure is surfaced
|
|
997
|
+
// out-of-band via workflow events and the completion wake.
|
|
998
|
+
completeScheduleRun(runId, {
|
|
999
|
+
status: "ok",
|
|
1000
|
+
output: `workflow run ${workflowRunId} started`,
|
|
1001
|
+
});
|
|
1002
|
+
} catch (err) {
|
|
1003
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1004
|
+
log.warn(
|
|
1005
|
+
{ err, jobId: schedule.id, name: schedule.name },
|
|
1006
|
+
"Manual workflow schedule execution failed",
|
|
1007
|
+
);
|
|
1008
|
+
completeScheduleRun(runId, { status: "error", error: errorMsg });
|
|
1009
|
+
}
|
|
1010
|
+
return handleListSchedules({});
|
|
1011
|
+
}
|
|
1012
|
+
|
|
714
1013
|
// Check if message is a task invocation (run_task:<task_id>)
|
|
715
1014
|
const taskMatch = schedule.message.match(/^run_task:(\S+)$/);
|
|
716
1015
|
if (taskMatch) {
|
|
@@ -735,6 +1034,9 @@ async function handleRunScheduleNow(id: string) {
|
|
|
735
1034
|
attachments: [],
|
|
736
1035
|
onEvent: () => {},
|
|
737
1036
|
isInteractive: false,
|
|
1037
|
+
...(schedule.inferenceProfile
|
|
1038
|
+
? { overrideProfile: schedule.inferenceProfile }
|
|
1039
|
+
: {}),
|
|
738
1040
|
});
|
|
739
1041
|
} finally {
|
|
740
1042
|
conversation.taskRunId = undefined;
|
|
@@ -781,6 +1083,9 @@ async function handleRunScheduleNow(id: string) {
|
|
|
781
1083
|
conversationId: schedule.wakeConversationId,
|
|
782
1084
|
hint: schedule.message,
|
|
783
1085
|
source: "defer",
|
|
1086
|
+
...(schedule.inferenceProfile
|
|
1087
|
+
? { forceOverrideProfile: schedule.inferenceProfile }
|
|
1088
|
+
: {}),
|
|
784
1089
|
});
|
|
785
1090
|
} catch (err) {
|
|
786
1091
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -828,6 +1133,9 @@ async function handleRunScheduleNow(id: string) {
|
|
|
828
1133
|
attachments: [],
|
|
829
1134
|
onEvent: () => {},
|
|
830
1135
|
isInteractive: false,
|
|
1136
|
+
...(schedule.inferenceProfile
|
|
1137
|
+
? { overrideProfile: schedule.inferenceProfile }
|
|
1138
|
+
: {}),
|
|
831
1139
|
});
|
|
832
1140
|
completeScheduleRun(runId, { status: "ok" });
|
|
833
1141
|
} catch (err) {
|