@vellumai/assistant 0.8.4 → 0.8.5

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 (438) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/docs/browser-use-architecture-phase2.md +1 -1
  3. package/knip.json +2 -1
  4. package/openapi.yaml +809 -11
  5. package/package.json +1 -1
  6. package/src/__tests__/anthropic-provider.test.ts +34 -37
  7. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
  8. package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
  9. package/src/__tests__/audit-log-rotation.test.ts +70 -16
  10. package/src/__tests__/background-workers-disk-pressure.test.ts +3 -3
  11. package/src/__tests__/btw-routes.test.ts +2 -3
  12. package/src/__tests__/call-controller.test.ts +0 -1
  13. package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
  14. package/src/__tests__/channel-guardian.test.ts +3 -3
  15. package/src/__tests__/checker.test.ts +6 -15
  16. package/src/__tests__/compaction-events.test.ts +1 -0
  17. package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
  18. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
  19. package/src/__tests__/computer-use-tools.test.ts +2 -4
  20. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  21. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
  22. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
  23. package/src/__tests__/conversation-agent-loop-overflow.test.ts +197 -2
  24. package/src/__tests__/conversation-agent-loop.test.ts +163 -122
  25. package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
  26. package/src/__tests__/conversation-clear-safety.test.ts +25 -25
  27. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
  28. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  29. package/src/__tests__/conversation-error.test.ts +31 -0
  30. package/src/__tests__/conversation-fork-crud.test.ts +178 -15
  31. package/src/__tests__/conversation-lifecycle.test.ts +52 -11
  32. package/src/__tests__/{conversation-load-cleaned-at.test.ts → conversation-load-history-stripped.test.ts} +13 -13
  33. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -0
  34. package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
  35. package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
  36. package/src/__tests__/conversation-skill-tools.test.ts +2 -5
  37. package/src/__tests__/conversation-store.test.ts +1 -1
  38. package/src/__tests__/conversation-sync-tags.test.ts +99 -32
  39. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
  40. package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
  41. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  42. package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
  43. package/src/__tests__/credential-execution-tools.test.ts +6 -6
  44. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  45. package/src/__tests__/credential-vault-unit.test.ts +2 -2
  46. package/src/__tests__/dynamic-page-surface.test.ts +2 -2
  47. package/src/__tests__/email-html-renderer.test.ts +12 -0
  48. package/src/__tests__/gateway-flag-listener.test.ts +237 -0
  49. package/src/__tests__/gemini-provider.test.ts +78 -0
  50. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  51. package/src/__tests__/guardian-outbound-http.test.ts +7 -5
  52. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  53. package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
  54. package/src/__tests__/heartbeat-service.test.ts +4 -0
  55. package/src/__tests__/host-shell-tool.test.ts +1 -1
  56. package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
  57. package/src/__tests__/list-messages-tool-merge.test.ts +70 -11
  58. package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
  59. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
  60. package/src/__tests__/llm-resolver.test.ts +77 -9
  61. package/src/__tests__/llm-usage-store.test.ts +66 -0
  62. package/src/__tests__/logger.test.ts +89 -0
  63. package/src/__tests__/mcp-abort-signal.test.ts +2 -2
  64. package/src/__tests__/media-generate-image.test.ts +31 -0
  65. package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
  66. package/src/__tests__/model-intents.test.ts +2 -4
  67. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  68. package/src/__tests__/onboarding-template-contract.test.ts +1 -1
  69. package/src/__tests__/openai-provider.test.ts +46 -0
  70. package/src/__tests__/openai-responses-provider.test.ts +114 -12
  71. package/src/__tests__/pending-interactions-resolved-event.test.ts +0 -1
  72. package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
  73. package/src/__tests__/platform.test.ts +2 -2
  74. package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
  75. package/src/__tests__/plugin-bootstrap.test.ts +2 -2
  76. package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
  77. package/src/__tests__/plugin-types.test.ts +3 -2
  78. package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
  79. package/src/__tests__/pricing.test.ts +12 -0
  80. package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
  81. package/src/__tests__/registry.test.ts +2 -8
  82. package/src/__tests__/require-fresh-approval.test.ts +2 -2
  83. package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
  84. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
  85. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  86. package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
  87. package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
  88. package/src/__tests__/skill-tool-factory.test.ts +1 -1
  89. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  90. package/src/__tests__/suggestion-routes.test.ts +1 -0
  91. package/src/__tests__/sync-message-contract.test.ts +59 -0
  92. package/src/__tests__/system-prompt.test.ts +145 -131
  93. package/src/__tests__/terminal-tools.test.ts +1 -1
  94. package/src/__tests__/tool-approval-handler.test.ts +1 -5
  95. package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
  96. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
  97. package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
  98. package/src/__tests__/tool-executor.test.ts +9 -62
  99. package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
  100. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  101. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
  102. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
  103. package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
  104. package/src/__tests__/usage-routes.test.ts +3 -0
  105. package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
  106. package/src/__tests__/workspace-git-service.test.ts +6 -5
  107. package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
  108. package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
  109. package/src/acp/prepare-agent-env.ts +78 -0
  110. package/src/acp/session-manager.ts +1 -1
  111. package/src/agent/loop.ts +8 -0
  112. package/src/api/README.md +5 -0
  113. package/src/api/index.ts +4 -0
  114. package/src/api/package.json +10 -0
  115. package/src/background-wake/background-wake-routes.test.ts +233 -0
  116. package/src/background-wake/runtime-registry.ts +24 -0
  117. package/src/cli/commands/__tests__/browser.test.ts +23 -5
  118. package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
  119. package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
  120. package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
  121. package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
  122. package/src/cli/commands/__tests__/memory-v2.test.ts +1 -0
  123. package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
  124. package/src/cli/commands/browser.ts +247 -0
  125. package/src/cli/commands/domain.ts +91 -41
  126. package/src/cli/commands/inference.ts +93 -40
  127. package/src/cli/commands/memory-v2-compare-render.ts +115 -0
  128. package/src/cli/commands/memory-v2.ts +176 -1
  129. package/src/cli/commands/memory-v3-render.ts +344 -0
  130. package/src/cli/commands/memory-v3.ts +316 -0
  131. package/src/cli/program.ts +2 -0
  132. package/src/config/assistant-feature-flags.ts +21 -9
  133. package/src/config/bundled-skills/document-editor/SKILL.md +11 -2
  134. package/src/config/bundled-skills/document-editor/TOOLS.json +18 -0
  135. package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
  136. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  137. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
  138. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
  139. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
  140. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
  141. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
  142. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
  143. package/src/config/bundled-tool-registry.ts +2 -0
  144. package/src/config/call-site-defaults.ts +7 -6
  145. package/src/config/feature-flag-registry.json +16 -0
  146. package/src/config/schemas/__tests__/memory-v2.test.ts +213 -1
  147. package/src/config/schemas/call-site-catalog.ts +21 -7
  148. package/src/config/schemas/llm.ts +12 -1
  149. package/src/config/schemas/memory-v2.ts +246 -0
  150. package/src/config/schemas/memory.ts +2 -1
  151. package/src/context/compactor.ts +52 -0
  152. package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
  153. package/src/conversations/message-consolidation.ts +404 -0
  154. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
  155. package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
  156. package/src/daemon/conversation-agent-loop-handlers.ts +2 -13
  157. package/src/daemon/conversation-agent-loop.ts +126 -76
  158. package/src/daemon/conversation-error.ts +31 -1
  159. package/src/daemon/conversation-lifecycle.ts +27 -22
  160. package/src/daemon/conversation-runtime-assembly.ts +10 -9
  161. package/src/daemon/conversation-tool-setup.ts +63 -3
  162. package/src/daemon/conversation-usage.ts +2 -0
  163. package/src/daemon/conversation.ts +14 -29
  164. package/src/daemon/disk-pressure-guard.ts +14 -2
  165. package/src/daemon/handlers/config-model.test.ts +1 -0
  166. package/src/daemon/handlers/conversations.ts +11 -3
  167. package/src/daemon/host-browser-proxy.ts +5 -5
  168. package/src/daemon/host-cu-proxy.ts +4 -4
  169. package/src/daemon/host-file-proxy.ts +4 -4
  170. package/src/daemon/host-proxy-base.ts +4 -4
  171. package/src/daemon/host-transfer-proxy.ts +10 -10
  172. package/src/daemon/lifecycle.ts +23 -20
  173. package/src/daemon/meet-manifest-loader.ts +1 -7
  174. package/src/daemon/message-types/conversations.ts +6 -9
  175. package/src/daemon/message-types/home.ts +1 -13
  176. package/src/daemon/message-types/messages.ts +6 -14
  177. package/src/daemon/message-types/sync.ts +14 -0
  178. package/src/daemon/shutdown-handlers.ts +24 -5
  179. package/src/daemon/switch-inference-profile-tool.ts +52 -0
  180. package/src/daemon/tool-setup-types.ts +13 -0
  181. package/src/events/relationship-state-updated.ts +25 -0
  182. package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -1
  183. package/src/home/home-greeting.ts +0 -9
  184. package/src/home/suggested-prompts.ts +0 -9
  185. package/src/ipc/gateway-flag-listener.ts +123 -0
  186. package/src/ipc/skill-routes/registries.ts +8 -12
  187. package/src/memory/__tests__/db-async-query.test.ts +165 -0
  188. package/src/memory/__tests__/db-maintenance.test.ts +115 -0
  189. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
  190. package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
  191. package/src/memory/__tests__/memory-retrospective-job.test.ts +7 -0
  192. package/src/memory/auto-analysis-enqueue.ts +5 -1
  193. package/src/memory/conversation-crud.ts +71 -70
  194. package/src/memory/conversation-starters-cadence.ts +3 -1
  195. package/src/memory/conversation-title-service.ts +19 -3
  196. package/src/memory/db-async-query.ts +214 -0
  197. package/src/memory/db-init.ts +10 -0
  198. package/src/memory/db-maintenance.ts +30 -21
  199. package/src/memory/graph/bootstrap.ts +8 -1
  200. package/src/memory/graph/capability-seed.ts +7 -3
  201. package/src/memory/graph/conversation-graph-memory.ts +100 -17
  202. package/src/memory/graph/extraction.ts +1 -5
  203. package/src/memory/graph/graph-search.ts +7 -1
  204. package/src/memory/indexer.ts +28 -18
  205. package/src/memory/job-handlers/cleanup.ts +76 -18
  206. package/src/memory/job-handlers/conversation-starters.ts +1 -4
  207. package/src/memory/jobs/embed-pkb-file.ts +6 -1
  208. package/src/memory/jobs-store.ts +14 -0
  209. package/src/memory/jobs-worker.ts +55 -22
  210. package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
  211. package/src/memory/llm-request-log-source-local.ts +7 -0
  212. package/src/memory/llm-request-log-source.ts +9 -2
  213. package/src/memory/llm-request-log-store.ts +43 -1
  214. package/src/memory/llm-usage-store.ts +24 -0
  215. package/src/memory/memory-retrospective-enqueue.ts +8 -1
  216. package/src/memory/memory-retrospective-job.ts +5 -0
  217. package/src/memory/memory-v2-activation-log-store.ts +15 -6
  218. package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
  219. package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
  220. package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
  221. package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
  222. package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
  223. package/src/memory/migrations/index.ts +17 -0
  224. package/src/memory/migrations/registry.ts +33 -0
  225. package/src/memory/schema/conversations.ts +1 -1
  226. package/src/memory/schema/infrastructure.ts +21 -0
  227. package/src/memory/tool-usage-store.ts +36 -8
  228. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
  229. package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
  230. package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
  231. package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
  232. package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
  233. package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
  234. package/src/memory/v2/__tests__/injection.test.ts +127 -98
  235. package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
  236. package/src/memory/v2/__tests__/router.test.ts +171 -3
  237. package/src/memory/v2/harness/compare.ts +57 -0
  238. package/src/memory/v2/harness/metrics.ts +124 -0
  239. package/src/memory/v2/harness/oracle.ts +145 -0
  240. package/src/memory/v2/harness/replay-input.ts +224 -0
  241. package/src/memory/v2/harness/retriever.ts +74 -0
  242. package/src/memory/v2/harness/router-retriever.ts +43 -0
  243. package/src/memory/v2/harness/runner.ts +106 -0
  244. package/src/memory/v2/harness/trace.ts +58 -0
  245. package/src/memory/v2/injection.ts +21 -15
  246. package/src/memory/v2/prompts/router.ts +26 -1
  247. package/src/memory/v2/qdrant.ts +14 -2
  248. package/src/memory/v2/router.ts +171 -18
  249. package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
  250. package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
  251. package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
  252. package/src/memory/v3/__tests__/edges.test.ts +563 -0
  253. package/src/memory/v3/__tests__/filter.test.ts +512 -0
  254. package/src/memory/v3/__tests__/gate.test.ts +574 -0
  255. package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
  256. package/src/memory/v3/__tests__/loop.test.ts +530 -0
  257. package/src/memory/v3/__tests__/retriever.test.ts +226 -0
  258. package/src/memory/v3/__tests__/scouts.test.ts +440 -0
  259. package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
  260. package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
  261. package/src/memory/v3/__tests__/traversal.test.ts +469 -0
  262. package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
  263. package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
  264. package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
  265. package/src/memory/v3/__tests__/validate.test.ts +245 -0
  266. package/src/memory/v3/auto-edges.ts +223 -0
  267. package/src/memory/v3/coactivation-store.ts +124 -0
  268. package/src/memory/v3/consolidation-job.ts +323 -0
  269. package/src/memory/v3/edge-learning-job.ts +160 -0
  270. package/src/memory/v3/edges.ts +249 -0
  271. package/src/memory/v3/filter.ts +281 -0
  272. package/src/memory/v3/gate.ts +334 -0
  273. package/src/memory/v3/index-composition.ts +113 -0
  274. package/src/memory/v3/llm-capture.ts +46 -0
  275. package/src/memory/v3/loop.ts +382 -0
  276. package/src/memory/v3/maintenance.ts +144 -0
  277. package/src/memory/v3/prompt-context.ts +33 -0
  278. package/src/memory/v3/prompts/consolidation.ts +458 -0
  279. package/src/memory/v3/prompts/system-prompts.ts +196 -0
  280. package/src/memory/v3/retriever.ts +33 -0
  281. package/src/memory/v3/scouts.ts +420 -0
  282. package/src/memory/v3/shadow-middleware.ts +305 -0
  283. package/src/memory/v3/traversal.ts +206 -0
  284. package/src/memory/v3/tree-index.ts +237 -0
  285. package/src/memory/v3/tree-store.ts +394 -0
  286. package/src/memory/v3/tree-walk.ts +351 -0
  287. package/src/memory/v3/types.ts +65 -0
  288. package/src/memory/v3/validate.ts +300 -0
  289. package/src/notifications/adapters/macos.ts +18 -1
  290. package/src/notifications/adapters/platform.ts +1 -1
  291. package/src/notifications/decision-engine.ts +1 -4
  292. package/src/notifications/emit-signal.ts +29 -49
  293. package/src/permissions/prompter.ts +3 -3
  294. package/src/permissions/question-prompter.ts +5 -2
  295. package/src/permissions/secret-prompter.ts +2 -2
  296. package/src/plugin-api/index.ts +4 -0
  297. package/src/plugin-api/types.ts +7 -33
  298. package/src/plugins/defaults/index.ts +6 -0
  299. package/src/plugins/defaults/injectors.ts +18 -11
  300. package/src/plugins/external-plugin-loader.ts +5 -68
  301. package/src/plugins/types.ts +11 -16
  302. package/src/proactive-artifact/aux-message-injector.ts +17 -4
  303. package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
  304. package/src/prompts/persona-resolver.ts +36 -21
  305. package/src/prompts/sections.ts +39 -7
  306. package/src/prompts/system-prompt.ts +50 -185
  307. package/src/prompts/templates/BOOTSTRAP.md +2 -2
  308. package/src/prompts/templates/system-sections.ts +230 -8
  309. package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
  310. package/src/providers/__tests__/retry-callsite.test.ts +85 -5
  311. package/src/providers/anthropic/client.ts +32 -66
  312. package/src/providers/call-site-routing.ts +14 -2
  313. package/src/providers/connection-model-compat.ts +38 -0
  314. package/src/providers/connection-resolution.ts +16 -2
  315. package/src/providers/gemini/client.ts +49 -6
  316. package/src/providers/inference/adapter-factory.ts +3 -0
  317. package/src/providers/minimax/client.ts +106 -0
  318. package/src/providers/model-catalog.ts +43 -0
  319. package/src/providers/model-intents.ts +1 -1
  320. package/src/providers/openai/chat-completions-provider.ts +6 -3
  321. package/src/providers/openai/codex-models.ts +18 -0
  322. package/src/providers/openai/responses-provider.ts +78 -21
  323. package/src/providers/provider-send-message.ts +7 -1
  324. package/src/providers/retry.ts +34 -3
  325. package/src/providers/thinking-config.ts +26 -1
  326. package/src/providers/usage-tracking.ts +2 -0
  327. package/src/runtime/AGENTS.md +2 -2
  328. package/src/runtime/agent-wake.ts +1 -0
  329. package/src/runtime/assistant-event-hub.ts +76 -6
  330. package/src/runtime/auth/route-policy.ts +36 -0
  331. package/src/runtime/btw-sidechain.ts +0 -6
  332. package/src/runtime/http-types.ts +0 -2
  333. package/src/runtime/migrations/vbundle-builder.ts +10 -3
  334. package/src/runtime/pending-interactions.ts +0 -1
  335. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +106 -0
  336. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +25 -6
  337. package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
  338. package/src/runtime/routes/acp-routes.test.ts +255 -6
  339. package/src/runtime/routes/acp-routes.ts +8 -1
  340. package/src/runtime/routes/avatar-routes.ts +10 -10
  341. package/src/runtime/routes/background-wake-routes.ts +188 -0
  342. package/src/runtime/routes/browser-tabs-routes.ts +200 -0
  343. package/src/runtime/routes/btw-routes.ts +0 -6
  344. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  345. package/src/runtime/routes/conversation-list-routes.ts +12 -4
  346. package/src/runtime/routes/conversation-management-routes.ts +77 -20
  347. package/src/runtime/routes/conversation-query-routes.ts +142 -36
  348. package/src/runtime/routes/conversation-routes.ts +252 -410
  349. package/src/runtime/routes/conversation-starter-routes.ts +6 -3
  350. package/src/runtime/routes/disk-pressure-routes.ts +1 -1
  351. package/src/runtime/routes/domain-routes.ts +60 -10
  352. package/src/runtime/routes/email-routes.ts +5 -2
  353. package/src/runtime/routes/events-routes.ts +54 -10
  354. package/src/runtime/routes/group-routes.ts +24 -8
  355. package/src/runtime/routes/host-browser-routes.ts +10 -2
  356. package/src/runtime/routes/host-cu-routes.ts +2 -2
  357. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
  358. package/src/runtime/routes/index.ts +8 -0
  359. package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
  360. package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
  361. package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
  362. package/src/runtime/routes/memory-item-routes.ts +8 -3
  363. package/src/runtime/routes/memory-v2-routes.ts +215 -5
  364. package/src/runtime/routes/memory-v3-routes.ts +316 -0
  365. package/src/runtime/routes/migration-routes.ts +21 -24
  366. package/src/runtime/routes/plugins-routes.ts +337 -0
  367. package/src/runtime/routes/rename-conversation-routes.ts +6 -2
  368. package/src/runtime/routes/secret-routes.ts +25 -5
  369. package/src/runtime/routes/settings-routes.ts +12 -11
  370. package/src/runtime/routes/slack-channel-routes.ts +5 -4
  371. package/src/runtime/routes/workspace-routes.ts +25 -10
  372. package/src/runtime/sync/resource-sync-events.ts +106 -38
  373. package/src/runtime/sync/sync-publisher.test.ts +49 -0
  374. package/src/runtime/sync/sync-publisher.ts +2 -1
  375. package/src/runtime/verification-outbound-actions.ts +73 -1
  376. package/src/telemetry/types.ts +12 -0
  377. package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
  378. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  379. package/src/tools/acp/spawn.test.ts +119 -0
  380. package/src/tools/acp/spawn.ts +15 -2
  381. package/src/tools/apps/definitions.ts +2 -8
  382. package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
  383. package/src/tools/ask-question/ask-question-tool.ts +38 -45
  384. package/src/tools/browser/__tests__/pinned-tabs.test.ts +70 -0
  385. package/src/tools/browser/browser-execution.ts +16 -3
  386. package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
  387. package/src/tools/browser/cdp-client/__tests__/types.test.ts +3 -0
  388. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +12 -0
  389. package/src/tools/browser/cdp-client/extension-cdp-client.ts +27 -1
  390. package/src/tools/browser/cdp-client/factory.ts +100 -17
  391. package/src/tools/browser/cdp-client/local-cdp-client.ts +12 -0
  392. package/src/tools/browser/cdp-client/types.ts +65 -0
  393. package/src/tools/browser/pinned-tabs.ts +96 -40
  394. package/src/tools/computer-use/definitions.ts +22 -78
  395. package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
  396. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
  397. package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
  398. package/src/tools/credentials/vault.ts +3 -9
  399. package/src/tools/document/document-tool.ts +59 -0
  400. package/src/tools/execution-target.ts +21 -23
  401. package/src/tools/executor.ts +6 -1
  402. package/src/tools/filesystem/edit.ts +3 -9
  403. package/src/tools/filesystem/list.ts +3 -9
  404. package/src/tools/filesystem/read.ts +3 -9
  405. package/src/tools/filesystem/write.ts +3 -9
  406. package/src/tools/host-filesystem/edit.ts +3 -9
  407. package/src/tools/host-filesystem/read.ts +3 -9
  408. package/src/tools/host-filesystem/transfer.ts +3 -9
  409. package/src/tools/host-filesystem/write.ts +3 -9
  410. package/src/tools/host-terminal/host-shell.ts +3 -9
  411. package/src/tools/mcp/mcp-tool-factory.ts +1 -8
  412. package/src/tools/memory/register.test.ts +1 -1
  413. package/src/tools/memory/register.ts +4 -9
  414. package/src/tools/network/web-fetch.ts +3 -9
  415. package/src/tools/network/web-search.ts +25 -32
  416. package/src/tools/registry.ts +7 -23
  417. package/src/tools/schema-transforms.ts +1 -1
  418. package/src/tools/skills/execute.ts +3 -9
  419. package/src/tools/skills/load.ts +3 -9
  420. package/src/tools/skills/skill-tool-factory.ts +1 -8
  421. package/src/tools/subagent/notify-parent.ts +3 -9
  422. package/src/tools/system/request-permission.ts +3 -9
  423. package/src/tools/terminal/shell.ts +3 -9
  424. package/src/tools/tool-defaults.ts +94 -0
  425. package/src/tools/types.ts +27 -98
  426. package/src/tools/ui-surface/definitions.ts +6 -22
  427. package/src/usage/pricing.ts +23 -0
  428. package/src/usage/types.ts +12 -0
  429. package/src/util/logger.ts +16 -7
  430. package/src/util/platform.ts +7 -2
  431. package/src/util/sqlite3-runtime.ts +65 -0
  432. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
  433. package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
  434. package/src/workspace/migrations/registry.ts +2 -0
  435. package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
  436. package/src/__tests__/message-complete-display-id.test.ts +0 -175
  437. package/src/daemon/query-complexity-router.ts +0 -75
  438. package/src/prompts/cache-boundary.ts +0 -8
@@ -0,0 +1,337 @@
1
+ /**
2
+ * Route handlers for the assistant plugins surface.
3
+ *
4
+ * GET /v1/plugins — list installed plugins under `<workspaceDir>/plugins/`.
5
+ * GET /v1/plugins/search — search the canonical GitHub catalog of installable plugins.
6
+ * DELETE /v1/plugins/:name — uninstall a plugin from `<workspaceDir>/plugins/<name>/`.
7
+ *
8
+ * The read-only routes are projections over the same library functions
9
+ * the CLI uses (`assistant plugins list`, `assistant plugins search`).
10
+ * The DELETE route is symmetric to `assistant plugins uninstall` and
11
+ * delegates to the same `uninstallPlugin` lib function. CLI / daemon /
12
+ * web stay aligned on what an installed or available plugin looks like.
13
+ *
14
+ * Install is intentionally not exposed here. The CLI remains the
15
+ * install surface because installation fetches from GitHub, applies
16
+ * sanitization, and writes to disk — a heavier flow than a single
17
+ * JSON request justifies right now.
18
+ *
19
+ * # Policy gating
20
+ *
21
+ * Every route declares `policyKey: "plugins"` + `requirePolicyEnforcement:
22
+ * true`. The HTTP router enforces via `enforcePolicy()` against the
23
+ * `plugins:GET` / `plugins/search:GET` / `plugins:DELETE` registry
24
+ * entries in `runtime/auth/route-policy.ts`. The IPC adapter exposes
25
+ * the same policies to the gateway IPC proxy, whose own policy table
26
+ * (`gateway/src/auth/ipc-route-policy.ts`) holds the matching entries
27
+ * for `plugins_list` / `plugins_search` / `plugins_uninstall`. Reads
28
+ * require `settings.read`; uninstall requires `settings.write`.
29
+ */
30
+
31
+ import { z } from "zod";
32
+
33
+ import { InvalidPluginNameError } from "../../cli/lib/install-from-github.js";
34
+ import {
35
+ type InstalledPluginInfo,
36
+ listInstalledPlugins,
37
+ } from "../../cli/lib/list-installed-plugins.js";
38
+ import {
39
+ InvalidSearchPatternError,
40
+ type PluginSearchMatch,
41
+ searchPlugins,
42
+ } from "../../cli/lib/search-plugins.js";
43
+ import {
44
+ PluginNotInstalledError,
45
+ uninstallPlugin,
46
+ } from "../../cli/lib/uninstall-plugin.js";
47
+ import {
48
+ BadRequestError,
49
+ InternalError,
50
+ NotFoundError,
51
+ } from "./errors.js";
52
+ import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
53
+
54
+ // ---------------------------------------------------------------------------
55
+ // Schema
56
+ // ---------------------------------------------------------------------------
57
+
58
+ const pluginInfoSchema = z.object({
59
+ id: z
60
+ .string()
61
+ .describe(
62
+ "Plugin's directory name (kebab-case). Matches `assistant plugins install <id>`.",
63
+ ),
64
+ name: z.string().describe("Display name. Equal to `id` today."),
65
+ description: z
66
+ .string()
67
+ .nullable()
68
+ .describe("From `package.json#description`; `null` when unknown."),
69
+ version: z
70
+ .string()
71
+ .nullable()
72
+ .describe("From `package.json#version`; `null` when unknown."),
73
+ path: z
74
+ .string()
75
+ .optional()
76
+ .describe("Absolute path to the plugin directory on the assistant host."),
77
+ issues: z
78
+ .array(z.string())
79
+ .optional()
80
+ .describe(
81
+ "Non-fatal issues with this entry (missing `package.json`, malformed JSON, ...). Omitted when clean.",
82
+ ),
83
+ });
84
+
85
+ const pluginsListResponseSchema = z.object({
86
+ plugins: z.array(pluginInfoSchema),
87
+ });
88
+
89
+ const pluginSearchMatchSchema = z.object({
90
+ name: z
91
+ .string()
92
+ .describe(
93
+ "Directory name under `experimental/plugins/`. Matches `assistant plugins install <name>`.",
94
+ ),
95
+ path: z
96
+ .string()
97
+ .describe(
98
+ "Repo-relative path of the match (e.g. `experimental/plugins/<name>`).",
99
+ ),
100
+ });
101
+
102
+ const pluginsSearchResponseSchema = z.object({
103
+ query: z
104
+ .string()
105
+ .describe("Echo of the requested query (ECMAScript regex source)."),
106
+ ref: z.string().describe("Git ref the catalog was listed at."),
107
+ matches: z
108
+ .array(pluginSearchMatchSchema)
109
+ .describe("Directory matches, sorted alphabetically by name."),
110
+ });
111
+
112
+ const pluginUninstallResponseSchema = z.object({
113
+ name: z
114
+ .string()
115
+ .describe(
116
+ "Directory name that was removed. Echoes the request's `:name` path parameter after sanitization.",
117
+ ),
118
+ target: z
119
+ .string()
120
+ .describe(
121
+ "Absolute path that was removed on the assistant host. Useful for audit logs and confirmation toasts.",
122
+ ),
123
+ });
124
+
125
+ // ---------------------------------------------------------------------------
126
+ // Helpers
127
+ // ---------------------------------------------------------------------------
128
+
129
+ interface PluginView {
130
+ id: string;
131
+ name: string;
132
+ description: string | null;
133
+ version: string | null;
134
+ path: string;
135
+ issues?: string[];
136
+ }
137
+
138
+ function projectPlugin(entry: InstalledPluginInfo): PluginView {
139
+ // `id` and `name` both track the directory name. `package.json#name` can
140
+ // be scoped (e.g. `@vendor/plugin-name`) which is fine for npm but not
141
+ // what the CLI uses to install — so we don't surface it as `name`.
142
+ const view: PluginView = {
143
+ id: entry.name,
144
+ name: entry.name,
145
+ description: entry.packageJson?.description ?? null,
146
+ version: entry.packageJson?.version ?? null,
147
+ path: entry.target,
148
+ };
149
+ if (entry.issues.length > 0) {
150
+ view.issues = [...entry.issues];
151
+ }
152
+ return view;
153
+ }
154
+
155
+ function matchesQuery(plugin: PluginView, needle: string): boolean {
156
+ const q = needle.toLowerCase();
157
+ if (plugin.id.toLowerCase().includes(q)) return true;
158
+ if (plugin.name.toLowerCase().includes(q)) return true;
159
+ if (plugin.description && plugin.description.toLowerCase().includes(q)) {
160
+ return true;
161
+ }
162
+ return false;
163
+ }
164
+
165
+ // ---------------------------------------------------------------------------
166
+ // Handler — list installed
167
+ // ---------------------------------------------------------------------------
168
+
169
+ function handleListPlugins({
170
+ queryParams = {},
171
+ }: RouteHandlerArgs): { plugins: PluginView[] } {
172
+ const q = queryParams.q?.trim();
173
+ const installed = listInstalledPlugins();
174
+ const projected = installed.map(projectPlugin);
175
+ const filtered = q ? projected.filter((p) => matchesQuery(p, q)) : projected;
176
+ return { plugins: filtered };
177
+ }
178
+
179
+ // ---------------------------------------------------------------------------
180
+ // Handler — search catalog
181
+ // ---------------------------------------------------------------------------
182
+
183
+ interface PluginsSearchResponse {
184
+ query: string;
185
+ ref: string;
186
+ matches: PluginSearchMatch[];
187
+ }
188
+
189
+ async function handleSearchPlugins({
190
+ queryParams = {},
191
+ }: RouteHandlerArgs): Promise<PluginsSearchResponse> {
192
+ // Empty string is a legitimate "match everything" query per the lib's
193
+ // contract — accept it without forcing the caller to pick a sentinel.
194
+ const query = queryParams.q ?? "";
195
+ const ref = queryParams.ref?.trim() || undefined;
196
+
197
+ try {
198
+ const result = await searchPlugins(
199
+ { query, ref },
200
+ { fetch: globalThis.fetch.bind(globalThis) },
201
+ );
202
+ // Re-pack `readonly` lib types into mutable copies so the route
203
+ // serializer's `Record<string, unknown>` contract holds. The wire
204
+ // shape is identical.
205
+ return {
206
+ query: result.query,
207
+ ref: result.ref,
208
+ matches: result.matches.map((m) => ({ name: m.name, path: m.path })),
209
+ };
210
+ } catch (err) {
211
+ if (err instanceof InvalidSearchPatternError) {
212
+ throw new BadRequestError(err.message);
213
+ }
214
+ throw new InternalError(
215
+ err instanceof Error ? err.message : "plugin catalog search failed",
216
+ );
217
+ }
218
+ }
219
+
220
+ // ---------------------------------------------------------------------------
221
+ // Handler — uninstall
222
+ // ---------------------------------------------------------------------------
223
+
224
+ interface PluginUninstallResponse {
225
+ name: string;
226
+ target: string;
227
+ }
228
+
229
+ function handleUninstallPlugin({
230
+ pathParams = {},
231
+ }: RouteHandlerArgs): PluginUninstallResponse {
232
+ // The HTTP router has already URL-decoded `:name` for us; pass it
233
+ // through verbatim — `uninstallPlugin` runs the same
234
+ // `sanitizePluginName` check the CLI uses, so attacker-supplied
235
+ // `../escape` style names get rejected before `rmSync` is reached.
236
+ const rawName = pathParams.name ?? "";
237
+
238
+ try {
239
+ const result = uninstallPlugin({ name: rawName });
240
+ return { name: result.name, target: result.target };
241
+ } catch (err) {
242
+ if (err instanceof InvalidPluginNameError) {
243
+ throw new BadRequestError(err.message);
244
+ }
245
+ if (err instanceof PluginNotInstalledError) {
246
+ throw new NotFoundError(err.message);
247
+ }
248
+ throw new InternalError(
249
+ err instanceof Error ? err.message : "plugin uninstall failed",
250
+ );
251
+ }
252
+ }
253
+
254
+ // ---------------------------------------------------------------------------
255
+ // Route definitions
256
+ // ---------------------------------------------------------------------------
257
+
258
+ export const ROUTES: RouteDefinition[] = [
259
+ {
260
+ operationId: "plugins_list",
261
+ endpoint: "plugins",
262
+ method: "GET",
263
+ policyKey: "plugins",
264
+ requirePolicyEnforcement: true,
265
+ summary: "List installed plugins",
266
+ description:
267
+ "Return one entry per directory under `<workspaceDir>/plugins/`, sorted alphabetically. Matches the CLI's `assistant plugins list`. Supports `?q=<text>` for case-insensitive substring matching across plugin id, name, and description.",
268
+ tags: ["plugins"],
269
+ queryParams: [
270
+ {
271
+ name: "q",
272
+ schema: { type: "string" },
273
+ description:
274
+ "Optional substring filter applied to plugin id, name, and description.",
275
+ },
276
+ ],
277
+ responseBody: pluginsListResponseSchema,
278
+ handler: handleListPlugins,
279
+ },
280
+ {
281
+ operationId: "plugins_search",
282
+ endpoint: "plugins/search",
283
+ method: "GET",
284
+ policyKey: "plugins",
285
+ requirePolicyEnforcement: true,
286
+ summary: "Search the plugin catalog",
287
+ description:
288
+ "List installable plugins from the canonical `vellum-ai/vellum-assistant` catalog at `experimental/plugins/`. The query is an ECMAScript regex matched case-insensitively against the directory name (e.g. `memory`, `^simple`). Empty query returns every entry. Mirrors the CLI's `assistant plugins search`.",
289
+ tags: ["plugins"],
290
+ queryParams: [
291
+ {
292
+ name: "q",
293
+ schema: { type: "string" },
294
+ description:
295
+ "ECMAScript regex pattern matched case-insensitively against catalog directory names. Empty/missing matches everything.",
296
+ },
297
+ {
298
+ name: "ref",
299
+ schema: { type: "string" },
300
+ description:
301
+ "Optional git ref to list the catalog at. Defaults to the CLI's `DEFAULT_PLUGIN_REF` (typically `main`).",
302
+ },
303
+ ],
304
+ responseBody: pluginsSearchResponseSchema,
305
+ handler: handleSearchPlugins,
306
+ },
307
+ {
308
+ operationId: "plugins_uninstall",
309
+ endpoint: "plugins/:name",
310
+ method: "DELETE",
311
+ policyKey: "plugins",
312
+ requirePolicyEnforcement: true,
313
+ summary: "Uninstall a plugin",
314
+ description:
315
+ "Remove the directory at `<workspaceDir>/plugins/<name>/`. Mirrors the CLI's `assistant plugins uninstall <name>` (without the interactive confirmation — the API caller is responsible for any prompt). The plugin name is sanitized by the same regex the CLI uses; `../escape`-style values, hidden names, and absolute paths return 400. Missing plugins return 404. The assistant must be restarted to drop the plugin from the running runtime.",
316
+ tags: ["plugins"],
317
+ pathParams: [
318
+ {
319
+ name: "name",
320
+ type: "string",
321
+ description:
322
+ "Directory name under `<workspaceDir>/plugins/`. Must match the kebab-case name accepted by `assistant plugins install`.",
323
+ },
324
+ ],
325
+ responseBody: pluginUninstallResponseSchema,
326
+ additionalResponses: {
327
+ "400": {
328
+ description:
329
+ "The plugin name failed sanitization (e.g. contained slashes, dots, or uppercase letters).",
330
+ },
331
+ "404": {
332
+ description: "No plugin directory exists with the given name.",
333
+ },
334
+ },
335
+ handler: handleUninstallPlugin,
336
+ },
337
+ ];
@@ -31,7 +31,7 @@ export const ROUTES: RouteDefinition[] = [
31
31
  description: "Update the display title of a conversation.",
32
32
  tags: ["conversations"],
33
33
  requestBody: RenameConversationBody,
34
- handler: ({ body }) => {
34
+ handler: ({ body, headers }) => {
35
35
  const parsed = RenameConversationBody.safeParse(body);
36
36
  if (!parsed.success) {
37
37
  throw new BadRequestError("conversationId and title are required");
@@ -46,7 +46,11 @@ export const ROUTES: RouteDefinition[] = [
46
46
 
47
47
  updateConversationTitle(conversationId, title, 0);
48
48
 
49
- publishConversationTitleChanged(conversationId, title);
49
+ publishConversationTitleChanged(
50
+ conversationId,
51
+ title,
52
+ headers?.["x-vellum-client-id"]?.trim() || undefined,
53
+ );
50
54
 
51
55
  return { ok: true };
52
56
  },
@@ -27,6 +27,7 @@ import { clearEmbeddingBackendCache } from "../../memory/embedding-backend.js";
27
27
  import { syncManualTokenConnection } from "../../oauth/manual-token-connection.js";
28
28
  import { validateAnthropicApiKey } from "../../providers/anthropic/client.js";
29
29
  import { validateGeminiApiKey } from "../../providers/gemini/client.js";
30
+ import { validateMinimaxApiKey } from "../../providers/minimax/client.js";
30
31
  import { validateOpenAIApiKey } from "../../providers/openai/client.js";
31
32
  import { initializeProviders } from "../../providers/registry.js";
32
33
  import { credentialKey } from "../../security/credential-key.js";
@@ -195,9 +196,21 @@ async function handleAddSecret({ body }: RouteHandlerArgs) {
195
196
  );
196
197
  return { success: false, error: validation.reason };
197
198
  }
199
+ } else if (name === "minimax") {
200
+ const validation = await validateMinimaxApiKey(value);
201
+ if (!validation.valid) {
202
+ log.warn(
203
+ { provider: name, reason: validation.reason },
204
+ "API key validation failed",
205
+ );
206
+ return { success: false, error: validation.reason };
207
+ }
198
208
  }
199
209
 
200
- const stored = await setSecureKeyAsync(credentialKey(name, "api_key"), value);
210
+ const stored = await setSecureKeyAsync(
211
+ credentialKey(name, "api_key"),
212
+ value,
213
+ );
201
214
  if (!stored) {
202
215
  throw new InternalError(
203
216
  `Failed to store API key in secure storage (backend: ${getActiveBackendName()})`,
@@ -339,7 +352,9 @@ async function handleReadSecret({ body }: RouteHandlerArgs) {
339
352
 
340
353
  try {
341
354
  let accountKey: string;
342
- let prefetchedResult: Awaited<ReturnType<typeof getSecureKeyResultAsync>> | undefined;
355
+ let prefetchedResult:
356
+ | Awaited<ReturnType<typeof getSecureKeyResultAsync>>
357
+ | undefined;
343
358
 
344
359
  if (type === "api_key") {
345
360
  if (
@@ -350,7 +365,9 @@ async function handleReadSecret({ body }: RouteHandlerArgs) {
350
365
  );
351
366
  }
352
367
  // Check credential namespace first; fall back to bare key only if not found and store is reachable.
353
- const credResult = await getSecureKeyResultAsync(credentialKey(name, "api_key"));
368
+ const credResult = await getSecureKeyResultAsync(
369
+ credentialKey(name, "api_key"),
370
+ );
354
371
  if (credResult.value === undefined && !credResult.unreachable) {
355
372
  accountKey = name;
356
373
  } else {
@@ -373,7 +390,8 @@ async function handleReadSecret({ body }: RouteHandlerArgs) {
373
390
  );
374
391
  }
375
392
 
376
- const { value, unreachable } = prefetchedResult ?? await getSecureKeyResultAsync(accountKey);
393
+ const { value, unreachable } =
394
+ prefetchedResult ?? (await getSecureKeyResultAsync(accountKey));
377
395
  if (value === undefined) {
378
396
  return { found: false, unreachable };
379
397
  }
@@ -543,7 +561,9 @@ async function handleListSecrets() {
543
561
  const field = rest.slice(slashIdx + 1);
544
562
  if (
545
563
  field === "api_key" &&
546
- API_KEY_PROVIDERS.includes(service as (typeof API_KEY_PROVIDERS)[number])
564
+ API_KEY_PROVIDERS.includes(
565
+ service as (typeof API_KEY_PROVIDERS)[number],
566
+ )
547
567
  ) {
548
568
  return [service];
549
569
  }
@@ -78,7 +78,10 @@ function handleVoiceConfigUpdate({ body = {} }: RouteHandlerArgs) {
78
78
  // Avatar generation
79
79
  // ---------------------------------------------------------------------------
80
80
 
81
- async function handleGenerateAvatar({ body = {} }: RouteHandlerArgs) {
81
+ async function handleGenerateAvatar({
82
+ body = {},
83
+ headers,
84
+ }: RouteHandlerArgs) {
82
85
  const { description } = body as { description?: string };
83
86
  if (!description?.trim()) {
84
87
  throw new BadRequestError("Description is required.");
@@ -95,7 +98,7 @@ async function handleGenerateAvatar({ body = {} }: RouteHandlerArgs) {
95
98
 
96
99
  const avatarPath = getAvatarImagePath();
97
100
 
98
- publishAvatarChanged();
101
+ publishAvatarChanged(headers?.["x-vellum-client-id"]?.trim() || undefined);
99
102
 
100
103
  return { ok: true, avatarPath };
101
104
  } catch (err) {
@@ -381,14 +384,7 @@ function handleToolNamesList() {
381
384
  };
382
385
  const schemas: Record<string, SchemaShape> = {};
383
386
 
384
- const rawDefs: ToolDefinition[] = [];
385
- for (const tool of tools) {
386
- try {
387
- rawDefs.push(tool.getDefinition());
388
- } catch {
389
- // Skip tools whose definitions can't be resolved
390
- }
391
- }
387
+ const rawDefs: ToolDefinition[] = tools;
392
388
 
393
389
  const transformedDefs = injectActivityField(rawDefs, ACTIVITY_SKIP_SET);
394
390
  for (const def of transformedDefs) {
@@ -444,7 +440,12 @@ async function handleToolPermissionSimulate({ body = {} }: RouteHandlerArgs) {
444
440
 
445
441
  try {
446
442
  const manifestOverride = resolveManifestOverride(toolName);
447
- const executionTarget = resolveExecutionTarget(toolName, manifestOverride);
443
+ // Permission Simulator path: registered tool wins, then explicit
444
+ // manifest override, then the standard inference rules.
445
+ const executionTarget =
446
+ getTool(toolName)?.executionTarget ??
447
+ manifestOverride?.execution_target ??
448
+ resolveExecutionTarget({ name: toolName });
448
449
  const executionContext =
449
450
  isInteractive === false ? "headless" : "conversation";
450
451
  const policyContext = { executionTarget, executionContext } as const;
@@ -93,6 +93,7 @@ function reasonForSlackError(err: unknown): ResolveReason {
93
93
 
94
94
  async function handleSlackChannelNameResolve({
95
95
  pathParams = {},
96
+ headers,
96
97
  }: RouteHandlerArgs): Promise<SlackChannelResolveResponse> {
97
98
  const conversationId = pathParams.conversationId?.trim();
98
99
  if (!conversationId) {
@@ -152,10 +153,10 @@ async function handleSlackChannelNameResolve({
152
153
  }
153
154
 
154
155
  updateExternalChatName(conversationId, channelName);
155
- await publishSyncInvalidation([
156
- SYNC_TAGS.conversationsList,
157
- conversationMetadataSyncTag(conversationId),
158
- ]);
156
+ await publishSyncInvalidation(
157
+ [SYNC_TAGS.conversationsList, conversationMetadataSyncTag(conversationId)],
158
+ headers?.["x-vellum-client-id"]?.trim() || undefined,
159
+ );
159
160
 
160
161
  return {
161
162
  channelId,
@@ -59,9 +59,12 @@ function isSoundsWorkspacePath(path: string): boolean {
59
59
  );
60
60
  }
61
61
 
62
- function publishSoundsConfigUpdatedForPaths(paths: string[]): void {
62
+ function publishSoundsConfigUpdatedForPaths(
63
+ paths: string[],
64
+ originClientId?: string,
65
+ ): void {
63
66
  if (paths.some(isSoundsWorkspacePath)) {
64
- publishSoundsConfigUpdated();
67
+ publishSoundsConfigUpdated(originClientId);
65
68
  }
66
69
  }
67
70
 
@@ -263,7 +266,7 @@ function handleWorkspaceFileContent({
263
266
  // POST /v1/workspace/write — create or overwrite a file
264
267
  // ---------------------------------------------------------------------------
265
268
 
266
- function handleWorkspaceWrite({ body }: RouteHandlerArgs) {
269
+ function handleWorkspaceWrite({ body, headers }: RouteHandlerArgs) {
267
270
  const path = body?.path as string | undefined;
268
271
  const content = body?.content as string | undefined;
269
272
  const encoding = body?.encoding as string | undefined;
@@ -292,7 +295,10 @@ function handleWorkspaceWrite({ body }: RouteHandlerArgs) {
292
295
 
293
296
  mkdirSync(dirname(resolved), { recursive: true });
294
297
  writeFileSync(resolved, buffer);
295
- publishSoundsConfigUpdatedForPaths([path]);
298
+ publishSoundsConfigUpdatedForPaths(
299
+ [path],
300
+ headers?.["x-vellum-client-id"]?.trim() || undefined,
301
+ );
296
302
 
297
303
  return { path, size: buffer.byteLength };
298
304
  }
@@ -301,7 +307,7 @@ function handleWorkspaceWrite({ body }: RouteHandlerArgs) {
301
307
  // POST /v1/workspace/mkdir — create directories
302
308
  // ---------------------------------------------------------------------------
303
309
 
304
- function handleWorkspaceMkdir({ body }: RouteHandlerArgs) {
310
+ function handleWorkspaceMkdir({ body, headers }: RouteHandlerArgs) {
305
311
  const path = body?.path as string | undefined;
306
312
  if (!path) {
307
313
  throw new BadRequestError("path is required");
@@ -320,7 +326,10 @@ function handleWorkspaceMkdir({ body }: RouteHandlerArgs) {
320
326
  }
321
327
 
322
328
  mkdirSync(resolved, { recursive: true });
323
- publishSoundsConfigUpdatedForPaths([path]);
329
+ publishSoundsConfigUpdatedForPaths(
330
+ [path],
331
+ headers?.["x-vellum-client-id"]?.trim() || undefined,
332
+ );
324
333
  return { path };
325
334
  }
326
335
 
@@ -328,7 +337,7 @@ function handleWorkspaceMkdir({ body }: RouteHandlerArgs) {
328
337
  // POST /v1/workspace/rename — rename/move files and directories
329
338
  // ---------------------------------------------------------------------------
330
339
 
331
- function handleWorkspaceRename({ body }: RouteHandlerArgs) {
340
+ function handleWorkspaceRename({ body, headers }: RouteHandlerArgs) {
332
341
  const oldPath = body?.oldPath as string | undefined;
333
342
  const newPath = body?.newPath as string | undefined;
334
343
  if (!oldPath || !newPath) {
@@ -360,7 +369,10 @@ function handleWorkspaceRename({ body }: RouteHandlerArgs) {
360
369
 
361
370
  mkdirSync(dirname(resolvedNew), { recursive: true });
362
371
  renameSync(resolvedOld, resolvedNew);
363
- publishSoundsConfigUpdatedForPaths([oldPath, newPath]);
372
+ publishSoundsConfigUpdatedForPaths(
373
+ [oldPath, newPath],
374
+ headers?.["x-vellum-client-id"]?.trim() || undefined,
375
+ );
364
376
  return { oldPath, newPath };
365
377
  }
366
378
 
@@ -368,7 +380,7 @@ function handleWorkspaceRename({ body }: RouteHandlerArgs) {
368
380
  // POST /v1/workspace/delete — delete files and directories
369
381
  // ---------------------------------------------------------------------------
370
382
 
371
- function handleWorkspaceDelete({ body }: RouteHandlerArgs) {
383
+ function handleWorkspaceDelete({ body, headers }: RouteHandlerArgs) {
372
384
  const path = body?.path as string | undefined;
373
385
  if (!path) {
374
386
  throw new BadRequestError("path is required");
@@ -388,7 +400,10 @@ function handleWorkspaceDelete({ body }: RouteHandlerArgs) {
388
400
  }
389
401
 
390
402
  rmSync(resolved, { recursive: true, force: true });
391
- publishSoundsConfigUpdatedForPaths([path]);
403
+ publishSoundsConfigUpdatedForPaths(
404
+ [path],
405
+ headers?.["x-vellum-client-id"]?.trim() || undefined,
406
+ );
392
407
  return { success: true };
393
408
  }
394
409