@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
@@ -1,28 +1,39 @@
1
1
  /**
2
- * User plugin loader — discovers plugins under `<workspaceDir>/plugins/*` and
3
- * invokes each plugin's registration side effect via a dynamic import.
2
+ * User plugin loader — discovers plugins under `<workspaceDir>/plugins/*` via
3
+ * one of two paths, gated by the contents of each candidate directory.
4
4
  *
5
- * A user plugin is a directory under `getWorkspaceDir()/plugins/` that contains a
6
- * `register.ts` (or `register.js` after compilation). The file is expected to
7
- * call {@link registerPlugin} at import time so the plugin ends up in the
8
- * registry before {@link bootstrapPlugins} runs during daemon startup.
5
+ * **External plugin framework path** (`package.json` present **and** no
6
+ * `register.{ts,js}`): the harness delegates to {@link loadExternalPlugin},
7
+ * which builds a `Plugin` from the directory's interface dirs (`hooks/`,
8
+ * `tools/`) and registers it directly. This path is opt-in by the plugin
9
+ * author and currently experimental — see
10
+ * `assistant/src/plugins/external-plugin-loader.ts` for the full
11
+ * convention.
12
+ *
13
+ * **Legacy path** (`register.{ts,js}` present): the file is dynamic-imported
14
+ * and expected to call {@link registerPlugin} at import time as a side
15
+ * effect, populating the registry before {@link bootstrapPlugins} runs.
16
+ *
17
+ * The legacy path takes precedence when a directory contains both
18
+ * `package.json` and `register.{ts,js}` — a migration-friendly default
19
+ * that keeps existing plugins (including the in-repo `examples/plugins/echo`
20
+ * reference) working unchanged while we iterate the external-plugin
21
+ * convention. A directory matching neither path is skipped silently.
9
22
  *
10
23
  * The loader deliberately:
11
24
  *
12
25
  * - Uses `getWorkspaceDir()` so each instance loads its own plugin set
13
26
  * when `VELLUM_WORKSPACE_DIR` is set.
14
- * - Prefers `register.js` over `register.ts` when both exist (compiled plugins
15
- * always win; this matches how `bun`/Node consumers resolve modules at
16
- * runtime in the compiled binary).
17
- * - Treats any error from the dynamic import as a per-plugin isolation
18
- * boundary: the offending directory is logged with `"Failed to load user
19
- * plugin <dir>: <err>"` and the loader moves on to the next candidate.
20
- * One bad user plugin must not crash the daemon.
21
- * - Bounds each dynamic import with a timeout
22
- * ({@link USER_PLUGIN_IMPORT_TIMEOUT_MS}) so a plugin whose top-level
23
- * `await` hangs or whose module evaluation never resolves cannot stall
24
- * daemon startup. Timed-out plugins are logged and skipped just like
25
- * thrown-error plugins.
27
+ * - Prefers `.js` over `.ts` per surface file (compiled-binary semantics).
28
+ * The external loader applies the same rule per surface file; the
29
+ * legacy path picks between `register.js` and `register.ts`.
30
+ * - Treats any error from a plugin load as a per-plugin isolation
31
+ * boundary. {@link loadExternalPlugin} owns its own try/catch/timeout;
32
+ * the legacy path is wrapped here. One bad user plugin must not crash
33
+ * the daemon.
34
+ * - Bounds each plugin load with {@link USER_PLUGIN_IMPORT_TIMEOUT_MS}
35
+ * so a plugin whose top-level `await` hangs or whose module evaluation
36
+ * never resolves cannot stall daemon startup.
26
37
  *
27
38
  * Call order relative to the rest of the plugin system:
28
39
  *
@@ -39,6 +50,7 @@ import { pathToFileURL } from "node:url";
39
50
 
40
51
  import { getLogger } from "../util/logger.js";
41
52
  import { getWorkspaceDir } from "../util/platform.js";
53
+ import { loadExternalPlugin } from "./external-plugin-loader.js";
42
54
  import { closeRegistration } from "./registry.js";
43
55
 
44
56
  const log = getLogger("user-plugin-loader");
@@ -55,9 +67,10 @@ const log = getLogger("user-plugin-loader");
55
67
  const USER_PLUGIN_IMPORT_TIMEOUT_MS = 10_000;
56
68
 
57
69
  /**
58
- * Scan `getWorkspaceDir()/plugins/` for subdirectories containing a
59
- * `register.{ts,js}` file, and dynamic-import each one so the module's
60
- * side-effecting {@link registerPlugin} calls populate the registry.
70
+ * Scan `getWorkspaceDir()/plugins/` for subdirectories, then dispatch each
71
+ * one to the external loader (if `package.json` is present and there is no
72
+ * `register.{ts,js}`) or the legacy side-effect importer (if
73
+ * `register.{ts,js}` is present).
61
74
  *
62
75
  * Invariants:
63
76
  *
@@ -68,24 +81,24 @@ const USER_PLUGIN_IMPORT_TIMEOUT_MS = 10_000;
68
81
  * - Does not return plugin instances. The registry is the single source of
69
82
  * truth for who got registered, and the caller inspects it directly.
70
83
  *
71
- * Must be called after first-party plugin side-effect imports have run and
72
- * before {@link bootstrapPlugins} — see the module docstring for the ordering
73
- * contract.
84
+ * Caller responsibilities:
85
+ *
86
+ * - Must be invoked exactly once during daemon startup, before
87
+ * `bootstrapPlugins()` walks the registry.
88
+ * - Holds no locks during the import — bun's dynamic `import()` resolution
89
+ * is concurrency-safe.
74
90
  */
75
91
  export async function loadUserPlugins(
76
92
  options: { importTimeoutMs?: number } = {},
77
93
  ): Promise<void> {
78
- const importTimeoutMs =
79
- options.importTimeoutMs ?? USER_PLUGIN_IMPORT_TIMEOUT_MS;
94
+ const importTimeoutMs = options.importTimeoutMs ?? USER_PLUGIN_IMPORT_TIMEOUT_MS;
95
+
80
96
  const pluginsDir = join(getWorkspaceDir(), "plugins");
97
+
81
98
  if (!existsSync(pluginsDir)) {
82
- log.debug(
83
- { pluginsDir },
84
- "loadUserPlugins: no plugins directory skipping",
85
- );
86
- // Close the registration window even on the fast path so a late arrival
87
- // from an unrelated source (e.g. a mis-ordered static import) still can't
88
- // slip in after bootstrap walks the registry.
99
+ // The clean-install case. Closing the registration window keeps the
100
+ // post-loader invariant uniform: `bootstrapPlugins()` may rely on the
101
+ // registry being final by the time `loadUserPlugins()` resolves.
89
102
  closeRegistration();
90
103
  return;
91
104
  }
@@ -118,10 +131,12 @@ export async function loadUserPlugins(
118
131
  }
119
132
  if (!stats.isDirectory()) continue;
120
133
 
121
- // Prefer the compiled `register.js` over the TypeScript source. In the
122
- // bun-compiled daemon binary only the compiled file can be imported;
123
- // in development both may exist, in which case resolving the compiled
124
- // artifact matches how the runtime would behave in production.
134
+ // Path selection: the legacy side-effect path takes precedence when
135
+ // both a `register.{ts,js}` and a `package.json` are present.
136
+ // Migration-friendly: any plugin in the wild today that happens to
137
+ // ship a `package.json` keeps loading via its existing register entry.
138
+ // The external-plugin path only fires when the directory is
139
+ // unambiguously the new convention.
125
140
  const jsPath = join(pluginDir, "register.js");
126
141
  const tsPath = join(pluginDir, "register.ts");
127
142
  let registerPath: string | undefined;
@@ -130,16 +145,24 @@ export async function loadUserPlugins(
130
145
  } else if (existsSync(tsPath)) {
131
146
  registerPath = tsPath;
132
147
  }
133
- if (!registerPath) {
148
+
149
+ if (registerPath === undefined) {
150
+ // External plugin framework path. `loadExternalPlugin` owns its own
151
+ // try/catch + timeout, so a `continue` is the entire branch here.
152
+ if (existsSync(join(pluginDir, "package.json"))) {
153
+ await loadExternalPlugin(pluginDir, { importTimeoutMs });
154
+ continue;
155
+ }
134
156
  log.debug(
135
157
  { pluginDir },
136
- "loadUserPlugins: no register.{ts,js} — skipping",
158
+ "loadUserPlugins: no register.{ts,js} or package.json — skipping",
137
159
  );
138
160
  continue;
139
161
  }
140
162
 
141
- // `import()` with a `file://` URL works identically under Node and bun
142
- // and sidesteps platform-specific absolute-path quirks on Windows.
163
+ // Legacy side-effect import path. `import()` with a `file://` URL
164
+ // works identically under Node and bun and sidesteps platform-specific
165
+ // absolute-path quirks on Windows.
143
166
  const moduleUrl = pathToFileURL(registerPath).href;
144
167
  let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
145
168
  try {
@@ -51,9 +51,15 @@ mock.module("../providers/provider-send-message.js", () => ({
51
51
 
52
52
  // rawAll mock
53
53
  let rawAllRows: Array<{ role: string; content: string }> = [];
54
+ let rawAllLastSql = "";
55
+ let rawAllLastArgs: unknown[] = [];
54
56
 
55
57
  mock.module("../memory/raw-query.js", () => ({
56
- rawAll: () => rawAllRows,
58
+ rawAll: (sql: string, ...args: unknown[]) => {
59
+ rawAllLastSql = sql;
60
+ rawAllLastArgs = args;
61
+ return rawAllRows;
62
+ },
57
63
  rawRun: () => 0,
58
64
  }));
59
65
 
@@ -279,6 +285,8 @@ function resetState() {
279
285
  copyResponse = "";
280
286
  providerSendCalls = [];
281
287
  rawAllRows = [];
288
+ rawAllLastSql = "";
289
+ rawAllLastArgs = [];
282
290
  bootstrapCalls = [];
283
291
  processMessageCalls = [];
284
292
  processMessageShouldThrow = false;
@@ -645,7 +653,7 @@ describe("runProactiveArtifactJob", () => {
645
653
  });
646
654
 
647
655
  describe("Transcript collection", () => {
648
- test("dual-condition transcript query uses userMessageCutoff and assistantMessageId", async () => {
656
+ test("transcript query is scoped to the triggering conversation", async () => {
649
657
  rawAllRows = defaultTranscript;
650
658
  decisionResponse = decisionNo;
651
659
 
@@ -656,8 +664,8 @@ describe("runProactiveArtifactJob", () => {
656
664
  broadcastMessage: mockBroadcast,
657
665
  });
658
666
 
659
- // The rawAll mock captures all calls; verify the decision was called
660
- // (proving transcript was collected and passed to decision)
667
+ expect(rawAllLastSql).toContain("AND m.conversation_id = ?");
668
+ expect(rawAllLastArgs).toEqual(["conv-1", 5000, "asst-msg-99"]);
661
669
  expect(
662
670
  providerSendCalls.some(
663
671
  (c) => c.callSite === "proactiveArtifactDecision",
@@ -57,12 +57,16 @@ export async function runProactiveArtifactJob(params: {
57
57
  let buildSucceeded = false;
58
58
  try {
59
59
  // ── Collect transcript (bounded) ────────────────────────────────
60
+ // The trigger window is workspace-wide, but raw transcript context sent to
61
+ // the LLM must stay scoped to the conversation that fired the job.
60
62
  const rows = rawAll<{ role: string; content: string }>(
61
63
  `SELECT m.role, m.content FROM messages m
62
64
  JOIN conversations c ON m.conversation_id = c.id
63
65
  WHERE c.conversation_type = 'standard'
66
+ AND m.conversation_id = ?
64
67
  AND (m.created_at <= ? OR m.id = ?)
65
68
  ORDER BY m.created_at ASC`,
69
+ params.conversationId,
66
70
  params.userMessageCutoff,
67
71
  params.assistantMessageId ?? "",
68
72
  );
@@ -123,6 +123,15 @@ describe("trigger-state", () => {
123
123
  expect(getUserMessageCountUpTo(350)).toBe(3);
124
124
  });
125
125
 
126
+ test("counts user messages across standard conversations", () => {
127
+ seedUserMessage(100);
128
+ seedUserMessage(200);
129
+ seedUserMessage(300);
130
+ seedUserMessage(400);
131
+
132
+ expect(getUserMessageCountUpTo(400)).toBe(4);
133
+ });
134
+
126
135
  test("caps at 11 due to LIMIT", () => {
127
136
  for (let i = 1; i <= 15; i++) {
128
137
  seedUserMessage(i * 100);
@@ -16,6 +16,10 @@ function guardPath(): string {
16
16
 
17
17
  /**
18
18
  * Count user messages in standard conversations with created_at <= beforeOrAt.
19
+ * This is intentionally cross-conversation: a user who starts a new thread
20
+ * early should still enter the proactive artifact trigger window. The job
21
+ * separately scopes raw transcript context to the triggering conversation.
22
+ *
19
23
  * LIMIT caps scan cost since we only care about thresholds up to TRIGGER_MAX.
20
24
  */
21
25
  export function getUserMessageCountUpTo(beforeOrAt: number): number {
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Tests for the Background Conversation gating in buildSystemPrompt.
3
+ *
4
+ * The Background Conversation guidance is gated on
5
+ * `options.isBackgroundConversation === true`. Interactive (default)
6
+ * conversations must pay zero token cost — the section must be entirely
7
+ * absent unless the flag is explicitly true.
8
+ */
9
+
10
+ import { mkdirSync } from "node:fs";
11
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
12
+
13
+ const TEST_DIR = process.env.VELLUM_WORKSPACE_DIR!;
14
+
15
+ const noopLogger: Record<string, unknown> = new Proxy(
16
+ {} as Record<string, unknown>,
17
+ {
18
+ get: (_target, prop) => (prop === "child" ? () => noopLogger : () => {}),
19
+ },
20
+ );
21
+
22
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
23
+ const realLogger = require("../../util/logger.js");
24
+ mock.module("../../util/logger.js", () => ({
25
+ ...realLogger,
26
+ getLogger: () => noopLogger,
27
+ getCliLogger: () => noopLogger,
28
+ truncateForLog: (v: string) => v,
29
+ initLogger: () => {},
30
+ pruneOldLogFiles: () => 0,
31
+ }));
32
+
33
+ const mockLoadedConfig: Record<string, unknown> = {};
34
+
35
+ mock.module("../../config/loader.js", () => ({
36
+ getConfig: () => ({
37
+ ui: {},
38
+ services: {
39
+ inference: {
40
+ mode: "your-own",
41
+ provider: "anthropic",
42
+ model: "claude-opus-4-6",
43
+ },
44
+ "image-generation": {
45
+ mode: "your-own",
46
+ provider: "gemini",
47
+ model: "gemini-3.1-flash-image-preview",
48
+ },
49
+ "web-search": { mode: "your-own", provider: "inference-provider-native" },
50
+ },
51
+ }),
52
+ loadConfig: () => mockLoadedConfig,
53
+ loadRawConfig: () => ({}),
54
+ saveConfig: () => {},
55
+ saveRawConfig: () => {},
56
+ invalidateConfigCache: () => {},
57
+ getNestedValue: () => undefined,
58
+ setNestedValue: () => {},
59
+ }));
60
+
61
+ const { buildSystemPrompt, SYSTEM_PROMPT_CACHE_BOUNDARY } =
62
+ await import("../system-prompt.js");
63
+
64
+ describe("buildSystemPrompt — Background Conversation gating", () => {
65
+ beforeEach(() => {
66
+ mkdirSync(TEST_DIR, { recursive: true });
67
+ });
68
+
69
+ test("isBackgroundConversation: true — appends the Background Conversation section", () => {
70
+ const result = buildSystemPrompt({ isBackgroundConversation: true });
71
+ expect(result).toContain("## Background Conversation");
72
+ expect(result).toContain("`notifications` skill");
73
+ expect(result).toContain("assistant notifications send");
74
+ });
75
+
76
+ test("isBackgroundConversation: false — section is omitted", () => {
77
+ const result = buildSystemPrompt({ isBackgroundConversation: false });
78
+ expect(result).not.toContain("## Background Conversation");
79
+ });
80
+
81
+ test("options undefined — section is omitted (interactive default)", () => {
82
+ const result = buildSystemPrompt(undefined);
83
+ expect(result).not.toContain("## Background Conversation");
84
+ });
85
+
86
+ test("options provided without the flag — section is omitted", () => {
87
+ const result = buildSystemPrompt({});
88
+ expect(result).not.toContain("## Background Conversation");
89
+ });
90
+
91
+ test("section lives in the static (cached) block, not the dynamic suffix", () => {
92
+ // The section is deterministic for a given conversationType, so it
93
+ // belongs in staticParts to share the cache block with other
94
+ // call-time-stable instructions.
95
+ const result = buildSystemPrompt({ isBackgroundConversation: true });
96
+ const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
97
+ expect(boundaryIdx).toBeGreaterThan(-1);
98
+ const staticBlock = result.slice(0, boundaryIdx);
99
+ const dynamicBlock = result.slice(
100
+ boundaryIdx + SYSTEM_PROMPT_CACHE_BOUNDARY.length,
101
+ );
102
+ expect(staticBlock).toContain("## Background Conversation");
103
+ expect(dynamicBlock).not.toContain("## Background Conversation");
104
+ });
105
+ });
@@ -219,10 +219,19 @@ export function ensurePromptFiles(): void {
219
219
  export interface BuildSystemPromptOptions {
220
220
  hasNoClient?: boolean;
221
221
  excludeBootstrap?: boolean;
222
+ excludeCustomPrefix?: boolean;
222
223
  userPersona?: string | null;
223
224
  channelPersona?: string | null;
224
225
  userSlug?: string | null;
225
226
  onboardingContext?: OnboardingContext;
227
+ /**
228
+ * When true, append the Background Conversation guidance instructing the
229
+ * model to invoke the `notifications` skill for progress, blockers, and
230
+ * completion. Set by callers when running a non-interactive
231
+ * background/scheduled conversation. Interactive conversations leave this
232
+ * unset so they pay zero token cost.
233
+ */
234
+ isBackgroundConversation?: boolean;
226
235
  }
227
236
 
228
237
  /**
@@ -242,7 +251,8 @@ export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
242
251
  // (IDENTITY.md, SOUL.md, users/<slug>.md, etc.) are edited between turns.
243
252
  const staticParts: string[] = [];
244
253
  const customPrefix = readCustomSystemPromptPrefix();
245
- if (customPrefix) staticParts.push(customPrefix);
254
+ if (customPrefix && !options?.excludeCustomPrefix)
255
+ staticParts.push(customPrefix);
246
256
  staticParts.push(buildParallelToolCallsSection());
247
257
  if (getIsContainerized()) staticParts.push(buildContainerizedSection());
248
258
  staticParts.push(buildCliReferenceSection());
@@ -254,6 +264,9 @@ export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
254
264
  staticParts.push(buildAccessPreferenceSection(hasNoClient));
255
265
  staticParts.push(buildCredentialSecuritySection());
256
266
  staticParts.push(buildExternalContentSection());
267
+ if (options?.isBackgroundConversation) {
268
+ staticParts.push(buildBackgroundConversationSection());
269
+ }
257
270
  // Memory Persistence, Memory Recall, Workspace Reflection, Learning from Mistakes
258
271
  // sections removed — guidance lives in memory_manage/memory_recall tool descriptions
259
272
  // and the Proactive Workspace Editing subsection in Configuration.
@@ -407,6 +420,14 @@ function buildExternalContentSection(): string {
407
420
  ].join("\n");
408
421
  }
409
422
 
423
+ function buildBackgroundConversationSection(): string {
424
+ return [
425
+ "## Background Conversation",
426
+ "",
427
+ 'You are running as a non-interactive background job — the user is not watching this conversation. To surface progress, blockers, or completion to the user, invoke the `notifications` skill (`assistant notifications send --message "..." --source-channel assistant_tool --is-async-background`). Finishing silently means the user sees nothing.',
428
+ ].join("\n");
429
+ }
430
+
410
431
  function buildIntegrationSection(): string {
411
432
  let connections: { provider: string; accountInfo?: string | null }[];
412
433
  try {
@@ -2,14 +2,12 @@ import { createHash } from "node:crypto";
2
2
  import { existsSync, readFileSync } from "node:fs";
3
3
 
4
4
  import { getConfig } from "../config/loader.js";
5
- import { INTERNAL_GUARDIAN_TRUST_CONTEXT } from "../daemon/trust-context.js";
6
5
  import {
7
6
  getMemoryCheckpoint,
8
7
  setMemoryCheckpoint,
9
8
  } from "../memory/checkpoints.js";
10
- import { bootstrapConversation } from "../memory/conversation-bootstrap.js";
11
- import { deleteConversation } from "../memory/conversation-crud.js";
12
- import { wakeAgentForOpportunity } from "../runtime/agent-wake.js";
9
+ import { runBackgroundJob } from "../runtime/background-job-runner.js";
10
+ import { hasReceivedUserMessage } from "../runtime/pre-first-message-gate.js";
13
11
  import { getLogger } from "../util/logger.js";
14
12
  import {
15
13
  getWorkspaceDirDisplay,
@@ -20,6 +18,12 @@ const log = getLogger("update-bulletin-job");
20
18
 
21
19
  const HASH_CHECKPOINT_KEY = "updates:last_processed_hash";
22
20
  const EMPTY_HASH = "empty";
21
+ /**
22
+ * Hard timeout for the update-bulletin agent turn. The agent reads a small
23
+ * markdown file and (usually) deletes it; 10 minutes is generous headroom for
24
+ * a slow model + any tool calls (e.g. memory writes).
25
+ */
26
+ const UPDATE_BULLETIN_TIMEOUT_MS = 10 * 60 * 1000;
23
27
 
24
28
  function updateBulletinHint(): string {
25
29
  const workspace = getWorkspaceDirDisplay();
@@ -47,31 +51,48 @@ function readTrimmedContent(path: string): ReadResult {
47
51
  /**
48
52
  * Fire-and-forget background processor for the release-notes bulletin.
49
53
  *
50
- * If `<workspace>/UPDATES.md` has new (unprocessed) content, this
51
- * bootstraps a background conversation and wakes the agent loop with a hint
52
- * pointing at the file. De-duplication uses a sha256 content hash stored in
53
- * the `updates:last_processed_hash` memory checkpoint — an `"empty"` sentinel
54
+ * If `<workspace>/UPDATES.md` has new (unprocessed) content, this drives a
55
+ * background conversation through `runBackgroundJob` with a hint pointing at
56
+ * the file. De-duplication uses a sha256 content hash stored in the
57
+ * `updates:last_processed_hash` memory checkpoint — an `"empty"` sentinel
54
58
  * represents a missing/blank file so the job skips the common no-op case.
55
59
  *
56
- * The function never throws: any error inside the bootstrap/wake flow is
57
- * logged at `warn` and swallowed, so callers can safely invoke it in a
58
- * non-awaited context.
60
+ * The function never throws: any error inside `runBackgroundJob` is captured
61
+ * in its structured result (which already emits an `activity.failed`
62
+ * notification) and surrounding errors are logged at `warn` and swallowed,
63
+ * so callers can safely invoke it in a non-awaited context.
59
64
  *
60
65
  * Checkpoint write rules (intentionally conservative — prefer retry over
61
66
  * poisoning the checkpoint when state is ambiguous):
62
67
  * - File missing → checkpoint = `EMPTY_HASH`.
63
68
  * - File present but unreadable → checkpoint UNCHANGED, warn logged.
64
- * - Wake not invoked (e.g. resolver not yet registered) → UNCHANGED.
65
- * - Wake invoked but no tool calls AND file unchanged → UNCHANGED
66
- * (indistinguishable from a silent failure; safer to retry).
67
- * - Wake invoked + (produced tool calls OR file deleted) → checkpoint
68
- * reflects the post-wake state.
69
+ * - `runBackgroundJob` returned `ok: false` checkpoint UNCHANGED so the
70
+ * next startup retries. The runner has already emitted an
71
+ * `activity.failed` notification.
72
+ * - Job ran successfully + file deleted/empty → checkpoint = `EMPTY_HASH`.
73
+ * - Job ran successfully + file unchanged → checkpoint = current hash
74
+ * (agent intentionally left the file).
69
75
  */
70
76
  export async function runUpdateBulletinJobIfNeeded(): Promise<void> {
71
77
  if (getConfig().updates.enabled === false) {
72
78
  return;
73
79
  }
74
80
 
81
+ // Warm-pool guard: don't process release notes before a real user has
82
+ // interacted with the assistant. Provider credentials are typically not
83
+ // registered until the user hatches the image, so this job would fail
84
+ // and leave a "Background job failed: update-bulletin" row in the
85
+ // sidebar the user inherits at hatch time. The checkpoint is left
86
+ // untouched on purpose — once the user sends their first message, the
87
+ // job runs on the next daemon start (or after the next UPDATES.md
88
+ // change) with normal semantics.
89
+ if (!hasReceivedUserMessage()) {
90
+ log.info(
91
+ "update-bulletin-job: skipped — daemon has not received a first user message yet",
92
+ );
93
+ return;
94
+ }
95
+
75
96
  try {
76
97
  const updatesPath = getWorkspacePromptPath("UPDATES.md");
77
98
  const initial = readTrimmedContent(updatesPath);
@@ -98,60 +119,39 @@ export async function runUpdateBulletinJobIfNeeded(): Promise<void> {
98
119
  return;
99
120
  }
100
121
 
101
- const conv = bootstrapConversation({
102
- conversationType: "background",
103
- source: "updates_bulletin",
122
+ const result = await runBackgroundJob({
123
+ jobName: "update-bulletin",
124
+ source: "update-bulletin",
104
125
  origin: "updates_bulletin",
105
- systemHint: "Processing release updates",
106
- groupId: "system:background",
107
- });
108
- const wakeResult = await wakeAgentForOpportunity({
109
- conversationId: conv.id,
110
- hint: updateBulletinHint(),
111
- source: "updates_bulletin",
112
- trustContext: INTERNAL_GUARDIAN_TRUST_CONTEXT,
126
+ prompt: updateBulletinHint(),
127
+ trustContext: {
128
+ sourceChannel: "vellum",
129
+ trustClass: "guardian",
130
+ },
131
+ callSite: "mainAgent",
132
+ timeoutMs: UPDATE_BULLETIN_TIMEOUT_MS,
113
133
  });
114
134
 
115
- if (!wakeResult.invoked) {
135
+ if (!result.ok) {
116
136
  log.warn(
117
- { conversationId: conv.id, reason: wakeResult.reason },
118
- "Update bulletin wake silently no-op'd (invoked=false); cleaning up orphan background conversation and leaving checkpoint unchanged so next startup retries",
137
+ {
138
+ conversationId: result.conversationId,
139
+ errorKind: result.errorKind,
140
+ err: result.error?.message,
141
+ },
142
+ "update-bulletin-job: runBackgroundJob returned ok=false; leaving checkpoint unchanged so next startup retries (failure notification already emitted by runner)",
119
143
  );
120
- // Belt-and-suspenders cleanup: `wakeAgentForOpportunity()` can return
121
- // `{invoked: false}` for reasons unrelated to the wake-resolver
122
- // registration order (resolver returns null because the conversation
123
- // cannot be hydrated, etc.). Without this cleanup each such occurrence
124
- // leaks a conversation DB row.
125
- //
126
- // Wrapped in its own try/catch so a cleanup failure never propagates
127
- // out of this fire-and-forget task.
128
- //
129
- // TODO: the `queueGenerateConversationTitle()` call that
130
- // `bootstrapConversation()` fires is already in flight by the time we
131
- // reach here. The title service checks `isReplaceableTitle()` before
132
- // writing, but the LLM sidechain call itself still runs against the
133
- // now-deleted conversation id. Adding a cancellation/existence hook
134
- // in `conversation-title-service.ts` would plug this one-call waste,
135
- // but this code path is rare, so we accept the one-time cost.
136
- try {
137
- deleteConversation(conv.id);
138
- } catch (err) {
139
- log.warn(
140
- { err, conversationId: conv.id },
141
- "update-bulletin-job: failed to delete orphan background conversation; continuing",
142
- );
143
- }
144
144
  return;
145
145
  }
146
146
 
147
- // Re-read after the wake. We need to know whether the file was deleted
148
- // or modified to decide whether to advance the checkpoint.
147
+ // Re-read after the job completed. We need to know whether the file was
148
+ // deleted or modified to decide whether to advance the checkpoint.
149
149
  const after = readTrimmedContent(updatesPath);
150
150
 
151
151
  if (after.kind === "error") {
152
152
  log.warn(
153
153
  { err: after.err, path: updatesPath },
154
- "update-bulletin-job: failed to re-read UPDATES.md after wake; leaving checkpoint unchanged so next startup retries",
154
+ "update-bulletin-job: failed to re-read UPDATES.md after job; leaving checkpoint unchanged so next startup retries",
155
155
  );
156
156
  return;
157
157
  }
@@ -166,26 +166,14 @@ export async function runUpdateBulletinJobIfNeeded(): Promise<void> {
166
166
  return;
167
167
  }
168
168
 
169
- if (!wakeResult.producedToolCalls) {
170
- // Wake returned cleanly but the agent did nothing observable AND the
171
- // file is still here. We can't distinguish "agent processed and chose
172
- // to no-op" from "silent failure", so leave the checkpoint alone and
173
- // let the next startup retry.
174
- log.warn(
175
- { conversationId: conv.id },
176
- "update-bulletin-job: wake produced no tool calls and file is unchanged; leaving checkpoint unchanged so next startup retries",
177
- );
178
- return;
179
- }
180
-
181
- // Wake produced tool calls and the file is still present — the agent
182
- // intentionally left it (or modified it). Record the current hash so we
183
- // don't re-wake on the same content.
169
+ // Job succeeded and the file is still present — the agent intentionally
170
+ // left it (or modified it). Record the current hash so we don't re-process
171
+ // the same content on next startup.
184
172
  setMemoryCheckpoint(HASH_CHECKPOINT_KEY, computeHash(after.content));
185
173
  } catch (err) {
186
174
  log.warn(
187
175
  { err },
188
- "update-bulletin-job: wake flow threw; swallowing so callers can fire-and-forget",
176
+ "update-bulletin-job: outer flow threw; swallowing so callers can fire-and-forget",
189
177
  );
190
178
  return;
191
179
  }