@vellumai/assistant 0.5.13 → 0.5.15

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 (425) hide show
  1. package/.env.example +1 -6
  2. package/AGENTS.md +4 -0
  3. package/ARCHITECTURE.md +0 -1
  4. package/bunfig.toml +1 -0
  5. package/docs/architecture/memory.md +3 -3
  6. package/openapi.yaml +127 -22
  7. package/package.json +1 -1
  8. package/src/__tests__/access-request-decision.test.ts +2 -32
  9. package/src/__tests__/actor-token-service.test.ts +1 -31
  10. package/src/__tests__/anthropic-provider.test.ts +53 -40
  11. package/src/__tests__/app-git-history.test.ts +9 -17
  12. package/src/__tests__/app-git-service.test.ts +14 -20
  13. package/src/__tests__/app-store-dir-names.test.ts +10 -20
  14. package/src/__tests__/approval-cascade.test.ts +2 -19
  15. package/src/__tests__/approval-primitive.test.ts +2 -27
  16. package/src/__tests__/approval-routes-http.test.ts +2 -30
  17. package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -28
  18. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -45
  19. package/src/__tests__/attachments-store.test.ts +5 -32
  20. package/src/__tests__/audit-log-rotation.test.ts +5 -36
  21. package/src/__tests__/avatar-e2e.test.ts +1 -9
  22. package/src/__tests__/avatar-generator.test.ts +1 -7
  23. package/src/__tests__/browser-fill-credential.test.ts +0 -4
  24. package/src/__tests__/browser-manager.test.ts +0 -6
  25. package/src/__tests__/call-controller.test.ts +1 -22
  26. package/src/__tests__/call-conversation-messages.test.ts +0 -21
  27. package/src/__tests__/call-domain.test.ts +0 -25
  28. package/src/__tests__/call-pointer-messages.test.ts +0 -21
  29. package/src/__tests__/call-recovery.test.ts +0 -22
  30. package/src/__tests__/call-routes-http.test.ts +0 -24
  31. package/src/__tests__/call-store.test.ts +0 -21
  32. package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -24
  33. package/src/__tests__/canonical-guardian-store.test.ts +48 -21
  34. package/src/__tests__/channel-approval-routes.test.ts +6 -26
  35. package/src/__tests__/channel-approvals.test.ts +1 -38
  36. package/src/__tests__/channel-delivery-store.test.ts +0 -21
  37. package/src/__tests__/channel-guardian.test.ts +0 -26
  38. package/src/__tests__/channel-reply-delivery.test.ts +5 -0
  39. package/src/__tests__/channel-retry-sweep.test.ts +0 -21
  40. package/src/__tests__/checker.test.ts +26 -61
  41. package/src/__tests__/clawhub.test.ts +9 -25
  42. package/src/__tests__/cli-command-risk-guard.test.ts +0 -18
  43. package/src/__tests__/config-loader-backfill.test.ts +9 -28
  44. package/src/__tests__/config-schema-cmd.test.ts +5 -25
  45. package/src/__tests__/config-schema.test.ts +21 -40
  46. package/src/__tests__/config-watcher.test.ts +4 -91
  47. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -21
  48. package/src/__tests__/contacts-tools.test.ts +0 -21
  49. package/src/__tests__/context-memory-e2e.test.ts +0 -21
  50. package/src/__tests__/context-window-manager.test.ts +130 -3
  51. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -4
  52. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -4
  53. package/src/__tests__/conversation-agent-loop.test.ts +0 -4
  54. package/src/__tests__/conversation-attachments.test.ts +1 -24
  55. package/src/__tests__/conversation-attention-store.test.ts +0 -21
  56. package/src/__tests__/conversation-attention-telegram.test.ts +0 -22
  57. package/src/__tests__/conversation-clear-safety.test.ts +0 -22
  58. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -21
  59. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -24
  60. package/src/__tests__/conversation-disk-view-integration.test.ts +1 -23
  61. package/src/__tests__/conversation-disk-view.test.ts +5 -27
  62. package/src/__tests__/conversation-error.test.ts +1 -1
  63. package/src/__tests__/conversation-fork-crud.test.ts +1 -33
  64. package/src/__tests__/conversation-fork-route.test.ts +0 -27
  65. package/src/__tests__/conversation-history-web-search.test.ts +23 -16
  66. package/src/__tests__/conversation-init.benchmark.test.ts +22 -43
  67. package/src/__tests__/conversation-key-store-disk-view.test.ts +8 -34
  68. package/src/__tests__/conversation-load-history-repair.test.ts +0 -4
  69. package/src/__tests__/conversation-pre-run-repair.test.ts +0 -4
  70. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -4
  71. package/src/__tests__/conversation-queue.test.ts +8 -8
  72. package/src/__tests__/conversation-routes-disk-view.test.ts +13 -51
  73. package/src/__tests__/conversation-runtime-assembly.test.ts +64 -38
  74. package/src/__tests__/conversation-slash-commands.test.ts +5 -0
  75. package/src/__tests__/conversation-slash-queue.test.ts +0 -4
  76. package/src/__tests__/conversation-slash-unknown.test.ts +0 -4
  77. package/src/__tests__/conversation-speed-override.test.ts +326 -0
  78. package/src/__tests__/conversation-starter-routes.test.ts +0 -23
  79. package/src/__tests__/conversation-store.test.ts +0 -21
  80. package/src/__tests__/conversation-unread-route.test.ts +0 -24
  81. package/src/__tests__/conversation-usage.test.ts +56 -21
  82. package/src/__tests__/conversation-wipe.test.ts +0 -21
  83. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
  84. package/src/__tests__/conversation-workspace-injection.test.ts +0 -4
  85. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -4
  86. package/src/__tests__/credential-execution-shell-lockdown.test.ts +8 -5
  87. package/src/__tests__/credential-vault-unit.test.ts +9 -428
  88. package/src/__tests__/credentials-cli.test.ts +10 -10
  89. package/src/__tests__/daemon-assistant-events.test.ts +0 -19
  90. package/src/__tests__/date-context.test.ts +77 -97
  91. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +7 -24
  92. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +29 -42
  93. package/src/__tests__/delete-managed-skill-tool.test.ts +2 -10
  94. package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -26
  95. package/src/__tests__/docker-signing-key-bootstrap.test.ts +61 -15
  96. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -36
  97. package/src/__tests__/email-cli.test.ts +6 -6
  98. package/src/__tests__/ephemeral-permissions.test.ts +5 -17
  99. package/src/__tests__/first-greeting.test.ts +4 -32
  100. package/src/__tests__/followup-tools.test.ts +0 -21
  101. package/src/__tests__/gateway-only-enforcement.test.ts +0 -20
  102. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -23
  103. package/src/__tests__/guardian-action-followup-executor.test.ts +0 -23
  104. package/src/__tests__/guardian-action-followup-store.test.ts +0 -21
  105. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -21
  106. package/src/__tests__/guardian-action-late-reply.test.ts +0 -21
  107. package/src/__tests__/guardian-action-store.test.ts +0 -21
  108. package/src/__tests__/guardian-action-sweep.test.ts +0 -21
  109. package/src/__tests__/guardian-binding-drift-heal.test.ts +0 -23
  110. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +172 -22
  111. package/src/__tests__/guardian-dispatch.test.ts +0 -21
  112. package/src/__tests__/guardian-grant-minting.test.ts +0 -22
  113. package/src/__tests__/guardian-outbound-http.test.ts +0 -22
  114. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -23
  115. package/src/__tests__/guardian-routing-invariants.test.ts +0 -22
  116. package/src/__tests__/guardian-routing-state.test.ts +0 -22
  117. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -24
  118. package/src/__tests__/headless-browser-interactions.test.ts +0 -4
  119. package/src/__tests__/headless-browser-navigate.test.ts +0 -4
  120. package/src/__tests__/headless-browser-read-tools.test.ts +0 -4
  121. package/src/__tests__/headless-browser-snapshot.test.ts +0 -4
  122. package/src/__tests__/heartbeat-service.test.ts +99 -26
  123. package/src/__tests__/hooks-blocking.test.ts +3 -3
  124. package/src/__tests__/hooks-config.test.ts +7 -7
  125. package/src/__tests__/hooks-discovery.test.ts +3 -3
  126. package/src/__tests__/hooks-integration.test.ts +5 -5
  127. package/src/__tests__/hooks-manager.test.ts +3 -3
  128. package/src/__tests__/hooks-runner.test.ts +5 -23
  129. package/src/__tests__/hooks-settings.test.ts +3 -3
  130. package/src/__tests__/hooks-templates.test.ts +3 -3
  131. package/src/__tests__/http-conversation-lineage.test.ts +0 -27
  132. package/src/__tests__/identity-intro-cache.test.ts +0 -4
  133. package/src/__tests__/inbound-invite-redemption.test.ts +0 -22
  134. package/src/__tests__/inline-skill-load-permissions.test.ts +5 -16
  135. package/src/__tests__/intent-routing.test.ts +2 -55
  136. package/src/__tests__/invite-redemption-service.test.ts +0 -21
  137. package/src/__tests__/invite-routes-http.test.ts +0 -21
  138. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +0 -17
  139. package/src/__tests__/journal-context.test.ts +8 -75
  140. package/src/__tests__/list-messages-attachments.test.ts +0 -22
  141. package/src/__tests__/llm-context-route-provider.test.ts +0 -21
  142. package/src/__tests__/llm-request-log-turn-query.test.ts +46 -28
  143. package/src/__tests__/llm-usage-store.test.ts +0 -21
  144. package/src/__tests__/log-export-workspace.test.ts +1 -1
  145. package/src/__tests__/managed-skill-lifecycle.test.ts +1 -1
  146. package/src/__tests__/managed-store.test.ts +1 -1
  147. package/src/__tests__/mcp-cli.test.ts +7 -10
  148. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -21
  149. package/src/__tests__/memory-jobs-worker-backoff.test.ts +0 -11
  150. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -21
  151. package/src/__tests__/memory-recall-log-store.test.ts +0 -27
  152. package/src/__tests__/memory-recall-quality.test.ts +0 -21
  153. package/src/__tests__/memory-regressions.experimental.test.ts +31 -30
  154. package/src/__tests__/memory-regressions.test.ts +282 -70
  155. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -21
  156. package/src/__tests__/memory-upsert-concurrency.test.ts +0 -21
  157. package/src/__tests__/messaging-send-tool.test.ts +201 -0
  158. package/src/__tests__/migration-cross-version-compatibility.test.ts +18 -13
  159. package/src/__tests__/migration-export-http.test.ts +7 -1
  160. package/src/__tests__/migration-import-commit-http.test.ts +16 -14
  161. package/src/__tests__/migration-import-preflight-http.test.ts +27 -44
  162. package/src/__tests__/migration-validate-http.test.ts +1 -28
  163. package/src/__tests__/native-web-search.test.ts +25 -22
  164. package/src/__tests__/non-member-access-request.test.ts +0 -22
  165. package/src/__tests__/notification-guardian-path.test.ts +0 -21
  166. package/src/__tests__/notification-schedule-dedup.test.ts +1 -25
  167. package/src/__tests__/oauth-apps-routes.test.ts +103 -2
  168. package/src/__tests__/oauth-cli.test.ts +52 -0
  169. package/src/__tests__/oauth-provider-profiles.test.ts +0 -16
  170. package/src/__tests__/oauth-provider-serializer.test.ts +232 -0
  171. package/src/__tests__/oauth-providers-routes.test.ts +257 -0
  172. package/src/__tests__/oauth-store.test.ts +0 -21
  173. package/src/__tests__/onboarding-template-contract.test.ts +2 -2
  174. package/src/__tests__/openai-provider.test.ts +261 -0
  175. package/src/__tests__/pairing-concurrent.test.ts +6 -6
  176. package/src/__tests__/pairing-routes.test.ts +7 -1
  177. package/src/__tests__/path-policy.test.ts +1 -1
  178. package/src/__tests__/platform.test.ts +64 -88
  179. package/src/__tests__/playbook-execution.test.ts +0 -21
  180. package/src/__tests__/playbook-tools.test.ts +0 -21
  181. package/src/__tests__/pricing.test.ts +100 -0
  182. package/src/__tests__/relay-server.test.ts +1 -25
  183. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -24
  184. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -24
  185. package/src/__tests__/runtime-events-sse.test.ts +0 -24
  186. package/src/__tests__/sandbox-diagnostics.test.ts +2 -1
  187. package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -1
  188. package/src/__tests__/schedule-store.test.ts +0 -21
  189. package/src/__tests__/schedule-tools.test.ts +0 -21
  190. package/src/__tests__/scheduler-recurrence.test.ts +0 -21
  191. package/src/__tests__/scoped-approval-grants.test.ts +0 -21
  192. package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -21
  193. package/src/__tests__/secret-allowlist.test.ts +1 -1
  194. package/src/__tests__/secret-ingress-channel.test.ts +0 -5
  195. package/src/__tests__/secret-ingress-cli.test.ts +0 -6
  196. package/src/__tests__/secret-ingress-http.test.ts +0 -5
  197. package/src/__tests__/secret-ingress.test.ts +0 -5
  198. package/src/__tests__/send-endpoint-busy.test.ts +0 -24
  199. package/src/__tests__/sequence-store.test.ts +0 -21
  200. package/src/__tests__/server-history-render.test.ts +0 -24
  201. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -4
  202. package/src/__tests__/skill-load-inline-command.test.ts +9 -0
  203. package/src/__tests__/skill-load-inline-includes.test.ts +9 -0
  204. package/src/__tests__/skill-load-tool.test.ts +11 -0
  205. package/src/__tests__/skills-uninstall.test.ts +10 -8
  206. package/src/__tests__/skills.test.ts +1 -1
  207. package/src/__tests__/slack-channel-config.test.ts +1 -1
  208. package/src/__tests__/slack-inbound-verification.test.ts +0 -22
  209. package/src/__tests__/starter-bundle.test.ts +4 -1
  210. package/src/__tests__/suggestion-routes.test.ts +2 -0
  211. package/src/__tests__/system-prompt.test.ts +1 -1
  212. package/src/__tests__/terminal-tools.test.ts +1 -1
  213. package/src/__tests__/test-preload.ts +31 -0
  214. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -1
  215. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  216. package/src/__tests__/tool-executor.test.ts +0 -20
  217. package/src/__tests__/tool-input-summary.test.ts +124 -0
  218. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -1
  219. package/src/__tests__/trust-store.test.ts +7 -1
  220. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -1
  221. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -1
  222. package/src/__tests__/trusted-contact-multichannel.test.ts +1 -1
  223. package/src/__tests__/trusted-contact-verification.test.ts +1 -1
  224. package/src/__tests__/turn-boundary-resolution.test.ts +1 -1
  225. package/src/__tests__/twilio-routes.test.ts +1 -1
  226. package/src/__tests__/update-bulletin.test.ts +1 -1
  227. package/src/__tests__/vbundle-pax-and-symlink.test.ts +1 -1
  228. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -0
  229. package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
  230. package/src/__tests__/voice-session-bridge.test.ts +1 -1
  231. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -4
  232. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
  233. package/src/__tests__/workspace-migration-down-functions.test.ts +15 -3
  234. package/src/__tests__/workspace-migration-seed-device-id.test.ts +40 -4
  235. package/src/agent/loop.ts +6 -9
  236. package/src/approvals/guardian-decision-primitive.ts +46 -18
  237. package/src/approvals/guardian-request-resolvers.ts +19 -2
  238. package/src/calls/active-call-lease.ts +2 -2
  239. package/src/cli/AGENTS.md +1 -1
  240. package/src/cli/commands/doctor.ts +9 -9
  241. package/src/cli/commands/memory.ts +142 -0
  242. package/src/cli/commands/oauth/__tests__/connect.test.ts +13 -11
  243. package/src/cli/commands/oauth/__tests__/ping.test.ts +1 -1
  244. package/src/cli/commands/oauth/connect.ts +13 -12
  245. package/src/cli/commands/oauth/index.ts +1 -1
  246. package/src/cli/commands/oauth/providers.ts +47 -62
  247. package/src/cli/commands/platform/__tests__/connect.test.ts +72 -46
  248. package/src/cli/commands/platform/__tests__/disconnect.test.ts +54 -1
  249. package/src/cli/commands/platform/__tests__/status.test.ts +36 -0
  250. package/src/cli/commands/platform/connect.ts +17 -7
  251. package/src/cli/commands/platform/disconnect.ts +28 -3
  252. package/src/cli/commands/platform/index.ts +3 -3
  253. package/src/cli.ts +1 -299
  254. package/src/config/assistant-feature-flags.ts +23 -15
  255. package/src/config/bundled-skills/app-builder/TOOLS.json +16 -0
  256. package/src/config/bundled-skills/app-builder/tools/app-create.ts +4 -0
  257. package/src/config/bundled-skills/app-builder/tools/app-delete.ts +5 -1
  258. package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +9 -1
  259. package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +5 -1
  260. package/src/config/bundled-skills/contacts/TOOLS.json +8 -0
  261. package/src/config/bundled-skills/contacts/tools/contact-search.ts +10 -1
  262. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +16 -2
  263. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +1 -0
  264. package/src/config/bundled-skills/messaging/SKILL.md +7 -7
  265. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +37 -0
  266. package/src/config/bundled-skills/slack/SKILL.md +18 -0
  267. package/src/config/env-registry.ts +15 -11
  268. package/src/config/env.ts +1 -11
  269. package/src/config/feature-flag-registry.json +16 -0
  270. package/src/config/schema.ts +4 -0
  271. package/src/config/schemas/heartbeat.ts +6 -1
  272. package/src/config/schemas/inference.ts +14 -3
  273. package/src/config/schemas/memory-processing.ts +16 -8
  274. package/src/config/schemas/memory-retrieval.ts +3 -3
  275. package/src/config/skills.ts +1 -1
  276. package/src/context/window-manager.ts +174 -51
  277. package/src/credential-execution/executable-discovery.ts +2 -2
  278. package/src/daemon/approved-devices-store.ts +2 -2
  279. package/src/daemon/assistant-attachments.ts +2 -0
  280. package/src/daemon/config-watcher.ts +4 -50
  281. package/src/daemon/conversation-agent-loop-handlers.ts +9 -1
  282. package/src/daemon/conversation-agent-loop.ts +12 -0
  283. package/src/daemon/conversation-error.ts +3 -5
  284. package/src/daemon/conversation-history.ts +7 -3
  285. package/src/daemon/conversation-lifecycle.ts +16 -0
  286. package/src/daemon/conversation-messaging.ts +1 -0
  287. package/src/daemon/conversation-notifiers.ts +67 -30
  288. package/src/daemon/conversation-process.ts +161 -2
  289. package/src/daemon/conversation-queue-manager.ts +2 -0
  290. package/src/daemon/conversation-runtime-assembly.ts +33 -11
  291. package/src/daemon/conversation-slash.ts +14 -3
  292. package/src/daemon/conversation-tool-setup.ts +2 -0
  293. package/src/daemon/conversation-usage.ts +32 -4
  294. package/src/daemon/conversation.ts +33 -1
  295. package/src/daemon/daemon-control.ts +32 -16
  296. package/src/daemon/date-context.ts +47 -45
  297. package/src/daemon/dictation-profile-store.ts +2 -2
  298. package/src/daemon/handlers/conversations.ts +19 -0
  299. package/src/daemon/handlers/shared.ts +14 -21
  300. package/src/daemon/lifecycle.ts +5 -7
  301. package/src/daemon/message-types/conversations.ts +2 -0
  302. package/src/daemon/message-types/guardian-actions.ts +3 -17
  303. package/src/daemon/message-types/integrations.ts +11 -1
  304. package/src/daemon/message-types/messages.ts +1 -0
  305. package/src/daemon/pairing-store.ts +2 -79
  306. package/src/daemon/server.ts +154 -8
  307. package/src/daemon/watch-handler.ts +65 -21
  308. package/src/email/guardrails.ts +3 -3
  309. package/src/heartbeat/heartbeat-service.ts +14 -7
  310. package/src/hooks/cli.ts +2 -2
  311. package/src/hooks/config.ts +2 -2
  312. package/src/hooks/discovery.ts +2 -2
  313. package/src/hooks/manager.ts +2 -2
  314. package/src/hooks/runner.ts +5 -2
  315. package/src/hooks/templates.ts +2 -2
  316. package/src/memory/admin.ts +181 -2
  317. package/src/memory/app-git-service.ts +61 -4
  318. package/src/memory/attachments-store.ts +2 -0
  319. package/src/memory/canonical-guardian-store.ts +16 -0
  320. package/src/memory/db-init.ts +8 -0
  321. package/src/memory/embedding-local.ts +5 -2
  322. package/src/memory/indexer.ts +44 -26
  323. package/src/memory/items-extractor.ts +34 -82
  324. package/src/memory/job-handlers/batch-extraction.ts +741 -0
  325. package/src/memory/job-handlers/journal-carry-forward.test.ts +383 -0
  326. package/src/memory/job-handlers/journal-carry-forward.ts +255 -0
  327. package/src/memory/jobs-store.ts +28 -0
  328. package/src/memory/jobs-worker.ts +56 -9
  329. package/src/memory/lifecycle-events-store.ts +4 -2
  330. package/src/memory/llm-request-log-store.ts +40 -2
  331. package/src/memory/llm-usage-store.ts +4 -3
  332. package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +71 -0
  333. package/src/memory/migrations/200-usage-llm-call-count.ts +20 -0
  334. package/src/memory/migrations/index.ts +2 -0
  335. package/src/memory/query-expansion.ts +83 -0
  336. package/src/memory/retriever.test.ts +119 -0
  337. package/src/memory/retriever.ts +513 -105
  338. package/src/memory/schema/guardian.ts +4 -0
  339. package/src/memory/schema/infrastructure.ts +1 -0
  340. package/src/memory/search/formatting.test.ts +140 -0
  341. package/src/memory/search/formatting.ts +143 -198
  342. package/src/memory/search/mmr.ts +136 -0
  343. package/src/memory/search/staleness.ts +0 -15
  344. package/src/memory/search/tier-classifier.ts +10 -21
  345. package/src/memory/search/types.ts +17 -0
  346. package/src/messaging/providers/slack/adapter.ts +51 -5
  347. package/src/notifications/broadcaster.ts +13 -0
  348. package/src/notifications/copy-composer.ts +8 -0
  349. package/src/oauth/connect-orchestrator.ts +1 -1
  350. package/src/oauth/connection-resolver.ts +2 -2
  351. package/src/oauth/provider-serializer.ts +116 -0
  352. package/src/permissions/trust-store.ts +24 -7
  353. package/src/prompts/__tests__/build-cli-reference-section.test.ts +5 -0
  354. package/src/prompts/journal-context.ts +50 -35
  355. package/src/prompts/persona-resolver.ts +1 -1
  356. package/src/prompts/system-prompt.ts +27 -28
  357. package/src/prompts/templates/BOOTSTRAP.md +14 -1
  358. package/src/prompts/templates/HEARTBEAT.md +10 -0
  359. package/src/prompts/templates/NOW.md +19 -25
  360. package/src/prompts/templates/SOUL.md +13 -1
  361. package/src/prompts/templates/UPDATES.md +12 -0
  362. package/src/prompts/update-bulletin.ts +1 -1
  363. package/src/providers/anthropic/client.ts +89 -18
  364. package/src/providers/model-catalog.ts +22 -2
  365. package/src/providers/model-intents.ts +2 -2
  366. package/src/providers/openai/client.ts +40 -1
  367. package/src/providers/retry.ts +23 -4
  368. package/src/providers/types.ts +2 -0
  369. package/src/runtime/assistant-scope.ts +1 -1
  370. package/src/runtime/auth/__tests__/credential-service.test.ts +1 -0
  371. package/src/runtime/auth/route-policy.ts +1 -0
  372. package/src/runtime/auth/token-service.ts +51 -29
  373. package/src/runtime/confirmation-request-guardian-bridge.ts +3 -1
  374. package/src/runtime/guardian-decision-types.ts +16 -10
  375. package/src/runtime/http-server.ts +3 -14
  376. package/src/runtime/http-types.ts +1 -0
  377. package/src/runtime/migrations/vbundle-builder.ts +7 -4
  378. package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -4
  379. package/src/runtime/migrations/vbundle-importer.ts +1 -1
  380. package/src/runtime/routes/conversation-query-routes.ts +40 -8
  381. package/src/runtime/routes/conversation-routes.ts +125 -3
  382. package/src/runtime/routes/guardian-action-routes.ts +9 -3
  383. package/src/runtime/routes/identity-routes.ts +25 -4
  384. package/src/runtime/routes/llm-context-normalization.ts +1 -0
  385. package/src/runtime/routes/log-export-routes.ts +34 -12
  386. package/src/runtime/routes/migration-routes.ts +6 -10
  387. package/src/runtime/routes/oauth-apps.ts +2 -9
  388. package/src/runtime/routes/oauth-providers.ts +60 -0
  389. package/src/runtime/routes/pairing-routes.ts +0 -8
  390. package/src/runtime/routes/settings-routes.ts +0 -1
  391. package/src/runtime/routes/telemetry-routes.ts +16 -4
  392. package/src/security/encrypted-store.ts +2 -2
  393. package/src/security/secret-allowlist.ts +3 -3
  394. package/src/signals/emit-event.ts +42 -0
  395. package/src/signals/user-message.ts +37 -0
  396. package/src/telemetry/usage-telemetry-reporter.test.ts +83 -19
  397. package/src/telemetry/usage-telemetry-reporter.ts +23 -17
  398. package/src/tools/browser/runtime-check.ts +2 -2
  399. package/src/tools/credentials/vault.ts +2 -249
  400. package/src/tools/memory/definitions.ts +1 -1
  401. package/src/tools/memory/handlers.test.ts +50 -8
  402. package/src/tools/memory/handlers.ts +3 -1
  403. package/src/tools/side-effects.ts +1 -6
  404. package/src/tools/terminal/safe-env.ts +3 -2
  405. package/src/tools/terminal/shell.ts +11 -14
  406. package/src/tools/tool-approval-handler.ts +20 -1
  407. package/src/tools/tool-input-summary.ts +66 -0
  408. package/src/tools/types.ts +4 -0
  409. package/src/usage/types.ts +4 -0
  410. package/src/util/device-id.ts +10 -10
  411. package/src/util/platform.ts +71 -33
  412. package/src/util/pricing.ts +19 -6
  413. package/src/util/strip-comment-lines.ts +28 -0
  414. package/src/workspace/git-service.ts +8 -18
  415. package/src/workspace/migrations/003-seed-device-id.ts +6 -4
  416. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +7 -1
  417. package/src/workspace/migrations/017-seed-persona-dirs.ts +2 -4
  418. package/src/workspace/migrations/021-move-signals-to-workspace.ts +84 -0
  419. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +94 -0
  420. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +86 -0
  421. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +126 -0
  422. package/src/workspace/migrations/migrate-to-workspace-volume.ts +3 -6
  423. package/src/workspace/migrations/registry.ts +8 -0
  424. package/src/signals/confirm.ts +0 -82
  425. package/src/signals/trust-rule.ts +0 -174
@@ -1,20 +1,15 @@
1
- import { mkdirSync, rmSync, writeFileSync } from "node:fs";
2
- import { tmpdir } from "node:os";
1
+ import { rmSync, writeFileSync } from "node:fs";
3
2
  import { join } from "node:path";
4
- import { beforeEach, describe, expect, mock, test } from "bun:test";
3
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
5
4
 
6
- // Mock platform to use a temp workspace dir
7
- let testWorkspaceDir: string;
8
-
9
- mock.module("../util/platform.js", () => ({
10
- getWorkspacePromptPath: (file: string) => join(testWorkspaceDir, file),
11
- }));
5
+ const testWorkspaceDir = process.env.VELLUM_WORKSPACE_DIR!;
12
6
 
13
7
  // Mock config loader
14
8
  let mockConfig = {
15
9
  heartbeat: {
16
10
  enabled: true,
17
11
  intervalMs: 60_000,
12
+ speed: "standard" as "standard" | "fast",
18
13
  activeHoursStart: undefined as number | undefined,
19
14
  activeHoursEnd: undefined as number | undefined,
20
15
  },
@@ -80,16 +75,19 @@ mock.module("../memory/conversation-title-service.js", () => ({
80
75
  const { HeartbeatService } = await import("../heartbeat/heartbeat-service.js");
81
76
 
82
77
  describe("HeartbeatService", () => {
83
- let processMessageCalls: Array<{ conversationId: string; content: string }>;
78
+ let processMessageCalls: Array<{
79
+ conversationId: string;
80
+ content: string;
81
+ options?: { speed?: string };
82
+ }>;
84
83
  let alerterCalls: Array<{ type: string; title: string; body: string }>;
85
84
 
86
- beforeEach(() => {
87
- testWorkspaceDir = join(
88
- tmpdir(),
89
- `vellum-hb-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
90
- );
91
- mkdirSync(testWorkspaceDir, { recursive: true });
85
+ afterEach(() => {
86
+ // Clean up HEARTBEAT.md between tests so file-existence tests don't leak
87
+ rmSync(join(testWorkspaceDir, "HEARTBEAT.md"), { force: true });
88
+ });
92
89
 
90
+ beforeEach(() => {
93
91
  processMessageCalls = [];
94
92
  alerterCalls = [];
95
93
  createdConversations.length = 0;
@@ -99,6 +97,7 @@ describe("HeartbeatService", () => {
99
97
  heartbeat: {
100
98
  enabled: true,
101
99
  intervalMs: 60_000,
100
+ speed: "standard",
102
101
  activeHoursStart: undefined,
103
102
  activeHoursEnd: undefined,
104
103
  },
@@ -109,14 +108,19 @@ describe("HeartbeatService", () => {
109
108
  processMessage?: (
110
109
  id: string,
111
110
  content: string,
111
+ options?: { speed?: string },
112
112
  ) => Promise<{ messageId: string }>;
113
113
  getCurrentHour?: () => number;
114
114
  }) {
115
115
  return new HeartbeatService({
116
116
  processMessage:
117
117
  overrides?.processMessage ??
118
- (async (conversationId: string, content: string) => {
119
- processMessageCalls.push({ conversationId, content });
118
+ (async (
119
+ conversationId: string,
120
+ content: string,
121
+ options?: { speed?: string },
122
+ ) => {
123
+ processMessageCalls.push({ conversationId, content, options });
120
124
  return { messageId: "msg-1" };
121
125
  }),
122
126
  alerter: (alert: { type: string; title: string; body: string }) => {
@@ -150,13 +154,60 @@ describe("HeartbeatService", () => {
150
154
  expect(processMessageCalls[0].content).toContain("Water the plants");
151
155
  });
152
156
 
157
+ test("comment lines in HEARTBEAT.md are stripped from prompt", async () => {
158
+ const checklist = [
159
+ "_ This is a comment that should be stripped",
160
+ "_ Another comment line",
161
+ "- Do the real task",
162
+ "- Check on something important",
163
+ ].join("\n");
164
+ writeFileSync(join(testWorkspaceDir, "HEARTBEAT.md"), checklist);
165
+
166
+ const service = createService();
167
+ await service.runOnce();
168
+
169
+ expect(processMessageCalls).toHaveLength(1);
170
+ expect(processMessageCalls[0].content).toContain("Do the real task");
171
+ expect(processMessageCalls[0].content).toContain(
172
+ "Check on something important",
173
+ );
174
+ expect(processMessageCalls[0].content).not.toContain(
175
+ "This is a comment that should be stripped",
176
+ );
177
+ expect(processMessageCalls[0].content).not.toContain(
178
+ "Another comment line",
179
+ );
180
+ });
181
+
182
+ test("comment lines inside fenced code blocks are preserved", async () => {
183
+ const checklist = [
184
+ "_ This comment should be stripped",
185
+ "- Check the Python snippet below still works:",
186
+ "```python",
187
+ "_instance = None",
188
+ "_private_var = 42",
189
+ "```",
190
+ ].join("\n");
191
+ writeFileSync(join(testWorkspaceDir, "HEARTBEAT.md"), checklist);
192
+
193
+ const service = createService();
194
+ await service.runOnce();
195
+
196
+ expect(processMessageCalls).toHaveLength(1);
197
+ expect(processMessageCalls[0].content).toContain("_instance = None");
198
+ expect(processMessageCalls[0].content).toContain("_private_var = 42");
199
+ expect(processMessageCalls[0].content).not.toContain(
200
+ "This comment should be stripped",
201
+ );
202
+ });
203
+
153
204
  test("default checklist used when no HEARTBEAT.md", async () => {
154
205
  const service = createService();
155
206
  await service.runOnce();
156
207
 
157
208
  expect(processMessageCalls).toHaveLength(1);
158
209
  expect(processMessageCalls[0].content).toContain(
159
- "Check the current weather",
210
+ "Check in with yourself",
160
211
  );
161
212
  });
162
213
 
@@ -343,7 +394,9 @@ describe("HeartbeatService", () => {
343
394
 
344
395
  expect(afterReset).not.toBeNull();
345
396
  // The new nextRunAt should be >= the interval from now
346
- expect(afterReset!).toBeGreaterThanOrEqual(before + mockConfig.heartbeat.intervalMs);
397
+ expect(afterReset!).toBeGreaterThanOrEqual(
398
+ before + mockConfig.heartbeat.intervalMs,
399
+ );
347
400
  service.stop();
348
401
  });
349
402
 
@@ -364,11 +417,31 @@ describe("HeartbeatService", () => {
364
417
  expect(service.nextRunAt).toBeNull();
365
418
  });
366
419
 
367
- test("cleanup", () => {
368
- try {
369
- rmSync(testWorkspaceDir, { recursive: true, force: true });
370
- } catch {
371
- /* ignore */
372
- }
420
+ test("passes heartbeat config speed to processMessage", async () => {
421
+ mockConfig.heartbeat.speed = "standard";
422
+ const service = createService();
423
+ await service.runOnce();
424
+
425
+ expect(processMessageCalls).toHaveLength(1);
426
+ expect(processMessageCalls[0].options).toEqual({ speed: "standard" });
427
+ });
428
+
429
+ test("heartbeat uses its own speed even when global config differs", async () => {
430
+ // Simulate: global config has fast, but heartbeat config has standard
431
+ mockConfig.heartbeat.speed = "standard";
432
+ const service = createService();
433
+ await service.runOnce();
434
+
435
+ expect(processMessageCalls).toHaveLength(1);
436
+ expect(processMessageCalls[0].options?.speed).toBe("standard");
437
+ });
438
+
439
+ test("heartbeat passes fast speed when explicitly configured", async () => {
440
+ mockConfig.heartbeat.speed = "fast";
441
+ const service = createService();
442
+ await service.runOnce();
443
+
444
+ expect(processMessageCalls).toHaveLength(1);
445
+ expect(processMessageCalls[0].options?.speed).toBe("fast");
373
446
  });
374
447
  });
@@ -9,9 +9,9 @@ import { tmpdir } from "node:os";
9
9
  import { join } from "node:path";
10
10
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
11
11
 
12
- // Set BASE_DATA_DIR before importing modules that use getRootDir()
12
+ // Set VELLUM_WORKSPACE_DIR before importing modules that use getWorkspaceDir()
13
13
  const testDir = join(tmpdir(), `hooks-blocking-test-${Date.now()}`);
14
- process.env.BASE_DATA_DIR = testDir;
14
+ process.env.VELLUM_WORKSPACE_DIR = testDir;
15
15
 
16
16
  import { saveHooksConfig } from "../hooks/config.js";
17
17
  import { HookManager, resetHookManager } from "../hooks/manager.js";
@@ -45,7 +45,7 @@ describe("Blocking Hooks", () => {
45
45
  let hooksDir: string;
46
46
 
47
47
  beforeEach(() => {
48
- hooksDir = join(testDir, ".vellum", "hooks");
48
+ hooksDir = join(testDir, "hooks");
49
49
  mkdirSync(hooksDir, { recursive: true });
50
50
  resetHookManager();
51
51
  });
@@ -9,9 +9,9 @@ import { tmpdir } from "node:os";
9
9
  import { join } from "node:path";
10
10
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
11
11
 
12
- // Set BASE_DATA_DIR before importing modules that use getRootDir()
12
+ // Set VELLUM_WORKSPACE_DIR before importing modules that use getWorkspaceDir()
13
13
  const testDir = join(tmpdir(), `hooks-config-test-${Date.now()}`);
14
- process.env.BASE_DATA_DIR = testDir;
14
+ process.env.VELLUM_WORKSPACE_DIR = testDir;
15
15
 
16
16
  import {
17
17
  ensureHookInConfig,
@@ -23,7 +23,7 @@ import {
23
23
 
24
24
  describe("Hooks Config", () => {
25
25
  beforeEach(() => {
26
- const hooksDir = join(testDir, ".vellum", "hooks");
26
+ const hooksDir = join(testDir, "hooks");
27
27
  mkdirSync(hooksDir, { recursive: true });
28
28
  });
29
29
 
@@ -38,7 +38,7 @@ describe("Hooks Config", () => {
38
38
  });
39
39
 
40
40
  test("loadHooksConfig reads existing config", () => {
41
- const configPath = join(testDir, ".vellum", "hooks", "config.json");
41
+ const configPath = join(testDir, "hooks", "config.json");
42
42
  writeFileSync(
43
43
  configPath,
44
44
  JSON.stringify({
@@ -52,7 +52,7 @@ describe("Hooks Config", () => {
52
52
  });
53
53
 
54
54
  test("loadHooksConfig returns defaults for invalid JSON", () => {
55
- const configPath = join(testDir, ".vellum", "hooks", "config.json");
55
+ const configPath = join(testDir, "hooks", "config.json");
56
56
  writeFileSync(configPath, "NOT VALID JSON {{{");
57
57
 
58
58
  const config = loadHooksConfig();
@@ -61,7 +61,7 @@ describe("Hooks Config", () => {
61
61
  });
62
62
 
63
63
  test("loadHooksConfig returns defaults for invalid structure", () => {
64
- const configPath = join(testDir, ".vellum", "hooks", "config.json");
64
+ const configPath = join(testDir, "hooks", "config.json");
65
65
  writeFileSync(configPath, JSON.stringify({ foo: "bar" }));
66
66
 
67
67
  const config = loadHooksConfig();
@@ -73,7 +73,7 @@ describe("Hooks Config", () => {
73
73
  const config = { version: 1, hooks: { "test-hook": { enabled: true } } };
74
74
  saveHooksConfig(config);
75
75
 
76
- const configPath = join(testDir, ".vellum", "hooks", "config.json");
76
+ const configPath = join(testDir, "hooks", "config.json");
77
77
  expect(existsSync(configPath)).toBe(true);
78
78
  const read = JSON.parse(readFileSync(configPath, "utf-8"));
79
79
  expect(read.hooks["test-hook"].enabled).toBe(true);
@@ -6,9 +6,9 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
6
6
  import { saveHooksConfig } from "../hooks/config.js";
7
7
  import { discoverHooks } from "../hooks/discovery.js";
8
8
 
9
- // Set BASE_DATA_DIR before importing modules that use getRootDir()
9
+ // Set VELLUM_WORKSPACE_DIR before importing modules that use platform helpers
10
10
  const testDir = join(tmpdir(), `hooks-discovery-test-${Date.now()}`);
11
- process.env.BASE_DATA_DIR = testDir;
11
+ process.env.VELLUM_WORKSPACE_DIR = testDir;
12
12
 
13
13
  function createHook(
14
14
  hooksDir: string,
@@ -29,7 +29,7 @@ describe("Hooks Discovery", () => {
29
29
  let hooksDir: string;
30
30
 
31
31
  beforeEach(() => {
32
- hooksDir = join(testDir, ".vellum", "hooks");
32
+ hooksDir = join(testDir, "hooks");
33
33
  mkdirSync(hooksDir, { recursive: true });
34
34
  });
35
35
 
@@ -9,9 +9,9 @@ import { tmpdir } from "node:os";
9
9
  import { join } from "node:path";
10
10
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
11
11
 
12
- // Set BASE_DATA_DIR before importing modules that use getRootDir()
12
+ // Set VELLUM_WORKSPACE_DIR before importing modules that use getWorkspaceDir()
13
13
  const testDir = join(tmpdir(), `hooks-integration-test-${Date.now()}`);
14
- process.env.BASE_DATA_DIR = testDir;
14
+ process.env.VELLUM_WORKSPACE_DIR = testDir;
15
15
 
16
16
  import { saveHooksConfig } from "../hooks/config.js";
17
17
  import { HookManager, resetHookManager } from "../hooks/manager.js";
@@ -43,7 +43,7 @@ describe("Hooks Integration", () => {
43
43
  let hooksDir: string;
44
44
 
45
45
  beforeEach(() => {
46
- hooksDir = join(testDir, ".vellum", "hooks");
46
+ hooksDir = join(testDir, "hooks");
47
47
  mkdirSync(hooksDir, { recursive: true });
48
48
  resetHookManager();
49
49
  });
@@ -89,7 +89,7 @@ describe("Hooks Integration", () => {
89
89
  hooksDir,
90
90
  "env-capture",
91
91
  ["daemon-start"],
92
- `#!/bin/bash\necho "$VELLUM_HOOK_EVENT|$VELLUM_HOOK_NAME|$VELLUM_ROOT_DIR" > "${outputFile}"`,
92
+ `#!/bin/bash\necho "$VELLUM_HOOK_EVENT|$VELLUM_HOOK_NAME|$VELLUM_WORKSPACE_DIR" > "${outputFile}"`,
93
93
  );
94
94
  saveHooksConfig({
95
95
  version: 1,
@@ -106,7 +106,7 @@ describe("Hooks Integration", () => {
106
106
  const parts = output.split("|");
107
107
  expect(parts[0]).toBe("daemon-start");
108
108
  expect(parts[1]).toBe("env-capture");
109
- expect(parts[2]).toContain(".vellum");
109
+ expect(parts[2]).toBe(testDir);
110
110
  });
111
111
 
112
112
  test("multiple hooks for same event run in order", async () => {
@@ -3,9 +3,9 @@ import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
5
5
 
6
- // Set BASE_DATA_DIR before importing modules that use getRootDir()
6
+ // Set VELLUM_WORKSPACE_DIR before importing modules that use getWorkspaceDir()
7
7
  const testDir = join(tmpdir(), `hooks-manager-test-${Date.now()}`);
8
- process.env.BASE_DATA_DIR = testDir;
8
+ process.env.VELLUM_WORKSPACE_DIR = testDir;
9
9
 
10
10
  import { saveHooksConfig } from "../hooks/config.js";
11
11
  import {
@@ -41,7 +41,7 @@ describe("HookManager", () => {
41
41
  let hooksDir: string;
42
42
 
43
43
  beforeEach(() => {
44
- hooksDir = join(testDir, ".vellum", "hooks");
44
+ hooksDir = join(testDir, "hooks");
45
45
  mkdirSync(hooksDir, { recursive: true });
46
46
  resetHookManager();
47
47
  });
@@ -3,9 +3,9 @@ import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
5
5
 
6
- // Set BASE_DATA_DIR before importing modules that use getRootDir()
6
+ // Set VELLUM_WORKSPACE_DIR before importing modules that use platform helpers
7
7
  const testDir = join(tmpdir(), `hooks-runner-test-${Date.now()}`);
8
- process.env.BASE_DATA_DIR = testDir;
8
+ process.env.VELLUM_WORKSPACE_DIR = testDir;
9
9
 
10
10
  import { runHookScript } from "../hooks/runner.js";
11
11
  import type { DiscoveredHook, HookEventData } from "../hooks/types.js";
@@ -40,7 +40,7 @@ describe("Hook Runner", () => {
40
40
  let hooksDir: string;
41
41
 
42
42
  beforeEach(() => {
43
- hooksDir = join(testDir, ".vellum", "hooks");
43
+ hooksDir = join(testDir, "hooks");
44
44
  mkdirSync(hooksDir, { recursive: true });
45
45
  });
46
46
 
@@ -136,26 +136,8 @@ describe("Hook Runner", () => {
136
136
  const result = await runHookScript(hook, eventData);
137
137
  expect(result.exitCode).toBe(0);
138
138
  const wsDir = result.stdout.trim();
139
- expect(wsDir).toContain(".vellum");
140
- expect(wsDir).toEndWith("workspace");
141
- });
142
-
143
- test("sets both VELLUM_ROOT_DIR and VELLUM_WORKSPACE_DIR", async () => {
144
- const hook = createTestHook(
145
- hooksDir,
146
- "both-dirs-hook",
147
- '#!/bin/bash\necho "$VELLUM_ROOT_DIR|$VELLUM_WORKSPACE_DIR"',
148
- );
149
- const eventData: HookEventData = { event: "pre-tool-execute" };
150
-
151
- const result = await runHookScript(hook, eventData);
152
- expect(result.exitCode).toBe(0);
153
- const [rootDir, wsDir] = result.stdout.trim().split("|");
154
- expect(rootDir).toContain(".vellum");
155
- expect(wsDir).toContain(".vellum");
156
- expect(wsDir).toEndWith("workspace");
157
- // workspace dir should be a subdirectory of root dir
158
- expect(wsDir).toStartWith(rootDir);
139
+ expect(wsDir.length).toBeGreaterThan(0);
140
+ expect(wsDir).toBe(testDir);
159
141
  });
160
142
 
161
143
  test("handles non-existent script gracefully", async () => {
@@ -9,9 +9,9 @@ import { tmpdir } from "node:os";
9
9
  import { join } from "node:path";
10
10
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
11
11
 
12
- // Set BASE_DATA_DIR before importing modules that use getRootDir()
12
+ // Set VELLUM_WORKSPACE_DIR before importing modules that use platform helpers
13
13
  const testDir = join(tmpdir(), `hooks-settings-test-${Date.now()}`);
14
- process.env.BASE_DATA_DIR = testDir;
14
+ process.env.VELLUM_WORKSPACE_DIR = testDir;
15
15
 
16
16
  import { getHookSettings } from "../hooks/config.js";
17
17
  import { saveHooksConfig } from "../hooks/config.js";
@@ -22,7 +22,7 @@ describe("Hook Settings", () => {
22
22
  let hooksDir: string;
23
23
 
24
24
  beforeEach(() => {
25
- hooksDir = join(testDir, ".vellum", "hooks");
25
+ hooksDir = join(testDir, "hooks");
26
26
  mkdirSync(hooksDir, { recursive: true });
27
27
  });
28
28
 
@@ -13,9 +13,9 @@ import { tmpdir } from "node:os";
13
13
  import { join } from "node:path";
14
14
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
15
15
 
16
- // Set BASE_DATA_DIR before importing modules that use getRootDir()
16
+ // Set VELLUM_WORKSPACE_DIR before importing modules that use platform helpers
17
17
  const testDir = join(tmpdir(), `hooks-templates-test-${Date.now()}`);
18
- process.env.BASE_DATA_DIR = testDir;
18
+ process.env.VELLUM_WORKSPACE_DIR = testDir;
19
19
 
20
20
  import {
21
21
  ensureHookInConfig,
@@ -45,7 +45,7 @@ describe("Hook Templates", () => {
45
45
  let hooksDir: string;
46
46
 
47
47
  beforeEach(() => {
48
- hooksDir = join(testDir, ".vellum", "hooks");
48
+ hooksDir = join(testDir, "hooks");
49
49
  mkdirSync(hooksDir, { recursive: true });
50
50
  });
51
51
 
@@ -1,27 +1,5 @@
1
- import { mkdtempSync, realpathSync, rmSync } from "node:fs";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
1
  import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
5
2
 
6
- const testDir = realpathSync(
7
- mkdtempSync(join(tmpdir(), "http-conversation-lineage-test-")),
8
- );
9
-
10
- mock.module("../util/platform.js", () => ({
11
- getRootDir: () => join(testDir, ".vellum"),
12
- getDataDir: () => join(testDir, ".vellum", "workspace", "data"),
13
- getWorkspaceDir: () => join(testDir, ".vellum", "workspace"),
14
- getConversationsDir: () =>
15
- join(testDir, ".vellum", "workspace", "conversations"),
16
- isMacOS: () => process.platform === "darwin",
17
- isLinux: () => process.platform === "linux",
18
- isWindows: () => process.platform === "win32",
19
- getPidPath: () => join(testDir, "test.pid"),
20
- getDbPath: () => join(testDir, "test.db"),
21
- getLogPath: () => join(testDir, "test.log"),
22
- ensureDataDir: () => {},
23
- }));
24
-
25
3
  mock.module("../util/logger.js", () => ({
26
4
  getLogger: () =>
27
5
  new Proxy({} as Record<string, unknown>, {
@@ -86,11 +64,6 @@ describe("conversation lineage in HTTP reads", () => {
86
64
  afterAll(async () => {
87
65
  await server?.stop();
88
66
  resetDb();
89
- try {
90
- rmSync(testDir, { recursive: true, force: true });
91
- } catch {
92
- /* best effort */
93
- }
94
67
  });
95
68
 
96
69
  test("GET /v1/conversations returns forkParent for surviving parents", async () => {
@@ -32,10 +32,6 @@ mock.module("../memory/checkpoints.js", () => ({
32
32
  // Simulated workspace file contents
33
33
  const workspaceFiles: Record<string, string> = {};
34
34
 
35
- mock.module("../util/platform.js", () => ({
36
- getWorkspacePromptPath: (name: string) => `/mock/workspace/${name}`,
37
- }));
38
-
39
35
  mock.module("node:fs", () => ({
40
36
  existsSync: (path: string) => {
41
37
  const name = path.split("/").pop() ?? "";
@@ -5,29 +5,12 @@
5
5
  * granted access without guardian approval, and that invalid/expired/revoked
6
6
  * tokens produce the correct deterministic refusal messages.
7
7
  */
8
- import { mkdtempSync, rmSync } from "node:fs";
9
- import { tmpdir } from "node:os";
10
- import { join } from "node:path";
11
8
  import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
12
9
 
13
10
  // ---------------------------------------------------------------------------
14
11
  // Test isolation: in-memory SQLite via temp directory
15
12
  // ---------------------------------------------------------------------------
16
13
 
17
- const testDir = mkdtempSync(join(tmpdir(), "inbound-invite-redemption-test-"));
18
-
19
- mock.module("../util/platform.js", () => ({
20
- getRootDir: () => testDir,
21
- getDataDir: () => testDir,
22
- isMacOS: () => process.platform === "darwin",
23
- isLinux: () => process.platform === "linux",
24
- isWindows: () => process.platform === "win32",
25
- getPidPath: () => join(testDir, "test.pid"),
26
- getDbPath: () => join(testDir, "test.db"),
27
- getLogPath: () => join(testDir, "test.log"),
28
- ensureDataDir: () => {},
29
- }));
30
-
31
14
  mock.module("../util/logger.js", () => ({
32
15
  getLogger: () =>
33
16
  new Proxy({} as Record<string, unknown>, {
@@ -94,11 +77,6 @@ initializeDb();
94
77
 
95
78
  afterAll(() => {
96
79
  resetDb();
97
- try {
98
- rmSync(testDir, { recursive: true });
99
- } catch {
100
- /* best effort */
101
- }
102
80
  });
103
81
 
104
82
  // ---------------------------------------------------------------------------
@@ -13,8 +13,7 @@
13
13
  * 5. Continue matching the existing skill_load:* flow for non-dynamic skills.
14
14
  */
15
15
 
16
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
17
- import { tmpdir } from "node:os";
16
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
18
17
  import { join } from "node:path";
19
18
  import { beforeEach, describe, expect, mock, test } from "bun:test";
20
19
 
@@ -22,20 +21,10 @@ import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
22
21
 
23
22
  // ── Mock setup (must be before any imports from the project) ──────────────
24
23
 
25
- const testDir = mkdtempSync(join(tmpdir(), "inline-skill-perm-test-"));
26
-
27
- mock.module("../util/platform.js", () => ({
28
- getRootDir: () => testDir,
29
- getDataDir: () => join(testDir, "data"),
30
- getWorkspaceSkillsDir: () => join(testDir, "skills"),
31
- isMacOS: () => process.platform === "darwin",
32
- isLinux: () => process.platform === "linux",
33
- isWindows: () => process.platform === "win32",
34
- getPidPath: () => join(testDir, "test.pid"),
35
- getDbPath: () => join(testDir, "test.db"),
36
- getLogPath: () => join(testDir, "test.log"),
37
- ensureDataDir: () => {},
38
- }));
24
+ const testDir = process.env.VELLUM_WORKSPACE_DIR!;
25
+
26
+ // Point the file-based trust backend at the test temp dir.
27
+ process.env.GATEWAY_SECURITY_DIR = join(testDir, "protected");
39
28
 
40
29
  mock.module("../util/logger.js", () => ({
41
30
  getLogger: () =>
@@ -1,39 +1,6 @@
1
- import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
2
- import { tmpdir } from "node:os";
1
+ import { readFileSync } from "node:fs";
3
2
  import { join } from "node:path";
4
- import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
5
-
6
- // ── Mock platform to isolate tests from the real workspace ────────────
7
- const TEST_DIR = join(tmpdir(), `vellum-routing-test-${crypto.randomUUID()}`);
8
-
9
- // eslint-disable-next-line @typescript-eslint/no-require-imports
10
- const realPlatform = require("../util/platform.js");
11
- mock.module("../util/platform.js", () => ({
12
- ...realPlatform,
13
- getRootDir: () => TEST_DIR,
14
- getDataDir: () => TEST_DIR,
15
- getWorkspaceDir: () => TEST_DIR,
16
- getWorkspaceConfigPath: () => join(TEST_DIR, "config.json"),
17
- getWorkspaceSkillsDir: () => join(TEST_DIR, "skills"),
18
- getWorkspaceHooksDir: () => join(TEST_DIR, "hooks"),
19
- getWorkspacePromptPath: (file: string) => join(TEST_DIR, file),
20
- ensureDataDir: () => {},
21
- getPidPath: () => join(TEST_DIR, "vellum.pid"),
22
- getDbPath: () => join(TEST_DIR, "data", "assistant.db"),
23
- getLogPath: () => join(TEST_DIR, "logs", "vellum.log"),
24
- getHistoryPath: () => join(TEST_DIR, "history"),
25
- getHooksDir: () => join(TEST_DIR, "hooks"),
26
-
27
- getSandboxRootDir: () => join(TEST_DIR, "sandbox"),
28
- getSandboxWorkingDir: () => TEST_DIR,
29
- getInterfacesDir: () => join(TEST_DIR, "interfaces"),
30
- isMacOS: () => process.platform === "darwin",
31
- isLinux: () => process.platform === "linux",
32
- isWindows: () => process.platform === "win32",
33
- getPlatformName: () => process.platform,
34
- getClipboardCommand: () => null,
35
- readSessionToken: () => null,
36
- }));
3
+ import { describe, expect, mock, test } from "bun:test";
37
4
 
38
5
  const noopLogger = new Proxy({} as Record<string, unknown>, {
39
6
  get: (_target, prop) => (prop === "child" ? () => noopLogger : () => {}),
@@ -120,16 +87,6 @@ const sendNotificationDef = notifToolsJson.tools.find(
120
87
  // =====================================================================
121
88
 
122
89
  describe("Task/Schedule routing NOT in system prompt (moved to tool descriptions)", () => {
123
- beforeEach(() => {
124
- mkdirSync(TEST_DIR, { recursive: true });
125
- });
126
-
127
- afterEach(() => {
128
- if (existsSync(TEST_DIR)) {
129
- rmSync(TEST_DIR, { recursive: true, force: true });
130
- }
131
- });
132
-
133
90
  test("system prompt does not contain the old routing section", () => {
134
91
  const prompt = buildSystemPrompt();
135
92
  expect(prompt).not.toContain(
@@ -227,16 +184,6 @@ describe("cross-tool routing consistency", () => {
227
184
  // =====================================================================
228
185
 
229
186
  describe("Activation hints in skills catalog", () => {
230
- beforeEach(() => {
231
- mkdirSync(TEST_DIR, { recursive: true });
232
- });
233
-
234
- afterEach(() => {
235
- if (existsSync(TEST_DIR)) {
236
- rmSync(TEST_DIR, { recursive: true, force: true });
237
- }
238
- });
239
-
240
187
  test("phone-calls skill includes hints and avoid-when in catalog line", () => {
241
188
  const prompt = buildSystemPrompt();
242
189
  const line = prompt.split("\n").find((l) => l.includes("**phone-calls**"));