@vellumai/assistant 0.8.1 → 0.8.3

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 (630) hide show
  1. package/ARCHITECTURE.md +13 -19
  2. package/Dockerfile +75 -1
  3. package/bun.lock +11 -1
  4. package/docker-entrypoint.sh +17 -0
  5. package/docker-init-apt-root.sh +167 -0
  6. package/docker-kata-apt-env.sh +39 -0
  7. package/docs/plugins.md +88 -47
  8. package/docs/skills.md +9 -7
  9. package/examples/plugins/echo/README.md +27 -27
  10. package/examples/plugins/echo/package.json +3 -0
  11. package/examples/plugins/echo/register.ts +31 -31
  12. package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
  13. package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
  14. package/openapi.yaml +642 -5
  15. package/package.json +3 -1
  16. package/scripts/generate-openapi.ts +83 -10
  17. package/scripts/sync-llm-catalog.ts +2 -2
  18. package/scripts/sync-web-search-catalog.ts +47 -25
  19. package/src/__tests__/agent-image-optimize.test.ts +11 -3
  20. package/src/__tests__/agent-loop-exit-reason.test.ts +272 -0
  21. package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
  23. package/src/__tests__/anthropic-provider.test.ts +45 -0
  24. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  25. package/src/__tests__/app-executors.test.ts +220 -4
  26. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  27. package/src/__tests__/bundled-asset.test.ts +6 -6
  28. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  29. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  30. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  31. package/src/__tests__/clawhub.test.ts +75 -16
  32. package/src/__tests__/compactor-tail-resolution.test.ts +147 -0
  33. package/src/__tests__/config-get-vision-flag.test.ts +136 -0
  34. package/src/__tests__/config-loader-backfill.test.ts +115 -18
  35. package/src/__tests__/config-schema.test.ts +21 -0
  36. package/src/__tests__/config-set-route.test.ts +80 -0
  37. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  38. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  39. package/src/__tests__/context-search-conversations-source.test.ts +117 -2
  40. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  41. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  42. package/src/__tests__/context-token-estimator.test.ts +31 -65
  43. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  44. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  45. package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
  46. package/src/__tests__/conversation-agent-loop.test.ts +59 -1
  47. package/src/__tests__/conversation-error.test.ts +42 -3
  48. package/src/__tests__/conversation-fork-crud.test.ts +82 -0
  49. package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
  50. package/src/__tests__/conversation-lifecycle.test.ts +173 -0
  51. package/src/__tests__/conversation-media-retry.test.ts +19 -8
  52. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  53. package/src/__tests__/conversation-pairing.test.ts +54 -0
  54. package/src/__tests__/conversation-process-callsite.test.ts +4 -1
  55. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  56. package/src/__tests__/conversation-queue.test.ts +4 -1
  57. package/src/__tests__/conversation-runtime-assembly.test.ts +102 -13
  58. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  59. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  60. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  61. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  62. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  63. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  64. package/src/__tests__/credential-security-invariants.test.ts +3 -2
  65. package/src/__tests__/date-context.test.ts +45 -0
  66. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  67. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  68. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  69. package/src/__tests__/dm-backfill.test.ts +121 -10
  70. package/src/__tests__/document-tool-security.test.ts +258 -0
  71. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  72. package/src/__tests__/edit-propagation.test.ts +33 -0
  73. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  74. package/src/__tests__/external-plugin-loader.test.ts +151 -55
  75. package/src/__tests__/filing-service.test.ts +140 -0
  76. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  77. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
  78. package/src/__tests__/guardian-dispatch.test.ts +1 -0
  79. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
  80. package/src/__tests__/heartbeat-service.test.ts +24 -164
  81. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  82. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  83. package/src/__tests__/helpers/wait-for.ts +21 -0
  84. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  85. package/src/__tests__/history-repair.test.ts +73 -0
  86. package/src/__tests__/host-app-control-proxy.test.ts +507 -10
  87. package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
  88. package/src/__tests__/image-credentials.test.ts +1 -1
  89. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  90. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
  91. package/src/__tests__/inference-profile-reaper.test.ts +4 -2
  92. package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
  93. package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
  94. package/src/__tests__/injector-background-turn.test.ts +153 -0
  95. package/src/__tests__/injector-chain.test.ts +15 -8
  96. package/src/__tests__/install-skill-routing.test.ts +155 -37
  97. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +99 -3
  98. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  99. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  100. package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
  101. package/src/__tests__/llm-catalog-parity.test.ts +58 -13
  102. package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
  103. package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
  104. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +36 -0
  105. package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
  106. package/src/__tests__/llm-resolver.test.ts +255 -2
  107. package/src/__tests__/llm-usage-store.test.ts +114 -0
  108. package/src/__tests__/managed-profile-guard.test.ts +41 -29
  109. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  110. package/src/__tests__/managed-store.test.ts +84 -192
  111. package/src/__tests__/media-generate-image.test.ts +1 -1
  112. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  113. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  114. package/src/__tests__/notification-decision-fallback.test.ts +0 -91
  115. package/src/__tests__/notification-decision-strategy.test.ts +14 -31
  116. package/src/__tests__/notification-deep-link.test.ts +15 -0
  117. package/src/__tests__/notification-guardian-path.test.ts +1 -2
  118. package/src/__tests__/notification-platform-adapter.test.ts +5 -4
  119. package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
  120. package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
  121. package/src/__tests__/oauth-commands-routes.test.ts +168 -16
  122. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  123. package/src/__tests__/openai-provider.test.ts +242 -3
  124. package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
  125. package/src/__tests__/openrouter-provider-only.test.ts +51 -3
  126. package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
  127. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  128. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  129. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +7 -2
  130. package/src/__tests__/platform.test.ts +2 -0
  131. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  132. package/src/__tests__/plugin-bootstrap.test.ts +10 -36
  133. package/src/__tests__/plugin-external-api.test.ts +68 -0
  134. package/src/__tests__/plugin-registry.test.ts +0 -77
  135. package/src/__tests__/plugin-route-contribution.test.ts +0 -1
  136. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  137. package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
  138. package/src/__tests__/plugin-types.test.ts +3 -13
  139. package/src/__tests__/process-message-background-slack.test.ts +8 -1
  140. package/src/__tests__/process-message-display-content.test.ts +421 -0
  141. package/src/__tests__/provider-catalog-visibility.test.ts +158 -0
  142. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  143. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +33 -31
  144. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  145. package/src/__tests__/schedule-routes.test.ts +50 -3
  146. package/src/__tests__/schedule-store.test.ts +94 -0
  147. package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
  148. package/src/__tests__/schema-transforms.test.ts +20 -0
  149. package/src/__tests__/search-skills-unified.test.ts +0 -5
  150. package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +1 -1
  151. package/src/__tests__/server-history-render.test.ts +43 -0
  152. package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
  153. package/src/__tests__/skill-load-tool.test.ts +27 -89
  154. package/src/__tests__/skill-memory.test.ts +23 -3
  155. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  156. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  157. package/src/__tests__/skills-install-extract.test.ts +49 -38
  158. package/src/__tests__/skills-install-staging.test.ts +159 -0
  159. package/src/__tests__/skills-uninstall.test.ts +9 -41
  160. package/src/__tests__/skills.test.ts +51 -58
  161. package/src/__tests__/slack-channel-config.test.ts +9 -0
  162. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  163. package/src/__tests__/system-prompt.test.ts +670 -63
  164. package/src/__tests__/terminal-tools.test.ts +28 -1
  165. package/src/__tests__/thread-backfill.test.ts +557 -27
  166. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  167. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  168. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  169. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  170. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  171. package/src/__tests__/tool-executor.test.ts +16 -4
  172. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  173. package/src/__tests__/turn-events-store.test.ts +256 -0
  174. package/src/__tests__/twilio-routes.test.ts +4 -0
  175. package/src/__tests__/user-plugin-loader.test.ts +0 -7
  176. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  177. package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
  178. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
  179. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
  180. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  181. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  182. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  183. package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
  184. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  185. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  186. package/src/a2a/__tests__/agent-card.test.ts +98 -0
  187. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
  188. package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
  189. package/src/a2a/__tests__/task-store.test.ts +246 -0
  190. package/src/a2a/agent-card.ts +58 -0
  191. package/src/a2a/feature-gate.ts +8 -0
  192. package/src/a2a/protocol-constants.ts +21 -0
  193. package/src/a2a/protocol-errors.ts +50 -0
  194. package/src/a2a/protocol-types.ts +162 -0
  195. package/src/a2a/task-store.ts +168 -0
  196. package/src/acp/resolve-agent.ts +1 -1
  197. package/src/agent/image-optimize.ts +13 -5
  198. package/src/agent/loop.ts +167 -18
  199. package/src/calls/voice-session-bridge.ts +61 -42
  200. package/src/channels/config.ts +9 -0
  201. package/src/channels/types.ts +122 -0
  202. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  203. package/src/cli/commands/__tests__/changelog.test.ts +304 -319
  204. package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
  205. package/src/cli/commands/__tests__/schedules.test.ts +960 -0
  206. package/src/cli/commands/changelog.ts +106 -42
  207. package/src/cli/commands/conversations.ts +102 -17
  208. package/src/cli/commands/default-action.ts +10 -53
  209. package/src/cli/commands/notifications.ts +388 -346
  210. package/src/cli/commands/plugins.ts +252 -0
  211. package/src/cli/commands/schedules.ts +683 -0
  212. package/src/cli/commands/telemetry.ts +40 -0
  213. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  214. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  215. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  216. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  217. package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
  218. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  219. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  220. package/src/cli/lib/cli-colors.ts +12 -0
  221. package/src/cli/lib/confirm-prompt.ts +79 -0
  222. package/src/cli/lib/install-from-github.ts +303 -0
  223. package/src/cli/lib/list-installed-plugins.ts +137 -0
  224. package/src/cli/lib/search-plugins.ts +163 -0
  225. package/src/cli/lib/uninstall-plugin.ts +82 -0
  226. package/src/cli/lib/unknown-command.ts +111 -0
  227. package/src/cli/program.ts +52 -2
  228. package/src/config/assistant-feature-flags.ts +24 -54
  229. package/src/config/bundled-skills/app-builder/SKILL.md +140 -22
  230. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  231. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  232. package/src/config/bundled-skills/document/SKILL.md +23 -3
  233. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  234. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  235. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  236. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  237. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  238. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  239. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  240. package/src/config/bundled-tool-registry.ts +6 -0
  241. package/src/config/call-site-defaults.ts +105 -0
  242. package/src/config/feature-flag-registry.json +41 -9
  243. package/src/config/llm-resolver.ts +52 -1
  244. package/src/config/loader.ts +64 -38
  245. package/src/config/schema.ts +9 -10
  246. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  247. package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
  248. package/src/config/schemas/channels.ts +17 -0
  249. package/src/config/schemas/compaction.ts +28 -0
  250. package/src/config/schemas/conversations.ts +10 -0
  251. package/src/config/schemas/heartbeat.ts +23 -0
  252. package/src/config/schemas/llm-request-logs.ts +31 -7
  253. package/src/config/schemas/llm.ts +1 -0
  254. package/src/config/schemas/memory-retrieval.ts +18 -0
  255. package/src/config/schemas/memory-retrospective.ts +1 -1
  256. package/src/config/schemas/memory-v2.ts +4 -4
  257. package/src/config/schemas/memory.ts +3 -1
  258. package/src/config/schemas/tools.ts +14 -0
  259. package/src/config/seed-inference-profiles.ts +99 -29
  260. package/src/config/skills.ts +3 -96
  261. package/src/context/compactor.ts +1107 -0
  262. package/src/context/token-estimator.ts +34 -36
  263. package/src/context/window-manager.ts +197 -1520
  264. package/src/credential-execution/managed-catalog.ts +37 -0
  265. package/src/credential-health/credential-health-service.ts +280 -19
  266. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +33 -18
  267. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  268. package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
  269. package/src/daemon/approval-generators.ts +8 -6
  270. package/src/daemon/config-watcher.ts +94 -31
  271. package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
  272. package/src/daemon/conversation-agent-loop.ts +198 -11
  273. package/src/daemon/conversation-error.ts +171 -37
  274. package/src/daemon/conversation-lifecycle.ts +53 -40
  275. package/src/daemon/conversation-messaging.ts +25 -6
  276. package/src/daemon/conversation-process.ts +49 -12
  277. package/src/daemon/conversation-runtime-assembly.ts +25 -1
  278. package/src/daemon/conversation-slash.ts +12 -5
  279. package/src/daemon/conversation-store.ts +11 -4
  280. package/src/daemon/conversation-tool-setup.ts +39 -7
  281. package/src/daemon/conversation.ts +33 -8
  282. package/src/daemon/date-context.ts +40 -0
  283. package/src/daemon/external-plugins-bootstrap.ts +217 -181
  284. package/src/daemon/first-greeting.ts +22 -2
  285. package/src/daemon/guardian-action-generators.ts +1 -125
  286. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
  287. package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
  288. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
  289. package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
  290. package/src/daemon/handlers/config-a2a.ts +289 -0
  291. package/src/daemon/handlers/config-model.ts +6 -5
  292. package/src/daemon/handlers/config-slack-channel.ts +15 -3
  293. package/src/daemon/handlers/conversations.ts +1 -0
  294. package/src/daemon/handlers/shared.ts +14 -5
  295. package/src/daemon/handlers/skills.ts +111 -108
  296. package/src/daemon/history-repair.ts +28 -1
  297. package/src/daemon/host-app-control-proxy.ts +153 -27
  298. package/src/daemon/host-proxy-preactivation.ts +85 -18
  299. package/src/daemon/lifecycle.ts +89 -91
  300. package/src/daemon/meet-host-supervisor.ts +5 -4
  301. package/src/daemon/memory-v2-startup.ts +85 -0
  302. package/src/daemon/message-protocol.ts +1 -0
  303. package/src/daemon/message-types/conversations.ts +25 -0
  304. package/src/daemon/message-types/messages.ts +61 -0
  305. package/src/daemon/message-types/notifications.ts +21 -0
  306. package/src/daemon/message-types/subagents.ts +1 -0
  307. package/src/daemon/message-types/sync.ts +1 -0
  308. package/src/daemon/pkb-reminder-builder.test.ts +11 -54
  309. package/src/daemon/pkb-reminder-builder.ts +5 -20
  310. package/src/daemon/plugin-source-watcher.ts +146 -0
  311. package/src/daemon/process-message.ts +24 -3
  312. package/src/daemon/server.ts +11 -2
  313. package/src/daemon/skill-memory-refresh.ts +33 -0
  314. package/src/daemon/wake-target-adapter.ts +2 -0
  315. package/src/documents/document-store.ts +221 -3
  316. package/src/embedded/plugin-api.ts +40 -0
  317. package/src/export/__tests__/transcript-formatter.test.ts +121 -0
  318. package/src/export/transcript-formatter.ts +54 -20
  319. package/src/filing/filing-service.ts +39 -0
  320. package/src/heartbeat/__tests__/heartbeat-service.test.ts +135 -6
  321. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  322. package/src/heartbeat/heartbeat-service.ts +73 -189
  323. package/src/home/__tests__/feed-types.test.ts +80 -0
  324. package/src/home/feed-types.ts +36 -2
  325. package/src/home/post-connect-feed.ts +1 -0
  326. package/src/index.ts +18 -1
  327. package/src/ipc/cli-client.ts +147 -45
  328. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  329. package/src/mcp/client.ts +20 -4
  330. package/src/media/image-credentials.ts +3 -3
  331. package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
  332. package/src/memory/__tests__/conversation-queries.test.ts +483 -0
  333. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  334. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
  335. package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
  336. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
  337. package/src/memory/__tests__/message-content.test.ts +35 -0
  338. package/src/memory/bookmark-crud.ts +42 -10
  339. package/src/memory/context-search/sources/conversations.ts +62 -2
  340. package/src/memory/context-search/sources/workspace.ts +4 -0
  341. package/src/memory/conversation-crud.ts +63 -19
  342. package/src/memory/conversation-queries.ts +197 -11
  343. package/src/memory/conversation-title-service.ts +26 -4
  344. package/src/memory/db-init.ts +12 -0
  345. package/src/memory/delivery-crud.ts +152 -5
  346. package/src/memory/embedding-backend.ts +4 -4
  347. package/src/memory/external-conversation-store.ts +66 -5
  348. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +150 -12
  349. package/src/memory/graph/conversation-graph-memory.ts +49 -21
  350. package/src/memory/graph/tools.ts +9 -40
  351. package/src/memory/indexer.ts +34 -29
  352. package/src/memory/invite-store.ts +53 -0
  353. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
  354. package/src/memory/jobs/embed-concept-page.ts +20 -11
  355. package/src/memory/jobs-worker.ts +6 -1
  356. package/src/memory/llm-request-log-source-clickhouse.ts +24 -12
  357. package/src/memory/llm-request-log-source.ts +19 -52
  358. package/src/memory/llm-request-log-store.ts +92 -1
  359. package/src/memory/llm-usage-store.ts +125 -5
  360. package/src/memory/memory-retrospective-enqueue.ts +1 -20
  361. package/src/memory/memory-retrospective-job.ts +33 -6
  362. package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
  363. package/src/memory/message-content.ts +1 -1
  364. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  365. package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
  366. package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
  367. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  368. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  369. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  370. package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
  371. package/src/memory/migrations/251-a2a-tasks.ts +49 -0
  372. package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
  373. package/src/memory/migrations/index.ts +9 -0
  374. package/src/memory/migrations/registry.ts +16 -0
  375. package/src/memory/onboarding-events-store.ts +106 -0
  376. package/src/memory/schema/a2a.ts +15 -0
  377. package/src/memory/schema/bookmarks.ts +0 -2
  378. package/src/memory/schema/calls.ts +1 -0
  379. package/src/memory/schema/index.ts +1 -0
  380. package/src/memory/schema/inference.ts +3 -3
  381. package/src/memory/schema/infrastructure.ts +13 -0
  382. package/src/memory/turn-events-store.ts +127 -2
  383. package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
  384. package/src/memory/v2/__tests__/activation.test.ts +0 -8
  385. package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
  386. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
  387. package/src/memory/v2/__tests__/injection.test.ts +288 -11
  388. package/src/memory/v2/__tests__/migration.test.ts +87 -0
  389. package/src/memory/v2/__tests__/page-index.test.ts +83 -0
  390. package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
  391. package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
  392. package/src/memory/v2/__tests__/router.test.ts +15 -0
  393. package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
  394. package/src/memory/v2/__tests__/static-context.test.ts +12 -1
  395. package/src/memory/v2/activation-store.ts +14 -16
  396. package/src/memory/v2/cli-command-content.ts +19 -0
  397. package/src/memory/v2/cli-command-store.ts +304 -0
  398. package/src/memory/v2/frontmatter-sweep.ts +7 -1
  399. package/src/memory/v2/injection.ts +81 -26
  400. package/src/memory/v2/migration.ts +49 -19
  401. package/src/memory/v2/page-index.ts +63 -8
  402. package/src/memory/v2/prompts/router.ts +11 -8
  403. package/src/memory/v2/prompts/sweep.ts +2 -2
  404. package/src/memory/v2/qdrant.ts +135 -7
  405. package/src/memory/v2/router.ts +9 -8
  406. package/src/memory/v2/skill-store.ts +120 -35
  407. package/src/memory/v2/static-context.ts +4 -4
  408. package/src/memory/v2/types.ts +23 -0
  409. package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
  410. package/src/messaging/providers/a2a/deliver.ts +156 -0
  411. package/src/messaging/providers/gmail/client.ts +9 -2
  412. package/src/messaging/providers/index.ts +11 -2
  413. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  414. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  415. package/src/messaging/providers/slack/adapter.ts +43 -5
  416. package/src/messaging/providers/slack/client.ts +27 -0
  417. package/src/messaging/providers/slack/deep-link.ts +65 -0
  418. package/src/messaging/providers/slack/download.ts +104 -0
  419. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  420. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  421. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  422. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  423. package/src/messaging/providers/slack/types.ts +20 -1
  424. package/src/notifications/__tests__/broadcaster.test.ts +203 -0
  425. package/src/notifications/__tests__/decision-engine.test.ts +283 -0
  426. package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
  427. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  428. package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
  429. package/src/notifications/adapters/macos.ts +12 -2
  430. package/src/notifications/broadcaster.ts +29 -4
  431. package/src/notifications/conversation-pairing.ts +2 -1
  432. package/src/notifications/copy-composer.ts +17 -64
  433. package/src/notifications/decision-engine.ts +113 -45
  434. package/src/notifications/deterministic-checks.ts +96 -0
  435. package/src/notifications/emit-signal.ts +21 -1
  436. package/src/notifications/home-feed-side-effect.ts +138 -5
  437. package/src/notifications/signal.ts +3 -5
  438. package/src/notifications/types.ts +8 -0
  439. package/src/oauth/connection-resolver.ts +8 -4
  440. package/src/oauth/platform-connection.test.ts +43 -3
  441. package/src/oauth/platform-connection.ts +19 -6
  442. package/src/oauth/seed-providers.ts +10 -1
  443. package/src/permissions/checker.ts +2 -0
  444. package/src/permissions/ipc-risk-types.ts +1 -0
  445. package/src/permissions/question-prompter.test.ts +416 -0
  446. package/src/permissions/question-prompter.ts +294 -0
  447. package/src/platform/client.test.ts +1 -1
  448. package/src/platform/client.ts +1 -1
  449. package/src/plugin-api/constants.ts +26 -0
  450. package/src/plugin-api/index.ts +34 -1
  451. package/src/plugin-api/types.ts +104 -22
  452. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  453. package/src/plugins/defaults/compaction.ts +0 -4
  454. package/src/plugins/defaults/empty-response.ts +0 -2
  455. package/src/plugins/defaults/history-repair.ts +0 -2
  456. package/src/plugins/defaults/injectors.ts +74 -22
  457. package/src/plugins/defaults/llm-call.ts +0 -2
  458. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  459. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  460. package/src/plugins/defaults/persistence.ts +0 -2
  461. package/src/plugins/defaults/title-generate.ts +0 -5
  462. package/src/plugins/defaults/token-estimate.ts +0 -2
  463. package/src/plugins/defaults/tool-error.ts +0 -7
  464. package/src/plugins/defaults/tool-execute.ts +0 -2
  465. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  466. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  467. package/src/plugins/external-api.ts +104 -0
  468. package/src/plugins/external-plugin-loader.ts +187 -42
  469. package/src/plugins/feature-gate.ts +22 -0
  470. package/src/plugins/pipeline.ts +37 -0
  471. package/src/plugins/registry.ts +48 -80
  472. package/src/plugins/types.ts +40 -26
  473. package/src/plugins/user-loader.ts +21 -2
  474. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  475. package/src/proactive-artifact/job.test.ts +37 -5
  476. package/src/prompts/__tests__/system-prompt.test.ts +10 -43
  477. package/src/prompts/__tests__/task-progress-hint-section.test.ts +95 -0
  478. package/src/prompts/normalize-onboarding.ts +27 -0
  479. package/src/prompts/sections.ts +302 -0
  480. package/src/prompts/system-prompt.ts +63 -174
  481. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  482. package/src/prompts/templates/system-sections.ts +164 -0
  483. package/src/providers/__tests__/inference.test.ts +24 -7
  484. package/src/providers/anthropic/client.ts +28 -28
  485. package/src/providers/call-site-routing.ts +24 -6
  486. package/src/providers/connection-resolution.ts +68 -11
  487. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
  488. package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
  489. package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
  490. package/src/providers/inference/adapter-factory.ts +32 -6
  491. package/src/providers/inference/auth.ts +12 -0
  492. package/src/providers/inference/backfill.ts +14 -1
  493. package/src/providers/inference/connections.ts +159 -34
  494. package/src/providers/inference/resolve-auth.ts +14 -4
  495. package/src/providers/model-catalog.ts +249 -12
  496. package/src/providers/model-intents.ts +3 -3
  497. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
  498. package/src/providers/openai/chat-completions-provider.ts +169 -8
  499. package/src/providers/openrouter/client.ts +49 -4
  500. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -2
  501. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  502. package/src/providers/provider-availability.ts +17 -2
  503. package/src/providers/provider-catalog-visibility.ts +38 -0
  504. package/src/providers/provider-send-message.ts +27 -12
  505. package/src/providers/registry.ts +52 -15
  506. package/src/providers/retry.ts +47 -1
  507. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  508. package/src/runtime/agent-wake.ts +103 -15
  509. package/src/runtime/auth/route-policy.ts +21 -1
  510. package/src/runtime/btw-sidechain.ts +2 -0
  511. package/src/runtime/http-server.ts +7 -16
  512. package/src/runtime/http-types.ts +19 -47
  513. package/src/runtime/migrations/origin-mode.ts +1 -1
  514. package/src/runtime/pending-interactions.ts +1 -0
  515. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
  516. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
  517. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
  518. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +172 -23
  519. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
  520. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
  521. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  522. package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
  523. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  524. package/src/runtime/routes/acp-routes.ts +5 -3
  525. package/src/runtime/routes/auth-routes.ts +1 -1
  526. package/src/runtime/routes/bookmark-routes.ts +5 -3
  527. package/src/runtime/routes/btw-routes.ts +5 -1
  528. package/src/runtime/routes/channel-availability-routes.ts +126 -0
  529. package/src/runtime/routes/consolidation-routes.ts +100 -0
  530. package/src/runtime/routes/conversation-cli-routes.ts +44 -3
  531. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  532. package/src/runtime/routes/conversation-management-routes.ts +17 -42
  533. package/src/runtime/routes/conversation-query-routes.ts +99 -35
  534. package/src/runtime/routes/conversation-routes.ts +97 -11
  535. package/src/runtime/routes/documents-routes.ts +25 -86
  536. package/src/runtime/routes/group-routes.ts +5 -0
  537. package/src/runtime/routes/inbound-conversation.ts +28 -8
  538. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  539. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
  540. package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
  541. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  542. package/src/runtime/routes/index.ts +8 -0
  543. package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
  544. package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
  545. package/src/runtime/routes/inference-provider-connection-routes.ts +199 -22
  546. package/src/runtime/routes/integrations/a2a.ts +235 -0
  547. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  548. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  549. package/src/runtime/routes/integrations/twilio.ts +6 -13
  550. package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
  551. package/src/runtime/routes/notification-routes.ts +1 -1
  552. package/src/runtime/routes/oauth-commands-routes.ts +105 -15
  553. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  554. package/src/runtime/routes/question-routes.ts +259 -0
  555. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  556. package/src/runtime/routes/schedule-routes.ts +4 -7
  557. package/src/runtime/routes/subagents-routes.ts +98 -18
  558. package/src/runtime/routes/telemetry-routes.ts +27 -0
  559. package/src/runtime/routes/tts-routes.ts +27 -2
  560. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  561. package/src/runtime/routes/workspace-routes.ts +28 -0
  562. package/src/runtime/services/conversation-serializer.ts +39 -7
  563. package/src/runtime/sync/resource-sync-events.ts +93 -1
  564. package/src/schedule/schedule-store.ts +27 -2
  565. package/src/schedule/scheduler.ts +9 -1
  566. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  567. package/src/security/untrusted-content.ts +93 -8
  568. package/src/skills/catalog-files.ts +1 -1
  569. package/src/skills/catalog-install.ts +233 -116
  570. package/src/skills/clawhub.ts +70 -13
  571. package/src/skills/managed-store.ts +4 -119
  572. package/src/skills/skillssh-registry.ts +27 -48
  573. package/src/subagent/manager.ts +17 -7
  574. package/src/telemetry/types.ts +113 -1
  575. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  576. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  577. package/src/tools/apps/executors.ts +58 -7
  578. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  579. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  580. package/src/tools/browser/browser-execution.ts +15 -11
  581. package/src/tools/computer-use/definitions.ts +3 -3
  582. package/src/tools/credentials/vault.ts +1 -1
  583. package/src/tools/document/document-tool.ts +124 -1
  584. package/src/tools/filesystem/edit.ts +1 -1
  585. package/src/tools/filesystem/list.ts +1 -1
  586. package/src/tools/filesystem/read.ts +1 -1
  587. package/src/tools/filesystem/write.ts +5 -2
  588. package/src/tools/host-filesystem/transfer.ts +1 -1
  589. package/src/tools/host-terminal/host-shell.ts +1 -1
  590. package/src/tools/memory/register.ts +1 -9
  591. package/src/tools/permission-checker.ts +1 -1
  592. package/src/tools/registry.ts +17 -7
  593. package/src/tools/schedule/create.ts +2 -2
  594. package/src/tools/schema-transforms.ts +7 -2
  595. package/src/tools/side-effects.ts +1 -0
  596. package/src/tools/skills/delete-managed.ts +4 -4
  597. package/src/tools/skills/execute.ts +1 -1
  598. package/src/tools/skills/scaffold-managed.ts +3 -2
  599. package/src/tools/subagent/notify-parent.ts +1 -1
  600. package/src/tools/system/request-permission.ts +2 -2
  601. package/src/tools/terminal/safe-env.ts +60 -1
  602. package/src/tools/tool-manifest.ts +2 -0
  603. package/src/tools/types.ts +107 -21
  604. package/src/tools/ui-surface/definitions.ts +6 -5
  605. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  606. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  607. package/src/types/onboarding-context.ts +2 -0
  608. package/src/util/errors.ts +17 -0
  609. package/src/util/platform.ts +10 -0
  610. package/src/watcher/__tests__/engine.test.ts +22 -0
  611. package/src/watcher/engine.ts +6 -2
  612. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
  613. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
  614. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
  615. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  616. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  617. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  618. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  619. package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
  620. package/src/workspace/migrations/registry.ts +10 -0
  621. package/src/workspace/migrations/runner.ts +39 -9
  622. package/src/workspace/migrations/types.ts +4 -0
  623. package/examples/plugins/echo/bun.lock +0 -25
  624. package/src/__tests__/context-window-manager.test.ts +0 -2481
  625. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
  626. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  627. package/src/context/prompts/compact.md +0 -26
  628. package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
  629. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  630. package/src/runtime/guardian-action-conversation-turn.ts +0 -99
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -62,6 +62,7 @@
62
62
  "openai": "6.29.0",
63
63
  "pino": "9.14.0",
64
64
  "pino-pretty": "13.1.3",
65
+ "semver": "7.8.0",
65
66
  "playwright": "1.58.2",
66
67
  "postgres": "3.4.8",
67
68
  "rrule": "2.8.1",
@@ -94,6 +95,7 @@
94
95
  "@types/archiver": "7.0.0",
95
96
  "@types/bun": "1.3.10",
96
97
  "@types/node": "25.5.0",
98
+ "@types/semver": "7.5.8",
97
99
  "@types/uuid": "10.0.0",
98
100
  "drizzle-kit": "0.30.6",
99
101
  "eslint": "10.0.3",
@@ -183,14 +183,26 @@ function toJSONSchemaObject(
183
183
  async function collectRoutesFromModules(): Promise<RouteEntry[]> {
184
184
  const routes: RouteEntry[] = [];
185
185
 
186
- const files = (await readdir(ROUTES_DIR, { recursive: true })).filter(
187
- (f) =>
188
- typeof f === "string" &&
189
- f.endsWith(".ts") &&
190
- !f.endsWith(".test.ts") &&
191
- !f.endsWith(".benchmark.test.ts") &&
192
- !f.includes("node_modules"),
193
- );
186
+ // Skip the `index.ts` barrel: it re-exports every other route module's
187
+ // ROUTES into a single combined array, so importing it would double-count
188
+ // every entry. The duplicate `method:endpoint` keys are deduped later by
189
+ // first-seen, but the surviving entry's `sourceModule` (used to derive
190
+ // OpenAPI `tags`) depends on `readdir` order — which is filesystem
191
+ // dependent and diverges between local sandbox and the CI runner, making
192
+ // the generator non-reproducible. Sort the file list as well so directory
193
+ // entry order can never affect the output.
194
+ const files = (await readdir(ROUTES_DIR, { recursive: true }))
195
+ .filter(
196
+ (f) =>
197
+ typeof f === "string" &&
198
+ f.endsWith(".ts") &&
199
+ !f.endsWith(".test.ts") &&
200
+ !f.endsWith(".benchmark.test.ts") &&
201
+ !f.includes("node_modules") &&
202
+ f !== "index.ts" &&
203
+ !f.endsWith("/index.ts"),
204
+ )
205
+ .sort();
194
206
 
195
207
  for (const file of files) {
196
208
  const filePath = join(ROUTES_DIR, file);
@@ -204,8 +216,20 @@ async function collectRoutesFromModules(): Promise<RouteEntry[]> {
204
216
  continue;
205
217
  }
206
218
 
207
- if ("ROUTES" in mod && Array.isArray(mod.ROUTES)) {
208
- for (const raw of mod.ROUTES) {
219
+ // Collect every export whose name is `ROUTES` or ends in `_ROUTES`.
220
+ // A handful of route files (e.g. `channel-route-definitions.ts`,
221
+ // `contact-prompt-routes.ts`) export under domain-prefixed names like
222
+ // `CHANNEL_ROUTES` and `CONTACT_PROMPT_ROUTES` rather than the
223
+ // canonical `ROUTES`. Without this fan-out the only way those routes
224
+ // reached the spec was via the `index.ts` barrel — which is excluded
225
+ // above for reproducibility.
226
+ const exportNames = Object.keys(mod)
227
+ .filter((k) => k === "ROUTES" || k.endsWith("_ROUTES"))
228
+ .sort();
229
+ for (const name of exportNames) {
230
+ const arr = mod[name];
231
+ if (!Array.isArray(arr)) continue;
232
+ for (const raw of arr) {
209
233
  const result = RouteEntrySchema.safeParse({
210
234
  ...(typeof raw === "object" && raw !== null ? raw : {}),
211
235
  sourceModule: file,
@@ -545,6 +569,55 @@ async function main() {
545
569
  }
546
570
  if (existing !== yamlOutput) {
547
571
  console.error("openapi.yaml is stale. Run: bun run generate:openapi");
572
+ // Emit the first byte-level divergence and a windowed diff around it
573
+ // so CI logs are actionable without a follow-up local repro.
574
+ const maxLen = Math.max(existing.length, yamlOutput.length);
575
+ let firstDiff = -1;
576
+ for (let i = 0; i < maxLen; i++) {
577
+ if (existing[i] !== yamlOutput[i]) {
578
+ firstDiff = i;
579
+ break;
580
+ }
581
+ }
582
+ if (firstDiff >= 0) {
583
+ const lineNo = (existing.slice(0, firstDiff).match(/\n/g) ?? []).length + 1;
584
+ const winStart = Math.max(0, firstDiff - 120);
585
+ const winEnd = Math.min(maxLen, firstDiff + 120);
586
+ console.error(`First divergence at byte ${firstDiff} (~line ${lineNo}):`);
587
+ console.error(` existing[${winStart}..${winEnd}]:`);
588
+ console.error(` ${JSON.stringify(existing.slice(winStart, winEnd))}`);
589
+ console.error(` generated[${winStart}..${winEnd}]:`);
590
+ console.error(` ${JSON.stringify(yamlOutput.slice(winStart, winEnd))}`);
591
+ }
592
+ // Also flag which path operations are present in one but not the other —
593
+ // the common failure mode is a missing or duplicated route entry, and
594
+ // the path keys are the actionable thing for the human reading the log.
595
+ const pathsRe = /^\s\s(\/\S+):/gm;
596
+ const existingPaths = new Set(
597
+ Array.from(existing.matchAll(pathsRe), (m) => m[1]),
598
+ );
599
+ const generatedPaths = new Set(
600
+ Array.from(yamlOutput.matchAll(pathsRe), (m) => m[1]),
601
+ );
602
+ const inExistingOnly = [...existingPaths].filter(
603
+ (p) => !generatedPaths.has(p),
604
+ );
605
+ const inGeneratedOnly = [...generatedPaths].filter(
606
+ (p) => !existingPaths.has(p),
607
+ );
608
+ if (inExistingOnly.length || inGeneratedOnly.length) {
609
+ console.error(
610
+ `Path set drift: existing has ${existingPaths.size} paths, generated has ${generatedPaths.size}`,
611
+ );
612
+ if (inGeneratedOnly.length) {
613
+ console.error(` Only in generated (missing from committed yaml):`);
614
+ for (const p of inGeneratedOnly.slice(0, 20)) console.error(` + ${p}`);
615
+ }
616
+ if (inExistingOnly.length) {
617
+ console.error(` Only in existing (stale entries in committed yaml):`);
618
+ for (const p of inExistingOnly.slice(0, 20)) console.error(` - ${p}`);
619
+ }
620
+ }
548
621
  process.exit(1);
549
622
  }
550
623
  console.log("openapi.yaml is up to date.");
@@ -97,8 +97,8 @@ function projectProvider(entry: ProviderCatalogEntry): Record<string, unknown> {
97
97
  projected.apiKeyPlaceholder = entry.apiKeyPlaceholder;
98
98
  if (entry.credentialsGuide !== undefined)
99
99
  projected.credentialsGuide = entry.credentialsGuide;
100
- if (entry.supportsManagedAuth !== undefined)
101
- projected.supportsManagedAuth = entry.supportsManagedAuth;
100
+ if (entry.supportsPlatformAuth !== undefined)
101
+ projected.supportsPlatformAuth = entry.supportsPlatformAuth;
102
102
  projected.defaultModel = entry.defaultModel;
103
103
  projected.models = entry.models.map(projectModel);
104
104
  // NOTE: `apiKeyUrl` intentionally omitted — clients use
@@ -1,17 +1,24 @@
1
1
  #!/usr/bin/env bun
2
2
  /**
3
- * Generate `meta/web-search-provider-catalog.json` from the canonical
4
- * `SEARCH_PROVIDER_CATALOG` in
3
+ * Generate the client-facing web-search-provider catalog JSON from the
4
+ * canonical `SEARCH_PROVIDER_CATALOG` in
5
5
  * `assistant/src/providers/search-provider-catalog.ts`.
6
6
  *
7
- * Companion to `sync-llm-catalog.ts`. The meta JSON is the cross-package
8
- * artifact consumed by:
7
+ * Two byte-identical copies are written:
8
+ * - `meta/web-search-provider-catalog.json` primary checked-in artifact,
9
+ * consumed by:
10
+ * - `cli/src/__tests__/search-provider-env-var-parity.test.ts`
11
+ * (drift guard for the CLI's hardcoded env-var mirror).
12
+ * - Downstream `vellum-assistant-platform/web/src/lib/generated/
13
+ * web-search-provider-catalog.json` (manually sync'd today; the
14
+ * scheduled sync workflow is a planned follow-up).
15
+ * - `clients/shared/Resources/web-search-provider-catalog.json` — SwiftPM
16
+ * resource bundled into `VellumAssistantShared`. SwiftPM cannot reach
17
+ * files outside a target's source directory, so this mirror is
18
+ * necessary; both files are produced by the same generator and
19
+ * asserted equal by the parity test, making drift impossible.
9
20
  *
10
- * - `cli/src/__tests__/search-provider-env-var-parity.test.ts`
11
- * (drift guard for the CLI's hardcoded env-var mirror).
12
- * - Downstream `vellum-assistant-platform/web/src/lib/generated/
13
- * web-search-provider-catalog.json` (manually sync'd today; scheduled
14
- * sync workflow is a planned follow-up).
21
+ * Companion to `sync-llm-catalog.ts`; same dual-write pattern.
15
22
  *
16
23
  * Usage:
17
24
  * cd assistant && bun run scripts/sync-web-search-catalog.ts
@@ -20,7 +27,7 @@
20
27
  */
21
28
 
22
29
  import { readFile, writeFile } from "node:fs/promises";
23
- import { join, resolve } from "node:path";
30
+ import { join, relative, resolve } from "node:path";
24
31
 
25
32
  import {
26
33
  SEARCH_PROVIDER_CATALOG,
@@ -28,7 +35,10 @@ import {
28
35
  } from "../src/providers/search-provider-catalog.js";
29
36
 
30
37
  const ROOT = resolve(import.meta.dir, "../..");
31
- const OUTPUT_PATH = join(ROOT, "meta/web-search-provider-catalog.json");
38
+ const OUTPUT_PATHS = [
39
+ join(ROOT, "meta/web-search-provider-catalog.json"),
40
+ join(ROOT, "clients/shared/Resources/web-search-provider-catalog.json"),
41
+ ] as const;
32
42
 
33
43
  /**
34
44
  * Bumped when the *shape* of the client catalog JSON changes in a way
@@ -84,24 +94,36 @@ async function main(): Promise<void> {
84
94
  const next = generate();
85
95
 
86
96
  if (checkMode) {
87
- let current = "";
88
- try {
89
- current = await readFile(OUTPUT_PATH, "utf8");
90
- } catch {
91
- // File doesn't exist yet — treat as stale.
92
- }
93
- if (current !== next) {
94
- console.error(
95
- `\n${OUTPUT_PATH} is out of sync with SEARCH_PROVIDER_CATALOG.\n` +
96
- `Run: cd assistant && bun run sync:web-search-catalog\n`,
97
- );
98
- process.exit(1);
97
+ let anyStale = false;
98
+ for (const path of OUTPUT_PATHS) {
99
+ const rel = relative(ROOT, path);
100
+ let existing = "";
101
+ try {
102
+ existing = await readFile(path, "utf-8");
103
+ } catch {
104
+ console.error(
105
+ `${rel} does not exist. Run: bun run sync:web-search-catalog`,
106
+ );
107
+ anyStale = true;
108
+ continue;
109
+ }
110
+ if (existing !== next) {
111
+ console.error(
112
+ `${rel} is stale. Run: bun run sync:web-search-catalog`,
113
+ );
114
+ anyStale = true;
115
+ continue;
116
+ }
117
+ console.log(`${rel} is up to date.`);
99
118
  }
119
+ if (anyStale) process.exit(1);
100
120
  return;
101
121
  }
102
122
 
103
- await writeFile(OUTPUT_PATH, next, "utf8");
104
- console.log(`Wrote ${OUTPUT_PATH}`);
123
+ for (const path of OUTPUT_PATHS) {
124
+ await writeFile(path, next, "utf-8");
125
+ console.log(`Wrote ${relative(ROOT, path)}`);
126
+ }
105
127
  }
106
128
 
107
129
  await main();
@@ -14,10 +14,18 @@ describe("shouldRescaleImage", () => {
14
14
  expect(shouldRescaleImage({ width: 1200, height: 800 }, 50_000)).toBe(
15
15
  false,
16
16
  );
17
- // Even a large file is fine as long as dimensions are within limits —
18
- // Anthropic's constraint is per-side pixels, not bytes.
17
+ });
18
+
19
+ it("rescales when raw bytes would inflate past Anthropic's 5 MB base64 cap", () => {
20
+ // Regression: an oversized image retained across compaction was rejected
21
+ // with "image.source.base64: image exceeds 5 MB maximum" even though
22
+ // dimensions were within the 1568 px cap. base64 inflates raw bytes by
23
+ // 4/3, so anything over ~3.5 MB raw risks crossing the 5 MB API limit.
19
24
  expect(shouldRescaleImage({ width: 1568, height: 1568 }, 5_000_000)).toBe(
20
- false,
25
+ true,
26
+ );
27
+ expect(shouldRescaleImage({ width: 1200, height: 800 }, 4_000_000)).toBe(
28
+ true,
21
29
  );
22
30
  });
23
31
 
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Tests for the `agent_loop_exit` instrumentation added in this PR.
3
+ *
4
+ * Coverage targets:
5
+ * 1. **One emit per run** — the idempotency guard fires once, even if the
6
+ * code path would otherwise reach two emit sites (the empty-response
7
+ * throw → catch-block fallback case).
8
+ * 2. **Reason matches break site** — for each reachable break site, the
9
+ * emitted reason is the one documented in `AgentLoopExitReason`.
10
+ * 3. **Always the last AgentEvent of the run** — consumers can rely on
11
+ * positional ordering to find it.
12
+ *
13
+ * Sites not exercised here (`empty_response_exhausted`, `aborted_via_error`)
14
+ * require deeper provider fakery and are best covered by integration tests
15
+ * once we wire up the empty-response pipeline mock.
16
+ */
17
+ import { describe, expect, test } from "bun:test";
18
+
19
+ import type {
20
+ AgentEvent,
21
+ CheckpointDecision,
22
+ CheckpointInfo,
23
+ } from "../agent/loop.js";
24
+ import { AgentLoop } from "../agent/loop.js";
25
+ import type {
26
+ Message,
27
+ Provider,
28
+ ProviderResponse,
29
+ SendMessageOptions,
30
+ ToolDefinition,
31
+ } from "../providers/types.js";
32
+
33
+ // ---------------------------------------------------------------------------
34
+ // Helpers (mirrored from agent-loop.test.ts so this file is self-contained)
35
+ // ---------------------------------------------------------------------------
36
+
37
+ function createMockProvider(responses: ProviderResponse[]): {
38
+ provider: Provider;
39
+ } {
40
+ let callIndex = 0;
41
+ const provider: Provider = {
42
+ name: "mock",
43
+ async sendMessage(
44
+ _messages: Message[],
45
+ _tools?: ToolDefinition[],
46
+ _systemPrompt?: string,
47
+ _options?: SendMessageOptions,
48
+ ): Promise<ProviderResponse> {
49
+ const response = responses[callIndex] ?? responses[responses.length - 1];
50
+ callIndex++;
51
+ return response;
52
+ },
53
+ };
54
+ return { provider };
55
+ }
56
+
57
+ function textResponse(text: string): ProviderResponse {
58
+ return {
59
+ content: [{ type: "text", text }],
60
+ model: "mock-model",
61
+ usage: { inputTokens: 10, outputTokens: 5 },
62
+ stopReason: "end_turn",
63
+ };
64
+ }
65
+
66
+ function toolUseResponse(
67
+ id: string,
68
+ name: string,
69
+ input: Record<string, unknown>,
70
+ ): ProviderResponse {
71
+ return {
72
+ content: [{ type: "tool_use", id, name, input }],
73
+ model: "mock-model",
74
+ usage: { inputTokens: 10, outputTokens: 5 },
75
+ stopReason: "tool_use",
76
+ };
77
+ }
78
+
79
+ const dummyTools: ToolDefinition[] = [
80
+ {
81
+ name: "read_file",
82
+ description: "Read a file",
83
+ input_schema: { type: "object", properties: { path: { type: "string" } } },
84
+ },
85
+ ];
86
+
87
+ const userMessage: Message = {
88
+ role: "user",
89
+ content: [{ type: "text", text: "Hello" }],
90
+ };
91
+
92
+ function lastExitEvent(
93
+ events: AgentEvent[],
94
+ ): Extract<AgentEvent, { type: "agent_loop_exit" }> | undefined {
95
+ return events.find(
96
+ (e): e is Extract<AgentEvent, { type: "agent_loop_exit" }> =>
97
+ e.type === "agent_loop_exit",
98
+ );
99
+ }
100
+
101
+ function countExitEvents(events: AgentEvent[]): number {
102
+ return events.filter((e) => e.type === "agent_loop_exit").length;
103
+ }
104
+
105
+ // ---------------------------------------------------------------------------
106
+ // Tests
107
+ // ---------------------------------------------------------------------------
108
+
109
+ describe("AgentLoop exit-reason instrumentation", () => {
110
+ test("emits exit event exactly once with 'no_tool_calls' on plain text response", async () => {
111
+ const { provider } = createMockProvider([textResponse("Hi there!")]);
112
+ const loop = new AgentLoop(provider, "system prompt");
113
+
114
+ const events: AgentEvent[] = [];
115
+ await loop.run([userMessage], (e) => {
116
+ events.push(e);
117
+ });
118
+
119
+ expect(countExitEvents(events)).toBe(1);
120
+ const exit = lastExitEvent(events);
121
+ expect(exit?.reason).toBe("no_tool_calls");
122
+ });
123
+
124
+ test("agent_loop_exit is the last event emitted", async () => {
125
+ const { provider } = createMockProvider([textResponse("Hi there!")]);
126
+ const loop = new AgentLoop(provider, "system prompt");
127
+
128
+ const events: AgentEvent[] = [];
129
+ await loop.run([userMessage], (e) => {
130
+ events.push(e);
131
+ });
132
+
133
+ expect(events.length).toBeGreaterThan(0);
134
+ expect(events[events.length - 1].type).toBe("agent_loop_exit");
135
+ });
136
+
137
+ test("emits 'aborted_pre_call' when signal is already aborted at run start", async () => {
138
+ const { provider } = createMockProvider([textResponse("never sent")]);
139
+ const loop = new AgentLoop(provider, "system prompt");
140
+
141
+ const controller = new AbortController();
142
+ controller.abort();
143
+
144
+ const events: AgentEvent[] = [];
145
+ await loop.run([userMessage], (e) => { events.push(e); }, controller.signal);
146
+
147
+ expect(countExitEvents(events)).toBe(1);
148
+ expect(lastExitEvent(events)?.reason).toBe("aborted_pre_call");
149
+ });
150
+
151
+ test("emits 'yield_to_user' when tool result requests yieldToUser", async () => {
152
+ const { provider } = createMockProvider([
153
+ toolUseResponse("t1", "read_file", { path: "/a.txt" }),
154
+ ]);
155
+ const toolExecutor = async () => ({
156
+ content: "ok",
157
+ isError: false,
158
+ yieldToUser: true,
159
+ });
160
+ const loop = new AgentLoop(
161
+ provider,
162
+ "system",
163
+ {},
164
+ dummyTools,
165
+ toolExecutor,
166
+ );
167
+
168
+ const events: AgentEvent[] = [];
169
+ await loop.run([userMessage], (e) => { events.push(e); });
170
+
171
+ expect(countExitEvents(events)).toBe(1);
172
+ expect(lastExitEvent(events)?.reason).toBe("yield_to_user");
173
+ });
174
+
175
+ test("emits 'checkpoint_yield' when onCheckpoint returns 'yield'", async () => {
176
+ const { provider } = createMockProvider([
177
+ toolUseResponse("t1", "read_file", { path: "/a.txt" }),
178
+ textResponse("never reached"),
179
+ ]);
180
+ const toolExecutor = async () => ({ content: "ok", isError: false });
181
+ const loop = new AgentLoop(
182
+ provider,
183
+ "system",
184
+ {},
185
+ dummyTools,
186
+ toolExecutor,
187
+ );
188
+
189
+ const onCheckpoint = (_info: CheckpointInfo): CheckpointDecision =>
190
+ "yield";
191
+
192
+ const events: AgentEvent[] = [];
193
+ await loop.run(
194
+ [userMessage],
195
+ (e) => { events.push(e); },
196
+ undefined,
197
+ undefined,
198
+ onCheckpoint,
199
+ );
200
+
201
+ expect(countExitEvents(events)).toBe(1);
202
+ expect(lastExitEvent(events)?.reason).toBe("checkpoint_yield");
203
+ });
204
+
205
+ test("emits 'error' when provider throws an unhandled error", async () => {
206
+ const provider: Provider = {
207
+ name: "broken",
208
+ async sendMessage(): Promise<ProviderResponse> {
209
+ throw new Error("provider exploded");
210
+ },
211
+ };
212
+ const loop = new AgentLoop(provider, "system prompt");
213
+
214
+ const events: AgentEvent[] = [];
215
+ await loop.run([userMessage], (e) => { events.push(e); });
216
+
217
+ expect(countExitEvents(events)).toBe(1);
218
+ expect(lastExitEvent(events)?.reason).toBe("error");
219
+ });
220
+
221
+ test("does not double-emit when multiple exit conditions stack", async () => {
222
+ // Tool returns yieldToUser AND the controller is aborted post-response —
223
+ // the first reached condition wins, but the guard prevents a second
224
+ // emit even if subsequent code paths attempt one.
225
+ const { provider } = createMockProvider([
226
+ toolUseResponse("t1", "read_file", { path: "/a.txt" }),
227
+ ]);
228
+ const toolExecutor = async () => ({
229
+ content: "ok",
230
+ isError: false,
231
+ yieldToUser: true,
232
+ });
233
+ const loop = new AgentLoop(
234
+ provider,
235
+ "system",
236
+ {},
237
+ dummyTools,
238
+ toolExecutor,
239
+ );
240
+
241
+ const events: AgentEvent[] = [];
242
+ await loop.run([userMessage], (e) => { events.push(e); });
243
+
244
+ expect(countExitEvents(events)).toBe(1);
245
+ });
246
+
247
+ test("emits 'aborted_during_tools' when signal aborts after tool execution", async () => {
248
+ const controller = new AbortController();
249
+ const { provider } = createMockProvider([
250
+ toolUseResponse("t1", "read_file", { path: "/a.txt" }),
251
+ ]);
252
+ // Abort the signal inside the tool executor so by the time the loop
253
+ // re-checks signal.aborted post-tools the abort has landed.
254
+ const toolExecutor = async () => {
255
+ controller.abort();
256
+ return { content: "ok", isError: false };
257
+ };
258
+ const loop = new AgentLoop(
259
+ provider,
260
+ "system",
261
+ {},
262
+ dummyTools,
263
+ toolExecutor,
264
+ );
265
+
266
+ const events: AgentEvent[] = [];
267
+ await loop.run([userMessage], (e) => { events.push(e); }, controller.signal);
268
+
269
+ expect(countExitEvents(events)).toBe(1);
270
+ expect(lastExitEvent(events)?.reason).toBe("aborted_during_tools");
271
+ });
272
+ });