@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
@@ -43,7 +43,7 @@ export type MemoryJobType =
43
43
  | "memory_v2_reembed"
44
44
  | "memory_v2_activation_recompute";
45
45
 
46
- const EMBED_JOB_TYPES: MemoryJobType[] = [
46
+ export const EMBED_JOB_TYPES: MemoryJobType[] = [
47
47
  "embed_segment",
48
48
  "embed_summary",
49
49
  "embed_media",
@@ -53,6 +53,21 @@ const EMBED_JOB_TYPES: MemoryJobType[] = [
53
53
  "graph_trigger_embed",
54
54
  ];
55
55
 
56
+ export const SLOW_LLM_JOB_TYPES: MemoryJobType[] = [
57
+ "graph_consolidate",
58
+ "graph_pattern_scan",
59
+ "graph_narrative_refine",
60
+ "graph_extract",
61
+ "conversation_analyze",
62
+ "build_conversation_summary",
63
+ "generate_conversation_starters",
64
+ "memory_v2_sweep",
65
+ "memory_v2_consolidate",
66
+ "memory_v2_migrate",
67
+ "backfill",
68
+ "graph_bootstrap",
69
+ ];
70
+
56
71
  export interface MemoryJob<T = Record<string, unknown>> {
57
72
  id: string;
58
73
  type: MemoryJobType;
@@ -351,8 +366,15 @@ export function enqueuePruneOldConversationsJob(
351
366
  return enqueueMemoryJob("prune_old_conversations", payload);
352
367
  }
353
368
 
354
- export function claimMemoryJobs(limit: number): MemoryJob[] {
355
- if (limit <= 0) return [];
369
+ export interface LaneBudgets {
370
+ slowLlm: number;
371
+ fast: number;
372
+ embed: number;
373
+ }
374
+
375
+ export function claimMemoryJobs(limits: LaneBudgets): MemoryJob[] {
376
+ if (limits.slowLlm <= 0 && limits.fast <= 0 && limits.embed <= 0) return [];
377
+
356
378
  const db = getDb();
357
379
  const now = Date.now();
358
380
  const pendingFilter = and(
@@ -360,38 +382,60 @@ export function claimMemoryJobs(limit: number): MemoryJob[] {
360
382
  lte(memoryJobs.runAfter, now),
361
383
  );
362
384
 
363
- // Claim non-embed jobs first, then fill remaining slots with embed jobs.
364
- // This prevents embed retries from starving other job types during a backend outage.
365
- const nonEmbedCandidates = db
366
- .select()
367
- .from(memoryJobs)
368
- .where(and(pendingFilter, notInArray(memoryJobs.type, EMBED_JOB_TYPES)))
369
- .orderBy(asc(memoryJobs.runAfter), asc(memoryJobs.createdAt))
370
- .limit(limit)
371
- .all();
385
+ // Slow lane: long-running LLM jobs (graph extract/consolidate, analysis, etc.).
386
+ const slowCandidates =
387
+ limits.slowLlm > 0
388
+ ? db
389
+ .select()
390
+ .from(memoryJobs)
391
+ .where(
392
+ and(pendingFilter, inArray(memoryJobs.type, SLOW_LLM_JOB_TYPES)),
393
+ )
394
+ .orderBy(asc(memoryJobs.runAfter), asc(memoryJobs.createdAt))
395
+ .limit(limits.slowLlm)
396
+ .all()
397
+ : [];
372
398
 
373
- const remainingSlots = limit - nonEmbedCandidates.length;
399
+ // Fast lane: everything that is neither slow-LLM nor embed.
400
+ const fastCandidates =
401
+ limits.fast > 0
402
+ ? db
403
+ .select()
404
+ .from(memoryJobs)
405
+ .where(
406
+ and(
407
+ pendingFilter,
408
+ notInArray(memoryJobs.type, SLOW_LLM_JOB_TYPES),
409
+ notInArray(memoryJobs.type, EMBED_JOB_TYPES),
410
+ ),
411
+ )
412
+ .orderBy(asc(memoryJobs.runAfter), asc(memoryJobs.createdAt))
413
+ .limit(limits.fast)
414
+ .all()
415
+ : [];
374
416
 
375
- // When the Qdrant circuit breaker is open, skip embed jobs entirely —
376
- // they would just be claimed → fail → deferred, wasting CPU cycles.
377
- // Exception: if the cooldown has elapsed (breaker ready for half-open probe),
378
- // allow exactly 1 embed job through so the breaker can self-heal.
417
+ // Embed lane: gated by the Qdrant circuit breaker. When the breaker is open,
418
+ // skip embed jobs entirely — they would just be claimed → fail → deferred,
419
+ // wasting CPU cycles. Exception: if the cooldown has elapsed (breaker ready
420
+ // for half-open probe), allow exactly 1 embed job through so the breaker
421
+ // can self-heal. Note: this gate applies ONLY to the embed lane; slow and
422
+ // fast lanes run unimpeded.
379
423
  const breakerOpen = isQdrantBreakerOpen();
380
424
  const probeAllowed = breakerOpen && shouldAllowQdrantProbe();
381
425
  const skipEmbedJobs = breakerOpen && !probeAllowed;
382
- const embedLimit = probeAllowed ? 1 : remainingSlots;
426
+ const embedLimit = probeAllowed ? Math.min(1, limits.embed) : limits.embed;
383
427
 
384
- if (skipEmbedJobs && remainingSlots > 0) {
428
+ if (skipEmbedJobs && limits.embed > 0) {
385
429
  log.debug("Skipping embed job claims — Qdrant circuit breaker is open");
386
430
  }
387
- if (probeAllowed && remainingSlots > 0) {
431
+ if (probeAllowed && limits.embed > 0) {
388
432
  log.debug(
389
433
  "Allowing 1 embed probe job — Qdrant circuit breaker cooldown elapsed",
390
434
  );
391
435
  }
392
436
 
393
437
  const embedCandidates =
394
- remainingSlots > 0 && !skipEmbedJobs
438
+ embedLimit > 0 && !skipEmbedJobs
395
439
  ? db
396
440
  .select()
397
441
  .from(memoryJobs)
@@ -401,7 +445,7 @@ export function claimMemoryJobs(limit: number): MemoryJob[] {
401
445
  .all()
402
446
  : [];
403
447
 
404
- const candidates = [...nonEmbedCandidates, ...embedCandidates];
448
+ const candidates = [...slowCandidates, ...fastCandidates, ...embedCandidates];
405
449
 
406
450
  const claimed: MemoryJob[] = [];
407
451
  for (const row of candidates) {
@@ -50,6 +50,7 @@ import {
50
50
  claimMemoryJobs,
51
51
  completeMemoryJob,
52
52
  deferMemoryJob,
53
+ EMBED_JOB_TYPES,
53
54
  enqueueMemoryJob,
54
55
  enqueuePruneOldConversationsJob,
55
56
  enqueuePruneOldLlmRequestLogsJob,
@@ -58,6 +59,7 @@ import {
58
59
  type MemoryJob,
59
60
  type MemoryJobType,
60
61
  resetRunningJobsToPending,
62
+ SLOW_LLM_JOB_TYPES,
61
63
  } from "./jobs-store.js";
62
64
  import { QdrantCircuitOpenError } from "./qdrant-circuit-breaker.js";
63
65
  import {
@@ -156,7 +158,9 @@ export function startMemoryJobsWorker(): MemoryJobsWorker {
156
158
  };
157
159
  }
158
160
 
159
- async function runMemoryJobsOnce(
161
+ type ProcessGroup = (group: MemoryJob[]) => Promise<number>;
162
+
163
+ export async function runMemoryJobsOnce(
160
164
  options: { enableScheduledCleanup?: boolean } = {},
161
165
  ): Promise<number> {
162
166
  const config = getConfig();
@@ -169,10 +173,20 @@ async function runMemoryJobsOnce(
169
173
  log.warn({ timedOut }, "Timed out stalled memory jobs");
170
174
  }
171
175
 
172
- const batchSize = Math.max(1, config.memory.jobs.batchSize);
173
- const concurrency = Math.max(1, config.memory.jobs.workerConcurrency);
174
- const jobs = claimMemoryJobs(batchSize);
175
- if (jobs.length === 0) {
176
+ const cfgSlow = Math.max(1, config.memory.jobs.slowLlmConcurrency);
177
+ const cfgFast = Math.max(1, config.memory.jobs.fastConcurrency);
178
+ const cfgEmbed = Math.max(1, config.memory.jobs.embedConcurrency);
179
+
180
+ // Claim per-lane budgets so a backlog of slow LLM jobs cannot starve fast
181
+ // jobs (and vice versa). The Qdrant circuit breaker still gates only the
182
+ // embed lane inside `claimMemoryJobs`.
183
+ const claimed = claimMemoryJobs({
184
+ slowLlm: cfgSlow,
185
+ fast: cfgFast,
186
+ embed: cfgEmbed,
187
+ });
188
+
189
+ if (claimed.length === 0) {
176
190
  if (enableScheduledCleanup) {
177
191
  maybeEnqueueScheduledCleanupJobs(config);
178
192
  }
@@ -181,34 +195,22 @@ async function runMemoryJobsOnce(
181
195
  return 0;
182
196
  }
183
197
 
184
- // Group jobs so they can run concurrently across independent work units.
185
- // Jobs targeting different conversations (via payload.conversationId) are
186
- // placed in separate groups and can run in parallel. Jobs targeting the
187
- // same conversation, or global jobs without a conversationId (backfill,
188
- // cleanup, rebuild_index), are grouped together and run sequentially to
189
- // prevent checkpoint races.
190
- const jobGroups = new Map<string, MemoryJob[]>();
191
- for (const job of jobs) {
192
- const convId =
193
- typeof job.payload.conversationId === "string"
194
- ? job.payload.conversationId
195
- : null;
196
- const groupKey = convId ? `${job.type}:${convId}` : job.type;
197
- let group = jobGroups.get(groupKey);
198
- if (!group) {
199
- group = [];
200
- jobGroups.set(groupKey, group);
198
+ const slowSet = new Set<MemoryJobType>(SLOW_LLM_JOB_TYPES);
199
+ const embedSet = new Set<MemoryJobType>(EMBED_JOB_TYPES);
200
+ const slowJobs: MemoryJob[] = [];
201
+ const fastJobs: MemoryJob[] = [];
202
+ const embedJobs: MemoryJob[] = [];
203
+ for (const job of claimed) {
204
+ if (slowSet.has(job.type)) {
205
+ slowJobs.push(job);
206
+ } else if (embedSet.has(job.type)) {
207
+ embedJobs.push(job);
208
+ } else {
209
+ fastJobs.push(job);
201
210
  }
202
- group.push(job);
203
211
  }
204
212
 
205
- let processed = 0;
206
- const typeGroups = [...jobGroups.values()];
207
-
208
- // Run type groups concurrently using a task pool (up to workerConcurrency
209
- // active at a time). Unlike the old wave approach, a new group starts as
210
- // soon as any slot frees up — no waiting for an entire wave to finish.
211
- const processGroup = async (group: MemoryJob[]): Promise<number> => {
213
+ const processGroup: ProcessGroup = async (group) => {
212
214
  let groupProcessed = 0;
213
215
  for (const job of group) {
214
216
  try {
@@ -229,8 +231,59 @@ async function runMemoryJobsOnce(
229
231
  return groupProcessed;
230
232
  };
231
233
 
234
+ // Run all three lanes in parallel. Each lane runs its own bounded task pool
235
+ // so a slow `graph_consolidate` cannot block embed or fast jobs from making
236
+ // progress, and per-`(type, conversationId)` grouping inside each lane keeps
237
+ // same-conversation jobs serialized.
238
+ const [slowProcessed, fastProcessed, embedProcessed] = await Promise.all([
239
+ runLanePool(slowJobs, cfgSlow, processGroup),
240
+ runLanePool(fastJobs, cfgFast, processGroup),
241
+ runLanePool(embedJobs, cfgEmbed, processGroup),
242
+ ]);
243
+
244
+ if (enableScheduledCleanup) {
245
+ maybeEnqueueScheduledCleanupJobs(config);
246
+ }
247
+ maybeEnqueueGraphMaintenanceJobs(config);
248
+ maybeRunDbMaintenance();
249
+ return slowProcessed + fastProcessed + embedProcessed;
250
+ }
251
+
252
+ /**
253
+ * Run a single lane's jobs through a bounded task pool of size `concurrency`.
254
+ *
255
+ * Jobs targeting different conversations (via payload.conversationId) are
256
+ * placed in separate groups and run in parallel up to the lane's concurrency
257
+ * cap. Jobs targeting the same conversation — or global jobs without a
258
+ * conversationId — share a group and run sequentially to avoid checkpoint
259
+ * races.
260
+ */
261
+ async function runLanePool(
262
+ jobs: MemoryJob[],
263
+ concurrency: number,
264
+ processGroup: ProcessGroup,
265
+ ): Promise<number> {
266
+ if (jobs.length === 0) return 0;
267
+
268
+ const groups = new Map<string, MemoryJob[]>();
269
+ for (const job of jobs) {
270
+ const convId =
271
+ typeof job.payload.conversationId === "string"
272
+ ? job.payload.conversationId
273
+ : null;
274
+ const groupKey = convId ? `${job.type}:${convId}` : job.type;
275
+ let group = groups.get(groupKey);
276
+ if (!group) {
277
+ group = [];
278
+ groups.set(groupKey, group);
279
+ }
280
+ group.push(job);
281
+ }
282
+
283
+ let processed = 0;
284
+ const typeGroups = [...groups.values()];
285
+
232
286
  if (typeGroups.length <= concurrency) {
233
- // Fast path: all groups fit within the concurrency limit
234
287
  const results = await Promise.allSettled(typeGroups.map(processGroup));
235
288
  for (const result of results) {
236
289
  if (result.status === "fulfilled") {
@@ -242,39 +295,35 @@ async function runMemoryJobsOnce(
242
295
  );
243
296
  }
244
297
  }
245
- } else {
246
- // Task pool: maintain `concurrency` in-flight groups at all times
247
- let nextIdx = 0;
248
-
249
- const startNext = (): Promise<void> | undefined => {
250
- if (nextIdx >= typeGroups.length) return undefined;
251
- const group = typeGroups[nextIdx++]!;
252
- return processGroup(group)
253
- .then(
254
- (count) => {
255
- processed += count;
256
- },
257
- (err) => {
258
- log.error(
259
- { err },
260
- "Memory job group rejected unexpectedly — jobs in this batch may have been dropped",
261
- );
262
- },
263
- )
264
- .then(() => startNext());
265
- };
266
-
267
- const workers = Array.from(
268
- { length: Math.min(concurrency, typeGroups.length) },
269
- () => startNext()!,
270
- );
271
- await Promise.all(workers);
298
+ return processed;
272
299
  }
273
- if (enableScheduledCleanup) {
274
- maybeEnqueueScheduledCleanupJobs(config);
275
- }
276
- maybeEnqueueGraphMaintenanceJobs(config);
277
- maybeRunDbMaintenance();
300
+
301
+ // Task pool: keep `concurrency` groups in flight at all times so a new group
302
+ // starts the instant any slot frees up.
303
+ let nextIdx = 0;
304
+ const startNext = (): Promise<void> | undefined => {
305
+ if (nextIdx >= typeGroups.length) return undefined;
306
+ const group = typeGroups[nextIdx++]!;
307
+ return processGroup(group)
308
+ .then(
309
+ (count) => {
310
+ processed += count;
311
+ },
312
+ (err) => {
313
+ log.error(
314
+ { err },
315
+ "Memory job group rejected unexpectedly — jobs in this batch may have been dropped",
316
+ );
317
+ },
318
+ )
319
+ .then(() => startNext());
320
+ };
321
+
322
+ const workers = Array.from(
323
+ { length: Math.min(concurrency, typeGroups.length) },
324
+ () => startNext()!,
325
+ );
326
+ await Promise.all(workers);
278
327
  return processed;
279
328
  }
280
329
 
@@ -14,7 +14,7 @@ export interface MemoryV2ConceptRowRecord {
14
14
  simNow: number;
15
15
  spreadContribution: number;
16
16
  source: "prior_state" | "ann_top50" | "both";
17
- status: "in_context" | "injected" | "not_injected";
17
+ status: "in_context" | "injected" | "not_injected" | "page_missing";
18
18
  }
19
19
 
20
20
  export interface MemoryV2SkillRowRecord {
@@ -0,0 +1,45 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+ import { tableHasColumn } from "./schema-introspection.js";
4
+ import { withCrashRecovery } from "./validate-migration-state.js";
5
+
6
+ const CHECKPOINT_KEY = "migration_heartbeat_runs_v1";
7
+
8
+ /**
9
+ * Create the heartbeat_runs table for tracking heartbeat execution lifecycle.
10
+ *
11
+ * Each row represents one scheduled heartbeat tick, tracking its progression
12
+ * through the status lifecycle: pending -> running -> ok/error/timeout, or
13
+ * pending -> skipped/missed/superseded.
14
+ */
15
+ export function migrateHeartbeatRuns(database: DrizzleDb): void {
16
+ withCrashRecovery(database, CHECKPOINT_KEY, () => {
17
+ if (tableHasColumn(database, "heartbeat_runs", "id")) {
18
+ return;
19
+ }
20
+ const raw = getSqliteFrom(database);
21
+ raw.exec(/*sql*/ `
22
+ CREATE TABLE IF NOT EXISTS heartbeat_runs (
23
+ id TEXT PRIMARY KEY,
24
+ scheduled_for INTEGER NOT NULL,
25
+ started_at INTEGER,
26
+ finished_at INTEGER,
27
+ duration_ms INTEGER,
28
+ status TEXT NOT NULL,
29
+ skip_reason TEXT,
30
+ error TEXT,
31
+ conversation_id TEXT,
32
+ created_at INTEGER NOT NULL
33
+ )
34
+ `);
35
+ raw.exec(/*sql*/ `
36
+ CREATE INDEX IF NOT EXISTS idx_heartbeat_runs_scheduled_for
37
+ ON heartbeat_runs (scheduled_for)
38
+ `);
39
+ });
40
+ }
41
+
42
+ export function downHeartbeatRuns(database: DrizzleDb): void {
43
+ const raw = getSqliteFrom(database);
44
+ raw.exec(/*sql*/ `DROP TABLE IF EXISTS heartbeat_runs`);
45
+ }
@@ -0,0 +1,20 @@
1
+ import type { DrizzleDb } from "../db-connection.js";
2
+ import { getSqliteFrom } from "../db-connection.js";
3
+
4
+ export function migrateScheduleRetryPolicy(database: DrizzleDb): void {
5
+ const raw = getSqliteFrom(database);
6
+ try {
7
+ raw.exec(
8
+ `ALTER TABLE cron_jobs ADD COLUMN max_retries INTEGER NOT NULL DEFAULT 3`,
9
+ );
10
+ } catch {
11
+ /* Column already exists */
12
+ }
13
+ try {
14
+ raw.exec(
15
+ `ALTER TABLE cron_jobs ADD COLUMN retry_backoff_ms INTEGER NOT NULL DEFAULT 60000`,
16
+ );
17
+ } catch {
18
+ /* Column already exists */
19
+ }
20
+ }
@@ -195,6 +195,11 @@ export {
195
195
  downToolInvocationsMatchedRuleId,
196
196
  migrateToolInvocationsMatchedRuleId,
197
197
  } from "./236-tool-invocations-matched-rule-id.js";
198
+ export {
199
+ downHeartbeatRuns,
200
+ migrateHeartbeatRuns,
201
+ } from "./237-heartbeat-runs.js";
202
+ export { migrateScheduleRetryPolicy } from "./238-schedule-retry-policy.js";
198
203
  export {
199
204
  MIGRATION_REGISTRY,
200
205
  type MigrationRegistryEntry,
@@ -47,6 +47,7 @@ import { downActivationState } from "./232-activation-state.js";
47
47
  import { downMemoryV2ActivationLogs } from "./234-memory-v2-activation-logs.js";
48
48
  import { downSlackCompactionWatermark } from "./235-slack-compaction-watermark.js";
49
49
  import { downToolInvocationsMatchedRuleId } from "./236-tool-invocations-matched-rule-id.js";
50
+ import { downHeartbeatRuns } from "./237-heartbeat-runs.js";
50
51
 
51
52
  export interface MigrationRegistryEntry {
52
53
  /** The checkpoint key written to memory_checkpoints on completion. */
@@ -404,6 +405,13 @@ export const MIGRATION_REGISTRY: MigrationRegistryEntry[] = [
404
405
  "Add matched_trust_rule_id column to tool_invocations for trust rule audit and rule editor UI",
405
406
  down: downToolInvocationsMatchedRuleId,
406
407
  },
408
+ {
409
+ key: "migration_heartbeat_runs_v1",
410
+ version: 47,
411
+ description:
412
+ "Create heartbeat_runs table for tracking heartbeat execution lifecycle with CAS state transitions",
413
+ down: downHeartbeatRuns,
414
+ },
407
415
  ];
408
416
 
409
417
  export function getMaxMigrationVersion(): number {
@@ -2,7 +2,9 @@
2
2
  // PKB — Qdrant hybrid search for indexed PKB markdown files
3
3
  // ---------------------------------------------------------------------------
4
4
 
5
+ import { getConfig } from "../../config/loader.js";
5
6
  import { getLogger } from "../../util/logger.js";
7
+ import { isMemoryV2ReadActive } from "../context-search/sources/memory-v2.js";
6
8
  import {
7
9
  isQdrantBreakerOpen,
8
10
  withQdrantBreaker,
@@ -40,6 +42,11 @@ export async function searchPkbFiles(
40
42
  limit: number,
41
43
  scopeIds?: string[],
42
44
  ): Promise<PkbSearchResult[]> {
45
+ // v2 owns the read path when both gates are on; v2 absorbs PKB as a read
46
+ // source, so PKB hint search short-circuits to keep traffic off the v1
47
+ // collection (avoiding OOM-crash risk from a corrupted sparse segment).
48
+ if (isMemoryV2ReadActive(getConfig())) return [];
49
+
43
50
  if (isQdrantBreakerOpen()) {
44
51
  log.warn("Qdrant circuit breaker open, skipping PKB search");
45
52
  return [];