@vellumai/assistant 0.10.2-dev.202606250318.5e7cfb0 → 0.10.2

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 (430) hide show
  1. package/bun.lock +0 -20
  2. package/docs/workspace-tools.md +33 -42
  3. package/eslint-rules/cli-no-daemon-internals.js +0 -6
  4. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +0 -31
  5. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +0 -44
  6. package/node_modules/@vellumai/gateway-client/src/index.ts +0 -14
  7. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +0 -17
  8. package/node_modules/@vellumai/service-contracts/package.json +0 -1
  9. package/node_modules/@vellumai/service-contracts/src/index.ts +0 -1
  10. package/openapi.yaml +0 -155
  11. package/package.json +1 -4
  12. package/scripts/test.sh +15 -36
  13. package/src/__tests__/actor-token-service.test.ts +14 -36
  14. package/src/__tests__/agent-loop-override-profile.test.ts +0 -1
  15. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -2
  16. package/src/__tests__/agent-wake-override-profile.test.ts +0 -2
  17. package/src/__tests__/annotate-activity-metadata.test.ts +0 -2
  18. package/src/__tests__/annotate-risk-options.test.ts +0 -2
  19. package/src/__tests__/approval-cascade.test.ts +0 -2
  20. package/src/__tests__/assistant-attachments.test.ts +0 -42
  21. package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
  22. package/src/__tests__/btw-routes.test.ts +0 -2
  23. package/src/__tests__/build-persisted-content.test.ts +0 -2
  24. package/src/__tests__/call-controller.test.ts +0 -19
  25. package/src/__tests__/channel-guardian.test.ts +58 -94
  26. package/src/__tests__/channel-reply-delivery.test.ts +0 -2
  27. package/src/__tests__/compaction-events.test.ts +0 -2
  28. package/src/__tests__/compaction.benchmark.test.ts +0 -2
  29. package/src/__tests__/compactor-call-site-logging.test.ts +0 -2
  30. package/src/__tests__/compactor-low-watermark-cut.test.ts +0 -2
  31. package/src/__tests__/compactor-preserved-tail-count.test.ts +0 -2
  32. package/src/__tests__/compactor-summary-call-truncation.test.ts +0 -2
  33. package/src/__tests__/compactor-web-search-strip.test.ts +0 -2
  34. package/src/__tests__/config-loader-backfill.test.ts +10 -123
  35. package/src/__tests__/config-schema.test.ts +0 -1
  36. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +29 -31
  37. package/src/__tests__/contacts-relay-reads.test.ts +15 -13
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -2
  39. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +0 -2
  40. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +0 -2
  41. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
  42. package/src/__tests__/conversation-agent-loop.test.ts +0 -134
  43. package/src/__tests__/conversation-analysis-routes.test.ts +0 -2
  44. package/src/__tests__/conversation-app-control-lifecycle.test.ts +0 -2
  45. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -2
  46. package/src/__tests__/conversation-history-web-search.test.ts +0 -2
  47. package/src/__tests__/conversation-load-history-repair.test.ts +0 -2
  48. package/src/__tests__/conversation-load-history-stripped.test.ts +0 -2
  49. package/src/__tests__/conversation-pairing.test.ts +0 -2
  50. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +0 -2
  51. package/src/__tests__/conversation-process-callsite.test.ts +0 -2
  52. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -2
  53. package/src/__tests__/conversation-queue.test.ts +0 -91
  54. package/src/__tests__/conversation-routes-guardian-reply.test.ts +0 -14
  55. package/src/__tests__/conversation-routes-slash-commands.test.ts +0 -14
  56. package/src/__tests__/conversation-slash-queue.test.ts +0 -2
  57. package/src/__tests__/conversation-slash-unknown.test.ts +0 -2
  58. package/src/__tests__/conversation-speed-override.test.ts +0 -2
  59. package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -29
  60. package/src/__tests__/conversation-title-service.test.ts +0 -2
  61. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -47
  62. package/src/__tests__/conversation-usage.test.ts +0 -2
  63. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -2
  64. package/src/__tests__/conversation-workspace-injection.test.ts +0 -2
  65. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -2
  66. package/src/__tests__/credential-security-invariants.test.ts +1 -1
  67. package/src/__tests__/db-migration-rollback.test.ts +171 -205
  68. package/src/__tests__/db-test-helpers.ts +4 -5
  69. package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -4
  70. package/src/__tests__/disk-pressure-guard.test.ts +0 -41
  71. package/src/__tests__/dm-persistence.test.ts +0 -2
  72. package/src/__tests__/emit-signal-routing-intent.test.ts +5 -10
  73. package/src/__tests__/events-dev-bypass-actor.test.ts +1 -7
  74. package/src/__tests__/exploration-drift-hook.test.ts +2 -3
  75. package/src/__tests__/filing-service.test.ts +0 -2
  76. package/src/__tests__/guardian-binding-drift-heal.test.ts +10 -75
  77. package/src/__tests__/guardian-dispatch.test.ts +1 -95
  78. package/src/__tests__/guardian-outbound-http.test.ts +0 -13
  79. package/src/__tests__/heartbeat-disk-pressure.test.ts +0 -2
  80. package/src/__tests__/heartbeat-service.test.ts +0 -2
  81. package/src/__tests__/helpers/channel-test-adapter.ts +7 -1
  82. package/src/__tests__/host-app-control-routes.test.ts +30 -24
  83. package/src/__tests__/host-bash-routes.test.ts +41 -31
  84. package/src/__tests__/host-browser-routes.test.ts +32 -26
  85. package/src/__tests__/host-cu-routes-targeted.test.ts +33 -25
  86. package/src/__tests__/host-file-routes-targeted.test.ts +52 -40
  87. package/src/__tests__/host-transfer-routes-targeted.test.ts +43 -31
  88. package/src/__tests__/http-user-message-parity.test.ts +8 -290
  89. package/src/__tests__/inbound-invite-redemption.test.ts +0 -28
  90. package/src/__tests__/inbound-slack-persistence.test.ts +0 -2
  91. package/src/__tests__/invite-redemption-service.test.ts +0 -198
  92. package/src/__tests__/llm-context-normalization.test.ts +0 -105
  93. package/src/__tests__/llm-request-log-error-payload.test.ts +9 -71
  94. package/src/__tests__/llm-usage-store.test.ts +0 -25
  95. package/src/__tests__/mcp-health-check.test.ts +1 -2
  96. package/src/__tests__/media-stream-server-integration.test.ts +0 -127
  97. package/src/__tests__/memory-retrieval-hook.test.ts +0 -2
  98. package/src/__tests__/messaging-send-tool.test.ts +0 -2
  99. package/src/__tests__/migration-import-from-url.test.ts +2 -2
  100. package/src/__tests__/mtime-cache.test.ts +5 -146
  101. package/src/__tests__/native-web-search.test.ts +0 -2
  102. package/src/__tests__/non-member-access-request.test.ts +17 -189
  103. package/src/__tests__/notification-broadcaster.test.ts +0 -4
  104. package/src/__tests__/notification-decision-recipient-context.test.ts +32 -33
  105. package/src/__tests__/notification-deep-link.test.ts +0 -6
  106. package/src/__tests__/notification-guardian-path.test.ts +0 -19
  107. package/src/__tests__/openai-provider.test.ts +12 -22
  108. package/src/__tests__/openai-responses-provider.test.ts +2 -12
  109. package/src/__tests__/outbound-slack-persistence.test.ts +0 -2
  110. package/src/__tests__/pending-interactions-resolved-event.test.ts +4 -7
  111. package/src/__tests__/persistence-secret-redaction.test.ts +0 -2
  112. package/src/__tests__/plugin-bootstrap.test.ts +73 -3
  113. package/src/__tests__/plugin-route-contribution.test.ts +17 -4
  114. package/src/__tests__/plugin-tool-contribution.test.ts +18 -3
  115. package/src/__tests__/plugin-types.test.ts +2 -0
  116. package/src/__tests__/process-message-background-slack.test.ts +0 -2
  117. package/src/__tests__/process-message-display-content.test.ts +0 -2
  118. package/src/__tests__/provider-error-scenarios.test.ts +4 -5
  119. package/src/__tests__/provider-usage-tracking.test.ts +0 -39
  120. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +0 -2
  121. package/src/__tests__/registry.test.ts +1 -4
  122. package/src/__tests__/relay-server.test.ts +25 -694
  123. package/src/__tests__/runtime-attachment-metadata.test.ts +1 -0
  124. package/src/__tests__/secret-ingress-http.test.ts +0 -14
  125. package/src/__tests__/send-endpoint-busy.test.ts +8 -30
  126. package/src/__tests__/skills.test.ts +0 -44
  127. package/src/__tests__/slack-inbound-verification.test.ts +2 -47
  128. package/src/__tests__/stt-hints.test.ts +13 -44
  129. package/src/__tests__/subagent-detail.test.ts +0 -27
  130. package/src/__tests__/subagent-disposal.test.ts +0 -65
  131. package/src/__tests__/subagent-notify-parent.test.ts +0 -2
  132. package/src/__tests__/subagent-role-registry.test.ts +2 -7
  133. package/src/__tests__/subagent-spawn-tool-fork.test.ts +0 -2
  134. package/src/__tests__/subagent-tools.test.ts +0 -2
  135. package/src/__tests__/suggestion-routes.test.ts +0 -2
  136. package/src/__tests__/title-generate-hook.test.ts +0 -2
  137. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -2
  138. package/src/__tests__/tool-executor.test.ts +11 -16
  139. package/src/__tests__/tool-preview-lifecycle.test.ts +0 -2
  140. package/src/__tests__/tool-result-metadata-plumbing.test.ts +0 -2
  141. package/src/__tests__/tool-start-timestamp.test.ts +0 -2
  142. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +10 -10
  143. package/src/__tests__/twilio-routes.test.ts +0 -96
  144. package/src/__tests__/ui-file-upload-surface.test.ts +0 -86
  145. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  146. package/src/__tests__/voice-invite-redemption.test.ts +0 -33
  147. package/src/__tests__/web-search-backend-failure.test.ts +0 -2
  148. package/src/__tests__/workspace-migration-remove-hooks.test.ts +35 -14
  149. package/src/__tests__/workspace-tool-loader.test.ts +2 -195
  150. package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
  151. package/src/agent/loop.ts +0 -56
  152. package/src/api/index.ts +1 -19
  153. package/src/api/responses/llm-request-log-entry.ts +0 -29
  154. package/src/api/responses/subagent-detail.ts +0 -17
  155. package/src/api/surfaces.ts +3 -39
  156. package/src/approvals/guardian-request-resolvers.ts +11 -1
  157. package/src/calls/__tests__/relay-setup-router.test.ts +4 -262
  158. package/src/calls/call-domain.ts +3 -3
  159. package/src/calls/guardian-dispatch.ts +8 -10
  160. package/src/calls/inbound-trust-reader.ts +1 -17
  161. package/src/calls/media-stream-server.ts +0 -21
  162. package/src/calls/relay-server.ts +50 -167
  163. package/src/calls/relay-setup-router.ts +7 -37
  164. package/src/calls/relay-verification.ts +4 -4
  165. package/src/calls/stt-hints.ts +12 -9
  166. package/src/calls/twilio-routes.ts +4 -14
  167. package/src/channels/types.ts +20 -10
  168. package/src/cli/commands/__tests__/cache.test.ts +1 -8
  169. package/src/cli/commands/cache.ts +181 -194
  170. package/src/cli/commands/db/__tests__/repair.test.ts +5 -6
  171. package/src/cli/commands/db/status.ts +1 -37
  172. package/src/cli/commands/mcp.ts +218 -252
  173. package/src/cli/commands/memory/index.ts +0 -2
  174. package/src/cli/commands/plugins.ts +3 -75
  175. package/src/cli/lib/__tests__/install-from-github.test.ts +0 -102
  176. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +1 -160
  177. package/src/cli/lib/list-installed-plugins.ts +1 -179
  178. package/src/config/__tests__/sync-gated-profiles.test.ts +3 -11
  179. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +17 -27
  180. package/src/config/bundled-skills/contacts/tools/contact-search.ts +3 -13
  181. package/src/config/bundled-skills/subagent/SKILL.md +1 -1
  182. package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
  183. package/src/config/feature-flag-registry.json +13 -5
  184. package/src/config/loader.ts +5 -38
  185. package/src/config/schemas/__tests__/memory-v3.test.ts +0 -1
  186. package/src/config/schemas/memory-lifecycle.ts +0 -12
  187. package/src/config/schemas/memory-v3.ts +0 -7
  188. package/src/config/schemas/memory.ts +0 -4
  189. package/src/config/schemas/timeouts.ts +0 -8
  190. package/src/config/seed-inference-profiles.ts +11 -21
  191. package/src/config/skills.ts +5 -27
  192. package/src/config/sync-gated-profiles.ts +13 -12
  193. package/src/contacts/contacts-write.ts +0 -3
  194. package/src/daemon/assistant-attachments.ts +4 -27
  195. package/src/daemon/conversation-agent-loop.ts +0 -28
  196. package/src/daemon/conversation-process.ts +16 -35
  197. package/src/daemon/conversation-surfaces.ts +38 -111
  198. package/src/daemon/conversation-tool-setup.ts +16 -50
  199. package/src/daemon/conversation.ts +1 -13
  200. package/src/daemon/disk-pressure-guard.ts +2 -12
  201. package/src/daemon/event-loop-watchdog.ts +1 -28
  202. package/src/daemon/external-plugins-bootstrap.ts +34 -4
  203. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -25
  204. package/src/daemon/handlers/config-a2a.ts +14 -6
  205. package/src/daemon/handlers/config-channels.ts +22 -78
  206. package/src/daemon/handlers/conversations.ts +0 -77
  207. package/src/daemon/lifecycle.ts +0 -4
  208. package/src/daemon/mcp-reload-service.ts +0 -10
  209. package/src/daemon/memory-v2-startup.test.ts +0 -72
  210. package/src/daemon/memory-v2-startup.ts +19 -87
  211. package/src/daemon/message-types/conversations.ts +0 -2
  212. package/src/daemon/message-types/surfaces.ts +12 -12
  213. package/src/daemon/server.ts +4 -0
  214. package/src/daemon/shutdown-handlers.ts +0 -20
  215. package/src/daemon/tool-setup-types.ts +0 -9
  216. package/src/daemon/workspace-tools-watcher.ts +328 -0
  217. package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
  218. package/src/ipc/assistant-server.ts +2 -2
  219. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
  220. package/src/mcp/client.ts +1 -15
  221. package/src/mcp/mcp-auth-orchestrator.ts +1 -6
  222. package/src/mcp/mcp-oauth-provider.ts +8 -19
  223. package/src/memory/__tests__/memory-retrospective-job.test.ts +0 -8
  224. package/src/memory/conversation-crud.ts +0 -38
  225. package/src/memory/db-connection.ts +3 -22
  226. package/src/memory/db-init.ts +502 -36
  227. package/src/memory/db-singleton.ts +4 -6
  228. package/src/memory/jobs-worker.ts +0 -58
  229. package/src/memory/llm-request-log-store.ts +1 -26
  230. package/src/memory/llm-usage-store.ts +20 -48
  231. package/src/memory/memory-retrospective-job.ts +8 -9
  232. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +56 -130
  233. package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
  234. package/src/memory/migrations/registry.ts +573 -0
  235. package/src/memory/migrations/run-migrations.ts +6 -90
  236. package/src/memory/migrations/validate-migration-state.ts +66 -101
  237. package/src/memory/schema/conversations.ts +0 -9
  238. package/src/memory/schema/infrastructure.ts +0 -20
  239. package/src/memory/v2/__tests__/cli-command-store.test.ts +0 -25
  240. package/src/memory/v2/__tests__/skill-store.test.ts +0 -80
  241. package/src/memory/v2/cli-command-store.ts +38 -75
  242. package/src/memory/v2/prompts/consolidation.ts +82 -13
  243. package/src/memory/v2/prompts/router.ts +93 -21
  244. package/src/memory/v2/skill-store.ts +31 -68
  245. package/src/notifications/__tests__/broadcaster.test.ts +8 -16
  246. package/src/notifications/__tests__/decision-engine.test.ts +9 -78
  247. package/src/notifications/broadcaster.ts +1 -8
  248. package/src/notifications/decision-engine.ts +7 -15
  249. package/src/notifications/destination-resolver.ts +24 -68
  250. package/src/notifications/emit-signal.ts +14 -39
  251. package/src/permissions/question-prompter.test.ts +1 -1
  252. package/src/permissions/question-prompter.ts +4 -7
  253. package/src/plugin-api/index.ts +6 -6
  254. package/src/plugin-api/types.ts +5 -3
  255. package/src/plugin-api/vision-support.test.ts +4 -28
  256. package/src/plugin-api/vision-support.ts +31 -66
  257. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -161
  258. package/src/plugins/defaults/advisor/consult.ts +6 -110
  259. package/src/plugins/defaults/advisor/steering.ts +2 -14
  260. package/src/plugins/defaults/advisor/tools/advisor.ts +5 -32
  261. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +1 -2
  262. package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +7 -47
  263. package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +11 -10
  264. package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +20 -12
  265. package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +11 -42
  266. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +3 -33
  267. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +4 -48
  268. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +8 -4
  269. package/src/plugins/defaults/memory-v3-shadow/injector.ts +15 -43
  270. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +2 -11
  271. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +13 -77
  272. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +11 -12
  273. package/src/plugins/mtime-cache.ts +291 -76
  274. package/src/plugins/pipeline.ts +13 -111
  275. package/src/plugins/types.ts +2 -0
  276. package/src/providers/anthropic/client.ts +0 -5
  277. package/src/providers/call-site-routing.ts +0 -4
  278. package/src/providers/model-catalog.ts +0 -16
  279. package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
  280. package/src/providers/openai/chat-completions-provider.ts +83 -37
  281. package/src/providers/openai/responses-provider.ts +46 -50
  282. package/src/providers/openrouter/client.ts +0 -5
  283. package/src/providers/provider-send-message.ts +0 -4
  284. package/src/providers/ratelimit.ts +0 -4
  285. package/src/providers/retry.ts +0 -4
  286. package/src/providers/types.ts +0 -9
  287. package/src/providers/usage-tracking.ts +0 -4
  288. package/src/runtime/__tests__/trust-verdict-consumer.test.ts +3 -335
  289. package/src/runtime/access-request-helper.ts +39 -19
  290. package/src/runtime/actor-trust-resolver.ts +2 -2
  291. package/src/runtime/assistant-event-hub.ts +1 -1
  292. package/src/runtime/assistant-stream-state.ts +2 -9
  293. package/src/runtime/auth/require-bound-guardian.ts +11 -21
  294. package/src/runtime/channel-verification-service.ts +31 -56
  295. package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
  296. package/src/runtime/guardian-vellum-migration.ts +7 -66
  297. package/src/runtime/invite-redemption-service.ts +187 -198
  298. package/src/runtime/local-actor-identity.ts +11 -76
  299. package/src/runtime/pending-interactions.ts +1 -11
  300. package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +5 -56
  301. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  302. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +0 -187
  303. package/src/runtime/routes/browser-routes.ts +1 -1
  304. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +5 -13
  305. package/src/runtime/routes/channel-verification-routes.ts +3 -3
  306. package/src/runtime/routes/contact-routes.ts +32 -8
  307. package/src/runtime/routes/conversation-cli-routes.ts +5 -4
  308. package/src/runtime/routes/conversation-list-routes.ts +7 -4
  309. package/src/runtime/routes/conversation-query-routes.ts +0 -72
  310. package/src/runtime/routes/conversation-routes.ts +85 -84
  311. package/src/runtime/routes/events-routes.ts +2 -2
  312. package/src/runtime/routes/global-search-routes.ts +1 -3
  313. package/src/runtime/routes/guardian-action-routes.ts +5 -4
  314. package/src/runtime/routes/host-app-control-routes.ts +4 -5
  315. package/src/runtime/routes/host-bash-routes.ts +4 -5
  316. package/src/runtime/routes/host-browser-routes.ts +11 -9
  317. package/src/runtime/routes/host-cu-routes.ts +4 -5
  318. package/src/runtime/routes/host-file-routes.ts +4 -5
  319. package/src/runtime/routes/host-transfer-routes.ts +6 -6
  320. package/src/runtime/routes/http-adapter.ts +1 -1
  321. package/src/runtime/routes/identity-routes.ts +2 -3
  322. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  323. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +5 -97
  324. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +49 -61
  325. package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -16
  326. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
  327. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +8 -21
  328. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +3 -14
  329. package/src/runtime/routes/index.ts +0 -2
  330. package/src/runtime/routes/llm-context-normalization.ts +0 -83
  331. package/src/runtime/routes/mcp-auth-routes.ts +19 -171
  332. package/src/runtime/routes/migration-rollback-routes.ts +3 -4
  333. package/src/runtime/routes/migration-routes.ts +1 -4
  334. package/src/runtime/routes/subagents-routes.ts +0 -5
  335. package/src/runtime/routes/surface-action-routes.ts +56 -42
  336. package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -1
  337. package/src/runtime/services/conversation-serializer.ts +9 -7
  338. package/src/runtime/tool-grant-request-helper.ts +3 -3
  339. package/src/runtime/trust-verdict-consumer.ts +9 -85
  340. package/src/runtime/verification-outbound-actions.ts +18 -18
  341. package/src/signals/user-message.ts +0 -16
  342. package/src/subagent/manager.ts +0 -9
  343. package/src/subagent/types.ts +3 -3
  344. package/src/telemetry/types.ts +1 -34
  345. package/src/telemetry/usage-telemetry-reporter.test.ts +2 -3
  346. package/src/telemetry/usage-telemetry-reporter.ts +3 -87
  347. package/src/tools/ask-question/ask-question-tool.test.ts +0 -29
  348. package/src/tools/ask-question/ask-question-tool.ts +0 -13
  349. package/src/tools/executor.ts +4 -4
  350. package/src/tools/registry.ts +0 -18
  351. package/src/tools/shared/filesystem/path-policy.ts +5 -12
  352. package/src/tools/tool-approval-handler.ts +1 -1
  353. package/src/tools/tool-defaults.ts +2 -9
  354. package/src/tools/tool-manifest.ts +0 -3
  355. package/src/tools/types.ts +2 -17
  356. package/src/tools/workspace-tools/loader.ts +244 -348
  357. package/src/util/errors.ts +1 -26
  358. package/src/util/platform.ts +0 -5
  359. package/src/workflows/library.test.ts +0 -140
  360. package/src/workflows/library.ts +28 -82
  361. package/src/workspace/migrations/017-seed-persona-dirs.ts +34 -3
  362. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +24 -3
  363. package/src/workspace/migrations/048-remove-workspace-hooks.ts +66 -14
  364. package/src/workspace/migrations/registry.ts +0 -2
  365. package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +0 -91
  366. package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +0 -48
  367. package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +0 -28
  368. package/node_modules/@vellumai/service-contracts/src/channels.ts +0 -41
  369. package/src/__tests__/code-search-tool.test.ts +0 -585
  370. package/src/__tests__/guardian-expiry-notifier.test.ts +0 -282
  371. package/src/__tests__/mcp-config-secret-boundary.test.ts +0 -390
  372. package/src/__tests__/plugin-pipeline.test.ts +0 -96
  373. package/src/__tests__/sse-actor-principal-guardian-source.test.ts +0 -102
  374. package/src/__tests__/steer-on-enqueue-question.test.ts +0 -181
  375. package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +0 -208
  376. package/src/agent/loop-exclusive-tool.test.ts +0 -150
  377. package/src/api/constants/sse-replay.ts +0 -41
  378. package/src/api/events/conversation-notice.ts +0 -26
  379. package/src/approvals/guardian-channel-delivery.ts +0 -30
  380. package/src/approvals/guardian-expiry-notifier.ts +0 -148
  381. package/src/cli/commands/memory/__tests__/worker.test.ts +0 -302
  382. package/src/cli/commands/memory/worker.ts +0 -175
  383. package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +0 -143
  384. package/src/config/prune-seeded-callsite-defaults.ts +0 -110
  385. package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +0 -129
  386. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +0 -312
  387. package/src/contacts/__tests__/member-write-relay.test.ts +0 -202
  388. package/src/contacts/guardian-delivery-reader.ts +0 -223
  389. package/src/contacts/member-write-relay.ts +0 -189
  390. package/src/daemon/conversation-notices.ts +0 -60
  391. package/src/daemon/handlers/__tests__/config-channels.test.ts +0 -225
  392. package/src/hooks/hook-loader.ts +0 -341
  393. package/src/mcp/mcp-header-store.ts +0 -134
  394. package/src/memory/__tests__/301-create-watchdog-events.test.ts +0 -110
  395. package/src/memory/__tests__/prompt-override.test.ts +0 -192
  396. package/src/memory/__tests__/watchdog-events-store.test.ts +0 -161
  397. package/src/memory/migrations/300-add-processing-started-at.ts +0 -30
  398. package/src/memory/migrations/301-create-watchdog-events.ts +0 -45
  399. package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +0 -224
  400. package/src/memory/prompt-override.ts +0 -129
  401. package/src/memory/steps.ts +0 -573
  402. package/src/memory/watchdog-events-store.ts +0 -87
  403. package/src/memory/worker-control.ts +0 -118
  404. package/src/memory/worker-process.ts +0 -72
  405. package/src/notifications/__tests__/connected-channels.test.ts +0 -114
  406. package/src/notifications/__tests__/destination-resolver.test.ts +0 -256
  407. package/src/onboarding/checkin-event.test.ts +0 -222
  408. package/src/onboarding/checkin-event.ts +0 -321
  409. package/src/onboarding/schedule-checkin.ts +0 -190
  410. package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +0 -106
  411. package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +0 -60
  412. package/src/plugins/defaults/advisor/context-pack.ts +0 -288
  413. package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +0 -146
  414. package/src/plugins/surface-import.ts +0 -121
  415. package/src/providers/openai/__tests__/api-error-normalization.test.ts +0 -321
  416. package/src/providers/openai/api-error-normalization.ts +0 -270
  417. package/src/runtime/__tests__/channel-verification-service.test.ts +0 -133
  418. package/src/runtime/__tests__/guardian-vellum-migration.test.ts +0 -181
  419. package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +0 -66
  420. package/src/runtime/__tests__/local-principal-trust.test.ts +0 -164
  421. package/src/runtime/anchored-guardian.test.ts +0 -156
  422. package/src/runtime/anchored-guardian.ts +0 -135
  423. package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +0 -99
  424. package/src/runtime/local-principal-trust.ts +0 -52
  425. package/src/runtime/routes/__tests__/contact-routes.test.ts +0 -212
  426. package/src/runtime/routes/__tests__/global-search-routes.test.ts +0 -93
  427. package/src/runtime/routes/onboarding-checkin-routes.ts +0 -86
  428. package/src/tools/filesystem/search.ts +0 -543
  429. package/src/util/telemetry-db-path.ts +0 -24
  430. package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +0 -134
@@ -102,17 +102,6 @@ export class AssistantError extends VellumError {
102
102
  export class ProviderError extends AssistantError {
103
103
  /** Delay (in ms) suggested by the server's Retry-After header, if present. */
104
104
  public readonly retryAfterMs?: number;
105
- /** Upstream provider error metadata, parsed from the raw non-2xx body. */
106
- public readonly apiErrorCode?: string;
107
- public readonly apiErrorType?: string;
108
- public readonly apiErrorParam?: string;
109
- public readonly requestId?: string;
110
- /**
111
- * Verbatim upstream non-2xx body (possibly truncated). Persisted so the
112
- * inspector's Raw tab can show the actual provider error payload, not just
113
- * the extracted fields.
114
- */
115
- public readonly rawBody?: string;
116
105
  /**
117
106
  * Tagged daemon-owned abort reason carried over from the AbortSignal that
118
107
  * triggered this error. Untyped here to avoid a daemon→util import cycle;
@@ -125,25 +114,11 @@ export class ProviderError extends AssistantError {
125
114
  message: string,
126
115
  public readonly provider: string,
127
116
  public readonly statusCode?: number,
128
- options?: {
129
- cause?: unknown;
130
- retryAfterMs?: number;
131
- abortReason?: unknown;
132
- apiErrorCode?: string;
133
- apiErrorType?: string;
134
- apiErrorParam?: string;
135
- requestId?: string;
136
- rawBody?: string;
137
- },
117
+ options?: { cause?: unknown; retryAfterMs?: number; abortReason?: unknown },
138
118
  ) {
139
119
  super(message, ErrorCode.PROVIDER_ERROR, options);
140
120
  this.name = "ProviderError";
141
121
  this.retryAfterMs = options?.retryAfterMs;
142
- this.apiErrorCode = options?.apiErrorCode;
143
- this.apiErrorType = options?.apiErrorType;
144
- this.apiErrorParam = options?.apiErrorParam;
145
- this.requestId = options?.requestId;
146
- this.rawBody = options?.rawBody;
147
122
  this.abortReason = options?.abortReason;
148
123
  }
149
124
  }
@@ -242,11 +242,6 @@ 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
-
250
245
  /**
251
246
  * Returns the workspace root for user-facing state.
252
247
  *
@@ -37,13 +37,6 @@ function writeWorkflow(file: string, source: string): void {
37
37
  writeFileSync(join(dir, file), source, "utf8");
38
38
  }
39
39
 
40
- /** Write `<workspace>/workflows/<name>/workflow.ts` (directory-style). */
41
- function writeDirWorkflow(name: string, source: string): void {
42
- const dir = join(workspaceDir, "workflows", name);
43
- mkdirSync(dir, { recursive: true });
44
- writeFileSync(join(dir, "workflow.ts"), source, "utf8");
45
- }
46
-
47
40
  describe("listWorkflows", () => {
48
41
  test("returns [] and does not create the dir when none exists", () => {
49
42
  expect(listWorkflows()).toEqual([]);
@@ -91,51 +84,6 @@ describe("listWorkflows", () => {
91
84
  expect(entries.map((e) => e.name)).toEqual(["real"]);
92
85
  });
93
86
 
94
- test("includes both flat and directory-style workflows", () => {
95
- writeWorkflow(
96
- "flat.workflow.ts",
97
- `export const meta = { name: "flat-wf", description: "flat" };\nreturn 1;`,
98
- );
99
- writeDirWorkflow(
100
- "dir-wf",
101
- `export const meta = { name: "dir-wf", description: "dir" };\nreturn 1;`,
102
- );
103
-
104
- const entries = listWorkflows().sort((a, b) =>
105
- a.name.localeCompare(b.name),
106
- );
107
- expect(entries.map((e) => e.name)).toEqual(["dir-wf", "flat-wf"]);
108
- expect(entries[0]!.path).toContain(join("dir-wf", "workflow.ts"));
109
- expect(entries[1]!.path).toContain("flat.workflow.ts");
110
- });
111
-
112
- test("ignores a subdir without a workflow.ts and does not recurse deeper", () => {
113
- writeWorkflow(
114
- "real.workflow.ts",
115
- `export const meta = { name: "real", description: "d" };\nreturn 1;`,
116
- );
117
- // Subdir with no entry-point workflow.ts.
118
- mkdirSync(join(workspaceDir, "workflows", "empty-dir"), {
119
- recursive: true,
120
- });
121
- writeFileSync(
122
- join(workspaceDir, "workflows", "empty-dir", "notes.ts"),
123
- `export const meta = { name: "ignored", description: "d" };`,
124
- "utf8",
125
- );
126
- // A nested workflow.ts one level deeper than supported must not be picked up.
127
- mkdirSync(join(workspaceDir, "workflows", "outer", "inner"), {
128
- recursive: true,
129
- });
130
- writeFileSync(
131
- join(workspaceDir, "workflows", "outer", "inner", "workflow.ts"),
132
- `export const meta = { name: "too-deep", description: "d" };`,
133
- "utf8",
134
- );
135
-
136
- expect(listWorkflows().map((e) => e.name)).toEqual(["real"]);
137
- });
138
-
139
87
  test("skips a file whose meta is computed/non-literal (cannot be statically extracted)", () => {
140
88
  writeWorkflow(
141
89
  "ok.workflow.ts",
@@ -183,92 +131,4 @@ describe("getWorkflow", () => {
183
131
  );
184
132
  expect(getWorkflow("ghost")).toBeNull();
185
133
  });
186
-
187
- test("resolves a directory-style workflow by meta.name", () => {
188
- const source = `export const meta = { name: "dir-digest", description: "d" };\nreturn agent("go");`;
189
- writeDirWorkflow("dir-digest", source);
190
-
191
- const resolved = getWorkflow("dir-digest");
192
- expect(resolved).not.toBeNull();
193
- expect(resolved!.source).toBe(source);
194
- expect(resolved!.path).toContain(join("dir-digest", "workflow.ts"));
195
- });
196
-
197
- test("resolves a directory-style workflow by directory name when meta.name differs", () => {
198
- // meta.name is "canonical" but the dir (and caller's name) is "by-dir".
199
- const source = `export const meta = { name: "canonical", description: "d" };\nreturn 1;`;
200
- writeDirWorkflow("by-dir", source);
201
-
202
- expect(getWorkflow("by-dir")!.source).toBe(source);
203
- // The canonical meta.name also resolves.
204
- expect(getWorkflow("canonical")!.source).toBe(source);
205
- });
206
-
207
- test("prefers a meta.name match over a base-name match", () => {
208
- // Asking for "wanted" should hit the file whose meta.name is "wanted",
209
- // not the file whose base name is "wanted" (whose meta.name differs).
210
- writeWorkflow(
211
- "wanted.workflow.ts",
212
- `export const meta = { name: "other", description: "by base name" };\nreturn 1;`,
213
- );
214
- const metaSource = `export const meta = { name: "wanted", description: "by meta name" };\nreturn 1;`;
215
- writeWorkflow("real.workflow.ts", metaSource);
216
-
217
- expect(getWorkflow("wanted")!.source).toBe(metaSource);
218
- });
219
- });
220
-
221
- describe("name collisions", () => {
222
- test("a directory wins over a same-base-name flat file", () => {
223
- writeWorkflow(
224
- "foo.workflow.ts",
225
- `export const meta = { name: "foo", description: "flat" };\nreturn "FLAT";`,
226
- );
227
- writeDirWorkflow(
228
- "foo",
229
- `export const meta = { name: "foo", description: "dir" };\nreturn "DIR";`,
230
- );
231
-
232
- const resolved = getWorkflow("foo")!;
233
- expect(resolved.source).toContain("DIR");
234
- expect(resolved.path.endsWith(join("foo", "workflow.ts"))).toBe(true);
235
-
236
- const foos = listWorkflows().filter((e) => e.name === "foo");
237
- expect(foos).toHaveLength(1);
238
- expect(foos[0]!.description).toBe("dir");
239
- });
240
-
241
- test("the shadowed flat file's own meta.name no longer resolves", () => {
242
- writeWorkflow(
243
- "bar.workflow.ts",
244
- `export const meta = { name: "flat-name", description: "flat" };\nreturn "FLAT";`,
245
- );
246
- writeDirWorkflow(
247
- "bar",
248
- `export const meta = { name: "dir-name", description: "dir" };\nreturn "DIR";`,
249
- );
250
-
251
- expect(getWorkflow("dir-name")!.source).toContain("DIR");
252
- expect(getWorkflow("flat-name")).toBeNull();
253
- // Base-name fallback also lands on the directory.
254
- expect(getWorkflow("bar")!.source).toContain("DIR");
255
- expect(listWorkflows().map((e) => e.name)).toEqual(["dir-name"]);
256
- });
257
-
258
- test("listWorkflows dedups a duplicate meta.name across different files", () => {
259
- writeWorkflow(
260
- "a.workflow.ts",
261
- `export const meta = { name: "dup", description: "from-a" };\nreturn "A";`,
262
- );
263
- writeDirWorkflow(
264
- "b",
265
- `export const meta = { name: "dup", description: "from-b" };\nreturn "B";`,
266
- );
267
-
268
- // One entry; the directory (yielded first) wins, consistent with getWorkflow.
269
- const dups = listWorkflows().filter((e) => e.name === "dup");
270
- expect(dups).toHaveLength(1);
271
- expect(dups[0]!.description).toBe("from-b");
272
- expect(getWorkflow("dup")!.source).toContain("B");
273
- });
274
134
  });
@@ -2,17 +2,13 @@
2
2
  * Saved-workflow LIBRARY — named workflows the assistant can invoke by name and
3
3
  * the scheduler can trigger.
4
4
  *
5
- * Saved workflows live at `<workspace>/workflows/`. The preferred layout is
6
- * directory-style `<name>/workflow.ts` (so a workflow and its state can sit in
7
- * one folder); flat `<name>.workflow.ts` is still supported but discouraged. On
8
- * a base-name collision the directory wins. Each is a normal workflow script: a
9
- * leading literal
10
- * `export const meta = { name, description }` followed by the script body. The
11
- * library only reads the STATIC `meta` (via {@link extractWorkflowMeta}, a
12
- * pure-literal extractor that never executes the untrusted source — only the
13
- * QuickJS sandbox may run it) and the raw source. Resolution to an actual run
14
- * goes through {@link executeWorkflow} / {@link WorkflowRunManager}, which run
15
- * the source in the sandbox.
5
+ * Saved workflows live at `<workspace>/workflows/*.workflow.ts`. Each file is a
6
+ * normal workflow script: a leading literal `export const meta = { name,
7
+ * description }` followed by the script body. The library only reads the STATIC
8
+ * `meta` (via {@link extractWorkflowMeta}, a pure-literal extractor that never
9
+ * executes the untrusted source — only the QuickJS sandbox may run it) and the
10
+ * raw source. Resolution to an actual run goes through {@link executeWorkflow} /
11
+ * {@link WorkflowRunManager}, which run the source in the sandbox.
16
12
  *
17
13
  * The directory is read lazily and is NOT created eagerly: if it does not exist,
18
14
  * {@link listWorkflows} returns `[]` and {@link getWorkflow} returns `null`.
@@ -23,7 +19,7 @@ import { join } from "node:path";
23
19
 
24
20
  import { getLogger } from "../util/logger.js";
25
21
  import { getWorkspaceDir } from "../util/platform.js";
26
- import { extractWorkflowMeta, type WorkflowMeta } from "./engine.js";
22
+ import { extractWorkflowMeta } from "./engine.js";
27
23
 
28
24
  const log = getLogger("workflow-library");
29
25
 
@@ -53,66 +49,31 @@ function workflowsDir(): string {
53
49
  return join(getWorkspaceDir(), "workflows");
54
50
  }
55
51
 
56
- /** Filename of the entry-point script inside a directory-style workflow. */
57
- const DIRECTORY_ENTRY = "workflow.ts";
58
-
59
52
  /** The filename (sans `.workflow.ts`) for `<workspace>/workflows/<base>.workflow.ts`. */
60
53
  function fileBaseName(file: string): string {
61
54
  return file.slice(0, -WORKFLOW_SUFFIX.length);
62
55
  }
63
56
 
64
- /** A saved workflow read off disk, before meta extraction. */
57
+ /** A `*.workflow.ts` file read off disk, before meta extraction. */
65
58
  interface RawWorkflowFile {
66
- /** Base identity for the filename/dir fallback match (see {@link getWorkflow}). */
67
- baseName: string;
59
+ file: string;
68
60
  path: string;
69
61
  source: string;
70
62
  }
71
63
 
72
64
  /**
73
- * Yield every readable saved workflow in a STABLE order (the raw filesystem
74
- * listing order is not guaranteed, so callers that take "the first match" stay
75
- * deterministic). Directory-style `<name>/workflow.ts` is yielded first and
76
- * WINS: a flat `<name>.workflow.ts` with the same base name is shadowed (skipped
77
- * with a warning). Yields nothing if the directory does not exist — it is never
78
- * created. An unreadable entry is skipped with a logged warning.
65
+ * Yield every readable `*.workflow.ts` file in the workflows directory (each read
66
+ * once). Returns nothing if the directory does not exist it is never created.
67
+ * An unreadable file is skipped with a logged warning.
79
68
  */
80
69
  function* readWorkflowFiles(): Generator<RawWorkflowFile> {
81
70
  const dir = workflowsDir();
82
71
  if (!existsSync(dir)) return;
83
- const entries = readdirSync(dir).sort();
84
- const seen = new Set<string>();
85
-
86
- // Directory-style first, so it wins over a same-base-name flat file.
87
- for (const entry of entries) {
88
- if (entry.endsWith(WORKFLOW_SUFFIX)) continue;
89
- const path = join(dir, entry, DIRECTORY_ENTRY);
90
- if (!existsSync(path)) continue;
91
- try {
92
- const source = readFileSync(path, "utf8");
93
- seen.add(entry);
94
- yield { baseName: entry, path, source };
95
- } catch (err) {
96
- log.warn({ err, path }, "Failed to read saved workflow; skipping");
97
- }
98
- }
99
-
100
- // Flat files second; skip any shadowed by a directory of the same base name.
101
- for (const entry of entries) {
102
- if (!entry.endsWith(WORKFLOW_SUFFIX)) continue;
103
- const baseName = fileBaseName(entry);
104
- const path = join(dir, entry);
105
- if (seen.has(baseName)) {
106
- log.warn(
107
- { path },
108
- `Flat workflow "${entry}" is shadowed by directory "${baseName}/"; skipping`,
109
- );
110
- continue;
111
- }
72
+ for (const file of readdirSync(dir)) {
73
+ if (!file.endsWith(WORKFLOW_SUFFIX)) continue;
74
+ const path = join(dir, file);
112
75
  try {
113
- const source = readFileSync(path, "utf8");
114
- seen.add(baseName);
115
- yield { baseName, path, source };
76
+ yield { file, path, source: readFileSync(path, "utf8") };
116
77
  } catch (err) {
117
78
  log.warn({ err, path }, "Failed to read saved workflow; skipping");
118
79
  }
@@ -120,57 +81,42 @@ function* readWorkflowFiles(): Generator<RawWorkflowFile> {
120
81
  }
121
82
 
122
83
  /**
123
- * List every saved workflow with a statically-extractable `meta`, deduplicated
124
- * by `meta.name` the first in {@link readWorkflowFiles}' stable order wins, a
125
- * later duplicate is skipped with a warning (matching {@link getWorkflow}'s
126
- * winner). A file whose `meta` cannot be statically extracted is skipped with a
127
- * warning rather than failing the whole listing.
84
+ * List every saved workflow with a statically-extractable `meta`. A file whose
85
+ * `meta` cannot be statically extracted (computed/missing/malformed) is SKIPPED
86
+ * with a logged warning rather than failing the whole listing.
128
87
  */
129
88
  export function listWorkflows(): SavedWorkflowEntry[] {
130
89
  const entries: SavedWorkflowEntry[] = [];
131
- const seenNames = new Set<string>();
132
90
  for (const { path, source } of readWorkflowFiles()) {
133
- let meta: WorkflowMeta;
134
91
  try {
135
- meta = extractWorkflowMeta(source);
92
+ const meta = extractWorkflowMeta(source);
93
+ entries.push({ name: meta.name, description: meta.description, path });
136
94
  } catch (err) {
137
95
  log.warn(
138
96
  { err, path },
139
97
  "Saved workflow has no statically-extractable meta; skipping",
140
98
  );
141
- continue;
142
- }
143
- if (seenNames.has(meta.name)) {
144
- log.warn(
145
- { path, name: meta.name },
146
- `Duplicate workflow name "${meta.name}"; skipping shadowed entry`,
147
- );
148
- continue;
149
99
  }
150
- seenNames.add(meta.name);
151
- entries.push({ name: meta.name, description: meta.description, path });
152
100
  }
153
101
  return entries;
154
102
  }
155
103
 
156
104
  /**
157
- * Resolve a saved workflow by name, deterministically {@link readWorkflowFiles}
158
- * yields in a stable order with directory-over-flat precedence, so "the first
159
- * match" is well-defined. Matches `meta.name` first (the canonical identity),
160
- * then falls back to the base name (directory or flat-file). Returns the source
161
- * + path, or `null` if nothing matches.
105
+ * Resolve a saved workflow by name. Matches against the workflow's `meta.name`
106
+ * first, then falls back to the filename base (`<base>.workflow.ts`). Returns the
107
+ * source + path, or `null` if no saved workflow matches.
162
108
  */
163
109
  export function getWorkflow(name: string): SavedWorkflowSource | null {
164
110
  let fileMatch: SavedWorkflowSource | null = null;
165
- for (const { baseName, path, source } of readWorkflowFiles()) {
111
+ for (const { file, path, source } of readWorkflowFiles()) {
166
112
  // Prefer a `meta.name` match — it is the canonical identity.
167
113
  try {
168
114
  if (extractWorkflowMeta(source).name === name) return { source, path };
169
115
  } catch {
170
116
  // Fall through: a file with non-extractable meta can still match by name.
171
117
  }
172
- // Remember the first base-name match as a fallback.
173
- if (fileMatch === null && baseName === name) {
118
+ // Remember the first filename-base match as a fallback.
119
+ if (fileMatch === null && fileBaseName(file) === name) {
174
120
  fileMatch = { source, path };
175
121
  }
176
122
  }
@@ -8,6 +8,11 @@ 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";
11
16
  import type { WorkspaceMigration } from "./types.js";
12
17
 
13
18
  // ── Inlined helpers ───────────────────────────────────────────────
@@ -117,9 +122,35 @@ export const seedPersonaDirsMigration: WorkspaceMigration = {
117
122
  // template from disk, since migration 031 deletes that template file.
118
123
  if (content === LEGACY_USER_MD_TEMPLATE_STRIPPED) return;
119
124
 
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");
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);
123
154
  if (!existsSync(destPath)) {
124
155
  copyFileSync(userMdPath, destPath);
125
156
  }
@@ -8,6 +8,10 @@ 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";
11
15
  import type { WorkspaceMigration } from "./types.js";
12
16
 
13
17
  export const scopeJournalToGuardianMigration: WorkspaceMigration = {
@@ -36,9 +40,26 @@ export const scopeJournalToGuardianMigration: WorkspaceMigration = {
36
40
  });
37
41
  if (mdFiles.length === 0) return;
38
42
 
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");
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);
42
63
  mkdirSync(destDir, { recursive: true });
43
64
  for (const f of mdFiles) {
44
65
  const src = join(journalDir, f);
@@ -1,29 +1,81 @@
1
1
  /**
2
- * Workspace migration 048: retained no-op.
2
+ * Workspace migration 046: Remove legacy `workspace/hooks/` directory.
3
3
  *
4
- * This migration previously deleted `<workspace>/hooks/`, treating it as dead
5
- * state. `<workspace>/hooks/` is now a supported surface: standalone hook files
6
- * placed there are loaded by the hook loader (see `src/hooks/hook-loader.ts`).
7
- * Deleting it would destroy user-authored hooks including on workspaces that
8
- * mount a preseeded `<workspace>/hooks/` before this migration first runs — so
9
- * the deletion has been removed.
4
+ * Migration 022 moved `~/.vellum/hooks/` into `$VELLUM_WORKSPACE_DIR/hooks/`.
5
+ * With the hook system entirely removed, that directory is dead state — it is
6
+ * no longer read or written by the assistant. This migration deletes the
7
+ * directory (and everything under it) so stale hook manifests, config, and
8
+ * executables do not linger in user workspaces.
10
9
  *
11
- * The migration is kept (rather than removed from the registry) so the applied
12
- * checkpoint sequence stays stable across already-migrated workspaces. It is a
13
- * no-op on every workspace, new or old.
10
+ * Idempotent: safe to re-run after interruption. A no-op when the directory
11
+ * is already absent.
14
12
  */
15
13
 
14
+ import { existsSync, readdirSync, rmSync, statSync } from "node:fs";
15
+ import { join } from "node:path";
16
+
17
+ import { getLogger } from "../../util/logger.js";
16
18
  import type { WorkspaceMigration } from "./types.js";
17
19
 
20
+ const log = getLogger("workspace-migration-048-remove-workspace-hooks");
21
+
22
+ /**
23
+ * Count files under `dir` recursively. Best-effort — returns the count we
24
+ * could successfully stat, and silently skips entries that fail (e.g. a
25
+ * symlink whose target is missing, a file removed concurrently). This is
26
+ * only used for log output, so a slightly stale count is acceptable.
27
+ */
28
+ function countFilesRecursive(dir: string): number {
29
+ let count = 0;
30
+ let entries: string[];
31
+ try {
32
+ entries = readdirSync(dir);
33
+ } catch {
34
+ return 0;
35
+ }
36
+ for (const entry of entries) {
37
+ const entryPath = join(dir, entry);
38
+ try {
39
+ const s = statSync(entryPath);
40
+ if (s.isDirectory()) {
41
+ count += countFilesRecursive(entryPath);
42
+ } else {
43
+ count += 1;
44
+ }
45
+ } catch {
46
+ // best-effort
47
+ }
48
+ }
49
+ return count;
50
+ }
51
+
18
52
  export const removeWorkspaceHooksMigration: WorkspaceMigration = {
19
53
  id: "048-remove-workspace-hooks",
20
- description: "Retained no-op (workspace/hooks is now a supported surface)",
54
+ description:
55
+ "Remove legacy workspace/hooks/ directory now that the hook system is gone",
56
+
57
+ run(workspaceDir: string): void {
58
+ const hooksDir = join(workspaceDir, "hooks");
59
+ if (!existsSync(hooksDir)) return;
21
60
 
22
- run(_workspaceDir: string): void {
23
- // No-op: `<workspace>/hooks/` is a supported surface and must be preserved.
61
+ const fileCount = countFilesRecursive(hooksDir);
62
+ try {
63
+ rmSync(hooksDir, { recursive: true, force: true });
64
+ log.info(
65
+ { path: hooksDir, fileCount },
66
+ "Removed legacy workspace hooks directory",
67
+ );
68
+ } catch (err) {
69
+ log.warn(
70
+ { err, path: hooksDir },
71
+ "Failed to remove legacy workspace hooks directory; leaving in place",
72
+ );
73
+ }
24
74
  },
25
75
 
26
76
  down(_workspaceDir: string): void {
27
- // No-op: forward-only; nothing to reverse.
77
+ // Forward-only: the hook system is gone and the directory contained no
78
+ // data the assistant still consumes. Restoring an empty directory would
79
+ // just reintroduce dead state.
28
80
  },
29
81
  };
@@ -108,7 +108,6 @@ import { dropSendDiagnosticsMigration } from "./107-drop-send-diagnostics.js";
108
108
  import { dropBalancedEconomyProfileMigration } from "./108-drop-balanced-economy-profile.js";
109
109
  import { swapQualityProfileToGlm52Migration } from "./109-swap-quality-profile-to-glm-5p2.js";
110
110
  import { flipBalancedProfileToTogetherMigration } from "./110-flip-balanced-profile-to-together.js";
111
- import { pruneSeededCallsiteDefaultsMigration } from "./111-prune-seeded-callsite-defaults.js";
112
111
  import { migrateToWorkspaceVolumeMigration } from "./migrate-to-workspace-volume.js";
113
112
  import type { WorkspaceMigration } from "./types.js";
114
113
 
@@ -228,5 +227,4 @@ export const WORKSPACE_MIGRATIONS: WorkspaceMigration[] = [
228
227
  dropBalancedEconomyProfileMigration,
229
228
  swapQualityProfileToGlm52Migration,
230
229
  flipBalancedProfileToTogetherMigration,
231
- pruneSeededCallsiteDefaultsMigration,
232
230
  ];