@vellumai/assistant 0.5.12 → 0.5.14

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 (442) hide show
  1. package/.env.example +1 -6
  2. package/AGENTS.md +4 -0
  3. package/ARCHITECTURE.md +0 -1
  4. package/Dockerfile +41 -9
  5. package/bunfig.toml +1 -0
  6. package/docs/architecture/memory.md +3 -3
  7. package/node_modules/@vellumai/ces-contracts/src/index.ts +7 -0
  8. package/node_modules/@vellumai/ces-contracts/src/rpc.ts +5 -0
  9. package/openapi.yaml +127 -22
  10. package/package.json +1 -1
  11. package/src/__tests__/access-request-decision.test.ts +2 -32
  12. package/src/__tests__/actor-token-service.test.ts +1 -31
  13. package/src/__tests__/anthropic-provider.test.ts +53 -40
  14. package/src/__tests__/app-git-history.test.ts +9 -17
  15. package/src/__tests__/app-git-service.test.ts +14 -20
  16. package/src/__tests__/app-store-dir-names.test.ts +10 -20
  17. package/src/__tests__/approval-cascade.test.ts +2 -19
  18. package/src/__tests__/approval-primitive.test.ts +2 -27
  19. package/src/__tests__/approval-routes-http.test.ts +2 -30
  20. package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -28
  21. package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -45
  22. package/src/__tests__/attachments-store.test.ts +5 -32
  23. package/src/__tests__/audit-log-rotation.test.ts +5 -36
  24. package/src/__tests__/avatar-e2e.test.ts +1 -9
  25. package/src/__tests__/avatar-generator.test.ts +1 -7
  26. package/src/__tests__/browser-fill-credential.test.ts +0 -4
  27. package/src/__tests__/browser-manager.test.ts +0 -6
  28. package/src/__tests__/call-controller.test.ts +1 -22
  29. package/src/__tests__/call-conversation-messages.test.ts +0 -21
  30. package/src/__tests__/call-domain.test.ts +0 -25
  31. package/src/__tests__/call-pointer-messages.test.ts +0 -21
  32. package/src/__tests__/call-recovery.test.ts +0 -22
  33. package/src/__tests__/call-routes-http.test.ts +0 -24
  34. package/src/__tests__/call-store.test.ts +0 -21
  35. package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -24
  36. package/src/__tests__/canonical-guardian-store.test.ts +48 -21
  37. package/src/__tests__/channel-approval-routes.test.ts +6 -26
  38. package/src/__tests__/channel-approvals.test.ts +1 -38
  39. package/src/__tests__/channel-delivery-store.test.ts +0 -21
  40. package/src/__tests__/channel-guardian.test.ts +0 -26
  41. package/src/__tests__/channel-reply-delivery.test.ts +5 -0
  42. package/src/__tests__/channel-retry-sweep.test.ts +0 -21
  43. package/src/__tests__/checker.test.ts +26 -61
  44. package/src/__tests__/clawhub.test.ts +9 -25
  45. package/src/__tests__/cli-command-risk-guard.test.ts +0 -18
  46. package/src/__tests__/config-loader-backfill.test.ts +9 -28
  47. package/src/__tests__/config-schema-cmd.test.ts +5 -25
  48. package/src/__tests__/config-schema.test.ts +21 -40
  49. package/src/__tests__/config-watcher.test.ts +4 -91
  50. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -21
  51. package/src/__tests__/contacts-tools.test.ts +0 -21
  52. package/src/__tests__/context-memory-e2e.test.ts +0 -21
  53. package/src/__tests__/context-window-manager.test.ts +130 -3
  54. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -4
  55. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -4
  56. package/src/__tests__/conversation-agent-loop.test.ts +0 -4
  57. package/src/__tests__/conversation-attachments.test.ts +1 -24
  58. package/src/__tests__/conversation-attention-store.test.ts +0 -21
  59. package/src/__tests__/conversation-attention-telegram.test.ts +0 -22
  60. package/src/__tests__/conversation-clear-safety.test.ts +0 -22
  61. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -21
  62. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -24
  63. package/src/__tests__/conversation-disk-view-integration.test.ts +1 -23
  64. package/src/__tests__/conversation-disk-view.test.ts +5 -27
  65. package/src/__tests__/conversation-error.test.ts +1 -1
  66. package/src/__tests__/conversation-fork-crud.test.ts +1 -33
  67. package/src/__tests__/conversation-fork-route.test.ts +0 -27
  68. package/src/__tests__/conversation-history-web-search.test.ts +23 -16
  69. package/src/__tests__/conversation-init.benchmark.test.ts +22 -43
  70. package/src/__tests__/conversation-key-store-disk-view.test.ts +8 -34
  71. package/src/__tests__/conversation-load-history-repair.test.ts +0 -4
  72. package/src/__tests__/conversation-pre-run-repair.test.ts +0 -4
  73. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -4
  74. package/src/__tests__/conversation-queue.test.ts +8 -8
  75. package/src/__tests__/conversation-routes-disk-view.test.ts +13 -51
  76. package/src/__tests__/conversation-runtime-assembly.test.ts +64 -38
  77. package/src/__tests__/conversation-slash-commands.test.ts +5 -0
  78. package/src/__tests__/conversation-slash-queue.test.ts +0 -4
  79. package/src/__tests__/conversation-slash-unknown.test.ts +0 -4
  80. package/src/__tests__/conversation-speed-override.test.ts +326 -0
  81. package/src/__tests__/conversation-starter-routes.test.ts +0 -23
  82. package/src/__tests__/conversation-store.test.ts +0 -21
  83. package/src/__tests__/conversation-unread-route.test.ts +0 -24
  84. package/src/__tests__/conversation-usage.test.ts +56 -21
  85. package/src/__tests__/conversation-wipe.test.ts +0 -21
  86. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
  87. package/src/__tests__/conversation-workspace-injection.test.ts +0 -4
  88. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -4
  89. package/src/__tests__/credential-execution-shell-lockdown.test.ts +8 -5
  90. package/src/__tests__/credential-vault-unit.test.ts +9 -428
  91. package/src/__tests__/credentials-cli.test.ts +10 -10
  92. package/src/__tests__/daemon-assistant-events.test.ts +0 -19
  93. package/src/__tests__/date-context.test.ts +77 -97
  94. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +7 -24
  95. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +29 -42
  96. package/src/__tests__/delete-managed-skill-tool.test.ts +2 -10
  97. package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -26
  98. package/src/__tests__/docker-signing-key-bootstrap.test.ts +61 -15
  99. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -36
  100. package/src/__tests__/email-cli.test.ts +6 -6
  101. package/src/__tests__/ephemeral-permissions.test.ts +5 -17
  102. package/src/__tests__/first-greeting.test.ts +11 -32
  103. package/src/__tests__/followup-tools.test.ts +0 -21
  104. package/src/__tests__/gateway-only-enforcement.test.ts +0 -20
  105. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -23
  106. package/src/__tests__/guardian-action-followup-executor.test.ts +0 -23
  107. package/src/__tests__/guardian-action-followup-store.test.ts +0 -21
  108. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -21
  109. package/src/__tests__/guardian-action-late-reply.test.ts +0 -21
  110. package/src/__tests__/guardian-action-store.test.ts +0 -21
  111. package/src/__tests__/guardian-action-sweep.test.ts +0 -21
  112. package/src/__tests__/guardian-binding-drift-heal.test.ts +0 -23
  113. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +172 -22
  114. package/src/__tests__/guardian-dispatch.test.ts +0 -21
  115. package/src/__tests__/guardian-grant-minting.test.ts +0 -22
  116. package/src/__tests__/guardian-outbound-http.test.ts +0 -22
  117. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -23
  118. package/src/__tests__/guardian-routing-invariants.test.ts +0 -22
  119. package/src/__tests__/guardian-routing-state.test.ts +0 -22
  120. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -24
  121. package/src/__tests__/headless-browser-interactions.test.ts +0 -4
  122. package/src/__tests__/headless-browser-navigate.test.ts +0 -4
  123. package/src/__tests__/headless-browser-read-tools.test.ts +0 -4
  124. package/src/__tests__/headless-browser-snapshot.test.ts +0 -4
  125. package/src/__tests__/heartbeat-service.test.ts +99 -26
  126. package/src/__tests__/hooks-blocking.test.ts +3 -3
  127. package/src/__tests__/hooks-config.test.ts +7 -7
  128. package/src/__tests__/hooks-discovery.test.ts +3 -3
  129. package/src/__tests__/hooks-integration.test.ts +5 -5
  130. package/src/__tests__/hooks-manager.test.ts +3 -3
  131. package/src/__tests__/hooks-runner.test.ts +5 -23
  132. package/src/__tests__/hooks-settings.test.ts +3 -3
  133. package/src/__tests__/hooks-templates.test.ts +3 -3
  134. package/src/__tests__/http-conversation-lineage.test.ts +0 -27
  135. package/src/__tests__/identity-intro-cache.test.ts +0 -4
  136. package/src/__tests__/inbound-invite-redemption.test.ts +0 -22
  137. package/src/__tests__/inline-skill-load-permissions.test.ts +5 -16
  138. package/src/__tests__/intent-routing.test.ts +2 -55
  139. package/src/__tests__/invite-redemption-service.test.ts +0 -21
  140. package/src/__tests__/invite-routes-http.test.ts +0 -21
  141. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +0 -17
  142. package/src/__tests__/journal-context.test.ts +8 -75
  143. package/src/__tests__/list-messages-attachments.test.ts +0 -22
  144. package/src/__tests__/llm-context-route-provider.test.ts +0 -21
  145. package/src/__tests__/llm-request-log-turn-query.test.ts +46 -28
  146. package/src/__tests__/llm-usage-store.test.ts +0 -21
  147. package/src/__tests__/log-export-workspace.test.ts +1 -1
  148. package/src/__tests__/managed-skill-lifecycle.test.ts +1 -1
  149. package/src/__tests__/managed-store.test.ts +1 -1
  150. package/src/__tests__/mcp-cli.test.ts +7 -10
  151. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -21
  152. package/src/__tests__/memory-jobs-worker-backoff.test.ts +0 -11
  153. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -21
  154. package/src/__tests__/memory-recall-log-store.test.ts +0 -27
  155. package/src/__tests__/memory-recall-quality.test.ts +0 -21
  156. package/src/__tests__/memory-regressions.experimental.test.ts +31 -30
  157. package/src/__tests__/memory-regressions.test.ts +282 -70
  158. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -21
  159. package/src/__tests__/memory-upsert-concurrency.test.ts +0 -21
  160. package/src/__tests__/messaging-send-tool.test.ts +201 -0
  161. package/src/__tests__/migration-cross-version-compatibility.test.ts +18 -13
  162. package/src/__tests__/migration-export-http.test.ts +7 -1
  163. package/src/__tests__/migration-import-commit-http.test.ts +16 -14
  164. package/src/__tests__/migration-import-preflight-http.test.ts +27 -44
  165. package/src/__tests__/migration-validate-http.test.ts +1 -28
  166. package/src/__tests__/native-web-search.test.ts +25 -22
  167. package/src/__tests__/navigate-settings-tab.test.ts +6 -2
  168. package/src/__tests__/non-member-access-request.test.ts +0 -22
  169. package/src/__tests__/notification-guardian-path.test.ts +0 -21
  170. package/src/__tests__/notification-schedule-dedup.test.ts +1 -25
  171. package/src/__tests__/oauth-apps-routes.test.ts +103 -2
  172. package/src/__tests__/oauth-cli.test.ts +52 -0
  173. package/src/__tests__/oauth-provider-profiles.test.ts +0 -16
  174. package/src/__tests__/oauth-provider-serializer.test.ts +232 -0
  175. package/src/__tests__/oauth-providers-routes.test.ts +257 -0
  176. package/src/__tests__/oauth-store.test.ts +0 -21
  177. package/src/__tests__/onboarding-template-contract.test.ts +2 -2
  178. package/src/__tests__/openai-provider.test.ts +261 -0
  179. package/src/__tests__/pairing-concurrent.test.ts +6 -6
  180. package/src/__tests__/pairing-routes.test.ts +7 -1
  181. package/src/__tests__/path-policy.test.ts +1 -1
  182. package/src/__tests__/platform.test.ts +62 -251
  183. package/src/__tests__/playbook-execution.test.ts +0 -21
  184. package/src/__tests__/playbook-tools.test.ts +0 -21
  185. package/src/__tests__/pricing.test.ts +100 -0
  186. package/src/__tests__/relay-server.test.ts +1 -25
  187. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -24
  188. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -24
  189. package/src/__tests__/runtime-events-sse.test.ts +0 -24
  190. package/src/__tests__/sandbox-diagnostics.test.ts +2 -1
  191. package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -1
  192. package/src/__tests__/schedule-store.test.ts +0 -21
  193. package/src/__tests__/schedule-tools.test.ts +0 -21
  194. package/src/__tests__/scheduler-recurrence.test.ts +0 -21
  195. package/src/__tests__/scoped-approval-grants.test.ts +0 -21
  196. package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -21
  197. package/src/__tests__/secret-allowlist.test.ts +1 -1
  198. package/src/__tests__/secret-ingress-channel.test.ts +0 -5
  199. package/src/__tests__/secret-ingress-cli.test.ts +0 -6
  200. package/src/__tests__/secret-ingress-http.test.ts +0 -5
  201. package/src/__tests__/secret-ingress.test.ts +0 -5
  202. package/src/__tests__/send-endpoint-busy.test.ts +0 -24
  203. package/src/__tests__/sequence-store.test.ts +0 -21
  204. package/src/__tests__/server-history-render.test.ts +0 -24
  205. package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -4
  206. package/src/__tests__/skill-feature-flags.test.ts +8 -0
  207. package/src/__tests__/skill-load-inline-command.test.ts +9 -0
  208. package/src/__tests__/skill-load-inline-includes.test.ts +9 -0
  209. package/src/__tests__/skill-load-tool.test.ts +11 -0
  210. package/src/__tests__/skill-secret-handling-guard.test.ts +212 -0
  211. package/src/__tests__/skills-uninstall.test.ts +10 -8
  212. package/src/__tests__/skills.test.ts +1 -1
  213. package/src/__tests__/slack-channel-config.test.ts +1 -1
  214. package/src/__tests__/slack-inbound-verification.test.ts +0 -22
  215. package/src/__tests__/starter-bundle.test.ts +4 -1
  216. package/src/__tests__/suggestion-routes.test.ts +2 -0
  217. package/src/__tests__/system-prompt.test.ts +1 -1
  218. package/src/__tests__/terminal-tools.test.ts +1 -1
  219. package/src/__tests__/test-preload.ts +31 -0
  220. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -1
  221. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -1
  222. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  223. package/src/__tests__/tool-executor.test.ts +0 -20
  224. package/src/__tests__/tool-input-summary.test.ts +124 -0
  225. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -1
  226. package/src/__tests__/trust-store.test.ts +7 -1
  227. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -1
  228. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -1
  229. package/src/__tests__/trusted-contact-multichannel.test.ts +1 -1
  230. package/src/__tests__/trusted-contact-verification.test.ts +1 -1
  231. package/src/__tests__/turn-boundary-resolution.test.ts +1 -1
  232. package/src/__tests__/twilio-routes.test.ts +1 -1
  233. package/src/__tests__/update-bulletin.test.ts +1 -1
  234. package/src/__tests__/vbundle-pax-and-symlink.test.ts +1 -1
  235. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -0
  236. package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
  237. package/src/__tests__/voice-session-bridge.test.ts +1 -1
  238. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -4
  239. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
  240. package/src/__tests__/workspace-migration-down-functions.test.ts +15 -3
  241. package/src/__tests__/workspace-migration-seed-device-id.test.ts +40 -4
  242. package/src/agent/loop.ts +6 -9
  243. package/src/approvals/guardian-decision-primitive.ts +46 -18
  244. package/src/approvals/guardian-request-resolvers.ts +19 -2
  245. package/src/calls/active-call-lease.ts +2 -2
  246. package/src/cli/AGENTS.md +1 -1
  247. package/src/cli/commands/doctor.ts +9 -9
  248. package/src/cli/commands/memory.ts +142 -0
  249. package/src/cli/commands/oauth/__tests__/connect.test.ts +13 -11
  250. package/src/cli/commands/oauth/__tests__/ping.test.ts +1 -1
  251. package/src/cli/commands/oauth/connect.ts +13 -12
  252. package/src/cli/commands/oauth/index.ts +1 -1
  253. package/src/cli/commands/oauth/providers.ts +47 -62
  254. package/src/cli/commands/platform/__tests__/connect.test.ts +250 -0
  255. package/src/cli/commands/platform/__tests__/disconnect.test.ts +290 -0
  256. package/src/cli/commands/platform/__tests__/status.test.ts +282 -0
  257. package/src/cli/commands/platform/connect.ts +17 -7
  258. package/src/cli/commands/platform/disconnect.ts +28 -3
  259. package/src/cli/commands/platform/index.ts +3 -3
  260. package/src/cli.ts +1 -299
  261. package/src/config/assistant-feature-flags.ts +23 -15
  262. package/src/config/bundled-skills/app-builder/TOOLS.json +16 -0
  263. package/src/config/bundled-skills/app-builder/tools/app-create.ts +4 -0
  264. package/src/config/bundled-skills/app-builder/tools/app-delete.ts +5 -1
  265. package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +9 -1
  266. package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +5 -1
  267. package/src/config/bundled-skills/contacts/TOOLS.json +8 -0
  268. package/src/config/bundled-skills/contacts/tools/contact-search.ts +10 -1
  269. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +16 -2
  270. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +1 -0
  271. package/src/config/bundled-skills/messaging/SKILL.md +7 -7
  272. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +37 -0
  273. package/src/config/bundled-skills/settings/TOOLS.json +5 -3
  274. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +4 -2
  275. package/src/config/bundled-skills/slack/SKILL.md +18 -0
  276. package/src/config/env-registry.ts +15 -11
  277. package/src/config/env.ts +1 -11
  278. package/src/config/feature-flag-registry.json +17 -1
  279. package/src/config/schema.ts +4 -0
  280. package/src/config/schemas/heartbeat.ts +6 -1
  281. package/src/config/schemas/inference.ts +14 -3
  282. package/src/config/schemas/memory-processing.ts +16 -8
  283. package/src/config/schemas/memory-retrieval.ts +3 -3
  284. package/src/config/skills.ts +1 -1
  285. package/src/context/window-manager.ts +174 -51
  286. package/src/credential-execution/client.ts +14 -2
  287. package/src/credential-execution/executable-discovery.ts +2 -2
  288. package/src/daemon/approved-devices-store.ts +2 -2
  289. package/src/daemon/assistant-attachments.ts +2 -0
  290. package/src/daemon/config-watcher.ts +4 -50
  291. package/src/daemon/conversation-agent-loop-handlers.ts +9 -1
  292. package/src/daemon/conversation-agent-loop.ts +12 -0
  293. package/src/daemon/conversation-error.ts +3 -5
  294. package/src/daemon/conversation-history.ts +7 -3
  295. package/src/daemon/conversation-lifecycle.ts +16 -0
  296. package/src/daemon/conversation-messaging.ts +1 -0
  297. package/src/daemon/conversation-notifiers.ts +67 -30
  298. package/src/daemon/conversation-process.ts +161 -2
  299. package/src/daemon/conversation-queue-manager.ts +2 -0
  300. package/src/daemon/conversation-runtime-assembly.ts +33 -11
  301. package/src/daemon/conversation-slash.ts +14 -3
  302. package/src/daemon/conversation-tool-setup.ts +2 -0
  303. package/src/daemon/conversation-usage.ts +32 -4
  304. package/src/daemon/conversation.ts +33 -1
  305. package/src/daemon/daemon-control.ts +32 -16
  306. package/src/daemon/date-context.ts +47 -45
  307. package/src/daemon/dictation-profile-store.ts +2 -2
  308. package/src/daemon/first-greeting.ts +6 -1
  309. package/src/daemon/handlers/conversations.ts +19 -0
  310. package/src/daemon/handlers/shared.ts +14 -21
  311. package/src/daemon/lifecycle.ts +18 -15
  312. package/src/daemon/message-types/conversations.ts +2 -0
  313. package/src/daemon/message-types/guardian-actions.ts +3 -17
  314. package/src/daemon/message-types/integrations.ts +11 -1
  315. package/src/daemon/message-types/messages.ts +1 -0
  316. package/src/daemon/pairing-store.ts +2 -79
  317. package/src/daemon/server.ts +154 -8
  318. package/src/daemon/watch-handler.ts +65 -21
  319. package/src/email/guardrails.ts +3 -3
  320. package/src/heartbeat/heartbeat-service.ts +14 -7
  321. package/src/hooks/cli.ts +2 -2
  322. package/src/hooks/config.ts +2 -2
  323. package/src/hooks/discovery.ts +2 -2
  324. package/src/hooks/manager.ts +2 -2
  325. package/src/hooks/runner.ts +5 -2
  326. package/src/hooks/templates.ts +2 -2
  327. package/src/index.ts +0 -12
  328. package/src/memory/admin.ts +181 -2
  329. package/src/memory/app-git-service.ts +61 -4
  330. package/src/memory/attachments-store.ts +2 -0
  331. package/src/memory/canonical-guardian-store.ts +16 -0
  332. package/src/memory/conversation-queries.ts +6 -6
  333. package/src/memory/db-init.ts +8 -0
  334. package/src/memory/embedding-local.ts +5 -2
  335. package/src/memory/indexer.ts +44 -26
  336. package/src/memory/items-extractor.ts +34 -82
  337. package/src/memory/job-handlers/batch-extraction.ts +741 -0
  338. package/src/memory/job-handlers/journal-carry-forward.test.ts +383 -0
  339. package/src/memory/job-handlers/journal-carry-forward.ts +255 -0
  340. package/src/memory/jobs-store.ts +28 -0
  341. package/src/memory/jobs-worker.ts +56 -9
  342. package/src/memory/journal-memory.ts +8 -2
  343. package/src/memory/lifecycle-events-store.ts +4 -2
  344. package/src/memory/llm-request-log-store.ts +40 -2
  345. package/src/memory/llm-usage-store.ts +4 -3
  346. package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +71 -0
  347. package/src/memory/migrations/200-usage-llm-call-count.ts +20 -0
  348. package/src/memory/migrations/index.ts +2 -0
  349. package/src/memory/query-expansion.ts +83 -0
  350. package/src/memory/retriever.test.ts +119 -0
  351. package/src/memory/retriever.ts +513 -105
  352. package/src/memory/schema/guardian.ts +4 -0
  353. package/src/memory/schema/infrastructure.ts +1 -0
  354. package/src/memory/search/formatting.test.ts +140 -0
  355. package/src/memory/search/formatting.ts +143 -198
  356. package/src/memory/search/mmr.ts +136 -0
  357. package/src/memory/search/staleness.ts +0 -15
  358. package/src/memory/search/tier-classifier.ts +10 -21
  359. package/src/memory/search/types.ts +17 -0
  360. package/src/messaging/providers/slack/adapter.ts +51 -5
  361. package/src/notifications/broadcaster.ts +13 -0
  362. package/src/notifications/copy-composer.ts +8 -0
  363. package/src/oauth/connect-orchestrator.ts +1 -1
  364. package/src/oauth/connection-resolver.ts +2 -2
  365. package/src/oauth/provider-serializer.ts +116 -0
  366. package/src/permissions/trust-store.ts +24 -7
  367. package/src/prompts/__tests__/build-cli-reference-section.test.ts +5 -0
  368. package/src/prompts/journal-context.ts +54 -36
  369. package/src/prompts/persona-resolver.ts +1 -1
  370. package/src/prompts/system-prompt.ts +38 -28
  371. package/src/prompts/templates/BOOTSTRAP.md +14 -1
  372. package/src/prompts/templates/HEARTBEAT.md +10 -0
  373. package/src/prompts/templates/NOW.md +19 -25
  374. package/src/prompts/templates/SOUL.md +13 -1
  375. package/src/prompts/templates/UPDATES.md +12 -0
  376. package/src/prompts/update-bulletin.ts +1 -1
  377. package/src/providers/anthropic/client.ts +89 -18
  378. package/src/providers/model-catalog.ts +22 -2
  379. package/src/providers/model-intents.ts +2 -2
  380. package/src/providers/openai/client.ts +40 -1
  381. package/src/providers/retry.ts +23 -4
  382. package/src/providers/types.ts +2 -0
  383. package/src/runtime/assistant-scope.ts +1 -1
  384. package/src/runtime/auth/__tests__/credential-service.test.ts +1 -0
  385. package/src/runtime/auth/route-policy.ts +1 -0
  386. package/src/runtime/auth/token-service.ts +51 -29
  387. package/src/runtime/confirmation-request-guardian-bridge.ts +3 -1
  388. package/src/runtime/guardian-decision-types.ts +16 -10
  389. package/src/runtime/http-server.ts +10 -29
  390. package/src/runtime/http-types.ts +1 -0
  391. package/src/runtime/migrations/rebind-secrets-screen.ts +2 -2
  392. package/src/runtime/migrations/vbundle-builder.ts +7 -4
  393. package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -4
  394. package/src/runtime/migrations/vbundle-importer.ts +1 -1
  395. package/src/runtime/routes/conversation-query-routes.ts +40 -8
  396. package/src/runtime/routes/conversation-routes.ts +125 -3
  397. package/src/runtime/routes/guardian-action-routes.ts +9 -3
  398. package/src/runtime/routes/identity-routes.ts +25 -4
  399. package/src/runtime/routes/llm-context-normalization.ts +1 -0
  400. package/src/runtime/routes/log-export-routes.ts +34 -12
  401. package/src/runtime/routes/migration-routes.ts +6 -10
  402. package/src/runtime/routes/oauth-apps.ts +2 -9
  403. package/src/runtime/routes/oauth-providers.ts +60 -0
  404. package/src/runtime/routes/pairing-routes.ts +0 -8
  405. package/src/runtime/routes/secret-routes.ts +9 -2
  406. package/src/runtime/routes/settings-routes.ts +0 -1
  407. package/src/runtime/routes/telemetry-routes.ts +16 -4
  408. package/src/security/encrypted-store.ts +2 -2
  409. package/src/security/secret-allowlist.ts +3 -3
  410. package/src/signals/emit-event.ts +42 -0
  411. package/src/signals/user-message.ts +37 -0
  412. package/src/telemetry/usage-telemetry-reporter.test.ts +83 -19
  413. package/src/telemetry/usage-telemetry-reporter.ts +23 -17
  414. package/src/tools/browser/browser-manager.ts +2 -2
  415. package/src/tools/browser/runtime-check.ts +2 -2
  416. package/src/tools/credentials/vault.ts +2 -249
  417. package/src/tools/memory/definitions.ts +1 -1
  418. package/src/tools/memory/handlers.test.ts +50 -8
  419. package/src/tools/memory/handlers.ts +3 -1
  420. package/src/tools/side-effects.ts +1 -6
  421. package/src/tools/terminal/safe-env.ts +3 -2
  422. package/src/tools/terminal/shell.ts +11 -14
  423. package/src/tools/tool-approval-handler.ts +20 -1
  424. package/src/tools/tool-input-summary.ts +66 -0
  425. package/src/tools/types.ts +4 -0
  426. package/src/usage/types.ts +4 -0
  427. package/src/util/device-id.ts +10 -10
  428. package/src/util/platform.ts +72 -124
  429. package/src/util/pricing.ts +19 -6
  430. package/src/util/strip-comment-lines.ts +28 -0
  431. package/src/workspace/git-service.ts +8 -18
  432. package/src/workspace/migrations/003-seed-device-id.ts +6 -4
  433. package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +7 -1
  434. package/src/workspace/migrations/017-seed-persona-dirs.ts +2 -4
  435. package/src/workspace/migrations/021-move-signals-to-workspace.ts +84 -0
  436. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +94 -0
  437. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +86 -0
  438. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +126 -0
  439. package/src/workspace/migrations/migrate-to-workspace-volume.ts +3 -6
  440. package/src/workspace/migrations/registry.ts +8 -0
  441. package/src/signals/confirm.ts +0 -82
  442. package/src/signals/trust-rule.ts +0 -174
@@ -0,0 +1,212 @@
1
+ import { execSync } from "node:child_process";
2
+ import { describe, expect, test } from "bun:test";
3
+
4
+ /**
5
+ * Guard test: SKILL.md files must never instruct the assistant to accept
6
+ * secrets (passwords, API keys, tokens, etc.) pasted directly in chat.
7
+ *
8
+ * Secrets must always be collected via `credential_store prompt`, which
9
+ * presents a secure native UI that keeps the value out of conversation
10
+ * history and LLM context.
11
+ *
12
+ * This guard prevents regressions like the gmail/messaging bundled skill
13
+ * violation where SKILL.md contained "Include client_secret too if they
14
+ * provide one" — directing the assistant to accept a secret value from
15
+ * the chat stream.
16
+ */
17
+
18
+ /** SKILL.md files permitted to contain otherwise-violating patterns. */
19
+ const ALLOWLIST = new Set<string>([
20
+ // Add paths here only if there is a genuine, documented exception.
21
+ ]);
22
+
23
+ /**
24
+ * Words that indicate the line is about a secret/credential value.
25
+ */
26
+ const SECRET_WORDS =
27
+ "secret|password|api[_\\s-]?key|auth[_\\s-]?token|private[_\\s-]?key|access[_\\s-]?token|client[_\\s-]?secret|signing[_\\s-]?key|bearer[_\\s-]?token";
28
+
29
+ /**
30
+ * Patterns that indicate the assistant is being told to accept a secret
31
+ * value directly in chat, rather than via credential_store prompt.
32
+ */
33
+ const VIOLATION_PATTERNS: RegExp[] = [
34
+ // "accept <secret> in/via/from chat/plaintext/the conversation"
35
+ new RegExp(
36
+ `accept\\s+.*(?:${SECRET_WORDS}).*\\b(?:in|via|from)\\s+(?:chat|plaintext|the\\s+conversation)`,
37
+ "i",
38
+ ),
39
+ new RegExp(
40
+ `accept\\s+.*\\b(?:in|via|from)\\s+(?:chat|plaintext|the\\s+conversation).*(?:${SECRET_WORDS})`,
41
+ "i",
42
+ ),
43
+ // "ask (the user|them) (for|to share/send/paste/type/provide) <secret>" where destination is chat
44
+ // Must have "the user" or "them" as the object to avoid matching third-party descriptions
45
+ // like "Discord will ask for a 2FA code before revealing the secret"
46
+ new RegExp(
47
+ `ask\\s+(?:the\\s+user|them)\\s+(?:for|to\\s+(?:share|send|paste|type|provide))\\s+(?:the\\s+|their\\s+|a\\s+)?(?:${SECRET_WORDS})`,
48
+ "i",
49
+ ),
50
+ // "Include <secret> too if they provide one" — the original gmail violation pattern
51
+ new RegExp(
52
+ `include\\s+(?:the\\s+)?(?:${SECRET_WORDS})\\s+(?:too|as\\s+well|also)\\s+if\\s+they\\s+provide`,
53
+ "i",
54
+ ),
55
+ // "<secret> pasted/typed/sent in chat/conversation/plaintext"
56
+ new RegExp(
57
+ `(?:${SECRET_WORDS})\\s+(?:pasted|typed|sent|provided|shared)\\s+(?:in|via|through)\\s+(?:chat|conversation|plaintext)`,
58
+ "i",
59
+ ),
60
+ // "paste/type/send <secret> in chat/here/the conversation"
61
+ new RegExp(
62
+ `(?:paste|type|send|share|provide)\\s+(?:the\\s+|your\\s+|their\\s+)?(?:${SECRET_WORDS})\\s+(?:in\\s+(?:chat|the\\s+conversation)|here)`,
63
+ "i",
64
+ ),
65
+ ];
66
+
67
+ /**
68
+ * Lines containing these negation words are typically instructing the
69
+ * assistant NOT to do something — these are not violations.
70
+ */
71
+ const NEGATION_PATTERNS =
72
+ /\b(?:never|do\s+not|don['']t|must\s+not|should\s+not|shouldn['']t)\b|\bNOT\b/;
73
+
74
+ /**
75
+ * Lines that are YAML-style field values within a credential_store prompt
76
+ * block (label, description, placeholder). These contain secret-related
77
+ * words but are secure UI text, not chat instructions.
78
+ */
79
+ const CREDENTIAL_STORE_UI_FIELD =
80
+ /^\s*(?:[-*]\s+)?(?:label|description|placeholder)\s*[:=]\s*/i;
81
+
82
+ interface Violation {
83
+ file: string;
84
+ line: number;
85
+ text: string;
86
+ }
87
+
88
+ function findViolations(): Violation[] {
89
+ const repoRoot = process.cwd() + "/..";
90
+
91
+ // Find all SKILL.md files tracked by git
92
+ let skillFiles: string[];
93
+ try {
94
+ const output = execSync(`git grep -l "" -- '*/SKILL.md'`, {
95
+ encoding: "utf-8",
96
+ cwd: repoRoot,
97
+ }).trim();
98
+ skillFiles = output.split("\n").filter((f) => f.length > 0);
99
+ } catch (err) {
100
+ if ((err as { status?: number }).status === 1) {
101
+ return []; // no SKILL.md files found
102
+ }
103
+ throw err;
104
+ }
105
+
106
+ // Filter to skills/ and assistant/src/config/bundled-skills/ directories
107
+ skillFiles = skillFiles.filter(
108
+ (f) =>
109
+ f.startsWith("skills/") ||
110
+ f.startsWith("assistant/src/config/bundled-skills/"),
111
+ );
112
+
113
+ const violations: Violation[] = [];
114
+
115
+ for (const filePath of skillFiles) {
116
+ if (ALLOWLIST.has(filePath)) continue;
117
+
118
+ let content: string;
119
+ try {
120
+ content = execSync(`git show HEAD:${filePath}`, {
121
+ encoding: "utf-8",
122
+ cwd: repoRoot,
123
+ });
124
+ } catch {
125
+ continue;
126
+ }
127
+
128
+ const lines = content.split("\n");
129
+
130
+ // Track whether we're inside a credential_store prompt block
131
+ // (indented YAML-like content after a credential_store mention)
132
+ let inCredentialStoreBlock = false;
133
+ let blockIndent = 0;
134
+
135
+ for (let i = 0; i < lines.length; i++) {
136
+ const line = lines[i];
137
+ const lineNumber = i + 1;
138
+
139
+ // Track credential_store prompt blocks
140
+ if (/credential_store\s+prompt/i.test(line)) {
141
+ inCredentialStoreBlock = true;
142
+ blockIndent = line.search(/\S/);
143
+ continue;
144
+ }
145
+
146
+ // Exit credential_store block when indentation returns to same or lesser level
147
+ if (inCredentialStoreBlock) {
148
+ const currentIndent = line.search(/\S/);
149
+ if (
150
+ currentIndent !== -1 &&
151
+ currentIndent <= blockIndent &&
152
+ line.trim().length > 0
153
+ ) {
154
+ inCredentialStoreBlock = false;
155
+ }
156
+ }
157
+
158
+ // Skip empty lines
159
+ if (line.trim().length === 0) continue;
160
+
161
+ // Skip negation lines — these instruct NOT to do something
162
+ if (NEGATION_PATTERNS.test(line)) continue;
163
+
164
+ // Skip credential_store UI field lines (label:, description:, placeholder:)
165
+ if (inCredentialStoreBlock && CREDENTIAL_STORE_UI_FIELD.test(line))
166
+ continue;
167
+
168
+ // Strip markdown backticks before pattern matching so that
169
+ // violations like `client_secret` are caught the same as bare words.
170
+ const stripped = line.replace(/`/g, "");
171
+
172
+ // Check against violation patterns
173
+ for (const pattern of VIOLATION_PATTERNS) {
174
+ if (pattern.test(stripped)) {
175
+ violations.push({
176
+ file: filePath,
177
+ line: lineNumber,
178
+ text: line.trim(),
179
+ });
180
+ break; // one violation per line is enough
181
+ }
182
+ }
183
+ }
184
+ }
185
+
186
+ return violations;
187
+ }
188
+
189
+ describe("SKILL.md secret handling guard", () => {
190
+ test("no SKILL.md files instruct accepting secrets in chat", () => {
191
+ const violations = findViolations();
192
+
193
+ if (violations.length > 0) {
194
+ const message = [
195
+ "Found SKILL.md files that instruct accepting secrets directly in chat.",
196
+ "Secrets must always be collected via `credential_store prompt`, which",
197
+ "presents a secure native UI that keeps values out of conversation history.",
198
+ "",
199
+ "Violations:",
200
+ ...violations.map((v) => ` - ${v.file}:${v.line}: ${v.text}`),
201
+ "",
202
+ "To fix: replace chat-based secret collection with a `credential_store prompt` call.",
203
+ "See any *-setup skill (e.g. skills/slack-app-setup/SKILL.md) for the correct pattern.",
204
+ "",
205
+ "If this is a genuine exception, add the file path to the ALLOWLIST in",
206
+ "skill-secret-handling-guard.test.ts.",
207
+ ].join("\n");
208
+
209
+ expect(violations, message).toEqual([]);
210
+ }
211
+ });
212
+ });
@@ -12,10 +12,10 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
12
12
  import { uninstallSkillLocally } from "../skills/catalog-install.js";
13
13
 
14
14
  let tempDir: string;
15
- let originalBaseDataDir: string | undefined;
15
+ let originalWorkspaceDir: string | undefined;
16
16
 
17
17
  function getSkillsDir(): string {
18
- return join(tempDir, ".vellum", "workspace", "skills");
18
+ return join(tempDir, "skills");
19
19
  }
20
20
 
21
21
  function getSkillsIndexPath(): string {
@@ -38,15 +38,17 @@ beforeEach(() => {
38
38
  tmpdir(),
39
39
  `skills-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
40
40
  );
41
- mkdirSync(join(tempDir, ".vellum", "workspace", "skills"), {
42
- recursive: true,
43
- });
44
- originalBaseDataDir = process.env.BASE_DATA_DIR;
45
- process.env.BASE_DATA_DIR = tempDir;
41
+ mkdirSync(join(tempDir, "skills"), { recursive: true });
42
+ originalWorkspaceDir = process.env.VELLUM_WORKSPACE_DIR;
43
+ process.env.VELLUM_WORKSPACE_DIR = tempDir;
46
44
  });
47
45
 
48
46
  afterEach(() => {
49
- process.env.BASE_DATA_DIR = originalBaseDataDir;
47
+ if (originalWorkspaceDir === undefined) {
48
+ delete process.env.VELLUM_WORKSPACE_DIR;
49
+ } else {
50
+ process.env.VELLUM_WORKSPACE_DIR = originalWorkspaceDir;
51
+ }
50
52
  rmSync(tempDir, { recursive: true, force: true });
51
53
  });
52
54
 
@@ -16,7 +16,7 @@ const TEST_DIR = join(tmpdir(), `vellum-skills-test-${crypto.randomUUID()}`);
16
16
  const realPlatform = require("../util/platform.js");
17
17
  mock.module("../util/platform.js", () => ({
18
18
  ...realPlatform,
19
- getRootDir: () => TEST_DIR,
19
+ getProtectedDir: () => join(TEST_DIR, "protected"),
20
20
  getDataDir: () => TEST_DIR,
21
21
 
22
22
  getSandboxRootDir: () => join(TEST_DIR, "sandbox"),
@@ -60,7 +60,7 @@ mock.module("../config/loader.js", () => ({
60
60
  }));
61
61
 
62
62
  mock.module("../util/platform.js", () => ({
63
- getRootDir: () => testDir,
63
+ getProtectedDir: () => join(testDir, "protected"),
64
64
  getDataDir: () => testDir,
65
65
 
66
66
  isMacOS: () => process.platform === "darwin",
@@ -8,29 +8,12 @@
8
8
  * 4. Notify the guardian of the access attempt
9
9
  * 5. When the user replies with the code in the DM, verify and activate
10
10
  */
11
- import { mkdtempSync, rmSync } from "node:fs";
12
- import { tmpdir } from "node:os";
13
- import { join } from "node:path";
14
11
  import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
15
12
 
16
13
  // ---------------------------------------------------------------------------
17
14
  // Test isolation: in-memory SQLite via temp directory
18
15
  // ---------------------------------------------------------------------------
19
16
 
20
- const testDir = mkdtempSync(join(tmpdir(), "slack-inbound-verification-test-"));
21
-
22
- mock.module("../util/platform.js", () => ({
23
- getRootDir: () => testDir,
24
- getDataDir: () => testDir,
25
- isMacOS: () => process.platform === "darwin",
26
- isLinux: () => process.platform === "linux",
27
- isWindows: () => process.platform === "win32",
28
- getPidPath: () => join(testDir, "test.pid"),
29
- getDbPath: () => join(testDir, "test.db"),
30
- getLogPath: () => join(testDir, "test.log"),
31
- ensureDataDir: () => {},
32
- }));
33
-
34
17
  mock.module("../util/logger.js", () => ({
35
18
  getLogger: () =>
36
19
  new Proxy({} as Record<string, unknown>, {
@@ -81,11 +64,6 @@ initializeDb();
81
64
 
82
65
  afterAll(() => {
83
66
  resetDb();
84
- try {
85
- rmSync(testDir, { recursive: true });
86
- } catch {
87
- /* best effort */
88
- }
89
67
  });
90
68
 
91
69
  // ---------------------------------------------------------------------------
@@ -14,9 +14,12 @@ const TRUST_PATH = join(TEST_ROOT, "protected", "trust.json");
14
14
  // We need to mock getRootDir before importing trust-store
15
15
  import { mock } from "bun:test";
16
16
 
17
+ // Point the file-based trust backend at the test temp dir.
18
+ process.env.GATEWAY_SECURITY_DIR = join(TEST_ROOT, "protected");
19
+
17
20
  // Mock the platform module to use our test root
18
21
  mock.module("../util/platform.js", () => ({
19
- getRootDir: () => TEST_ROOT,
22
+ getProtectedDir: () => join(TEST_ROOT, "protected"),
20
23
  }));
21
24
 
22
25
  // Mock the skills config module used by defaults.ts
@@ -81,6 +81,7 @@ mock.module("../daemon/handlers/shared.js", () => ({
81
81
  textSegments: [],
82
82
  contentOrder: [],
83
83
  surfaces: [],
84
+ thinkingSegments: [],
84
85
  };
85
86
  }
86
87
  return {
@@ -90,6 +91,7 @@ mock.module("../daemon/handlers/shared.js", () => ({
90
91
  textSegments: [],
91
92
  contentOrder: [],
92
93
  surfaces: [],
94
+ thinkingSegments: [],
93
95
  };
94
96
  },
95
97
  }));
@@ -18,7 +18,7 @@ import { mock } from "bun:test";
18
18
  const realPlatform = require("../util/platform.js");
19
19
  mock.module("../util/platform.js", () => ({
20
20
  ...realPlatform,
21
- getRootDir: () => TEST_DIR,
21
+ getProtectedDir: () => join(TEST_DIR, "protected"),
22
22
  getDataDir: () => TEST_DIR,
23
23
  getWorkspaceDir: () => TEST_DIR,
24
24
  getWorkspaceConfigPath: () => join(TEST_DIR, "config.json"),
@@ -19,7 +19,7 @@ mock.module("../util/logger.js", () => ({
19
19
  const testTmpDir = mkdtempSync(join(tmpdir(), "terminal-test-"));
20
20
 
21
21
  mock.module("../util/platform.js", () => ({
22
- getRootDir: () => testTmpDir,
22
+ getProtectedDir: () => join(testTmpDir, "protected"),
23
23
  getDataDir: () => join(testTmpDir, "data"),
24
24
  getSandboxWorkingDir: () => join(testTmpDir, "sandbox"),
25
25
  isMacOS: () => process.platform === "darwin",
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Shared test preload — runs before every test file.
3
+ *
4
+ * Creates a per-file temporary directory and sets VELLUM_WORKSPACE_DIR so that
5
+ * all workspace-derived helpers (getDataDir, getDbPath, getConversationsDir, …)
6
+ * resolve under the temp dir instead of the real ~/.vellum/workspace.
7
+ *
8
+ * Individual test files can retrieve the workspace dir via getWorkspaceDir()
9
+ * from platform.ts, or directly from process.env.VELLUM_WORKSPACE_DIR.
10
+ *
11
+ * Cleanup: the temp dir is removed after all tests in the file complete.
12
+ */
13
+
14
+ import { mkdtempSync, realpathSync, rmSync } from "node:fs";
15
+ import { tmpdir } from "node:os";
16
+ import { join } from "node:path";
17
+ import { afterAll } from "bun:test";
18
+
19
+ const testDir = realpathSync(
20
+ mkdtempSync(join(tmpdir(), "vellum-test-workspace-")),
21
+ );
22
+ process.env.VELLUM_WORKSPACE_DIR = testDir;
23
+
24
+ afterAll(() => {
25
+ delete process.env.VELLUM_WORKSPACE_DIR;
26
+ try {
27
+ rmSync(testDir, { recursive: true, force: true });
28
+ } catch {
29
+ /* best-effort cleanup */
30
+ }
31
+ });
@@ -205,7 +205,7 @@ function makeSystemPrompt(size: "small" | "production" = "small"): string {
205
205
  "### OAuth Setup",
206
206
  "Most integrations use OAuth for authentication.",
207
207
  "Guide the user through the OAuth flow when setting up a new integration:",
208
- "1. Navigate to Settings > Integrations",
208
+ "1. Navigate to Settings > Models & Services",
209
209
  "2. Click 'Connect' for the desired service",
210
210
  "3. Authorize in the browser popup",
211
211
  "4. Confirm the connection is active",
@@ -76,7 +76,7 @@ mock.module("../tools/network/script-proxy/index.js", () => ({
76
76
  }));
77
77
 
78
78
  mock.module("../util/platform.js", () => ({
79
- getRootDir: () => "/tmp",
79
+ getProtectedDir: () => "/tmp/protected",
80
80
  getDataDir: () => "/tmp",
81
81
  getWorkspaceDir: () => "/tmp/workspace",
82
82
  getConversationsDir: () => "/tmp/workspace/conversations",
@@ -237,7 +237,7 @@ describe("Tool execution pipeline benchmark", () => {
237
237
 
238
238
  const p50 = percentile(timings, 50);
239
239
  expect(p50).toBeLessThan(5);
240
- expect(results[0]).toBe(RiskLevel.Medium);
240
+ expect(results[0]).toBe(RiskLevel.Low);
241
241
  });
242
242
 
243
243
  test("check: full permission check for low-risk tool", async () => {
@@ -1209,12 +1209,6 @@ describe("isSideEffectTool", () => {
1209
1209
  );
1210
1210
  });
1211
1211
 
1212
- test("credential_store oauth2_connect is a side-effect", () => {
1213
- expect(
1214
- isSideEffectTool("credential_store", { action: "oauth2_connect" }),
1215
- ).toBe(true);
1216
- });
1217
-
1218
1212
  test("credential_store list is NOT a side-effect", () => {
1219
1213
  expect(isSideEffectTool("credential_store", { action: "list" })).toBe(
1220
1214
  false,
@@ -1731,20 +1725,6 @@ describe("ToolExecutor forcePromptSideEffects enforcement", () => {
1731
1725
  expect(promptCalled).toBe(true);
1732
1726
  });
1733
1727
 
1734
- test("credential_store oauth2_connect forces prompt in private conversation", async () => {
1735
- checkResultOverride = { decision: "allow", reason: "Matched trust rule" };
1736
-
1737
- const executor = new ToolExecutor(makeTrackingPrompter());
1738
- const result = await executor.execute(
1739
- "credential_store",
1740
- { action: "oauth2_connect", provider: "google" },
1741
- makeContext({ forcePromptSideEffects: true }),
1742
- );
1743
-
1744
- expect(result.isError).toBe(false);
1745
- expect(promptCalled).toBe(true);
1746
- });
1747
-
1748
1728
  test("credential_store list does NOT force prompt in private conversation", async () => {
1749
1729
  checkResultOverride = { decision: "allow", reason: "Matched trust rule" };
1750
1730
 
@@ -0,0 +1,124 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { summarizeToolInput } from "../tools/tool-input-summary.js";
4
+
5
+ describe("summarizeToolInput", () => {
6
+ test("bash with short command returns full command", () => {
7
+ expect(summarizeToolInput("bash", { command: "ls -la" })).toBe("ls -la");
8
+ });
9
+
10
+ test("bash with long command truncates with ellipsis", () => {
11
+ const longCmd = "a".repeat(200);
12
+ const result = summarizeToolInput("bash", { command: longCmd });
13
+ expect(result.length).toBe(121); // 120 chars + ellipsis
14
+ expect(result.endsWith("…")).toBe(true);
15
+ expect(result.startsWith("a".repeat(120))).toBe(true);
16
+ });
17
+
18
+ test("terminal tool behaves like bash", () => {
19
+ expect(summarizeToolInput("terminal", { command: "echo hello" })).toBe(
20
+ "echo hello",
21
+ );
22
+ });
23
+
24
+ test("file_read returns file path", () => {
25
+ expect(
26
+ summarizeToolInput("file_read", { file_path: "/src/index.ts" }),
27
+ ).toBe("/src/index.ts");
28
+ });
29
+
30
+ test("file_write returns file path from path key", () => {
31
+ expect(summarizeToolInput("file_write", { path: "/src/main.ts" })).toBe(
32
+ "/src/main.ts",
33
+ );
34
+ });
35
+
36
+ test("file_edit prefers file_path over path", () => {
37
+ expect(
38
+ summarizeToolInput("file_edit", {
39
+ file_path: "/preferred.ts",
40
+ path: "/fallback.ts",
41
+ }),
42
+ ).toBe("/preferred.ts");
43
+ });
44
+
45
+ test("web_fetch returns URL truncated to 100 chars", () => {
46
+ const longUrl = `https://example.com/${"x".repeat(200)}`;
47
+ const result = summarizeToolInput("web_fetch", { url: longUrl });
48
+ expect(result.length).toBe(101); // 100 chars + ellipsis
49
+ expect(result.endsWith("…")).toBe(true);
50
+ });
51
+
52
+ test("web_fetch with short URL returns full URL", () => {
53
+ expect(
54
+ summarizeToolInput("web_fetch", { url: "https://example.com" }),
55
+ ).toBe("https://example.com");
56
+ });
57
+
58
+ test("network_request behaves like web_fetch", () => {
59
+ expect(
60
+ summarizeToolInput("network_request", { url: "https://api.test.com" }),
61
+ ).toBe("https://api.test.com");
62
+ });
63
+
64
+ test("empty input returns empty string", () => {
65
+ expect(summarizeToolInput("bash", {})).toBe("");
66
+ expect(summarizeToolInput("file_read", {})).toBe("");
67
+ expect(summarizeToolInput("web_fetch", {})).toBe("");
68
+ expect(summarizeToolInput("unknown_tool", {})).toBe("");
69
+ });
70
+
71
+ test("unknown tool with string input returns first string value", () => {
72
+ expect(
73
+ summarizeToolInput("custom_tool", {
74
+ query: "search for something",
75
+ count: 10,
76
+ }),
77
+ ).toBe("search for something");
78
+ });
79
+
80
+ test("unknown tool with long string truncates to 80 chars", () => {
81
+ const longVal = "b".repeat(150);
82
+ const result = summarizeToolInput("custom_tool", { data: longVal });
83
+ expect(result.length).toBe(81); // 80 chars + ellipsis
84
+ expect(result.endsWith("…")).toBe(true);
85
+ });
86
+
87
+ test("input with no string values returns empty string", () => {
88
+ expect(
89
+ summarizeToolInput("custom_tool", { count: 42, flag: true, obj: {} }),
90
+ ).toBe("");
91
+ });
92
+
93
+ test("whitespace-only string values are treated as empty", () => {
94
+ expect(summarizeToolInput("bash", { command: " " })).toBe("");
95
+ expect(summarizeToolInput("custom_tool", { data: " \n\t " })).toBe("");
96
+ });
97
+
98
+ test("host_bash behaves like bash", () => {
99
+ expect(summarizeToolInput("host_bash", { command: "git status" })).toBe(
100
+ "git status",
101
+ );
102
+ });
103
+
104
+ test("host_file_read behaves like file_read", () => {
105
+ expect(
106
+ summarizeToolInput("host_file_read", { file_path: "/src/index.ts" }),
107
+ ).toBe("/src/index.ts");
108
+ });
109
+
110
+ test("host_file_write behaves like file_write", () => {
111
+ expect(
112
+ summarizeToolInput("host_file_write", { path: "/src/main.ts" }),
113
+ ).toBe("/src/main.ts");
114
+ });
115
+
116
+ test("host_file_edit behaves like file_edit", () => {
117
+ expect(
118
+ summarizeToolInput("host_file_edit", {
119
+ file_path: "/preferred.ts",
120
+ path: "/fallback.ts",
121
+ }),
122
+ ).toBe("/preferred.ts");
123
+ });
124
+ });
@@ -8,12 +8,13 @@
8
8
  * - handleToolResult includes toolUseId in emitted tool_result
9
9
  * - Event ordering: tool_use_preview_start → input_json_delta → tool_use
10
10
  */
11
+ import { join } from "node:path";
11
12
  import { beforeEach, describe, expect, mock, test } from "bun:test";
12
13
 
13
14
  // ── Mock platform (must precede imports that read it) ─────────────────────────
14
15
  mock.module("../util/platform.js", () => ({
15
16
  getSessionTokenPath: () => "/tmp/test-token",
16
- getRootDir: () => "/tmp/test",
17
+ getProtectedDir: () => join("/tmp/test", "protected"),
17
18
  getDataDir: () => "/tmp/test",
18
19
  getWorkspaceDir: () => "/tmp/test/workspace",
19
20
  getWorkspaceSkillsDir: () => "/tmp/test/skills",
@@ -12,9 +12,15 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
12
12
  // Create a temp directory for the trust file
13
13
  const testDir = mkdtempSync(join(tmpdir(), "trust-store-test-"));
14
14
 
15
+ // Point the file-based trust backend at the test temp dir so
16
+ // getGatewaySecurityDir() (which checks this env var first) writes
17
+ // trust.json under the test directory instead of ~/.vellum/protected.
18
+ process.env.GATEWAY_SECURITY_DIR = join(testDir, "protected");
19
+
15
20
  // Mock platform module so trust-store writes to temp dir instead of ~/.vellum
16
21
  mock.module("../util/platform.js", () => ({
17
- getRootDir: () => testDir,
22
+ getProtectedDir: () => join(testDir, "protected"),
23
+ getWorkspaceDir: () => join(testDir, "workspace"),
18
24
  getDataDir: () => testDir,
19
25
  isMacOS: () => process.platform === "darwin",
20
26
  isLinux: () => process.platform === "linux",
@@ -30,7 +30,7 @@ const testDir = mkdtempSync(join(tmpdir(), "tc-inline-approval-integration-"));
30
30
 
31
31
  mock.module("../util/platform.js", () => ({
32
32
  getDataDir: () => testDir,
33
- getRootDir: () => testDir,
33
+ getProtectedDir: () => join(testDir, "protected"),
34
34
  isMacOS: () => process.platform === "darwin",
35
35
  isLinux: () => process.platform === "linux",
36
36
  isWindows: () => process.platform === "win32",
@@ -23,7 +23,7 @@ import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
23
23
  const testDir = mkdtempSync(join(tmpdir(), "trusted-contact-lifecycle-notif-"));
24
24
 
25
25
  mock.module("../util/platform.js", () => ({
26
- getRootDir: () => testDir,
26
+ getProtectedDir: () => join(testDir, "protected"),
27
27
  getDataDir: () => testDir,
28
28
  isMacOS: () => process.platform === "darwin",
29
29
  isLinux: () => process.platform === "linux",
@@ -18,7 +18,7 @@ import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
18
18
  const testDir = mkdtempSync(join(tmpdir(), "trusted-contact-multichannel-"));
19
19
 
20
20
  mock.module("../util/platform.js", () => ({
21
- getRootDir: () => testDir,
21
+ getProtectedDir: () => join(testDir, "protected"),
22
22
  getDataDir: () => testDir,
23
23
  isMacOS: () => process.platform === "darwin",
24
24
  isLinux: () => process.platform === "linux",
@@ -21,7 +21,7 @@ import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
21
21
  const testDir = mkdtempSync(join(tmpdir(), "trusted-contact-verify-test-"));
22
22
 
23
23
  mock.module("../util/platform.js", () => ({
24
- getRootDir: () => testDir,
24
+ getProtectedDir: () => join(testDir, "protected"),
25
25
  getDataDir: () => testDir,
26
26
  isMacOS: () => process.platform === "darwin",
27
27
  isLinux: () => process.platform === "linux",
@@ -10,7 +10,7 @@ const workspaceDir = join(testDir, ".vellum", "workspace");
10
10
  const conversationsDir = join(workspaceDir, "conversations");
11
11
 
12
12
  mock.module("../util/platform.js", () => ({
13
- getRootDir: () => join(testDir, ".vellum"),
13
+ getProtectedDir: () => join(join(testDir, ".vellum"), "protected"),
14
14
  getDataDir: () => join(workspaceDir, "data"),
15
15
  getWorkspaceDir: () => workspaceDir,
16
16
  getConversationsDir: () => conversationsDir,