@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
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Sync assistant identity fields to the platform Assistant record.
3
+ *
4
+ * When IDENTITY.md changes on disk the daemon broadcasts an
5
+ * `identity_changed` event to connected clients. This module hooks into
6
+ * that same change signal and PATCHes the platform `Assistant` record so
7
+ * the name (and, in future, other fields) stays in sync.
8
+ *
9
+ * Requests are serialized so that rapid name changes (A → B) never race:
10
+ * only the most recently requested name is sent, and a stale in-flight
11
+ * response cannot overwrite a newer value.
12
+ *
13
+ * The sync is best-effort and fire-and-forget — network failures are
14
+ * logged but never surface to callers.
15
+ */
16
+
17
+ import { getLogger } from "../util/logger.js";
18
+ import { VellumPlatformClient } from "./client.js";
19
+
20
+ const log = getLogger("sync-identity");
21
+
22
+ /** Track the last successfully synced name (used inside doSync to skip redundant PATCHes). */
23
+ let lastSyncedName: string | null = null;
24
+
25
+ /** Track the last requested name (used for dedup at enqueue time). */
26
+ let lastRequestedName: string | null = null;
27
+
28
+ /**
29
+ * Monotonically increasing sequence number. Each call to
30
+ * `syncIdentityNameToPlatform` bumps this; after a PATCH completes we
31
+ * only update `lastSyncedName` when `seq` still matches, guaranteeing
32
+ * the newest name always wins.
33
+ */
34
+ let seq = 0;
35
+
36
+ /** Chain promise that serializes in-flight PATCH requests. */
37
+ let pending: Promise<void> = Promise.resolve();
38
+
39
+ /**
40
+ * Push the current assistant name to the platform `Assistant` record.
41
+ *
42
+ * No-op when:
43
+ * - The platform client cannot be created (not platform-hosted / missing creds).
44
+ * - No assistant ID is configured.
45
+ * - The name is empty or unchanged since the last request.
46
+ */
47
+ export function syncIdentityNameToPlatform(name: string): void {
48
+ if (!name || name === lastRequestedName) return;
49
+
50
+ lastRequestedName = name;
51
+
52
+ const mySeq = ++seq;
53
+
54
+ pending = pending
55
+ .then(() => doSync(name, mySeq))
56
+ .catch(() => {
57
+ // swallowed — doSync already logs internally
58
+ });
59
+ }
60
+
61
+ async function doSync(name: string, requestSeq: number): Promise<void> {
62
+ try {
63
+ // A newer call has already been enqueued — skip this stale request.
64
+ if (requestSeq !== seq) return;
65
+
66
+ // Re-check after awaiting the previous request in the chain.
67
+ if (name === lastSyncedName) return;
68
+
69
+ const client = await VellumPlatformClient.create();
70
+ if (!client) {
71
+ clearRequestedIfLatest(requestSeq);
72
+ return;
73
+ }
74
+
75
+ const assistantId = client.platformAssistantId;
76
+ if (!assistantId) {
77
+ clearRequestedIfLatest(requestSeq);
78
+ return;
79
+ }
80
+
81
+ const resp = await client.fetch(
82
+ `/v1/assistants/${encodeURIComponent(assistantId)}/`,
83
+ {
84
+ method: "PATCH",
85
+ headers: { "Content-Type": "application/json" },
86
+ body: JSON.stringify({ name }),
87
+ signal: AbortSignal.timeout(15_000),
88
+ },
89
+ );
90
+
91
+ if (resp.ok) {
92
+ // Only update cache if no newer request has been enqueued since we
93
+ // started this PATCH — prevents a slow response from overwriting a
94
+ // fresher value.
95
+ if (requestSeq === seq) {
96
+ lastSyncedName = name;
97
+ }
98
+ log.info({ name, assistantId }, "Synced assistant name to platform");
99
+ } else {
100
+ clearRequestedIfLatest(requestSeq);
101
+ const text = await resp.text();
102
+ log.warn(
103
+ { status: resp.status, body: text, assistantId },
104
+ "Failed to sync assistant name to platform",
105
+ );
106
+ }
107
+ } catch (err) {
108
+ clearRequestedIfLatest(requestSeq);
109
+ log.warn({ err }, "Error syncing assistant name to platform");
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Reset `lastRequestedName` when the latest request failed, so that the
115
+ * next call with the same name is allowed through instead of being deduped.
116
+ */
117
+ function clearRequestedIfLatest(requestSeq: number): void {
118
+ if (requestSeq === seq) {
119
+ lastRequestedName = lastSyncedName;
120
+ }
121
+ }
122
+
123
+ /** Reset cached state (for testing). */
124
+ export function _resetSyncState(): void {
125
+ lastSyncedName = null;
126
+ lastRequestedName = null;
127
+ seq = 0;
128
+ pending = Promise.resolve();
129
+ }
@@ -1,5 +1,10 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { basename, join } from "node:path";
1
+ import {
2
+ existsSync,
3
+ mkdirSync,
4
+ readFileSync,
5
+ writeFileSync,
6
+ } from "node:fs";
7
+ import { basename, dirname, join } from "node:path";
3
8
 
4
9
  import {
5
10
  findContactByChannelExternalId,
@@ -16,6 +21,28 @@ import { stripCommentLines } from "../util/strip-comment-lines.js";
16
21
 
17
22
  const log = getLogger("persona-resolver");
18
23
 
24
+ // ── Guardian persona template ─────────────────────────────────────
25
+ //
26
+ // Scaffold written to `users/<slug>.md` when a guardian is resolved
27
+ // but no per-user persona file yet exists. Kept in sync with the
28
+ // legacy workspace USER.md template so that upgrading users preserve
29
+ // the same editable shape. Exported so consumers can detect the
30
+ // unmodified scaffold (e.g. heartbeat's `isShallowProfile`).
31
+ export const GUARDIAN_PERSONA_TEMPLATE = `_ Lines starting with _ are comments - they won't appear in the system prompt
32
+
33
+ # User Profile
34
+
35
+ Store details about your user here. Edit freely - build this over time as you learn about them. Don't be pushy about seeking details, but when you learn something, write it down. More context makes you more useful.
36
+
37
+ - Preferred name/reference:
38
+ - Pronouns:
39
+ - Locale:
40
+ - Work role:
41
+ - Goals:
42
+ - Hobbies/fun:
43
+ - Daily tools:
44
+ `;
45
+
19
46
  // ── Types ──────────────────────────────────────────────────────────
20
47
 
21
48
  export interface PersonaContext {
@@ -98,6 +125,22 @@ function resolveUserFilename(
98
125
  return null;
99
126
  }
100
127
 
128
+ /**
129
+ * Resolve the absolute on-disk path to the guardian's per-user persona
130
+ * file (e.g. `<workspace>/users/sidd.md`). Returns `null` when no
131
+ * guardian is resolvable (no guardian contact, or its `userFile` is
132
+ * unusable / fails basename validation).
133
+ *
134
+ * This does not check whether the file exists — it only resolves the
135
+ * path. Callers use it alongside `ensureGuardianPersonaFile` to open
136
+ * or scaffold the file.
137
+ */
138
+ export function resolveGuardianPersonaPath(): string | null {
139
+ const filename = resolveUserFilename(undefined);
140
+ if (!filename) return null;
141
+ return join(getWorkspaceDir(), "users", filename);
142
+ }
143
+
101
144
  /**
102
145
  * Resolve a short slug identifying the current user, derived from
103
146
  * their contact's userFile. Used to scope per-user workspace directories
@@ -192,3 +235,84 @@ export function resolvePersonaContext(
192
235
  export function resolveGuardianPersona(): string | null {
193
236
  return resolveUserPersona(undefined);
194
237
  }
238
+
239
+ /**
240
+ * Resolve the guardian's user persona strictly from their own
241
+ * `users/<slug>.md` file, with NO fallback to `users/default.md`.
242
+ *
243
+ * Returns `null` when no guardian contact is resolvable, the
244
+ * guardian's userFile is unset, or the file is missing / empty.
245
+ *
246
+ * Used by callers that derive guardian-specific attributes (name,
247
+ * pronouns) where `default.md` content would incorrectly override an
248
+ * intentional caller-supplied fallback such as `Contact.displayName`.
249
+ * System-prompt callers that want the default.md fallback should
250
+ * continue to use `resolveGuardianPersona`.
251
+ */
252
+ export function resolveGuardianPersonaStrict(): string | null {
253
+ const filename = resolveUserFilename(undefined);
254
+ if (!filename) return null;
255
+ const filePath = join(getWorkspaceDir(), "users", filename);
256
+ if (!existsSync(filePath)) return null;
257
+ return readPersonaFile(filePath);
258
+ }
259
+
260
+ /**
261
+ * Write the guardian persona template scaffold to `users/<userFile>`
262
+ * when the file does not yet exist. No-op when the file already
263
+ * exists (safe against clobbering user edits).
264
+ *
265
+ * @param userFile - A filename (not a bare slug), matching the shape
266
+ * of `Contact.userFile` — a basename with a `.md` suffix
267
+ * (e.g. `"sidd.md"`). The path traversal guard rejects values that
268
+ * are not a clean basename.
269
+ *
270
+ * Creates the parent `users/` directory if missing.
271
+ */
272
+ export function ensureGuardianPersonaFile(userFile: string): void {
273
+ if (basename(userFile) !== userFile || userFile === ".." || userFile === ".") {
274
+ log.warn(
275
+ { userFile },
276
+ "Guardian persona userFile contains path traversal; refusing to write",
277
+ );
278
+ return;
279
+ }
280
+
281
+ const filePath = join(getWorkspaceDir(), "users", userFile);
282
+ if (existsSync(filePath)) return;
283
+
284
+ mkdirSync(dirname(filePath), { recursive: true });
285
+ writeFileSync(filePath, GUARDIAN_PERSONA_TEMPLATE, "utf-8");
286
+ log.debug({ path: filePath }, "Wrote guardian persona scaffold");
287
+ }
288
+
289
+ /**
290
+ * Return `true` when the persona file at `filePath` has been edited by
291
+ * the user (its stripped content differs from the bare scaffold
292
+ * template). Returns `false` when the file is missing, unreadable,
293
+ * empty after stripping, or byte-identical to the template after
294
+ * stripping comment lines.
295
+ *
296
+ * Used by the vbundle importer to decide whether a legacy
297
+ * `prompts/USER.md` entry may safely overwrite `users/<slug>.md`.
298
+ */
299
+ export function isGuardianPersonaCustomized(filePath: string): boolean {
300
+ if (!existsSync(filePath)) return false;
301
+
302
+ let content: string;
303
+ try {
304
+ content = readFileSync(filePath, "utf-8");
305
+ } catch (err) {
306
+ log.warn(
307
+ { err, path: filePath },
308
+ "Failed to read persona file while checking customization",
309
+ );
310
+ return false;
311
+ }
312
+
313
+ const stripped = stripCommentLines(content);
314
+ if (stripped.length === 0) return false;
315
+
316
+ const templateStripped = stripCommentLines(GUARDIAN_PERSONA_TEMPLATE);
317
+ return stripped !== templateStripped;
318
+ }
@@ -11,6 +11,7 @@ import { join } from "node:path";
11
11
  import { getIsContainerized } from "../config/env-registry.js";
12
12
  import { loadConfig } from "../config/loader.js";
13
13
  import { listConnections } from "../oauth/oauth-store.js";
14
+ import type { OnboardingContext } from "../types/onboarding-context.js";
14
15
  import { resolveBundledDir } from "../util/bundled-asset.js";
15
16
  import { getLogger } from "../util/logger.js";
16
17
  import {
@@ -25,7 +26,27 @@ export { SYSTEM_PROMPT_CACHE_BOUNDARY };
25
26
 
26
27
  const log = getLogger("system-prompt");
27
28
 
28
- const PROMPT_FILES = ["SOUL.md", "IDENTITY.md", "USER.md"] as const;
29
+ const PROMPT_FILES = ["SOUL.md", "IDENTITY.md"] as const;
30
+
31
+ function hasPopulatedUsersDir(): boolean {
32
+ try {
33
+ const usersDir = join(getWorkspaceDir(), "users");
34
+ if (!existsSync(usersDir)) return false;
35
+ return readdirSync(usersDir).length > 0;
36
+ } catch {
37
+ return false;
38
+ }
39
+ }
40
+
41
+ function hasExistingConversations(): boolean {
42
+ try {
43
+ const convDir = getConversationsDir();
44
+ if (!existsSync(convDir)) return false;
45
+ return readdirSync(convDir).length > 0;
46
+ } catch {
47
+ return false;
48
+ }
49
+ }
29
50
 
30
51
  /**
31
52
  * Copy template prompt files into the data directory if they don't already exist.
@@ -43,10 +64,16 @@ export function ensurePromptFiles(): void {
43
64
  "templates",
44
65
  );
45
66
 
46
- // Track whether this is a fresh workspace (no core prompt files exist yet).
47
- const isFirstRun = PROMPT_FILES.every(
48
- (file) => !existsSync(getWorkspacePromptPath(file)),
49
- );
67
+ // Track whether this is a fresh workspace. A workspace counts as fresh
68
+ // only when none of these signals are present: core prompt files, a
69
+ // populated `users/` directory, or existing conversations. Upgraded
70
+ // workspaces that dropped USER.md but still carry personas or history
71
+ // would otherwise be mistaken for fresh installs and re-trigger
72
+ // onboarding.
73
+ const isFirstRun =
74
+ PROMPT_FILES.every((file) => !existsSync(getWorkspacePromptPath(file))) &&
75
+ !hasPopulatedUsersDir() &&
76
+ !hasExistingConversations();
50
77
 
51
78
  for (const file of PROMPT_FILES) {
52
79
  const dest = getWorkspacePromptPath(file);
@@ -190,7 +217,7 @@ export function ensurePromptFiles(): void {
190
217
  *
191
218
  * Composition:
192
219
  * 1. Base prompt: IDENTITY.md + SOUL.md (guaranteed to exist after ensurePromptFiles)
193
- * 2. Append USER.md (user profile)
220
+ * 2. Append the resolved user persona from users/<slug>.md (via options.userPersona)
194
221
  * 3. If BOOTSTRAP.md exists, append first-run ritual instructions
195
222
  */
196
223
  export interface BuildSystemPromptOptions {
@@ -199,6 +226,7 @@ export interface BuildSystemPromptOptions {
199
226
  userPersona?: string | null;
200
227
  channelPersona?: string | null;
201
228
  userSlug?: string | null;
229
+ onboardingContext?: OnboardingContext;
202
230
  }
203
231
 
204
232
  /**
@@ -215,7 +243,7 @@ export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
215
243
  // ── Static instruction sections (stable across turns) ──
216
244
  // These sections are deterministic within a process lifetime. They form
217
245
  // the first cache block so they remain cached even when workspace files
218
- // (IDENTITY.md, SOUL.md, USER.md, etc.) are edited between turns.
246
+ // (IDENTITY.md, SOUL.md, users/<slug>.md, etc.) are edited between turns.
219
247
  const staticParts: string[] = [];
220
248
  const customPrefix = readCustomSystemPromptPrefix();
221
249
  if (customPrefix) staticParts.push(customPrefix);
@@ -242,13 +270,11 @@ export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
242
270
 
243
271
  const soulPath = getWorkspacePromptPath("SOUL.md");
244
272
  const identityPath = getWorkspacePromptPath("IDENTITY.md");
245
- const userPath = getWorkspacePromptPath("USER.md");
246
273
  const bootstrapPath = getWorkspacePromptPath("BOOTSTRAP.md");
247
274
  const updatesPath = getWorkspacePromptPath("UPDATES.md");
248
275
 
249
276
  const soul = readPromptFile(soulPath);
250
277
  const identity = readPromptFile(identityPath);
251
- const user = readPromptFile(userPath);
252
278
  const bootstrap = readPromptFile(bootstrapPath);
253
279
  const updates = readPromptFile(updatesPath);
254
280
 
@@ -262,7 +288,6 @@ export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
262
288
  // source and skip them — SOUL.md provides sufficient personality defaults
263
289
  // until onboarding completes.
264
290
  const identityIsTemplate = isTemplateContent(identity, "IDENTITY.md");
265
- const userIsTemplate = isTemplateContent(user, "USER.md");
266
291
 
267
292
  if (identity && !identityIsTemplate) {
268
293
  // Strip placeholder lines (e.g. "- **Name:** _(not yet chosen)_") so
@@ -278,13 +303,28 @@ export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
278
303
  if (soul) dynamicParts.push(soul);
279
304
  if (options?.userPersona) dynamicParts.push(options.userPersona);
280
305
  if (options?.channelPersona) dynamicParts.push(options.channelPersona);
281
- if (user && !userIsTemplate && !options?.userPersona) dynamicParts.push(user);
282
306
  if (includeBootstrap) {
307
+ const userSlug = options?.userSlug ?? "default";
308
+ const bootstrapWithSlug = bootstrap.replaceAll(
309
+ "{{USER_PERSONA_FILE}}",
310
+ `${userSlug}.md`,
311
+ );
283
312
  dynamicParts.push(
284
313
  "# First-Run Ritual\n\n" +
285
314
  "BOOTSTRAP.md is present — this is your first conversation. Follow its instructions.\n\n" +
286
- bootstrap,
315
+ bootstrapWithSlug,
287
316
  );
317
+
318
+ if (options?.onboardingContext) {
319
+ dynamicParts.push(
320
+ "## Pre-chat Onboarding Context\n\n" +
321
+ "The user completed the native pre-chat onboarding. Here is their context:\n\n" +
322
+ "```json\n" +
323
+ JSON.stringify(options.onboardingContext, null, 2) +
324
+ "\n```\n\n" +
325
+ "Use this to personalize your opener and skip redundant discovery.",
326
+ );
327
+ }
288
328
  }
289
329
  if (updates) {
290
330
  dynamicParts.push(
@@ -423,9 +463,11 @@ function buildContainerizedSection(): string {
423
463
  function buildParallelToolCallsSection(): string {
424
464
  return [
425
465
  "<use_parallel_tool_calls>",
426
- "For maximum efficiency, whenever you perform multiple independent operations, invoke all relevant tools simultaneously rather than sequentially. Prioritize calling tools in parallel whenever possible. For example, when reading 3 files, run 3 tool calls in parallel to read all 3 files into context at the same time. When running multiple read-only commands like `ls` or `list_dir`, always run all of the commands in parallel. Err on the side of maximizing parallel tool calls rather than running too many tools sequentially.",
466
+ "Batch independent tool calls into the same response. An extra LLM round trip costs orders of magnitude more than a few wasted tool calls err on the side of parallelizing when calls are independent. Reading multiple files, `glob`/`grep`, `ls`, `git status`/`diff`/`log`, type-checks, and tests should be batched.",
467
+ "",
468
+ "Before emitting a single tool call, ask whether your next turn would be another tool call that doesn't consume this one's output — if so, they belong together. Serialized tool calls without a real data dependency are a bug.",
427
469
  "",
428
- "For non-trivial independent workstreams — research, coding tasks, multi-step investigations — aggressively delegate to subagents (load the `subagent` skill for tools and instructions). Spawn subagents early and often; the cost of an unnecessary subagent is far lower than the cost of serializing work you could have parallelized.",
470
+ "For non-trivial independent workstreams — research, coding, multi-step investigations — delegate to subagents (load the `subagent` skill) and spawn them early and in parallel; an unnecessary subagent is cheaper than serialized work.",
429
471
  "</use_parallel_tool_calls>",
430
472
  ].join("\n");
431
473
  }
@@ -490,7 +532,7 @@ function readPromptFile(path: string): string | null {
490
532
  }
491
533
 
492
534
  /**
493
- * Reads the core identity/personality prompt files (SOUL.md, IDENTITY.md, USER.md)
535
+ * Reads the core identity/personality prompt files (SOUL.md, IDENTITY.md)
494
536
  * and concatenates whichever exist. Returns null if none are present.
495
537
  *
496
538
  * This is useful for injecting identity context into subsystems (e.g. memory
@@ -504,10 +546,9 @@ export function buildCoreIdentityContext(opts?: {
504
546
  const content = readPromptFile(getWorkspacePromptPath(file));
505
547
  if (!content) continue;
506
548
  // SOUL.md is always included — it provides personality defaults even
507
- // before onboarding completes. Only skip IDENTITY.md and USER.md when
508
- // they are still unmodified templates (matching buildSystemPrompt).
549
+ // before onboarding completes. Only skip IDENTITY.md when it is still
550
+ // an unmodified template (matching buildSystemPrompt).
509
551
  if (file !== "SOUL.md" && isTemplateContent(content, file)) continue;
510
- if (file === "USER.md" && opts?.userPersona) continue;
511
552
  parts.push(content);
512
553
  }
513
554
  if (opts?.userPersona) parts.push(opts.userPersona);
@@ -16,7 +16,7 @@ This is your first conversation. This document gives you goals and constraints
16
16
 
17
17
  5. **Offer the next level** — once you know something, offer a capability it enables. Not as a reward — as a natural relationship step.
18
18
 
19
- 6. **Write everything immediately** — every fact learned gets saved to USER.md the same turn. Style observations go to SOUL.md. No batching.
19
+ 6. **Write everything immediately** — every fact learned gets saved to users/{{USER_PERSONA_FILE}} the same turn. Style observations go to SOUL.md. No batching.
20
20
 
21
21
  7. **Clean up** — delete BOOTSTRAP.md and BOOTSTRAP-REFERENCE.md at the end of this conversation, regardless of how far you got. One-shot.
22
22
 
@@ -37,15 +37,15 @@ This is your first conversation. This document gives you goals and constraints
37
37
 
38
38
  ## Technical Contract (what must be prescribed)
39
39
 
40
- **Files to create/update:** IDENTITY.md, SOUL.md, USER.md
40
+ **Files to create/update:** IDENTITY.md, SOUL.md, users/{{USER_PERSONA_FILE}}
41
41
 
42
42
  **File format:** preserve existing field structure:
43
43
  - IDENTITY.md: Name, Emoji, Nature, Personality, Role
44
- - USER.md: Preferred name, Pronouns, Locale, Work role, Goals, Hobbies/fun, Daily tools
44
+ - users/{{USER_PERSONA_FILE}}: Preferred name, Pronouns, Locale, Work role, Goals, Hobbies/fun, Daily tools
45
45
 
46
46
  Use `file_edit` immediately, silently, never mention file names or tool names to the user.
47
47
 
48
- The contents of IDENTITY.md, SOUL.md, and USER.md are already in your system prompt — use the exact text you see there for `old_string` in `file_edit`.
48
+ The contents of IDENTITY.md, SOUL.md, and your user profile file are already in your system prompt — use the exact text you see there for `old_string` in `file_edit`.
49
49
 
50
50
  After tool calls, do not repeat yourself — your text before tool calls is already visible to the user.
51
51
 
@@ -90,7 +90,7 @@ If an `onboarding` JSON context is present in this conversation, the user alread
90
90
  - `tools` array -> know which integration offers to surface first, infer work profile
91
91
  - `tasks` array -> know what "prove value fast" means for this person
92
92
  - `tone` string -> calibrate warmth/formality
93
- - `userName` / `assistantName` -> write to IDENTITY.md and USER.md immediately, skip name exchange
93
+ - `userName` / `assistantName` -> write to IDENTITY.md and users/{{USER_PERSONA_FILE}} immediately, skip name exchange
94
94
 
95
95
  If no onboarding context is present, infer everything fresh from conversation.
96
96
 
@@ -73,7 +73,9 @@ You have a personal knowledge base (`pkb/`) in your workspace. It holds facts, p
73
73
  - **threads.md** - Active commitments, follow-ups, and projects. Always in your context.
74
74
  - **buffer.md** - Inbox of recently learned facts, waiting to be filed.
75
75
 
76
- **When you learn something:** Call `remember` IMMEDIATELY. Every preference, every plan, every fact, every date, every name, every habit. The bar is not "is this important?" it's "would I be embarrassed if I forgot this?" Call it multiple times per conversation. Remembering too much costs nothing (it's one line appended to a file). Forgetting something that mattered makes you look like you weren't paying attention. Don't categorize, don't batch, don't wait. Just capture it and stay in the conversation. Filing happens later.
76
+ **When you learn something:** Call `remember` IMMEDIATELY. Capture anything concrete about their life — preferences, names, times, plans, states, habits, opinions, health details, routines, commitments. Don't judge importance; filing decides that later. Default to remembering; only skip obvious noise (small talk, hypotheticals, things they're just musing about). Call it multiple times per conversation. Remembering too much costs nothing (one line appended to a file). Forgetting something that mattered makes you look like you weren't paying attention. Don't categorize, don't batch, don't wait. Just capture it and stay in the conversation. Filing happens later.
77
+
78
+ **Corrections are the highest priority.** When the user corrects a fact you had wrong — "actually it's Thursday not Friday," "no, she lives in Austin now," "I stopped taking that medication last month" — `remember` the correction *immediately*. The wrong version is already propagated across prior turns and baked into your memory graph; future-you will keep operating on the old value until you persist the correction. A correction is not a "small fix," it's a structural edit to what you believe. Never skip a correction even if you'd skip the equivalent fresh fact.
77
79
 
78
80
  **Topic files** live in subdirectories of `pkb/` (health, preferences, people, schedule, work, etc.). You created these and you manage them. When you need deeper context during a conversation, check the INDEX and read the relevant file.
79
81
 
@@ -10,6 +10,12 @@ _ Format is freeform markdown. Write notes that help the assistant
10
10
  _ understand what changed and how it affects behavior, capabilities,
11
11
  _ or available tools. Focus on what matters to the user experience.
12
12
 
13
+ <!-- vellum-update-release:gemini-live-stt -->
14
+ ## Google Gemini speech-to-text now uses the Live API
15
+
16
+ If your user is configured with `services.stt.provider: "google-gemini"`, transcription now streams over the Gemini Live WebSocket API and emits true partial transcripts in real time, instead of the previous polling approximation that re-uploaded the full audio buffer every second. Same Gemini API key, same setup — only the transport changed. Latency for partials should drop noticeably.
17
+ <!-- /vellum-update-release:gemini-live-stt -->
18
+
13
19
  <!-- vellum-update-release:rm-dangerous-skip-perms -->
14
20
  ## `dangerouslySkipPermissions` removed
15
21
 
@@ -36,3 +42,9 @@ Some Slack image attachments were stored incorrectly due to a missing OAuth scop
36
42
  This has been fixed automatically: the corrupted attachments were removed from affected conversations during this update, and the OAuth scope issue has been resolved so new image uploads work correctly. If your user mentions missing images from earlier conversations, this is why — the images were never successfully received in the first place.
37
43
  <!-- /vellum-update-release:corrupted-attachment-cleanup -->
38
44
 
45
+ <!-- vellum-update-release:llm-log-retention-setting -->
46
+ ## LLM request log retention is now configurable
47
+
48
+ Your user can now choose how long LLM request logs are kept on their device from Settings → Permissions & Privacy on the macOS app. The default stays at 1 day, but they can pick 7, 30, or 90 days, or "Never" to retain logs indefinitely. If your user asks about managing their privacy or controlling how much LLM request history is retained locally, point them at this new picker.
49
+ <!-- /vellum-update-release:llm-log-retention-setting -->
50
+
@@ -0,0 +1,20 @@
1
+ _ Lines starting with _ are comments - they won't appear in the system prompt
2
+ _ This file shapes how you behave when responding in Slack. Edit it freely.
3
+
4
+ # Slack
5
+
6
+ ## Delivery
7
+
8
+ Skip the research narration. In Slack, every message you send before your final answer posts as a separate visible message - not a live stream. "Let me look that up" and "Researching now..." just add noise. Use your tools, then deliver the result.
9
+
10
+ Your personality, warmth, humor, and opinions are welcome. Just don't narrate the process of finding the answer.
11
+
12
+ ## Formatting
13
+
14
+ Never use markdown tables (pipe-delimited). Slack cannot render them. Use bullet points with bold labels instead.
15
+
16
+ When presenting information from web search, cite sources as inline hyperlinks woven into your text (e.g., "the round closed at $122B, [per CNBC](url)"). Don't dump a references list at the end.
17
+
18
+ ## Long-form content
19
+
20
+ When your response would be long, dense, or highly structured (reports, teardowns, detailed analyses), use your judgment on whether to write it as an attached file vs posting inline. Consider what reads better in Slack - a wall of text with "See more" truncation often doesn't.
@@ -42,18 +42,35 @@ export function appendReleaseBlock(
42
42
  }
43
43
 
44
44
  /**
45
- * Extracts content-level markers (non-version feature markers) from the
46
- * template body. These are markers like `schedule-reminder-unification`
47
- * that identify the _content_ rather than the release version.
45
+ * Filters template content to only include content blocks whose opening
46
+ * markers are not already present in the existing workspace content.
47
+ *
48
+ * Each content block is delimited by opening/closing marker pairs:
49
+ * <!-- vellum-update-release:id --> ... <!-- /vellum-update-release:id -->
50
+ *
51
+ * If the template has no block structure (no matched open/close pairs),
52
+ * returns the original body unchanged for backward compatibility.
53
+ * Returns empty string when all blocks are already present.
48
54
  */
49
- export function extractContentMarkers(body: string): string[] {
50
- const ids: string[] = [];
51
- const regex = /<!-- vellum-update-release:(.+?) -->/g;
55
+ export function filterNewContentBlocks(
56
+ body: string,
57
+ existing: string,
58
+ ): string {
59
+ const blockRegex =
60
+ /(<!-- vellum-update-release:(.+?) -->[\s\S]*?<!-- \/vellum-update-release:\2 -->)/g;
61
+ const blocks: Array<{ full: string; id: string }> = [];
52
62
  let match: RegExpExecArray | null;
53
- while ((match = regex.exec(body)) !== null) {
54
- ids.push(match[1]);
63
+ while ((match = blockRegex.exec(body)) !== null) {
64
+ blocks.push({ full: match[1], id: match[2] });
55
65
  }
56
- return ids;
66
+
67
+ if (blocks.length === 0) return body;
68
+
69
+ const newBlocks = blocks.filter((b) => !hasReleaseBlock(existing, b.id));
70
+
71
+ if (newBlocks.length === 0) return "";
72
+
73
+ return newBlocks.map((b) => b.full).join("\n\n");
57
74
  }
58
75
 
59
76
  /** Extracts all version strings from release markers found in `content`. */