@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
@@ -32,6 +32,7 @@
32
32
  // for the next turn and drop below `epsilon` if no longer relevant.
33
33
 
34
34
  import type { AssistantConfig } from "../../config/types.js";
35
+ import { applyCorrectionIfCalibrated } from "../anisotropy.js";
35
36
  import {
36
37
  embedWithBackend,
37
38
  generateSparseEmbedding,
@@ -40,22 +41,29 @@ import { clampUnitInterval } from "../validation.js";
40
41
  import type { EdgeIndex } from "./edge-index.js";
41
42
  import { hybridQueryConceptPages } from "./qdrant.js";
42
43
  import { simBatch, simSkillBatch } from "./sim.js";
43
- import { hybridQuerySkills } from "./skill-qdrant.js";
44
44
  import type { ActivationState, EverInjectedEntry } from "./types.js";
45
45
 
46
46
  /**
47
- * Top-K size for the un-restricted ANN candidate query against the v2
48
- * concept-page collection. The design doc fixes this at 50 — small enough to
49
- * keep the per-turn round-trip cheap, large enough to surface relevant pages
50
- * outside the prior active set.
47
+ * Sentinel passed to Qdrant when `config.memory.v2.ann_candidate_limit` is
48
+ * `null` (unlimited). Qdrant's query API requires an explicit numeric
49
+ * `limit`, so unlimited is represented as a number large enough that any
50
+ * realistic concept-page collection is returned in full.
51
+ *
52
+ * Why not `Number.MAX_SAFE_INTEGER`: Qdrant's sparse-vector `SearchContext`
53
+ * pre-allocates `limit * 16` bytes per query, so passing `MAX_SAFE_INTEGER`
54
+ * triggers a ~144 PB allocation and SIGABRTs the Qdrant process. 1_000_000
55
+ * is ~16 MB of pre-allocation in Qdrant — generous headroom over realistic
56
+ * concept-page counts (low thousands today) while staying well clear of
57
+ * the OOM cliff. Bump explicitly via `ann_candidate_limit` if you ever
58
+ * outgrow it.
51
59
  */
52
- const ANN_CANDIDATE_LIMIT = 50;
60
+ const UNLIMITED_ANN_CANDIDATE_LIMIT = 1_000_000;
53
61
 
54
62
  // ---------------------------------------------------------------------------
55
63
  // Candidate selection
56
64
  // ---------------------------------------------------------------------------
57
65
 
58
- export interface SelectCandidatesParams {
66
+ interface SelectCandidatesParams {
59
67
  /**
60
68
  * Prior-turn activation snapshot. Slugs with activation strictly greater
61
69
  * than `config.memory.v2.epsilon` are carried forward as candidates so the
@@ -71,7 +79,7 @@ export interface SelectCandidatesParams {
71
79
  config: AssistantConfig;
72
80
  }
73
81
 
74
- export interface SelectCandidatesResult {
82
+ interface SelectCandidatesResult {
75
83
  /** Union of `fromPrior` and `fromAnn` — the per-turn candidate set. */
76
84
  candidates: Set<string>;
77
85
  /** Slugs carried forward from `priorState` because their activation > epsilon. */
@@ -118,13 +126,15 @@ export async function selectCandidates(
118
126
 
119
127
  if (annQueryText.length > 0) {
120
128
  const denseResult = await embedWithBackend(config, [annQueryText]);
121
- const dense = denseResult.vectors[0];
122
- const sparse = generateSparseEmbedding(annQueryText);
123
- const hits = await hybridQueryConceptPages(
124
- dense,
125
- sparse,
126
- ANN_CANDIDATE_LIMIT,
129
+ const dense = await applyCorrectionIfCalibrated(
130
+ denseResult.vectors[0],
131
+ denseResult.provider,
132
+ denseResult.model,
127
133
  );
134
+ const sparse = generateSparseEmbedding(annQueryText);
135
+ const limit =
136
+ config.memory.v2.ann_candidate_limit ?? UNLIMITED_ANN_CANDIDATE_LIMIT;
137
+ const hits = await hybridQueryConceptPages(dense, sparse, limit);
128
138
  for (const hit of hits) fromAnn.add(hit.slug);
129
139
  }
130
140
 
@@ -137,7 +147,7 @@ export async function selectCandidates(
137
147
  // Own activation
138
148
  // ---------------------------------------------------------------------------
139
149
 
140
- export interface ComputeOwnActivationParams {
150
+ interface ComputeOwnActivationParams {
141
151
  candidates: ReadonlySet<string>;
142
152
  priorState: ActivationState | null;
143
153
  userText: string;
@@ -151,7 +161,7 @@ export interface ComputeOwnActivationParams {
151
161
  * coefficient weighting is applied. Surfaced for telemetry / inspector views
152
162
  * so the UI can show how each term contributed to the final value.
153
163
  */
154
- export interface OwnActivationBreakdown {
164
+ interface OwnActivationBreakdown {
155
165
  /** `d * prev(slug)` — the decayed prior-turn activation contribution. */
156
166
  priorContribution: number;
157
167
  /** Raw `sim(user, slug)` similarity, before `c_user` weighting. */
@@ -162,7 +172,7 @@ export interface OwnActivationBreakdown {
162
172
  simNow: number;
163
173
  }
164
174
 
165
- export interface ComputeOwnActivationResult {
175
+ interface ComputeOwnActivationResult {
166
176
  /** Final clamped own-activation value per slug. */
167
177
  activation: Map<string, number>;
168
178
  /** Per-slug breakdown of the inputs that fed into `activation`. */
@@ -222,7 +232,7 @@ export async function computeOwnActivation(
222
232
  // Spreading activation
223
233
  // ---------------------------------------------------------------------------
224
234
 
225
- export interface SpreadActivationResult {
235
+ interface SpreadActivationResult {
226
236
  /** Final activation value per slug after spreading. */
227
237
  final: Map<string, number>;
228
238
  /**
@@ -239,20 +249,25 @@ export interface SpreadActivationResult {
239
249
  * Apply 2-hop spreading activation with neighborhood normalization. Edges are
240
250
  * directed: an edge A→B means A's activation contributes to B's final value.
241
251
  *
242
- * A(n) = [ A_o(n) + k · Σ_{m∈in1(n)} A_o(m) + k² · Σ_{m∈in2(n)} A_o(m) ]
243
- * / (1 + k · #in1(n) + k² · #in2(n))
252
+ * A(n) = [ A_o(n) + Σ_{r: |active_inR(n)| > 0} k^r · L2(active_inR(n)) ]
253
+ * / [ 1 + Σ_{r: |active_inR(n)| > 0} k^r ]
254
+ *
255
+ * `active_inR(n)` is the subset of structural predecessors at hop `r` that
256
+ * also appear in `ownActivation` (i.e. made the candidate set). `L2(.)` is
257
+ * the quadratic mean √(mean(A_o²)) — a mild bias toward strong outliers
258
+ * compared to the arithmetic mean, without letting a single high-cosine
259
+ * predecessor dominate the way `max` would.
244
260
  *
245
- * For each candidate slug `n`, BFS walks `incoming` adjacency to gather
246
- * predecessors at distance 1, 2 (i.e. nodes from which a directed path of
247
- * that length leads into `n`). The denominator counts those structural
248
- * predecessors at each hop (whether or not they appear in `ownActivation`)
249
- * so a pure source — no incoming edges — collapses to `A == A_o`. Missing
250
- * predecessors contribute 0 to the numerator.
261
+ * Hops with **no** active predecessors are dropped from BOTH numerator and
262
+ * denominator so a high-in-degree hub with mostly-inactive neighbors stays
263
+ * near `A_o` instead of being crushed by the structural count. A pure
264
+ * source (no incoming edges, or every edge points at a non-candidate)
265
+ * collapses to `A == A_o`.
251
266
  *
252
- * Bounded in [0, 1]: with `A_o [0, 1]` and `k [0, 1]`, the numerator is
253
- * at most `1 + k · #in1 + k² · #in2` — exactly the denominator — so the
254
- * ratio is at most 1. `clampUnitInterval` guards against numerical drift
255
- * and out-of-range inputs.
267
+ * Bounded in [0, 1]: every `L2` term max active A_o 1, so the numerator
268
+ * is at most `1 + Σ k^r` — exactly the denominator — so the ratio is at most
269
+ * 1. `clampUnitInterval` guards against numerical drift and out-of-range
270
+ * inputs.
256
271
  *
257
272
  * Pure function — no I/O. Reads the precomputed `incoming` map from
258
273
  * `edgeIndex` and runs a per-source BFS bounded by `hops`.
@@ -282,22 +297,28 @@ export function spreadActivation(
282
297
  // hop-0 only via `numerator = ownValue`.
283
298
  const distance = bfsPredecessorDistances(edgeIndex.incoming, slug, hops);
284
299
 
300
+ // Bucket only predecessors that are in `ownActivation` (the candidate
301
+ // set). Structural predecessors that didn't make the cut contribute
302
+ // nothing — neither to the numerator nor the denominator — so hub
303
+ // in-degree alone never penalizes a node.
304
+ const ringActiveCounts: number[] = new Array(hops + 1).fill(0);
305
+ const ringSquareSums: number[] = new Array(hops + 1).fill(0);
306
+ for (const [predecessor, hop] of distance) {
307
+ const predValue = ownActivation.get(predecessor);
308
+ if (predValue === undefined) continue;
309
+ ringActiveCounts[hop] += 1;
310
+ ringSquareSums[hop] += predValue * predValue;
311
+ }
312
+
285
313
  let numerator = ownValue;
286
314
  let denominator = 1;
287
315
  let kPow = 1;
288
- // Accumulate per-hop contributions in a single pass. We need per-hop
289
- // counts to weight by k^r, so bucket as we go.
290
- const ringCounts: number[] = new Array(hops + 1).fill(0);
291
- const ringSums: number[] = new Array(hops + 1).fill(0);
292
- for (const [predecessor, hop] of distance) {
293
- ringCounts[hop] += 1;
294
- ringSums[hop] += ownActivation.get(predecessor) ?? 0;
295
- }
296
316
  for (let r = 1; r <= hops; r++) {
297
317
  kPow *= k;
298
- if (ringCounts[r] === 0) continue;
299
- numerator += kPow * ringSums[r];
300
- denominator += kPow * ringCounts[r];
318
+ if (ringActiveCounts[r] === 0) continue;
319
+ const rms = Math.sqrt(ringSquareSums[r] / ringActiveCounts[r]);
320
+ numerator += kPow * rms;
321
+ denominator += kPow;
301
322
  }
302
323
 
303
324
  const finalValue = clampUnitInterval(numerator / denominator);
@@ -347,7 +368,7 @@ function bfsPredecessorDistances(
347
368
  // Injection selection
348
369
  // ---------------------------------------------------------------------------
349
370
 
350
- export interface SelectInjectionsParams {
371
+ interface SelectInjectionsParams {
351
372
  /** Final activation map after spread. */
352
373
  A: ReadonlyMap<string, number>;
353
374
  /** Slugs already attached to a prior user message (with their turn). */
@@ -356,7 +377,7 @@ export interface SelectInjectionsParams {
356
377
  topK: number;
357
378
  }
358
379
 
359
- export interface SelectInjectionsResult {
380
+ interface SelectInjectionsResult {
360
381
  /** Top-K slugs by activation (descending), used for the cached top-now view. */
361
382
  topNow: string[];
362
383
  /**
@@ -408,47 +429,7 @@ export function selectInjections(
408
429
  // c_assistant, c_now}` — the design doc (§9) deliberately shares them with
409
430
  // concept-page activation rather than introducing parallel knobs.
410
431
 
411
- export interface SelectSkillCandidatesParams {
412
- userText: string;
413
- assistantText: string;
414
- nowText: string;
415
- config: AssistantConfig;
416
- /** Top-K size for the ANN query against `memory_v2_skills`. */
417
- topK: number;
418
- }
419
-
420
- /**
421
- * ANN top-K against the skills collection using the concatenated turn text.
422
- * Runs a single embedding pass over `concat(user, assistant, now)` and a
423
- * single hybrid Qdrant query — there is no prior-state carry-forward (skills
424
- * are stateless).
425
- *
426
- * Returns a `Set<string>` of skill ids that hit either channel. Empty when
427
- * the joined text is empty or `topK <= 0`.
428
- */
429
- export async function selectSkillCandidates(
430
- params: SelectSkillCandidatesParams,
431
- ): Promise<Set<string>> {
432
- const { userText, assistantText, nowText, config, topK } = params;
433
-
434
- const candidates = new Set<string>();
435
- if (topK <= 0) return candidates;
436
-
437
- const annQueryText = [userText, assistantText, nowText]
438
- .filter((s) => s.length > 0)
439
- .join("\n");
440
- if (annQueryText.length === 0) return candidates;
441
-
442
- const denseResult = await embedWithBackend(config, [annQueryText]);
443
- const dense = denseResult.vectors[0];
444
- const sparse = generateSparseEmbedding(annQueryText);
445
- const hits = await hybridQuerySkills(dense, sparse, topK);
446
- for (const hit of hits) candidates.add(hit.id);
447
-
448
- return candidates;
449
- }
450
-
451
- export interface ComputeSkillActivationParams {
432
+ interface ComputeSkillActivationParams {
452
433
  candidates: ReadonlySet<string>;
453
434
  userText: string;
454
435
  assistantText: string;
@@ -461,7 +442,7 @@ export interface ComputeSkillActivationParams {
461
442
  * coefficient weighting. Skills have no decay term, so the breakdown is just
462
443
  * the three raw sims. Surfaced for telemetry / inspector views.
463
444
  */
464
- export interface SkillActivationBreakdown {
445
+ interface SkillActivationBreakdown {
465
446
  /** Raw `sim(user, skill)` similarity, before `c_user` weighting. */
466
447
  simUser: number;
467
448
  /** Raw `sim(assistant, skill)` similarity, before `c_assistant` weighting. */
@@ -470,7 +451,7 @@ export interface SkillActivationBreakdown {
470
451
  simNow: number;
471
452
  }
472
453
 
473
- export interface ComputeSkillActivationResult {
454
+ interface ComputeSkillActivationResult {
474
455
  /** Final clamped skill-activation value per id. */
475
456
  activation: Map<string, number>;
476
457
  /** Per-skill breakdown of the raw sim inputs that fed into `activation`. */
@@ -520,14 +501,14 @@ export async function computeSkillActivation(
520
501
  return { activation, breakdown };
521
502
  }
522
503
 
523
- export interface SelectSkillInjectionsParams {
504
+ interface SelectSkillInjectionsParams {
524
505
  /** Final skill activation map. */
525
506
  A: ReadonlyMap<string, number>;
526
507
  /** Cap on the per-turn skill slate, e.g. `config.memory.v2.skills_top_k`. */
527
508
  topK: number;
528
509
  }
529
510
 
530
- export interface SelectSkillInjectionsResult {
511
+ interface SelectSkillInjectionsResult {
531
512
  /**
532
513
  * Top-K skill ids by activation (descending), tie-broken lexicographically.
533
514
  * Skills are re-presented every turn — no `toInject` delta — so the caller
@@ -66,13 +66,11 @@ import {
66
66
  type MemoryJob,
67
67
  type MemoryJobType,
68
68
  } from "../jobs-store.js";
69
+ import { MEMORY_V2_CONSOLIDATION_SOURCE } from "./constants.js";
69
70
  import { resolveConsolidationPrompt } from "./prompts/consolidation.js";
70
71
 
71
72
  const log = getLogger("memory-v2-consolidate");
72
73
 
73
- /** Source string identifying this wake in `agent-wake` logs and surfaces. */
74
- const WAKE_SOURCE = "memory_v2_consolidation";
75
-
76
74
  /**
77
75
  * Follow-up jobs to fan out after a successful consolidation.
78
76
  *
@@ -144,7 +142,7 @@ export async function memoryV2ConsolidateJob(
144
142
  // writes in the assistant's voice.
145
143
  const conversation = bootstrapConversation({
146
144
  conversationType: "background",
147
- source: WAKE_SOURCE,
145
+ source: MEMORY_V2_CONSOLIDATION_SOURCE,
148
146
  origin: "memory_consolidation",
149
147
  systemHint: "Running memory consolidation",
150
148
  groupId: "system:background",
@@ -159,7 +157,7 @@ export async function memoryV2ConsolidateJob(
159
157
  config.memory.v2.consolidation_prompt_path,
160
158
  cutoff,
161
159
  ),
162
- source: WAKE_SOURCE,
160
+ source: MEMORY_V2_CONSOLIDATION_SOURCE,
163
161
  trustContext: INTERNAL_GUARDIAN_TRUST_CONTEXT,
164
162
  });
165
163
  wakeInvoked = result.invoked;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Canonical `conversations.source` string for background memory v2
3
+ * consolidation runs. Lives in a tiny constants module so the route layer can
4
+ * recognize consolidation conversations without importing the consolidation
5
+ * job (which pulls in agent-wake + bootstrap dependencies).
6
+ */
7
+ export const MEMORY_V2_CONSOLIDATION_SOURCE = "memory_v2_consolidation";
@@ -37,14 +37,13 @@ import {
37
37
  computeSkillActivation,
38
38
  selectCandidates,
39
39
  selectInjections,
40
- selectSkillCandidates,
41
40
  selectSkillInjections,
42
41
  spreadActivation,
43
42
  } from "./activation.js";
44
43
  import { hydrate, save } from "./activation-store.js";
45
44
  import { getEdgeIndex } from "./edge-index.js";
46
45
  import { readPage, renderPageContent } from "./page-store.js";
47
- import { getSkillCapability } from "./skill-store.js";
46
+ import { getAllSkillIds, getSkillCapability } from "./skill-store.js";
48
47
  import type { ActivationState, EverInjectedEntry } from "./types.js";
49
48
 
50
49
  const log = getLogger("memory-v2-injection");
@@ -89,10 +88,11 @@ export interface InjectMemoryV2BlockParams {
89
88
 
90
89
  export interface InjectMemoryV2BlockResult {
91
90
  /**
92
- * Rendered `<memory>` block, ready to prepend to the current
93
- * user message — or `null` when nothing new is eligible for injection.
94
- * `null` is the cache-stable default: the caller adds nothing to the new
95
- * user message and prior attachments stay byte-identical.
91
+ * Inner content for the `<memory>` block, ready for the caller to wrap
92
+ * exactly once at injection time — or `null` when nothing new is eligible
93
+ * for injection. `null` is the cache-stable default: the caller adds
94
+ * nothing to the new user message and prior attachments stay
95
+ * byte-identical.
96
96
  */
97
97
  block: string | null;
98
98
  /**
@@ -183,16 +183,11 @@ export async function injectMemoryV2Block(
183
183
  const slugsToRender = mode === "context-load" ? topNow : toInject;
184
184
 
185
185
  // (6b) Skill pipeline — a sibling pipeline to the concept-page one above.
186
- // Skills are stateless: no decay carry-over, no spread, no `everInjected`
187
- // dedup. The top-K relevant skills are re-presented every turn so the
188
- // agent can drop and pick them up freely.
189
- const skillCandidates = await selectSkillCandidates({
190
- userText: userMessage,
191
- assistantText: assistantMessage,
192
- nowText,
193
- config,
194
- topK: config.memory.v2.top_k_skills,
195
- });
186
+ // Skills are stateless (no decay, no spread, no `everInjected` dedup) and
187
+ // the catalog is small, so every known skill is scored every turn. The
188
+ // top-K injection slate is re-presented every turn so the agent can drop
189
+ // and pick skills up freely; the inspector renders the full ranked list.
190
+ const skillCandidates = new Set(getAllSkillIds());
196
191
  const { activation: skillActivation, breakdown: skillBreakdown } =
197
192
  await computeSkillActivation({
198
193
  candidates: skillCandidates,
@@ -241,9 +236,30 @@ export async function injectMemoryV2Block(
241
236
 
242
237
  await save(database, conversationId, nextActivationState);
243
238
 
244
- // Record per-turn activation telemetry. This runs *before* the cache-stable
245
- // empty-block return so we capture diagnostics even on no-op turns. Failures
246
- // are warn-logged and never block memory injection.
239
+ // Render before recording telemetry so the activation log can mark slugs
240
+ // whose backing file is gone those are no-op renders that would otherwise
241
+ // be indistinguishable from successful "injected" rows in the log.
242
+ // `renderInjectionBlock` itself short-circuits on empty inputs.
243
+ const { block, missingSlugs } = await renderInjectionBlock(
244
+ workspaceDir,
245
+ slugsToRender,
246
+ topSkillIds,
247
+ );
248
+ const missingSlugSet = new Set(missingSlugs);
249
+ if (missingSlugs.length > 0) {
250
+ log.warn(
251
+ {
252
+ conversationId,
253
+ turn: currentTurn,
254
+ missingSlugs,
255
+ renderedCount: slugsToRender.length - missingSlugs.length,
256
+ },
257
+ "Memory v2 injection skipped slugs whose page was missing on disk — Qdrant index may be stale; consider reembed",
258
+ );
259
+ }
260
+
261
+ // Record per-turn activation telemetry. Failures are warn-logged and never
262
+ // block memory injection.
247
263
  const toInjectSet = new Set(toInject);
248
264
  const renderedSet = new Set(slugsToRender);
249
265
  const topSkillIdSet = new Set(topSkillIds);
@@ -260,6 +276,10 @@ export async function injectMemoryV2Block(
260
276
  // - per-turn: cached attachments from prior turns are still on the
261
277
  // user message, so prior-everInjected slugs are `in_context` and
262
278
  // the delta (`toInject`) is `injected`.
279
+ // `page_missing` overrides any "would-have-been-injected" status when
280
+ // `readPage` returned null for the slug — telemetry surfaces stale
281
+ // ANN/edge entries instead of silently masquerading as a successful
282
+ // injection.
263
283
  let status: MemoryV2ConceptRowRecord["status"];
264
284
  if (mode === "context-load") {
265
285
  status = renderedSet.has(slug) ? "injected" : "not_injected";
@@ -270,6 +290,9 @@ export async function injectMemoryV2Block(
270
290
  } else {
271
291
  status = "not_injected";
272
292
  }
293
+ if (status === "injected" && missingSlugSet.has(slug)) {
294
+ status = "page_missing";
295
+ }
273
296
  return {
274
297
  slug,
275
298
  finalActivation: finalActivation.get(slug) ?? 0,
@@ -327,23 +350,6 @@ export async function injectMemoryV2Block(
327
350
  );
328
351
  }
329
352
 
330
- // (7) Cache-stable empty path: nothing to render AND no ranked skills.
331
- if (slugsToRender.length === 0 && topSkillIds.length === 0) {
332
- return { block: null, toInject: [] };
333
- }
334
-
335
- // (8) Render. Both `topNow` and `toInject` are activation-descending
336
- // (selectInjections sorts before slicing), so `slugsToRender` doubles as
337
- // the render order. Per-turn: only the new slugs render (prior turns'
338
- // attachments stay cached on prior user messages). Context-load: full
339
- // top-K renders so the fresh user message gets a complete activation dump.
340
- // Skills are appended after concept-page sections.
341
- const block = await renderInjectionBlock(
342
- workspaceDir,
343
- slugsToRender,
344
- topSkillIds,
345
- );
346
-
347
353
  return { block, toInject: newlyInjected };
348
354
  }
349
355
 
@@ -351,14 +357,38 @@ export async function injectMemoryV2Block(
351
357
  // Internal helpers
352
358
  // ---------------------------------------------------------------------------
353
359
 
360
+ interface RenderInjectionBlockResult {
361
+ /**
362
+ * Inner content for the `<memory>` block (concept-page sections + optional
363
+ * skills suffix), or `null` when both the concept-page list and the skill
364
+ * list collapse to empty after cache misses (no on-disk pages, no
365
+ * resolvable skill ids). Returned unwrapped so the caller can wrap it
366
+ * exactly once at injection time, matching v1's contract: callers that
367
+ * cache the value (`lastInjectedBlock`) or persist it (`memoryInjectedBlock`
368
+ * in message metadata) re-wrap on use, and storing the wrapped form here
369
+ * caused a double wrap on reinject after compaction and on rehydrate from
370
+ * DB.
371
+ */
372
+ block: string | null;
373
+ /**
374
+ * Slugs that `readPage` returned null for. Surfaced so the caller can
375
+ * mark them in the activation log (`status: "page_missing"`) and emit
376
+ * a warning — silent drops here previously masked stale Qdrant /
377
+ * edge-index entries that pointed at pages no longer on disk.
378
+ */
379
+ missingSlugs: string[];
380
+ }
381
+
354
382
  /**
355
- * Render the `<memory>` block for a list of slugs and a list of
356
- * ranked skill ids.
383
+ * Render the inner content of the `<memory>` block for a list of slugs and
384
+ * a list of ranked skill ids. The caller wraps the result in
385
+ * `<memory>...</memory>` exactly once at injection time.
357
386
  *
358
387
  * Concept pages are read in parallel via `readPage`. Pages whose file has
359
388
  * gone missing between selection and render (e.g. consolidation deleted
360
- * them) are silently dropped the activation state still records them in
361
- * `everInjected` so we don't keep re-attempting on every turn.
389
+ * them, folder reorg renamed the slug) are dropped from the rendered
390
+ * block but reported back via `missingSlugs` so callers can surface the
391
+ * divergence.
362
392
  *
363
393
  * Skill ids are looked up via `getSkillCapability`. Ids that the cache no
364
394
  * longer knows (e.g. uninstalled mid-run) are silently dropped, mirroring
@@ -370,7 +400,6 @@ export async function injectMemoryV2Block(
370
400
  * the agent sees the page's edges and any referenced media paths alongside
371
401
  * the prose:
372
402
  *
373
- * <memory>
374
403
  * ### <slug-1>
375
404
  * ---
376
405
  * edges:
@@ -390,28 +419,29 @@ export async function injectMemoryV2Block(
390
419
  * ### Skills You Can Use
391
420
  * - <skill-1 content>
392
421
  * - <skill-2 content>
393
- * </memory>
394
- *
395
- * Returns `null` when both lists collapse to empty after cache misses so
396
- * the caller can fall through to its empty-block path instead of attaching
397
- * an empty `<memory>` wrapper.
398
422
  */
399
423
  async function renderInjectionBlock(
400
424
  workspaceDir: string,
401
425
  slugs: string[],
402
426
  skillIds: string[],
403
- ): Promise<string | null> {
427
+ ): Promise<RenderInjectionBlockResult> {
404
428
  const pages = await Promise.all(
405
429
  slugs.map(async (slug) => {
406
430
  const page = await readPage(workspaceDir, slug);
407
- return page ? { slug, content: renderPageContent(page).trim() } : null;
431
+ return { slug, page };
408
432
  }),
409
433
  );
410
434
 
411
435
  const sections: string[] = [];
412
- for (const entry of pages) {
413
- if (!entry || entry.content.length === 0) continue;
414
- sections.push(`### ${entry.slug}\n${entry.content}`);
436
+ const missingSlugs: string[] = [];
437
+ for (const { slug, page } of pages) {
438
+ if (!page) {
439
+ missingSlugs.push(slug);
440
+ continue;
441
+ }
442
+ const content = renderPageContent(page).trim();
443
+ if (content.length === 0) continue;
444
+ sections.push(`### ${slug}\n${content}`);
415
445
  }
416
446
 
417
447
  // v2's skills collection is skills-only, so the activation suffix always applies.
@@ -425,7 +455,10 @@ async function renderInjectionBlock(
425
455
  sections.push(`### Skills You Can Use\n${skillLines.join("\n")}`);
426
456
  }
427
457
 
428
- if (sections.length === 0) return null;
458
+ if (sections.length === 0) return { block: null, missingSlugs };
429
459
 
430
- return `<memory>\n${sections.join("\n\n")}\n</memory>`;
460
+ return {
461
+ block: sections.join("\n\n"),
462
+ missingSlugs,
463
+ };
431
464
  }