@vellumai/assistant 0.7.3 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (778) hide show
  1. package/AGENTS.md +11 -0
  2. package/ARCHITECTURE.md +29 -28
  3. package/Dockerfile +6 -4
  4. package/README.md +2 -2
  5. package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
  6. package/bun.lock +3 -0
  7. package/docker-entrypoint.sh +16 -0
  8. package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
  9. package/eslint-rules/cli-no-daemon-internals.js +283 -0
  10. package/eslint.config.mjs +12 -0
  11. package/knip.json +3 -1
  12. package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
  13. package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
  14. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
  15. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
  16. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
  17. package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
  18. package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
  19. package/openapi.yaml +4126 -959
  20. package/package.json +5 -1
  21. package/scripts/generate-openapi.ts +52 -4
  22. package/scripts/sync-llm-catalog.ts +165 -0
  23. package/scripts/sync-web-search-catalog.ts +107 -0
  24. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
  25. package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
  26. package/src/__tests__/annotate-risk-options.test.ts +291 -0
  27. package/src/__tests__/anthropic-provider.test.ts +92 -2
  28. package/src/__tests__/app-control-flow.test.ts +7 -0
  29. package/src/__tests__/approval-cascade.test.ts +8 -16
  30. package/src/__tests__/approval-routes-http.test.ts +6 -0
  31. package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
  32. package/src/__tests__/auto-analysis-end-to-end.test.ts +12 -25
  33. package/src/__tests__/avatar-identity-sync.test.ts +87 -0
  34. package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
  35. package/src/__tests__/btw-routes.test.ts +1 -0
  36. package/src/__tests__/call-constants.test.ts +10 -1
  37. package/src/__tests__/call-controller.test.ts +127 -0
  38. package/src/__tests__/call-site-routing-provider.test.ts +172 -45
  39. package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
  40. package/src/__tests__/channel-policy.test.ts +12 -0
  41. package/src/__tests__/checker.test.ts +89 -0
  42. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +88 -30
  43. package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
  44. package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
  45. package/src/__tests__/config-loader-backfill.test.ts +526 -102
  46. package/src/__tests__/config-loader-corrupt.test.ts +68 -0
  47. package/src/__tests__/config-loader-platform-defaults.test.ts +345 -8
  48. package/src/__tests__/config-schema-cmd.test.ts +63 -29
  49. package/src/__tests__/config-schema.test.ts +14 -3
  50. package/src/__tests__/config-set-platform-guard.test.ts +75 -152
  51. package/src/__tests__/config-set-route.test.ts +198 -0
  52. package/src/__tests__/config-watcher.test.ts +6 -0
  53. package/src/__tests__/contacts-tools.test.ts +51 -199
  54. package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
  55. package/src/__tests__/context-search-agent-runner.test.ts +22 -138
  56. package/src/__tests__/context-search-conversations-source.test.ts +42 -16
  57. package/src/__tests__/context-search-fanout.test.ts +20 -157
  58. package/src/__tests__/context-search-memory-source.test.ts +3 -26
  59. package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
  60. package/src/__tests__/context-search-types.test.ts +7 -2
  61. package/src/__tests__/context-window-manager.test.ts +389 -1
  62. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -6
  63. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
  64. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -1
  65. package/src/__tests__/conversation-agent-loop.test.ts +3 -3
  66. package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
  67. package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
  68. package/src/__tests__/conversation-error.test.ts +38 -0
  69. package/src/__tests__/conversation-fork-crud.test.ts +241 -1
  70. package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
  71. package/src/__tests__/conversation-init.benchmark.test.ts +2 -1
  72. package/src/__tests__/conversation-lifecycle.test.ts +124 -0
  73. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
  74. package/src/__tests__/conversation-process-callsite.test.ts +22 -7
  75. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -6
  76. package/src/__tests__/conversation-runtime-assembly.test.ts +19 -10
  77. package/src/__tests__/conversation-slash-commands.test.ts +194 -2
  78. package/src/__tests__/conversation-slash-unknown.test.ts +1 -6
  79. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
  80. package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
  81. package/src/__tests__/conversation-surfaces-data-persist.test.ts +73 -1
  82. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +59 -0
  83. package/src/__tests__/conversation-workspace-injection.test.ts +1 -7
  84. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -7
  85. package/src/__tests__/credential-security-invariants.test.ts +5 -6
  86. package/src/__tests__/daemon-credential-client.test.ts +56 -1
  87. package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
  88. package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
  89. package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
  90. package/src/__tests__/db-proxy-transaction.test.ts +206 -0
  91. package/src/__tests__/external-plugin-loader.test.ts +458 -0
  92. package/src/__tests__/filing-service.test.ts +25 -22
  93. package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
  94. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  95. package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
  96. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -34
  97. package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
  98. package/src/__tests__/heartbeat-service.test.ts +50 -233
  99. package/src/__tests__/history-repair.test.ts +89 -0
  100. package/src/__tests__/host-app-control-proxy.test.ts +109 -1
  101. package/src/__tests__/host-app-control-routes.test.ts +247 -1
  102. package/src/__tests__/host-browser-proxy.test.ts +416 -20
  103. package/src/__tests__/host-browser-routes.test.ts +325 -33
  104. package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
  105. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
  106. package/src/__tests__/inference-profile-reaper.test.ts +154 -0
  107. package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
  108. package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
  109. package/src/__tests__/injector-chain.test.ts +24 -16
  110. package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
  111. package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
  112. package/src/__tests__/install-skill-routing.test.ts +2 -2
  113. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +169 -67
  114. package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
  115. package/src/__tests__/llm-catalog-parity.test.ts +146 -0
  116. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
  117. package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
  118. package/src/__tests__/llm-resolver.test.ts +46 -0
  119. package/src/__tests__/managed-profile-guard.test.ts +131 -2
  120. package/src/__tests__/mcp-auth-routes.test.ts +1 -0
  121. package/src/__tests__/mcp-cli.test.ts +182 -220
  122. package/src/__tests__/mcp-health-check.test.ts +56 -27
  123. package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
  124. package/src/__tests__/message-complete-display-id.test.ts +175 -0
  125. package/src/__tests__/notification-decision-fallback.test.ts +91 -0
  126. package/src/__tests__/notification-decision-strategy.test.ts +22 -0
  127. package/src/__tests__/notification-platform-adapter.test.ts +229 -0
  128. package/src/__tests__/oauth-cli.test.ts +38 -1888
  129. package/src/__tests__/oauth-commands-routes.test.ts +711 -0
  130. package/src/__tests__/oauth-connect-routes.test.ts +174 -11
  131. package/src/__tests__/oauth-providers-routes.test.ts +14 -10
  132. package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
  133. package/src/__tests__/openai-responses-provider.test.ts +17 -0
  134. package/src/__tests__/plugin-bootstrap.test.ts +31 -2
  135. package/src/__tests__/plugin-route-contribution.test.ts +31 -3
  136. package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
  137. package/src/__tests__/plugin-types.test.ts +13 -11
  138. package/src/__tests__/process-message-background-slack.test.ts +46 -0
  139. package/src/__tests__/profile-entry-status.test.ts +43 -0
  140. package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
  141. package/src/__tests__/provider-registry-ollama.test.ts +12 -4
  142. package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
  143. package/src/__tests__/relay-server.test.ts +164 -2
  144. package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
  145. package/src/__tests__/schedule-retry.test.ts +56 -4
  146. package/src/__tests__/schedule-routes.test.ts +104 -0
  147. package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
  148. package/src/__tests__/scheduler-recurrence.test.ts +87 -34
  149. package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
  150. package/src/__tests__/scheduler-wake.test.ts +0 -63
  151. package/src/__tests__/secret-allowlist.test.ts +1 -0
  152. package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
  153. package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
  154. package/src/__tests__/secret-response-routing.test.ts +7 -5
  155. package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
  156. package/src/__tests__/server-history-render.test.ts +82 -0
  157. package/src/__tests__/shell-credential-ref.test.ts +95 -3
  158. package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
  159. package/src/__tests__/skill-include-graph.test.ts +31 -0
  160. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  161. package/src/__tests__/skill-load-tool.test.ts +42 -16
  162. package/src/__tests__/skills.test.ts +39 -0
  163. package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
  164. package/src/__tests__/suggestion-routes.test.ts +3 -3
  165. package/src/__tests__/sync-message-contract.test.ts +63 -0
  166. package/src/__tests__/task-scheduler.test.ts +88 -23
  167. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
  168. package/src/__tests__/tool-executor.test.ts +155 -0
  169. package/src/__tests__/update-bulletin-job.test.ts +96 -193
  170. package/src/__tests__/usage-cli.test.ts +11 -73
  171. package/src/__tests__/user-plugin-loader.test.ts +145 -0
  172. package/src/__tests__/vercel-config.test.ts +168 -0
  173. package/src/__tests__/voice-session-bridge.test.ts +3 -0
  174. package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
  175. package/src/__tests__/web-search.test.ts +303 -2
  176. package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
  177. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
  178. package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +153 -0
  179. package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
  180. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
  181. package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
  182. package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
  183. package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
  184. package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
  185. package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
  186. package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
  187. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +15 -27
  188. package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
  189. package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
  190. package/src/acp/__tests__/helpers/which-stub.ts +4 -2
  191. package/src/acp/resolve-agent.test.ts +25 -0
  192. package/src/acp/resolve-agent.ts +13 -2
  193. package/src/acp/session-manager.ts +14 -0
  194. package/src/agent/loop.ts +11 -0
  195. package/src/approvals/guardian-decision-primitive.ts +0 -13
  196. package/src/approvals/guardian-request-resolvers.ts +19 -102
  197. package/src/calls/call-constants.ts +5 -8
  198. package/src/calls/call-controller.ts +130 -67
  199. package/src/calls/relay-server.ts +42 -1
  200. package/src/calls/relay-setup-router.ts +36 -0
  201. package/src/calls/types.ts +1 -0
  202. package/src/calls/voice-session-bridge.ts +24 -5
  203. package/src/channels/config.ts +14 -1
  204. package/src/channels/types.ts +1 -0
  205. package/src/cli/AGENTS.md +164 -4
  206. package/src/cli/__tests__/notifications.test.ts +54 -0
  207. package/src/cli/commands/__tests__/avatar.test.ts +540 -0
  208. package/src/cli/commands/__tests__/backup.test.ts +236 -776
  209. package/src/cli/commands/__tests__/cache.test.ts +1 -1
  210. package/src/cli/commands/__tests__/changelog.test.ts +593 -0
  211. package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
  212. package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
  213. package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
  214. package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
  215. package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
  216. package/src/cli/commands/__tests__/email-core.test.ts +579 -0
  217. package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
  218. package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
  219. package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
  220. package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
  221. package/src/cli/commands/__tests__/skills.test.ts +563 -0
  222. package/src/cli/commands/__tests__/status.test.ts +249 -0
  223. package/src/cli/commands/__tests__/stt.test.ts +320 -0
  224. package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
  225. package/src/cli/commands/__tests__/tts.test.ts +321 -0
  226. package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
  227. package/src/cli/commands/attachment.ts +8 -3
  228. package/src/cli/commands/audit.ts +95 -64
  229. package/src/cli/commands/auth.ts +61 -58
  230. package/src/cli/commands/avatar.ts +276 -390
  231. package/src/cli/commands/backup.ts +409 -505
  232. package/src/cli/commands/bash.ts +9 -5
  233. package/src/cli/commands/browser.ts +28 -9
  234. package/src/cli/commands/cache.ts +9 -4
  235. package/src/cli/commands/changelog.ts +414 -0
  236. package/src/cli/commands/channel-verification-sessions.ts +238 -317
  237. package/src/cli/commands/clients.ts +8 -3
  238. package/src/cli/commands/completions.ts +9 -9
  239. package/src/cli/commands/config.ts +102 -72
  240. package/src/cli/commands/contacts.ts +575 -696
  241. package/src/cli/commands/conversations-defer.ts +17 -69
  242. package/src/cli/commands/conversations-import.ts +90 -253
  243. package/src/cli/commands/conversations.ts +346 -436
  244. package/src/cli/commands/credential-execution.ts +9 -6
  245. package/src/cli/commands/credentials.ts +456 -736
  246. package/src/cli/commands/domain.ts +128 -206
  247. package/src/cli/commands/email.ts +606 -794
  248. package/src/cli/commands/gateway.ts +8 -1
  249. package/src/cli/commands/image-generation.ts +157 -205
  250. package/src/cli/commands/inference-providers.ts +352 -0
  251. package/src/cli/commands/inference-session.ts +415 -0
  252. package/src/cli/commands/inference.ts +87 -65
  253. package/src/cli/commands/keys.ts +8 -3
  254. package/src/cli/commands/mcp.ts +103 -287
  255. package/src/cli/commands/memory-v2.ts +163 -517
  256. package/src/cli/commands/notifications.ts +33 -7
  257. package/src/cli/commands/oauth/apps.ts +292 -261
  258. package/src/cli/commands/oauth/connect.ts +182 -345
  259. package/src/cli/commands/oauth/disconnect.ts +16 -215
  260. package/src/cli/commands/oauth/index.ts +49 -45
  261. package/src/cli/commands/oauth/mode.ts +43 -199
  262. package/src/cli/commands/oauth/ping.ts +17 -125
  263. package/src/cli/commands/oauth/providers.ts +732 -921
  264. package/src/cli/commands/oauth/request.ts +60 -350
  265. package/src/cli/commands/oauth/shared.ts +11 -121
  266. package/src/cli/commands/oauth/status.ts +31 -121
  267. package/src/cli/commands/oauth/token.ts +13 -55
  268. package/src/cli/commands/pending.ts +19 -10
  269. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
  270. package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
  271. package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
  272. package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
  273. package/src/cli/commands/platform/connect.ts +16 -80
  274. package/src/cli/commands/platform/disconnect.ts +14 -112
  275. package/src/cli/commands/platform/index.ts +177 -246
  276. package/src/cli/commands/routes.ts +153 -336
  277. package/src/cli/commands/sequence.ts +316 -360
  278. package/src/cli/commands/skills.ts +449 -671
  279. package/src/cli/commands/status.ts +58 -37
  280. package/src/cli/commands/stt.ts +94 -262
  281. package/src/cli/commands/task.ts +14 -40
  282. package/src/cli/commands/trust.ts +8 -3
  283. package/src/cli/commands/tts.ts +162 -167
  284. package/src/cli/commands/ui.ts +35 -42
  285. package/src/cli/commands/usage.ts +188 -126
  286. package/src/cli/commands/watchers.ts +8 -3
  287. package/src/cli/commands/webhooks.ts +99 -193
  288. package/src/cli/lib/__tests__/register-command.test.ts +85 -0
  289. package/src/cli/lib/daemon-credential-client.ts +4 -5
  290. package/src/cli/lib/nested-value.ts +44 -0
  291. package/src/cli/lib/open-browser.ts +36 -0
  292. package/src/cli/lib/register-command.ts +19 -0
  293. package/src/cli/lib/time-ago.ts +34 -0
  294. package/src/cli/program.ts +2 -4
  295. package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
  296. package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
  297. package/src/cli/utils/conversation-id.ts +30 -0
  298. package/src/cli/utils/parse-duration.ts +41 -0
  299. package/src/config/acp-defaults.test.ts +5 -1
  300. package/src/config/acp-defaults.ts +11 -4
  301. package/src/config/bundled-skills/acp/TOOLS.json +2 -2
  302. package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
  303. package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
  304. package/src/config/bundled-skills/contacts/SKILL.md +12 -45
  305. package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
  306. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
  307. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
  308. package/src/config/bundled-tool-registry.ts +0 -2
  309. package/src/config/feature-flag-registry.json +17 -17
  310. package/src/config/llm-resolver.ts +16 -1
  311. package/src/config/loader.ts +148 -33
  312. package/src/config/raw-config-utils.ts +2 -30
  313. package/src/config/schema.ts +4 -0
  314. package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
  315. package/src/config/schemas/call-site-catalog.ts +29 -7
  316. package/src/config/schemas/llm-request-logs.ts +57 -0
  317. package/src/config/schemas/llm.ts +52 -2
  318. package/src/config/schemas/memory-retrospective.ts +48 -0
  319. package/src/config/schemas/memory-v2.ts +33 -2
  320. package/src/config/schemas/memory.ts +4 -0
  321. package/src/config/schemas/services.ts +15 -12
  322. package/src/config/seed-inference-profiles.ts +195 -134
  323. package/src/contacts/contact-store.ts +0 -61
  324. package/src/context/window-manager.ts +191 -5
  325. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +111 -0
  326. package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
  327. package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
  328. package/src/daemon/approval-generators.ts +23 -29
  329. package/src/daemon/config-watcher.ts +2 -0
  330. package/src/daemon/conversation-agent-loop-handlers.ts +56 -0
  331. package/src/daemon/conversation-agent-loop.ts +140 -107
  332. package/src/daemon/conversation-error.ts +21 -0
  333. package/src/daemon/conversation-lifecycle.ts +68 -13
  334. package/src/daemon/conversation-process.ts +36 -19
  335. package/src/daemon/conversation-runtime-assembly.ts +14 -5
  336. package/src/daemon/conversation-slash.ts +175 -23
  337. package/src/daemon/conversation-store.ts +17 -10
  338. package/src/daemon/conversation-surfaces.ts +92 -26
  339. package/src/daemon/conversation-tool-setup.ts +33 -19
  340. package/src/daemon/conversation.ts +49 -10
  341. package/src/daemon/external-plugins-bootstrap.ts +18 -8
  342. package/src/daemon/guardian-action-generators.ts +7 -22
  343. package/src/daemon/handlers/config-model.ts +8 -126
  344. package/src/daemon/handlers/config-slack-channel.ts +10 -7
  345. package/src/daemon/handlers/config-vercel.ts +3 -1
  346. package/src/daemon/handlers/shared.ts +26 -0
  347. package/src/daemon/handlers/skills.ts +84 -5
  348. package/src/daemon/history-repair.ts +33 -6
  349. package/src/daemon/host-app-control-proxy.ts +44 -19
  350. package/src/daemon/host-bash-proxy.ts +85 -158
  351. package/src/daemon/host-browser-proxy.ts +97 -36
  352. package/src/daemon/host-cu-proxy.ts +1 -1
  353. package/src/daemon/host-file-proxy.ts +1 -1
  354. package/src/daemon/host-proxy-base.ts +13 -1
  355. package/src/daemon/host-proxy-preactivation.ts +25 -1
  356. package/src/daemon/host-transfer-proxy.ts +2 -2
  357. package/src/daemon/identity-helpers.ts +19 -0
  358. package/src/daemon/lifecycle.ts +128 -114
  359. package/src/daemon/meet-host-supervisor.ts +15 -15
  360. package/src/daemon/memory-v2-startup.ts +62 -14
  361. package/src/daemon/message-protocol.ts +6 -0
  362. package/src/daemon/message-types/bookmarks.ts +18 -0
  363. package/src/daemon/message-types/conversations.ts +12 -9
  364. package/src/daemon/message-types/messages.ts +28 -2
  365. package/src/daemon/message-types/sync.ts +60 -0
  366. package/src/daemon/pkb-reminder-builder.test.ts +54 -13
  367. package/src/daemon/pkb-reminder-builder.ts +21 -7
  368. package/src/daemon/process-message.ts +56 -23
  369. package/src/daemon/server.ts +23 -18
  370. package/src/daemon/shutdown-handlers.ts +0 -2
  371. package/src/daemon/tool-setup-types.ts +9 -0
  372. package/src/daemon/tool-side-effects.ts +6 -4
  373. package/src/daemon/wake-target-adapter.ts +11 -0
  374. package/src/documents/document-store.ts +35 -1
  375. package/src/export/transcript-formatter.ts +61 -2
  376. package/src/filing/filing-service.ts +42 -56
  377. package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
  378. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  379. package/src/heartbeat/heartbeat-service.ts +149 -128
  380. package/src/home/__tests__/feed-types.test.ts +63 -131
  381. package/src/home/__tests__/feed-writer.test.ts +77 -278
  382. package/src/home/__tests__/post-connect-feed.test.ts +9 -12
  383. package/src/home/feed-types.ts +19 -73
  384. package/src/home/feed-writer.ts +25 -156
  385. package/src/home/post-connect-feed.ts +1 -3
  386. package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
  387. package/src/ipc/__tests__/email-ipc.test.ts +506 -0
  388. package/src/ipc/__tests__/exit-helper.test.ts +104 -0
  389. package/src/ipc/__tests__/streaming-client.test.ts +237 -0
  390. package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
  391. package/src/ipc/assistant-server.ts +148 -42
  392. package/src/ipc/cli-client.ts +370 -50
  393. package/src/ipc/routes/db-proxy-transaction.ts +151 -0
  394. package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
  395. package/src/ipc/skill-routes/events.ts +30 -3
  396. package/src/ipc/skill-server.ts +99 -42
  397. package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
  398. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
  399. package/src/live-voice/live-voice-session-manager.ts +11 -4
  400. package/src/live-voice/live-voice-session.ts +14 -6
  401. package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
  402. package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
  403. package/src/memory/__tests__/conversation-types.test.ts +36 -0
  404. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
  405. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +10 -57
  406. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
  407. package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
  408. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
  409. package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
  410. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
  411. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
  412. package/src/memory/bookmark-crud.ts +179 -0
  413. package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
  414. package/src/memory/context-search/agent-protocol.ts +5 -1
  415. package/src/memory/context-search/agent-runner.ts +60 -85
  416. package/src/memory/context-search/limits.ts +1 -4
  417. package/src/memory/context-search/search.ts +23 -113
  418. package/src/memory/context-search/sources/conversations.ts +18 -6
  419. package/src/memory/context-search/sources/memory-v2.ts +40 -31
  420. package/src/memory/context-search/sources/memory.ts +9 -2
  421. package/src/memory/context-search/sources/workspace.ts +13 -10
  422. package/src/memory/context-search/types.ts +1 -1
  423. package/src/memory/conversation-bootstrap.ts +11 -0
  424. package/src/memory/conversation-crud.ts +312 -10
  425. package/src/memory/conversation-queries.ts +9 -5
  426. package/src/memory/conversation-title-service.ts +1 -0
  427. package/src/memory/conversation-types.ts +16 -0
  428. package/src/memory/db-init.ts +14 -0
  429. package/src/memory/embedding-backend.ts +2 -1
  430. package/src/memory/embedding-runtime-manager.ts +1 -2
  431. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +104 -61
  432. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
  433. package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
  434. package/src/memory/graph/conversation-graph-memory.ts +108 -14
  435. package/src/memory/graph/extraction.ts +4 -0
  436. package/src/memory/graph/graph-memory-state-store.ts +16 -3
  437. package/src/memory/graph/graph-search.test.ts +6 -5
  438. package/src/memory/graph/graph-search.ts +3 -4
  439. package/src/memory/graph/retriever.test.ts +12 -7
  440. package/src/memory/graph/retriever.ts +4 -5
  441. package/src/memory/graph/tool-handlers.ts +20 -11
  442. package/src/memory/graph/tools.ts +48 -9
  443. package/src/memory/indexer.ts +18 -2
  444. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +120 -6
  445. package/src/memory/jobs/embed-concept-page.ts +261 -89
  446. package/src/memory/jobs-store.ts +51 -1
  447. package/src/memory/jobs-worker.ts +60 -7
  448. package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
  449. package/src/memory/llm-request-log-source-local.ts +26 -0
  450. package/src/memory/llm-request-log-source.ts +97 -0
  451. package/src/memory/llm-request-log-store.ts +1 -1
  452. package/src/memory/memory-retrospective-constants.ts +13 -0
  453. package/src/memory/memory-retrospective-enqueue.ts +114 -0
  454. package/src/memory/memory-retrospective-job.ts +351 -0
  455. package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
  456. package/src/memory/memory-retrospective-state.ts +162 -0
  457. package/src/memory/memory-retrospective-trigger-check.ts +91 -0
  458. package/src/memory/memory-v2-activation-log-store.ts +49 -5
  459. package/src/memory/memory-v2-concept-frequency.ts +4 -0
  460. package/src/memory/message-content.ts +38 -1
  461. package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
  462. package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
  463. package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
  464. package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
  465. package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
  466. package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
  467. package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
  468. package/src/memory/migrations/242-message-bookmarks.ts +38 -0
  469. package/src/memory/migrations/243-provider-connections.ts +68 -0
  470. package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
  471. package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
  472. package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
  473. package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
  474. package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
  475. package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
  476. package/src/memory/migrations/index.ts +7 -0
  477. package/src/memory/pkb/pkb-search.test.ts +6 -5
  478. package/src/memory/pkb/pkb-search.ts +4 -5
  479. package/src/memory/published-pages-store.ts +16 -0
  480. package/src/memory/qdrant-client.ts +3 -0
  481. package/src/memory/schema/bookmarks.ts +38 -0
  482. package/src/memory/schema/conversations.ts +2 -0
  483. package/src/memory/schema/index.ts +2 -0
  484. package/src/memory/schema/inference.ts +29 -0
  485. package/src/memory/schema/memory-core.ts +9 -0
  486. package/src/memory/search/semantic.ts +5 -9
  487. package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
  488. package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
  489. package/src/memory/v2/__tests__/activation.test.ts +46 -9
  490. package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
  491. package/src/memory/v2/__tests__/consolidation-job.test.ts +140 -163
  492. package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
  493. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
  494. package/src/memory/v2/__tests__/injection.test.ts +768 -33
  495. package/src/memory/v2/__tests__/migration.test.ts +7 -3
  496. package/src/memory/v2/__tests__/page-index.test.ts +277 -0
  497. package/src/memory/v2/__tests__/page-store.test.ts +14 -1
  498. package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
  499. package/src/memory/v2/__tests__/qdrant.test.ts +382 -9
  500. package/src/memory/v2/__tests__/reranker.test.ts +4 -4
  501. package/src/memory/v2/__tests__/router.test.ts +516 -0
  502. package/src/memory/v2/__tests__/sim.test.ts +163 -8
  503. package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
  504. package/src/memory/v2/__tests__/static-context.test.ts +8 -35
  505. package/src/memory/v2/__tests__/sweep-job.test.ts +114 -33
  506. package/src/memory/v2/activation-store.ts +34 -5
  507. package/src/memory/v2/activation.ts +40 -27
  508. package/src/memory/v2/backfill-jobs.ts +17 -84
  509. package/src/memory/v2/consolidation-job.ts +92 -86
  510. package/src/memory/v2/frontmatter-sweep.ts +91 -0
  511. package/src/memory/v2/injection.ts +466 -115
  512. package/src/memory/v2/migration.ts +117 -20
  513. package/src/memory/v2/page-index.ts +191 -0
  514. package/src/memory/v2/page-store.ts +42 -0
  515. package/src/memory/v2/prompts/consolidation.ts +14 -7
  516. package/src/memory/v2/prompts/router.ts +192 -0
  517. package/src/memory/v2/qdrant.ts +307 -133
  518. package/src/memory/v2/reranker.ts +14 -7
  519. package/src/memory/v2/router.ts +322 -0
  520. package/src/memory/v2/sim.ts +88 -34
  521. package/src/memory/v2/skill-store.ts +118 -29
  522. package/src/memory/v2/static-context.ts +20 -17
  523. package/src/memory/v2/sweep-job.ts +127 -102
  524. package/src/memory/v2/types.ts +16 -5
  525. package/src/memory/validation.ts +13 -0
  526. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
  527. package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
  528. package/src/notifications/__tests__/signal-registry.test.ts +17 -0
  529. package/src/notifications/adapters/platform.ts +171 -0
  530. package/src/notifications/conversation-pairing.ts +2 -2
  531. package/src/notifications/copy-composer.ts +61 -12
  532. package/src/notifications/decision-engine.ts +46 -0
  533. package/src/notifications/destination-resolver.ts +21 -0
  534. package/src/notifications/emit-signal.ts +28 -1
  535. package/src/notifications/home-feed-side-effect.ts +111 -0
  536. package/src/notifications/signal.ts +5 -0
  537. package/src/permissions/checker.ts +12 -0
  538. package/src/permissions/gateway-threshold-reader.ts +116 -8
  539. package/src/permissions/ipc-risk-types.ts +2 -0
  540. package/src/permissions/prompter.ts +86 -96
  541. package/src/permissions/secret-prompter.ts +31 -31
  542. package/src/plugin-api/index.ts +13 -0
  543. package/src/plugin-api/package.json +12 -0
  544. package/src/plugin-api/types.ts +62 -0
  545. package/src/plugins/defaults/injectors.ts +20 -5
  546. package/src/plugins/external-plugin-loader.ts +294 -0
  547. package/src/plugins/types.ts +46 -30
  548. package/src/plugins/user-loader.ts +64 -41
  549. package/src/proactive-artifact/job.test.ts +63 -8
  550. package/src/proactive-artifact/job.ts +20 -2
  551. package/src/proactive-artifact/message-copy.ts +18 -1
  552. package/src/proactive-artifact/trigger-state.test.ts +9 -0
  553. package/src/proactive-artifact/trigger-state.ts +4 -0
  554. package/src/prompts/__tests__/system-prompt.test.ts +105 -0
  555. package/src/prompts/system-prompt.ts +22 -1
  556. package/src/prompts/templates/SOUL.md +13 -28
  557. package/src/prompts/update-bulletin-job.ts +61 -73
  558. package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
  559. package/src/providers/__tests__/inference.test.ts +288 -0
  560. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  561. package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
  562. package/src/providers/__tests__/retry-callsite.test.ts +14 -32
  563. package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
  564. package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
  565. package/src/providers/anthropic/client.ts +95 -26
  566. package/src/providers/call-site-routing.ts +94 -16
  567. package/src/providers/connection-resolution.ts +163 -0
  568. package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
  569. package/src/providers/inference/adapter-factory.ts +173 -0
  570. package/src/providers/inference/auth.ts +112 -0
  571. package/src/providers/inference/backfill.ts +196 -0
  572. package/src/providers/inference/connections.ts +356 -0
  573. package/src/providers/inference/resolve-auth.ts +65 -0
  574. package/src/providers/model-catalog.ts +104 -6
  575. package/src/providers/openai/responses-provider.ts +4 -2
  576. package/src/providers/provider-env-vars.ts +17 -7
  577. package/src/providers/provider-secret-catalog.ts +49 -30
  578. package/src/providers/provider-send-message.ts +41 -20
  579. package/src/providers/registry.ts +143 -159
  580. package/src/providers/retry.ts +18 -10
  581. package/src/providers/search-provider-catalog.ts +121 -0
  582. package/src/runtime/AGENTS.md +18 -5
  583. package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
  584. package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
  585. package/src/runtime/actor-trust-resolver.ts +32 -10
  586. package/src/runtime/agent-wake.ts +35 -6
  587. package/src/runtime/assistant-event-hub.ts +3 -85
  588. package/src/runtime/auth/route-policy.ts +304 -8
  589. package/src/runtime/auth/same-actor.ts +2 -0
  590. package/src/runtime/background-job-runner.ts +339 -0
  591. package/src/runtime/btw-sidechain.ts +1 -0
  592. package/src/runtime/channel-approvals.ts +3 -2
  593. package/src/runtime/guardian-reply-router.ts +0 -10
  594. package/src/runtime/http-router.ts +36 -1
  595. package/src/runtime/http-server.ts +31 -5
  596. package/src/runtime/http-types.ts +2 -0
  597. package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
  598. package/src/runtime/middleware/request-logger.ts +62 -1
  599. package/src/runtime/pending-interactions.ts +19 -15
  600. package/src/runtime/pre-first-message-gate.ts +83 -0
  601. package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
  602. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
  603. package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
  604. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
  605. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
  606. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
  607. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
  608. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
  609. package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
  610. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
  611. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  612. package/src/runtime/routes/acp-routes.ts +10 -8
  613. package/src/runtime/routes/app-management-routes.ts +228 -3
  614. package/src/runtime/routes/approval-routes.ts +7 -21
  615. package/src/runtime/routes/audit-routes.ts +43 -0
  616. package/src/runtime/routes/auth-routes.ts +72 -0
  617. package/src/runtime/routes/avatar-routes.ts +273 -20
  618. package/src/runtime/routes/backup-routes.ts +406 -2
  619. package/src/runtime/routes/bookmark-routes.ts +154 -0
  620. package/src/runtime/routes/channel-verification-routes.ts +2 -1
  621. package/src/runtime/routes/consolidation-routes.ts +8 -9
  622. package/src/runtime/routes/contact-routes.ts +0 -160
  623. package/src/runtime/routes/conversation-cli-routes.ts +192 -0
  624. package/src/runtime/routes/conversation-management-routes.ts +30 -43
  625. package/src/runtime/routes/conversation-query-routes.ts +373 -82
  626. package/src/runtime/routes/conversation-routes.ts +31 -10
  627. package/src/runtime/routes/conversations-import-routes.ts +229 -0
  628. package/src/runtime/routes/credential-routes.ts +540 -0
  629. package/src/runtime/routes/debug-bash-routes.ts +2 -0
  630. package/src/runtime/routes/debug-routes.ts +2 -2
  631. package/src/runtime/routes/document-pdf-renderer.ts +5 -1
  632. package/src/runtime/routes/domain-routes.ts +167 -0
  633. package/src/runtime/routes/email-routes.ts +603 -0
  634. package/src/runtime/routes/errors.ts +2 -2
  635. package/src/runtime/routes/events-routes.ts +192 -0
  636. package/src/runtime/routes/filing-routes.ts +2 -3
  637. package/src/runtime/routes/home-feed-routes.ts +6 -78
  638. package/src/runtime/routes/host-app-control-routes.ts +44 -2
  639. package/src/runtime/routes/host-browser-routes.ts +103 -22
  640. package/src/runtime/routes/http-adapter.ts +2 -0
  641. package/src/runtime/routes/identity-routes.ts +5 -0
  642. package/src/runtime/routes/image-generation-routes.ts +99 -0
  643. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
  644. package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
  645. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
  646. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -7
  647. package/src/runtime/routes/index.ts +36 -0
  648. package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
  649. package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
  650. package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
  651. package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
  652. package/src/runtime/routes/inference-send-routes.ts +115 -0
  653. package/src/runtime/routes/integrations/twilio.ts +1 -0
  654. package/src/runtime/routes/mcp-auth-routes.ts +283 -9
  655. package/src/runtime/routes/memory-item-routes.test.ts +3 -9
  656. package/src/runtime/routes/memory-item-routes.ts +5 -6
  657. package/src/runtime/routes/memory-v2-routes.ts +105 -404
  658. package/src/runtime/routes/notification-routes.ts +2 -0
  659. package/src/runtime/routes/oauth-apps.ts +112 -7
  660. package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
  661. package/src/runtime/routes/oauth-connect-routes.ts +67 -5
  662. package/src/runtime/routes/oauth-providers.ts +298 -8
  663. package/src/runtime/routes/platform-routes.ts +336 -0
  664. package/src/runtime/routes/playground/inject-failures.ts +2 -1
  665. package/src/runtime/routes/playground/reset-circuit.ts +2 -1
  666. package/src/runtime/routes/playground/state.ts +2 -1
  667. package/src/runtime/routes/publish-routes.ts +221 -0
  668. package/src/runtime/routes/schedule-routes.ts +82 -0
  669. package/src/runtime/routes/sequence-routes.ts +291 -0
  670. package/src/runtime/routes/settings-routes.ts +2 -10
  671. package/src/runtime/routes/skills-routes.ts +31 -1
  672. package/src/runtime/routes/stt-routes.ts +240 -3
  673. package/src/runtime/routes/surface-action-routes.ts +43 -7
  674. package/src/runtime/routes/tts-routes.ts +67 -0
  675. package/src/runtime/routes/types.ts +32 -0
  676. package/src/runtime/routes/user-routes-cli.ts +243 -0
  677. package/src/runtime/routes/webhook-routes.ts +165 -0
  678. package/src/runtime/sync/resource-sync-events.ts +25 -0
  679. package/src/runtime/sync/sync-publisher.test.ts +105 -0
  680. package/src/runtime/sync/sync-publisher.ts +21 -0
  681. package/src/schedule/scheduler.ts +200 -123
  682. package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
  683. package/src/security/secret-patterns.ts +3 -0
  684. package/src/sequence/engine.ts +38 -40
  685. package/src/skills/include-graph.ts +35 -13
  686. package/src/subagent/manager.ts +20 -15
  687. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
  688. package/src/tools/browser/browser-execution.ts +15 -4
  689. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
  690. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
  691. package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
  692. package/src/tools/browser/cdp-client/factory.ts +66 -5
  693. package/src/tools/browser/runtime-check.ts +77 -0
  694. package/src/tools/document/document-tool.ts +20 -0
  695. package/src/tools/executor.ts +18 -2
  696. package/src/tools/memory/register.test.ts +10 -8
  697. package/src/tools/memory/register.ts +9 -1
  698. package/src/tools/network/__tests__/web-search.test.ts +156 -0
  699. package/src/tools/network/web-search.ts +280 -37
  700. package/src/tools/permission-checker.ts +28 -5
  701. package/src/tools/skills/load.ts +24 -20
  702. package/src/tools/subagent/spawn.ts +3 -3
  703. package/src/tools/terminal/shell.ts +44 -0
  704. package/src/tools/tool-name-aliases.ts +19 -0
  705. package/src/tools/types.ts +19 -1
  706. package/src/usage/attribution.ts +3 -2
  707. package/src/util/pricing.ts +86 -160
  708. package/src/watcher/__tests__/engine.test.ts +301 -0
  709. package/src/watcher/constants.ts +7 -0
  710. package/src/watcher/engine.ts +90 -90
  711. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
  712. package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
  713. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
  714. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +4 -62
  715. package/src/workspace/migrations/069-seed-onboarding-threads.ts +34 -0
  716. package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
  717. package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
  718. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
  719. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
  720. package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
  721. package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
  722. package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
  723. package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
  724. package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
  725. package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
  726. package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
  727. package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
  728. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
  729. package/src/workspace/migrations/registry.ts +28 -0
  730. package/src/workspace/migrations/runner.ts +13 -2
  731. package/src/workspace/migrations/types.ts +13 -3
  732. package/src/workspace/provider-commit-message-generator.ts +3 -2
  733. package/src/__tests__/context-search-pkb-source.test.ts +0 -492
  734. package/src/__tests__/credentials-cli.test.ts +0 -1225
  735. package/src/__tests__/memory-admin-recall.test.ts +0 -213
  736. package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
  737. package/src/cli/commands/__tests__/email-download.test.ts +0 -260
  738. package/src/cli/commands/__tests__/email-list.test.ts +0 -216
  739. package/src/cli/commands/__tests__/email-register.test.ts +0 -186
  740. package/src/cli/commands/__tests__/email-send.test.ts +0 -416
  741. package/src/cli/commands/__tests__/email-status.test.ts +0 -185
  742. package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
  743. package/src/cli/commands/__tests__/routes.test.ts +0 -562
  744. package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
  745. package/src/cli/commands/autonomy.ts +0 -365
  746. package/src/cli/commands/memory.ts +0 -424
  747. package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -1201
  748. package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
  749. package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
  750. package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
  751. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
  752. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
  753. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
  754. package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
  755. package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
  756. package/src/cli/lib/daemon-avatar-client.ts +0 -37
  757. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
  758. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
  759. package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
  760. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
  761. package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
  762. package/src/home/__tests__/emit-feed-event.test.ts +0 -169
  763. package/src/home/__tests__/feed-population-integration.test.ts +0 -312
  764. package/src/home/__tests__/feed-scheduler.test.ts +0 -222
  765. package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
  766. package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
  767. package/src/home/__tests__/rollup-producer.test.ts +0 -507
  768. package/src/home/assistant-feed-authoring.ts +0 -135
  769. package/src/home/emit-feed-event.ts +0 -169
  770. package/src/home/feed-scheduler.ts +0 -281
  771. package/src/home/platform-gmail-digest.ts +0 -163
  772. package/src/home/rewrite-command-preview.ts +0 -66
  773. package/src/home/rewrite-feed-title.ts +0 -58
  774. package/src/home/rollup-producer.ts +0 -426
  775. package/src/memory/admin.ts +0 -326
  776. package/src/memory/context-search/sources/pkb.ts +0 -477
  777. package/src/memory/graph/compaction.ts +0 -299
  778. /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
@@ -5,17 +5,16 @@
5
5
  * One representative call site (the `installSkill` bundled branch) is
6
6
  * exercised — all 5 sites share the same delegation to
7
7
  * `maybeSeedMemoryV2Skills`, so a single suite covers behavior. Validates:
8
- * - flag + config both on → helper invoked after seedSkillGraphNodes
9
- * and the seed observed (callOrder picks up "v2")
10
- * - flag off → helper still invoked, but the seed short-circuits
11
- * - config.memory.v2.enabled off → helper still invoked, seed short-circuits
8
+ * - config on → helper invoked after seedSkillGraphNodes and the seed
9
+ * observed (callOrder picks up "v2")
10
+ * - config off → helper still invoked, but the seed short-circuits
12
11
  *
13
12
  * The handler delegates to `maybeSeedMemoryV2Skills` from
14
13
  * `daemon/memory-v2-startup.ts`. We mock that module directly so the test
15
14
  * does not have to drain the dynamic-import microtask chain. The helper's
16
- * gate semantics (flag + config + rejection swallowing) are covered by
17
- * `lifecycle-memory-v2-seed.test.ts`; here we only verify that the
18
- * handler invokes the helper synchronously with the live config.
15
+ * gate semantics are covered by `lifecycle-memory-v2-seed.test.ts`; here
16
+ * we only verify that the handler invokes the helper synchronously with
17
+ * the live config.
19
18
  */
20
19
  import { beforeEach, describe, expect, mock, test } from "bun:test";
21
20
 
@@ -23,7 +22,7 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
23
22
  // Programmable test state
24
23
  // ---------------------------------------------------------------------------
25
24
 
26
- const flagsState = { flagEnabled: true, configV2Enabled: true };
25
+ const flagsState = { configV2Enabled: true };
27
26
 
28
27
  const callOrder: string[] = [];
29
28
 
@@ -55,10 +54,7 @@ mock.module("../config/skills.js", () => ({
55
54
  }));
56
55
 
57
56
  mock.module("../config/assistant-feature-flags.js", () => ({
58
- isAssistantFeatureFlagEnabled: (key: string) => {
59
- if (key === "memory-v2-enabled") return flagsState.flagEnabled;
60
- return true;
61
- },
57
+ isAssistantFeatureFlagEnabled: () => true,
62
58
  }));
63
59
 
64
60
  // Stub both `getConfig` and `loadConfig`. `loadConfig` is reached by code
@@ -220,18 +216,17 @@ const { installSkill } = await import("../daemon/handlers/skills.js");
220
216
 
221
217
  describe("v2 skill re-seed gating in skill handlers", () => {
222
218
  beforeEach(() => {
223
- flagsState.flagEnabled = true;
224
219
  flagsState.configV2Enabled = true;
225
220
  callOrder.length = 0;
226
221
  mockSeedSkillGraphNodes.mockClear();
227
222
  mockMaybeSeedMemoryV2Skills.mockClear();
228
223
  mockMaybeSeedMemoryV2Skills.mockImplementation((config) => {
229
- if (!flagsState.flagEnabled || !config.memory.v2.enabled) return;
224
+ if (!config.memory.v2.enabled) return;
230
225
  callOrder.push("v2");
231
226
  });
232
227
  });
233
228
 
234
- test("flag + config both on → maybeSeedMemoryV2Skills invoked after seedSkillGraphNodes", async () => {
229
+ test("config on → maybeSeedMemoryV2Skills invoked after seedSkillGraphNodes", async () => {
235
230
  const result = await installSkill({ slug: "bundled-skill" });
236
231
 
237
232
  expect(result.success).toBe(true);
@@ -240,17 +235,6 @@ describe("v2 skill re-seed gating in skill handlers", () => {
240
235
  expect(callOrder).toEqual(["v1", "v2"]);
241
236
  });
242
237
 
243
- test("flag off → seed mock observes the disabled flag and skips", async () => {
244
- flagsState.flagEnabled = false;
245
-
246
- const result = await installSkill({ slug: "bundled-skill" });
247
-
248
- expect(result.success).toBe(true);
249
- expect(mockSeedSkillGraphNodes).toHaveBeenCalledTimes(1);
250
- expect(mockMaybeSeedMemoryV2Skills).toHaveBeenCalledTimes(1);
251
- expect(callOrder).toEqual(["v1"]);
252
- });
253
-
254
238
  test("config.memory.v2.enabled off → seed mock observes config and skips", async () => {
255
239
  flagsState.configV2Enabled = false;
256
240
 
@@ -261,12 +245,4 @@ describe("v2 skill re-seed gating in skill handlers", () => {
261
245
  expect(mockMaybeSeedMemoryV2Skills).toHaveBeenCalledTimes(1);
262
246
  expect(callOrder).toEqual(["v1"]);
263
247
  });
264
-
265
- // Note: "seed rejection swallowed" is now an internal concern of
266
- // `maybeSeedMemoryV2Skills` — it dispatches the seed call as a
267
- // fire-and-forget promise with `.catch(log.warn)`. That behavior is
268
- // covered by `lifecycle-memory-v2-seed.test.ts`. From the handler's
269
- // perspective, we only need to verify the helper is invoked
270
- // synchronously with the correct config — which the cases above already
271
- // exercise.
272
248
  });
@@ -1,5 +1,12 @@
1
1
  import { beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
+ // Default the warm-pool gate to OPEN — these tests probe disk-pressure
4
+ // behavior, not the pre-first-message guard.
5
+ mock.module("../runtime/pre-first-message-gate.js", () => ({
6
+ hasReceivedUserMessage: () => true,
7
+ _resetPreFirstMessageGateCacheForTests: () => {},
8
+ }));
9
+
3
10
  mock.module("../util/logger.js", () => ({
4
11
  getLogger: () => ({
5
12
  info: () => {},
@@ -63,6 +70,10 @@ mock.module("../memory/conversation-crud.js", () => ({
63
70
  createdConversations.push(opts);
64
71
  return { id: "conv-1", ...opts };
65
72
  },
73
+ // runBackgroundJob (loaded transitively via heartbeat-service) imports
74
+ // addMessage. Disk-pressure short-circuits before addMessage ever runs,
75
+ // but the mock module must still expose every name the real module does.
76
+ addMessage: () => Promise.resolve({ id: "mock-msg-id" }),
66
77
  }));
67
78
 
68
79
  const mockProcessMessage = mock(() => Promise.resolve({ messageId: "msg-1" }));
@@ -70,9 +81,11 @@ mock.module("../daemon/process-message.js", () => ({
70
81
  processMessage: mockProcessMessage,
71
82
  }));
72
83
 
73
- const mockEmitFeedEvent = mock(() => Promise.resolve());
74
- mock.module("../home/emit-feed-event.js", () => ({
75
- emitFeedEvent: mockEmitFeedEvent,
84
+ const emittedNotificationSignals: Array<{ sourceEventName?: string }> = [];
85
+ mock.module("../notifications/emit-signal.js", () => ({
86
+ emitNotificationSignal: async (opts: { sourceEventName?: string }) => {
87
+ emittedNotificationSignals.push({ sourceEventName: opts.sourceEventName });
88
+ },
76
89
  }));
77
90
 
78
91
  mock.module("../prompts/persona-resolver.js", () => ({
@@ -131,10 +144,10 @@ describe("HeartbeatService disk pressure gate", () => {
131
144
  mockMarkStaleRunningAsError.mockClear();
132
145
  mockMarkStaleRunningAsError.mockImplementation(() => 0);
133
146
  mockProcessMessage.mockClear();
134
- mockEmitFeedEvent.mockClear();
147
+ emittedNotificationSignals.length = 0;
135
148
  });
136
149
 
137
- test("skips without creating heartbeat rows, conversations, or feed events", async () => {
150
+ test("skips without creating heartbeat rows, conversations, or notifications", async () => {
138
151
  const service = new HeartbeatService({
139
152
  alerter: () => {},
140
153
  });
@@ -148,7 +161,7 @@ describe("HeartbeatService disk pressure gate", () => {
148
161
  expect(mockSkipHeartbeatRun).not.toHaveBeenCalled();
149
162
  expect(createdConversations).toHaveLength(0);
150
163
  expect(mockProcessMessage).not.toHaveBeenCalled();
151
- expect(mockEmitFeedEvent).not.toHaveBeenCalled();
164
+ expect(emittedNotificationSignals).toHaveLength(0);
152
165
  });
153
166
 
154
167
  test("allows forced user-initiated heartbeat runs while locked", async () => {
@@ -168,7 +181,7 @@ describe("HeartbeatService disk pressure gate", () => {
168
181
  expect(mockProcessMessage).toHaveBeenCalledTimes(1);
169
182
  });
170
183
 
171
- test("start recovery skips missed-run feed events while locked", async () => {
184
+ test("start recovery skips missed-run notifications while locked", async () => {
172
185
  mockMarkStaleRunsAsMissed.mockImplementationOnce(() => 1);
173
186
  const service = new HeartbeatService({
174
187
  alerter: () => {},
@@ -178,6 +191,6 @@ describe("HeartbeatService disk pressure gate", () => {
178
191
  await service.stop();
179
192
 
180
193
  expect(mockMarkStaleRunsAsMissed).toHaveBeenCalledTimes(1);
181
- expect(mockEmitFeedEvent).not.toHaveBeenCalled();
194
+ expect(emittedNotificationSignals).toHaveLength(0);
182
195
  });
183
196
  });
@@ -1,17 +1,17 @@
1
1
  import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import {
4
- afterEach,
5
- beforeEach,
6
- describe,
7
- expect,
8
- jest,
9
- mock,
10
- test,
11
- } from "bun:test";
3
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
12
4
 
13
5
  const testWorkspaceDir = process.env.VELLUM_WORKSPACE_DIR!;
14
6
 
7
+ // Default the warm-pool gate to OPEN for existing tests — they predate
8
+ // the gate and expect heartbeat/filing/etc. to run on every tick. Tests
9
+ // that specifically exercise the gate path override this mock locally.
10
+ mock.module("../runtime/pre-first-message-gate.js", () => ({
11
+ hasReceivedUserMessage: () => true,
12
+ _resetPreFirstMessageGateCacheForTests: () => {},
13
+ }));
14
+
15
15
  // ── Heartbeat run store mock ───────────────────────────────────────
16
16
  const mockInsertPendingHeartbeatRun = mock(() => "mock-run-id");
17
17
  const mockStartHeartbeatRun = mock(() => true);
@@ -34,12 +34,6 @@ mock.module("../heartbeat/heartbeat-run-store.js", () => ({
34
34
  countCompletedHeartbeatRuns: mockCountCompletedHeartbeatRuns,
35
35
  }));
36
36
 
37
- // ── Feed event mock ───────────────────────────────────────────────
38
- const mockEmitFeedEvent = mock(() => Promise.resolve());
39
- mock.module("../home/emit-feed-event.js", () => ({
40
- emitFeedEvent: mockEmitFeedEvent,
41
- }));
42
-
43
37
  // Mock config loader
44
38
  let mockConfig = {
45
39
  heartbeat: {
@@ -383,8 +377,6 @@ describe("HeartbeatService", () => {
383
377
  mockListHeartbeatRuns.mockImplementation(() => []);
384
378
  mockCountCompletedHeartbeatRuns.mockClear();
385
379
  mockCountCompletedHeartbeatRuns.mockImplementation(() => 10);
386
- mockEmitFeedEvent.mockClear();
387
- mockEmitFeedEvent.mockImplementation(() => Promise.resolve());
388
380
 
389
381
  mockConfig = {
390
382
  heartbeat: {
@@ -848,15 +840,11 @@ describe("HeartbeatService", () => {
848
840
  await service.runOnce();
849
841
  await new Promise((resolve) => setTimeout(resolve, 0));
850
842
 
851
- expect(conversationCreatedCalls).toHaveLength(0);
843
+ // The conversation surfaces to the sidebar via the runner's bootstrap
844
+ // callback for *every* heartbeat — "silent OK" means no notification
845
+ // signal is emitted, not that the conversation is hidden.
846
+ expect(conversationCreatedCalls).toHaveLength(1);
852
847
  expect(emittedNotificationSignals).toHaveLength(0);
853
- const successFeedCalls = mockEmitFeedEvent.mock.calls.filter(
854
- (call: unknown[]) => {
855
- const opts = call[0] as { dedupKey?: string };
856
- return opts.dedupKey?.startsWith("heartbeat:ok:");
857
- },
858
- );
859
- expect(successFeedCalls).toHaveLength(0);
860
848
  });
861
849
 
862
850
  test("HEARTBEAT_OK stays silent when earlier content mentions HEARTBEAT_ALERT", async () => {
@@ -897,7 +885,9 @@ describe("HeartbeatService", () => {
897
885
  await service.runOnce();
898
886
  await new Promise((resolve) => setTimeout(resolve, 0));
899
887
 
900
- expect(conversationCreatedCalls).toHaveLength(0);
888
+ // Conversation surfaces via the runner bootstrap, but no notification
889
+ // is emitted since the disposition is OK.
890
+ expect(conversationCreatedCalls).toHaveLength(1);
901
891
  expect(emittedNotificationSignals).toHaveLength(0);
902
892
  });
903
893
 
@@ -1564,7 +1554,10 @@ describe("HeartbeatService", () => {
1564
1554
  await service.runOnce();
1565
1555
  await new Promise((resolve) => setTimeout(resolve, 0));
1566
1556
 
1567
- expect(conversationCreatedCalls).toHaveLength(0);
1557
+ // The bootstrap-time surface fires regardless of CAS (it happens
1558
+ // before completeHeartbeatRun). CAS-false suppresses the alert
1559
+ // notification emit but not the sidebar entry.
1560
+ expect(conversationCreatedCalls).toHaveLength(1);
1568
1561
  expect(emittedNotificationSignals).toHaveLength(0);
1569
1562
  });
1570
1563
 
@@ -1581,15 +1574,6 @@ describe("HeartbeatService", () => {
1581
1574
 
1582
1575
  // completeHeartbeatRun returned false, so alerter should NOT be called
1583
1576
  expect(alerterCalls).toHaveLength(0);
1584
-
1585
- // No failure feed event either
1586
- const failCalls = mockEmitFeedEvent.mock.calls.filter(
1587
- (call: unknown[]) => {
1588
- const opts = call[0] as { dedupKey?: string };
1589
- return opts.dedupKey?.startsWith("heartbeat:fail:");
1590
- },
1591
- );
1592
- expect(failCalls).toHaveLength(0);
1593
1577
  });
1594
1578
 
1595
1579
  test("active-hours skip calls skipHeartbeatRun", async () => {
@@ -1738,39 +1722,14 @@ describe("HeartbeatService", () => {
1738
1722
  expect(mockSupersedePendingRun).toHaveBeenCalledWith("mock-run-id");
1739
1723
  });
1740
1724
 
1741
- test("timeout calls completeHeartbeatRun with status timeout", async () => {
1742
- jest.useFakeTimers();
1743
- try {
1744
- let resolveRun: () => void;
1745
- const runPromise = new Promise<void>((r) => {
1746
- resolveRun = r;
1747
- });
1748
-
1749
- const service = createService({
1750
- processMessage: async () => {
1751
- await runPromise;
1752
- return { messageId: "msg-1" };
1753
- },
1754
- });
1755
-
1756
- const runOncePromise = service.runOnce();
1757
- // Advance past the 30-minute timeout
1758
- jest.advanceTimersByTime(30 * 60 * 1000 + 1000);
1759
- await runOncePromise;
1760
-
1761
- expect(mockCompleteHeartbeatRun).toHaveBeenCalledWith("mock-run-id", {
1762
- status: "timeout",
1763
- error: "Heartbeat execution exceeded the 30-minute timeout",
1764
- });
1765
-
1766
- // Clean up — resolve the hanging promise so it doesn't leak
1767
- resolveRun!();
1768
- } finally {
1769
- jest.useRealTimers();
1770
- }
1771
- });
1725
+ // Note: the heartbeat-specific behavior on timeout is the trivial
1726
+ // `errorKind === "timeout" ? "timeout" : "error"` mapping. The runner
1727
+ // owns the actual timeout race (covered in
1728
+ // `background-job-runner.test.ts`), so we don't reproduce its
1729
+ // setTimeout-based timing here fake timers don't reliably propagate
1730
+ // into the runner's module scope across bun versions.
1772
1731
 
1773
- test("failure feed event has urgency high and includes error message", async () => {
1732
+ test("failure emits activity.failed notification with errorKind exception", async () => {
1774
1733
  const service = createService({
1775
1734
  processMessage: async () => {
1776
1735
  throw new Error("web_search outage");
@@ -1779,192 +1738,50 @@ describe("HeartbeatService", () => {
1779
1738
 
1780
1739
  await service.runOnce();
1781
1740
 
1782
- const failCalls = mockEmitFeedEvent.mock.calls.filter(
1783
- (call: unknown[]) => {
1784
- const opts = call[0] as { title?: string };
1785
- return opts.title === "Heartbeat Failed";
1786
- },
1787
- );
1788
- expect(failCalls).toHaveLength(1);
1789
- const opts = (failCalls as any[][])[0][0] as {
1790
- urgency?: string;
1791
- summary?: string;
1792
- };
1793
- expect(opts.urgency).toBe("high");
1794
- expect(opts.summary).toContain("web_search outage");
1795
- });
1796
-
1797
- test("CAS false on complete suppresses failure feed event", async () => {
1798
- mockCompleteHeartbeatRun.mockImplementation(() => false);
1799
-
1800
- const service = createService({
1801
- processMessage: async () => {
1802
- throw new Error("some error");
1803
- },
1804
- });
1805
-
1806
- await service.runOnce();
1807
-
1808
- const failCalls = mockEmitFeedEvent.mock.calls.filter(
1809
- (call: unknown[]) => {
1810
- const opts = call[0] as { title?: string };
1811
- return opts.title === "Heartbeat Failed";
1812
- },
1741
+ const failSignals = emittedNotificationSignals.filter(
1742
+ (s) => s.sourceEventName === "activity.failed",
1813
1743
  );
1814
- expect(failCalls).toHaveLength(0);
1744
+ expect(failSignals).toHaveLength(1);
1745
+ const signal = failSignals[0]!;
1746
+ expect(signal.contextPayload.jobName).toBe("heartbeat");
1747
+ expect(signal.contextPayload.errorKind).toBe("exception");
1748
+ expect(signal.contextPayload.errorMessage).toContain("web_search outage");
1749
+ expect(signal.attentionHints?.urgency).toBe("medium");
1750
+ expect(signal.attentionHints?.isAsyncBackground).toBe(true);
1815
1751
  });
1816
1752
 
1817
- test("timeout emits feed event with urgency high", async () => {
1818
- jest.useFakeTimers();
1819
- try {
1820
- let resolveRun: () => void;
1821
- const runPromise = new Promise<void>((r) => {
1822
- resolveRun = r;
1823
- });
1824
-
1825
- const service = createService({
1826
- processMessage: async () => {
1827
- await runPromise;
1828
- return { messageId: "msg-1" };
1829
- },
1830
- });
1831
-
1832
- const runOncePromise = service.runOnce();
1833
- jest.advanceTimersByTime(30 * 60 * 1000 + 1000);
1834
- await runOncePromise;
1835
-
1836
- const timeoutCalls = mockEmitFeedEvent.mock.calls.filter(
1837
- (call: unknown[]) => {
1838
- const opts = call[0] as { title?: string };
1839
- return opts.title === "Heartbeat Timed Out";
1840
- },
1841
- );
1842
- expect(timeoutCalls).toHaveLength(1);
1843
- const opts = (timeoutCalls as any[][])[0][0] as {
1844
- urgency?: string;
1845
- };
1846
- expect(opts.urgency).toBe("high");
1847
-
1848
- resolveRun!();
1849
- } finally {
1850
- jest.useRealTimers();
1851
- }
1852
- });
1853
-
1854
- test("CAS false on timeout suppresses timeout feed event", async () => {
1855
- jest.useFakeTimers();
1856
- try {
1857
- mockCompleteHeartbeatRun.mockImplementation(() => false);
1858
-
1859
- let resolveRun: () => void;
1860
- const runPromise = new Promise<void>((r) => {
1861
- resolveRun = r;
1862
- });
1863
-
1864
- const service = createService({
1865
- processMessage: async () => {
1866
- await runPromise;
1867
- return { messageId: "msg-1" };
1868
- },
1869
- });
1870
-
1871
- const runOncePromise = service.runOnce();
1872
- jest.advanceTimersByTime(30 * 60 * 1000 + 1000);
1873
- await runOncePromise;
1874
-
1875
- // completeHeartbeatRun returned false, so no timeout feed event
1876
- const timeoutCalls = mockEmitFeedEvent.mock.calls.filter(
1877
- (call: unknown[]) => {
1878
- const opts = call[0] as { title?: string };
1879
- return opts.title === "Heartbeat Timed Out";
1880
- },
1881
- );
1882
- expect(timeoutCalls).toHaveLength(0);
1883
-
1884
- resolveRun!();
1885
- } finally {
1886
- jest.useRealTimers();
1887
- }
1888
- });
1889
-
1890
- test("late run emits late feed event", async () => {
1891
- const service = createService();
1892
- service.start();
1893
-
1894
- // Set the pending run to be 10 minutes in the past
1895
- (service as any)._nextRunAt = Date.now() - 10 * 60 * 1000;
1896
- (service as any)._pendingRunId = "late-run-id";
1897
-
1898
- await service.runOnce();
1899
-
1900
- const lateCalls = mockEmitFeedEvent.mock.calls.filter(
1901
- (call: unknown[]) => {
1902
- const opts = call[0] as { title?: string };
1903
- return opts.title === "Heartbeat Ran Late";
1904
- },
1905
- );
1906
- expect(lateCalls).toHaveLength(1);
1907
- const opts = (lateCalls as any[][])[0][0] as {
1908
- urgency?: string;
1909
- summary?: string;
1910
- };
1911
- expect(opts.urgency).toBe("medium");
1912
- expect(opts.summary).toContain("10 minutes late");
1913
-
1914
- await service.stop();
1915
- });
1916
-
1917
- test("on-time run does not emit late feed event", async () => {
1918
- const service = createService();
1919
- await service.runOnce();
1920
-
1921
- const lateCalls = mockEmitFeedEvent.mock.calls.filter(
1922
- (call: unknown[]) => {
1923
- const opts = call[0] as { title?: string };
1924
- return opts.title === "Heartbeat Ran Late";
1925
- },
1926
- );
1927
- expect(lateCalls).toHaveLength(0);
1928
- });
1929
-
1930
- test("start() emits missed-run feed event when stale rows exist", () => {
1753
+ test("start() emits activity.failed notification when stale rows exist", () => {
1931
1754
  mockMarkStaleRunsAsMissed.mockImplementation(() => 2);
1932
1755
  mockMarkStaleRunningAsError.mockImplementation(() => 1);
1933
1756
 
1934
1757
  const service = createService();
1935
1758
  service.start();
1936
1759
 
1937
- const missedCalls = mockEmitFeedEvent.mock.calls.filter(
1938
- (call: unknown[]) => {
1939
- const opts = call[0] as { title?: string };
1940
- return opts.title === "Heartbeat Runs Missed";
1941
- },
1760
+ const missedSignals = emittedNotificationSignals.filter(
1761
+ (s) => s.sourceEventName === "activity.failed",
1942
1762
  );
1943
- expect(missedCalls).toHaveLength(1);
1944
- const opts = (missedCalls as any[][])[0][0] as {
1945
- urgency?: string;
1946
- summary?: string;
1947
- };
1948
- expect(opts.urgency).toBe("high");
1949
- expect(opts.summary).toContain("3");
1763
+ expect(missedSignals).toHaveLength(1);
1764
+ const signal = missedSignals[0]!;
1765
+ expect(signal.dedupeKey).toContain("activity-failed:heartbeat-missed:");
1766
+ expect(signal.contextPayload.jobName).toBe("heartbeat");
1767
+ const errorMessage = signal.contextPayload.errorMessage as string;
1768
+ expect(errorMessage).toContain("3");
1769
+ expect(signal.attentionHints?.urgency).toBe("medium");
1950
1770
 
1951
1771
  service.stop();
1952
1772
  });
1953
1773
 
1954
- test("start() does not emit missed-run feed event when counts are 0", () => {
1774
+ test("start() does not emit notification when counts are 0", () => {
1955
1775
  mockMarkStaleRunsAsMissed.mockImplementation(() => 0);
1956
1776
  mockMarkStaleRunningAsError.mockImplementation(() => 0);
1957
1777
 
1958
1778
  const service = createService();
1959
1779
  service.start();
1960
1780
 
1961
- const missedCalls = mockEmitFeedEvent.mock.calls.filter(
1962
- (call: unknown[]) => {
1963
- const opts = call[0] as { title?: string };
1964
- return opts.title === "Heartbeat Runs Missed";
1965
- },
1781
+ const missedSignals = emittedNotificationSignals.filter(
1782
+ (s) => s.sourceEventName === "activity.failed",
1966
1783
  );
1967
- expect(missedCalls).toHaveLength(0);
1784
+ expect(missedSignals).toHaveLength(0);
1968
1785
  service.stop();
1969
1786
  });
1970
1787
  });
@@ -724,6 +724,95 @@ describe("repairHistory", () => {
724
724
  expect(userMsg.content.every((b) => b.type !== "tool_result")).toBe(true);
725
725
  });
726
726
 
727
+ test("downgrades orphan web_search_tool_result in assistant message to text", () => {
728
+ // Inverse of the orphan-server_tool_use case. A web_search_tool_result
729
+ // in an assistant message whose tool_use_id has no preceding
730
+ // server_tool_use in the same message would 400 at the API. Downgrade
731
+ // to text so the model still sees the search results.
732
+ const messages: Message[] = [
733
+ { role: "user", content: [{ type: "text", text: "search" }] },
734
+ {
735
+ role: "assistant",
736
+ content: [
737
+ { type: "text", text: "Here's what I found." },
738
+ {
739
+ type: "web_search_tool_result",
740
+ tool_use_id: "srvtoolu_orphan",
741
+ content: [
742
+ {
743
+ type: "web_search_result",
744
+ url: "https://example.com",
745
+ title: "Example",
746
+ encrypted_content: "enc_abc",
747
+ },
748
+ ],
749
+ },
750
+ ],
751
+ },
752
+ ];
753
+
754
+ const { messages: repaired, stats } = repairHistory(messages);
755
+
756
+ expect(stats.orphanToolResultsDowngraded).toBe(1);
757
+ expect(stats.missingToolResultsInserted).toBe(0);
758
+
759
+ const assistantMsg = repaired[1];
760
+ expect(
761
+ assistantMsg.content.every((b) => b.type !== "web_search_tool_result"),
762
+ ).toBe(true);
763
+ const downgraded = assistantMsg.content.find(
764
+ (b) =>
765
+ b.type === "text" &&
766
+ (b as { text: string }).text.includes("srvtoolu_orphan"),
767
+ );
768
+ expect(downgraded).toBeDefined();
769
+ });
770
+
771
+ test("repairs both orphan directions within the same assistant message", () => {
772
+ // server_tool_use without a result AND a stray wsr from a different id —
773
+ // both must be repaired in one pass.
774
+ const messages: Message[] = [
775
+ { role: "user", content: [{ type: "text", text: "go" }] },
776
+ {
777
+ role: "assistant",
778
+ content: [
779
+ {
780
+ type: "server_tool_use",
781
+ id: "stu_missing_result",
782
+ name: "web_search",
783
+ input: { query: "alpha" },
784
+ },
785
+ {
786
+ type: "web_search_tool_result",
787
+ tool_use_id: "stu_no_use",
788
+ content: [
789
+ { type: "web_search_result", url: "https://x.test", title: "X" },
790
+ ],
791
+ },
792
+ ],
793
+ },
794
+ ];
795
+
796
+ const { messages: repaired, stats } = repairHistory(messages);
797
+
798
+ expect(stats.missingToolResultsInserted).toBe(1);
799
+ expect(stats.orphanToolResultsDowngraded).toBe(1);
800
+
801
+ const assistantMsg = repaired[1];
802
+ // Synthetic result inserted immediately after the orphan server_tool_use.
803
+ const blockTypes = assistantMsg.content.map((b) => b.type);
804
+ expect(blockTypes[0]).toBe("server_tool_use");
805
+ expect(blockTypes[1]).toBe("web_search_tool_result");
806
+ expect(
807
+ (assistantMsg.content[1] as { tool_use_id: string }).tool_use_id,
808
+ ).toBe("stu_missing_result");
809
+ // The orphan wsr is downgraded to text.
810
+ expect(blockTypes[2]).toBe("text");
811
+ expect((assistantMsg.content[2] as { text: string }).text).toContain(
812
+ "stu_no_use",
813
+ );
814
+ });
815
+
727
816
  test("downgrades type-mismatched web_search_tool_result for tool_use", () => {
728
817
  // A web_search_tool_result paired with a regular tool_use ID is a type mismatch
729
818
  const messages: Message[] = [