@vellumai/assistant 0.7.3 → 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 (778) hide show
  1. package/AGENTS.md +11 -0
  2. package/ARCHITECTURE.md +29 -28
  3. package/Dockerfile +6 -4
  4. package/README.md +2 -2
  5. package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
  6. package/bun.lock +3 -0
  7. package/docker-entrypoint.sh +16 -0
  8. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  9. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  10. package/eslint.config.mjs +12 -0
  11. package/knip.json +3 -1
  12. package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
  13. package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
  14. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
  15. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
  16. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
  17. package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
  18. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  19. package/openapi.yaml +4126 -959
  20. package/package.json +5 -1
  21. package/scripts/generate-openapi.ts +52 -4
  22. package/scripts/sync-llm-catalog.ts +165 -0
  23. package/scripts/sync-web-search-catalog.ts +107 -0
  24. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  25. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  26. package/src/__tests__/annotate-risk-options.test.ts +291 -0
  27. package/src/__tests__/anthropic-provider.test.ts +92 -2
  28. package/src/__tests__/app-control-flow.test.ts +7 -0
  29. package/src/__tests__/approval-cascade.test.ts +8 -16
  30. package/src/__tests__/approval-routes-http.test.ts +6 -0
  31. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  32. package/src/__tests__/auto-analysis-end-to-end.test.ts +12 -25
  33. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  34. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  35. package/src/__tests__/btw-routes.test.ts +1 -0
  36. package/src/__tests__/call-constants.test.ts +10 -1
  37. package/src/__tests__/call-controller.test.ts +127 -0
  38. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  39. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  40. package/src/__tests__/channel-policy.test.ts +12 -0
  41. package/src/__tests__/checker.test.ts +89 -0
  42. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +88 -30
  43. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  44. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  45. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  46. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  47. package/src/__tests__/config-loader-platform-defaults.test.ts +345 -8
  48. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  49. package/src/__tests__/config-schema.test.ts +14 -3
  50. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  51. package/src/__tests__/config-set-route.test.ts +198 -0
  52. package/src/__tests__/config-watcher.test.ts +6 -0
  53. package/src/__tests__/contacts-tools.test.ts +51 -199
  54. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  55. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  56. package/src/__tests__/context-search-conversations-source.test.ts +42 -16
  57. package/src/__tests__/context-search-fanout.test.ts +20 -157
  58. package/src/__tests__/context-search-memory-source.test.ts +3 -26
  59. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
  60. package/src/__tests__/context-search-types.test.ts +7 -2
  61. package/src/__tests__/context-window-manager.test.ts +389 -1
  62. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -6
  63. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
  64. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -1
  65. package/src/__tests__/conversation-agent-loop.test.ts +3 -3
  66. package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
  67. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  68. package/src/__tests__/conversation-error.test.ts +38 -0
  69. package/src/__tests__/conversation-fork-crud.test.ts +241 -1
  70. package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
  71. package/src/__tests__/conversation-init.benchmark.test.ts +2 -1
  72. package/src/__tests__/conversation-lifecycle.test.ts +124 -0
  73. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  74. package/src/__tests__/conversation-process-callsite.test.ts +22 -7
  75. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -6
  76. package/src/__tests__/conversation-runtime-assembly.test.ts +19 -10
  77. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  78. package/src/__tests__/conversation-slash-unknown.test.ts +1 -6
  79. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
  80. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  81. package/src/__tests__/conversation-surfaces-data-persist.test.ts +73 -1
  82. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +59 -0
  83. package/src/__tests__/conversation-workspace-injection.test.ts +1 -7
  84. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -7
  85. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  86. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  87. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  88. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  89. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  90. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  91. package/src/__tests__/external-plugin-loader.test.ts +458 -0
  92. package/src/__tests__/filing-service.test.ts +25 -22
  93. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  94. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  95. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  96. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -34
  97. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  98. package/src/__tests__/heartbeat-service.test.ts +50 -233
  99. package/src/__tests__/history-repair.test.ts +89 -0
  100. package/src/__tests__/host-app-control-proxy.test.ts +109 -1
  101. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  102. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  103. package/src/__tests__/host-browser-routes.test.ts +325 -33
  104. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  105. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  106. package/src/__tests__/inference-profile-reaper.test.ts +154 -0
  107. package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
  108. package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
  109. package/src/__tests__/injector-chain.test.ts +24 -16
  110. package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
  111. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  112. package/src/__tests__/install-skill-routing.test.ts +2 -2
  113. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +169 -67
  114. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  115. package/src/__tests__/llm-catalog-parity.test.ts +146 -0
  116. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
  117. package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
  118. package/src/__tests__/llm-resolver.test.ts +46 -0
  119. package/src/__tests__/managed-profile-guard.test.ts +131 -2
  120. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  121. package/src/__tests__/mcp-cli.test.ts +182 -220
  122. package/src/__tests__/mcp-health-check.test.ts +56 -27
  123. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  124. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  125. package/src/__tests__/notification-decision-fallback.test.ts +91 -0
  126. package/src/__tests__/notification-decision-strategy.test.ts +22 -0
  127. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  128. package/src/__tests__/oauth-cli.test.ts +38 -1888
  129. package/src/__tests__/oauth-commands-routes.test.ts +711 -0
  130. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  131. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  132. package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
  133. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  134. package/src/__tests__/plugin-bootstrap.test.ts +31 -2
  135. package/src/__tests__/plugin-route-contribution.test.ts +31 -3
  136. package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
  137. package/src/__tests__/plugin-types.test.ts +13 -11
  138. package/src/__tests__/process-message-background-slack.test.ts +46 -0
  139. package/src/__tests__/profile-entry-status.test.ts +43 -0
  140. package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
  141. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  142. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  143. package/src/__tests__/relay-server.test.ts +164 -2
  144. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  145. package/src/__tests__/schedule-retry.test.ts +56 -4
  146. package/src/__tests__/schedule-routes.test.ts +104 -0
  147. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  148. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  149. package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
  150. package/src/__tests__/scheduler-wake.test.ts +0 -63
  151. package/src/__tests__/secret-allowlist.test.ts +1 -0
  152. package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
  153. package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
  154. package/src/__tests__/secret-response-routing.test.ts +7 -5
  155. package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
  156. package/src/__tests__/server-history-render.test.ts +82 -0
  157. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  158. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  159. package/src/__tests__/skill-include-graph.test.ts +31 -0
  160. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  161. package/src/__tests__/skill-load-tool.test.ts +42 -16
  162. package/src/__tests__/skills.test.ts +39 -0
  163. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  164. package/src/__tests__/suggestion-routes.test.ts +3 -3
  165. package/src/__tests__/sync-message-contract.test.ts +63 -0
  166. package/src/__tests__/task-scheduler.test.ts +88 -23
  167. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
  168. package/src/__tests__/tool-executor.test.ts +155 -0
  169. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  170. package/src/__tests__/usage-cli.test.ts +11 -73
  171. package/src/__tests__/user-plugin-loader.test.ts +145 -0
  172. package/src/__tests__/vercel-config.test.ts +168 -0
  173. package/src/__tests__/voice-session-bridge.test.ts +3 -0
  174. package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
  175. package/src/__tests__/web-search.test.ts +303 -2
  176. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  177. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
  178. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +153 -0
  179. package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
  180. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
  181. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  182. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  183. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  184. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  185. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  186. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  187. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +15 -27
  188. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  189. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  190. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  191. package/src/acp/resolve-agent.test.ts +25 -0
  192. package/src/acp/resolve-agent.ts +13 -2
  193. package/src/acp/session-manager.ts +14 -0
  194. package/src/agent/loop.ts +11 -0
  195. package/src/approvals/guardian-decision-primitive.ts +0 -13
  196. package/src/approvals/guardian-request-resolvers.ts +19 -102
  197. package/src/calls/call-constants.ts +5 -8
  198. package/src/calls/call-controller.ts +130 -67
  199. package/src/calls/relay-server.ts +42 -1
  200. package/src/calls/relay-setup-router.ts +36 -0
  201. package/src/calls/types.ts +1 -0
  202. package/src/calls/voice-session-bridge.ts +24 -5
  203. package/src/channels/config.ts +14 -1
  204. package/src/channels/types.ts +1 -0
  205. package/src/cli/AGENTS.md +164 -4
  206. package/src/cli/__tests__/notifications.test.ts +54 -0
  207. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  208. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  209. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  210. package/src/cli/commands/__tests__/changelog.test.ts +593 -0
  211. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  212. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  213. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  214. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  215. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  216. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  217. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  218. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  219. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  220. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  221. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  222. package/src/cli/commands/__tests__/status.test.ts +249 -0
  223. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  224. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  225. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  226. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  227. package/src/cli/commands/attachment.ts +8 -3
  228. package/src/cli/commands/audit.ts +95 -64
  229. package/src/cli/commands/auth.ts +61 -58
  230. package/src/cli/commands/avatar.ts +276 -390
  231. package/src/cli/commands/backup.ts +409 -505
  232. package/src/cli/commands/bash.ts +9 -5
  233. package/src/cli/commands/browser.ts +28 -9
  234. package/src/cli/commands/cache.ts +9 -4
  235. package/src/cli/commands/changelog.ts +414 -0
  236. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  237. package/src/cli/commands/clients.ts +8 -3
  238. package/src/cli/commands/completions.ts +9 -9
  239. package/src/cli/commands/config.ts +102 -72
  240. package/src/cli/commands/contacts.ts +575 -696
  241. package/src/cli/commands/conversations-defer.ts +17 -69
  242. package/src/cli/commands/conversations-import.ts +90 -253
  243. package/src/cli/commands/conversations.ts +346 -436
  244. package/src/cli/commands/credential-execution.ts +9 -6
  245. package/src/cli/commands/credentials.ts +456 -736
  246. package/src/cli/commands/domain.ts +128 -206
  247. package/src/cli/commands/email.ts +606 -794
  248. package/src/cli/commands/gateway.ts +8 -1
  249. package/src/cli/commands/image-generation.ts +157 -205
  250. package/src/cli/commands/inference-providers.ts +352 -0
  251. package/src/cli/commands/inference-session.ts +415 -0
  252. package/src/cli/commands/inference.ts +87 -65
  253. package/src/cli/commands/keys.ts +8 -3
  254. package/src/cli/commands/mcp.ts +103 -287
  255. package/src/cli/commands/memory-v2.ts +163 -517
  256. package/src/cli/commands/notifications.ts +33 -7
  257. package/src/cli/commands/oauth/apps.ts +292 -261
  258. package/src/cli/commands/oauth/connect.ts +182 -345
  259. package/src/cli/commands/oauth/disconnect.ts +16 -215
  260. package/src/cli/commands/oauth/index.ts +49 -45
  261. package/src/cli/commands/oauth/mode.ts +43 -199
  262. package/src/cli/commands/oauth/ping.ts +17 -125
  263. package/src/cli/commands/oauth/providers.ts +732 -921
  264. package/src/cli/commands/oauth/request.ts +60 -350
  265. package/src/cli/commands/oauth/shared.ts +11 -121
  266. package/src/cli/commands/oauth/status.ts +31 -121
  267. package/src/cli/commands/oauth/token.ts +13 -55
  268. package/src/cli/commands/pending.ts +19 -10
  269. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  270. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  271. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  272. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  273. package/src/cli/commands/platform/connect.ts +16 -80
  274. package/src/cli/commands/platform/disconnect.ts +14 -112
  275. package/src/cli/commands/platform/index.ts +177 -246
  276. package/src/cli/commands/routes.ts +153 -336
  277. package/src/cli/commands/sequence.ts +316 -360
  278. package/src/cli/commands/skills.ts +449 -671
  279. package/src/cli/commands/status.ts +58 -37
  280. package/src/cli/commands/stt.ts +94 -262
  281. package/src/cli/commands/task.ts +14 -40
  282. package/src/cli/commands/trust.ts +8 -3
  283. package/src/cli/commands/tts.ts +162 -167
  284. package/src/cli/commands/ui.ts +35 -42
  285. package/src/cli/commands/usage.ts +188 -126
  286. package/src/cli/commands/watchers.ts +8 -3
  287. package/src/cli/commands/webhooks.ts +99 -193
  288. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  289. package/src/cli/lib/daemon-credential-client.ts +4 -5
  290. package/src/cli/lib/nested-value.ts +44 -0
  291. package/src/cli/lib/open-browser.ts +36 -0
  292. package/src/cli/lib/register-command.ts +19 -0
  293. package/src/cli/lib/time-ago.ts +34 -0
  294. package/src/cli/program.ts +2 -4
  295. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  296. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  297. package/src/cli/utils/conversation-id.ts +30 -0
  298. package/src/cli/utils/parse-duration.ts +41 -0
  299. package/src/config/acp-defaults.test.ts +5 -1
  300. package/src/config/acp-defaults.ts +11 -4
  301. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  302. package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
  303. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  304. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  305. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  306. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  307. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  308. package/src/config/bundled-tool-registry.ts +0 -2
  309. package/src/config/feature-flag-registry.json +17 -17
  310. package/src/config/llm-resolver.ts +16 -1
  311. package/src/config/loader.ts +148 -33
  312. package/src/config/raw-config-utils.ts +2 -30
  313. package/src/config/schema.ts +4 -0
  314. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  315. package/src/config/schemas/call-site-catalog.ts +29 -7
  316. package/src/config/schemas/llm-request-logs.ts +57 -0
  317. package/src/config/schemas/llm.ts +52 -2
  318. package/src/config/schemas/memory-retrospective.ts +48 -0
  319. package/src/config/schemas/memory-v2.ts +33 -2
  320. package/src/config/schemas/memory.ts +4 -0
  321. package/src/config/schemas/services.ts +15 -12
  322. package/src/config/seed-inference-profiles.ts +195 -134
  323. package/src/contacts/contact-store.ts +0 -61
  324. package/src/context/window-manager.ts +191 -5
  325. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +111 -0
  326. package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
  327. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  328. package/src/daemon/approval-generators.ts +23 -29
  329. package/src/daemon/config-watcher.ts +2 -0
  330. package/src/daemon/conversation-agent-loop-handlers.ts +56 -0
  331. package/src/daemon/conversation-agent-loop.ts +140 -107
  332. package/src/daemon/conversation-error.ts +21 -0
  333. package/src/daemon/conversation-lifecycle.ts +68 -13
  334. package/src/daemon/conversation-process.ts +36 -19
  335. package/src/daemon/conversation-runtime-assembly.ts +14 -5
  336. package/src/daemon/conversation-slash.ts +175 -23
  337. package/src/daemon/conversation-store.ts +17 -10
  338. package/src/daemon/conversation-surfaces.ts +92 -26
  339. package/src/daemon/conversation-tool-setup.ts +33 -19
  340. package/src/daemon/conversation.ts +49 -10
  341. package/src/daemon/external-plugins-bootstrap.ts +18 -8
  342. package/src/daemon/guardian-action-generators.ts +7 -22
  343. package/src/daemon/handlers/config-model.ts +8 -126
  344. package/src/daemon/handlers/config-slack-channel.ts +10 -7
  345. package/src/daemon/handlers/config-vercel.ts +3 -1
  346. package/src/daemon/handlers/shared.ts +26 -0
  347. package/src/daemon/handlers/skills.ts +84 -5
  348. package/src/daemon/history-repair.ts +33 -6
  349. package/src/daemon/host-app-control-proxy.ts +44 -19
  350. package/src/daemon/host-bash-proxy.ts +85 -158
  351. package/src/daemon/host-browser-proxy.ts +97 -36
  352. package/src/daemon/host-cu-proxy.ts +1 -1
  353. package/src/daemon/host-file-proxy.ts +1 -1
  354. package/src/daemon/host-proxy-base.ts +13 -1
  355. package/src/daemon/host-proxy-preactivation.ts +25 -1
  356. package/src/daemon/host-transfer-proxy.ts +2 -2
  357. package/src/daemon/identity-helpers.ts +19 -0
  358. package/src/daemon/lifecycle.ts +128 -114
  359. package/src/daemon/meet-host-supervisor.ts +15 -15
  360. package/src/daemon/memory-v2-startup.ts +62 -14
  361. package/src/daemon/message-protocol.ts +6 -0
  362. package/src/daemon/message-types/bookmarks.ts +18 -0
  363. package/src/daemon/message-types/conversations.ts +12 -9
  364. package/src/daemon/message-types/messages.ts +28 -2
  365. package/src/daemon/message-types/sync.ts +60 -0
  366. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  367. package/src/daemon/pkb-reminder-builder.ts +21 -7
  368. package/src/daemon/process-message.ts +56 -23
  369. package/src/daemon/server.ts +23 -18
  370. package/src/daemon/shutdown-handlers.ts +0 -2
  371. package/src/daemon/tool-setup-types.ts +9 -0
  372. package/src/daemon/tool-side-effects.ts +6 -4
  373. package/src/daemon/wake-target-adapter.ts +11 -0
  374. package/src/documents/document-store.ts +35 -1
  375. package/src/export/transcript-formatter.ts +61 -2
  376. package/src/filing/filing-service.ts +42 -56
  377. package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
  378. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  379. package/src/heartbeat/heartbeat-service.ts +149 -128
  380. package/src/home/__tests__/feed-types.test.ts +63 -131
  381. package/src/home/__tests__/feed-writer.test.ts +77 -278
  382. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  383. package/src/home/feed-types.ts +19 -73
  384. package/src/home/feed-writer.ts +25 -156
  385. package/src/home/post-connect-feed.ts +1 -3
  386. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  387. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  388. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  389. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  390. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  391. package/src/ipc/assistant-server.ts +148 -42
  392. package/src/ipc/cli-client.ts +370 -50
  393. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  394. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  395. package/src/ipc/skill-routes/events.ts +30 -3
  396. package/src/ipc/skill-server.ts +99 -42
  397. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  398. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  399. package/src/live-voice/live-voice-session-manager.ts +11 -4
  400. package/src/live-voice/live-voice-session.ts +14 -6
  401. package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
  402. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  403. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  404. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  405. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +10 -57
  406. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  407. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  408. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
  409. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  410. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  411. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  412. package/src/memory/bookmark-crud.ts +179 -0
  413. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  414. package/src/memory/context-search/agent-protocol.ts +5 -1
  415. package/src/memory/context-search/agent-runner.ts +60 -85
  416. package/src/memory/context-search/limits.ts +1 -4
  417. package/src/memory/context-search/search.ts +23 -113
  418. package/src/memory/context-search/sources/conversations.ts +18 -6
  419. package/src/memory/context-search/sources/memory-v2.ts +40 -31
  420. package/src/memory/context-search/sources/memory.ts +9 -2
  421. package/src/memory/context-search/sources/workspace.ts +13 -10
  422. package/src/memory/context-search/types.ts +1 -1
  423. package/src/memory/conversation-bootstrap.ts +11 -0
  424. package/src/memory/conversation-crud.ts +312 -10
  425. package/src/memory/conversation-queries.ts +9 -5
  426. package/src/memory/conversation-title-service.ts +1 -0
  427. package/src/memory/conversation-types.ts +16 -0
  428. package/src/memory/db-init.ts +14 -0
  429. package/src/memory/embedding-backend.ts +2 -1
  430. package/src/memory/embedding-runtime-manager.ts +1 -2
  431. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +104 -61
  432. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
  433. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  434. package/src/memory/graph/conversation-graph-memory.ts +108 -14
  435. package/src/memory/graph/extraction.ts +4 -0
  436. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  437. package/src/memory/graph/graph-search.test.ts +6 -5
  438. package/src/memory/graph/graph-search.ts +3 -4
  439. package/src/memory/graph/retriever.test.ts +12 -7
  440. package/src/memory/graph/retriever.ts +4 -5
  441. package/src/memory/graph/tool-handlers.ts +20 -11
  442. package/src/memory/graph/tools.ts +48 -9
  443. package/src/memory/indexer.ts +18 -2
  444. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +120 -6
  445. package/src/memory/jobs/embed-concept-page.ts +261 -89
  446. package/src/memory/jobs-store.ts +51 -1
  447. package/src/memory/jobs-worker.ts +60 -7
  448. package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
  449. package/src/memory/llm-request-log-source-local.ts +26 -0
  450. package/src/memory/llm-request-log-source.ts +97 -0
  451. package/src/memory/llm-request-log-store.ts +1 -1
  452. package/src/memory/memory-retrospective-constants.ts +13 -0
  453. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  454. package/src/memory/memory-retrospective-job.ts +351 -0
  455. package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
  456. package/src/memory/memory-retrospective-state.ts +162 -0
  457. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  458. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  459. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  460. package/src/memory/message-content.ts +38 -1
  461. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  462. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  463. package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
  464. package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
  465. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  466. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  467. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  468. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  469. package/src/memory/migrations/243-provider-connections.ts +68 -0
  470. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  471. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  472. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  473. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  474. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  475. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  476. package/src/memory/migrations/index.ts +7 -0
  477. package/src/memory/pkb/pkb-search.test.ts +6 -5
  478. package/src/memory/pkb/pkb-search.ts +4 -5
  479. package/src/memory/published-pages-store.ts +16 -0
  480. package/src/memory/qdrant-client.ts +3 -0
  481. package/src/memory/schema/bookmarks.ts +38 -0
  482. package/src/memory/schema/conversations.ts +2 -0
  483. package/src/memory/schema/index.ts +2 -0
  484. package/src/memory/schema/inference.ts +29 -0
  485. package/src/memory/schema/memory-core.ts +9 -0
  486. package/src/memory/search/semantic.ts +5 -9
  487. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  488. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  489. package/src/memory/v2/__tests__/activation.test.ts +46 -9
  490. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  491. package/src/memory/v2/__tests__/consolidation-job.test.ts +140 -163
  492. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  493. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  494. package/src/memory/v2/__tests__/injection.test.ts +768 -33
  495. package/src/memory/v2/__tests__/migration.test.ts +7 -3
  496. package/src/memory/v2/__tests__/page-index.test.ts +277 -0
  497. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  498. package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
  499. package/src/memory/v2/__tests__/qdrant.test.ts +382 -9
  500. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  501. package/src/memory/v2/__tests__/router.test.ts +516 -0
  502. package/src/memory/v2/__tests__/sim.test.ts +163 -8
  503. package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
  504. package/src/memory/v2/__tests__/static-context.test.ts +8 -35
  505. package/src/memory/v2/__tests__/sweep-job.test.ts +114 -33
  506. package/src/memory/v2/activation-store.ts +34 -5
  507. package/src/memory/v2/activation.ts +40 -27
  508. package/src/memory/v2/backfill-jobs.ts +17 -84
  509. package/src/memory/v2/consolidation-job.ts +92 -86
  510. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  511. package/src/memory/v2/injection.ts +466 -115
  512. package/src/memory/v2/migration.ts +117 -20
  513. package/src/memory/v2/page-index.ts +191 -0
  514. package/src/memory/v2/page-store.ts +42 -0
  515. package/src/memory/v2/prompts/consolidation.ts +14 -7
  516. package/src/memory/v2/prompts/router.ts +192 -0
  517. package/src/memory/v2/qdrant.ts +307 -133
  518. package/src/memory/v2/reranker.ts +14 -7
  519. package/src/memory/v2/router.ts +322 -0
  520. package/src/memory/v2/sim.ts +88 -34
  521. package/src/memory/v2/skill-store.ts +118 -29
  522. package/src/memory/v2/static-context.ts +20 -17
  523. package/src/memory/v2/sweep-job.ts +127 -102
  524. package/src/memory/v2/types.ts +16 -5
  525. package/src/memory/validation.ts +13 -0
  526. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  527. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  528. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  529. package/src/notifications/adapters/platform.ts +171 -0
  530. package/src/notifications/conversation-pairing.ts +2 -2
  531. package/src/notifications/copy-composer.ts +61 -12
  532. package/src/notifications/decision-engine.ts +46 -0
  533. package/src/notifications/destination-resolver.ts +21 -0
  534. package/src/notifications/emit-signal.ts +28 -1
  535. package/src/notifications/home-feed-side-effect.ts +111 -0
  536. package/src/notifications/signal.ts +5 -0
  537. package/src/permissions/checker.ts +12 -0
  538. package/src/permissions/gateway-threshold-reader.ts +116 -8
  539. package/src/permissions/ipc-risk-types.ts +2 -0
  540. package/src/permissions/prompter.ts +86 -96
  541. package/src/permissions/secret-prompter.ts +31 -31
  542. package/src/plugin-api/index.ts +13 -0
  543. package/src/plugin-api/package.json +12 -0
  544. package/src/plugin-api/types.ts +62 -0
  545. package/src/plugins/defaults/injectors.ts +20 -5
  546. package/src/plugins/external-plugin-loader.ts +294 -0
  547. package/src/plugins/types.ts +46 -30
  548. package/src/plugins/user-loader.ts +64 -41
  549. package/src/proactive-artifact/job.test.ts +63 -8
  550. package/src/proactive-artifact/job.ts +20 -2
  551. package/src/proactive-artifact/message-copy.ts +18 -1
  552. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  553. package/src/proactive-artifact/trigger-state.ts +4 -0
  554. package/src/prompts/__tests__/system-prompt.test.ts +105 -0
  555. package/src/prompts/system-prompt.ts +22 -1
  556. package/src/prompts/templates/SOUL.md +13 -28
  557. package/src/prompts/update-bulletin-job.ts +61 -73
  558. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  559. package/src/providers/__tests__/inference.test.ts +288 -0
  560. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  561. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  562. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  563. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  564. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  565. package/src/providers/anthropic/client.ts +95 -26
  566. package/src/providers/call-site-routing.ts +94 -16
  567. package/src/providers/connection-resolution.ts +163 -0
  568. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  569. package/src/providers/inference/adapter-factory.ts +173 -0
  570. package/src/providers/inference/auth.ts +112 -0
  571. package/src/providers/inference/backfill.ts +196 -0
  572. package/src/providers/inference/connections.ts +356 -0
  573. package/src/providers/inference/resolve-auth.ts +65 -0
  574. package/src/providers/model-catalog.ts +104 -6
  575. package/src/providers/openai/responses-provider.ts +4 -2
  576. package/src/providers/provider-env-vars.ts +17 -7
  577. package/src/providers/provider-secret-catalog.ts +49 -30
  578. package/src/providers/provider-send-message.ts +41 -20
  579. package/src/providers/registry.ts +143 -159
  580. package/src/providers/retry.ts +18 -10
  581. package/src/providers/search-provider-catalog.ts +121 -0
  582. package/src/runtime/AGENTS.md +18 -5
  583. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  584. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  585. package/src/runtime/actor-trust-resolver.ts +32 -10
  586. package/src/runtime/agent-wake.ts +35 -6
  587. package/src/runtime/assistant-event-hub.ts +3 -85
  588. package/src/runtime/auth/route-policy.ts +304 -8
  589. package/src/runtime/auth/same-actor.ts +2 -0
  590. package/src/runtime/background-job-runner.ts +339 -0
  591. package/src/runtime/btw-sidechain.ts +1 -0
  592. package/src/runtime/channel-approvals.ts +3 -2
  593. package/src/runtime/guardian-reply-router.ts +0 -10
  594. package/src/runtime/http-router.ts +36 -1
  595. package/src/runtime/http-server.ts +31 -5
  596. package/src/runtime/http-types.ts +2 -0
  597. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  598. package/src/runtime/middleware/request-logger.ts +62 -1
  599. package/src/runtime/pending-interactions.ts +19 -15
  600. package/src/runtime/pre-first-message-gate.ts +83 -0
  601. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  602. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
  603. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  604. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
  605. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
  606. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  607. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  608. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
  609. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  610. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  611. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  612. package/src/runtime/routes/acp-routes.ts +10 -8
  613. package/src/runtime/routes/app-management-routes.ts +228 -3
  614. package/src/runtime/routes/approval-routes.ts +7 -21
  615. package/src/runtime/routes/audit-routes.ts +43 -0
  616. package/src/runtime/routes/auth-routes.ts +72 -0
  617. package/src/runtime/routes/avatar-routes.ts +273 -20
  618. package/src/runtime/routes/backup-routes.ts +406 -2
  619. package/src/runtime/routes/bookmark-routes.ts +154 -0
  620. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  621. package/src/runtime/routes/consolidation-routes.ts +8 -9
  622. package/src/runtime/routes/contact-routes.ts +0 -160
  623. package/src/runtime/routes/conversation-cli-routes.ts +192 -0
  624. package/src/runtime/routes/conversation-management-routes.ts +30 -43
  625. package/src/runtime/routes/conversation-query-routes.ts +373 -82
  626. package/src/runtime/routes/conversation-routes.ts +31 -10
  627. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  628. package/src/runtime/routes/credential-routes.ts +540 -0
  629. package/src/runtime/routes/debug-bash-routes.ts +2 -0
  630. package/src/runtime/routes/debug-routes.ts +2 -2
  631. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  632. package/src/runtime/routes/domain-routes.ts +167 -0
  633. package/src/runtime/routes/email-routes.ts +603 -0
  634. package/src/runtime/routes/errors.ts +2 -2
  635. package/src/runtime/routes/events-routes.ts +192 -0
  636. package/src/runtime/routes/filing-routes.ts +2 -3
  637. package/src/runtime/routes/home-feed-routes.ts +6 -78
  638. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  639. package/src/runtime/routes/host-browser-routes.ts +103 -22
  640. package/src/runtime/routes/http-adapter.ts +2 -0
  641. package/src/runtime/routes/identity-routes.ts +5 -0
  642. package/src/runtime/routes/image-generation-routes.ts +99 -0
  643. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
  644. package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
  645. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  646. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -7
  647. package/src/runtime/routes/index.ts +36 -0
  648. package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
  649. package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
  650. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  651. package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
  652. package/src/runtime/routes/inference-send-routes.ts +115 -0
  653. package/src/runtime/routes/integrations/twilio.ts +1 -0
  654. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  655. package/src/runtime/routes/memory-item-routes.test.ts +3 -9
  656. package/src/runtime/routes/memory-item-routes.ts +5 -6
  657. package/src/runtime/routes/memory-v2-routes.ts +105 -404
  658. package/src/runtime/routes/notification-routes.ts +2 -0
  659. package/src/runtime/routes/oauth-apps.ts +112 -7
  660. package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
  661. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  662. package/src/runtime/routes/oauth-providers.ts +298 -8
  663. package/src/runtime/routes/platform-routes.ts +336 -0
  664. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  665. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  666. package/src/runtime/routes/playground/state.ts +2 -1
  667. package/src/runtime/routes/publish-routes.ts +221 -0
  668. package/src/runtime/routes/schedule-routes.ts +82 -0
  669. package/src/runtime/routes/sequence-routes.ts +291 -0
  670. package/src/runtime/routes/settings-routes.ts +2 -10
  671. package/src/runtime/routes/skills-routes.ts +31 -1
  672. package/src/runtime/routes/stt-routes.ts +240 -3
  673. package/src/runtime/routes/surface-action-routes.ts +43 -7
  674. package/src/runtime/routes/tts-routes.ts +67 -0
  675. package/src/runtime/routes/types.ts +32 -0
  676. package/src/runtime/routes/user-routes-cli.ts +243 -0
  677. package/src/runtime/routes/webhook-routes.ts +165 -0
  678. package/src/runtime/sync/resource-sync-events.ts +25 -0
  679. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  680. package/src/runtime/sync/sync-publisher.ts +21 -0
  681. package/src/schedule/scheduler.ts +200 -123
  682. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  683. package/src/security/secret-patterns.ts +3 -0
  684. package/src/sequence/engine.ts +38 -40
  685. package/src/skills/include-graph.ts +35 -13
  686. package/src/subagent/manager.ts +20 -15
  687. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  688. package/src/tools/browser/browser-execution.ts +15 -4
  689. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  690. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  691. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  692. package/src/tools/browser/cdp-client/factory.ts +66 -5
  693. package/src/tools/browser/runtime-check.ts +77 -0
  694. package/src/tools/document/document-tool.ts +20 -0
  695. package/src/tools/executor.ts +18 -2
  696. package/src/tools/memory/register.test.ts +10 -8
  697. package/src/tools/memory/register.ts +9 -1
  698. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  699. package/src/tools/network/web-search.ts +280 -37
  700. package/src/tools/permission-checker.ts +28 -5
  701. package/src/tools/skills/load.ts +24 -20
  702. package/src/tools/subagent/spawn.ts +3 -3
  703. package/src/tools/terminal/shell.ts +44 -0
  704. package/src/tools/tool-name-aliases.ts +19 -0
  705. package/src/tools/types.ts +19 -1
  706. package/src/usage/attribution.ts +3 -2
  707. package/src/util/pricing.ts +86 -160
  708. package/src/watcher/__tests__/engine.test.ts +301 -0
  709. package/src/watcher/constants.ts +7 -0
  710. package/src/watcher/engine.ts +90 -90
  711. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  712. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  713. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
  714. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +4 -62
  715. package/src/workspace/migrations/069-seed-onboarding-threads.ts +34 -0
  716. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
  717. package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
  718. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
  719. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
  720. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  721. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  722. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  723. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  724. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  725. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  726. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  727. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  728. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  729. package/src/workspace/migrations/registry.ts +28 -0
  730. package/src/workspace/migrations/runner.ts +13 -2
  731. package/src/workspace/migrations/types.ts +13 -3
  732. package/src/workspace/provider-commit-message-generator.ts +3 -2
  733. package/src/__tests__/context-search-pkb-source.test.ts +0 -492
  734. package/src/__tests__/credentials-cli.test.ts +0 -1225
  735. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  736. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  737. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  738. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  739. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  740. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  741. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  742. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  743. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  744. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  745. package/src/cli/commands/autonomy.ts +0 -365
  746. package/src/cli/commands/memory.ts +0 -424
  747. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -1201
  748. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  749. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  750. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  751. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  752. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  753. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  754. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  755. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  756. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  757. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  758. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  759. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  760. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  761. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  762. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  763. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  764. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  765. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  766. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  767. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  768. package/src/home/assistant-feed-authoring.ts +0 -135
  769. package/src/home/emit-feed-event.ts +0 -169
  770. package/src/home/feed-scheduler.ts +0 -281
  771. package/src/home/platform-gmail-digest.ts +0 -163
  772. package/src/home/rewrite-command-preview.ts +0 -66
  773. package/src/home/rewrite-feed-title.ts +0 -58
  774. package/src/home/rollup-producer.ts +0 -426
  775. package/src/memory/admin.ts +0 -326
  776. package/src/memory/context-search/sources/pkb.ts +0 -477
  777. package/src/memory/graph/compaction.ts +0 -299
  778. /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Tests for the per-route success-suppression behavior of `withRequestLogging`.
3
+ *
4
+ * Default behavior (log every request) is exercised end-to-end by the rest
5
+ * of the runtime test suite; the cases here pin down the opt-in
6
+ * `RouteLoggingConfig.silenceSuccessAfter` path.
7
+ */
8
+
9
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
10
+
11
+ interface CapturedLog {
12
+ level: "info" | "warn" | "error";
13
+ status?: number;
14
+ }
15
+
16
+ const captured: CapturedLog[] = [];
17
+
18
+ // Replace the module-level logger so the tests see exactly which lines
19
+ // would have been emitted, without going through pino at all. The mock
20
+ // must be installed BEFORE `request-logger.ts` is loaded — otherwise the
21
+ // real `getLogger()` is captured at module init. Dynamic-import the
22
+ // module after the mock to enforce that ordering.
23
+ mock.module("../../../util/logger.js", () => ({
24
+ getLogger: () => ({
25
+ info: (data: unknown) =>
26
+ captured.push({ level: "info", status: extractStatus(data) }),
27
+ warn: (data: unknown) =>
28
+ captured.push({ level: "warn", status: extractStatus(data) }),
29
+ error: (data: unknown) =>
30
+ captured.push({ level: "error", status: extractStatus(data) }),
31
+ debug: () => {},
32
+ trace: () => {},
33
+ fatal: () => {},
34
+ }),
35
+ }));
36
+
37
+ const {
38
+ _resetRequestLoggingCountersForTests,
39
+ withRequestLogging,
40
+ }: typeof import("../request-logger.js") = await import(
41
+ "../request-logger.js"
42
+ );
43
+
44
+ type RequestLogMetadata = import("../request-logger.js").RequestLogMetadata;
45
+
46
+ function extractStatus(data: unknown): number | undefined {
47
+ if (data && typeof data === "object" && "status" in data) {
48
+ const s = (data as { status?: unknown }).status;
49
+ return typeof s === "number" ? s : undefined;
50
+ }
51
+ return undefined;
52
+ }
53
+
54
+ function reqAt(path: string): Request {
55
+ return new Request(`http://localhost${path}`);
56
+ }
57
+
58
+ function ok(): Promise<Response> {
59
+ return Promise.resolve(new Response(null, { status: 200 }));
60
+ }
61
+
62
+ function status(code: number): () => Promise<Response> {
63
+ return () => Promise.resolve(new Response(null, { status: code }));
64
+ }
65
+
66
+ describe("withRequestLogging", () => {
67
+ beforeEach(() => {
68
+ captured.length = 0;
69
+ _resetRequestLoggingCountersForTests();
70
+ });
71
+
72
+ afterEach(() => {
73
+ _resetRequestLoggingCountersForTests();
74
+ });
75
+
76
+ test("logs every successful request when no metadata is supplied", async () => {
77
+ for (let i = 0; i < 10; i++) {
78
+ await withRequestLogging(reqAt("/v1/anything"), ok);
79
+ }
80
+ expect(captured.filter((c) => c.level === "info").length).toBe(10);
81
+ expect(captured.every((c) => c.status === 200)).toBe(true);
82
+ });
83
+
84
+ test("silences successes after the configured threshold", async () => {
85
+ const meta: RequestLogMetadata = {
86
+ counterKey: "health",
87
+ config: { silenceSuccessAfter: 3 },
88
+ };
89
+ for (let i = 0; i < 8; i++) {
90
+ await withRequestLogging(reqAt("/v1/health"), ok, meta);
91
+ }
92
+ // First 3 successes log; remaining 5 are suppressed.
93
+ const infos = captured.filter((c) => c.level === "info");
94
+ expect(infos.length).toBe(3);
95
+ expect(infos.every((c) => c.status === 200)).toBe(true);
96
+ });
97
+
98
+ test("4xx and 5xx always log, even after success suppression kicks in", async () => {
99
+ const meta: RequestLogMetadata = {
100
+ counterKey: "health",
101
+ config: { silenceSuccessAfter: 2 },
102
+ };
103
+ // Burn through the success budget.
104
+ await withRequestLogging(reqAt("/v1/health"), ok, meta);
105
+ await withRequestLogging(reqAt("/v1/health"), ok, meta);
106
+ // 3rd, 4th, 5th: should be suppressed for 200, but 4xx/5xx must log.
107
+ await withRequestLogging(reqAt("/v1/health"), ok, meta); // suppressed
108
+ await withRequestLogging(reqAt("/v1/health"), status(404), meta); // warn
109
+ await withRequestLogging(reqAt("/v1/health"), status(500), meta); // error
110
+ await withRequestLogging(reqAt("/v1/health"), ok, meta); // suppressed
111
+
112
+ expect(captured.filter((c) => c.level === "info").length).toBe(2);
113
+ expect(captured.filter((c) => c.level === "warn").length).toBe(1);
114
+ expect(captured.filter((c) => c.level === "error").length).toBe(1);
115
+ });
116
+
117
+ test("counters are isolated per counterKey", async () => {
118
+ const metaA: RequestLogMetadata = {
119
+ counterKey: "route-a",
120
+ config: { silenceSuccessAfter: 2 },
121
+ };
122
+ const metaB: RequestLogMetadata = {
123
+ counterKey: "route-b",
124
+ config: { silenceSuccessAfter: 2 },
125
+ };
126
+ // Exhaust A's budget.
127
+ await withRequestLogging(reqAt("/v1/a"), ok, metaA);
128
+ await withRequestLogging(reqAt("/v1/a"), ok, metaA);
129
+ await withRequestLogging(reqAt("/v1/a"), ok, metaA); // suppressed
130
+ // B still has full budget.
131
+ await withRequestLogging(reqAt("/v1/b"), ok, metaB);
132
+ await withRequestLogging(reqAt("/v1/b"), ok, metaB);
133
+
134
+ expect(captured.filter((c) => c.level === "info").length).toBe(4);
135
+ });
136
+
137
+ test("a route with `silenceSuccessAfter: 0` is treated as opt-out", async () => {
138
+ // Zero is reserved as "no threshold configured" so config authors can
139
+ // toggle the field without re-deriving the default. The route still
140
+ // logs every request.
141
+ const meta: RequestLogMetadata = {
142
+ counterKey: "zero",
143
+ config: { silenceSuccessAfter: 0 },
144
+ };
145
+ for (let i = 0; i < 4; i++) {
146
+ await withRequestLogging(reqAt("/v1/zero"), ok, meta);
147
+ }
148
+ expect(captured.filter((c) => c.level === "info").length).toBe(4);
149
+ });
150
+
151
+ test("errors thrown by the handler propagate and emit an error line", async () => {
152
+ const meta: RequestLogMetadata = {
153
+ counterKey: "boom",
154
+ config: { silenceSuccessAfter: 1 },
155
+ };
156
+ const thrower = () => Promise.reject(new Error("boom"));
157
+ await expect(
158
+ withRequestLogging(reqAt("/v1/boom"), thrower, meta),
159
+ ).rejects.toThrow("boom");
160
+ expect(captured.filter((c) => c.level === "error").length).toBe(1);
161
+ });
162
+ });
@@ -3,14 +3,73 @@
3
3
  *
4
4
  * Logs method, path, status, and latency for every request to aid
5
5
  * debugging client issues. Uses structured Pino logging.
6
+ *
7
+ * Routes can opt in to suppress the per-request INFO line after a confirmed
8
+ * run of N successful responses by declaring `logging.silenceSuccessAfter`
9
+ * on the route definition. Warning (4xx) and error (5xx) lines are always
10
+ * emitted regardless of this setting.
6
11
  */
7
12
 
8
13
  import { getLogger } from "../../util/logger.js";
14
+ import type { RouteLoggingConfig } from "../routes/types.js";
9
15
 
10
16
  const log = getLogger("http-request");
11
17
 
12
18
  const UNKNOWN = "unknown" as const;
13
19
 
20
+ /**
21
+ * Optional metadata supplied by the caller (typically resolved from the
22
+ * matched route) that lets the middleware adjust its per-request log
23
+ * behavior — e.g. suppressing success logs after a threshold for noisy
24
+ * polling endpoints like `/v1/health`.
25
+ */
26
+ export interface RequestLogMetadata {
27
+ /**
28
+ * Stable identifier used as the success-suppression counter key. Two
29
+ * requests with the same counterKey share a single counter (so all
30
+ * variants of a parameterized route share suppression state).
31
+ */
32
+ counterKey: string;
33
+ config: RouteLoggingConfig;
34
+ }
35
+
36
+ // Module-level counter map. Tracks the cumulative number of successful
37
+ // (status < 400) responses logged per `counterKey`. Once the count passes
38
+ // the route's `silenceSuccessAfter` threshold, further successful responses
39
+ // skip the per-request INFO log line. Counters never reset within a
40
+ // process — the behavior is "the route worked, stop spamming".
41
+ const successCounters = new Map<string, number>();
42
+
43
+ /**
44
+ * Test-only: reset the per-route success counters. Production code never
45
+ * calls this; tests use it to isolate suppression state between cases.
46
+ */
47
+ export function _resetRequestLoggingCountersForTests(): void {
48
+ successCounters.clear();
49
+ }
50
+
51
+ /**
52
+ * Decide whether the success log line should be suppressed for this
53
+ * request. Returns true when the counter has already reached the
54
+ * configured threshold. Pure read — the counter is only mutated when
55
+ * the log line is actually emitted.
56
+ */
57
+ function shouldSuppressSuccess(meta: RequestLogMetadata | undefined): boolean {
58
+ if (!meta) return false;
59
+ const threshold = meta.config.silenceSuccessAfter;
60
+ if (threshold === undefined || threshold <= 0) return false;
61
+ const current = successCounters.get(meta.counterKey) ?? 0;
62
+ return current >= threshold;
63
+ }
64
+
65
+ /** Record that we just emitted a success log line for this route. */
66
+ function bumpSuccessCounter(meta: RequestLogMetadata | undefined): void {
67
+ if (!meta) return;
68
+ if (meta.config.silenceSuccessAfter === undefined) return;
69
+ const current = successCounters.get(meta.counterKey) ?? 0;
70
+ successCounters.set(meta.counterKey, current + 1);
71
+ }
72
+
14
73
  /**
15
74
  * Wrap a request handler to log request metadata and response timing.
16
75
  *
@@ -20,6 +79,7 @@ const UNKNOWN = "unknown" as const;
20
79
  export async function withRequestLogging(
21
80
  req: Request,
22
81
  handler: () => Promise<Response>,
82
+ meta?: RequestLogMetadata,
23
83
  ): Promise<Response> {
24
84
  const start = performance.now();
25
85
  const url = new URL(req.url);
@@ -66,8 +126,9 @@ export async function withRequestLogging(
66
126
  log.error(logData, `${method} ${path} -> ${status} (${latencyMs}ms)`);
67
127
  } else if (status >= 400) {
68
128
  log.warn(logData, `${method} ${path} -> ${status} (${latencyMs}ms)`);
69
- } else {
129
+ } else if (!shouldSuppressSuccess(meta)) {
70
130
  log.info(logData, `${method} ${path} -> ${status} (${latencyMs}ms)`);
131
+ bumpSuccessCounter(meta);
71
132
  }
72
133
 
73
134
  return response;
@@ -3,22 +3,22 @@
3
3
  * confirmation, secret, host_bash, host_file, host_cu, host_browser, and
4
4
  * host_transfer interactions.
5
5
  *
6
- * For confirmation_request and secret_request, the onEvent callback in
7
- * assistant-event-hub registers the interaction here.
6
+ * All request types self-register with their full RPC lifecycle state
7
+ * (resolve/reject callbacks, timer, abort detach):
8
8
  *
9
- * For host proxy interactions (host_bash, host_file, host_cu, host_browser,
10
- * host_transfer), the proxy itself registers with full RPC lifecycle state
11
- * (resolve/reject callbacks, timer, abort detach). This eliminates the
12
- * per-proxy `private pending` maps — all pending state lives here.
9
+ * - Host proxies (host_bash, host_file, host_cu, host_browser,
10
+ * host_app_control, host_transfer): register in request(), using
11
+ * rpcResolve/rpcReject/timer/detachAbort/metadata.
12
+ *
13
+ * - Prompters (PermissionPrompter, SecretPrompter): register in prompt(),
14
+ * using promptResolve/promptReject/timer/toolUseId.
13
15
  *
14
16
  * Standalone HTTP endpoints (/v1/confirm, /v1/secret, /v1/trust-rules,
15
- * /v1/host-bash-result, /v1/host-file-result, /v1/host-cu-result,
16
- * /v1/host-browser-result) look up the conversation from this tracker to
17
+ * /v1/host-bash-result, etc.) look up the conversation from this tracker to
17
18
  * resolve the interaction.
18
19
  */
19
20
 
20
21
  import type { UserDecision } from "../permissions/types.js";
21
- import type { ToolExecutionResult } from "../tools/types.js";
22
22
 
23
23
  export interface ConfirmationDetails {
24
24
  toolName: string;
@@ -68,18 +68,20 @@ export interface PendingInteraction {
68
68
  */
69
69
  targetActorPrincipalId?: string;
70
70
 
71
- // -- RPC lifecycle (populated by host proxies) --
71
+ // -- RPC lifecycle (all interaction types) --
72
72
 
73
- /** Resolve the caller's Promise with a tool execution result. */
74
- rpcResolve?: (result: ToolExecutionResult) => void;
73
+ /** Resolve the caller's Promise. Typed as unknown; callers cast at use sites. */
74
+ rpcResolve?: (value: unknown) => void;
75
75
  /** Reject the caller's Promise with an error. */
76
76
  rpcReject?: (err: Error) => void;
77
- /** Proxy-side timeout timer. Cleared on resolve/abort/dispose. */
77
+ /** Timeout timer. Cleared automatically on resolve(). */
78
78
  timer?: ReturnType<typeof setTimeout>;
79
79
  /** Detach the abort listener from the caller's signal. No-op when no signal was passed. */
80
80
  detachAbort?: () => void;
81
81
  /** Proxy-specific metadata (e.g. timeoutSec for bash, operation/path for file). */
82
82
  metadata?: Record<string, unknown>;
83
+ /** toolUseId associated with a confirmation_request (PermissionPrompter). */
84
+ toolUseId?: string;
83
85
  }
84
86
 
85
87
  const pending = new Map<string, PendingInteraction>();
@@ -144,7 +146,8 @@ export function getByConversation(
144
146
  * proxy timer would fire with a spurious timeout error.
145
147
  */
146
148
  export function removeByConversation(conversationId: string): void {
147
- for (const [requestId, interaction] of pending) {
149
+ // Snapshot keys to avoid mutation-during-iteration.
150
+ for (const [requestId, interaction] of [...pending]) {
148
151
  if (
149
152
  interaction.conversationId === conversationId &&
150
153
  interaction.kind !== "host_bash" &&
@@ -155,7 +158,8 @@ export function removeByConversation(conversationId: string): void {
155
158
  interaction.kind !== "host_transfer" &&
156
159
  interaction.kind !== "acp_confirmation"
157
160
  ) {
158
- pending.delete(requestId);
161
+ // resolve() clears the stored timer and detaches abort listeners.
162
+ resolve(requestId);
159
163
  }
160
164
  }
161
165
  }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Gate for LLM-touching background work that runs between daemon start and
3
+ * the user's first interaction.
4
+ *
5
+ * ## Why this exists
6
+ *
7
+ * Cloud-hosted assistants are served from a warm pool: the daemon boots,
8
+ * background services initialize, and the image waits to be claimed by a
9
+ * real user. Before the user claims an image, no provider credentials are
10
+ * registered, so any background job that tries to call the LLM fails — and
11
+ * those failure rows persist in the local SQLite database, becoming visible
12
+ * in the conversation sidebar the moment the user hatches.
13
+ *
14
+ * ## The probe
15
+ *
16
+ * `hasReceivedUserMessage()` returns `true` once at least one
17
+ * `role='user'` message exists in a `conversation_type='standard'`
18
+ * conversation. Background / scheduled conversations don't count — they're
19
+ * exactly the noise we're trying to suppress.
20
+ *
21
+ * The result is cached in-process after the first `true` because the flag
22
+ * is monotonic: a user message, once present, is never deleted in a way
23
+ * that should re-open the gate. (Even a destructive sweep that wipes all
24
+ * messages would still want background jobs paused until a real user
25
+ * interaction resumes, so re-querying on miss is the correct behavior.)
26
+ *
27
+ * Callers should treat the gate as advisory + defense-in-depth: prefer to
28
+ * skip at the service level (heartbeat, update-bulletin, etc.) so no run
29
+ * row / conversation row is created at all, and rely on the gate inside
30
+ * `runBackgroundJob` as the universal backstop.
31
+ */
32
+ import { rawGet } from "../memory/raw-query.js";
33
+ import { getLogger } from "../util/logger.js";
34
+
35
+ const log = getLogger("pre-first-message-gate");
36
+
37
+ let cachedHasUserMessage = false;
38
+
39
+ /**
40
+ * Returns `true` if the local store has ever recorded a user-authored
41
+ * message in a standard conversation.
42
+ *
43
+ * Cheap: indexed lookup with `LIMIT 1`. After the first `true` result the
44
+ * answer is cached in-process and subsequent calls are O(1).
45
+ *
46
+ * On query error the function logs a warning and returns `false` — the
47
+ * conservative interpretation is "we can't prove the user has interacted,
48
+ * so don't fire background work."
49
+ */
50
+ export function hasReceivedUserMessage(): boolean {
51
+ if (cachedHasUserMessage) return true;
52
+
53
+ try {
54
+ const row = rawGet<{ one: number }>(
55
+ `SELECT 1 AS one FROM messages m
56
+ JOIN conversations c ON m.conversation_id = c.id
57
+ WHERE m.role = 'user'
58
+ AND c.conversation_type = 'standard'
59
+ LIMIT 1`,
60
+ );
61
+ if (row != null) {
62
+ cachedHasUserMessage = true;
63
+ return true;
64
+ }
65
+ return false;
66
+ } catch (err) {
67
+ log.warn(
68
+ { err },
69
+ "hasReceivedUserMessage: query failed; treating as not-yet-received so background work stays paused",
70
+ );
71
+ return false;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Test-only reset of the in-process cache. Real code paths must never
77
+ * call this — the cache is monotonic by design.
78
+ *
79
+ * @internal
80
+ */
81
+ export function _resetPreFirstMessageGateCacheForTests(): void {
82
+ cachedHasUserMessage = false;
83
+ }
@@ -720,10 +720,17 @@ describe("handleBackupVerify", () => {
720
720
  // ---------------------------------------------------------------------------
721
721
 
722
722
  describe("ROUTES", () => {
723
- test("registers four routes with the expected endpoint+method pairs", () => {
723
+ test("registers routes with the expected endpoint+method pairs", () => {
724
724
  const pairs = ROUTES.map((d) => `${d.method} ${d.endpoint}`).sort();
725
725
  expect(pairs).toEqual([
726
+ "GET backup/destinations",
727
+ "GET backup/status",
726
728
  "GET backups",
729
+ "POST backup/destinations/add",
730
+ "POST backup/destinations/remove",
731
+ "POST backup/destinations/set-encrypt",
732
+ "POST backup/disable",
733
+ "POST backup/enable",
727
734
  "POST backups/create",
728
735
  "POST backups/restore",
729
736
  "POST backups/verify",
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Tests for the bookmark route handlers in `bookmark-routes.ts`.
3
+ *
4
+ * Covers:
5
+ * - POST + GET round-trip
6
+ * - POST idempotency (no duplicate row, same id returned)
7
+ * - POST FK validation (unknown messageId → 4xx)
8
+ * - DELETE /by-message/:messageId
9
+ * - SSE event publication on create AND delete
10
+ */
11
+
12
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
13
+
14
+ // Capture publish() invocations so the tests can assert on emitted events
15
+ // without spinning up real SSE infrastructure.
16
+ const publishCalls: unknown[] = [];
17
+
18
+ mock.module("../../assistant-event-hub.js", () => ({
19
+ assistantEventHub: {
20
+ publish: async (event: unknown) => {
21
+ publishCalls.push(event);
22
+ },
23
+ subscribe: () => () => {},
24
+ },
25
+ }));
26
+
27
+ import { getDb } from "../../../memory/db-connection.js";
28
+ import { initializeDb } from "../../../memory/db-init.js";
29
+ import {
30
+ conversations,
31
+ messageBookmarks,
32
+ messages,
33
+ } from "../../../memory/schema.js";
34
+ import { ROUTES as BOOKMARK_ROUTES } from "../bookmark-routes.js";
35
+ import type { RouteDefinition, RouteHandlerArgs } from "../types.js";
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // DB bootstrap
39
+ // ---------------------------------------------------------------------------
40
+
41
+ initializeDb();
42
+
43
+ // ---------------------------------------------------------------------------
44
+ // Helpers
45
+ // ---------------------------------------------------------------------------
46
+
47
+ function findHandler(operationId: string): RouteDefinition["handler"] {
48
+ const route = BOOKMARK_ROUTES.find((r) => r.operationId === operationId);
49
+ if (!route) throw new Error(`Route ${operationId} not found`);
50
+ return route.handler;
51
+ }
52
+
53
+ const listHandler = findHandler("bookmarks_list");
54
+ const createHandler = findHandler("bookmarks_create");
55
+ const deleteByMessageHandler = findHandler("bookmarks_delete_by_message");
56
+
57
+ function clearDb(): void {
58
+ const db = getDb();
59
+ // Bookmarks first (FK), then messages, then conversations.
60
+ db.delete(messageBookmarks).run();
61
+ db.delete(messages).run();
62
+ db.delete(conversations).run();
63
+ }
64
+
65
+ function seedConversationAndMessage(opts: {
66
+ conversationId: string;
67
+ messageId: string;
68
+ conversationTitle?: string;
69
+ messageContent?: string;
70
+ messageRole?: string;
71
+ }): void {
72
+ const now = Date.now();
73
+ const db = getDb();
74
+ db.insert(conversations)
75
+ .values({
76
+ id: opts.conversationId,
77
+ title: opts.conversationTitle ?? "Test conversation",
78
+ createdAt: now,
79
+ updatedAt: now,
80
+ source: "test",
81
+ conversationType: "standard",
82
+ memoryScopeId: "default",
83
+ })
84
+ .run();
85
+ db.insert(messages)
86
+ .values({
87
+ id: opts.messageId,
88
+ conversationId: opts.conversationId,
89
+ role: opts.messageRole ?? "user",
90
+ content: opts.messageContent ?? "hello world",
91
+ createdAt: now,
92
+ })
93
+ .run();
94
+ }
95
+
96
+ async function call(
97
+ handler: RouteDefinition["handler"],
98
+ args: RouteHandlerArgs,
99
+ ): Promise<unknown> {
100
+ return await handler(args);
101
+ }
102
+
103
+ interface EventEnvelope {
104
+ message: { type: string; [key: string]: unknown };
105
+ }
106
+
107
+ function publishedTypes(): string[] {
108
+ return publishCalls.map((e) => (e as EventEnvelope).message.type);
109
+ }
110
+
111
+ beforeEach(() => {
112
+ clearDb();
113
+ publishCalls.length = 0;
114
+ });
115
+
116
+ // ---------------------------------------------------------------------------
117
+ // Tests
118
+ // ---------------------------------------------------------------------------
119
+
120
+ describe("bookmark routes", () => {
121
+ test("POST then GET returns the new bookmark", async () => {
122
+ seedConversationAndMessage({
123
+ conversationId: "conv-1",
124
+ messageId: "msg-1",
125
+ messageContent: "first message body",
126
+ messageRole: "assistant",
127
+ });
128
+
129
+ const created = (await call(createHandler, {
130
+ body: { messageId: "msg-1", conversationId: "conv-1" },
131
+ })) as { id: string; messageId: string; messagePreview: string };
132
+
133
+ expect(created.messageId).toBe("msg-1");
134
+ expect(created.messagePreview).toBe("first message body");
135
+
136
+ const listed = (await call(listHandler, {})) as {
137
+ bookmarks: Array<{ id: string; messageId: string }>;
138
+ };
139
+
140
+ expect(listed.bookmarks).toHaveLength(1);
141
+ expect(listed.bookmarks[0]?.id).toBe(created.id);
142
+ expect(listed.bookmarks[0]?.messageId).toBe("msg-1");
143
+ });
144
+
145
+ test("POST is idempotent — second call returns same id, no duplicate row", async () => {
146
+ seedConversationAndMessage({
147
+ conversationId: "conv-2",
148
+ messageId: "msg-2",
149
+ });
150
+
151
+ const first = (await call(createHandler, {
152
+ body: { messageId: "msg-2", conversationId: "conv-2" },
153
+ })) as { id: string };
154
+ const second = (await call(createHandler, {
155
+ body: { messageId: "msg-2", conversationId: "conv-2" },
156
+ })) as { id: string };
157
+
158
+ expect(second.id).toBe(first.id);
159
+
160
+ const listed = (await call(listHandler, {})) as {
161
+ bookmarks: unknown[];
162
+ };
163
+ expect(listed.bookmarks).toHaveLength(1);
164
+ });
165
+
166
+ test("POST with non-existent messageId returns a 4xx", async () => {
167
+ seedConversationAndMessage({
168
+ conversationId: "conv-3",
169
+ messageId: "msg-3",
170
+ });
171
+
172
+ let caught: unknown = null;
173
+ try {
174
+ await call(createHandler, {
175
+ body: { messageId: "missing-msg", conversationId: "conv-3" },
176
+ });
177
+ } catch (err) {
178
+ caught = err;
179
+ }
180
+
181
+ expect(caught).not.toBeNull();
182
+ // RouteError subclasses (BadRequestError, NotFoundError, …) carry a
183
+ // `statusCode` field that the HTTP adapter forwards to the wire — assert
184
+ // that we throw something in the 4xx range without coupling to the
185
+ // specific subclass.
186
+ const statusCode = (caught as { statusCode?: number }).statusCode;
187
+ expect(
188
+ typeof statusCode === "number" && statusCode >= 400 && statusCode < 500,
189
+ ).toBe(true);
190
+ });
191
+
192
+ test("DELETE /by-message/:messageId removes the row", async () => {
193
+ seedConversationAndMessage({
194
+ conversationId: "conv-5",
195
+ messageId: "msg-5",
196
+ });
197
+ await call(createHandler, {
198
+ body: { messageId: "msg-5", conversationId: "conv-5" },
199
+ });
200
+
201
+ const result = (await call(deleteByMessageHandler, {
202
+ pathParams: { messageId: "msg-5" },
203
+ })) as { success: boolean };
204
+ expect(result.success).toBe(true);
205
+
206
+ const listed = (await call(listHandler, {})) as { bookmarks: unknown[] };
207
+ expect(listed.bookmarks).toHaveLength(0);
208
+ });
209
+
210
+ test("publishes SSE events on create AND delete", async () => {
211
+ seedConversationAndMessage({
212
+ conversationId: "conv-6",
213
+ messageId: "msg-6",
214
+ });
215
+
216
+ const created = (await call(createHandler, {
217
+ body: { messageId: "msg-6", conversationId: "conv-6" },
218
+ })) as { id: string };
219
+
220
+ // Publishes are fire-and-forget (`.catch(...)`), so let any pending
221
+ // microtasks settle before asserting.
222
+ await new Promise((r) => setTimeout(r, 0));
223
+
224
+ expect(publishedTypes()).toEqual(["bookmark.created"]);
225
+ const createdEvent = publishCalls[0] as EventEnvelope;
226
+ expect(createdEvent.message.type).toBe("bookmark.created");
227
+ expect(
228
+ (createdEvent.message as unknown as { bookmark: { id: string } }).bookmark
229
+ .id,
230
+ ).toBe(created.id);
231
+
232
+ publishCalls.length = 0;
233
+
234
+ await call(deleteByMessageHandler, { pathParams: { messageId: "msg-6" } });
235
+ await new Promise((r) => setTimeout(r, 0));
236
+
237
+ expect(publishedTypes()).toEqual(["bookmark.deleted"]);
238
+ const deletedEvent = publishCalls[0] as EventEnvelope;
239
+ expect(
240
+ (deletedEvent.message as unknown as { messageId?: string }).messageId,
241
+ ).toBe("msg-6");
242
+ });
243
+
244
+ test("DELETE on a non-existent messageId does not publish", async () => {
245
+ await call(deleteByMessageHandler, {
246
+ pathParams: { messageId: "does-not-exist" },
247
+ });
248
+ await new Promise((r) => setTimeout(r, 0));
249
+ expect(publishCalls).toHaveLength(0);
250
+ });
251
+ });