@vellumai/assistant 0.7.2 → 0.7.3

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 (347) hide show
  1. package/ARCHITECTURE.md +16 -1
  2. package/docs/architecture/memory.md +5 -2
  3. package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +13 -4
  4. package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -9
  5. package/node_modules/@vellumai/slack-text/src/index.test.ts +18 -35
  6. package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
  7. package/openapi.yaml +449 -22
  8. package/package.json +1 -1
  9. package/src/__tests__/app-control-flow.test.ts +21 -11
  10. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  11. package/src/__tests__/assistant-event.test.ts +0 -10
  12. package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -7
  13. package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -0
  14. package/src/__tests__/auto-analysis-end-to-end.test.ts +62 -1
  15. package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
  16. package/src/__tests__/call-conversation-messages.test.ts +8 -2
  17. package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
  18. package/src/__tests__/channel-readiness-service.test.ts +4 -2
  19. package/src/__tests__/config-loader-backfill.test.ts +379 -0
  20. package/src/__tests__/config-schema.test.ts +1 -0
  21. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +18 -9
  22. package/src/__tests__/config-watcher.test.ts +140 -69
  23. package/src/__tests__/context-search-agent-runner.test.ts +61 -3
  24. package/src/__tests__/context-search-conversations-source.test.ts +0 -24
  25. package/src/__tests__/context-search-fanout.test.ts +0 -1
  26. package/src/__tests__/context-search-memory-source.test.ts +3 -7
  27. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
  28. package/src/__tests__/context-search-pkb-source.test.ts +0 -1
  29. package/src/__tests__/context-search-workspace-source.test.ts +0 -1
  30. package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
  31. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
  32. package/src/__tests__/conversation-agent-loop.test.ts +454 -5
  33. package/src/__tests__/conversation-error.test.ts +150 -3
  34. package/src/__tests__/conversation-process-callsite.test.ts +43 -0
  35. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
  36. package/src/__tests__/conversation-runtime-assembly.test.ts +65 -0
  37. package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
  38. package/src/__tests__/conversation-speed-override.test.ts +0 -3
  39. package/src/__tests__/conversation-store.test.ts +0 -18
  40. package/src/__tests__/conversation-surfaces-app-control.test.ts +15 -4
  41. package/src/__tests__/conversation-surfaces-data-persist.test.ts +404 -0
  42. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +2 -5
  43. package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
  44. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
  45. package/src/__tests__/credentials-cli.test.ts +7 -0
  46. package/src/__tests__/cu-unified-flow.test.ts +176 -10
  47. package/src/__tests__/date-context.test.ts +164 -2
  48. package/src/__tests__/disk-pressure-guard.test.ts +262 -0
  49. package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
  50. package/src/__tests__/disk-pressure-policy.test.ts +241 -0
  51. package/src/__tests__/disk-pressure-routes.test.ts +379 -0
  52. package/src/__tests__/disk-pressure-tools.test.ts +277 -0
  53. package/src/__tests__/disk-usage.test.ts +150 -0
  54. package/src/__tests__/events-client-registration.test.ts +52 -0
  55. package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
  56. package/src/__tests__/file-write-tool.test.ts +4 -10
  57. package/src/__tests__/filing-service.test.ts +3 -4
  58. package/src/__tests__/heartbeat-disk-pressure.test.ts +183 -0
  59. package/src/__tests__/heartbeat-service.test.ts +260 -11
  60. package/src/__tests__/host-app-control-proxy.test.ts +195 -25
  61. package/src/__tests__/host-bash-proxy.test.ts +227 -34
  62. package/src/__tests__/host-bash-routes.test.ts +178 -13
  63. package/src/__tests__/host-cu-proxy.test.ts +210 -3
  64. package/src/__tests__/host-cu-routes-targeted.test.ts +141 -12
  65. package/src/__tests__/host-file-proxy-targeted.test.ts +48 -9
  66. package/src/__tests__/host-file-proxy.test.ts +268 -6
  67. package/src/__tests__/host-file-routes-targeted.test.ts +175 -17
  68. package/src/__tests__/host-transfer-proxy-targeted.test.ts +408 -59
  69. package/src/__tests__/host-transfer-routes-targeted.test.ts +232 -17
  70. package/src/__tests__/http-user-message-parity.test.ts +107 -1
  71. package/src/__tests__/injector-chain.test.ts +18 -6
  72. package/src/__tests__/injector-disk-pressure.test.ts +224 -0
  73. package/src/__tests__/managed-profile-guard.test.ts +18 -0
  74. package/src/__tests__/mcp-abort-signal.test.ts +130 -0
  75. package/src/__tests__/memory-admin-recall.test.ts +3 -11
  76. package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
  77. package/src/__tests__/normalize-onboarding.test.ts +180 -0
  78. package/src/__tests__/oauth-connect-routes.test.ts +316 -0
  79. package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
  80. package/src/__tests__/onboarding-persona-write.test.ts +308 -0
  81. package/src/__tests__/openai-provider.test.ts +45 -8
  82. package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
  83. package/src/__tests__/platform-callback-registration.test.ts +21 -4
  84. package/src/__tests__/platform.test.ts +2 -1
  85. package/src/__tests__/playbook-execution.test.ts +0 -43
  86. package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
  87. package/src/__tests__/prechat-onboarding-contract.test.ts +214 -27
  88. package/src/__tests__/provider-tool-name.test.ts +23 -0
  89. package/src/__tests__/relay-server.test.ts +15 -4
  90. package/src/__tests__/runtime-events-sse.test.ts +4 -8
  91. package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
  92. package/src/__tests__/secret-ingress-http.test.ts +0 -1
  93. package/src/__tests__/suggestion-routes.test.ts +46 -0
  94. package/src/__tests__/twilio-validation.test.ts +2 -2
  95. package/src/__tests__/workspace-migration-065-bump-stale-heartbeat-interval.test.ts +122 -0
  96. package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
  97. package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
  98. package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +90 -0
  99. package/src/approvals/guardian-decision-primitive.ts +13 -0
  100. package/src/approvals/guardian-request-resolvers.ts +16 -17
  101. package/src/backup/snapshot-lock.ts +2 -27
  102. package/src/bundler/compiler-tools.ts +3 -2
  103. package/src/calls/call-conversation-messages.ts +46 -10
  104. package/src/cli/commands/__tests__/webhooks.test.ts +0 -4
  105. package/src/cli/commands/bash.ts +35 -108
  106. package/src/cli/commands/contacts.ts +64 -25
  107. package/src/cli/commands/credentials.ts +56 -0
  108. package/src/cli/commands/memory-v2.ts +7 -6
  109. package/src/cli/commands/oauth/__tests__/connect.test.ts +437 -1
  110. package/src/cli/commands/oauth/connect.ts +127 -1
  111. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -3
  112. package/src/cli/commands/platform/__tests__/connect.test.ts +7 -1
  113. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  114. package/src/cli/commands/platform/__tests__/status.test.ts +103 -6
  115. package/src/cli/commands/platform/index.ts +16 -7
  116. package/src/cli/commands/status.ts +57 -0
  117. package/src/cli/program.ts +4 -2
  118. package/src/config/assistant-feature-flags.ts +13 -3
  119. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -3
  120. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +13 -7
  121. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
  122. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
  123. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
  124. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
  125. package/src/config/env.ts +0 -8
  126. package/src/config/feature-flag-registry.json +27 -3
  127. package/src/config/loader.ts +127 -8
  128. package/src/config/schemas/__tests__/memory-v2.test.ts +10 -5
  129. package/src/config/schemas/call-site-catalog.ts +14 -0
  130. package/src/config/schemas/channels.ts +0 -5
  131. package/src/config/schemas/heartbeat.ts +1 -1
  132. package/src/config/schemas/llm.ts +2 -0
  133. package/src/config/schemas/memory-lifecycle.ts +13 -0
  134. package/src/config/schemas/memory-v2.ts +75 -11
  135. package/src/config/schemas/platform.ts +43 -3
  136. package/src/config/schemas/services.ts +28 -0
  137. package/src/config/seed-inference-profiles.ts +230 -33
  138. package/src/contacts/contact-store.ts +0 -25
  139. package/src/daemon/__tests__/conversation-tool-setup.test.ts +86 -25
  140. package/src/daemon/assistant-attachments.ts +4 -4
  141. package/src/daemon/config-watcher.ts +85 -57
  142. package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
  143. package/src/daemon/conversation-agent-loop.ts +170 -33
  144. package/src/daemon/conversation-error.ts +87 -15
  145. package/src/daemon/conversation-lifecycle.ts +1 -3
  146. package/src/daemon/conversation-process.ts +8 -0
  147. package/src/daemon/conversation-runtime-assembly.ts +26 -0
  148. package/src/daemon/conversation-store.ts +2 -2
  149. package/src/daemon/conversation-surfaces.ts +195 -15
  150. package/src/daemon/conversation-tool-setup.ts +57 -14
  151. package/src/daemon/conversation.ts +17 -22
  152. package/src/daemon/date-context.ts +71 -22
  153. package/src/daemon/disk-pressure-background-gate.ts +73 -0
  154. package/src/daemon/disk-pressure-guard.ts +343 -0
  155. package/src/daemon/disk-pressure-policy.ts +163 -0
  156. package/src/daemon/handlers/shared.ts +0 -1
  157. package/src/daemon/handlers/skills.ts +3 -4
  158. package/src/daemon/host-app-control-proxy.ts +137 -41
  159. package/src/daemon/host-bash-proxy.ts +46 -21
  160. package/src/daemon/host-cu-proxy.ts +49 -3
  161. package/src/daemon/host-file-proxy.ts +43 -7
  162. package/src/daemon/host-transfer-proxy.ts +95 -4
  163. package/src/daemon/lifecycle.ts +79 -28
  164. package/src/daemon/meet-host-supervisor.ts +4 -4
  165. package/src/daemon/meet-manifest-loader.ts +0 -1
  166. package/src/daemon/memory-v2-startup.ts +14 -4
  167. package/src/daemon/message-protocol.ts +3 -0
  168. package/src/daemon/message-types/conversations.ts +4 -0
  169. package/src/daemon/message-types/disk-pressure.ts +9 -0
  170. package/src/daemon/message-types/messages.ts +3 -0
  171. package/src/daemon/profiler-run-store.ts +5 -5
  172. package/src/daemon/tool-setup-types.ts +2 -2
  173. package/src/documents/document-store.ts +85 -0
  174. package/src/filing/filing-service.ts +30 -5
  175. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +9 -16
  176. package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +36 -0
  177. package/src/heartbeat/heartbeat-run-store.ts +13 -0
  178. package/src/heartbeat/heartbeat-service.ts +205 -31
  179. package/src/home/feed-scheduler.ts +18 -0
  180. package/src/inbound/platform-callback-registration.ts +8 -15
  181. package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
  182. package/src/ipc/assistant-server.ts +56 -2
  183. package/src/ipc/gateway-client.ts +37 -3
  184. package/src/live-voice/live-voice-archive.ts +4 -4
  185. package/src/live-voice/protocol.ts +5 -7
  186. package/src/media/image-service.ts +1 -7
  187. package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
  188. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +52 -22
  189. package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
  190. package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -0
  191. package/src/memory/admin.ts +5 -9
  192. package/src/memory/context-search/agent-runner.ts +19 -2
  193. package/src/memory/context-search/sources/conversations.ts +2 -11
  194. package/src/memory/context-search/sources/memory-v2.ts +5 -4
  195. package/src/memory/context-search/sources/memory.ts +0 -1
  196. package/src/memory/context-search/types.ts +0 -1
  197. package/src/memory/conversation-crud.ts +4 -12
  198. package/src/memory/db-init.ts +2 -0
  199. package/src/memory/embedding-runtime-manager.ts +119 -5
  200. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +32 -21
  201. package/src/memory/graph/conversation-graph-memory.ts +42 -54
  202. package/src/memory/graph/extraction.ts +1 -3
  203. package/src/memory/graph/graph-search.test.ts +10 -67
  204. package/src/memory/graph/graph-search.ts +1 -20
  205. package/src/memory/graph/retriever.test.ts +6 -0
  206. package/src/memory/graph/retriever.ts +6 -10
  207. package/src/memory/indexer.ts +54 -45
  208. package/src/memory/job-handlers/backfill.ts +2 -11
  209. package/src/memory/job-handlers/cleanup.ts +43 -0
  210. package/src/memory/job-handlers/embedding.ts +6 -8
  211. package/src/memory/job-handlers/summarization.ts +2 -7
  212. package/src/memory/jobs-store.ts +48 -0
  213. package/src/memory/jobs-worker.ts +81 -43
  214. package/src/memory/memory-v2-activation-log-store.ts +32 -14
  215. package/src/memory/memory-v2-concept-frequency.ts +169 -0
  216. package/src/memory/migrations/239-trace-events-created-at-index.ts +18 -0
  217. package/src/memory/migrations/index.ts +1 -0
  218. package/src/memory/pkb/pkb-search.test.ts +6 -0
  219. package/src/memory/qdrant-client.ts +0 -13
  220. package/src/memory/rerank-local.ts +374 -0
  221. package/src/memory/search/semantic.ts +6 -67
  222. package/src/memory/trace-event-store.ts +1 -17
  223. package/src/memory/v2/__tests__/activation.test.ts +311 -250
  224. package/src/memory/v2/__tests__/consolidation-job.test.ts +40 -8
  225. package/src/memory/v2/__tests__/injection.test.ts +157 -167
  226. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
  227. package/src/memory/v2/__tests__/qdrant.test.ts +16 -0
  228. package/src/memory/v2/__tests__/reranker.test.ts +338 -0
  229. package/src/memory/v2/__tests__/sim.test.ts +5 -199
  230. package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
  231. package/src/memory/v2/__tests__/static-context.test.ts +76 -1
  232. package/src/memory/v2/activation.ts +149 -156
  233. package/src/memory/v2/consolidation-job.ts +62 -12
  234. package/src/memory/v2/injection.ts +47 -60
  235. package/src/memory/v2/prompts/consolidation.ts +36 -1
  236. package/src/memory/v2/qdrant.ts +99 -0
  237. package/src/memory/v2/reranker.ts +177 -0
  238. package/src/memory/v2/sim.ts +10 -84
  239. package/src/memory/v2/skill-content.ts +4 -3
  240. package/src/memory/v2/skill-store.ts +82 -59
  241. package/src/memory/v2/static-context.ts +22 -0
  242. package/src/memory/v2/types.ts +10 -10
  243. package/src/notifications/copy-composer.ts +13 -0
  244. package/src/notifications/signal.ts +4 -0
  245. package/src/oauth/AGENTS.md +3 -1
  246. package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
  247. package/src/oauth/connect-orchestrator.ts +2 -0
  248. package/src/oauth/connection-resolver.test.ts +66 -1
  249. package/src/oauth/connection-resolver.ts +55 -1
  250. package/src/oauth/oauth-connect-state.ts +77 -0
  251. package/src/oauth/seed-providers.ts +58 -1
  252. package/src/plugins/defaults/injectors.ts +35 -2
  253. package/src/plugins/defaults/memory-retrieval.ts +5 -6
  254. package/src/plugins/types.ts +7 -0
  255. package/src/proactive-artifact/aux-message-injector.ts +74 -0
  256. package/src/proactive-artifact/decision.test.ts +226 -0
  257. package/src/proactive-artifact/decision.ts +165 -0
  258. package/src/proactive-artifact/index.ts +7 -0
  259. package/src/proactive-artifact/job.test.ts +867 -0
  260. package/src/proactive-artifact/job.ts +352 -0
  261. package/src/proactive-artifact/message-copy.ts +41 -0
  262. package/src/proactive-artifact/trigger-state.test.ts +277 -0
  263. package/src/proactive-artifact/trigger-state.ts +119 -0
  264. package/src/prompts/normalize-onboarding.ts +80 -0
  265. package/src/prompts/persona-resolver.ts +101 -9
  266. package/src/prompts/system-prompt.ts +21 -7
  267. package/src/prompts/templates/BOOTSTRAP.md +13 -5
  268. package/src/providers/__tests__/retry-callsite.test.ts +222 -1
  269. package/src/providers/model-intents.ts +7 -0
  270. package/src/providers/openrouter/client.ts +8 -0
  271. package/src/providers/retry.ts +50 -0
  272. package/src/providers/types.ts +1 -0
  273. package/src/runtime/__tests__/agent-wake.test.ts +456 -3
  274. package/src/runtime/agent-wake.ts +238 -100
  275. package/src/runtime/assistant-event-hub.ts +36 -6
  276. package/src/runtime/assistant-event.ts +0 -1
  277. package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
  278. package/src/runtime/auth/route-policy.ts +14 -1
  279. package/src/runtime/auth/same-actor.ts +216 -0
  280. package/src/runtime/channel-retry-sweep.ts +65 -1
  281. package/src/runtime/guardian-reply-router.ts +10 -0
  282. package/src/runtime/local-actor-identity.ts +52 -11
  283. package/src/runtime/pending-interactions.ts +8 -0
  284. package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
  285. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +0 -5
  286. package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
  287. package/src/runtime/routes/client-routes.ts +20 -2
  288. package/src/runtime/routes/contact-routes.ts +0 -25
  289. package/src/runtime/routes/conversation-routes.ts +35 -26
  290. package/src/runtime/routes/debug-bash-routes.ts +163 -0
  291. package/src/runtime/routes/disk-pressure-routes.ts +121 -0
  292. package/src/runtime/routes/document-pdf-renderer.ts +6 -2
  293. package/src/runtime/routes/documents-routes.ts +2 -75
  294. package/src/runtime/routes/events-routes.ts +41 -9
  295. package/src/runtime/routes/host-bash-routes.ts +23 -3
  296. package/src/runtime/routes/host-cu-routes.ts +33 -6
  297. package/src/runtime/routes/host-file-routes.ts +32 -6
  298. package/src/runtime/routes/host-transfer-routes.ts +79 -16
  299. package/src/runtime/routes/identity-routes.ts +7 -138
  300. package/src/runtime/routes/inbound-message-handler.ts +77 -12
  301. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -0
  302. package/src/runtime/routes/index.ts +6 -0
  303. package/src/runtime/routes/memory-item-routes.test.ts +41 -15
  304. package/src/runtime/routes/memory-v2-routes.ts +33 -0
  305. package/src/runtime/routes/oauth-connect-routes.ts +153 -0
  306. package/src/runtime/verification-outbound-actions.ts +4 -4
  307. package/src/schedule/run-script.ts +37 -5
  308. package/src/schedule/scheduler.ts +20 -1
  309. package/src/security/encrypted-store.ts +2 -0
  310. package/src/security/secure-keys.ts +55 -0
  311. package/src/skills/remote-skill-policy.ts +4 -10
  312. package/src/subagent/index.ts +1 -7
  313. package/src/subagent/manager.ts +1 -15
  314. package/src/tasks/task-runner.ts +0 -1
  315. package/src/tasks/task-store.ts +0 -3
  316. package/src/tools/background-tool-registry.ts +17 -3
  317. package/src/tools/host-filesystem/edit.test.ts +151 -0
  318. package/src/tools/host-filesystem/edit.ts +43 -1
  319. package/src/tools/host-filesystem/read.test.ts +129 -0
  320. package/src/tools/host-filesystem/read.ts +43 -1
  321. package/src/tools/host-filesystem/transfer.test.ts +127 -2
  322. package/src/tools/host-filesystem/transfer.ts +56 -11
  323. package/src/tools/host-filesystem/write.test.ts +134 -0
  324. package/src/tools/host-filesystem/write.ts +43 -1
  325. package/src/tools/host-terminal/host-shell.ts +13 -6
  326. package/src/tools/mcp/mcp-tool-factory.ts +2 -1
  327. package/src/tools/memory/register.test.ts +12 -9
  328. package/src/tools/memory/register.ts +1 -2
  329. package/src/tools/provider-tool-name.ts +28 -0
  330. package/src/tools/registry.ts +30 -9
  331. package/src/tools/terminal/shell.ts +9 -1
  332. package/src/tools/tool-approval-handler.ts +31 -6
  333. package/src/tools/types.ts +24 -2
  334. package/src/tts/provider-catalog.ts +3 -5
  335. package/src/util/disk-usage.ts +138 -0
  336. package/src/util/platform.ts +21 -11
  337. package/src/util/process-liveness.ts +26 -0
  338. package/src/workspace/heartbeat-service.ts +19 -0
  339. package/src/workspace/migrations/065-bump-stale-heartbeat-interval.ts +60 -0
  340. package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
  341. package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +72 -0
  342. package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
  343. package/src/workspace/migrations/registry.ts +8 -0
  344. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
  345. package/src/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
  346. package/src/memory/v2/skill-qdrant.ts +0 -404
  347. package/src/signals/bash.ts +0 -198
@@ -20,6 +20,8 @@ const mockSkipHeartbeatRun = mock(() => true);
20
20
  const mockSupersedePendingRun = mock(() => true);
21
21
  const mockMarkStaleRunsAsMissed = mock(() => 0);
22
22
  const mockMarkStaleRunningAsError = mock(() => 0);
23
+ const mockListHeartbeatRuns = mock(() => []);
24
+ const mockCountCompletedHeartbeatRuns = mock(() => 10);
23
25
  mock.module("../heartbeat/heartbeat-run-store.js", () => ({
24
26
  insertPendingHeartbeatRun: mockInsertPendingHeartbeatRun,
25
27
  startHeartbeatRun: mockStartHeartbeatRun,
@@ -28,6 +30,8 @@ mock.module("../heartbeat/heartbeat-run-store.js", () => ({
28
30
  supersedePendingRun: mockSupersedePendingRun,
29
31
  markStaleRunsAsMissed: mockMarkStaleRunsAsMissed,
30
32
  markStaleRunningAsError: mockMarkStaleRunningAsError,
33
+ listHeartbeatRuns: mockListHeartbeatRuns,
34
+ countCompletedHeartbeatRuns: mockCountCompletedHeartbeatRuns,
31
35
  }));
32
36
 
33
37
  // ── Feed event mock ───────────────────────────────────────────────
@@ -119,6 +123,14 @@ mock.module("../prompts/persona-resolver.js", () => ({
119
123
  const createdConversations: Array<{ title: string; conversationType: string }> =
120
124
  [];
121
125
  let conversationIdCounter = 0;
126
+ const mockStoredMessages: Array<{
127
+ id: string;
128
+ conversationId: string;
129
+ role: string;
130
+ content: string;
131
+ createdAt: number;
132
+ metadata: string | null;
133
+ }> = [];
122
134
 
123
135
  mock.module("../memory/conversation-crud.js", () => ({
124
136
  setConversationOriginChannelIfUnset: () => {},
@@ -127,7 +139,7 @@ mock.module("../memory/conversation-crud.js", () => ({
127
139
  updateConversationTitle: () => {},
128
140
  updateConversationUsage: () => {},
129
141
  addMessage: () => ({ id: "mock-msg-id" }),
130
- getMessages: () => [],
142
+ getMessages: () => mockStoredMessages,
131
143
  getConversation: () => ({
132
144
  id: "conv-1",
133
145
  contextSummary: null,
@@ -199,21 +211,36 @@ mock.module("../credential-health/credential-health-service.js", () => ({
199
211
  // `notifyUnhealthyCredentials` dynamically imports `emitNotificationSignal`.
200
212
  // Track calls so tests can assert which credentials were notified about.
201
213
  const emittedNotificationSignals: Array<{
214
+ sourceEventName?: string;
215
+ sourceChannel?: string;
202
216
  sourceContextId: string;
203
217
  dedupeKey: string;
218
+ attentionHints?: Record<string, unknown>;
204
219
  contextPayload: Record<string, unknown>;
220
+ conversationAffinityHint?: Record<string, string>;
221
+ conversationMetadata?: Record<string, unknown>;
205
222
  }> = [];
206
223
 
207
224
  mock.module("../notifications/emit-signal.js", () => ({
208
225
  emitNotificationSignal: async (opts: {
226
+ sourceEventName?: string;
227
+ sourceChannel?: string;
209
228
  sourceContextId: string;
210
229
  dedupeKey: string;
230
+ attentionHints?: Record<string, unknown>;
211
231
  contextPayload: Record<string, unknown>;
232
+ conversationAffinityHint?: Record<string, string>;
233
+ conversationMetadata?: Record<string, unknown>;
212
234
  }) => {
213
235
  emittedNotificationSignals.push({
236
+ sourceEventName: opts.sourceEventName,
237
+ sourceChannel: opts.sourceChannel,
214
238
  sourceContextId: opts.sourceContextId,
215
239
  dedupeKey: opts.dedupeKey,
240
+ attentionHints: opts.attentionHints,
216
241
  contextPayload: opts.contextPayload,
242
+ conversationAffinityHint: opts.conversationAffinityHint,
243
+ conversationMetadata: opts.conversationMetadata,
217
244
  });
218
245
  },
219
246
  }));
@@ -318,6 +345,7 @@ describe("HeartbeatService", () => {
318
345
  alerterCalls = [];
319
346
  createdConversations.length = 0;
320
347
  conversationIdCounter = 0;
348
+ mockStoredMessages.length = 0;
321
349
  mockGuardianPersona = null;
322
350
  mockCredentialHealthReport = null;
323
351
  mockCheckAllCredentialsFail = false;
@@ -351,6 +379,10 @@ describe("HeartbeatService", () => {
351
379
  mockMarkStaleRunsAsMissed.mockImplementation(() => 0);
352
380
  mockMarkStaleRunningAsError.mockClear();
353
381
  mockMarkStaleRunningAsError.mockImplementation(() => 0);
382
+ mockListHeartbeatRuns.mockClear();
383
+ mockListHeartbeatRuns.mockImplementation(() => []);
384
+ mockCountCompletedHeartbeatRuns.mockClear();
385
+ mockCountCompletedHeartbeatRuns.mockImplementation(() => 10);
354
386
  mockEmitFeedEvent.mockClear();
355
387
  mockEmitFeedEvent.mockImplementation(() => Promise.resolve());
356
388
 
@@ -369,6 +401,10 @@ describe("HeartbeatService", () => {
369
401
  function createService(overrides?: {
370
402
  processMessage?: (...args: unknown[]) => Promise<{ messageId: string }>;
371
403
  getCurrentHour?: () => number;
404
+ onConversationCreated?: (info: {
405
+ conversationId: string;
406
+ title: string;
407
+ }) => void;
372
408
  }) {
373
409
  if (overrides?.processMessage) {
374
410
  setTestProcessMessage(overrides.processMessage);
@@ -377,6 +413,7 @@ describe("HeartbeatService", () => {
377
413
  alerter: (alert: { type: string; title: string; body: string }) => {
378
414
  alerterCalls.push(alert);
379
415
  },
416
+ onConversationCreated: overrides?.onConversationCreated,
380
417
  getCurrentHour: overrides?.getCurrentHour,
381
418
  });
382
419
  }
@@ -721,6 +758,149 @@ describe("HeartbeatService", () => {
721
758
  });
722
759
  });
723
760
 
761
+ test("HEARTBEAT_ALERT emits a notification signal and surfaces the conversation", async () => {
762
+ const conversationCreatedCalls: Array<{
763
+ conversationId: string;
764
+ title: string;
765
+ }> = [];
766
+ const service = createService({
767
+ onConversationCreated: (info) => conversationCreatedCalls.push(info),
768
+ processMessage: async (...args: unknown[]) => {
769
+ const conversationId = args[0] as string;
770
+ mockStoredMessages.push({
771
+ id: "assistant-alert-1",
772
+ conversationId,
773
+ role: "assistant",
774
+ content: JSON.stringify([
775
+ {
776
+ type: "text",
777
+ text: "The first heartbeat found a concrete follow-up for the guardian.\nHEARTBEAT_ALERT",
778
+ },
779
+ ]),
780
+ createdAt: Date.now(),
781
+ metadata: null,
782
+ });
783
+ return { messageId: "user-heartbeat-1" };
784
+ },
785
+ });
786
+
787
+ await service.runOnce();
788
+ await new Promise((resolve) => setTimeout(resolve, 0));
789
+
790
+ expect(conversationCreatedCalls).toEqual([
791
+ { conversationId: "conv-1", title: "Heartbeat" },
792
+ ]);
793
+ expect(emittedNotificationSignals).toHaveLength(1);
794
+ expect(emittedNotificationSignals[0]).toMatchObject({
795
+ sourceEventName: "heartbeat.alert",
796
+ sourceChannel: "watcher",
797
+ sourceContextId: "mock-run-id",
798
+ dedupeKey: "heartbeat:alert:mock-run-id",
799
+ attentionHints: {
800
+ requiresAction: true,
801
+ urgency: "medium",
802
+ isAsyncBackground: true,
803
+ visibleInSourceNow: false,
804
+ },
805
+ conversationAffinityHint: { vellum: "conv-1" },
806
+ conversationMetadata: {
807
+ source: "heartbeat",
808
+ groupId: "system:background",
809
+ },
810
+ });
811
+ expect(emittedNotificationSignals[0].contextPayload.summary).toBe(
812
+ "The first heartbeat found a concrete follow-up for the guardian.",
813
+ );
814
+ expect(emittedNotificationSignals[0].contextPayload.messageId).toBe(
815
+ "assistant-alert-1",
816
+ );
817
+ expect(
818
+ emittedNotificationSignals[0].contextPayload.sourceInterface,
819
+ ).toBeUndefined();
820
+ });
821
+
822
+ test("HEARTBEAT_OK stays silent", async () => {
823
+ const conversationCreatedCalls: Array<{
824
+ conversationId: string;
825
+ title: string;
826
+ }> = [];
827
+ const service = createService({
828
+ onConversationCreated: (info) => conversationCreatedCalls.push(info),
829
+ processMessage: async (...args: unknown[]) => {
830
+ const conversationId = args[0] as string;
831
+ mockStoredMessages.push({
832
+ id: "assistant-ok-1",
833
+ conversationId,
834
+ role: "assistant",
835
+ content: JSON.stringify([
836
+ {
837
+ type: "text",
838
+ text: "Everything looks good.\nHEARTBEAT_OK",
839
+ },
840
+ ]),
841
+ createdAt: Date.now(),
842
+ metadata: null,
843
+ });
844
+ return { messageId: "user-heartbeat-1" };
845
+ },
846
+ });
847
+
848
+ await service.runOnce();
849
+ await new Promise((resolve) => setTimeout(resolve, 0));
850
+
851
+ expect(conversationCreatedCalls).toHaveLength(0);
852
+ 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
+ });
861
+
862
+ test("HEARTBEAT_OK stays silent when earlier content mentions HEARTBEAT_ALERT", async () => {
863
+ const conversationCreatedCalls: Array<{
864
+ conversationId: string;
865
+ title: string;
866
+ }> = [];
867
+ const service = createService({
868
+ onConversationCreated: (info) => conversationCreatedCalls.push(info),
869
+ processMessage: async (...args: unknown[]) => {
870
+ const conversationId = args[0] as string;
871
+ mockStoredMessages.push({
872
+ id: "assistant-ok-2",
873
+ conversationId,
874
+ role: "assistant",
875
+ content: JSON.stringify([
876
+ {
877
+ type: "thinking",
878
+ thinking:
879
+ "I should decide between HEARTBEAT_ALERT and HEARTBEAT_OK.",
880
+ },
881
+ {
882
+ type: "tool_result",
883
+ content: "Tool output mentions HEARTBEAT_ALERT.",
884
+ },
885
+ {
886
+ type: "text",
887
+ text: "I considered HEARTBEAT_ALERT, but there is nothing useful to surface.\nHEARTBEAT_OK",
888
+ },
889
+ ]),
890
+ createdAt: Date.now(),
891
+ metadata: null,
892
+ });
893
+ return { messageId: "user-heartbeat-1" };
894
+ },
895
+ });
896
+
897
+ await service.runOnce();
898
+ await new Promise((resolve) => setTimeout(resolve, 0));
899
+
900
+ expect(conversationCreatedCalls).toHaveLength(0);
901
+ expect(emittedNotificationSignals).toHaveLength(0);
902
+ });
903
+
724
904
  test("end-to-end: llm.callSites.heartbeatAgent.speed resolves to 'fast'", async () => {
725
905
  // Verifies the contract that PR 7 establishes: heartbeat passes
726
906
  // `callSite: 'heartbeatAgent'`, and the LLM resolver maps that to the
@@ -1354,20 +1534,38 @@ describe("HeartbeatService", () => {
1354
1534
  });
1355
1535
  });
1356
1536
 
1357
- test("CAS false suppresses success feed event", async () => {
1537
+ test("CAS false suppresses success surfacing", async () => {
1358
1538
  mockCompleteHeartbeatRun.mockImplementation(() => false);
1359
1539
 
1360
- const service = createService();
1540
+ const conversationCreatedCalls: Array<{
1541
+ conversationId: string;
1542
+ title: string;
1543
+ }> = [];
1544
+ const service = createService({
1545
+ onConversationCreated: (info) => conversationCreatedCalls.push(info),
1546
+ processMessage: async (...args: unknown[]) => {
1547
+ const conversationId = args[0] as string;
1548
+ mockStoredMessages.push({
1549
+ id: "assistant-alert-1",
1550
+ conversationId,
1551
+ role: "assistant",
1552
+ content: JSON.stringify([
1553
+ {
1554
+ type: "text",
1555
+ text: "Something worth surfacing.\nHEARTBEAT_ALERT",
1556
+ },
1557
+ ]),
1558
+ createdAt: Date.now(),
1559
+ metadata: null,
1560
+ });
1561
+ return { messageId: "msg-1" };
1562
+ },
1563
+ });
1361
1564
  await service.runOnce();
1565
+ await new Promise((resolve) => setTimeout(resolve, 0));
1362
1566
 
1363
- // completeHeartbeatRun returned false, so no feed event should be emitted for success
1364
- const successCalls = mockEmitFeedEvent.mock.calls.filter(
1365
- (call: unknown[]) => {
1366
- const opts = call[0] as { dedupKey?: string };
1367
- return opts.dedupKey?.startsWith("heartbeat:ok:");
1368
- },
1369
- );
1370
- expect(successCalls).toHaveLength(0);
1567
+ expect(conversationCreatedCalls).toHaveLength(0);
1568
+ expect(emittedNotificationSignals).toHaveLength(0);
1371
1569
  });
1372
1570
 
1373
1571
  test("CAS false suppresses failure alerter and feed event", async () => {
@@ -1770,4 +1968,55 @@ describe("HeartbeatService", () => {
1770
1968
  service.stop();
1771
1969
  });
1772
1970
  });
1971
+
1972
+ describe("early heartbeat nudge", () => {
1973
+ test("includes <early-heartbeat> when completedRunCount is 0", () => {
1974
+ const service = createService();
1975
+ const { prompt } = service.buildPrompt("- Check things", [], 0);
1976
+
1977
+ expect(prompt).toContain("<early-heartbeat>");
1978
+ expect(prompt).toContain("first heartbeats");
1979
+ });
1980
+
1981
+ test("includes <early-heartbeat> when completedRunCount is 2", () => {
1982
+ const service = createService();
1983
+ const { prompt } = service.buildPrompt("- Check things", [], 2);
1984
+
1985
+ expect(prompt).toContain("<early-heartbeat>");
1986
+ });
1987
+
1988
+ test("omits <early-heartbeat> when completedRunCount is 3", () => {
1989
+ const service = createService();
1990
+ const { prompt } = service.buildPrompt("- Check things", [], 3);
1991
+
1992
+ expect(prompt).not.toContain("<early-heartbeat>");
1993
+ });
1994
+
1995
+ test("omits <early-heartbeat> when completedRunCount is 10", () => {
1996
+ const service = createService();
1997
+ const { prompt } = service.buildPrompt("- Check things", [], 10);
1998
+
1999
+ expect(prompt).not.toContain("<early-heartbeat>");
2000
+ });
2001
+
2002
+ test("executeRun passes completed run count to buildPrompt", async () => {
2003
+ mockCountCompletedHeartbeatRuns.mockImplementation(() => 0);
2004
+
2005
+ const service = createService();
2006
+ await service.runOnce();
2007
+
2008
+ expect(processMessageCalls).toHaveLength(1);
2009
+ expect(processMessageCalls[0].content).toContain("<early-heartbeat>");
2010
+ });
2011
+
2012
+ test("executeRun omits nudge when enough runs have completed", async () => {
2013
+ mockCountCompletedHeartbeatRuns.mockImplementation(() => 5);
2014
+
2015
+ const service = createService();
2016
+ await service.runOnce();
2017
+
2018
+ expect(processMessageCalls).toHaveLength(1);
2019
+ expect(processMessageCalls[0].content).not.toContain("<early-heartbeat>");
2020
+ });
2021
+ });
1773
2022
  });