@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
@@ -532,7 +532,7 @@ describe("GET /v1/suggestion", () => {
532
532
  );
533
533
  });
534
534
 
535
- test("uses conversationStarters call site", async () => {
535
+ test("uses replySuggestion call site", async () => {
536
536
  const provider = makeMockProvider("Quick reply");
537
537
  mockGetConfiguredProvider.mockImplementation(async () => provider);
538
538
  mockGetConversationByKey.mockImplementation(() => ({
@@ -558,7 +558,7 @@ describe("GET /v1/suggestion", () => {
558
558
  const options = callArgs[3] as
559
559
  | { config?: { callSite?: string } }
560
560
  | undefined;
561
- expect(options?.config?.callSite).toBe("conversationStarters");
561
+ expect(options?.config?.callSite).toBe("replySuggestion");
562
562
  });
563
563
 
564
564
  test("disables thinking and zeros effort to avoid Anthropic temp/thinking 400", async () => {
@@ -612,7 +612,7 @@ describe("GET /v1/suggestion", () => {
612
612
  // whenever the request triggers extended thinking (e.g. Opus 4.x at
613
613
  // `effort: "xhigh"`). The suggestion generator must only send a
614
614
  // single user-role message so it stays compatible with every
615
- // possible `conversationStarters` call-site config.
615
+ // possible `replySuggestion` call-site config.
616
616
  const provider = makeMockProvider("<reply>Sure, works for me</reply>");
617
617
  mockGetConfiguredProvider.mockImplementation(async () => provider);
618
618
  mockGetConversationByKey.mockImplementation(() => ({
@@ -0,0 +1,63 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ buildSyncChangedMessage,
5
+ conversationMessagesSyncTag,
6
+ type ServerMessage,
7
+ SYNC_TAGS,
8
+ SyncChangedMessageSchema,
9
+ } from "../daemon/message-protocol.js";
10
+
11
+ describe("sync message contract", () => {
12
+ test("sync_changed is assignable to ServerMessage", () => {
13
+ const message: ServerMessage = {
14
+ type: "sync_changed",
15
+ tags: [SYNC_TAGS.assistantAvatar],
16
+ };
17
+
18
+ expect(message).toEqual({
19
+ type: "sync_changed",
20
+ tags: [SYNC_TAGS.assistantAvatar],
21
+ });
22
+ });
23
+
24
+ test("buildSyncChangedMessage dedupes tags", () => {
25
+ const message = buildSyncChangedMessage([
26
+ SYNC_TAGS.assistantAvatar,
27
+ SYNC_TAGS.assistantAvatar,
28
+ conversationMessagesSyncTag("conversation-123"),
29
+ ]);
30
+
31
+ expect(message).toEqual({
32
+ type: "sync_changed",
33
+ tags: [
34
+ SYNC_TAGS.assistantAvatar,
35
+ "conversation:conversation-123:messages",
36
+ ],
37
+ });
38
+ });
39
+
40
+ test("schema rejects malformed sync_changed payloads", () => {
41
+ expect(() =>
42
+ SyncChangedMessageSchema.parse({
43
+ type: "sync_changed",
44
+ tags: [],
45
+ }),
46
+ ).toThrow();
47
+
48
+ expect(() =>
49
+ SyncChangedMessageSchema.parse({
50
+ type: "sync_changed",
51
+ tags: [""],
52
+ }),
53
+ ).toThrow();
54
+
55
+ expect(() =>
56
+ SyncChangedMessageSchema.parse({
57
+ type: "sync_changed",
58
+ tags: [SYNC_TAGS.assistantAvatar],
59
+ cursor: 1,
60
+ }),
61
+ ).toThrow();
62
+ });
63
+ });
@@ -7,6 +7,57 @@ mock.module("../util/logger.js", () => ({
7
7
  }),
8
8
  }));
9
9
 
10
+ // Mock the shared `runBackgroundJob` runner so the scheduler's fresh-bootstrap
11
+ // talk-mode path stays observable. Each invocation creates a new conversation
12
+ // row and pushes the prompt onto the per-test handler set via
13
+ // `onRunBackgroundJobCall`. `run_task:` schedules use a different code path
14
+ // and do not invoke this runner.
15
+ let onRunBackgroundJobCall:
16
+ | ((info: {
17
+ conversationId: string;
18
+ prompt: string;
19
+ trustContext: { sourceChannel: string; trustClass: string };
20
+ }) => void)
21
+ | null = null;
22
+ mock.module("../runtime/background-job-runner.js", () => ({
23
+ runBackgroundJob: async (opts: {
24
+ prompt: string;
25
+ groupId?: string;
26
+ trustContext: { sourceChannel: string; trustClass: string };
27
+ }) => {
28
+ const { createConversation } =
29
+ await import("../memory/conversation-crud.js");
30
+ const conv = createConversation({
31
+ title: "(test stub)",
32
+ conversationType: "background",
33
+ source: "schedule",
34
+ ...(opts.groupId ? { groupId: opts.groupId } : {}),
35
+ });
36
+ onRunBackgroundJobCall?.({
37
+ conversationId: conv.id,
38
+ prompt: opts.prompt,
39
+ trustContext: opts.trustContext,
40
+ });
41
+ return { conversationId: conv.id, ok: true };
42
+ },
43
+ }));
44
+
45
+ // Capture `emitNotificationSignal` calls so tests can assert that scheduled
46
+ // task failures surface via the notification pipeline (home feed + native).
47
+ const emitNotificationCalls: Array<Record<string, unknown>> = [];
48
+ mock.module("../notifications/emit-signal.js", () => ({
49
+ emitNotificationSignal: async (params: Record<string, unknown>) => {
50
+ emitNotificationCalls.push(params);
51
+ return {
52
+ signalId: "stub-signal",
53
+ deduplicated: false,
54
+ dispatched: true,
55
+ reason: "ok",
56
+ deliveryResults: [],
57
+ };
58
+ },
59
+ }));
60
+
10
61
  import { getDb } from "../memory/db-connection.js";
11
62
  import { initializeDb } from "../memory/db-init.js";
12
63
  import {
@@ -111,6 +162,8 @@ describe("scheduler run_task detection", () => {
111
162
  db.run("DELETE FROM tasks");
112
163
  db.run("DELETE FROM messages");
113
164
  db.run("DELETE FROM conversations");
165
+ onRunBackgroundJobCall = null;
166
+ emitNotificationCalls.length = 0;
114
167
  });
115
168
 
116
169
  test("run_task:<id> messages trigger runTask instead of processMessage", async () => {
@@ -163,7 +216,7 @@ describe("scheduler run_task detection", () => {
163
216
  expect(typeof runTaskCalls[0].options?.taskRunId).toBe("string");
164
217
  });
165
218
 
166
- test("regular messages still go through processMessage normally", async () => {
219
+ test("regular messages route through the runBackgroundJob runner", async () => {
167
220
  // Create a regular schedule (no run_task: prefix)
168
221
  const schedule = createSchedule({
169
222
  name: "Regular Schedule",
@@ -174,36 +227,32 @@ describe("scheduler run_task detection", () => {
174
227
 
175
228
  forceScheduleDue(schedule.id);
176
229
 
177
- const processedMessages: Array<{
230
+ const runnerCalls: Array<{
178
231
  conversationId: string;
179
- message: string;
180
- options?: { trustClass?: string; taskRunId?: string };
232
+ prompt: string;
233
+ trustContext: { sourceChannel: string; trustClass: string };
181
234
  }> = [];
182
- const processMessage = async (
183
- conversationId: string,
184
- message: string,
185
- options?: { trustClass?: string; taskRunId?: string },
186
- ) => {
187
- processedMessages.push({ conversationId, message, options });
235
+ onRunBackgroundJobCall = (info) => {
236
+ runnerCalls.push(info);
188
237
  };
189
238
 
190
- const scheduler = startScheduler(processMessage, () => {});
239
+ const scheduler = startScheduler(
240
+ async () => {},
241
+ () => {},
242
+ );
191
243
 
192
244
  await new Promise((resolve) => setTimeout(resolve, 500));
193
245
  scheduler.stop();
194
246
 
195
- // processMessage should have been called with the regular message
196
- expect(
197
- processedMessages.some((m) => m.message === "Do something normal"),
198
- ).toBe(true);
199
- expect(
200
- processedMessages.some(
201
- (m) =>
202
- m.message === "Do something normal" &&
203
- m.options?.trustClass === "guardian" &&
204
- m.options?.taskRunId === undefined,
205
- ),
206
- ).toBe(true);
247
+ // The runner should have been invoked with the schedule message and a
248
+ // guardian trust context, mirroring the historical inline `processMessage`
249
+ // call that the migration replaced.
250
+ expect(runnerCalls.length).toBe(1);
251
+ expect(runnerCalls[0].prompt).toBe("Do something normal");
252
+ expect(runnerCalls[0].trustContext).toEqual({
253
+ sourceChannel: "vellum",
254
+ trustClass: "guardian",
255
+ });
207
256
  });
208
257
 
209
258
  test("handles task not found gracefully", async () => {
@@ -232,5 +281,21 @@ describe("scheduler run_task detection", () => {
232
281
  expect(runs.length).toBeGreaterThanOrEqual(1);
233
282
  expect(runs[0].status).toBe("error");
234
283
  expect(runs[0].error).toContain("Task not found");
284
+
285
+ // Failed scheduled tasks must surface via the notification pipeline so
286
+ // they reach the home feed and native macOS notifications. The shape
287
+ // mirrors what `runBackgroundJob` emits for its own failures.
288
+ const failureSignal = emitNotificationCalls.find(
289
+ (p) => p.sourceEventName === "activity.failed",
290
+ );
291
+ expect(failureSignal).toBeDefined();
292
+ expect(failureSignal?.sourceChannel).toBe("scheduler");
293
+ const payload = failureSignal?.contextPayload as Record<string, unknown>;
294
+ expect(payload.jobName).toBe("task:nonexistent-task-id");
295
+ expect(payload.errorKind).toBe("exception");
296
+ expect(typeof payload.errorMessage).toBe("string");
297
+ expect(failureSignal?.dedupeKey).toMatch(
298
+ /^activity-failed:task:nonexistent-task-id:\d{4}-\d{2}-\d{2}$/,
299
+ );
235
300
  });
236
301
  });
@@ -1,17 +1,10 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
- import {
4
- afterEach,
5
- beforeEach,
6
- describe,
7
- expect,
8
- mock,
9
- test,
10
- } from "bun:test";
3
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
11
4
 
12
5
  import { getWorkspacePromptPath } from "../util/platform.js";
13
6
 
14
- // ── fs.readFileSync override (for gap 3 test) ────────────────────────
7
+ // ── fs.readFileSync override (for read-failure test) ─────────────────
15
8
  // We mock node:fs so we can inject a readFileSync that throws for the
16
9
  // workspace path. All other call sites fall through to the real fs.
17
10
  const realReadFileSync = readFileSync;
@@ -54,62 +47,58 @@ mock.module("../config/loader.js", () => ({
54
47
  getConfig: () => ({ updates: updatesConfig }),
55
48
  }));
56
49
 
57
- // ── bootstrapConversation + wakeAgentForOpportunity mocks ────────────
58
- let bootstrapCalls = 0;
59
- let bootstrapLastArgs: Record<string, unknown> | null = null;
60
- let wakeCalls = 0;
61
- let wakeLastArgs: Record<string, unknown> | null = null;
62
- let wakeShouldThrow = false;
63
- let wakeInvoked = true;
64
- let wakeProducedToolCalls = false;
65
- // A side-effect function invoked during wake. Lets tests simulate the
66
- // agent deleting UPDATES.md while the wake is in flight.
67
- let wakeSideEffect: (() => void) | null = null;
68
-
69
- mock.module("../memory/conversation-bootstrap.js", () => ({
70
- bootstrapConversation: (opts: Record<string, unknown>) => {
71
- bootstrapCalls += 1;
72
- bootstrapLastArgs = opts;
73
- return { id: `conv-${bootstrapCalls}` };
74
- },
75
- }));
50
+ // ── pre-first-message gate stub ──────────────────────────────────────
51
+ // Default: gate open (user has interacted) so the existing happy/sad
52
+ // paths exercise the bulletin logic. A dedicated test below flips this
53
+ // to false to assert the gate trips.
54
+ let preFirstMessageGateOpen = true;
76
55
 
77
- // ── deleteConversation mock (orphan cleanup path) ────────────────────
78
- let deleteCalls = 0;
79
- const deletedIds: string[] = [];
80
- let deleteShouldThrow = false;
81
-
82
- mock.module("../memory/conversation-crud.js", () => ({
83
- deleteConversation: (id: string) => {
84
- deleteCalls += 1;
85
- deletedIds.push(id);
86
- if (deleteShouldThrow) {
87
- throw new Error("simulated delete failure");
88
- }
89
- return { segmentIds: [], deletedSummaryIds: [] };
90
- },
56
+ mock.module("../runtime/pre-first-message-gate.js", () => ({
57
+ hasReceivedUserMessage: () => preFirstMessageGateOpen,
91
58
  }));
92
59
 
93
- mock.module("../runtime/agent-wake.js", () => ({
94
- wakeAgentForOpportunity: async (opts: Record<string, unknown>) => {
95
- wakeCalls += 1;
96
- wakeLastArgs = opts;
97
- if (wakeSideEffect) {
98
- wakeSideEffect();
60
+ // ── runBackgroundJob mock ────────────────────────────────────────────
61
+ let runBackgroundJobCalls = 0;
62
+ let runBackgroundJobLastArgs: Record<string, unknown> | null = null;
63
+ let runBackgroundJobShouldThrow = false;
64
+ let runBackgroundJobOk = true;
65
+ let runBackgroundJobErrorKind:
66
+ | "timeout"
67
+ | "model_provider"
68
+ | "exception"
69
+ | undefined = undefined;
70
+ let runBackgroundJobErrorMessage: string | undefined = undefined;
71
+ // A side-effect function invoked during the job. Lets tests simulate the
72
+ // agent deleting UPDATES.md while the job is running.
73
+ let runBackgroundJobSideEffect: (() => void) | null = null;
74
+
75
+ mock.module("../runtime/background-job-runner.js", () => ({
76
+ runBackgroundJob: async (opts: Record<string, unknown>) => {
77
+ runBackgroundJobCalls += 1;
78
+ runBackgroundJobLastArgs = opts;
79
+ if (runBackgroundJobSideEffect) {
80
+ runBackgroundJobSideEffect();
99
81
  }
100
- if (wakeShouldThrow) {
101
- throw new Error("simulated wake failure");
82
+ if (runBackgroundJobShouldThrow) {
83
+ throw new Error("simulated runner failure");
84
+ }
85
+ if (runBackgroundJobOk) {
86
+ return {
87
+ conversationId: `conv-${runBackgroundJobCalls}`,
88
+ ok: true,
89
+ };
102
90
  }
103
91
  return {
104
- invoked: wakeInvoked,
105
- producedToolCalls: wakeProducedToolCalls,
92
+ conversationId: `conv-${runBackgroundJobCalls}`,
93
+ ok: false,
94
+ error: new Error(runBackgroundJobErrorMessage ?? "simulated failure"),
95
+ errorKind: runBackgroundJobErrorKind ?? "exception",
106
96
  };
107
97
  },
108
98
  }));
109
99
 
110
- const { runUpdateBulletinJobIfNeeded } = await import(
111
- "../prompts/update-bulletin-job.js"
112
- );
100
+ const { runUpdateBulletinJobIfNeeded } =
101
+ await import("../prompts/update-bulletin-job.js");
113
102
 
114
103
  const HASH_CHECKPOINT_KEY = "updates:last_processed_hash";
115
104
  const EMPTY_HASH = "empty";
@@ -124,19 +113,16 @@ describe("runUpdateBulletinJobIfNeeded", () => {
124
113
  beforeEach(() => {
125
114
  store.clear();
126
115
  setCheckpointCallCount = 0;
127
- bootstrapCalls = 0;
128
- bootstrapLastArgs = null;
129
- wakeCalls = 0;
130
- wakeLastArgs = null;
131
- wakeShouldThrow = false;
132
- wakeInvoked = true;
133
- wakeProducedToolCalls = false;
134
- wakeSideEffect = null;
116
+ runBackgroundJobCalls = 0;
117
+ runBackgroundJobLastArgs = null;
118
+ runBackgroundJobShouldThrow = false;
119
+ runBackgroundJobOk = true;
120
+ runBackgroundJobErrorKind = undefined;
121
+ runBackgroundJobErrorMessage = undefined;
122
+ runBackgroundJobSideEffect = null;
135
123
  readFileSyncOverride = null;
136
124
  updatesConfig.enabled = true;
137
- deleteCalls = 0;
138
- deletedIds.length = 0;
139
- deleteShouldThrow = false;
125
+ preFirstMessageGateOpen = true;
140
126
  if (existsSync(workspacePath)) {
141
127
  rmSync(workspacePath);
142
128
  }
@@ -148,38 +134,45 @@ describe("runUpdateBulletinJobIfNeeded", () => {
148
134
  }
149
135
  });
150
136
 
151
- test("config disabled — no bootstrap, no wake, no checkpoint change", async () => {
137
+ test("config disabled — no job, no checkpoint change", async () => {
152
138
  updatesConfig.enabled = false;
153
139
  writeFileSync(workspacePath, "## Real content", "utf-8");
154
140
 
155
141
  await runUpdateBulletinJobIfNeeded();
156
142
 
157
- expect(bootstrapCalls).toBe(0);
158
- expect(wakeCalls).toBe(0);
143
+ expect(runBackgroundJobCalls).toBe(0);
144
+ expect(setCheckpointCallCount).toBe(0);
145
+ expect(store.has(HASH_CHECKPOINT_KEY)).toBe(false);
146
+ });
147
+
148
+ test("pre-first-message gate closed — no job, checkpoint left UNCHANGED so the job retries after the user interacts", async () => {
149
+ preFirstMessageGateOpen = false;
150
+ writeFileSync(workspacePath, "## Real content", "utf-8");
151
+
152
+ await runUpdateBulletinJobIfNeeded();
153
+
154
+ expect(runBackgroundJobCalls).toBe(0);
159
155
  expect(setCheckpointCallCount).toBe(0);
160
156
  expect(store.has(HASH_CHECKPOINT_KEY)).toBe(false);
161
157
  });
162
158
 
163
- test("file missing, stored hash absent — no wake; stored becomes 'empty'", async () => {
159
+ test("file missing, stored hash absent — no job; stored becomes 'empty'", async () => {
164
160
  expect(existsSync(workspacePath)).toBe(false);
165
161
  expect(store.has(HASH_CHECKPOINT_KEY)).toBe(false);
166
162
 
167
163
  await runUpdateBulletinJobIfNeeded();
168
164
 
169
- expect(bootstrapCalls).toBe(0);
170
- expect(wakeCalls).toBe(0);
165
+ expect(runBackgroundJobCalls).toBe(0);
171
166
  expect(store.get(HASH_CHECKPOINT_KEY)).toBe(EMPTY_HASH);
172
167
  });
173
168
 
174
- test("file missing, stored hash already 'empty' — no wake; no checkpoint write", async () => {
169
+ test("file missing, stored hash already 'empty' — no job; no checkpoint write", async () => {
175
170
  store.set(HASH_CHECKPOINT_KEY, EMPTY_HASH);
176
- // Reset the counter to ignore the priming write above.
177
171
  setCheckpointCallCount = 0;
178
172
 
179
173
  await runUpdateBulletinJobIfNeeded();
180
174
 
181
- expect(bootstrapCalls).toBe(0);
182
- expect(wakeCalls).toBe(0);
175
+ expect(runBackgroundJobCalls).toBe(0);
183
176
  expect(setCheckpointCallCount).toBe(0);
184
177
  expect(store.get(HASH_CHECKPOINT_KEY)).toBe(EMPTY_HASH);
185
178
  });
@@ -189,28 +182,25 @@ describe("runUpdateBulletinJobIfNeeded", () => {
189
182
 
190
183
  await runUpdateBulletinJobIfNeeded();
191
184
 
192
- expect(bootstrapCalls).toBe(0);
193
- expect(wakeCalls).toBe(0);
185
+ expect(runBackgroundJobCalls).toBe(0);
194
186
  expect(store.get(HASH_CHECKPOINT_KEY)).toBe(EMPTY_HASH);
195
187
  });
196
188
 
197
- test("file present with content, wake produced tool calls bootstrap + wake; stored hash is sha256(trimmed); source/origin are snake_case", async () => {
189
+ test("file present, job ok=true, file unchanged — stored hash is sha256(trimmed); jobName/source are kebab-case", async () => {
198
190
  const content = "## Release 1.2.3\n\nNew thing.\n";
199
191
  writeFileSync(workspacePath, content, "utf-8");
200
- wakeProducedToolCalls = true;
201
192
 
202
193
  await runUpdateBulletinJobIfNeeded();
203
194
 
204
- expect(bootstrapCalls).toBe(1);
205
- expect(wakeCalls).toBe(1);
195
+ expect(runBackgroundJobCalls).toBe(1);
206
196
  expect(store.get(HASH_CHECKPOINT_KEY)).toBe(sha256(content.trim()));
207
- // Gap 4: confirm snake_case reached the downstream mocks.
208
- expect(bootstrapLastArgs?.source).toBe("updates_bulletin");
209
- expect(bootstrapLastArgs?.origin).toBe("updates_bulletin");
210
- expect(wakeLastArgs?.source).toBe("updates_bulletin");
197
+ expect(runBackgroundJobLastArgs?.jobName).toBe("update-bulletin");
198
+ expect(runBackgroundJobLastArgs?.source).toBe("update-bulletin");
199
+ expect(runBackgroundJobLastArgs?.origin).toBe("updates_bulletin");
200
+ expect(runBackgroundJobLastArgs?.callSite).toBe("mainAgent");
211
201
  });
212
202
 
213
- test("file present, stored hash matches current — no wake", async () => {
203
+ test("file present, stored hash matches current — no job", async () => {
214
204
  const content = "## Release 1.2.3\n\nSame content.\n";
215
205
  writeFileSync(workspacePath, content, "utf-8");
216
206
  store.set(HASH_CHECKPOINT_KEY, sha256(content.trim()));
@@ -218,138 +208,53 @@ describe("runUpdateBulletinJobIfNeeded", () => {
218
208
 
219
209
  await runUpdateBulletinJobIfNeeded();
220
210
 
221
- expect(bootstrapCalls).toBe(0);
222
- expect(wakeCalls).toBe(0);
223
- expect(setCheckpointCallCount).toBe(0);
224
- });
225
-
226
- test("wake returns invoked:false — checkpoint UNCHANGED + orphan conversation is cleaned up", async () => {
227
- const content = "## Release Q\n\nResolver-missing scenario.\n";
228
- writeFileSync(workspacePath, content, "utf-8");
229
- wakeInvoked = false;
230
- wakeProducedToolCalls = false;
231
-
232
- await runUpdateBulletinJobIfNeeded();
233
-
234
- expect(bootstrapCalls).toBe(1);
235
- expect(wakeCalls).toBe(1);
236
- // Critical: do NOT poison the checkpoint (round-1 behavior preserved).
237
- expect(store.has(HASH_CHECKPOINT_KEY)).toBe(false);
238
- expect(setCheckpointCallCount).toBe(0);
239
- // Belt-and-suspenders: the orphan background conversation bootstrapped
240
- // before the wake must be deleted so we don't leak DB rows on every
241
- // silent no-op.
242
- expect(deleteCalls).toBe(1);
243
- expect(deletedIds).toEqual(["conv-1"]);
244
- });
245
-
246
- test("wake returns invoked:false AND deleteConversation throws — function still returns (cleanup error is swallowed)", async () => {
247
- const content = "## Release Q2\n\nDelete-throws scenario.\n";
248
- writeFileSync(workspacePath, content, "utf-8");
249
- wakeInvoked = false;
250
- wakeProducedToolCalls = false;
251
- deleteShouldThrow = true;
252
-
253
- await expect(runUpdateBulletinJobIfNeeded()).resolves.toBeUndefined();
254
-
255
- expect(bootstrapCalls).toBe(1);
256
- expect(wakeCalls).toBe(1);
257
- expect(deleteCalls).toBe(1);
258
- // Checkpoint still untouched.
259
- expect(store.has(HASH_CHECKPOINT_KEY)).toBe(false);
211
+ expect(runBackgroundJobCalls).toBe(0);
260
212
  expect(setCheckpointCallCount).toBe(0);
261
213
  });
262
214
 
263
- test("wake invoked normallydeleteConversation is NOT called (happy path)", async () => {
264
- const content = "## Release Q3\n\nNormal happy-path scenario.\n";
265
- writeFileSync(workspacePath, content, "utf-8");
266
- wakeInvoked = true;
267
- wakeProducedToolCalls = true;
268
-
269
- await runUpdateBulletinJobIfNeeded();
270
-
271
- expect(bootstrapCalls).toBe(1);
272
- expect(wakeCalls).toBe(1);
273
- expect(deleteCalls).toBe(0);
274
- expect(deletedIds).toEqual([]);
275
- });
276
-
277
- test("wake invoked but no tool calls AND file unchanged — checkpoint UNCHANGED (retry next startup)", async () => {
278
- const content = "## Release R\n\nSilent no-op scenario.\n";
215
+ test("runBackgroundJob returns ok=falsecheckpoint UNCHANGED so next startup retries", async () => {
216
+ const content = "## Release Q\n\nFailure scenario.\n";
279
217
  writeFileSync(workspacePath, content, "utf-8");
280
- wakeInvoked = true;
281
- wakeProducedToolCalls = false;
218
+ runBackgroundJobOk = false;
219
+ runBackgroundJobErrorKind = "exception";
220
+ runBackgroundJobErrorMessage = "boom";
282
221
 
283
222
  await runUpdateBulletinJobIfNeeded();
284
223
 
285
- expect(bootstrapCalls).toBe(1);
286
- expect(wakeCalls).toBe(1);
224
+ expect(runBackgroundJobCalls).toBe(1);
225
+ // Critical: do NOT poison the checkpoint when the job fails.
287
226
  expect(store.has(HASH_CHECKPOINT_KEY)).toBe(false);
288
227
  expect(setCheckpointCallCount).toBe(0);
289
228
  });
290
229
 
291
- test("wake invoked, no tool calls, but file deleted mid-wakecheckpoint becomes 'empty'", async () => {
292
- const content = "## Release S\n\nAgent deleted file.\n";
230
+ test("runBackgroundJob ok=true + agent deletes file mid-runstored hash becomes 'empty'", async () => {
231
+ const content = "## Release X\n\nStuff to process.\n";
293
232
  writeFileSync(workspacePath, content, "utf-8");
294
- wakeInvoked = true;
295
- wakeProducedToolCalls = false;
296
- wakeSideEffect = () => {
233
+ runBackgroundJobSideEffect = () => {
297
234
  rmSync(workspacePath);
298
235
  };
299
236
 
300
237
  await runUpdateBulletinJobIfNeeded();
301
238
 
302
- expect(bootstrapCalls).toBe(1);
303
- expect(wakeCalls).toBe(1);
239
+ expect(runBackgroundJobCalls).toBe(1);
304
240
  expect(existsSync(workspacePath)).toBe(false);
305
241
  expect(store.get(HASH_CHECKPOINT_KEY)).toBe(EMPTY_HASH);
306
242
  });
307
243
 
308
- test("wake invoked + produced tool calls + file unchanged checkpoint = hash of content (agent decided this is the right state)", async () => {
309
- const content = "## Release T\n\nAgent processed, chose to leave file.\n";
310
- writeFileSync(workspacePath, content, "utf-8");
311
- wakeInvoked = true;
312
- wakeProducedToolCalls = true;
313
-
314
- await runUpdateBulletinJobIfNeeded();
315
-
316
- expect(bootstrapCalls).toBe(1);
317
- expect(wakeCalls).toBe(1);
318
- expect(store.get(HASH_CHECKPOINT_KEY)).toBe(sha256(content.trim()));
319
- });
320
-
321
- test("file present, stored hash differs — wake invoked; stored hash updates", async () => {
244
+ test("file present, stored hash differs job invoked; stored hash updates", async () => {
322
245
  const oldContent = "## Old";
323
246
  const newContent = "## New content v2";
324
247
  writeFileSync(workspacePath, newContent, "utf-8");
325
248
  store.set(HASH_CHECKPOINT_KEY, sha256(oldContent));
326
- wakeProducedToolCalls = true;
327
249
 
328
250
  await runUpdateBulletinJobIfNeeded();
329
251
 
330
- expect(bootstrapCalls).toBe(1);
331
- expect(wakeCalls).toBe(1);
252
+ expect(runBackgroundJobCalls).toBe(1);
332
253
  expect(store.get(HASH_CHECKPOINT_KEY)).toBe(sha256(newContent.trim()));
333
254
  expect(store.get(HASH_CHECKPOINT_KEY)).not.toBe(sha256(oldContent));
334
255
  });
335
256
 
336
- test("agent deletes file mid-wake (producedToolCalls=true)stored hash becomes 'empty'", async () => {
337
- const content = "## Release X\n\nStuff to process.\n";
338
- writeFileSync(workspacePath, content, "utf-8");
339
- wakeProducedToolCalls = true;
340
- wakeSideEffect = () => {
341
- rmSync(workspacePath);
342
- };
343
-
344
- await runUpdateBulletinJobIfNeeded();
345
-
346
- expect(bootstrapCalls).toBe(1);
347
- expect(wakeCalls).toBe(1);
348
- expect(existsSync(workspacePath)).toBe(false);
349
- expect(store.get(HASH_CHECKPOINT_KEY)).toBe(EMPTY_HASH);
350
- });
351
-
352
- test("file present but readFileSync throws — checkpoint UNCHANGED; warn logged (gap 3)", async () => {
257
+ test("file present but readFileSync throwscheckpoint UNCHANGED; warn logged", async () => {
353
258
  const content = "## Release U\n\nSimulated read failure.\n";
354
259
  writeFileSync(workspacePath, content, "utf-8");
355
260
 
@@ -366,22 +271,20 @@ describe("runUpdateBulletinJobIfNeeded", () => {
366
271
  readFileSyncOverride = null;
367
272
  }
368
273
 
369
- expect(bootstrapCalls).toBe(0);
370
- expect(wakeCalls).toBe(0);
274
+ expect(runBackgroundJobCalls).toBe(0);
371
275
  expect(store.has(HASH_CHECKPOINT_KEY)).toBe(false);
372
276
  expect(setCheckpointCallCount).toBe(0);
373
277
  });
374
278
 
375
- test("wake throws — function does not reject; warning logged", async () => {
279
+ test("runBackgroundJob throws — function does not reject; warning logged", async () => {
376
280
  const content = "## Release Z";
377
281
  writeFileSync(workspacePath, content, "utf-8");
378
- wakeShouldThrow = true;
282
+ runBackgroundJobShouldThrow = true;
379
283
 
380
284
  // Must not throw.
381
285
  await expect(runUpdateBulletinJobIfNeeded()).resolves.toBeUndefined();
382
286
 
383
- expect(bootstrapCalls).toBe(1);
384
- expect(wakeCalls).toBe(1);
287
+ expect(runBackgroundJobCalls).toBe(1);
385
288
  // Hash was never updated because the try/catch returned before the
386
289
  // self-healing step.
387
290
  expect(store.has(HASH_CHECKPOINT_KEY)).toBe(false);