@vellumai/assistant 0.6.3 → 0.6.4

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 (667) hide show
  1. package/ARCHITECTURE.md +273 -10
  2. package/Dockerfile +2 -3
  3. package/bun.lock +5 -13
  4. package/docs/backup-troubleshooting.md +52 -0
  5. package/docs/browser-use-architecture-phase2.md +174 -0
  6. package/docs/stt-provider-onboarding.md +120 -0
  7. package/knip.json +12 -2
  8. package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
  9. package/node_modules/@vellumai/ces-contracts/package.json +3 -3
  10. package/openapi.yaml +982 -72
  11. package/package.json +4 -6
  12. package/scripts/generate-openapi.ts +0 -1
  13. package/scripts/test.sh +73 -18
  14. package/src/__tests__/agent-image-optimize.test.ts +28 -0
  15. package/src/__tests__/agent-loop.test.ts +123 -0
  16. package/src/__tests__/anthropic-provider.test.ts +263 -10
  17. package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
  18. package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
  19. package/src/__tests__/browser-fill-credential.test.ts +11 -0
  20. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
  21. package/src/__tests__/browser-skill-endstate.test.ts +31 -7
  22. package/src/__tests__/btw-routes.test.ts +7 -0
  23. package/src/__tests__/call-controller.test.ts +581 -20
  24. package/src/__tests__/catalog-files.test.ts +138 -0
  25. package/src/__tests__/channel-invite-transport.test.ts +2 -2
  26. package/src/__tests__/channel-readiness-routes.test.ts +16 -20
  27. package/src/__tests__/channel-readiness-service.test.ts +12 -7
  28. package/src/__tests__/checker.test.ts +157 -10
  29. package/src/__tests__/clawhub-files.test.ts +347 -0
  30. package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
  31. package/src/__tests__/config-analysis.test.ts +100 -0
  32. package/src/__tests__/config-schema.test.ts +1013 -66
  33. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
  34. package/src/__tests__/config-watcher.test.ts +43 -8
  35. package/src/__tests__/contact-store-user-file.test.ts +512 -0
  36. package/src/__tests__/contacts-write.test.ts +197 -0
  37. package/src/__tests__/context-window-manager.test.ts +88 -0
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  39. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  40. package/src/__tests__/conversation-agent-loop.test.ts +98 -2
  41. package/src/__tests__/conversation-confirmation-signals.test.ts +135 -0
  42. package/src/__tests__/conversation-error.test.ts +70 -0
  43. package/src/__tests__/conversation-history-web-search.test.ts +11 -4
  44. package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
  45. package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
  46. package/src/__tests__/conversation-list-source.test.ts +145 -0
  47. package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
  48. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  49. package/src/__tests__/conversation-queue.test.ts +901 -60
  50. package/src/__tests__/conversation-routes-disk-view.test.ts +270 -0
  51. package/src/__tests__/conversation-runtime-assembly.test.ts +55 -0
  52. package/src/__tests__/conversation-skill-tools.test.ts +7 -4
  53. package/src/__tests__/conversation-slash-commands.test.ts +33 -0
  54. package/src/__tests__/conversation-slash-queue.test.ts +89 -18
  55. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  56. package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
  57. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  59. package/src/__tests__/credential-health-service.test.ts +352 -0
  60. package/src/__tests__/credential-security-invariants.test.ts +5 -3
  61. package/src/__tests__/credential-vault-unit.test.ts +379 -3
  62. package/src/__tests__/credentials-cli.test.ts +40 -16
  63. package/src/__tests__/cross-provider-web-search.test.ts +146 -35
  64. package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
  65. package/src/__tests__/device-id.test.ts +112 -0
  66. package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
  67. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
  68. package/src/__tests__/email-html-renderer.test.ts +71 -0
  69. package/src/__tests__/email-invite-adapter.test.ts +36 -32
  70. package/src/__tests__/emit-event-signal.test.ts +71 -0
  71. package/src/__tests__/extension-id-sync-guard.test.ts +75 -8
  72. package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
  73. package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
  74. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  75. package/src/__tests__/gemini-provider.test.ts +64 -0
  76. package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
  77. package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
  78. package/src/__tests__/gmail-archive-gate.test.ts +246 -0
  79. package/src/__tests__/gmail-preferences.test.ts +117 -0
  80. package/src/__tests__/headless-browser-interactions.test.ts +43 -0
  81. package/src/__tests__/headless-browser-mode.test.ts +614 -0
  82. package/src/__tests__/headless-browser-navigate.test.ts +142 -5
  83. package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
  84. package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
  85. package/src/__tests__/heartbeat-service.test.ts +70 -17
  86. package/src/__tests__/home-state-routes.test.ts +162 -0
  87. package/src/__tests__/host-bash-proxy.test.ts +0 -5
  88. package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
  89. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
  90. package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
  91. package/src/__tests__/host-cu-proxy.test.ts +0 -5
  92. package/src/__tests__/identity-intro-cache.test.ts +40 -10
  93. package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
  94. package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
  95. package/src/__tests__/llm-context-normalization.test.ts +488 -0
  96. package/src/__tests__/llm-context-route-provider.test.ts +86 -5
  97. package/src/__tests__/llm-usage-store.test.ts +363 -0
  98. package/src/__tests__/media-stream-output.test.ts +555 -0
  99. package/src/__tests__/media-stream-parser.test.ts +374 -0
  100. package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
  101. package/src/__tests__/media-stream-stt-session.test.ts +588 -0
  102. package/src/__tests__/media-turn-detector.test.ts +440 -0
  103. package/src/__tests__/message-queue.test.ts +125 -0
  104. package/src/__tests__/migration-export-http.test.ts +6 -6
  105. package/src/__tests__/migration-import-commit-http.test.ts +8 -6
  106. package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
  107. package/src/__tests__/migration-validate-http.test.ts +3 -3
  108. package/src/__tests__/mock-gateway-ipc.ts +151 -0
  109. package/src/__tests__/model-intents.test.ts +2 -2
  110. package/src/__tests__/oauth-apps-routes.test.ts +1 -0
  111. package/src/__tests__/oauth-cli.test.ts +2 -0
  112. package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
  113. package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
  114. package/src/__tests__/oauth-providers-routes.test.ts +2 -0
  115. package/src/__tests__/oauth-store.test.ts +85 -0
  116. package/src/__tests__/oauth2-gateway-transport.test.ts +249 -6
  117. package/src/__tests__/onboarding-template-contract.test.ts +6 -13
  118. package/src/__tests__/openai-provider.test.ts +176 -0
  119. package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
  120. package/src/__tests__/openai-responses-provider.test.ts +1105 -0
  121. package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
  122. package/src/__tests__/outlook-unsubscribe.test.ts +31 -2
  123. package/src/__tests__/persona-resolver.test.ts +251 -0
  124. package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
  125. package/src/__tests__/platform.test.ts +92 -1
  126. package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
  127. package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
  128. package/src/__tests__/pricing.test.ts +174 -0
  129. package/src/__tests__/qdrant-manager.test.ts +29 -8
  130. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
  131. package/src/__tests__/relationship-state-contract.test.ts +175 -0
  132. package/src/__tests__/relay-server.test.ts +423 -5
  133. package/src/__tests__/search-skills-unified.test.ts +118 -0
  134. package/src/__tests__/secret-scanner-executor.test.ts +4 -0
  135. package/src/__tests__/secure-keys.test.ts +107 -0
  136. package/src/__tests__/send-endpoint-busy.test.ts +5 -1
  137. package/src/__tests__/sequence-store.test.ts +1 -1
  138. package/src/__tests__/server-history-render.test.ts +49 -0
  139. package/src/__tests__/settings-routes.test.ts +201 -0
  140. package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
  141. package/src/__tests__/skills-file-content-endpoint.test.ts +276 -145
  142. package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
  143. package/src/__tests__/skills.test.ts +5 -2
  144. package/src/__tests__/skillssh-files.test.ts +446 -0
  145. package/src/__tests__/slack-block-formatting.test.ts +110 -0
  146. package/src/__tests__/slack-channel-config.test.ts +564 -1
  147. package/src/__tests__/stt-catalog-parity.test.ts +282 -0
  148. package/src/__tests__/stt-stream-session.test.ts +535 -0
  149. package/src/__tests__/system-prompt.test.ts +112 -26
  150. package/src/__tests__/telephony-stt-routing.test.ts +329 -0
  151. package/src/__tests__/terminal-tools.test.ts +18 -7
  152. package/src/__tests__/test-preload.ts +18 -0
  153. package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
  154. package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
  155. package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
  156. package/src/__tests__/tool-executor.test.ts +33 -24
  157. package/src/__tests__/tool-result-truncation.test.ts +36 -0
  158. package/src/__tests__/trust-store.test.ts +7 -1
  159. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
  160. package/src/__tests__/tts-catalog-parity.test.ts +345 -0
  161. package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
  162. package/src/__tests__/twilio-routes.test.ts +376 -0
  163. package/src/__tests__/unicode.test.ts +293 -0
  164. package/src/__tests__/update-bulletin-format.test.ts +59 -0
  165. package/src/__tests__/update-bulletin.test.ts +206 -5
  166. package/src/__tests__/usage-routes.test.ts +25 -4
  167. package/src/__tests__/user-reference.test.ts +46 -61
  168. package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
  169. package/src/__tests__/voice-config-update.test.ts +403 -0
  170. package/src/__tests__/voice-quality.test.ts +434 -19
  171. package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
  172. package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
  173. package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
  174. package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
  175. package/src/__tests__/workspace-migration-meets.test.ts +244 -0
  176. package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
  177. package/src/__tests__/workspace-policy.test.ts +2 -0
  178. package/src/agent/image-optimize.ts +24 -12
  179. package/src/agent/loop.ts +43 -3
  180. package/src/backup/__tests__/backup-key.test.ts +152 -0
  181. package/src/backup/__tests__/backup-worker.test.ts +767 -0
  182. package/src/backup/__tests__/list-snapshots.test.ts +87 -0
  183. package/src/backup/__tests__/local-writer.test.ts +218 -0
  184. package/src/backup/__tests__/offsite-writer.test.ts +641 -0
  185. package/src/backup/__tests__/paths.test.ts +300 -0
  186. package/src/backup/__tests__/restore.test.ts +498 -0
  187. package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
  188. package/src/backup/__tests__/stream-crypt.test.ts +228 -0
  189. package/src/backup/backup-key.ts +137 -0
  190. package/src/backup/backup-worker.ts +459 -0
  191. package/src/backup/list-snapshots.ts +147 -0
  192. package/src/backup/local-writer.ts +133 -0
  193. package/src/backup/offsite-writer.ts +222 -0
  194. package/src/backup/paths.ts +226 -0
  195. package/src/backup/restore.ts +322 -0
  196. package/src/backup/snapshot-lock.ts +431 -0
  197. package/src/backup/stream-crypt.ts +263 -0
  198. package/src/bundler/package-resolver.ts +4 -0
  199. package/src/calls/audio-store.ts +11 -5
  200. package/src/calls/call-controller.ts +226 -71
  201. package/src/calls/call-domain.ts +9 -0
  202. package/src/calls/call-speech-output.ts +190 -0
  203. package/src/calls/call-transport.ts +77 -0
  204. package/src/calls/media-stream-audio-transcode.ts +173 -0
  205. package/src/calls/media-stream-output.ts +660 -0
  206. package/src/calls/media-stream-parser.ts +300 -0
  207. package/src/calls/media-stream-protocol.ts +166 -0
  208. package/src/calls/media-stream-server.ts +592 -0
  209. package/src/calls/media-stream-stt-session.ts +460 -0
  210. package/src/calls/media-turn-detector.ts +230 -0
  211. package/src/calls/relay-server.ts +90 -75
  212. package/src/calls/resolve-call-tts-provider.ts +136 -0
  213. package/src/calls/telephony-stt-routing.ts +145 -0
  214. package/src/calls/tts-call-strategy.ts +161 -0
  215. package/src/calls/tts-text-sanitizer.ts +32 -16
  216. package/src/calls/twilio-routes.ts +281 -17
  217. package/src/calls/voice-quality.ts +78 -35
  218. package/src/calls/voice-session-bridge.ts +8 -1
  219. package/src/channels/types.ts +16 -0
  220. package/src/cli/__tests__/run-assistant-command.ts +11 -1
  221. package/src/cli/commands/__tests__/backup.test.ts +1165 -0
  222. package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
  223. package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
  224. package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
  225. package/src/cli/commands/__tests__/email-download.test.ts +16 -1
  226. package/src/cli/commands/__tests__/email-list.test.ts +22 -4
  227. package/src/cli/commands/__tests__/email-register.test.ts +4 -4
  228. package/src/cli/commands/__tests__/email-send.test.ts +37 -4
  229. package/src/cli/commands/__tests__/email-status.test.ts +5 -1
  230. package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
  231. package/src/cli/commands/backup.ts +993 -0
  232. package/src/cli/commands/conversations.ts +77 -0
  233. package/src/cli/commands/credentials.ts +0 -1
  234. package/src/cli/commands/domain.ts +210 -0
  235. package/src/cli/commands/email.ts +255 -3
  236. package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
  237. package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
  238. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
  239. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
  240. package/src/cli/commands/oauth/mode.ts +12 -3
  241. package/src/cli/commands/oauth/providers.ts +15 -0
  242. package/src/cli/commands/oauth/shared.ts +2 -1
  243. package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -9
  244. package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
  245. package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
  246. package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
  247. package/src/cli/program.ts +30 -4
  248. package/src/config/__tests__/backup-schema.test.ts +134 -0
  249. package/src/config/assistant-feature-flags.ts +61 -62
  250. package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +37 -1
  251. package/src/config/bundled-skills/browser/SKILL.md +30 -5
  252. package/src/config/bundled-skills/browser/TOOLS.json +123 -0
  253. package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
  254. package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
  255. package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
  256. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
  257. package/src/config/bundled-skills/contacts/SKILL.md +2 -2
  258. package/src/config/bundled-skills/gmail/SKILL.md +53 -7
  259. package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
  260. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
  261. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
  262. package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
  263. package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
  264. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
  265. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
  266. package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
  267. package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
  268. package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
  269. package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
  270. package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
  271. package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
  272. package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
  273. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
  274. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  275. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
  276. package/src/config/bundled-skills/outlook/SKILL.md +2 -2
  277. package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
  278. package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
  279. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
  280. package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
  281. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  282. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
  283. package/src/config/bundled-skills/slack/SKILL.md +1 -0
  284. package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
  285. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
  286. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
  287. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
  288. package/src/config/bundled-tool-registry.ts +8 -0
  289. package/src/config/env-registry.ts +24 -0
  290. package/src/config/env.ts +34 -10
  291. package/src/config/feature-flag-registry.json +46 -14
  292. package/src/config/loader.ts +26 -12
  293. package/src/config/schema.ts +35 -10
  294. package/src/config/schemas/__tests__/stt.test.ts +43 -0
  295. package/src/config/schemas/analysis.ts +51 -0
  296. package/src/config/schemas/backup.ts +72 -0
  297. package/src/config/schemas/calls.ts +1 -26
  298. package/src/config/schemas/elevenlabs.ts +0 -59
  299. package/src/config/schemas/filing.ts +47 -7
  300. package/src/config/schemas/heartbeat.ts +27 -5
  301. package/src/config/schemas/host-browser.ts +47 -1
  302. package/src/config/schemas/inference.ts +1 -1
  303. package/src/config/schemas/memory-lifecycle.ts +14 -2
  304. package/src/config/schemas/services.ts +44 -0
  305. package/src/config/schemas/stt.ts +59 -0
  306. package/src/config/schemas/tts.ts +230 -0
  307. package/src/config/schemas/updates.ts +14 -0
  308. package/src/config/skills.ts +4 -0
  309. package/src/config/types.ts +4 -0
  310. package/src/contacts/contact-store.ts +56 -11
  311. package/src/contacts/contacts-write.ts +38 -1
  312. package/src/context/post-turn-tool-result-truncation.ts +3 -2
  313. package/src/context/tool-result-truncation.ts +2 -1
  314. package/src/context/window-manager.ts +45 -12
  315. package/src/credential-execution/executable-discovery.ts +12 -2
  316. package/src/credential-execution/process-manager.ts +33 -2
  317. package/src/credential-health/credential-health-service.ts +366 -0
  318. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
  319. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
  320. package/src/daemon/__tests__/conversation-tool-setup.test.ts +17 -8
  321. package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
  322. package/src/daemon/config-watcher.ts +99 -5
  323. package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
  324. package/src/daemon/conversation-agent-loop.ts +101 -24
  325. package/src/daemon/conversation-error.ts +11 -0
  326. package/src/daemon/conversation-history.ts +40 -6
  327. package/src/daemon/conversation-launch.ts +220 -0
  328. package/src/daemon/conversation-lifecycle.ts +59 -9
  329. package/src/daemon/conversation-messaging.ts +37 -3
  330. package/src/daemon/conversation-notifiers.ts +5 -0
  331. package/src/daemon/conversation-process.ts +581 -19
  332. package/src/daemon/conversation-queue-manager.ts +24 -0
  333. package/src/daemon/conversation-runtime-assembly.ts +11 -1
  334. package/src/daemon/conversation-slash.ts +36 -0
  335. package/src/daemon/conversation-surfaces.ts +94 -4
  336. package/src/daemon/conversation-tool-setup.ts +25 -0
  337. package/src/daemon/conversation-usage.ts +7 -4
  338. package/src/daemon/conversation.ts +86 -28
  339. package/src/daemon/handlers/config-slack-channel.ts +269 -94
  340. package/src/daemon/handlers/conversations.ts +4 -1
  341. package/src/daemon/handlers/shared.ts +22 -0
  342. package/src/daemon/handlers/skills.ts +321 -77
  343. package/src/daemon/host-browser-proxy.ts +2 -1
  344. package/src/daemon/lifecycle.ts +122 -25
  345. package/src/daemon/message-protocol.ts +6 -0
  346. package/src/daemon/message-types/conversations.ts +34 -1
  347. package/src/daemon/message-types/home.ts +40 -0
  348. package/src/daemon/message-types/meet.ts +143 -0
  349. package/src/daemon/message-types/messages.ts +14 -0
  350. package/src/daemon/message-types/schedules.ts +34 -2
  351. package/src/daemon/message-types/skills.ts +16 -0
  352. package/src/daemon/message-types/surfaces.ts +2 -0
  353. package/src/daemon/server.ts +347 -2
  354. package/src/daemon/shutdown-handlers.ts +32 -4
  355. package/src/daemon/shutdown-registry.ts +40 -0
  356. package/src/daemon/tool-side-effects.ts +9 -0
  357. package/src/email/html-renderer.ts +76 -0
  358. package/src/heartbeat/heartbeat-service.ts +93 -7
  359. package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
  360. package/src/home/__tests__/emit-feed-event.test.ts +169 -0
  361. package/src/home/__tests__/feed-scheduler.test.ts +194 -0
  362. package/src/home/__tests__/feed-types.test.ts +275 -0
  363. package/src/home/__tests__/feed-writer.test.ts +688 -0
  364. package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
  365. package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
  366. package/src/home/__tests__/progress-formula.test.ts +213 -0
  367. package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
  368. package/src/home/__tests__/rollup-producer.test.ts +398 -0
  369. package/src/home/assistant-feed-authoring.ts +124 -0
  370. package/src/home/emit-feed-event.ts +158 -0
  371. package/src/home/feed-scheduler.ts +247 -0
  372. package/src/home/feed-types.ts +181 -0
  373. package/src/home/feed-writer.ts +469 -0
  374. package/src/home/platform-gmail-digest.ts +163 -0
  375. package/src/home/progress-formula.ts +86 -0
  376. package/src/home/relationship-state-writer.ts +824 -0
  377. package/src/home/relationship-state.ts +143 -0
  378. package/src/home/rollup-producer.ts +384 -0
  379. package/src/hooks/runner.ts +7 -0
  380. package/src/inbound/platform-callback-registration.ts +12 -3
  381. package/src/inbound/public-ingress-urls.ts +12 -0
  382. package/src/instrument.ts +1 -1
  383. package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
  384. package/src/ipc/cli-client.ts +151 -0
  385. package/src/ipc/cli-server.ts +234 -0
  386. package/src/ipc/gateway-client.ts +180 -0
  387. package/src/ipc/routes/index.ts +5 -0
  388. package/src/ipc/routes/wake-conversation.ts +19 -0
  389. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
  390. package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
  391. package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
  392. package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
  393. package/src/memory/app-store.ts +1 -1
  394. package/src/memory/attachments-store.ts +70 -0
  395. package/src/memory/auto-analysis-enqueue.ts +127 -0
  396. package/src/memory/auto-analysis-guard.ts +27 -0
  397. package/src/memory/cleanup-schedule-state.ts +37 -0
  398. package/src/memory/conversation-analyze-job.ts +73 -0
  399. package/src/memory/conversation-crud.ts +99 -0
  400. package/src/memory/conversation-disk-view.ts +7 -0
  401. package/src/memory/conversation-group-migration.ts +34 -2
  402. package/src/memory/conversation-queries.ts +6 -5
  403. package/src/memory/db-init.ts +6 -0
  404. package/src/memory/db-maintenance.ts +108 -0
  405. package/src/memory/db.ts +1 -0
  406. package/src/memory/graph/conversation-graph-memory.ts +15 -0
  407. package/src/memory/graph/extraction.test.ts +23 -0
  408. package/src/memory/graph/extraction.ts +8 -0
  409. package/src/memory/graph/retriever.ts +27 -18
  410. package/src/memory/graph/scoring.test.ts +186 -0
  411. package/src/memory/graph/scoring.ts +31 -1
  412. package/src/memory/graph/tools.ts +1 -1
  413. package/src/memory/group-crud.ts +6 -1
  414. package/src/memory/indexer.ts +95 -16
  415. package/src/memory/job-handlers/cleanup.ts +11 -8
  416. package/src/memory/job-handlers/conversation-starters.ts +16 -10
  417. package/src/memory/jobs-store.ts +64 -4
  418. package/src/memory/jobs-worker.ts +22 -9
  419. package/src/memory/llm-usage-store.ts +92 -56
  420. package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
  421. package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
  422. package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
  423. package/src/memory/migrations/index.ts +6 -0
  424. package/src/memory/migrations/registry.ts +8 -0
  425. package/src/memory/qdrant-manager.ts +43 -16
  426. package/src/memory/schema/conversations.ts +2 -0
  427. package/src/memory/schema/oauth.ts +3 -0
  428. package/src/memory/usage-buckets.ts +396 -0
  429. package/src/messaging/providers/gmail/client.ts +57 -6
  430. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
  431. package/src/messaging/providers/slack/adapter.ts +143 -38
  432. package/src/messaging/providers/slack/client.ts +16 -0
  433. package/src/messaging/providers/slack/types.ts +4 -0
  434. package/src/notifications/decision-engine.ts +3 -3
  435. package/src/notifications/signal.ts +5 -0
  436. package/src/oauth/__tests__/identity-verifier.test.ts +1 -0
  437. package/src/oauth/byo-connection.test.ts +18 -1
  438. package/src/oauth/byo-connection.ts +3 -1
  439. package/src/oauth/connect-orchestrator.ts +2 -0
  440. package/src/oauth/connection-resolver.ts +6 -2
  441. package/src/oauth/connection.ts +2 -0
  442. package/src/oauth/oauth-store.ts +9 -0
  443. package/src/oauth/platform-connection.test.ts +98 -0
  444. package/src/oauth/platform-connection.ts +52 -31
  445. package/src/oauth/seed-providers.ts +7 -0
  446. package/src/permissions/checker.ts +16 -6
  447. package/src/permissions/defaults.ts +49 -1
  448. package/src/permissions/trust-store.ts +3 -3
  449. package/src/permissions/workspace-policy.ts +3 -0
  450. package/src/platform/client.test.ts +10 -0
  451. package/src/platform/sync-identity.ts +129 -0
  452. package/src/prompts/persona-resolver.ts +126 -2
  453. package/src/prompts/system-prompt.ts +59 -18
  454. package/src/prompts/templates/BOOTSTRAP.md +5 -5
  455. package/src/prompts/templates/SOUL.md +3 -1
  456. package/src/prompts/templates/UPDATES.md +12 -0
  457. package/src/prompts/templates/channels/slack.md +20 -0
  458. package/src/prompts/update-bulletin-format.ts +26 -9
  459. package/src/prompts/update-bulletin.ts +34 -23
  460. package/src/prompts/user-reference.ts +20 -17
  461. package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
  462. package/src/providers/anthropic/client.ts +157 -61
  463. package/src/providers/fireworks/client.ts +2 -2
  464. package/src/providers/gemini/client.ts +9 -1
  465. package/src/providers/model-catalog.ts +6 -0
  466. package/src/providers/model-intents.ts +4 -4
  467. package/src/providers/ollama/client.ts +2 -2
  468. package/src/providers/openai/chat-completions-provider.ts +474 -0
  469. package/src/providers/openai/client.ts +25 -440
  470. package/src/providers/openai/responses-provider.ts +502 -0
  471. package/src/providers/openrouter/client.ts +101 -4
  472. package/src/providers/provider-secret-catalog.ts +139 -0
  473. package/src/providers/registry.ts +2 -2
  474. package/src/providers/retry.ts +14 -3
  475. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
  476. package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
  477. package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
  478. package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
  479. package/src/providers/speech-to-text/deepgram.test.ts +332 -0
  480. package/src/providers/speech-to-text/deepgram.ts +115 -0
  481. package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
  482. package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
  483. package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
  484. package/src/providers/speech-to-text/google-gemini.ts +101 -0
  485. package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
  486. package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
  487. package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
  488. package/src/providers/speech-to-text/openai-whisper.ts +63 -33
  489. package/src/providers/speech-to-text/provider-catalog.ts +306 -0
  490. package/src/providers/speech-to-text/resolve.ts +386 -6
  491. package/src/providers/types.ts +9 -0
  492. package/src/runtime/AGENTS.md +43 -1
  493. package/src/runtime/__tests__/agent-wake.test.ts +831 -0
  494. package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
  495. package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
  496. package/src/runtime/agent-wake.ts +512 -0
  497. package/src/runtime/auth/__tests__/route-policy.test.ts +40 -0
  498. package/src/runtime/auth/route-policy.ts +30 -5
  499. package/src/runtime/auth/token-service.ts +56 -1
  500. package/src/runtime/btw-sidechain.ts +2 -0
  501. package/src/runtime/capability-tokens.ts +10 -10
  502. package/src/runtime/channel-invite-transport.ts +1 -1
  503. package/src/runtime/channel-invite-transports/email.ts +14 -6
  504. package/src/runtime/channel-readiness-service.ts +12 -22
  505. package/src/runtime/chrome-extension-registry.ts +38 -2
  506. package/src/runtime/http-server.ts +395 -10
  507. package/src/runtime/http-types.ts +6 -2
  508. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
  509. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
  510. package/src/runtime/migrations/migration-transport.ts +1 -0
  511. package/src/runtime/migrations/migration-wizard.ts +1 -0
  512. package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
  513. package/src/runtime/migrations/vbundle-importer.ts +34 -0
  514. package/src/runtime/pending-interactions.ts +0 -11
  515. package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
  516. package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
  517. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
  518. package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
  519. package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
  520. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
  521. package/src/runtime/routes/app-management-routes.ts +12 -18
  522. package/src/runtime/routes/attachment-routes.test.ts +9 -3
  523. package/src/runtime/routes/attachment-routes.ts +216 -17
  524. package/src/runtime/routes/backup-routes.ts +519 -0
  525. package/src/runtime/routes/browser-extension-pair-routes.ts +82 -23
  526. package/src/runtime/routes/btw-routes.ts +8 -6
  527. package/src/runtime/routes/contact-routes.test.ts +298 -0
  528. package/src/runtime/routes/contact-routes.ts +132 -5
  529. package/src/runtime/routes/conversation-analysis-routes.ts +22 -142
  530. package/src/runtime/routes/conversation-management-routes.ts +115 -0
  531. package/src/runtime/routes/conversation-routes.ts +367 -146
  532. package/src/runtime/routes/filing-routes.ts +93 -0
  533. package/src/runtime/routes/home-feed-routes.ts +334 -0
  534. package/src/runtime/routes/home-state-routes.ts +138 -0
  535. package/src/runtime/routes/host-browser-routes.ts +3 -14
  536. package/src/runtime/routes/identity-intro-cache.ts +7 -3
  537. package/src/runtime/routes/identity-routes.ts +3 -17
  538. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
  539. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
  540. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
  541. package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
  542. package/src/runtime/routes/integrations/slack/channel.ts +11 -3
  543. package/src/runtime/routes/integrations/slack/share.ts +45 -7
  544. package/src/runtime/routes/llm-context-normalization.ts +303 -0
  545. package/src/runtime/routes/memory-item-routes.test.ts +3 -2
  546. package/src/runtime/routes/migration-routes.ts +40 -5
  547. package/src/runtime/routes/settings-routes.ts +22 -5
  548. package/src/runtime/routes/skills-routes.ts +76 -7
  549. package/src/runtime/routes/stt-routes.ts +233 -0
  550. package/src/runtime/routes/surface-action-routes.ts +41 -2
  551. package/src/runtime/routes/tts-routes.ts +108 -24
  552. package/src/runtime/routes/usage-routes.ts +30 -2
  553. package/src/runtime/routes/user-route-dispatcher.ts +50 -5
  554. package/src/runtime/routes/user-routes.ts +13 -1
  555. package/src/runtime/routes/work-items-routes.ts +8 -1
  556. package/src/runtime/runtime-mode.ts +33 -0
  557. package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
  558. package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
  559. package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
  560. package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
  561. package/src/runtime/services/analyze-conversation.ts +344 -0
  562. package/src/runtime/services/analyze-deps-singleton.ts +32 -0
  563. package/src/runtime/services/auto-analysis-prompt.ts +55 -0
  564. package/src/runtime/skill-route-registry.ts +49 -0
  565. package/src/runtime/slack-block-formatting.ts +437 -10
  566. package/src/schedule/scheduler.ts +50 -0
  567. package/src/security/oauth2.ts +26 -4
  568. package/src/security/secure-keys.ts +25 -2
  569. package/src/security/token-manager.ts +8 -0
  570. package/src/sequence/engine.ts +23 -0
  571. package/src/sequence/types.ts +1 -1
  572. package/src/skills/catalog-files.ts +64 -2
  573. package/src/skills/category-inference.ts +122 -0
  574. package/src/skills/clawhub-files.ts +213 -0
  575. package/src/skills/clawhub.ts +84 -23
  576. package/src/skills/skill-file-provider.ts +40 -0
  577. package/src/skills/skillssh-files.ts +395 -0
  578. package/src/skills/skillssh-registry.ts +4 -4
  579. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
  580. package/src/stt/__tests__/types.test.ts +89 -0
  581. package/src/stt/daemon-batch-transcriber.ts +195 -0
  582. package/src/stt/stt-stream-session.ts +499 -0
  583. package/src/stt/types.ts +330 -0
  584. package/src/stt/wav-encoder.test.ts +373 -0
  585. package/src/stt/wav-encoder.ts +175 -0
  586. package/src/subagent/manager.ts +38 -14
  587. package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
  588. package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
  589. package/src/tools/browser/browser-execution.ts +1163 -23
  590. package/src/tools/browser/browser-manager.ts +45 -0
  591. package/src/tools/browser/browser-mode-constants.ts +12 -0
  592. package/src/tools/browser/browser-mode.ts +92 -0
  593. package/src/tools/browser/browser-status-constants.ts +33 -0
  594. package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +393 -0
  595. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
  596. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
  597. package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
  598. package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +183 -17
  599. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
  600. package/src/tools/browser/cdp-client/errors.ts +15 -0
  601. package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
  602. package/src/tools/browser/cdp-client/factory.ts +797 -87
  603. package/src/tools/browser/cdp-client/index.ts +16 -2
  604. package/src/tools/browser/cdp-client/types.ts +68 -0
  605. package/src/tools/credentials/vault.ts +35 -6
  606. package/src/tools/network/web-fetch.ts +5 -2
  607. package/src/tools/network/web-search.ts +5 -2
  608. package/src/tools/shared/shell-output.ts +3 -1
  609. package/src/tools/side-effects.ts +2 -0
  610. package/src/tools/skills/sandbox-runner.ts +3 -2
  611. package/src/tools/terminal/safe-env.ts +10 -2
  612. package/src/tools/terminal/shell.ts +15 -4
  613. package/src/tools/tool-manifest.ts +21 -0
  614. package/src/tools/types.ts +17 -0
  615. package/src/tools/ui-surface/definitions.ts +6 -1
  616. package/src/tts/__tests__/provider-adapters.test.ts +834 -0
  617. package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
  618. package/src/tts/__tests__/provider-catalog.test.ts +183 -0
  619. package/src/tts/__tests__/provider-registry.test.ts +90 -0
  620. package/src/tts/provider-catalog.ts +201 -0
  621. package/src/tts/provider-registry.ts +73 -0
  622. package/src/tts/providers/deepgram-provider.ts +219 -0
  623. package/src/tts/providers/elevenlabs-provider.ts +211 -0
  624. package/src/tts/providers/fish-audio-provider.ts +183 -0
  625. package/src/tts/providers/index.ts +42 -0
  626. package/src/tts/providers/register-builtins.ts +130 -0
  627. package/src/tts/synthesize-text.ts +110 -0
  628. package/src/tts/tts-config-resolver.ts +78 -0
  629. package/src/tts/types.ts +153 -0
  630. package/src/types/onboarding-context.ts +7 -0
  631. package/src/util/abort-reasons.ts +58 -0
  632. package/src/util/device-id.ts +32 -16
  633. package/src/util/errors.ts +9 -1
  634. package/src/util/platform.ts +54 -10
  635. package/src/util/pricing.ts +66 -3
  636. package/src/util/spawn.ts +1 -1
  637. package/src/util/truncate.ts +4 -2
  638. package/src/util/unicode.ts +201 -0
  639. package/src/version.ts +19 -24
  640. package/src/watcher/engine.ts +23 -0
  641. package/src/watcher/watcher-store.ts +31 -0
  642. package/src/workspace/migrations/003-seed-device-id.ts +9 -3
  643. package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
  644. package/src/workspace/migrations/029-seed-pkb.ts +1 -1
  645. package/src/workspace/migrations/031-drop-user-md.ts +317 -0
  646. package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
  647. package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
  648. package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
  649. package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
  650. package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
  651. package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
  652. package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
  653. package/src/workspace/migrations/registry.ts +16 -0
  654. package/src/workspace/top-level-renderer.ts +13 -1
  655. package/src/workspace/turn-commit.ts +31 -0
  656. package/src/__tests__/email-cli.test.ts +0 -297
  657. package/src/__tests__/email-service-config-fallback.test.ts +0 -102
  658. package/src/cli/commands/browser-relay.ts +0 -466
  659. package/src/email/guardrails.ts +0 -221
  660. package/src/email/provider.ts +0 -117
  661. package/src/email/providers/agentmail.ts +0 -361
  662. package/src/email/providers/index.ts +0 -65
  663. package/src/email/service.ts +0 -384
  664. package/src/email/types.ts +0 -126
  665. package/src/prompts/templates/USER.md +0 -13
  666. package/src/providers/speech-to-text/types.ts +0 -17
  667. package/src/runtime/routes/browser-cdp-routes.ts +0 -229
@@ -3,6 +3,7 @@ import { describe, expect, test } from "bun:test";
3
3
  import {
4
4
  appendReleaseBlock,
5
5
  extractReleaseIds,
6
+ filterNewContentBlocks,
6
7
  hasReleaseBlock,
7
8
  releaseMarker,
8
9
  } from "../prompts/update-bulletin-format.js";
@@ -83,6 +84,64 @@ describe("appendReleaseBlock", () => {
83
84
  });
84
85
  });
85
86
 
87
+ describe("filterNewContentBlocks", () => {
88
+ const blockA = [
89
+ "<!-- vellum-update-release:entry-a -->",
90
+ "## Entry A",
91
+ "Content for A.",
92
+ "<!-- /vellum-update-release:entry-a -->",
93
+ ].join("\n");
94
+
95
+ const blockB = [
96
+ "<!-- vellum-update-release:entry-b -->",
97
+ "## Entry B",
98
+ "Content for B.",
99
+ "<!-- /vellum-update-release:entry-b -->",
100
+ ].join("\n");
101
+
102
+ const blockC = [
103
+ "<!-- vellum-update-release:entry-c -->",
104
+ "## Entry C",
105
+ "Content for C.",
106
+ "<!-- /vellum-update-release:entry-c -->",
107
+ ].join("\n");
108
+
109
+ test("returns body unchanged when no block structure exists", () => {
110
+ const body = "## What's New\n\nPlain text notes.\n";
111
+ expect(filterNewContentBlocks(body, "anything")).toBe(body);
112
+ });
113
+
114
+ test("returns all blocks when none are present in existing content", () => {
115
+ const body = `${blockA}\n\n${blockB}`;
116
+ const result = filterNewContentBlocks(body, "no markers here");
117
+ expect(result).toContain("entry-a");
118
+ expect(result).toContain("entry-b");
119
+ });
120
+
121
+ test("returns empty string when all blocks are already present", () => {
122
+ const body = `${blockA}\n\n${blockB}`;
123
+ const existing = `<!-- vellum-update-release:0.9.0 -->\n${blockA}\n\n${blockB}\n`;
124
+ expect(filterNewContentBlocks(body, existing)).toBe("");
125
+ });
126
+
127
+ test("returns only new blocks when some are already present", () => {
128
+ const body = `${blockA}\n\n${blockB}\n\n${blockC}`;
129
+ const existing = `<!-- vellum-update-release:0.9.0 -->\n${blockA}\n\n${blockB}\n`;
130
+
131
+ const result = filterNewContentBlocks(body, existing);
132
+ expect(result).toContain("entry-c");
133
+ expect(result).toContain("Content for C.");
134
+ expect(result).not.toContain("entry-a");
135
+ expect(result).not.toContain("entry-b");
136
+ });
137
+
138
+ test("handles single-block template", () => {
139
+ const result = filterNewContentBlocks(blockA, "no markers");
140
+ expect(result).toContain("entry-a");
141
+ expect(result).toContain("Content for A.");
142
+ });
143
+ });
144
+
86
145
  describe("extractReleaseIds", () => {
87
146
  test("returns all version strings from multiple markers", () => {
88
147
  const content = [
@@ -31,6 +31,13 @@ mock.module("../memory/checkpoints.js", () => ({
31
31
  ),
32
32
  }));
33
33
 
34
+ // --- Mutable config stub for updates.enabled tests ---
35
+ const updatesConfig = { enabled: true };
36
+
37
+ mock.module("../config/loader.js", () => ({
38
+ getConfig: () => ({ updates: updatesConfig }),
39
+ }));
40
+
34
41
  // --- Temp directory for template files ---
35
42
  // Avoids mutating the real source-controlled UPDATES.md template, preventing
36
43
  // race conditions with parallel test execution and working tree corruption
@@ -60,6 +67,7 @@ const COMMENT_ONLY_TEMPLATE =
60
67
  describe("syncUpdateBulletinOnStartup", () => {
61
68
  beforeEach(() => {
62
69
  store.clear();
70
+ updatesConfig.enabled = true;
63
71
  // Remove any leftover workspace UPDATES.md from a previous test
64
72
  if (existsSync(workspacePath)) {
65
73
  rmSync(workspacePath);
@@ -92,6 +100,18 @@ describe("syncUpdateBulletinOnStartup", () => {
92
100
  expect(content).toContain("What's New");
93
101
  });
94
102
 
103
+ it("skips materialization entirely when updates.enabled is false", () => {
104
+ updatesConfig.enabled = false;
105
+ expect(existsSync(workspacePath)).toBe(false);
106
+
107
+ syncUpdateBulletinOnStartup();
108
+
109
+ expect(existsSync(workspacePath)).toBe(false);
110
+ // No checkpoint writes should have happened either.
111
+ expect(store.get("updates:active_releases")).toBeUndefined();
112
+ expect(store.get("updates:completed_releases")).toBeUndefined();
113
+ });
114
+
95
115
  it("appends release block when workspace file exists without current marker", () => {
96
116
  const preExisting =
97
117
  "<!-- vellum-update-release:0.9.0 -->\nOld release notes.\n";
@@ -133,28 +153,34 @@ describe("syncUpdateBulletinOnStartup", () => {
133
153
  });
134
154
 
135
155
  it("marks active releases as completed when UPDATES.md is deleted", () => {
136
- // Pre-populate active releases in the store
137
- store.set("updates:active_releases", JSON.stringify(["0.8.0", "0.9.0"]));
156
+ // Pre-populate active releases including the current one — simulates
157
+ // a normal session where 1.0.0 was materialized alongside leftover
158
+ // entries from prior versions.
159
+ store.set(
160
+ "updates:active_releases",
161
+ JSON.stringify(["0.8.0", "0.9.0", "1.0.0"]),
162
+ );
138
163
 
139
164
  // Workspace file does not exist — simulates the assistant having deleted it
140
165
  expect(existsSync(workspacePath)).toBe(false);
141
166
 
142
167
  syncUpdateBulletinOnStartup();
143
168
 
144
- // Active set should be cleared (except for the newly-added current release)
169
+ // Active set should be cleared
145
170
  const activeRaw = store.get("updates:active_releases");
146
171
  expect(activeRaw).toBeDefined();
147
172
  const active: string[] = JSON.parse(activeRaw!);
148
- // The old releases should not be in the active set
149
173
  expect(active).not.toContain("0.8.0");
150
174
  expect(active).not.toContain("0.9.0");
175
+ expect(active).not.toContain("1.0.0");
151
176
 
152
- // The old releases should now be completed
177
+ // All releases should now be completed
153
178
  const completedRaw = store.get("updates:completed_releases");
154
179
  expect(completedRaw).toBeDefined();
155
180
  const completed: string[] = JSON.parse(completedRaw!);
156
181
  expect(completed).toContain("0.8.0");
157
182
  expect(completed).toContain("0.9.0");
183
+ expect(completed).toContain("1.0.0");
158
184
  });
159
185
 
160
186
  it("does not recreate completed release after deletion", () => {
@@ -173,6 +199,82 @@ describe("syncUpdateBulletinOnStartup", () => {
173
199
  expect(existsSync(workspacePath)).toBe(false);
174
200
  });
175
201
 
202
+ it("treats an empty UPDATES.md as dismissal of the current release", () => {
203
+ // Pre-populate active so we're past the fresh-install guard.
204
+ store.set("updates:active_releases", JSON.stringify(["1.0.0"]));
205
+ writeFileSync(workspacePath, "", "utf-8");
206
+
207
+ syncUpdateBulletinOnStartup();
208
+
209
+ // File should be left alone (still empty, not refilled).
210
+ expect(existsSync(workspacePath)).toBe(true);
211
+ expect(readFileSync(workspacePath, "utf-8")).toBe("");
212
+
213
+ const completed: string[] = JSON.parse(
214
+ store.get("updates:completed_releases")!,
215
+ );
216
+ expect(completed).toContain("1.0.0");
217
+ });
218
+
219
+ it("treats a whitespace-only UPDATES.md as dismissal of the current release", () => {
220
+ // Pre-populate active with the current release so the dismissal branch
221
+ // is scoped correctly to this version.
222
+ store.set("updates:active_releases", JSON.stringify(["1.0.0"]));
223
+ writeFileSync(workspacePath, " \n\n\t\n", "utf-8");
224
+
225
+ syncUpdateBulletinOnStartup();
226
+
227
+ // File should be left alone — content must not be re-appended.
228
+ const content = readFileSync(workspacePath, "utf-8");
229
+ expect(content).not.toContain("<!-- vellum-update-release:1.0.0 -->");
230
+
231
+ const completed: string[] = JSON.parse(
232
+ store.get("updates:completed_releases")!,
233
+ );
234
+ expect(completed).toContain("1.0.0");
235
+ });
236
+
237
+ it("materializes new version bulletin after a prior release was dismissed", () => {
238
+ // Verifies that a new version's bulletin is materialized even when a
239
+ // prior release exists in the completed set. Dismissal detection is
240
+ // scoped to the current release — only suppress if this version was
241
+ // already active or completed, not because an unrelated version was.
242
+ store.set("updates:completed_releases", JSON.stringify(["0.9.0"]));
243
+ expect(existsSync(workspacePath)).toBe(false);
244
+
245
+ syncUpdateBulletinOnStartup();
246
+
247
+ expect(existsSync(workspacePath)).toBe(true);
248
+ const content = readFileSync(workspacePath, "utf-8");
249
+ expect(content).toContain("<!-- vellum-update-release:1.0.0 -->");
250
+ expect(content).toContain("What's New");
251
+
252
+ // 1.0.0 must NOT have been auto-completed.
253
+ const completed: string[] = JSON.parse(
254
+ store.get("updates:completed_releases")!,
255
+ );
256
+ expect(completed).not.toContain("1.0.0");
257
+
258
+ // 1.0.0 should be in the active set.
259
+ const active: string[] = JSON.parse(store.get("updates:active_releases")!);
260
+ expect(active).toContain("1.0.0");
261
+ });
262
+
263
+ it("creates file on fresh install even though it is missing", () => {
264
+ // Both active and completed are empty — brand-new DB.
265
+ expect(store.get("updates:active_releases")).toBeUndefined();
266
+ expect(store.get("updates:completed_releases")).toBeUndefined();
267
+ expect(existsSync(workspacePath)).toBe(false);
268
+
269
+ syncUpdateBulletinOnStartup();
270
+
271
+ // Fresh install should materialize, not dismiss.
272
+ expect(existsSync(workspacePath)).toBe(true);
273
+ expect(readFileSync(workspacePath, "utf-8")).toContain(
274
+ "<!-- vellum-update-release:1.0.0 -->",
275
+ );
276
+ });
277
+
176
278
  it("merges pending old block with new release block", () => {
177
279
  // Pre-create workspace file with an old release block
178
280
  const oldContent =
@@ -239,6 +341,105 @@ describe("syncUpdateBulletinOnStartup", () => {
239
341
  expect(existsSync(workspacePath)).toBe(false);
240
342
  });
241
343
 
344
+ it("only appends new content blocks on version bump with extended template", () => {
345
+ // Workspace already has entries A and B from a prior release
346
+ const oldContent = [
347
+ "<!-- vellum-update-release:0.9.0 -->",
348
+ "<!-- vellum-update-release:entry-a -->",
349
+ "## Entry A",
350
+ "Content for A.",
351
+ "<!-- /vellum-update-release:entry-a -->",
352
+ "",
353
+ "<!-- vellum-update-release:entry-b -->",
354
+ "## Entry B",
355
+ "Content for B.",
356
+ "<!-- /vellum-update-release:entry-b -->",
357
+ "",
358
+ ].join("\n");
359
+ writeFileSync(workspacePath, oldContent, "utf-8");
360
+
361
+ // Template now has A, B, C — C is the only new entry
362
+ const extendedTemplate = [
363
+ "<!-- vellum-update-release:entry-a -->",
364
+ "## Entry A",
365
+ "Content for A.",
366
+ "<!-- /vellum-update-release:entry-a -->",
367
+ "",
368
+ "<!-- vellum-update-release:entry-b -->",
369
+ "## Entry B",
370
+ "Content for B.",
371
+ "<!-- /vellum-update-release:entry-b -->",
372
+ "",
373
+ "<!-- vellum-update-release:entry-c -->",
374
+ "## Entry C",
375
+ "New content for C.",
376
+ "<!-- /vellum-update-release:entry-c -->",
377
+ "",
378
+ ].join("\n");
379
+ writeFileSync(
380
+ join(tempTemplateDir, "UPDATES.md"),
381
+ extendedTemplate,
382
+ "utf-8",
383
+ );
384
+
385
+ syncUpdateBulletinOnStartup();
386
+
387
+ const content = readFileSync(workspacePath, "utf-8");
388
+
389
+ // New release block should be present
390
+ expect(content).toContain("<!-- vellum-update-release:1.0.0 -->");
391
+
392
+ // Entry C should appear
393
+ expect(content).toContain("entry-c");
394
+ expect(content).toContain("New content for C.");
395
+
396
+ // Entries A and B should NOT be duplicated
397
+ const countA = (
398
+ content.match(/<!-- vellum-update-release:entry-a -->/g) || []
399
+ ).length;
400
+ const countB = (
401
+ content.match(/<!-- vellum-update-release:entry-b -->/g) || []
402
+ ).length;
403
+ expect(countA).toBe(1);
404
+ expect(countB).toBe(1);
405
+ });
406
+
407
+ it("skips append when all template content blocks already exist in workspace", () => {
408
+ // Workspace already has entries A and B from a prior release
409
+ const oldContent = [
410
+ "<!-- vellum-update-release:0.9.0 -->",
411
+ "<!-- vellum-update-release:entry-a -->",
412
+ "## Entry A",
413
+ "<!-- /vellum-update-release:entry-a -->",
414
+ "",
415
+ "<!-- vellum-update-release:entry-b -->",
416
+ "## Entry B",
417
+ "<!-- /vellum-update-release:entry-b -->",
418
+ "",
419
+ ].join("\n");
420
+ writeFileSync(workspacePath, oldContent, "utf-8");
421
+
422
+ // Template has the same A and B — nothing new
423
+ const sameTemplate = [
424
+ "<!-- vellum-update-release:entry-a -->",
425
+ "## Entry A",
426
+ "<!-- /vellum-update-release:entry-a -->",
427
+ "",
428
+ "<!-- vellum-update-release:entry-b -->",
429
+ "## Entry B",
430
+ "<!-- /vellum-update-release:entry-b -->",
431
+ "",
432
+ ].join("\n");
433
+ writeFileSync(join(tempTemplateDir, "UPDATES.md"), sameTemplate, "utf-8");
434
+
435
+ syncUpdateBulletinOnStartup();
436
+
437
+ const content = readFileSync(workspacePath, "utf-8");
438
+
439
+ // No 1.0.0 block should be added — all content already present
440
+ expect(content).not.toContain("<!-- vellum-update-release:1.0.0 -->");
441
+ });
442
+
242
443
  it("preserves existing file when atomic write fails", () => {
243
444
  const originalContent =
244
445
  "<!-- vellum-update-release:0.9.0 -->\nOriginal content.\n";
@@ -202,11 +202,32 @@ describe("usage routes", () => {
202
202
  // -- daily buckets --
203
203
 
204
204
  describe("GET /v1/usage/daily", () => {
205
- test("returns empty buckets array for empty range", async () => {
206
- const res = await dispatch("GET", "usage/daily?from=0&to=999999999999");
205
+ test("returns zero-filled buckets when no events in range", async () => {
206
+ const from = new Date("2025-01-15T00:00:00Z").getTime();
207
+ const to = new Date("2025-01-17T23:59:59Z").getTime();
208
+ const res = await dispatch("GET", `usage/daily?from=${from}&to=${to}`);
207
209
  expect(res.status).toBe(200);
208
- const body = (await res.json()) as { buckets: unknown[] };
209
- expect(body.buckets).toEqual([]);
210
+ const body = (await res.json()) as {
211
+ buckets: Array<{
212
+ date: string;
213
+ eventCount: number;
214
+ totalInputTokens: number;
215
+ totalOutputTokens: number;
216
+ totalEstimatedCostUsd: number;
217
+ }>;
218
+ };
219
+ expect(body.buckets).toHaveLength(3);
220
+ expect(body.buckets.map((b) => b.date)).toEqual([
221
+ "2025-01-15",
222
+ "2025-01-16",
223
+ "2025-01-17",
224
+ ]);
225
+ for (const bucket of body.buckets) {
226
+ expect(bucket.eventCount).toBe(0);
227
+ expect(bucket.totalInputTokens).toBe(0);
228
+ expect(bucket.totalOutputTokens).toBe(0);
229
+ expect(bucket.totalEstimatedCostUsd).toBe(0);
230
+ }
210
231
  });
211
232
 
212
233
  test("returns daily buckets for seeded data", async () => {
@@ -1,27 +1,26 @@
1
- import * as realFs from "node:fs";
2
- import { join } from "node:path";
1
+ /**
2
+ * Tests for user-reference resolvers. After the drop-user-md migration,
3
+ * `readPreferredNameFromUserMd` and `resolveUserPronouns` source their
4
+ * content from the guardian's per-user persona file via
5
+ * `resolveGuardianPersonaStrict()` (no `default.md` fallback). We mock
6
+ * the persona-resolver module directly so tests can drive the input
7
+ * content without touching disk.
8
+ */
9
+
3
10
  import { beforeEach, describe, expect, mock, test } from "bun:test";
4
11
 
5
- const TEST_DIR = process.env.VELLUM_WORKSPACE_DIR!;
6
-
7
- // Mutable state the tests control
8
- let mockFileExists = false;
9
- let mockFileContent = "";
10
-
11
- mock.module("node:fs", () => ({
12
- ...realFs,
13
- existsSync: (path: string) => {
14
- if (path === join(TEST_DIR, "USER.md")) return mockFileExists;
15
- return false;
16
- },
17
- readFileSync: (path: string, _encoding: string) => {
18
- if (path === join(TEST_DIR, "USER.md") && mockFileExists)
19
- return mockFileContent;
20
- throw new Error(`ENOENT: no such file: ${path}`);
21
- },
12
+ // Mutable state the tests control — represents the value returned by
13
+ // `resolveGuardianPersonaStrict()` (comment-stripped string, or null
14
+ // when no guardian / empty / missing guardian-specific file).
15
+ let mockGuardianPersona: string | null = null;
16
+
17
+ mock.module("../prompts/persona-resolver.js", () => ({
18
+ resolveGuardianPersona: () => mockGuardianPersona,
19
+ resolveGuardianPersonaStrict: () => mockGuardianPersona,
22
20
  }));
23
21
 
24
- // Import after mocks are in place
22
+ // Import after mocks are in place so the module under test binds to
23
+ // the stubbed implementation.
25
24
  const {
26
25
  resolveUserReference,
27
26
  resolveUserPronouns,
@@ -31,18 +30,16 @@ const {
31
30
 
32
31
  describe("resolveUserReference", () => {
33
32
  beforeEach(() => {
34
- mockFileExists = false;
35
- mockFileContent = "";
33
+ mockGuardianPersona = null;
36
34
  });
37
35
 
38
- test('returns "my human" when USER.md does not exist', () => {
39
- mockFileExists = false;
36
+ test('returns "my human" when no guardian persona exists', () => {
37
+ mockGuardianPersona = null;
40
38
  expect(resolveUserReference()).toBe("my human");
41
39
  });
42
40
 
43
41
  test('returns "my human" when preferred name field is empty', () => {
44
- mockFileExists = true;
45
- mockFileContent = [
42
+ mockGuardianPersona = [
46
43
  "## Onboarding Snapshot",
47
44
  "",
48
45
  "- Preferred name/reference:",
@@ -53,8 +50,7 @@ describe("resolveUserReference", () => {
53
50
  });
54
51
 
55
52
  test("returns the configured name when it is set", () => {
56
- mockFileExists = true;
57
- mockFileContent = [
53
+ mockGuardianPersona = [
58
54
  "## Onboarding Snapshot",
59
55
  "",
60
56
  "- Preferred name/reference: John",
@@ -65,27 +61,24 @@ describe("resolveUserReference", () => {
65
61
  });
66
62
 
67
63
  test("trims whitespace around the configured name", () => {
68
- mockFileExists = true;
69
- mockFileContent = "- Preferred name/reference: Alice \n";
64
+ mockGuardianPersona = "- Preferred name/reference: Alice \n";
70
65
  expect(resolveUserReference()).toBe("Alice");
71
66
  });
72
67
  });
73
68
 
74
69
  describe("resolveUserPronouns", () => {
75
70
  beforeEach(() => {
76
- mockFileExists = false;
77
- mockFileContent = "";
71
+ mockGuardianPersona = null;
78
72
  });
79
73
 
80
- test("returns null when USER.md does not exist", () => {
81
- mockFileExists = false;
74
+ test("returns null when no guardian persona exists", () => {
75
+ mockGuardianPersona = null;
82
76
  expect(resolveUserPronouns()).toBeNull();
83
77
  });
84
78
 
85
- test("returns pronouns from flat USER.md (no Onboarding Snapshot)", () => {
86
- mockFileExists = true;
87
- mockFileContent = [
88
- "# USER.md",
79
+ test("returns pronouns from flat persona file (no Onboarding Snapshot)", () => {
80
+ mockGuardianPersona = [
81
+ "# User Profile",
89
82
  "",
90
83
  "- Preferred name/reference: Alice",
91
84
  "- Pronouns: she/her",
@@ -95,9 +88,8 @@ describe("resolveUserPronouns", () => {
95
88
  });
96
89
 
97
90
  test("returns null when pronouns field is empty in flat format", () => {
98
- mockFileExists = true;
99
- mockFileContent = [
100
- "# USER.md",
91
+ mockGuardianPersona = [
92
+ "# User Profile",
101
93
  "",
102
94
  "- Preferred name/reference: Alice",
103
95
  "- Pronouns:",
@@ -107,8 +99,7 @@ describe("resolveUserPronouns", () => {
107
99
  });
108
100
 
109
101
  test("returns pronouns from legacy Onboarding Snapshot section", () => {
110
- mockFileExists = true;
111
- mockFileContent = [
102
+ mockGuardianPersona = [
112
103
  "## Onboarding Snapshot",
113
104
  "",
114
105
  "- Pronouns: they/them",
@@ -117,8 +108,7 @@ describe("resolveUserPronouns", () => {
117
108
  });
118
109
 
119
110
  test("prefers pronouns above Onboarding Snapshot over inside it", () => {
120
- mockFileExists = true;
121
- mockFileContent = [
111
+ mockGuardianPersona = [
122
112
  "Pronouns: he/him",
123
113
  "",
124
114
  "## Onboarding Snapshot",
@@ -129,8 +119,7 @@ describe("resolveUserPronouns", () => {
129
119
  });
130
120
 
131
121
  test("returns null for declined_by_user", () => {
132
- mockFileExists = true;
133
- mockFileContent = [
122
+ mockGuardianPersona = [
134
123
  "- Preferred name/reference: Alice",
135
124
  "- Pronouns: declined_by_user",
136
125
  ].join("\n");
@@ -138,8 +127,7 @@ describe("resolveUserPronouns", () => {
138
127
  });
139
128
 
140
129
  test("strips inferred: prefix", () => {
141
- mockFileExists = true;
142
- mockFileContent = [
130
+ mockGuardianPersona = [
143
131
  "- Preferred name/reference: Alice",
144
132
  "- Pronouns: inferred: she/her",
145
133
  ].join("\n");
@@ -149,13 +137,11 @@ describe("resolveUserPronouns", () => {
149
137
 
150
138
  describe("resolveGuardianName", () => {
151
139
  beforeEach(() => {
152
- mockFileExists = false;
153
- mockFileContent = "";
140
+ mockGuardianPersona = null;
154
141
  });
155
142
 
156
- test("returns USER.md name when present, ignoring guardianDisplayName", () => {
157
- mockFileExists = true;
158
- mockFileContent = [
143
+ test("returns persona name when present, ignoring guardianDisplayName", () => {
144
+ mockGuardianPersona = [
159
145
  "## Onboarding Snapshot",
160
146
  "",
161
147
  "- Preferred name/reference: John",
@@ -163,9 +149,8 @@ describe("resolveGuardianName", () => {
163
149
  expect(resolveGuardianName("Jane")).toBe("John");
164
150
  });
165
151
 
166
- test('returns "my human" when USER.md explicitly sets name to default value', () => {
167
- mockFileExists = true;
168
- mockFileContent = [
152
+ test('returns "my human" when persona explicitly sets name to default value', () => {
153
+ mockGuardianPersona = [
169
154
  "## Onboarding Snapshot",
170
155
  "",
171
156
  "- Preferred name/reference: my human",
@@ -174,20 +159,20 @@ describe("resolveGuardianName", () => {
174
159
  expect(resolveGuardianName("Jane")).toBe("my human");
175
160
  });
176
161
 
177
- test("falls back to guardianDisplayName when USER.md is empty", () => {
178
- mockFileExists = false;
162
+ test("falls back to guardianDisplayName when persona is empty", () => {
163
+ mockGuardianPersona = null;
179
164
  expect(resolveGuardianName("Jane")).toBe("Jane");
180
165
  });
181
166
 
182
167
  test("falls back to DEFAULT_USER_REFERENCE when both are empty", () => {
183
- mockFileExists = false;
168
+ mockGuardianPersona = null;
184
169
  expect(resolveGuardianName()).toBe(DEFAULT_USER_REFERENCE);
185
170
  expect(resolveGuardianName(null)).toBe(DEFAULT_USER_REFERENCE);
186
171
  expect(resolveGuardianName("")).toBe(DEFAULT_USER_REFERENCE);
187
172
  });
188
173
 
189
174
  test("trims whitespace on guardianDisplayName fallback", () => {
190
- mockFileExists = false;
175
+ mockGuardianPersona = null;
191
176
  expect(resolveGuardianName(" Jane ")).toBe("Jane");
192
177
  });
193
178
  });
@@ -73,8 +73,12 @@ mock.module("../permissions/checker.js", () => ({
73
73
  generateScopeOptions: () => [],
74
74
  }));
75
75
 
76
+ // Mock every export so downstream test files that dynamically import modules
77
+ // with a static `from "../memory/tool-usage-store.js"` still see all symbols.
76
78
  mock.module("../memory/tool-usage-store.js", () => ({
77
79
  recordToolInvocation: () => {},
80
+ getRecentInvocations: () => [],
81
+ rotateToolInvocations: () => 0,
78
82
  }));
79
83
 
80
84
  mock.module("../tools/registry.js", () => ({