@vellumai/assistant 0.8.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (506) hide show
  1. package/ARCHITECTURE.md +2 -7
  2. package/Dockerfile +75 -1
  3. package/bun.lock +11 -1
  4. package/docker-entrypoint.sh +5 -0
  5. package/docker-init-apt-root.sh +94 -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 +325 -3
  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-wake-disk-pressure-callsite.test.ts +131 -0
  21. package/src/__tests__/anthropic-provider.test.ts +45 -0
  22. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  23. package/src/__tests__/app-executors.test.ts +220 -4
  24. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  25. package/src/__tests__/bundled-asset.test.ts +6 -6
  26. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  27. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  28. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  29. package/src/__tests__/clawhub.test.ts +75 -16
  30. package/src/__tests__/compactor-tail-resolution.test.ts +41 -0
  31. package/src/__tests__/config-schema.test.ts +21 -0
  32. package/src/__tests__/config-set-route.test.ts +80 -0
  33. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  34. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  35. package/src/__tests__/context-search-conversations-source.test.ts +117 -2
  36. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  37. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  38. package/src/__tests__/context-token-estimator.test.ts +1 -0
  39. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  40. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  41. package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
  42. package/src/__tests__/conversation-agent-loop.test.ts +2 -0
  43. package/src/__tests__/conversation-error.test.ts +42 -3
  44. package/src/__tests__/conversation-fork-crud.test.ts +82 -0
  45. package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
  46. package/src/__tests__/conversation-lifecycle.test.ts +173 -0
  47. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  48. package/src/__tests__/conversation-pairing.test.ts +54 -0
  49. package/src/__tests__/conversation-process-callsite.test.ts +4 -1
  50. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  51. package/src/__tests__/conversation-queue.test.ts +4 -1
  52. package/src/__tests__/conversation-runtime-assembly.test.ts +76 -9
  53. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  54. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  55. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  56. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  57. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  59. package/src/__tests__/credential-security-invariants.test.ts +3 -2
  60. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  61. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  62. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  63. package/src/__tests__/dm-backfill.test.ts +121 -10
  64. package/src/__tests__/document-tool-security.test.ts +258 -0
  65. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  66. package/src/__tests__/edit-propagation.test.ts +33 -0
  67. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  68. package/src/__tests__/external-plugin-loader.test.ts +60 -36
  69. package/src/__tests__/filing-service.test.ts +140 -0
  70. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  71. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
  72. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  73. package/src/__tests__/helpers/wait-for.ts +21 -0
  74. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  75. package/src/__tests__/history-repair.test.ts +73 -0
  76. package/src/__tests__/host-app-control-proxy.test.ts +266 -10
  77. package/src/__tests__/image-credentials.test.ts +1 -1
  78. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  79. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
  80. package/src/__tests__/inference-profile-reaper.test.ts +4 -2
  81. package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
  82. package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
  83. package/src/__tests__/injector-chain.test.ts +10 -8
  84. package/src/__tests__/install-skill-routing.test.ts +155 -37
  85. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +92 -3
  86. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  87. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  88. package/src/__tests__/llm-catalog-parity.test.ts +55 -13
  89. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +34 -0
  90. package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
  91. package/src/__tests__/llm-usage-store.test.ts +114 -0
  92. package/src/__tests__/managed-profile-guard.test.ts +31 -29
  93. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  94. package/src/__tests__/managed-store.test.ts +84 -192
  95. package/src/__tests__/media-generate-image.test.ts +1 -1
  96. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  97. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  98. package/src/__tests__/oauth-commands-routes.test.ts +168 -16
  99. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  100. package/src/__tests__/openai-provider.test.ts +24 -0
  101. package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
  102. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  103. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  104. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +1 -1
  105. package/src/__tests__/platform.test.ts +2 -0
  106. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  107. package/src/__tests__/plugin-bootstrap.test.ts +10 -36
  108. package/src/__tests__/plugin-external-api.test.ts +68 -0
  109. package/src/__tests__/plugin-registry.test.ts +0 -77
  110. package/src/__tests__/plugin-route-contribution.test.ts +0 -1
  111. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  112. package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
  113. package/src/__tests__/plugin-types.test.ts +3 -13
  114. package/src/__tests__/process-message-background-slack.test.ts +8 -1
  115. package/src/__tests__/process-message-display-content.test.ts +421 -0
  116. package/src/__tests__/provider-catalog-visibility.test.ts +142 -0
  117. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  118. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +8 -8
  119. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  120. package/src/__tests__/schedule-routes.test.ts +50 -3
  121. package/src/__tests__/schedule-store.test.ts +94 -0
  122. package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
  123. package/src/__tests__/schema-transforms.test.ts +20 -0
  124. package/src/__tests__/search-skills-unified.test.ts +0 -5
  125. package/src/__tests__/server-history-render.test.ts +43 -0
  126. package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
  127. package/src/__tests__/skill-load-tool.test.ts +27 -89
  128. package/src/__tests__/skill-memory.test.ts +23 -3
  129. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  130. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  131. package/src/__tests__/skills-install-extract.test.ts +49 -38
  132. package/src/__tests__/skills-install-staging.test.ts +159 -0
  133. package/src/__tests__/skills-uninstall.test.ts +9 -41
  134. package/src/__tests__/skills.test.ts +51 -58
  135. package/src/__tests__/slack-channel-config.test.ts +9 -0
  136. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  137. package/src/__tests__/system-prompt.test.ts +737 -63
  138. package/src/__tests__/terminal-tools.test.ts +28 -1
  139. package/src/__tests__/thread-backfill.test.ts +557 -27
  140. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  141. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  142. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  143. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  144. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  145. package/src/__tests__/tool-executor.test.ts +16 -4
  146. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  147. package/src/__tests__/turn-events-store.test.ts +256 -0
  148. package/src/__tests__/twilio-routes.test.ts +4 -0
  149. package/src/__tests__/user-plugin-loader.test.ts +0 -7
  150. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  151. package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
  152. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
  153. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
  154. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  155. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  156. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  157. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  158. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  159. package/src/acp/resolve-agent.ts +1 -1
  160. package/src/agent/image-optimize.ts +13 -5
  161. package/src/calls/voice-session-bridge.ts +61 -42
  162. package/src/channels/types.ts +108 -0
  163. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  164. package/src/cli/commands/__tests__/changelog.test.ts +304 -319
  165. package/src/cli/commands/__tests__/schedules.test.ts +491 -0
  166. package/src/cli/commands/changelog.ts +106 -42
  167. package/src/cli/commands/conversations.ts +102 -17
  168. package/src/cli/commands/default-action.ts +10 -53
  169. package/src/cli/commands/notifications.ts +329 -317
  170. package/src/cli/commands/plugins.ts +185 -0
  171. package/src/cli/commands/schedules.ts +391 -0
  172. package/src/cli/commands/telemetry.ts +40 -0
  173. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  174. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  175. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  176. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  177. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  178. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  179. package/src/cli/lib/cli-colors.ts +12 -0
  180. package/src/cli/lib/confirm-prompt.ts +79 -0
  181. package/src/cli/lib/install-from-github.ts +304 -0
  182. package/src/cli/lib/list-installed-plugins.ts +137 -0
  183. package/src/cli/lib/uninstall-plugin.ts +82 -0
  184. package/src/cli/lib/unknown-command.ts +111 -0
  185. package/src/cli/program.ts +38 -2
  186. package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
  187. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  188. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  189. package/src/config/bundled-skills/document/SKILL.md +23 -3
  190. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  191. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  192. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  193. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  194. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  195. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  196. package/src/config/bundled-tool-registry.ts +6 -0
  197. package/src/config/feature-flag-registry.json +41 -1
  198. package/src/config/loader.ts +64 -38
  199. package/src/config/schema.ts +7 -10
  200. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  201. package/src/config/schemas/channels.ts +8 -0
  202. package/src/config/schemas/compaction.ts +28 -0
  203. package/src/config/schemas/heartbeat.ts +9 -0
  204. package/src/config/schemas/llm-request-logs.ts +31 -7
  205. package/src/config/schemas/llm.ts +3 -0
  206. package/src/config/schemas/memory-retrieval.ts +18 -0
  207. package/src/config/schemas/tools.ts +14 -0
  208. package/src/config/skills.ts +3 -96
  209. package/src/context/compactor.ts +1047 -0
  210. package/src/context/token-estimator.ts +2 -2
  211. package/src/context/window-manager.ts +197 -1520
  212. package/src/credential-execution/managed-catalog.ts +37 -0
  213. package/src/credential-health/credential-health-service.ts +280 -19
  214. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +34 -0
  215. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  216. package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
  217. package/src/daemon/approval-generators.ts +8 -6
  218. package/src/daemon/config-watcher.ts +94 -31
  219. package/src/daemon/conversation-agent-loop.ts +169 -9
  220. package/src/daemon/conversation-error.ts +171 -37
  221. package/src/daemon/conversation-lifecycle.ts +53 -40
  222. package/src/daemon/conversation-messaging.ts +25 -6
  223. package/src/daemon/conversation-process.ts +49 -12
  224. package/src/daemon/conversation-runtime-assembly.ts +16 -1
  225. package/src/daemon/conversation-slash.ts +12 -5
  226. package/src/daemon/conversation-store.ts +11 -4
  227. package/src/daemon/conversation-tool-setup.ts +39 -7
  228. package/src/daemon/conversation.ts +33 -1
  229. package/src/daemon/external-plugins-bootstrap.ts +217 -181
  230. package/src/daemon/first-greeting.ts +22 -2
  231. package/src/daemon/handlers/config-model.ts +6 -5
  232. package/src/daemon/handlers/config-slack-channel.ts +15 -3
  233. package/src/daemon/handlers/shared.ts +14 -5
  234. package/src/daemon/handlers/skills.ts +111 -108
  235. package/src/daemon/history-repair.ts +28 -1
  236. package/src/daemon/host-app-control-proxy.ts +98 -23
  237. package/src/daemon/lifecycle.ts +45 -35
  238. package/src/daemon/meet-host-supervisor.ts +5 -4
  239. package/src/daemon/memory-v2-startup.ts +49 -0
  240. package/src/daemon/message-protocol.ts +1 -0
  241. package/src/daemon/message-types/conversations.ts +25 -0
  242. package/src/daemon/message-types/messages.ts +61 -0
  243. package/src/daemon/message-types/subagents.ts +1 -0
  244. package/src/daemon/message-types/sync.ts +1 -0
  245. package/src/daemon/pkb-reminder-builder.test.ts +1 -1
  246. package/src/daemon/pkb-reminder-builder.ts +1 -1
  247. package/src/daemon/plugin-source-watcher.ts +146 -0
  248. package/src/daemon/process-message.ts +21 -3
  249. package/src/daemon/server.ts +11 -2
  250. package/src/daemon/skill-memory-refresh.ts +29 -0
  251. package/src/documents/document-store.ts +221 -3
  252. package/src/embedded/plugin-api.ts +40 -0
  253. package/src/filing/filing-service.ts +39 -0
  254. package/src/heartbeat/__tests__/heartbeat-service.test.ts +91 -6
  255. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  256. package/src/heartbeat/heartbeat-service.ts +41 -0
  257. package/src/home/__tests__/feed-types.test.ts +40 -0
  258. package/src/home/feed-types.ts +22 -0
  259. package/src/home/post-connect-feed.ts +1 -0
  260. package/src/index.ts +18 -1
  261. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  262. package/src/mcp/client.ts +20 -4
  263. package/src/media/image-credentials.ts +3 -3
  264. package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
  265. package/src/memory/__tests__/conversation-queries.test.ts +263 -0
  266. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  267. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
  268. package/src/memory/__tests__/message-content.test.ts +35 -0
  269. package/src/memory/bookmark-crud.ts +42 -10
  270. package/src/memory/context-search/sources/conversations.ts +62 -2
  271. package/src/memory/context-search/sources/workspace.ts +4 -0
  272. package/src/memory/conversation-crud.ts +63 -19
  273. package/src/memory/conversation-queries.ts +110 -10
  274. package/src/memory/db-init.ts +6 -0
  275. package/src/memory/delivery-crud.ts +152 -5
  276. package/src/memory/embedding-backend.ts +4 -4
  277. package/src/memory/external-conversation-store.ts +66 -5
  278. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +66 -9
  279. package/src/memory/graph/conversation-graph-memory.ts +31 -15
  280. package/src/memory/graph/tools.ts +3 -3
  281. package/src/memory/indexer.ts +34 -29
  282. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
  283. package/src/memory/jobs/embed-concept-page.ts +20 -11
  284. package/src/memory/jobs-worker.ts +6 -1
  285. package/src/memory/llm-request-log-source-clickhouse.ts +17 -10
  286. package/src/memory/llm-request-log-source.ts +19 -52
  287. package/src/memory/llm-usage-store.ts +125 -5
  288. package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
  289. package/src/memory/message-content.ts +1 -1
  290. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  291. package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
  292. package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
  293. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  294. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  295. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  296. package/src/memory/migrations/index.ts +6 -0
  297. package/src/memory/migrations/registry.ts +8 -0
  298. package/src/memory/onboarding-events-store.ts +106 -0
  299. package/src/memory/schema/bookmarks.ts +0 -2
  300. package/src/memory/schema/calls.ts +1 -0
  301. package/src/memory/schema/inference.ts +1 -3
  302. package/src/memory/schema/infrastructure.ts +12 -0
  303. package/src/memory/turn-events-store.ts +127 -2
  304. package/src/memory/v2/__tests__/activation.test.ts +0 -8
  305. package/src/memory/v2/__tests__/injection.test.ts +98 -8
  306. package/src/memory/v2/__tests__/migration.test.ts +87 -0
  307. package/src/memory/v2/__tests__/page-index.test.ts +83 -0
  308. package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
  309. package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
  310. package/src/memory/v2/__tests__/router.test.ts +15 -0
  311. package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
  312. package/src/memory/v2/injection.ts +32 -6
  313. package/src/memory/v2/migration.ts +49 -19
  314. package/src/memory/v2/page-index.ts +35 -5
  315. package/src/memory/v2/prompts/router.ts +11 -8
  316. package/src/memory/v2/prompts/sweep.ts +2 -2
  317. package/src/memory/v2/qdrant.ts +135 -7
  318. package/src/memory/v2/router.ts +9 -8
  319. package/src/memory/v2/skill-store.ts +120 -35
  320. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  321. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  322. package/src/messaging/providers/slack/adapter.ts +43 -5
  323. package/src/messaging/providers/slack/client.ts +27 -0
  324. package/src/messaging/providers/slack/deep-link.ts +65 -0
  325. package/src/messaging/providers/slack/download.ts +104 -0
  326. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  327. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  328. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  329. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  330. package/src/messaging/providers/slack/types.ts +20 -1
  331. package/src/notifications/conversation-pairing.ts +2 -1
  332. package/src/notifications/decision-engine.ts +2 -1
  333. package/src/notifications/emit-signal.ts +20 -1
  334. package/src/notifications/home-feed-side-effect.ts +54 -0
  335. package/src/notifications/signal.ts +3 -1
  336. package/src/oauth/connection-resolver.ts +8 -4
  337. package/src/oauth/platform-connection.ts +6 -2
  338. package/src/oauth/seed-providers.ts +10 -1
  339. package/src/permissions/checker.ts +2 -0
  340. package/src/permissions/ipc-risk-types.ts +1 -0
  341. package/src/permissions/question-prompter.test.ts +416 -0
  342. package/src/permissions/question-prompter.ts +294 -0
  343. package/src/platform/client.test.ts +1 -1
  344. package/src/platform/client.ts +1 -1
  345. package/src/plugin-api/constants.ts +26 -0
  346. package/src/plugin-api/index.ts +34 -1
  347. package/src/plugin-api/types.ts +104 -22
  348. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  349. package/src/plugins/defaults/compaction.ts +0 -4
  350. package/src/plugins/defaults/empty-response.ts +0 -2
  351. package/src/plugins/defaults/history-repair.ts +0 -2
  352. package/src/plugins/defaults/injectors.ts +36 -3
  353. package/src/plugins/defaults/llm-call.ts +0 -2
  354. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  355. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  356. package/src/plugins/defaults/persistence.ts +0 -2
  357. package/src/plugins/defaults/title-generate.ts +0 -5
  358. package/src/plugins/defaults/token-estimate.ts +0 -2
  359. package/src/plugins/defaults/tool-error.ts +0 -7
  360. package/src/plugins/defaults/tool-execute.ts +0 -2
  361. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  362. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  363. package/src/plugins/external-api.ts +104 -0
  364. package/src/plugins/external-plugin-loader.ts +105 -32
  365. package/src/plugins/feature-gate.ts +22 -0
  366. package/src/plugins/pipeline.ts +37 -0
  367. package/src/plugins/registry.ts +48 -80
  368. package/src/plugins/types.ts +31 -26
  369. package/src/plugins/user-loader.ts +21 -2
  370. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  371. package/src/proactive-artifact/job.test.ts +37 -5
  372. package/src/prompts/__tests__/system-prompt.test.ts +12 -0
  373. package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
  374. package/src/prompts/normalize-onboarding.ts +27 -0
  375. package/src/prompts/sections.ts +302 -0
  376. package/src/prompts/system-prompt.ts +63 -166
  377. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  378. package/src/prompts/templates/system-sections.ts +173 -0
  379. package/src/providers/__tests__/inference.test.ts +22 -7
  380. package/src/providers/anthropic/client.ts +28 -28
  381. package/src/providers/connection-resolution.ts +7 -0
  382. package/src/providers/inference/adapter-factory.ts +41 -4
  383. package/src/providers/inference/connections.ts +74 -29
  384. package/src/providers/inference/resolve-auth.ts +12 -4
  385. package/src/providers/model-catalog.ts +294 -12
  386. package/src/providers/openai/chat-completions-provider.ts +10 -2
  387. package/src/providers/openrouter/client.ts +7 -0
  388. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
  389. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  390. package/src/providers/provider-availability.ts +17 -2
  391. package/src/providers/provider-catalog-visibility.ts +36 -0
  392. package/src/providers/registry.ts +22 -14
  393. package/src/providers/retry.ts +47 -1
  394. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  395. package/src/runtime/agent-wake.ts +42 -14
  396. package/src/runtime/auth/route-policy.ts +8 -1
  397. package/src/runtime/btw-sidechain.ts +2 -0
  398. package/src/runtime/http-types.ts +19 -0
  399. package/src/runtime/migrations/origin-mode.ts +1 -1
  400. package/src/runtime/pending-interactions.ts +1 -0
  401. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
  402. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
  403. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +107 -20
  404. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  405. package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
  406. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  407. package/src/runtime/routes/acp-routes.ts +5 -3
  408. package/src/runtime/routes/auth-routes.ts +1 -1
  409. package/src/runtime/routes/bookmark-routes.ts +5 -3
  410. package/src/runtime/routes/btw-routes.ts +5 -1
  411. package/src/runtime/routes/channel-availability-routes.ts +121 -0
  412. package/src/runtime/routes/conversation-cli-routes.ts +44 -3
  413. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  414. package/src/runtime/routes/conversation-management-routes.ts +17 -42
  415. package/src/runtime/routes/conversation-query-routes.ts +40 -35
  416. package/src/runtime/routes/conversation-routes.ts +90 -11
  417. package/src/runtime/routes/documents-routes.ts +25 -86
  418. package/src/runtime/routes/group-routes.ts +5 -0
  419. package/src/runtime/routes/inbound-conversation.ts +28 -8
  420. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  421. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
  422. package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
  423. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  424. package/src/runtime/routes/index.ts +6 -0
  425. package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
  426. package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
  427. package/src/runtime/routes/inference-provider-connection-routes.ts +65 -21
  428. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  429. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  430. package/src/runtime/routes/integrations/twilio.ts +6 -13
  431. package/src/runtime/routes/notification-routes.ts +1 -1
  432. package/src/runtime/routes/oauth-commands-routes.ts +105 -15
  433. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  434. package/src/runtime/routes/question-routes.ts +259 -0
  435. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  436. package/src/runtime/routes/schedule-routes.ts +4 -7
  437. package/src/runtime/routes/subagents-routes.ts +57 -18
  438. package/src/runtime/routes/telemetry-routes.ts +27 -0
  439. package/src/runtime/routes/tts-routes.ts +27 -2
  440. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  441. package/src/runtime/routes/workspace-routes.ts +28 -0
  442. package/src/runtime/services/conversation-serializer.ts +39 -7
  443. package/src/runtime/sync/resource-sync-events.ts +93 -1
  444. package/src/schedule/schedule-store.ts +27 -2
  445. package/src/schedule/scheduler.ts +9 -1
  446. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  447. package/src/security/untrusted-content.ts +93 -8
  448. package/src/skills/catalog-files.ts +1 -1
  449. package/src/skills/catalog-install.ts +233 -116
  450. package/src/skills/clawhub.ts +70 -13
  451. package/src/skills/managed-store.ts +4 -119
  452. package/src/skills/skillssh-registry.ts +27 -48
  453. package/src/subagent/manager.ts +15 -7
  454. package/src/telemetry/types.ts +113 -1
  455. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  456. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  457. package/src/tools/apps/executors.ts +58 -7
  458. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  459. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  460. package/src/tools/browser/browser-execution.ts +15 -11
  461. package/src/tools/computer-use/definitions.ts +3 -3
  462. package/src/tools/credentials/vault.ts +1 -1
  463. package/src/tools/document/document-tool.ts +124 -1
  464. package/src/tools/filesystem/edit.ts +1 -1
  465. package/src/tools/filesystem/list.ts +1 -1
  466. package/src/tools/filesystem/read.ts +1 -1
  467. package/src/tools/filesystem/write.ts +5 -2
  468. package/src/tools/host-filesystem/transfer.ts +1 -1
  469. package/src/tools/host-terminal/host-shell.ts +1 -1
  470. package/src/tools/permission-checker.ts +1 -1
  471. package/src/tools/registry.ts +17 -7
  472. package/src/tools/schedule/create.ts +2 -2
  473. package/src/tools/schema-transforms.ts +7 -2
  474. package/src/tools/side-effects.ts +1 -0
  475. package/src/tools/skills/delete-managed.ts +4 -4
  476. package/src/tools/skills/execute.ts +1 -1
  477. package/src/tools/skills/scaffold-managed.ts +3 -2
  478. package/src/tools/subagent/notify-parent.ts +1 -1
  479. package/src/tools/system/request-permission.ts +2 -2
  480. package/src/tools/terminal/safe-env.ts +60 -1
  481. package/src/tools/tool-manifest.ts +2 -0
  482. package/src/tools/types.ts +72 -21
  483. package/src/tools/ui-surface/definitions.ts +6 -5
  484. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  485. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  486. package/src/types/onboarding-context.ts +2 -0
  487. package/src/util/errors.ts +17 -0
  488. package/src/util/platform.ts +10 -0
  489. package/src/watcher/__tests__/engine.test.ts +22 -0
  490. package/src/watcher/engine.ts +6 -2
  491. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
  492. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
  493. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
  494. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  495. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  496. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  497. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  498. package/src/workspace/migrations/registry.ts +8 -0
  499. package/src/workspace/migrations/runner.ts +39 -9
  500. package/src/workspace/migrations/types.ts +4 -0
  501. package/examples/plugins/echo/bun.lock +0 -25
  502. package/src/__tests__/context-window-manager.test.ts +0 -2481
  503. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  504. package/src/context/prompts/compact.md +0 -26
  505. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  506. /package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +0 -0
@@ -22,6 +22,8 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
22
22
  interface MockProviderRow {
23
23
  provider: string;
24
24
  managedServiceConfigKey: string | null;
25
+ baseUrl: string | null;
26
+ injectionTemplates: string | null;
25
27
  pingUrl: string | null;
26
28
  pingMethod: string | null;
27
29
  pingHeaders: string | null;
@@ -31,6 +33,8 @@ interface MockProviderRow {
31
33
  const baseProvider: MockProviderRow = {
32
34
  provider: "google",
33
35
  managedServiceConfigKey: "google-oauth",
36
+ baseUrl: "https://api.google.com",
37
+ injectionTemplates: null,
34
38
  pingUrl: null,
35
39
  pingMethod: null,
36
40
  pingHeaders: null,
@@ -45,7 +49,10 @@ let mockApps: Record<string, unknown> = {};
45
49
  let mockTokenValue = "tok-fake";
46
50
  let platformAvailable = true;
47
51
  let platformAssistantId: string | null = "assistant-1";
48
- let mockFetchImpl: (path: string, init?: RequestInit) => Promise<{
52
+ let mockFetchImpl: (
53
+ path: string,
54
+ init?: RequestInit,
55
+ ) => Promise<{
49
56
  ok: boolean;
50
57
  status: number;
51
58
  json: () => Promise<unknown>;
@@ -61,6 +68,7 @@ let mockResolveResponse: {
61
68
  headers: Record<string, string>;
62
69
  body: unknown;
63
70
  } = { status: 200, headers: {}, body: { ok: true } };
71
+ let mockResolveRequests: unknown[] = [];
64
72
 
65
73
  const mockDisconnectOAuthProvider = mock(() => Promise.resolve());
66
74
  const mockSaveRawConfig = mock(() => undefined);
@@ -102,7 +110,10 @@ mock.module("../oauth/oauth-store.js", () => ({
102
110
 
103
111
  mock.module("../oauth/connection-resolver.js", () => ({
104
112
  resolveOAuthConnection: async (_provider: string) => ({
105
- request: async () => mockResolveResponse,
113
+ request: async (req: unknown) => {
114
+ mockResolveRequests.push(req);
115
+ return mockResolveResponse;
116
+ },
106
117
  }),
107
118
  }));
108
119
 
@@ -119,17 +130,19 @@ mock.module("../platform/client.js", () => ({
119
130
  }));
120
131
 
121
132
  mock.module("../security/token-manager.js", () => ({
122
- withValidToken: async <T,>(
123
- _provider: string,
124
- fn: (t: string) => Promise<T>,
125
- ) => fn(mockTokenValue),
133
+ withValidToken: async <T>(_provider: string, fn: (t: string) => Promise<T>) =>
134
+ fn(mockTokenValue),
126
135
  }));
127
136
 
128
137
  mock.module("../config/loader.js", () => ({
129
138
  getConfig: () => ({ services: {} }),
130
139
  loadRawConfig: () => ({ services: {} }),
131
140
  saveRawConfig: mockSaveRawConfig,
132
- setNestedValue: (obj: Record<string, unknown>, path: string, value: unknown) => {
141
+ setNestedValue: (
142
+ obj: Record<string, unknown>,
143
+ path: string,
144
+ value: unknown,
145
+ ) => {
133
146
  const parts = path.split(".");
134
147
  let cur: Record<string, unknown> = obj;
135
148
  for (let i = 0; i < parts.length - 1; i++) {
@@ -152,7 +165,11 @@ mock.module("../config/schemas/services.js", () => ({
152
165
  },
153
166
  }));
154
167
 
155
- import { BadRequestError, InternalError, NotFoundError } from "../runtime/routes/errors.js";
168
+ import {
169
+ BadRequestError,
170
+ InternalError,
171
+ NotFoundError,
172
+ } from "../runtime/routes/errors.js";
156
173
  import { ROUTES } from "../runtime/routes/oauth-commands-routes.js";
157
174
  import type { RouteHandlerArgs } from "../runtime/routes/types.js";
158
175
 
@@ -194,6 +211,7 @@ beforeEach(() => {
194
211
  text: async () => "",
195
212
  });
196
213
  mockResolveResponse = { status: 200, headers: {}, body: { ok: true } };
214
+ mockResolveRequests = [];
197
215
  mockDisconnectOAuthProvider.mockClear();
198
216
  mockSaveRawConfig.mockClear();
199
217
  });
@@ -430,7 +448,10 @@ describe("GET oauth/status", () => {
430
448
  ];
431
449
  const result = (await getRoute("GET", "oauth/status").handler(
432
450
  makeArgs({ queryParams: { provider: "google" } }),
433
- )) as { mode: string; connections: Array<{ id: string; grantedScopes: string[] }> };
451
+ )) as {
452
+ mode: string;
453
+ connections: Array<{ id: string; grantedScopes: string[] }>;
454
+ };
434
455
  expect(result.mode).toBe("byo");
435
456
  expect(result.connections).toHaveLength(1);
436
457
  expect(result.connections[0]!.id).toBe("conn-1");
@@ -507,7 +528,11 @@ describe("POST oauth/ping", () => {
507
528
  ...baseProvider,
508
529
  pingUrl: "https://api.google.com/v1/me",
509
530
  };
510
- mockResolveResponse = { status: 401, headers: {}, body: { error: "unauthorized" } };
531
+ mockResolveResponse = {
532
+ status: 401,
533
+ headers: {},
534
+ body: { error: "unauthorized" },
535
+ };
511
536
  const result = (await getRoute("POST", "oauth/ping").handler(
512
537
  makeArgs({ body: { provider: "google" } }),
513
538
  )) as { ok: boolean; status: number; hint?: string };
@@ -543,7 +568,9 @@ describe("POST oauth/token", () => {
543
568
  // No active connections registered for google
544
569
  await expect(
545
570
  getRoute("POST", "oauth/token").handler(
546
- makeArgs({ body: { provider: "google", account: "missing@example.com" } }),
571
+ makeArgs({
572
+ body: { provider: "google", account: "missing@example.com" },
573
+ }),
547
574
  ),
548
575
  ).rejects.toBeInstanceOf(NotFoundError);
549
576
  });
@@ -578,6 +605,130 @@ describe("POST oauth/request", () => {
578
605
  expect(result.body).toEqual({ hello: "world" });
579
606
  });
580
607
 
608
+ test("rejects absolute URL host outside provider base host when no injection templates exist", async () => {
609
+ await expect(
610
+ getRoute("POST", "oauth/request").handler(
611
+ makeArgs({
612
+ body: { provider: "google", url: "https://attacker.example/v1/me" },
613
+ }),
614
+ ),
615
+ ).rejects.toBeInstanceOf(BadRequestError);
616
+ expect(mockResolveRequests).toHaveLength(0);
617
+ });
618
+
619
+ test("rejects protocol downgrade for absolute OAuth request URLs", async () => {
620
+ await expect(
621
+ getRoute("POST", "oauth/request").handler(
622
+ makeArgs({
623
+ body: { provider: "google", url: "http://api.google.com/v1/me" },
624
+ }),
625
+ ),
626
+ ).rejects.toBeInstanceOf(BadRequestError);
627
+ expect(mockResolveRequests).toHaveLength(0);
628
+ });
629
+
630
+ test("rejects absolute URL host outside provider injection templates", async () => {
631
+ mockProviders.slack_channel = {
632
+ ...baseProvider,
633
+ provider: "slack_channel",
634
+ managedServiceConfigKey: null,
635
+ baseUrl: "https://slack.com/api",
636
+ injectionTemplates: JSON.stringify([
637
+ {
638
+ hostPattern: "slack.com",
639
+ injectionType: "header",
640
+ headerName: "Authorization",
641
+ valuePrefix: "Bearer ",
642
+ },
643
+ ]),
644
+ };
645
+
646
+ await expect(
647
+ getRoute("POST", "oauth/request").handler(
648
+ makeArgs({
649
+ body: {
650
+ provider: "slack_channel",
651
+ url: "https://attacker.example/api/auth.test",
652
+ },
653
+ }),
654
+ ),
655
+ ).rejects.toBeInstanceOf(BadRequestError);
656
+ expect(mockResolveRequests).toHaveLength(0);
657
+ });
658
+
659
+ test("allows absolute URL host matching provider injection templates", async () => {
660
+ mockProviders.slack_channel = {
661
+ ...baseProvider,
662
+ provider: "slack_channel",
663
+ managedServiceConfigKey: null,
664
+ baseUrl: "https://slack.com/api",
665
+ injectionTemplates: JSON.stringify([
666
+ {
667
+ hostPattern: "slack.com",
668
+ injectionType: "header",
669
+ headerName: "Authorization",
670
+ valuePrefix: "Bearer ",
671
+ },
672
+ ]),
673
+ };
674
+
675
+ await getRoute("POST", "oauth/request").handler(
676
+ makeArgs({
677
+ body: {
678
+ provider: "slack_channel",
679
+ url: "https://slack.com/api/auth.test?team=T123",
680
+ },
681
+ }),
682
+ );
683
+
684
+ expect(mockResolveRequests).toEqual([
685
+ {
686
+ method: "GET",
687
+ path: "/api/auth.test",
688
+ query: { team: "T123" },
689
+ baseUrl: "https://slack.com",
690
+ },
691
+ ]);
692
+ });
693
+
694
+ test("allows cross-host absolute URLs declared by provider injection templates", async () => {
695
+ mockProviders.google = {
696
+ ...baseProvider,
697
+ baseUrl: "https://gmail.googleapis.com/gmail/v1/users/me",
698
+ injectionTemplates: JSON.stringify([
699
+ {
700
+ hostPattern: "gmail.googleapis.com",
701
+ injectionType: "header",
702
+ headerName: "Authorization",
703
+ valuePrefix: "Bearer ",
704
+ },
705
+ {
706
+ hostPattern: "www.googleapis.com",
707
+ injectionType: "header",
708
+ headerName: "Authorization",
709
+ valuePrefix: "Bearer ",
710
+ },
711
+ ]),
712
+ };
713
+
714
+ await getRoute("POST", "oauth/request").handler(
715
+ makeArgs({
716
+ body: {
717
+ provider: "google",
718
+ url: "https://www.googleapis.com/calendar/v3/calendars",
719
+ },
720
+ }),
721
+ );
722
+
723
+ expect(mockResolveRequests).toEqual([
724
+ {
725
+ method: "GET",
726
+ path: "/calendar/v3/calendars",
727
+ baseUrl: "https://www.googleapis.com",
728
+ },
729
+ ]);
730
+ });
731
+
581
732
  test("attaches reconnect hint on 401 response", async () => {
582
733
  mockResolveResponse = { status: 401, headers: {}, body: { error: "no" } };
583
734
  const result = (await getRoute("POST", "oauth/request").handler(
@@ -681,10 +832,7 @@ describe("GET oauth/managed-connect/poll", () => {
681
832
  ],
682
833
  text: async () => "",
683
834
  });
684
- const result = (await getRoute(
685
- "GET",
686
- "oauth/managed-connect/poll",
687
- ).handler(
835
+ const result = (await getRoute("GET", "oauth/managed-connect/poll").handler(
688
836
  makeArgs({ queryParams: { provider: "google" } }),
689
837
  )) as {
690
838
  ok: boolean;
@@ -696,7 +844,11 @@ describe("GET oauth/managed-connect/poll", () => {
696
844
  };
697
845
  expect(result.ok).toBe(true);
698
846
  expect(result.connections).toEqual([
699
- { id: "conn-1", account_label: "alice@example.com", scopes_granted: ["email"] },
847
+ {
848
+ id: "conn-1",
849
+ account_label: "alice@example.com",
850
+ scopes_granted: ["email"],
851
+ },
700
852
  ]);
701
853
  });
702
854
 
@@ -21,6 +21,15 @@ initializeDb();
21
21
  seedOAuthProviders();
22
22
 
23
23
  describe("oauth provider profiles (DB-seeded)", () => {
24
+ test("google provider row includes Drive in default scopes", () => {
25
+ const provider = getProvider("google");
26
+
27
+ expect(provider).toBeDefined();
28
+ expect(JSON.parse(provider!.defaultScopes)).toContain(
29
+ "https://www.googleapis.com/auth/drive",
30
+ );
31
+ });
32
+
24
33
  test("google provider row contains bearer injection templates for 3 Google API hosts", () => {
25
34
  const provider = getProvider("google");
26
35
 
@@ -1344,6 +1344,30 @@ describe("OpenRouterProvider reasoning", () => {
1344
1344
  expect(lastCreateParams!.reasoning).toEqual({ enabled: false });
1345
1345
  });
1346
1346
 
1347
+ test("sends OpenRouter app-attribution headers on OpenAI-compatible requests", async () => {
1348
+ const provider = new OpenRouterProvider("or-key", "x-ai/grok-4");
1349
+ await provider.sendMessage([userMsg("hi")], undefined, undefined, {
1350
+ config: {
1351
+ usageAttributionHeaders: {
1352
+ "Vellum-Organization-Id": "org-123",
1353
+ },
1354
+ },
1355
+ });
1356
+
1357
+ expect(lastCreateOptions?.headers).toEqual(
1358
+ expect.objectContaining({
1359
+ "HTTP-Referer": "https://www.vellum.ai",
1360
+ "X-OpenRouter-Title": "Vellum Assistant",
1361
+ "X-OpenRouter-Categories": "personal-agent,cli-agent",
1362
+ "Vellum-Organization-Id": "org-123",
1363
+ }),
1364
+ );
1365
+ expect(lastCreateParams).not.toHaveProperty("HTTP-Referer");
1366
+ expect(lastCreateParams).not.toHaveProperty("X-OpenRouter-Title");
1367
+ expect(lastCreateParams).not.toHaveProperty("X-OpenRouter-Categories");
1368
+ expect(lastCreateParams).not.toHaveProperty("usageAttributionHeaders");
1369
+ });
1370
+
1347
1371
  test("RetryProvider + OpenRouterProvider enables thinking end-to-end", async () => {
1348
1372
  const provider = new OpenRouterProvider("or-key", "x-ai/grok-4");
1349
1373
  const retry = new RetryProvider(provider);
@@ -91,18 +91,26 @@ describe("OpenAI Responses API cutover guard", () => {
91
91
  ).toBe(true);
92
92
 
93
93
  // The factory must NOT instantiate OpenAIChatCompletionsProvider or
94
- // OpenAIProvider (the backward-compatible alias) for the "openai" key.
95
- // Chat-completions classes may appear in imports but should not be
96
- // instantiated in the openai factory entry.
97
- const chatCompletionsInstantiations = [
98
- ...source.matchAll(/new\s+OpenAIChatCompletionsProvider\s*\(/g),
99
- ...source.matchAll(/new\s+OpenAIProvider\s*\(/g),
94
+ // OpenAIProvider (the backward-compatible alias) inside the `openai:`
95
+ // factory entry. Other entries (e.g. `zai:`, `deepseek:`, `minimax:`)
96
+ // may legitimately use OpenAIChatCompletionsProvider since that's the
97
+ // OpenAI Chat Completions transport for third-party endpoints.
98
+ const openaiEntryRegion =
99
+ /(?:^|\s)openai\s*:\s*\([^)]*\)\s*=>\s*[\s\S]{0,400}?(?=\}\s*,\s*[a-z-]+\s*:|\}\s*;)/m.exec(
100
+ source,
101
+ )?.[0] ?? "";
102
+ const chatCompletionsInstantiationsInOpenAi = [
103
+ ...openaiEntryRegion.matchAll(
104
+ /new\s+OpenAIChatCompletionsProvider\s*\(/g,
105
+ ),
106
+ ...openaiEntryRegion.matchAll(/new\s+OpenAIProvider\s*\(/g),
100
107
  ];
101
108
  expect(
102
- chatCompletionsInstantiations.length,
109
+ chatCompletionsInstantiationsInOpenAi.length,
103
110
  [
104
- "adapter-factory.ts must NOT instantiate OpenAIChatCompletionsProvider or",
105
- "OpenAIProvider (legacy alias). Use OpenAIResponsesProvider for openai.",
111
+ "ADAPTER_FACTORIES['openai'] must NOT instantiate",
112
+ "OpenAIChatCompletionsProvider or OpenAIProvider (legacy alias).",
113
+ "Use OpenAIResponsesProvider for openai.",
106
114
  ].join("\n"),
107
115
  ).toBe(0);
108
116
 
@@ -453,7 +453,6 @@ describe("overflow-reduce pipeline", () => {
453
453
  manifest: {
454
454
  name: "spy-overflow",
455
455
  version: "0.0.1",
456
- requires: { pluginRuntime: "v1", overflowReduceApi: "v1" },
457
456
  },
458
457
  middleware: { overflowReduce: spy },
459
458
  };
@@ -512,7 +511,6 @@ describe("overflow-reduce pipeline", () => {
512
511
  manifest: {
513
512
  name: "short-circuit-overflow",
514
513
  version: "0.0.1",
515
- requires: { pluginRuntime: "v1", overflowReduceApi: "v1" },
516
514
  },
517
515
  middleware: { overflowReduce: shortCircuit },
518
516
  });
@@ -267,7 +267,6 @@ describe("persistence pipeline", () => {
267
267
  manifest: {
268
268
  name: "mock-persistence",
269
269
  version: "0.0.1",
270
- requires: { pluginRuntime: "v1" },
271
270
  },
272
271
  middleware: { persistence: redirect },
273
272
  };
@@ -352,7 +351,6 @@ describe("persistence pipeline", () => {
352
351
  manifest: {
353
352
  name: "late-user-plugin",
354
353
  version: "0.0.1",
355
- requires: { pluginRuntime: "v1" },
356
354
  },
357
355
  middleware: { persistence: userMiddleware },
358
356
  });
@@ -32,7 +32,7 @@ import {
32
32
  hasManagedProxyPrereqs,
33
33
  managedFallbackEnabledFor,
34
34
  resolveManagedProxyContext,
35
- } from "../providers/managed-proxy/context.js";
35
+ } from "../providers/platform-proxy/context.js";
36
36
 
37
37
  describe("resolveManagedProxyContext", () => {
38
38
  beforeEach(() => {
@@ -16,6 +16,7 @@ import {
16
16
  getWorkspaceConfigPath,
17
17
  getWorkspaceDir,
18
18
  getWorkspaceHooksDir,
19
+ getWorkspacePluginsDir,
19
20
  getWorkspacePromptPath,
20
21
  getWorkspaceSkillsDir,
21
22
  getXdgVellumConfigDirName,
@@ -161,6 +162,7 @@ describe("workspace path primitives", () => {
161
162
  expect(getWorkspaceConfigPath()).toBe(join(ws, "config.json"));
162
163
  expect(getWorkspaceSkillsDir()).toBe(join(ws, "skills"));
163
164
  expect(getWorkspaceHooksDir()).toBe(join(ws, "hooks"));
165
+ expect(getWorkspacePluginsDir()).toBe(join(ws, "plugins"));
164
166
  expect(getWorkspacePromptPath("IDENTITY.md")).toBe(join(ws, "IDENTITY.md"));
165
167
  expect(getWorkspacePromptPath("SOUL.md")).toBe(join(ws, "SOUL.md"));
166
168
  });
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Smoke tests for the workspace-level `@vellumai/plugin-api` shim.
3
+ *
4
+ * - shim files are materialized at `<workspaceDir>/node_modules/@vellumai/plugin-api/`
5
+ * - the shim's index.js re-binds each runtime export from globalThis
6
+ * - the shim is idempotent across re-runs
7
+ * - a fake plugin in `<workspaceDir>/plugins/<name>/` can resolve the
8
+ * bare `@vellumai/plugin-api` specifier via Node-style walk-up,
9
+ * proving the end-to-end import path works for real user plugins
10
+ *
11
+ * As plugin-api's runtime surface grows in follow-up PRs, the shim's
12
+ * generated export list expands automatically — the test below covers
13
+ * the generator (`buildShimSource`) directly so we don't need to
14
+ * update assertions every time an export is added.
15
+ */
16
+
17
+ import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
18
+ import { tmpdir } from "node:os";
19
+ import { join } from "node:path";
20
+ import { describe, expect, test } from "bun:test";
21
+
22
+ import {
23
+ PLUGIN_API_EXPORTS,
24
+ PLUGIN_API_REGISTRY_KEY,
25
+ } from "../embedded/plugin-api.js";
26
+ import {
27
+ buildShimSource,
28
+ ensurePluginApiShim,
29
+ } from "../plugins/ensure-plugin-api-shim.js";
30
+
31
+ const SHIM_REL_PATH = "node_modules/@vellumai/plugin-api";
32
+
33
+ describe("buildShimSource", () => {
34
+ test("emits a globalThis trampoline + one binding per export", () => {
35
+ const source = buildShimSource(
36
+ ["foo", "bar"],
37
+ Symbol.for("vellum.plugin-api.v1"),
38
+ );
39
+ expect(source).toBe(
40
+ `const api = globalThis[Symbol.for("vellum.plugin-api.v1")];\n` +
41
+ `export const foo = api.foo;\n` +
42
+ `export const bar = api.bar;\n`,
43
+ );
44
+ });
45
+
46
+ test("handles an empty export list (today's types-only surface)", () => {
47
+ const source = buildShimSource(
48
+ [],
49
+ Symbol.for("vellum.plugin-api.v1"),
50
+ );
51
+ expect(source).toBe(
52
+ `const api = globalThis[Symbol.for("vellum.plugin-api.v1")];\n`,
53
+ );
54
+ });
55
+ });
56
+
57
+ describe("ensurePluginApiShim", () => {
58
+ test("creates a resolvable @vellumai/plugin-api package under workspaceDir", async () => {
59
+ const workspaceDir = await mkdtemp(join(tmpdir(), "plugin-api-shim-"));
60
+ await ensurePluginApiShim({ workspaceDir });
61
+
62
+ const shimDir = join(workspaceDir, SHIM_REL_PATH);
63
+ const indexJs = await readFile(join(shimDir, "index.js"), "utf8");
64
+ expect(indexJs).toBe(buildShimSource());
65
+
66
+ const pkg = JSON.parse(
67
+ await readFile(join(shimDir, "package.json"), "utf8"),
68
+ );
69
+ expect(pkg.name).toBe("@vellumai/plugin-api");
70
+ expect(pkg.type).toBe("module");
71
+ expect(pkg.main).toBe("./index.js");
72
+ expect(typeof pkg.version).toBe("string");
73
+ });
74
+
75
+ test("is idempotent — re-running yields the same shim contents", async () => {
76
+ const workspaceDir = await mkdtemp(join(tmpdir(), "plugin-api-shim-"));
77
+ await ensurePluginApiShim({ workspaceDir });
78
+ const first = await readFile(
79
+ join(workspaceDir, SHIM_REL_PATH, "index.js"),
80
+ "utf8",
81
+ );
82
+
83
+ await ensurePluginApiShim({ workspaceDir });
84
+ const second = await readFile(
85
+ join(workspaceDir, SHIM_REL_PATH, "index.js"),
86
+ "utf8",
87
+ );
88
+ expect(second).toBe(first);
89
+ });
90
+
91
+ test("globalThis is populated with the plugin-api namespace", () => {
92
+ // Importing the embed wrapper has the side effect of installing the
93
+ // namespace on globalThis. By the time this test runs (any earlier
94
+ // test in the file has already imported it), the registry must be
95
+ // populated.
96
+ const namespace = (globalThis as Record<symbol, unknown>)[
97
+ PLUGIN_API_REGISTRY_KEY
98
+ ];
99
+ expect(namespace).toBeDefined();
100
+ // Exports list is non-null but may be empty until runtime exports
101
+ // migrate in later PRs.
102
+ expect(Array.isArray(PLUGIN_API_EXPORTS)).toBe(true);
103
+ });
104
+
105
+ test("a fake user plugin can resolve @vellumai/plugin-api via Node-style walk-up", async () => {
106
+ const workspaceDir = await mkdtemp(join(tmpdir(), "plugin-api-shim-"));
107
+ await ensurePluginApiShim({ workspaceDir });
108
+
109
+ const pluginDir = join(workspaceDir, "plugins", "fake-plugin");
110
+ await mkdir(pluginDir, { recursive: true });
111
+ await writeFile(
112
+ join(pluginDir, "register.js"),
113
+ `import * as api from "@vellumai/plugin-api";\nexport { api };\n`,
114
+ );
115
+
116
+ // Resolution walks up: plugins/fake-plugin → plugins → workspaceDir
117
+ // → workspaceDir/node_modules/@vellumai/plugin-api → shim → globalThis
118
+ // → plugin-api namespace. If any link in that chain is broken, this
119
+ // import throws.
120
+ const mod: { api: Record<string, unknown> } = await import(
121
+ join(pluginDir, "register.js")
122
+ );
123
+ expect(mod.api).toBeDefined();
124
+ });
125
+ });
@@ -43,7 +43,6 @@ import {
43
43
  import { runShutdownHooks } from "../daemon/shutdown-registry.js";
44
44
  import { RiskLevel } from "../permissions/types.js";
45
45
  import {
46
- ASSISTANT_API_VERSIONS,
47
46
  getInjectors,
48
47
  getMiddlewaresFor,
49
48
  registerPlugin,
@@ -84,7 +83,6 @@ function buildPlugin(
84
83
  onShutdown?: () => Promise<void>;
85
84
  } = {},
86
85
  options: {
87
- requires?: Record<string, string>;
88
86
  requiresCredential?: string[];
89
87
  requiresFlag?: string[];
90
88
  } = {},
@@ -111,7 +109,6 @@ function buildPlugin(
111
109
  manifest: {
112
110
  name,
113
111
  version: "0.0.1",
114
- requires: options.requires ?? { pluginRuntime: "v1" },
115
112
  ...(options.requiresCredential
116
113
  ? { requiresCredential: options.requiresCredential }
117
114
  : {}),
@@ -161,10 +158,6 @@ describe("plugin bootstrap", () => {
161
158
  );
162
159
  expect(existsSync(ctx.pluginStorageDir)).toBe(true);
163
160
  expect(ctx.assistantVersion).toBe("9.9.9-test");
164
- // apiVersions must surface the canonical capability table from the
165
- // registry so plugins can negotiate at runtime.
166
- expect(ctx.apiVersions).toBe(ASSISTANT_API_VERSIONS);
167
- expect(ctx.apiVersions.pluginRuntime).toEqual(["v1"]);
168
161
  });
169
162
 
170
163
  test("credential resolution: init receives the resolved value under credentials[key]", async () => {
@@ -215,29 +208,15 @@ describe("plugin bootstrap", () => {
215
208
  expect(msg).toContain("absent-key");
216
209
  });
217
210
 
218
- test("version mismatch: registration surfaces a clear error naming the plugin", () => {
219
- // The assistant only exposes pluginRuntime@v1 asking for v99 must fail
220
- // registration with the plugin name in the message. The error is raised
221
- // at registerPlugin() rather than bootstrap, because the registry is the
222
- // single authoritative point of capability validation.
223
- const plugin = buildPlugin(
224
- "from-the-future",
225
- {},
226
- { requires: { pluginRuntime: "v99" } },
227
- );
228
-
229
- let caught: unknown;
230
- try {
231
- registerPlugin(plugin);
232
- } catch (err) {
233
- caught = err;
234
- }
235
- expect(caught).toBeInstanceOf(PluginExecutionError);
236
- const msg = (caught as PluginExecutionError).message;
237
- expect(msg).toContain("from-the-future");
238
- expect(msg).toContain("pluginRuntime");
239
- expect(msg).toContain("v99");
240
- expect((caught as PluginExecutionError).pluginName).toBe("from-the-future");
211
+ test("version mismatch: external plugin loader rejects when peerDependency unsatisfied", async () => {
212
+ // Host-compat negotiation lives in the external-plugin loader against
213
+ // `peerDependencies["@vellumai/plugin-api"]`. The registry no longer
214
+ // re-validates a manifest-level `requires` block the loader is the
215
+ // single authoritative point. End-to-end coverage of the loader path
216
+ // lives in `external-plugin-loader.test.ts`; this test asserts the
217
+ // bootstrap doesn't gain its own validation surface.
218
+ const plugin = buildPlugin("compat-claim-checked-upstream");
219
+ expect(() => registerPlugin(plugin)).not.toThrow();
241
220
  });
242
221
 
243
222
  test("plugin init throw: bootstrap throws a PluginExecutionError naming the plugin", async () => {
@@ -395,13 +374,8 @@ describe("plugin bootstrap", () => {
395
374
  {
396
375
  name: "gated-off-tool",
397
376
  description: "should not be registered",
398
- category: "plugin-test",
399
377
  defaultRiskLevel: RiskLevel.Low,
400
- getDefinition: () => ({
401
- name: "gated-off-tool",
402
- description: "should not be registered",
403
- input_schema: { type: "object", properties: {}, required: [] },
404
- }),
378
+ input_schema: { type: "object", properties: {}, required: [] },
405
379
  execute: async () => ({ content: "nope", isError: false }),
406
380
  },
407
381
  ],