@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
|
@@ -136,13 +136,9 @@ function makeCompletingConversation(): Conversation {
|
|
|
136
136
|
const messages: unknown[] = [];
|
|
137
137
|
return {
|
|
138
138
|
isProcessing: () => processing,
|
|
139
|
-
persistUserMessage: (
|
|
140
|
-
_content: string,
|
|
141
|
-
_attachments: unknown[],
|
|
142
|
-
requestId?: string,
|
|
143
|
-
) => {
|
|
139
|
+
persistUserMessage: (options: { requestId?: string }) => {
|
|
144
140
|
processing = true;
|
|
145
|
-
return requestId ?? "msg-1";
|
|
141
|
+
return { id: options.requestId ?? "msg-1", deduplicated: false };
|
|
146
142
|
},
|
|
147
143
|
memoryPolicy: {
|
|
148
144
|
scopeId: "default",
|
|
@@ -188,18 +184,14 @@ function makeHangingConversation(): Conversation {
|
|
|
188
184
|
const messages: unknown[] = [];
|
|
189
185
|
const enqueuedMessages: Array<{
|
|
190
186
|
content: string;
|
|
191
|
-
onEvent
|
|
192
|
-
requestId
|
|
187
|
+
onEvent?: (msg: ServerMessage) => void;
|
|
188
|
+
requestId?: string;
|
|
193
189
|
}> = [];
|
|
194
190
|
return {
|
|
195
191
|
isProcessing: () => processing,
|
|
196
|
-
persistUserMessage: (
|
|
197
|
-
_content: string,
|
|
198
|
-
_attachments: unknown[],
|
|
199
|
-
requestId?: string,
|
|
200
|
-
) => {
|
|
192
|
+
persistUserMessage: (options: { requestId?: string }) => {
|
|
201
193
|
processing = true;
|
|
202
|
-
return requestId ?? "msg-1";
|
|
194
|
+
return { id: options.requestId ?? "msg-1", deduplicated: false };
|
|
203
195
|
},
|
|
204
196
|
memoryPolicy: {
|
|
205
197
|
scopeId: "default",
|
|
@@ -223,14 +215,20 @@ function makeHangingConversation(): Conversation {
|
|
|
223
215
|
hasPendingConfirmation: () => false,
|
|
224
216
|
denyAllPendingConfirmations: () => {},
|
|
225
217
|
getQueueDepth: () => enqueuedMessages.length,
|
|
226
|
-
enqueueMessage: (
|
|
227
|
-
content: string
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
218
|
+
enqueueMessage: (options: {
|
|
219
|
+
content: string;
|
|
220
|
+
onEvent?: (msg: ServerMessage) => void;
|
|
221
|
+
requestId?: string;
|
|
222
|
+
}) => {
|
|
223
|
+
enqueuedMessages.push({
|
|
224
|
+
content: options.content,
|
|
225
|
+
onEvent: options.onEvent,
|
|
226
|
+
requestId: options.requestId,
|
|
227
|
+
});
|
|
228
|
+
return {
|
|
229
|
+
queued: true,
|
|
230
|
+
requestId: options.requestId ?? "hanging-req",
|
|
231
|
+
};
|
|
234
232
|
},
|
|
235
233
|
runAgentLoop: async () => {
|
|
236
234
|
// Hang forever
|
|
@@ -259,14 +257,9 @@ function makePendingApprovalConversation(
|
|
|
259
257
|
const messages: unknown[] = [];
|
|
260
258
|
const runAgentLoopMock = mock(async () => {});
|
|
261
259
|
const enqueueMessageMock = mock(
|
|
262
|
-
(
|
|
263
|
-
_content: string,
|
|
264
|
-
_attachments: unknown[],
|
|
265
|
-
_onEvent: (msg: ServerMessage) => void,
|
|
266
|
-
queuedRequestId: string,
|
|
267
|
-
) => ({
|
|
260
|
+
(options: { content: string; requestId?: string }) => ({
|
|
268
261
|
queued: true,
|
|
269
|
-
requestId:
|
|
262
|
+
requestId: options.requestId ?? "queued-req",
|
|
270
263
|
}),
|
|
271
264
|
);
|
|
272
265
|
const denyAllPendingConfirmationsMock = mock(() => {
|
|
@@ -278,11 +271,10 @@ function makePendingApprovalConversation(
|
|
|
278
271
|
|
|
279
272
|
const conversation = {
|
|
280
273
|
isProcessing: () => processing,
|
|
281
|
-
persistUserMessage: (
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
) => reqId ?? "msg-1",
|
|
274
|
+
persistUserMessage: (options: { requestId?: string }) => ({
|
|
275
|
+
id: options.requestId ?? "msg-1",
|
|
276
|
+
deduplicated: false,
|
|
277
|
+
}),
|
|
286
278
|
memoryPolicy: {
|
|
287
279
|
scopeId: "default",
|
|
288
280
|
includeDefaultFallback: false,
|
|
@@ -895,7 +887,7 @@ describe("POST /v1/messages — queue-if-busy and hub publishing", () => {
|
|
|
895
887
|
await stopServer();
|
|
896
888
|
});
|
|
897
889
|
|
|
898
|
-
test("accepts message when conversationKey is omitted (
|
|
890
|
+
test("accepts message when conversationKey is omitted (vellum channel mints fresh)", async () => {
|
|
899
891
|
await startServer(() => makeCompletingConversation());
|
|
900
892
|
|
|
901
893
|
const res = await fetch(messagesUrl(), {
|
|
@@ -908,14 +900,24 @@ describe("POST /v1/messages — queue-if-busy and hub publishing", () => {
|
|
|
908
900
|
}),
|
|
909
901
|
});
|
|
910
902
|
expect(res.status).toBe(202);
|
|
903
|
+
const body = (await res.json()) as {
|
|
904
|
+
accepted: boolean;
|
|
905
|
+
conversationId: string;
|
|
906
|
+
};
|
|
907
|
+
expect(body.accepted).toBe(true);
|
|
908
|
+
expect(body.conversationId).toBeTruthy();
|
|
909
|
+
|
|
910
|
+
// The vellum channel never falls through to the shared
|
|
911
|
+
// `default:vellum:<interface>` thread: each empty-handed send mints
|
|
912
|
+
// a fresh conversation so the first-message id surfaces to the client.
|
|
913
|
+
expect(getConversationByKey("default:vellum:macos")).toBeNull();
|
|
911
914
|
|
|
912
915
|
await stopServer();
|
|
913
916
|
});
|
|
914
917
|
|
|
915
|
-
test("two
|
|
918
|
+
test("two empty-handed vellum sends each mint distinct conversations", async () => {
|
|
916
919
|
await startServer(() => makeCompletingConversation());
|
|
917
920
|
|
|
918
|
-
// First message — no conversationKey
|
|
919
921
|
const res1 = await fetch(messagesUrl(), {
|
|
920
922
|
method: "POST",
|
|
921
923
|
headers: { "Content-Type": "application/json", ...AUTH_HEADERS },
|
|
@@ -926,8 +928,8 @@ describe("POST /v1/messages — queue-if-busy and hub publishing", () => {
|
|
|
926
928
|
}),
|
|
927
929
|
});
|
|
928
930
|
expect(res1.status).toBe(202);
|
|
931
|
+
const body1 = (await res1.json()) as { conversationId: string };
|
|
929
932
|
|
|
930
|
-
// Second message — same channel/interface, still no conversationKey
|
|
931
933
|
const res2 = await fetch(messagesUrl(), {
|
|
932
934
|
method: "POST",
|
|
933
935
|
headers: { "Content-Type": "application/json", ...AUTH_HEADERS },
|
|
@@ -938,12 +940,49 @@ describe("POST /v1/messages — queue-if-busy and hub publishing", () => {
|
|
|
938
940
|
}),
|
|
939
941
|
});
|
|
940
942
|
expect(res2.status).toBe(202);
|
|
943
|
+
const body2 = (await res2.json()) as { conversationId: string };
|
|
944
|
+
|
|
945
|
+
expect(body1.conversationId).toBeTruthy();
|
|
946
|
+
expect(body2.conversationId).toBeTruthy();
|
|
947
|
+
expect(body1.conversationId).not.toBe(body2.conversationId);
|
|
948
|
+
|
|
949
|
+
await stopServer();
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
test("two empty-handed phone sends share the default channel thread", async () => {
|
|
953
|
+
await startServer(() => makeCompletingConversation());
|
|
954
|
+
|
|
955
|
+
const res1 = await fetch(messagesUrl(), {
|
|
956
|
+
method: "POST",
|
|
957
|
+
headers: { "Content-Type": "application/json", ...AUTH_HEADERS },
|
|
958
|
+
body: JSON.stringify({
|
|
959
|
+
content: "First",
|
|
960
|
+
sourceChannel: "phone",
|
|
961
|
+
interface: "phone",
|
|
962
|
+
}),
|
|
963
|
+
});
|
|
964
|
+
expect(res1.status).toBe(202);
|
|
965
|
+
const body1 = (await res1.json()) as { conversationId: string };
|
|
966
|
+
|
|
967
|
+
const res2 = await fetch(messagesUrl(), {
|
|
968
|
+
method: "POST",
|
|
969
|
+
headers: { "Content-Type": "application/json", ...AUTH_HEADERS },
|
|
970
|
+
body: JSON.stringify({
|
|
971
|
+
content: "Second",
|
|
972
|
+
sourceChannel: "phone",
|
|
973
|
+
interface: "phone",
|
|
974
|
+
}),
|
|
975
|
+
});
|
|
976
|
+
expect(res2.status).toBe(202);
|
|
977
|
+
const body2 = (await res2.json()) as { conversationId: string };
|
|
941
978
|
|
|
942
|
-
//
|
|
943
|
-
//
|
|
944
|
-
|
|
979
|
+
// Non-vellum channels keep the legacy `default:<channel>:<interface>`
|
|
980
|
+
// co-location so repeated inbound messages from the same external
|
|
981
|
+
// channel/interface land on a single thread.
|
|
982
|
+
expect(body1.conversationId).toBe(body2.conversationId);
|
|
983
|
+
const mapping = getConversationByKey("default:phone:phone");
|
|
945
984
|
expect(mapping).not.toBeNull();
|
|
946
|
-
expect(mapping!.conversationId).
|
|
985
|
+
expect(mapping!.conversationId).toBe(body1.conversationId);
|
|
947
986
|
|
|
948
987
|
await stopServer();
|
|
949
988
|
});
|
|
@@ -578,7 +578,10 @@ describe("getAttachmentsForMessage", () => {
|
|
|
578
578
|
db.run("DELETE FROM conversations");
|
|
579
579
|
});
|
|
580
580
|
|
|
581
|
-
async function createMessage(
|
|
581
|
+
async function createMessage(
|
|
582
|
+
role: "user" | "assistant" | "system",
|
|
583
|
+
content: string,
|
|
584
|
+
): Promise<string> {
|
|
582
585
|
const conv = createConversation("test");
|
|
583
586
|
const msg = await addMessage(conv.id, role, content);
|
|
584
587
|
return msg.id;
|
|
@@ -7,17 +7,15 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
isAssistantFeatureFlagEnabled,
|
|
13
|
-
} from "../config/assistant-feature-flags.js";
|
|
10
|
+
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
11
|
+
import { setOverridesForTesting } from "./feature-flag-test-helpers.js";
|
|
14
12
|
|
|
15
13
|
beforeEach(() => {
|
|
16
|
-
|
|
14
|
+
setOverridesForTesting({});
|
|
17
15
|
});
|
|
18
16
|
|
|
19
17
|
afterEach(() => {
|
|
20
|
-
|
|
18
|
+
setOverridesForTesting({});
|
|
21
19
|
});
|
|
22
20
|
import type { AssistantConfig } from "../config/schema.js";
|
|
23
21
|
import { resolveSkillStates, skillFlagKey } from "../config/skill-state.js";
|
|
@@ -150,7 +148,7 @@ describe("frontmatter feature-flag integration", () => {
|
|
|
150
148
|
});
|
|
151
149
|
|
|
152
150
|
test("resolveSkillStates includes skill with featureFlag when flag is ON", () => {
|
|
153
|
-
|
|
151
|
+
setOverridesForTesting({
|
|
154
152
|
"email-channel": true,
|
|
155
153
|
});
|
|
156
154
|
const skill = buildSkillSummary("email-setup", SKILL_MD_WITH_FLAG)!;
|
|
@@ -174,7 +172,7 @@ describe("frontmatter feature-flag integration", () => {
|
|
|
174
172
|
test("resolveSkillStates never gates skill without featureFlag", () => {
|
|
175
173
|
const skill = buildSkillSummary("plain-skill", SKILL_MD_WITHOUT_FLAG)!;
|
|
176
174
|
// Even with an explicit false override for this skill ID, it should pass through
|
|
177
|
-
|
|
175
|
+
setOverridesForTesting({
|
|
178
176
|
"plain-skill": false,
|
|
179
177
|
});
|
|
180
178
|
const config = makeConfig();
|
|
@@ -210,7 +208,7 @@ describe("frontmatter feature-flag integration", () => {
|
|
|
210
208
|
expect(resolvedDefault.length).toBe(0);
|
|
211
209
|
|
|
212
210
|
// Step 6: With override enabled, skill passes through
|
|
213
|
-
|
|
211
|
+
setOverridesForTesting({ [key!]: true });
|
|
214
212
|
const configOn = makeConfig();
|
|
215
213
|
expect(isAssistantFeatureFlagEnabled(key!, configOn)).toBe(true);
|
|
216
214
|
|
|
@@ -219,7 +217,7 @@ describe("frontmatter feature-flag integration", () => {
|
|
|
219
217
|
expect(resolvedOn[0].summary.id).toBe("email-setup");
|
|
220
218
|
|
|
221
219
|
// Step 7: With override disabled, skill is filtered out
|
|
222
|
-
|
|
220
|
+
setOverridesForTesting({ [key!]: false });
|
|
223
221
|
const configOff = makeConfig();
|
|
224
222
|
expect(isAssistantFeatureFlagEnabled(key!, configOff)).toBe(false);
|
|
225
223
|
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
_setOverridesForTesting,
|
|
5
|
-
isAssistantFeatureFlagEnabled,
|
|
6
|
-
} from "../config/assistant-feature-flags.js";
|
|
3
|
+
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
7
4
|
import type { AssistantConfig } from "../config/schema.js";
|
|
8
5
|
import { resolveSkillStates, skillFlagKey } from "../config/skill-state.js";
|
|
9
6
|
import type { SkillSummary } from "../config/skills.js";
|
|
7
|
+
import { setOverridesForTesting } from "./feature-flag-test-helpers.js";
|
|
10
8
|
|
|
11
9
|
beforeEach(() => {
|
|
12
|
-
|
|
10
|
+
setOverridesForTesting({});
|
|
13
11
|
});
|
|
14
12
|
|
|
15
13
|
afterEach(() => {
|
|
16
|
-
|
|
14
|
+
setOverridesForTesting({});
|
|
17
15
|
});
|
|
18
16
|
|
|
19
17
|
const DECLARED_FLAG_ID = "email-channel";
|
|
@@ -103,7 +101,7 @@ describe("isAssistantFeatureFlagEnabled with skillFlagKey", () => {
|
|
|
103
101
|
});
|
|
104
102
|
|
|
105
103
|
test("returns true when skill key is explicitly true", () => {
|
|
106
|
-
|
|
104
|
+
setOverridesForTesting({ [DECLARED_FLAG_KEY]: true });
|
|
107
105
|
const config = makeConfig();
|
|
108
106
|
expect(
|
|
109
107
|
isAssistantFeatureFlagEnabled(
|
|
@@ -114,7 +112,7 @@ describe("isAssistantFeatureFlagEnabled with skillFlagKey", () => {
|
|
|
114
112
|
});
|
|
115
113
|
|
|
116
114
|
test("returns false when skill key is explicitly false", () => {
|
|
117
|
-
|
|
115
|
+
setOverridesForTesting({ [DECLARED_FLAG_KEY]: false });
|
|
118
116
|
const config = makeConfig();
|
|
119
117
|
expect(
|
|
120
118
|
isAssistantFeatureFlagEnabled(
|
|
@@ -136,7 +134,7 @@ describe("isAssistantFeatureFlagEnabled", () => {
|
|
|
136
134
|
});
|
|
137
135
|
|
|
138
136
|
test("file-based override overrides registry default", () => {
|
|
139
|
-
|
|
137
|
+
setOverridesForTesting({ [DECLARED_FLAG_KEY]: false });
|
|
140
138
|
const config = makeConfig();
|
|
141
139
|
expect(isAssistantFeatureFlagEnabled(DECLARED_FLAG_KEY, config)).toBe(
|
|
142
140
|
false,
|
|
@@ -152,7 +150,7 @@ describe("isAssistantFeatureFlagEnabled", () => {
|
|
|
152
150
|
});
|
|
153
151
|
|
|
154
152
|
test("respects persisted overrides for undeclared keys", () => {
|
|
155
|
-
|
|
153
|
+
setOverridesForTesting({ "some-undeclared-flag": false });
|
|
156
154
|
const config = makeConfig();
|
|
157
155
|
expect(isAssistantFeatureFlagEnabled("some-undeclared-flag", config)).toBe(
|
|
158
156
|
false,
|
|
@@ -166,7 +164,7 @@ describe("isAssistantFeatureFlagEnabled", () => {
|
|
|
166
164
|
|
|
167
165
|
describe("resolveSkillStates with feature flags", () => {
|
|
168
166
|
test("flag OFF skill does not appear in resolved list", () => {
|
|
169
|
-
|
|
167
|
+
setOverridesForTesting({
|
|
170
168
|
[DECLARED_FLAG_KEY]: false,
|
|
171
169
|
[ENABLED_UNDECLARED_FLAG_KEY]: true,
|
|
172
170
|
});
|
|
@@ -188,7 +186,7 @@ describe("resolveSkillStates with feature flags", () => {
|
|
|
188
186
|
});
|
|
189
187
|
|
|
190
188
|
test("flag ON skill appears normally", () => {
|
|
191
|
-
|
|
189
|
+
setOverridesForTesting({
|
|
192
190
|
[DECLARED_FLAG_KEY]: true,
|
|
193
191
|
[ENABLED_UNDECLARED_FLAG_KEY]: true,
|
|
194
192
|
});
|
|
@@ -230,7 +228,7 @@ describe("resolveSkillStates with feature flags", () => {
|
|
|
230
228
|
});
|
|
231
229
|
|
|
232
230
|
test("feature flag OFF takes precedence over user-enabled config entry", () => {
|
|
233
|
-
|
|
231
|
+
setOverridesForTesting({ [DECLARED_FLAG_KEY]: false });
|
|
234
232
|
const catalog = [makeSkill(DECLARED_SKILL_ID, "bundled", DECLARED_FLAG_ID)];
|
|
235
233
|
const config = makeConfig({
|
|
236
234
|
skills: {
|
|
@@ -256,7 +254,7 @@ describe("resolveSkillStates with feature flags", () => {
|
|
|
256
254
|
});
|
|
257
255
|
|
|
258
256
|
test("multiple skills with mixed flags — persisted overrides respected", () => {
|
|
259
|
-
|
|
257
|
+
setOverridesForTesting({
|
|
260
258
|
[DECLARED_FLAG_KEY]: false,
|
|
261
259
|
[ENABLED_UNDECLARED_FLAG_KEY]: true,
|
|
262
260
|
deploy: false,
|
|
@@ -296,7 +294,7 @@ describe("resolveSkillStates with frontmatter featureFlag", () => {
|
|
|
296
294
|
});
|
|
297
295
|
|
|
298
296
|
test("skill with featureFlag is included when override enables it", () => {
|
|
299
|
-
|
|
297
|
+
setOverridesForTesting({ [DECLARED_FLAG_KEY]: true });
|
|
300
298
|
const catalog = [makeSkill(DECLARED_SKILL_ID, "bundled", DECLARED_FLAG_ID)];
|
|
301
299
|
const config = makeConfig();
|
|
302
300
|
|
|
@@ -320,7 +318,7 @@ describe("resolveSkillStates with frontmatter featureFlag", () => {
|
|
|
320
318
|
// This proves the implicit skillId→flag mapping is gone:
|
|
321
319
|
// setting feature_flags.my-skill.enabled = false has no effect
|
|
322
320
|
// when the skill itself does not declare a featureFlag.
|
|
323
|
-
|
|
321
|
+
setOverridesForTesting({
|
|
324
322
|
"my-skill": false,
|
|
325
323
|
});
|
|
326
324
|
const catalog = [makeSkill("my-skill")];
|
|
@@ -6,7 +6,7 @@ import { mkdirSync, writeFileSync } from "node:fs";
|
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { setOverridesForTesting } from "./feature-flag-test-helpers.js";
|
|
10
10
|
|
|
11
11
|
const TEST_DIR = process.env.VELLUM_WORKSPACE_DIR!;
|
|
12
12
|
|
|
@@ -89,11 +89,11 @@ describe("skill_load feature flag enforcement", () => {
|
|
|
89
89
|
beforeEach(() => {
|
|
90
90
|
mkdirSync(join(TEST_DIR, "skills"), { recursive: true });
|
|
91
91
|
currentConfig = {};
|
|
92
|
-
|
|
92
|
+
setOverridesForTesting({});
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
afterEach(() => {
|
|
96
|
-
|
|
96
|
+
setOverridesForTesting({});
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
test("returns deterministic error for flag OFF skill", async () => {
|
|
@@ -104,7 +104,7 @@ describe("skill_load feature flag enforcement", () => {
|
|
|
104
104
|
"Use the feature.",
|
|
105
105
|
);
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
setOverridesForTesting({ [DECLARED_FLAG_KEY]: false });
|
|
108
108
|
|
|
109
109
|
const result = await executeSkillLoad({ skill: DECLARED_SKILL_ID });
|
|
110
110
|
|
|
@@ -121,7 +121,7 @@ describe("skill_load feature flag enforcement", () => {
|
|
|
121
121
|
"Use the feature.",
|
|
122
122
|
);
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
setOverridesForTesting({ [DECLARED_FLAG_KEY]: true });
|
|
125
125
|
|
|
126
126
|
const result = await executeSkillLoad({ skill: DECLARED_SKILL_ID });
|
|
127
127
|
|
|
@@ -45,6 +45,10 @@ mock.module("../config/skill-state.js", () => ({
|
|
|
45
45
|
|
|
46
46
|
// Mock assistant-feature-flags to avoid loading the real module (which
|
|
47
47
|
// triggers file I/O and env-registry imports that hang in test context).
|
|
48
|
+
// The seed-state backdoor is the test-only helper module — we mirror
|
|
49
|
+
// production's design: tests reach into `feature-flag-cache.ts` (or its
|
|
50
|
+
// test-helper wrapper) to seed cached overrides, never through the
|
|
51
|
+
// resolver module itself.
|
|
48
52
|
let _mockOverrides: Record<string, boolean> = {};
|
|
49
53
|
mock.module("../config/assistant-feature-flags.js", () => ({
|
|
50
54
|
isAssistantFeatureFlagEnabled: (key: string, _config: unknown): boolean => {
|
|
@@ -55,10 +59,12 @@ mock.module("../config/assistant-feature-flags.js", () => ({
|
|
|
55
59
|
clearFeatureFlagOverridesCache: () => {
|
|
56
60
|
_mockOverrides = {};
|
|
57
61
|
},
|
|
58
|
-
|
|
62
|
+
getAssistantFeatureFlagDefaults: () => ({}),
|
|
63
|
+
}));
|
|
64
|
+
mock.module("./feature-flag-test-helpers.js", () => ({
|
|
65
|
+
setOverridesForTesting: (overrides: Record<string, boolean>) => {
|
|
59
66
|
_mockOverrides = { ...overrides };
|
|
60
67
|
},
|
|
61
|
-
getAssistantFeatureFlagDefaults: () => ({}),
|
|
62
68
|
}));
|
|
63
69
|
|
|
64
70
|
mock.module("../skills/active-skill-tools.js", () => {
|
|
@@ -109,12 +115,13 @@ mock.module("../skills/tool-manifest.js", () => ({
|
|
|
109
115
|
}));
|
|
110
116
|
|
|
111
117
|
mock.module("../tools/skills/skill-tool-factory.js", () => ({
|
|
118
|
+
// Mirrors the real factory: no skillId in/out — ownership is recorded by
|
|
119
|
+
// the registry at `registerSkillTools(skillId, tools)` time.
|
|
112
120
|
createSkillToolsFromManifest: (
|
|
113
121
|
entries: SkillToolManifest["tools"],
|
|
114
|
-
skillId: string,
|
|
115
122
|
_skillDir: string,
|
|
116
|
-
|
|
117
|
-
|
|
123
|
+
_versionHash: string,
|
|
124
|
+
_bundled?: boolean,
|
|
118
125
|
): Tool[] => {
|
|
119
126
|
return entries.map((entry) => ({
|
|
120
127
|
name: entry.name,
|
|
@@ -122,10 +129,6 @@ mock.module("../tools/skills/skill-tool-factory.js", () => ({
|
|
|
122
129
|
category: entry.category,
|
|
123
130
|
defaultRiskLevel: RiskLevel.Medium,
|
|
124
131
|
executionTarget: "sandbox" as const,
|
|
125
|
-
origin: "skill" as const,
|
|
126
|
-
ownerSkillId: skillId,
|
|
127
|
-
ownerSkillVersionHash: versionHash,
|
|
128
|
-
ownerSkillBundled: bundled ?? undefined,
|
|
129
132
|
input_schema: entry.input_schema as object,
|
|
130
133
|
execute: async () => ({ content: "", isError: false }),
|
|
131
134
|
}));
|
|
@@ -133,18 +136,14 @@ mock.module("../tools/skills/skill-tool-factory.js", () => ({
|
|
|
133
136
|
}));
|
|
134
137
|
|
|
135
138
|
mock.module("../tools/registry.js", () => ({
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
for (const id of skillIds) {
|
|
146
|
-
mockSkillRefCount.set(id, (mockSkillRefCount.get(id) ?? 0) + 1);
|
|
147
|
-
}
|
|
139
|
+
// Matches the new signature: `registerSkillTools(skillId, tools)`. The
|
|
140
|
+
// skillId comes from the caller (conversation-skill-tools) and is the
|
|
141
|
+
// sole source of truth for ownership.
|
|
142
|
+
registerSkillTools: (skillId: string, tools: Tool[]) => {
|
|
143
|
+
const existing = mockRegisteredTools.get(skillId) ?? [];
|
|
144
|
+
existing.push(...tools);
|
|
145
|
+
mockRegisteredTools.set(skillId, existing);
|
|
146
|
+
mockSkillRefCount.set(skillId, (mockSkillRefCount.get(skillId) ?? 0) + 1);
|
|
148
147
|
return tools;
|
|
149
148
|
},
|
|
150
149
|
unregisterSkillTools: (skillId: string) => {
|
|
@@ -166,6 +165,23 @@ mock.module("../tools/registry.js", () => ({
|
|
|
166
165
|
}
|
|
167
166
|
return found;
|
|
168
167
|
},
|
|
168
|
+
// Mirrors the registry's `ownersByName` accessor: derives the owning
|
|
169
|
+
// skillId from `mockRegisteredTools` keying so the production
|
|
170
|
+
// `getToolOwner(name)` call in `conversation-skill-tools.ts` resolves to
|
|
171
|
+
// the same shape the real registry would return.
|
|
172
|
+
getToolOwner: (
|
|
173
|
+
name: string,
|
|
174
|
+
): { kind: "skill" | "plugin" | "mcp"; id: string } | undefined => {
|
|
175
|
+
let ownerSkillId: string | undefined;
|
|
176
|
+
for (const [skillId, tools] of mockRegisteredTools.entries()) {
|
|
177
|
+
for (const tool of tools) {
|
|
178
|
+
if (tool.name === name) ownerSkillId = skillId;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return ownerSkillId === undefined
|
|
182
|
+
? undefined
|
|
183
|
+
: { kind: "skill", id: ownerSkillId };
|
|
184
|
+
},
|
|
169
185
|
getSkillToolNames: () => {
|
|
170
186
|
const names: string[] = [];
|
|
171
187
|
for (const tools of mockRegisteredTools.values()) {
|
|
@@ -212,10 +228,8 @@ mock.module("../util/logger.js", () => ({
|
|
|
212
228
|
|
|
213
229
|
const { projectSkillTools, resetSkillToolProjection } =
|
|
214
230
|
await import("../daemon/conversation-skill-tools.js");
|
|
215
|
-
const {
|
|
216
|
-
|
|
217
|
-
_setOverridesForTesting: (o: Record<string, boolean>) => void;
|
|
218
|
-
};
|
|
231
|
+
const { setOverridesForTesting } =
|
|
232
|
+
await import("./feature-flag-test-helpers.js");
|
|
219
233
|
|
|
220
234
|
// ---------------------------------------------------------------------------
|
|
221
235
|
// Helpers
|
|
@@ -289,12 +303,12 @@ describe("projectSkillTools feature flag enforcement", () => {
|
|
|
289
303
|
mockUnregisteredSkillIds = [];
|
|
290
304
|
mockSkillRefCount = new Map();
|
|
291
305
|
currentConfig = {};
|
|
292
|
-
|
|
306
|
+
setOverridesForTesting({});
|
|
293
307
|
resetSkillToolProjection();
|
|
294
308
|
});
|
|
295
309
|
|
|
296
310
|
afterEach(() => {
|
|
297
|
-
|
|
311
|
+
setOverridesForTesting({});
|
|
298
312
|
});
|
|
299
313
|
|
|
300
314
|
test("no skill tools projected for flag OFF skill even with old markers", () => {
|
|
@@ -308,7 +322,7 @@ describe("projectSkillTools feature flag enforcement", () => {
|
|
|
308
322
|
const prevActive = new Map<string, string>();
|
|
309
323
|
|
|
310
324
|
// Feature flag is OFF — use protected directory override
|
|
311
|
-
|
|
325
|
+
setOverridesForTesting({ [DECLARED_FLAG_KEY]: false });
|
|
312
326
|
|
|
313
327
|
const result = projectSkillTools(history, {
|
|
314
328
|
previouslyActiveSkillIds: prevActive,
|
|
@@ -329,7 +343,7 @@ describe("projectSkillTools feature flag enforcement", () => {
|
|
|
329
343
|
const prevActive = new Map<string, string>();
|
|
330
344
|
|
|
331
345
|
// Feature flag is ON — use protected directory override
|
|
332
|
-
|
|
346
|
+
setOverridesForTesting({ [DECLARED_FLAG_KEY]: true });
|
|
333
347
|
|
|
334
348
|
const result = projectSkillTools(history, {
|
|
335
349
|
previouslyActiveSkillIds: prevActive,
|
|
@@ -420,7 +434,7 @@ describe("projectSkillTools feature flag enforcement", () => {
|
|
|
420
434
|
const prevActive = new Map<string, string>();
|
|
421
435
|
|
|
422
436
|
// Declared skill is OFF; plain-skill has no featureFlag so remains ON.
|
|
423
|
-
|
|
437
|
+
setOverridesForTesting({ [DECLARED_FLAG_KEY]: false });
|
|
424
438
|
|
|
425
439
|
const result = projectSkillTools(history, {
|
|
426
440
|
previouslyActiveSkillIds: prevActive,
|
|
@@ -102,18 +102,15 @@ mock.module("../tools/skills/skill-tool-factory.js", () => ({
|
|
|
102
102
|
entries: Array<{ name: string; description: string; input_schema: object }>,
|
|
103
103
|
skillId: string,
|
|
104
104
|
_skillDir: string,
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
_versionHash: string,
|
|
106
|
+
_bundled?: boolean,
|
|
107
107
|
) =>
|
|
108
108
|
entries.map((e) => ({
|
|
109
109
|
name: e.name,
|
|
110
110
|
description: e.description,
|
|
111
111
|
category: "skill",
|
|
112
112
|
defaultRiskLevel: "low",
|
|
113
|
-
|
|
114
|
-
ownerSkillId: skillId,
|
|
115
|
-
ownerSkillVersionHash: versionHash,
|
|
116
|
-
ownerSkillBundled: bundled,
|
|
113
|
+
owner: { kind: "skill" as const, id: skillId },
|
|
117
114
|
input_schema: e.input_schema,
|
|
118
115
|
execute: async () => ({ content: "", isError: false }),
|
|
119
116
|
})),
|
|
@@ -166,7 +163,8 @@ mock.module("../tools/registry.js", () => ({
|
|
|
166
163
|
},
|
|
167
164
|
unregisterSkillTools: (skillId: string) => {
|
|
168
165
|
for (const [name, t] of benchmarkRegistry) {
|
|
169
|
-
|
|
166
|
+
const owner = (t as { owner?: { kind: string; id: string } }).owner;
|
|
167
|
+
if (owner?.kind === "skill" && owner.id === skillId)
|
|
170
168
|
benchmarkRegistry.delete(name);
|
|
171
169
|
}
|
|
172
170
|
},
|