@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
@@ -70,6 +70,30 @@ const BuildEventParams = z.object({
70
70
  conversationId: z.string().optional(),
71
71
  });
72
72
 
73
+ // ---------------------------------------------------------------------------
74
+ // Event-type blocklist
75
+ // ---------------------------------------------------------------------------
76
+
77
+ /**
78
+ * Message types that skills are NOT allowed to publish. These are
79
+ * daemon→client control events that trigger host-side execution (bash,
80
+ * file I/O, browser automation, credential prompts) or register pending
81
+ * interactions. Allowing a skill to publish them would bypass the
82
+ * trust/approval gates and escape the skill isolation boundary.
83
+ *
84
+ * Prefix-based: any event whose `type` starts with `"host_"` is blocked,
85
+ * covering current types (`host_bash_request`, `host_file_request`,
86
+ * `host_browser_request`, `host_cu_request`, `host_transfer_request`,
87
+ * and their `_cancel` counterparts) plus any future `host_*` additions.
88
+ */
89
+ function isBlockedEventType(type: unknown): boolean {
90
+ if (typeof type !== "string") return true;
91
+ if (type.startsWith("host_")) return true;
92
+ if (type === "confirmation_request") return true;
93
+ if (type === "secret_request") return true;
94
+ return false;
95
+ }
96
+
73
97
  // ---------------------------------------------------------------------------
74
98
  // Handlers
75
99
  // ---------------------------------------------------------------------------
@@ -78,9 +102,12 @@ async function handlePublish(
78
102
  params?: Record<string, unknown>,
79
103
  ): Promise<{ published: true }> {
80
104
  const { event } = PublishParams.parse(params);
81
- // Contract types the hub as `AssistantEvent<ServerMessage>`; the wire-level
82
- // message is an opaque record that satisfies the `ServerMessage` structural
83
- // shape (`{ type: string; [key: string]: unknown }`). Cast at the boundary.
105
+ const msgType = (event.message as Record<string, unknown>)?.type;
106
+ if (isBlockedEventType(msgType)) {
107
+ throw new Error(
108
+ `Skills cannot publish events of type "${String(msgType)}"`,
109
+ );
110
+ }
84
111
  await assistantEventHub.publish(event as never);
85
112
  return { published: true };
86
113
  }
@@ -328,6 +328,52 @@ describe("LiveVoiceSessionManager", () => {
328
328
  expect(manager.activeSessionId).toBe("session-1");
329
329
  });
330
330
 
331
+ test("holds the session lock until close completes", async () => {
332
+ const sessions: TestSession[] = [];
333
+ let resolveClose: (() => void) | undefined;
334
+ const manager = new LiveVoiceSessionManager({
335
+ createSessionId: mock(() => `session-${sessions.length + 1}`),
336
+ createSession: () => {
337
+ const session = createTestSession({
338
+ close: mock(
339
+ (reason: LiveVoiceSessionCloseReason) =>
340
+ new Promise<void>((resolve) => {
341
+ sessions[sessions.length - 1]?.closeReasons.push(reason);
342
+ resolveClose = resolve;
343
+ }),
344
+ ),
345
+ });
346
+ sessions.push(session);
347
+ return session;
348
+ },
349
+ });
350
+ const first = createSink();
351
+ const second = createSink();
352
+ const third = createSink();
353
+
354
+ await manager.startSession(START_FRAME, first.sink);
355
+ const releasePromise = manager.releaseSession("session-1", "client_end");
356
+ const concurrent = await manager.startSession(START_FRAME, second.sink);
357
+ const concurrentDispatch = await manager.handleClientFrame("session-1", {
358
+ type: "interrupt",
359
+ });
360
+
361
+ expect(concurrent).toEqual({
362
+ status: "busy",
363
+ activeSessionId: "session-1",
364
+ frame: { type: "busy", seq: 1, activeSessionId: "session-1" },
365
+ });
366
+ expect(concurrentDispatch).toEqual({ status: "not_found" });
367
+ expect(sessions).toHaveLength(1);
368
+
369
+ resolveClose?.();
370
+ await releasePromise;
371
+
372
+ const next = await manager.startSession(START_FRAME, third.sink);
373
+ expect(next).toEqual({ status: "accepted", sessionId: "session-2" });
374
+ expect(manager.activeSessionId).toBe("session-2");
375
+ });
376
+
331
377
  test("does not import runtime, gateway, provider, or conversation modules", async () => {
332
378
  const source = await Bun.file(
333
379
  new URL("../live-voice-session-manager.ts", import.meta.url),
@@ -348,6 +348,7 @@ describe("RuntimeHttpServer live voice WebSocket shell", () => {
348
348
  conversationId: "conversation-after-error",
349
349
  });
350
350
  expect(typeof ready.sessionId).toBe("string");
351
+ expect(ready.seq as number).toBeGreaterThan(error.seq as number);
351
352
  });
352
353
 
353
354
  test("releases the session lock when the WebSocket closes", async () => {
@@ -85,6 +85,7 @@ export type LiveVoiceSessionReleaseResult =
85
85
  interface ActiveLiveVoiceSession {
86
86
  sessionId: string;
87
87
  session: LiveVoiceSession;
88
+ closing: boolean;
88
89
  }
89
90
 
90
91
  export class LiveVoiceSessionManager {
@@ -132,7 +133,7 @@ export class LiveVoiceSessionManager {
132
133
  },
133
134
  };
134
135
  const session = this.createSession(context);
135
- this.activeSession = { sessionId, session };
136
+ this.activeSession = { sessionId, session, closing: false };
136
137
 
137
138
  try {
138
139
  await session.start();
@@ -198,14 +199,20 @@ export class LiveVoiceSessionManager {
198
199
  return { released: false };
199
200
  }
200
201
 
201
- this.activeSession = null;
202
- await active.session.close(reason);
202
+ active.closing = true;
203
+ try {
204
+ await active.session.close(reason);
205
+ } finally {
206
+ if (this.activeSession === active) {
207
+ this.activeSession = null;
208
+ }
209
+ }
203
210
  return { released: true, sessionId };
204
211
  }
205
212
 
206
213
  private findActiveSession(sessionId: string): ActiveLiveVoiceSession | null {
207
214
  const active = this.activeSession;
208
- if (active === null || active.sessionId !== sessionId) {
215
+ if (active === null || active.sessionId !== sessionId || active.closing) {
209
216
  return null;
210
217
  }
211
218
 
@@ -307,7 +307,6 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
307
307
  )}`,
308
308
  });
309
309
  this.state = "transcriber_closed";
310
- this.transcriber = null;
311
310
  }
312
311
  await this.startAssistantTurnIfReady();
313
312
  await this.drainOutboundFrames();
@@ -340,12 +339,15 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
340
339
  return;
341
340
  }
342
341
  case "error":
342
+ // Non-terminal: providers like OpenAI Whisper emit `error` for
343
+ // transient poll failures and continue streaming. Let `closed` /
344
+ // `final` drive turn lifecycle so we don't drain audio buffers or
345
+ // mark the turn cancelled prematurely.
343
346
  await this.sendFrame({
344
347
  type: "error",
345
348
  code: LiveVoiceProtocolErrorCode.InvalidField,
346
349
  message: event.message,
347
350
  });
348
- await this.finalizePendingTurn("stt_error");
349
351
  return;
350
352
  case "closed":
351
353
  if (!this.isClosed) {
@@ -470,16 +472,22 @@ export class LiveVoiceSession implements LiveVoiceSessionContract {
470
472
  },
471
473
  },
472
474
  onError: (message) => {
473
- if (!this.isActiveAssistantTurn(token)) return;
475
+ const activeTurn = this.activeAssistantTurn;
476
+ if (
477
+ !this.isActiveAssistantTurn(token) ||
478
+ activeTurn?.assistantCompleted
479
+ ) {
480
+ return;
481
+ }
474
482
  void (async () => {
475
483
  await this.sendFrame({
476
484
  type: "error",
477
485
  code: LiveVoiceProtocolErrorCode.InvalidField,
478
486
  message,
479
487
  });
480
- const activeTurn = this.activeAssistantTurn;
481
- if (activeTurn?.token !== token) return;
482
- await this.finalizeAssistantTurn(activeTurn, "cancelled", "error");
488
+ const currentTurn = this.activeAssistantTurn;
489
+ if (currentTurn?.token !== token) return;
490
+ await this.finalizeAssistantTurn(currentTurn, "cancelled", "error");
483
491
  })();
484
492
  },
485
493
  });
@@ -0,0 +1,258 @@
1
+ import { Database } from "bun:sqlite";
2
+ import { describe, expect, test } from "bun:test";
3
+
4
+ import { drizzle } from "drizzle-orm/bun-sqlite";
5
+
6
+ import {
7
+ createBookmark,
8
+ deleteBookmarkByMessageId,
9
+ listBookmarks,
10
+ } from "../bookmark-crud.js";
11
+ import type { DrizzleDb } from "../db-connection.js";
12
+ import { getSqliteFrom } from "../db-connection.js";
13
+ import { migrateMessageBookmarks } from "../migrations/242-message-bookmarks.js";
14
+ import * as schema from "../schema.js";
15
+
16
+ /**
17
+ * Recreate just enough of the conversations + messages tables to satisfy
18
+ * the bookmark JOIN and FK CASCADE behavior. The PR-2 schema test uses
19
+ * the same lightweight bootstrap.
20
+ */
21
+ function bootstrapMessageTables(raw: Database): void {
22
+ raw.exec(/*sql*/ `
23
+ CREATE TABLE IF NOT EXISTS memory_checkpoints (
24
+ key TEXT PRIMARY KEY,
25
+ value TEXT NOT NULL,
26
+ updated_at INTEGER NOT NULL
27
+ )
28
+ `);
29
+ raw.exec(/*sql*/ `
30
+ CREATE TABLE IF NOT EXISTS conversations (
31
+ id TEXT PRIMARY KEY,
32
+ title TEXT,
33
+ created_at INTEGER NOT NULL,
34
+ updated_at INTEGER NOT NULL
35
+ )
36
+ `);
37
+ raw.exec(/*sql*/ `
38
+ CREATE TABLE IF NOT EXISTS messages (
39
+ id TEXT PRIMARY KEY,
40
+ conversation_id TEXT NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
41
+ role TEXT NOT NULL,
42
+ content TEXT NOT NULL,
43
+ created_at INTEGER NOT NULL
44
+ )
45
+ `);
46
+ }
47
+
48
+ interface SeedOptions {
49
+ conversationId: string;
50
+ messageId: string;
51
+ conversationTitle?: string | null;
52
+ messageContent?: string;
53
+ messageRole?: string;
54
+ messageCreatedAt?: number;
55
+ }
56
+
57
+ function seedConversationAndMessage(raw: Database, opts: SeedOptions): void {
58
+ const now = Date.now();
59
+ raw
60
+ .query(
61
+ `INSERT INTO conversations (id, title, created_at, updated_at) VALUES (?, ?, ?, ?)`,
62
+ )
63
+ .run(opts.conversationId, opts.conversationTitle ?? "Example", now, now);
64
+ raw
65
+ .query(
66
+ `INSERT INTO messages (id, conversation_id, role, content, created_at) VALUES (?, ?, ?, ?, ?)`,
67
+ )
68
+ .run(
69
+ opts.messageId,
70
+ opts.conversationId,
71
+ opts.messageRole ?? "user",
72
+ opts.messageContent ?? "hello",
73
+ opts.messageCreatedAt ?? now,
74
+ );
75
+ }
76
+
77
+ function setupDb(): { db: DrizzleDb; raw: Database } {
78
+ const sqlite = new Database(":memory:");
79
+ sqlite.exec("PRAGMA journal_mode=WAL");
80
+ sqlite.exec("PRAGMA foreign_keys = ON");
81
+ const db = drizzle(sqlite, { schema });
82
+ const raw = getSqliteFrom(db);
83
+ bootstrapMessageTables(raw);
84
+ migrateMessageBookmarks(db);
85
+ return { db, raw };
86
+ }
87
+
88
+ describe("bookmark-crud", () => {
89
+ test("createBookmark is idempotent — second call returns the existing row", () => {
90
+ const { db, raw } = setupDb();
91
+ seedConversationAndMessage(raw, {
92
+ conversationId: "conv-1",
93
+ messageId: "msg-1",
94
+ });
95
+
96
+ const first = createBookmark(db, {
97
+ messageId: "msg-1",
98
+ conversationId: "conv-1",
99
+ });
100
+ const second = createBookmark(db, {
101
+ messageId: "msg-1",
102
+ conversationId: "conv-1",
103
+ });
104
+
105
+ expect(second.id).toBe(first.id);
106
+ expect(second.createdAt).toBe(first.createdAt);
107
+
108
+ const all = listBookmarks(db);
109
+ expect(all.length).toBe(1);
110
+ });
111
+
112
+ test("createBookmark returns the JOIN-shaped summary directly", () => {
113
+ const { db, raw } = setupDb();
114
+ seedConversationAndMessage(raw, {
115
+ conversationId: "conv-summary",
116
+ messageId: "msg-summary",
117
+ conversationTitle: "Title goes here",
118
+ messageContent: "summary body",
119
+ messageRole: "assistant",
120
+ });
121
+
122
+ const summary = createBookmark(db, {
123
+ messageId: "msg-summary",
124
+ conversationId: "conv-summary",
125
+ });
126
+
127
+ expect(summary.conversationTitle).toBe("Title goes here");
128
+ expect(summary.messagePreview).toBe("summary body");
129
+ expect(summary.messageRole).toBe("assistant");
130
+ expect(typeof summary.messageCreatedAt).toBe("number");
131
+ });
132
+
133
+ test("listBookmarks returns rows newest-first and includes joined fields", () => {
134
+ const { db, raw } = setupDb();
135
+ seedConversationAndMessage(raw, {
136
+ conversationId: "conv-A",
137
+ messageId: "msg-A",
138
+ conversationTitle: "Older",
139
+ messageContent: "first message",
140
+ messageRole: "user",
141
+ });
142
+ seedConversationAndMessage(raw, {
143
+ conversationId: "conv-B",
144
+ messageId: "msg-B",
145
+ conversationTitle: "Newer",
146
+ messageContent: "second message",
147
+ messageRole: "assistant",
148
+ });
149
+
150
+ // Force deterministic ordering by inserting bookmarks with explicit
151
+ // created_at values 1 ms apart.
152
+ raw
153
+ .query(
154
+ `INSERT INTO message_bookmarks (id, message_id, conversation_id, created_at) VALUES (?, ?, ?, ?)`,
155
+ )
156
+ .run("bm-A", "msg-A", "conv-A", 1000);
157
+ raw
158
+ .query(
159
+ `INSERT INTO message_bookmarks (id, message_id, conversation_id, created_at) VALUES (?, ?, ?, ?)`,
160
+ )
161
+ .run("bm-B", "msg-B", "conv-B", 2000);
162
+
163
+ const result = listBookmarks(db);
164
+ expect(result.map((r) => r.id)).toEqual(["bm-B", "bm-A"]);
165
+ expect(result[0]?.conversationTitle).toBe("Newer");
166
+ expect(result[0]?.messagePreview).toBe("second message");
167
+ expect(result[0]?.messageRole).toBe("assistant");
168
+ expect(result[1]?.conversationTitle).toBe("Older");
169
+ expect(result[1]?.messageRole).toBe("user");
170
+ });
171
+
172
+ test("listBookmarks excludes bookmarks whose conversation no longer exists (CASCADE)", () => {
173
+ const { db, raw } = setupDb();
174
+ seedConversationAndMessage(raw, {
175
+ conversationId: "conv-keep",
176
+ messageId: "msg-keep",
177
+ });
178
+ seedConversationAndMessage(raw, {
179
+ conversationId: "conv-drop",
180
+ messageId: "msg-drop",
181
+ });
182
+ createBookmark(db, {
183
+ messageId: "msg-keep",
184
+ conversationId: "conv-keep",
185
+ });
186
+ createBookmark(db, {
187
+ messageId: "msg-drop",
188
+ conversationId: "conv-drop",
189
+ });
190
+ expect(listBookmarks(db).length).toBe(2);
191
+
192
+ // Deleting the parent conversation cascades through messages → bookmarks.
193
+ raw.query(`DELETE FROM conversations WHERE id = ?`).run("conv-drop");
194
+
195
+ const remaining = listBookmarks(db);
196
+ expect(remaining.length).toBe(1);
197
+ expect(remaining[0]?.conversationId).toBe("conv-keep");
198
+ });
199
+
200
+ test("deleteBookmarkByMessageId removes the matching row", () => {
201
+ const { db, raw } = setupDb();
202
+ seedConversationAndMessage(raw, {
203
+ conversationId: "conv-d",
204
+ messageId: "msg-d",
205
+ });
206
+ createBookmark(db, { messageId: "msg-d", conversationId: "conv-d" });
207
+ expect(listBookmarks(db).length).toBe(1);
208
+
209
+ expect(deleteBookmarkByMessageId(db, "msg-d")).toBe(true);
210
+ expect(listBookmarks(db).length).toBe(0);
211
+ // Calling again with no row present returns false.
212
+ expect(deleteBookmarkByMessageId(db, "msg-d")).toBe(false);
213
+ });
214
+
215
+ test("messagePreview decodes JSON-serialized ContentBlock[] rows", () => {
216
+ const { db, raw } = setupDb();
217
+ seedConversationAndMessage(raw, {
218
+ conversationId: "conv-blocks",
219
+ messageId: "msg-blocks",
220
+ messageContent: JSON.stringify([
221
+ { type: "text", text: "Hello, can you help with…" },
222
+ ]),
223
+ messageRole: "user",
224
+ });
225
+
226
+ const summary = createBookmark(db, {
227
+ messageId: "msg-blocks",
228
+ conversationId: "conv-blocks",
229
+ });
230
+
231
+ // Without the decode step, this would render as the raw JSON literal
232
+ // (`[{"type":"text","text":"…"}]`) rather than the spoken text.
233
+ expect(summary.messagePreview).toBe("Hello, can you help with…");
234
+ const listed = listBookmarks(db);
235
+ expect(listed[0]?.messagePreview).toBe("Hello, can you help with…");
236
+ });
237
+
238
+ test("messagePreview concatenates multi-text blocks and drops non-text blocks", () => {
239
+ const { db, raw } = setupDb();
240
+ seedConversationAndMessage(raw, {
241
+ conversationId: "conv-multi",
242
+ messageId: "msg-multi",
243
+ messageContent: JSON.stringify([
244
+ { type: "text", text: "first paragraph" },
245
+ { type: "tool_use", id: "x", name: "noop", input: {} },
246
+ { type: "text", text: "second paragraph" },
247
+ ]),
248
+ messageRole: "assistant",
249
+ });
250
+
251
+ const summary = createBookmark(db, {
252
+ messageId: "msg-multi",
253
+ conversationId: "conv-multi",
254
+ });
255
+
256
+ expect(summary.messagePreview).toBe("first paragraph\nsecond paragraph");
257
+ });
258
+ });
@@ -0,0 +1,181 @@
1
+ import { Database } from "bun:sqlite";
2
+ import { describe, expect, test } from "bun:test";
3
+
4
+ import { drizzle } from "drizzle-orm/bun-sqlite";
5
+
6
+ import { getSqliteFrom } from "../db-connection.js";
7
+ import { migrateMessageBookmarks } from "../migrations/242-message-bookmarks.js";
8
+ import * as schema from "../schema.js";
9
+
10
+ interface ColumnRow {
11
+ name: string;
12
+ type: string;
13
+ notnull: number;
14
+ pk: number;
15
+ }
16
+
17
+ interface IndexInfoRow {
18
+ name: string;
19
+ unique: number;
20
+ }
21
+
22
+ interface CountRow {
23
+ n: number;
24
+ }
25
+
26
+ function createTestDb() {
27
+ const sqlite = new Database(":memory:");
28
+ sqlite.exec("PRAGMA journal_mode=WAL");
29
+ sqlite.exec("PRAGMA foreign_keys = ON");
30
+ return drizzle(sqlite, { schema });
31
+ }
32
+
33
+ function bootstrapCheckpointsTable(raw: Database): void {
34
+ raw.exec(/*sql*/ `
35
+ CREATE TABLE IF NOT EXISTS memory_checkpoints (
36
+ key TEXT PRIMARY KEY,
37
+ value TEXT NOT NULL,
38
+ updated_at INTEGER NOT NULL
39
+ )
40
+ `);
41
+ }
42
+
43
+ /**
44
+ * Recreate just enough of the conversations + messages tables to exercise
45
+ * CASCADE behavior. We only need the columns the FKs reference (id) plus
46
+ * NOT NULL columns required to insert a row.
47
+ */
48
+ function bootstrapMessageTables(raw: Database): void {
49
+ raw.exec(/*sql*/ `
50
+ CREATE TABLE IF NOT EXISTS conversations (
51
+ id TEXT PRIMARY KEY,
52
+ title TEXT,
53
+ created_at INTEGER NOT NULL,
54
+ updated_at INTEGER NOT NULL
55
+ )
56
+ `);
57
+ raw.exec(/*sql*/ `
58
+ CREATE TABLE IF NOT EXISTS messages (
59
+ id TEXT PRIMARY KEY,
60
+ conversation_id TEXT NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
61
+ role TEXT NOT NULL,
62
+ content TEXT NOT NULL,
63
+ created_at INTEGER NOT NULL
64
+ )
65
+ `);
66
+ }
67
+
68
+ function seedConversationAndMessage(
69
+ raw: Database,
70
+ conversationId: string,
71
+ messageId: string,
72
+ ): void {
73
+ const now = Date.now();
74
+ raw
75
+ .query(
76
+ `INSERT INTO conversations (id, title, created_at, updated_at) VALUES (?, ?, ?, ?)`,
77
+ )
78
+ .run(conversationId, "Example", now, now);
79
+ raw
80
+ .query(
81
+ `INSERT INTO messages (id, conversation_id, role, content, created_at) VALUES (?, ?, ?, ?, ?)`,
82
+ )
83
+ .run(messageId, conversationId, "user", "hello", now);
84
+ }
85
+
86
+ function insertBookmark(
87
+ raw: Database,
88
+ id: string,
89
+ messageId: string,
90
+ conversationId: string,
91
+ ): void {
92
+ raw
93
+ .query(
94
+ `INSERT INTO message_bookmarks (id, message_id, conversation_id, created_at) VALUES (?, ?, ?, ?)`,
95
+ )
96
+ .run(id, messageId, conversationId, Date.now());
97
+ }
98
+
99
+ function countBookmarks(raw: Database): number {
100
+ const row = raw
101
+ .query(`SELECT COUNT(*) AS n FROM message_bookmarks`)
102
+ .get() as CountRow | null;
103
+ return row?.n ?? 0;
104
+ }
105
+
106
+ describe("message_bookmarks schema migration", () => {
107
+ test("creates table with expected columns and indexes", () => {
108
+ const db = createTestDb();
109
+ const raw = getSqliteFrom(db);
110
+ bootstrapCheckpointsTable(raw);
111
+ bootstrapMessageTables(raw);
112
+
113
+ migrateMessageBookmarks(db);
114
+
115
+ const columns = raw
116
+ .query(`PRAGMA table_info(message_bookmarks)`)
117
+ .all() as ColumnRow[];
118
+ const byName = new Map(columns.map((c) => [c.name, c]));
119
+ expect(byName.get("id")?.pk).toBe(1);
120
+ expect(byName.get("id")?.type).toBe("TEXT");
121
+ expect(byName.get("message_id")?.notnull).toBe(1);
122
+ expect(byName.get("conversation_id")?.notnull).toBe(1);
123
+ expect(byName.get("created_at")?.notnull).toBe(1);
124
+ expect(byName.get("created_at")?.type).toBe("INTEGER");
125
+
126
+ const indexes = raw
127
+ .query(`PRAGMA index_list(message_bookmarks)`)
128
+ .all() as IndexInfoRow[];
129
+ const indexByName = new Map(indexes.map((i) => [i.name, i]));
130
+ const uniqIndex = indexByName.get("message_bookmarks_message_id_uniq");
131
+ expect(uniqIndex).toBeDefined();
132
+ expect(uniqIndex?.unique).toBe(1);
133
+ expect(indexByName.get("message_bookmarks_created_at_idx")).toBeDefined();
134
+ });
135
+
136
+ test("CASCADE removes bookmark when parent message is deleted", () => {
137
+ const db = createTestDb();
138
+ const raw = getSqliteFrom(db);
139
+ bootstrapCheckpointsTable(raw);
140
+ bootstrapMessageTables(raw);
141
+ migrateMessageBookmarks(db);
142
+
143
+ seedConversationAndMessage(raw, "conv-1", "msg-1");
144
+ insertBookmark(raw, "bm-1", "msg-1", "conv-1");
145
+ expect(countBookmarks(raw)).toBe(1);
146
+
147
+ raw.query(`DELETE FROM messages WHERE id = ?`).run("msg-1");
148
+ expect(countBookmarks(raw)).toBe(0);
149
+ });
150
+
151
+ test("CASCADE removes bookmark when parent conversation is deleted", () => {
152
+ const db = createTestDb();
153
+ const raw = getSqliteFrom(db);
154
+ bootstrapCheckpointsTable(raw);
155
+ bootstrapMessageTables(raw);
156
+ migrateMessageBookmarks(db);
157
+
158
+ seedConversationAndMessage(raw, "conv-2", "msg-2");
159
+ insertBookmark(raw, "bm-2", "msg-2", "conv-2");
160
+ expect(countBookmarks(raw)).toBe(1);
161
+
162
+ raw.query(`DELETE FROM conversations WHERE id = ?`).run("conv-2");
163
+ // Deleting the conversation cascades to messages, which cascades to
164
+ // bookmarks. Either FK alone would suffice; both fire here.
165
+ expect(countBookmarks(raw)).toBe(0);
166
+ });
167
+
168
+ test("unique constraint on message_id rejects a second bookmark", () => {
169
+ const db = createTestDb();
170
+ const raw = getSqliteFrom(db);
171
+ bootstrapCheckpointsTable(raw);
172
+ bootstrapMessageTables(raw);
173
+ migrateMessageBookmarks(db);
174
+
175
+ seedConversationAndMessage(raw, "conv-3", "msg-3");
176
+ insertBookmark(raw, "bm-3a", "msg-3", "conv-3");
177
+
178
+ expect(() => insertBookmark(raw, "bm-3b", "msg-3", "conv-3")).toThrow();
179
+ expect(countBookmarks(raw)).toBe(1);
180
+ });
181
+ });
@@ -0,0 +1,36 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { isBackgroundConversationType } from "../conversation-types.js";
4
+
5
+ describe("isBackgroundConversationType", () => {
6
+ test("returns true for background", () => {
7
+ expect(isBackgroundConversationType("background")).toBe(true);
8
+ });
9
+
10
+ test("returns true for scheduled", () => {
11
+ expect(isBackgroundConversationType("scheduled")).toBe(true);
12
+ });
13
+
14
+ test("returns false for standard", () => {
15
+ expect(isBackgroundConversationType("standard")).toBe(false);
16
+ });
17
+
18
+ test("returns false for null", () => {
19
+ expect(isBackgroundConversationType(null)).toBe(false);
20
+ });
21
+
22
+ test("returns false for undefined", () => {
23
+ expect(isBackgroundConversationType(undefined)).toBe(false);
24
+ });
25
+
26
+ test("returns false for empty string", () => {
27
+ expect(isBackgroundConversationType("")).toBe(false);
28
+ });
29
+
30
+ test.each(["chat", "foo", "BACKGROUND", "Scheduled", " background "])(
31
+ "returns false for unknown string %p",
32
+ (value) => {
33
+ expect(isBackgroundConversationType(value)).toBe(false);
34
+ },
35
+ );
36
+ });