@vellumai/assistant 0.8.0 → 0.8.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 (692) hide show
  1. package/AGENTS.md +11 -0
  2. package/Dockerfile +5 -4
  3. package/README.md +2 -2
  4. package/docker-entrypoint.sh +16 -0
  5. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  6. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  7. package/eslint.config.mjs +12 -0
  8. package/knip.json +2 -1
  9. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  10. package/openapi.yaml +4847 -1698
  11. package/package.json +3 -1
  12. package/scripts/generate-openapi.ts +52 -4
  13. package/scripts/sync-llm-catalog.ts +165 -0
  14. package/scripts/sync-web-search-catalog.ts +107 -0
  15. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  16. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  17. package/src/__tests__/anthropic-provider.test.ts +92 -2
  18. package/src/__tests__/app-control-flow.test.ts +7 -0
  19. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  20. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  21. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  22. package/src/__tests__/btw-routes.test.ts +1 -0
  23. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  24. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  25. package/src/__tests__/channel-policy.test.ts +12 -0
  26. package/src/__tests__/checker.test.ts +89 -0
  27. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +35 -7
  28. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  29. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  30. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  31. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  32. package/src/__tests__/config-loader-platform-defaults.test.ts +77 -23
  33. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  34. package/src/__tests__/config-schema.test.ts +14 -3
  35. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  36. package/src/__tests__/config-set-route.test.ts +198 -0
  37. package/src/__tests__/config-watcher.test.ts +6 -0
  38. package/src/__tests__/contacts-tools.test.ts +51 -199
  39. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  40. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  41. package/src/__tests__/context-search-conversations-source.test.ts +42 -16
  42. package/src/__tests__/context-search-fanout.test.ts +20 -157
  43. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
  44. package/src/__tests__/context-search-types.test.ts +7 -2
  45. package/src/__tests__/context-window-manager.test.ts +389 -1
  46. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  47. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  48. package/src/__tests__/conversation-error.test.ts +38 -0
  49. package/src/__tests__/conversation-fork-crud.test.ts +241 -1
  50. package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
  51. package/src/__tests__/conversation-init.benchmark.test.ts +1 -0
  52. package/src/__tests__/conversation-lifecycle.test.ts +124 -0
  53. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  54. package/src/__tests__/conversation-process-callsite.test.ts +21 -1
  55. package/src/__tests__/conversation-runtime-assembly.test.ts +4 -4
  56. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  57. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  58. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  59. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  60. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  61. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  62. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  63. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  64. package/src/__tests__/external-plugin-loader.test.ts +458 -0
  65. package/src/__tests__/filing-service.test.ts +23 -3
  66. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  67. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  68. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  69. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -8
  70. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  71. package/src/__tests__/heartbeat-service.test.ts +50 -233
  72. package/src/__tests__/history-repair.test.ts +89 -0
  73. package/src/__tests__/host-app-control-proxy.test.ts +109 -1
  74. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  75. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  76. package/src/__tests__/host-browser-routes.test.ts +325 -33
  77. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  78. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  79. package/src/__tests__/inference-profile-reaper.test.ts +154 -0
  80. package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
  81. package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
  82. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  83. package/src/__tests__/install-skill-routing.test.ts +2 -2
  84. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +15 -0
  85. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  86. package/src/__tests__/llm-catalog-parity.test.ts +146 -0
  87. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
  88. package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
  89. package/src/__tests__/llm-resolver.test.ts +46 -0
  90. package/src/__tests__/managed-profile-guard.test.ts +131 -2
  91. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  92. package/src/__tests__/mcp-cli.test.ts +182 -220
  93. package/src/__tests__/mcp-health-check.test.ts +56 -27
  94. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  95. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  96. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  97. package/src/__tests__/oauth-cli.test.ts +38 -2009
  98. package/src/__tests__/oauth-commands-routes.test.ts +711 -0
  99. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  100. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  101. package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
  102. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  103. package/src/__tests__/plugin-bootstrap.test.ts +31 -2
  104. package/src/__tests__/plugin-route-contribution.test.ts +31 -3
  105. package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
  106. package/src/__tests__/plugin-types.test.ts +13 -11
  107. package/src/__tests__/process-message-background-slack.test.ts +46 -0
  108. package/src/__tests__/profile-entry-status.test.ts +43 -0
  109. package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
  110. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  111. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  112. package/src/__tests__/relay-server.test.ts +118 -0
  113. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  114. package/src/__tests__/schedule-retry.test.ts +56 -4
  115. package/src/__tests__/schedule-routes.test.ts +104 -0
  116. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  117. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  118. package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
  119. package/src/__tests__/scheduler-wake.test.ts +0 -63
  120. package/src/__tests__/secret-allowlist.test.ts +1 -0
  121. package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
  122. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  123. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  124. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  125. package/src/__tests__/skill-load-tool.test.ts +2 -4
  126. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  127. package/src/__tests__/suggestion-routes.test.ts +3 -3
  128. package/src/__tests__/sync-message-contract.test.ts +63 -0
  129. package/src/__tests__/task-scheduler.test.ts +88 -23
  130. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  131. package/src/__tests__/usage-cli.test.ts +11 -73
  132. package/src/__tests__/user-plugin-loader.test.ts +145 -0
  133. package/src/__tests__/vercel-config.test.ts +168 -0
  134. package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
  135. package/src/__tests__/web-search.test.ts +303 -2
  136. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  137. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
  138. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +53 -20
  139. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
  140. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  141. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  142. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  143. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  144. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  145. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  146. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  147. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  148. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  149. package/src/acp/resolve-agent.test.ts +25 -0
  150. package/src/acp/resolve-agent.ts +13 -2
  151. package/src/acp/session-manager.ts +14 -0
  152. package/src/approvals/guardian-request-resolvers.ts +32 -87
  153. package/src/calls/relay-server.ts +35 -0
  154. package/src/calls/relay-setup-router.ts +36 -0
  155. package/src/calls/types.ts +1 -0
  156. package/src/calls/voice-session-bridge.ts +23 -4
  157. package/src/channels/config.ts +14 -1
  158. package/src/channels/types.ts +1 -0
  159. package/src/cli/AGENTS.md +164 -4
  160. package/src/cli/__tests__/notifications.test.ts +54 -0
  161. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  162. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  163. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  164. package/src/cli/commands/__tests__/changelog.test.ts +593 -0
  165. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  166. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  167. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  168. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  169. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  170. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  171. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  172. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  173. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  174. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  175. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  176. package/src/cli/commands/__tests__/status.test.ts +249 -0
  177. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  178. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  179. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  180. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  181. package/src/cli/commands/attachment.ts +8 -3
  182. package/src/cli/commands/audit.ts +95 -64
  183. package/src/cli/commands/auth.ts +61 -58
  184. package/src/cli/commands/avatar.ts +276 -390
  185. package/src/cli/commands/backup.ts +409 -505
  186. package/src/cli/commands/bash.ts +9 -5
  187. package/src/cli/commands/browser.ts +28 -9
  188. package/src/cli/commands/cache.ts +9 -4
  189. package/src/cli/commands/changelog.ts +414 -0
  190. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  191. package/src/cli/commands/clients.ts +8 -3
  192. package/src/cli/commands/completions.ts +9 -9
  193. package/src/cli/commands/config.ts +102 -72
  194. package/src/cli/commands/contacts.ts +575 -696
  195. package/src/cli/commands/conversations-defer.ts +17 -69
  196. package/src/cli/commands/conversations-import.ts +90 -253
  197. package/src/cli/commands/conversations.ts +346 -436
  198. package/src/cli/commands/credential-execution.ts +9 -6
  199. package/src/cli/commands/credentials.ts +456 -736
  200. package/src/cli/commands/domain.ts +128 -206
  201. package/src/cli/commands/email.ts +606 -794
  202. package/src/cli/commands/gateway.ts +8 -1
  203. package/src/cli/commands/image-generation.ts +157 -205
  204. package/src/cli/commands/inference-providers.ts +352 -0
  205. package/src/cli/commands/inference-session.ts +415 -0
  206. package/src/cli/commands/inference.ts +87 -65
  207. package/src/cli/commands/keys.ts +8 -3
  208. package/src/cli/commands/mcp.ts +103 -287
  209. package/src/cli/commands/memory-v2.ts +162 -516
  210. package/src/cli/commands/notifications.ts +33 -7
  211. package/src/cli/commands/oauth/apps.ts +292 -261
  212. package/src/cli/commands/oauth/connect.ts +176 -297
  213. package/src/cli/commands/oauth/disconnect.ts +16 -215
  214. package/src/cli/commands/oauth/index.ts +49 -45
  215. package/src/cli/commands/oauth/mode.ts +43 -199
  216. package/src/cli/commands/oauth/ping.ts +17 -125
  217. package/src/cli/commands/oauth/providers.ts +732 -921
  218. package/src/cli/commands/oauth/request.ts +60 -350
  219. package/src/cli/commands/oauth/shared.ts +11 -121
  220. package/src/cli/commands/oauth/status.ts +31 -121
  221. package/src/cli/commands/oauth/token.ts +13 -55
  222. package/src/cli/commands/pending.ts +19 -10
  223. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  224. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  225. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  226. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  227. package/src/cli/commands/platform/connect.ts +16 -80
  228. package/src/cli/commands/platform/disconnect.ts +14 -112
  229. package/src/cli/commands/platform/index.ts +177 -246
  230. package/src/cli/commands/routes.ts +153 -336
  231. package/src/cli/commands/sequence.ts +316 -360
  232. package/src/cli/commands/skills.ts +449 -671
  233. package/src/cli/commands/status.ts +58 -37
  234. package/src/cli/commands/stt.ts +94 -262
  235. package/src/cli/commands/task.ts +14 -40
  236. package/src/cli/commands/trust.ts +8 -3
  237. package/src/cli/commands/tts.ts +162 -167
  238. package/src/cli/commands/ui.ts +35 -42
  239. package/src/cli/commands/usage.ts +188 -126
  240. package/src/cli/commands/watchers.ts +8 -3
  241. package/src/cli/commands/webhooks.ts +99 -193
  242. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  243. package/src/cli/lib/daemon-credential-client.ts +4 -5
  244. package/src/cli/lib/nested-value.ts +44 -0
  245. package/src/cli/lib/open-browser.ts +36 -0
  246. package/src/cli/lib/register-command.ts +19 -0
  247. package/src/cli/lib/time-ago.ts +34 -0
  248. package/src/cli/program.ts +2 -4
  249. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  250. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  251. package/src/cli/utils/conversation-id.ts +30 -0
  252. package/src/cli/utils/parse-duration.ts +41 -0
  253. package/src/config/acp-defaults.test.ts +5 -1
  254. package/src/config/acp-defaults.ts +11 -4
  255. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  256. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  257. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  258. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  259. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  260. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  261. package/src/config/bundled-tool-registry.ts +0 -2
  262. package/src/config/feature-flag-registry.json +16 -0
  263. package/src/config/llm-resolver.ts +16 -1
  264. package/src/config/loader.ts +76 -14
  265. package/src/config/raw-config-utils.ts +2 -30
  266. package/src/config/schema.ts +4 -0
  267. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  268. package/src/config/schemas/call-site-catalog.ts +29 -7
  269. package/src/config/schemas/llm-request-logs.ts +57 -0
  270. package/src/config/schemas/llm.ts +52 -2
  271. package/src/config/schemas/memory-retrospective.ts +48 -0
  272. package/src/config/schemas/memory-v2.ts +32 -1
  273. package/src/config/schemas/memory.ts +4 -0
  274. package/src/config/schemas/services.ts +15 -12
  275. package/src/config/seed-inference-profiles.ts +195 -134
  276. package/src/contacts/contact-store.ts +0 -61
  277. package/src/context/window-manager.ts +191 -5
  278. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +79 -0
  279. package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
  280. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  281. package/src/daemon/approval-generators.ts +23 -29
  282. package/src/daemon/config-watcher.ts +2 -0
  283. package/src/daemon/conversation-agent-loop-handlers.ts +24 -0
  284. package/src/daemon/conversation-agent-loop.ts +127 -97
  285. package/src/daemon/conversation-error.ts +21 -0
  286. package/src/daemon/conversation-lifecycle.ts +46 -5
  287. package/src/daemon/conversation-process.ts +36 -19
  288. package/src/daemon/conversation-runtime-assembly.ts +14 -5
  289. package/src/daemon/conversation-slash.ts +175 -23
  290. package/src/daemon/conversation-store.ts +17 -10
  291. package/src/daemon/conversation-surfaces.ts +76 -12
  292. package/src/daemon/conversation-tool-setup.ts +24 -14
  293. package/src/daemon/conversation.ts +48 -9
  294. package/src/daemon/external-plugins-bootstrap.ts +18 -8
  295. package/src/daemon/guardian-action-generators.ts +7 -22
  296. package/src/daemon/handlers/config-model.ts +8 -126
  297. package/src/daemon/handlers/config-slack-channel.ts +10 -7
  298. package/src/daemon/handlers/config-vercel.ts +3 -1
  299. package/src/daemon/handlers/skills.ts +84 -5
  300. package/src/daemon/history-repair.ts +33 -6
  301. package/src/daemon/host-app-control-proxy.ts +44 -19
  302. package/src/daemon/host-bash-proxy.ts +85 -158
  303. package/src/daemon/host-browser-proxy.ts +96 -35
  304. package/src/daemon/host-proxy-base.ts +13 -1
  305. package/src/daemon/host-proxy-preactivation.ts +25 -1
  306. package/src/daemon/identity-helpers.ts +19 -0
  307. package/src/daemon/lifecycle.ts +42 -43
  308. package/src/daemon/meet-host-supervisor.ts +15 -15
  309. package/src/daemon/memory-v2-startup.ts +9 -2
  310. package/src/daemon/message-protocol.ts +6 -0
  311. package/src/daemon/message-types/bookmarks.ts +18 -0
  312. package/src/daemon/message-types/conversations.ts +12 -9
  313. package/src/daemon/message-types/messages.ts +9 -1
  314. package/src/daemon/message-types/sync.ts +60 -0
  315. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  316. package/src/daemon/pkb-reminder-builder.ts +21 -7
  317. package/src/daemon/process-message.ts +56 -23
  318. package/src/daemon/server.ts +23 -18
  319. package/src/daemon/shutdown-handlers.ts +0 -2
  320. package/src/daemon/tool-setup-types.ts +9 -0
  321. package/src/daemon/tool-side-effects.ts +6 -4
  322. package/src/daemon/wake-target-adapter.ts +11 -0
  323. package/src/export/transcript-formatter.ts +61 -2
  324. package/src/filing/filing-service.ts +40 -53
  325. package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
  326. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  327. package/src/heartbeat/heartbeat-service.ts +148 -127
  328. package/src/home/__tests__/feed-types.test.ts +63 -131
  329. package/src/home/__tests__/feed-writer.test.ts +77 -278
  330. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  331. package/src/home/feed-types.ts +19 -73
  332. package/src/home/feed-writer.ts +25 -156
  333. package/src/home/post-connect-feed.ts +1 -3
  334. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  335. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  336. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  337. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  338. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  339. package/src/ipc/assistant-server.ts +55 -6
  340. package/src/ipc/cli-client.ts +370 -50
  341. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  342. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  343. package/src/ipc/skill-routes/events.ts +30 -3
  344. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  345. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  346. package/src/live-voice/live-voice-session-manager.ts +11 -4
  347. package/src/live-voice/live-voice-session.ts +14 -6
  348. package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
  349. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  350. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  351. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  352. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  353. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  354. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
  355. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  356. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  357. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  358. package/src/memory/bookmark-crud.ts +179 -0
  359. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  360. package/src/memory/context-search/agent-protocol.ts +5 -1
  361. package/src/memory/context-search/agent-runner.ts +60 -85
  362. package/src/memory/context-search/limits.ts +1 -4
  363. package/src/memory/context-search/search.ts +23 -113
  364. package/src/memory/context-search/sources/conversations.ts +18 -6
  365. package/src/memory/context-search/sources/memory-v2.ts +39 -14
  366. package/src/memory/context-search/sources/memory.ts +7 -0
  367. package/src/memory/context-search/sources/workspace.ts +13 -10
  368. package/src/memory/context-search/types.ts +1 -1
  369. package/src/memory/conversation-bootstrap.ts +11 -0
  370. package/src/memory/conversation-crud.ts +312 -10
  371. package/src/memory/conversation-queries.ts +9 -5
  372. package/src/memory/conversation-title-service.ts +1 -0
  373. package/src/memory/conversation-types.ts +16 -0
  374. package/src/memory/db-init.ts +14 -0
  375. package/src/memory/embedding-backend.ts +2 -1
  376. package/src/memory/embedding-runtime-manager.ts +1 -2
  377. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  378. package/src/memory/graph/conversation-graph-memory.ts +76 -5
  379. package/src/memory/graph/extraction.ts +4 -0
  380. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  381. package/src/memory/graph/tool-handlers.ts +17 -7
  382. package/src/memory/graph/tools.ts +44 -5
  383. package/src/memory/indexer.ts +17 -0
  384. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +13 -15
  385. package/src/memory/jobs/embed-concept-page.ts +45 -9
  386. package/src/memory/jobs-store.ts +51 -1
  387. package/src/memory/jobs-worker.ts +52 -3
  388. package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
  389. package/src/memory/llm-request-log-source-local.ts +26 -0
  390. package/src/memory/llm-request-log-source.ts +97 -0
  391. package/src/memory/llm-request-log-store.ts +1 -1
  392. package/src/memory/memory-retrospective-constants.ts +13 -0
  393. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  394. package/src/memory/memory-retrospective-job.ts +351 -0
  395. package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
  396. package/src/memory/memory-retrospective-state.ts +162 -0
  397. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  398. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  399. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  400. package/src/memory/message-content.ts +38 -1
  401. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  402. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  403. package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
  404. package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
  405. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  406. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  407. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  408. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  409. package/src/memory/migrations/243-provider-connections.ts +68 -0
  410. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  411. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  412. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  413. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  414. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  415. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  416. package/src/memory/migrations/index.ts +7 -0
  417. package/src/memory/published-pages-store.ts +16 -0
  418. package/src/memory/schema/bookmarks.ts +38 -0
  419. package/src/memory/schema/conversations.ts +2 -0
  420. package/src/memory/schema/index.ts +2 -0
  421. package/src/memory/schema/inference.ts +29 -0
  422. package/src/memory/schema/memory-core.ts +9 -0
  423. package/src/memory/search/semantic.ts +1 -4
  424. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  425. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  426. package/src/memory/v2/__tests__/activation.test.ts +11 -4
  427. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  428. package/src/memory/v2/__tests__/consolidation-job.test.ts +123 -135
  429. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  430. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  431. package/src/memory/v2/__tests__/injection.test.ts +628 -10
  432. package/src/memory/v2/__tests__/migration.test.ts +7 -3
  433. package/src/memory/v2/__tests__/page-index.test.ts +277 -0
  434. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  435. package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
  436. package/src/memory/v2/__tests__/qdrant.test.ts +72 -0
  437. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  438. package/src/memory/v2/__tests__/router.test.ts +516 -0
  439. package/src/memory/v2/__tests__/sim.test.ts +45 -1
  440. package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
  441. package/src/memory/v2/__tests__/static-context.test.ts +7 -22
  442. package/src/memory/v2/__tests__/sweep-job.test.ts +95 -0
  443. package/src/memory/v2/activation-store.ts +34 -5
  444. package/src/memory/v2/activation.ts +40 -27
  445. package/src/memory/v2/backfill-jobs.ts +17 -84
  446. package/src/memory/v2/consolidation-job.ts +85 -78
  447. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  448. package/src/memory/v2/injection.ts +440 -109
  449. package/src/memory/v2/migration.ts +117 -20
  450. package/src/memory/v2/page-index.ts +191 -0
  451. package/src/memory/v2/page-store.ts +3 -0
  452. package/src/memory/v2/prompts/consolidation.ts +9 -7
  453. package/src/memory/v2/prompts/router.ts +192 -0
  454. package/src/memory/v2/qdrant.ts +100 -87
  455. package/src/memory/v2/reranker.ts +14 -7
  456. package/src/memory/v2/router.ts +322 -0
  457. package/src/memory/v2/sim.ts +25 -12
  458. package/src/memory/v2/skill-store.ts +118 -29
  459. package/src/memory/v2/static-context.ts +16 -9
  460. package/src/memory/v2/sweep-job.ts +122 -96
  461. package/src/memory/v2/types.ts +10 -6
  462. package/src/memory/validation.ts +13 -0
  463. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  464. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  465. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  466. package/src/notifications/adapters/platform.ts +171 -0
  467. package/src/notifications/conversation-pairing.ts +2 -2
  468. package/src/notifications/copy-composer.ts +15 -0
  469. package/src/notifications/destination-resolver.ts +21 -0
  470. package/src/notifications/emit-signal.ts +28 -1
  471. package/src/notifications/home-feed-side-effect.ts +111 -0
  472. package/src/notifications/signal.ts +5 -0
  473. package/src/permissions/checker.ts +12 -0
  474. package/src/permissions/ipc-risk-types.ts +2 -0
  475. package/src/plugin-api/index.ts +13 -0
  476. package/src/plugin-api/package.json +12 -0
  477. package/src/plugin-api/types.ts +62 -0
  478. package/src/plugins/defaults/injectors.ts +19 -3
  479. package/src/plugins/external-plugin-loader.ts +294 -0
  480. package/src/plugins/types.ts +46 -30
  481. package/src/plugins/user-loader.ts +64 -41
  482. package/src/proactive-artifact/job.test.ts +12 -4
  483. package/src/proactive-artifact/job.ts +4 -0
  484. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  485. package/src/proactive-artifact/trigger-state.ts +4 -0
  486. package/src/prompts/__tests__/system-prompt.test.ts +105 -0
  487. package/src/prompts/system-prompt.ts +22 -1
  488. package/src/prompts/update-bulletin-job.ts +61 -73
  489. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  490. package/src/providers/__tests__/inference.test.ts +288 -0
  491. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  492. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  493. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  494. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  495. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  496. package/src/providers/anthropic/client.ts +95 -26
  497. package/src/providers/call-site-routing.ts +94 -16
  498. package/src/providers/connection-resolution.ts +163 -0
  499. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  500. package/src/providers/inference/adapter-factory.ts +173 -0
  501. package/src/providers/inference/auth.ts +112 -0
  502. package/src/providers/inference/backfill.ts +196 -0
  503. package/src/providers/inference/connections.ts +356 -0
  504. package/src/providers/inference/resolve-auth.ts +65 -0
  505. package/src/providers/model-catalog.ts +104 -6
  506. package/src/providers/openai/responses-provider.ts +4 -2
  507. package/src/providers/provider-env-vars.ts +17 -7
  508. package/src/providers/provider-secret-catalog.ts +49 -30
  509. package/src/providers/provider-send-message.ts +41 -20
  510. package/src/providers/registry.ts +143 -159
  511. package/src/providers/retry.ts +18 -10
  512. package/src/providers/search-provider-catalog.ts +121 -0
  513. package/src/runtime/AGENTS.md +18 -5
  514. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  515. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  516. package/src/runtime/actor-trust-resolver.ts +32 -10
  517. package/src/runtime/agent-wake.ts +35 -6
  518. package/src/runtime/assistant-event-hub.ts +3 -85
  519. package/src/runtime/auth/route-policy.ts +303 -8
  520. package/src/runtime/auth/same-actor.ts +2 -0
  521. package/src/runtime/background-job-runner.ts +339 -0
  522. package/src/runtime/btw-sidechain.ts +1 -0
  523. package/src/runtime/http-router.ts +36 -1
  524. package/src/runtime/http-server.ts +31 -5
  525. package/src/runtime/http-types.ts +2 -0
  526. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  527. package/src/runtime/middleware/request-logger.ts +62 -1
  528. package/src/runtime/pre-first-message-gate.ts +83 -0
  529. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  530. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
  531. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  532. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
  533. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
  534. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  535. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  536. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +4 -4
  537. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  538. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  539. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  540. package/src/runtime/routes/acp-routes.ts +10 -8
  541. package/src/runtime/routes/app-management-routes.ts +228 -3
  542. package/src/runtime/routes/approval-routes.ts +0 -18
  543. package/src/runtime/routes/audit-routes.ts +43 -0
  544. package/src/runtime/routes/auth-routes.ts +72 -0
  545. package/src/runtime/routes/avatar-routes.ts +273 -20
  546. package/src/runtime/routes/backup-routes.ts +406 -2
  547. package/src/runtime/routes/bookmark-routes.ts +154 -0
  548. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  549. package/src/runtime/routes/contact-routes.ts +0 -160
  550. package/src/runtime/routes/conversation-cli-routes.ts +192 -0
  551. package/src/runtime/routes/conversation-management-routes.ts +30 -43
  552. package/src/runtime/routes/conversation-query-routes.ts +334 -86
  553. package/src/runtime/routes/conversation-routes.ts +31 -10
  554. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  555. package/src/runtime/routes/credential-routes.ts +540 -0
  556. package/src/runtime/routes/debug-routes.ts +2 -2
  557. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  558. package/src/runtime/routes/domain-routes.ts +167 -0
  559. package/src/runtime/routes/email-routes.ts +603 -0
  560. package/src/runtime/routes/errors.ts +2 -2
  561. package/src/runtime/routes/events-routes.ts +192 -0
  562. package/src/runtime/routes/home-feed-routes.ts +6 -78
  563. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  564. package/src/runtime/routes/host-browser-routes.ts +103 -22
  565. package/src/runtime/routes/http-adapter.ts +2 -0
  566. package/src/runtime/routes/identity-routes.ts +5 -0
  567. package/src/runtime/routes/image-generation-routes.ts +99 -0
  568. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
  569. package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
  570. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  571. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -4
  572. package/src/runtime/routes/index.ts +36 -0
  573. package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
  574. package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
  575. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  576. package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
  577. package/src/runtime/routes/inference-send-routes.ts +115 -0
  578. package/src/runtime/routes/integrations/twilio.ts +1 -0
  579. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  580. package/src/runtime/routes/memory-v2-routes.ts +13 -398
  581. package/src/runtime/routes/notification-routes.ts +2 -0
  582. package/src/runtime/routes/oauth-apps.ts +112 -7
  583. package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
  584. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  585. package/src/runtime/routes/oauth-providers.ts +298 -8
  586. package/src/runtime/routes/platform-routes.ts +336 -0
  587. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  588. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  589. package/src/runtime/routes/playground/state.ts +2 -1
  590. package/src/runtime/routes/publish-routes.ts +221 -0
  591. package/src/runtime/routes/schedule-routes.ts +82 -0
  592. package/src/runtime/routes/sequence-routes.ts +291 -0
  593. package/src/runtime/routes/settings-routes.ts +2 -10
  594. package/src/runtime/routes/skills-routes.ts +31 -1
  595. package/src/runtime/routes/stt-routes.ts +240 -3
  596. package/src/runtime/routes/surface-action-routes.ts +43 -7
  597. package/src/runtime/routes/tts-routes.ts +67 -0
  598. package/src/runtime/routes/types.ts +32 -0
  599. package/src/runtime/routes/user-routes-cli.ts +243 -0
  600. package/src/runtime/routes/webhook-routes.ts +165 -0
  601. package/src/runtime/sync/resource-sync-events.ts +25 -0
  602. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  603. package/src/runtime/sync/sync-publisher.ts +21 -0
  604. package/src/schedule/scheduler.ts +200 -123
  605. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  606. package/src/security/secret-patterns.ts +3 -0
  607. package/src/sequence/engine.ts +38 -40
  608. package/src/subagent/manager.ts +20 -15
  609. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  610. package/src/tools/browser/browser-execution.ts +15 -4
  611. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  612. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  613. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  614. package/src/tools/browser/cdp-client/factory.ts +66 -5
  615. package/src/tools/browser/runtime-check.ts +77 -0
  616. package/src/tools/memory/register.test.ts +3 -3
  617. package/src/tools/memory/register.ts +9 -1
  618. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  619. package/src/tools/network/web-search.ts +280 -37
  620. package/src/tools/permission-checker.ts +13 -5
  621. package/src/tools/subagent/spawn.ts +3 -3
  622. package/src/tools/terminal/shell.ts +44 -0
  623. package/src/usage/attribution.ts +3 -2
  624. package/src/util/pricing.ts +86 -160
  625. package/src/watcher/__tests__/engine.test.ts +301 -0
  626. package/src/watcher/constants.ts +7 -0
  627. package/src/watcher/engine.ts +90 -90
  628. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  629. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  630. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
  631. package/src/workspace/migrations/069-seed-onboarding-threads.ts +8 -2
  632. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
  633. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
  634. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  635. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  636. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  637. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  638. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  639. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  640. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  641. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  642. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  643. package/src/workspace/migrations/registry.ts +22 -0
  644. package/src/workspace/migrations/runner.ts +13 -2
  645. package/src/workspace/migrations/types.ts +13 -3
  646. package/src/workspace/provider-commit-message-generator.ts +3 -2
  647. package/src/__tests__/context-search-pkb-source.test.ts +0 -498
  648. package/src/__tests__/credentials-cli.test.ts +0 -1225
  649. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  650. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  651. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  652. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  653. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  654. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  655. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  656. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  657. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  658. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  659. package/src/cli/commands/autonomy.ts +0 -365
  660. package/src/cli/commands/memory.ts +0 -424
  661. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -947
  662. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  663. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  664. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  665. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  666. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  667. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  668. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  669. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  670. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  671. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  672. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  673. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  674. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  675. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  676. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  677. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  678. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  679. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  680. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  681. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  682. package/src/home/assistant-feed-authoring.ts +0 -135
  683. package/src/home/emit-feed-event.ts +0 -169
  684. package/src/home/feed-scheduler.ts +0 -281
  685. package/src/home/platform-gmail-digest.ts +0 -163
  686. package/src/home/rewrite-command-preview.ts +0 -66
  687. package/src/home/rewrite-feed-title.ts +0 -58
  688. package/src/home/rollup-producer.ts +0 -426
  689. package/src/memory/admin.ts +0 -326
  690. package/src/memory/context-search/sources/pkb.ts +0 -476
  691. package/src/memory/graph/compaction.ts +0 -299
  692. /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
@@ -28,7 +28,6 @@ import { extname, isAbsolute, join, relative } from "node:path";
28
28
 
29
29
  import { getLogger } from "../../../util/logger.js";
30
30
  import { embedWithRetry } from "../../embed.js";
31
- import { generateSparseEmbedding } from "../../embedding-backend.js";
32
31
  import { spreadActivation } from "../../v2/activation.js";
33
32
  import { getEdgeIndex } from "../../v2/edge-index.js";
34
33
  import {
@@ -37,7 +36,8 @@ import {
37
36
  slugFromConceptPath,
38
37
  } from "../../v2/page-store.js";
39
38
  import { hybridQueryConceptPages } from "../../v2/qdrant.js";
40
- import { clampUnitInterval } from "../../validation.js";
39
+ import { fuseHalf } from "../../v2/sim.js";
40
+ import { generateBm25QueryEmbedding } from "../../v2/sparse-bm25.js";
41
41
  import type {
42
42
  RecallEvidence,
43
43
  RecallSearchContext,
@@ -173,7 +173,7 @@ async function activationEvidence(
173
173
  });
174
174
  const denseVector = denseResult.vectors[0];
175
175
  if (!denseVector || denseVector.length === 0) return [];
176
- const sparseVector = generateSparseEmbedding(trimmedQuery);
176
+ const sparseVector = generateBm25QueryEmbedding(trimmedQuery);
177
177
 
178
178
  const annLimit =
179
179
  context.config.memory.v2.ann_candidate_limit ??
@@ -188,24 +188,43 @@ async function activationEvidence(
188
188
  const { dense_weight: denseWeight, sparse_weight: sparseWeight } =
189
189
  context.config.memory.v2;
190
190
 
191
- let maxSparse = 0;
191
+ // Mirror sim.ts: normalize body and summary sparse channels against their
192
+ // own per-batch maxima, fuse each half via clamp01(dense·w_d + sparse·w_s),
193
+ // then take max(body, summary) per slug. Pages without a summary embedding
194
+ // return undefined for both summary scores; the max collapses cleanly to
195
+ // the body score so legacy pages keep their pre-summary ranking.
196
+ let maxBodySparse = 0;
197
+ let maxSummarySparse = 0;
192
198
  for (const hit of hits) {
193
- if (hit.sparseScore !== undefined && hit.sparseScore > maxSparse) {
194
- maxSparse = hit.sparseScore;
199
+ if (hit.sparseScore !== undefined && hit.sparseScore > maxBodySparse) {
200
+ maxBodySparse = hit.sparseScore;
201
+ }
202
+ if (
203
+ hit.summarySparseScore !== undefined &&
204
+ hit.summarySparseScore > maxSummarySparse
205
+ ) {
206
+ maxSummarySparse = hit.summarySparseScore;
195
207
  }
196
208
  }
197
209
 
198
210
  const ownActivation = new Map<string, number>();
199
211
  for (const hit of hits) {
200
- const dense = hit.denseScore ?? 0;
201
- const sparseNormalized =
202
- hit.sparseScore !== undefined && maxSparse > 0
203
- ? hit.sparseScore / maxSparse
204
- : 0;
205
- ownActivation.set(
206
- hit.slug,
207
- clampUnitInterval(denseWeight * dense + sparseWeight * sparseNormalized),
212
+ const bodyScore = fuseHalf(
213
+ hit.denseScore,
214
+ hit.sparseScore,
215
+ maxBodySparse,
216
+ denseWeight,
217
+ sparseWeight,
218
+ );
219
+ const summaryScore = fuseHalf(
220
+ hit.summaryDenseScore,
221
+ hit.summarySparseScore,
222
+ maxSummarySparse,
223
+ denseWeight,
224
+ sparseWeight,
208
225
  );
226
+ const score = Math.max(bodyScore ?? 0, summaryScore ?? bodyScore ?? 0);
227
+ ownActivation.set(hit.slug, score);
209
228
  }
210
229
 
211
230
  const edgeIndex = await getEdgeIndex(context.workingDir);
@@ -295,11 +314,13 @@ async function lexicalEvidence(
295
314
  if (!conceptsRoot) return [];
296
315
 
297
316
  const matches: MemoryV2LexicalMatch[] = [];
317
+ const visitedDirectories = new Set<string>([conceptsRoot]);
298
318
  await walkConceptsDirectory(
299
319
  conceptsRoot,
300
320
  conceptsRoot,
301
321
  queryTerms,
302
322
  matches,
323
+ visitedDirectories,
303
324
  context.signal,
304
325
  );
305
326
 
@@ -330,6 +351,7 @@ async function walkConceptsDirectory(
330
351
  conceptsRoot: string,
331
352
  queryTerms: ReadonlySet<string>,
332
353
  matches: MemoryV2LexicalMatch[],
354
+ visitedDirectories: Set<string>,
333
355
  signal: AbortSignal | undefined,
334
356
  ): Promise<void> {
335
357
  throwIfAborted(signal);
@@ -364,11 +386,14 @@ async function walkConceptsDirectory(
364
386
  }
365
387
 
366
388
  if (entryStats.isDirectory()) {
389
+ if (visitedDirectories.has(entryRealPath)) continue;
390
+ visitedDirectories.add(entryRealPath);
367
391
  await walkConceptsDirectory(
368
392
  entryRealPath,
369
393
  conceptsRoot,
370
394
  queryTerms,
371
395
  matches,
396
+ visitedDirectories,
372
397
  signal,
373
398
  );
374
399
  continue;
@@ -34,6 +34,7 @@ export async function searchMemorySource(
34
34
  });
35
35
  queryVector = result.vectors[0] ?? null;
36
36
  } catch (err) {
37
+ if (context.signal?.aborted || isAbortError(err)) throw err;
37
38
  log.warn({ err }, "Failed to embed memory recall query");
38
39
  return { evidence: [] };
39
40
  }
@@ -67,11 +68,17 @@ export async function searchMemorySource(
67
68
 
68
69
  return { evidence };
69
70
  } catch (err) {
71
+ if (context.signal?.aborted || isAbortError(err)) throw err;
70
72
  log.warn({ err }, "Failed to search memory graph for recall");
71
73
  return { evidence: [] };
72
74
  }
73
75
  }
74
76
 
77
+ function isAbortError(err: unknown): boolean {
78
+ if (!(err instanceof Error)) return false;
79
+ return err.name === "AbortError" || err.name === "APIUserAbortError";
80
+ }
81
+
75
82
  function memoryNodeToEvidence(node: MemoryNode, score: number): RecallEvidence {
76
83
  return {
77
84
  id: `memory:${node.id}`,
@@ -23,7 +23,6 @@ const PATH_LITERAL_PATTERN =
23
23
 
24
24
  const WORKSPACE_BUCKETS: readonly WorkspaceBucket[] = [
25
25
  { name: "root", relativePath: "", budget: 100, rootFilesOnly: true },
26
- { name: "pkb", relativePath: "pkb", budget: 500 },
27
26
  { name: "memory", relativePath: "memory", budget: 500 },
28
27
  { name: "journal", relativePath: "journal", budget: 250 },
29
28
  { name: "scratch", relativePath: "scratch", budget: 500 },
@@ -66,6 +65,9 @@ const SECRET_SEGMENT_NAMES = new Set([
66
65
  "ces-security",
67
66
  ]);
68
67
 
68
+ const SECRET_TOKEN_PATTERN =
69
+ /(?:^|[-_.])(?:keys?|secrets?|tokens?)(?:[-_.]|$)/i;
70
+
69
71
  const QUERY_STOP_WORDS = new Set([
70
72
  "a",
71
73
  "about",
@@ -591,8 +593,13 @@ async function maybeSearchResolvedFile(
591
593
  matches: WorkspaceMatch[],
592
594
  state: WalkState,
593
595
  ): Promise<void> {
596
+ if (state.scannedRelativePaths.has(relativePath)) {
597
+ return;
598
+ }
599
+ state.scannedRelativePaths.add(relativePath);
600
+ state.scannedFiles += 1;
601
+
594
602
  if (
595
- state.scannedRelativePaths.has(relativePath) ||
596
603
  shouldSkipWorkspaceFile(relativePath) ||
597
604
  shouldSkipFilePath(relativePath) ||
598
605
  fileSizeBytes > WORKSPACE_SOURCE_MAX_FILE_SIZE_BYTES
@@ -600,8 +607,6 @@ async function maybeSearchResolvedFile(
600
607
  return;
601
608
  }
602
609
 
603
- state.scannedRelativePaths.add(relativePath);
604
- state.scannedFiles += 1;
605
610
  matches.push(
606
611
  ...(await searchFile(realPath, relativePath, fileSizeBytes, queryTerms)),
607
612
  );
@@ -1049,8 +1054,6 @@ function getPathPriorityBoost(relativePath: string): number {
1049
1054
  if (relativePath.startsWith("scratch/")) return 0.35;
1050
1055
  if (relativePath.startsWith("users/")) return 0.3;
1051
1056
  if (relativePath.startsWith("journal/")) return 0.25;
1052
- if (relativePath.startsWith("pkb/archive/")) return -0.15;
1053
- if (relativePath.startsWith("pkb/")) return 0.2;
1054
1057
  if (relativePath.startsWith("memory/concepts/")) return 0.4;
1055
1058
  if (relativePath.startsWith("memory/")) return 0.2;
1056
1059
  if (
@@ -1181,15 +1184,15 @@ function shouldSkipSegmentName(name: string): boolean {
1181
1184
  return (
1182
1185
  GENERATED_OR_DEPENDENCY_DIR_NAMES.has(lowerName) ||
1183
1186
  lowerName.startsWith(".env") ||
1184
- lowerName.includes("key") ||
1185
- lowerName.includes("secret") ||
1186
- lowerName.includes("token") ||
1187
+ SECRET_TOKEN_PATTERN.test(lowerName) ||
1187
1188
  lowerName.startsWith("credentials") ||
1188
1189
  SECRET_SEGMENT_NAMES.has(lowerName)
1189
1190
  );
1190
1191
  }
1191
1192
 
1192
- function normalizeWorkspacePathLiteral(pathLiteral: string): string | null {
1193
+ export function normalizeWorkspacePathLiteral(
1194
+ pathLiteral: string,
1195
+ ): string | null {
1193
1196
  const trimmed = pathLiteral
1194
1197
  .trim()
1195
1198
  .replace(/^["'`]+|["'`.,;:)>\]}]+$/g, "")
@@ -1,6 +1,6 @@
1
1
  import type { AssistantConfig } from "../../config/schema.js";
2
2
 
3
- export type RecallSource = "memory" | "pkb" | "conversations" | "workspace";
3
+ export type RecallSource = "memory" | "conversations" | "workspace";
4
4
 
5
5
  export type RecallDepth = "fast" | "standard" | "deep";
6
6
 
@@ -12,6 +12,14 @@ export interface BootstrapConversationOptions {
12
12
  systemHint: string;
13
13
  scheduleJobId?: string;
14
14
  groupId?: string;
15
+ /**
16
+ * When set, the new conversation is linked to its parent via the
17
+ * `fork_parent_conversation_id` column. Used by background jobs that
18
+ * spawn analysis conversations off a source conversation (auto-analyze,
19
+ * memory-retrospective) so the parent → child relationship is queryable
20
+ * later (e.g. "find the most recent retrospective for this source").
21
+ */
22
+ forkParentConversationId?: string;
15
23
  }
16
24
 
17
25
  export function bootstrapConversation(opts: BootstrapConversationOptions) {
@@ -21,6 +29,9 @@ export function bootstrapConversation(opts: BootstrapConversationOptions) {
21
29
  ...(opts.source && { source: opts.source }),
22
30
  ...(opts.scheduleJobId && { scheduleJobId: opts.scheduleJobId }),
23
31
  ...(opts.groupId && { groupId: opts.groupId }),
32
+ ...(opts.forkParentConversationId && {
33
+ forkParentConversationId: opts.forkParentConversationId,
34
+ }),
24
35
  });
25
36
  queueGenerateConversationTitle({
26
37
  conversationId: conversation.id,
@@ -9,6 +9,7 @@ import {
9
9
  gt,
10
10
  gte,
11
11
  inArray,
12
+ isNotNull,
12
13
  isNull,
13
14
  like,
14
15
  lt,
@@ -45,8 +46,11 @@ import {
45
46
  } from "./conversation-disk-view.js";
46
47
  import { ensureDisplayOrderMigration } from "./conversation-display-order-migration.js";
47
48
  import { ensureGroupMigration } from "./conversation-group-migration.js";
48
- import { getDb } from "./db-connection.js";
49
+ import { getDb, getSqliteFrom } from "./db-connection.js";
50
+ import { forkGraphMemoryState } from "./graph/graph-memory-state-store.js";
49
51
  import { indexMessageNow } from "./indexer.js";
52
+ import { MEMORY_RETROSPECTIVE_SOURCE } from "./memory-retrospective-constants.js";
53
+ import { forkRetrospectiveState } from "./memory-retrospective-state.js";
50
54
  import { rawExec, rawGet, rawRun } from "./raw-query.js";
51
55
  import {
52
56
  channelInboundEvents,
@@ -60,6 +64,7 @@ import {
60
64
  toolInvocations,
61
65
  } from "./schema.js";
62
66
  import { cancelPendingJobsForConversation } from "./task-memory-cleanup.js";
67
+ import { forkActivationState } from "./v2/activation-store.js";
63
68
 
64
69
  const log = getLogger("conversation-store");
65
70
 
@@ -112,6 +117,7 @@ export const messageMetadataSchema = z
112
117
  workspaceBlock: z.string().optional(),
113
118
  nowScratchpadBlock: z.string().optional(),
114
119
  pkbContextBlock: z.string().optional(),
120
+ memoryV2StaticBlock: z.string().optional(),
115
121
  })
116
122
  .passthrough();
117
123
 
@@ -186,6 +192,8 @@ export interface ConversationRow {
186
192
  lastMessageAt: number | null;
187
193
  archivedAt: number | null;
188
194
  inferenceProfile: string | null;
195
+ inferenceProfileSessionId: string | null;
196
+ inferenceProfileExpiresAt: number | null;
189
197
  }
190
198
 
191
199
  export const parseConversation = createRowMapper<
@@ -216,6 +224,8 @@ export const parseConversation = createRowMapper<
216
224
  lastMessageAt: "lastMessageAt",
217
225
  archivedAt: "archivedAt",
218
226
  inferenceProfile: "inferenceProfile",
227
+ inferenceProfileSessionId: "inferenceProfileSessionId",
228
+ inferenceProfileExpiresAt: "inferenceProfileExpiresAt",
219
229
  });
220
230
 
221
231
  export interface MessageRow {
@@ -427,6 +437,60 @@ export function findAnalysisConversationFor(
427
437
  return row ? { id: row.id } : null;
428
438
  }
429
439
 
440
+ /**
441
+ * Find the most recent memory-retrospective background conversation rooted
442
+ * at `parentConversationId`. Used by the memory-retrospective job handler
443
+ * to load the prior retrospective's `remember` calls into the new run's
444
+ * `<already_remembered>` block — bounded source-of-truth for "what the
445
+ * prior pass already saved" that scales as the source conversation grows.
446
+ *
447
+ * Walks up `forkParentConversationId` when no retrospective exists at the
448
+ * current level. This lets a forked conversation inherit dedup context from
449
+ * its source's most recent retro on the fork's *first* retrospective —
450
+ * otherwise the fork would re-save every fact the source already retro'd.
451
+ * Once the fork accumulates its own retros, those are found at the first
452
+ * iteration and we never walk up.
453
+ *
454
+ * Returns `null` when no prior retrospective exists anywhere in the fork
455
+ * chain (true first-run case).
456
+ *
457
+ * Hits `idx_conversations_fork_parent_conversation_id` for the
458
+ * `forkParentConversationId` lookup.
459
+ */
460
+ const MAX_FORK_CHAIN_DEPTH = 16;
461
+
462
+ export function findMostRecentRetrospectiveFor(
463
+ parentConversationId: string,
464
+ ): { id: string } | null {
465
+ const db = getDb();
466
+ let currentId: string | null = parentConversationId;
467
+ for (let depth = 0; depth < MAX_FORK_CHAIN_DEPTH && currentId; depth++) {
468
+ const row = db
469
+ .select({ id: conversations.id })
470
+ .from(conversations)
471
+ .where(
472
+ and(
473
+ eq(conversations.source, MEMORY_RETROSPECTIVE_SOURCE),
474
+ eq(conversations.forkParentConversationId, currentId),
475
+ ),
476
+ )
477
+ .orderBy(desc(conversations.createdAt))
478
+ .limit(1)
479
+ .get();
480
+ if (row) return { id: row.id };
481
+
482
+ const parent = db
483
+ .select({
484
+ forkParentConversationId: conversations.forkParentConversationId,
485
+ })
486
+ .from(conversations)
487
+ .where(eq(conversations.id, currentId))
488
+ .get();
489
+ currentId = parent?.forkParentConversationId ?? null;
490
+ }
491
+ return null;
492
+ }
493
+
430
494
  /**
431
495
  * Returns the `source` column for the given conversation, or null if
432
496
  * not found. Tiny convenience used by the recursion guard in the
@@ -661,6 +725,19 @@ export function forkConversation(params: {
661
725
  latestAssistantMessageAt: latestForkedAssistant?.messageAt ?? null,
662
726
  });
663
727
 
728
+ // Carry the parent's per-conversation memory state into the child so the
729
+ // forked thread resumes with the same activation/injection log and
730
+ // in-context tracker the parent had at fork time.
731
+ forkActivationState(db, sourceConversation.id, fc.id);
732
+ forkGraphMemoryState(sourceConversation.id, fc.id);
733
+ forkRetrospectiveState({
734
+ database: db,
735
+ sourceConversationId: sourceConversation.id,
736
+ forkedConversationId: fc.id,
737
+ forkedMessageIds,
738
+ lastCopiedSourceMessageId: messagesToCopy.at(-1)?.id ?? null,
739
+ });
740
+
664
741
  return fc;
665
742
  });
666
743
 
@@ -1003,6 +1080,90 @@ export function selectSlackMetaCandidateMetadata(
1003
1080
  return out;
1004
1081
  }
1005
1082
 
1083
+ /**
1084
+ * Count messages in a conversation that were created strictly after the
1085
+ * `afterMessageId` reference message. If `afterMessageId` is `null` or empty,
1086
+ * counts all messages in the conversation. If the referenced message no
1087
+ * longer exists (e.g. deleted by a separate flow), returns 0 — callers
1088
+ * decide how to react to a vanished reference, and the conservative answer
1089
+ * here is "no new work."
1090
+ *
1091
+ * Used by the memory-retrospective trigger check to decide whether to fire
1092
+ * the message-count trigger without loading message bodies.
1093
+ */
1094
+ export function countMessagesAfter(
1095
+ conversationId: string,
1096
+ afterMessageId: string | null,
1097
+ ): number {
1098
+ const db = getDb();
1099
+ if (afterMessageId === null || afterMessageId === "") {
1100
+ const row = db
1101
+ .select({ c: count() })
1102
+ .from(messages)
1103
+ .where(eq(messages.conversationId, conversationId))
1104
+ .get();
1105
+ return row?.c ?? 0;
1106
+ }
1107
+ const ref = db
1108
+ .select({ createdAt: messages.createdAt })
1109
+ .from(messages)
1110
+ .where(eq(messages.id, afterMessageId))
1111
+ .get();
1112
+ if (!ref) return 0;
1113
+ const row = db
1114
+ .select({ c: count() })
1115
+ .from(messages)
1116
+ .where(
1117
+ and(
1118
+ eq(messages.conversationId, conversationId),
1119
+ gt(messages.createdAt, ref.createdAt),
1120
+ ),
1121
+ )
1122
+ .get();
1123
+ return row?.c ?? 0;
1124
+ }
1125
+
1126
+ /**
1127
+ * Return messages in a conversation created strictly after the
1128
+ * `afterMessageId` reference. If the reference is `null`/empty, returns all
1129
+ * messages. If the reference doesn't exist, returns an empty array (mirrors
1130
+ * `countMessagesAfter`'s conservative semantics). Used by the
1131
+ * memory-retrospective job handler to load the message slice it processes.
1132
+ */
1133
+ export function getMessagesAfter(
1134
+ conversationId: string,
1135
+ afterMessageId: string | null,
1136
+ ): MessageRow[] {
1137
+ const db = getDb();
1138
+ if (afterMessageId === null || afterMessageId === "") {
1139
+ return db
1140
+ .select()
1141
+ .from(messages)
1142
+ .where(eq(messages.conversationId, conversationId))
1143
+ .orderBy(asc(messages.createdAt))
1144
+ .all()
1145
+ .map(parseMessage);
1146
+ }
1147
+ const ref = db
1148
+ .select({ createdAt: messages.createdAt })
1149
+ .from(messages)
1150
+ .where(eq(messages.id, afterMessageId))
1151
+ .get();
1152
+ if (!ref) return [];
1153
+ return db
1154
+ .select()
1155
+ .from(messages)
1156
+ .where(
1157
+ and(
1158
+ eq(messages.conversationId, conversationId),
1159
+ gt(messages.createdAt, ref.createdAt),
1160
+ ),
1161
+ )
1162
+ .orderBy(asc(messages.createdAt))
1163
+ .all()
1164
+ .map(parseMessage);
1165
+ }
1166
+
1006
1167
  /**
1007
1168
  * Efficient existence check — returns true if the conversation has at least
1008
1169
  * one message row. Uses `LIMIT 1` + `select({ 1 })` to avoid loading and
@@ -1226,6 +1387,10 @@ export function unarchiveConversation(id: string): boolean {
1226
1387
  * Set or clear the inference profile override for a conversation.
1227
1388
  * Pass `null` to clear the override and fall back to the workspace
1228
1389
  * `llm.activeProfile` resolution.
1390
+ *
1391
+ * Also clears any stale session columns (`inferenceProfileSessionId`,
1392
+ * `inferenceProfileExpiresAt`) so that the reaper and lazy expiry check
1393
+ * cannot later clobber the newly-set profile.
1229
1394
  */
1230
1395
  export function setConversationInferenceProfile(
1231
1396
  conversationId: string,
@@ -1233,17 +1398,130 @@ export function setConversationInferenceProfile(
1233
1398
  ): void {
1234
1399
  const db = getDb();
1235
1400
  db.update(conversations)
1236
- .set({ inferenceProfile: profile, updatedAt: Date.now() })
1401
+ .set({
1402
+ inferenceProfile: profile,
1403
+ inferenceProfileSessionId: null,
1404
+ inferenceProfileExpiresAt: null,
1405
+ updatedAt: Date.now(),
1406
+ })
1407
+ .where(eq(conversations.id, conversationId))
1408
+ .run();
1409
+ }
1410
+
1411
+ /**
1412
+ * Atomically set the inference profile, session id, and expiry timestamp for
1413
+ * a conversation. Pass `null` for all three to clear the session-backed
1414
+ * override and fall back to the workspace `llm.activeProfile` resolution.
1415
+ */
1416
+ export function setConversationInferenceProfileSession(
1417
+ conversationId: string,
1418
+ profile: string | null,
1419
+ sessionId: string | null,
1420
+ expiresAt: number | null,
1421
+ ): void {
1422
+ const db = getDb();
1423
+ db.update(conversations)
1424
+ .set({
1425
+ inferenceProfile: profile,
1426
+ inferenceProfileSessionId: sessionId,
1427
+ inferenceProfileExpiresAt: expiresAt,
1428
+ updatedAt: Date.now(),
1429
+ })
1237
1430
  .where(eq(conversations.id, conversationId))
1238
1431
  .run();
1239
1432
  }
1240
1433
 
1434
+ /**
1435
+ * Clear all conversations whose session-backed inference profile has expired.
1436
+ * Returns an array of `{ conversationId, sessionId }` for each cleared row so
1437
+ * callers can emit the appropriate update events.
1438
+ */
1439
+ export function clearExpiredInferenceProfiles(
1440
+ now: number,
1441
+ ): Array<{ conversationId: string; sessionId: string | null }> {
1442
+ const raw = getSqliteFrom(getDb());
1443
+ // Two-step approach: SELECT to get pre-clear sessionIds, then UPDATE.
1444
+ // The UPDATE re-applies the WHERE condition for CAS safety.
1445
+ // RETURNING the id lets us know which rows were actually cleared.
1446
+ const expired = raw
1447
+ .prepare(
1448
+ `
1449
+ SELECT id AS conversationId, inference_profile_session_id AS sessionId
1450
+ FROM conversations
1451
+ WHERE inference_profile_expires_at IS NOT NULL AND inference_profile_expires_at <= ?
1452
+ `,
1453
+ )
1454
+ .all(now) as Array<{ conversationId: string; sessionId: string | null }>;
1455
+
1456
+ if (expired.length === 0) return [];
1457
+
1458
+ const ids = expired.map((r) => r.conversationId);
1459
+ const placeholders = ids.map(() => "?").join(", ");
1460
+
1461
+ const actuallyCleared = raw
1462
+ .prepare(
1463
+ `
1464
+ UPDATE conversations
1465
+ SET inference_profile = NULL, inference_profile_session_id = NULL, inference_profile_expires_at = NULL
1466
+ WHERE id IN (${placeholders}) AND inference_profile_expires_at IS NOT NULL AND inference_profile_expires_at <= ?
1467
+ RETURNING id AS conversationId
1468
+ `,
1469
+ )
1470
+ .all(...ids, now) as Array<{ conversationId: string }>;
1471
+
1472
+ const clearedSet = new Set(actuallyCleared.map((r) => r.conversationId));
1473
+ return expired.filter((r) => clearedSet.has(r.conversationId));
1474
+ }
1475
+
1476
+ /**
1477
+ * List conversations with an active (non-expired) session-backed inference
1478
+ * profile. Pass a `conversationId` to narrow to a single conversation.
1479
+ */
1480
+ export function listActiveInferenceProfileSessions(
1481
+ conversationId?: string,
1482
+ ): Array<{
1483
+ conversationId: string;
1484
+ conversationTitle: string | null;
1485
+ profile: string;
1486
+ sessionId: string;
1487
+ expiresAt: number;
1488
+ }> {
1489
+ const db = getDb();
1490
+ const now = Date.now();
1491
+ const baseConditions = [
1492
+ isNotNull(conversations.inferenceProfile),
1493
+ isNotNull(conversations.inferenceProfileExpiresAt),
1494
+ gt(conversations.inferenceProfileExpiresAt, now),
1495
+ isNotNull(conversations.inferenceProfileSessionId),
1496
+ ];
1497
+ if (conversationId) {
1498
+ baseConditions.push(eq(conversations.id, conversationId));
1499
+ }
1500
+ return db
1501
+ .select({
1502
+ conversationId: conversations.id,
1503
+ conversationTitle: conversations.title,
1504
+ profile: conversations.inferenceProfile,
1505
+ sessionId: conversations.inferenceProfileSessionId,
1506
+ expiresAt: conversations.inferenceProfileExpiresAt,
1507
+ })
1508
+ .from(conversations)
1509
+ .where(and(...baseConditions))
1510
+ .all() as Array<{
1511
+ conversationId: string;
1512
+ conversationTitle: string | null;
1513
+ profile: string;
1514
+ sessionId: string;
1515
+ expiresAt: number;
1516
+ }>;
1517
+ }
1518
+
1241
1519
  /**
1242
1520
  * Resolve the per-turn inference-profile override from an already-loaded
1243
- * conversation row. Returns the row's `inferenceProfile` for non-background
1244
- * conversations, `undefined` otherwise background turns (subagent fan-out,
1245
- * scheduled tasks, update bulletins) run on the workspace defaults rather
1246
- * than inheriting an interactive override.
1521
+ * conversation row. Returns the row's `inferenceProfile` for interactive
1522
+ * conversations, `undefined` for automation threads (subagent fan-out,
1523
+ * scheduled tasks, update bulletins) so they run on the workspace defaults
1524
+ * rather than inheriting an interactive override.
1247
1525
  *
1248
1526
  * Prefer this row-based form when the caller already needs to read the
1249
1527
  * conversation row for other reasons (e.g. the agent loop's title check).
@@ -1251,7 +1529,27 @@ export function setConversationInferenceProfile(
1251
1529
  export function getConversationOverrideProfileFromRow(
1252
1530
  conv: ConversationRow | null,
1253
1531
  ): string | undefined {
1254
- if (conv?.conversationType === "background") return undefined;
1532
+ if (
1533
+ conv?.conversationType === "background" ||
1534
+ conv?.conversationType === "scheduled"
1535
+ ) {
1536
+ return undefined;
1537
+ }
1538
+ // Treat an expired session as if the override is absent. The eager reaper
1539
+ // clears the row and emits the update event; the lazy check here ensures
1540
+ // correctness on read paths before the reaper fires.
1541
+ //
1542
+ // `<=` (not `<`) for boundary consistency with the rest of the session
1543
+ // logic: the reaper SQL uses `expires_at <= ?`, and the active-session
1544
+ // queries use `expiresAt > now` (i.e. treat exact-expiry as inactive).
1545
+ // Without this, a session at the exact-expiry millisecond would be served
1546
+ // for one extra turn here while being cleared by the reaper.
1547
+ if (
1548
+ conv?.inferenceProfileExpiresAt != null &&
1549
+ conv.inferenceProfileExpiresAt <= Date.now()
1550
+ ) {
1551
+ return undefined;
1552
+ }
1255
1553
  return conv?.inferenceProfile ?? undefined;
1256
1554
  }
1257
1555
 
@@ -1481,8 +1779,10 @@ export function updateMessageMetadata(
1481
1779
  /**
1482
1780
  * Bulk-remove the metadata fields that back the blocks stripped by
1483
1781
  * `stripInjectionsForCompaction` — currently `pkbSystemReminderBlock`
1484
- * (`<system_reminder>`), `nowScratchpadBlock` (`<NOW.md …>`), and
1485
- * `pkbContextBlock` (`<knowledge_base>`). Called from compaction-strip
1782
+ * (`<system_reminder>`), `nowScratchpadBlock` (`<NOW.md …>`),
1783
+ * `pkbContextBlock` (`<knowledge_base>`), and `memoryV2StaticBlock`
1784
+ * (the static `<memory>\n…</memory>` block matched by the `<memory>\n`
1785
+ * prefix in `RUNTIME_INJECTION_PREFIXES`). Called from compaction-strip
1486
1786
  * sites so post-restart rehydration stays consistent with the in-memory
1487
1787
  * state produced by `stripInjectionsForCompaction` (which removes those
1488
1788
  * tags from live messages but cannot touch the DB). Fields backing
@@ -1498,7 +1798,8 @@ export function clearStrippedInjectionMetadataForConversation(
1498
1798
  metadata,
1499
1799
  '$.pkbSystemReminderBlock',
1500
1800
  '$.nowScratchpadBlock',
1501
- '$.pkbContextBlock'
1801
+ '$.pkbContextBlock',
1802
+ '$.memoryV2StaticBlock'
1502
1803
  )
1503
1804
  WHERE conversation_id = ?
1504
1805
  AND role = 'user'
@@ -1507,6 +1808,7 @@ export function clearStrippedInjectionMetadataForConversation(
1507
1808
  json_extract(metadata, '$.pkbSystemReminderBlock') IS NOT NULL
1508
1809
  OR json_extract(metadata, '$.nowScratchpadBlock') IS NOT NULL
1509
1810
  OR json_extract(metadata, '$.pkbContextBlock') IS NOT NULL
1811
+ OR json_extract(metadata, '$.memoryV2StaticBlock') IS NOT NULL
1510
1812
  )`,
1511
1813
  conversationId,
1512
1814
  );