@vellumai/assistant 0.8.5 → 0.8.6
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 +33 -1
- package/ARCHITECTURE.md +1 -1
- package/bunfig.toml +6 -1
- package/docs/credential-execution-service.md +6 -6
- package/docs/plugins.md +4 -3
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +12 -13
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +4 -1
- package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
- package/openapi.yaml +1900 -166
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +3 -2
- package/src/__tests__/agent-loop-exit-reason.test.ts +102 -9
- package/src/__tests__/agent-loop-override-profile.test.ts +2 -1
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/annotate-risk-options.test.ts +1 -0
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +9 -13
- package/src/__tests__/assert-not-live-db.ts +79 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +9 -25
- package/src/__tests__/audit-log-rotation.test.ts +2 -2
- package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
- package/src/__tests__/background-workers-disk-pressure.test.ts +5 -8
- package/src/__tests__/browser-skill-endstate.test.ts +3 -3
- package/src/__tests__/btw-routes.test.ts +3 -2
- package/src/__tests__/call-controller.test.ts +3 -2
- package/src/__tests__/channel-approval-routes.test.ts +3 -2
- package/src/__tests__/channel-guardian.test.ts +3 -2
- package/src/__tests__/channel-readiness-slack-remote.test.ts +175 -0
- package/src/__tests__/channel-reply-delivery.test.ts +35 -0
- package/src/__tests__/channel-retry-sweep.test.ts +320 -3
- package/src/__tests__/checker.test.ts +12 -12
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/compaction-trail-store.test.ts +264 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
- package/src/__tests__/computer-use-tools.test.ts +12 -14
- package/src/__tests__/config-loader-backfill.test.ts +13 -28
- package/src/__tests__/config-loader-corrupt.test.ts +5 -5
- package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
- package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
- package/src/__tests__/config-schema.test.ts +10 -10
- package/src/__tests__/connection-model-compat.test.ts +83 -0
- package/src/__tests__/contacts-tools.test.ts +3 -2
- package/src/__tests__/context-token-estimator.test.ts +22 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +5 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +34 -0
- package/src/__tests__/conversation-agent-loop.test.ts +488 -2
- package/src/__tests__/conversation-analysis-routes.test.ts +1 -0
- package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -0
- package/src/__tests__/conversation-attention-store.test.ts +101 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- package/src/__tests__/conversation-error.test.ts +30 -0
- package/src/__tests__/conversation-fork-crud.test.ts +69 -8
- package/src/__tests__/conversation-fork-route.test.ts +3 -2
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
- package/src/__tests__/conversation-lifecycle.test.ts +1 -0
- package/src/__tests__/conversation-list-source.test.ts +3 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -1
- package/src/__tests__/conversation-load-history-stripped.test.ts +1 -0
- package/src/__tests__/conversation-pairing.test.ts +53 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +26 -7
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -0
- package/src/__tests__/conversation-queue.test.ts +333 -291
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -18
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +33 -8
- package/src/__tests__/conversation-routes-slash-commands.test.ts +33 -2
- package/src/__tests__/conversation-runtime-assembly.test.ts +78 -0
- package/src/__tests__/conversation-skill-tools.test.ts +38 -142
- package/src/__tests__/conversation-slash-queue.test.ts +84 -32
- package/src/__tests__/conversation-slash-unknown.test.ts +5 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +46 -0
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -3
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -17
- package/src/__tests__/conversation-sync-tags.test.ts +128 -12
- package/src/__tests__/conversation-title-service.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +30 -0
- package/src/__tests__/conversation-usage.test.ts +1 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -0
- package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
- package/src/__tests__/credential-broker-server-use.test.ts +5 -5
- package/src/__tests__/credential-execution-client.test.ts +72 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
- package/src/__tests__/credential-health-service.test.ts +252 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -5
- package/src/__tests__/credential-vault-unit.test.ts +19 -19
- package/src/__tests__/credential-vault.test.ts +5 -5
- package/src/__tests__/cross-provider-web-search.test.ts +56 -2
- package/src/__tests__/db-connection-isolation.test.ts +7 -6
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
- package/src/__tests__/db-test-helpers.ts +58 -0
- package/src/__tests__/disk-pressure-guard.test.ts +58 -41
- package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
- package/src/__tests__/disk-pressure-routes.test.ts +0 -33
- package/src/__tests__/disk-pressure-tools.test.ts +0 -4
- package/src/__tests__/dm-persistence.test.ts +26 -40
- package/src/__tests__/document-create-dedupe.test.ts +189 -0
- package/src/__tests__/document-find-replace.test.ts +3 -2
- package/src/__tests__/document-tool-security.test.ts +81 -2
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
- package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
- package/src/__tests__/encrypted-store.test.ts +11 -9
- package/src/__tests__/feature-flag-test-helpers.ts +53 -0
- package/src/__tests__/filing-service.test.ts +1 -0
- package/src/__tests__/first-greeting.test.ts +62 -12
- package/src/__tests__/gateway-flag-listener.test.ts +0 -1
- package/src/__tests__/gemini-provider.test.ts +26 -0
- package/src/__tests__/guardian-action-sweep.test.ts +3 -2
- package/src/__tests__/guardian-outbound-http.test.ts +3 -2
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -0
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/helpers/mock-logger.ts +26 -0
- package/src/__tests__/host-bash-routes.test.ts +1 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-shell-tool.test.ts +5 -4
- package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
- package/src/__tests__/http-conversation-lineage.test.ts +3 -2
- package/src/__tests__/http-user-message-parity.test.ts +29 -7
- package/src/__tests__/identity-intro-cache.test.ts +133 -22
- package/src/__tests__/inbound-slack-persistence.test.ts +44 -72
- package/src/__tests__/inference-profile-reaper.test.ts +3 -2
- package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
- package/src/__tests__/injector-disk-pressure.test.ts +3 -17
- package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
- package/src/__tests__/list-messages-hidden-metadata.test.ts +80 -0
- package/src/__tests__/llm-context-normalization.test.ts +42 -0
- package/src/__tests__/llm-resolver.test.ts +331 -0
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
- package/src/__tests__/mcp-abort-signal.test.ts +14 -0
- package/src/__tests__/mcp-client-auth.test.ts +14 -0
- package/src/__tests__/messaging-send-tool.test.ts +1 -0
- package/src/__tests__/migration-import-from-url.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +18 -2
- package/src/__tests__/model-intents.test.ts +3 -3
- package/src/__tests__/native-web-search.test.ts +30 -2
- package/src/__tests__/notification-deep-link.test.ts +62 -0
- package/src/__tests__/oauth-commands-routes.test.ts +37 -0
- package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
- package/src/__tests__/oauth-store.test.ts +3 -2
- package/src/__tests__/onboarding-template-contract.test.ts +3 -2
- package/src/__tests__/openai-provider.test.ts +8 -9
- package/src/__tests__/openai-responses-provider.test.ts +70 -10
- package/src/__tests__/openrouter-provider-only.test.ts +27 -5
- package/src/__tests__/outbound-slack-persistence.test.ts +46 -1
- package/src/__tests__/persistence-pipeline.test.ts +139 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +83 -12
- package/src/__tests__/plugin-bootstrap.test.ts +9 -11
- package/src/__tests__/plugin-tool-contribution.test.ts +41 -38
- package/src/__tests__/process-message-background-slack.test.ts +21 -16
- package/src/__tests__/process-message-display-content.test.ts +19 -22
- package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
- package/src/__tests__/provider-platform-proxy-integration.test.ts +216 -4
- package/src/__tests__/provider-registry-ollama.test.ts +45 -22
- package/src/__tests__/recording-handler.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +82 -76
- package/src/__tests__/relay-server.test.ts +10 -10
- package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
- package/src/__tests__/schedule-store.test.ts +16 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
- package/src/__tests__/secret-ingress-http.test.ts +5 -1
- package/src/__tests__/secure-keys.test.ts +3 -3
- package/src/__tests__/send-endpoint-busy.test.ts +81 -42
- package/src/__tests__/server-history-render.test.ts +4 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +8 -10
- package/src/__tests__/skill-feature-flags.test.ts +14 -16
- package/src/__tests__/skill-load-feature-flag.test.ts +5 -5
- package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
- package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
- package/src/__tests__/skill-tool-factory.test.ts +96 -95
- package/src/__tests__/slack-channel-config.test.ts +3 -3
- package/src/__tests__/subagent-call-site-routing.test.ts +11 -3
- package/src/__tests__/subagent-disposal.test.ts +27 -8
- package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
- package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
- package/src/__tests__/subagent-manager-notify.test.ts +20 -8
- package/src/__tests__/subagent-notify-parent.test.ts +5 -4
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
- package/src/__tests__/subagent-tools.test.ts +2 -1
- package/src/__tests__/suggestion-routes.test.ts +1 -0
- package/src/__tests__/system-prompt.test.ts +38 -0
- package/src/__tests__/test-preload-verifier.ts +68 -0
- package/src/__tests__/test-preload.ts +32 -39
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +20 -7
- package/src/__tests__/tool-executor.test.ts +55 -10
- package/src/__tests__/tool-preview-lifecycle.test.ts +1 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/twilio-routes.test.ts +3 -2
- package/src/__tests__/validate-input.test.ts +381 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -1
- package/src/__tests__/voice-session-bridge.test.ts +37 -28
- package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
- package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
- package/src/acp/session-manager.ts +5 -6
- package/src/agent/loop.ts +80 -0
- package/src/api/README.md +124 -2
- package/src/api/constants/call-sites.ts +27 -0
- package/src/api/events/assistant-outbound-attachment.ts +51 -0
- package/src/api/events/assistant-text-delta.ts +32 -0
- package/src/api/events/assistant-turn-start.ts +33 -0
- package/src/api/events/document-comment-created.ts +48 -0
- package/src/api/events/document-comment-deleted.ts +24 -0
- package/src/api/events/document-comment-reopened.ts +25 -0
- package/src/api/events/document-comment-resolved.ts +27 -0
- package/src/api/events/generation-cancelled.ts +24 -0
- package/src/api/events/generation-handoff.ts +41 -0
- package/src/api/events/message-complete.ts +42 -0
- package/src/api/events/open-url.ts +30 -0
- package/src/{events → api/events}/relationship-state-updated.ts +3 -3
- package/src/api/events/tool-use-start.ts +32 -0
- package/src/api/index.ts +128 -3
- package/src/api/responses/llm-context-response.ts +39 -0
- package/src/api/responses/llm-request-log-entry.ts +93 -0
- package/src/api/responses/memory-recall-log.ts +65 -0
- package/src/api/responses/memory-v2-activation-log.ts +78 -0
- package/src/background-wake/background-wake-routes.test.ts +687 -52
- package/src/background-wake/platform-client.test.ts +308 -0
- package/src/background-wake/platform-client.ts +167 -0
- package/src/background-wake/publisher.ts +91 -0
- package/src/background-wake/runtime-registry.ts +2 -2
- package/src/background-wake/wake-intent-hooks.test.ts +282 -0
- package/src/calls/guardian-dispatch.ts +1 -0
- package/src/calls/voice-session-bridge.ts +4 -4
- package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
- package/src/cli/commands/__tests__/notifications.test.ts +184 -40
- package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
- package/src/cli/commands/channels/index.ts +229 -0
- package/src/cli/commands/memory-v3-render.ts +147 -0
- package/src/cli/commands/memory-v3.ts +255 -4
- package/src/cli/commands/notifications.ts +365 -55
- package/src/cli/lib/open-browser.ts +7 -2
- package/src/cli/program.ts +2 -0
- package/src/config/assistant-feature-flags.ts +23 -42
- package/src/config/bundled-skills/document-editor/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
- package/src/config/call-site-defaults.ts +1 -1
- package/src/config/feature-flag-cache.ts +86 -0
- package/src/config/feature-flag-registry.json +17 -17
- package/src/config/llm-context-resolution.ts +10 -1
- package/src/config/llm-resolver.ts +121 -15
- package/src/config/loader.ts +4 -5
- package/src/config/schemas/__tests__/memory-v2.test.ts +15 -0
- package/src/config/schemas/heartbeat.ts +1 -1
- package/src/config/schemas/llm.ts +90 -1
- package/src/config/schemas/memory-v2.ts +26 -0
- package/src/config/schemas/services.ts +6 -2
- package/src/config/seed-inference-profiles.ts +36 -16
- package/src/context/token-estimator.ts +10 -5
- package/src/credential-execution/executable-discovery.ts +40 -0
- package/src/credential-execution/process-manager.ts +6 -2
- package/src/credential-health/credential-health-service.ts +125 -40
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +13 -15
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
- package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
- package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +1 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +389 -68
- package/src/daemon/conversation-agent-loop.ts +132 -28
- package/src/daemon/conversation-error.ts +33 -5
- package/src/daemon/conversation-messaging.ts +84 -43
- package/src/daemon/conversation-process.ts +74 -37
- package/src/daemon/conversation-runtime-assembly.ts +29 -9
- package/src/daemon/conversation-skill-tools.ts +14 -30
- package/src/daemon/conversation-surfaces.ts +69 -34
- package/src/daemon/conversation-tool-setup.ts +33 -48
- package/src/daemon/conversation.ts +26 -46
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/daemon-skill-host.ts +9 -2
- package/src/daemon/disk-pressure-guard.ts +27 -29
- package/src/daemon/first-greeting.ts +31 -13
- package/src/daemon/handlers/shared.ts +6 -1
- package/src/daemon/lifecycle.ts +12 -12
- package/src/daemon/mcp-reload-service.ts +1 -1
- package/src/daemon/meet-manifest-loader.ts +10 -17
- package/src/daemon/message-types/conversations.ts +20 -22
- package/src/daemon/message-types/document-comments.ts +8 -44
- package/src/daemon/message-types/home.ts +2 -2
- package/src/daemon/message-types/integrations.ts +2 -7
- package/src/daemon/message-types/messages.ts +23 -38
- package/src/daemon/message-types/subagents.ts +6 -0
- package/src/daemon/process-message.ts +9 -9
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +16 -0
- package/src/daemon/switch-inference-profile-tool.ts +13 -3
- package/src/daemon/tool-setup-types.ts +0 -6
- package/src/daemon/wake-target-adapter.ts +10 -0
- package/src/documents/document-store.ts +38 -0
- package/src/export/__tests__/transcript-formatter.test.ts +1 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +29 -0
- package/src/heartbeat/heartbeat-service.ts +63 -0
- package/src/home/__tests__/feed-writer.test.ts +161 -0
- package/src/home/__tests__/post-connect-feed.test.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +55 -59
- package/src/home/feed-writer.ts +146 -7
- package/src/home/suggested-prompts.ts +27 -145
- package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
- package/src/ipc/gateway-client.test.ts +4 -1
- package/src/ipc/skill-routes/__tests__/memory.test.ts +1 -0
- package/src/ipc/skill-routes/__tests__/registries.test.ts +36 -7
- package/src/ipc/skill-routes/memory.ts +4 -3
- package/src/ipc/skill-routes/registries.ts +28 -29
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
- package/src/memory/conversation-attention-store.ts +17 -3
- package/src/memory/conversation-crud.ts +352 -112
- package/src/memory/db-connection.ts +29 -19
- package/src/memory/db-init.ts +4 -0
- package/src/memory/db-singleton.ts +77 -0
- package/src/memory/delivery-channels.ts +82 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
- package/src/memory/graph/retriever.test.ts +3 -3
- package/src/memory/job-handlers/embedding.test.ts +3 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
- package/src/memory/jobs-worker.ts +12 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +80 -0
- package/src/memory/llm-request-log-source-local.ts +24 -0
- package/src/memory/llm-request-log-source.ts +31 -0
- package/src/memory/llm-request-log-store.ts +188 -3
- package/src/memory/memory-v2-activation-log-store.ts +95 -1
- package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
- package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/schema/conversations.ts +9 -1
- package/src/memory/schema/inference.ts +0 -1
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
- package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
- package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
- package/src/memory/v2/__tests__/sweep-job.test.ts +6 -3
- package/src/memory/v2/harness/metrics.ts +5 -1
- package/src/memory/v2/harness/replay-input.ts +19 -3
- package/src/memory/v2/harness/runner.ts +6 -0
- package/src/memory/v2/harness/trace.ts +6 -0
- package/src/memory/v3/__tests__/consolidation-job.test.ts +2 -4
- package/src/memory/v3/__tests__/coretrieval-seed.test.ts +270 -0
- package/src/memory/v3/__tests__/edges.test.ts +144 -1
- package/src/memory/v3/__tests__/filter.test.ts +48 -0
- package/src/memory/v3/__tests__/gate.test.ts +96 -33
- package/src/memory/v3/__tests__/index-composition.test.ts +58 -0
- package/src/memory/v3/__tests__/loop.test.ts +250 -5
- package/src/memory/v3/__tests__/scouts.test.ts +49 -0
- package/src/memory/v3/__tests__/shadow-diff.test.ts +225 -0
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +88 -2
- package/src/memory/v3/__tests__/traversal.test.ts +39 -0
- package/src/memory/v3/__tests__/tree-walk.test.ts +77 -0
- package/src/memory/v3/__tests__/validate.test.ts +32 -0
- package/src/memory/v3/coretrieval-seed.ts +240 -0
- package/src/memory/v3/edges.ts +58 -21
- package/src/memory/v3/filter.ts +27 -22
- package/src/memory/v3/gate.ts +51 -36
- package/src/memory/v3/index-composition.ts +18 -5
- package/src/memory/v3/loop.ts +65 -17
- package/src/memory/v3/scouts.ts +15 -4
- package/src/memory/v3/shadow-diff.ts +287 -0
- package/src/memory/v3/shadow-middleware.ts +44 -2
- package/src/memory/v3/traversal.ts +6 -1
- package/src/memory/v3/tree-walk.ts +6 -1
- package/src/memory/v3/validate.ts +56 -33
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
- package/src/notifications/adapters/slack.ts +45 -11
- package/src/notifications/broadcaster.ts +114 -63
- package/src/notifications/conversation-pairing.ts +23 -3
- package/src/notifications/decisions-store.ts +32 -1
- package/src/notifications/deliveries-store.ts +45 -0
- package/src/notifications/edit-notification.ts +201 -0
- package/src/notifications/emit-signal.ts +11 -1
- package/src/notifications/signal.ts +10 -0
- package/src/notifications/types.ts +37 -0
- package/src/oauth/byo-connection.test.ts +67 -3
- package/src/oauth/byo-connection.ts +32 -5
- package/src/oauth/connect-orchestrator.ts +9 -0
- package/src/oauth/connection-resolver.test.ts +76 -0
- package/src/oauth/connection-resolver.ts +49 -10
- package/src/oauth/manual-token-connection.ts +51 -3
- package/src/oauth/seed-providers.ts +3 -0
- package/src/permissions/approval-policy.test.ts +19 -5
- package/src/permissions/approval-policy.ts +14 -3
- package/src/permissions/checker.ts +21 -8
- package/src/platform/client.test.ts +24 -1
- package/src/platform/client.ts +8 -0
- package/src/platform/feature-gate.ts +15 -0
- package/src/plugins/defaults/injectors.ts +2 -8
- package/src/plugins/defaults/persistence.ts +25 -6
- package/src/plugins/types.ts +57 -13
- package/src/proactive-artifact/job.test.ts +1 -0
- package/src/prompts/__tests__/system-prompt.test.ts +4 -4
- package/src/prompts/system-prompt.ts +38 -40
- package/src/prompts/template-detection.ts +10 -4
- package/src/prompts/templates/BOOTSTRAP.md +7 -11
- package/src/prompts/templates/IDENTITY.md +0 -2
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
- package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
- package/src/providers/call-site-routing.ts +33 -9
- package/src/providers/connection-model-compat.ts +23 -0
- package/src/providers/connection-resolution.ts +39 -20
- package/src/providers/fireworks/client.ts +1 -0
- package/src/providers/gemini/client.ts +24 -3
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
- package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
- package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
- package/src/providers/inference/auth.ts +0 -8
- package/src/providers/inference/connections.ts +3 -66
- package/src/providers/inference/resolve-auth.ts +2 -3
- package/src/providers/model-catalog.ts +35 -1
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -5
- package/src/providers/openai/chat-completions-provider.ts +110 -12
- package/src/providers/openai/codex-models.ts +2 -0
- package/src/providers/openai/responses-provider.ts +53 -53
- package/src/providers/openrouter/client.ts +13 -8
- package/src/providers/provider-send-message.ts +18 -9
- package/src/providers/registry.ts +48 -8
- package/src/providers/retry.ts +16 -4
- package/src/providers/search-provider-catalog.ts +17 -9
- package/src/providers/types.ts +9 -0
- package/src/runtime/__tests__/agent-wake.test.ts +1 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +1 -0
- package/src/runtime/access-request-helper.ts +1 -0
- package/src/runtime/auth/route-policy.ts +10 -0
- package/src/runtime/channel-readiness-service.ts +68 -0
- package/src/runtime/channel-reply-delivery.ts +23 -0
- package/src/runtime/channel-retry-sweep.ts +47 -14
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- package/src/runtime/migrations/vbundle-builder.ts +3 -2
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -50
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +51 -3
- package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +35 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
- package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
- package/src/runtime/routes/acp-routes-list.test.ts +3 -0
- package/src/runtime/routes/app-management-routes.ts +111 -4
- package/src/runtime/routes/background-wake-routes.ts +188 -20
- package/src/runtime/routes/btw-routes.ts +4 -4
- package/src/runtime/routes/conversation-analysis-routes.ts +6 -0
- package/src/runtime/routes/conversation-compaction-routes.ts +263 -0
- package/src/runtime/routes/conversation-list-routes.ts +147 -0
- package/src/runtime/routes/conversation-management-routes.ts +39 -14
- package/src/runtime/routes/conversation-query-routes.ts +60 -10
- package/src/runtime/routes/conversation-routes.ts +186 -140
- package/src/runtime/routes/conversations-import-routes.ts +19 -6
- package/src/runtime/routes/documents-routes.ts +10 -1
- package/src/runtime/routes/group-routes.ts +11 -0
- package/src/runtime/routes/home-feed-routes.ts +129 -0
- package/src/runtime/routes/identity-intro-cache.ts +61 -16
- package/src/runtime/routes/identity-routes.ts +30 -9
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +530 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +57 -8
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +5 -26
- package/src/runtime/routes/integrations/vercel.ts +15 -0
- package/src/runtime/routes/llm-context-normalization.ts +7 -2
- package/src/runtime/routes/memory-v3-routes.ts +160 -2
- package/src/runtime/routes/migration-routes.ts +20 -13
- package/src/runtime/routes/notification-routes.ts +63 -1
- package/src/runtime/routes/oauth-commands-routes.ts +6 -1
- package/src/runtime/routes/surface-action-routes.ts +1 -38
- package/src/runtime/routes/surface-content-routes.ts +12 -5
- package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
- package/src/runtime/routes/wipe-conversation-routes.ts +3 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -0
- package/src/runtime/slack-dm-text-delivery.ts +177 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -1
- package/src/runtime/tool-grant-request-helper.ts +1 -0
- package/src/schedule/schedule-store.ts +8 -1
- package/src/schedule/scheduler.ts +111 -15
- package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
- package/src/security/encrypted-store.ts +7 -16
- package/src/security/store-path-override.ts +61 -0
- package/src/signals/user-message.ts +5 -8
- package/src/skills/validate-input.ts +177 -0
- package/src/subagent/manager.ts +13 -13
- package/src/subagent/types.ts +6 -0
- package/src/tasks/tool-sanitizer.ts +2 -2
- package/src/tools/apps/definitions.ts +35 -21
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
- package/src/tools/computer-use/definitions.ts +268 -266
- package/src/tools/document/document-tool.ts +131 -8
- package/src/tools/execution-target.ts +2 -5
- package/src/tools/executor.ts +18 -55
- package/src/tools/host-filesystem/edit.test.ts +1 -0
- package/src/tools/host-filesystem/read.test.ts +1 -0
- package/src/tools/host-filesystem/transfer.test.ts +31 -6
- package/src/tools/host-filesystem/write.test.ts +1 -0
- package/src/tools/mcp/mcp-tool-factory.ts +0 -2
- package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
- package/src/tools/network/__tests__/web-search.test.ts +211 -3
- package/src/tools/network/managed-search-proxy.ts +183 -0
- package/src/tools/network/web-search.ts +199 -44
- package/src/tools/policy-context.ts +3 -1
- package/src/tools/registry.ts +146 -103
- package/src/tools/schedule/create.ts +1 -1
- package/src/tools/skills/skill-tool-factory.ts +17 -36
- package/src/tools/subagent/spawn.ts +3 -0
- package/src/tools/tool-approval-handler.ts +10 -4
- package/src/tools/tool-name-aliases.ts +72 -14
- package/src/tools/types.ts +17 -15
- package/src/tools/ui-surface/definitions.ts +98 -86
- package/src/types/onboarding-context.ts +6 -0
- package/src/usage/attribution.ts +32 -1
- package/src/util/browser.ts +7 -2
- package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
- package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
- package/src/workspace/migrations/registry.ts +4 -0
|
@@ -27,6 +27,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
27
27
|
getConversationOriginChannel: () => null,
|
|
28
28
|
getMessages: () => null,
|
|
29
29
|
createConversation: () => ({ id: "mock-conv" }),
|
|
30
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
30
31
|
}));
|
|
31
32
|
|
|
32
33
|
/**
|
|
@@ -37,11 +38,11 @@ const capturedMessages: string[] = [];
|
|
|
37
38
|
|
|
38
39
|
mock.module("../daemon/conversation-store.js", () => ({
|
|
39
40
|
findConversation: (_id: string) => ({
|
|
40
|
-
enqueueMessage: (content: string) => {
|
|
41
|
-
capturedMessages.push(content);
|
|
41
|
+
enqueueMessage: (options: { content: string }) => {
|
|
42
|
+
capturedMessages.push(options.content);
|
|
42
43
|
return { queued: true };
|
|
43
44
|
},
|
|
44
|
-
persistUserMessage: async () => "mock-msg",
|
|
45
|
+
persistUserMessage: async () => ({ id: "mock-msg", deduplicated: false }),
|
|
45
46
|
runAgentLoop: async () => {},
|
|
46
47
|
}),
|
|
47
48
|
addConversation: () => {},
|
|
@@ -106,7 +107,7 @@ function injectSubagent(
|
|
|
106
107
|
sendToClient: () => {},
|
|
107
108
|
usageStats: { inputTokens: 0, outputTokens: 0, estimatedCost: 0 },
|
|
108
109
|
enqueueMessage: () => ({ queued: false }),
|
|
109
|
-
persistUserMessage: () => "msg-1",
|
|
110
|
+
persistUserMessage: async () => ({ id: "msg-1", deduplicated: false }),
|
|
110
111
|
runAgentLoop: async () => {},
|
|
111
112
|
};
|
|
112
113
|
internals.subagents.set(subagentId, {
|
|
@@ -26,6 +26,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
26
26
|
getConversationOriginChannel: () => null,
|
|
27
27
|
getMessages: () => null,
|
|
28
28
|
createConversation: () => ({ id: "mock-conv" }),
|
|
29
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
29
30
|
}));
|
|
30
31
|
|
|
31
32
|
import {
|
|
@@ -318,6 +319,63 @@ describe("subagent_spawn fork parameter", () => {
|
|
|
318
319
|
);
|
|
319
320
|
});
|
|
320
321
|
|
|
322
|
+
test("threads context.toolUseId into config as parentToolUseId", async () => {
|
|
323
|
+
const manager = getSubagentManager();
|
|
324
|
+
const originalSpawn = manager.spawn.bind(manager);
|
|
325
|
+
|
|
326
|
+
let capturedConfig: Record<string, unknown> | undefined;
|
|
327
|
+
manager.spawn = async (config: Record<string, unknown>) => {
|
|
328
|
+
capturedConfig = config;
|
|
329
|
+
return "tool-use-id-subagent";
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
try {
|
|
333
|
+
const result = await executeSubagentSpawn(
|
|
334
|
+
{
|
|
335
|
+
label: "Stamped task",
|
|
336
|
+
objective: "Do something",
|
|
337
|
+
},
|
|
338
|
+
makeContext("tool-use-conv", {
|
|
339
|
+
sendToClient: () => {},
|
|
340
|
+
toolUseId: "toolu_123",
|
|
341
|
+
}),
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
expect(result.isError).toBe(false);
|
|
345
|
+
expect(capturedConfig).toBeDefined();
|
|
346
|
+
expect(capturedConfig!.parentToolUseId).toBe("toolu_123");
|
|
347
|
+
} finally {
|
|
348
|
+
manager.spawn = originalSpawn;
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
test("omits parentToolUseId when no toolUseId is in context", async () => {
|
|
353
|
+
const manager = getSubagentManager();
|
|
354
|
+
const originalSpawn = manager.spawn.bind(manager);
|
|
355
|
+
|
|
356
|
+
let capturedConfig: Record<string, unknown> | undefined;
|
|
357
|
+
manager.spawn = async (config: Record<string, unknown>) => {
|
|
358
|
+
capturedConfig = config;
|
|
359
|
+
return "no-tool-use-id-subagent";
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
const result = await executeSubagentSpawn(
|
|
364
|
+
{
|
|
365
|
+
label: "Unstamped task",
|
|
366
|
+
objective: "Do something",
|
|
367
|
+
},
|
|
368
|
+
makeContext("no-tool-use-conv", { sendToClient: () => {} }),
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
expect(result.isError).toBe(false);
|
|
372
|
+
expect(capturedConfig).toBeDefined();
|
|
373
|
+
expect(capturedConfig!.parentToolUseId).toBeUndefined();
|
|
374
|
+
} finally {
|
|
375
|
+
manager.spawn = originalSpawn;
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
|
|
321
379
|
test("fork: true shallow copies parent messages", async () => {
|
|
322
380
|
const manager = getSubagentManager();
|
|
323
381
|
const originalSpawn = manager.spawn.bind(manager);
|
|
@@ -30,6 +30,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
30
30
|
getConversationOriginChannel: () => null,
|
|
31
31
|
getMessages: (conversationId: string) => mockGetMessages(conversationId),
|
|
32
32
|
createConversation: () => ({ id: "mock-conv" }),
|
|
33
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
33
34
|
}));
|
|
34
35
|
|
|
35
36
|
import { getSubagentManager } from "../subagent/index.js";
|
|
@@ -97,7 +98,7 @@ function injectSubagent(
|
|
|
97
98
|
sendToClient: () => {},
|
|
98
99
|
usageStats: { inputTokens: 0, outputTokens: 0, estimatedCost: 0 },
|
|
99
100
|
enqueueMessage: () => ({ queued: false }),
|
|
100
|
-
persistUserMessage: () => "msg-1",
|
|
101
|
+
persistUserMessage: async () => ({ id: "msg-1", deduplicated: false }),
|
|
101
102
|
runAgentLoop: async () => {},
|
|
102
103
|
};
|
|
103
104
|
internals.subagents.set(subagentId, {
|
|
@@ -52,6 +52,7 @@ const mockGetMessages = mock((_conversationId: string) => [
|
|
|
52
52
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
53
53
|
getMessages: mockGetMessages,
|
|
54
54
|
getConversation: (_id: string) => null,
|
|
55
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
55
56
|
}));
|
|
56
57
|
|
|
57
58
|
const mockGetConfiguredProvider = mock(async () => ({
|
|
@@ -171,6 +171,44 @@ describe("buildSystemPrompt", () => {
|
|
|
171
171
|
expect(result).toContain("# Soul\n\nBe thoughtful.");
|
|
172
172
|
});
|
|
173
173
|
|
|
174
|
+
test("gates off the unmodified bundled IDENTITY.md template when no BOOTSTRAP.md is present", () => {
|
|
175
|
+
// Regression: the seeded IDENTITY.md ships with `_`-comment lines, so
|
|
176
|
+
// the raw workspace body never equals the comment-stripped bundled
|
|
177
|
+
// template. `isTemplateContent` must comment-strip BOTH sides — otherwise
|
|
178
|
+
// detection fails and the `08-identity` transform leaks the blank
|
|
179
|
+
// template scaffolding into every fresh post-onboarding prompt.
|
|
180
|
+
const bundledIdentity = readFileSync(
|
|
181
|
+
join(import.meta.dirname, "..", "prompts", "templates", "IDENTITY.md"),
|
|
182
|
+
"utf-8",
|
|
183
|
+
);
|
|
184
|
+
writeFileSync(join(TEST_DIR, "IDENTITY.md"), bundledIdentity);
|
|
185
|
+
writeFileSync(join(TEST_DIR, "SOUL.md"), "# Soul\n\nBe thoughtful.");
|
|
186
|
+
const result = buildSystemPrompt();
|
|
187
|
+
// SOUL still renders; the template scaffolding must not.
|
|
188
|
+
expect(result).toContain("# Soul\n\nBe thoughtful.");
|
|
189
|
+
expect(result).not.toContain("(not yet chosen)");
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("includes the unmodified bundled IDENTITY.md template during bootstrap", () => {
|
|
193
|
+
// The mirror case: when BOOTSTRAP.md is active the template is included
|
|
194
|
+
// verbatim so the model can see the field structure and produce a valid
|
|
195
|
+
// file_write on the first turn.
|
|
196
|
+
const bundledIdentity = readFileSync(
|
|
197
|
+
join(import.meta.dirname, "..", "prompts", "templates", "IDENTITY.md"),
|
|
198
|
+
"utf-8",
|
|
199
|
+
);
|
|
200
|
+
writeFileSync(join(TEST_DIR, "IDENTITY.md"), bundledIdentity);
|
|
201
|
+
writeFileSync(
|
|
202
|
+
join(TEST_DIR, "BOOTSTRAP.md"),
|
|
203
|
+
"# First run\n\nGet started.",
|
|
204
|
+
);
|
|
205
|
+
const result = buildSystemPrompt();
|
|
206
|
+
// The bundled template's placeholder scaffolding (the `(not yet chosen)`
|
|
207
|
+
// field markers) renders verbatim during bootstrap — the mirror of the
|
|
208
|
+
// gating test above, which asserts it is absent without BOOTSTRAP.md.
|
|
209
|
+
expect(result).toContain("(not yet chosen)");
|
|
210
|
+
});
|
|
211
|
+
|
|
174
212
|
test("trims whitespace from file content", () => {
|
|
175
213
|
writeFileSync(join(TEST_DIR, "SOUL.md"), "\n Be kind \n\n");
|
|
176
214
|
const result = buildSystemPrompt();
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test preload verifier — runs immediately after test-preload.ts.
|
|
3
|
+
*
|
|
4
|
+
* Bun already exits with code 1 when a preload throws, has an unresolvable
|
|
5
|
+
* import, or doesn't exist on disk (verified empirically). This file is
|
|
6
|
+
* the explicit check for the remaining failure mode: the main preload
|
|
7
|
+
* ran successfully but didn't actually isolate workspace state (e.g. a
|
|
8
|
+
* future refactor of test-preload.ts introduces a logic bug that no-ops
|
|
9
|
+
* the env override).
|
|
10
|
+
*
|
|
11
|
+
* Without this verifier, that failure mode would silently let tests
|
|
12
|
+
* resolve `VELLUM_WORKSPACE_DIR` to the real `/workspace` and potentially
|
|
13
|
+
* destroy live data.
|
|
14
|
+
*
|
|
15
|
+
* Order matters: bunfig.toml lists this AFTER test-preload.ts so the
|
|
16
|
+
* main preload's env writes are observable here.
|
|
17
|
+
*
|
|
18
|
+
* No source-module imports — only node stdlib (matches the same rule
|
|
19
|
+
* test-preload.ts enforces).
|
|
20
|
+
*
|
|
21
|
+
* Positive assertion: `VELLUM_WORKSPACE_DIR` MUST resolve under
|
|
22
|
+
* `os.tmpdir()`. This mirrors exactly what test-preload.ts does
|
|
23
|
+
* (`mkdtempSync(join(tmpdir(), "vellum-test-"))`), so any other value
|
|
24
|
+
* indicates the preload either didn't run or didn't take effect.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { realpathSync } from "node:fs";
|
|
28
|
+
import { tmpdir } from "node:os";
|
|
29
|
+
import { sep } from "node:path";
|
|
30
|
+
|
|
31
|
+
function fail(reason: string): never {
|
|
32
|
+
throw new Error(
|
|
33
|
+
[
|
|
34
|
+
`Test preload verifier failed: ${reason}.`,
|
|
35
|
+
"",
|
|
36
|
+
"test-preload.ts ran but did not isolate workspace state. Common",
|
|
37
|
+
"causes:",
|
|
38
|
+
" - a logic bug in test-preload.ts (env override removed/reordered)",
|
|
39
|
+
" - a preload phase threw silently and the verifier still picked up",
|
|
40
|
+
" partial state",
|
|
41
|
+
].join("\n"),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function canonicalize(p: string): string {
|
|
46
|
+
try {
|
|
47
|
+
return realpathSync(p);
|
|
48
|
+
} catch {
|
|
49
|
+
return p;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const workspaceDir = process.env.VELLUM_WORKSPACE_DIR?.trim();
|
|
54
|
+
if (!workspaceDir) {
|
|
55
|
+
fail("VELLUM_WORKSPACE_DIR is not set after main preload");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const resolvedWorkspace = canonicalize(workspaceDir);
|
|
59
|
+
const tmpRoot = canonicalize(tmpdir());
|
|
60
|
+
|
|
61
|
+
if (
|
|
62
|
+
resolvedWorkspace !== tmpRoot &&
|
|
63
|
+
!resolvedWorkspace.startsWith(tmpRoot + sep)
|
|
64
|
+
) {
|
|
65
|
+
fail(
|
|
66
|
+
`VELLUM_WORKSPACE_DIR resolves to ${resolvedWorkspace}, which is not under ${tmpRoot}`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -1,56 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared test preload — runs before every test file.
|
|
3
3
|
*
|
|
4
|
-
* Creates a per-
|
|
5
|
-
* all workspace-derived helpers (getDataDir, getDbPath,
|
|
6
|
-
* resolve under the temp dir
|
|
7
|
-
*
|
|
8
|
-
* Individual test files can retrieve the workspace dir via getWorkspaceDir()
|
|
9
|
-
* from platform.ts, or directly from process.env.VELLUM_WORKSPACE_DIR.
|
|
4
|
+
* Creates a per-process temporary directory and sets VELLUM_WORKSPACE_DIR so
|
|
5
|
+
* that all workspace-derived helpers (getDataDir, getDbPath,
|
|
6
|
+
* getConversationsDir, getProtectedDir, …) resolve under the temp dir
|
|
7
|
+
* instead of the real $VELLUM_WORKSPACE_DIR.
|
|
10
8
|
*
|
|
11
9
|
* Cleanup: the temp dir is removed after all tests in the file complete.
|
|
10
|
+
*
|
|
11
|
+
* No source-module imports
|
|
12
|
+
* ------------------------
|
|
13
|
+
* The only static imports in this file are node stdlib (`node:fs`,
|
|
14
|
+
* `node:os`, `node:path`), `bun:test`, and helpers in this same directory.
|
|
15
|
+
* Importing from the assistant directly runs the risk of triggering import
|
|
16
|
+
* time side effects and import from node modules that may not exist in
|
|
17
|
+
* some environments.
|
|
12
18
|
*/
|
|
13
19
|
|
|
14
|
-
import { mkdtempSync, realpathSync, rmSync } from "node:fs";
|
|
20
|
+
import { mkdirSync, mkdtempSync, realpathSync, rmSync } from "node:fs";
|
|
15
21
|
import { tmpdir } from "node:os";
|
|
16
22
|
import { join } from "node:path";
|
|
17
23
|
import { afterAll } from "bun:test";
|
|
18
24
|
|
|
19
|
-
import { installGatewayIpcMock } from "
|
|
20
|
-
import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
|
|
21
|
-
import { resetDb } from "../memory/db-connection.js";
|
|
22
|
-
import { _setStorePath } from "../security/encrypted-store.js";
|
|
25
|
+
import { installGatewayIpcMock } from "./mock-gateway-ipc.js";
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
// --- Phase 1: env override (zero source-module imports above this point) ---
|
|
28
|
+
|
|
29
|
+
// Layout: <tmpRoot>/workspace as VELLUM_WORKSPACE_DIR. The parent of
|
|
30
|
+
// VELLUM_WORKSPACE_DIR is what `vellumRoot()` resolves to, so a separate
|
|
31
|
+
// tmpRoot per process gives `getProtectedDir()` and friends per-process
|
|
32
|
+
// isolation without needing an explicit `setStorePathForTesting()`.
|
|
33
|
+
const tmpRoot = realpathSync(mkdtempSync(join(tmpdir(), "vellum-test-")));
|
|
34
|
+
const testDir = join(tmpRoot, "workspace");
|
|
35
|
+
mkdirSync(testDir);
|
|
27
36
|
process.env.VELLUM_WORKSPACE_DIR = testDir;
|
|
28
37
|
process.env.VELLUM_PLATFORM_URL = "https://test-platform.vellum.ai";
|
|
29
38
|
process.exitCode = 0;
|
|
30
39
|
|
|
31
|
-
// Isolate the encrypted credential store per test file. Without this,
|
|
32
|
-
// parallel test processes all read/write the same ~/.vellum/protected/keys.enc,
|
|
33
|
-
// causing races when one file deletes a key while another sets it.
|
|
34
|
-
_setStorePath(join(testDir, "keys.enc"));
|
|
35
|
-
|
|
36
|
-
// Mock gateway IPC so no test accidentally connects to a real gateway socket.
|
|
37
|
-
// Tests that need to control IPC responses use mockGatewayIpc() / resetMockGatewayIpc().
|
|
38
|
-
installGatewayIpcMock();
|
|
39
|
-
|
|
40
|
-
// Pre-populate the feature-flag override cache so `initFeatureFlagOverrides()`
|
|
41
|
-
// short-circuits its retry loop — there is no real gateway in tests, and the
|
|
42
|
-
// retry backoff would otherwise exceed the per-test timeout for any test that
|
|
43
|
-
// builds the CLI program. Tests exercising the retry behavior call
|
|
44
|
-
// `clearFeatureFlagOverridesCache()` first.
|
|
45
|
-
_setOverridesForTesting({});
|
|
46
|
-
|
|
47
|
-
// Force-close any DB connection inherited from the parent process (e.g. when
|
|
48
|
-
// the test runner is spawned by the running assistant via a pre-push hook).
|
|
49
|
-
// Without this, the db singleton in db-connection.ts may still point at the
|
|
50
|
-
// real ~/.vellum/workspace database, and test cleanup (DELETE FROM …) would
|
|
51
|
-
// wipe production data — contacts, channels, credentials, etc.
|
|
52
|
-
resetDb();
|
|
53
|
-
|
|
54
40
|
// Prevent tests from routing credential writes through the real CES
|
|
55
41
|
// (Credential Execution Service). Without this, setSecureKeyAsync() in
|
|
56
42
|
// containerized environments writes to the live credential store.
|
|
@@ -59,8 +45,15 @@ const savedCesCredentialUrl = process.env.CES_CREDENTIAL_URL;
|
|
|
59
45
|
delete process.env.IS_CONTAINERIZED;
|
|
60
46
|
delete process.env.CES_CREDENTIAL_URL;
|
|
61
47
|
|
|
48
|
+
// --- Phase 2: install the IPC mock (no source-module imports) ---
|
|
49
|
+
|
|
50
|
+
// Mock gateway IPC so no test accidentally connects to a real gateway socket.
|
|
51
|
+
// The mock returns a sentinel for `get_feature_flags` to short-circuit the
|
|
52
|
+
// retry loop in `initFeatureFlagOverrides()`. Tests that need specific IPC
|
|
53
|
+
// responses use `mockGatewayIpc()` / `resetMockGatewayIpc()`.
|
|
54
|
+
installGatewayIpcMock();
|
|
55
|
+
|
|
62
56
|
afterAll(() => {
|
|
63
|
-
resetDb();
|
|
64
57
|
process.exitCode = 0;
|
|
65
58
|
delete process.env.VELLUM_WORKSPACE_DIR;
|
|
66
59
|
delete process.env.VELLUM_PLATFORM_URL;
|
|
@@ -71,7 +64,7 @@ afterAll(() => {
|
|
|
71
64
|
process.env.CES_CREDENTIAL_URL = savedCesCredentialUrl;
|
|
72
65
|
}
|
|
73
66
|
try {
|
|
74
|
-
rmSync(
|
|
67
|
+
rmSync(tmpRoot, { recursive: true, force: true });
|
|
75
68
|
} catch {
|
|
76
69
|
/* best-effort cleanup */
|
|
77
70
|
}
|
|
@@ -72,6 +72,7 @@ mock.module("../permissions/checker.js", () => ({
|
|
|
72
72
|
|
|
73
73
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
74
74
|
createConversation: (title: string) => ({ id: "conversation-1", title }),
|
|
75
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
75
76
|
}));
|
|
76
77
|
|
|
77
78
|
// Mock every export so downstream test files that dynamically import modules
|
|
@@ -85,15 +86,16 @@ mock.module("../memory/tool-usage-store.js", () => ({
|
|
|
85
86
|
mock.module("../tools/registry.js", () => ({
|
|
86
87
|
getTool: (name: string) => {
|
|
87
88
|
if (name === "unknown_tool") return undefined;
|
|
88
|
-
// Skill tools carry
|
|
89
|
+
// Skill tools carry executionTarget from their manifest. Ownership lives
|
|
90
|
+
// on the registry (mocked below via getToolOwner reading the override's
|
|
91
|
+
// owner field), so it doesn't appear on the Tool object itself.
|
|
89
92
|
if (name === "skill_host_tool") {
|
|
90
93
|
return {
|
|
91
94
|
name,
|
|
92
95
|
description: "skill host tool",
|
|
93
96
|
category: "skill",
|
|
94
97
|
defaultRiskLevel: "low",
|
|
95
|
-
|
|
96
|
-
ownerSkillId: "test-skill",
|
|
98
|
+
owner: { kind: "skill", id: "test-skill" },
|
|
97
99
|
executionTarget: "host" as const,
|
|
98
100
|
input_schema: {},
|
|
99
101
|
execute: async () => {
|
|
@@ -108,8 +110,7 @@ mock.module("../tools/registry.js", () => ({
|
|
|
108
110
|
description: "skill sandbox tool",
|
|
109
111
|
category: "skill",
|
|
110
112
|
defaultRiskLevel: "low",
|
|
111
|
-
|
|
112
|
-
ownerSkillId: "test-skill",
|
|
113
|
+
owner: { kind: "skill", id: "test-skill" },
|
|
113
114
|
executionTarget: "sandbox" as const,
|
|
114
115
|
input_schema: {},
|
|
115
116
|
execute: async () => {
|
|
@@ -126,8 +127,7 @@ mock.module("../tools/registry.js", () => ({
|
|
|
126
127
|
description: "skill tool with host_ prefix but sandbox target",
|
|
127
128
|
category: "skill",
|
|
128
129
|
defaultRiskLevel: "low",
|
|
129
|
-
|
|
130
|
-
ownerSkillId: "test-skill",
|
|
130
|
+
owner: { kind: "skill", id: "test-skill" },
|
|
131
131
|
executionTarget: "sandbox" as const,
|
|
132
132
|
input_schema: {},
|
|
133
133
|
execute: async () => {
|
|
@@ -158,6 +158,19 @@ mock.module("../tools/registry.js", () => ({
|
|
|
158
158
|
},
|
|
159
159
|
};
|
|
160
160
|
},
|
|
161
|
+
// Ownership lives on the registry post-refactor. Mirror that by surfacing
|
|
162
|
+
// the optional `owner`-shaped field set inline on the override-produced
|
|
163
|
+
// tool (see the skill_* branches above).
|
|
164
|
+
getToolOwner: (name: string) => {
|
|
165
|
+
if (
|
|
166
|
+
name === "skill_host_tool" ||
|
|
167
|
+
name === "skill_sandbox_tool" ||
|
|
168
|
+
name === "host_skill_sandboxed"
|
|
169
|
+
) {
|
|
170
|
+
return { kind: "skill" as const, id: "test-skill" };
|
|
171
|
+
}
|
|
172
|
+
return undefined;
|
|
173
|
+
},
|
|
161
174
|
}));
|
|
162
175
|
|
|
163
176
|
mock.module("../tools/shared/filesystem/path-policy.js", () => ({
|
|
@@ -148,6 +148,16 @@ mock.module("../tools/registry.js", () => ({
|
|
|
148
148
|
};
|
|
149
149
|
},
|
|
150
150
|
getAllTools: () => (getAllToolsOverride ? getAllToolsOverride() : []),
|
|
151
|
+
// Ownership lives on the registry post-refactor; production reads it via
|
|
152
|
+
// getToolOwner(name) rather than a field on the Tool object. Mirror that by
|
|
153
|
+
// surfacing the optional `owner`-shaped field from the override-produced
|
|
154
|
+
// tool so existing tests can encode owner inline.
|
|
155
|
+
getToolOwner: (name: string) => {
|
|
156
|
+
const t = getToolOverride?.(name) as
|
|
157
|
+
| { owner?: { kind: "skill" | "mcp" | "plugin"; id: string } }
|
|
158
|
+
| undefined;
|
|
159
|
+
return t?.owner;
|
|
160
|
+
},
|
|
151
161
|
}));
|
|
152
162
|
|
|
153
163
|
mock.module("../tools/shared/filesystem/path-policy.js", () => ({
|
|
@@ -224,6 +234,48 @@ describe("ToolExecutor allowedToolNames gating", () => {
|
|
|
224
234
|
expect(result.content).toBe("ok");
|
|
225
235
|
});
|
|
226
236
|
|
|
237
|
+
test("canonicalizes legacy computer_use_press_key alias before active-tool gating", async () => {
|
|
238
|
+
const executor = new ToolExecutor(makePrompter());
|
|
239
|
+
const allowed = new Set(["computer_use_key"]);
|
|
240
|
+
const result = await executor.execute(
|
|
241
|
+
"computer_use_press_key",
|
|
242
|
+
{
|
|
243
|
+
key: "Space",
|
|
244
|
+
modifiers: ["Command"],
|
|
245
|
+
reasoning: "Open Spotlight",
|
|
246
|
+
},
|
|
247
|
+
makeContext({ allowedToolNames: allowed }),
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
expect(result.isError).toBe(false);
|
|
251
|
+
expect(result.content).toBe("ok");
|
|
252
|
+
expect(lastCheckArgs?.toolName).toBe("computer_use_key");
|
|
253
|
+
expect(lastCheckArgs?.input).toEqual({
|
|
254
|
+
key: "cmd+space",
|
|
255
|
+
reasoning: "Open Spotlight",
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test("lowercases legacy computer_use_press_key key-only input", async () => {
|
|
260
|
+
const executor = new ToolExecutor(makePrompter());
|
|
261
|
+
const allowed = new Set(["computer_use_key"]);
|
|
262
|
+
const result = await executor.execute(
|
|
263
|
+
"computer_use_press_key",
|
|
264
|
+
{
|
|
265
|
+
key: "Space",
|
|
266
|
+
reasoning: "Pause media",
|
|
267
|
+
},
|
|
268
|
+
makeContext({ allowedToolNames: allowed }),
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
expect(result.isError).toBe(false);
|
|
272
|
+
expect(lastCheckArgs?.toolName).toBe("computer_use_key");
|
|
273
|
+
expect(lastCheckArgs?.input).toEqual({
|
|
274
|
+
key: "space",
|
|
275
|
+
reasoning: "Pause media",
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
227
279
|
test("preserves exact active create_app tool before applying compatibility aliases", async () => {
|
|
228
280
|
const executor = new ToolExecutor(makePrompter());
|
|
229
281
|
const allowed = new Set(["create_app", "app_create"]);
|
|
@@ -334,8 +386,7 @@ describe("ToolExecutor allowedToolNames gating", () => {
|
|
|
334
386
|
category: "skill",
|
|
335
387
|
defaultRiskLevel: RiskLevel.Low,
|
|
336
388
|
executionTarget: "sandbox" as const,
|
|
337
|
-
|
|
338
|
-
ownerSkillId: "my-skill",
|
|
389
|
+
owner: { kind: "skill", id: "my-skill" },
|
|
339
390
|
input_schema: { type: "object" as const, properties: {} },
|
|
340
391
|
execute: async () => fakeToolResult,
|
|
341
392
|
};
|
|
@@ -371,9 +422,7 @@ describe("ToolExecutor policy context plumbing", () => {
|
|
|
371
422
|
description: "skill tool",
|
|
372
423
|
category: "skill",
|
|
373
424
|
defaultRiskLevel: RiskLevel.Low,
|
|
374
|
-
|
|
375
|
-
ownerSkillId: "my-skill-123",
|
|
376
|
-
ownerSkillVersionHash: "abc123hash",
|
|
425
|
+
owner: { kind: "skill", id: "my-skill-123" },
|
|
377
426
|
executionTarget: "sandbox" as const,
|
|
378
427
|
input_schema: { type: "object" as const, properties: {} },
|
|
379
428
|
execute: async () => fakeToolResult,
|
|
@@ -424,7 +473,6 @@ describe("ToolExecutor policy context plumbing", () => {
|
|
|
424
473
|
category: "core",
|
|
425
474
|
defaultRiskLevel: RiskLevel.Low,
|
|
426
475
|
executionTarget: "sandbox" as const,
|
|
427
|
-
origin: "core" as const,
|
|
428
476
|
input_schema: { type: "object" as const, properties: {} },
|
|
429
477
|
execute: async () => fakeToolResult,
|
|
430
478
|
};
|
|
@@ -453,9 +501,7 @@ describe("ToolExecutor policy context plumbing", () => {
|
|
|
453
501
|
description: "host skill tool",
|
|
454
502
|
category: "skill",
|
|
455
503
|
defaultRiskLevel: RiskLevel.Low,
|
|
456
|
-
|
|
457
|
-
ownerSkillId: "host-skill",
|
|
458
|
-
ownerSkillVersionHash: "host-hash",
|
|
504
|
+
owner: { kind: "skill", id: "host-skill" },
|
|
459
505
|
executionTarget: "host" as const,
|
|
460
506
|
input_schema: { type: "object" as const, properties: {} },
|
|
461
507
|
execute: async () => fakeToolResult,
|
|
@@ -477,7 +523,6 @@ describe("ToolExecutor policy context plumbing", () => {
|
|
|
477
523
|
executionTarget: "host",
|
|
478
524
|
});
|
|
479
525
|
});
|
|
480
|
-
|
|
481
526
|
});
|
|
482
527
|
|
|
483
528
|
// ---------------------------------------------------------------------------
|
|
@@ -45,6 +45,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
45
45
|
getMessageById: () => null,
|
|
46
46
|
updateMessageContent: () => {},
|
|
47
47
|
provenanceFromTrustContext: () => ({}),
|
|
48
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
48
49
|
}));
|
|
49
50
|
|
|
50
51
|
mock.module("../memory/llm-request-log-store.js", () => ({
|
|
@@ -44,6 +44,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
44
44
|
getMessageById: () => null,
|
|
45
45
|
updateMessageContent: () => {},
|
|
46
46
|
provenanceFromTrustContext: () => ({}),
|
|
47
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
47
48
|
}));
|
|
48
49
|
|
|
49
50
|
mock.module("../memory/llm-request-log-store.js", () => ({
|
|
@@ -337,7 +337,7 @@ import {
|
|
|
337
337
|
handleVoiceWebhook,
|
|
338
338
|
} from "../calls/twilio-routes.js";
|
|
339
339
|
import { DEFAULT_ELEVENLABS_VOICE_ID } from "../config/schemas/elevenlabs.js";
|
|
340
|
-
import { getDb
|
|
340
|
+
import { getDb } from "../memory/db-connection.js";
|
|
341
341
|
import { initializeDb } from "../memory/db-init.js";
|
|
342
342
|
import { conversations } from "../memory/schema.js";
|
|
343
343
|
import {
|
|
@@ -347,6 +347,7 @@ import {
|
|
|
347
347
|
handleSetTwilioCredentials,
|
|
348
348
|
} from "../runtime/routes/integrations/twilio.js";
|
|
349
349
|
import { credentialKey } from "../security/credential-key.js";
|
|
350
|
+
import { resetDbForTesting } from "./db-test-helpers.js";
|
|
350
351
|
|
|
351
352
|
initializeDb();
|
|
352
353
|
|
|
@@ -487,7 +488,7 @@ describe("twilio webhook routes", () => {
|
|
|
487
488
|
|
|
488
489
|
afterAll(() => {
|
|
489
490
|
globalThis.fetch = originalFetch;
|
|
490
|
-
|
|
491
|
+
resetDbForTesting();
|
|
491
492
|
});
|
|
492
493
|
|
|
493
494
|
// ── Callback idempotency / replay tests ───────────────────────────
|