@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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Cron } from "croner";
|
|
2
|
-
import { and, asc, desc, eq, isNull, lte, sql } from "drizzle-orm";
|
|
2
|
+
import { and, asc, desc, eq, isNull, lt, lte, sql } from "drizzle-orm";
|
|
3
3
|
import { v4 as uuid } from "uuid";
|
|
4
4
|
|
|
5
5
|
import { getDb } from "../memory/db-connection.js";
|
|
@@ -26,7 +26,12 @@ function notifySchedulesChanged(): void {
|
|
|
26
26
|
);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export type ScheduleMode =
|
|
29
|
+
export type ScheduleMode =
|
|
30
|
+
| "notify"
|
|
31
|
+
| "execute"
|
|
32
|
+
| "script"
|
|
33
|
+
| "wake"
|
|
34
|
+
| "workflow";
|
|
30
35
|
export type RoutingIntent = "single_channel" | "multi_channel" | "all_channels";
|
|
31
36
|
export type ScheduleStatus = "active" | "firing" | "fired" | "cancelled";
|
|
32
37
|
|
|
@@ -42,6 +47,12 @@ export interface ScheduleJob {
|
|
|
42
47
|
message: string;
|
|
43
48
|
script: string | null;
|
|
44
49
|
wakeConversationId: string | null;
|
|
50
|
+
/** Saved workflow to trigger; only used when mode = 'workflow'. */
|
|
51
|
+
workflowName: string | null;
|
|
52
|
+
/** Args passed verbatim to the workflow run; only used when mode = 'workflow'. */
|
|
53
|
+
workflowArgs: unknown;
|
|
54
|
+
/** Capability manifest scoping the schedule's run; null = unconstrained. */
|
|
55
|
+
capabilities: unknown | null;
|
|
45
56
|
nextRunAt: number;
|
|
46
57
|
lastRunAt: number | null;
|
|
47
58
|
lastStatus: string | null;
|
|
@@ -50,6 +61,11 @@ export interface ScheduleJob {
|
|
|
50
61
|
retryBackoffMs: number;
|
|
51
62
|
/** Script-mode execution timeout override (ms); null = use the default. */
|
|
52
63
|
timeoutMs: number | null;
|
|
64
|
+
/**
|
|
65
|
+
* Inference profile (`llm.profiles` key) applied to the schedule's
|
|
66
|
+
* LLM-executed runs; null = default main-agent model selection.
|
|
67
|
+
*/
|
|
68
|
+
inferenceProfile: string | null;
|
|
53
69
|
createdFromConversationId: string | null;
|
|
54
70
|
createdBy: string;
|
|
55
71
|
mode: ScheduleMode;
|
|
@@ -92,6 +108,9 @@ export function createSchedule(params: {
|
|
|
92
108
|
message: string;
|
|
93
109
|
script?: string | null;
|
|
94
110
|
wakeConversationId?: string | null;
|
|
111
|
+
workflowName?: string | null;
|
|
112
|
+
workflowArgs?: unknown;
|
|
113
|
+
capabilities?: unknown;
|
|
95
114
|
enabled?: boolean;
|
|
96
115
|
createdBy?: string;
|
|
97
116
|
syntax?: ScheduleSyntax;
|
|
@@ -105,6 +124,7 @@ export function createSchedule(params: {
|
|
|
105
124
|
maxRetries?: number;
|
|
106
125
|
retryBackoffMs?: number;
|
|
107
126
|
timeoutMs?: number | null;
|
|
127
|
+
inferenceProfile?: string | null;
|
|
108
128
|
createdFromConversationId?: string | null;
|
|
109
129
|
}): ScheduleJob {
|
|
110
130
|
const expression = params.expression ?? params.cronExpression ?? null;
|
|
@@ -142,6 +162,7 @@ export function createSchedule(params: {
|
|
|
142
162
|
const maxRetries = params.maxRetries ?? 3;
|
|
143
163
|
const retryBackoffMs = params.retryBackoffMs ?? 60000;
|
|
144
164
|
const timeoutMs = params.timeoutMs ?? null;
|
|
165
|
+
const inferenceProfile = params.inferenceProfile ?? null;
|
|
145
166
|
const createdFromConversationId = params.createdFromConversationId ?? null;
|
|
146
167
|
const description = normalizeDescription(
|
|
147
168
|
params.description,
|
|
@@ -168,6 +189,13 @@ export function createSchedule(params: {
|
|
|
168
189
|
message: params.message,
|
|
169
190
|
script: params.script ?? null,
|
|
170
191
|
wakeConversationId: params.wakeConversationId ?? null,
|
|
192
|
+
workflowName: params.workflowName ?? null,
|
|
193
|
+
workflowArgsJson:
|
|
194
|
+
params.workflowArgs === undefined
|
|
195
|
+
? null
|
|
196
|
+
: JSON.stringify(params.workflowArgs),
|
|
197
|
+
capabilitiesJson:
|
|
198
|
+
params.capabilities != null ? JSON.stringify(params.capabilities) : null,
|
|
171
199
|
nextRunAt,
|
|
172
200
|
lastRunAt: null as number | null,
|
|
173
201
|
lastStatus: null as string | null,
|
|
@@ -175,6 +203,7 @@ export function createSchedule(params: {
|
|
|
175
203
|
maxRetries,
|
|
176
204
|
retryBackoffMs,
|
|
177
205
|
timeoutMs,
|
|
206
|
+
inferenceProfile,
|
|
178
207
|
createdFromConversationId,
|
|
179
208
|
createdBy: params.createdBy ?? "agent",
|
|
180
209
|
mode,
|
|
@@ -273,9 +302,13 @@ export function updateSchedule(
|
|
|
273
302
|
quiet?: boolean;
|
|
274
303
|
reuseConversation?: boolean;
|
|
275
304
|
wakeConversationId?: string | null;
|
|
305
|
+
workflowName?: string | null;
|
|
306
|
+
workflowArgs?: unknown;
|
|
307
|
+
capabilities?: unknown;
|
|
276
308
|
maxRetries?: number;
|
|
277
309
|
retryBackoffMs?: number;
|
|
278
310
|
timeoutMs?: number | null;
|
|
311
|
+
inferenceProfile?: string | null;
|
|
279
312
|
createdFromConversationId?: string | null;
|
|
280
313
|
},
|
|
281
314
|
): ScheduleJob | null {
|
|
@@ -340,10 +373,28 @@ export function updateSchedule(
|
|
|
340
373
|
set.reuseConversation = updates.reuseConversation;
|
|
341
374
|
if (updates.wakeConversationId !== undefined)
|
|
342
375
|
set.wakeConversationId = updates.wakeConversationId;
|
|
376
|
+
if (updates.workflowName !== undefined)
|
|
377
|
+
set.workflowName = updates.workflowName;
|
|
378
|
+
// `workflowArgs` may legitimately be any JSON value (including null), so
|
|
379
|
+
// detect presence by key rather than `!== undefined`.
|
|
380
|
+
if ("workflowArgs" in updates)
|
|
381
|
+
set.workflowArgsJson =
|
|
382
|
+
updates.workflowArgs === undefined
|
|
383
|
+
? null
|
|
384
|
+
: JSON.stringify(updates.workflowArgs);
|
|
385
|
+
// `capabilities` may legitimately be any JSON value (including null), so
|
|
386
|
+
// detect presence by key rather than `!== undefined`.
|
|
387
|
+
if ("capabilities" in updates)
|
|
388
|
+
set.capabilitiesJson =
|
|
389
|
+
updates.capabilities == null
|
|
390
|
+
? null
|
|
391
|
+
: JSON.stringify(updates.capabilities);
|
|
343
392
|
if (updates.maxRetries !== undefined) set.maxRetries = updates.maxRetries;
|
|
344
393
|
if (updates.retryBackoffMs !== undefined)
|
|
345
394
|
set.retryBackoffMs = updates.retryBackoffMs;
|
|
346
395
|
if (updates.timeoutMs !== undefined) set.timeoutMs = updates.timeoutMs;
|
|
396
|
+
if (updates.inferenceProfile !== undefined)
|
|
397
|
+
set.inferenceProfile = updates.inferenceProfile;
|
|
347
398
|
if (updates.createdFromConversationId !== undefined)
|
|
348
399
|
set.createdFromConversationId = updates.createdFromConversationId;
|
|
349
400
|
|
|
@@ -545,6 +596,41 @@ export function failOneShot(id: string): void {
|
|
|
545
596
|
if (rawChanges() > 0) notifySchedulesChanged();
|
|
546
597
|
}
|
|
547
598
|
|
|
599
|
+
/**
|
|
600
|
+
* Re-arm a just-claimed schedule so it is due again on the very next tick,
|
|
601
|
+
* WITHOUT counting as a run or bumping the retry count. Sets status back to
|
|
602
|
+
* 'active' and pulls `nextRunAt` back to now: `claimDueSchedules` advances
|
|
603
|
+
* `nextRunAt` for recurring jobs (and flips one-shots to 'firing'), so without
|
|
604
|
+
* resetting both the claimed occurrence would be dropped until the next cron
|
|
605
|
+
* time. Used when a tick claims a job it cannot yet process (e.g. a workflow
|
|
606
|
+
* schedule that fired before the tool registry finished initializing at boot);
|
|
607
|
+
* unlike a failure path it does not touch `retryCount`, since the deferral is
|
|
608
|
+
* not the schedule's fault. Keyed by id only (no status guard) because recurring
|
|
609
|
+
* claims leave the row 'active' while one-shot claims leave it 'firing', and it
|
|
610
|
+
* runs immediately after the claim within the same tick.
|
|
611
|
+
*
|
|
612
|
+
* Also restores `enabled: true`. `claimDueSchedules` disables a row whose
|
|
613
|
+
* claimed occurrence was its LAST (a one-shot, or the final fire of a finite
|
|
614
|
+
* RRULE), but the due-claim query requires `enabled = true` — so a deferred
|
|
615
|
+
* final occurrence would never be re-claimed and the run would be silently lost.
|
|
616
|
+
* The deferred occurrence has not actually run yet, so re-enabling is correct;
|
|
617
|
+
* when it later fires, the claim path re-applies the right `enabled` state.
|
|
618
|
+
*/
|
|
619
|
+
export function deferClaimedSchedule(id: string): void {
|
|
620
|
+
const db = getDb();
|
|
621
|
+
const now = Date.now();
|
|
622
|
+
db.update(scheduleJobs)
|
|
623
|
+
.set({
|
|
624
|
+
status: "active",
|
|
625
|
+
enabled: true,
|
|
626
|
+
nextRunAt: now,
|
|
627
|
+
updatedAt: now,
|
|
628
|
+
})
|
|
629
|
+
.where(eq(scheduleJobs.id, id))
|
|
630
|
+
.run();
|
|
631
|
+
if (rawChanges() > 0) notifySchedulesChanged();
|
|
632
|
+
}
|
|
633
|
+
|
|
548
634
|
/**
|
|
549
635
|
* Revert a one-shot from 'firing' back to 'active' and increment its
|
|
550
636
|
* retry count. Used when a wake times out waiting for an idle conversation
|
|
@@ -718,12 +804,26 @@ export function getLastScheduleConversationId(jobId: string): string | null {
|
|
|
718
804
|
return row?.conversationId ?? null;
|
|
719
805
|
}
|
|
720
806
|
|
|
721
|
-
|
|
807
|
+
/**
|
|
808
|
+
* List runs for a schedule, newest first. When `before` is set, only runs
|
|
809
|
+
* with `createdAt` strictly older than it are returned (cursor for
|
|
810
|
+
* paginating into history).
|
|
811
|
+
*/
|
|
812
|
+
export function getScheduleRuns(
|
|
813
|
+
jobId: string,
|
|
814
|
+
limit?: number,
|
|
815
|
+
before?: number,
|
|
816
|
+
): ScheduleRun[] {
|
|
722
817
|
const db = getDb();
|
|
723
818
|
const rows = db
|
|
724
819
|
.select()
|
|
725
820
|
.from(scheduleRuns)
|
|
726
|
-
.where(
|
|
821
|
+
.where(
|
|
822
|
+
and(
|
|
823
|
+
eq(scheduleRuns.jobId, jobId),
|
|
824
|
+
before != null ? lt(scheduleRuns.createdAt, before) : undefined,
|
|
825
|
+
),
|
|
826
|
+
)
|
|
727
827
|
.orderBy(desc(scheduleRuns.createdAt))
|
|
728
828
|
.limit(limit ?? 10)
|
|
729
829
|
.all();
|
|
@@ -886,6 +986,36 @@ export function describeCronExpression(expr: string | null): string {
|
|
|
886
986
|
}
|
|
887
987
|
}
|
|
888
988
|
|
|
989
|
+
// Stepped or fixed minutes constrained to a contiguous range of hours,
|
|
990
|
+
// every day/month (e.g. "*/30 7-23 * * *" → "Every 30 minutes, 7 AM–11 PM").
|
|
991
|
+
const hoursAreContiguousRange =
|
|
992
|
+
!allHours &&
|
|
993
|
+
activeHours.length > 1 &&
|
|
994
|
+
activeHours.every((h, i) => i === 0 || h === activeHours[i - 1] + 1);
|
|
995
|
+
|
|
996
|
+
if (hoursAreContiguousRange && anyDayAndMonth) {
|
|
997
|
+
const hourLabel = (h: number) => {
|
|
998
|
+
const period = h >= 12 ? "PM" : "AM";
|
|
999
|
+
return `${h % 12 || 12} ${period}`;
|
|
1000
|
+
};
|
|
1001
|
+
const rangeStr = `${hourLabel(activeHours[0])}–${hourLabel(
|
|
1002
|
+
activeHours[activeHours.length - 1],
|
|
1003
|
+
)}`;
|
|
1004
|
+
|
|
1005
|
+
if (steppedMinutes && activeMinutes[0] === 0) {
|
|
1006
|
+
const step = activeMinutes[1] - activeMinutes[0];
|
|
1007
|
+
const isRegularStep = activeMinutes.every((v, i) => v === i * step);
|
|
1008
|
+
if (isRegularStep && 60 % step === 0) {
|
|
1009
|
+
return `Every ${step} minutes, ${rangeStr}`;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
if (fixedMinute) {
|
|
1013
|
+
return activeMinutes[0] === 0
|
|
1014
|
+
? `Hourly, ${rangeStr}`
|
|
1015
|
+
: `Hourly at minute ${activeMinutes[0]}, ${rangeStr}`;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
|
|
889
1019
|
// Fallback: return the raw expression
|
|
890
1020
|
return expr;
|
|
891
1021
|
} catch {
|
|
@@ -999,6 +1129,9 @@ function parseJobRow(row: typeof scheduleJobs.$inferSelect): ScheduleJob {
|
|
|
999
1129
|
message: row.message,
|
|
1000
1130
|
script: row.script ?? null,
|
|
1001
1131
|
wakeConversationId: row.wakeConversationId ?? null,
|
|
1132
|
+
workflowName: row.workflowName ?? null,
|
|
1133
|
+
workflowArgs: parseOptionalJson(row.workflowArgsJson),
|
|
1134
|
+
capabilities: parseOptionalJson(row.capabilitiesJson),
|
|
1002
1135
|
nextRunAt: row.nextRunAt,
|
|
1003
1136
|
lastRunAt: row.lastRunAt,
|
|
1004
1137
|
lastStatus: row.lastStatus,
|
|
@@ -1006,6 +1139,7 @@ function parseJobRow(row: typeof scheduleJobs.$inferSelect): ScheduleJob {
|
|
|
1006
1139
|
maxRetries: row.maxRetries ?? 3,
|
|
1007
1140
|
retryBackoffMs: row.retryBackoffMs ?? 60000,
|
|
1008
1141
|
timeoutMs: row.timeoutMs ?? null,
|
|
1142
|
+
inferenceProfile: row.inferenceProfile ?? null,
|
|
1009
1143
|
createdFromConversationId: row.createdFromConversationId ?? null,
|
|
1010
1144
|
createdBy: row.createdBy,
|
|
1011
1145
|
mode: (row.mode ?? "execute") as ScheduleMode,
|
|
@@ -1038,6 +1172,20 @@ function safeParseJson(
|
|
|
1038
1172
|
}
|
|
1039
1173
|
}
|
|
1040
1174
|
|
|
1175
|
+
/**
|
|
1176
|
+
* Parse a nullable JSON column into an arbitrary value. Unlike
|
|
1177
|
+
* {@link safeParseJson}, the result is not coerced to an object — workflow
|
|
1178
|
+
* args may be any JSON value — and an absent/unparseable column yields null.
|
|
1179
|
+
*/
|
|
1180
|
+
function parseOptionalJson(json: string | null | undefined): unknown {
|
|
1181
|
+
if (json == null) return null;
|
|
1182
|
+
try {
|
|
1183
|
+
return JSON.parse(json);
|
|
1184
|
+
} catch {
|
|
1185
|
+
return null;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1041
1189
|
function parseRunRow(row: typeof scheduleRuns.$inferSelect): ScheduleRun {
|
|
1042
1190
|
return {
|
|
1043
1191
|
id: row.id,
|
|
@@ -16,6 +16,12 @@ export interface ScheduleMessageOptions {
|
|
|
16
16
|
* the default `mainAgent` route.
|
|
17
17
|
*/
|
|
18
18
|
callSite?: LLMCallSite;
|
|
19
|
+
/**
|
|
20
|
+
* Optional ad-hoc inference-profile override (`llm.profiles` key) applied
|
|
21
|
+
* to every LLM call the run issues — a schedule's pinned profile. Omitted
|
|
22
|
+
* = the call site's default resolution (main-agent model selection).
|
|
23
|
+
*/
|
|
24
|
+
overrideProfile?: string;
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
export type ScheduleMessageProcessor = (
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
diskPressureBackgroundSkipLogFields,
|
|
4
4
|
shouldLogDiskPressureBackgroundSkip,
|
|
5
5
|
} from "../daemon/disk-pressure-background-gate.js";
|
|
6
|
+
import { INTERNAL_GUARDIAN_TRUST_CONTEXT } from "../daemon/trust-context.js";
|
|
6
7
|
import { bootstrapConversation } from "../memory/conversation-bootstrap.js";
|
|
7
8
|
import { getConversation } from "../memory/conversation-crud.js";
|
|
8
9
|
import { invalidateAssistantInferredItemsForConversation } from "../memory/task-memory-cleanup.js";
|
|
@@ -10,8 +11,11 @@ import { emitNotificationSignal } from "../notifications/emit-signal.js";
|
|
|
10
11
|
import { wakeAgentForOpportunity } from "../runtime/agent-wake.js";
|
|
11
12
|
import { runBackgroundJob } from "../runtime/background-job-runner.js";
|
|
12
13
|
import { runSequencesOnce } from "../sequence/engine.js";
|
|
14
|
+
import { areCoreToolsInitialized } from "../tools/registry.js";
|
|
13
15
|
import { getLogger } from "../util/logger.js";
|
|
14
16
|
import { runWatchersOnce, type WatcherNotifier } from "../watcher/engine.js";
|
|
17
|
+
import { normalizeCapabilityManifest } from "../workflows/capabilities.js";
|
|
18
|
+
import { getWorkflowRunManager } from "../workflows/run-manager.js";
|
|
15
19
|
import { hasSetConstructs } from "./recurrence-engine.js";
|
|
16
20
|
import { applyRetryDecision, decideRetry } from "./retry-policy.js";
|
|
17
21
|
import { runScript, type ScriptResult } from "./run-script.js";
|
|
@@ -20,6 +24,7 @@ import {
|
|
|
20
24
|
completeOneShot,
|
|
21
25
|
completeScheduleRun,
|
|
22
26
|
createScheduleRun,
|
|
27
|
+
deferClaimedSchedule,
|
|
23
28
|
failOneShotPermanently,
|
|
24
29
|
getLastScheduleConversationId,
|
|
25
30
|
listSchedules,
|
|
@@ -357,6 +362,9 @@ export async function runScheduleDueWorkOnce(
|
|
|
357
362
|
conversationId: wakeConversationId,
|
|
358
363
|
hint: job.message,
|
|
359
364
|
source: "defer",
|
|
365
|
+
...(job.inferenceProfile
|
|
366
|
+
? { forceOverrideProfile: job.inferenceProfile }
|
|
367
|
+
: {}),
|
|
360
368
|
});
|
|
361
369
|
|
|
362
370
|
if (result.reason === "timeout" && isOneShot) {
|
|
@@ -433,6 +441,85 @@ export async function runScheduleDueWorkOnce(
|
|
|
433
441
|
continue;
|
|
434
442
|
}
|
|
435
443
|
|
|
444
|
+
// ── Workflow mode (trigger a saved workflow by name) ────────────
|
|
445
|
+
if (job.mode === "workflow") {
|
|
446
|
+
if (!job.workflowName) {
|
|
447
|
+
log.warn(
|
|
448
|
+
{ jobId: job.id, name: job.name },
|
|
449
|
+
"Workflow schedule has no workflowName — skipping",
|
|
450
|
+
);
|
|
451
|
+
mark("skipped");
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
// Boot race: the scheduler starts before initializeProvidersAndTools()
|
|
455
|
+
// registers the read-only baseline (file_read/web_fetch/etc.) that
|
|
456
|
+
// resolveCapabilities grants to every workflow run. Launching now would
|
|
457
|
+
// give the run an EMPTY baseline and degrade/fail it. Defer the claimed
|
|
458
|
+
// job back to due so it fires on a later tick once tools are registered
|
|
459
|
+
// (the window is just the few seconds of daemon boot). Non-workflow modes
|
|
460
|
+
// don't depend on the baseline, so only this branch gates.
|
|
461
|
+
if (!areCoreToolsInitialized()) {
|
|
462
|
+
log.info(
|
|
463
|
+
{ jobId: job.id, name: job.name, workflowName: job.workflowName },
|
|
464
|
+
"Deferring workflow schedule until tools are registered",
|
|
465
|
+
);
|
|
466
|
+
deferClaimedSchedule(job.id);
|
|
467
|
+
mark("skipped");
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
const runId = createScheduleRun(job.id, `workflow:${job.id}`);
|
|
471
|
+
let failed = false;
|
|
472
|
+
try {
|
|
473
|
+
log.info(
|
|
474
|
+
{
|
|
475
|
+
jobId: job.id,
|
|
476
|
+
name: job.name,
|
|
477
|
+
workflowName: job.workflowName,
|
|
478
|
+
isOneShot,
|
|
479
|
+
},
|
|
480
|
+
"Triggering workflow schedule",
|
|
481
|
+
);
|
|
482
|
+
const { runId: workflowRunId } = getWorkflowRunManager().start({
|
|
483
|
+
name: job.workflowName,
|
|
484
|
+
args: job.workflowArgs ?? {},
|
|
485
|
+
// Where the completion summary is delivered (an agent wake). Prefer an
|
|
486
|
+
// explicit wake target, then fall back to the conversation that
|
|
487
|
+
// created the schedule — workflow schedules made via `schedule_create`
|
|
488
|
+
// store that as `createdFromConversationId` and leave
|
|
489
|
+
// `wakeConversationId` unset, so without this fallback their result
|
|
490
|
+
// would surface only to live SSE listeners / the DB, never delivered.
|
|
491
|
+
conversationId:
|
|
492
|
+
job.wakeConversationId ??
|
|
493
|
+
job.createdFromConversationId ??
|
|
494
|
+
undefined,
|
|
495
|
+
// The schedule's persisted capability manifest scopes the run; a
|
|
496
|
+
// legacy schedule with null `capabilities` normalizes to the read-only
|
|
497
|
+
// baseline.
|
|
498
|
+
manifest: normalizeCapabilityManifest(job.capabilities),
|
|
499
|
+
trustContext: INTERNAL_GUARDIAN_TRUST_CONTEXT,
|
|
500
|
+
});
|
|
501
|
+
// `start` launches the run fire-and-forget and returns synchronously;
|
|
502
|
+
// a successful trigger is recorded as ok. Workflow completion/failure
|
|
503
|
+
// is surfaced out-of-band via workflow events and the completion wake.
|
|
504
|
+
completeScheduleRun(runId, {
|
|
505
|
+
status: "ok",
|
|
506
|
+
output: `workflow run ${workflowRunId} started`,
|
|
507
|
+
});
|
|
508
|
+
if (isOneShot) completeOneShot(job.id);
|
|
509
|
+
} catch (err) {
|
|
510
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
511
|
+
log.warn(
|
|
512
|
+
{ err, jobId: job.id, name: job.name, isOneShot },
|
|
513
|
+
"Workflow schedule trigger failed",
|
|
514
|
+
);
|
|
515
|
+
completeScheduleRun(runId, { status: "error", error: errorMsg });
|
|
516
|
+
handleExecutionFailure({ job, errorMsg, isOneShot });
|
|
517
|
+
failed = true;
|
|
518
|
+
}
|
|
519
|
+
mark(failed ? "failed" : "completed");
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
|
|
436
523
|
// ── Execute mode ────────────────────────────────────────────────
|
|
437
524
|
|
|
438
525
|
// Check if message is a task invocation (run_task:<task_id>)
|
|
@@ -470,6 +557,9 @@ export async function runScheduleDueWorkOnce(
|
|
|
470
557
|
await processMessage(conversationId, message, {
|
|
471
558
|
trustClass: "guardian",
|
|
472
559
|
taskRunId,
|
|
560
|
+
...(job.inferenceProfile
|
|
561
|
+
? { overrideProfile: job.inferenceProfile }
|
|
562
|
+
: {}),
|
|
473
563
|
});
|
|
474
564
|
},
|
|
475
565
|
);
|
|
@@ -602,6 +692,9 @@ export async function runScheduleDueWorkOnce(
|
|
|
602
692
|
try {
|
|
603
693
|
await processMessage(conversationId, job.message, {
|
|
604
694
|
trustClass: "guardian",
|
|
695
|
+
...(job.inferenceProfile
|
|
696
|
+
? { overrideProfile: job.inferenceProfile }
|
|
697
|
+
: {}),
|
|
605
698
|
});
|
|
606
699
|
ok = true;
|
|
607
700
|
} catch (err) {
|
|
@@ -622,6 +715,9 @@ export async function runScheduleDueWorkOnce(
|
|
|
622
715
|
systemHint: `Schedule: ${job.name}`,
|
|
623
716
|
trustContext: { sourceChannel: "vellum", trustClass: "guardian" },
|
|
624
717
|
callSite: "mainAgent",
|
|
718
|
+
...(job.inferenceProfile
|
|
719
|
+
? { overrideProfile: job.inferenceProfile }
|
|
720
|
+
: {}),
|
|
625
721
|
timeoutMs: SCHEDULE_TALK_TIMEOUT_MS,
|
|
626
722
|
origin: "schedule",
|
|
627
723
|
groupId: "system:scheduled",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* User-defined allowlist for suppressing secret scanner false positives.
|
|
3
3
|
*
|
|
4
|
-
* Reads
|
|
4
|
+
* Reads `$VELLUM_WORKSPACE_DIR/data/secret-allowlist.json` (if present) and provides
|
|
5
5
|
* `isAllowlisted(value)` to check whether a matched value should be
|
|
6
6
|
* suppressed.
|
|
7
7
|
*
|
|
@@ -6,7 +6,7 @@ import { getWorkspaceSkillsDir } from "../util/platform.js";
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Returns the managed skills root directory. Managed skills are user-installed
|
|
9
|
-
* skills that live under
|
|
9
|
+
* skills that live under $VELLUM_WORKSPACE_DIR/skills.
|
|
10
10
|
*/
|
|
11
11
|
export function getManagedSkillsRoot(): string {
|
|
12
12
|
return normalizeDirPath(getWorkspaceSkillsDir());
|
|
@@ -12,6 +12,7 @@ import { compileApp } from "../../bundler/app-compiler.js";
|
|
|
12
12
|
import { generateAppIcon } from "../../media/app-icon-generator.js";
|
|
13
13
|
import type { AppDefinition } from "../../memory/app-store.js";
|
|
14
14
|
import { getAppDirPath } from "../../memory/app-store.js";
|
|
15
|
+
import { getLogger } from "../../util/logger.js";
|
|
15
16
|
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
17
18
|
// Shared result type
|
|
@@ -55,6 +56,11 @@ export interface AppStoreWriter {
|
|
|
55
56
|
): AppDefinition;
|
|
56
57
|
deleteApp(id: string): void;
|
|
57
58
|
writeAppFile(appId: string, path: string, content: string): void;
|
|
59
|
+
/**
|
|
60
|
+
* Associate a freshly created app with the conversation that created it.
|
|
61
|
+
* Optional so test doubles need not implement it; the real store does.
|
|
62
|
+
*/
|
|
63
|
+
addAppConversationId?(appId: string, conversationId: string): boolean;
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
export type AppStore = AppStoreReader & AppStoreWriter;
|
|
@@ -158,6 +164,7 @@ export async function executeAppCreate(
|
|
|
158
164
|
input: AppCreateInput,
|
|
159
165
|
store: AppStore,
|
|
160
166
|
proxyToolResolver?: ProxyResolver,
|
|
167
|
+
conversationId?: string,
|
|
161
168
|
): Promise<ExecutorResult> {
|
|
162
169
|
// The model sometimes omits a name; resolve a sensible one rather than
|
|
163
170
|
// erroring out so the build still succeeds. Users can rename via app_update.
|
|
@@ -213,6 +220,22 @@ export async function executeAppCreate(
|
|
|
213
220
|
formatVersion: 2,
|
|
214
221
|
});
|
|
215
222
|
|
|
223
|
+
// Associate the app with its conversation at creation so subsequent
|
|
224
|
+
// `app_*` calls in the same turn can resolve it even when the model omits
|
|
225
|
+
// `app_id` (see resolveAppId). Without this the link is only formed at
|
|
226
|
+
// `app_open`, leaving the create→update→refresh gap unresolvable.
|
|
227
|
+
// Best-effort: a failed association must never fail the create.
|
|
228
|
+
if (conversationId && store.addAppConversationId) {
|
|
229
|
+
try {
|
|
230
|
+
store.addAppConversationId(app.id, conversationId);
|
|
231
|
+
} catch (err) {
|
|
232
|
+
getLogger("app-executors").debug(
|
|
233
|
+
{ err, appId: app.id, conversationId },
|
|
234
|
+
"Failed to associate app with conversation at create",
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
216
239
|
// Scaffold multifile app with src/ files and compile to dist/
|
|
217
240
|
const htmlSafeName = name
|
|
218
241
|
.replace(/&/g, "&")
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { listAppsByConversation } from "../../memory/app-store.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the `app_id` an app-builder tool should operate on.
|
|
5
|
+
*
|
|
6
|
+
* Weaker models routinely omit `app_id` when calling `app_*` tools through
|
|
7
|
+
* `skill_execute`, even though the active app's id is in their context from the
|
|
8
|
+
* preceding `app_create`/`app_update` result. Left unhandled, the executor
|
|
9
|
+
* fails with a cryptic "Invalid ID: undefined", the model retries the same
|
|
10
|
+
* empty call, and the turn burns many steps before recovering.
|
|
11
|
+
*
|
|
12
|
+
* An explicit, non-empty `app_id` always wins. When it is missing, fall back to
|
|
13
|
+
* the conversation's most-recently-updated app — the one being actively built.
|
|
14
|
+
* Returns `null` when no app_id is supplied and the conversation has no app, so
|
|
15
|
+
* callers can surface an actionable error instead of a raw store throw.
|
|
16
|
+
*
|
|
17
|
+
* Not used for destructive operations (`app_delete`): deleting an inferred app
|
|
18
|
+
* is unsafe, so deletion requires an explicit id.
|
|
19
|
+
*/
|
|
20
|
+
export function resolveAppId(
|
|
21
|
+
input: Record<string, unknown>,
|
|
22
|
+
conversationId: string,
|
|
23
|
+
): string | null {
|
|
24
|
+
if (typeof input.app_id === "string" && input.app_id.trim().length > 0) {
|
|
25
|
+
return input.app_id;
|
|
26
|
+
}
|
|
27
|
+
// `listAppsByConversation` preserves `listApps`' updatedAt-descending order,
|
|
28
|
+
// so the first entry is the app the model is actively working on.
|
|
29
|
+
const apps = listAppsByConversation(conversationId);
|
|
30
|
+
return apps.length > 0 ? apps[0].id : null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Error payload returned when no app_id is supplied and none can be inferred. */
|
|
34
|
+
export function missingAppIdError(): { content: string; isError: boolean } {
|
|
35
|
+
return {
|
|
36
|
+
content: JSON.stringify({
|
|
37
|
+
error:
|
|
38
|
+
"app_id is required and no active app exists in this conversation. Call app_create first, or pass app_id explicitly.",
|
|
39
|
+
}),
|
|
40
|
+
isError: true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -669,11 +669,10 @@ export async function executeBrowserNavigate(
|
|
|
669
669
|
const newTab = input.new_tab === true;
|
|
670
670
|
if (newTab && cdp.kind === "extension") {
|
|
671
671
|
try {
|
|
672
|
-
const result = await cdp.send<{
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
);
|
|
672
|
+
const result = await cdp.send<{
|
|
673
|
+
tabId?: number | string;
|
|
674
|
+
clientId?: string;
|
|
675
|
+
}>("Vellum.createTab", {}, context.signal);
|
|
677
676
|
const tabId =
|
|
678
677
|
typeof result?.tabId === "number"
|
|
679
678
|
? String(result.tabId)
|
|
@@ -720,8 +719,7 @@ export async function executeBrowserNavigate(
|
|
|
720
719
|
// We're early-returning before the main try/finally block below,
|
|
721
720
|
// so we must dispose the cdp client manually to avoid leaking it.
|
|
722
721
|
clearPinnedTab(context.conversationId);
|
|
723
|
-
const message =
|
|
724
|
-
err instanceof Error ? err.message : String(err);
|
|
722
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
725
723
|
log.warn(
|
|
726
724
|
{ conversationId: context.conversationId, err },
|
|
727
725
|
"Vellum.createTab failed; aborting --new-tab navigate",
|
|
@@ -1075,7 +1073,7 @@ export async function executeBrowserNavigate(
|
|
|
1075
1073
|
"1. Take a snapshot to find the sign-in form elements",
|
|
1076
1074
|
);
|
|
1077
1075
|
lines.push(
|
|
1078
|
-
"2. Use credential fill to enter email/password from
|
|
1076
|
+
"2. Use credential fill to enter email/password from the credential vault",
|
|
1079
1077
|
);
|
|
1080
1078
|
lines.push(
|
|
1081
1079
|
"3. For email verification codes, use ui_show with a form to request the code mid-turn",
|
|
@@ -1102,7 +1100,7 @@ export async function executeBrowserNavigate(
|
|
|
1102
1100
|
lines.push("Handle this by interacting with the login form:");
|
|
1103
1101
|
lines.push("1. Take a snapshot to find the sign-in form elements");
|
|
1104
1102
|
lines.push(
|
|
1105
|
-
"2. Use credential fill to enter email/password from
|
|
1103
|
+
"2. Use credential fill to enter email/password from the credential vault",
|
|
1106
1104
|
);
|
|
1107
1105
|
lines.push(
|
|
1108
1106
|
"3. For email verification codes, use ui_show with a form to request the code mid-turn",
|
|
@@ -2175,13 +2173,13 @@ export async function executeBrowserFillCredential(
|
|
|
2175
2173
|
reason.includes("no stored value")
|
|
2176
2174
|
) {
|
|
2177
2175
|
return {
|
|
2178
|
-
content: `No credential stored for ${service}/${field}. Use
|
|
2176
|
+
content: `No credential stored for ${service}/${field}. Use \`assistant credentials set\` to save it first.`,
|
|
2179
2177
|
isError: true,
|
|
2180
2178
|
};
|
|
2181
2179
|
}
|
|
2182
2180
|
if (reason.includes("not allowed to use credential")) {
|
|
2183
2181
|
return {
|
|
2184
|
-
content: `Policy denied: ${reason} Update the credential's allowed_tools via
|
|
2182
|
+
content: `Policy denied: ${reason} Update the credential's allowed_tools via \`assistant credentials set\` if this tool should have access.`,
|
|
2185
2183
|
isError: true,
|
|
2186
2184
|
};
|
|
2187
2185
|
}
|
|
@@ -76,7 +76,7 @@ export class CredentialBroker {
|
|
|
76
76
|
reason:
|
|
77
77
|
`Tool "${request.toolName}" is not allowed to use credential ${request.service}/${request.field}. ` +
|
|
78
78
|
(tools.length === 0
|
|
79
|
-
? "No tools are currently allowed - update the credential
|
|
79
|
+
? "No tools are currently allowed - update the credential's allowed_tools via `assistant credentials set`."
|
|
80
80
|
: `Allowed tools: ${tools.join(", ")}.`),
|
|
81
81
|
};
|
|
82
82
|
}
|
|
@@ -186,7 +186,7 @@ export class CredentialBroker {
|
|
|
186
186
|
reason:
|
|
187
187
|
`Tool "${request.toolName}" is not allowed to use credential ${request.service}/${request.field}. ` +
|
|
188
188
|
(tools.length === 0
|
|
189
|
-
? "No tools are currently allowed - update the credential
|
|
189
|
+
? "No tools are currently allowed - update the credential's allowed_tools via `assistant credentials set`."
|
|
190
190
|
: `Allowed tools: ${tools.join(", ")}.`),
|
|
191
191
|
};
|
|
192
192
|
}
|
|
@@ -282,7 +282,7 @@ export class CredentialBroker {
|
|
|
282
282
|
reason:
|
|
283
283
|
`Tool "${request.toolName}" is not allowed to use credential ${request.service}/${request.field}. ` +
|
|
284
284
|
(tools.length === 0
|
|
285
|
-
? "No tools are currently allowed - update the credential
|
|
285
|
+
? "No tools are currently allowed - update the credential's allowed_tools via `assistant credentials set`."
|
|
286
286
|
: `Allowed tools: ${tools.join(", ")}.`),
|
|
287
287
|
};
|
|
288
288
|
}
|
|
@@ -365,7 +365,7 @@ export class CredentialBroker {
|
|
|
365
365
|
reason:
|
|
366
366
|
`Tool "${request.requestingTool}" is not allowed to use credential ${metadata.service}/${metadata.field}. ` +
|
|
367
367
|
(tools.length === 0
|
|
368
|
-
? "No tools are currently allowed - update the credential
|
|
368
|
+
? "No tools are currently allowed - update the credential's allowed_tools via `assistant credentials set`."
|
|
369
369
|
: `Allowed tools: ${tools.join(", ")}.`),
|
|
370
370
|
};
|
|
371
371
|
}
|