@vellumai/assistant 0.10.0 → 0.10.1-staging.1

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 (824) hide show
  1. package/ARCHITECTURE.md +36 -37
  2. package/bun.lock +3 -0
  3. package/docs/workflows.md +12 -7
  4. package/eslint-rules/cli-no-daemon-internals.js +6 -0
  5. package/node_modules/@slack/types/LICENSE +23 -0
  6. package/node_modules/@slack/types/README.md +32 -0
  7. package/node_modules/@slack/types/dist/block-kit/block-elements.d.ts +953 -0
  8. package/node_modules/@slack/types/dist/block-kit/block-elements.d.ts.map +1 -0
  9. package/node_modules/@slack/types/dist/block-kit/block-elements.js +4 -0
  10. package/node_modules/@slack/types/dist/block-kit/block-elements.js.map +1 -0
  11. package/node_modules/@slack/types/dist/block-kit/blocks.d.ts +474 -0
  12. package/node_modules/@slack/types/dist/block-kit/blocks.d.ts.map +1 -0
  13. package/node_modules/@slack/types/dist/block-kit/blocks.js +3 -0
  14. package/node_modules/@slack/types/dist/block-kit/blocks.js.map +1 -0
  15. package/node_modules/@slack/types/dist/block-kit/composition-objects.d.ts +237 -0
  16. package/node_modules/@slack/types/dist/block-kit/composition-objects.d.ts.map +1 -0
  17. package/node_modules/@slack/types/dist/block-kit/composition-objects.js +4 -0
  18. package/node_modules/@slack/types/dist/block-kit/composition-objects.js.map +1 -0
  19. package/node_modules/@slack/types/dist/block-kit/extensions.d.ts +88 -0
  20. package/node_modules/@slack/types/dist/block-kit/extensions.d.ts.map +1 -0
  21. package/node_modules/@slack/types/dist/block-kit/extensions.js +3 -0
  22. package/node_modules/@slack/types/dist/block-kit/extensions.js.map +1 -0
  23. package/node_modules/@slack/types/dist/calls.d.ts +26 -0
  24. package/node_modules/@slack/types/dist/calls.d.ts.map +1 -0
  25. package/node_modules/@slack/types/dist/calls.js +6 -0
  26. package/node_modules/@slack/types/dist/calls.js.map +1 -0
  27. package/node_modules/@slack/types/dist/chunk.d.ts +52 -0
  28. package/node_modules/@slack/types/dist/chunk.d.ts.map +1 -0
  29. package/node_modules/@slack/types/dist/chunk.js +3 -0
  30. package/node_modules/@slack/types/dist/chunk.js.map +1 -0
  31. package/node_modules/@slack/types/dist/common/bot-profile.d.ts +12 -0
  32. package/node_modules/@slack/types/dist/common/bot-profile.d.ts.map +1 -0
  33. package/node_modules/@slack/types/dist/common/bot-profile.js +3 -0
  34. package/node_modules/@slack/types/dist/common/bot-profile.js.map +1 -0
  35. package/node_modules/@slack/types/dist/common/status-emoji-display-info.d.ts +6 -0
  36. package/node_modules/@slack/types/dist/common/status-emoji-display-info.d.ts.map +1 -0
  37. package/node_modules/@slack/types/dist/common/status-emoji-display-info.js +3 -0
  38. package/node_modules/@slack/types/dist/common/status-emoji-display-info.js.map +1 -0
  39. package/node_modules/@slack/types/dist/dialog.d.ts +36 -0
  40. package/node_modules/@slack/types/dist/dialog.d.ts.map +1 -0
  41. package/node_modules/@slack/types/dist/dialog.js +3 -0
  42. package/node_modules/@slack/types/dist/dialog.js.map +1 -0
  43. package/node_modules/@slack/types/dist/events/app.d.ts +204 -0
  44. package/node_modules/@slack/types/dist/events/app.d.ts.map +1 -0
  45. package/node_modules/@slack/types/dist/events/app.js +3 -0
  46. package/node_modules/@slack/types/dist/events/app.js.map +1 -0
  47. package/node_modules/@slack/types/dist/events/assistant.d.ts +29 -0
  48. package/node_modules/@slack/types/dist/events/assistant.d.ts.map +1 -0
  49. package/node_modules/@slack/types/dist/events/assistant.js +3 -0
  50. package/node_modules/@slack/types/dist/events/assistant.js.map +1 -0
  51. package/node_modules/@slack/types/dist/events/call.d.ts +8 -0
  52. package/node_modules/@slack/types/dist/events/call.d.ts.map +1 -0
  53. package/node_modules/@slack/types/dist/events/call.js +3 -0
  54. package/node_modules/@slack/types/dist/events/call.js.map +1 -0
  55. package/node_modules/@slack/types/dist/events/channel.d.ts +85 -0
  56. package/node_modules/@slack/types/dist/events/channel.d.ts.map +1 -0
  57. package/node_modules/@slack/types/dist/events/channel.js +3 -0
  58. package/node_modules/@slack/types/dist/events/channel.js.map +1 -0
  59. package/node_modules/@slack/types/dist/events/dnd.d.ts +24 -0
  60. package/node_modules/@slack/types/dist/events/dnd.d.ts.map +1 -0
  61. package/node_modules/@slack/types/dist/events/dnd.js +3 -0
  62. package/node_modules/@slack/types/dist/events/dnd.js.map +1 -0
  63. package/node_modules/@slack/types/dist/events/email.d.ts +6 -0
  64. package/node_modules/@slack/types/dist/events/email.d.ts.map +1 -0
  65. package/node_modules/@slack/types/dist/events/email.js +3 -0
  66. package/node_modules/@slack/types/dist/events/email.js.map +1 -0
  67. package/node_modules/@slack/types/dist/events/emoji.d.ts +11 -0
  68. package/node_modules/@slack/types/dist/events/emoji.d.ts.map +1 -0
  69. package/node_modules/@slack/types/dist/events/emoji.js +3 -0
  70. package/node_modules/@slack/types/dist/events/emoji.js.map +1 -0
  71. package/node_modules/@slack/types/dist/events/entity-details-requested.d.ts +21 -0
  72. package/node_modules/@slack/types/dist/events/entity-details-requested.d.ts.map +1 -0
  73. package/node_modules/@slack/types/dist/events/entity-details-requested.js +3 -0
  74. package/node_modules/@slack/types/dist/events/entity-details-requested.js.map +1 -0
  75. package/node_modules/@slack/types/dist/events/file.d.ts +60 -0
  76. package/node_modules/@slack/types/dist/events/file.d.ts.map +1 -0
  77. package/node_modules/@slack/types/dist/events/file.js +4 -0
  78. package/node_modules/@slack/types/dist/events/file.js.map +1 -0
  79. package/node_modules/@slack/types/dist/events/function.d.ts +33 -0
  80. package/node_modules/@slack/types/dist/events/function.d.ts.map +1 -0
  81. package/node_modules/@slack/types/dist/events/function.js +3 -0
  82. package/node_modules/@slack/types/dist/events/function.js.map +1 -0
  83. package/node_modules/@slack/types/dist/events/grid-migration.d.ts +9 -0
  84. package/node_modules/@slack/types/dist/events/grid-migration.d.ts.map +1 -0
  85. package/node_modules/@slack/types/dist/events/grid-migration.js +3 -0
  86. package/node_modules/@slack/types/dist/events/grid-migration.js.map +1 -0
  87. package/node_modules/@slack/types/dist/events/group.d.ts +55 -0
  88. package/node_modules/@slack/types/dist/events/group.d.ts.map +1 -0
  89. package/node_modules/@slack/types/dist/events/group.js +3 -0
  90. package/node_modules/@slack/types/dist/events/group.js.map +1 -0
  91. package/node_modules/@slack/types/dist/events/im.d.ts +26 -0
  92. package/node_modules/@slack/types/dist/events/im.d.ts.map +1 -0
  93. package/node_modules/@slack/types/dist/events/im.js +3 -0
  94. package/node_modules/@slack/types/dist/events/im.js.map +1 -0
  95. package/node_modules/@slack/types/dist/events/index.d.ts +60 -0
  96. package/node_modules/@slack/types/dist/events/index.d.ts.map +1 -0
  97. package/node_modules/@slack/types/dist/events/index.js +43 -0
  98. package/node_modules/@slack/types/dist/events/index.js.map +1 -0
  99. package/node_modules/@slack/types/dist/events/invite.d.ts +20 -0
  100. package/node_modules/@slack/types/dist/events/invite.d.ts.map +1 -0
  101. package/node_modules/@slack/types/dist/events/invite.js +3 -0
  102. package/node_modules/@slack/types/dist/events/invite.js.map +1 -0
  103. package/node_modules/@slack/types/dist/events/link-shared.d.ts +16 -0
  104. package/node_modules/@slack/types/dist/events/link-shared.d.ts.map +1 -0
  105. package/node_modules/@slack/types/dist/events/link-shared.js +3 -0
  106. package/node_modules/@slack/types/dist/events/link-shared.js.map +1 -0
  107. package/node_modules/@slack/types/dist/events/member.d.ts +19 -0
  108. package/node_modules/@slack/types/dist/events/member.d.ts.map +1 -0
  109. package/node_modules/@slack/types/dist/events/member.js +3 -0
  110. package/node_modules/@slack/types/dist/events/member.js.map +1 -0
  111. package/node_modules/@slack/types/dist/events/message-metadata.d.ts +38 -0
  112. package/node_modules/@slack/types/dist/events/message-metadata.d.ts.map +1 -0
  113. package/node_modules/@slack/types/dist/events/message-metadata.js +3 -0
  114. package/node_modules/@slack/types/dist/events/message-metadata.js.map +1 -0
  115. package/node_modules/@slack/types/dist/events/message.d.ts +306 -0
  116. package/node_modules/@slack/types/dist/events/message.d.ts.map +1 -0
  117. package/node_modules/@slack/types/dist/events/message.js +3 -0
  118. package/node_modules/@slack/types/dist/events/message.js.map +1 -0
  119. package/node_modules/@slack/types/dist/events/pin.d.ts +60 -0
  120. package/node_modules/@slack/types/dist/events/pin.d.ts.map +1 -0
  121. package/node_modules/@slack/types/dist/events/pin.js +3 -0
  122. package/node_modules/@slack/types/dist/events/pin.js.map +1 -0
  123. package/node_modules/@slack/types/dist/events/reaction.d.ts +23 -0
  124. package/node_modules/@slack/types/dist/events/reaction.d.ts.map +1 -0
  125. package/node_modules/@slack/types/dist/events/reaction.js +3 -0
  126. package/node_modules/@slack/types/dist/events/reaction.js.map +1 -0
  127. package/node_modules/@slack/types/dist/events/shared-channel.d.ts +134 -0
  128. package/node_modules/@slack/types/dist/events/shared-channel.d.ts.map +1 -0
  129. package/node_modules/@slack/types/dist/events/shared-channel.js +3 -0
  130. package/node_modules/@slack/types/dist/events/shared-channel.js.map +1 -0
  131. package/node_modules/@slack/types/dist/events/star.d.ts +13 -0
  132. package/node_modules/@slack/types/dist/events/star.d.ts.map +1 -0
  133. package/node_modules/@slack/types/dist/events/star.js +3 -0
  134. package/node_modules/@slack/types/dist/events/star.js.map +1 -0
  135. package/node_modules/@slack/types/dist/events/steps-from-apps.d.ts +82 -0
  136. package/node_modules/@slack/types/dist/events/steps-from-apps.d.ts.map +1 -0
  137. package/node_modules/@slack/types/dist/events/steps-from-apps.js +3 -0
  138. package/node_modules/@slack/types/dist/events/steps-from-apps.js.map +1 -0
  139. package/node_modules/@slack/types/dist/events/subteam.d.ts +66 -0
  140. package/node_modules/@slack/types/dist/events/subteam.d.ts.map +1 -0
  141. package/node_modules/@slack/types/dist/events/subteam.js +3 -0
  142. package/node_modules/@slack/types/dist/events/subteam.js.map +1 -0
  143. package/node_modules/@slack/types/dist/events/team.d.ts +99 -0
  144. package/node_modules/@slack/types/dist/events/team.d.ts.map +1 -0
  145. package/node_modules/@slack/types/dist/events/team.js +3 -0
  146. package/node_modules/@slack/types/dist/events/team.js.map +1 -0
  147. package/node_modules/@slack/types/dist/events/token.d.ts +8 -0
  148. package/node_modules/@slack/types/dist/events/token.d.ts.map +1 -0
  149. package/node_modules/@slack/types/dist/events/token.js +3 -0
  150. package/node_modules/@slack/types/dist/events/token.js.map +1 -0
  151. package/node_modules/@slack/types/dist/events/user.d.ts +313 -0
  152. package/node_modules/@slack/types/dist/events/user.d.ts.map +1 -0
  153. package/node_modules/@slack/types/dist/events/user.js +3 -0
  154. package/node_modules/@slack/types/dist/events/user.js.map +1 -0
  155. package/node_modules/@slack/types/dist/index.d.ts +12 -0
  156. package/node_modules/@slack/types/dist/index.d.ts.map +1 -0
  157. package/node_modules/@slack/types/dist/index.js +28 -0
  158. package/node_modules/@slack/types/dist/index.js.map +1 -0
  159. package/node_modules/@slack/types/dist/message-attachments.d.ts +171 -0
  160. package/node_modules/@slack/types/dist/message-attachments.d.ts.map +1 -0
  161. package/node_modules/@slack/types/dist/message-attachments.js +3 -0
  162. package/node_modules/@slack/types/dist/message-attachments.js.map +1 -0
  163. package/node_modules/@slack/types/dist/message-metadata.d.ts +281 -0
  164. package/node_modules/@slack/types/dist/message-metadata.d.ts.map +1 -0
  165. package/node_modules/@slack/types/dist/message-metadata.js +27 -0
  166. package/node_modules/@slack/types/dist/message-metadata.js.map +1 -0
  167. package/node_modules/@slack/types/dist/views.d.ts +71 -0
  168. package/node_modules/@slack/types/dist/views.d.ts.map +1 -0
  169. package/node_modules/@slack/types/dist/views.js +3 -0
  170. package/node_modules/@slack/types/dist/views.js.map +1 -0
  171. package/node_modules/@slack/types/package.json +47 -0
  172. package/node_modules/@vellumai/gateway-client/bun.lock +3 -0
  173. package/node_modules/@vellumai/gateway-client/package.json +1 -0
  174. package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +69 -0
  175. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +65 -0
  176. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +162 -0
  177. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +8 -0
  178. package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
  179. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +4 -2
  180. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +3 -2
  181. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +78 -0
  182. package/openapi.yaml +345 -18
  183. package/package.json +2 -1
  184. package/scripts/memory-inspect.ts +24 -14
  185. package/src/__tests__/access-request-seed-content-blocks.test.ts +83 -103
  186. package/src/__tests__/activation-early-marking.test.ts +1 -1
  187. package/src/__tests__/actor-token-service.test.ts +3 -3
  188. package/src/__tests__/agent-loop-callsite-precedence.test.ts +1 -40
  189. package/src/__tests__/agent-loop-compaction-events.test.ts +0 -1
  190. package/src/__tests__/agent-loop-compaction-strip.test.ts +0 -1
  191. package/src/__tests__/agent-loop-exit-reason.test.ts +0 -1
  192. package/src/__tests__/agent-loop-pushes-post-hook-prompt.test.ts +306 -0
  193. package/src/__tests__/agent-loop-regrowth-guard.test.ts +0 -1
  194. package/src/__tests__/agent-loop.test.ts +3 -0
  195. package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
  196. package/src/__tests__/anthropic-provider.test.ts +143 -9
  197. package/src/__tests__/app-builder-skill-instructions.test.ts +47 -5
  198. package/src/__tests__/app-conversation-ids-backfill.test.ts +1 -1
  199. package/src/__tests__/app-source-watcher.test.ts +30 -10
  200. package/src/__tests__/approval-cascade.test.ts +6 -0
  201. package/src/__tests__/approval-interception-trust-gates.test.ts +151 -0
  202. package/src/__tests__/approval-primitive.test.ts +1 -1
  203. package/src/__tests__/approval-routes-http.test.ts +1 -1
  204. package/src/__tests__/assistant-attachments.test.ts +155 -0
  205. package/src/__tests__/assistant-event-hub-machine-name.test.ts +2 -4
  206. package/src/__tests__/assistant-events-sse-hardening.test.ts +1 -1
  207. package/src/__tests__/assistant-events-sse-shed.test.ts +1 -1
  208. package/src/__tests__/attachment-upload-trusted-source.test.ts +13 -8
  209. package/src/__tests__/attachments-store.test.ts +1 -1
  210. package/src/__tests__/audit-log-rotation.test.ts +50 -54
  211. package/src/__tests__/auth-fallback-events-store.test.ts +1 -1
  212. package/src/__tests__/auto-analysis-end-to-end.test.ts +9 -14
  213. package/src/__tests__/background-shell-bash.test.ts +4 -1
  214. package/src/__tests__/background-shell-host-bash.test.ts +17 -3
  215. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -0
  216. package/src/__tests__/call-controller.test.ts +1 -1
  217. package/src/__tests__/call-conversation-messages.test.ts +1 -1
  218. package/src/__tests__/call-domain.test.ts +1 -1
  219. package/src/__tests__/call-pointer-messages.test.ts +3 -4
  220. package/src/__tests__/call-recovery.test.ts +1 -1
  221. package/src/__tests__/call-routes-http.test.ts +1 -1
  222. package/src/__tests__/call-store.test.ts +1 -1
  223. package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
  224. package/src/__tests__/canonical-guardian-store.test.ts +24 -1
  225. package/src/__tests__/channel-approval-routes.test.ts +73 -1119
  226. package/src/__tests__/channel-delivery-store.test.ts +1 -1
  227. package/src/__tests__/channel-guardian.test.ts +265 -641
  228. package/src/__tests__/channel-inbound-disk-pressure.test.ts +1 -2
  229. package/src/__tests__/channel-retry-sweep.test.ts +1 -1
  230. package/src/__tests__/compaction-events.test.ts +6 -0
  231. package/src/__tests__/compaction-trail-store.test.ts +6 -5
  232. package/src/__tests__/compaction.benchmark.test.ts +0 -1
  233. package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
  234. package/src/__tests__/config-loader-backfill.test.ts +183 -51
  235. package/src/__tests__/config-schema.test.ts +34 -0
  236. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -2
  237. package/src/__tests__/contact-store-user-file.test.ts +2 -2
  238. package/src/__tests__/contacts-relay-reads.test.ts +409 -0
  239. package/src/__tests__/contacts-tools.test.ts +4 -4
  240. package/src/__tests__/contacts-write.test.ts +1 -2
  241. package/src/__tests__/context-search-conversations-source.test.ts +1 -1
  242. package/src/__tests__/context-window-manager-compact-retry.test.ts +6 -2
  243. package/src/__tests__/context-window-manager-overflow-rung.test.ts +6 -2
  244. package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
  245. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +3 -0
  246. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +3 -0
  247. package/src/__tests__/conversation-agent-loop-overflow.test.ts +3 -0
  248. package/src/__tests__/conversation-agent-loop.test.ts +3 -0
  249. package/src/__tests__/conversation-attachments.test.ts +2 -5
  250. package/src/__tests__/conversation-attention-store.test.ts +1 -1
  251. package/src/__tests__/conversation-attention-telegram.test.ts +1 -2
  252. package/src/__tests__/conversation-clear-safety.test.ts +1 -1
  253. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -0
  254. package/src/__tests__/conversation-crud-inference-profile.test.ts +1 -1
  255. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +12 -19
  256. package/src/__tests__/conversation-disk-view-integration.test.ts +1 -1
  257. package/src/__tests__/conversation-disk-view.test.ts +1 -1
  258. package/src/__tests__/conversation-fork-crud.test.ts +10 -8
  259. package/src/__tests__/conversation-fork-retrospective.test.ts +250 -0
  260. package/src/__tests__/conversation-fork-route.test.ts +1 -1
  261. package/src/__tests__/conversation-inference-profile-list.test.ts +1 -1
  262. package/src/__tests__/conversation-inference-profile-route.test.ts +1 -1
  263. package/src/__tests__/conversation-init.benchmark.test.ts +1 -1
  264. package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
  265. package/src/__tests__/conversation-lifecycle.test.ts +117 -0
  266. package/src/__tests__/conversation-list-source.test.ts +3 -3
  267. package/src/__tests__/conversation-process-callsite.test.ts +6 -14
  268. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
  269. package/src/__tests__/conversation-queue.test.ts +6 -0
  270. package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
  271. package/src/__tests__/conversation-runtime-assembly.test.ts +115 -12
  272. package/src/__tests__/conversation-slash-queue.test.ts +6 -0
  273. package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
  274. package/src/__tests__/conversation-speed-override.test.ts +6 -0
  275. package/src/__tests__/conversation-starter-routes.test.ts +5 -5
  276. package/src/__tests__/conversation-store.test.ts +1 -1
  277. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -1
  278. package/src/__tests__/conversation-sync-tags.test.ts +1 -1
  279. package/src/__tests__/conversation-usage.test.ts +1 -1
  280. package/src/__tests__/conversation-wipe.test.ts +9 -8
  281. package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -0
  282. package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
  283. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
  284. package/src/__tests__/conversations-import-system-filter.test.ts +1 -1
  285. package/src/__tests__/copy-composer-tc-templates.test.ts +17 -0
  286. package/src/__tests__/credential-security-invariants.test.ts +0 -1
  287. package/src/__tests__/db-acp-history.test.ts +2 -2
  288. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +5 -7
  289. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +6 -7
  290. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +5 -10
  291. package/src/__tests__/db-migration-rollback.test.ts +129 -39
  292. package/src/__tests__/db-proxy-transaction.test.ts +1 -1
  293. package/src/__tests__/db-schedule-syntax-migration.test.ts +0 -11
  294. package/src/__tests__/db-test-helpers.ts +36 -19
  295. package/src/__tests__/delete-propagation.test.ts +1 -1
  296. package/src/__tests__/deterministic-verification-control-plane.test.ts +26 -8
  297. package/src/__tests__/disk-pressure-tools.test.ts +41 -1
  298. package/src/__tests__/dm-backfill.test.ts +1 -1
  299. package/src/__tests__/drop-capability-card-state-migration.test.ts +0 -8
  300. package/src/__tests__/edit-propagation.test.ts +1 -1
  301. package/src/__tests__/emit-signal-routing-intent.test.ts +83 -0
  302. package/src/__tests__/empty-response-hook.test.ts +42 -0
  303. package/src/__tests__/events-client-registration.test.ts +1 -1
  304. package/src/__tests__/followup-tools.test.ts +1 -1
  305. package/src/__tests__/gemini-count-tokens.test.ts +70 -0
  306. package/src/__tests__/guardian-action-sweep.test.ts +9 -2
  307. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  308. package/src/__tests__/guardian-card-withdrawal.test.ts +1 -1
  309. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +1 -1
  310. package/src/__tests__/guardian-dispatch.test.ts +1 -1
  311. package/src/__tests__/guardian-outbound-http.test.ts +7 -12
  312. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +1 -1
  313. package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
  314. package/src/__tests__/guardian-routing-state.test.ts +1 -2
  315. package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -1
  316. package/src/__tests__/headless-browser-mode.test.ts +2 -2
  317. package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
  318. package/src/__tests__/heartbeat-service.test.ts +6 -0
  319. package/src/__tests__/helpers/channel-test-adapter.ts +98 -0
  320. package/src/__tests__/http-conversation-lineage.test.ts +1 -1
  321. package/src/__tests__/image-recovery-hook.test.ts +1 -1
  322. package/src/__tests__/inbound-invite-redemption.test.ts +1 -2
  323. package/src/__tests__/inbound-trust-verdict.test.ts +254 -0
  324. package/src/__tests__/inference-profile-reaper.test.ts +1 -1
  325. package/src/__tests__/inference-profile-session-handler.test.ts +1 -1
  326. package/src/__tests__/inference-profile-session-ipc.test.ts +1 -1
  327. package/src/__tests__/injector-chain.test.ts +1 -1
  328. package/src/__tests__/injector-disk-pressure.test.ts +11 -6
  329. package/src/__tests__/internal-telemetry-routes.test.ts +1 -1
  330. package/src/__tests__/invite-redemption-service.test.ts +244 -43
  331. package/src/__tests__/invite-routes-http.test.ts +35 -186
  332. package/src/__tests__/invite-service-ipc.test.ts +287 -0
  333. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +5 -5
  334. package/src/__tests__/jobs-store-upsert-debounced.test.ts +9 -12
  335. package/src/__tests__/list-messages-attachments.test.ts +42 -1
  336. package/src/__tests__/list-messages-client-message-id.test.ts +1 -1
  337. package/src/__tests__/list-messages-hidden-metadata.test.ts +1 -1
  338. package/src/__tests__/list-messages-page-latest.test.ts +1 -1
  339. package/src/__tests__/list-messages-tool-merge.test.ts +1 -1
  340. package/src/__tests__/llm-context-route-provider.test.ts +69 -4
  341. package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +9 -5
  342. package/src/__tests__/llm-request-log-call-site.test.ts +6 -6
  343. package/src/__tests__/llm-request-log-turn-query.test.ts +27 -13
  344. package/src/__tests__/llm-usage-store.test.ts +40 -1
  345. package/src/__tests__/log-export-routes.test.ts +1 -1
  346. package/src/__tests__/log-export-workspace.test.ts +3 -3
  347. package/src/__tests__/memory-jobs-worker-lanes.test.ts +5 -5
  348. package/src/__tests__/memory-recall-log-store.test.ts +1 -1
  349. package/src/__tests__/memory-upsert-concurrency.test.ts +3 -4
  350. package/src/__tests__/messages-after-tiebreaker.test.ts +1 -1
  351. package/src/__tests__/migration-import-from-url.test.ts +2 -2
  352. package/src/__tests__/mtime-cache.test.ts +375 -0
  353. package/src/__tests__/non-member-access-request.test.ts +1 -2
  354. package/src/__tests__/notification-candidate-guardian-context.test.ts +203 -0
  355. package/src/__tests__/notification-guardian-path.test.ts +1 -1
  356. package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
  357. package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
  358. package/src/__tests__/oauth-provider-visibility.test.ts +1 -1
  359. package/src/__tests__/oauth-store.test.ts +1 -1
  360. package/src/__tests__/persist-unsendable-image-downscale.test.ts +1 -1
  361. package/src/__tests__/persist-unsendable-image.test.ts +1 -1
  362. package/src/__tests__/persona-resolver.test.ts +39 -1
  363. package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
  364. package/src/__tests__/playbook-execution.test.ts +1 -1
  365. package/src/__tests__/playbook-tools.test.ts +1 -1
  366. package/src/__tests__/plugin-api-model-profiles.test.ts +74 -21
  367. package/src/__tests__/plugin-bootstrap.test.ts +78 -0
  368. package/src/__tests__/provider-platform-proxy-integration.test.ts +25 -5
  369. package/src/__tests__/provider-usage-tracking.test.ts +1 -1
  370. package/src/__tests__/prune-old-conversations-job.test.ts +1 -1
  371. package/src/__tests__/reaction-persistence.test.ts +1 -1
  372. package/src/__tests__/relay-server.test.ts +357 -56
  373. package/src/__tests__/runtime-attachment-metadata.test.ts +10 -1
  374. package/src/__tests__/runtime-events-sse-bilingual.test.ts +7 -9
  375. package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
  376. package/src/__tests__/runtime-events-sse-reconnect.test.ts +1 -1
  377. package/src/__tests__/runtime-events-sse.test.ts +1 -1
  378. package/src/__tests__/schedule-retry.test.ts +1 -1
  379. package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -1
  380. package/src/__tests__/schedule-routes.test.ts +1 -1
  381. package/src/__tests__/schedule-store.test.ts +1 -1
  382. package/src/__tests__/schedule-tools.test.ts +1 -1
  383. package/src/__tests__/scheduler-disk-pressure.test.ts +1 -1
  384. package/src/__tests__/scheduler-recurrence.test.ts +1 -1
  385. package/src/__tests__/scheduler-reuse-conversation.test.ts +1 -1
  386. package/src/__tests__/scheduler-wake.test.ts +2 -1
  387. package/src/__tests__/scoped-approval-grants.test.ts +1 -1
  388. package/src/__tests__/scoped-grant-security-matrix.test.ts +5 -5
  389. package/src/__tests__/scrub-corrupted-image-attachments.test.ts +0 -8
  390. package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -0
  391. package/src/__tests__/send-endpoint-busy.test.ts +1 -1
  392. package/src/__tests__/sequence-store.test.ts +1 -1
  393. package/src/__tests__/server-history-render.test.ts +40 -1
  394. package/src/__tests__/settings-routes.test.ts +11 -10
  395. package/src/__tests__/skill-load-tool.test.ts +72 -0
  396. package/src/__tests__/slack-inbound-verification.test.ts +1 -3
  397. package/src/__tests__/slack-messaging-token-resolution.test.ts +13 -2
  398. package/src/__tests__/slack-reaction-canonical-approval.test.ts +1 -1
  399. package/src/__tests__/subagent-tool-gate-mode.test.ts +2 -73
  400. package/src/__tests__/subagent-tools.test.ts +1 -31
  401. package/src/__tests__/system-prompt.test.ts +1 -1
  402. package/src/__tests__/system-storage-cleanup-skill.test.ts +56 -0
  403. package/src/__tests__/task-compiler.test.ts +1 -1
  404. package/src/__tests__/task-management-tools.test.ts +1 -1
  405. package/src/__tests__/task-memory-cleanup.test.ts +9 -6
  406. package/src/__tests__/task-scheduler.test.ts +1 -1
  407. package/src/__tests__/thread-backfill.test.ts +1 -1
  408. package/src/__tests__/tool-approval-handler.test.ts +1 -1
  409. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +2 -0
  410. package/src/__tests__/tool-executor.test.ts +32 -1
  411. package/src/__tests__/tool-grant-request-escalation.test.ts +1 -2
  412. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +73 -1
  413. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +34 -34
  414. package/src/__tests__/trusted-contact-multichannel.test.ts +1 -2
  415. package/src/__tests__/trusted-contact-verification.test.ts +1 -1
  416. package/src/__tests__/turn-boundary-resolution.test.ts +3 -3
  417. package/src/__tests__/turn-events-store.test.ts +1 -1
  418. package/src/__tests__/twilio-routes.test.ts +2 -3
  419. package/src/__tests__/usage-cache-backfill-migration.test.ts +20 -10
  420. package/src/__tests__/usage-routes.test.ts +1 -1
  421. package/src/__tests__/user-plugin-loader.test.ts +34 -29
  422. package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
  423. package/src/__tests__/voice-invite-redemption.test.ts +134 -36
  424. package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
  425. package/src/__tests__/voice-session-bridge.test.ts +1 -1
  426. package/src/__tests__/workspace-git-service.test.ts +114 -1
  427. package/src/__tests__/workspace-heartbeat-service.test.ts +45 -0
  428. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +1 -1
  429. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
  430. package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +88 -18
  431. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +6 -6
  432. package/src/__tests__/workspace-migration-109-swap-quality-profile-to-glm-5p2.test.ts +281 -0
  433. package/src/__tests__/workspace-migration-110-flip-balanced-profile-to-together.test.ts +167 -0
  434. package/src/__tests__/workspace-migrations-runner.test.ts +55 -0
  435. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +1 -1
  436. package/src/a2a/__tests__/task-store.test.ts +1 -1
  437. package/src/acp/__tests__/session-manager-persistence.test.ts +1 -1
  438. package/src/acp/__tests__/session-manager-resume.test.ts +22 -11
  439. package/src/acp/__tests__/session-manager-startup.test.ts +1 -1
  440. package/src/acp/__tests__/session-manager.test.ts +72 -1
  441. package/src/acp/index.ts +10 -0
  442. package/src/acp/session-manager.ts +35 -0
  443. package/src/agent/loop.ts +45 -27
  444. package/src/api/index.ts +0 -6
  445. package/src/approvals/AGENTS.md +1 -2
  446. package/src/approvals/guardian-decision-primitive.ts +13 -210
  447. package/src/approvals/guardian-request-resolvers.ts +104 -58
  448. package/src/background-wake/wake-intent-hooks.test.ts +1 -1
  449. package/src/calls/__tests__/inbound-trust-reader.test.ts +110 -0
  450. package/src/calls/__tests__/relay-setup-router.test.ts +88 -62
  451. package/src/calls/inbound-trust-reader.ts +40 -0
  452. package/src/calls/relay-server.ts +65 -23
  453. package/src/calls/relay-setup-router.ts +20 -6
  454. package/src/calls/relay-verification.ts +7 -7
  455. package/src/cli/commands/contacts.ts +6 -24
  456. package/src/cli/commands/db/__tests__/repair.test.ts +15 -6
  457. package/src/cli/commands/db/__tests__/status.test.ts +7 -3
  458. package/src/cli/commands/db/status.ts +212 -33
  459. package/src/cli/commands/memory/__tests__/memory-v3.test.ts +6 -1
  460. package/src/cli/commands/memory/index.ts +2 -0
  461. package/src/cli/commands/memory/memory-retrospective.ts +129 -0
  462. package/src/cli/commands/memory/memory-v3.ts +176 -4
  463. package/src/cli/commands/plugins.ts +268 -11
  464. package/src/cli/lib/__tests__/install-from-github.test.ts +40 -0
  465. package/src/cli/lib/__tests__/plugin-pin-history.test.ts +162 -0
  466. package/src/cli/lib/__tests__/toggle-plugin.test.ts +158 -0
  467. package/src/cli/lib/install-from-github.ts +47 -6
  468. package/src/cli/lib/plugin-marketplace.ts +11 -0
  469. package/src/cli/lib/plugin-pin-history.ts +257 -0
  470. package/src/cli/lib/toggle-plugin.ts +146 -0
  471. package/src/config/__tests__/sync-gated-profiles.test.ts +2 -2
  472. package/src/config/bundled-skills/app-builder/SKILL.md +15 -33
  473. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +3 -8
  474. package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +64 -37
  475. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +1 -1
  476. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +14 -72
  477. package/src/config/bundled-skills/app-builder/references/examples/README.md +1 -2
  478. package/src/config/bundled-skills/contacts/SKILL.md +7 -12
  479. package/src/config/bundled-skills/messaging/tools/shared.ts +4 -1
  480. package/src/config/bundled-skills/system-storage-cleanup/SKILL.md +74 -0
  481. package/src/config/bundled-skills/workflows/SKILL.md +4 -3
  482. package/src/config/call-site-defaults.ts +11 -2
  483. package/src/config/feature-flag-registry.json +0 -8
  484. package/src/config/profile-dispatchability.ts +11 -0
  485. package/src/config/schemas/call-site-catalog.ts +7 -0
  486. package/src/config/schemas/llm.ts +2 -0
  487. package/src/config/schemas/memory-lifecycle.ts +5 -3
  488. package/src/config/schemas/timeouts.ts +24 -0
  489. package/src/config/seed-inference-profiles.ts +133 -45
  490. package/src/config/sync-gated-profiles.ts +13 -1
  491. package/src/contacts/contact-store.ts +21 -0
  492. package/src/contacts/member-status.ts +9 -0
  493. package/src/credential-health/credential-health-service.ts +1 -5
  494. package/src/daemon/__tests__/conversation-tool-setup.test.ts +44 -0
  495. package/src/daemon/app-source-watcher.ts +31 -18
  496. package/src/daemon/assistant-attachments.ts +94 -4
  497. package/src/daemon/conversation-agent-loop-handlers.ts +3 -0
  498. package/src/daemon/conversation-agent-loop.ts +9 -36
  499. package/src/daemon/conversation-runtime-assembly.ts +91 -66
  500. package/src/daemon/conversation-tool-setup.ts +20 -63
  501. package/src/daemon/conversation.ts +144 -52
  502. package/src/daemon/event-loop-watchdog.test.ts +85 -0
  503. package/src/daemon/event-loop-watchdog.ts +133 -0
  504. package/src/daemon/external-plugins-bootstrap.ts +26 -80
  505. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +1 -1
  506. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +1 -1
  507. package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +1 -1
  508. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +1 -1
  509. package/src/daemon/handlers/__tests__/config-a2a.test.ts +1 -1
  510. package/src/daemon/handlers/config-channels.ts +32 -18
  511. package/src/daemon/handlers/conversations.ts +7 -0
  512. package/src/daemon/handlers/shared.ts +7 -0
  513. package/src/daemon/lifecycle.ts +16 -3
  514. package/src/daemon/message-types/inbox.ts +0 -6
  515. package/src/daemon/message-types/messages.ts +0 -4
  516. package/src/daemon/message-types/surfaces.ts +18 -8
  517. package/src/daemon/server.ts +0 -4
  518. package/src/daemon/tool-setup-types.ts +0 -7
  519. package/src/daemon/trust-context.ts +6 -0
  520. package/src/daemon/wake-conversation-ops.ts +70 -0
  521. package/src/daemon/workspace-tools-watcher.ts +7 -3
  522. package/src/documents/document-comments-store.test.ts +1 -1
  523. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +1 -1
  524. package/src/heartbeat/__tests__/heartbeat-service.test.ts +6 -0
  525. package/src/heartbeat/heartbeat-service.ts +3 -4
  526. package/src/ipc/__tests__/attachment-ipc.test.ts +1 -1
  527. package/src/ipc/__tests__/browser-ipc.test.ts +73 -2
  528. package/src/ipc/__tests__/watcher-ipc.test.ts +59 -39
  529. package/src/ipc/assistant-server.ts +8 -0
  530. package/src/ipc/gateway-client.ts +2 -1
  531. package/src/ipc/routes/__tests__/invite-ipc-routes.test.ts +58 -0
  532. package/src/ipc/routes/invite-ipc-routes.ts +66 -0
  533. package/src/live-voice/__tests__/live-voice-archive.test.ts +1 -1
  534. package/src/memory/__tests__/activation-session-store.test.ts +1 -1
  535. package/src/memory/__tests__/auto-analysis-guard.test.ts +1 -1
  536. package/src/memory/__tests__/conversation-group-migration.test.ts +1 -1
  537. package/src/memory/__tests__/conversation-queries.test.ts +1 -1
  538. package/src/memory/__tests__/db-async-query.test.ts +1 -1
  539. package/src/memory/__tests__/db-logs-attach.test.ts +110 -0
  540. package/src/memory/__tests__/db-maintenance.test.ts +28 -36
  541. package/src/memory/__tests__/db-memory-attach.test.ts +113 -0
  542. package/src/memory/__tests__/find-analysis-conversation.test.ts +1 -1
  543. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +1 -1
  544. package/src/memory/__tests__/fork-message-copy.test.ts +232 -0
  545. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +3 -0
  546. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +5 -5
  547. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +8 -6
  548. package/src/memory/__tests__/memory-retrospective-job.test.ts +30 -37
  549. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +69 -66
  550. package/src/memory/__tests__/memory-retrospective-state.test.ts +1 -1
  551. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +1 -1
  552. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +1 -1
  553. package/src/memory/__tests__/onboarding-events-store.test.ts +1 -1
  554. package/src/memory/__tests__/table-relocation.test.ts +129 -0
  555. package/src/memory/conversation-crud.ts +461 -152
  556. package/src/memory/db-async-query.ts +89 -5
  557. package/src/memory/db-connection.ts +101 -18
  558. package/src/memory/db-init.ts +409 -234
  559. package/src/memory/db-maintenance.ts +43 -38
  560. package/src/memory/db-singleton.ts +45 -19
  561. package/src/memory/fork-message-copy.ts +170 -0
  562. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +92 -0
  563. package/src/memory/graph/bootstrap.test.ts +6 -3
  564. package/src/memory/graph/retriever.test.ts +12 -12
  565. package/src/memory/graph/store.test.ts +15 -25
  566. package/src/memory/graph/store.ts +23 -14
  567. package/src/memory/graph/tool-handlers.ts +34 -5
  568. package/src/memory/graph/tools.ts +5 -2
  569. package/src/memory/indexer.ts +21 -9
  570. package/src/memory/job-handlers/cleanup.ts +10 -3
  571. package/src/memory/job-handlers/embedding.test.ts +4 -4
  572. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +4 -4
  573. package/src/memory/jobs/embed-pkb-file.test.ts +7 -7
  574. package/src/memory/jobs-store.ts +36 -24
  575. package/src/memory/llm-request-log-store.ts +51 -19
  576. package/src/memory/llm-usage-store.ts +31 -1
  577. package/src/memory/memory-retrospective-job.ts +27 -19
  578. package/src/memory/memory-retrospective-startup-cleanup.ts +10 -2
  579. package/src/memory/migrations/{100-core-tables.ts → 000-core-tables.ts} +6 -10
  580. package/src/memory/migrations/104-core-indexes.ts +1 -1
  581. package/src/memory/migrations/126-backfill-guardian-principal-id.ts +189 -196
  582. package/src/memory/migrations/127-guardian-principal-id-not-null.ts +98 -105
  583. package/src/memory/migrations/134-contacts-notes-column.ts +66 -69
  584. package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +19 -22
  585. package/src/memory/migrations/136-drop-assistant-id-columns.ts +227 -230
  586. package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +204 -209
  587. package/src/memory/migrations/141-rename-verification-table.ts +45 -48
  588. package/src/memory/migrations/142-rename-verification-session-id-column.ts +16 -23
  589. package/src/memory/migrations/143-rename-guardian-verification-values.ts +23 -30
  590. package/src/memory/migrations/144-rename-voice-to-phone.ts +133 -136
  591. package/src/memory/migrations/145-drop-accounts-table.ts +4 -7
  592. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +79 -82
  593. package/src/memory/migrations/148-drop-reminders-table.ts +3 -6
  594. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +71 -78
  595. package/src/memory/migrations/157-invite-contact-id.ts +73 -76
  596. package/src/memory/migrations/162-guardian-timestamps-epoch-ms.ts +44 -58
  597. package/src/memory/migrations/169-rename-gmail-provider-key-to-google.ts +36 -43
  598. package/src/memory/migrations/174-rename-thread-starters-table.ts +30 -37
  599. package/src/memory/migrations/176-drop-capability-card-state.ts +17 -22
  600. package/src/memory/migrations/177-create-trace-events-table.ts +23 -28
  601. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +36 -43
  602. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +14 -21
  603. package/src/memory/migrations/191-backfill-audio-attachment-mime-types.ts +17 -24
  604. package/src/memory/migrations/192-contacts-user-file-column.ts +6 -9
  605. package/src/memory/migrations/193-add-source-type-columns.ts +33 -36
  606. package/src/memory/migrations/194-memory-recall-logs.ts +34 -39
  607. package/src/memory/migrations/196-strip-integration-prefix-from-provider-keys.ts +59 -66
  608. package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +41 -48
  609. package/src/memory/migrations/204-rename-memory-graph-type-values.ts +11 -18
  610. package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +76 -83
  611. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +50 -57
  612. package/src/memory/migrations/211-memory-recall-logs-query-context.ts +6 -11
  613. package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +4 -9
  614. package/src/memory/migrations/217-conversation-host-access.ts +13 -18
  615. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +86 -93
  616. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +41 -48
  617. package/src/memory/migrations/230-acp-session-history.ts +23 -28
  618. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +58 -62
  619. package/src/memory/migrations/232-activation-state.ts +11 -16
  620. package/src/memory/migrations/233-document-conversations.ts +20 -25
  621. package/src/memory/migrations/234-memory-v2-activation-logs.ts +26 -31
  622. package/src/memory/migrations/235-slack-compaction-watermark.ts +5 -10
  623. package/src/memory/migrations/236-tool-invocations-matched-rule-id.ts +6 -11
  624. package/src/memory/migrations/237-heartbeat-runs.ts +22 -27
  625. package/src/memory/migrations/239-trace-events-created-at-index.ts +4 -9
  626. package/src/memory/migrations/242-message-bookmarks.ts +17 -22
  627. package/src/memory/migrations/245-memory-retrospective-state.ts +8 -13
  628. package/src/memory/migrations/249-normalize-slack-external-content.ts +37 -41
  629. package/src/memory/migrations/251-a2a-tasks.ts +27 -32
  630. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +12 -17
  631. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +10 -15
  632. package/src/memory/migrations/256-memory-v2-injection-events.ts +70 -74
  633. package/src/memory/migrations/259-conversation-cleaned-at.ts +4 -9
  634. package/src/memory/migrations/260-rename-cleaned-at.ts +11 -16
  635. package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +3 -8
  636. package/src/memory/migrations/262-memory-v3-coactivation.ts +21 -26
  637. package/src/memory/migrations/263-memory-v3-auto-edges.ts +14 -19
  638. package/src/memory/migrations/270-schedule-description.ts +7 -12
  639. package/src/memory/migrations/272-acp-session-history-cwd.ts +8 -13
  640. package/src/memory/migrations/281-memory-retrospective-remembered-log.ts +8 -13
  641. package/src/memory/migrations/297-move-llm-request-logs-to-logs-db.ts +111 -0
  642. package/src/memory/migrations/298-move-memory-jobs-to-memory-db.ts +128 -0
  643. package/src/memory/migrations/299-canonical-guardian-deliveries-conversation-index.ts +19 -0
  644. package/src/memory/migrations/__tests__/297-move-llm-request-logs.test.ts +180 -0
  645. package/src/memory/migrations/__tests__/run-migrations.test.ts +333 -7
  646. package/src/memory/migrations/helpers/relocation.ts +227 -0
  647. package/src/memory/migrations/registry.ts +63 -0
  648. package/src/memory/migrations/run-migrations.ts +187 -16
  649. package/src/memory/migrations/validate-migration-state.ts +50 -145
  650. package/src/memory/raw-query.ts +47 -2
  651. package/src/memory/skill-loaded-events-store.test.ts +1 -1
  652. package/src/memory/task-memory-cleanup.ts +62 -41
  653. package/src/memory/tool-executed-events-store.test.ts +1 -1
  654. package/src/memory/turn-trace-store.test.ts +1 -1
  655. package/src/memory/v2/__tests__/backfill-jobs.test.ts +16 -15
  656. package/src/memory/v2/__tests__/harness-compare.test.ts +1 -1
  657. package/src/memory/v2/__tests__/harness-oracle.test.ts +1 -1
  658. package/src/memory/v2/__tests__/harness-replay-input.test.ts +1 -1
  659. package/src/memory/v2/__tests__/sweep-job.test.ts +2 -2
  660. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +38 -0
  661. package/src/memory/v3-eval/__tests__/eval-tally.test.ts +139 -0
  662. package/src/memory/v3-eval/eval-packets.ts +197 -12
  663. package/src/memory/v3-eval/eval-tally.ts +234 -0
  664. package/src/messaging/provider.ts +10 -0
  665. package/src/messaging/providers/gmail/adapter.ts +1 -0
  666. package/src/messaging/providers/gmail/client.ts +14 -0
  667. package/src/messaging/providers/index.ts +1 -1
  668. package/src/messaging/providers/slack/send.test.ts +87 -39
  669. package/src/messaging/providers/slack/send.ts +84 -105
  670. package/src/notifications/README.md +9 -5
  671. package/src/notifications/__tests__/deterministic-checks.test.ts +43 -1
  672. package/src/notifications/adapters/slack.ts +12 -10
  673. package/src/notifications/approval-card-builder.ts +81 -20
  674. package/src/notifications/approval-card-data.ts +8 -5
  675. package/src/notifications/canonical-delivery-recorder.ts +7 -5
  676. package/src/notifications/conversation-candidates.ts +24 -59
  677. package/src/notifications/copy-composer.ts +48 -68
  678. package/src/notifications/deterministic-checks.ts +19 -16
  679. package/src/notifications/emit-signal.ts +29 -1
  680. package/src/notifications/trusted-contact-payloads.ts +70 -0
  681. package/src/oauth/byo-connection.test.ts +9 -0
  682. package/src/oauth/connection-resolver.test.ts +146 -6
  683. package/src/oauth/connection-resolver.ts +132 -5
  684. package/src/oauth/oauth-store.ts +16 -3
  685. package/src/oauth/scope-utils.ts +21 -0
  686. package/src/plugin-api/index.ts +9 -4
  687. package/src/plugin-api/model-profiles.test.ts +123 -0
  688. package/src/plugin-api/model-profiles.ts +5 -1
  689. package/src/plugin-api/vision-support.test.ts +149 -0
  690. package/src/plugin-api/vision-support.ts +78 -0
  691. package/src/plugins/defaults/compaction/window-manager.ts +45 -64
  692. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +13 -4
  693. package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +302 -0
  694. package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +103 -0
  695. package/src/plugins/defaults/image-fallback/package.json +14 -0
  696. package/src/plugins/defaults/image-fallback/src/caption-cache.ts +49 -0
  697. package/src/plugins/defaults/image-fallback/src/image-persist.ts +59 -0
  698. package/src/plugins/defaults/image-fallback/src/vision-caption.ts +120 -0
  699. package/src/plugins/defaults/index.ts +23 -0
  700. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +14 -1
  701. package/src/plugins/defaults/memory-retrieval/injectors.ts +4 -4
  702. package/src/plugins/external-plugin-loader.ts +47 -6
  703. package/src/plugins/mtime-cache.ts +772 -0
  704. package/src/plugins/pipeline.ts +7 -2
  705. package/src/plugins/registry.ts +16 -5
  706. package/src/plugins/user-loader.ts +22 -76
  707. package/src/prompts/persona-resolver.ts +29 -11
  708. package/src/prompts/system-prompt.ts +1 -1
  709. package/src/prompts/templates/system-sections.ts +4 -4
  710. package/src/providers/__tests__/count-tokens-forwarding.test.ts +98 -0
  711. package/src/providers/anthropic/client.ts +254 -185
  712. package/src/providers/call-site-routing.ts +10 -0
  713. package/src/providers/gemini/client.ts +43 -0
  714. package/src/providers/inference/adapter-factory.ts +6 -0
  715. package/src/providers/inference/connections.ts +6 -1
  716. package/src/providers/model-catalog.ts +37 -0
  717. package/src/providers/platform-proxy/constants.ts +5 -0
  718. package/src/providers/ratelimit.ts +9 -0
  719. package/src/providers/retry.ts +10 -0
  720. package/src/providers/together/client.ts +35 -0
  721. package/src/providers/types.ts +16 -0
  722. package/src/providers/usage-tracking.ts +7 -0
  723. package/src/runtime/AGENTS.md +9 -1
  724. package/src/runtime/__tests__/agent-wake.test.ts +259 -4
  725. package/src/runtime/__tests__/slack-block-formatting.test.ts +39 -10
  726. package/src/runtime/__tests__/trust-verdict-consumer.test.ts +417 -0
  727. package/src/runtime/actor-trust-resolver.ts +8 -16
  728. package/src/runtime/agent-wake.ts +183 -60
  729. package/src/runtime/channel-reply-delivery.ts +6 -3
  730. package/src/runtime/guardian-decision-types.ts +3 -22
  731. package/src/runtime/http-server.ts +1 -15
  732. package/src/runtime/invite-redemption-service.ts +155 -6
  733. package/src/runtime/invite-service.ts +113 -62
  734. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +3 -0
  735. package/src/runtime/routes/__tests__/acp-routes.test.ts +1 -1
  736. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -1
  737. package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +277 -0
  738. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +140 -0
  739. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +26 -7
  740. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +14 -10
  741. package/src/runtime/routes/__tests__/contact-routes-update-channel-relay.test.ts +164 -0
  742. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  743. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +1 -1
  744. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +8 -8
  745. package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +1 -1
  746. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +1 -3
  747. package/src/runtime/routes/__tests__/invite-relay-routes.test.ts +240 -0
  748. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +4 -0
  749. package/src/runtime/routes/__tests__/plugins-routes.test.ts +143 -0
  750. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +1 -1
  751. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +1 -1
  752. package/src/runtime/routes/acp-routes-list.test.ts +4 -0
  753. package/src/runtime/routes/acp-routes.test.ts +5 -6
  754. package/src/runtime/routes/attachment-routes.ts +21 -17
  755. package/src/runtime/routes/browser-routes.ts +19 -1
  756. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +5 -9
  757. package/src/runtime/routes/channel-verification-routes.ts +12 -1
  758. package/src/runtime/routes/contact-routes.ts +275 -164
  759. package/src/runtime/routes/conversation-query-routes.ts +15 -5
  760. package/src/runtime/routes/conversation-routes.ts +24 -3
  761. package/src/runtime/routes/conversation-starter-routes.ts +7 -8
  762. package/src/runtime/routes/guardian-approval-interception.ts +13 -274
  763. package/src/runtime/routes/inbound-message-handler.ts +20 -15
  764. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +285 -0
  765. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +45 -34
  766. package/src/runtime/routes/inbound-stages/admission-policy.ts +20 -5
  767. package/src/runtime/routes/log-export-routes.ts +2 -2
  768. package/src/runtime/routes/memory-eval-routes.ts +92 -0
  769. package/src/runtime/routes/memory-item-routes.test.ts +12 -11
  770. package/src/runtime/routes/migration-routes.ts +51 -40
  771. package/src/runtime/routes/plugins-routes.ts +164 -8
  772. package/src/runtime/routes/schedule-routes.ts +1 -0
  773. package/src/runtime/routes/usage-routes.ts +3 -0
  774. package/src/runtime/routes/work-items-routes.test.ts +1 -1
  775. package/src/runtime/slack-block-formatting.ts +46 -48
  776. package/src/runtime/trust-verdict-consumer.ts +172 -0
  777. package/src/schedule/scheduler.ts +6 -9
  778. package/src/telemetry/usage-telemetry-reporter.test.ts +1 -1
  779. package/src/tools/ask-question/ask-question-tool.test.ts +60 -52
  780. package/src/tools/ask-question/ask-question-tool.ts +14 -73
  781. package/src/tools/browser/__tests__/browser-status.test.ts +20 -0
  782. package/src/tools/browser/browser-execution.ts +16 -4
  783. package/src/tools/document/document-comment-tool.test.ts +1 -1
  784. package/src/tools/executor.ts +15 -3
  785. package/src/tools/host-terminal/host-shell.ts +28 -9
  786. package/src/tools/memory/register.test.ts +32 -0
  787. package/src/tools/skills/load.ts +43 -2
  788. package/src/tools/subagent/spawn.ts +4 -10
  789. package/src/tools/terminal/shell.ts +16 -5
  790. package/src/tools/types.ts +1 -0
  791. package/src/util/fs-watcher-error.ts +36 -0
  792. package/src/util/logs-db-path.ts +22 -0
  793. package/src/util/memory-db-path.ts +23 -0
  794. package/src/watcher/providers/gmail.ts +7 -2
  795. package/src/workflows/engine-integration.test.ts +1 -1
  796. package/src/workflows/engine.test.ts +1 -1
  797. package/src/workflows/engine.ts +22 -0
  798. package/src/workflows/fanout-load.test.ts +1 -1
  799. package/src/workflows/journal-store.test.ts +1 -1
  800. package/src/workflows/leaf-runner.test.ts +40 -1
  801. package/src/workflows/leaf-runner.ts +26 -1
  802. package/src/workspace/git-service.ts +144 -29
  803. package/src/workspace/migrations/109-swap-quality-profile-to-glm-5p2.ts +121 -0
  804. package/src/workspace/migrations/110-flip-balanced-profile-to-together.ts +82 -0
  805. package/src/workspace/migrations/registry.ts +4 -0
  806. package/src/workspace/migrations/runner.ts +32 -2
  807. package/src/__tests__/access-request-decision.test.ts +0 -375
  808. package/src/__tests__/guardian-grant-minting.test.ts +0 -607
  809. package/src/__tests__/plugin-source-watcher.test.ts +0 -302
  810. package/src/api/events/turn-profile-auto-routed.ts +0 -28
  811. package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +0 -107
  812. package/src/daemon/plugin-source-watcher.ts +0 -278
  813. package/src/daemon/switch-inference-profile-tool.ts +0 -62
  814. package/src/memory/guardian-approvals.ts +0 -361
  815. package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +0 -66
  816. package/src/memory/migrations/038-actor-token-records.ts +0 -45
  817. package/src/memory/migrations/039-actor-refresh-token-records.ts +0 -57
  818. package/src/memory/migrations/103-complex-migrations.ts +0 -23
  819. package/src/memory/migrations/113-late-migrations.ts +0 -30
  820. package/src/memory/migrations/index.ts +0 -301
  821. package/src/runtime/routes/access-request-decision.ts +0 -297
  822. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +0 -963
  823. package/src/runtime/routes/channel-guardian-routes.ts +0 -19
  824. package/src/runtime/routes/guardian-expiry-sweep.ts +0 -132
@@ -58,11 +58,10 @@ async function runDbMaintenance(): Promise<void> {
58
58
  );
59
59
 
60
60
  // Prune finished workflow runs (and their journals) past the retention
61
- // window BEFORE VACUUM so the freed pages are reclaimed in the same pass.
62
- // This is a fast bounded DELETE on the small workflow tables, so it runs on
63
- // the main connection (`rawRun`) unlike VACUUM/optimize it doesn't need the
64
- // async/subprocess path. The maintenance scheduler below already defers this
65
- // whole routine to an idle window.
61
+ // window. This is a fast bounded DELETE on the small workflow tables, so it
62
+ // runs on the main connection (`rawRun`). SQLite reuses the pages it frees
63
+ // for later writeswe deliberately do not VACUUM to hand them back to the
64
+ // OS (see the WAL note below).
66
65
  try {
67
66
  const deletedRuns = pruneRuns(getConfig().workflows.journalRetentionDays);
68
67
  if (deletedRuns > 0) {
@@ -72,18 +71,9 @@ async function runDbMaintenance(): Promise<void> {
72
71
  log.warn({ err }, "Workflow run pruning failed (non-fatal)");
73
72
  }
74
73
 
75
- // VACUUM is the long-running one minutes on a multi-GB DB. PRAGMA
76
- // optimize is fast but routed through the same async path for
77
- // consistency and to keep both off the main thread when the CLI
78
- // backend is available.
79
- const vacuumResult = await runAsyncSqlite("VACUUM");
80
- if (!vacuumResult.ok) {
81
- log.warn(
82
- { error: vacuumResult.error, backend: vacuumResult.backend },
83
- "VACUUM failed (non-fatal)",
84
- );
85
- }
86
-
74
+ // Refresh the query planner's statistics. PRAGMA optimize is cheap; it is
75
+ // routed through the async path for consistency and to keep it off the main
76
+ // thread when the sqlite3 CLI backend is available.
87
77
  const optimizeResult = await runAsyncSqlite("PRAGMA optimize");
88
78
  if (!optimizeResult.ok) {
89
79
  log.warn(
@@ -92,26 +82,42 @@ async function runDbMaintenance(): Promise<void> {
92
82
  );
93
83
  }
94
84
 
95
- const after = getDbStats();
96
- const reclaimedPages = before.pageCount - after.pageCount;
97
- const reclaimedBytes =
98
- before.fileSizeBytes != null && after.fileSizeBytes != null
99
- ? before.fileSizeBytes - after.fileSizeBytes
100
- : null;
85
+ // Truncate the WAL so it doesn't sit at its high-water mark. We intentionally
86
+ // do NOT run a full VACUUM: in WAL mode VACUUM rewrites the whole database
87
+ // through the WAL, inflating it to ~the database size and needing up to 2x the
88
+ // DB size in free disk to finish. SQLite already reuses freed pages for new
89
+ // writes, so eager space return isn't worth that cost on a multi-GB database.
90
+ //
91
+ // The checkpoint goes through the async path (sqlite3 subprocess when one is
92
+ // available) for the same reason VACUUM/optimize do: a synchronous
93
+ // wal_checkpoint(TRUNCATE) on the shared connection blocks the event loop
94
+ // while it checkpoints frames and waits out readers — the health/IPC stall
95
+ // runAsyncSqlite exists to avoid. A checkpoint from a separate connection
96
+ // still truncates the shared WAL; if a reader holds it back it's a
97
+ // best-effort no-op and the next maintenance pass retries.
98
+ const checkpointResult = await runAsyncSqlite(
99
+ "PRAGMA wal_checkpoint(TRUNCATE)",
100
+ );
101
+ if (!checkpointResult.ok) {
102
+ log.warn(
103
+ { error: checkpointResult.error, backend: checkpointResult.backend },
104
+ "WAL checkpoint failed (non-fatal)",
105
+ );
106
+ }
101
107
 
108
+ const after = getDbStats();
102
109
  log.info(
103
110
  {
104
- backend: vacuumResult.backend,
105
- vacuumOk: vacuumResult.ok,
106
111
  optimizeOk: optimizeResult.ok,
107
- vacuumElapsedMs: vacuumResult.elapsedMs,
112
+ optimizeBackend: optimizeResult.backend,
108
113
  optimizeElapsedMs: optimizeResult.elapsedMs,
109
- beforePageCount: before.pageCount,
110
- afterPageCount: after.pageCount,
111
- reclaimedPages,
112
- beforeFileSizeBytes: before.fileSizeBytes,
113
- afterFileSizeBytes: after.fileSizeBytes,
114
- reclaimedBytes,
114
+ checkpointOk: checkpointResult.ok,
115
+ checkpointBackend: checkpointResult.backend,
116
+ checkpointResult: checkpointResult.stdout?.trim(),
117
+ checkpointElapsedMs: checkpointResult.elapsedMs,
118
+ pageCount: after.pageCount,
119
+ freelistCount: after.freelistCount,
120
+ fileSizeBytes: after.fileSizeBytes,
115
121
  },
116
122
  "Database maintenance complete",
117
123
  );
@@ -126,12 +132,11 @@ export async function maybeRunDbMaintenance(nowMs = Date.now()): Promise<void> {
126
132
  );
127
133
  if (nowMs - lastRun < intervalMs) return;
128
134
 
129
- // VACUUM holds an exclusive lock on the database for its full duration —
130
- // minutes on a multi-GB DB during which every other write fails with
131
- // SQLITE_BUSY ("database is locked"). Defer maintenance until the user has
132
- // been quiet for `quietPeriodMs` so that lock never lands mid-conversation.
133
- // The checkpoint below is only written once maintenance actually runs, so a
134
- // deferred run is simply retried on a later (still-idle) worker tick.
135
+ // Maintenance still takes brief write locks (PRAGMA optimize and the
136
+ // truncating WAL checkpoint), so defer it until the user has been quiet for
137
+ // `quietPeriodMs` and those locks never land mid-conversation. The checkpoint
138
+ // below is only written once maintenance actually runs, so a deferred run is
139
+ // simply retried on a later (still-idle) worker tick.
135
140
  if (quietPeriodMs > 0) {
136
141
  const lastUserMessageAt = getLastUserMessageTimestamp();
137
142
  if (lastUserMessageAt > 0 && nowMs - lastUserMessageAt < quietPeriodMs) {
@@ -1,15 +1,20 @@
1
1
  /**
2
- * Holds the assistant DB connection singleton and its close callback.
2
+ * Holds the assistant DB connection singletons and their close callbacks.
3
3
  *
4
- * Lives in its own module (rather than alongside the resolver in
5
- * `db-connection.ts`) so test code can reset the singleton without
4
+ * Three connections live here under the same `globalThis.vellumAssistant`
5
+ * namespace, keyed by slot: the `main` daemon connection, the `logs`
6
+ * connection (`assistant-logs.db`), and the `memory` connection
7
+ * (`assistant-memory.db`). Each is opened lazily by `db-connection.ts`.
8
+ *
9
+ * Lives in its own module (rather than alongside the resolvers in
10
+ * `db-connection.ts`) so test code can reset the singletons without
6
11
  * importing `db-connection.ts` — which transitively pulls
7
12
  * `drizzle-orm/bun-sqlite`. Stdlib-only by design: this file must remain
8
13
  * safe to import from the test preload's load-time chain, where a broken
9
14
  * `node_modules` symlink has historically tripped the env override
10
15
  * (see DB ghost #3, /workspace/journal/2026-05-25-db-ghost-3-recovery.md).
11
16
  *
12
- * State is held on `globalThis.vellumAssistant.dbSingleton` so test
17
+ * State is held on `globalThis.vellumAssistant.dbSingletons` so test
13
18
  * helpers in `__tests__/` can read/write it WITHOUT importing this
14
19
  * module — they declare the same slot shape locally and access the
15
20
  * globalThis namespace directly. See
@@ -21,50 +26,71 @@
21
26
  * parameter on `getStoredDb<DrizzleDb>()`.
22
27
  *
23
28
  * Consumers:
24
- * - `db-connection.ts` (opens/owns the connection)
25
- * - production callers that need to close the active connection
29
+ * - `db-connection.ts` (opens/owns the connections)
30
+ * - production callers that need to close the active connections
26
31
  * (migration routes, vbundle import, backup/restore, daemon shutdown)
27
32
  * - `__tests__/db-test-helpers.ts` (per-test reset, via globalThis)
28
33
  */
29
34
 
35
+ /** Which connection a slot holds. */
36
+ export type DbSlotKey = "main" | "logs" | "memory";
37
+
30
38
  type DbSlot = {
31
39
  db: unknown;
32
40
  closer: (() => void) | null;
33
41
  };
34
42
 
43
+ type DbSlots = Record<DbSlotKey, DbSlot>;
44
+
35
45
  type VellumAssistantNamespace = {
36
- dbSingleton?: DbSlot;
46
+ dbSingletons?: DbSlots;
37
47
  };
38
48
 
39
- function slot(): DbSlot {
49
+ function emptySlot(): DbSlot {
50
+ return { db: null, closer: null };
51
+ }
52
+
53
+ function slots(): DbSlots {
40
54
  const g = globalThis as { vellumAssistant?: VellumAssistantNamespace };
41
55
  const ns = (g.vellumAssistant ??= {});
42
- return (ns.dbSingleton ??= { db: null, closer: null });
56
+ return (ns.dbSingletons ??= {
57
+ main: emptySlot(),
58
+ logs: emptySlot(),
59
+ memory: emptySlot(),
60
+ });
61
+ }
62
+
63
+ function slot(key: DbSlotKey): DbSlot {
64
+ return slots()[key];
43
65
  }
44
66
 
45
- /** Read the current singleton, narrowed to `T`. `null` means not yet opened. */
46
- export function getStoredDb<T>(): T | null {
47
- return slot().db as T | null;
67
+ /** Read the current singleton for `key`, narrowed to `T`. `null` means not yet opened. */
68
+ export function getStoredDb<T>(key: DbSlotKey): T | null {
69
+ return slot(key).db as T | null;
48
70
  }
49
71
 
50
72
  /**
51
73
  * Store a freshly-opened connection and the closer to run on
52
- * `clearStoredDb()`. The closer must be self-contained — it is invoked
74
+ * `clearStoredDb(key)`. The closer must be self-contained — it is invoked
53
75
  * inside a try/catch, so partial failures are swallowed (best-effort
54
76
  * close on shutdown / restore paths).
55
77
  */
56
- export function setStoredDb<T>(newDb: T, close: () => void): void {
57
- const s = slot();
78
+ export function setStoredDb<T>(
79
+ key: DbSlotKey,
80
+ newDb: T,
81
+ close: () => void,
82
+ ): void {
83
+ const s = slot(key);
58
84
  s.db = newDb;
59
85
  s.closer = close;
60
86
  }
61
87
 
62
88
  /**
63
- * Close the active connection (if any) via the stored closer, then drop
64
- * both. Idempotent: safe to call when no connection is stored.
89
+ * Close the active connection for `key` (if any) via the stored closer,
90
+ * then drop both. Idempotent: safe to call when no connection is stored.
65
91
  */
66
- export function clearStoredDb(): void {
67
- const s = slot();
92
+ export function clearStoredDb(key: DbSlotKey): void {
93
+ const s = slot(key);
68
94
  if (s.closer) {
69
95
  try {
70
96
  s.closer();
@@ -0,0 +1,170 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Fork message-copy — off-event-loop bulk copy of a conversation's messages.
3
+ // ---------------------------------------------------------------------------
4
+ //
5
+ // The fork-based memory retrospective copies the entire source conversation's
6
+ // message rows into a throwaway background conversation. Done in-process via
7
+ // `bun:sqlite` (synchronous), that copy is the single longest uninterruptible
8
+ // block on the daemon's event loop — on a multi-GB database it pegs the CPU
9
+ // for minutes, stalling `/healthz` and the gateway's IPC calls (see the
10
+ // runtime-freeze investigation).
11
+ //
12
+ // This module moves the message-row copy off the event loop via
13
+ // {@link runAsyncSqlite}, which runs the statements in a `sqlite3` CLI
14
+ // subprocess (its own connection; SQLite file-locking arbitrates with the
15
+ // in-process handle). The content/metadata never round-trip through JS — they
16
+ // are copied SELECT→INSERT inside SQLite — so there is no string-escaping
17
+ // hazard and the heavy bytes stay in the database.
18
+ //
19
+ // Two properties matter for correctness:
20
+ //
21
+ // 1. **Lock-friendly batching.** The main connection runs `busy_timeout=5000`
22
+ // (`db-connection.ts`). A single `INSERT … SELECT` over the whole history
23
+ // is one implicit transaction that holds the write lock for its full
24
+ // duration — a concurrent in-process write (a live user turn persisting a
25
+ // message) would then block up to 5 s and throw `SQLITE_BUSY`. So the copy
26
+ // is split into batches that each auto-commit, releasing the lock between
27
+ // batches so in-process writers slip in.
28
+ //
29
+ // 2. **`cloneForkMessageMetadata` parity.** The in-process fork stamps
30
+ // `forkSourceMessageId` onto each copied row's metadata. The SQL `CASE`
31
+ // below is a faithful translation of that helper's three branches
32
+ // (object → preserve-or-stamp; null/non-object/invalid → fresh object).
33
+ // `client_message_id` is intentionally NOT copied, matching the
34
+ // in-process fork (a forked row is not a client-submitted message).
35
+
36
+ import { type AsyncSqliteResult, runAsyncSqlite } from "./db-async-query.js";
37
+
38
+ /**
39
+ * Default batch size for the chunked copy. Each batch is one `INSERT … SELECT`
40
+ * that auto-commits, so this bounds how long the subprocess holds the write
41
+ * lock before releasing it to in-process writers. Small enough to keep the
42
+ * lock-hold window short on a bloated database; large enough that per-batch
43
+ * statement overhead stays negligible against the row copy.
44
+ */
45
+ export const DEFAULT_FORK_COPY_BATCH_SIZE = 500;
46
+
47
+ /**
48
+ * A single source→fork message-id mapping. `oldId` is an existing source
49
+ * message id; `newId` is the freshly-generated id the fork row will carry.
50
+ * Generated by the caller (in JS) so the same map drives both this copy and
51
+ * the in-process attachment relink that follows.
52
+ */
53
+ export interface ForkIdPair {
54
+ oldId: string;
55
+ newId: string;
56
+ }
57
+
58
+ export interface CopyForkMessagesOptions {
59
+ /** The fork conversation id every copied row is assigned to. */
60
+ forkConversationId: string;
61
+ /** Ordered source→fork id pairs for every message to copy. */
62
+ idPairs: readonly ForkIdPair[];
63
+ /** Override the per-batch row count (see {@link DEFAULT_FORK_COPY_BATCH_SIZE}). */
64
+ batchSize?: number;
65
+ /** Test-only passthrough to force the in-process backend. */
66
+ forceInProcess?: boolean;
67
+ }
68
+
69
+ // Server-generated ids only ever use this charset. We interpolate ids (never
70
+ // message content) into the SQL script, so reject anything outside it as a
71
+ // defense-in-depth guard against a malformed id breaking out of the literal.
72
+ const SAFE_ID = /^[A-Za-z0-9_-]{1,64}$/;
73
+
74
+ function assertSafeId(id: string): void {
75
+ if (!SAFE_ID.test(id)) {
76
+ throw new Error(
77
+ `fork message-copy: unsafe id literal: ${JSON.stringify(id)}`,
78
+ );
79
+ }
80
+ }
81
+
82
+ /**
83
+ * SQL translation of `cloneForkMessageMetadata`. `m` is the source `messages`
84
+ * row alias; the stamped value is the SOURCE message id (`m.id`), preserving an
85
+ * existing `forkSourceMessageId` when the metadata is already an object.
86
+ */
87
+ const METADATA_CLONE_EXPR = `CASE
88
+ WHEN json_valid(m.metadata) AND json_type(m.metadata) = 'object'
89
+ THEN json_set(m.metadata, '$.forkSourceMessageId',
90
+ coalesce(json_extract(m.metadata, '$.forkSourceMessageId'), m.id))
91
+ ELSE json_object('forkSourceMessageId', m.id)
92
+ END`;
93
+
94
+ /**
95
+ * Build the multi-statement SQL script that copies the given id pairs into the
96
+ * fork conversation in lock-friendly batches. Exported for unit testing the
97
+ * generated SQL without spawning a subprocess.
98
+ */
99
+ export function buildForkCopyScript(options: CopyForkMessagesOptions): string {
100
+ const { forkConversationId, idPairs } = options;
101
+ assertSafeId(forkConversationId);
102
+ const batchSize = Math.max(
103
+ 1,
104
+ options.batchSize ?? DEFAULT_FORK_COPY_BATCH_SIZE,
105
+ );
106
+
107
+ const statements: string[] = [
108
+ // Connection-scoped staging table holding the current batch's id map. The
109
+ // unqualified `messages` reference in the SELECT resolves to the main DB.
110
+ `CREATE TEMP TABLE IF NOT EXISTS _fork_id_map (old_id TEXT PRIMARY KEY, new_id TEXT NOT NULL);`,
111
+ ];
112
+
113
+ for (let i = 0; i < idPairs.length; i += batchSize) {
114
+ const batch = idPairs.slice(i, i + batchSize);
115
+ const values = batch
116
+ .map(({ oldId, newId }) => {
117
+ assertSafeId(oldId);
118
+ assertSafeId(newId);
119
+ return `('${oldId}','${newId}')`;
120
+ })
121
+ .join(",");
122
+
123
+ // Each batch auto-commits (no surrounding BEGIN), so the write lock is
124
+ // released between batches for in-process writers.
125
+ statements.push(`DELETE FROM _fork_id_map;`);
126
+ statements.push(
127
+ `INSERT INTO _fork_id_map (old_id, new_id) VALUES ${values};`,
128
+ );
129
+ statements.push(
130
+ `INSERT INTO messages (id, conversation_id, role, content, created_at, metadata)
131
+ SELECT map.new_id, '${forkConversationId}', m.role, m.content, m.created_at, ${METADATA_CLONE_EXPR}
132
+ FROM messages m JOIN _fork_id_map map ON map.old_id = m.id;`,
133
+ );
134
+ }
135
+
136
+ // Drop the staging table so the in-process fallback (which runs on the shared
137
+ // daemon connection) does not leave it lingering; harmless for the subprocess
138
+ // backend, whose connection is discarded on exit.
139
+ statements.push(`DROP TABLE IF EXISTS _fork_id_map;`);
140
+
141
+ return statements.join("\n");
142
+ }
143
+
144
+ /**
145
+ * Copy the message rows for a fork off the event loop, in lock-friendly
146
+ * batches. Resolves once every batch has committed (or rejects via the
147
+ * returned result's `ok: false` on subprocess failure — the caller is
148
+ * responsible for cleaning up the partially-built fork).
149
+ *
150
+ * No-op (returns a synthetic ok result) when there is nothing to copy.
151
+ */
152
+ export async function copyForkMessagesViaSubprocess(
153
+ options: CopyForkMessagesOptions,
154
+ ): Promise<AsyncSqliteResult> {
155
+ if (options.idPairs.length === 0) {
156
+ return {
157
+ ok: true,
158
+ backend: "in-process-blocking",
159
+ error: null,
160
+ elapsedMs: 0,
161
+ };
162
+ }
163
+
164
+ const sql = buildForkCopyScript(options);
165
+ return runAsyncSqlite(sql, {
166
+ ...(options.forceInProcess
167
+ ? { forceBackend: "in-process-blocking" as const }
168
+ : {}),
169
+ });
170
+ }
@@ -227,3 +227,95 @@ describe("handleRemember — memory.v2.enabled off (v1 PKB path)", () => {
227
227
  }
228
228
  });
229
229
  });
230
+
231
+ describe("handleRemember — batch (array) content", () => {
232
+ test("v2: writes every fact from an array in a single call", () => {
233
+ const result = handleRemember(
234
+ { content: ["fact one", "fact two", "fact three"] },
235
+ "conv-batch-1",
236
+ "default",
237
+ CONFIG,
238
+ );
239
+
240
+ expect(result.success).toBe(true);
241
+ expect(result.message).toContain("3");
242
+
243
+ const memoryDir = join(tmpWorkspace, "memory");
244
+ const buffer = readFileSync(join(memoryDir, "buffer.md"), "utf-8");
245
+ expect(buffer).toContain("fact one");
246
+ expect(buffer).toContain("fact two");
247
+ expect(buffer).toContain("fact three");
248
+ // Three independent timestamped bullets from the one call.
249
+ expect(buffer.match(/^- /gm)?.length).toBe(3);
250
+
251
+ const archive = readFileSync(
252
+ join(memoryDir, "archive", todaysArchiveBasename()),
253
+ "utf-8",
254
+ );
255
+ expect(archive).toContain("fact one");
256
+ expect(archive).toContain("fact three");
257
+ });
258
+
259
+ test("v1: a batched call still enqueues exactly two re-index jobs (per file, not per fact)", () => {
260
+ const result = handleRemember(
261
+ { content: ["alpha", "beta", "gamma"] },
262
+ "conv-batch-2",
263
+ "default",
264
+ CONFIG_V2_OFF,
265
+ );
266
+
267
+ expect(result.success).toBe(true);
268
+
269
+ const pkbDir = join(tmpWorkspace, "pkb");
270
+ const buffer = readFileSync(join(pkbDir, "buffer.md"), "utf-8");
271
+ expect(buffer).toContain("alpha");
272
+ expect(buffer).toContain("beta");
273
+ expect(buffer).toContain("gamma");
274
+ expect(buffer.match(/^- /gm)?.length).toBe(3);
275
+
276
+ // One enqueue per written file (buffer + archive), regardless of fact count.
277
+ expect(enqueueCalls).toHaveLength(2);
278
+ });
279
+
280
+ test("drops blank entries and rejects an all-empty array", () => {
281
+ const ok = handleRemember(
282
+ { content: [" ", "kept fact", ""] },
283
+ "conv-batch-3",
284
+ "default",
285
+ CONFIG,
286
+ );
287
+ expect(ok.success).toBe(true);
288
+ const buffer = readFileSync(
289
+ join(tmpWorkspace, "memory", "buffer.md"),
290
+ "utf-8",
291
+ );
292
+ expect(buffer).toContain("kept fact");
293
+ expect(buffer.match(/^- /gm)?.length).toBe(1);
294
+
295
+ const empty = handleRemember(
296
+ { content: [" ", ""] },
297
+ "conv-batch-4",
298
+ "default",
299
+ CONFIG,
300
+ );
301
+ expect(empty.success).toBe(false);
302
+ expect(empty.message).toContain("content is required");
303
+ });
304
+
305
+ test("single-string content still records exactly one fact", () => {
306
+ const result = handleRemember(
307
+ { content: "lone fact" },
308
+ "conv-batch-5",
309
+ "default",
310
+ CONFIG,
311
+ );
312
+ expect(result.success).toBe(true);
313
+ expect(result.message).toBe("Saved to knowledge base.");
314
+ const buffer = readFileSync(
315
+ join(tmpWorkspace, "memory", "buffer.md"),
316
+ "utf-8",
317
+ );
318
+ expect(buffer).toContain("lone fact");
319
+ expect(buffer.match(/^- /gm)?.length).toBe(1);
320
+ });
321
+ });
@@ -1,6 +1,7 @@
1
1
  import { beforeAll, beforeEach, describe, expect, test } from "bun:test";
2
2
 
3
3
  import { setMemoryCheckpoint } from "../checkpoints.js";
4
+ import { getMemoryDb } from "../db-connection.js";
4
5
  import { initializeDb } from "../db-init.js";
5
6
  import { rawAll, rawGet, rawRun, resetTestTables } from "../raw-query.js";
6
7
  import { migrateToolCreatedItems } from "./bootstrap.js";
@@ -15,13 +16,15 @@ const MIGRATE_ITEMS_CHECKPOINT = "graph_bootstrap:migrated_tool_items";
15
16
  // Setup
16
17
  // ---------------------------------------------------------------------------
17
18
 
18
- beforeAll(() => {
19
- initializeDb();
19
+ beforeAll(async () => {
20
+ await initializeDb();
20
21
  });
21
22
 
22
23
  beforeEach(() => {
23
24
  // Clear graph nodes and checkpoints between tests so each test starts clean.
24
- resetTestTables("memory_graph_nodes", "memory_checkpoints", "memory_jobs");
25
+ // memory_jobs lives in the dedicated memory connection; the rest in main.
26
+ resetTestTables("memory_graph_nodes", "memory_checkpoints");
27
+ getMemoryDb()!.run("DELETE FROM memory_jobs");
25
28
  });
26
29
 
27
30
  // ---------------------------------------------------------------------------
@@ -124,18 +124,18 @@ function makeCapabilityNode(content: string, capId: string): NewNode {
124
124
  }
125
125
 
126
126
  describe("loadContextMemory — query/sparse vector surfacing", () => {
127
- beforeAll(() => {
128
- initializeDb();
127
+ beforeAll(async () => {
128
+ await initializeDb();
129
129
  });
130
130
 
131
- beforeEach(() => {
131
+ beforeEach(async () => {
132
132
  embedShouldThrow = false;
133
133
  embedVector = [0.1, 0.2, 0.3];
134
134
  embedCallCount = 0;
135
135
  embedRouter = null;
136
136
  searchRouter = null;
137
137
  resetDbForTesting();
138
- initializeDb();
138
+ await initializeDb();
139
139
  });
140
140
 
141
141
  test("returns the dense queryVector when embedding succeeds", async () => {
@@ -180,18 +180,18 @@ describe("loadContextMemory — query/sparse vector surfacing", () => {
180
180
  });
181
181
 
182
182
  describe("retrieveForTurn — query/sparse vector surfacing", () => {
183
- beforeAll(() => {
184
- initializeDb();
183
+ beforeAll(async () => {
184
+ await initializeDb();
185
185
  });
186
186
 
187
- beforeEach(() => {
187
+ beforeEach(async () => {
188
188
  embedShouldThrow = false;
189
189
  embedVector = [0.1, 0.2, 0.3];
190
190
  embedCallCount = 0;
191
191
  embedRouter = null;
192
192
  searchRouter = null;
193
193
  resetDbForTesting();
194
- initializeDb();
194
+ await initializeDb();
195
195
  });
196
196
 
197
197
  test("returns the dense queryVector when embedding succeeds", async () => {
@@ -309,8 +309,8 @@ describe("retrieveForTurn — topic-pivot recovery", () => {
309
309
  };
310
310
  }
311
311
 
312
- beforeAll(() => {
313
- initializeDb();
312
+ beforeAll(async () => {
313
+ await initializeDb();
314
314
  });
315
315
 
316
316
  beforeEach(() => {
@@ -458,8 +458,8 @@ describe("loadContextMemory — dual-query capability ranking", () => {
458
458
  return [];
459
459
  }
460
460
 
461
- beforeAll(() => {
462
- initializeDb();
461
+ beforeAll(async () => {
462
+ await initializeDb();
463
463
  });
464
464
 
465
465
  beforeEach(() => {
@@ -61,8 +61,8 @@ function makeNewNode(overrides: Partial<NewNode> = {}): NewNode {
61
61
  };
62
62
  }
63
63
 
64
- beforeAll(() => {
65
- initializeDb();
64
+ beforeAll(async () => {
65
+ await initializeDb();
66
66
  });
67
67
 
68
68
  beforeEach(() => {
@@ -259,12 +259,8 @@ describe("queryNodes", () => {
259
259
  });
260
260
 
261
261
  test("filters by eventDateAfter", () => {
262
- createNode(
263
- makeNewNode({ content: "Old.", eventDate: 1700000000000 }),
264
- );
265
- createNode(
266
- makeNewNode({ content: "Recent.", eventDate: 1710000000000 }),
267
- );
262
+ createNode(makeNewNode({ content: "Old.", eventDate: 1700000000000 }));
263
+ createNode(makeNewNode({ content: "Recent.", eventDate: 1710000000000 }));
268
264
  createNode(makeNewNode({ content: "None.", eventDate: null }));
269
265
 
270
266
  const results = queryNodes({ eventDateAfter: 1705000000000 });
@@ -273,12 +269,8 @@ describe("queryNodes", () => {
273
269
  });
274
270
 
275
271
  test("filters by eventDateBefore", () => {
276
- createNode(
277
- makeNewNode({ content: "Old.", eventDate: 1700000000000 }),
278
- );
279
- createNode(
280
- makeNewNode({ content: "Recent.", eventDate: 1710000000000 }),
281
- );
272
+ createNode(makeNewNode({ content: "Old.", eventDate: 1700000000000 }));
273
+ createNode(makeNewNode({ content: "Recent.", eventDate: 1710000000000 }));
282
274
  createNode(makeNewNode({ content: "None.", eventDate: null }));
283
275
 
284
276
  const results = queryNodes({ eventDateBefore: 1705000000000 });
@@ -287,15 +279,9 @@ describe("queryNodes", () => {
287
279
  });
288
280
 
289
281
  test("combines eventDateAfter and eventDateBefore for range query", () => {
290
- createNode(
291
- makeNewNode({ content: "Before.", eventDate: 1690000000000 }),
292
- );
293
- createNode(
294
- makeNewNode({ content: "In range.", eventDate: 1700000000000 }),
295
- );
296
- createNode(
297
- makeNewNode({ content: "After.", eventDate: 1720000000000 }),
298
- );
282
+ createNode(makeNewNode({ content: "Before.", eventDate: 1690000000000 }));
283
+ createNode(makeNewNode({ content: "In range.", eventDate: 1700000000000 }));
284
+ createNode(makeNewNode({ content: "After.", eventDate: 1720000000000 }));
299
285
 
300
286
  const results = queryNodes({
301
287
  eventDateAfter: 1695000000000,
@@ -687,7 +673,9 @@ describe("supersedeNode", () => {
687
673
 
688
674
  test("inherits eventDate from old node when new node has null eventDate", () => {
689
675
  const eventDate = 1712534400000; // April 8 2024
690
- const old = createNode(makeNewNode({ content: "Dentist April 8.", eventDate }));
676
+ const old = createNode(
677
+ makeNewNode({ content: "Dentist April 8.", eventDate }),
678
+ );
691
679
  const newNodeInput = makeNewNode({
692
680
  content: "Dentist appointment rescheduled.",
693
681
  eventDate: null,
@@ -699,7 +687,9 @@ describe("supersedeNode", () => {
699
687
  });
700
688
 
701
689
  test("uses new node eventDate when both nodes have eventDate", () => {
702
- const old = createNode(makeNewNode({ content: "Flight Tuesday.", eventDate: 1712534400000 }));
690
+ const old = createNode(
691
+ makeNewNode({ content: "Flight Tuesday.", eventDate: 1712534400000 }),
692
+ );
703
693
  const newEventDate = 1712620800000;
704
694
  const newNodeInput = makeNewNode({
705
695
  content: "Flight moved to Thursday.",