@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091853.fbaa2ae

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 (475) 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 +263 -121
  12. package/package.json +2 -2
  13. package/src/__tests__/activation-early-marking.test.ts +120 -0
  14. package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
  15. package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
  16. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
  17. package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
  18. package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
  19. package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
  20. package/src/__tests__/agent-loop-thinking.test.ts +36 -20
  21. package/src/__tests__/agent-loop.test.ts +441 -96
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
  23. package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
  24. package/src/__tests__/anthropic-provider.test.ts +23 -8
  25. package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
  26. package/src/__tests__/app-control-flow.test.ts +1 -1
  27. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  28. package/src/__tests__/app-executors.test.ts +132 -0
  29. package/src/__tests__/approval-cascade.test.ts +6 -5
  30. package/src/__tests__/approval-routes-http.test.ts +4 -1
  31. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  32. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  33. package/src/__tests__/channel-approvals.test.ts +1 -1
  34. package/src/__tests__/compaction-circuit.test.ts +258 -0
  35. package/src/__tests__/compaction-direct.test.ts +146 -0
  36. package/src/__tests__/compaction-events.test.ts +7 -7
  37. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  38. package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
  39. package/src/__tests__/context-overflow-reducer.test.ts +265 -125
  40. package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
  41. package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
  42. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
  43. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
  44. package/src/__tests__/conversation-agent-loop-overflow.test.ts +158 -234
  45. package/src/__tests__/conversation-agent-loop.test.ts +342 -623
  46. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  47. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
  48. package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
  49. package/src/__tests__/conversation-error.test.ts +15 -1
  50. package/src/__tests__/conversation-history-web-search.test.ts +6 -1
  51. package/src/__tests__/conversation-media-retry.test.ts +1 -1
  52. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
  53. package/src/__tests__/conversation-process-callsite.test.ts +15 -15
  54. package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
  55. package/src/__tests__/conversation-queue.test.ts +10 -10
  56. package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
  57. package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
  58. package/src/__tests__/conversation-slash-queue.test.ts +7 -5
  59. package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
  60. package/src/__tests__/conversation-speed-override.test.ts +11 -10
  61. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  62. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
  63. package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
  64. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
  65. package/src/__tests__/conversation-undo.test.ts +2 -2
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
  67. package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
  69. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  70. package/src/__tests__/corrected-target.test.ts +93 -0
  71. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  72. package/src/__tests__/cu-unified-flow.test.ts +36 -0
  73. package/src/__tests__/db-acp-history.test.ts +101 -0
  74. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  75. package/src/__tests__/empty-response-hook.test.ts +1 -1
  76. package/src/__tests__/file-write-tool.test.ts +63 -0
  77. package/src/__tests__/gateway-only-guard.test.ts +17 -2
  78. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  79. package/src/__tests__/guardian-routing-invariants.test.ts +93 -4
  80. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  81. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  82. package/src/__tests__/heartbeat-service.test.ts +1 -0
  83. package/src/__tests__/history-repair-hook.test.ts +5 -1
  84. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  85. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  86. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  87. package/src/__tests__/inference-profile-reaper.test.ts +62 -0
  88. package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
  89. package/src/__tests__/injector-background-turn.test.ts +13 -23
  90. package/src/__tests__/injector-chain.test.ts +268 -44
  91. package/src/__tests__/injector-disk-pressure.test.ts +210 -52
  92. package/src/__tests__/injector-document-comments.test.ts +97 -114
  93. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  94. package/src/__tests__/injector-v3-suppression.test.ts +4 -4
  95. package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
  96. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  97. package/src/__tests__/llm-resolver.test.ts +73 -0
  98. package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
  99. package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
  100. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  101. package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
  102. package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
  103. package/src/__tests__/persist-unsendable-image.test.ts +97 -1
  104. package/src/__tests__/plugin-api-shim.test.ts +6 -9
  105. package/src/__tests__/plugin-bootstrap.test.ts +91 -81
  106. package/src/__tests__/plugin-registry.test.ts +3 -49
  107. package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
  108. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  109. package/src/__tests__/plugin-types.test.ts +0 -70
  110. package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
  111. package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
  112. package/src/__tests__/published-app-updater.test.ts +138 -0
  113. package/src/__tests__/reaction-persistence.test.ts +1 -1
  114. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  115. package/src/__tests__/skill-feature-flags-integration.test.ts +31 -0
  116. package/src/__tests__/steer-tool-repair.test.ts +1 -1
  117. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  118. package/src/__tests__/subagent-detail.test.ts +25 -7
  119. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  120. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  121. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  122. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  123. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  124. package/src/__tests__/title-generate-hook.test.ts +5 -1
  125. package/src/__tests__/tool-error-hook.test.ts +1 -1
  126. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
  127. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  128. package/src/__tests__/web-fetch.test.ts +45 -0
  129. package/src/acp/__tests__/agent-process.test.ts +161 -0
  130. package/src/acp/__tests__/client-handler.test.ts +40 -0
  131. package/src/acp/__tests__/helpers/acp-config-stub.ts +0 -2
  132. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  133. package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
  134. package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
  135. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  136. package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
  137. package/src/acp/agent-process.ts +61 -1
  138. package/src/acp/auto-install.test.ts +280 -0
  139. package/src/acp/auto-install.ts +232 -0
  140. package/src/acp/client-handler.ts +31 -0
  141. package/src/acp/prepare-agent-env.ts +80 -27
  142. package/src/acp/resolve-agent.test.ts +188 -28
  143. package/src/acp/resolve-agent.ts +119 -42
  144. package/src/acp/resume-hint.ts +23 -0
  145. package/src/acp/session-manager.ts +507 -73
  146. package/src/agent/compaction-circuit.ts +60 -102
  147. package/src/agent/loop.ts +403 -251
  148. package/src/api/responses/conversation-message.ts +22 -2
  149. package/src/api/responses/memory-v3-selection-log.ts +19 -10
  150. package/src/approvals/guardian-request-resolvers.ts +27 -1
  151. package/src/background-wake/next-wake.ts +1 -0
  152. package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
  153. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  154. package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
  155. package/src/cli/commands/memory-v3.ts +57 -199
  156. package/src/cli/commands/plugins.ts +43 -37
  157. package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
  158. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
  159. package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
  160. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
  161. package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
  162. package/src/cli/lib/install-from-github.ts +797 -60
  163. package/src/cli/lib/plugin-catalog-cache.ts +84 -0
  164. package/src/cli/lib/plugin-details.ts +416 -0
  165. package/src/cli/lib/plugin-marketplace.ts +215 -0
  166. package/src/cli/lib/search-plugins.ts +201 -29
  167. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  168. package/src/config/acp-defaults.test.ts +10 -0
  169. package/src/config/acp-defaults.ts +9 -3
  170. package/src/config/acp-schema.ts +1 -7
  171. package/src/config/bundled-skills/acp/SKILL.md +80 -41
  172. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  173. package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
  174. package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
  175. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  176. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  177. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  178. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  179. package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
  180. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  181. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  182. package/src/config/bundled-tool-registry.ts +4 -0
  183. package/src/config/call-site-defaults.ts +0 -3
  184. package/src/config/feature-flag-registry.json +10 -16
  185. package/src/config/llm-resolver.ts +39 -7
  186. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
  187. package/src/config/schemas/call-site-catalog.ts +0 -21
  188. package/src/config/schemas/heartbeat.ts +9 -0
  189. package/src/config/schemas/llm.ts +17 -3
  190. package/src/config/schemas/memory-v3.ts +58 -8
  191. package/src/config/seed-inference-profiles.ts +18 -0
  192. package/src/context/compactor.ts +22 -4
  193. package/src/context/post-turn-tool-result-truncation.ts +39 -1
  194. package/src/context/strip-injections.ts +8 -2
  195. package/src/daemon/conversation-agent-loop-handlers.ts +18 -36
  196. package/src/daemon/conversation-agent-loop.ts +308 -1297
  197. package/src/daemon/conversation-error.ts +31 -4
  198. package/src/daemon/conversation-history.ts +1 -1
  199. package/src/daemon/conversation-lifecycle.ts +11 -255
  200. package/src/daemon/conversation-media-retry.ts +19 -6
  201. package/src/daemon/conversation-messaging.ts +17 -0
  202. package/src/daemon/conversation-process.ts +22 -141
  203. package/src/daemon/conversation-queue-manager.ts +8 -0
  204. package/src/daemon/conversation-registry.ts +159 -0
  205. package/src/daemon/conversation-runtime-assembly.ts +361 -393
  206. package/src/daemon/conversation-store.ts +9 -90
  207. package/src/daemon/conversation-surfaces.ts +165 -11
  208. package/src/daemon/conversation-workspace.ts +17 -0
  209. package/src/daemon/conversation.ts +444 -62
  210. package/src/daemon/disk-pressure-policy.ts +0 -1
  211. package/src/daemon/external-plugins-bootstrap.ts +36 -46
  212. package/src/daemon/handlers/config-channels.ts +11 -2
  213. package/src/daemon/handlers/conversations.ts +3 -1
  214. package/src/daemon/handlers/skills.ts +4 -1
  215. package/src/daemon/host-app-control-proxy.ts +72 -57
  216. package/src/daemon/host-proxy-preactivation.ts +1 -3
  217. package/src/daemon/lifecycle.ts +21 -7
  218. package/src/daemon/persist-unsendable-image.ts +62 -25
  219. package/src/daemon/process-message.ts +1 -1
  220. package/src/daemon/server.ts +2 -0
  221. package/src/daemon/tool-side-effects.ts +15 -0
  222. package/src/daemon/wake-conversation-ops.ts +269 -0
  223. package/src/embedded/plugin-api.ts +2 -2
  224. package/src/export/__tests__/transcript-formatter.test.ts +5 -0
  225. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  226. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  227. package/src/heartbeat/heartbeat-service.ts +26 -0
  228. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  229. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  230. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  231. package/src/ipc/skill-routes/memory.ts +4 -2
  232. package/src/memory/__tests__/activation-session-store.test.ts +41 -0
  233. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
  234. package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
  235. package/src/memory/activation-session-store.ts +43 -0
  236. package/src/memory/conversation-crud.ts +29 -19
  237. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  238. package/src/memory/db-init.ts +6 -0
  239. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  240. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
  241. package/src/memory/jobs-worker.ts +25 -1
  242. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  243. package/src/memory/migrations/273-onboarding-events-funnel-columns.ts +46 -0
  244. package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
  245. package/src/memory/migrations/index.ts +3 -0
  246. package/src/memory/onboarding-events-store.ts +66 -18
  247. package/src/memory/schema/acp.ts +4 -0
  248. package/src/memory/schema/infrastructure.ts +13 -0
  249. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
  250. package/src/memory/v2/__tests__/page-store.test.ts +22 -0
  251. package/src/memory/v2/consolidation-job.ts +2 -63
  252. package/src/memory/v2/types.ts +5 -0
  253. package/src/messaging/providers/telegram-bot/api.ts +14 -5
  254. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  255. package/src/notifications/access-request-copy.ts +298 -0
  256. package/src/notifications/adapters/slack.ts +3 -3
  257. package/src/notifications/adapters/telegram.ts +9 -2
  258. package/src/notifications/copy-composer.ts +49 -267
  259. package/src/notifications/decision-engine.ts +16 -35
  260. package/src/notifications/home-feed-side-effect.ts +1 -6
  261. package/src/plugin-api/constants.ts +4 -0
  262. package/src/plugin-api/index.ts +40 -11
  263. package/src/plugin-api/types.ts +108 -0
  264. package/src/plugins/defaults/compaction/compact.ts +106 -0
  265. package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
  266. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  267. package/src/plugins/defaults/compaction/manager-store.ts +57 -0
  268. package/src/plugins/defaults/compaction/package.json +1 -2
  269. package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
  270. package/src/plugins/defaults/empty-response/package.json +0 -1
  271. package/src/plugins/defaults/history-repair/package.json +0 -1
  272. package/src/plugins/defaults/index.ts +156 -26
  273. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
  274. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
  275. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
  276. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
  277. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  278. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
  279. package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
  280. package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
  281. package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
  282. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
  283. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
  284. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
  285. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
  286. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
  287. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
  288. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
  289. package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
  290. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
  291. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
  292. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
  293. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
  294. package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
  295. package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
  296. package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
  297. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  298. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  299. package/src/plugins/defaults/memory-v3-shadow/injector.ts +76 -0
  300. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
  301. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
  302. package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
  303. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
  304. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
  305. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
  306. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
  307. package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
  308. package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
  309. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
  310. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
  311. package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
  312. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -1
  313. package/src/plugins/defaults/title-generate/package.json +0 -1
  314. package/src/plugins/defaults/tool-error/package.json +0 -1
  315. package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
  316. package/src/plugins/external-api.ts +12 -2
  317. package/src/plugins/pipeline.ts +6 -293
  318. package/src/plugins/registry.ts +9 -37
  319. package/src/plugins/types.ts +76 -381
  320. package/src/plugins/user-loader.ts +30 -127
  321. package/src/prompts/__tests__/system-prompt.test.ts +6 -0
  322. package/src/prompts/system-prompt.ts +61 -10
  323. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
  324. package/src/providers/anthropic/client.ts +9 -10
  325. package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
  326. package/src/providers/inference/logit-bias.ts +55 -0
  327. package/src/providers/model-catalog.ts +34 -0
  328. package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
  329. package/src/providers/openai/chat-completions-provider.ts +44 -1
  330. package/src/providers/retry.ts +22 -0
  331. package/src/providers/types.ts +6 -0
  332. package/src/runtime/__tests__/agent-wake.test.ts +555 -691
  333. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  334. package/src/runtime/agent-wake.ts +108 -209
  335. package/src/runtime/assistant-event-hub.ts +1 -1
  336. package/src/runtime/channel-approvals.ts +1 -1
  337. package/src/runtime/interactive-ui.ts +1 -1
  338. package/src/runtime/routes/__tests__/acp-routes.test.ts +466 -55
  339. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  340. package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
  341. package/src/runtime/routes/__tests__/stt-routes.test.ts +112 -0
  342. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  343. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  344. package/src/runtime/routes/acp-routes.test.ts +185 -32
  345. package/src/runtime/routes/acp-routes.ts +328 -30
  346. package/src/runtime/routes/app-management-routes.ts +3 -0
  347. package/src/runtime/routes/approval-routes.ts +1 -1
  348. package/src/runtime/routes/browser-routes.ts +1 -1
  349. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  350. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  351. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  352. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  353. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  354. package/src/runtime/routes/conversation-routes.ts +30 -2
  355. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  356. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  357. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  358. package/src/runtime/routes/host-cu-routes.ts +1 -1
  359. package/src/runtime/routes/identity-routes.ts +18 -3
  360. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  361. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  362. package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
  363. package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
  364. package/src/runtime/routes/memory-v3-routes.ts +65 -305
  365. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
  366. package/src/runtime/routes/playground/helpers.ts +1 -1
  367. package/src/runtime/routes/plugins-routes.ts +337 -35
  368. package/src/runtime/routes/stt-routes.ts +45 -12
  369. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  370. package/src/runtime/routes/work-items-routes.ts +2 -4
  371. package/src/runtime/routes/workspace-routes.ts +50 -15
  372. package/src/runtime/services/conversation-serializer.ts +1 -1
  373. package/src/runtime/verification-outbound-actions.ts +147 -2
  374. package/src/runtime/verification-templates.ts +29 -3
  375. package/src/services/published-app-updater.ts +30 -8
  376. package/src/signals/cancel.ts +2 -4
  377. package/src/subagent/manager.ts +21 -5
  378. package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
  379. package/src/telemetry/activation-funnel.ts +167 -0
  380. package/src/telemetry/types.ts +13 -0
  381. package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
  382. package/src/telemetry/usage-telemetry-reporter.ts +26 -1
  383. package/src/tools/acp/context.ts +20 -0
  384. package/src/tools/acp/list-agents.test.ts +9 -19
  385. package/src/tools/acp/list-agents.ts +3 -15
  386. package/src/tools/acp/spawn.test.ts +173 -202
  387. package/src/tools/acp/spawn.ts +37 -172
  388. package/src/tools/acp/steer.test.ts +105 -8
  389. package/src/tools/acp/steer.ts +48 -17
  390. package/src/tools/apps/executors.ts +166 -50
  391. package/src/tools/browser/browser-execution.ts +12 -2
  392. package/src/tools/filesystem/write.ts +34 -0
  393. package/src/tools/network/web-fetch.ts +65 -24
  394. package/src/tools/skills/load.ts +1 -1
  395. package/src/tools/subagent/spawn.ts +2 -4
  396. package/src/tools/ui-surface/definitions.ts +32 -5
  397. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  398. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
  399. package/docs/plugins.md +0 -832
  400. package/examples/plugins/echo/register.ts +0 -143
  401. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  402. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  403. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  404. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
  405. package/src/__tests__/pipeline-runner.test.ts +0 -554
  406. package/src/daemon/wake-target-adapter.ts +0 -253
  407. package/src/memory/v3/__tests__/assign.test.ts +0 -242
  408. package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
  409. package/src/memory/v3/__tests__/core.test.ts +0 -39
  410. package/src/memory/v3/__tests__/health.test.ts +0 -219
  411. package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
  412. package/src/memory/v3/__tests__/needle.test.ts +0 -107
  413. package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
  414. package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
  415. package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
  416. package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
  417. package/src/memory/v3/__tests__/router.test.ts +0 -332
  418. package/src/memory/v3/__tests__/selector.test.ts +0 -470
  419. package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
  420. package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
  421. package/src/memory/v3/__tests__/tree.test.ts +0 -192
  422. package/src/memory/v3/assign.ts +0 -268
  423. package/src/memory/v3/capabilities.ts +0 -124
  424. package/src/memory/v3/core.ts +0 -26
  425. package/src/memory/v3/health.ts +0 -0
  426. package/src/memory/v3/maintain-job.ts +0 -314
  427. package/src/memory/v3/needle.ts +0 -115
  428. package/src/memory/v3/orchestrate.ts +0 -126
  429. package/src/memory/v3/page-content.ts +0 -34
  430. package/src/memory/v3/provider-blocks.ts +0 -26
  431. package/src/memory/v3/reconcile.ts +0 -523
  432. package/src/memory/v3/router.ts +0 -190
  433. package/src/memory/v3/selection-log-store.ts +0 -84
  434. package/src/memory/v3/selector.ts +0 -226
  435. package/src/memory/v3/shadow-plugin.ts +0 -411
  436. package/src/memory/v3/snapshot.ts +0 -209
  437. package/src/memory/v3/tree.ts +0 -174
  438. package/src/memory/v3/types.ts +0 -59
  439. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  440. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  441. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  442. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  443. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  444. package/src/plugins/defaults/compaction/register.ts +0 -35
  445. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  446. package/src/plugins/defaults/empty-response/register.ts +0 -23
  447. package/src/plugins/defaults/history-repair/register.ts +0 -24
  448. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
  449. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  450. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  451. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  452. package/src/plugins/defaults/title-generate/register.ts +0 -35
  453. package/src/plugins/defaults/tool-error/register.ts +0 -23
  454. package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
  455. package/src/proactive-artifact/aux-message-injector.ts +0 -97
  456. package/src/proactive-artifact/decision.test.ts +0 -226
  457. package/src/proactive-artifact/decision.ts +0 -165
  458. package/src/proactive-artifact/index.ts +0 -7
  459. package/src/proactive-artifact/job.test.ts +0 -962
  460. package/src/proactive-artifact/job.ts +0 -372
  461. package/src/proactive-artifact/message-copy.ts +0 -58
  462. package/src/proactive-artifact/trigger-state.test.ts +0 -286
  463. package/src/proactive-artifact/trigger-state.ts +0 -123
  464. package/src/util/map-limit.ts +0 -27
  465. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  466. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  467. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  468. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  469. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  470. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  471. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  472. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  473. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  474. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  475. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
@@ -38,17 +38,11 @@ import { RiskLevel } from "../permissions/types.js";
38
38
  import { registerDefaultPlugins } from "../plugins/defaults/index.js";
39
39
  import {
40
40
  closeRegistration,
41
- getMiddlewaresFor,
42
41
  getRegisteredPlugins,
43
42
  registerPlugin,
44
43
  resetPluginRegistryForTests,
45
44
  } from "../plugins/registry.js";
46
- import {
47
- type PipelineMiddlewareMap,
48
- type Plugin,
49
- PluginExecutionError,
50
- type PluginInitContext,
51
- } from "../plugins/types.js";
45
+ import { type Plugin, type PluginInitContext } from "../plugins/types.js";
52
46
  import { APP_VERSION } from "../version.js";
53
47
  import { setOverridesForTesting } from "./feature-flag-test-helpers.js";
54
48
 
@@ -176,9 +170,9 @@ describe("plugin bootstrap", () => {
176
170
  expect(received?.credentials).toEqual({ "some-key": "super-secret-value" });
177
171
  });
178
172
 
179
- test("credential resolution: missing credential fails bootstrap with the plugin named", async () => {
173
+ test("credential resolution: a plugin whose required credential is missing is skipped, not fatal", async () => {
174
+ // GIVEN a plugin that requires a credential the store cannot resolve
180
175
  getSecureKeyAsyncMock.mockImplementation(async () => undefined);
181
-
182
176
  registerPlugin(
183
177
  buildPlugin(
184
178
  "missing-cred",
@@ -187,16 +181,14 @@ describe("plugin bootstrap", () => {
187
181
  ),
188
182
  );
189
183
 
190
- let caught: unknown;
191
- try {
192
- await bootstrapPlugins();
193
- } catch (err) {
194
- caught = err;
195
- }
196
- expect(caught).toBeInstanceOf(PluginExecutionError);
197
- const msg = (caught as PluginExecutionError).message;
198
- expect(msg).toContain("missing-cred");
199
- expect(msg).toContain("absent-key");
184
+ // WHEN bootstrap runs
185
+ // THEN it completes without throwing — the unresolvable credential is
186
+ // contained to that plugin (same per-plugin isolation as an init throw)
187
+ await bootstrapPlugins();
188
+
189
+ // AND the plugin is dropped from the registry
190
+ const names = getRegisteredPlugins().map((p) => p.manifest.name);
191
+ expect(names).not.toContain("missing-cred");
200
192
  });
201
193
 
202
194
  test("version mismatch: external plugin loader rejects when peerDependency unsatisfied", async () => {
@@ -210,7 +202,8 @@ describe("plugin bootstrap", () => {
210
202
  expect(() => registerPlugin(plugin)).not.toThrow();
211
203
  });
212
204
 
213
- test("plugin init throw: bootstrap throws a PluginExecutionError naming the plugin", async () => {
205
+ test("plugin init throw: bootstrap contains the failure and does not throw", async () => {
206
+ // GIVEN a plugin whose init throws
214
207
  registerPlugin(
215
208
  buildPlugin("broken", {
216
209
  async init() {
@@ -219,65 +212,91 @@ describe("plugin bootstrap", () => {
219
212
  }),
220
213
  );
221
214
 
222
- let caught: unknown;
223
- try {
224
- await bootstrapPlugins();
225
- } catch (err) {
226
- caught = err;
227
- }
228
- expect(caught).toBeInstanceOf(PluginExecutionError);
229
- const msg = (caught as PluginExecutionError).message;
230
- expect(msg).toContain("broken");
231
- expect(msg).toContain("kaboom");
215
+ // WHEN bootstrap runs
216
+ // THEN it completes without throwing — a single plugin's init failure is
217
+ // contained to that plugin rather than aborting the whole plugin layer
218
+ await bootstrapPlugins();
219
+
220
+ // AND the failing plugin is dropped from the registry so its hooks never
221
+ // participate in the turn lifecycle
222
+ const names = getRegisteredPlugins().map((p) => p.manifest.name);
223
+ expect(names).not.toContain("broken");
232
224
  });
233
225
 
234
- test("partial-init failure: earlier plugins' onShutdown runs in reverse before the error propagates", async () => {
235
- // If plugin N throws during init, every plugin 1..N-1 that already made
236
- // it through its full init+contribution phase must have onShutdown()
237
- // invoked in reverse registration order before bootstrap re-throws.
238
- // Without this, earlier plugins leak live tools/routes/skills because
239
- // the shutdown hook is only registered once the entire loop completes.
240
- const callOrder: string[] = [];
226
+ test("mid-list init failure: surrounding plugins still initialize and survive", async () => {
227
+ // GIVEN two healthy plugins surrounding one whose init throws, registered
228
+ // in order
229
+ const initialized: string[] = [];
230
+ const shutDownDuringBootstrap: string[] = [];
241
231
  registerPlugin(
242
- buildPlugin("survivor-a", {
243
- async init() {},
232
+ buildPlugin("before", {
233
+ async init() {
234
+ initialized.push("before");
235
+ },
244
236
  async onShutdown() {
245
- callOrder.push("survivor-a");
237
+ shutDownDuringBootstrap.push("before");
246
238
  },
247
239
  }),
248
240
  );
249
241
  registerPlugin(
250
- buildPlugin("survivor-b", {
251
- async init() {},
252
- async onShutdown() {
253
- callOrder.push("survivor-b");
242
+ buildPlugin("failing", {
243
+ async init() {
244
+ throw new Error("mid-bootstrap failure");
254
245
  },
255
246
  }),
256
247
  );
257
248
  registerPlugin(
258
- buildPlugin("failing", {
249
+ buildPlugin("after", {
259
250
  async init() {
260
- throw new Error("mid-bootstrap failure");
251
+ initialized.push("after");
261
252
  },
262
253
  async onShutdown() {
263
- // Never called — this plugin never completes init, so it was never
264
- // added to the active list that teardown walks.
265
- callOrder.push("failing");
254
+ shutDownDuringBootstrap.push("after");
266
255
  },
267
256
  }),
268
257
  );
269
258
 
270
- let caught: unknown;
271
- try {
272
- await bootstrapPlugins();
273
- } catch (err) {
274
- caught = err;
275
- }
276
- expect(caught).toBeInstanceOf(PluginExecutionError);
277
-
278
- // Reverse order: survivor-b registered after survivor-a, so it tears
279
- // down first; "failing" never entered the active list.
280
- expect(callOrder).toEqual(["survivor-b", "survivor-a"]);
259
+ // WHEN bootstrap runs
260
+ await bootstrapPlugins();
261
+
262
+ // THEN both healthy plugins initialized — the one registered after the
263
+ // failure is not skipped
264
+ expect(initialized).toEqual(["before", "after"]);
265
+ // AND neither healthy plugin was torn down by the failure (their
266
+ // onShutdown only runs at real shutdown, not during bootstrap)
267
+ expect(shutDownDuringBootstrap).toEqual([]);
268
+ // AND the failing plugin is dropped while both survivors remain registered
269
+ const names = getRegisteredPlugins().map((p) => p.manifest.name);
270
+ expect(names).toContain("before");
271
+ expect(names).toContain("after");
272
+ expect(names).not.toContain("failing");
273
+ });
274
+
275
+ test("user-plugin init failure leaves the first-party defaults registered", async () => {
276
+ // GIVEN the first-party defaults are registered ahead of user plugins, as
277
+ // at daemon startup
278
+ registerDefaultPlugins();
279
+ // AND a user plugin registered after them whose init throws
280
+ registerPlugin(
281
+ buildPlugin("breaking-user-plugin", {
282
+ async init() {
283
+ throw new Error("user plugin boom");
284
+ },
285
+ }),
286
+ );
287
+
288
+ // WHEN bootstrap runs
289
+ await bootstrapPlugins();
290
+
291
+ // THEN the failing user plugin is dropped from the registry
292
+ const names = getRegisteredPlugins().map((p) => p.manifest.name);
293
+ expect(names).not.toContain("breaking-user-plugin");
294
+ // AND every first-party default survived, so core turn behavior (memory
295
+ // retrieval, history repair, title generation) keeps running in degraded
296
+ // mode instead of being torn down with the failing plugin
297
+ expect(names).toContain("default-memory-retrieval");
298
+ expect(names).toContain("default-history-repair");
299
+ expect(names).toContain("default-title-generate");
281
300
  });
282
301
 
283
302
  test("shutdown order: onShutdown fires in reverse registration order", async () => {
@@ -485,33 +504,24 @@ describe("plugin bootstrap", () => {
485
504
  expect(initFired).toBe(false);
486
505
  });
487
506
 
488
- test("requiresFlag disabled: plugin middleware is dropped from the registry", async () => {
489
- // Regression: prior to the unregisterPlugin() call on the flag-gated skip
490
- // path, `getMiddlewaresFor()` iterated over every entry in
491
- // `registeredPlugins` — so a gated-off plugin's middleware still ran on
492
- // every pipeline invocation even though `init()` had never fired to set up
493
- // the state it depended on.
494
- setOverridesForTesting({ "plugin-middleware-disabled": false });
495
-
496
- const gatedMiddleware: PipelineMiddlewareMap["compaction"] = async (
497
- args,
498
- next,
499
- ) => next(args);
507
+ test("requiresFlag disabled: the skipped plugin is removed from the registry", async () => {
508
+ // Regression: a flag-gated skip must call `unregisterPlugin()` so the
509
+ // gated-off plugin does not linger in `registeredPlugins` with its
510
+ // `init()` never having fired to set up the state it depends on.
511
+ setOverridesForTesting({ "plugin-registry-disabled": false });
512
+
500
513
  const plugin = buildPlugin(
501
- "gated-middleware",
502
- {
503
- middleware: { compaction: gatedMiddleware },
504
- },
505
- { requiresFlag: ["plugin-middleware-disabled"] },
514
+ "gated-registry",
515
+ {},
516
+ { requiresFlag: ["plugin-registry-disabled"] },
506
517
  );
507
518
  registerPlugin(plugin);
508
519
 
509
520
  await bootstrapPlugins();
510
521
 
511
- // The middleware slot must not expose the flag-gated plugin's contribution.
512
- // The default plugins also contribute compaction middleware, so we key on
513
- // identity rather than asserting an empty list.
514
- expect(getMiddlewaresFor("compaction")).not.toContain(gatedMiddleware);
522
+ // The gated-off plugin must not survive in the registry snapshot.
523
+ const names = getRegisteredPlugins().map((p) => p.manifest.name);
524
+ expect(names).not.toContain("gated-registry");
515
525
  });
516
526
 
517
527
  test("requiresFlag disabled: no shutdown hook entry installed for the skipped plugin", async () => {
@@ -2,26 +2,19 @@
2
2
  * Tests for the plugin registry (PR 13).
3
3
  *
4
4
  * Covers successful registration, required-field and duplicate-name
5
- * validation, capability-version negotiation error messaging, injector
6
- * ordering, and middleware collection order.
5
+ * validation, capability-version negotiation error messaging, and
6
+ * registration order.
7
7
  */
8
8
 
9
9
  import { beforeEach, describe, expect, test } from "bun:test";
10
10
 
11
11
  import {
12
12
  closeRegistration,
13
- getMiddlewaresFor,
14
13
  getRegisteredPlugins,
15
14
  registerPlugin,
16
15
  resetPluginRegistryForTests,
17
16
  } from "../plugins/registry.js";
18
- import {
19
- type CompactionArgs,
20
- type CompactionResult,
21
- type Middleware,
22
- type Plugin,
23
- PluginExecutionError,
24
- } from "../plugins/types.js";
17
+ import { type Plugin, PluginExecutionError } from "../plugins/types.js";
25
18
 
26
19
  /** Build a minimal, valid plugin with the given name and optional extras. */
27
20
  function buildPlugin(
@@ -147,45 +140,6 @@ describe("plugin registry", () => {
147
140
  expect(() => registerPlugin(bad)).toThrow(/manifest\.version is required/);
148
141
  });
149
142
 
150
- test("getMiddlewaresFor returns middleware in registration order", () => {
151
- const firstMw: Middleware<CompactionArgs, CompactionResult> = async (
152
- args,
153
- next,
154
- ) => next(args);
155
- const secondMw: Middleware<CompactionArgs, CompactionResult> = async (
156
- args,
157
- next,
158
- ) => next(args);
159
-
160
- registerPlugin(
161
- buildPlugin("plugin-first", { middleware: { compaction: firstMw } }),
162
- );
163
- registerPlugin(
164
- buildPlugin("plugin-second", { middleware: { compaction: secondMw } }),
165
- );
166
-
167
- const middlewares = getMiddlewaresFor("compaction");
168
- expect(middlewares).toHaveLength(2);
169
- // Identity comparison proves the middleware instances come back in
170
- // registration order — outer→inner composition semantics belong to the
171
- // pipeline runner (PR 12), not the registry.
172
- expect(middlewares[0]).toBe(firstMw);
173
- expect(middlewares[1]).toBe(secondMw);
174
- });
175
-
176
- test("getMiddlewaresFor skips plugins without a middleware for the pipeline", () => {
177
- const mw: Middleware<CompactionArgs, CompactionResult> = async (
178
- args,
179
- next,
180
- ) => next(args);
181
- registerPlugin(buildPlugin("bare"));
182
- registerPlugin(buildPlugin("has-mw", { middleware: { compaction: mw } }));
183
-
184
- const middlewares = getMiddlewaresFor("compaction");
185
- expect(middlewares).toHaveLength(1);
186
- expect(middlewares[0]).toBe(mw);
187
- });
188
-
189
143
  test("getRegisteredPlugins reflects registration order", () => {
190
144
  registerPlugin(buildPlugin("one"));
191
145
  registerPlugin(buildPlugin("two"));
@@ -202,7 +202,8 @@ describe("plugin skill contributions", () => {
202
202
  expect(getPluginContributedSkillSummaries()).toEqual([]);
203
203
  });
204
204
 
205
- test("duplicate skill id across plugins fails bootstrap with the plugin named", async () => {
205
+ test("duplicate skill id across plugins drops the colliding plugin, keeping the first", async () => {
206
+ // GIVEN two plugins that both declare the same skill id
206
207
  const shared: PluginSkillRegistration = {
207
208
  id: "contested-id",
208
209
  name: "contested",
@@ -215,22 +216,22 @@ describe("plugin skill contributions", () => {
215
216
  description: "Second",
216
217
  body: "from second plugin",
217
218
  };
218
-
219
+ // AND the first registered plugin claims the id; the second collides on it
219
220
  registerPlugin(buildSkillPlugin("first-plugin", [shared]));
220
221
  registerPlugin(buildSkillPlugin("second-plugin", [duplicate]));
221
222
 
222
- let caught: unknown;
223
- try {
224
- await bootstrapPlugins();
225
- } catch (err) {
226
- caught = err;
227
- }
228
- expect(caught).toBeInstanceOf(PluginExecutionError);
229
- // The error must identify which plugin tripped the collision so
230
- // operators can deploy a fix.
231
- const msg = (caught as PluginExecutionError).message;
232
- expect(msg).toContain("second-plugin");
233
- expect(msg).toContain("contested-id");
223
+ // WHEN bootstrap runs
224
+ // THEN it does not throw — the collision is contained to the colliding
225
+ // plugin rather than aborting the whole plugin layer
226
+ await bootstrapPlugins();
227
+
228
+ // AND the first plugin keeps the contested skill, while the second
229
+ // plugin's colliding contribution is rolled back and dropped
230
+ expect(
231
+ getPluginContributedSkillDefinition("contested-id")?.description,
232
+ ).toBe("First");
233
+ expect(getPluginSkillRefCount("first-plugin")).toBe(1);
234
+ expect(getPluginSkillRefCount("second-plugin")).toBe(0);
234
235
  });
235
236
 
236
237
  test("intra-batch duplicate skill id in one plugin is rejected at registration", () => {
@@ -218,9 +218,7 @@ describe("plugin tool contributions", () => {
218
218
  });
219
219
 
220
220
  test("tools are only registered after init() succeeds", async () => {
221
- // A plugin whose init throws must not contribute tools — the bootstrap
222
- // aborts with a PluginExecutionError, and nothing from this plugin
223
- // should leak into the tool registry.
221
+ // GIVEN a plugin that declares a tool but throws during init()
224
222
  const plugin = buildPlugin("delta-broken", {
225
223
  async init() {
226
224
  throw new Error("boom");
@@ -229,7 +227,12 @@ describe("plugin tool contributions", () => {
229
227
  });
230
228
  registerPlugin(plugin);
231
229
 
232
- await expect(bootstrapPlugins()).rejects.toThrow(/delta-broken/);
230
+ // WHEN bootstrap runs
231
+ // THEN it does not throw — the init failure is contained to this plugin
232
+ await bootstrapPlugins();
233
+
234
+ // AND the failing plugin's tool is rolled back, never leaking into the
235
+ // registry
233
236
  expect(getTool("delta-tool")).toBeUndefined();
234
237
  });
235
238
  });
@@ -13,18 +13,10 @@ import { describe, expect, test } from "bun:test";
13
13
  import type { TrustContext } from "../daemon/trust-context.js";
14
14
  import { RiskLevel } from "../permissions/types.js";
15
15
  import {
16
- type CircuitBreakerArgs,
17
- type CircuitBreakerResult,
18
- type CompactionArgs,
19
- type CompactionResult,
20
- type Middleware,
21
- type OverflowReduceArgs,
22
- type OverflowReduceResult,
23
16
  type Plugin,
24
17
  PluginExecutionError,
25
18
  type PluginInitContext,
26
19
  type PluginManifest,
27
- PluginTimeoutError,
28
20
  type TurnContext,
29
21
  } from "../plugins/types.js";
30
22
  import type { Tool } from "../tools/types.js";
@@ -38,7 +30,6 @@ const sampleTurnContext: TurnContext = {
38
30
  requestId: "req-abc",
39
31
  conversationId: "conv-xyz",
40
32
  turnIndex: 0,
41
- pluginName: "sample-plugin",
42
33
  trust: sampleTrust,
43
34
  };
44
35
 
@@ -52,43 +43,6 @@ describe("plugin core types", () => {
52
43
  config: { parse: (input: unknown) => input },
53
44
  };
54
45
 
55
- // `overflowReduce` has a concrete arg/result shape (PR 23). Uses a
56
- // dedicated passthrough that returns a structurally-correct result so
57
- // `satisfies Plugin` keeps verifying the signature.
58
- const overflowReducePassthrough: Middleware<
59
- OverflowReduceArgs,
60
- OverflowReduceResult
61
- > = async (args, _next, _ctx) => ({
62
- messages: args.messages,
63
- runMessages: args.runMessages,
64
- injectionMode: "full",
65
- reducerState: {
66
- appliedTiers: [],
67
- injectionMode: "full",
68
- exhausted: true,
69
- },
70
- reducerCompacted: false,
71
- attempts: 0,
72
- });
73
-
74
- // Slot-specific passthrough for the `compaction` pipeline — PR 25
75
- // narrowed its args/result away from the generic `{ input: unknown }`
76
- // placeholder, so the generic `passthrough` no longer satisfies its
77
- // middleware signature. This dedicated middleware keeps the shape-only
78
- // assertion for the slot without forcing every other slot to narrow.
79
- const compactionPassthrough: Middleware<
80
- CompactionArgs,
81
- CompactionResult
82
- > = async (args, next, _ctx) => next(args);
83
-
84
- // `circuitBreaker` carries a concrete arg shape (pipeline wrapping
85
- // landed ahead of the other slots) so it needs its own passthrough
86
- // rather than reusing the generic placeholder.
87
- const circuitPassthrough: Middleware<
88
- CircuitBreakerArgs,
89
- CircuitBreakerResult
90
- > = async (args, next, _ctx) => next(args);
91
-
92
46
  const sampleTool: Tool = {
93
47
  name: "sample-tool",
94
48
  description: "Sample plugin tool",
@@ -132,34 +86,10 @@ describe("plugin core types", () => {
132
86
  body: "## Sample\n\nPlugin-provided skill body.",
133
87
  },
134
88
  ],
135
- middleware: {
136
- compaction: compactionPassthrough,
137
- overflowReduce: overflowReducePassthrough,
138
- circuitBreaker: circuitPassthrough,
139
- },
140
89
  } satisfies Plugin;
141
90
 
142
91
  // Minimal runtime check so the test body is non-empty.
143
92
  expect(plugin.manifest.name).toBe("sample-plugin");
144
- expect(plugin.middleware.compaction).toBe(compactionPassthrough);
145
- });
146
-
147
- test("PluginTimeoutError carries pipeline, plugin, and elapsed fields", () => {
148
- const err = new PluginTimeoutError("compaction", "sample-plugin", 30000);
149
- expect(err).toBeInstanceOf(Error);
150
- expect(err.name).toBe("PluginTimeoutError");
151
- expect(err.pipeline).toBe("compaction");
152
- expect(err.pluginName).toBe("sample-plugin");
153
- expect(err.elapsedMs).toBe(30000);
154
- expect(err.message).toContain("compaction");
155
- expect(err.message).toContain("30000");
156
- expect(err.message).toContain("sample-plugin");
157
- });
158
-
159
- test("PluginTimeoutError omits plugin suffix when unknown", () => {
160
- const err = new PluginTimeoutError("circuitBreaker", undefined, 1234);
161
- expect(err.pluginName).toBeUndefined();
162
- expect(err.message).not.toContain("offending plugin");
163
93
  });
164
94
 
165
95
  test("PluginExecutionError carries the plugin name and message", () => {
@@ -12,6 +12,7 @@ import {
12
12
  TARGET_CHARS,
13
13
  THRESHOLD_CHARS,
14
14
  TOOL_RESULT_DIR,
15
+ TRUNCATION_EXEMPT_TOOLS,
15
16
  TRUNCATION_MARKER,
16
17
  } from "../context/post-turn-tool-result-truncation.js";
17
18
  import type { ContentBlock, Message } from "../providers/types.js";
@@ -151,6 +152,74 @@ describe("postTurnTruncateToolResults", () => {
151
152
  expect(stub).toContain(filePath);
152
153
  });
153
154
 
155
+ test("skill_load result above threshold is NOT truncated (durable instructions exempt)", () => {
156
+ // Regression for JARVIS-1000: a hosted assistant lost its app-builder skill
157
+ // workflow when the large skill_load result was middle-truncated between the
158
+ // turn that loaded the skill and the turn that used it, then fell back to a
159
+ // local-dev (vite/localhost) build path.
160
+ const toolUseId = "tool_skill_load";
161
+ const skillBody = "S".repeat(THRESHOLD_CHARS + 5_000);
162
+ const messages: Message[] = [
163
+ {
164
+ role: "assistant",
165
+ content: [
166
+ {
167
+ type: "tool_use" as const,
168
+ id: toolUseId,
169
+ name: "skill_load",
170
+ input: { skill: "app-builder" },
171
+ },
172
+ ],
173
+ },
174
+ { role: "user", content: [makeToolResult(skillBody, toolUseId)] },
175
+ ];
176
+
177
+ const { messages: result, truncatedCount } =
178
+ postTurnTruncateToolResults(messages, { conversationDir: convDir });
179
+
180
+ expect(truncatedCount).toBe(0);
181
+ expect(result).toBe(messages); // same reference — no copy
182
+ expect(existsSync(join(convDir, TOOL_RESULT_DIR))).toBe(false);
183
+
184
+ const block = result[1].content[0] as {
185
+ type: "tool_result";
186
+ content: string;
187
+ };
188
+ expect(block.content).toBe(skillBody);
189
+ expect(TRUNCATION_EXEMPT_TOOLS.has("skill_load")).toBe(true);
190
+ });
191
+
192
+ test("non-exempt tool result above threshold is still truncated when paired with a tool_use", () => {
193
+ // Control for the exemption: same shape as the skill_load case, but a tool
194
+ // that is NOT exempt must still be truncated.
195
+ const toolUseId = "tool_bash_1";
196
+ const longContent = "B".repeat(THRESHOLD_CHARS + 5_000);
197
+ const messages: Message[] = [
198
+ {
199
+ role: "assistant",
200
+ content: [
201
+ {
202
+ type: "tool_use" as const,
203
+ id: toolUseId,
204
+ name: "bash",
205
+ input: { command: "cat big.log" },
206
+ },
207
+ ],
208
+ },
209
+ { role: "user", content: [makeToolResult(longContent, toolUseId)] },
210
+ ];
211
+
212
+ const { messages: result, truncatedCount } =
213
+ postTurnTruncateToolResults(messages, { conversationDir: convDir });
214
+
215
+ expect(truncatedCount).toBe(1);
216
+ const block = result[1].content[0] as {
217
+ type: "tool_result";
218
+ content: string;
219
+ };
220
+ expect(block.content).toContain(TRUNCATION_MARKER);
221
+ });
222
+
154
223
  test("file path is deterministic for the same toolUseId", () => {
155
224
  const id = "tool_use_deterministic";
156
225
  const path1 = getToolResultFilePath("/some/dir", id);