@vellumai/assistant 0.7.1 → 0.7.2

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.
Files changed (535) hide show
  1. package/ARCHITECTURE.md +32 -49
  2. package/Dockerfile +1 -0
  3. package/README.md +1 -2
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +9 -3
  5. package/bun.lock +26 -26
  6. package/docs/architecture/security.md +20 -0
  7. package/docs/plugins.md +7 -9
  8. package/knip.json +1 -0
  9. package/node_modules/@vellumai/gateway-client/src/index.ts +1 -0
  10. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +39 -1
  11. package/node_modules/@vellumai/gateway-client/src/types.ts +11 -0
  12. package/node_modules/@vellumai/service-contracts/package.json +2 -0
  13. package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +4 -0
  14. package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +107 -0
  15. package/node_modules/@vellumai/service-contracts/src/index.ts +5 -1
  16. package/node_modules/@vellumai/service-contracts/src/ingress.ts +24 -0
  17. package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +84 -0
  18. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +9 -0
  19. package/node_modules/@vellumai/twilio-client/bun.lock +24 -0
  20. package/node_modules/@vellumai/twilio-client/package.json +18 -0
  21. package/node_modules/@vellumai/twilio-client/src/__tests__/twilio-client.test.ts +128 -0
  22. package/node_modules/@vellumai/twilio-client/src/index.ts +179 -0
  23. package/node_modules/@vellumai/twilio-client/tsconfig.json +20 -0
  24. package/openapi.yaml +565 -12
  25. package/package.json +6 -3
  26. package/src/__tests__/app-builder-tool-scripts.test.ts +3 -3
  27. package/src/__tests__/app-bundler.test.ts +170 -1
  28. package/src/__tests__/app-control-flow.test.ts +374 -0
  29. package/src/__tests__/app-control-no-global-cgevent.test.ts +98 -0
  30. package/src/__tests__/app-control-tool-schemas.test.ts +621 -0
  31. package/src/__tests__/app-executors.test.ts +30 -43
  32. package/src/__tests__/approval-routes-http.test.ts +23 -6
  33. package/src/__tests__/assistant-event-hub-machine-name.test.ts +146 -0
  34. package/src/__tests__/assistant-event-hub-targeted.test.ts +257 -0
  35. package/src/__tests__/assistant-event-hub.test.ts +109 -2
  36. package/src/__tests__/assistant-event.test.ts +10 -0
  37. package/src/__tests__/assistant-events-sse-hardening.test.ts +7 -2
  38. package/src/__tests__/assistant-feature-flags-integration.test.ts +11 -7
  39. package/src/__tests__/background-shell-host-bash.test.ts +14 -15
  40. package/src/__tests__/bootstrap-turn-cleanup.test.ts +44 -0
  41. package/src/__tests__/btw-routes.test.ts +13 -4
  42. package/src/__tests__/call-controller.test.ts +49 -1
  43. package/src/__tests__/call-domain.test.ts +0 -2
  44. package/src/__tests__/call-routes-http.test.ts +0 -2
  45. package/src/__tests__/channel-readiness-service.test.ts +59 -1
  46. package/src/__tests__/checker.test.ts +3 -4
  47. package/src/__tests__/config-loader-backfill.test.ts +90 -155
  48. package/src/__tests__/config-loader-platform-defaults.test.ts +196 -0
  49. package/src/__tests__/config-schema-cmd.test.ts +0 -1
  50. package/src/__tests__/config-set-platform-guard.test.ts +48 -4
  51. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +2 -2
  52. package/src/__tests__/config-watcher.test.ts +2 -2
  53. package/src/__tests__/conversation-app-control-instantiation.test.ts +392 -0
  54. package/src/__tests__/conversation-app-control-lifecycle.test.ts +237 -0
  55. package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
  56. package/src/__tests__/conversation-lifecycle.test.ts +36 -0
  57. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +283 -0
  58. package/src/__tests__/conversation-routes-disk-view.test.ts +6 -0
  59. package/src/__tests__/conversation-routes-guardian-reply.test.ts +120 -72
  60. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  61. package/src/__tests__/conversation-slash-commands.test.ts +0 -4
  62. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +202 -0
  63. package/src/__tests__/conversation-surfaces-app-control.test.ts +317 -0
  64. package/src/__tests__/credential-execution-feature-gates.test.ts +5 -12
  65. package/src/__tests__/credential-execution-managed-contract.test.ts +3 -131
  66. package/src/__tests__/credentials-cli.test.ts +5 -12
  67. package/src/__tests__/cu-unified-flow.test.ts +185 -23
  68. package/src/__tests__/daemon-credential-client.test.ts +101 -19
  69. package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
  70. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  71. package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
  72. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -2
  73. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -2
  74. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -1
  75. package/src/__tests__/heartbeat-service.test.ts +718 -1
  76. package/src/__tests__/helpers/call-route-handler.ts +7 -1
  77. package/src/__tests__/host-app-control-proxy.test.ts +602 -0
  78. package/src/__tests__/host-app-control-routes.test.ts +263 -0
  79. package/src/__tests__/host-bash-proxy.test.ts +246 -47
  80. package/src/__tests__/host-bash-routes.test.ts +294 -0
  81. package/src/__tests__/host-browser-proxy.test.ts +24 -22
  82. package/src/__tests__/host-browser-routes.test.ts +39 -13
  83. package/src/__tests__/host-cu-proxy.test.ts +41 -52
  84. package/src/__tests__/host-cu-routes-targeted.test.ts +300 -0
  85. package/src/__tests__/host-file-edit-tool.test.ts +47 -1
  86. package/src/__tests__/host-file-proxy-targeted.test.ts +339 -0
  87. package/src/__tests__/host-file-proxy.test.ts +37 -43
  88. package/src/__tests__/host-file-read-tool.test.ts +17 -0
  89. package/src/__tests__/host-file-routes-targeted.test.ts +262 -0
  90. package/src/__tests__/host-file-write-tool.test.ts +42 -1
  91. package/src/__tests__/host-proxy-base.test.ts +312 -0
  92. package/src/__tests__/host-shell-tool.test.ts +22 -4
  93. package/src/__tests__/host-transfer-proxy-targeted.test.ts +583 -0
  94. package/src/__tests__/host-transfer-proxy.test.ts +121 -22
  95. package/src/__tests__/host-transfer-routes-targeted.test.ts +447 -0
  96. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  97. package/src/__tests__/identity-intro-cache.test.ts +29 -0
  98. package/src/__tests__/identity-routes.test.ts +103 -1
  99. package/src/__tests__/init-feature-flag-overrides.test.ts +26 -3
  100. package/src/__tests__/inline-command-runner.test.ts +0 -1
  101. package/src/__tests__/inline-skill-load-permissions.test.ts +5 -11
  102. package/src/__tests__/integration-status.test.ts +85 -5
  103. package/src/__tests__/intent-routing.test.ts +0 -1
  104. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +95 -5
  105. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +17 -0
  106. package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
  107. package/src/__tests__/mcp-auth-routes.test.ts +197 -0
  108. package/src/__tests__/mcp-cli.test.ts +338 -2
  109. package/src/__tests__/memory-jobs-worker-lanes.test.ts +188 -0
  110. package/src/__tests__/migration-import-commit-http.test.ts +108 -2
  111. package/src/__tests__/mock-gateway-ipc.ts +1 -0
  112. package/src/__tests__/oauth-cli.test.ts +0 -2
  113. package/src/__tests__/oauth2-gateway-transport.test.ts +0 -1
  114. package/src/__tests__/persistence-secret-redaction.test.ts +299 -0
  115. package/src/__tests__/platform-bash-auto-approve.test.ts +5 -9
  116. package/src/__tests__/prechat-onboarding-contract.test.ts +3 -1
  117. package/src/__tests__/process-message-background-slack.test.ts +2 -0
  118. package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
  119. package/src/__tests__/public-ingress-urls.test.ts +97 -0
  120. package/src/__tests__/require-fresh-approval.test.ts +0 -1
  121. package/src/__tests__/retry-backoff.test.ts +87 -0
  122. package/src/__tests__/runtime-events-sse.test.ts +10 -6
  123. package/src/__tests__/sanitize-config-for-transfer.test.ts +24 -2
  124. package/src/__tests__/schedule-retry.test.ts +715 -0
  125. package/src/__tests__/script-proxy-mitm-handler.test.ts +1 -1
  126. package/src/__tests__/secret-ingress-http.test.ts +1 -0
  127. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  128. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -1
  129. package/src/__tests__/skill-feature-flags.test.ts +43 -41
  130. package/src/__tests__/skill-load-feature-flag.test.ts +13 -14
  131. package/src/__tests__/skill-load-inline-command.test.ts +0 -51
  132. package/src/__tests__/skill-load-inline-includes.test.ts +0 -43
  133. package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
  134. package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -1
  135. package/src/__tests__/slack-channel-config.test.ts +9 -14
  136. package/src/__tests__/system-prompt-ask-mode.test.ts +0 -1
  137. package/src/__tests__/system-prompt.test.ts +0 -1
  138. package/src/__tests__/telegram-config.test.ts +0 -1
  139. package/src/__tests__/test-preload.ts +8 -0
  140. package/src/__tests__/tool-approval-handler.test.ts +3 -4
  141. package/src/__tests__/tool-audit-listener.test.ts +48 -0
  142. package/src/__tests__/tool-execute-pipeline.test.ts +0 -1
  143. package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
  144. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
  145. package/src/__tests__/tool-executor.test.ts +0 -1
  146. package/src/__tests__/twilio-config.test.ts +3 -16
  147. package/src/__tests__/twilio-routes.test.ts +3 -5
  148. package/src/__tests__/twilio-validation.test.ts +93 -0
  149. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -4
  150. package/src/__tests__/verification-control-plane-policy.test.ts +2 -4
  151. package/src/__tests__/voice-ingress-preflight.test.ts +19 -0
  152. package/src/__tests__/workspace-migration-006-services-config.test.ts +3 -2
  153. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +1 -5
  154. package/src/__tests__/workspace-migration-down-functions.test.ts +8 -8
  155. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +10 -6
  156. package/src/backup/__tests__/paths.test.ts +0 -22
  157. package/src/backup/__tests__/restore.test.ts +51 -151
  158. package/src/backup/paths.ts +2 -18
  159. package/src/backup/restore.ts +107 -231
  160. package/src/bundler/app-bundler.ts +51 -3
  161. package/src/calls/relay-server.ts +4 -44
  162. package/src/calls/twilio-config.ts +2 -17
  163. package/src/calls/twilio-rest.ts +33 -105
  164. package/src/calls/twilio-routes.ts +11 -12
  165. package/src/channels/types.ts +8 -7
  166. package/src/cli/commands/__tests__/backup.test.ts +6 -277
  167. package/src/cli/commands/__tests__/gateway.test.ts +288 -0
  168. package/src/cli/commands/__tests__/memory-v2.test.ts +4 -0
  169. package/src/cli/commands/__tests__/webhooks.test.ts +0 -1
  170. package/src/cli/commands/backup.ts +6 -331
  171. package/src/cli/commands/clients.ts +36 -37
  172. package/src/cli/commands/contacts.ts +73 -0
  173. package/src/cli/commands/conversations.ts +2 -5
  174. package/src/cli/commands/credentials.ts +15 -7
  175. package/src/cli/commands/domain.ts +66 -15
  176. package/src/cli/commands/gateway.ts +183 -0
  177. package/src/cli/commands/keys.ts +9 -6
  178. package/src/cli/commands/mcp.ts +116 -156
  179. package/src/cli/commands/memory-v2.ts +296 -1
  180. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
  181. package/src/cli/commands/platform/__tests__/connect.test.ts +0 -2
  182. package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -2
  183. package/src/cli/commands/platform/__tests__/status.test.ts +13 -15
  184. package/src/cli/commands/platform/disconnect.ts +5 -4
  185. package/src/cli/commands/platform/index.ts +0 -18
  186. package/src/cli/lib/daemon-credential-client.ts +110 -28
  187. package/src/cli/program.ts +2 -0
  188. package/src/config/assistant-feature-flags.ts +67 -10
  189. package/src/config/bundled-skills/acp/SKILL.md +6 -0
  190. package/src/config/bundled-skills/acp/TOOLS.json +1 -22
  191. package/src/config/bundled-skills/app-builder/SKILL.md +14 -109
  192. package/src/config/bundled-skills/app-builder/TOOLS.json +1 -28
  193. package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -10
  194. package/src/config/bundled-skills/app-control/SKILL.md +75 -0
  195. package/src/config/bundled-skills/app-control/TOOLS.json +299 -0
  196. package/src/config/bundled-skills/app-control/tools/app-control-click.ts +12 -0
  197. package/src/config/bundled-skills/app-control/tools/app-control-combo.ts +12 -0
  198. package/src/config/bundled-skills/app-control/tools/app-control-drag.ts +12 -0
  199. package/src/config/bundled-skills/app-control/tools/app-control-observe.ts +12 -0
  200. package/src/config/bundled-skills/app-control/tools/app-control-press.ts +12 -0
  201. package/src/config/bundled-skills/app-control/tools/app-control-sequence.ts +12 -0
  202. package/src/config/bundled-skills/app-control/tools/app-control-start.ts +12 -0
  203. package/src/config/bundled-skills/app-control/tools/app-control-stop.ts +12 -0
  204. package/src/config/bundled-skills/app-control/tools/app-control-type.ts +12 -0
  205. package/src/config/bundled-skills/computer-use/SKILL.md +6 -0
  206. package/src/config/bundled-skills/computer-use/TOOLS.json +67 -43
  207. package/src/config/bundled-skills/contacts/TOOLS.json +0 -16
  208. package/src/config/bundled-skills/document/TOOLS.json +0 -8
  209. package/src/config/bundled-skills/followups/TOOLS.json +0 -12
  210. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  211. package/src/config/bundled-skills/image-studio/TOOLS.json +0 -4
  212. package/src/config/bundled-skills/media-processing/TOOLS.json +0 -24
  213. package/src/config/bundled-skills/messaging/TOOLS.json +0 -40
  214. package/src/config/bundled-skills/phone-calls/TOOLS.json +0 -12
  215. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +19 -4
  216. package/src/config/bundled-skills/playbooks/TOOLS.json +0 -16
  217. package/src/config/bundled-skills/schedule/TOOLS.json +14 -14
  218. package/src/config/bundled-skills/sequences/TOOLS.json +0 -36
  219. package/src/config/bundled-skills/settings/SKILL.md +4 -0
  220. package/src/config/bundled-skills/settings/TOOLS.json +0 -12
  221. package/src/config/bundled-skills/skill-management/SKILL.md +6 -0
  222. package/src/config/bundled-skills/skill-management/TOOLS.json +0 -8
  223. package/src/config/bundled-skills/subagent/SKILL.md +6 -2
  224. package/src/config/bundled-skills/subagent/TOOLS.json +0 -20
  225. package/src/config/bundled-skills/transcribe/SKILL.md +4 -0
  226. package/src/config/bundled-skills/transcribe/TOOLS.json +0 -4
  227. package/src/config/bundled-tool-registry.ts +21 -0
  228. package/src/config/env-registry.ts +0 -2
  229. package/src/config/env.ts +19 -12
  230. package/src/config/feature-flag-registry.json +21 -133
  231. package/src/config/loader.ts +73 -99
  232. package/src/config/sanitize-for-transfer.ts +2 -0
  233. package/src/config/schemas/__tests__/memory-lifecycle.test.ts +80 -0
  234. package/src/config/schemas/__tests__/memory-v2.test.ts +7 -4
  235. package/src/config/schemas/calls.ts +0 -9
  236. package/src/config/schemas/heartbeat.ts +63 -0
  237. package/src/config/schemas/ingress.ts +10 -6
  238. package/src/config/schemas/llm.ts +5 -10
  239. package/src/config/schemas/memory-lifecycle.ts +77 -24
  240. package/src/config/schemas/memory-v2.ts +48 -4
  241. package/src/config/schemas/platform.ts +6 -0
  242. package/src/config/schemas/services.ts +1 -15
  243. package/src/config/schemas/skills.ts +0 -6
  244. package/src/config/seed-inference-profiles.ts +1 -1
  245. package/src/contacts/contact-store.ts +0 -30
  246. package/src/contacts/contacts-write.ts +0 -27
  247. package/src/context/window-manager.ts +1 -2
  248. package/src/credential-execution/feature-gates.ts +10 -10
  249. package/src/credential-execution/process-manager.ts +12 -41
  250. package/src/daemon/__tests__/conversation-tool-setup.test.ts +126 -5
  251. package/src/daemon/bootstrap-turn-cleanup.ts +45 -0
  252. package/src/daemon/config-watcher.ts +4 -3
  253. package/src/daemon/conversation-agent-loop-handlers.ts +21 -3
  254. package/src/daemon/conversation-agent-loop.ts +32 -28
  255. package/src/daemon/conversation-lifecycle.ts +8 -1
  256. package/src/daemon/conversation-process.ts +16 -11
  257. package/src/daemon/conversation-runtime-assembly.ts +2 -2
  258. package/src/daemon/conversation-surfaces.ts +125 -4
  259. package/src/daemon/conversation-tool-setup.ts +16 -55
  260. package/src/daemon/conversation.ts +21 -2
  261. package/src/daemon/doordash-steps.ts +1 -1
  262. package/src/daemon/handlers/shared.ts +4 -1
  263. package/src/daemon/host-app-control-proxy.ts +293 -0
  264. package/src/daemon/host-bash-proxy.ts +84 -74
  265. package/src/daemon/host-browser-proxy.ts +67 -82
  266. package/src/daemon/host-cu-proxy.ts +81 -86
  267. package/src/daemon/host-file-proxy.ts +93 -69
  268. package/src/daemon/host-proxy-base.ts +294 -0
  269. package/src/daemon/host-proxy-preactivation.ts +82 -0
  270. package/src/daemon/host-transfer-proxy.ts +247 -129
  271. package/src/daemon/lifecycle.ts +115 -117
  272. package/src/daemon/message-protocol.ts +3 -8
  273. package/src/daemon/message-types/contacts.ts +23 -1
  274. package/src/daemon/message-types/conversations.ts +11 -8
  275. package/src/daemon/message-types/host-app-control.ts +150 -0
  276. package/src/daemon/message-types/host-bash.ts +4 -0
  277. package/src/daemon/message-types/host-cu.ts +2 -0
  278. package/src/daemon/message-types/host-file.ts +4 -0
  279. package/src/daemon/message-types/host-transfer.ts +3 -0
  280. package/src/daemon/message-types/schedules.ts +8 -3
  281. package/src/daemon/message-types/skills.ts +2 -2
  282. package/src/daemon/process-message.ts +18 -1
  283. package/src/daemon/shutdown-handlers.ts +0 -3
  284. package/src/daemon/tool-setup-types.ts +51 -0
  285. package/src/daemon/tool-side-effects.ts +1 -1
  286. package/src/events/tool-audit-listener.ts +2 -1
  287. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +15 -7
  288. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +216 -0
  289. package/src/heartbeat/heartbeat-run-store.ts +236 -0
  290. package/src/heartbeat/heartbeat-service.ts +280 -49
  291. package/src/home/__tests__/post-connect-feed.test.ts +99 -0
  292. package/src/home/__tests__/relationship-state-writer.test.ts +11 -9
  293. package/src/home/__tests__/suggested-prompts.test.ts +89 -0
  294. package/src/home/post-connect-feed.ts +68 -0
  295. package/src/home/relationship-state-writer.ts +17 -92
  296. package/src/home/suggested-prompts.ts +46 -10
  297. package/src/inbound/public-ingress-urls.ts +32 -34
  298. package/src/ipc/__tests__/route-error-envelope.test.ts +80 -0
  299. package/src/ipc/assistant-server.ts +14 -1
  300. package/src/ipc/cli-client.ts +32 -1
  301. package/src/live-voice/live-voice-metrics.ts +10 -10
  302. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +304 -0
  303. package/src/mcp/mcp-auth-orchestrator.ts +213 -0
  304. package/src/mcp/mcp-auth-state.ts +133 -0
  305. package/src/mcp/mcp-oauth-provider.ts +19 -0
  306. package/src/memory/__tests__/jobs-store-job-classes.test.ts +24 -0
  307. package/src/memory/__tests__/qdrant-client-sentinel.test.ts +49 -0
  308. package/src/memory/__tests__/sparse-tokenize.test.ts +66 -0
  309. package/src/memory/anisotropy.test.ts +247 -0
  310. package/src/memory/anisotropy.ts +443 -0
  311. package/src/memory/auto-analysis-constants.ts +17 -0
  312. package/src/memory/auto-analysis-guard.ts +5 -15
  313. package/src/memory/canonical-guardian-store.ts +7 -7
  314. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +122 -0
  315. package/src/memory/context-search/agent-protocol.ts +6 -6
  316. package/src/memory/context-search/agent-runner.ts +32 -7
  317. package/src/memory/context-search/sources/memory-v2.ts +17 -5
  318. package/src/memory/conversation-crud.ts +1 -1
  319. package/src/memory/conversation-key-store.ts +2 -15
  320. package/src/memory/db-init.ts +4 -0
  321. package/src/memory/embedding-backend.ts +9 -21
  322. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +49 -4
  323. package/src/memory/graph/conversation-graph-memory.ts +1 -24
  324. package/src/memory/graph/graph-search.ts +8 -0
  325. package/src/memory/graph/retriever.ts +28 -0
  326. package/src/memory/graph/tools.ts +1 -1
  327. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +8 -2
  328. package/src/memory/jobs/embed-concept-page.ts +28 -2
  329. package/src/memory/jobs/embed-pkb-file.test.ts +2 -2
  330. package/src/memory/jobs-store.ts +66 -22
  331. package/src/memory/jobs-worker.ts +112 -63
  332. package/src/memory/memory-v2-activation-log-store.ts +1 -1
  333. package/src/memory/migrations/237-heartbeat-runs.ts +45 -0
  334. package/src/memory/migrations/238-schedule-retry-policy.ts +20 -0
  335. package/src/memory/migrations/index.ts +5 -0
  336. package/src/memory/migrations/registry.ts +8 -0
  337. package/src/memory/pkb/pkb-search.ts +7 -0
  338. package/src/memory/qdrant-client.ts +50 -20
  339. package/src/memory/schema/infrastructure.ts +15 -0
  340. package/src/memory/search/semantic.ts +7 -0
  341. package/src/memory/sparse-tokenize.ts +49 -0
  342. package/src/memory/v2/__tests__/activation.test.ts +77 -95
  343. package/src/memory/v2/__tests__/injection.test.ts +43 -21
  344. package/src/memory/v2/__tests__/sim.test.ts +166 -6
  345. package/src/memory/v2/__tests__/sparse-bm25.test.ts +292 -0
  346. package/src/memory/v2/__tests__/static-context.test.ts +0 -1
  347. package/src/memory/v2/activation.ts +69 -88
  348. package/src/memory/v2/consolidation-job.ts +3 -5
  349. package/src/memory/v2/constants.ts +7 -0
  350. package/src/memory/v2/injection.ts +86 -53
  351. package/src/memory/v2/prompts/consolidation.ts +312 -91
  352. package/src/memory/v2/qdrant.ts +99 -1
  353. package/src/memory/v2/sim.ts +126 -16
  354. package/src/memory/v2/skill-qdrant.ts +12 -3
  355. package/src/memory/v2/skill-store.ts +16 -1
  356. package/src/memory/v2/sparse-bm25.ts +245 -0
  357. package/src/memory/v2/static-context.ts +6 -5
  358. package/src/messaging/providers/gmail/types.ts +0 -49
  359. package/src/messaging/providers/slack/adapter.ts +1 -31
  360. package/src/messaging/providers/slack/types.ts +0 -32
  361. package/src/notifications/README.md +10 -10
  362. package/src/notifications/broadcaster.ts +1 -1
  363. package/src/notifications/guardian-question-mode.ts +5 -5
  364. package/src/oauth/connect-orchestrator.ts +4 -0
  365. package/src/oauth/credential-token-resolver.ts +1 -3
  366. package/src/oauth/manual-token-connection.ts +0 -4
  367. package/src/outbound-proxy/index.ts +1 -37
  368. package/src/outbound-proxy/logging.ts +1 -1
  369. package/src/outbound-proxy/policy.ts +6 -5
  370. package/src/outbound-proxy/router.ts +2 -1
  371. package/src/permissions/approval-policy.test.ts +6 -275
  372. package/src/permissions/approval-policy.ts +0 -51
  373. package/src/permissions/checker.test.ts +0 -1
  374. package/src/permissions/checker.ts +3 -17
  375. package/src/permissions/gateway-threshold-reader.ts +2 -0
  376. package/src/permissions/prompter.ts +34 -1
  377. package/src/permissions/secret-prompter.ts +6 -2
  378. package/src/prompts/bootstrap-cleanup.ts +27 -0
  379. package/src/prompts/system-prompt.ts +3 -18
  380. package/src/prompts/templates/SOUL.md +13 -1
  381. package/src/providers/speech-to-text/provider-catalog.ts +7 -8
  382. package/src/runtime/assistant-event-hub.ts +118 -96
  383. package/src/runtime/assistant-event.ts +1 -0
  384. package/src/runtime/auth/__tests__/middleware.test.ts +11 -56
  385. package/src/runtime/auth/middleware.ts +0 -96
  386. package/src/runtime/auth/route-policy.ts +19 -0
  387. package/src/runtime/btw-sidechain.ts +2 -3
  388. package/src/runtime/channel-invite-transport.ts +2 -48
  389. package/src/runtime/channel-invite-transports/email.ts +1 -1
  390. package/src/runtime/channel-invite-transports/slack.ts +1 -1
  391. package/src/runtime/channel-invite-transports/telegram.ts +1 -1
  392. package/src/runtime/channel-invite-transports/voice.ts +1 -1
  393. package/src/runtime/channel-invite-transports/whatsapp.ts +1 -1
  394. package/src/runtime/channel-invite-types.ts +54 -0
  395. package/src/runtime/channel-readiness-service.ts +32 -13
  396. package/src/runtime/http-server.ts +3 -329
  397. package/src/runtime/http-types.ts +0 -5
  398. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +413 -0
  399. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +260 -0
  400. package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +189 -0
  401. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +153 -1
  402. package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +451 -0
  403. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  404. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +515 -0
  405. package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +437 -0
  406. package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +319 -0
  407. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +51 -1
  408. package/src/runtime/migrations/migration-transport.ts +7 -7
  409. package/src/runtime/migrations/vbundle-builder.ts +327 -60
  410. package/src/runtime/migrations/vbundle-import-analyzer.ts +4 -4
  411. package/src/runtime/migrations/vbundle-import-policy.ts +172 -0
  412. package/src/runtime/migrations/vbundle-importer.ts +245 -68
  413. package/src/runtime/migrations/vbundle-streaming-importer.ts +326 -35
  414. package/src/runtime/migrations/vbundle-streaming-validator.ts +157 -4
  415. package/src/runtime/migrations/vbundle-tar-stream.ts +15 -6
  416. package/src/runtime/migrations/vbundle-validator.ts +114 -0
  417. package/src/runtime/pending-interactions.ts +35 -9
  418. package/src/runtime/routes/__tests__/backup-routes.test.ts +22 -150
  419. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
  420. package/src/runtime/routes/__tests__/gateway-log-routes.test.ts +242 -0
  421. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +112 -0
  422. package/src/runtime/routes/approval-interception-types.ts +13 -0
  423. package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +1 -1
  424. package/src/runtime/routes/backup-routes.ts +15 -38
  425. package/src/runtime/routes/btw-routes.ts +14 -37
  426. package/src/runtime/routes/client-routes.ts +1 -0
  427. package/src/runtime/routes/contact-prompt-routes.ts +183 -0
  428. package/src/runtime/routes/conversation-query-routes.ts +36 -1
  429. package/src/runtime/routes/conversation-routes.ts +30 -13
  430. package/src/runtime/routes/document-pdf-renderer.ts +165 -0
  431. package/src/runtime/routes/documents-routes.ts +30 -0
  432. package/src/runtime/routes/errors.ts +19 -4
  433. package/src/runtime/routes/events-routes.ts +12 -6
  434. package/src/runtime/routes/gateway-log-routes.ts +79 -0
  435. package/src/runtime/routes/guardian-approval-interception.ts +2 -8
  436. package/src/runtime/routes/heartbeat-routes.ts +103 -38
  437. package/src/runtime/routes/host-app-control-routes.ts +134 -0
  438. package/src/runtime/routes/host-bash-routes.ts +36 -6
  439. package/src/runtime/routes/host-browser-routes.ts +108 -13
  440. package/src/runtime/routes/host-cu-routes.ts +44 -14
  441. package/src/runtime/routes/host-file-routes.ts +33 -10
  442. package/src/runtime/routes/host-transfer-routes.ts +64 -24
  443. package/src/runtime/routes/http-adapter.ts +1 -0
  444. package/src/runtime/routes/identity-intro-cache.ts +30 -0
  445. package/src/runtime/routes/identity-routes.ts +15 -43
  446. package/src/runtime/routes/inbound-message-handler.ts +1 -9
  447. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -7
  448. package/src/runtime/routes/inbound-stages/edit-intercept.ts +0 -8
  449. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +0 -20
  450. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +5 -13
  451. package/src/runtime/routes/index.ts +8 -0
  452. package/src/runtime/routes/mcp-auth-routes.ts +132 -0
  453. package/src/runtime/routes/memory-item-routes.ts +10 -12
  454. package/src/runtime/routes/memory-v2-routes.ts +441 -1
  455. package/src/runtime/routes/migration-routes.ts +96 -0
  456. package/src/runtime/routes/schedule-routes.ts +7 -0
  457. package/src/runtime/verification-templates.ts +4 -7
  458. package/src/schedule/integration-status.ts +66 -2
  459. package/src/schedule/recurrence-engine.ts +4 -1
  460. package/src/schedule/retry-backoff.ts +18 -0
  461. package/src/schedule/retry-policy.ts +82 -0
  462. package/src/schedule/schedule-recovery.ts +64 -0
  463. package/src/schedule/schedule-store.ts +106 -2
  464. package/src/schedule/scheduler-types.ts +25 -0
  465. package/src/schedule/scheduler.ts +63 -38
  466. package/src/security/oauth-callback-registry.ts +8 -0
  467. package/src/sequence/analytics.ts +5 -5
  468. package/src/sequence/engine.ts +1 -1
  469. package/src/skills/catalog-files.ts +2 -8
  470. package/src/skills/include-graph.ts +5 -5
  471. package/src/skills/remote-skill-policy.ts +5 -5
  472. package/src/skills/skill-file-provider.ts +1 -1
  473. package/src/skills/skill-file-types.ts +13 -0
  474. package/src/skills/skillssh-audit-types.ts +28 -0
  475. package/src/skills/skillssh-registry.ts +8 -21
  476. package/src/telemetry/types.ts +2 -0
  477. package/src/telemetry/usage-telemetry-reporter.test.ts +21 -0
  478. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  479. package/src/tools/app-control/skill-proxy-bridge.ts +28 -0
  480. package/src/tools/apps/executors.ts +56 -69
  481. package/src/tools/browser/__tests__/browser-status.test.ts +21 -18
  482. package/src/tools/browser/browser-execution.ts +2 -2
  483. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +55 -4
  484. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +12 -6
  485. package/src/tools/browser/cdp-client/factory.ts +23 -24
  486. package/src/tools/browser/cdp-client/index.ts +1 -14
  487. package/src/tools/computer-use/definitions.ts +42 -20
  488. package/src/tools/executor.ts +2 -0
  489. package/src/tools/host-filesystem/edit.ts +26 -0
  490. package/src/tools/host-filesystem/read.ts +26 -0
  491. package/src/tools/host-filesystem/transfer.ts +31 -1
  492. package/src/tools/host-filesystem/write.ts +26 -0
  493. package/src/tools/host-terminal/host-shell.ts +58 -0
  494. package/src/tools/schedule/create.ts +6 -0
  495. package/src/tools/schedule/list.ts +2 -0
  496. package/src/tools/schedule/update.ts +10 -0
  497. package/src/tools/shared/filesystem/file-ops-service.ts +2 -0
  498. package/src/tools/shared/filesystem/path-policy.ts +25 -1
  499. package/src/tools/skills/load.ts +0 -32
  500. package/src/tools/tool-approval-handler.ts +1 -5
  501. package/src/tools/types.ts +4 -0
  502. package/src/usage/pricing.ts +1 -1
  503. package/src/workspace/hatched-date.ts +86 -0
  504. package/src/workspace/migrations/003-seed-device-id.ts +1 -1
  505. package/src/workspace/migrations/006-services-config.ts +8 -5
  506. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +3 -9
  507. package/src/workspace/migrations/021-move-signals-to-workspace.ts +4 -10
  508. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +4 -10
  509. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +4 -11
  510. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +3 -10
  511. package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +3 -2
  512. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +2 -1
  513. package/src/workspace/migrations/059-move-pid-to-workspace.ts +3 -8
  514. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +3 -8
  515. package/src/workspace/migrations/AGENTS.md +1 -1
  516. package/src/workspace/migrations/migrate-to-workspace-volume.ts +4 -10
  517. package/src/workspace/migrations/utils.ts +21 -0
  518. package/src/__tests__/host-browser-e2e-cloud.test.ts +0 -443
  519. package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +0 -226
  520. package/src/__tests__/host-browser-ws-events-e2e.test.ts +0 -427
  521. package/src/__tests__/twilio-rest.test.ts +0 -34
  522. package/src/backup/__tests__/backup-key.test.ts +0 -152
  523. package/src/backup/__tests__/backup-worker.test.ts +0 -782
  524. package/src/backup/__tests__/offsite-writer.test.ts +0 -641
  525. package/src/backup/__tests__/stream-crypt.test.ts +0 -228
  526. package/src/backup/backup-key.ts +0 -137
  527. package/src/backup/backup-worker.ts +0 -472
  528. package/src/backup/offsite-writer.ts +0 -222
  529. package/src/backup/stream-crypt.ts +0 -263
  530. package/src/daemon/message-types/pairing.ts +0 -58
  531. package/src/outbound-proxy/config.ts +0 -20
  532. package/src/outbound-proxy/health.ts +0 -18
  533. package/src/outbound-proxy/types.ts +0 -150
  534. package/src/runtime/capability-tokens.ts +0 -190
  535. package/src/signals/mcp-reload.ts +0 -18
@@ -36,6 +36,10 @@ import { cliIpcCall } from "../../ipc/cli-client.js";
36
36
  import type {
37
37
  MemoryV2BackfillOp,
38
38
  MemoryV2BackfillResult,
39
+ MemoryV2ExplainSimilarityResult,
40
+ MemoryV2ExplainSimilarityStats,
41
+ MemoryV2FitAnisotropyResult,
42
+ MemoryV2RebuildCorpusStatsResult,
39
43
  MemoryV2ReembedSkillsResult,
40
44
  MemoryV2ValidateResult,
41
45
  } from "../../runtime/routes/memory-v2-routes.js";
@@ -73,6 +77,74 @@ async function runBackfillOp(
73
77
  log.info(`Queued ${op} job: ${result.result!.jobId}`);
74
78
  }
75
79
 
80
+ /** Format a number for table output. */
81
+ function fmt(n: number | null, decimals: number): string {
82
+ if (n === null) return "—";
83
+ return n.toFixed(decimals);
84
+ }
85
+
86
+ /** Render the per-channel breakdown table + stats for the explain command. */
87
+ function printExplainResult(result: MemoryV2ExplainSimilarityResult): void {
88
+ log.info(
89
+ `dense_weight=${result.config.dense_weight} sparse_weight=${result.config.sparse_weight}`,
90
+ );
91
+
92
+ for (const channel of result.channels) {
93
+ log.info("");
94
+ log.info(`── channel: ${channel.channel} ──`);
95
+ log.info(`text: ${channel.textPreview}`);
96
+ log.info(
97
+ `maxSparse (used for normalization): ${channel.maxSparse.toFixed(4)}`,
98
+ );
99
+ log.info(
100
+ `effective: dw=${channel.effectiveDenseWeight.toFixed(3)} sw=${channel.effectiveSparseWeight.toFixed(3)} (sparseSpread=${channel.sparseSpread.toFixed(3)})`,
101
+ );
102
+ log.info("");
103
+ log.info(
104
+ "slug".padEnd(48) +
105
+ "dense".padStart(10) +
106
+ "sparseRaw".padStart(12) +
107
+ "sparseNorm".padStart(12) +
108
+ "fused".padStart(10),
109
+ );
110
+ log.info("─".repeat(92));
111
+ for (const row of channel.rows) {
112
+ const slugCol =
113
+ row.slug.length > 47 ? `${row.slug.slice(0, 46)}…` : row.slug;
114
+ log.info(
115
+ slugCol.padEnd(48) +
116
+ fmt(row.denseScore, 4).padStart(10) +
117
+ fmt(row.sparseRaw, 4).padStart(12) +
118
+ fmt(row.sparseNorm, 4).padStart(12) +
119
+ fmt(row.fused, 4).padStart(10),
120
+ );
121
+ }
122
+ log.info("");
123
+ log.info("Stats (per channel):");
124
+ log.info(` ${formatStatLine("dense ", channel.stats.dense)}`);
125
+ log.info(` ${formatStatLine("sparseRaw ", channel.stats.sparseRaw)}`);
126
+ log.info(` ${formatStatLine("sparseNorm ", channel.stats.sparseNorm)}`);
127
+ log.info(` ${formatStatLine("fused ", channel.stats.fused)}`);
128
+ }
129
+ }
130
+
131
+ function formatStatLine(
132
+ label: string,
133
+ stats: MemoryV2ExplainSimilarityStats,
134
+ ): string {
135
+ if (stats.count === 0) {
136
+ return `${label} n=0`;
137
+ }
138
+ const range = stats.max - stats.min;
139
+ return (
140
+ `${label} n=${String(stats.count).padStart(3)}` +
141
+ ` range=[${stats.min.toFixed(4)}, ${stats.max.toFixed(4)}]` +
142
+ ` (Δ=${range.toFixed(4)})` +
143
+ ` mean=${stats.mean.toFixed(4)}` +
144
+ ` std=${stats.stddev.toFixed(4)}`
145
+ );
146
+ }
147
+
76
148
  // ---------------------------------------------------------------------------
77
149
  // Registration
78
150
  // ---------------------------------------------------------------------------
@@ -119,13 +191,18 @@ Subcommands fall into three groups:
119
191
  validate Print a diagnostic report of orphan outgoing-edge
120
192
  targets, oversized pages, and parse failures.
121
193
 
194
+ Calibration:
195
+ fit-anisotropy Fit Mu & Viswanath's all-but-the-top correction so
196
+ embedding cosines stop clustering in a narrow band.
197
+
122
198
  Examples:
123
199
  $ assistant memory v2 validate
124
200
  $ assistant memory v2 migrate
125
201
  $ assistant memory v2 migrate --force
126
202
  $ assistant memory v2 reembed
127
203
  $ assistant memory v2 reembed-skills
128
- $ assistant memory v2 activation`,
204
+ $ assistant memory v2 activation
205
+ $ assistant memory v2 fit-anisotropy`,
129
206
  );
130
207
 
131
208
  // ── migrate ───────────────────────────────────────────────────────────
@@ -241,6 +318,224 @@ Examples:
241
318
  await runBackfillOp("activation-recompute");
242
319
  });
243
320
 
321
+ // ── explain ───────────────────────────────────────────────────────────
322
+
323
+ v2.command("explain")
324
+ .description(
325
+ "Diagnose dense vs sparse score distributions for a query (read-only)",
326
+ )
327
+ .requiredOption(
328
+ "--text <text>",
329
+ "Query text to embed and score against the concept-page collection (the user channel).",
330
+ )
331
+ .option(
332
+ "--assistant-text <text>",
333
+ "Optional second query text — scored independently as the assistant channel.",
334
+ )
335
+ .option(
336
+ "--now-text <text>",
337
+ "Optional third query text — scored independently as the now channel.",
338
+ )
339
+ .option(
340
+ "--top <n>",
341
+ "Number of top hits to fetch per channel (default 25)",
342
+ "25",
343
+ )
344
+ .addHelpText(
345
+ "after",
346
+ `
347
+ Embeds the supplied text(s), runs the hybrid dense + sparse query against
348
+ the v2 concept-page Qdrant collection, and prints per-slug raw dense, raw
349
+ sparse, normalized sparse, and fused scores plus per-channel summary
350
+ statistics (range, mean, stddev). Use this to identify whether dense
351
+ embedding compression (anisotropy) or per-batch sparse normalization is
352
+ the dominant cause of score compression at the head of the activation
353
+ distribution.
354
+
355
+ Read-only: does not mutate Qdrant, the workspace, or the activation log.
356
+
357
+ Interpretation:
358
+ Dense range < 0.1 AND sparseNorm range > 0.5 → embedding anisotropy
359
+ Dense range > 0.2 AND sparseNorm range < 0.1 → sparse max-normalization
360
+ Both compressed → both contribute
361
+ Both wide → channel mixing is the cause
362
+
363
+ Examples:
364
+ $ assistant memory v2 explain --text "what's bothering me"
365
+ $ assistant memory v2 explain --text "..." --top 50
366
+ $ assistant memory v2 explain --text "..." --assistant-text "..." --now-text "..."`,
367
+ )
368
+ .action(
369
+ async (opts: {
370
+ text: string;
371
+ assistantText?: string;
372
+ nowText?: string;
373
+ top: string;
374
+ }) => {
375
+ const top = Number.parseInt(opts.top, 10);
376
+ if (!Number.isFinite(top) || top < 1) {
377
+ log.error("--top must be a positive integer");
378
+ process.exitCode = 1;
379
+ return;
380
+ }
381
+
382
+ const result = await cliIpcCall<MemoryV2ExplainSimilarityResult>(
383
+ "memory_v2_explain_similarity",
384
+ {
385
+ body: {
386
+ userText: opts.text,
387
+ assistantText: opts.assistantText,
388
+ nowText: opts.nowText,
389
+ top,
390
+ },
391
+ },
392
+ );
393
+
394
+ if (!result.ok) {
395
+ log.error(result.error ?? "Failed to run similarity diagnostic");
396
+ process.exitCode = 1;
397
+ return;
398
+ }
399
+
400
+ printExplainResult(result.result!);
401
+ },
402
+ );
403
+
404
+ // ── rebuild-corpus-stats ──────────────────────────────────────────────
405
+
406
+ v2.command("rebuild-corpus-stats")
407
+ .description(
408
+ "Rebuild the BM25 corpus stats (DF table + avg doc length) used by the sparse channel",
409
+ )
410
+ .addHelpText(
411
+ "after",
412
+ `
413
+ Walks every concept page on disk and recomputes the document-frequency
414
+ table and average document length used to weight BM25 sparse vectors.
415
+ Atomic swap — the previous stats stay live until the new ones are ready.
416
+
417
+ Run after bulk content imports, after manually editing many pages, or to
418
+ recover from a startup rebuild that errored.
419
+
420
+ Note: this only refreshes the in-memory stats used to *construct* new
421
+ document-side sparse vectors. Existing sparse vectors stored in Qdrant
422
+ are not refreshed by this command — pair with 'assistant memory v2
423
+ reembed' if document-side weights need updating.
424
+
425
+ Examples:
426
+ $ assistant memory v2 rebuild-corpus-stats`,
427
+ )
428
+ .action(async () => {
429
+ const result = await cliIpcCall<MemoryV2RebuildCorpusStatsResult>(
430
+ "memory_v2_rebuild_corpus_stats",
431
+ { body: {} },
432
+ );
433
+
434
+ if (!result.ok) {
435
+ log.error(result.error ?? "Failed to rebuild corpus stats");
436
+ process.exitCode = 1;
437
+ return;
438
+ }
439
+
440
+ const r = result.result!;
441
+ log.info(`Rebuilt BM25 corpus stats: ${r.totalDocs} docs.`);
442
+ log.info(` avg doc length: ${r.avgDl.toFixed(2)} tokens`);
443
+ log.info(` vocabulary buckets: ${r.vocabularyBuckets.toLocaleString()}`);
444
+ });
445
+
446
+ // ── fit-anisotropy ────────────────────────────────────────────────────
447
+
448
+ v2.command("fit-anisotropy")
449
+ .description(
450
+ "Fit the embedding anisotropy correction (Mu & Viswanath 'all-but-the-top')",
451
+ )
452
+ .option(
453
+ "--k <n>",
454
+ "Number of leading principal components to project out (default 1; raise to 2-3 only if PC2/PC3 also explain >5% variance)",
455
+ "1",
456
+ )
457
+ .option(
458
+ "--sample <n>",
459
+ "Maximum stored dense vectors to pull for the fit (default 5000)",
460
+ "5000",
461
+ )
462
+ .addHelpText(
463
+ "after",
464
+ `
465
+ Modern transformer embeddings (Gemini in particular) are anisotropic — every
466
+ vector lives in a narrow cone of the embedding space, which compresses cosine
467
+ similarities into a tight band (typically 0.4-0.7) and lets a few dominant
468
+ directions drown out semantic signal.
469
+
470
+ This command samples stored dense vectors from the concept-page Qdrant
471
+ collection, fits a corpus mean + top-k principal components, and persists the
472
+ calibration. Subsequent embeds and queries apply the correction automatically:
473
+
474
+ vec' = vec - mean
475
+ for each pc_i: vec' = vec' - (vec' · pc_i) pc_i
476
+ vec' = vec' / ||vec'||
477
+
478
+ After fitting, run 'assistant memory v2 reembed' to re-process every stored
479
+ concept page through the new calibration. Until then, queries are corrected
480
+ but stored vectors are not — the score range stays the same and retrieval may
481
+ degrade. The calibration is keyed by (provider, model, dim), so changing
482
+ embedding model invalidates it.
483
+
484
+ Recommended k:
485
+ k=1 safest default. Removes the dominant common direction.
486
+ k=2-3 only when explained-variance ratios show PC2/PC3 each above ~5%.
487
+ k>3 not justified without a labeled retrieval eval set.
488
+
489
+ Examples:
490
+ $ assistant memory v2 fit-anisotropy
491
+ $ assistant memory v2 fit-anisotropy --k 2
492
+ $ assistant memory v2 fit-anisotropy --sample 10000`,
493
+ )
494
+ .action(async (opts: { k: string; sample: string }) => {
495
+ const k = Number.parseInt(opts.k, 10);
496
+ const sample = Number.parseInt(opts.sample, 10);
497
+ if (!Number.isFinite(k) || k < 1) {
498
+ log.error("--k must be a positive integer");
499
+ process.exitCode = 1;
500
+ return;
501
+ }
502
+ if (!Number.isFinite(sample) || sample < 1) {
503
+ log.error("--sample must be a positive integer");
504
+ process.exitCode = 1;
505
+ return;
506
+ }
507
+
508
+ const result = await cliIpcCall<MemoryV2FitAnisotropyResult>(
509
+ "memory_v2_fit_anisotropy",
510
+ { body: { k, sample } },
511
+ );
512
+
513
+ if (!result.ok) {
514
+ log.error(result.error ?? "Failed to fit anisotropy calibration");
515
+ process.exitCode = 1;
516
+ return;
517
+ }
518
+
519
+ const r = result.result!;
520
+ log.info(
521
+ `Fit anisotropy calibration: ${r.provider}/${r.model} (dim=${r.dim})`,
522
+ );
523
+ log.info(` samples: ${r.sampleCount}`);
524
+ log.info(` total variance: ${r.totalVariance.toFixed(6)}`);
525
+ log.info(" explained variance per component:");
526
+ for (let i = 0; i < r.componentVariance.length; i++) {
527
+ const ratio = (r.explainedVarianceRatio[i] * 100).toFixed(2);
528
+ log.info(
529
+ ` PC${i + 1}: ${r.componentVariance[i].toFixed(6)} (${ratio}%)`,
530
+ );
531
+ }
532
+ log.info(` written to: ${r.path}`);
533
+ log.info("");
534
+ log.info(
535
+ "Next step: run 'assistant memory v2 reembed' so every stored concept page is re-written under the new calibration. Until then, query-side correction operates on stored vectors that were embedded without it.",
536
+ );
537
+ });
538
+
244
539
  // ── validate ──────────────────────────────────────────────────────────
245
540
 
246
541
  v2.command("validate")
@@ -61,7 +61,6 @@ mock.module("../../../../config/loader.js", () => ({
61
61
  }),
62
62
  loadConfig: () => ({}),
63
63
  invalidateConfigCache: () => {},
64
- saveConfig: () => {},
65
64
  loadRawConfig: () => ({}),
66
65
  saveRawConfig: () => {},
67
66
  }));
@@ -88,13 +88,11 @@ mock.module("../../../../config/loader.js", () => ({
88
88
  }),
89
89
  loadConfig: () => ({}),
90
90
  invalidateConfigCache: () => {},
91
- saveConfig: () => {},
92
91
  loadRawConfig: () => ({}),
93
92
  saveRawConfig: () => {},
94
93
  getNestedValue: () => undefined,
95
94
  setNestedValue: () => {},
96
95
  applyNestedDefaults: (config: unknown) => config,
97
- deepMergeMissing: () => false,
98
96
  deepMergeOverwrite: () => {},
99
97
  mergeDefaultWorkspaceConfig: () => {},
100
98
  }));
@@ -109,13 +109,11 @@ mock.module("../../../../config/loader.js", () => ({
109
109
  }),
110
110
  loadConfig: () => ({}),
111
111
  invalidateConfigCache: () => {},
112
- saveConfig: () => {},
113
112
  loadRawConfig: () => ({}),
114
113
  saveRawConfig: () => {},
115
114
  getNestedValue: () => undefined,
116
115
  setNestedValue: () => {},
117
116
  applyNestedDefaults: (config: unknown) => config,
118
- deepMergeMissing: () => false,
119
117
  deepMergeOverwrite: () => {},
120
118
  mergeDefaultWorkspaceConfig: () => {},
121
119
  }));
@@ -75,6 +75,8 @@ mock.module("../../../../util/logger.js", () => ({
75
75
  LOG_FILE_PATTERN: /^assistant-(\d{4}-\d{2}-\d{2})\.log$/,
76
76
  }));
77
77
 
78
+ // Also mock the CLI logger singleton so log.info calls do not write to stdout.
79
+
78
80
  mock.module("../../../../config/loader.js", () => ({
79
81
  API_KEY_PROVIDERS: [] as const,
80
82
  getConfig: () => ({
@@ -89,13 +91,11 @@ mock.module("../../../../config/loader.js", () => ({
89
91
  }),
90
92
  loadConfig: () => ({}),
91
93
  invalidateConfigCache: () => {},
92
- saveConfig: () => {},
93
94
  loadRawConfig: () => ({}),
94
95
  saveRawConfig: () => {},
95
96
  getNestedValue: () => undefined,
96
97
  setNestedValue: () => {},
97
98
  applyNestedDefaults: (config: unknown) => config,
98
- deepMergeMissing: () => false,
99
99
  deepMergeOverwrite: () => {},
100
100
  mergeDefaultWorkspaceConfig: () => {},
101
101
  }));
@@ -166,14 +166,15 @@ describe("assistant platform status", () => {
166
166
  process.exitCode = 0;
167
167
  });
168
168
 
169
- test("connected platform returns full status with stored credentials", async () => {
169
+ test("platform pod returns full status from context", async () => {
170
170
  /**
171
- * When the assistant has stored platform credentials and a valid
172
- * registration context, the status command should report connected
173
- * with all context fields populated.
171
+ * When the assistant is running as a platform-managed pod, the status
172
+ * command reports all fields from the registration context plus
173
+ * organizationId and userId from the keychain. The connected field
174
+ * is absent — platform status does not expose it.
174
175
  */
175
176
 
176
- // GIVEN a containerized environment with platform configuration
177
+ // GIVEN a containerized platform environment
177
178
  mockResolvePlatformCallbackRegistrationContext = async () => ({
178
179
  isPlatform: true,
179
180
  platformBaseUrl: "https://platform.vellum.ai",
@@ -184,12 +185,9 @@ describe("assistant platform status", () => {
184
185
  enabled: true,
185
186
  });
186
187
 
187
- // AND stored platform credentials exist
188
+ // AND credentials are stored in the keychain
188
189
  mockGetSecureKeyAsync = async (account: string) => {
189
- if (account === "credential/vellum/platform_base_url")
190
- return "https://platform.vellum.ai";
191
- if (account === "credential/vellum/assistant_api_key")
192
- return "sk-test-key";
190
+ if (account === "credential/vellum/webhook_secret") return "wh-secret";
193
191
  if (account === "credential/vellum/platform_organization_id")
194
192
  return "org-456";
195
193
  if (account === "credential/vellum/platform_user_id") return "user-789";
@@ -213,8 +211,8 @@ describe("assistant platform status", () => {
213
211
  expect(parsed.assistantId).toBe("asst-abc-123");
214
212
  expect(parsed.hasInternalApiKey).toBe(true);
215
213
  expect(parsed.hasAssistantApiKey).toBe(true);
214
+ expect(parsed.hasWebhookSecret).toBe(true);
216
215
  expect(parsed.available).toBe(true);
217
- expect(parsed.connected).toBe(true);
218
216
  expect(parsed.organizationId).toBe("org-456");
219
217
  expect(parsed.userId).toBe("user-789");
220
218
  });
@@ -243,7 +241,7 @@ describe("assistant platform status", () => {
243
241
  // THEN the command succeeds
244
242
  expect(exitCode).toBe(0);
245
243
 
246
- // AND stdout contains no JSON (writeOutput is skipped in plain text mode)
247
- expect(stdout.trim()).toBe("");
244
+ // Plain-text mode logs via log.info verify writeOutput (JSON) was NOT called
245
+ expect(() => JSON.parse(stdout.trim())).toThrow();
248
246
  });
249
247
  });
@@ -95,17 +95,18 @@ Examples:
95
95
 
96
96
  const failedKeys: string[] = [];
97
97
  for (const key of keysToDelete) {
98
- const result = await deleteSecureKeyViaDaemon(
98
+ const delResult = await deleteSecureKeyViaDaemon(
99
99
  "credential",
100
100
  `${key.service}:${key.field}`,
101
101
  );
102
- if (result === "error") {
103
- failedKeys.push(`${key.service}:${key.field}`);
102
+ if (delResult.result === "error") {
103
+ const detail = delResult.error ? `: ${delResult.error}` : "";
104
+ failedKeys.push(`${key.service}:${key.field}${detail}`);
104
105
  }
105
106
  }
106
107
 
107
108
  if (failedKeys.length > 0) {
108
- writeError(`Failed to delete credentials: ${failedKeys.join(", ")}`);
109
+ writeError(`Failed to delete credentials: ${failedKeys.join("; ")}`);
109
110
  return;
110
111
  }
111
112
 
@@ -68,7 +68,6 @@ Fields:
68
68
  hasWebhookSecret Whether a stored webhook secret is available (needed
69
69
  for email and other inbound webhook channels)
70
70
  available Whether callback registration prerequisites are satisfied
71
- connected Whether platform credentials are stored (boolean)
72
71
  organizationId The platform organization ID (from stored credentials)
73
72
  userId The platform user ID (from stored credentials)
74
73
 
@@ -80,19 +79,6 @@ Examples:
80
79
  try {
81
80
  const context = await resolvePlatformCallbackRegistrationContext();
82
81
 
83
- const storedBaseUrl =
84
- (await getSecureKeyAsync(
85
- credentialKey(
86
- CREDENTIAL_KEYS.baseUrl.service,
87
- CREDENTIAL_KEYS.baseUrl.field,
88
- ),
89
- )) ?? "";
90
- const hasStoredApiKey = !!(await getSecureKeyAsync(
91
- credentialKey(
92
- CREDENTIAL_KEYS.apiKey.service,
93
- CREDENTIAL_KEYS.apiKey.field,
94
- ),
95
- ));
96
82
  const organizationId =
97
83
  (
98
84
  await getSecureKeyAsync(
@@ -116,8 +102,6 @@ Examples:
116
102
  credentialKey("vellum", "webhook_secret"),
117
103
  ));
118
104
 
119
- const connected = !!storedBaseUrl && hasStoredApiKey;
120
-
121
105
  const result = {
122
106
  isPlatform: context.isPlatform,
123
107
  baseUrl: context.platformBaseUrl,
@@ -126,7 +110,6 @@ Examples:
126
110
  hasAssistantApiKey: context.hasAssistantApiKey,
127
111
  hasWebhookSecret,
128
112
  available: context.enabled,
129
- connected,
130
113
  organizationId: organizationId || null,
131
114
  userId: userId || null,
132
115
  };
@@ -149,7 +132,6 @@ Examples:
149
132
  log.info(
150
133
  `Callback registration available: ${result.available ? "yes" : "no"}`,
151
134
  );
152
- log.info(`Connected: ${connected}`);
153
135
  log.info(`Organization ID: ${organizationId || "(not set)"}`);
154
136
  log.info(`User ID: ${userId || "(not set)"}`);
155
137
  }