@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
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
rmSync,
|
|
6
|
+
writeFileSync,
|
|
7
|
+
} from "node:fs";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
11
|
+
|
|
12
|
+
import { memoryRouterCostOptimizedProfileMigration } from "../workspace/migrations/090-memory-router-cost-optimized-profile.js";
|
|
13
|
+
|
|
14
|
+
let workspaceDir: string;
|
|
15
|
+
|
|
16
|
+
function freshWorkspace(): void {
|
|
17
|
+
workspaceDir = join(
|
|
18
|
+
tmpdir(),
|
|
19
|
+
`vellum-migration-090-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
20
|
+
);
|
|
21
|
+
mkdirSync(workspaceDir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function writeConfig(data: Record<string, unknown>): void {
|
|
25
|
+
writeFileSync(
|
|
26
|
+
join(workspaceDir, "config.json"),
|
|
27
|
+
JSON.stringify(data, null, 2) + "\n",
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readConfig(): Record<string, unknown> {
|
|
32
|
+
return JSON.parse(readFileSync(join(workspaceDir, "config.json"), "utf-8"));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function configPath(): string {
|
|
36
|
+
return join(workspaceDir, "config.json");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
freshWorkspace();
|
|
41
|
+
delete process.env.VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
delete process.env.VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH;
|
|
46
|
+
if (existsSync(workspaceDir)) {
|
|
47
|
+
rmSync(workspaceDir, { recursive: true, force: true });
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe("090-memory-router-cost-optimized-profile migration", () => {
|
|
52
|
+
test("has correct migration id", () => {
|
|
53
|
+
expect(memoryRouterCostOptimizedProfileMigration.id).toBe(
|
|
54
|
+
"090-memory-router-cost-optimized-profile",
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("flips the 087-seeded balanced profile to cost-optimized with 1M context", () => {
|
|
59
|
+
writeConfig({
|
|
60
|
+
llm: {
|
|
61
|
+
default: { provider: "anthropic" },
|
|
62
|
+
callSites: {
|
|
63
|
+
memoryRouter: { profile: "balanced" },
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
memoryRouterCostOptimizedProfileMigration.run(workspaceDir);
|
|
69
|
+
|
|
70
|
+
const config = readConfig() as {
|
|
71
|
+
llm: { callSites: Record<string, Record<string, unknown>> };
|
|
72
|
+
};
|
|
73
|
+
// Must mirror the shipped default in call-site-defaults.ts: dropping the
|
|
74
|
+
// contextWindow would regress migrated users to the profile's ~200k window
|
|
75
|
+
// because the resolver prefers an explicit callSites entry.
|
|
76
|
+
expect(config.llm.callSites.memoryRouter).toEqual({
|
|
77
|
+
profile: "cost-optimized",
|
|
78
|
+
contextWindow: { maxInputTokens: 1_000_000 },
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("flips when default provider is absent (treated as Anthropic)", () => {
|
|
83
|
+
writeConfig({
|
|
84
|
+
llm: {
|
|
85
|
+
callSites: {
|
|
86
|
+
memoryRouter: { profile: "balanced" },
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
memoryRouterCostOptimizedProfileMigration.run(workspaceDir);
|
|
92
|
+
|
|
93
|
+
const config = readConfig() as {
|
|
94
|
+
llm: { callSites: Record<string, Record<string, unknown>> };
|
|
95
|
+
};
|
|
96
|
+
expect(config.llm.callSites.memoryRouter).toEqual({
|
|
97
|
+
profile: "cost-optimized",
|
|
98
|
+
contextWindow: { maxInputTokens: 1_000_000 },
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("upgrades a bare cost-optimized entry left by a pre-fix run to add 1M context", () => {
|
|
103
|
+
// An earlier (pre-fix) run of this migration wrote a bare
|
|
104
|
+
// { profile: "cost-optimized" } with no contextWindow. Re-running must
|
|
105
|
+
// recover the 1M window for those users.
|
|
106
|
+
writeConfig({
|
|
107
|
+
llm: {
|
|
108
|
+
default: { provider: "anthropic" },
|
|
109
|
+
callSites: {
|
|
110
|
+
memoryRouter: { profile: "cost-optimized" },
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
memoryRouterCostOptimizedProfileMigration.run(workspaceDir);
|
|
116
|
+
|
|
117
|
+
const config = readConfig() as {
|
|
118
|
+
llm: { callSites: Record<string, Record<string, unknown>> };
|
|
119
|
+
};
|
|
120
|
+
expect(config.llm.callSites.memoryRouter).toEqual({
|
|
121
|
+
profile: "cost-optimized",
|
|
122
|
+
contextWindow: { maxInputTokens: 1_000_000 },
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("does not create the call site when missing — shipped default handles it", () => {
|
|
127
|
+
// Fresh installs and BYOK-skipped-by-087 workspaces have no memoryRouter
|
|
128
|
+
// entry. The shipped default in call-site-defaults.ts already supplies
|
|
129
|
+
// cost-optimized + 1M context, so this migration should not write anything.
|
|
130
|
+
writeConfig({
|
|
131
|
+
llm: { default: { provider: "anthropic" } },
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const before = readFileSync(configPath(), "utf-8");
|
|
135
|
+
memoryRouterCostOptimizedProfileMigration.run(workspaceDir);
|
|
136
|
+
const after = readFileSync(configPath(), "utf-8");
|
|
137
|
+
|
|
138
|
+
expect(after).toBe(before);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("does nothing when config.json is absent", () => {
|
|
142
|
+
memoryRouterCostOptimizedProfileMigration.run(workspaceDir);
|
|
143
|
+
expect(existsSync(configPath())).toBe(false);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("skips BYOK / non-Anthropic workspaces to avoid breaking memoryRouter", () => {
|
|
147
|
+
// `cost-optimized` resolves to a managed connection that BYOK installs
|
|
148
|
+
// disable. Rewriting there would silently disable memory injection.
|
|
149
|
+
writeConfig({
|
|
150
|
+
llm: {
|
|
151
|
+
default: { provider: "openai", model: "gpt-5.4" },
|
|
152
|
+
callSites: {
|
|
153
|
+
memoryRouter: { profile: "balanced" },
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
memoryRouterCostOptimizedProfileMigration.run(workspaceDir);
|
|
159
|
+
|
|
160
|
+
const config = readConfig() as {
|
|
161
|
+
llm: { callSites: Record<string, Record<string, unknown>> };
|
|
162
|
+
};
|
|
163
|
+
expect(config.llm.callSites.memoryRouter).toEqual({ profile: "balanced" });
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("skips BYOK workspaces with no existing memoryRouter entry", () => {
|
|
167
|
+
writeConfig({
|
|
168
|
+
llm: { default: { provider: "gemini" } },
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
memoryRouterCostOptimizedProfileMigration.run(workspaceDir);
|
|
172
|
+
|
|
173
|
+
const config = readConfig() as {
|
|
174
|
+
llm: { callSites?: Record<string, Record<string, unknown>> };
|
|
175
|
+
};
|
|
176
|
+
expect(config.llm.callSites?.memoryRouter).toBeUndefined();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("preserves user customizations (model pin)", () => {
|
|
180
|
+
writeConfig({
|
|
181
|
+
llm: {
|
|
182
|
+
default: { provider: "anthropic" },
|
|
183
|
+
callSites: {
|
|
184
|
+
memoryRouter: { model: "claude-sonnet-4-6" },
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
memoryRouterCostOptimizedProfileMigration.run(workspaceDir);
|
|
190
|
+
|
|
191
|
+
const config = readConfig() as {
|
|
192
|
+
llm: { callSites: Record<string, Record<string, unknown>> };
|
|
193
|
+
};
|
|
194
|
+
expect(config.llm.callSites.memoryRouter).toEqual({
|
|
195
|
+
model: "claude-sonnet-4-6",
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("preserves user customizations (profile + tuning fields)", () => {
|
|
200
|
+
writeConfig({
|
|
201
|
+
llm: {
|
|
202
|
+
default: { provider: "anthropic" },
|
|
203
|
+
callSites: {
|
|
204
|
+
memoryRouter: { profile: "balanced", effort: "low" },
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
memoryRouterCostOptimizedProfileMigration.run(workspaceDir);
|
|
210
|
+
|
|
211
|
+
const config = readConfig() as {
|
|
212
|
+
llm: { callSites: Record<string, Record<string, unknown>> };
|
|
213
|
+
};
|
|
214
|
+
expect(config.llm.callSites.memoryRouter).toEqual({
|
|
215
|
+
profile: "balanced",
|
|
216
|
+
effort: "low",
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("preserves the original 077-seeded shape if still present", () => {
|
|
221
|
+
writeConfig({
|
|
222
|
+
llm: {
|
|
223
|
+
default: { provider: "anthropic" },
|
|
224
|
+
callSites: {
|
|
225
|
+
memoryRouter: {
|
|
226
|
+
model: "claude-sonnet-4-6",
|
|
227
|
+
contextWindow: { maxInputTokens: 1_000_000 },
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
memoryRouterCostOptimizedProfileMigration.run(workspaceDir);
|
|
234
|
+
|
|
235
|
+
const config = readConfig() as {
|
|
236
|
+
llm: { callSites: Record<string, Record<string, unknown>> };
|
|
237
|
+
};
|
|
238
|
+
expect(config.llm.callSites.memoryRouter).toEqual({
|
|
239
|
+
model: "claude-sonnet-4-6",
|
|
240
|
+
contextWindow: { maxInputTokens: 1_000_000 },
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("preserves quality-optimized override", () => {
|
|
245
|
+
writeConfig({
|
|
246
|
+
llm: {
|
|
247
|
+
default: { provider: "anthropic" },
|
|
248
|
+
callSites: {
|
|
249
|
+
memoryRouter: { profile: "quality-optimized" },
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
memoryRouterCostOptimizedProfileMigration.run(workspaceDir);
|
|
255
|
+
|
|
256
|
+
const config = readConfig() as {
|
|
257
|
+
llm: { callSites: Record<string, Record<string, unknown>> };
|
|
258
|
+
};
|
|
259
|
+
expect(config.llm.callSites.memoryRouter).toEqual({
|
|
260
|
+
profile: "quality-optimized",
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("is idempotent — second run does not change the call site", () => {
|
|
265
|
+
writeConfig({
|
|
266
|
+
llm: {
|
|
267
|
+
default: { provider: "anthropic" },
|
|
268
|
+
callSites: {
|
|
269
|
+
memoryRouter: { profile: "balanced" },
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
memoryRouterCostOptimizedProfileMigration.run(workspaceDir);
|
|
275
|
+
const afterFirst = readFileSync(configPath(), "utf-8");
|
|
276
|
+
memoryRouterCostOptimizedProfileMigration.run(workspaceDir);
|
|
277
|
+
const afterSecond = readFileSync(configPath(), "utf-8");
|
|
278
|
+
|
|
279
|
+
expect(afterSecond).toBe(afterFirst);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test("skips when VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH is set", () => {
|
|
283
|
+
process.env.VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH = "/tmp/overlay.json";
|
|
284
|
+
writeConfig({
|
|
285
|
+
llm: {
|
|
286
|
+
default: { provider: "anthropic" },
|
|
287
|
+
callSites: {
|
|
288
|
+
memoryRouter: { profile: "balanced" },
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
memoryRouterCostOptimizedProfileMigration.run(workspaceDir);
|
|
294
|
+
|
|
295
|
+
const config = readConfig() as {
|
|
296
|
+
llm: { callSites: Record<string, Record<string, unknown>> };
|
|
297
|
+
};
|
|
298
|
+
expect(config.llm.callSites.memoryRouter).toEqual({ profile: "balanced" });
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test("preserves sibling call-site entries", () => {
|
|
302
|
+
writeConfig({
|
|
303
|
+
llm: {
|
|
304
|
+
default: { provider: "anthropic" },
|
|
305
|
+
callSites: {
|
|
306
|
+
mainAgent: { model: "claude-opus-4-7", maxTokens: 32000 },
|
|
307
|
+
memoryRouter: { profile: "balanced" },
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
memoryRouterCostOptimizedProfileMigration.run(workspaceDir);
|
|
313
|
+
|
|
314
|
+
const config = readConfig() as {
|
|
315
|
+
llm: { callSites: Record<string, Record<string, unknown>> };
|
|
316
|
+
};
|
|
317
|
+
expect(config.llm.callSites.mainAgent).toEqual({
|
|
318
|
+
model: "claude-opus-4-7",
|
|
319
|
+
maxTokens: 32000,
|
|
320
|
+
});
|
|
321
|
+
expect(config.llm.callSites.memoryRouter).toEqual({
|
|
322
|
+
profile: "cost-optimized",
|
|
323
|
+
contextWindow: { maxInputTokens: 1_000_000 },
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
});
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for workspace migration `091-retighten-migration-onboarding-thread`.
|
|
3
|
+
*
|
|
4
|
+
* The migration rewrites the assistant-migration onboarding bullet in
|
|
5
|
+
* `memory/threads.md` (seeded by 069) only for newly-created workspaces
|
|
6
|
+
* (`ctx.isNewWorkspace === true`) when the exact old bullet is present. It is a
|
|
7
|
+
* no-op on upgrade, when the old bullet is absent, and when the file is
|
|
8
|
+
* missing. It must be idempotent and compose with 069 (069 seeds the old
|
|
9
|
+
* bullets, then 091 retightens bullet 3).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
existsSync,
|
|
14
|
+
mkdirSync,
|
|
15
|
+
mkdtempSync,
|
|
16
|
+
readFileSync,
|
|
17
|
+
rmSync,
|
|
18
|
+
writeFileSync,
|
|
19
|
+
} from "node:fs";
|
|
20
|
+
import { tmpdir } from "node:os";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
23
|
+
|
|
24
|
+
import { seedOnboardingThreadsMigration } from "../workspace/migrations/069-seed-onboarding-threads.js";
|
|
25
|
+
import { retightenMigrationOnboardingThreadMigration } from "../workspace/migrations/091-retighten-migration-onboarding-thread.js";
|
|
26
|
+
import type { MigrationRunContext } from "../workspace/migrations/types.js";
|
|
27
|
+
|
|
28
|
+
const NEW_WORKSPACE_CTX: MigrationRunContext = { isNewWorkspace: true };
|
|
29
|
+
const UPGRADE_CTX: MigrationRunContext = { isNewWorkspace: false };
|
|
30
|
+
|
|
31
|
+
// Literal copy of the 4-bullet block 069 seeds into memory/threads.md. The
|
|
32
|
+
// third bullet is the one 091 retightens.
|
|
33
|
+
const ONBOARDING_THREADS = `- Figure out what kind of personality would best mesh with your user. Figure out who you are and what your voice should be. Your choice should be DISTINCT and have CHARACTER. Once you've figured this out, rewrite SOUL.md and IDENTITY.md in your own voice to define who you are.
|
|
34
|
+
- Work with your user to set a custom avatar for yourself. This task is done once data/avatar/avatar-image.png exists.
|
|
35
|
+
- Ask your user if they use ChatGPT, Claude or another AI tool and offer to help them import memories from there.
|
|
36
|
+
- Suggest connecting to available messaging services like Slack or Telegram
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
const OLD_BULLET =
|
|
40
|
+
"- Ask your user if they use ChatGPT, Claude or another AI tool and offer to help them import memories from there.";
|
|
41
|
+
|
|
42
|
+
let workspaceDir: string;
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
workspaceDir = mkdtempSync(join(tmpdir(), "vellum-migration-091-test-"));
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
afterEach(() => {
|
|
49
|
+
if (existsSync(workspaceDir)) {
|
|
50
|
+
rmSync(workspaceDir, { recursive: true, force: true });
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
function seedThreads(content: string): void {
|
|
55
|
+
mkdirSync(join(workspaceDir, "memory"), { recursive: true });
|
|
56
|
+
writeFileSync(join(workspaceDir, "memory", "threads.md"), content, "utf-8");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function readThreads(): string {
|
|
60
|
+
return readFileSync(join(workspaceDir, "memory", "threads.md"), "utf-8");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
describe("091-retighten-migration-onboarding-thread migration", () => {
|
|
64
|
+
test("has correct id and description", () => {
|
|
65
|
+
expect(retightenMigrationOnboardingThreadMigration.id).toBe(
|
|
66
|
+
"091-retighten-migration-onboarding-thread",
|
|
67
|
+
);
|
|
68
|
+
expect(retightenMigrationOnboardingThreadMigration.description).toContain(
|
|
69
|
+
"threads.md",
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("replaces the old bullet on a new workspace, preserving the others", () => {
|
|
74
|
+
seedThreads(ONBOARDING_THREADS);
|
|
75
|
+
|
|
76
|
+
retightenMigrationOnboardingThreadMigration.run(
|
|
77
|
+
workspaceDir,
|
|
78
|
+
NEW_WORKSPACE_CTX,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const content = readThreads();
|
|
82
|
+
expect(content).not.toContain(OLD_BULLET);
|
|
83
|
+
expect(content).toContain("first real task");
|
|
84
|
+
expect(content).toContain("ChatGPT, Claude");
|
|
85
|
+
// The other three onboarding bullets are preserved verbatim.
|
|
86
|
+
expect(content).toContain("Figure out what kind of personality");
|
|
87
|
+
expect(content).toContain("data/avatar/avatar-image.png");
|
|
88
|
+
expect(content).toContain("Slack or Telegram");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("no-op on upgrade even when the old bullet is present", () => {
|
|
92
|
+
seedThreads(ONBOARDING_THREADS);
|
|
93
|
+
|
|
94
|
+
retightenMigrationOnboardingThreadMigration.run(workspaceDir, UPGRADE_CTX);
|
|
95
|
+
|
|
96
|
+
expect(readThreads()).toBe(ONBOARDING_THREADS);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("no-op when the old bullet is absent (user-edited content)", () => {
|
|
100
|
+
const edited = "- A bullet the user wrote themselves.\n";
|
|
101
|
+
seedThreads(edited);
|
|
102
|
+
|
|
103
|
+
retightenMigrationOnboardingThreadMigration.run(
|
|
104
|
+
workspaceDir,
|
|
105
|
+
NEW_WORKSPACE_CTX,
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
expect(readThreads()).toBe(edited);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("no-op when memory/threads.md does not exist", () => {
|
|
112
|
+
retightenMigrationOnboardingThreadMigration.run(
|
|
113
|
+
workspaceDir,
|
|
114
|
+
NEW_WORKSPACE_CTX,
|
|
115
|
+
);
|
|
116
|
+
expect(existsSync(join(workspaceDir, "memory", "threads.md"))).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("idempotent — second run produces identical content", () => {
|
|
120
|
+
seedThreads(ONBOARDING_THREADS);
|
|
121
|
+
|
|
122
|
+
retightenMigrationOnboardingThreadMigration.run(
|
|
123
|
+
workspaceDir,
|
|
124
|
+
NEW_WORKSPACE_CTX,
|
|
125
|
+
);
|
|
126
|
+
const afterFirst = readThreads();
|
|
127
|
+
|
|
128
|
+
retightenMigrationOnboardingThreadMigration.run(
|
|
129
|
+
workspaceDir,
|
|
130
|
+
NEW_WORKSPACE_CTX,
|
|
131
|
+
);
|
|
132
|
+
const afterSecond = readThreads();
|
|
133
|
+
|
|
134
|
+
expect(afterSecond).toBe(afterFirst);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("composes 069 -> 091: fresh empty workspace ends with the new bullet", () => {
|
|
138
|
+
// Mirror how the 069 test sets up: create an empty threads.md first.
|
|
139
|
+
seedThreads("");
|
|
140
|
+
|
|
141
|
+
seedOnboardingThreadsMigration.run(workspaceDir, NEW_WORKSPACE_CTX);
|
|
142
|
+
retightenMigrationOnboardingThreadMigration.run(
|
|
143
|
+
workspaceDir,
|
|
144
|
+
NEW_WORKSPACE_CTX,
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const content = readThreads();
|
|
148
|
+
expect(content).not.toContain(OLD_BULLET);
|
|
149
|
+
expect(content).toContain("first real task");
|
|
150
|
+
expect(content).toContain("ChatGPT, Claude");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("down() is a no-op — rewritten content remains", () => {
|
|
154
|
+
seedThreads(ONBOARDING_THREADS);
|
|
155
|
+
|
|
156
|
+
retightenMigrationOnboardingThreadMigration.run(
|
|
157
|
+
workspaceDir,
|
|
158
|
+
NEW_WORKSPACE_CTX,
|
|
159
|
+
);
|
|
160
|
+
const rewritten = readThreads();
|
|
161
|
+
|
|
162
|
+
retightenMigrationOnboardingThreadMigration.down(workspaceDir);
|
|
163
|
+
|
|
164
|
+
expect(readThreads()).toBe(rewritten);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
@@ -460,14 +460,13 @@ export class AcpSessionManager {
|
|
|
460
460
|
current.parentConversationId,
|
|
461
461
|
);
|
|
462
462
|
if (parentConversation) {
|
|
463
|
-
const enqueueResult = parentConversation.enqueueMessage(
|
|
464
|
-
notifyMessage,
|
|
465
|
-
|
|
466
|
-
);
|
|
463
|
+
const enqueueResult = parentConversation.enqueueMessage({
|
|
464
|
+
content: notifyMessage,
|
|
465
|
+
});
|
|
467
466
|
if (!enqueueResult.queued && !enqueueResult.rejected) {
|
|
468
467
|
parentConversation
|
|
469
|
-
.persistUserMessage(notifyMessage
|
|
470
|
-
.then((messageId) =>
|
|
468
|
+
.persistUserMessage({ content: notifyMessage })
|
|
469
|
+
.then(({ id: messageId }) =>
|
|
471
470
|
parentConversation.runAgentLoop(notifyMessage, messageId),
|
|
472
471
|
)
|
|
473
472
|
.catch((err) => {
|
package/src/agent/loop.ts
CHANGED
|
@@ -111,6 +111,8 @@ export type AgentLoopExitReason =
|
|
|
111
111
|
* leaving `agent_loop_exit_reason` NULL.
|
|
112
112
|
*/
|
|
113
113
|
| "budget_yield_unrecovered"
|
|
114
|
+
/** Provider stopped because the configured output-token limit was reached. */
|
|
115
|
+
| "max_tokens_reached"
|
|
114
116
|
/** User cancellation landed after a non-terminal checkpoint yield. */
|
|
115
117
|
| "aborted_after_checkpoint"
|
|
116
118
|
/** Signal aborted while the catch handler was synthesizing an error turn. */
|
|
@@ -119,9 +121,19 @@ export type AgentLoopExitReason =
|
|
|
119
121
|
| "error";
|
|
120
122
|
|
|
121
123
|
export type AgentEvent =
|
|
124
|
+
/**
|
|
125
|
+
* Emitted once per LLM call inside the loop, immediately before the
|
|
126
|
+
* `provider.sendMessage` invocation. Carries the optional `callSite` tag so
|
|
127
|
+
* downstream handlers (the daemon's persistence pipeline) can decide
|
|
128
|
+
* whether to reserve a row for this call. One `llm_call_started` precedes
|
|
129
|
+
* every `message_complete` for the same call; multi-call agent turns emit
|
|
130
|
+
* one pair per call.
|
|
131
|
+
*/
|
|
132
|
+
| { type: "llm_call_started"; callSite?: LLMCallSite }
|
|
122
133
|
| { type: "text_delta"; text: string }
|
|
123
134
|
| { type: "thinking_delta"; thinking: string }
|
|
124
135
|
| { type: "message_complete"; message: Message }
|
|
136
|
+
| { type: "max_tokens_reached"; stopReason: string }
|
|
125
137
|
| {
|
|
126
138
|
type: "tool_use";
|
|
127
139
|
id: string;
|
|
@@ -256,6 +268,18 @@ const DEFAULT_CONFIG: AgentLoopConfig = {
|
|
|
256
268
|
|
|
257
269
|
const MAX_CONSECUTIVE_ERROR_NUDGES = 3;
|
|
258
270
|
const MAX_EMPTY_RESPONSE_RETRIES = 1;
|
|
271
|
+
const MAX_TOKENS_STOP_REASONS = new Set([
|
|
272
|
+
"length",
|
|
273
|
+
"max_output_tokens",
|
|
274
|
+
"max_tokens",
|
|
275
|
+
]);
|
|
276
|
+
|
|
277
|
+
export function isMaxTokensStopReason(
|
|
278
|
+
stopReason: string | null | undefined,
|
|
279
|
+
): boolean {
|
|
280
|
+
if (!stopReason) return false;
|
|
281
|
+
return MAX_TOKENS_STOP_REASONS.has(stopReason.trim().toLowerCase());
|
|
282
|
+
}
|
|
259
283
|
|
|
260
284
|
/**
|
|
261
285
|
* Build a minimal {@link TurnContext} for pipeline invocations inside the
|
|
@@ -603,6 +627,16 @@ export class AgentLoop {
|
|
|
603
627
|
if (callSite) {
|
|
604
628
|
providerConfig.callSite = callSite;
|
|
605
629
|
providerConfig.usageTracking = "manual";
|
|
630
|
+
// Per-conversation seed for deterministic `mix`-profile expansion.
|
|
631
|
+
// Sourced from the orchestrator-supplied turn context's
|
|
632
|
+
// conversationId so every LLM call in a conversation resolves the
|
|
633
|
+
// same mix arm (stable across turns and retries, and across daemon
|
|
634
|
+
// restarts since the seed is the durable conversation id). Absent
|
|
635
|
+
// for standalone `AgentLoop` instances (unit tests / no turnContext)
|
|
636
|
+
// — those fall back to per-call random mix selection.
|
|
637
|
+
if (turnContext?.conversationId) {
|
|
638
|
+
providerConfig.selectionSeed = turnContext.conversationId;
|
|
639
|
+
}
|
|
606
640
|
}
|
|
607
641
|
|
|
608
642
|
// Per-call inference-profile override. The resolver layers
|
|
@@ -751,6 +785,18 @@ export class AgentLoop {
|
|
|
751
785
|
toolUseTurns,
|
|
752
786
|
);
|
|
753
787
|
|
|
788
|
+
// Announce the LLM-call boundary so downstream handlers (the
|
|
789
|
+
// daemon's persistence pipeline) can reserve an empty assistant row
|
|
790
|
+
// and stamp the resulting `messageId` onto every streaming event the
|
|
791
|
+
// call emits. Emit as late as possible — after history stripping,
|
|
792
|
+
// arg construction, and turn-context resolution — so the gap
|
|
793
|
+
// between "we said the call started" and the actual provider HTTP
|
|
794
|
+
// call is minimized. Awaited so the row is created and the
|
|
795
|
+
// `assistant_turn_start` wire event reaches the client BEFORE the
|
|
796
|
+
// provider starts streaming deltas — the deltas downstream will
|
|
797
|
+
// carry the freshly-reserved id.
|
|
798
|
+
await onEvent({ type: "llm_call_started", callSite });
|
|
799
|
+
|
|
754
800
|
// Inner try/catch narrows error-recording scope to the provider
|
|
755
801
|
// call itself. The outer agent-loop catch (below) wraps the entire
|
|
756
802
|
// turn body (tool execution, plugin pipelines, checkpoints), so
|
|
@@ -862,6 +908,40 @@ export class AgentLoop {
|
|
|
862
908
|
"LLM call complete",
|
|
863
909
|
);
|
|
864
910
|
|
|
911
|
+
if (isMaxTokensStopReason(response.stopReason)) {
|
|
912
|
+
const safeContent = response.content.filter(
|
|
913
|
+
(block) =>
|
|
914
|
+
block.type !== "tool_use" &&
|
|
915
|
+
block.type !== "server_tool_use" &&
|
|
916
|
+
block.type !== "web_search_tool_result",
|
|
917
|
+
);
|
|
918
|
+
const safeAssistantMessage: Message = {
|
|
919
|
+
role: "assistant",
|
|
920
|
+
content: safeContent,
|
|
921
|
+
};
|
|
922
|
+
rlog.warn(
|
|
923
|
+
{
|
|
924
|
+
turn: toolUseTurns,
|
|
925
|
+
stopReason: response.stopReason,
|
|
926
|
+
contentBlocks: response.content.length,
|
|
927
|
+
safeContentBlocks: safeContent.length,
|
|
928
|
+
toolUseCount: toolUseBlocks.length,
|
|
929
|
+
},
|
|
930
|
+
"LLM response reached output token limit",
|
|
931
|
+
);
|
|
932
|
+
history.push(safeAssistantMessage);
|
|
933
|
+
await onEvent({
|
|
934
|
+
type: "max_tokens_reached",
|
|
935
|
+
stopReason: response.stopReason,
|
|
936
|
+
});
|
|
937
|
+
await onEvent({
|
|
938
|
+
type: "message_complete",
|
|
939
|
+
message: safeAssistantMessage,
|
|
940
|
+
});
|
|
941
|
+
await emitExit("max_tokens_reached");
|
|
942
|
+
break;
|
|
943
|
+
}
|
|
944
|
+
|
|
865
945
|
// Detect empty responses: no user-visible text and no tool calls.
|
|
866
946
|
// This can happen when the model fails to produce output after
|
|
867
947
|
// receiving a large tool result. Retry once with a nudge before
|