@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
@@ -110,7 +110,7 @@ describe("memory-v3-live v2 suppression", () => {
110
110
  ];
111
111
 
112
112
  const result = await applyRuntimeInjections(runMessages, {
113
- turnContext: makeTurnContext(),
113
+ ...makeTurnContext(),
114
114
  });
115
115
 
116
116
  // Exactly one <memory> source across the WHOLE assembled context.
@@ -149,7 +149,7 @@ describe("memory-v3-live v2 suppression", () => {
149
149
  ];
150
150
 
151
151
  const result = await applyRuntimeInjections(runMessages, {
152
- turnContext: makeTurnContext(),
152
+ ...makeTurnContext(),
153
153
  });
154
154
 
155
155
  // v2's block survives — the turn still ships memory.
@@ -177,7 +177,7 @@ describe("memory-v3-live v2 suppression", () => {
177
177
  // captures the exact pre-flag assembly behavior: v2 prefix stays, v3
178
178
  // splices after it.
179
179
  const offResult = await applyRuntimeInjections(runMessages, {
180
- turnContext: makeTurnContext(),
180
+ ...makeTurnContext(),
181
181
  });
182
182
 
183
183
  // The tail keeps v2's block AND gains v3's (the historical double-injection
@@ -206,7 +206,7 @@ describe("memory-v3-live v2 suppression", () => {
206
206
  ];
207
207
 
208
208
  const result = await applyRuntimeInjections(runMessages, {
209
- turnContext: makeTurnContext(),
209
+ ...makeTurnContext(),
210
210
  });
211
211
 
212
212
  expect(result.messages).toEqual(runMessages);
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Tests for handleListMessages clientMessageId projection.
3
+ *
4
+ * Verifies that the persisted idempotency nonce is echoed back on the
5
+ * messages snapshot row so a client can correlate its optimistic row with
6
+ * the confirmed server row by identity instead of matching message text.
7
+ */
8
+
9
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
10
+
11
+ mock.module("../util/logger.js", () => ({
12
+ getLogger: () =>
13
+ new Proxy({} as Record<string, unknown>, {
14
+ get: () => () => {},
15
+ }),
16
+ }));
17
+
18
+ mock.module("../config/loader.js", () => ({
19
+ getConfig: () => ({
20
+ ui: {},
21
+ model: "test",
22
+ provider: "test",
23
+ memory: { enabled: false },
24
+ rateLimit: { maxRequestsPerMinute: 0 },
25
+ }),
26
+ }));
27
+
28
+ import { addMessage, createConversation } from "../memory/conversation-crud.js";
29
+ import { getDb } from "../memory/db-connection.js";
30
+ import { initializeDb } from "../memory/db-init.js";
31
+ import { handleListMessages } from "../runtime/routes/conversation-routes.js";
32
+
33
+ initializeDb();
34
+
35
+ function resetTables() {
36
+ const db = getDb();
37
+ db.run("DELETE FROM messages");
38
+ db.run("DELETE FROM conversations");
39
+ }
40
+
41
+ interface MessagePayload {
42
+ role: string;
43
+ clientMessageId?: string;
44
+ }
45
+
46
+ describe("handleListMessages clientMessageId", () => {
47
+ beforeEach(resetTables);
48
+
49
+ test("echoes the persisted clientMessageId onto the user row", async () => {
50
+ // GIVEN a user message persisted with a client-generated idempotency nonce
51
+ const conv = createConversation();
52
+ await addMessage(
53
+ conv.id,
54
+ "user",
55
+ JSON.stringify([{ type: "text", text: "hello" }]),
56
+ { clientMessageId: "nonce-abc", skipIndexing: true },
57
+ );
58
+
59
+ // WHEN the messages snapshot is built
60
+ const response = handleListMessages({
61
+ queryParams: { conversationId: conv.id },
62
+ });
63
+ const body = response as { messages: MessagePayload[] };
64
+
65
+ // THEN the snapshot row carries the nonce back for id-based correlation
66
+ expect(body.messages).toHaveLength(1);
67
+ expect(body.messages[0].role).toBe("user");
68
+ expect(body.messages[0].clientMessageId).toBe("nonce-abc");
69
+ });
70
+
71
+ test("omits clientMessageId when the row was persisted without one", async () => {
72
+ // GIVEN a user message persisted without a client nonce (e.g. a channel send)
73
+ const conv = createConversation();
74
+ await addMessage(
75
+ conv.id,
76
+ "user",
77
+ JSON.stringify([{ type: "text", text: "hello" }]),
78
+ { skipIndexing: true },
79
+ );
80
+
81
+ // WHEN the messages snapshot is built
82
+ const response = handleListMessages({
83
+ queryParams: { conversationId: conv.id },
84
+ });
85
+ const body = response as { messages: MessagePayload[] };
86
+
87
+ // THEN the field is absent rather than emitted as null/empty
88
+ expect(body.messages).toHaveLength(1);
89
+ expect(body.messages[0].clientMessageId).toBeUndefined();
90
+ });
91
+ });
@@ -96,6 +96,44 @@ describe("handleListMessages metadata.hidden filtering", () => {
96
96
  expect(llmRows[1].metadata).toContain('"hidden":true');
97
97
  });
98
98
 
99
+ test("UI serializer omits system rows but LLM-side getMessages includes them", async () => {
100
+ // GIVEN a conversation with a system row sandwiched between two
101
+ // renderable turns (e.g. a skill-authored context message)
102
+ const conv = createConversation();
103
+ await addMessage(
104
+ conv.id,
105
+ "user",
106
+ JSON.stringify([{ type: "text", text: "first visible" }]),
107
+ );
108
+ await addMessage(
109
+ conv.id,
110
+ "system",
111
+ JSON.stringify([{ type: "text", text: "system scaffolding" }]),
112
+ );
113
+ await addMessage(
114
+ conv.id,
115
+ "assistant",
116
+ JSON.stringify([{ type: "text", text: "second visible" }]),
117
+ );
118
+
119
+ // WHEN the UI history list is serialized
120
+ const response = handleListMessages({
121
+ queryParams: { conversationId: conv.id },
122
+ });
123
+ const body = response as { messages: MessagePayload[] };
124
+
125
+ // THEN only the user/assistant turns are returned, never the system row
126
+ expect(body.messages.map((m) => m.role)).toEqual(["user", "assistant"]);
127
+ expect(
128
+ body.messages.some((m) => plainText(m).includes("system scaffolding")),
129
+ ).toBe(false);
130
+
131
+ // AND the LLM-side loader still includes the system row for agent context
132
+ const llmRows = getMessages(conv.id);
133
+ expect(llmRows).toHaveLength(3);
134
+ expect(llmRows[1].role).toBe("system");
135
+ });
136
+
99
137
  test("messages without metadata or with hidden=false are returned", async () => {
100
138
  const conv = createConversation();
101
139
  await addMessage(
@@ -1249,3 +1249,76 @@ describe("resolveDefaultProfileKey", () => {
1249
1249
  expect(resolveDefaultProfileKey("mainAgent", llm)).toBe("balanced");
1250
1250
  });
1251
1251
  });
1252
+
1253
+ describe("resolveCallSiteConfig logitBias provenance", () => {
1254
+ const kimi = "accounts/fireworks/models/kimi-k2p6";
1255
+
1256
+ test("forwards logitBias from the active profile that opted in", () => {
1257
+ const llm = LLMSchema.parse({
1258
+ default: fullDefault,
1259
+ profiles: {
1260
+ "balanced-economy": {
1261
+ provider: "fireworks",
1262
+ model: kimi,
1263
+ logitBias: "suppress-cjk",
1264
+ },
1265
+ },
1266
+ activeProfile: "balanced-economy",
1267
+ });
1268
+ expect(resolveCallSiteConfig("mainAgent", llm).logitBias).toBe(
1269
+ "suppress-cjk",
1270
+ );
1271
+ });
1272
+
1273
+ test("a higher-precedence override profile that omits logitBias clears it", () => {
1274
+ // Active profile opts in, but a pinned (override) Kimi profile did not —
1275
+ // the override must not inherit suppress-cjk just because it resolves to
1276
+ // Fireworks.
1277
+ const llm = LLMSchema.parse({
1278
+ default: fullDefault,
1279
+ profiles: {
1280
+ "balanced-economy": {
1281
+ provider: "fireworks",
1282
+ model: kimi,
1283
+ logitBias: "suppress-cjk",
1284
+ },
1285
+ "my-kimi": { provider: "fireworks", model: kimi },
1286
+ },
1287
+ activeProfile: "balanced-economy",
1288
+ });
1289
+ const resolved = resolveCallSiteConfig("mainAgent", llm, {
1290
+ overrideProfile: "my-kimi",
1291
+ });
1292
+ expect(resolved.logitBias).toBeUndefined();
1293
+ });
1294
+
1295
+ test("does not leak the active profile's logitBias into a call site's own profile", () => {
1296
+ // For non-main call sites the call-site profile wins; since it didn't opt
1297
+ // in, the active profile's preset must not bleed through.
1298
+ const llm = LLMSchema.parse({
1299
+ default: fullDefault,
1300
+ profiles: {
1301
+ "balanced-economy": {
1302
+ provider: "fireworks",
1303
+ model: kimi,
1304
+ logitBias: "suppress-cjk",
1305
+ },
1306
+ plain: { provider: "anthropic", model: "claude-opus-4-7" },
1307
+ },
1308
+ activeProfile: "balanced-economy",
1309
+ callSites: { memoryExtraction: { profile: "plain" } },
1310
+ });
1311
+ expect(
1312
+ resolveCallSiteConfig("memoryExtraction", llm).logitBias,
1313
+ ).toBeUndefined();
1314
+ });
1315
+
1316
+ test("a logitBias on a non-profile layer (llm.default) does not apply when the winning profile omits it", () => {
1317
+ const llm = LLMSchema.parse({
1318
+ default: { ...fullDefault, logitBias: "suppress-cjk" },
1319
+ profiles: { plain: { provider: "anthropic", model: "claude-opus-4-7" } },
1320
+ activeProfile: "plain",
1321
+ });
1322
+ expect(resolveCallSiteConfig("mainAgent", llm).logitBias).toBeUndefined();
1323
+ });
1324
+ });
@@ -1,12 +1,16 @@
1
1
  /**
2
- * Tests for the default `user-prompt-submit-temp` hook (memory retrieval).
2
+ * Tests for the default `user-prompt-submit` hook (memory retrieval +
3
+ * runtime injection).
3
4
  *
4
5
  * Covers the retrieval behavior, the side effects the hook owns (injected-block
5
- * metadata, recall log, `memory_recalled` event), trust gating, error
6
- * propagation, and abort-signal forwarding. Uses `mock.module` to stub the
7
- * persistence helpers so the test doesn't touch the developer's real
8
- * `~/.vellum` or database. The memory graph handle is a hand-rolled fake
9
- * passed on the hook context — the hook only needs `prepareMemory`.
6
+ * metadata, recall log, `memory_recalled` event), trust gating, the runtime
7
+ * injection the hook applies for every actor (and the assembled-block persist),
8
+ * error propagation, and abort-signal forwarding. Uses `mock.module` to stub
9
+ * the persistence helpers, `applyRuntimeInjections`, and the conversation
10
+ * registry / trust resolver so the test doesn't touch the developer's real
11
+ * `~/.vellum`, database, or live conversation state. The memory graph handle,
12
+ * abort signal, and trust class are self-resolved by the hook from a fake
13
+ * conversation installed in the registry mock.
10
14
  */
11
15
 
12
16
  import { beforeEach, describe, expect, mock, test } from "bun:test";
@@ -23,13 +27,52 @@ mock.module("../memory/memory-recall-log-store.js", () => ({
23
27
  recordMemoryRecallLog: recordMemoryRecallLogMock,
24
28
  }));
25
29
 
30
+ // Pass-through injection stub: returns the input messages unchanged with no
31
+ // assembled blocks, so existing array-identity assertions hold. Individual
32
+ // tests override it via `mockImplementationOnce` to exercise injected blocks.
33
+ const applyRuntimeInjectionsMock = mock(async (messages: unknown) => ({
34
+ messages,
35
+ blocks: {},
36
+ }));
37
+ mock.module("../daemon/conversation-runtime-assembly.js", () => ({
38
+ applyRuntimeInjections: applyRuntimeInjectionsMock,
39
+ resolveTurnInboundActorContext: () => null,
40
+ resolveTurnModelProfileLabel: () => null,
41
+ }));
42
+
43
+ // The hook self-resolves the live conversation and its trust class; both are
44
+ // driven from these refs so each test controls what the hook sees.
45
+ let currentConversation: Conversation | undefined;
46
+ let currentTrustClass: "guardian" | "unknown" = "guardian";
47
+ const findConversationOrSubagentMock = mock(
48
+ (_conversationId?: string) => currentConversation,
49
+ );
50
+ mock.module("../daemon/conversation-registry.js", () => ({
51
+ findConversationOrSubagent: findConversationOrSubagentMock,
52
+ }));
53
+ mock.module("../daemon/trust-context.js", () => ({
54
+ resolveTrustClass: () => currentTrustClass,
55
+ }));
56
+ mock.module("../config/loader.js", () => ({
57
+ getConfig: () => ({}) as AssistantConfig,
58
+ }));
59
+
60
+ // The hook publishes `memory_recalled` (and forwards the sink into
61
+ // `prepareMemory` for `memory_status`) through the shared `broadcastMessage`
62
+ // hub. Stub it so tests can assert what the hook emits.
63
+ const broadcastMessageMock = mock((_msg: unknown) => {});
64
+ mock.module("../runtime/assistant-event-hub.js", () => ({
65
+ broadcastMessage: broadcastMessageMock,
66
+ }));
67
+
68
+ import type { UserPromptSubmitContext } from "@vellumai/plugin-api";
69
+
26
70
  import type { AssistantConfig } from "../config/schema.js";
71
+ import type { Conversation } from "../daemon/conversation.js";
27
72
  import type { ServerMessage } from "../daemon/message-protocol.js";
28
73
  import type { ConversationGraphMemory } from "../memory/graph/conversation-graph-memory.js";
29
74
  import type { QdrantSparseVector } from "../memory/qdrant-client.js";
30
- import userPromptSubmitMemoryRetrieval, {
31
- type MemoryRetrievalHookContext,
32
- } from "../plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.js";
75
+ import userPromptSubmitMemoryRetrieval from "../plugins/defaults/memory-retrieval/hooks/user-prompt-submit.js";
33
76
  import type { Message } from "../providers/types.js";
34
77
 
35
78
  /** Canonical metrics payload the graph retriever attaches to a real hit. */
@@ -102,31 +145,55 @@ function makeFakeGraphMemory(overrides?: {
102
145
  }
103
146
 
104
147
  function makeHookCtx(
105
- overrides: Partial<MemoryRetrievalHookContext> = {},
106
- ): MemoryRetrievalHookContext {
107
- const { memory } = makeFakeGraphMemory();
148
+ overrides: Partial<UserPromptSubmitContext> = {},
149
+ ): UserPromptSubmitContext {
108
150
  return {
109
- graphMemory: memory,
110
- config: {} as AssistantConfig,
111
- onEvent: () => {},
112
- isTrustedActor: true,
113
151
  conversationId: "conv-test",
114
152
  userMessageId: "msg-test",
115
153
  logger: {
116
154
  warn: () => {},
117
- } as unknown as MemoryRetrievalHookContext["logger"],
118
- signal: new AbortController().signal,
155
+ } as unknown as UserPromptSubmitContext["logger"],
119
156
  latestMessages: [],
157
+ requestId: "req-test",
158
+ isNonInteractive: false,
159
+ modelProfileKey: null,
160
+ prompt: "",
161
+ originalMessages: [],
120
162
  ...overrides,
121
163
  };
122
164
  }
123
165
 
166
+ /**
167
+ * Install a fake live conversation for the hook to self-resolve by id: the
168
+ * graph handle, abort signal, and trust class all come from here rather than
169
+ * the hook context.
170
+ */
171
+ function installConversation(
172
+ graphMemory: ConversationGraphMemory,
173
+ opts?: {
174
+ trusted?: boolean;
175
+ signal?: AbortSignal;
176
+ },
177
+ ): void {
178
+ currentTrustClass = opts?.trusted === false ? "unknown" : "guardian";
179
+ currentConversation = {
180
+ graphMemory,
181
+ trustContext: undefined,
182
+ abortController: { signal: opts?.signal ?? new AbortController().signal },
183
+ } as unknown as Conversation;
184
+ }
185
+
124
186
  beforeEach(() => {
125
187
  updateMessageMetadataMock.mockReset();
126
188
  recordMemoryRecallLogMock.mockReset();
189
+ applyRuntimeInjectionsMock.mockClear();
190
+ findConversationOrSubagentMock.mockClear();
191
+ broadcastMessageMock.mockReset();
192
+ currentConversation = undefined;
193
+ currentTrustClass = "guardian";
127
194
  });
128
195
 
129
- describe("user-prompt-submit-temp hook (memory retrieval)", () => {
196
+ describe("user-prompt-submit hook (memory retrieval)", () => {
130
197
  test("adopts the injected run messages when the actor is trusted", async () => {
131
198
  const injected: Message[] = [
132
199
  { role: "user", content: [{ type: "text", text: "injected" }] },
@@ -134,7 +201,8 @@ describe("user-prompt-submit-temp hook (memory retrieval)", () => {
134
201
  const { memory, prepareMemoryMock } = makeFakeGraphMemory({
135
202
  messages: injected,
136
203
  });
137
- const ctx = makeHookCtx({ graphMemory: memory, isTrustedActor: true });
204
+ installConversation(memory, { trusted: true });
205
+ const ctx = makeHookCtx();
138
206
 
139
207
  await userPromptSubmitMemoryRetrieval(ctx);
140
208
 
@@ -156,7 +224,8 @@ describe("user-prompt-submit-temp hook (memory retrieval)", () => {
156
224
  userQueryVector: userDense,
157
225
  userQuerySparseVector: userSparse,
158
226
  });
159
- const userCtx = makeHookCtx({ graphMemory: withUserQuery.memory });
227
+ installConversation(withUserQuery.memory, { trusted: true });
228
+ const userCtx = makeHookCtx();
160
229
  await userPromptSubmitMemoryRetrieval(userCtx);
161
230
  // User-query pair wins — never crossed with the summary signal — and is
162
231
  // recorded back onto the graph handle for the PKB-reminder injector.
@@ -169,7 +238,8 @@ describe("user-prompt-submit-temp hook (memory retrieval)", () => {
169
238
  queryVector: summaryDense,
170
239
  sparseVector: summarySparse,
171
240
  });
172
- const summaryCtx = makeHookCtx({ graphMemory: summaryOnly.memory });
241
+ installConversation(summaryOnly.memory, { trusted: true });
242
+ const summaryCtx = makeHookCtx();
173
243
  await userPromptSubmitMemoryRetrieval(summaryCtx);
174
244
  expect(summaryOnly.recordPkbQueryVectorsMock).toHaveBeenCalledWith(
175
245
  summaryDense,
@@ -177,17 +247,59 @@ describe("user-prompt-submit-temp hook (memory retrieval)", () => {
177
247
  );
178
248
  });
179
249
 
250
+ test("applies runtime injection on the retrieved history and persists the assembled blocks", async () => {
251
+ const retrieved: Message[] = [
252
+ { role: "user", content: [{ type: "text", text: "retrieved" }] },
253
+ ];
254
+ const injected: Message[] = [
255
+ { role: "user", content: [{ type: "text", text: "injected" }] },
256
+ ];
257
+ const { memory } = makeFakeGraphMemory({ messages: retrieved });
258
+ applyRuntimeInjectionsMock.mockImplementationOnce(async () => ({
259
+ messages: injected,
260
+ blocks: { unifiedTurnContext: "tc-block" },
261
+ }));
262
+ installConversation(memory, { trusted: true });
263
+ const ctx = makeHookCtx({ userMessageId: "msg-77" });
264
+
265
+ await userPromptSubmitMemoryRetrieval(ctx);
266
+
267
+ // Injection runs on the retrieved history; its result becomes the working
268
+ // list the loop reads back.
269
+ expect(applyRuntimeInjectionsMock).toHaveBeenCalledTimes(1);
270
+ expect(applyRuntimeInjectionsMock.mock.calls[0]?.[0]).toBe(retrieved);
271
+ expect(ctx.latestMessages).toBe(injected);
272
+ // The assembled blocks are persisted onto the user message metadata.
273
+ expect(updateMessageMetadataMock).toHaveBeenCalledWith("msg-77", {
274
+ turnContextBlock: "tc-block",
275
+ });
276
+ });
277
+
278
+ test("applies runtime injection for untrusted actors despite skipping retrieval", async () => {
279
+ const { memory, prepareMemoryMock } = makeFakeGraphMemory();
280
+ const seeded: Message[] = [
281
+ { role: "user", content: [{ type: "text", text: "seeded" }] },
282
+ ];
283
+ installConversation(memory, { trusted: false });
284
+ const ctx = makeHookCtx({ latestMessages: seeded });
285
+
286
+ await userPromptSubmitMemoryRetrieval(ctx);
287
+
288
+ // The memory-graph step is gated on trust, but injection runs for everyone
289
+ // — on the seeded history, since no retrieval replaced it.
290
+ expect(prepareMemoryMock).not.toHaveBeenCalled();
291
+ expect(applyRuntimeInjectionsMock).toHaveBeenCalledTimes(1);
292
+ expect(applyRuntimeInjectionsMock.mock.calls[0]?.[0]).toBe(seeded);
293
+ });
294
+
180
295
  test("skips graph retrieval and side effects for untrusted actors", async () => {
181
296
  const { memory, prepareMemoryMock, recordPkbQueryVectorsMock } =
182
297
  makeFakeGraphMemory();
183
298
  const seeded: Message[] = [
184
299
  { role: "user", content: [{ type: "text", text: "seeded" }] },
185
300
  ];
186
- const ctx = makeHookCtx({
187
- graphMemory: memory,
188
- isTrustedActor: false,
189
- latestMessages: seeded,
190
- });
301
+ installConversation(memory, { trusted: false });
302
+ const ctx = makeHookCtx({ latestMessages: seeded });
191
303
 
192
304
  await userPromptSubmitMemoryRetrieval(ctx);
193
305
 
@@ -201,14 +313,12 @@ describe("user-prompt-submit-temp hook (memory retrieval)", () => {
201
313
  });
202
314
 
203
315
  test("persists injected block, recall log, and emits memory_recalled", async () => {
204
- const received: ServerMessage[] = [];
205
316
  const { memory } = makeFakeGraphMemory({
206
317
  injectedBlockText: "injected-block",
207
318
  metrics: makeMetrics(),
208
319
  });
320
+ installConversation(memory, { trusted: true });
209
321
  const ctx = makeHookCtx({
210
- graphMemory: memory,
211
- onEvent: (msg) => received.push(msg),
212
322
  userMessageId: "msg-42",
213
323
  conversationId: "conv-42",
214
324
  });
@@ -225,13 +335,16 @@ describe("user-prompt-submit-temp hook (memory retrieval)", () => {
225
335
  };
226
336
  expect(logEntry.conversationId).toBe("conv-42");
227
337
  expect(logEntry.reason).toBe("graph:none");
228
- expect(received).toHaveLength(1);
229
- expect(received[0]?.type).toBe("memory_recalled");
338
+ // The `memory_recalled` summary publishes through the shared broadcast hub.
339
+ expect(broadcastMessageMock).toHaveBeenCalledTimes(1);
340
+ const emitted = broadcastMessageMock.mock.calls[0]?.[0] as ServerMessage;
341
+ expect(emitted.type).toBe("memory_recalled");
230
342
  });
231
343
 
232
344
  test("skips metadata persist when no block text is injected", async () => {
233
345
  const { memory } = makeFakeGraphMemory({ injectedBlockText: null });
234
- const ctx = makeHookCtx({ graphMemory: memory });
346
+ installConversation(memory, { trusted: true });
347
+ const ctx = makeHookCtx();
235
348
 
236
349
  await userPromptSubmitMemoryRetrieval(ctx);
237
350
 
@@ -254,16 +367,17 @@ describe("user-prompt-submit-temp hook (memory retrieval)", () => {
254
367
  const graphMemory = {
255
368
  prepareMemory: failingPrepare,
256
369
  } as unknown as ConversationGraphMemory;
257
- const ctx = makeHookCtx({ graphMemory, isTrustedActor: true });
370
+ installConversation(graphMemory, { trusted: true });
371
+ const ctx = makeHookCtx();
258
372
 
259
373
  await expect(userPromptSubmitMemoryRetrieval(ctx)).rejects.toThrow(
260
374
  "retrieval failed",
261
375
  );
262
376
  });
263
377
 
264
- test("forwards the context abort signal into prepareMemory", async () => {
265
- // The hook hands its `ctx.signal` straight to `prepareMemory` so an
266
- // external cancel aborts the underlying retrieval.
378
+ test("forwards the conversation abort signal into prepareMemory", async () => {
379
+ // The hook hands the live conversation's abort signal straight to
380
+ // `prepareMemory` so an external cancel aborts the underlying retrieval.
267
381
  let capturedSignal: AbortSignal | undefined;
268
382
  const prepareMemoryMock = mock(
269
383
  async (
@@ -288,7 +402,11 @@ describe("user-prompt-submit-temp hook (memory retrieval)", () => {
288
402
  recordPkbQueryVectors: mock(() => {}),
289
403
  } as unknown as ConversationGraphMemory;
290
404
  const controller = new AbortController();
291
- const ctx = makeHookCtx({ graphMemory, signal: controller.signal });
405
+ installConversation(graphMemory, {
406
+ trusted: true,
407
+ signal: controller.signal,
408
+ });
409
+ const ctx = makeHookCtx();
292
410
 
293
411
  await userPromptSubmitMemoryRetrieval(ctx);
294
412