@vellumai/assistant 0.8.8 → 0.8.9-staging.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 (360) hide show
  1. package/ARCHITECTURE.md +6 -6
  2. package/bun.lock +2 -2
  3. package/examples/plugins/echo/README.md +61 -60
  4. package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
  5. package/examples/plugins/echo/hooks/stop.ts +16 -0
  6. package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
  7. package/examples/plugins/echo/package.json +1 -2
  8. package/examples/plugins/echo/src/emit.ts +19 -0
  9. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
  10. package/openapi.yaml +235 -6
  11. package/package.json +2 -2
  12. package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
  13. package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
  14. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
  15. package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
  16. package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
  17. package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
  18. package/src/__tests__/agent-loop-thinking.test.ts +36 -20
  19. package/src/__tests__/agent-loop.test.ts +441 -96
  20. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
  21. package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
  22. package/src/__tests__/anthropic-provider.test.ts +1 -1
  23. package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
  24. package/src/__tests__/app-control-flow.test.ts +1 -1
  25. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  26. package/src/__tests__/app-executors.test.ts +132 -0
  27. package/src/__tests__/approval-cascade.test.ts +5 -4
  28. package/src/__tests__/approval-routes-http.test.ts +4 -1
  29. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  30. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  31. package/src/__tests__/channel-approvals.test.ts +1 -1
  32. package/src/__tests__/compaction-circuit.test.ts +258 -0
  33. package/src/__tests__/compaction-direct.test.ts +132 -0
  34. package/src/__tests__/compaction-events.test.ts +5 -5
  35. package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
  36. package/src/__tests__/context-overflow-reducer.test.ts +1 -1
  37. package/src/__tests__/conversation-abort-tool-results.test.ts +6 -4
  38. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
  39. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
  40. package/src/__tests__/conversation-agent-loop-overflow.test.ts +142 -218
  41. package/src/__tests__/conversation-agent-loop.test.ts +297 -586
  42. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  43. package/src/__tests__/conversation-confirmation-signals.test.ts +5 -4
  44. package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
  45. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  46. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -4
  47. package/src/__tests__/conversation-process-callsite.test.ts +14 -14
  48. package/src/__tests__/conversation-provider-retry-repair.test.ts +70 -65
  49. package/src/__tests__/conversation-queue.test.ts +9 -9
  50. package/src/__tests__/conversation-runtime-assembly.test.ts +923 -231
  51. package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
  52. package/src/__tests__/conversation-slash-queue.test.ts +6 -4
  53. package/src/__tests__/conversation-slash-unknown.test.ts +6 -4
  54. package/src/__tests__/conversation-speed-override.test.ts +10 -9
  55. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  56. package/src/__tests__/conversation-workspace-cache-state.test.ts +23 -20
  57. package/src/__tests__/conversation-workspace-injection.test.ts +68 -6
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +14 -11
  59. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  60. package/src/__tests__/credential-security-invariants.test.ts +0 -1
  61. package/src/__tests__/db-acp-history.test.ts +101 -0
  62. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  63. package/src/__tests__/empty-response-hook.test.ts +1 -1
  64. package/src/__tests__/file-write-tool.test.ts +63 -0
  65. package/src/__tests__/gateway-only-guard.test.ts +12 -2
  66. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  67. package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
  68. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  69. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  70. package/src/__tests__/heartbeat-service.test.ts +1 -0
  71. package/src/__tests__/history-repair-hook.test.ts +1 -1
  72. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  73. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  74. package/src/__tests__/inference-profile-reaper.test.ts +62 -0
  75. package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
  76. package/src/__tests__/injector-background-turn.test.ts +13 -23
  77. package/src/__tests__/injector-chain.test.ts +268 -44
  78. package/src/__tests__/injector-disk-pressure.test.ts +210 -52
  79. package/src/__tests__/injector-document-comments.test.ts +97 -114
  80. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  81. package/src/__tests__/injector-v3-suppression.test.ts +4 -4
  82. package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
  83. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  84. package/src/__tests__/memory-retrieval-hook.test.ts +131 -26
  85. package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
  86. package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
  87. package/src/__tests__/plugin-api-shim.test.ts +6 -9
  88. package/src/__tests__/plugin-bootstrap.test.ts +12 -23
  89. package/src/__tests__/plugin-registry.test.ts +3 -49
  90. package/src/__tests__/plugin-types.test.ts +0 -70
  91. package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
  92. package/src/__tests__/reaction-persistence.test.ts +1 -1
  93. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  94. package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
  95. package/src/__tests__/steer-tool-repair.test.ts +1 -1
  96. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  97. package/src/__tests__/subagent-detail.test.ts +25 -7
  98. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  99. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  100. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  101. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  102. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  103. package/src/__tests__/title-generate-hook.test.ts +1 -1
  104. package/src/__tests__/tool-error-hook.test.ts +1 -1
  105. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
  106. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  107. package/src/acp/__tests__/agent-process.test.ts +161 -0
  108. package/src/acp/__tests__/client-handler.test.ts +40 -0
  109. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  110. package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
  111. package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
  112. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  113. package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
  114. package/src/acp/agent-process.ts +61 -1
  115. package/src/acp/auto-install.test.ts +280 -0
  116. package/src/acp/auto-install.ts +232 -0
  117. package/src/acp/client-handler.ts +31 -0
  118. package/src/acp/feature-gate.test.ts +48 -0
  119. package/src/acp/feature-gate.ts +34 -0
  120. package/src/acp/prepare-agent-env.ts +80 -27
  121. package/src/acp/resolve-agent.test.ts +225 -9
  122. package/src/acp/resolve-agent.ts +122 -17
  123. package/src/acp/resume-hint.ts +23 -0
  124. package/src/acp/session-manager.ts +507 -73
  125. package/src/agent/compaction-circuit.ts +60 -102
  126. package/src/agent/loop.ts +414 -248
  127. package/src/api/responses/conversation-message.ts +14 -1
  128. package/src/approvals/guardian-request-resolvers.ts +1 -1
  129. package/src/background-wake/next-wake.ts +1 -0
  130. package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
  131. package/src/cli/commands/plugins.ts +43 -37
  132. package/src/cli/lib/__tests__/install-from-github.test.ts +429 -111
  133. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
  134. package/src/cli/lib/__tests__/plugin-details.test.ts +372 -0
  135. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +220 -0
  136. package/src/cli/lib/__tests__/search-plugins.test.ts +226 -32
  137. package/src/cli/lib/install-from-github.ts +464 -55
  138. package/src/cli/lib/plugin-catalog-cache.ts +84 -0
  139. package/src/cli/lib/plugin-details.ts +409 -0
  140. package/src/cli/lib/plugin-marketplace.ts +197 -0
  141. package/src/cli/lib/search-plugins.ts +195 -29
  142. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  143. package/src/config/acp-defaults.test.ts +10 -0
  144. package/src/config/acp-defaults.ts +6 -0
  145. package/src/config/bundled-skills/acp/SKILL.md +85 -33
  146. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  147. package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
  148. package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
  149. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  150. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  151. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  152. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  153. package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
  154. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  155. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  156. package/src/config/bundled-tool-registry.ts +4 -0
  157. package/src/config/call-site-defaults.ts +0 -2
  158. package/src/config/feature-flag-registry.json +15 -6
  159. package/src/config/schemas/call-site-catalog.ts +0 -14
  160. package/src/config/schemas/heartbeat.ts +9 -0
  161. package/src/config/schemas/llm.ts +0 -2
  162. package/src/context/compactor.ts +22 -4
  163. package/src/context/strip-injections.ts +8 -2
  164. package/src/context/window-manager.ts +27 -13
  165. package/src/daemon/conversation-agent-loop-handlers.ts +10 -35
  166. package/src/daemon/conversation-agent-loop.ts +175 -1055
  167. package/src/daemon/conversation-lifecycle.ts +11 -255
  168. package/src/daemon/conversation-process.ts +8 -136
  169. package/src/daemon/conversation-registry.ts +159 -0
  170. package/src/daemon/conversation-runtime-assembly.ts +293 -392
  171. package/src/daemon/conversation-store.ts +9 -90
  172. package/src/daemon/conversation-surfaces.ts +24 -8
  173. package/src/daemon/conversation-workspace.ts +17 -0
  174. package/src/daemon/conversation.ts +404 -57
  175. package/src/daemon/disk-pressure-policy.ts +0 -1
  176. package/src/daemon/external-plugins-bootstrap.ts +14 -19
  177. package/src/daemon/handlers/conversations.ts +3 -1
  178. package/src/daemon/handlers/skills.ts +4 -1
  179. package/src/daemon/host-proxy-preactivation.ts +1 -3
  180. package/src/daemon/lifecycle.ts +21 -7
  181. package/src/daemon/server.ts +2 -0
  182. package/src/daemon/wake-conversation-ops.ts +269 -0
  183. package/src/embedded/plugin-api.ts +2 -2
  184. package/src/export/__tests__/transcript-formatter.test.ts +5 -0
  185. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  186. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  187. package/src/heartbeat/heartbeat-service.ts +26 -0
  188. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  189. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  190. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  191. package/src/ipc/skill-routes/memory.ts +4 -2
  192. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
  193. package/src/memory/conversation-crud.ts +29 -19
  194. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  195. package/src/memory/db-init.ts +2 -0
  196. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  197. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
  198. package/src/memory/jobs-worker.ts +25 -1
  199. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  200. package/src/memory/migrations/index.ts +1 -0
  201. package/src/memory/schema/acp.ts +4 -0
  202. package/src/memory/v2/__tests__/consolidation-job.test.ts +3 -3
  203. package/src/memory/v2/consolidation-job.ts +13 -4
  204. package/src/plugin-api/constants.ts +4 -0
  205. package/src/plugin-api/index.ts +6 -5
  206. package/src/plugin-api/types.ts +75 -0
  207. package/src/plugins/defaults/compaction/compact.ts +59 -0
  208. package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +7 -7
  209. package/src/plugins/defaults/compaction/manager-store.ts +57 -0
  210. package/src/plugins/defaults/compaction/package.json +1 -2
  211. package/src/plugins/defaults/empty-response/package.json +0 -1
  212. package/src/plugins/defaults/history-repair/package.json +0 -1
  213. package/src/plugins/defaults/index.ts +135 -26
  214. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
  215. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +184 -74
  216. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
  217. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
  218. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
  219. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
  220. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +9 -6
  221. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
  222. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +8 -5
  223. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
  224. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
  225. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +10 -5
  226. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
  227. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +5 -5
  228. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +16 -17
  229. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -2
  230. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +9 -5
  231. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +5 -2
  232. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  233. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  234. package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
  235. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +15 -8
  236. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +2 -2
  237. package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
  238. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
  239. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/provider-blocks.ts +1 -1
  240. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +7 -3
  241. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
  242. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +5 -5
  243. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
  244. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +7 -7
  245. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +32 -94
  246. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
  247. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +1 -1
  248. package/src/plugins/defaults/title-generate/package.json +0 -1
  249. package/src/plugins/defaults/tool-error/package.json +0 -1
  250. package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
  251. package/src/plugins/pipeline.ts +6 -293
  252. package/src/plugins/registry.ts +9 -37
  253. package/src/plugins/types.ts +76 -381
  254. package/src/plugins/user-loader.ts +30 -127
  255. package/src/prompts/__tests__/system-prompt.test.ts +6 -0
  256. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +35 -3
  257. package/src/runtime/__tests__/agent-wake.test.ts +555 -691
  258. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  259. package/src/runtime/agent-wake.ts +108 -209
  260. package/src/runtime/assistant-event-hub.ts +1 -1
  261. package/src/runtime/channel-approvals.ts +1 -1
  262. package/src/runtime/interactive-ui.ts +1 -1
  263. package/src/runtime/routes/__tests__/acp-routes.test.ts +315 -55
  264. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  265. package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
  266. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  267. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  268. package/src/runtime/routes/acp-routes.test.ts +89 -25
  269. package/src/runtime/routes/acp-routes.ts +81 -29
  270. package/src/runtime/routes/approval-routes.ts +1 -1
  271. package/src/runtime/routes/browser-routes.ts +1 -1
  272. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  273. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  274. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  275. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  276. package/src/runtime/routes/conversation-routes.ts +28 -2
  277. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  278. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  279. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  280. package/src/runtime/routes/host-cu-routes.ts +1 -1
  281. package/src/runtime/routes/identity-routes.ts +18 -3
  282. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  283. package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
  284. package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
  285. package/src/runtime/routes/memory-v3-routes.ts +16 -6
  286. package/src/runtime/routes/playground/helpers.ts +1 -1
  287. package/src/runtime/routes/plugins-routes.ts +337 -35
  288. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  289. package/src/runtime/routes/work-items-routes.ts +2 -4
  290. package/src/runtime/services/conversation-serializer.ts +1 -1
  291. package/src/signals/cancel.ts +2 -4
  292. package/src/subagent/manager.ts +21 -5
  293. package/src/tools/acp/context.ts +20 -0
  294. package/src/tools/acp/list-agents.test.ts +8 -2
  295. package/src/tools/acp/spawn.test.ts +176 -195
  296. package/src/tools/acp/spawn.ts +37 -172
  297. package/src/tools/acp/steer.test.ts +105 -8
  298. package/src/tools/acp/steer.ts +48 -17
  299. package/src/tools/apps/executors.ts +166 -50
  300. package/src/tools/filesystem/write.ts +34 -0
  301. package/src/tools/subagent/spawn.ts +2 -4
  302. package/src/tools/ui-surface/definitions.ts +25 -5
  303. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  304. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
  305. package/docs/plugins.md +0 -832
  306. package/examples/plugins/echo/register.ts +0 -143
  307. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  308. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  309. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  310. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
  311. package/src/__tests__/pipeline-runner.test.ts +0 -554
  312. package/src/__tests__/plugin-external-api.test.ts +0 -68
  313. package/src/daemon/wake-target-adapter.ts +0 -253
  314. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  315. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  316. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  317. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  318. package/src/plugins/defaults/compaction/register.ts +0 -35
  319. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  320. package/src/plugins/defaults/empty-response/register.ts +0 -23
  321. package/src/plugins/defaults/history-repair/register.ts +0 -24
  322. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  323. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  324. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  325. package/src/plugins/defaults/title-generate/register.ts +0 -35
  326. package/src/plugins/defaults/tool-error/register.ts +0 -23
  327. package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
  328. package/src/plugins/external-api.ts +0 -104
  329. package/src/proactive-artifact/aux-message-injector.ts +0 -97
  330. package/src/proactive-artifact/decision.test.ts +0 -226
  331. package/src/proactive-artifact/decision.ts +0 -165
  332. package/src/proactive-artifact/index.ts +0 -7
  333. package/src/proactive-artifact/job.test.ts +0 -962
  334. package/src/proactive-artifact/job.ts +0 -372
  335. package/src/proactive-artifact/message-copy.ts +0 -58
  336. package/src/proactive-artifact/trigger-state.test.ts +0 -286
  337. package/src/proactive-artifact/trigger-state.ts +0 -123
  338. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
  339. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
  340. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  341. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  342. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +0 -0
  343. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
  344. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/provider-blocks.test.ts +0 -0
  345. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
  346. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
  347. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  348. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  349. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/core.ts +0 -0
  350. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  351. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  352. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  353. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  354. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  355. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  356. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -0
  357. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
  358. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
  359. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
  360. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Tests for {@link fetchMarketplaceEntries} and {@link resolveMarketplaceSource}.
3
+ *
4
+ * Network is replaced with an in-memory fixture passed via the `fetch`
5
+ * dependency — no globals are monkey-patched.
6
+ */
7
+
8
+ import { describe, expect, test } from "bun:test";
9
+
10
+ import type { FetchLike } from "../install-from-github.js";
11
+ import {
12
+ fetchMarketplaceEntries,
13
+ type MarketplaceEntry,
14
+ MarketplaceFetchError,
15
+ resolveMarketplaceSource,
16
+ } from "../plugin-marketplace.js";
17
+
18
+ const MANIFEST_URL_PREFIX =
19
+ "https://api.github.com/repos/vellum-ai/vellum-assistant/contents/experimental/plugins/marketplace.json";
20
+
21
+ /** Serve `body` (any value) as the raw manifest file at the manifest URL. */
22
+ function manifestFetch(body: unknown, status = 200, raw?: string): FetchLike {
23
+ return (async (input: RequestInfo | URL) => {
24
+ const url = typeof input === "string" ? input : input.toString();
25
+ if (!url.startsWith(MANIFEST_URL_PREFIX)) {
26
+ return new Response("unexpected url: " + url, { status: 500 });
27
+ }
28
+ if (status !== 200) {
29
+ return new Response("error", { status });
30
+ }
31
+ return new Response(raw ?? JSON.stringify(body), { status: 200 });
32
+ }) as FetchLike;
33
+ }
34
+
35
+ const VALID_MANIFEST = {
36
+ name: "vellum-assistant",
37
+ owner: { name: "Vellum" },
38
+ plugins: [
39
+ {
40
+ name: "caveman",
41
+ source: {
42
+ source: "github",
43
+ repo: "JuliusBrussee/caveman",
44
+ ref: "v1.8.2",
45
+ },
46
+ description: "Ultra-compressed communication mode.",
47
+ category: "productivity",
48
+ },
49
+ {
50
+ name: "nested",
51
+ source: {
52
+ source: "github",
53
+ repo: "acme/monorepo",
54
+ path: "packages/nested",
55
+ ref: "abc123",
56
+ },
57
+ },
58
+ ],
59
+ };
60
+
61
+ describe("fetchMarketplaceEntries", () => {
62
+ test("parses a valid manifest and returns its plugin entries", async () => {
63
+ // GIVEN a canonical repo serving a valid marketplace manifest
64
+ const fetch = manifestFetch(VALID_MANIFEST);
65
+
66
+ // WHEN we fetch the marketplace entries at a ref
67
+ const entries = await fetchMarketplaceEntries({ fetch }, { ref: "main" });
68
+
69
+ // THEN both whitelisted plugins are returned with their pinned sources
70
+ expect(entries.map((e) => e.name)).toEqual(["caveman", "nested"]);
71
+ expect(entries[0]!.source).toEqual({
72
+ source: "github",
73
+ repo: "JuliusBrussee/caveman",
74
+ ref: "v1.8.2",
75
+ });
76
+ expect(entries[1]!.source.path).toBe("packages/nested");
77
+ });
78
+
79
+ test("forwards the ref to the GitHub Contents API", async () => {
80
+ // GIVEN a fixture that records the ref query parameter
81
+ let seenRef: string | undefined;
82
+ const fetch: FetchLike = (async (input: RequestInfo | URL) => {
83
+ const url = typeof input === "string" ? input : input.toString();
84
+ seenRef = /[?&]ref=([^&]+)/.exec(url)?.[1];
85
+ return new Response(JSON.stringify(VALID_MANIFEST), { status: 200 });
86
+ }) as FetchLike;
87
+
88
+ // WHEN we fetch at an explicit ref
89
+ await fetchMarketplaceEntries({ fetch }, { ref: "feat-branch" });
90
+
91
+ // THEN the ref reaches GitHub
92
+ expect(seenRef).toBe("feat-branch");
93
+ });
94
+
95
+ test("treats a missing manifest (404) as an empty whitelist", async () => {
96
+ // GIVEN no manifest exists at this ref
97
+ const fetch = manifestFetch(null, 404);
98
+
99
+ // WHEN we fetch the entries
100
+ const entries = await fetchMarketplaceEntries({ fetch }, { ref: "main" });
101
+
102
+ // THEN the absence is a normal empty result, not an error
103
+ expect(entries).toEqual([]);
104
+ });
105
+
106
+ test("throws MarketplaceFetchError on non-404 HTTP failure", async () => {
107
+ // GIVEN GitHub returns a server error
108
+ const fetch = manifestFetch(null, 503);
109
+
110
+ // WHEN / THEN the failure surfaces as a typed error
111
+ await expect(
112
+ fetchMarketplaceEntries({ fetch }, { ref: "main" }),
113
+ ).rejects.toBeInstanceOf(MarketplaceFetchError);
114
+ });
115
+
116
+ test("throws MarketplaceFetchError on non-JSON body", async () => {
117
+ // GIVEN the manifest body is not valid JSON
118
+ const fetch = manifestFetch(undefined, 200, "{ not json");
119
+
120
+ // WHEN / THEN parsing fails loudly
121
+ await expect(
122
+ fetchMarketplaceEntries({ fetch }, { ref: "main" }),
123
+ ).rejects.toBeInstanceOf(MarketplaceFetchError);
124
+ });
125
+
126
+ test("rejects a manifest entry missing a pinned ref", async () => {
127
+ // GIVEN an entry whose source omits the required ref
128
+ const fetch = manifestFetch({
129
+ name: "x",
130
+ plugins: [
131
+ { name: "floating", source: { source: "github", repo: "a/b" } },
132
+ ],
133
+ });
134
+
135
+ // WHEN / THEN schema validation rejects the unpinned source
136
+ await expect(
137
+ fetchMarketplaceEntries({ fetch }, { ref: "main" }),
138
+ ).rejects.toBeInstanceOf(MarketplaceFetchError);
139
+ });
140
+
141
+ test("rejects a path that escapes the repo root", async () => {
142
+ // GIVEN an entry whose path contains a parent-segment escape
143
+ const fetch = manifestFetch({
144
+ name: "x",
145
+ plugins: [
146
+ {
147
+ name: "escape",
148
+ source: {
149
+ source: "github",
150
+ repo: "a/b",
151
+ path: "../../etc",
152
+ ref: "main",
153
+ },
154
+ },
155
+ ],
156
+ });
157
+
158
+ // WHEN / THEN the unsafe path is rejected
159
+ await expect(
160
+ fetchMarketplaceEntries({ fetch }, { ref: "main" }),
161
+ ).rejects.toBeInstanceOf(MarketplaceFetchError);
162
+ });
163
+ });
164
+
165
+ describe("resolveMarketplaceSource", () => {
166
+ const entries: readonly MarketplaceEntry[] = [
167
+ {
168
+ name: "caveman",
169
+ source: {
170
+ source: "github",
171
+ repo: "JuliusBrussee/caveman",
172
+ ref: "v1.8.2",
173
+ },
174
+ },
175
+ {
176
+ name: "nested",
177
+ source: {
178
+ source: "github",
179
+ repo: "acme/monorepo",
180
+ path: "packages/nested",
181
+ ref: "abc123",
182
+ },
183
+ },
184
+ ];
185
+
186
+ test("resolves a repo-root plugin to owner/repo with empty path", () => {
187
+ // GIVEN whitelisted entries
188
+ // WHEN resolving a name whose source has no sub-path
189
+ const resolved = resolveMarketplaceSource("caveman", entries);
190
+
191
+ // THEN the owner/repo split out and path defaults to repo root
192
+ expect(resolved).toEqual({
193
+ owner: "JuliusBrussee",
194
+ repo: "caveman",
195
+ path: "",
196
+ ref: "v1.8.2",
197
+ });
198
+ });
199
+
200
+ test("resolves a sub-path plugin to its directory", () => {
201
+ // GIVEN whitelisted entries
202
+ // WHEN resolving a name whose source pins a sub-path
203
+ const resolved = resolveMarketplaceSource("nested", entries);
204
+
205
+ // THEN the sub-path is preserved
206
+ expect(resolved).toEqual({
207
+ owner: "acme",
208
+ repo: "monorepo",
209
+ path: "packages/nested",
210
+ ref: "abc123",
211
+ });
212
+ });
213
+
214
+ test("returns null for a name not in the whitelist", () => {
215
+ // GIVEN whitelisted entries
216
+ // WHEN resolving an unknown name
217
+ // THEN no source is returned (caller falls back to first-party)
218
+ expect(resolveMarketplaceSource("unknown", entries)).toBeNull();
219
+ });
220
+ });
@@ -11,6 +11,7 @@ import { describe, expect, test } from "bun:test";
11
11
  import {
12
12
  type FetchLike,
13
13
  InvalidSearchPatternError,
14
+ PluginCatalogUnavailableError,
14
15
  searchPlugins,
15
16
  } from "../search-plugins.js";
16
17
 
@@ -180,44 +181,73 @@ describe("searchPlugins", () => {
180
181
  expect(result.matches.map((m) => m.name)).toEqual(["simple-memory"]);
181
182
  });
182
183
 
183
- test("HTTP 5xx from GitHub propagates with the status code", async () => {
184
- await expect(
185
- searchPlugins(
186
- { query: "memory" },
187
- {
188
- fetch: (async () =>
189
- new Response("upstream broken", { status: 503 })) as FetchLike,
190
- },
191
- ),
192
- ).rejects.toThrow(/HTTP 503/);
184
+ test("HTTP 5xx is a transient PluginCatalogUnavailableError", async () => {
185
+ // GIVEN GitHub returns a 5xx (upstream outage)
186
+ // WHEN we search
187
+ // THEN the failure is classified transient so the caller can serve a
188
+ // stale cache and the route can map it to 503 (not a misleading 500).
189
+ const err = await searchPlugins(
190
+ { query: "memory" },
191
+ {
192
+ fetch: (async () =>
193
+ new Response("upstream broken", { status: 503 })) as FetchLike,
194
+ },
195
+ ).catch((e: unknown) => e);
196
+ expect(err).toBeInstanceOf(PluginCatalogUnavailableError);
197
+ expect((err as PluginCatalogUnavailableError).status).toBe(503);
193
198
  });
194
199
 
195
- test("HTTP 403 (rate-limited / forbidden) surfaces as an error", async () => {
196
- await expect(
197
- searchPlugins(
198
- { query: "memory" },
199
- {
200
- fetch: (async () =>
201
- new Response("rate limit exceeded", { status: 403 })) as FetchLike,
202
- },
203
- ),
204
- ).rejects.toThrow(/HTTP 403/);
200
+ test("rate-limited 403 (x-ratelimit-remaining: 0) is transient", async () => {
201
+ // GIVEN GitHub returns 403 with the rate-limit budget exhausted
202
+ // WHEN we search
203
+ // THEN it's transient — this is exactly the unauthenticated 60 req/hr
204
+ // exhaustion that empties the catalog, and it must surface as 503.
205
+ const err = await searchPlugins(
206
+ { query: "memory" },
207
+ {
208
+ fetch: (async () =>
209
+ new Response("rate limit exceeded", {
210
+ status: 403,
211
+ headers: { "x-ratelimit-remaining": "0" },
212
+ })) as FetchLike,
213
+ },
214
+ ).catch((e: unknown) => e);
215
+ expect(err).toBeInstanceOf(PluginCatalogUnavailableError);
216
+ expect((err as PluginCatalogUnavailableError).status).toBe(403);
217
+ });
218
+
219
+ test("a 403 without the rate-limit signal is a hard error", async () => {
220
+ // GIVEN a bare 403 (genuine permissions problem, not rate limiting)
221
+ // WHEN we search
222
+ // THEN it is NOT transient — serving a stale catalog would mask a real
223
+ // misconfiguration, so it propagates as a plain error.
224
+ const err = await searchPlugins(
225
+ { query: "memory" },
226
+ {
227
+ fetch: (async () =>
228
+ new Response("forbidden", { status: 403 })) as FetchLike,
229
+ },
230
+ ).catch((e: unknown) => e);
231
+ expect(err).toBeInstanceOf(Error);
232
+ expect(err).not.toBeInstanceOf(PluginCatalogUnavailableError);
233
+ expect((err as Error).message).toMatch(/HTTP 403/);
205
234
  });
206
235
 
207
- test("404 on the plugins prefix surfaces as an error (not silently empty)", async () => {
236
+ test("404 on the plugins prefix is a hard error (not silently empty)", async () => {
208
237
  // Distinct from `installPlugin`, where 404 on a specific plugin name is
209
238
  // normal "not found". For the search, 404 on the prefix means the
210
- // canonical source path itself is gone — that's an upstream problem
211
- // worth surfacing, not a clean empty result.
212
- await expect(
213
- searchPlugins(
214
- { query: "memory" },
215
- {
216
- fetch: (async () =>
217
- new Response("not found", { status: 404 })) as FetchLike,
218
- },
219
- ),
220
- ).rejects.toThrow(/HTTP 404/);
239
+ // canonical source path itself is gone — that's a real misconfiguration
240
+ // worth surfacing, not a clean empty result and not a transient outage.
241
+ const err = await searchPlugins(
242
+ { query: "memory" },
243
+ {
244
+ fetch: (async () =>
245
+ new Response("not found", { status: 404 })) as FetchLike,
246
+ },
247
+ ).catch((e: unknown) => e);
248
+ expect(err).toBeInstanceOf(Error);
249
+ expect(err).not.toBeInstanceOf(PluginCatalogUnavailableError);
250
+ expect((err as Error).message).toMatch(/HTTP 404/);
221
251
  });
222
252
 
223
253
  test("returns matches sorted by name", async () => {
@@ -238,6 +268,170 @@ describe("searchPlugins", () => {
238
268
  ]);
239
269
  });
240
270
 
271
+ test("merges whitelisted marketplace entries with first-party dirs", async () => {
272
+ // GIVEN the canonical repo serves both a first-party plugin listing and a
273
+ // marketplace manifest whitelisting an external plugin
274
+ const manifest = {
275
+ name: "vellum-assistant",
276
+ plugins: [
277
+ {
278
+ name: "caveman",
279
+ source: {
280
+ source: "github",
281
+ repo: "JuliusBrussee/caveman",
282
+ ref: "v1.8.2",
283
+ },
284
+ description: "Ultra-compressed communication mode.",
285
+ },
286
+ ],
287
+ };
288
+ const fetch: FetchLike = (async (input: RequestInfo | URL) => {
289
+ const url = typeof input === "string" ? input : input.toString();
290
+ if (url.includes("marketplace.json")) {
291
+ return new Response(JSON.stringify(manifest), { status: 200 });
292
+ }
293
+ return new Response(
294
+ JSON.stringify([
295
+ {
296
+ name: "simple-memory",
297
+ path: "experimental/plugins/simple-memory",
298
+ type: "dir",
299
+ size: 0,
300
+ download_url: null,
301
+ },
302
+ ]),
303
+ { status: 200, headers: { "content-type": "application/json" } },
304
+ );
305
+ }) as FetchLike;
306
+
307
+ // WHEN we search the catalog with a match-all query
308
+ const result = await searchPlugins({ query: "" }, { fetch });
309
+
310
+ // THEN both sources appear, sorted by name, each tagged with its origin
311
+ expect(result.matches).toEqual([
312
+ {
313
+ name: "caveman",
314
+ path: "github:JuliusBrussee/caveman@v1.8.2",
315
+ description: "Ultra-compressed communication mode.",
316
+ source: {
317
+ kind: "github",
318
+ repo: "JuliusBrussee/caveman",
319
+ ref: "v1.8.2",
320
+ },
321
+ },
322
+ {
323
+ name: "simple-memory",
324
+ path: "experimental/plugins/simple-memory",
325
+ source: { kind: "first-party" },
326
+ },
327
+ ]);
328
+ });
329
+
330
+ test("filters marketplace entries by the query too", async () => {
331
+ // GIVEN a marketplace whitelisting an external plugin and no first-party dirs
332
+ const manifest = {
333
+ name: "vellum-assistant",
334
+ plugins: [
335
+ {
336
+ name: "caveman",
337
+ source: {
338
+ source: "github",
339
+ repo: "JuliusBrussee/caveman",
340
+ ref: "v1.8.2",
341
+ },
342
+ },
343
+ ],
344
+ };
345
+ const fetch: FetchLike = (async (input: RequestInfo | URL) => {
346
+ const url = typeof input === "string" ? input : input.toString();
347
+ if (url.includes("marketplace.json")) {
348
+ return new Response(JSON.stringify(manifest), { status: 200 });
349
+ }
350
+ return new Response(JSON.stringify([]), { status: 200 });
351
+ }) as FetchLike;
352
+
353
+ // WHEN the query matches no plugin name
354
+ const result = await searchPlugins({ query: "memory" }, { fetch });
355
+
356
+ // THEN the non-matching marketplace entry is excluded
357
+ expect(result.matches).toEqual([]);
358
+ });
359
+
360
+ test("first-party dirs win a name collision with the marketplace", async () => {
361
+ // GIVEN both a first-party dir and a marketplace entry named "caveman"
362
+ const manifest = {
363
+ name: "vellum-assistant",
364
+ plugins: [
365
+ {
366
+ name: "caveman",
367
+ source: {
368
+ source: "github",
369
+ repo: "JuliusBrussee/caveman",
370
+ ref: "v1.8.2",
371
+ },
372
+ },
373
+ ],
374
+ };
375
+ const fetch: FetchLike = (async (input: RequestInfo | URL) => {
376
+ const url = typeof input === "string" ? input : input.toString();
377
+ if (url.includes("marketplace.json")) {
378
+ return new Response(JSON.stringify(manifest), { status: 200 });
379
+ }
380
+ return new Response(
381
+ JSON.stringify([
382
+ {
383
+ name: "caveman",
384
+ path: "experimental/plugins/caveman",
385
+ type: "dir",
386
+ size: 0,
387
+ download_url: null,
388
+ },
389
+ ]),
390
+ { status: 200 },
391
+ );
392
+ }) as FetchLike;
393
+
394
+ // WHEN we search
395
+ const result = await searchPlugins({ query: "caveman" }, { fetch });
396
+
397
+ // THEN only the first-party entry surfaces — the manifest is additive
398
+ expect(result.matches).toEqual([
399
+ {
400
+ name: "caveman",
401
+ path: "experimental/plugins/caveman",
402
+ source: { kind: "first-party" },
403
+ },
404
+ ]);
405
+ });
406
+
407
+ test("a broken marketplace manifest degrades to the first-party listing", async () => {
408
+ // GIVEN the manifest is malformed but the first-party listing is healthy
409
+ const fetch: FetchLike = (async (input: RequestInfo | URL) => {
410
+ const url = typeof input === "string" ? input : input.toString();
411
+ if (url.includes("marketplace.json")) {
412
+ return new Response("{ not json", { status: 200 });
413
+ }
414
+ return new Response(
415
+ JSON.stringify([
416
+ {
417
+ name: "simple-memory",
418
+ path: "experimental/plugins/simple-memory",
419
+ type: "dir",
420
+ size: 0,
421
+ download_url: null,
422
+ },
423
+ ]),
424
+ { status: 200 },
425
+ );
426
+ }) as FetchLike;
427
+
428
+ // WHEN we search
429
+ const result = await searchPlugins({ query: "" }, { fetch });
430
+
431
+ // THEN the core catalog is unaffected by the broken whitelist
432
+ expect(result.matches.map((m) => m.name)).toEqual(["simple-memory"]);
433
+ });
434
+
241
435
  test("sends no Authorization header (canonical source is a public repo)", async () => {
242
436
  let seenAuth: string | undefined;
243
437
  let seenUserAgent: string | undefined;