@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
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QuickJS-WASM script sandbox for the workflow orchestration engine.
|
|
3
|
+
*
|
|
4
|
+
* Workflow scripts authored by the assistant run here with NO ambient
|
|
5
|
+
* capabilities: no `fetch`, `process`, `Bun`, `require`, no network, no
|
|
6
|
+
* filesystem, no dynamic `import()`. The only way for a script to affect the
|
|
7
|
+
* outside world is through host functions explicitly injected by the caller
|
|
8
|
+
* (later PRs inject `agent()`, `parallel()`, `pipeline()`, etc).
|
|
9
|
+
*
|
|
10
|
+
* Scripts must be deterministic so a later PR can replay them for resume —
|
|
11
|
+
* hence `Date.now`, `Math.random`, and argless `new Date()` are banned and
|
|
12
|
+
* throw {@link WorkflowDeterminismError}. Time and entropy must be passed in
|
|
13
|
+
* via `args`.
|
|
14
|
+
*
|
|
15
|
+
* Host functions may return promises; the async QuickJS variant (asyncify)
|
|
16
|
+
* suspends the VM while the host promise settles and resumes with the result.
|
|
17
|
+
* From the script's perspective host calls are therefore SYNCHRONOUS — authors
|
|
18
|
+
* call `agent(...)` and get the value back directly, no `await`. (Asyncify can
|
|
19
|
+
* only unwind the main eval stack, never a promise continuation, so scripts
|
|
20
|
+
* must stay synchronous; see {@link buildRunner}.)
|
|
21
|
+
*/
|
|
22
|
+
import singlefileAsyncVariant from "@jitl/quickjs-singlefile-mjs-release-asyncify";
|
|
23
|
+
import {
|
|
24
|
+
newQuickJSAsyncWASMModuleFromVariant,
|
|
25
|
+
type QuickJSAsyncContext,
|
|
26
|
+
type QuickJSHandle,
|
|
27
|
+
} from "quickjs-emscripten";
|
|
28
|
+
|
|
29
|
+
import { deterministicStringify } from "./deterministic-stringify.js";
|
|
30
|
+
|
|
31
|
+
const DEFAULT_MEMORY_LIMIT_BYTES = 256 * 1024 * 1024;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* CPU budget for a single *contiguous* stretch of script execution between host
|
|
35
|
+
* calls. The interrupt handler — invoked periodically by the VM as it executes —
|
|
36
|
+
* returns `true` once this elapses, so a pure `while(true){}` (which never yields
|
|
37
|
+
* to a host call) is interrupted well before the daemon is starved.
|
|
38
|
+
*
|
|
39
|
+
* Crucially this bounds uninterrupted SCRIPT CPU, not total run wall-clock. The
|
|
40
|
+
* deadline is reset around every host-call boundary (see {@link installNativeBridge}),
|
|
41
|
+
* so seconds spent suspended in asyncify awaiting a host promise (each `agent()`
|
|
42
|
+
* leaf is a multi-second LLM round-trip) do NOT count against it. Total-runtime
|
|
43
|
+
* limits are the caller's responsibility via {@link CreateWorkflowSandboxOptions.signal}.
|
|
44
|
+
*/
|
|
45
|
+
const INTERRUPT_DEADLINE_MS = 5_000;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Minimum latency (ms) a host call must have awaited for its time to be excused
|
|
49
|
+
* from the contiguous-CPU {@link INTERRUPT_DEADLINE_MS} guard. The deadline is
|
|
50
|
+
* reset around a host call so a multi-second leaf/agent round-trip (the VM
|
|
51
|
+
* suspended in asyncify) is not counted as script CPU — but ONLY for calls that
|
|
52
|
+
* actually awaited that long. Cheap synchronous host fns (`usage`, `phase`,
|
|
53
|
+
* `log`, `leaf`) return in microseconds; if they reset the deadline too, a tight
|
|
54
|
+
* loop over one (e.g. `while (true) usage()`) would refresh the guard every
|
|
55
|
+
* iteration and pin the run slot burning CPU forever. Sub-threshold calls do not
|
|
56
|
+
* reset, so such a loop trips the CPU guard at {@link INTERRUPT_DEADLINE_MS}.
|
|
57
|
+
*/
|
|
58
|
+
const HOST_CALL_DEADLINE_RESET_THRESHOLD_MS = 50;
|
|
59
|
+
|
|
60
|
+
/** Thrown when a script invokes a banned non-deterministic primitive. */
|
|
61
|
+
export class WorkflowDeterminismError extends Error {
|
|
62
|
+
constructor(message: string) {
|
|
63
|
+
super(message);
|
|
64
|
+
this.name = "WorkflowDeterminismError";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Thrown when the sandboxed script itself throws or rejects. */
|
|
69
|
+
export class WorkflowScriptError extends Error {
|
|
70
|
+
/** The QuickJS-side stack trace, when one was available. */
|
|
71
|
+
readonly scriptStack?: string;
|
|
72
|
+
constructor(message: string, scriptStack?: string) {
|
|
73
|
+
super(message);
|
|
74
|
+
this.name = "WorkflowScriptError";
|
|
75
|
+
this.scriptStack = scriptStack;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Thrown when the script exceeds CPU (interrupt) or memory limits, or is aborted. */
|
|
80
|
+
export class WorkflowResourceError extends Error {
|
|
81
|
+
constructor(message: string) {
|
|
82
|
+
super(message);
|
|
83
|
+
this.name = "WorkflowResourceError";
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface CreateWorkflowSandboxOptions {
|
|
88
|
+
/**
|
|
89
|
+
* Host functions exposed to the script as globals of the same name. Each is
|
|
90
|
+
* called with JSON-marshalled arguments and may return a value or a promise;
|
|
91
|
+
* a returned promise suspends the VM (asyncify) until it settles, so the
|
|
92
|
+
* script observes a synchronous return value.
|
|
93
|
+
*/
|
|
94
|
+
hostFunctions: Record<
|
|
95
|
+
string,
|
|
96
|
+
(...args: unknown[]) => unknown | Promise<unknown>
|
|
97
|
+
>;
|
|
98
|
+
/** Receives strings passed to `console.log` / `console.error` inside the VM. */
|
|
99
|
+
onLog?: (msg: string) => void;
|
|
100
|
+
/** Aborts an in-flight run; the VM is interrupted and torn down. */
|
|
101
|
+
signal?: AbortSignal;
|
|
102
|
+
/** Runtime memory ceiling. Defaults to 256 MiB. */
|
|
103
|
+
memoryLimitBytes?: number;
|
|
104
|
+
/**
|
|
105
|
+
* CPU budget, in milliseconds, for a single contiguous stretch of script
|
|
106
|
+
* execution between host calls. Reset around every host-call boundary, so it
|
|
107
|
+
* never counts time suspended awaiting a host promise. Defaults to 5000.
|
|
108
|
+
* Primarily a test seam for exercising the interrupt guard with short delays;
|
|
109
|
+
* production should use the default.
|
|
110
|
+
*/
|
|
111
|
+
interruptDeadlineMs?: number;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface WorkflowSandbox {
|
|
115
|
+
/** Run `scriptSource` (JS or TS) with `args` available as the global `args`. */
|
|
116
|
+
run(scriptSource: string, args: unknown): Promise<unknown>;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Bootstrap code evaluated in the VM before the user script. It:
|
|
121
|
+
* - removes/poisons ambient capabilities,
|
|
122
|
+
* - installs determinism bans,
|
|
123
|
+
* - wires `console.*` and each host function to the single native bridge
|
|
124
|
+
* (`__hostCall` for host fns, `__hostLog` for logging).
|
|
125
|
+
*
|
|
126
|
+
* The host-function names are baked in so the script sees real globals (not a
|
|
127
|
+
* proxy) and so typos surface as ReferenceErrors at author time.
|
|
128
|
+
*/
|
|
129
|
+
function buildBootstrap(hostFunctionNames: string[]): string {
|
|
130
|
+
// Host functions are SYNCHRONOUS wrappers: the native bridge is asyncified,
|
|
131
|
+
// so QuickJS unwinds and resumes the VM stack transparently and `__hostCall`
|
|
132
|
+
// returns the resolved value directly (no VM-side promise). Authors may still
|
|
133
|
+
// write `await agent(...)` — awaiting a non-promise is a harmless no-op.
|
|
134
|
+
const hostFnSetup = hostFunctionNames
|
|
135
|
+
.map((name) => {
|
|
136
|
+
const key = JSON.stringify(name);
|
|
137
|
+
return `globalThis[${key}] = function (...callArgs) {
|
|
138
|
+
const resJson = __hostCall(${key}, JSON.stringify(callArgs));
|
|
139
|
+
return resJson === undefined ? undefined : JSON.parse(resJson);
|
|
140
|
+
};`;
|
|
141
|
+
})
|
|
142
|
+
.join("\n");
|
|
143
|
+
|
|
144
|
+
return `(() => {
|
|
145
|
+
"use strict";
|
|
146
|
+
|
|
147
|
+
const determinismError = (what) => {
|
|
148
|
+
const e = new Error(
|
|
149
|
+
what + " is non-deterministic and banned in workflow scripts. " +
|
|
150
|
+
"Pass timestamps/entropy in via 'args' so runs are reproducible for resume."
|
|
151
|
+
);
|
|
152
|
+
e.name = "WorkflowDeterminismError";
|
|
153
|
+
return e;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// --- Determinism bans -------------------------------------------------
|
|
157
|
+
Date.now = () => { throw determinismError("Date.now()"); };
|
|
158
|
+
const RealDate = Date;
|
|
159
|
+
const BannedDate = function (...dateArgs) {
|
|
160
|
+
if (dateArgs.length === 0) throw determinismError("new Date()");
|
|
161
|
+
return new RealDate(...dateArgs);
|
|
162
|
+
};
|
|
163
|
+
BannedDate.prototype = RealDate.prototype;
|
|
164
|
+
// Close the constructor escape hatch: the shared prototype's constructor still
|
|
165
|
+
// points at the original Date, so new (Date.prototype.constructor)() -- and
|
|
166
|
+
// someDate.constructor on any instance, since new Date(args) returns a RealDate
|
|
167
|
+
// whose prototype is this same object -- would bypass the new-Date() ban and
|
|
168
|
+
// read wall-clock time. Repoint it at the banned wrapper and lock the slot so a
|
|
169
|
+
// script can neither reach nor restore RealDate (otherwise closure-private).
|
|
170
|
+
// instanceof Date is unaffected because the prototype object itself is unchanged.
|
|
171
|
+
Object.defineProperty(RealDate.prototype, "constructor", {
|
|
172
|
+
value: BannedDate,
|
|
173
|
+
writable: false,
|
|
174
|
+
enumerable: false,
|
|
175
|
+
configurable: false,
|
|
176
|
+
});
|
|
177
|
+
BannedDate.UTC = RealDate.UTC;
|
|
178
|
+
BannedDate.parse = RealDate.parse;
|
|
179
|
+
BannedDate.now = () => { throw determinismError("Date.now()"); };
|
|
180
|
+
globalThis.Date = BannedDate;
|
|
181
|
+
Math.random = () => { throw determinismError("Math.random()"); };
|
|
182
|
+
|
|
183
|
+
// --- Strip ambient capabilities --------------------------------------
|
|
184
|
+
// Defensive: most of these never exist in QuickJS, but delete any that
|
|
185
|
+
// a host/intrinsic might surface, and ensure timers are absent.
|
|
186
|
+
for (const cap of [
|
|
187
|
+
"fetch", "XMLHttpRequest", "WebSocket", "process", "Bun", "require",
|
|
188
|
+
"module", "global", "setTimeout", "setInterval", "setImmediate",
|
|
189
|
+
"clearTimeout", "clearInterval", "queueMicrotask", "navigator",
|
|
190
|
+
]) {
|
|
191
|
+
try { delete globalThis[cap]; } catch (_e) {}
|
|
192
|
+
try {
|
|
193
|
+
Object.defineProperty(globalThis, cap, {
|
|
194
|
+
value: undefined, writable: false, configurable: false,
|
|
195
|
+
});
|
|
196
|
+
} catch (_e) {}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// --- Logging ----------------------------------------------------------
|
|
200
|
+
const log = (...parts) => {
|
|
201
|
+
try {
|
|
202
|
+
__hostLog(parts.map((p) =>
|
|
203
|
+
typeof p === "string" ? p : JSON.stringify(p)
|
|
204
|
+
).join(" "));
|
|
205
|
+
} catch (_e) {}
|
|
206
|
+
};
|
|
207
|
+
globalThis.console = { log, error: log, warn: log, info: log, debug: log };
|
|
208
|
+
|
|
209
|
+
// --- Host functions ---------------------------------------------------
|
|
210
|
+
${hostFnSetup}
|
|
211
|
+
})();`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* The user script body, wrapped in a SYNCHRONOUS function so `args` is in scope
|
|
216
|
+
* and an explicit `return` is supported. Workflow scripts are synchronous: host
|
|
217
|
+
* functions block the script via asyncify (the VM's WASM stack is unwound while
|
|
218
|
+
* the host promise settles, then restored), so authors call `agent(...)` and
|
|
219
|
+
* get the value back directly — no `await`.
|
|
220
|
+
*
|
|
221
|
+
* Asyncify can only suspend the main eval stack, never a promise continuation,
|
|
222
|
+
* so an `async`/`await` script would deadlock on its second host call. A
|
|
223
|
+
* synchronous runner keeps every host call on the eval stack where asyncify
|
|
224
|
+
* works. (`return` returns the value directly; the host reads it from the
|
|
225
|
+
* `evalCodeAsync` result.)
|
|
226
|
+
*/
|
|
227
|
+
function buildRunner(scriptSource: string): string {
|
|
228
|
+
return `(function () {
|
|
229
|
+
const args = globalThis.__args;
|
|
230
|
+
${scriptSource}
|
|
231
|
+
})();`;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function createWorkflowSandbox(
|
|
235
|
+
opts: CreateWorkflowSandboxOptions,
|
|
236
|
+
): WorkflowSandbox {
|
|
237
|
+
const transpiler = new Bun.Transpiler({ loader: "ts" });
|
|
238
|
+
const memoryLimitBytes = opts.memoryLimitBytes ?? DEFAULT_MEMORY_LIMIT_BYTES;
|
|
239
|
+
const interruptDeadlineMs = opts.interruptDeadlineMs ?? INTERRUPT_DEADLINE_MS;
|
|
240
|
+
const hostFunctionNames = Object.keys(opts.hostFunctions);
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
async run(scriptSource: string, args: unknown): Promise<unknown> {
|
|
244
|
+
// Wrap first, then transpile, so the author may use top-level `return`
|
|
245
|
+
// and TS syntax inside the synchronous runner. Transpile before spinning
|
|
246
|
+
// up the VM so author TS errors fail cheaply.
|
|
247
|
+
let runnerSource: string;
|
|
248
|
+
try {
|
|
249
|
+
runnerSource = transpiler.transformSync(buildRunner(scriptSource));
|
|
250
|
+
} catch (e) {
|
|
251
|
+
throw new WorkflowScriptError(
|
|
252
|
+
`Failed to transpile workflow script: ${(e as Error).message}`,
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// A fresh WASM module/runtime/context per run is the isolation boundary:
|
|
257
|
+
// no realm, heap, or host-ref state is shared between workflow runs.
|
|
258
|
+
// The context is created via the module-level `newContext()` so the
|
|
259
|
+
// runtime is an *owned lifetime* of the context — disposing the context
|
|
260
|
+
// tears down the runtime in the correct internal order (disposing the
|
|
261
|
+
// runtime separately mis-orders host-ref teardown and aborts the VM).
|
|
262
|
+
// Use the SINGLE-FILE async variant (WASM embedded in the JS module). The
|
|
263
|
+
// default loader fetches a sidecar `emscripten-module.wasm`, which is
|
|
264
|
+
// absent from the compiled daemon's virtual `/$bunfs/` filesystem
|
|
265
|
+
// (`bun build --compile` bundles JS but not the .wasm), so every workflow
|
|
266
|
+
// run aborts with ENOENT. The singlefile variant rides along with the JS
|
|
267
|
+
// bundle and works in source, Docker, and the compiled binary alike.
|
|
268
|
+
const QuickJS = await newQuickJSAsyncWASMModuleFromVariant(
|
|
269
|
+
singlefileAsyncVariant,
|
|
270
|
+
);
|
|
271
|
+
const vm = QuickJS.newContext() as QuickJSAsyncContext;
|
|
272
|
+
const runtime = vm.runtime;
|
|
273
|
+
runtime.setMemoryLimit(memoryLimitBytes);
|
|
274
|
+
|
|
275
|
+
// The deadline bounds CONTIGUOUS script CPU between host calls, not total
|
|
276
|
+
// run wall-clock. It is held in a one-element box so the native bridge can
|
|
277
|
+
// reset it around each host-call boundary (where the VM is suspended in
|
|
278
|
+
// asyncify awaiting a host promise — time that must not count against the
|
|
279
|
+
// CPU guard). `signal.aborted` remains the hard, total-runtime stop.
|
|
280
|
+
const deadline = { at: Date.now() + interruptDeadlineMs };
|
|
281
|
+
runtime.setInterruptHandler(() => {
|
|
282
|
+
if (opts.signal?.aborted) return true;
|
|
283
|
+
return Date.now() > deadline.at;
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
installNativeBridge(vm, opts, () => {
|
|
288
|
+
deadline.at = Date.now() + interruptDeadlineMs;
|
|
289
|
+
});
|
|
290
|
+
seedArgs(vm, args);
|
|
291
|
+
|
|
292
|
+
// Bootstrap: strip capabilities, install bans, wire host fns.
|
|
293
|
+
const bootstrapResult = await vm.evalCodeAsync(
|
|
294
|
+
buildBootstrap(hostFunctionNames),
|
|
295
|
+
"workflow-bootstrap.js",
|
|
296
|
+
);
|
|
297
|
+
unwrapOrThrow(vm, bootstrapResult).dispose();
|
|
298
|
+
|
|
299
|
+
// Evaluating the runner drives all asyncified host calls to completion
|
|
300
|
+
// (asyncify suspends/resumes the VM stack inside evalCodeAsync itself)
|
|
301
|
+
// and returns the script's value directly.
|
|
302
|
+
const evalResult = await vm.evalCodeAsync(
|
|
303
|
+
runnerSource,
|
|
304
|
+
"workflow-script.js",
|
|
305
|
+
);
|
|
306
|
+
using valueHandle = unwrapOrThrow(vm, evalResult);
|
|
307
|
+
return vm.dump(valueHandle);
|
|
308
|
+
} catch (e) {
|
|
309
|
+
rethrowResourceErrorIfAborted(opts.signal, e);
|
|
310
|
+
throw e;
|
|
311
|
+
} finally {
|
|
312
|
+
// Disposing the context disposes its owned runtime too.
|
|
313
|
+
vm.dispose();
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function installNativeBridge(
|
|
320
|
+
vm: QuickJSAsyncContext,
|
|
321
|
+
opts: CreateWorkflowSandboxOptions,
|
|
322
|
+
resetDeadline: () => void,
|
|
323
|
+
): void {
|
|
324
|
+
// Single asyncified bridge for all host functions. Suspends the VM while the
|
|
325
|
+
// host promise settles, then resumes with a JSON string (or undefined).
|
|
326
|
+
const hostCall = vm.newAsyncifiedFunction(
|
|
327
|
+
"__hostCall",
|
|
328
|
+
async (nameHandle, argsJsonHandle) => {
|
|
329
|
+
const name = vm.getString(nameHandle);
|
|
330
|
+
const argsJson = vm.getString(argsJsonHandle);
|
|
331
|
+
const fn = opts.hostFunctions[name];
|
|
332
|
+
if (!fn) {
|
|
333
|
+
// Unreachable from a well-formed script (host names are baked into
|
|
334
|
+
// bootstrap globals), but surface as a VM exception if it ever is.
|
|
335
|
+
throw new Error(`Unknown host function: ${name}`);
|
|
336
|
+
}
|
|
337
|
+
const parsedArgs = JSON.parse(argsJson) as unknown[];
|
|
338
|
+
// A host throw/rejection propagates as a VM exception the script can
|
|
339
|
+
// catch, or — if uncaught — as a WorkflowScriptError to the caller.
|
|
340
|
+
const startedAtMs = Date.now();
|
|
341
|
+
try {
|
|
342
|
+
const result = await fn(...parsedArgs);
|
|
343
|
+
// `undefined` JSON-stringifies to `undefined`; map it to the VM's
|
|
344
|
+
// undefined so the sync wrapper returns `undefined` rather than NaN.
|
|
345
|
+
if (result === undefined) return vm.undefined;
|
|
346
|
+
return vm.newString(deterministicStringify(result));
|
|
347
|
+
} finally {
|
|
348
|
+
// The VM is about to resume script execution. Reset the CPU budget so the
|
|
349
|
+
// (possibly multi-second) host-call latency we just awaited does not count
|
|
350
|
+
// against the interrupt deadline — that guards contiguous script CPU only,
|
|
351
|
+
// not cumulative host-call wall-clock. This runs in `finally` so it
|
|
352
|
+
// resets on a host REJECTION too: when a leaf fails after a multi-second
|
|
353
|
+
// round-trip and the script catches it, the catch/cleanup block must not
|
|
354
|
+
// resume against an already-expired deadline and be killed as a
|
|
355
|
+
// WorkflowResourceError instead of handling the failure. But reset ONLY
|
|
356
|
+
// when the call actually awaited a meaningful amount of time: a cheap
|
|
357
|
+
// synchronous host fn (`usage`/`phase`/`log`/`leaf`) returns in
|
|
358
|
+
// microseconds, and resetting for it would let `while (true) usage()`
|
|
359
|
+
// refresh the deadline every iteration and burn CPU forever. Sub-threshold
|
|
360
|
+
// calls don't reset, so such a loop trips the guard at the deadline.
|
|
361
|
+
// `signal` remains the hard stop.
|
|
362
|
+
if (Date.now() - startedAtMs >= HOST_CALL_DEADLINE_RESET_THRESHOLD_MS) {
|
|
363
|
+
resetDeadline();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
);
|
|
368
|
+
hostCall.consume((handle) => vm.setProp(vm.global, "__hostCall", handle));
|
|
369
|
+
|
|
370
|
+
const hostLog = vm.newFunction("__hostLog", (msgHandle) => {
|
|
371
|
+
const msg = vm.getString(msgHandle);
|
|
372
|
+
opts.onLog?.(msg);
|
|
373
|
+
});
|
|
374
|
+
hostLog.consume((handle) => vm.setProp(vm.global, "__hostLog", handle));
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function seedArgs(vm: QuickJSAsyncContext, args: unknown): void {
|
|
378
|
+
// Embed the args JSON in a parse expression so `__args` is a native VM object
|
|
379
|
+
// (not a host reference) without leaving the raw JSON string on a global.
|
|
380
|
+
const literal = JSON.stringify(deterministicStringify(args));
|
|
381
|
+
const parsed = vm.evalCode(`JSON.parse(${literal})`);
|
|
382
|
+
const handle = unwrapOrThrow(vm, parsed);
|
|
383
|
+
handle.consume((h) => vm.setProp(vm.global, "__args", h));
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Unwrap a QuickJS eval/call result, converting a VM-side error into a typed
|
|
388
|
+
* {@link WorkflowScriptError} (or {@link WorkflowDeterminismError}) with the
|
|
389
|
+
* VM stack attached.
|
|
390
|
+
*/
|
|
391
|
+
function unwrapOrThrow(
|
|
392
|
+
vm: QuickJSAsyncContext,
|
|
393
|
+
result: { error: QuickJSHandle } | { value: QuickJSHandle } | QuickJSHandle,
|
|
394
|
+
): QuickJSHandle {
|
|
395
|
+
// evalCode / evalCodeAsync return a SuccessOrFail; a bare handle is success.
|
|
396
|
+
if (result && typeof result === "object" && "error" in result) {
|
|
397
|
+
const errorHandle = (result as { error: QuickJSHandle }).error;
|
|
398
|
+
try {
|
|
399
|
+
throw vmErrorToTyped(vm, errorHandle);
|
|
400
|
+
} finally {
|
|
401
|
+
errorHandle.dispose();
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (result && typeof result === "object" && "value" in result) {
|
|
405
|
+
return (result as { value: QuickJSHandle }).value;
|
|
406
|
+
}
|
|
407
|
+
return result as QuickJSHandle;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function vmErrorToTyped(
|
|
411
|
+
vm: QuickJSAsyncContext,
|
|
412
|
+
errorHandle: QuickJSHandle,
|
|
413
|
+
): Error {
|
|
414
|
+
const dumped = vm.dump(errorHandle) as
|
|
415
|
+
| { name?: string; message?: string; stack?: string }
|
|
416
|
+
| string
|
|
417
|
+
| undefined;
|
|
418
|
+
|
|
419
|
+
if (typeof dumped === "string") {
|
|
420
|
+
return new WorkflowScriptError(dumped);
|
|
421
|
+
}
|
|
422
|
+
const name = dumped?.name;
|
|
423
|
+
const message = dumped?.message ?? "Workflow script error";
|
|
424
|
+
const stack = dumped?.stack;
|
|
425
|
+
|
|
426
|
+
if (name === "WorkflowDeterminismError") {
|
|
427
|
+
return new WorkflowDeterminismError(message);
|
|
428
|
+
}
|
|
429
|
+
return new WorkflowScriptError(message, stack);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function rethrowResourceErrorIfAborted(
|
|
433
|
+
signal: AbortSignal | undefined,
|
|
434
|
+
caught: unknown,
|
|
435
|
+
): void {
|
|
436
|
+
// An interrupt (CPU deadline or abort) surfaces as a QuickJS "interrupted"
|
|
437
|
+
// error; normalize it to a typed resource error so callers can distinguish
|
|
438
|
+
// a wedged/aborted script from an author bug.
|
|
439
|
+
const message = caught instanceof Error ? caught.message : String(caught);
|
|
440
|
+
if (signal?.aborted || /interrupt/i.test(message)) {
|
|
441
|
+
throw new WorkflowResourceError(
|
|
442
|
+
signal?.aborted
|
|
443
|
+
? "Workflow script aborted via signal."
|
|
444
|
+
: "Workflow script exceeded its execution time budget and was interrupted.",
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
|
+
import { getCatalogProviderForModel } from "../providers/model-catalog.js";
|
|
5
|
+
|
|
4
6
|
// Enable adaptive thinking on the managed "balanced" and "quality-optimized"
|
|
5
7
|
// profiles.
|
|
6
8
|
//
|
|
@@ -28,11 +30,14 @@ import { join } from "node:path";
|
|
|
28
30
|
// - Are source: "user" (user-created profiles are untouched)
|
|
29
31
|
// - Have a non-managed, non-absent source (unknown origin)
|
|
30
32
|
// - Resolve to a non-Anthropic provider (adaptive thinking is
|
|
31
|
-
// Anthropic-specific).
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
33
|
+
// Anthropic-specific). Effective provider is the profile's explicit
|
|
34
|
+
// `provider`; otherwise the provider implied by a known catalog `model`
|
|
35
|
+
// (mirroring the resolver's withImpliedProviderForKnownModel); otherwise
|
|
36
|
+
// llm.default.provider — with a completely absent llm.default.provider
|
|
37
|
+
// treated as Anthropic, matching migration 052's own `?? "anthropic"`
|
|
38
|
+
// default. This keeps the legacy non-Anthropic empty `{}` shells seeded by
|
|
39
|
+
// migration 052 off the repair while still patching profiles that pin a
|
|
40
|
+
// known Claude model under a non-Anthropic default.
|
|
36
41
|
|
|
37
42
|
const ADAPTIVE_THINKING = { enabled: true, streamThinking: true } as const;
|
|
38
43
|
const TARGET_PROFILES = ["balanced", "quality-optimized"] as const;
|
|
@@ -63,8 +68,9 @@ export function repairAdaptiveThinkingOnManagedProfiles(
|
|
|
63
68
|
const profiles = readObject(llm.profiles);
|
|
64
69
|
if (profiles === null) return;
|
|
65
70
|
|
|
66
|
-
// Profiles without an explicit provider
|
|
67
|
-
//
|
|
71
|
+
// Profiles without an explicit provider and without a model-implied provider
|
|
72
|
+
// inherit llm.default.provider at resolution time; an absent
|
|
73
|
+
// llm.default.provider resolves to Anthropic.
|
|
68
74
|
const defaultBlock = readObject(llm.default);
|
|
69
75
|
const defaultProvider =
|
|
70
76
|
typeof defaultBlock?.provider === "string"
|
|
@@ -81,12 +87,16 @@ export function repairAdaptiveThinkingOnManagedProfiles(
|
|
|
81
87
|
// Legacy profiles created before the `source` metadata field was introduced
|
|
82
88
|
// have source=undefined. Treat these as managed when the profile name is one
|
|
83
89
|
// of the canonical managed names (which TARGET_PROFILES already guarantees)
|
|
84
|
-
// and the effective provider
|
|
85
|
-
//
|
|
90
|
+
// and the effective provider is Anthropic. Effective provider is the
|
|
91
|
+
// explicit `provider`, else the provider implied by a known catalog
|
|
92
|
+
// `model`, else the inherited llm.default.provider. Explicit
|
|
93
|
+
// `source: "user"` profiles are always skipped.
|
|
86
94
|
if (profile.source === "user") continue;
|
|
87
95
|
if (profile.source !== undefined && profile.source !== "managed") continue;
|
|
88
|
-
const effectiveProvider =
|
|
89
|
-
|
|
96
|
+
const effectiveProvider = resolveEffectiveProvider(
|
|
97
|
+
profile,
|
|
98
|
+
defaultProvider,
|
|
99
|
+
);
|
|
90
100
|
if (effectiveProvider !== "anthropic") continue;
|
|
91
101
|
|
|
92
102
|
// Skip if thinking is already enabled.
|
|
@@ -105,6 +115,18 @@ export function repairAdaptiveThinkingOnManagedProfiles(
|
|
|
105
115
|
}
|
|
106
116
|
}
|
|
107
117
|
|
|
118
|
+
function resolveEffectiveProvider(
|
|
119
|
+
profile: Record<string, unknown>,
|
|
120
|
+
defaultProvider: string,
|
|
121
|
+
): string {
|
|
122
|
+
if (typeof profile.provider === "string") return profile.provider;
|
|
123
|
+
if (typeof profile.model === "string") {
|
|
124
|
+
const implied = getCatalogProviderForModel(profile.model);
|
|
125
|
+
if (implied !== undefined) return implied;
|
|
126
|
+
}
|
|
127
|
+
return defaultProvider;
|
|
128
|
+
}
|
|
129
|
+
|
|
108
130
|
function readObject(value: unknown): Record<string, unknown> | null {
|
|
109
131
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
110
132
|
return null;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Workspace migration 021: Move signals directory from root to workspace.
|
|
3
3
|
*
|
|
4
4
|
* Previously, `~/.vellum/signals/` lived directly under the Vellum root. This
|
|
5
|
-
* migration moves any existing signal files into
|
|
5
|
+
* migration moves any existing signal files into `$VELLUM_WORKSPACE_DIR/signals/`
|
|
6
6
|
* so that getSignalsDir() resolves correctly under the workspace.
|
|
7
7
|
*
|
|
8
8
|
* Signal files are ephemeral IPC artifacts (written, read once, then stale),
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Previously, `~/.vellum/hooks/` lived directly under the Vellum root. This
|
|
5
5
|
* migration moves existing hook directories and files into
|
|
6
|
-
*
|
|
6
|
+
* `$VELLUM_WORKSPACE_DIR/hooks/` so that getWorkspaceHooksDir() resolves
|
|
7
7
|
* correctly under the workspace.
|
|
8
8
|
*
|
|
9
9
|
* Hooks are persistent user-installed scripts (manifests, config, executables),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Workspace migration 026: Backfill install-meta.json for existing skills
|
|
3
3
|
*
|
|
4
|
-
* Scans
|
|
4
|
+
* Scans $VELLUM_WORKSPACE_DIR/skills/ for installed skill directories and writes
|
|
5
5
|
* an install-meta.json for each skill that lacks one, inferring the origin
|
|
6
6
|
* from legacy version.json and .integrity.json files.
|
|
7
7
|
*
|
|
@@ -13,7 +13,8 @@ threads.md
|
|
|
13
13
|
buffer.md
|
|
14
14
|
`;
|
|
15
15
|
|
|
16
|
-
const INDEX_ENTRY =
|
|
16
|
+
const INDEX_ENTRY =
|
|
17
|
+
"- _autoinject.md — Controls which files are loaded into every conversation";
|
|
17
18
|
|
|
18
19
|
export const seedPkbAutoinjectMigration: WorkspaceMigration = {
|
|
19
20
|
id: "030-seed-pkb-autoinject",
|
|
@@ -300,10 +300,7 @@ export const dropUserMdMigration: WorkspaceMigration = {
|
|
|
300
300
|
unlinkSync(userMdPath);
|
|
301
301
|
log.info({ path: userMdPath }, "Deleted legacy workspace USER.md");
|
|
302
302
|
} catch (err) {
|
|
303
|
-
log.warn(
|
|
304
|
-
{ err, path: userMdPath },
|
|
305
|
-
"Failed to delete legacy USER.md",
|
|
306
|
-
);
|
|
303
|
+
log.warn({ err, path: userMdPath }, "Failed to delete legacy USER.md");
|
|
307
304
|
}
|
|
308
305
|
}
|
|
309
306
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Workspace migration 046: Remove legacy `workspace/hooks/` directory.
|
|
3
3
|
*
|
|
4
|
-
* Migration 022 moved `~/.vellum/hooks/` into
|
|
4
|
+
* Migration 022 moved `~/.vellum/hooks/` into `$VELLUM_WORKSPACE_DIR/hooks/`.
|
|
5
5
|
* With the hook system entirely removed, that directory is dead state — it is
|
|
6
6
|
* no longer read or written by the assistant. This migration deletes the
|
|
7
7
|
* directory (and everything under it) so stale hook manifests, config, and
|
|
@@ -9,7 +9,9 @@ import { join } from "node:path";
|
|
|
9
9
|
import { getLogger } from "../../util/logger.js";
|
|
10
10
|
import type { WorkspaceMigration } from "./types.js";
|
|
11
11
|
|
|
12
|
-
const log = getLogger(
|
|
12
|
+
const log = getLogger(
|
|
13
|
+
"workspace-migration-056-release-notes-inference-profile-reordering",
|
|
14
|
+
);
|
|
13
15
|
|
|
14
16
|
const MIGRATION_ID = "056-release-notes-inference-profile-reordering";
|
|
15
17
|
const MARKER = `<!-- release-note-id:${MIGRATION_ID} -->`;
|
|
@@ -25,7 +27,8 @@ profile dropdown, chat profile picker, and per-call-site overrides.
|
|
|
25
27
|
export const releaseNotesInferenceProfileReorderingMigration: WorkspaceMigration =
|
|
26
28
|
{
|
|
27
29
|
id: MIGRATION_ID,
|
|
28
|
-
description:
|
|
30
|
+
description:
|
|
31
|
+
"Append release notes for inference profile reordering to UPDATES.md",
|
|
29
32
|
|
|
30
33
|
run(workspaceDir: string): void {
|
|
31
34
|
const updatesPath = join(workspaceDir, "UPDATES.md");
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Workspace migration 061: Move backup.key from protected/ to workspace.
|
|
3
3
|
*
|
|
4
4
|
* The backup encryption key previously lived at ~/.vellum/protected/backup.key.
|
|
5
|
-
* This migration copies it to
|
|
5
|
+
* This migration copies it to $VELLUM_WORKSPACE_DIR/.backup.key so the daemon
|
|
6
6
|
* no longer depends on the protected directory (owned by the gateway).
|
|
7
7
|
*
|
|
8
8
|
* The old file is removed after a successful copy. If the new file already
|
|
@@ -47,7 +47,9 @@ import { join } from "node:path";
|
|
|
47
47
|
import { getLogger } from "../../util/logger.js";
|
|
48
48
|
import type { WorkspaceMigration } from "./types.js";
|
|
49
49
|
|
|
50
|
-
const log = getLogger(
|
|
50
|
+
const log = getLogger(
|
|
51
|
+
"workspace-migration-082-backfill-managed-profile-labels",
|
|
52
|
+
);
|
|
51
53
|
|
|
52
54
|
/**
|
|
53
55
|
* Bare template labels for the canonical managed profile triplet. Kept in
|
|
@@ -123,7 +125,11 @@ export const backfillManagedProfileLabelsMigration: WorkspaceMigration = {
|
|
|
123
125
|
if (!modified) return;
|
|
124
126
|
|
|
125
127
|
try {
|
|
126
|
-
writeFileSync(
|
|
128
|
+
writeFileSync(
|
|
129
|
+
configPath,
|
|
130
|
+
JSON.stringify(parsed, null, 2) + "\n",
|
|
131
|
+
"utf-8",
|
|
132
|
+
);
|
|
127
133
|
log.info(
|
|
128
134
|
{ path: configPath },
|
|
129
135
|
"Backfilled missing labels on canonical managed inference profiles",
|