@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091926.ebb2d62

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 (490) hide show
  1. package/ARCHITECTURE.md +6 -6
  2. package/bun.lock +2 -2
  3. package/docs/activation-funnel-telemetry.md +310 -0
  4. package/examples/plugins/echo/README.md +61 -60
  5. package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
  6. package/examples/plugins/echo/hooks/stop.ts +16 -0
  7. package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
  8. package/examples/plugins/echo/package.json +1 -2
  9. package/examples/plugins/echo/src/emit.ts +19 -0
  10. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
  11. package/openapi.yaml +273 -121
  12. package/package.json +2 -2
  13. package/scripts/sync-llm-catalog.ts +2 -0
  14. package/src/__tests__/activation-early-marking.test.ts +120 -0
  15. package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
  16. package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
  17. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
  18. package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
  19. package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
  20. package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
  21. package/src/__tests__/agent-loop-thinking.test.ts +36 -20
  22. package/src/__tests__/agent-loop.test.ts +441 -96
  23. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
  24. package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
  25. package/src/__tests__/anthropic-provider.test.ts +23 -8
  26. package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
  27. package/src/__tests__/app-control-flow.test.ts +1 -1
  28. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  29. package/src/__tests__/app-executors.test.ts +132 -0
  30. package/src/__tests__/approval-cascade.test.ts +6 -5
  31. package/src/__tests__/approval-routes-http.test.ts +4 -1
  32. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  33. package/src/__tests__/build-persisted-content.test.ts +75 -1
  34. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  35. package/src/__tests__/channel-approvals.test.ts +1 -1
  36. package/src/__tests__/compaction-circuit.test.ts +258 -0
  37. package/src/__tests__/compaction-direct.test.ts +146 -0
  38. package/src/__tests__/compaction-events.test.ts +7 -7
  39. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  40. package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
  41. package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
  42. package/src/__tests__/context-overflow-reducer.test.ts +265 -125
  43. package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
  44. package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
  45. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
  46. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
  47. package/src/__tests__/conversation-agent-loop-overflow.test.ts +158 -234
  48. package/src/__tests__/conversation-agent-loop.test.ts +342 -623
  49. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  50. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
  51. package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
  52. package/src/__tests__/conversation-error.test.ts +15 -1
  53. package/src/__tests__/conversation-history-web-search.test.ts +6 -1
  54. package/src/__tests__/conversation-media-retry.test.ts +1 -1
  55. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
  56. package/src/__tests__/conversation-process-callsite.test.ts +15 -15
  57. package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
  58. package/src/__tests__/conversation-queue.test.ts +10 -10
  59. package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
  60. package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
  61. package/src/__tests__/conversation-slash-queue.test.ts +7 -5
  62. package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
  63. package/src/__tests__/conversation-speed-override.test.ts +11 -10
  64. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  65. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
  66. package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
  67. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
  68. package/src/__tests__/conversation-undo.test.ts +2 -2
  69. package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
  70. package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
  71. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
  72. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  73. package/src/__tests__/corrected-target.test.ts +93 -0
  74. package/src/__tests__/credential-execution-feature-gates.test.ts +5 -4
  75. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  76. package/src/__tests__/cu-unified-flow.test.ts +36 -0
  77. package/src/__tests__/db-acp-history.test.ts +101 -0
  78. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  79. package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
  80. package/src/__tests__/empty-response-hook.test.ts +1 -1
  81. package/src/__tests__/file-write-tool.test.ts +63 -0
  82. package/src/__tests__/gateway-only-guard.test.ts +17 -2
  83. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  84. package/src/__tests__/guardian-routing-invariants.test.ts +93 -4
  85. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  86. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  87. package/src/__tests__/heartbeat-service.test.ts +1 -0
  88. package/src/__tests__/history-repair-hook.test.ts +5 -1
  89. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  90. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  91. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  92. package/src/__tests__/inference-profile-reaper.test.ts +62 -0
  93. package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
  94. package/src/__tests__/injector-background-turn.test.ts +13 -23
  95. package/src/__tests__/injector-chain.test.ts +268 -44
  96. package/src/__tests__/injector-disk-pressure.test.ts +210 -52
  97. package/src/__tests__/injector-document-comments.test.ts +97 -114
  98. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  99. package/src/__tests__/injector-v3-suppression.test.ts +4 -4
  100. package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
  101. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  102. package/src/__tests__/llm-catalog-parity.test.ts +4 -0
  103. package/src/__tests__/llm-resolver.test.ts +73 -0
  104. package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
  105. package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
  106. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  107. package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
  108. package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
  109. package/src/__tests__/persist-unsendable-image.test.ts +97 -1
  110. package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
  111. package/src/__tests__/plugin-api-shim.test.ts +6 -9
  112. package/src/__tests__/plugin-bootstrap.test.ts +91 -81
  113. package/src/__tests__/plugin-registry.test.ts +3 -49
  114. package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
  115. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  116. package/src/__tests__/plugin-types.test.ts +0 -70
  117. package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
  118. package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
  119. package/src/__tests__/published-app-updater.test.ts +138 -0
  120. package/src/__tests__/reaction-persistence.test.ts +1 -1
  121. package/src/__tests__/retry-thinking-adaptive-only.test.ts +245 -0
  122. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  123. package/src/__tests__/server-history-render.test.ts +39 -0
  124. package/src/__tests__/skill-feature-flags-integration.test.ts +31 -0
  125. package/src/__tests__/steer-tool-repair.test.ts +1 -1
  126. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  127. package/src/__tests__/subagent-detail.test.ts +25 -7
  128. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  129. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  130. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  131. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  132. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  133. package/src/__tests__/title-generate-hook.test.ts +5 -1
  134. package/src/__tests__/tool-error-hook.test.ts +1 -1
  135. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
  136. package/src/__tests__/tool-start-timestamp.test.ts +218 -0
  137. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  138. package/src/__tests__/web-fetch.test.ts +45 -0
  139. package/src/acp/__tests__/agent-process.test.ts +161 -0
  140. package/src/acp/__tests__/client-handler.test.ts +40 -0
  141. package/src/acp/__tests__/helpers/acp-config-stub.ts +0 -2
  142. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  143. package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
  144. package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
  145. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  146. package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
  147. package/src/acp/agent-process.ts +61 -1
  148. package/src/acp/auto-install.test.ts +280 -0
  149. package/src/acp/auto-install.ts +232 -0
  150. package/src/acp/client-handler.ts +31 -0
  151. package/src/acp/prepare-agent-env.ts +80 -27
  152. package/src/acp/resolve-agent.test.ts +188 -28
  153. package/src/acp/resolve-agent.ts +119 -42
  154. package/src/acp/resume-hint.ts +23 -0
  155. package/src/acp/session-manager.ts +507 -73
  156. package/src/agent/compaction-circuit.ts +60 -102
  157. package/src/agent/loop.ts +403 -251
  158. package/src/api/events/tool-result.ts +9 -0
  159. package/src/api/events/tool-use-start.ts +7 -0
  160. package/src/api/responses/conversation-message.ts +72 -13
  161. package/src/api/responses/memory-v3-selection-log.ts +19 -10
  162. package/src/approvals/guardian-request-resolvers.ts +27 -1
  163. package/src/background-wake/next-wake.ts +1 -0
  164. package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
  165. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  166. package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
  167. package/src/cli/commands/memory-v3.ts +57 -199
  168. package/src/cli/commands/plugins.ts +43 -37
  169. package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
  170. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
  171. package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
  172. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
  173. package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
  174. package/src/cli/lib/install-from-github.ts +797 -60
  175. package/src/cli/lib/plugin-catalog-cache.ts +84 -0
  176. package/src/cli/lib/plugin-details.ts +416 -0
  177. package/src/cli/lib/plugin-marketplace.ts +215 -0
  178. package/src/cli/lib/search-plugins.ts +201 -29
  179. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  180. package/src/config/acp-defaults.test.ts +10 -0
  181. package/src/config/acp-defaults.ts +9 -3
  182. package/src/config/acp-schema.ts +1 -7
  183. package/src/config/bundled-skills/acp/SKILL.md +80 -41
  184. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  185. package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
  186. package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
  187. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  188. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  189. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  190. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  191. package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
  192. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  193. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  194. package/src/config/bundled-tool-registry.ts +4 -0
  195. package/src/config/call-site-defaults.ts +0 -3
  196. package/src/config/feature-flag-registry.json +10 -56
  197. package/src/config/llm-resolver.ts +39 -7
  198. package/src/config/loader.ts +5 -22
  199. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
  200. package/src/config/schemas/call-site-catalog.ts +0 -21
  201. package/src/config/schemas/heartbeat.ts +9 -0
  202. package/src/config/schemas/llm.ts +17 -3
  203. package/src/config/schemas/memory-v3.ts +58 -8
  204. package/src/config/seed-inference-profiles.ts +18 -0
  205. package/src/context/compactor.ts +22 -4
  206. package/src/context/post-turn-tool-result-truncation.ts +39 -1
  207. package/src/context/strip-injections.ts +8 -2
  208. package/src/daemon/conversation-agent-loop-handlers.ts +120 -42
  209. package/src/daemon/conversation-agent-loop.ts +308 -1297
  210. package/src/daemon/conversation-error.ts +31 -4
  211. package/src/daemon/conversation-history.ts +1 -1
  212. package/src/daemon/conversation-lifecycle.ts +11 -255
  213. package/src/daemon/conversation-media-retry.ts +19 -6
  214. package/src/daemon/conversation-messaging.ts +17 -0
  215. package/src/daemon/conversation-process.ts +22 -141
  216. package/src/daemon/conversation-queue-manager.ts +8 -0
  217. package/src/daemon/conversation-registry.ts +159 -0
  218. package/src/daemon/conversation-runtime-assembly.ts +361 -393
  219. package/src/daemon/conversation-store.ts +9 -90
  220. package/src/daemon/conversation-surfaces.ts +165 -11
  221. package/src/daemon/conversation-workspace.ts +17 -0
  222. package/src/daemon/conversation.ts +444 -62
  223. package/src/daemon/disk-pressure-policy.ts +0 -1
  224. package/src/daemon/external-plugins-bootstrap.ts +36 -46
  225. package/src/daemon/handlers/config-channels.ts +11 -2
  226. package/src/daemon/handlers/conversations.ts +3 -1
  227. package/src/daemon/handlers/shared.ts +9 -1
  228. package/src/daemon/handlers/skills.ts +4 -1
  229. package/src/daemon/host-app-control-proxy.ts +72 -57
  230. package/src/daemon/host-proxy-preactivation.ts +1 -3
  231. package/src/daemon/lifecycle.ts +21 -7
  232. package/src/daemon/persist-unsendable-image.ts +62 -25
  233. package/src/daemon/process-message.ts +1 -1
  234. package/src/daemon/server.ts +2 -0
  235. package/src/daemon/tool-side-effects.ts +15 -0
  236. package/src/daemon/wake-conversation-ops.ts +269 -0
  237. package/src/embedded/plugin-api.ts +2 -2
  238. package/src/export/__tests__/transcript-formatter.test.ts +5 -0
  239. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  240. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  241. package/src/heartbeat/heartbeat-service.ts +26 -0
  242. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  243. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  244. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  245. package/src/ipc/skill-routes/memory.ts +4 -2
  246. package/src/memory/__tests__/activation-session-store.test.ts +41 -0
  247. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
  248. package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
  249. package/src/memory/activation-session-store.ts +43 -0
  250. package/src/memory/conversation-crud.ts +29 -19
  251. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  252. package/src/memory/db-init.ts +6 -0
  253. package/src/memory/embedding-backend.ts +5 -22
  254. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  255. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
  256. package/src/memory/jobs-worker.ts +25 -1
  257. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  258. package/src/memory/migrations/273-onboarding-events-funnel-columns.ts +46 -0
  259. package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
  260. package/src/memory/migrations/index.ts +3 -0
  261. package/src/memory/onboarding-events-store.ts +66 -18
  262. package/src/memory/schema/acp.ts +4 -0
  263. package/src/memory/schema/infrastructure.ts +13 -0
  264. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
  265. package/src/memory/v2/__tests__/page-store.test.ts +22 -0
  266. package/src/memory/v2/consolidation-job.ts +2 -63
  267. package/src/memory/v2/types.ts +5 -0
  268. package/src/messaging/providers/telegram-bot/api.ts +14 -5
  269. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  270. package/src/notifications/access-request-copy.ts +298 -0
  271. package/src/notifications/adapters/slack.ts +3 -3
  272. package/src/notifications/adapters/telegram.ts +9 -2
  273. package/src/notifications/copy-composer.ts +49 -267
  274. package/src/notifications/decision-engine.ts +16 -35
  275. package/src/notifications/home-feed-side-effect.ts +1 -6
  276. package/src/plugin-api/constants.ts +4 -0
  277. package/src/plugin-api/index.ts +40 -11
  278. package/src/plugin-api/types.ts +108 -0
  279. package/src/plugins/defaults/compaction/compact.ts +106 -0
  280. package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
  281. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  282. package/src/plugins/defaults/compaction/manager-store.ts +57 -0
  283. package/src/plugins/defaults/compaction/package.json +1 -2
  284. package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
  285. package/src/plugins/defaults/empty-response/package.json +0 -1
  286. package/src/plugins/defaults/history-repair/package.json +0 -1
  287. package/src/plugins/defaults/index.ts +156 -26
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
  289. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
  290. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
  291. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
  292. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  293. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
  297. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
  298. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
  299. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
  300. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
  301. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
  302. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
  303. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
  304. package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
  305. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
  306. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
  307. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
  308. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
  309. package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
  310. package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
  311. package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
  312. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  313. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  314. package/src/plugins/defaults/memory-v3-shadow/injector.ts +76 -0
  315. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
  316. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
  317. package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
  318. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
  319. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
  320. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
  321. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
  322. package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
  323. package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
  324. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
  325. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
  326. package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
  327. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -1
  328. package/src/plugins/defaults/title-generate/package.json +0 -1
  329. package/src/plugins/defaults/tool-error/package.json +0 -1
  330. package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
  331. package/src/plugins/external-api.ts +12 -2
  332. package/src/plugins/pipeline.ts +6 -293
  333. package/src/plugins/registry.ts +9 -37
  334. package/src/plugins/types.ts +76 -381
  335. package/src/plugins/user-loader.ts +30 -127
  336. package/src/prompts/__tests__/system-prompt.test.ts +6 -0
  337. package/src/prompts/system-prompt.ts +61 -10
  338. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
  339. package/src/providers/anthropic/client.ts +9 -10
  340. package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
  341. package/src/providers/inference/logit-bias.ts +55 -0
  342. package/src/providers/model-catalog.ts +57 -0
  343. package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
  344. package/src/providers/openai/chat-completions-provider.ts +44 -1
  345. package/src/providers/retry.ts +57 -7
  346. package/src/providers/types.ts +6 -0
  347. package/src/runtime/__tests__/agent-wake.test.ts +555 -691
  348. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  349. package/src/runtime/agent-wake.ts +108 -209
  350. package/src/runtime/assistant-event-hub.ts +1 -1
  351. package/src/runtime/channel-approvals.ts +1 -1
  352. package/src/runtime/interactive-ui.ts +1 -1
  353. package/src/runtime/routes/__tests__/acp-routes.test.ts +466 -55
  354. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  355. package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
  356. package/src/runtime/routes/__tests__/stt-routes.test.ts +112 -0
  357. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  358. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  359. package/src/runtime/routes/acp-routes.test.ts +185 -32
  360. package/src/runtime/routes/acp-routes.ts +328 -30
  361. package/src/runtime/routes/app-management-routes.ts +3 -0
  362. package/src/runtime/routes/approval-routes.ts +1 -1
  363. package/src/runtime/routes/browser-routes.ts +1 -1
  364. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  365. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  366. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  367. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  368. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  369. package/src/runtime/routes/conversation-routes.ts +30 -2
  370. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  371. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  372. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  373. package/src/runtime/routes/host-cu-routes.ts +1 -1
  374. package/src/runtime/routes/identity-routes.ts +18 -3
  375. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  376. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  377. package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
  378. package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
  379. package/src/runtime/routes/memory-v3-routes.ts +65 -305
  380. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
  381. package/src/runtime/routes/playground/helpers.ts +1 -1
  382. package/src/runtime/routes/plugins-routes.ts +337 -35
  383. package/src/runtime/routes/stt-routes.ts +45 -12
  384. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  385. package/src/runtime/routes/work-items-routes.ts +2 -4
  386. package/src/runtime/routes/workspace-routes.ts +50 -15
  387. package/src/runtime/services/conversation-serializer.ts +1 -1
  388. package/src/runtime/verification-outbound-actions.ts +147 -2
  389. package/src/runtime/verification-templates.ts +29 -3
  390. package/src/services/published-app-updater.ts +30 -8
  391. package/src/signals/cancel.ts +2 -4
  392. package/src/subagent/manager.ts +21 -5
  393. package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
  394. package/src/telemetry/activation-funnel.ts +167 -0
  395. package/src/telemetry/types.ts +13 -0
  396. package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
  397. package/src/telemetry/usage-telemetry-reporter.ts +26 -1
  398. package/src/tools/acp/context.ts +20 -0
  399. package/src/tools/acp/list-agents.test.ts +9 -19
  400. package/src/tools/acp/list-agents.ts +3 -15
  401. package/src/tools/acp/spawn.test.ts +173 -202
  402. package/src/tools/acp/spawn.ts +37 -172
  403. package/src/tools/acp/steer.test.ts +105 -8
  404. package/src/tools/acp/steer.ts +48 -17
  405. package/src/tools/apps/executors.ts +166 -50
  406. package/src/tools/browser/browser-execution.ts +12 -2
  407. package/src/tools/filesystem/write.ts +34 -0
  408. package/src/tools/network/web-fetch.ts +65 -24
  409. package/src/tools/skills/load.ts +1 -1
  410. package/src/tools/subagent/spawn.ts +2 -4
  411. package/src/tools/ui-surface/definitions.ts +32 -5
  412. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  413. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
  414. package/docs/plugins.md +0 -832
  415. package/examples/plugins/echo/register.ts +0 -143
  416. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  417. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  418. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  419. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
  420. package/src/__tests__/pipeline-runner.test.ts +0 -554
  421. package/src/daemon/wake-target-adapter.ts +0 -253
  422. package/src/memory/v3/__tests__/assign.test.ts +0 -242
  423. package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
  424. package/src/memory/v3/__tests__/core.test.ts +0 -39
  425. package/src/memory/v3/__tests__/health.test.ts +0 -219
  426. package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
  427. package/src/memory/v3/__tests__/needle.test.ts +0 -107
  428. package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
  429. package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
  430. package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
  431. package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
  432. package/src/memory/v3/__tests__/router.test.ts +0 -332
  433. package/src/memory/v3/__tests__/selector.test.ts +0 -470
  434. package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
  435. package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
  436. package/src/memory/v3/__tests__/tree.test.ts +0 -192
  437. package/src/memory/v3/assign.ts +0 -268
  438. package/src/memory/v3/capabilities.ts +0 -124
  439. package/src/memory/v3/core.ts +0 -26
  440. package/src/memory/v3/health.ts +0 -0
  441. package/src/memory/v3/maintain-job.ts +0 -314
  442. package/src/memory/v3/needle.ts +0 -115
  443. package/src/memory/v3/orchestrate.ts +0 -126
  444. package/src/memory/v3/page-content.ts +0 -34
  445. package/src/memory/v3/provider-blocks.ts +0 -26
  446. package/src/memory/v3/reconcile.ts +0 -523
  447. package/src/memory/v3/router.ts +0 -190
  448. package/src/memory/v3/selection-log-store.ts +0 -84
  449. package/src/memory/v3/selector.ts +0 -226
  450. package/src/memory/v3/shadow-plugin.ts +0 -411
  451. package/src/memory/v3/snapshot.ts +0 -209
  452. package/src/memory/v3/tree.ts +0 -174
  453. package/src/memory/v3/types.ts +0 -59
  454. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  455. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  456. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  457. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  458. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  459. package/src/plugins/defaults/compaction/register.ts +0 -35
  460. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  461. package/src/plugins/defaults/empty-response/register.ts +0 -23
  462. package/src/plugins/defaults/history-repair/register.ts +0 -24
  463. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
  464. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  465. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  466. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  467. package/src/plugins/defaults/title-generate/register.ts +0 -35
  468. package/src/plugins/defaults/tool-error/register.ts +0 -23
  469. package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
  470. package/src/proactive-artifact/aux-message-injector.ts +0 -97
  471. package/src/proactive-artifact/decision.test.ts +0 -226
  472. package/src/proactive-artifact/decision.ts +0 -165
  473. package/src/proactive-artifact/index.ts +0 -7
  474. package/src/proactive-artifact/job.test.ts +0 -962
  475. package/src/proactive-artifact/job.ts +0 -372
  476. package/src/proactive-artifact/message-copy.ts +0 -58
  477. package/src/proactive-artifact/trigger-state.test.ts +0 -286
  478. package/src/proactive-artifact/trigger-state.ts +0 -123
  479. package/src/util/map-limit.ts +0 -27
  480. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  481. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  482. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  483. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  484. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  485. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  486. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  487. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  488. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  489. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  490. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Read the curated plugin marketplace manifest from the canonical repo.
3
+ *
4
+ * The manifest at `experimental/plugins/marketplace.json` whitelists external
5
+ * ecosystem plugins so they appear in `assistant plugins search` / the web
6
+ * catalog and become installable by name. Its shape is a subset of the
7
+ * Claude Code marketplace manifest
8
+ * (https://code.claude.com/docs/en/plugin-marketplaces) — `name` + `owner` +
9
+ * a `plugins` array where each entry carries a `name` and a `source`. Only
10
+ * `github` sources are resolved today.
11
+ *
12
+ * Like the first-party plugin listing, the manifest is fetched from the repo
13
+ * at a git ref (via the GitHub Contents API) rather than bundled into the
14
+ * assistant build — so the whitelist can grow without shipping a new release.
15
+ * Every external source pins an explicit `ref` that MUST be a full commit SHA:
16
+ * the fetched code is locked to an immutable revision. Tags and branches are
17
+ * rejected because they are mutable — an upstream owner could retag/repoint
18
+ * them to attacker code that the daemon would then `import()` (the install
19
+ * tree is dynamically loaded). A SHA cannot be repointed, so the reviewed
20
+ * manifest fully determines what gets executed.
21
+ *
22
+ * Designed for direct programmatic use with an injected `fetch`, mirroring
23
+ * {@link ./search-plugins} and {@link ./install-from-github}.
24
+ */
25
+
26
+ import { z } from "zod";
27
+
28
+ // Type-only import: keeps the dependency direction one-way at runtime
29
+ // (install-from-github resolves sources *through* this module) while still
30
+ // sharing the injected-`fetch` shape.
31
+ import type { FetchLike } from "./install-from-github.js";
32
+
33
+ /** Canonical location of the marketplace manifest. */
34
+ const MARKETPLACE_SOURCE_OWNER = "vellum-ai";
35
+ const MARKETPLACE_SOURCE_REPO = "vellum-assistant";
36
+ const MARKETPLACE_FILE_PATH = "experimental/plugins/marketplace.json";
37
+
38
+ // ---------------------------------------------------------------------------
39
+ // Manifest schema (subset of the Claude Code marketplace schema)
40
+ // ---------------------------------------------------------------------------
41
+
42
+ /** `owner/repo`, kebab/underscore/dot segments — matches GitHub's slug rules. */
43
+ const REPO_SLUG_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
44
+ /** Install name: a single kebab-case path segment (same rule as the CLI). */
45
+ const PLUGIN_NAME_RE = /^[a-z0-9][a-z0-9_-]*$/;
46
+ /**
47
+ * Full Git commit SHA — 40 hex chars (SHA-1) or 64 (SHA-256). External
48
+ * marketplace refs must be a complete object name so the install is pinned to
49
+ * an immutable revision; abbreviated SHAs, tags, and branches are all mutable
50
+ * or ambiguous and are rejected.
51
+ */
52
+ const COMMIT_SHA_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i;
53
+
54
+ const githubSourceSchema = z.object({
55
+ /** Discriminator. Only GitHub sources are resolved today. */
56
+ source: z.literal("github"),
57
+ /** `owner/repo` of the external plugin repository. */
58
+ repo: z.string().regex(REPO_SLUG_RE, "expected an `owner/repo` slug"),
59
+ /**
60
+ * Directory within the repo that holds the plugin root. Omitted / empty
61
+ * means the repository root. Must not escape the repo.
62
+ */
63
+ path: z
64
+ .string()
65
+ .refine(
66
+ (p) => !p.split(/[/\\]/).some((seg) => seg === ".." || seg === ""),
67
+ "path must be a clean repo-relative directory",
68
+ )
69
+ .optional(),
70
+ /**
71
+ * Immutable revision to fetch the plugin from. Must be a full commit SHA:
72
+ * tags and branches are mutable, so allowing them would let an upstream
73
+ * owner retarget a curated entry at code the daemon later `import()`s (RCE).
74
+ * A full SHA pins the install to exactly the reviewed bytes.
75
+ */
76
+ ref: z
77
+ .string()
78
+ .regex(
79
+ COMMIT_SHA_RE,
80
+ "expected a full commit SHA (40 or 64 hex chars); tags and branches are mutable and not allowed",
81
+ ),
82
+ });
83
+
84
+ const marketplaceEntrySchema = z.object({
85
+ /** Install name. `assistant plugins install <name>` resolves to this entry. */
86
+ name: z.string().regex(PLUGIN_NAME_RE, "expected a kebab-case install name"),
87
+ source: githubSourceSchema,
88
+ description: z.string().optional(),
89
+ /** Free-form grouping hint (e.g. `productivity`). Informational. */
90
+ category: z.string().optional(),
91
+ homepage: z.string().optional(),
92
+ license: z.string().optional(),
93
+ });
94
+
95
+ const marketplaceManifestSchema = z.object({
96
+ name: z.string(),
97
+ owner: z
98
+ .object({
99
+ name: z.string(),
100
+ url: z.string().optional(),
101
+ email: z.string().optional(),
102
+ })
103
+ .optional(),
104
+ plugins: z.array(marketplaceEntrySchema),
105
+ });
106
+
107
+ /** A single whitelisted external plugin entry. */
108
+ export type MarketplaceEntry = z.infer<typeof marketplaceEntrySchema>;
109
+
110
+ /** Concrete GitHub coordinates an entry resolves to for install. */
111
+ export interface ResolvedPluginSource {
112
+ readonly owner: string;
113
+ readonly repo: string;
114
+ /** Directory within the repo holding the plugin root; `""` = repo root. */
115
+ readonly path: string;
116
+ /** Git ref to fetch from. */
117
+ readonly ref: string;
118
+ }
119
+
120
+ /** Options controlling which marketplace revision to read. */
121
+ export interface FetchMarketplaceOptions {
122
+ /** Ref of the canonical repo to read the manifest from. */
123
+ readonly ref: string;
124
+ }
125
+
126
+ /** Dependencies injected by the caller. */
127
+ export interface FetchMarketplaceDeps {
128
+ /** HTTP client. Production callers pass `globalThis.fetch.bind(globalThis)`. */
129
+ readonly fetch: FetchLike;
130
+ }
131
+
132
+ /**
133
+ * The manifest could not be read or did not validate. Distinct from "no
134
+ * manifest at this ref" (a missing file is a normal, empty result) — this
135
+ * signals an upstream/parse problem the caller may choose to surface or
136
+ * degrade past.
137
+ */
138
+ export class MarketplaceFetchError extends Error {
139
+ constructor(message: string) {
140
+ super(message);
141
+ this.name = "MarketplaceFetchError";
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Fetch and validate the marketplace manifest, returning its plugin entries.
147
+ *
148
+ * A missing manifest (HTTP 404) is treated as an empty whitelist — the
149
+ * marketplace is supplementary to the first-party plugin listing, so its
150
+ * absence is not an error. Any other HTTP failure, a non-JSON body, or a
151
+ * schema violation throws {@link MarketplaceFetchError}.
152
+ */
153
+ export async function fetchMarketplaceEntries(
154
+ deps: FetchMarketplaceDeps,
155
+ opts: FetchMarketplaceOptions,
156
+ ): Promise<readonly MarketplaceEntry[]> {
157
+ const { ref } = opts;
158
+ const url =
159
+ `https://api.github.com/repos/${MARKETPLACE_SOURCE_OWNER}/${MARKETPLACE_SOURCE_REPO}` +
160
+ `/contents/${MARKETPLACE_FILE_PATH}?ref=${encodeURIComponent(ref)}`;
161
+
162
+ const res = await deps.fetch(url, {
163
+ headers: {
164
+ // Ask for the raw file body rather than the base64-wrapped Contents
165
+ // envelope, so the response text is the manifest JSON directly.
166
+ Accept: "application/vnd.github.raw",
167
+ "User-Agent": "vellum-assistant-cli",
168
+ },
169
+ });
170
+
171
+ if (res.status === 404) return [];
172
+ if (!res.ok) {
173
+ throw new MarketplaceFetchError(
174
+ `Marketplace manifest fetch failed for ${MARKETPLACE_FILE_PATH} @ ${ref}: HTTP ${res.status}`,
175
+ );
176
+ }
177
+
178
+ let json: unknown;
179
+ try {
180
+ json = JSON.parse(await res.text());
181
+ } catch (err) {
182
+ throw new MarketplaceFetchError(
183
+ `Marketplace manifest is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
184
+ );
185
+ }
186
+
187
+ const parsed = marketplaceManifestSchema.safeParse(json);
188
+ if (!parsed.success) {
189
+ throw new MarketplaceFetchError(
190
+ `Marketplace manifest failed validation: ${parsed.error.message}`,
191
+ );
192
+ }
193
+
194
+ return parsed.data.plugins;
195
+ }
196
+
197
+ /**
198
+ * Resolve a plugin name to concrete GitHub coordinates using the supplied
199
+ * marketplace entries. Returns `null` when no entry claims the name (the
200
+ * caller then falls back to the first-party source convention).
201
+ */
202
+ export function resolveMarketplaceSource(
203
+ name: string,
204
+ entries: readonly MarketplaceEntry[],
205
+ ): ResolvedPluginSource | null {
206
+ const entry = entries.find((e) => e.name === name);
207
+ if (!entry) return null;
208
+ const [owner, repo] = entry.source.repo.split("/", 2) as [string, string];
209
+ return {
210
+ owner,
211
+ repo,
212
+ path: entry.source.path ?? "",
213
+ ref: entry.source.ref,
214
+ };
215
+ }
@@ -1,10 +1,17 @@
1
1
  /**
2
- * Search for plugin directories in the canonical GitHub source.
2
+ * Search the installable plugin catalog in the canonical GitHub source.
3
3
  *
4
- * Lists `vellum-ai/vellum-assistant/experimental/plugins/` at the configured
5
- * git ref and filters the directory entries by case-insensitive ECMAScript
6
- * regex. A plain query like `"memory"` matches anywhere in the name; anchors
7
- * like `"^simple"` work without escaping.
4
+ * The catalog is the union of two sources, both fetched from the repo at the
5
+ * configured git ref:
6
+ * 1. First-party plugins — directories under
7
+ * `vellum-ai/vellum-assistant/experimental/plugins/`.
8
+ * 2. Whitelisted external ecosystem plugins — entries in the curated
9
+ * `experimental/plugins/marketplace.json` manifest (see
10
+ * {@link ./plugin-marketplace}).
11
+ *
12
+ * Entries are filtered by case-insensitive ECMAScript regex against the
13
+ * plugin name. A plain query like `"memory"` matches anywhere in the name;
14
+ * anchors like `"^simple"` work without escaping.
8
15
  *
9
16
  * Designed for direct programmatic use. The CLI command
10
17
  * `assistant plugins search <query>` is a thin wrapper that supplies
@@ -15,6 +22,10 @@
15
22
 
16
23
  import type { FetchLike } from "./install-from-github.js";
17
24
  import { DEFAULT_PLUGIN_REF } from "./install-from-github.js";
25
+ import {
26
+ fetchMarketplaceEntries,
27
+ type MarketplaceEntry,
28
+ } from "./plugin-marketplace.js";
18
29
 
19
30
  // Re-export the dep-injection type so callers can grab everything they need
20
31
  // from one module rather than reaching into `install-from-github.js`.
@@ -50,12 +61,33 @@ export interface SearchPluginsDeps {
50
61
  readonly fetch: FetchLike;
51
62
  }
52
63
 
53
- /** One matching plugin directory. */
64
+ /** Where a catalog match comes from. */
65
+ export type PluginMatchSource =
66
+ | { readonly kind: "first-party" }
67
+ | {
68
+ readonly kind: "github";
69
+ /** `owner/repo` of the external plugin repository. */
70
+ readonly repo: string;
71
+ /** Directory within the repo, when the plugin is not at the root. */
72
+ readonly path?: string;
73
+ /** Pinned git ref the plugin is fetched from. */
74
+ readonly ref: string;
75
+ };
76
+
77
+ /** One matching catalog entry. */
54
78
  export interface PluginSearchMatch {
55
- /** Directory name under `experimental/plugins/`. */
79
+ /** Install name — `assistant plugins install <name>` resolves to it. */
56
80
  readonly name: string;
57
- /** Path within the repo (e.g. `experimental/plugins/<name>`). */
81
+ /**
82
+ * Human-readable origin of the entry: the repo-relative path for
83
+ * first-party plugins (e.g. `experimental/plugins/<name>`) or a
84
+ * `github:owner/repo@ref` locator for external ones.
85
+ */
58
86
  readonly path: string;
87
+ /** Short description, when known (external entries only today). */
88
+ readonly description?: string;
89
+ /** Discriminated origin, so callers can render/install accordingly. */
90
+ readonly source: PluginMatchSource;
59
91
  }
60
92
 
61
93
  /** Search result envelope. */
@@ -75,12 +107,28 @@ export class InvalidSearchPatternError extends Error {
75
107
  }
76
108
 
77
109
  /**
78
- * List directories under `experimental/plugins/` at {@link opts.ref} and
79
- * filter by {@link opts.query}.
80
- *
81
- * Only `type === "dir"` entries are returned — `experimental/plugins/`
82
- * follows a convention where each plugin lives in its own directory, so
83
- * loose files at the prefix are not plugins.
110
+ * The catalog source (GitHub) was reachable but refused or could not serve
111
+ * the request right now — rate limiting (HTTP 403 with the rate-limit budget
112
+ * exhausted, or 429) or an upstream 5xx. Distinct from a hard 404 on the
113
+ * plugins prefix (a real "source gone" misconfiguration): a transient
114
+ * upstream failure should surface as a retryable "temporarily unavailable"
115
+ * rather than a generic internal error, and is a candidate for serving a
116
+ * stale cached catalog.
117
+ */
118
+ export class PluginCatalogUnavailableError extends Error {
119
+ /** Upstream HTTP status that triggered the failure. */
120
+ readonly status: number;
121
+ constructor(message: string, status: number) {
122
+ super(message);
123
+ this.name = "PluginCatalogUnavailableError";
124
+ this.status = status;
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Build the catalog at {@link opts.ref} and return the entries whose name
130
+ * matches {@link opts.query} (case-insensitive ECMAScript regex; an empty
131
+ * query matches everything).
84
132
  */
85
133
  export async function searchPlugins(
86
134
  opts: SearchPluginsOptions,
@@ -92,17 +140,125 @@ export async function searchPlugins(
92
140
  // the network — keeps "user typo" cheap to recover from.
93
141
  const matcher = buildMatcher(opts.query);
94
142
 
95
- const entries = await listDir(PLUGIN_SOURCE_PATH_PREFIX, ref, deps.fetch);
143
+ const { matches: catalog } = await loadPluginCatalog({ ref }, deps);
144
+ const matches = catalog.filter((m) => matcher(m.name));
145
+
146
+ return { query: opts.query, ref, matches };
147
+ }
148
+
149
+ /**
150
+ * Validate that {@link query} compiles as a case-insensitive ECMAScript regex,
151
+ * throwing {@link InvalidSearchPatternError} if not. Lets a caching caller
152
+ * (the daemon) reject a malformed query before loading the catalog, so a typo
153
+ * is a cheap deterministic 400 rather than a wasted GitHub request.
154
+ */
155
+ export function assertValidSearchPattern(query: string): void {
156
+ buildMatcher(query);
157
+ }
158
+
159
+ /**
160
+ * Filter a pre-loaded {@link PluginCatalog} by {@link query}, compiling it as
161
+ * a case-insensitive ECMAScript regex (an empty query matches everything).
162
+ * Lets a caching caller (the daemon) reuse one catalog load across many
163
+ * searches. Throws {@link InvalidSearchPatternError} on a malformed pattern.
164
+ */
165
+ export function filterPluginCatalog(
166
+ catalog: PluginCatalog,
167
+ query: string,
168
+ ): PluginSearchMatch[] {
169
+ const matcher = buildMatcher(query);
170
+ return catalog.matches.filter((m) => matcher(m.name));
171
+ }
172
+
173
+ /** The full, unfiltered catalog at a given ref. */
174
+ export interface PluginCatalog {
175
+ readonly ref: string;
176
+ /** Every catalog entry, deduped and sorted alphabetically by name. */
177
+ readonly matches: readonly PluginSearchMatch[];
178
+ }
179
+
180
+ /**
181
+ * Build the full catalog at {@link opts.ref}: every first-party plugin
182
+ * directory under `experimental/plugins/` merged with every whitelisted
183
+ * external entry in the marketplace manifest.
184
+ *
185
+ * The result is **query-independent** — `searchPlugins` applies the regex
186
+ * filter in memory afterwards. That separation is what lets a long-lived
187
+ * caller (the daemon) cache one catalog load and serve any number of
188
+ * searches from it without re-hitting GitHub (see {@link ./plugin-catalog-cache}).
189
+ */
190
+ export async function loadPluginCatalog(
191
+ opts: { readonly ref?: string },
192
+ deps: SearchPluginsDeps,
193
+ ): Promise<PluginCatalog> {
194
+ const ref = opts.ref ?? DEFAULT_PLUGIN_REF;
195
+
196
+ const [entries, marketplace] = await Promise.all([
197
+ listDir(PLUGIN_SOURCE_PATH_PREFIX, ref, deps.fetch),
198
+ fetchMarketplaceSafe(deps.fetch, ref),
199
+ ]);
96
200
 
97
201
  const matches: PluginSearchMatch[] = [];
202
+ const seen = new Set<string>();
203
+
204
+ // Whitelisted external entries own their name. A same-named
205
+ // `experimental/plugins/<name>/` directory is that plugin's curated adapter
206
+ // stub (overlaid onto the clone at install time), not a standalone
207
+ // first-party plugin — so it must surface once, as the external entry.
208
+ for (const entry of marketplace) {
209
+ if (seen.has(entry.name)) continue;
210
+ matches.push(marketplaceMatch(entry));
211
+ seen.add(entry.name);
212
+ }
213
+
214
+ // First-party plugins are the in-repo directories whose name the marketplace
215
+ // does not claim.
98
216
  for (const entry of entries) {
99
217
  if (entry.type !== "dir") continue;
100
- if (!matcher(entry.name)) continue;
101
- matches.push({ name: entry.name, path: entry.path });
218
+ if (seen.has(entry.name)) continue;
219
+ matches.push({
220
+ name: entry.name,
221
+ path: entry.path,
222
+ source: { kind: "first-party" },
223
+ });
224
+ seen.add(entry.name);
102
225
  }
226
+
103
227
  matches.sort((a, b) => a.name.localeCompare(b.name));
104
228
 
105
- return { query: opts.query, ref, matches };
229
+ return { ref, matches };
230
+ }
231
+
232
+ /**
233
+ * Project a marketplace entry onto the catalog match shape, building a
234
+ * `github:owner/repo[/path]@ref` locator for display.
235
+ */
236
+ function marketplaceMatch(entry: MarketplaceEntry): PluginSearchMatch {
237
+ const { repo, path, ref } = entry.source;
238
+ const locator = `github:${repo}${path ? `/${path}` : ""}@${ref}`;
239
+ return {
240
+ name: entry.name,
241
+ path: locator,
242
+ description: entry.description,
243
+ source: { kind: "github", repo, path, ref },
244
+ };
245
+ }
246
+
247
+ /**
248
+ * Fetch the marketplace manifest, degrading to an empty whitelist on any
249
+ * failure. The manifest is supplementary to the first-party listing, so a
250
+ * missing or malformed manifest must never break the core catalog — mirroring
251
+ * the daemon's "never block over a subsystem failure" philosophy.
252
+ */
253
+ async function fetchMarketplaceSafe(
254
+ fetchFn: FetchLike,
255
+ ref: string,
256
+ ): Promise<readonly MarketplaceEntry[]> {
257
+ try {
258
+ return await fetchMarketplaceEntries({ fetch: fetchFn }, { ref });
259
+ } catch {
260
+ return [];
261
+ }
106
262
  }
107
263
 
108
264
  function buildMatcher(query: string): (name: string) => boolean {
@@ -127,13 +283,17 @@ async function listDir(
127
283
 
128
284
  const res = await githubFetch(url, fetchFn);
129
285
  if (!res.ok) {
130
- // Unlike `installPlugin`, where 404 on a specific plugin name is a
131
- // legitimate "not found" outcome, 404 on the plugins prefix itself
132
- // means the canonical source path is gone — surface it as an error
133
- // rather than silently returning empty results.
134
- throw new Error(
135
- `GitHub contents listing failed for ${apiPath} @ ${ref}: HTTP ${res.status}`,
136
- );
286
+ const detail = `GitHub contents listing failed for ${apiPath} @ ${ref}: HTTP ${res.status}`;
287
+ // Rate limiting (403 with the budget exhausted, or 429) and upstream
288
+ // 5xx are transient — surface them as a retryable "temporarily
289
+ // unavailable" so the caller can serve a stale cache and the route can
290
+ // map to 503 instead of a misleading 500. A 404 on the plugins prefix
291
+ // itself means the canonical source path is gone (a real
292
+ // misconfiguration), so it stays a hard error.
293
+ if (isTransientUpstreamStatus(res)) {
294
+ throw new PluginCatalogUnavailableError(detail, res.status);
295
+ }
296
+ throw new Error(detail);
137
297
  }
138
298
 
139
299
  const body = (await res.json()) as unknown;
@@ -150,10 +310,22 @@ async function listDir(
150
310
  * request. Unauthenticated — the canonical source is a public repo, mirroring
151
311
  * `installPlugin` which uses the same envelope.
152
312
  */
153
- async function githubFetch(
154
- url: string,
155
- fetchFn: FetchLike,
156
- ): Promise<Response> {
313
+ /**
314
+ * Whether a non-OK GitHub response should be treated as a transient
315
+ * "temporarily unavailable" failure rather than a hard error. Covers
316
+ * rate limiting (429, or 403 once the rate-limit budget is exhausted) and
317
+ * upstream server errors (5xx). A bare 403 without the rate-limit signal
318
+ * (e.g. a genuine permissions problem) is not treated as transient.
319
+ */
320
+ function isTransientUpstreamStatus(res: Response): boolean {
321
+ if (res.status === 429 || res.status >= 500) return true;
322
+ if (res.status === 403) {
323
+ return res.headers.get("x-ratelimit-remaining") === "0";
324
+ }
325
+ return false;
326
+ }
327
+
328
+ async function githubFetch(url: string, fetchFn: FetchLike): Promise<Response> {
157
329
  return fetchFn(url, {
158
330
  headers: {
159
331
  Accept: "application/vnd.github+json",
@@ -85,8 +85,8 @@ describe("unified feature flag registry guard", () => {
85
85
  ) {
86
86
  violations.push(`${prefix}: missing or non-string 'description'`);
87
87
  }
88
- if (typeof flag.defaultEnabled !== "boolean") {
89
- violations.push(`${prefix}: missing or non-boolean 'defaultEnabled'`);
88
+ if (typeof flag.defaultEnabled !== "boolean" && typeof flag.defaultEnabled !== "string") {
89
+ violations.push(`${prefix}: missing or invalid 'defaultEnabled' (expected boolean or string)`);
90
90
  }
91
91
  }
92
92
 
@@ -10,6 +10,7 @@ describe("DEFAULT_ACP_AGENT_PROFILES", () => {
10
10
  expect(Object.keys(DEFAULT_ACP_AGENT_PROFILES).sort()).toEqual([
11
11
  "claude",
12
12
  "codex",
13
+ "gemini",
13
14
  ]);
14
15
  });
15
16
 
@@ -29,6 +30,14 @@ describe("DEFAULT_ACP_AGENT_PROFILES", () => {
29
30
  });
30
31
  });
31
32
 
33
+ test("gemini profile speaks native ACP via gemini --acp (no adapter binary)", () => {
34
+ expect(DEFAULT_ACP_AGENT_PROFILES.gemini).toEqual({
35
+ command: "gemini",
36
+ args: ["--acp"],
37
+ description: "Google Gemini CLI (native ACP via gemini --acp)",
38
+ });
39
+ });
40
+
32
41
  test("is deeply frozen so mutation throws in strict mode", () => {
33
42
  expect(Object.isFrozen(DEFAULT_ACP_AGENT_PROFILES)).toBe(true);
34
43
  for (const profile of Object.values(DEFAULT_ACP_AGENT_PROFILES)) {
@@ -46,6 +55,7 @@ describe("DEFAULT_AGENT_NPM_PACKAGES", () => {
46
55
  expect(DEFAULT_AGENT_NPM_PACKAGES).toEqual({
47
56
  "claude-agent-acp": "@agentclientprotocol/claude-agent-acp",
48
57
  "codex-acp": "@zed-industries/codex-acp",
58
+ gemini: "@google/gemini-cli",
49
59
  });
50
60
  });
51
61
 
@@ -9,9 +9,9 @@ const FROZEN_EMPTY_ARGS = Object.freeze([] as string[]) as unknown as string[];
9
9
  /**
10
10
  * Default ACP agent profiles that ship with the assistant.
11
11
  *
12
- * When `acp.enabled: true` and the user has not provided a config entry for an
13
- * agent id, the resolver falls back to this map so common agents like `claude`
14
- * and `codex` Just Work without requiring per-user config.
12
+ * When the user has not provided a config entry for an agent id, the resolver
13
+ * falls back to this map so common agents like `claude` and `codex` Just Work
14
+ * without requiring per-user config.
15
15
  *
16
16
  * Keyed by agent id. Deeply frozen — the outer object, each profile, and the
17
17
  * `args` arrays — so mutation throws in strict mode rather than silently
@@ -30,6 +30,11 @@ export const DEFAULT_ACP_AGENT_PROFILES: Readonly<
30
30
  args: FROZEN_EMPTY_ARGS,
31
31
  description: "OpenAI Codex CLI (via @zed-industries/codex-acp)",
32
32
  }),
33
+ gemini: Object.freeze({
34
+ command: "gemini",
35
+ args: Object.freeze(["--acp"]) as unknown as string[],
36
+ description: "Google Gemini CLI (native ACP via gemini --acp)",
37
+ }),
33
38
  });
34
39
 
35
40
  /**
@@ -44,4 +49,5 @@ export const DEFAULT_AGENT_NPM_PACKAGES: Readonly<Record<string, string>> =
44
49
  Object.freeze({
45
50
  "claude-agent-acp": "@agentclientprotocol/claude-agent-acp",
46
51
  "codex-acp": "@zed-industries/codex-acp",
52
+ gemini: "@google/gemini-cli",
47
53
  });
@@ -20,12 +20,6 @@ const AcpAgentConfigSchema = z
20
20
 
21
21
  export const AcpConfigSchema = z
22
22
  .object({
23
- enabled: z
24
- .boolean()
25
- .default(false)
26
- .describe(
27
- "Whether the Agent Communication Protocol (ACP) system is enabled",
28
- ),
29
23
  maxConcurrentSessions: z
30
24
  .number()
31
25
  .int()
@@ -40,7 +34,7 @@ export const AcpConfigSchema = z
40
34
  .describe("Map of agent names to their configurations"),
41
35
  })
42
36
  .describe(
43
- "Agent Communication Protocol (ACP) — enables inter-agent communication and delegation",
37
+ "Agent Communication Protocol (ACP) — inter-agent communication and delegation",
44
38
  );
45
39
 
46
40
  export type AcpConfig = z.infer<typeof AcpConfigSchema>;