@vellumai/assistant 0.7.2 → 0.8.0
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/ARCHITECTURE.md +45 -29
- package/Dockerfile +1 -0
- package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
- package/bun.lock +3 -0
- package/docs/architecture/memory.md +5 -2
- package/knip.json +1 -0
- package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +13 -4
- package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
- package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
- package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -9
- package/node_modules/@vellumai/slack-text/src/index.test.ts +18 -35
- package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
- package/openapi.yaml +470 -25
- package/package.json +3 -1
- package/src/__tests__/annotate-risk-options.test.ts +291 -0
- package/src/__tests__/app-control-flow.test.ts +21 -11
- package/src/__tests__/approval-cascade.test.ts +8 -16
- package/src/__tests__/approval-routes-http.test.ts +6 -0
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/assistant-event.test.ts +0 -10
- package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -7
- package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -0
- package/src/__tests__/auto-analysis-end-to-end.test.ts +48 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
- package/src/__tests__/call-constants.test.ts +10 -1
- package/src/__tests__/call-controller.test.ts +127 -0
- package/src/__tests__/call-conversation-messages.test.ts +8 -2
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
- package/src/__tests__/channel-readiness-service.test.ts +4 -2
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +58 -28
- package/src/__tests__/config-loader-backfill.test.ts +379 -0
- package/src/__tests__/config-loader-platform-defaults.test.ts +284 -1
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +18 -9
- package/src/__tests__/config-watcher.test.ts +140 -69
- package/src/__tests__/context-search-agent-runner.test.ts +61 -3
- package/src/__tests__/context-search-conversations-source.test.ts +0 -24
- package/src/__tests__/context-search-fanout.test.ts +0 -1
- package/src/__tests__/context-search-memory-source.test.ts +6 -33
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
- package/src/__tests__/context-search-pkb-source.test.ts +12 -7
- package/src/__tests__/context-search-workspace-source.test.ts +0 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop.test.ts +457 -8
- package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
- package/src/__tests__/conversation-error.test.ts +150 -3
- package/src/__tests__/conversation-init.benchmark.test.ts +1 -1
- package/src/__tests__/conversation-process-callsite.test.ts +38 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +74 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
- package/src/__tests__/conversation-speed-override.test.ts +0 -3
- package/src/__tests__/conversation-store.test.ts +0 -18
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
- package/src/__tests__/conversation-surfaces-app-control.test.ts +15 -4
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +476 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +61 -5
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/credentials-cli.test.ts +7 -0
- package/src/__tests__/cu-unified-flow.test.ts +176 -10
- package/src/__tests__/date-context.test.ts +164 -2
- package/src/__tests__/disk-pressure-guard.test.ts +262 -0
- package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
- package/src/__tests__/disk-pressure-policy.test.ts +241 -0
- package/src/__tests__/disk-pressure-routes.test.ts +379 -0
- package/src/__tests__/disk-pressure-tools.test.ts +277 -0
- package/src/__tests__/disk-usage.test.ts +150 -0
- package/src/__tests__/events-client-registration.test.ts +52 -0
- package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
- package/src/__tests__/file-write-tool.test.ts +4 -10
- package/src/__tests__/filing-service.test.ts +2 -20
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -26
- package/src/__tests__/heartbeat-disk-pressure.test.ts +183 -0
- package/src/__tests__/heartbeat-service.test.ts +260 -11
- package/src/__tests__/host-app-control-proxy.test.ts +195 -25
- package/src/__tests__/host-bash-proxy.test.ts +227 -34
- package/src/__tests__/host-bash-routes.test.ts +178 -13
- package/src/__tests__/host-cu-proxy.test.ts +210 -3
- package/src/__tests__/host-cu-routes-targeted.test.ts +141 -12
- package/src/__tests__/host-file-proxy-targeted.test.ts +48 -9
- package/src/__tests__/host-file-proxy.test.ts +268 -6
- package/src/__tests__/host-file-routes-targeted.test.ts +175 -17
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +408 -59
- package/src/__tests__/host-transfer-routes-targeted.test.ts +232 -17
- package/src/__tests__/http-user-message-parity.test.ts +107 -1
- package/src/__tests__/injector-chain.test.ts +36 -16
- package/src/__tests__/injector-disk-pressure.test.ts +224 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +154 -67
- package/src/__tests__/managed-profile-guard.test.ts +18 -0
- package/src/__tests__/mcp-abort-signal.test.ts +130 -0
- package/src/__tests__/memory-admin-recall.test.ts +3 -11
- package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
- package/src/__tests__/normalize-onboarding.test.ts +180 -0
- package/src/__tests__/notification-decision-fallback.test.ts +91 -0
- package/src/__tests__/notification-decision-strategy.test.ts +22 -0
- package/src/__tests__/oauth-cli.test.ts +121 -0
- package/src/__tests__/oauth-connect-routes.test.ts +316 -0
- package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
- package/src/__tests__/onboarding-persona-write.test.ts +308 -0
- package/src/__tests__/openai-provider.test.ts +45 -8
- package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
- package/src/__tests__/platform-callback-registration.test.ts +21 -4
- package/src/__tests__/platform.test.ts +2 -1
- package/src/__tests__/playbook-execution.test.ts +0 -43
- package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +214 -27
- package/src/__tests__/provider-tool-name.test.ts +23 -0
- package/src/__tests__/relay-server.test.ts +60 -5
- package/src/__tests__/runtime-events-sse.test.ts +4 -8
- package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
- package/src/__tests__/secret-ingress-http.test.ts +0 -1
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
- package/src/__tests__/secret-response-routing.test.ts +7 -5
- package/src/__tests__/server-history-render.test.ts +82 -0
- package/src/__tests__/skill-include-graph.test.ts +31 -0
- package/src/__tests__/skill-load-tool.test.ts +44 -16
- package/src/__tests__/skills.test.ts +39 -0
- package/src/__tests__/suggestion-routes.test.ts +46 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
- package/src/__tests__/tool-executor.test.ts +155 -0
- package/src/__tests__/twilio-validation.test.ts +2 -2
- package/src/__tests__/voice-session-bridge.test.ts +3 -0
- package/src/__tests__/workspace-migration-065-bump-stale-heartbeat-interval.test.ts +122 -0
- package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
- package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
- package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +120 -0
- package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
- package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +78 -0
- package/src/agent/loop.ts +11 -0
- package/src/approvals/guardian-request-resolvers.ts +3 -32
- package/src/backup/snapshot-lock.ts +2 -27
- package/src/bundler/compiler-tools.ts +3 -2
- package/src/calls/call-constants.ts +5 -8
- package/src/calls/call-controller.ts +130 -67
- package/src/calls/call-conversation-messages.ts +46 -10
- package/src/calls/relay-server.ts +7 -1
- package/src/calls/voice-session-bridge.ts +1 -1
- package/src/cli/commands/__tests__/webhooks.test.ts +0 -4
- package/src/cli/commands/bash.ts +35 -108
- package/src/cli/commands/contacts.ts +64 -25
- package/src/cli/commands/credentials.ts +56 -0
- package/src/cli/commands/memory-v2.ts +11 -10
- package/src/cli/commands/oauth/__tests__/connect.test.ts +401 -219
- package/src/cli/commands/oauth/connect.ts +124 -40
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -3
- package/src/cli/commands/platform/__tests__/connect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +103 -6
- package/src/cli/commands/platform/index.ts +16 -7
- package/src/cli/commands/status.ts +57 -0
- package/src/cli/program.ts +4 -2
- package/src/config/assistant-feature-flags.ts +13 -3
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -3
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +13 -7
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
- package/src/config/env.ts +0 -8
- package/src/config/feature-flag-registry.json +13 -5
- package/src/config/loader.ts +199 -27
- package/src/config/schemas/__tests__/memory-v2.test.ts +10 -5
- package/src/config/schemas/call-site-catalog.ts +14 -0
- package/src/config/schemas/channels.ts +0 -5
- package/src/config/schemas/heartbeat.ts +1 -1
- package/src/config/schemas/llm.ts +2 -0
- package/src/config/schemas/memory-lifecycle.ts +13 -0
- package/src/config/schemas/memory-v2.ts +76 -12
- package/src/config/schemas/platform.ts +43 -3
- package/src/config/schemas/services.ts +28 -0
- package/src/config/seed-inference-profiles.ts +230 -33
- package/src/contacts/contact-store.ts +0 -25
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +32 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +86 -25
- package/src/daemon/assistant-attachments.ts +4 -4
- package/src/daemon/config-watcher.ts +85 -57
- package/src/daemon/conversation-agent-loop-handlers.ts +38 -0
- package/src/daemon/conversation-agent-loop.ts +183 -43
- package/src/daemon/conversation-error.ts +87 -15
- package/src/daemon/conversation-lifecycle.ts +22 -10
- package/src/daemon/conversation-process.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +26 -0
- package/src/daemon/conversation-store.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +211 -29
- package/src/daemon/conversation-tool-setup.ts +66 -19
- package/src/daemon/conversation.ts +18 -23
- package/src/daemon/date-context.ts +71 -22
- package/src/daemon/disk-pressure-background-gate.ts +73 -0
- package/src/daemon/disk-pressure-guard.ts +343 -0
- package/src/daemon/disk-pressure-policy.ts +163 -0
- package/src/daemon/handlers/shared.ts +26 -1
- package/src/daemon/handlers/skills.ts +3 -4
- package/src/daemon/host-app-control-proxy.ts +137 -41
- package/src/daemon/host-bash-proxy.ts +47 -22
- package/src/daemon/host-browser-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +50 -4
- package/src/daemon/host-file-proxy.ts +44 -8
- package/src/daemon/host-transfer-proxy.ts +97 -6
- package/src/daemon/lifecycle.ts +167 -101
- package/src/daemon/meet-host-supervisor.ts +4 -4
- package/src/daemon/meet-manifest-loader.ts +0 -1
- package/src/daemon/memory-v2-startup.ts +66 -15
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +4 -0
- package/src/daemon/message-types/disk-pressure.ts +9 -0
- package/src/daemon/message-types/messages.ts +22 -1
- package/src/daemon/profiler-run-store.ts +5 -5
- package/src/daemon/tool-setup-types.ts +2 -2
- package/src/documents/document-store.ts +119 -0
- package/src/filing/filing-service.ts +29 -5
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +9 -16
- package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +36 -0
- package/src/heartbeat/heartbeat-run-store.ts +13 -0
- package/src/heartbeat/heartbeat-service.ts +205 -31
- package/src/home/feed-scheduler.ts +18 -0
- package/src/inbound/platform-callback-registration.ts +8 -15
- package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
- package/src/ipc/assistant-server.ts +149 -38
- package/src/ipc/gateway-client.ts +37 -3
- package/src/ipc/skill-server.ts +99 -42
- package/src/live-voice/live-voice-archive.ts +4 -4
- package/src/live-voice/protocol.ts +5 -7
- package/src/media/image-service.ts +1 -7
- package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +34 -51
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
- package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -0
- package/src/memory/admin.ts +5 -9
- package/src/memory/context-search/agent-runner.ts +19 -2
- package/src/memory/context-search/sources/conversations.ts +2 -11
- package/src/memory/context-search/sources/memory-v2.ts +1 -16
- package/src/memory/context-search/sources/memory.ts +2 -3
- package/src/memory/context-search/sources/pkb.ts +2 -3
- package/src/memory/context-search/types.ts +0 -1
- package/src/memory/conversation-crud.ts +4 -12
- package/src/memory/db-init.ts +2 -0
- package/src/memory/embedding-runtime-manager.ts +119 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +136 -82
- package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
- package/src/memory/graph/conversation-graph-memory.ts +72 -61
- package/src/memory/graph/extraction.ts +1 -3
- package/src/memory/graph/graph-search.test.ts +11 -67
- package/src/memory/graph/graph-search.ts +4 -24
- package/src/memory/graph/retriever.test.ts +12 -1
- package/src/memory/graph/retriever.ts +10 -15
- package/src/memory/graph/tool-handlers.ts +3 -4
- package/src/memory/graph/tools.ts +4 -4
- package/src/memory/indexer.ts +53 -45
- package/src/memory/job-handlers/backfill.ts +2 -11
- package/src/memory/job-handlers/cleanup.ts +43 -0
- package/src/memory/job-handlers/embedding.ts +6 -8
- package/src/memory/job-handlers/summarization.ts +2 -7
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +116 -0
- package/src/memory/jobs/embed-concept-page.ts +223 -87
- package/src/memory/jobs-store.ts +48 -0
- package/src/memory/jobs-worker.ts +85 -43
- package/src/memory/memory-v2-activation-log-store.ts +32 -14
- package/src/memory/memory-v2-concept-frequency.ts +169 -0
- package/src/memory/migrations/239-trace-events-created-at-index.ts +18 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/pkb/pkb-search.test.ts +7 -0
- package/src/memory/pkb/pkb-search.ts +4 -5
- package/src/memory/qdrant-client.ts +3 -13
- package/src/memory/rerank-local.ts +374 -0
- package/src/memory/search/semantic.ts +10 -72
- package/src/memory/trace-event-store.ts +1 -17
- package/src/memory/v2/__tests__/activation.test.ts +346 -255
- package/src/memory/v2/__tests__/consolidation-job.test.ts +61 -40
- package/src/memory/v2/__tests__/injection.test.ts +297 -190
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
- package/src/memory/v2/__tests__/qdrant.test.ts +326 -9
- package/src/memory/v2/__tests__/reranker.test.ts +338 -0
- package/src/memory/v2/__tests__/sim.test.ts +113 -196
- package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
- package/src/memory/v2/__tests__/static-context.test.ts +77 -14
- package/src/memory/v2/__tests__/sweep-job.test.ts +19 -33
- package/src/memory/v2/activation.ts +149 -156
- package/src/memory/v2/consolidation-job.ts +69 -20
- package/src/memory/v2/injection.ts +75 -68
- package/src/memory/v2/page-store.ts +39 -0
- package/src/memory/v2/prompts/consolidation.ts +41 -1
- package/src/memory/v2/qdrant.ts +306 -46
- package/src/memory/v2/reranker.ts +177 -0
- package/src/memory/v2/sim.ts +77 -110
- package/src/memory/v2/skill-content.ts +4 -3
- package/src/memory/v2/skill-store.ts +82 -59
- package/src/memory/v2/static-context.ts +26 -8
- package/src/memory/v2/sweep-job.ts +5 -6
- package/src/memory/v2/types.ts +17 -10
- package/src/notifications/copy-composer.ts +47 -0
- package/src/notifications/decision-engine.ts +46 -0
- package/src/notifications/signal.ts +4 -0
- package/src/oauth/AGENTS.md +3 -1
- package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
- package/src/oauth/connect-orchestrator.ts +2 -0
- package/src/oauth/connection-resolver.test.ts +66 -1
- package/src/oauth/connection-resolver.ts +55 -1
- package/src/oauth/oauth-connect-state.ts +77 -0
- package/src/oauth/seed-providers.ts +58 -1
- package/src/permissions/gateway-threshold-reader.ts +116 -8
- package/src/permissions/prompter.ts +86 -96
- package/src/permissions/secret-prompter.ts +31 -31
- package/src/plugins/defaults/injectors.ts +36 -4
- package/src/plugins/defaults/memory-retrieval.ts +5 -6
- package/src/plugins/types.ts +7 -0
- package/src/proactive-artifact/aux-message-injector.ts +74 -0
- package/src/proactive-artifact/decision.test.ts +226 -0
- package/src/proactive-artifact/decision.ts +165 -0
- package/src/proactive-artifact/index.ts +7 -0
- package/src/proactive-artifact/job.test.ts +914 -0
- package/src/proactive-artifact/job.ts +366 -0
- package/src/proactive-artifact/message-copy.ts +58 -0
- package/src/proactive-artifact/trigger-state.test.ts +277 -0
- package/src/proactive-artifact/trigger-state.ts +119 -0
- package/src/prompts/normalize-onboarding.ts +80 -0
- package/src/prompts/persona-resolver.ts +101 -9
- package/src/prompts/system-prompt.ts +21 -7
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/SOUL.md +13 -28
- package/src/providers/__tests__/retry-callsite.test.ts +222 -1
- package/src/providers/model-intents.ts +7 -0
- package/src/providers/openrouter/client.ts +8 -0
- package/src/providers/retry.ts +50 -0
- package/src/providers/types.ts +1 -0
- package/src/runtime/__tests__/agent-wake.test.ts +456 -3
- package/src/runtime/agent-wake.ts +238 -100
- package/src/runtime/assistant-event-hub.ts +36 -6
- package/src/runtime/assistant-event.ts +0 -1
- package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
- package/src/runtime/auth/route-policy.ts +15 -1
- package/src/runtime/auth/same-actor.ts +216 -0
- package/src/runtime/channel-approvals.ts +3 -2
- package/src/runtime/channel-retry-sweep.ts +65 -1
- package/src/runtime/local-actor-identity.ts +52 -11
- package/src/runtime/pending-interactions.ts +27 -15
- package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +0 -5
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
- package/src/runtime/routes/approval-routes.ts +7 -3
- package/src/runtime/routes/client-routes.ts +20 -2
- package/src/runtime/routes/consolidation-routes.ts +8 -9
- package/src/runtime/routes/contact-routes.ts +0 -25
- package/src/runtime/routes/conversation-query-routes.ts +44 -1
- package/src/runtime/routes/conversation-routes.ts +35 -26
- package/src/runtime/routes/debug-bash-routes.ts +165 -0
- package/src/runtime/routes/disk-pressure-routes.ts +121 -0
- package/src/runtime/routes/document-pdf-renderer.ts +6 -2
- package/src/runtime/routes/documents-routes.ts +2 -75
- package/src/runtime/routes/events-routes.ts +41 -9
- package/src/runtime/routes/filing-routes.ts +2 -3
- package/src/runtime/routes/host-bash-routes.ts +23 -3
- package/src/runtime/routes/host-cu-routes.ts +33 -6
- package/src/runtime/routes/host-file-routes.ts +32 -6
- package/src/runtime/routes/host-transfer-routes.ts +79 -16
- package/src/runtime/routes/identity-routes.ts +7 -138
- package/src/runtime/routes/inbound-message-handler.ts +77 -12
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/memory-item-routes.test.ts +37 -17
- package/src/runtime/routes/memory-item-routes.ts +5 -6
- package/src/runtime/routes/memory-v2-routes.ts +136 -17
- package/src/runtime/routes/oauth-connect-routes.ts +153 -0
- package/src/runtime/verification-outbound-actions.ts +4 -4
- package/src/schedule/run-script.ts +37 -5
- package/src/schedule/scheduler.ts +20 -1
- package/src/security/encrypted-store.ts +2 -0
- package/src/security/secure-keys.ts +55 -0
- package/src/skills/include-graph.ts +35 -13
- package/src/skills/remote-skill-policy.ts +4 -10
- package/src/subagent/index.ts +1 -7
- package/src/subagent/manager.ts +1 -15
- package/src/tasks/task-runner.ts +0 -1
- package/src/tasks/task-store.ts +0 -3
- package/src/tools/background-tool-registry.ts +17 -3
- package/src/tools/document/document-tool.ts +20 -0
- package/src/tools/executor.ts +18 -2
- package/src/tools/host-filesystem/edit.test.ts +151 -0
- package/src/tools/host-filesystem/edit.ts +43 -1
- package/src/tools/host-filesystem/read.test.ts +129 -0
- package/src/tools/host-filesystem/read.ts +43 -1
- package/src/tools/host-filesystem/transfer.test.ts +127 -2
- package/src/tools/host-filesystem/transfer.ts +56 -11
- package/src/tools/host-filesystem/write.test.ts +134 -0
- package/src/tools/host-filesystem/write.ts +43 -1
- package/src/tools/host-terminal/host-shell.ts +13 -6
- package/src/tools/mcp/mcp-tool-factory.ts +2 -1
- package/src/tools/memory/register.test.ts +14 -9
- package/src/tools/memory/register.ts +1 -2
- package/src/tools/permission-checker.ts +15 -0
- package/src/tools/provider-tool-name.ts +28 -0
- package/src/tools/registry.ts +30 -9
- package/src/tools/skills/load.ts +24 -20
- package/src/tools/terminal/shell.ts +9 -1
- package/src/tools/tool-approval-handler.ts +31 -6
- package/src/tools/tool-name-aliases.ts +19 -0
- package/src/tools/types.ts +43 -3
- package/src/tts/provider-catalog.ts +3 -5
- package/src/util/disk-usage.ts +138 -0
- package/src/util/platform.ts +21 -11
- package/src/util/process-liveness.ts +26 -0
- package/src/workspace/heartbeat-service.ts +19 -0
- package/src/workspace/migrations/065-bump-stale-heartbeat-interval.ts +60 -0
- package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
- package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +14 -0
- package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
- package/src/workspace/migrations/069-seed-onboarding-threads.ts +28 -0
- package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
- package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
- package/src/workspace/migrations/registry.ts +14 -0
- package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
- package/src/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
- package/src/memory/v2/skill-qdrant.ts +0 -404
- package/src/signals/bash.ts +0 -198
|
@@ -67,7 +67,8 @@ function upsertMemoryItem(opts: {
|
|
|
67
67
|
created: now,
|
|
68
68
|
lastAccessed: now,
|
|
69
69
|
lastConsolidated: now,
|
|
70
|
-
emotionalCharge:
|
|
70
|
+
emotionalCharge:
|
|
71
|
+
'{"valence":0,"intensity":0.1,"decayCurve":"linear","decayRate":0.05,"originalIntensity":0.1}',
|
|
71
72
|
fidelity: "vivid",
|
|
72
73
|
confidence: 0.8,
|
|
73
74
|
significance: clampUnitInterval(opts.importance),
|
|
@@ -87,7 +88,7 @@ function upsertMemoryItem(opts: {
|
|
|
87
88
|
|
|
88
89
|
export async function run(
|
|
89
90
|
input: Record<string, unknown>,
|
|
90
|
-
|
|
91
|
+
_context: ToolContext,
|
|
91
92
|
): Promise<ToolExecutionResult> {
|
|
92
93
|
const platform = input.platform as string | undefined;
|
|
93
94
|
const maxMessages = Math.min(
|
|
@@ -119,7 +120,7 @@ export async function run(
|
|
|
119
120
|
return err("No style patterns were extracted. Try with more messages.");
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
const scopeId =
|
|
123
|
+
const scopeId = "default";
|
|
123
124
|
let savedCount = 0;
|
|
124
125
|
|
|
125
126
|
for (const pattern of result.stylePatterns) {
|
|
@@ -10,18 +10,24 @@ Run `assistant config set calls.enabled true`.
|
|
|
10
10
|
|
|
11
11
|
## "No public base URL configured"
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
First check whether this is a managed/platform assistant:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
assistant platform status --json
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
If it reports an available platform assistant, do not install or start ngrok for Twilio. The gateway should use Velay, and `velayTunnel.connected` should become `true` after registration. If this is a local/self-hosted assistant without Velay, run the **public-ingress** skill to set up ngrok or another custom tunnel and configure `ingress.publicBaseUrl`.
|
|
14
20
|
|
|
15
21
|
## Call fails immediately after initiating
|
|
16
22
|
|
|
17
23
|
- Check that the phone number is in E.164 format
|
|
18
24
|
- Verify Twilio credentials are correct (wrong auth token causes API errors)
|
|
19
25
|
- On trial accounts, ensure the destination number is verified
|
|
20
|
-
- Check that the configured tunnel is still running. For ngrok, use `curl -s http://127.0.0.1:4040/api/tunnels`. For Velay, confirm
|
|
26
|
+
- Check that the configured tunnel is still running. For ngrok, use `curl -s http://127.0.0.1:4040/api/tunnels`. For Velay, run `assistant platform status --json` and confirm `velayTunnel.connected` is `true`, or check gateway logs for `Velay tunnel registered`.
|
|
21
27
|
|
|
22
28
|
## Call connects but no audio / one-way audio
|
|
23
29
|
|
|
24
|
-
- The ConversationRelay WebSocket may not be connecting. If you are using ngrok or a custom tunnel, check that `ingress.publicBaseUrl` is correct and the tunnel is active. If you are using Velay, check
|
|
30
|
+
- The ConversationRelay WebSocket may not be connecting. If you are using ngrok or a custom tunnel, check that `ingress.publicBaseUrl` is correct and the tunnel is active. If you are using Velay, check `assistant platform status --json`; a 503 from `https://velay.../<assistant-id>/...` usually means the assistant tunnel is not connected or the gateway did not complete the WebSocket open, not that ngrok is required.
|
|
25
31
|
- Verify the assistant is running
|
|
26
32
|
|
|
27
33
|
## "Number not eligible for caller identity"
|
|
@@ -50,20 +56,20 @@ assistant config set ingress.publicBaseUrl "<new-url>"
|
|
|
50
56
|
|
|
51
57
|
Or re-run the public-ingress skill to auto-detect and save the new URL.
|
|
52
58
|
|
|
53
|
-
|
|
59
|
+
Do not rotate ngrok to work around a managed Velay WebSocket failure. Fix the Velay tunnel state instead, or restart the assistant/gateway so it re-registers.
|
|
54
60
|
|
|
55
61
|
## Velay tunnel is not registering
|
|
56
62
|
|
|
57
|
-
- Confirm vembda passes `VELAY_BASE_URL
|
|
63
|
+
- Confirm vembda passes the environment-appropriate `VELAY_BASE_URL` to the gateway container.
|
|
58
64
|
- Re-hatch or restart the assistant after changing the environment.
|
|
59
65
|
- Check gateway logs for `Velay tunnel connected` followed by `Velay tunnel registered`.
|
|
60
|
-
- If `VELAY_BASE_URL` is not set, the gateway does not start the Velay client. Use ngrok or another custom tunnel in `ingress.publicBaseUrl`.
|
|
66
|
+
- If `VELAY_BASE_URL` is not set on a local/self-hosted assistant, the gateway does not start the Velay client. Use ngrok or another custom tunnel in `ingress.publicBaseUrl`.
|
|
61
67
|
|
|
62
68
|
## Local Twilio Velay smoke tests
|
|
63
69
|
|
|
64
70
|
- HTTP bridge: request `${VELAY_PUBLIC_BASE_URL}/<assistant-id>/healthz` and `${VELAY_PUBLIC_BASE_URL}/<assistant-id>/schema`. When testing a JSON webhook route under active development, POST a small JSON body through the same Velay public URL and confirm the gateway receives it.
|
|
65
71
|
- Synthetic WebSocket: connect a local WebSocket client to `${VELAY_PUBLIC_BASE_URL}/<assistant-id>/webhooks/twilio/relay?callSessionId=session-123&token=<edge-token>` and confirm the upgrade reaches the gateway.
|
|
66
|
-
- Real Twilio call:
|
|
72
|
+
- Real Twilio call: wait for the gateway to register with Velay, then place a call and confirm Twilio fetches `/webhooks/twilio/voice` and opens the relay or media-stream WebSocket through the Velay URL.
|
|
67
73
|
|
|
68
74
|
## Call drops after 30 seconds of silence
|
|
69
75
|
|
|
@@ -18,7 +18,7 @@ const VALID_AUTONOMY_LEVELS = new Set<string>(["auto", "draft", "notify"]);
|
|
|
18
18
|
|
|
19
19
|
export async function executePlaybookCreate(
|
|
20
20
|
input: Record<string, unknown>,
|
|
21
|
-
|
|
21
|
+
_context: ToolContext,
|
|
22
22
|
): Promise<ToolExecutionResult> {
|
|
23
23
|
const trigger = input.trigger as string;
|
|
24
24
|
const action = input.action as string;
|
|
@@ -58,7 +58,7 @@ export async function executePlaybookCreate(
|
|
|
58
58
|
const sanitizedTrigger = trigger.replace(/[\r\n]+/g, " ");
|
|
59
59
|
const subject = `Playbook: ${sanitizedTrigger}`.slice(0, 80);
|
|
60
60
|
const content = `${subject}\n${statement}`;
|
|
61
|
-
const scopeId =
|
|
61
|
+
const scopeId = "default";
|
|
62
62
|
|
|
63
63
|
try {
|
|
64
64
|
const db = getDb();
|
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
|
|
8
8
|
export async function executePlaybookDelete(
|
|
9
9
|
input: Record<string, unknown>,
|
|
10
|
-
|
|
10
|
+
_context: ToolContext,
|
|
11
11
|
): Promise<ToolExecutionResult> {
|
|
12
12
|
const playbookId = input.playbook_id as string;
|
|
13
13
|
if (!playbookId || typeof playbookId !== "string") {
|
|
@@ -17,7 +17,7 @@ export async function executePlaybookDelete(
|
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const scopeId =
|
|
20
|
+
const scopeId = "default";
|
|
21
21
|
|
|
22
22
|
try {
|
|
23
23
|
const existing = getNode(playbookId);
|
|
@@ -10,9 +10,9 @@ import type {
|
|
|
10
10
|
|
|
11
11
|
export async function executePlaybookList(
|
|
12
12
|
input: Record<string, unknown>,
|
|
13
|
-
|
|
13
|
+
_context: ToolContext,
|
|
14
14
|
): Promise<ToolExecutionResult> {
|
|
15
|
-
const scopeId =
|
|
15
|
+
const scopeId = "default";
|
|
16
16
|
const channelFilter =
|
|
17
17
|
typeof input.channel === "string" ? input.channel : null;
|
|
18
18
|
const categoryFilter =
|
|
@@ -18,7 +18,7 @@ const VALID_AUTONOMY_LEVELS = new Set<string>(["auto", "draft", "notify"]);
|
|
|
18
18
|
|
|
19
19
|
export async function executePlaybookUpdate(
|
|
20
20
|
input: Record<string, unknown>,
|
|
21
|
-
|
|
21
|
+
_context: ToolContext,
|
|
22
22
|
): Promise<ToolExecutionResult> {
|
|
23
23
|
const playbookId = input.playbook_id as string;
|
|
24
24
|
if (!playbookId || typeof playbookId !== "string") {
|
|
@@ -28,7 +28,7 @@ export async function executePlaybookUpdate(
|
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
const scopeId =
|
|
31
|
+
const scopeId = "default";
|
|
32
32
|
|
|
33
33
|
try {
|
|
34
34
|
const existing = getNode(playbookId);
|
package/src/config/env.ts
CHANGED
|
@@ -253,14 +253,6 @@ export function getPlatformUserId(): string {
|
|
|
253
253
|
return str("PLATFORM_USER_ID") ?? _platformUserIdOverride ?? "";
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
/**
|
|
257
|
-
* PLATFORM_INTERNAL_API_KEY — static internal gateway key for authenticating
|
|
258
|
-
* with the platform's internal gateway callback route registration endpoint.
|
|
259
|
-
*/
|
|
260
|
-
export function getPlatformInternalApiKey(): string {
|
|
261
|
-
return str("PLATFORM_INTERNAL_API_KEY") ?? "";
|
|
262
|
-
}
|
|
263
|
-
|
|
264
256
|
// ── Startup validation ──────────────────────────────────────────────────────
|
|
265
257
|
|
|
266
258
|
/**
|
|
@@ -242,11 +242,11 @@
|
|
|
242
242
|
"defaultEnabled": false
|
|
243
243
|
},
|
|
244
244
|
{
|
|
245
|
-
"id": "
|
|
245
|
+
"id": "safe-storage-limits",
|
|
246
246
|
"scope": "assistant",
|
|
247
|
-
"key": "
|
|
248
|
-
"label": "
|
|
249
|
-
"description": "
|
|
247
|
+
"key": "safe-storage-limits",
|
|
248
|
+
"label": "Safe Storage Limits",
|
|
249
|
+
"description": "Enable disk pressure protection flows that block background work and remote actors while storage is critically low.",
|
|
250
250
|
"defaultEnabled": false
|
|
251
251
|
},
|
|
252
252
|
{
|
|
@@ -255,7 +255,7 @@
|
|
|
255
255
|
"key": "account-deletion",
|
|
256
256
|
"label": "Account Deletion",
|
|
257
257
|
"description": "Surfaces the user-initiated account deletion flow in client settings.",
|
|
258
|
-
"defaultEnabled":
|
|
258
|
+
"defaultEnabled": true
|
|
259
259
|
},
|
|
260
260
|
{
|
|
261
261
|
"id": "app-control",
|
|
@@ -272,6 +272,14 @@
|
|
|
272
272
|
"label": "Analyze Conversation",
|
|
273
273
|
"description": "Show the 'Analyze' / 'Analyze conversation' option in conversation context menus and the conversation title actions dropdown.",
|
|
274
274
|
"defaultEnabled": false
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
"id": "pro-plan-adjust",
|
|
278
|
+
"scope": "assistant",
|
|
279
|
+
"key": "pro-plan-adjust",
|
|
280
|
+
"label": "Pro Plan Adjust",
|
|
281
|
+
"description": "Show the rich Plan card (current plan, features, Manage/Upgrade CTA) at the top of the macOS Settings → Billing tab.",
|
|
282
|
+
"defaultEnabled": false
|
|
275
283
|
}
|
|
276
284
|
]
|
|
277
285
|
}
|
package/src/config/loader.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "node:fs";
|
|
8
8
|
import { basename, dirname, join } from "node:path";
|
|
9
9
|
|
|
10
|
+
import { safeStatSync } from "../util/fs.js";
|
|
10
11
|
import { getLogger } from "../util/logger.js";
|
|
11
12
|
import {
|
|
12
13
|
ensureDataDir,
|
|
@@ -22,8 +23,22 @@ export { API_KEY_PROVIDERS } from "../providers/provider-secret-catalog.js";
|
|
|
22
23
|
const log = getLogger("config");
|
|
23
24
|
|
|
24
25
|
let cached: AssistantConfig | null = null;
|
|
26
|
+
let cachedFileSignature: ConfigFileSignature | null = null;
|
|
25
27
|
let loading = false;
|
|
26
28
|
|
|
29
|
+
type ConfigFileSignature =
|
|
30
|
+
| {
|
|
31
|
+
path: string;
|
|
32
|
+
exists: true;
|
|
33
|
+
size: number;
|
|
34
|
+
mtimeMs: number;
|
|
35
|
+
ctimeMs: number;
|
|
36
|
+
}
|
|
37
|
+
| {
|
|
38
|
+
path: string;
|
|
39
|
+
exists: false;
|
|
40
|
+
};
|
|
41
|
+
|
|
27
42
|
function getConfigPath(): string {
|
|
28
43
|
return getWorkspaceConfigPath();
|
|
29
44
|
}
|
|
@@ -32,6 +47,48 @@ function ensureMigratedDataDir(): void {
|
|
|
32
47
|
ensureDataDir();
|
|
33
48
|
}
|
|
34
49
|
|
|
50
|
+
function readConfigFileSignature(configPath: string): ConfigFileSignature {
|
|
51
|
+
const stats = safeStatSync(configPath);
|
|
52
|
+
if (!stats) {
|
|
53
|
+
return {
|
|
54
|
+
path: configPath,
|
|
55
|
+
exists: false,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
path: configPath,
|
|
61
|
+
exists: true,
|
|
62
|
+
size: stats.size,
|
|
63
|
+
mtimeMs: stats.mtimeMs,
|
|
64
|
+
ctimeMs: stats.ctimeMs,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function configFileSignaturesEqual(
|
|
69
|
+
a: ConfigFileSignature,
|
|
70
|
+
b: ConfigFileSignature,
|
|
71
|
+
): boolean {
|
|
72
|
+
if (a.path !== b.path || a.exists !== b.exists) return false;
|
|
73
|
+
if (!a.exists || !b.exists) return true;
|
|
74
|
+
return (
|
|
75
|
+
a.size === b.size && a.mtimeMs === b.mtimeMs && a.ctimeMs === b.ctimeMs
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function getCachedConfigIfFresh(): AssistantConfig | null {
|
|
80
|
+
if (!cached || !cachedFileSignature) return null;
|
|
81
|
+
|
|
82
|
+
const currentSignature = readConfigFileSignature(getConfigPath());
|
|
83
|
+
if (configFileSignaturesEqual(cachedFileSignature, currentSignature)) {
|
|
84
|
+
return cached;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
cached = null;
|
|
88
|
+
cachedFileSignature = null;
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
35
92
|
/**
|
|
36
93
|
* Parse a raw config through the Zod schema, applying all nested defaults.
|
|
37
94
|
*
|
|
@@ -51,14 +108,16 @@ function cloneDefaultConfig(): AssistantConfig {
|
|
|
51
108
|
|
|
52
109
|
/**
|
|
53
110
|
* Returns deployment-context-aware config defaults that override schema
|
|
54
|
-
* defaults for platform-managed assistants.
|
|
55
|
-
* a
|
|
111
|
+
* defaults for platform-managed assistants. Applied to every `loadConfig()`
|
|
112
|
+
* call as a fill-only pass — they only fill keys that are absent from the
|
|
113
|
+
* raw config on disk, so an explicit user choice (e.g. saving "your-own"
|
|
114
|
+
* via the macOS Models & Services UI) always wins.
|
|
56
115
|
*
|
|
57
116
|
* IS_PLATFORM is set by the Vellum platform launcher for all hosted
|
|
58
117
|
* assistant deployments. Local, Docker, and bare-metal assistants are
|
|
59
118
|
* unaffected.
|
|
60
119
|
*/
|
|
61
|
-
function getDeploymentContextDefaults(): Record<string, unknown> {
|
|
120
|
+
export function getDeploymentContextDefaults(): Record<string, unknown> {
|
|
62
121
|
if (process.env.IS_PLATFORM !== "true" && process.env.IS_PLATFORM !== "1") {
|
|
63
122
|
return {};
|
|
64
123
|
}
|
|
@@ -73,10 +132,57 @@ function getDeploymentContextDefaults(): Record<string, unknown> {
|
|
|
73
132
|
"linear-oauth": managed,
|
|
74
133
|
"github-oauth": managed,
|
|
75
134
|
"notion-oauth": managed,
|
|
135
|
+
"asana-oauth": managed,
|
|
136
|
+
"todoist-oauth": managed,
|
|
137
|
+
"discord-oauth": managed,
|
|
138
|
+
"hubspot-oauth": managed,
|
|
76
139
|
},
|
|
77
140
|
};
|
|
78
141
|
}
|
|
79
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Apply `contextDefaults` to `target` for any leaf keys that are absent from
|
|
145
|
+
* `fileConfig` (the raw config-on-disk payload). Mutates `target` in place.
|
|
146
|
+
*
|
|
147
|
+
* "Absent" is checked at the leaf level by walking the `contextDefaults`
|
|
148
|
+
* shape: nested objects recurse so a partial override on disk (e.g.
|
|
149
|
+
* `{services: {inference: {model: "x"}}}` with no explicit `mode`) lets the
|
|
150
|
+
* context default for `mode` win while leaving the user's `model` untouched.
|
|
151
|
+
*
|
|
152
|
+
* Pre-condition: `target` has already been passed through `validateWithSchema`
|
|
153
|
+
* so every nested object in `contextDefaults` has a corresponding object in
|
|
154
|
+
* `target`. The defensive whole-subtree assignment in the `!targetChild`
|
|
155
|
+
* branch only fires for malformed inputs.
|
|
156
|
+
*/
|
|
157
|
+
export function fillContextDefaultsForMissingKeys(
|
|
158
|
+
target: Record<string, unknown>,
|
|
159
|
+
fileConfig: Record<string, unknown>,
|
|
160
|
+
contextDefaults: Record<string, unknown>,
|
|
161
|
+
): void {
|
|
162
|
+
for (const [key, value] of Object.entries(contextDefaults)) {
|
|
163
|
+
const fileVal = fileConfig[key];
|
|
164
|
+
if (
|
|
165
|
+
value !== null &&
|
|
166
|
+
typeof value === "object" &&
|
|
167
|
+
!Array.isArray(value)
|
|
168
|
+
) {
|
|
169
|
+
const targetChild = readPlainObject(target[key]);
|
|
170
|
+
const fileChild = readPlainObject(fileVal);
|
|
171
|
+
if (targetChild) {
|
|
172
|
+
fillContextDefaultsForMissingKeys(
|
|
173
|
+
targetChild,
|
|
174
|
+
fileChild ?? {},
|
|
175
|
+
value as Record<string, unknown>,
|
|
176
|
+
);
|
|
177
|
+
} else {
|
|
178
|
+
target[key] = structuredClone(value);
|
|
179
|
+
}
|
|
180
|
+
} else if (fileVal === undefined) {
|
|
181
|
+
target[key] = value;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
80
186
|
/**
|
|
81
187
|
* Build a filesystem-safe ISO-8601 timestamp for use in quarantine filenames.
|
|
82
188
|
* Replaces `:` (invalid on Windows, confusing on macOS Finder) with `-` so the
|
|
@@ -343,6 +449,13 @@ function stripNullLeaves(value: unknown): unknown {
|
|
|
343
449
|
return out;
|
|
344
450
|
}
|
|
345
451
|
|
|
452
|
+
function readPlainObject(value: unknown): Record<string, unknown> | null {
|
|
453
|
+
if (value == null || typeof value !== "object" || Array.isArray(value)) {
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
return value as Record<string, unknown>;
|
|
457
|
+
}
|
|
458
|
+
|
|
346
459
|
/**
|
|
347
460
|
* Deep-merge `overrides` into `target`, overwriting leaf values.
|
|
348
461
|
* Recursively merges nested objects; scalars and arrays from `overrides`
|
|
@@ -406,6 +519,18 @@ export function deepMergeOverwrite(
|
|
|
406
519
|
}
|
|
407
520
|
}
|
|
408
521
|
|
|
522
|
+
export type DefaultWorkspaceConfigMergeResult = {
|
|
523
|
+
providedLlmProfileNames: Set<string>;
|
|
524
|
+
providedLlmActiveProfile: boolean;
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
function emptyDefaultWorkspaceConfigMergeResult(): DefaultWorkspaceConfigMergeResult {
|
|
528
|
+
return {
|
|
529
|
+
providedLlmProfileNames: new Set(),
|
|
530
|
+
providedLlmActiveProfile: false,
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
|
|
409
534
|
/**
|
|
410
535
|
* Merge default workspace config from the file referenced by
|
|
411
536
|
* VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH into the workspace config on disk.
|
|
@@ -415,9 +540,11 @@ export function deepMergeOverwrite(
|
|
|
415
540
|
* Schema defaults are no longer materialized into the file on load — the
|
|
416
541
|
* in-memory `loadConfig()` cache applies them at access time instead.
|
|
417
542
|
*/
|
|
418
|
-
export function mergeDefaultWorkspaceConfig():
|
|
543
|
+
export function mergeDefaultWorkspaceConfig(): DefaultWorkspaceConfigMergeResult {
|
|
419
544
|
const defaultConfigPath = process.env.VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH;
|
|
420
|
-
if (!defaultConfigPath || !existsSync(defaultConfigPath))
|
|
545
|
+
if (!defaultConfigPath || !existsSync(defaultConfigPath)) {
|
|
546
|
+
return emptyDefaultWorkspaceConfigMergeResult();
|
|
547
|
+
}
|
|
421
548
|
|
|
422
549
|
let defaults: unknown;
|
|
423
550
|
try {
|
|
@@ -428,7 +555,7 @@ export function mergeDefaultWorkspaceConfig(): void {
|
|
|
428
555
|
"Failed to read default workspace config from %s",
|
|
429
556
|
defaultConfigPath,
|
|
430
557
|
);
|
|
431
|
-
return;
|
|
558
|
+
return emptyDefaultWorkspaceConfigMergeResult();
|
|
432
559
|
}
|
|
433
560
|
|
|
434
561
|
if (
|
|
@@ -436,16 +563,44 @@ export function mergeDefaultWorkspaceConfig(): void {
|
|
|
436
563
|
typeof defaults !== "object" ||
|
|
437
564
|
Array.isArray(defaults)
|
|
438
565
|
) {
|
|
439
|
-
return;
|
|
566
|
+
return emptyDefaultWorkspaceConfigMergeResult();
|
|
440
567
|
}
|
|
441
568
|
|
|
569
|
+
const llmDefaults = readPlainObject(
|
|
570
|
+
(defaults as Record<string, unknown>).llm,
|
|
571
|
+
);
|
|
572
|
+
const providedProfiles = readPlainObject(llmDefaults?.profiles);
|
|
573
|
+
const mergeResult: DefaultWorkspaceConfigMergeResult = {
|
|
574
|
+
providedLlmProfileNames: new Set(
|
|
575
|
+
providedProfiles ? Object.keys(providedProfiles) : [],
|
|
576
|
+
),
|
|
577
|
+
providedLlmActiveProfile:
|
|
578
|
+
llmDefaults != null &&
|
|
579
|
+
Object.prototype.hasOwnProperty.call(llmDefaults, "activeProfile"),
|
|
580
|
+
};
|
|
581
|
+
|
|
442
582
|
const configPath = getConfigPath();
|
|
443
583
|
let existing: Record<string, unknown> = {};
|
|
444
584
|
if (existsSync(configPath)) {
|
|
445
585
|
try {
|
|
446
586
|
existing = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
447
|
-
} catch {
|
|
448
|
-
|
|
587
|
+
} catch (err) {
|
|
588
|
+
quarantineCorruptConfig(configPath, err);
|
|
589
|
+
// After preserving the corrupt file, start fresh so the default overlay
|
|
590
|
+
// can still initialize a valid config for this startup.
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (mergeResult.providedLlmProfileNames.size > 0) {
|
|
595
|
+
// Default-config profile entries are authoritative fragments. Remove any
|
|
596
|
+
// old same-name profile first so recursive merge does not leave stale
|
|
597
|
+
// provider-specific leaves behind.
|
|
598
|
+
const existingLlm = readPlainObject(existing.llm);
|
|
599
|
+
const existingProfiles = readPlainObject(existingLlm?.profiles);
|
|
600
|
+
if (existingProfiles) {
|
|
601
|
+
for (const name of mergeResult.providedLlmProfileNames) {
|
|
602
|
+
delete existingProfiles[name];
|
|
603
|
+
}
|
|
449
604
|
}
|
|
450
605
|
}
|
|
451
606
|
|
|
@@ -456,6 +611,7 @@ export function mergeDefaultWorkspaceConfig(): void {
|
|
|
456
611
|
mkdirSync(dir, { recursive: true });
|
|
457
612
|
}
|
|
458
613
|
writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
|
|
614
|
+
invalidateConfigCache();
|
|
459
615
|
|
|
460
616
|
// Move the temp file into the workspace directory as a permanent record.
|
|
461
617
|
// This prevents re-application on daemon restart (the env var still points
|
|
@@ -471,10 +627,13 @@ export function mergeDefaultWorkspaceConfig(): void {
|
|
|
471
627
|
} catch {
|
|
472
628
|
log.info("Merged default workspace config from %s", defaultConfigPath);
|
|
473
629
|
}
|
|
630
|
+
|
|
631
|
+
return mergeResult;
|
|
474
632
|
}
|
|
475
633
|
|
|
476
634
|
export function loadConfig(): AssistantConfig {
|
|
477
|
-
|
|
635
|
+
const freshCached = getCachedConfigIfFresh();
|
|
636
|
+
if (freshCached) return freshCached;
|
|
478
637
|
|
|
479
638
|
// Re-entrancy guard: log calls during loading (e.g. file-mode warning)
|
|
480
639
|
// can trigger loadConfig again. Return defaults to break the cycle
|
|
@@ -551,11 +710,31 @@ export function loadConfig(): AssistantConfig {
|
|
|
551
710
|
}
|
|
552
711
|
}
|
|
553
712
|
|
|
713
|
+
// Layer deployment-context defaults (e.g. IS_PLATFORM=true → all service
|
|
714
|
+
// modes = "managed") onto the in-memory config for any leaves that aren't
|
|
715
|
+
// explicitly set in `fileConfig`. This runs on every load — not just the
|
|
716
|
+
// first — because the workspace config file is written by upstream
|
|
717
|
+
// lifecycle steps (`mergeDefaultWorkspaceConfig`, `seedInferenceProfiles`)
|
|
718
|
+
// before `loadConfig()` is reached. Gating on `!configFileExisted` would
|
|
719
|
+
// make the context defaults dead code on platform-managed daemons whose
|
|
720
|
+
// config.json was created by those earlier steps without service-mode
|
|
721
|
+
// entries. Explicit user choices on disk are preserved because the helper
|
|
722
|
+
// only fills missing keys.
|
|
723
|
+
const contextDefaults = getDeploymentContextDefaults();
|
|
724
|
+
if (Object.keys(contextDefaults).length > 0) {
|
|
725
|
+
fillContextDefaultsForMissingKeys(
|
|
726
|
+
config as unknown as Record<string, unknown>,
|
|
727
|
+
fileConfig,
|
|
728
|
+
contextDefaults,
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
|
|
554
732
|
// First-launch seed only: when config.json does not exist, write the full
|
|
555
|
-
// schema defaults
|
|
556
|
-
//
|
|
557
|
-
//
|
|
558
|
-
//
|
|
733
|
+
// schema defaults (with any deployment-context overrides already applied
|
|
734
|
+
// above) to disk so users can discover and edit all available options.
|
|
735
|
+
// When the file already exists, leave it alone — disk represents user
|
|
736
|
+
// intent, while the in-memory `cached: AssistantConfig` (above) has all
|
|
737
|
+
// schema defaults applied via `applyNestedDefaults`/`validateWithSchema`,
|
|
559
738
|
// so consumers calling `getConfig().memory.v2.bm25_b` continue to receive
|
|
560
739
|
// the schema default whenever the field is absent on disk.
|
|
561
740
|
//
|
|
@@ -573,18 +752,6 @@ export function loadConfig(): AssistantConfig {
|
|
|
573
752
|
}
|
|
574
753
|
// Strip dataDir (runtime-derived) from the persisted config
|
|
575
754
|
const { dataDir: _, ...persistable } = config;
|
|
576
|
-
|
|
577
|
-
// Layer deployment context defaults on top of schema defaults.
|
|
578
|
-
// These are overrides the daemon derives from its environment (e.g.
|
|
579
|
-
// IS_PLATFORM → all service modes = "managed"). Schema defaults
|
|
580
|
-
// remain the fallback for non-platform deployments.
|
|
581
|
-
const contextDefaults = getDeploymentContextDefaults();
|
|
582
|
-
if (Object.keys(contextDefaults).length > 0) {
|
|
583
|
-
deepMergeOverwrite(
|
|
584
|
-
persistable as Record<string, unknown>,
|
|
585
|
-
contextDefaults,
|
|
586
|
-
);
|
|
587
|
-
}
|
|
588
755
|
writeFileSync(configPath, JSON.stringify(persistable, null, 2) + "\n");
|
|
589
756
|
log.info("Wrote default config to %s", configPath);
|
|
590
757
|
} catch (err) {
|
|
@@ -593,12 +760,14 @@ export function loadConfig(): AssistantConfig {
|
|
|
593
760
|
}
|
|
594
761
|
|
|
595
762
|
cached = config;
|
|
763
|
+
cachedFileSignature = readConfigFileSignature(configPath);
|
|
596
764
|
|
|
597
765
|
loading = false;
|
|
598
766
|
return config;
|
|
599
767
|
} catch (err) {
|
|
600
768
|
// Loading failed — clear cached so the next call retries
|
|
601
769
|
cached = null;
|
|
770
|
+
cachedFileSignature = null;
|
|
602
771
|
loading = false;
|
|
603
772
|
throw err;
|
|
604
773
|
}
|
|
@@ -632,7 +801,8 @@ export function getConfig(): AssistantConfig {
|
|
|
632
801
|
* workspace-existence check runs.
|
|
633
802
|
*/
|
|
634
803
|
export function getConfigReadOnly(): AssistantConfig {
|
|
635
|
-
|
|
804
|
+
const freshCached = getCachedConfigIfFresh();
|
|
805
|
+
if (freshCached) return freshCached;
|
|
636
806
|
|
|
637
807
|
const configPath = getConfigPath();
|
|
638
808
|
let fileConfig: Record<string, unknown> = {};
|
|
@@ -649,6 +819,7 @@ export function getConfigReadOnly(): AssistantConfig {
|
|
|
649
819
|
|
|
650
820
|
export function invalidateConfigCache(): void {
|
|
651
821
|
cached = null;
|
|
822
|
+
cachedFileSignature = null;
|
|
652
823
|
loading = false;
|
|
653
824
|
}
|
|
654
825
|
|
|
@@ -686,6 +857,7 @@ export function saveRawConfig(config: Record<string, unknown>): void {
|
|
|
686
857
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
687
858
|
|
|
688
859
|
cached = null; // invalidate cache
|
|
860
|
+
cachedFileSignature = null;
|
|
689
861
|
}
|
|
690
862
|
|
|
691
863
|
export function getNestedValue(
|
|
@@ -7,7 +7,7 @@ describe("MemoryV2ConfigSchema", () => {
|
|
|
7
7
|
test("parses an empty object to documented defaults", () => {
|
|
8
8
|
const parsed = MemoryV2ConfigSchema.parse({});
|
|
9
9
|
expect(parsed).toEqual({
|
|
10
|
-
enabled:
|
|
10
|
+
enabled: true,
|
|
11
11
|
sweep_enabled: false,
|
|
12
12
|
d: 0.3,
|
|
13
13
|
c_user: 0.3,
|
|
@@ -15,9 +15,8 @@ describe("MemoryV2ConfigSchema", () => {
|
|
|
15
15
|
c_now: 0.2,
|
|
16
16
|
k: 0.5,
|
|
17
17
|
hops: 2,
|
|
18
|
-
top_k:
|
|
18
|
+
top_k: 25,
|
|
19
19
|
ann_candidate_limit: null,
|
|
20
|
-
top_k_skills: 5,
|
|
21
20
|
epsilon: 0.01,
|
|
22
21
|
dense_weight: 0.85,
|
|
23
22
|
sparse_weight: 0.15,
|
|
@@ -26,6 +25,13 @@ describe("MemoryV2ConfigSchema", () => {
|
|
|
26
25
|
consolidation_interval_hours: 4,
|
|
27
26
|
max_page_chars: 5000,
|
|
28
27
|
consolidation_prompt_path: null,
|
|
28
|
+
rerank: {
|
|
29
|
+
enabled: false,
|
|
30
|
+
top_k: 50,
|
|
31
|
+
alpha: 0.3,
|
|
32
|
+
model: "Alibaba-NLP/gte-reranker-modernbert-base",
|
|
33
|
+
dtype: "q8",
|
|
34
|
+
},
|
|
29
35
|
});
|
|
30
36
|
});
|
|
31
37
|
|
|
@@ -155,12 +161,11 @@ describe("MemoryConfigSchema integration with v2 block", () => {
|
|
|
155
161
|
test("parses an empty memory config and includes a v2 block with defaults", () => {
|
|
156
162
|
const parsed = MemoryConfigSchema.parse({});
|
|
157
163
|
expect(parsed.v2).toBeDefined();
|
|
158
|
-
expect(parsed.v2.enabled).toBe(
|
|
164
|
+
expect(parsed.v2.enabled).toBe(true);
|
|
159
165
|
expect(parsed.v2.sweep_enabled).toBe(false);
|
|
160
166
|
expect(parsed.v2.d).toBe(0.3);
|
|
161
167
|
expect(parsed.v2.dense_weight).toBe(0.85);
|
|
162
168
|
expect(parsed.v2.sparse_weight).toBe(0.15);
|
|
163
|
-
expect(parsed.v2.top_k_skills).toBe(5);
|
|
164
169
|
expect(parsed.v2.consolidation_interval_hours).toBe(4);
|
|
165
170
|
expect(parsed.v2.max_page_chars).toBe(5000);
|
|
166
171
|
});
|
|
@@ -264,6 +264,20 @@ const CATALOG_RECORD: CatalogRecord = {
|
|
|
264
264
|
description: "General-purpose LLM inference call site for skill use.",
|
|
265
265
|
domain: "skills",
|
|
266
266
|
},
|
|
267
|
+
proactiveArtifactDecision: {
|
|
268
|
+
id: "proactiveArtifactDecision",
|
|
269
|
+
displayName: "Proactive Artifact Decision",
|
|
270
|
+
description:
|
|
271
|
+
"Decides what personalized artifact to build for new users based on conversation context.",
|
|
272
|
+
domain: "agentLoop",
|
|
273
|
+
},
|
|
274
|
+
proactiveArtifactBuild: {
|
|
275
|
+
id: "proactiveArtifactBuild",
|
|
276
|
+
displayName: "Proactive Artifact Build",
|
|
277
|
+
description:
|
|
278
|
+
"Builds the personalized artifact in a background conversation with tool access.",
|
|
279
|
+
domain: "agentLoop",
|
|
280
|
+
},
|
|
267
281
|
};
|
|
268
282
|
|
|
269
283
|
// Source of truth for call-site display metadata. API responses and usage
|
|
@@ -121,8 +121,3 @@ export const SlackConfigSchema = z
|
|
|
121
121
|
.describe("Slack bot display name"),
|
|
122
122
|
})
|
|
123
123
|
.describe("Slack channel configuration");
|
|
124
|
-
|
|
125
|
-
export type TwilioConfig = z.infer<typeof TwilioConfigSchema>;
|
|
126
|
-
export type WhatsAppConfig = z.infer<typeof WhatsAppConfigSchema>;
|
|
127
|
-
export type TelegramConfig = z.infer<typeof TelegramConfigSchema>;
|
|
128
|
-
export type SlackConfig = z.infer<typeof SlackConfigSchema>;
|
|
@@ -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(30 * 60_000)
|
|
15
15
|
.describe("Time between heartbeat checks in milliseconds"),
|
|
16
16
|
cronExpression: z
|
|
17
17
|
.string()
|