@vellumai/assistant 0.10.1 → 0.10.2-dev.202606241651.2d2b40d

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 (367) hide show
  1. package/docs/workspace-tools.md +42 -33
  2. package/eslint-rules/cli-no-daemon-internals.js +6 -0
  3. package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +91 -0
  4. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +31 -0
  5. package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +48 -0
  6. package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
  7. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +17 -0
  8. package/openapi.yaml +74 -1
  9. package/package.json +1 -1
  10. package/scripts/test.sh +36 -15
  11. package/src/__tests__/actor-token-service.test.ts +36 -14
  12. package/src/__tests__/agent-loop-override-profile.test.ts +1 -0
  13. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  14. package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
  15. package/src/__tests__/annotate-activity-metadata.test.ts +2 -0
  16. package/src/__tests__/annotate-risk-options.test.ts +2 -0
  17. package/src/__tests__/approval-cascade.test.ts +2 -0
  18. package/src/__tests__/background-workers-disk-pressure.test.ts +2 -0
  19. package/src/__tests__/btw-routes.test.ts +2 -0
  20. package/src/__tests__/build-persisted-content.test.ts +2 -0
  21. package/src/__tests__/call-controller.test.ts +19 -0
  22. package/src/__tests__/channel-guardian.test.ts +94 -58
  23. package/src/__tests__/channel-reply-delivery.test.ts +2 -0
  24. package/src/__tests__/compaction-events.test.ts +2 -0
  25. package/src/__tests__/compaction.benchmark.test.ts +2 -0
  26. package/src/__tests__/compactor-call-site-logging.test.ts +2 -0
  27. package/src/__tests__/compactor-low-watermark-cut.test.ts +2 -0
  28. package/src/__tests__/compactor-preserved-tail-count.test.ts +2 -0
  29. package/src/__tests__/compactor-summary-call-truncation.test.ts +2 -0
  30. package/src/__tests__/compactor-web-search-strip.test.ts +2 -0
  31. package/src/__tests__/computer-use-tools.test.ts +13 -0
  32. package/src/__tests__/config-loader-backfill.test.ts +5 -1
  33. package/src/__tests__/config-schema.test.ts +1 -0
  34. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
  35. package/src/__tests__/contacts-relay-reads.test.ts +13 -15
  36. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  37. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -0
  38. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
  39. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
  40. package/src/__tests__/conversation-agent-loop.test.ts +7 -0
  41. package/src/__tests__/conversation-analysis-routes.test.ts +2 -0
  42. package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -0
  43. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -0
  44. package/src/__tests__/conversation-history-web-search.test.ts +2 -0
  45. package/src/__tests__/conversation-load-history-repair.test.ts +2 -0
  46. package/src/__tests__/conversation-load-history-stripped.test.ts +2 -0
  47. package/src/__tests__/conversation-pairing.test.ts +2 -0
  48. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
  49. package/src/__tests__/conversation-process-callsite.test.ts +2 -0
  50. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  51. package/src/__tests__/conversation-queue.test.ts +91 -0
  52. package/src/__tests__/conversation-routes-guardian-reply.test.ts +14 -0
  53. package/src/__tests__/conversation-routes-slash-commands.test.ts +14 -0
  54. package/src/__tests__/conversation-slash-queue.test.ts +2 -0
  55. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  56. package/src/__tests__/conversation-speed-override.test.ts +2 -0
  57. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +65 -0
  58. package/src/__tests__/conversation-title-service.test.ts +2 -0
  59. package/src/__tests__/conversation-tool-setup-attribution.test.ts +47 -0
  60. package/src/__tests__/conversation-usage.test.ts +2 -0
  61. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
  62. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  63. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  64. package/src/__tests__/credential-security-invariants.test.ts +0 -1
  65. package/src/__tests__/db-migration-rollback.test.ts +205 -171
  66. package/src/__tests__/db-test-helpers.ts +5 -4
  67. package/src/__tests__/deterministic-verification-control-plane.test.ts +4 -2
  68. package/src/__tests__/disk-pressure-guard.test.ts +41 -0
  69. package/src/__tests__/dm-persistence.test.ts +2 -0
  70. package/src/__tests__/emit-signal-routing-intent.test.ts +10 -5
  71. package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
  72. package/src/__tests__/filing-service.test.ts +2 -0
  73. package/src/__tests__/guardian-binding-drift-heal.test.ts +75 -10
  74. package/src/__tests__/guardian-dispatch.test.ts +95 -1
  75. package/src/__tests__/guardian-outbound-http.test.ts +13 -0
  76. package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
  77. package/src/__tests__/heartbeat-service.test.ts +2 -0
  78. package/src/__tests__/helpers/channel-test-adapter.ts +1 -7
  79. package/src/__tests__/host-app-control-routes.test.ts +24 -30
  80. package/src/__tests__/host-bash-routes.test.ts +31 -41
  81. package/src/__tests__/host-browser-routes.test.ts +26 -32
  82. package/src/__tests__/host-cu-proxy.test.ts +299 -0
  83. package/src/__tests__/host-cu-routes-targeted.test.ts +25 -33
  84. package/src/__tests__/host-file-routes-targeted.test.ts +40 -52
  85. package/src/__tests__/host-transfer-routes-targeted.test.ts +31 -43
  86. package/src/__tests__/http-user-message-parity.test.ts +167 -8
  87. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  88. package/src/__tests__/invite-redemption-service.test.ts +43 -0
  89. package/src/__tests__/llm-context-normalization.test.ts +105 -0
  90. package/src/__tests__/llm-usage-store.test.ts +25 -0
  91. package/src/__tests__/media-stream-server-integration.test.ts +127 -0
  92. package/src/__tests__/memory-retrieval-hook.test.ts +2 -0
  93. package/src/__tests__/messaging-send-tool.test.ts +2 -0
  94. package/src/__tests__/migration-import-from-url.test.ts +2 -2
  95. package/src/__tests__/native-web-search.test.ts +2 -0
  96. package/src/__tests__/non-member-access-request.test.ts +189 -17
  97. package/src/__tests__/notification-broadcaster.test.ts +4 -0
  98. package/src/__tests__/notification-decision-recipient-context.test.ts +33 -32
  99. package/src/__tests__/notification-deep-link.test.ts +6 -0
  100. package/src/__tests__/notification-guardian-path.test.ts +19 -0
  101. package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
  102. package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
  103. package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
  104. package/src/__tests__/plugin-bootstrap.test.ts +3 -73
  105. package/src/__tests__/plugin-route-contribution.test.ts +4 -17
  106. package/src/__tests__/plugin-tool-contribution.test.ts +3 -18
  107. package/src/__tests__/plugin-types.test.ts +0 -2
  108. package/src/__tests__/process-message-background-slack.test.ts +2 -0
  109. package/src/__tests__/process-message-display-content.test.ts +2 -0
  110. package/src/__tests__/provider-usage-tracking.test.ts +39 -0
  111. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
  112. package/src/__tests__/registry.test.ts +3 -0
  113. package/src/__tests__/relay-server.test.ts +694 -25
  114. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  115. package/src/__tests__/secret-ingress-http.test.ts +14 -0
  116. package/src/__tests__/send-endpoint-busy.test.ts +30 -8
  117. package/src/__tests__/skills.test.ts +44 -0
  118. package/src/__tests__/slack-inbound-verification.test.ts +47 -2
  119. package/src/__tests__/sse-actor-principal-guardian-source.test.ts +102 -0
  120. package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
  121. package/src/__tests__/stt-hints.test.ts +44 -13
  122. package/src/__tests__/subagent-detail.test.ts +27 -0
  123. package/src/__tests__/subagent-disposal.test.ts +65 -0
  124. package/src/__tests__/subagent-notify-parent.test.ts +2 -0
  125. package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
  126. package/src/__tests__/subagent-tools.test.ts +2 -0
  127. package/src/__tests__/suggestion-routes.test.ts +2 -0
  128. package/src/__tests__/title-generate-hook.test.ts +2 -0
  129. package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -0
  130. package/src/__tests__/tool-executor.test.ts +16 -11
  131. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
  132. package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -0
  133. package/src/__tests__/tool-start-timestamp.test.ts +2 -0
  134. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +10 -10
  135. package/src/__tests__/twilio-routes.test.ts +96 -0
  136. package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
  137. package/src/__tests__/web-search-backend-failure.test.ts +2 -0
  138. package/src/__tests__/workspace-tool-loader.test.ts +195 -2
  139. package/src/agent/loop-exclusive-tool.test.ts +150 -0
  140. package/src/agent/loop.ts +56 -0
  141. package/src/api/constants/sse-replay.ts +41 -0
  142. package/src/api/index.ts +6 -0
  143. package/src/api/responses/llm-request-log-entry.ts +25 -0
  144. package/src/api/responses/subagent-detail.ts +17 -0
  145. package/src/calls/__tests__/relay-setup-router.test.ts +262 -4
  146. package/src/calls/call-domain.ts +3 -3
  147. package/src/calls/guardian-dispatch.ts +10 -8
  148. package/src/calls/inbound-trust-reader.ts +17 -1
  149. package/src/calls/media-stream-server.ts +21 -0
  150. package/src/calls/relay-server.ts +167 -50
  151. package/src/calls/relay-setup-router.ts +37 -7
  152. package/src/calls/relay-verification.ts +4 -4
  153. package/src/calls/stt-hints.ts +9 -12
  154. package/src/calls/twilio-routes.ts +14 -4
  155. package/src/cli/commands/__tests__/cache.test.ts +8 -1
  156. package/src/cli/commands/cache.ts +194 -181
  157. package/src/cli/commands/db/__tests__/repair.test.ts +6 -5
  158. package/src/cli/commands/db/status.ts +37 -1
  159. package/src/cli/commands/mcp.ts +252 -218
  160. package/src/cli/commands/memory/__tests__/worker.test.ts +302 -0
  161. package/src/cli/commands/memory/index.ts +2 -0
  162. package/src/cli/commands/memory/worker.ts +175 -0
  163. package/src/cli/commands/plugins.ts +75 -3
  164. package/src/cli/lib/__tests__/install-from-github.test.ts +102 -0
  165. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +160 -1
  166. package/src/cli/lib/list-installed-plugins.ts +179 -1
  167. package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
  168. package/src/config/bundled-skills/computer-use/TOOLS.json +6 -1
  169. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +27 -17
  170. package/src/config/bundled-skills/contacts/tools/contact-search.ts +13 -3
  171. package/src/config/feature-flag-registry.json +0 -8
  172. package/src/config/loader.ts +36 -5
  173. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  174. package/src/config/schemas/memory-lifecycle.ts +12 -0
  175. package/src/config/schemas/memory-v3.ts +7 -0
  176. package/src/config/schemas/memory.ts +4 -0
  177. package/src/config/schemas/timeouts.ts +8 -0
  178. package/src/config/seed-inference-profiles.ts +14 -5
  179. package/src/config/skills.ts +27 -5
  180. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
  181. package/src/contacts/contacts-write.ts +3 -0
  182. package/src/contacts/guardian-delivery-reader.ts +223 -0
  183. package/src/daemon/conversation-agent-loop.ts +9 -0
  184. package/src/daemon/conversation-process.ts +39 -17
  185. package/src/daemon/conversation-surfaces.ts +8 -0
  186. package/src/daemon/conversation-tool-setup.ts +49 -16
  187. package/src/daemon/conversation.ts +21 -2
  188. package/src/daemon/disk-pressure-guard.ts +12 -2
  189. package/src/daemon/event-loop-watchdog.ts +28 -1
  190. package/src/daemon/external-plugins-bootstrap.ts +4 -34
  191. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -0
  192. package/src/daemon/handlers/__tests__/config-channels.test.ts +225 -0
  193. package/src/daemon/handlers/config-a2a.ts +6 -14
  194. package/src/daemon/handlers/config-channels.ts +78 -22
  195. package/src/daemon/handlers/conversations.ts +77 -0
  196. package/src/daemon/host-cu-proxy.ts +102 -11
  197. package/src/daemon/lifecycle.ts +4 -0
  198. package/src/daemon/memory-v2-startup.test.ts +72 -0
  199. package/src/daemon/memory-v2-startup.ts +87 -19
  200. package/src/daemon/server.ts +0 -4
  201. package/src/daemon/shutdown-handlers.ts +20 -0
  202. package/src/daemon/tool-setup-types.ts +9 -0
  203. package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
  204. package/src/ipc/assistant-server.ts +2 -2
  205. package/src/memory/__tests__/301-create-watchdog-events.test.ts +110 -0
  206. package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
  207. package/src/memory/__tests__/prompt-override.test.ts +192 -0
  208. package/src/memory/__tests__/watchdog-events-store.test.ts +161 -0
  209. package/src/memory/conversation-crud.ts +38 -0
  210. package/src/memory/db-connection.ts +22 -3
  211. package/src/memory/db-init.ts +36 -502
  212. package/src/memory/db-singleton.ts +6 -4
  213. package/src/memory/jobs-worker.ts +58 -0
  214. package/src/memory/llm-usage-store.ts +48 -20
  215. package/src/memory/memory-retrospective-job.ts +9 -8
  216. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +13 -3
  217. package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -27
  218. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +130 -56
  219. package/src/memory/migrations/300-add-processing-started-at.ts +30 -0
  220. package/src/memory/migrations/301-create-watchdog-events.ts +45 -0
  221. package/src/memory/migrations/__tests__/014-backfill-inbox-thread-state.test.ts +108 -0
  222. package/src/memory/migrations/__tests__/136-drop-assistant-id-columns.test.ts +82 -0
  223. package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +224 -0
  224. package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
  225. package/src/memory/migrations/run-migrations.ts +90 -6
  226. package/src/memory/migrations/schema-introspection.ts +14 -0
  227. package/src/memory/migrations/validate-migration-state.ts +101 -66
  228. package/src/memory/prompt-override.ts +129 -0
  229. package/src/memory/schema/conversations.ts +9 -0
  230. package/src/memory/schema/infrastructure.ts +20 -0
  231. package/src/memory/steps.ts +573 -0
  232. package/src/memory/v2/__tests__/cli-command-store.test.ts +25 -0
  233. package/src/memory/v2/__tests__/skill-store.test.ts +80 -0
  234. package/src/memory/v2/cli-command-store.ts +75 -38
  235. package/src/memory/v2/prompts/consolidation.ts +13 -82
  236. package/src/memory/v2/prompts/router.ts +21 -93
  237. package/src/memory/v2/skill-store.ts +68 -31
  238. package/src/memory/watchdog-events-store.ts +87 -0
  239. package/src/memory/worker-control.ts +118 -0
  240. package/src/memory/worker-process.ts +72 -0
  241. package/src/notifications/__tests__/broadcaster.test.ts +16 -8
  242. package/src/notifications/__tests__/connected-channels.test.ts +114 -0
  243. package/src/notifications/__tests__/decision-engine.test.ts +78 -9
  244. package/src/notifications/__tests__/destination-resolver.test.ts +256 -0
  245. package/src/notifications/broadcaster.ts +8 -1
  246. package/src/notifications/decision-engine.ts +15 -7
  247. package/src/notifications/destination-resolver.ts +68 -24
  248. package/src/notifications/emit-signal.ts +39 -14
  249. package/src/onboarding/checkin-event.test.ts +220 -0
  250. package/src/onboarding/checkin-event.ts +321 -0
  251. package/src/onboarding/schedule-checkin.ts +190 -0
  252. package/src/permissions/question-prompter.test.ts +1 -1
  253. package/src/permissions/question-prompter.ts +7 -4
  254. package/src/plugin-api/index.ts +6 -6
  255. package/src/plugin-api/types.ts +3 -5
  256. package/src/plugin-api/vision-support.test.ts +28 -4
  257. package/src/plugin-api/vision-support.ts +66 -31
  258. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +161 -0
  259. package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +106 -0
  260. package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +60 -0
  261. package/src/plugins/defaults/advisor/consult.ts +110 -6
  262. package/src/plugins/defaults/advisor/context-pack.ts +288 -0
  263. package/src/plugins/defaults/advisor/steering.ts +14 -2
  264. package/src/plugins/defaults/advisor/tools/advisor.ts +32 -5
  265. package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
  266. package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +10 -11
  267. package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +12 -20
  268. package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
  269. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +11 -2
  270. package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +146 -0
  271. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +29 -1
  272. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +8 -1
  273. package/src/plugins/mtime-cache.ts +7 -2
  274. package/src/plugins/types.ts +0 -2
  275. package/src/providers/anthropic/client.ts +5 -0
  276. package/src/providers/call-site-routing.ts +4 -0
  277. package/src/providers/model-catalog.ts +16 -0
  278. package/src/providers/openai/responses-provider.ts +5 -0
  279. package/src/providers/openrouter/client.ts +5 -0
  280. package/src/providers/provider-send-message.ts +4 -0
  281. package/src/providers/ratelimit.ts +4 -0
  282. package/src/providers/retry.ts +4 -0
  283. package/src/providers/types.ts +9 -0
  284. package/src/providers/usage-tracking.ts +4 -0
  285. package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
  286. package/src/runtime/__tests__/guardian-vellum-migration.test.ts +181 -0
  287. package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
  288. package/src/runtime/__tests__/local-principal-trust.test.ts +164 -0
  289. package/src/runtime/__tests__/trust-verdict-consumer.test.ts +335 -3
  290. package/src/runtime/access-request-helper.ts +19 -39
  291. package/src/runtime/actor-trust-resolver.ts +2 -2
  292. package/src/runtime/anchored-guardian.test.ts +156 -0
  293. package/src/runtime/anchored-guardian.ts +135 -0
  294. package/src/runtime/assistant-event-hub.ts +1 -1
  295. package/src/runtime/assistant-stream-state.ts +9 -2
  296. package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +99 -0
  297. package/src/runtime/auth/require-bound-guardian.ts +21 -11
  298. package/src/runtime/channel-verification-service.ts +56 -31
  299. package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
  300. package/src/runtime/guardian-vellum-migration.ts +66 -7
  301. package/src/runtime/invite-redemption-service.ts +50 -18
  302. package/src/runtime/local-actor-identity.ts +76 -11
  303. package/src/runtime/local-principal-trust.ts +52 -0
  304. package/src/runtime/pending-interactions.ts +11 -1
  305. package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +56 -5
  306. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  307. package/src/runtime/routes/__tests__/contact-routes.test.ts +212 -0
  308. package/src/runtime/routes/__tests__/global-search-routes.test.ts +93 -0
  309. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +215 -1
  310. package/src/runtime/routes/browser-routes.ts +1 -1
  311. package/src/runtime/routes/channel-verification-routes.ts +3 -3
  312. package/src/runtime/routes/contact-routes.ts +8 -32
  313. package/src/runtime/routes/conversation-cli-routes.ts +4 -5
  314. package/src/runtime/routes/conversation-list-routes.ts +4 -7
  315. package/src/runtime/routes/conversation-routes.ts +74 -81
  316. package/src/runtime/routes/events-routes.ts +2 -2
  317. package/src/runtime/routes/global-search-routes.ts +3 -1
  318. package/src/runtime/routes/guardian-action-routes.ts +4 -5
  319. package/src/runtime/routes/host-app-control-routes.ts +5 -4
  320. package/src/runtime/routes/host-bash-routes.ts +5 -4
  321. package/src/runtime/routes/host-browser-routes.ts +9 -11
  322. package/src/runtime/routes/host-cu-routes.ts +5 -4
  323. package/src/runtime/routes/host-file-routes.ts +5 -4
  324. package/src/runtime/routes/host-transfer-routes.ts +6 -6
  325. package/src/runtime/routes/http-adapter.ts +1 -1
  326. package/src/runtime/routes/identity-routes.ts +3 -2
  327. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  328. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +97 -5
  329. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +61 -49
  330. package/src/runtime/routes/inbound-stages/background-dispatch.ts +16 -4
  331. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
  332. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +21 -8
  333. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -3
  334. package/src/runtime/routes/index.ts +2 -0
  335. package/src/runtime/routes/llm-context-normalization.ts +71 -0
  336. package/src/runtime/routes/mcp-auth-routes.ts +38 -15
  337. package/src/runtime/routes/migration-rollback-routes.ts +4 -3
  338. package/src/runtime/routes/migration-routes.ts +4 -1
  339. package/src/runtime/routes/onboarding-checkin-routes.ts +86 -0
  340. package/src/runtime/routes/subagents-routes.ts +5 -0
  341. package/src/runtime/routes/surface-action-routes.ts +51 -55
  342. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  343. package/src/runtime/services/conversation-serializer.ts +7 -9
  344. package/src/runtime/tool-grant-request-helper.ts +3 -3
  345. package/src/runtime/trust-verdict-consumer.ts +85 -9
  346. package/src/runtime/verification-outbound-actions.ts +18 -18
  347. package/src/signals/user-message.ts +16 -0
  348. package/src/subagent/manager.ts +9 -0
  349. package/src/telemetry/types.ts +34 -1
  350. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
  351. package/src/telemetry/usage-telemetry-reporter.ts +87 -3
  352. package/src/tools/ask-question/ask-question-tool.test.ts +29 -0
  353. package/src/tools/ask-question/ask-question-tool.ts +13 -0
  354. package/src/tools/computer-use/definitions.ts +8 -2
  355. package/src/tools/executor.ts +4 -4
  356. package/src/tools/registry.ts +18 -0
  357. package/src/tools/tool-approval-handler.ts +1 -1
  358. package/src/tools/tool-defaults.ts +9 -2
  359. package/src/tools/types.ts +17 -2
  360. package/src/tools/workspace-tools/loader.ts +348 -244
  361. package/src/util/platform.ts +5 -0
  362. package/src/util/telemetry-db-path.ts +24 -0
  363. package/src/workspace/migrations/017-seed-persona-dirs.ts +3 -34
  364. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +3 -24
  365. package/src/__tests__/workspace-tools-watcher-flag.test.ts +0 -70
  366. package/src/daemon/workspace-tools-watcher.ts +0 -328
  367. package/src/memory/migrations/registry.ts +0 -573
@@ -242,6 +242,11 @@ export function getEmbedWorkerPidPath(): string {
242
242
  return join(getWorkspaceDir(), "embed-worker.pid");
243
243
  }
244
244
 
245
+ /** Returns the path to the memory-worker PID file ($VELLUM_WORKSPACE_DIR/memory-worker.pid). */
246
+ export function getMemoryWorkerPidPath(): string {
247
+ return join(getWorkspaceDir(), "memory-worker.pid");
248
+ }
249
+
245
250
  /**
246
251
  * Returns the workspace root for user-facing state.
247
252
  *
@@ -0,0 +1,24 @@
1
+ import { join } from "node:path";
2
+
3
+ import { getDataDir } from "./platform.js";
4
+
5
+ /**
6
+ * Path to the dedicated SQLite file that houses telemetry event tables
7
+ * (starting with `watchdog_events`). It lives in the same `data/db` directory
8
+ * as the main DB and is opened on its own connection (see `getTelemetryDb()`
9
+ * in `memory/db-connection.ts`). Splitting telemetry tables into their own
10
+ * file keeps the main DB — and its WAL — small and lets the two files
11
+ * VACUUM/checkpoint independently, so a burst of watchdog events can no longer
12
+ * bloat the main database.
13
+ *
14
+ * Kept in its own leaf module rather than alongside `getDbPath()` in
15
+ * `platform.ts`: `platform.ts` is imported very early and widely, and adding an
16
+ * export to it that low-level consumers (e.g. `db-connection.ts`) pull in
17
+ * across the daemon's large, cyclic import graph trips a Bun link-order bug
18
+ * ("Export named 'getTelemetryDbPath' not found"). Isolating it here keeps
19
+ * `platform.ts`'s module shape stable — same reasoning as `logs-db-path.ts`
20
+ * and `memory-db-path.ts`.
21
+ */
22
+ export function getTelemetryDbPath(): string {
23
+ return join(getDataDir(), "db", "assistant-telemetry.db");
24
+ }
@@ -8,11 +8,6 @@ import {
8
8
  } from "node:fs";
9
9
  import { join } from "node:path";
10
10
 
11
- import { desc, eq } from "drizzle-orm";
12
-
13
- import { generateUserFileSlug } from "../../contacts/contact-store.js";
14
- import { getDb } from "../../memory/db-connection.js";
15
- import { contacts } from "../../memory/schema/contacts.js";
16
11
  import type { WorkspaceMigration } from "./types.js";
17
12
 
18
13
  // ── Inlined helpers ───────────────────────────────────────────────
@@ -122,35 +117,9 @@ export const seedPersonaDirsMigration: WorkspaceMigration = {
122
117
  // template from disk, since migration 031 deletes that template file.
123
118
  if (content === LEGACY_USER_MD_TEMPLATE_STRIPPED) return;
124
119
 
125
- // Determine destination filename based on guardian contact
126
- let destFilename = "guardian.md";
127
- try {
128
- const db = getDb();
129
- const guardian = db
130
- .select()
131
- .from(contacts)
132
- .where(eq(contacts.role, "guardian"))
133
- .orderBy(desc(contacts.createdAt))
134
- .limit(1)
135
- .get();
136
-
137
- if (guardian) {
138
- if (guardian.userFile) {
139
- destFilename = guardian.userFile;
140
- } else {
141
- const slug = generateUserFileSlug(guardian.displayName);
142
- db.update(contacts)
143
- .set({ userFile: slug })
144
- .where(eq(contacts.id, guardian.id))
145
- .run();
146
- destFilename = slug;
147
- }
148
- }
149
- } catch {
150
- // DB might not be initialized yet — fall back to guardian.md
151
- }
152
-
153
- const destPath = join(workspaceDir, "users", destFilename);
120
+ // Seed the canonical `users/guardian.md` the runtime persona resolver
121
+ // falls back to this name, so no assistant-DB lookup is needed.
122
+ const destPath = join(workspaceDir, "users", "guardian.md");
154
123
  if (!existsSync(destPath)) {
155
124
  copyFileSync(userMdPath, destPath);
156
125
  }
@@ -8,10 +8,6 @@ import {
8
8
  } from "node:fs";
9
9
  import { join } from "node:path";
10
10
 
11
- import { desc, eq } from "drizzle-orm";
12
-
13
- import { getDb } from "../../memory/db-connection.js";
14
- import { contacts } from "../../memory/schema/contacts.js";
15
11
  import type { WorkspaceMigration } from "./types.js";
16
12
 
17
13
  export const scopeJournalToGuardianMigration: WorkspaceMigration = {
@@ -40,26 +36,9 @@ export const scopeJournalToGuardianMigration: WorkspaceMigration = {
40
36
  });
41
37
  if (mdFiles.length === 0) return;
42
38
 
43
- // Resolve guardian user slug (same pattern as 017-seed-persona-dirs)
44
- let slug = "guardian";
45
- try {
46
- const db = getDb();
47
- const guardian = db
48
- .select()
49
- .from(contacts)
50
- .where(eq(contacts.role, "guardian"))
51
- .orderBy(desc(contacts.createdAt))
52
- .limit(1)
53
- .get();
54
- if (guardian?.userFile) {
55
- slug = guardian.userFile.replace(/\.md$/, "");
56
- }
57
- } catch {
58
- // DB not ready — use fallback "guardian"
59
- }
60
-
61
- // Create per-user directory and move files (renameSync preserves birthtimes)
62
- const destDir = join(journalDir, slug);
39
+ // Scope under the canonical `guardian` slug the runtime resolver falls
40
+ // back to it, so no assistant-DB lookup is needed.
41
+ const destDir = join(journalDir, "guardian");
63
42
  mkdirSync(destDir, { recursive: true });
64
43
  for (const f of mdFiles) {
65
44
  const src = join(journalDir, f);
@@ -1,70 +0,0 @@
1
- /**
2
- * Tests that the `workspace-tools-watcher` feature flag gates the dynamic
3
- * hot-reload watcher in `WorkspaceToolsWatcher.start()`.
4
- *
5
- * The initial disk scan (`loadWorkspaceTools()`) is unconditional and lives
6
- * elsewhere; this suite only covers whether the live `fs.watch` loop mounts.
7
- */
8
- import { mkdirSync, rmSync } from "node:fs";
9
- import { tmpdir } from "node:os";
10
- import { join } from "node:path";
11
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
12
-
13
- import { clearFeatureFlagOverridesCache } from "../config/assistant-feature-flags.js";
14
- import { WorkspaceToolsWatcher } from "../daemon/workspace-tools-watcher.js";
15
- import { setOverridesForTesting } from "./feature-flag-test-helpers.js";
16
-
17
- const FLAG = "workspace-tools-watcher";
18
- const TEST_BASE_DIR = join(
19
- tmpdir(),
20
- `vellum-workspace-tools-watcher-flag-test-${process.pid}-${Date.now()}`,
21
- );
22
- let caseCounter = 0;
23
-
24
- function freshWorkspaceWithToolsDir(): void {
25
- caseCounter += 1;
26
- const dir = join(TEST_BASE_DIR, `case-${caseCounter}`);
27
- mkdirSync(join(dir, "tools"), { recursive: true });
28
- process.env.VELLUM_WORKSPACE_DIR = dir;
29
- }
30
-
31
- beforeEach(() => {
32
- WorkspaceToolsWatcher.resetForTests();
33
- clearFeatureFlagOverridesCache();
34
- freshWorkspaceWithToolsDir();
35
- });
36
-
37
- afterEach(() => {
38
- WorkspaceToolsWatcher.resetForTests();
39
- clearFeatureFlagOverridesCache();
40
- delete process.env.VELLUM_WORKSPACE_DIR;
41
- rmSync(TEST_BASE_DIR, { recursive: true, force: true });
42
- });
43
-
44
- describe("WorkspaceToolsWatcher feature-flag gate", () => {
45
- test("does not mount the watch loop when the flag is off", () => {
46
- // GIVEN the workspace tools directory exists
47
- // AND the watcher flag is disabled
48
- setOverridesForTesting({ [FLAG]: false });
49
-
50
- // WHEN the watcher starts
51
- const watcher = WorkspaceToolsWatcher.getInstance();
52
- watcher.start();
53
-
54
- // THEN no fs.watch loop is mounted
55
- expect(watcher.isWatchingForTests()).toBe(false);
56
- });
57
-
58
- test("mounts the watch loop when the flag is on", () => {
59
- // GIVEN the workspace tools directory exists
60
- // AND the watcher flag is enabled
61
- setOverridesForTesting({ [FLAG]: true });
62
-
63
- // WHEN the watcher starts
64
- const watcher = WorkspaceToolsWatcher.getInstance();
65
- watcher.start();
66
-
67
- // THEN a live fs.watch loop is mounted
68
- expect(watcher.isWatchingForTests()).toBe(true);
69
- });
70
- });
@@ -1,328 +0,0 @@
1
- /**
2
- * Filesystem watcher for `<workspaceDir>/tools/`.
3
- *
4
- * Watches the workspace-tools directory non-recursively using fs.watch.
5
- * On any add/change/delete event, debounces per filename stem (= tool
6
- * name) and reconciles registry state against on-disk state: registers
7
- * newly added tools, re-imports changed tools (cache-busting via the
8
- * loader's per-import URL query string), unregisters deleted tools,
9
- * strips core tools when a `.removed` sentinel appears, restores them
10
- * when the sentinel disappears.
11
- *
12
- * No assistant restart is required — the file watcher closes the
13
- * "edit a file, see the change" loop the same way the apps watcher and
14
- * plugin source watcher do for their respective directories.
15
- *
16
- * ## Why per-stem reconciliation
17
- *
18
- * The watcher receives `(eventType, filename)` from fs.watch but the
19
- * eventType ("rename" vs "change") is unreliable across editors and
20
- * platforms — vim atomic-save shows as a rename of the original file
21
- * plus an add of a new file, VS Code shows as a change in place, etc.
22
- * Rather than route on eventType, we debounce per stem and re-derive
23
- * the world: "given what's on disk right now for `<stem>.*`, what
24
- * registry state should the assistant be in?"
25
- *
26
- * This is the same eventual-consistency pattern the plugin mtime cache
27
- * uses — the watcher exists to KICK the reconciler, not to be the
28
- * source of truth about what changed.
29
- *
30
- * ## Lifecycle position
31
- *
32
- * Started after the initial `loadWorkspaceTools()` scan completes
33
- * during daemon startup, gated on the `workspace-tools-watcher` feature
34
- * flag — when the flag is off the initial scan still runs but no watch
35
- * loop is mounted, so workspace tools load from disk once and live edits
36
- * need a restart. Stopped on assistant shutdown alongside the
37
- * other long-lived watchers. Stoppage during shutdown does not
38
- * unregister tools — those go away with the process; the watcher's
39
- * only job is to keep the registry fresh while the assistant is up.
40
- */
41
-
42
- import { existsSync, type FSWatcher, watch } from "node:fs";
43
-
44
- import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
45
- import { getConfig } from "../config/loader.js";
46
- import {
47
- getCoreToolOverride,
48
- getTool,
49
- getToolOwner,
50
- removeCoreToolViaWorkspace,
51
- restoreStrippedCoreTool,
52
- unregisterWorkspaceTool,
53
- } from "../tools/registry.js";
54
- import {
55
- classifyWorkspaceToolEntry,
56
- findWinningWorkspaceToolPath,
57
- loadSingleWorkspaceTool,
58
- } from "../tools/workspace-tools/loader.js";
59
- import { DebouncerMap } from "../util/debounce.js";
60
- import { attachFsWatcherErrorHandler } from "../util/fs-watcher-error.js";
61
- import { getLogger } from "../util/logger.js";
62
- import { getWorkspaceToolsDir } from "../util/platform.js";
63
-
64
- const log = getLogger("workspace-tools-watcher");
65
-
66
- /**
67
- * Gates the dynamic hot-reload path. When disabled, workspace tools still
68
- * load from disk once at daemon startup via {@link loadWorkspaceTools}; only
69
- * the live watch → re-registration loop is suppressed. Read at its point of
70
- * use in {@link WorkspaceToolsWatcher.start} so a daemon restart picks up a
71
- * changed value.
72
- */
73
- const WORKSPACE_TOOLS_WATCHER_FLAG = "workspace-tools-watcher" as const;
74
-
75
- /**
76
- * Wait this long after the last fs event for a given stem before
77
- * reconciling. Editor saves often emit a burst of 2–4 events; the
78
- * debounce collapses them into a single load.
79
- */
80
- const RECONCILE_DEBOUNCE_MS = 250;
81
-
82
- export class WorkspaceToolsWatcher {
83
- /**
84
- * Process-wide singleton. Callers reach the watcher via
85
- * {@link WorkspaceToolsWatcher.getInstance} rather than instantiating
86
- * directly so the daemon `start()`/`stop()` lifecycle and any future
87
- * "trigger a manual reconcile" code paths share one watcher across the
88
- * assistant process lifetime.
89
- */
90
- private static singleton: WorkspaceToolsWatcher | null = null;
91
-
92
- static getInstance(): WorkspaceToolsWatcher {
93
- WorkspaceToolsWatcher.singleton ??= new WorkspaceToolsWatcher();
94
- return WorkspaceToolsWatcher.singleton;
95
- }
96
-
97
- /** Test-only — drops the singleton so the next `getInstance()` rebuilds. */
98
- static resetForTests(): void {
99
- WorkspaceToolsWatcher.singleton?.stop();
100
- WorkspaceToolsWatcher.singleton = null;
101
- }
102
-
103
- /** Test-only — whether a live `fs.watch` loop is currently mounted. */
104
- isWatchingForTests(): boolean {
105
- return this.watcher !== null;
106
- }
107
-
108
- private watcher: FSWatcher | null = null;
109
- private debouncer = new DebouncerMap({
110
- defaultDelayMs: RECONCILE_DEBOUNCE_MS,
111
- maxEntries: 100,
112
- });
113
- /**
114
- * Promise queue per stem — guarantees that two events for the same
115
- * stem can't run concurrently and corrupt the unregister/load
116
- * sequence. The queue is single-deep (the chained promise simply
117
- * awaits the in-flight one before running its own work), so the
118
- * debouncer's collapsing already does most of the deduplication; this
119
- * queue exists for the case where a second event lands during the
120
- * in-flight reconcile's `await loadSingleWorkspaceTool`.
121
- */
122
- private inflight = new Map<string, Promise<void>>();
123
-
124
- start(): void {
125
- if (this.watcher) return;
126
- if (
127
- !isAssistantFeatureFlagEnabled(WORKSPACE_TOOLS_WATCHER_FLAG, getConfig())
128
- ) {
129
- log.debug(
130
- "Workspace tools watcher disabled by feature flag; workspace tools load from disk at startup only (restart required to pick up edits)",
131
- );
132
- return;
133
- }
134
- const toolsDir = getWorkspaceToolsDir();
135
- if (!existsSync(toolsDir)) {
136
- log.debug(
137
- { toolsDir },
138
- "Workspace tools directory does not exist; watcher not started (will not auto-start on directory creation — restart required)",
139
- );
140
- return;
141
- }
142
- try {
143
- this.watcher = watch(
144
- toolsDir,
145
- { recursive: false },
146
- (_eventType, filename) => {
147
- if (!filename) return;
148
- const classified = classifyWorkspaceToolEntry(filename);
149
- if (!classified) {
150
- // Not a workspace-tool file — ignore (README.md, .DS_Store, etc.)
151
- return;
152
- }
153
- this.debouncer.schedule(`stem:${classified.stem}`, () => {
154
- this.scheduleReconcile(classified.stem);
155
- });
156
- },
157
- );
158
- // Async FSWatcher errors (e.g. ENOSPC, ENXIO) arrive as an 'error' event;
159
- // without a listener they crash the daemon. Degrade to a dead watcher.
160
- attachFsWatcherErrorHandler(this.watcher, log, toolsDir);
161
- log.info({ toolsDir }, "Workspace tools watcher started");
162
- } catch (err) {
163
- log.warn(
164
- { err, toolsDir },
165
- "Failed to start workspace tools watcher — workspace tools will only register at startup",
166
- );
167
- }
168
- }
169
-
170
- stop(): void {
171
- this.debouncer.cancelAll();
172
- if (this.watcher) {
173
- this.watcher.close();
174
- this.watcher = null;
175
- }
176
- }
177
-
178
- /**
179
- * Chain `reconcileStem(stem)` after any in-flight reconcile for the
180
- * same stem so we never run two `loadSingleWorkspaceTool` calls
181
- * concurrently for the same name.
182
- */
183
- private scheduleReconcile(stem: string): void {
184
- const prev = this.inflight.get(stem) ?? Promise.resolve();
185
- const next = prev
186
- .catch(() => {
187
- /* swallow — error already logged in the prior tick */
188
- })
189
- .then(() => this.reconcileStem(stem))
190
- .catch((err) => {
191
- const message = err instanceof Error ? err.message : String(err);
192
- log.error(
193
- { err, stem },
194
- `workspace-tools-watcher: reconcile for "${stem}" threw: ${message}`,
195
- );
196
- })
197
- .finally(() => {
198
- // Only clear if we're still the current in-flight promise.
199
- if (this.inflight.get(stem) === next) {
200
- this.inflight.delete(stem);
201
- }
202
- });
203
- this.inflight.set(stem, next);
204
- }
205
-
206
- /**
207
- * Reconcile a single stem (= tool name) with on-disk state.
208
- *
209
- * Possible (live, removed) tuples and their resolution:
210
- *
211
- * - `(present, absent)` → ensure workspace tool is registered using
212
- * the winning live file; re-import if a previous registration
213
- * pointed at a different path
214
- * - `(absent, present)` → ensure core tool is stripped (and any
215
- * previous workspace registration torn down first)
216
- * - `(absent, absent)` → ensure neither stripped state nor live
217
- * registration remains
218
- * - `(present, present)` → ambiguous; tear down both so neither
219
- * state survives (matches the initial-scan contract)
220
- */
221
- private async reconcileStem(stem: string): Promise<void> {
222
- const toolsDir = getWorkspaceToolsDir();
223
- if (!existsSync(toolsDir)) {
224
- this.teardownStem(stem);
225
- return;
226
- }
227
-
228
- const { livePath, hasRemovedSentinel } = findWinningWorkspaceToolPath(
229
- toolsDir,
230
- stem,
231
- );
232
-
233
- // Ambiguous: tear down everything for this stem.
234
- if (hasRemovedSentinel && livePath !== null) {
235
- log.error(
236
- { stem, toolsDir, livePath },
237
- `workspace-tools-watcher: "${stem}" has both a live file and a .removed sentinel — tearing down both`,
238
- );
239
- this.teardownStem(stem);
240
- return;
241
- }
242
-
243
- // Neither: tear down anything we own.
244
- if (!hasRemovedSentinel && livePath === null) {
245
- this.teardownStem(stem);
246
- return;
247
- }
248
-
249
- if (hasRemovedSentinel) {
250
- // Strip the core tool. Unregister any prior workspace registration
251
- // first so removeCoreToolViaWorkspace doesn't throw on the
252
- // workspace-owned-name check.
253
- if (getToolOwner(stem)?.kind === "workspace") {
254
- unregisterWorkspaceTool(stem);
255
- }
256
- try {
257
- removeCoreToolViaWorkspace(stem);
258
- } catch (err) {
259
- const message = err instanceof Error ? err.message : String(err);
260
- log.error(
261
- { err, stem },
262
- `workspace-tools-watcher: failed to strip "${stem}": ${message}`,
263
- );
264
- }
265
- return;
266
- }
267
-
268
- if (livePath !== null) {
269
- // If we previously stripped this core tool, restore it before the
270
- // workspace registration runs so the override path sees the
271
- // expected baseline (core tool present → stash + replace).
272
- if (getCoreToolOverride(stem) && !getTool(stem)) {
273
- restoreStrippedCoreTool(stem);
274
- }
275
-
276
- // If a workspace tool is already registered under this name,
277
- // unregister it so the loader can re-import cleanly. This covers
278
- // file-change events (same path, new contents) as well as
279
- // extension-precedence flips (e.g. user added foo.js next to
280
- // foo.ts — now .js wins and the registration must update).
281
- if (getToolOwner(stem)?.kind === "workspace") {
282
- unregisterWorkspaceTool(stem);
283
- }
284
-
285
- const registered = await loadSingleWorkspaceTool(livePath);
286
- if (registered) {
287
- log.info(
288
- { stem, livePath },
289
- `Workspace tool "${stem}" registered via watcher`,
290
- );
291
- }
292
- // loadSingleWorkspaceTool already logs failures with attribution.
293
- }
294
- }
295
-
296
- /**
297
- * Tear down any workspace-tool state we own for `stem`: unregister a
298
- * live workspace tool, restore a core tool we stripped. Both no-ops
299
- * when there's nothing to undo.
300
- */
301
- private teardownStem(stem: string): void {
302
- if (getToolOwner(stem)?.kind === "workspace") {
303
- unregisterWorkspaceTool(stem);
304
- log.info(
305
- { stem },
306
- `Workspace tool "${stem}" unregistered via watcher (file removed)`,
307
- );
308
- }
309
- if (getCoreToolOverride(stem) && !getTool(stem)) {
310
- restoreStrippedCoreTool(stem);
311
- }
312
- }
313
-
314
- // ── Test affordances ────────────────────────────────────────────────
315
- //
316
- // These are intentionally narrow — exposed for the watcher tests so
317
- // they can drive reconcile() deterministically without waiting on the
318
- // debouncer, and inspect whether an in-flight promise is settled.
319
- // Not part of the public lifecycle API.
320
-
321
- /**
322
- * Run a reconcile for `stem` synchronously (well, asynchronously, but
323
- * without going through the debouncer). For tests.
324
- */
325
- async _testReconcile(stem: string): Promise<void> {
326
- await this.reconcileStem(stem);
327
- }
328
- }