@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
|
@@ -100,7 +100,7 @@ Use `notify` for simple reminders ("remind me to take medicine at 9am"), `execut
|
|
|
100
100
|
|
|
101
101
|
## Conversation Reuse
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
Recurring schedules reuse the same conversation across runs by default — subsequent runs continue the conversation from the last successful run, preserving context and channel thread continuity. Set `reuse_conversation: false` explicitly if each run should start with a fresh conversation (e.g. independent reports that shouldn't accumulate prior context). One-shot schedules always create a fresh conversation.
|
|
104
104
|
|
|
105
105
|
- Only applies to **recurring** schedules; ignored for one-shot schedules.
|
|
106
106
|
- If the prior conversation has been deleted, a new one is created automatically.
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
},
|
|
63
63
|
"reuse_conversation": {
|
|
64
64
|
"type": "boolean",
|
|
65
|
-
"description": "When true, reuse the same conversation across recurring schedule runs instead of creating a new one each time.
|
|
65
|
+
"description": "When true, reuse the same conversation across recurring schedule runs instead of creating a new one each time. Defaults to true for recurring schedules, false for one-shot. Set to false explicitly if each run should start fresh."
|
|
66
66
|
},
|
|
67
67
|
"max_retries": {
|
|
68
68
|
"type": "integer",
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
},
|
|
162
162
|
"reuse_conversation": {
|
|
163
163
|
"type": "boolean",
|
|
164
|
-
"description": "When true, reuse the same conversation across recurring schedule runs instead of creating a new one each time.
|
|
164
|
+
"description": "When true, reuse the same conversation across recurring schedule runs instead of creating a new one each time. Defaults to true for recurring schedules. Set to false explicitly if each run should start fresh."
|
|
165
165
|
},
|
|
166
166
|
"max_retries": {
|
|
167
167
|
"type": "integer",
|
|
@@ -23,7 +23,7 @@ export const CALL_SITE_DEFAULTS: Record<LLMCallSite, CallSiteDefaultConfig> = {
|
|
|
23
23
|
emptyStateGreeting: { profile: "balanced" },
|
|
24
24
|
|
|
25
25
|
memoryRouter: {
|
|
26
|
-
profile: "
|
|
26
|
+
profile: "cost-optimized",
|
|
27
27
|
contextWindow: { maxInputTokens: 1000000 },
|
|
28
28
|
},
|
|
29
29
|
recall: {
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-level cache for resolved feature flag override values.
|
|
3
|
+
*
|
|
4
|
+
* Lives in its own module (rather than alongside the resolver in
|
|
5
|
+
* `assistant-feature-flags.ts`) so test code can read/write the cache
|
|
6
|
+
* without going through `assistant-feature-flags.ts` — which transitively
|
|
7
|
+
* pulls `util/logger.js` (pino) and the gateway IPC client. Stdlib-only
|
|
8
|
+
* by design: this file must remain safe to import from the test
|
|
9
|
+
* preload's load-time chain, where a broken `node_modules` symlink has
|
|
10
|
+
* historically tripped the env override (see DB ghost #3,
|
|
11
|
+
* /workspace/journal/2026-05-25-db-ghost-3-recovery.md).
|
|
12
|
+
*
|
|
13
|
+
* State is held on `globalThis.vellumAssistant.featureFlagCache` so test
|
|
14
|
+
* helpers in `__tests__/` can read/write it WITHOUT importing this
|
|
15
|
+
* module — they declare the same slot shape locally and access the
|
|
16
|
+
* globalThis namespace directly. See
|
|
17
|
+
* `__tests__/feature-flag-test-helpers.ts` for the test-side mirror;
|
|
18
|
+
* the slot shape MUST stay in sync between the two.
|
|
19
|
+
*
|
|
20
|
+
* Both `overrides` and `fromGateway` were previously module-level `let`
|
|
21
|
+
* bindings inside `assistant-feature-flags.ts`. The semantics are
|
|
22
|
+
* preserved exactly: `overrides === null` means "no fetch has populated
|
|
23
|
+
* the cache yet"; `fromGateway === true` means "the cache is
|
|
24
|
+
* authoritative — `initFeatureFlagOverrides()` should not clobber it".
|
|
25
|
+
*
|
|
26
|
+
* Consumers:
|
|
27
|
+
* - `assistant-feature-flags.ts` (resolver — reads/writes via gateway fetch)
|
|
28
|
+
* - `__tests__/feature-flag-test-helpers.ts` (seeds for tests, via globalThis)
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
type FlagSlot = {
|
|
32
|
+
overrides: Record<string, boolean> | null;
|
|
33
|
+
fromGateway: boolean;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type VellumAssistantNamespace = {
|
|
37
|
+
featureFlagCache?: FlagSlot;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
function slot(): FlagSlot {
|
|
41
|
+
const g = globalThis as { vellumAssistant?: VellumAssistantNamespace };
|
|
42
|
+
const ns = (g.vellumAssistant ??= {});
|
|
43
|
+
return (ns.featureFlagCache ??= { overrides: null, fromGateway: false });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Read the current override cache. `null` means not yet populated. */
|
|
47
|
+
export function getCachedOverrides(): Record<string, boolean> | null {
|
|
48
|
+
return slot().overrides;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* True when the cache was populated by either a gateway IPC fetch or by a
|
|
53
|
+
* test helper. Used by `initFeatureFlagOverrides()` to short-circuit a
|
|
54
|
+
* second fetch (e.g. when a CLI entry point runs after the daemon has
|
|
55
|
+
* already initialized) and by tests to prevent the retry loop from
|
|
56
|
+
* clobbering preseeded state.
|
|
57
|
+
*/
|
|
58
|
+
export function isCachedFromGateway(): boolean {
|
|
59
|
+
return slot().fromGateway;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Replace the cache with a clone of `overrides`. The `fromGateway` flag
|
|
64
|
+
* is set by the caller — production callers pass `true` after a
|
|
65
|
+
* successful gateway fetch; test helpers also pass `true` so subsequent
|
|
66
|
+
* `initFeatureFlagOverrides()` calls are no-ops.
|
|
67
|
+
*/
|
|
68
|
+
export function setCachedOverrides(
|
|
69
|
+
overrides: Record<string, boolean>,
|
|
70
|
+
options: { fromGateway: boolean },
|
|
71
|
+
): void {
|
|
72
|
+
const s = slot();
|
|
73
|
+
s.overrides = { ...overrides };
|
|
74
|
+
s.fromGateway = options.fromGateway;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Drop the cache. The next `loadOverrides()` returns an empty record (so
|
|
79
|
+
* flag checks fall through to registry defaults) and the next
|
|
80
|
+
* `initFeatureFlagOverrides()` re-fetches from the gateway.
|
|
81
|
+
*/
|
|
82
|
+
export function clearCachedOverrides(): void {
|
|
83
|
+
const s = slot();
|
|
84
|
+
s.overrides = null;
|
|
85
|
+
s.fromGateway = false;
|
|
86
|
+
}
|
|
@@ -25,6 +25,14 @@
|
|
|
25
25
|
"description": "Enable user-hosted onboarding flow",
|
|
26
26
|
"defaultEnabled": false
|
|
27
27
|
},
|
|
28
|
+
{
|
|
29
|
+
"id": "prechat-onboarding-condensed-flow",
|
|
30
|
+
"scope": "client",
|
|
31
|
+
"key": "prechat-onboarding-condensed-flow",
|
|
32
|
+
"label": "Condensed Pre-chat Onboarding",
|
|
33
|
+
"description": "Enable the condensed pre-chat onboarding flow for a standard LaunchDarkly percentage rollout.",
|
|
34
|
+
"defaultEnabled": false
|
|
35
|
+
},
|
|
28
36
|
{
|
|
29
37
|
"id": "local-docker-enabled",
|
|
30
38
|
"scope": "client",
|
|
@@ -190,7 +198,7 @@
|
|
|
190
198
|
"scope": "assistant",
|
|
191
199
|
"key": "fast-mode",
|
|
192
200
|
"label": "Fast Mode",
|
|
193
|
-
"description": "Enable Anthropic fast mode for Opus models (4.6, 4.7), delivering up to 2.5x higher output tokens per second at premium pricing",
|
|
201
|
+
"description": "Enable Anthropic fast mode for Opus models (4.6, 4.7, 4.8), delivering up to 2.5x higher output tokens per second at premium pricing",
|
|
194
202
|
"defaultEnabled": false
|
|
195
203
|
},
|
|
196
204
|
{
|
|
@@ -281,14 +289,6 @@
|
|
|
281
289
|
"description": "Expose the developer-only Compaction Playground tab in macOS Settings and enable the /playground/* HTTP endpoints for exercising compaction conditions. Dev-only; default off.",
|
|
282
290
|
"defaultEnabled": false
|
|
283
291
|
},
|
|
284
|
-
{
|
|
285
|
-
"id": "safe-storage-limits",
|
|
286
|
-
"scope": "assistant",
|
|
287
|
-
"key": "safe-storage-limits",
|
|
288
|
-
"label": "Safe Storage Limits",
|
|
289
|
-
"description": "Enable disk pressure protection flows that block background work and remote actors while storage is critically low.",
|
|
290
|
-
"defaultEnabled": false
|
|
291
|
-
},
|
|
292
292
|
{
|
|
293
293
|
"id": "account-deletion",
|
|
294
294
|
"scope": "assistant",
|
|
@@ -313,14 +313,6 @@
|
|
|
313
313
|
"description": "Show the 'Analyze' / 'Analyze conversation' option in conversation context menus and the conversation title actions dropdown.",
|
|
314
314
|
"defaultEnabled": false
|
|
315
315
|
},
|
|
316
|
-
{
|
|
317
|
-
"id": "pro-plan-adjust",
|
|
318
|
-
"scope": "client",
|
|
319
|
-
"key": "pro-plan-adjust",
|
|
320
|
-
"label": "Pro Plan Adjust",
|
|
321
|
-
"description": "Show the rich Plan card (current plan, features, Manage/Upgrade CTA) at the top of the macOS Settings \u2192 Billing tab.",
|
|
322
|
-
"defaultEnabled": false
|
|
323
|
-
},
|
|
324
316
|
{
|
|
325
317
|
"id": "external-plugins",
|
|
326
318
|
"scope": "assistant",
|
|
@@ -440,6 +432,14 @@
|
|
|
440
432
|
"label": "Memory Router Playground",
|
|
441
433
|
"description": "Expose the developer-only Memory Router Playground tab in macOS Settings and the /assistant/memory-router-playground web page for dry-running v4 router config overrides against the live page index. Dev-only; default off.",
|
|
442
434
|
"defaultEnabled": false
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
"id": "platform-features-in-local-mode",
|
|
438
|
+
"scope": "assistant",
|
|
439
|
+
"key": "platform-features-in-local-mode",
|
|
440
|
+
"label": "Platform Features in Local Mode",
|
|
441
|
+
"description": "When enabled, the assistant can call the Vellum platform API from local mode. When disabled, all platform API clients in the daemon, gateway, CES, and web UI no-op with a debug log instead of making outbound requests.",
|
|
442
|
+
"defaultEnabled": true
|
|
443
443
|
}
|
|
444
444
|
]
|
|
445
445
|
}
|
|
@@ -27,12 +27,21 @@ export function resolveEffectiveContextWindow({
|
|
|
27
27
|
llm,
|
|
28
28
|
callSite,
|
|
29
29
|
overrideProfile,
|
|
30
|
+
selectionSeed,
|
|
30
31
|
}: {
|
|
31
32
|
llm: LLMConfig;
|
|
32
33
|
callSite: LLMCallSite;
|
|
33
34
|
overrideProfile?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Per-conversation mix seed (the conversation id). Threaded so context-window
|
|
37
|
+
* sizing for a mix profile reflects the same arm the dispatch path picks.
|
|
38
|
+
*/
|
|
39
|
+
selectionSeed?: string;
|
|
34
40
|
}): EffectiveContextWindow {
|
|
35
|
-
const resolved = resolveCallSiteConfig(callSite, llm, {
|
|
41
|
+
const resolved = resolveCallSiteConfig(callSite, llm, {
|
|
42
|
+
overrideProfile,
|
|
43
|
+
selectionSeed,
|
|
44
|
+
});
|
|
36
45
|
const catalogModel = PROVIDER_CATALOG.find(
|
|
37
46
|
(provider) => provider.id === resolved.provider,
|
|
38
47
|
)?.models.find((model) => model.id === resolved.model);
|
|
@@ -42,33 +42,60 @@ import {
|
|
|
42
42
|
* resolver stays pure; schema validation in `LLMSchema.superRefine` catches
|
|
43
43
|
* unknown `activeProfile` references at config-load time.
|
|
44
44
|
*
|
|
45
|
-
*
|
|
45
|
+
* A profile reference that points at a "mix" profile is expanded to one of its
|
|
46
|
+
* constituent profiles by a seeded weighted pick (see `resolveProfileFragment`
|
|
47
|
+
* and `opts.selectionSeed`). Expansion happens uniformly at every dereference
|
|
48
|
+
* spot, so a mix works as `activeProfile`, `overrideProfile`, or a call-site
|
|
49
|
+
* `profile`.
|
|
50
|
+
*
|
|
51
|
+
* Pure & synchronous: no I/O, no async work. (Random selection only occurs for
|
|
52
|
+
* mix profiles when no `selectionSeed` is supplied; with a seed the pick is
|
|
53
|
+
* deterministic.)
|
|
46
54
|
*/
|
|
55
|
+
export interface ResolveCallSiteOpts {
|
|
56
|
+
overrideProfile?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Per-conversation seed for expanding `mix` profiles. The chosen constituent
|
|
59
|
+
* is a deterministic function of `selectionSeed` + the mix profile's own
|
|
60
|
+
* name, so every `resolveCallSiteConfig` call for the same conversation picks
|
|
61
|
+
* the SAME arm (stable across turns, retries, and restarts). Pass the
|
|
62
|
+
* conversation id. When absent, the resolver falls back to a fresh random
|
|
63
|
+
* pick per call — acceptable only for one-shot/background call sites that
|
|
64
|
+
* resolve config exactly once per invocation.
|
|
65
|
+
*/
|
|
66
|
+
selectionSeed?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Invoked once for each mix profile the resolver expands, reporting which
|
|
69
|
+
* constituent was chosen. Used by A/B-eval recording (usage attribution).
|
|
70
|
+
*/
|
|
71
|
+
onMixSelected?: (info: { mixProfile: string; chosenProfile: string }) => void;
|
|
72
|
+
}
|
|
73
|
+
|
|
47
74
|
export function resolveCallSiteConfig(
|
|
48
75
|
callSite: LLMCallSite,
|
|
49
76
|
llm: z.infer<typeof LLMSchema>,
|
|
50
|
-
opts:
|
|
77
|
+
opts: ResolveCallSiteOpts = {},
|
|
51
78
|
): z.infer<typeof LLMConfigBase> {
|
|
52
79
|
const layers: Mergeable[] = [llm.default as Mergeable];
|
|
53
80
|
|
|
54
|
-
const activeFragment =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
81
|
+
const activeFragment = resolveProfileFragment(llm.activeProfile, llm, opts);
|
|
82
|
+
const overrideFragment = resolveProfileFragment(
|
|
83
|
+
opts.overrideProfile,
|
|
84
|
+
llm,
|
|
85
|
+
opts,
|
|
86
|
+
);
|
|
60
87
|
const site =
|
|
61
88
|
llm.callSites?.[callSite] ??
|
|
62
89
|
effectiveDefault(callSite, llm, opts.overrideProfile != null);
|
|
63
90
|
|
|
64
91
|
if (callSite === "mainAgent") {
|
|
65
|
-
appendCallSiteLayers(layers, callSite, llm, site);
|
|
92
|
+
appendCallSiteLayers(layers, callSite, llm, site, opts);
|
|
66
93
|
appendProfileLayer(layers, activeFragment);
|
|
67
94
|
appendProfileLayer(layers, overrideFragment);
|
|
68
95
|
} else {
|
|
69
96
|
appendProfileLayer(layers, activeFragment);
|
|
70
97
|
appendProfileLayer(layers, overrideFragment);
|
|
71
|
-
appendCallSiteLayers(layers, callSite, llm, site);
|
|
98
|
+
appendCallSiteLayers(layers, callSite, llm, site, opts);
|
|
72
99
|
}
|
|
73
100
|
|
|
74
101
|
return finalize(deepMerge(...layers.map(withImpliedProviderForKnownModel)));
|
|
@@ -80,6 +107,81 @@ export function resolveCallSiteConfig(
|
|
|
80
107
|
|
|
81
108
|
type Mergeable = Record<string, unknown>;
|
|
82
109
|
|
|
110
|
+
/**
|
|
111
|
+
* FNV-1a 32-bit string hash → unit float in [0, 1). Deterministic and stable
|
|
112
|
+
* across runtimes — the mix-pick contract depends on identical output for
|
|
113
|
+
* identical input forever, so the constants must never change. (Mirrors the
|
|
114
|
+
* private hash in `memory/v2/page-index.ts`; intentionally re-declared here so
|
|
115
|
+
* the resolver's determinism contract is self-contained and cannot be broken
|
|
116
|
+
* by an unrelated edit to that module.)
|
|
117
|
+
*/
|
|
118
|
+
function seededUnitFloat(seed: string): number {
|
|
119
|
+
let h = 0x811c9dc5;
|
|
120
|
+
for (let i = 0; i < seed.length; i++) {
|
|
121
|
+
h ^= seed.charCodeAt(i);
|
|
122
|
+
h = Math.imul(h, 0x01000193);
|
|
123
|
+
}
|
|
124
|
+
// `>>> 0` → unsigned 32-bit; divide by 2^32 to land in [0, 1).
|
|
125
|
+
return (h >>> 0) / 0x100000000;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Pick one entry from a weighted list given a unit float in [0, 1). Weights
|
|
130
|
+
* are relative and normalized by their sum. Assumes `entries` is non-empty
|
|
131
|
+
* with positive weights (guaranteed by `MixSchema`: `.min(2)` + positive).
|
|
132
|
+
*/
|
|
133
|
+
function weightedPick<T extends { weight: number }>(
|
|
134
|
+
entries: readonly T[],
|
|
135
|
+
unit: number,
|
|
136
|
+
): T {
|
|
137
|
+
const total = entries.reduce((sum, e) => sum + e.weight, 0);
|
|
138
|
+
// Defensive: a degenerate total (unreachable post-schema) → first arm.
|
|
139
|
+
if (!(total > 0)) return entries[0];
|
|
140
|
+
let threshold = unit * total;
|
|
141
|
+
for (const entry of entries) {
|
|
142
|
+
threshold -= entry.weight;
|
|
143
|
+
if (threshold < 0) return entry;
|
|
144
|
+
}
|
|
145
|
+
// Floating-point fall-through (unit ≈ 1): return the last arm.
|
|
146
|
+
return entries[entries.length - 1];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Dereference a profile name to its concrete `ProfileEntry`, expanding a mix
|
|
151
|
+
* profile by a seeded weighted pick. Returns `undefined` when the name is
|
|
152
|
+
* unknown (parity with the silent fall-through callers already rely on).
|
|
153
|
+
*
|
|
154
|
+
* Mix expansion is one level only — `LLMSchema.superRefine` guarantees arms
|
|
155
|
+
* are standard (non-mix) profiles, so this never recurses unboundedly. A
|
|
156
|
+
* chosen arm pointing at a missing profile (only reachable in hand-crafted,
|
|
157
|
+
* unparsed configs) falls through to `undefined`.
|
|
158
|
+
*
|
|
159
|
+
* Pure & synchronous (the only impurity is `Math.random()` in the no-seed
|
|
160
|
+
* fallback path).
|
|
161
|
+
*/
|
|
162
|
+
function resolveProfileFragment(
|
|
163
|
+
name: string | undefined,
|
|
164
|
+
llm: z.infer<typeof LLMSchema>,
|
|
165
|
+
opts: ResolveCallSiteOpts,
|
|
166
|
+
): ProfileEntry | undefined {
|
|
167
|
+
if (name == null) return undefined;
|
|
168
|
+
const entry = llm.profiles?.[name];
|
|
169
|
+
if (entry?.mix == null) return entry;
|
|
170
|
+
|
|
171
|
+
// Mix: pick one constituent. Seed by per-conversation seed + the mix's own
|
|
172
|
+
// name so two different mixes in the same conversation pick independently,
|
|
173
|
+
// but the same mix always resolves to the same arm within the conversation.
|
|
174
|
+
const unit =
|
|
175
|
+
opts.selectionSeed != null
|
|
176
|
+
? seededUnitFloat(`${opts.selectionSeed}\u0000${name}`)
|
|
177
|
+
: Math.random();
|
|
178
|
+
const chosen = weightedPick(entry.mix, unit);
|
|
179
|
+
opts.onMixSelected?.({ mixProfile: name, chosenProfile: chosen.profile });
|
|
180
|
+
|
|
181
|
+
// The chosen arm must be a standard profile (enforced by superRefine).
|
|
182
|
+
return llm.profiles?.[chosen.profile];
|
|
183
|
+
}
|
|
184
|
+
|
|
83
185
|
/**
|
|
84
186
|
* Returns the effective default profile key the resolver would actually
|
|
85
187
|
* select for a call site when no per-turn `overrideProfile` is supplied.
|
|
@@ -170,16 +272,16 @@ function appendCallSiteLayers(
|
|
|
170
272
|
callSite: LLMCallSite,
|
|
171
273
|
llm: z.infer<typeof LLMSchema>,
|
|
172
274
|
site: z.infer<typeof LLMSchema>["callSites"][LLMCallSite] | undefined,
|
|
275
|
+
opts: ResolveCallSiteOpts,
|
|
173
276
|
): void {
|
|
174
277
|
if (site != null) {
|
|
175
278
|
if (site.profile != null) {
|
|
176
|
-
const profileFragment
|
|
177
|
-
llm.profiles?.[site.profile];
|
|
279
|
+
const profileFragment = resolveProfileFragment(site.profile, llm, opts);
|
|
178
280
|
if (profileFragment == null) {
|
|
179
281
|
// Defensive: `LLMSchema.superRefine` already rejects unknown profile
|
|
180
|
-
// references at config load, so this branch is
|
|
181
|
-
// config that survived schema validation. Throw a
|
|
182
|
-
// a hand-crafted (un-parsed) config slips through.
|
|
282
|
+
// references (and unknown mix arms) at config load, so this branch is
|
|
283
|
+
// unreachable for any config that survived schema validation. Throw a
|
|
284
|
+
// clear error in case a hand-crafted (un-parsed) config slips through.
|
|
183
285
|
throw new Error(
|
|
184
286
|
`LLM call site "${callSite}" references undefined profile "${site.profile}"`,
|
|
185
287
|
);
|
|
@@ -198,6 +300,10 @@ function profileConfigFragment(profile: ProfileEntry): Mergeable {
|
|
|
198
300
|
source: _source,
|
|
199
301
|
label: _label,
|
|
200
302
|
description: _description,
|
|
303
|
+
// `mix` never reaches here in practice (a mix expands to a standard
|
|
304
|
+
// profile before this point), but strip it defensively so it can never
|
|
305
|
+
// leak into the merged `LLMConfigBase`.
|
|
306
|
+
mix: _mix,
|
|
201
307
|
...config
|
|
202
308
|
} = profile;
|
|
203
309
|
return config as Mergeable;
|
package/src/config/loader.ts
CHANGED
|
@@ -122,6 +122,9 @@ export function getDeploymentContextDefaults(): Record<string, unknown> {
|
|
|
122
122
|
if (process.env.IS_PLATFORM !== "true" && process.env.IS_PLATFORM !== "1") {
|
|
123
123
|
return {};
|
|
124
124
|
}
|
|
125
|
+
// `web-search.mode = managed` enables platform-backed app-executed search
|
|
126
|
+
// for non-native inference providers while preserving provider-native hosted
|
|
127
|
+
// search for providers/models that support it.
|
|
125
128
|
const managed = { mode: "managed" as const };
|
|
126
129
|
return {
|
|
127
130
|
services: {
|
|
@@ -161,11 +164,7 @@ export function fillContextDefaultsForMissingKeys(
|
|
|
161
164
|
): void {
|
|
162
165
|
for (const [key, value] of Object.entries(contextDefaults)) {
|
|
163
166
|
const fileVal = fileConfig[key];
|
|
164
|
-
if (
|
|
165
|
-
value !== null &&
|
|
166
|
-
typeof value === "object" &&
|
|
167
|
-
!Array.isArray(value)
|
|
168
|
-
) {
|
|
167
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
169
168
|
const targetChild = readPlainObject(target[key]);
|
|
170
169
|
const fileChild = readPlainObject(fileVal);
|
|
171
170
|
if (targetChild) {
|
|
@@ -224,6 +224,7 @@ describe("MemoryV3ConfigSchema", () => {
|
|
|
224
224
|
denseQuota: { activeDomain: 30, offDomain: 8 },
|
|
225
225
|
hotLimit: 50,
|
|
226
226
|
lanes: { hot: true, sparse: true, dense: true, tree: true, edges: true },
|
|
227
|
+
edges: { learnedAdjacencyThreshold: 0, maxPulls: 400 },
|
|
227
228
|
ks: [5, 10, 25, 50],
|
|
228
229
|
write: {
|
|
229
230
|
enabled: false,
|
|
@@ -272,6 +273,20 @@ describe("MemoryV3ConfigSchema", () => {
|
|
|
272
273
|
expect(parsed.denseQuota).toEqual({ activeDomain: 50, offDomain: 12 });
|
|
273
274
|
});
|
|
274
275
|
|
|
276
|
+
test("accepts a partial denseQuota override and defaults the rest", () => {
|
|
277
|
+
// Overriding only one leaf must merge with defaults, not fail validation
|
|
278
|
+
// (which would discard the entire user config).
|
|
279
|
+
const onlyActive = MemoryV3ConfigSchema.parse({
|
|
280
|
+
denseQuota: { activeDomain: 50 },
|
|
281
|
+
});
|
|
282
|
+
expect(onlyActive.denseQuota).toEqual({ activeDomain: 50, offDomain: 8 });
|
|
283
|
+
|
|
284
|
+
const onlyOff = MemoryV3ConfigSchema.parse({
|
|
285
|
+
denseQuota: { offDomain: 12 },
|
|
286
|
+
});
|
|
287
|
+
expect(onlyOff.denseQuota).toEqual({ activeDomain: 30, offDomain: 12 });
|
|
288
|
+
});
|
|
289
|
+
|
|
275
290
|
test("accepts a partial lanes override and defaults the rest", () => {
|
|
276
291
|
const parsed = MemoryV3ConfigSchema.parse({ lanes: { dense: false } });
|
|
277
292
|
expect(parsed.lanes).toEqual({
|
|
@@ -11,7 +11,7 @@ export const HeartbeatConfigSchema = z
|
|
|
11
11
|
.number({ error: "heartbeat.intervalMs must be a number" })
|
|
12
12
|
.int("heartbeat.intervalMs must be an integer")
|
|
13
13
|
.positive("heartbeat.intervalMs must be a positive integer")
|
|
14
|
-
.default(
|
|
14
|
+
.default(60 * 60_000)
|
|
15
15
|
.describe("Time between heartbeat checks in milliseconds"),
|
|
16
16
|
cronExpression: z
|
|
17
17
|
.string()
|
|
@@ -309,7 +309,7 @@ export const LLMConfigBase = z.object({
|
|
|
309
309
|
* naturally — the underlying profile-level field is on `ProfileEntry`.
|
|
310
310
|
*/
|
|
311
311
|
provider_connection: z.string().min(1).optional(),
|
|
312
|
-
model: ModelSchema.default("claude-opus-4-
|
|
312
|
+
model: ModelSchema.default("claude-opus-4-8"),
|
|
313
313
|
maxTokens: MaxTokensSchema.default(64000),
|
|
314
314
|
effort: EffortEnum.default("max"),
|
|
315
315
|
speed: SpeedEnum.default("standard"),
|
|
@@ -344,6 +344,27 @@ type LLMConfigFragment = z.infer<typeof LLMConfigFragment>;
|
|
|
344
344
|
export const ProfileStatusSchema = z.enum(["active", "disabled"]);
|
|
345
345
|
export type ProfileStatus = z.infer<typeof ProfileStatusSchema>;
|
|
346
346
|
|
|
347
|
+
// ---------------------------------------------------------------------------
|
|
348
|
+
// Mix profiles
|
|
349
|
+
//
|
|
350
|
+
// A "mix" profile carries no model config of its own. Instead it references a
|
|
351
|
+
// weighted list of other (standard) profiles; at resolve time exactly one
|
|
352
|
+
// constituent is chosen by weight. The pick is a deterministic function of a
|
|
353
|
+
// per-conversation seed (the conversation id — see `resolveCallSiteConfig`'s
|
|
354
|
+
// `selectionSeed`), so a conversation always lands on the same arm across all
|
|
355
|
+
// its turns, retries, and even daemon restarts, while different conversations
|
|
356
|
+
// split according to the weights — and the chosen arm is recordable for A/B
|
|
357
|
+
// evaluation. Weights are relative (normalized by their sum at pick time), so
|
|
358
|
+
// `[{weight:80},{weight:20}]` and `[{weight:4},{weight:1}]` are equivalent.
|
|
359
|
+
// ---------------------------------------------------------------------------
|
|
360
|
+
const MixArmSchema = z.object({
|
|
361
|
+
profile: z.string().min(1),
|
|
362
|
+
weight: z.number().finite().positive(),
|
|
363
|
+
});
|
|
364
|
+
export type MixArm = z.infer<typeof MixArmSchema>;
|
|
365
|
+
|
|
366
|
+
const MixSchema = z.array(MixArmSchema).min(2);
|
|
367
|
+
|
|
347
368
|
/**
|
|
348
369
|
* A named profile entry: an `LLMConfigFragment` augmented with
|
|
349
370
|
* presentation/ownership metadata. These fields are intentionally kept off
|
|
@@ -379,6 +400,17 @@ export const ProfileEntry = LLMConfigFragment.extend({
|
|
|
379
400
|
* #30362 even though the schema didn't accept it until now.
|
|
380
401
|
*/
|
|
381
402
|
status: ProfileStatusSchema.nullable().optional(),
|
|
403
|
+
/**
|
|
404
|
+
* When present, this profile is a "mix": it carries no model config and
|
|
405
|
+
* instead references a weighted list of standard profiles. The resolver
|
|
406
|
+
* expands a mix by a seeded weighted pick (see `resolveCallSiteConfig`).
|
|
407
|
+
* `LLMSchema.superRefine` enforces that (a) every referenced profile exists,
|
|
408
|
+
* (b) no referenced profile is itself a mix (no nesting), (c) no arm
|
|
409
|
+
* references the mix itself, and (d) a mix carries no `LLMConfigFragment`
|
|
410
|
+
* config field — only metadata (`label`, `description`, `status`, `source`)
|
|
411
|
+
* may accompany `mix`.
|
|
412
|
+
*/
|
|
413
|
+
mix: MixSchema.optional(),
|
|
382
414
|
});
|
|
383
415
|
export type ProfileEntry = z.infer<typeof ProfileEntry>;
|
|
384
416
|
|
|
@@ -444,6 +476,63 @@ export const LLMSchema = z
|
|
|
444
476
|
message: `Profile "${config.activeProfile}" referenced by llm.activeProfile is not defined in llm.profiles`,
|
|
445
477
|
});
|
|
446
478
|
}
|
|
479
|
+
|
|
480
|
+
// --- Mix profile validation --------------------------------------------
|
|
481
|
+
// Config keys a mix profile must NOT also set (a mix only references other
|
|
482
|
+
// profiles + metadata). Derived from the fragment shape plus the
|
|
483
|
+
// ProfileEntry-only `provider_connection` so it can't drift if a new config
|
|
484
|
+
// field is added to `LLMConfigFragment`.
|
|
485
|
+
const MIX_DISALLOWED_CONFIG_KEYS = [
|
|
486
|
+
...Object.keys(LLMConfigFragment.shape),
|
|
487
|
+
"provider_connection",
|
|
488
|
+
];
|
|
489
|
+
const mixProfileNames = new Set(
|
|
490
|
+
Object.entries(config.profiles ?? {})
|
|
491
|
+
.filter(([, profile]) => profile?.mix != null)
|
|
492
|
+
.map(([name]) => name),
|
|
493
|
+
);
|
|
494
|
+
for (const [name, profile] of Object.entries(config.profiles ?? {})) {
|
|
495
|
+
if (profile?.mix == null) continue;
|
|
496
|
+
// (d) A mix must not also carry model config — the resolved config comes
|
|
497
|
+
// entirely from the chosen constituent.
|
|
498
|
+
for (const key of MIX_DISALLOWED_CONFIG_KEYS) {
|
|
499
|
+
if ((profile as Record<string, unknown>)[key] !== undefined) {
|
|
500
|
+
ctx.addIssue({
|
|
501
|
+
code: "custom",
|
|
502
|
+
path: ["profiles", name, key],
|
|
503
|
+
message: `Mix profile "${name}" cannot also set "${key}" — a mix only references other profiles plus metadata (label, description, status).`,
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
for (const [index, arm] of profile.mix.entries()) {
|
|
508
|
+
// (c) No self-reference.
|
|
509
|
+
if (arm.profile === name) {
|
|
510
|
+
ctx.addIssue({
|
|
511
|
+
code: "custom",
|
|
512
|
+
path: ["profiles", name, "mix", index, "profile"],
|
|
513
|
+
message: `Mix profile "${name}" cannot reference itself.`,
|
|
514
|
+
});
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
// (a) Referenced profile must exist.
|
|
518
|
+
if (!profileNames.has(arm.profile)) {
|
|
519
|
+
ctx.addIssue({
|
|
520
|
+
code: "custom",
|
|
521
|
+
path: ["profiles", name, "mix", index, "profile"],
|
|
522
|
+
message: `Mix profile "${name}" references profile "${arm.profile}" which is not defined in llm.profiles.`,
|
|
523
|
+
});
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
// (b) No nesting — a mix arm must be a standard (non-mix) profile.
|
|
527
|
+
if (mixProfileNames.has(arm.profile)) {
|
|
528
|
+
ctx.addIssue({
|
|
529
|
+
code: "custom",
|
|
530
|
+
path: ["profiles", name, "mix", index, "profile"],
|
|
531
|
+
message: `Mix profile "${name}" references another mix profile "${arm.profile}" — mixes cannot be nested; constituents must be standard profiles.`,
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
447
536
|
});
|
|
448
537
|
|
|
449
538
|
export type LLMConfig = z.infer<typeof LLMSchema>;
|
|
@@ -466,11 +466,13 @@ export const MemoryV3ConfigSchema = z
|
|
|
466
466
|
.number({
|
|
467
467
|
error: "memory.v3.denseQuota.activeDomain must be a number",
|
|
468
468
|
})
|
|
469
|
+
.default(30)
|
|
469
470
|
.describe(
|
|
470
471
|
"Dense-lane candidate quota allocated to the conversation's active domain.",
|
|
471
472
|
),
|
|
472
473
|
offDomain: z
|
|
473
474
|
.number({ error: "memory.v3.denseQuota.offDomain must be a number" })
|
|
475
|
+
.default(8)
|
|
474
476
|
.describe(
|
|
475
477
|
"Dense-lane candidate quota allocated to off-domain (exploratory) retrieval.",
|
|
476
478
|
),
|
|
@@ -520,6 +522,29 @@ export const MemoryV3ConfigSchema = z
|
|
|
520
522
|
.describe(
|
|
521
523
|
"Per-lane on/off toggles for the v3 multi-lane retrieval fanout. All lanes on by default.",
|
|
522
524
|
),
|
|
525
|
+
edges: z
|
|
526
|
+
.object({
|
|
527
|
+
learnedAdjacencyThreshold: z
|
|
528
|
+
.number({
|
|
529
|
+
error: "memory.v3.edges.learnedAdjacencyThreshold must be a number",
|
|
530
|
+
})
|
|
531
|
+
.min(0, "memory.v3.edges.learnedAdjacencyThreshold must be >= 0")
|
|
532
|
+
.default(0)
|
|
533
|
+
.describe(
|
|
534
|
+
"Association-strength cutoff for merging the learned co-retrieval graph (memory_v3_auto_edges) into the edge-expansion lane. Seeded edges are weighted seedWeight × NPMI, so this is effectively a minimum-NPMI gate: NPMI ≥ threshold / seedWeight (e.g. with the default seedWeight 2.0, threshold 1.0 ≈ NPMI ≥ 0.5, 1.2 ≈ NPMI ≥ 0.6). When > 0, edges at or above this weight are read via aboveThreshold() and merged with the curated frontmatter graph as expandEdges' extraAdjacency. 0 (default) = OFF: the edge lane uses curated edges only and behavior is unchanged. Seed the learned graph with `assistant memory v3 seed-edges`.",
|
|
535
|
+
),
|
|
536
|
+
maxPulls: z
|
|
537
|
+
.number({ error: "memory.v3.edges.maxPulls must be a number" })
|
|
538
|
+
.min(0, "memory.v3.edges.maxPulls must be >= 0")
|
|
539
|
+
.default(400)
|
|
540
|
+
.describe(
|
|
541
|
+
"Hard cap on the edge lane's contribution to the gate's candidate pool (the unioned 1–2 hop curated∪learned neighborhood). The shipped default 400 lets a dense curated graph dominate the pool and drown the gate's recall of high-precision hits; lower values (~40) keep the lane focused. 0 effectively disables edge pulls.",
|
|
542
|
+
),
|
|
543
|
+
})
|
|
544
|
+
.default({ learnedAdjacencyThreshold: 0, maxPulls: 400 })
|
|
545
|
+
.describe(
|
|
546
|
+
"Edge-expansion lane configuration. Gates whether the learned co-retrieval graph augments the curated edge graph.",
|
|
547
|
+
),
|
|
523
548
|
ks: z
|
|
524
549
|
.array(z.number({ error: "memory.v3.ks entries must be numbers" }))
|
|
525
550
|
.default([5, 10, 25, 50])
|
|
@@ -597,6 +622,7 @@ export const MemoryV3ConfigSchema = z
|
|
|
597
622
|
denseQuota: { activeDomain: 30, offDomain: 8 },
|
|
598
623
|
hotLimit: 50,
|
|
599
624
|
lanes: { hot: true, sparse: true, dense: true, tree: true, edges: true },
|
|
625
|
+
edges: { learnedAdjacencyThreshold: 0, maxPulls: 400 },
|
|
600
626
|
ks: [5, 10, 25, 50],
|
|
601
627
|
write: {
|
|
602
628
|
enabled: false,
|