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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (490) hide show
  1. package/ARCHITECTURE.md +6 -6
  2. package/bun.lock +2 -2
  3. package/docs/activation-funnel-telemetry.md +310 -0
  4. package/examples/plugins/echo/README.md +61 -60
  5. package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
  6. package/examples/plugins/echo/hooks/stop.ts +16 -0
  7. package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
  8. package/examples/plugins/echo/package.json +1 -2
  9. package/examples/plugins/echo/src/emit.ts +19 -0
  10. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
  11. package/openapi.yaml +273 -121
  12. package/package.json +2 -2
  13. package/scripts/sync-llm-catalog.ts +2 -0
  14. package/src/__tests__/activation-early-marking.test.ts +120 -0
  15. package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
  16. package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
  17. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
  18. package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
  19. package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
  20. package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
  21. package/src/__tests__/agent-loop-thinking.test.ts +36 -20
  22. package/src/__tests__/agent-loop.test.ts +441 -96
  23. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
  24. package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
  25. package/src/__tests__/anthropic-provider.test.ts +23 -8
  26. package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
  27. package/src/__tests__/app-control-flow.test.ts +1 -1
  28. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  29. package/src/__tests__/app-executors.test.ts +132 -0
  30. package/src/__tests__/approval-cascade.test.ts +6 -5
  31. package/src/__tests__/approval-routes-http.test.ts +4 -1
  32. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  33. package/src/__tests__/build-persisted-content.test.ts +75 -1
  34. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  35. package/src/__tests__/channel-approvals.test.ts +1 -1
  36. package/src/__tests__/compaction-circuit.test.ts +258 -0
  37. package/src/__tests__/compaction-direct.test.ts +146 -0
  38. package/src/__tests__/compaction-events.test.ts +7 -7
  39. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  40. package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
  41. package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
  42. package/src/__tests__/context-overflow-reducer.test.ts +265 -125
  43. package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
  44. package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
  45. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
  46. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
  47. package/src/__tests__/conversation-agent-loop-overflow.test.ts +158 -234
  48. package/src/__tests__/conversation-agent-loop.test.ts +342 -623
  49. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  50. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
  51. package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
  52. package/src/__tests__/conversation-error.test.ts +15 -1
  53. package/src/__tests__/conversation-history-web-search.test.ts +6 -1
  54. package/src/__tests__/conversation-media-retry.test.ts +1 -1
  55. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
  56. package/src/__tests__/conversation-process-callsite.test.ts +15 -15
  57. package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
  58. package/src/__tests__/conversation-queue.test.ts +10 -10
  59. package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
  60. package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
  61. package/src/__tests__/conversation-slash-queue.test.ts +7 -5
  62. package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
  63. package/src/__tests__/conversation-speed-override.test.ts +11 -10
  64. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  65. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
  66. package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
  67. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
  68. package/src/__tests__/conversation-undo.test.ts +2 -2
  69. package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
  70. package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
  71. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
  72. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  73. package/src/__tests__/corrected-target.test.ts +93 -0
  74. package/src/__tests__/credential-execution-feature-gates.test.ts +5 -4
  75. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  76. package/src/__tests__/cu-unified-flow.test.ts +36 -0
  77. package/src/__tests__/db-acp-history.test.ts +101 -0
  78. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  79. package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
  80. package/src/__tests__/empty-response-hook.test.ts +1 -1
  81. package/src/__tests__/file-write-tool.test.ts +63 -0
  82. package/src/__tests__/gateway-only-guard.test.ts +17 -2
  83. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  84. package/src/__tests__/guardian-routing-invariants.test.ts +93 -4
  85. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  86. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  87. package/src/__tests__/heartbeat-service.test.ts +1 -0
  88. package/src/__tests__/history-repair-hook.test.ts +5 -1
  89. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  90. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  91. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  92. package/src/__tests__/inference-profile-reaper.test.ts +62 -0
  93. package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
  94. package/src/__tests__/injector-background-turn.test.ts +13 -23
  95. package/src/__tests__/injector-chain.test.ts +268 -44
  96. package/src/__tests__/injector-disk-pressure.test.ts +210 -52
  97. package/src/__tests__/injector-document-comments.test.ts +97 -114
  98. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  99. package/src/__tests__/injector-v3-suppression.test.ts +4 -4
  100. package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
  101. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  102. package/src/__tests__/llm-catalog-parity.test.ts +4 -0
  103. package/src/__tests__/llm-resolver.test.ts +73 -0
  104. package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
  105. package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
  106. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  107. package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
  108. package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
  109. package/src/__tests__/persist-unsendable-image.test.ts +97 -1
  110. package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
  111. package/src/__tests__/plugin-api-shim.test.ts +6 -9
  112. package/src/__tests__/plugin-bootstrap.test.ts +91 -81
  113. package/src/__tests__/plugin-registry.test.ts +3 -49
  114. package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
  115. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  116. package/src/__tests__/plugin-types.test.ts +0 -70
  117. package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
  118. package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
  119. package/src/__tests__/published-app-updater.test.ts +138 -0
  120. package/src/__tests__/reaction-persistence.test.ts +1 -1
  121. package/src/__tests__/retry-thinking-adaptive-only.test.ts +245 -0
  122. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  123. package/src/__tests__/server-history-render.test.ts +39 -0
  124. package/src/__tests__/skill-feature-flags-integration.test.ts +31 -0
  125. package/src/__tests__/steer-tool-repair.test.ts +1 -1
  126. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  127. package/src/__tests__/subagent-detail.test.ts +25 -7
  128. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  129. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  130. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  131. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  132. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  133. package/src/__tests__/title-generate-hook.test.ts +5 -1
  134. package/src/__tests__/tool-error-hook.test.ts +1 -1
  135. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
  136. package/src/__tests__/tool-start-timestamp.test.ts +218 -0
  137. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  138. package/src/__tests__/web-fetch.test.ts +45 -0
  139. package/src/acp/__tests__/agent-process.test.ts +161 -0
  140. package/src/acp/__tests__/client-handler.test.ts +40 -0
  141. package/src/acp/__tests__/helpers/acp-config-stub.ts +0 -2
  142. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  143. package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
  144. package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
  145. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  146. package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
  147. package/src/acp/agent-process.ts +61 -1
  148. package/src/acp/auto-install.test.ts +280 -0
  149. package/src/acp/auto-install.ts +232 -0
  150. package/src/acp/client-handler.ts +31 -0
  151. package/src/acp/prepare-agent-env.ts +80 -27
  152. package/src/acp/resolve-agent.test.ts +188 -28
  153. package/src/acp/resolve-agent.ts +119 -42
  154. package/src/acp/resume-hint.ts +23 -0
  155. package/src/acp/session-manager.ts +507 -73
  156. package/src/agent/compaction-circuit.ts +60 -102
  157. package/src/agent/loop.ts +403 -251
  158. package/src/api/events/tool-result.ts +9 -0
  159. package/src/api/events/tool-use-start.ts +7 -0
  160. package/src/api/responses/conversation-message.ts +72 -13
  161. package/src/api/responses/memory-v3-selection-log.ts +19 -10
  162. package/src/approvals/guardian-request-resolvers.ts +27 -1
  163. package/src/background-wake/next-wake.ts +1 -0
  164. package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
  165. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  166. package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
  167. package/src/cli/commands/memory-v3.ts +57 -199
  168. package/src/cli/commands/plugins.ts +43 -37
  169. package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
  170. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
  171. package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
  172. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
  173. package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
  174. package/src/cli/lib/install-from-github.ts +797 -60
  175. package/src/cli/lib/plugin-catalog-cache.ts +84 -0
  176. package/src/cli/lib/plugin-details.ts +416 -0
  177. package/src/cli/lib/plugin-marketplace.ts +215 -0
  178. package/src/cli/lib/search-plugins.ts +201 -29
  179. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  180. package/src/config/acp-defaults.test.ts +10 -0
  181. package/src/config/acp-defaults.ts +9 -3
  182. package/src/config/acp-schema.ts +1 -7
  183. package/src/config/bundled-skills/acp/SKILL.md +80 -41
  184. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  185. package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
  186. package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
  187. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  188. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  189. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  190. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  191. package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
  192. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  193. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  194. package/src/config/bundled-tool-registry.ts +4 -0
  195. package/src/config/call-site-defaults.ts +0 -3
  196. package/src/config/feature-flag-registry.json +10 -56
  197. package/src/config/llm-resolver.ts +39 -7
  198. package/src/config/loader.ts +5 -22
  199. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
  200. package/src/config/schemas/call-site-catalog.ts +0 -21
  201. package/src/config/schemas/heartbeat.ts +9 -0
  202. package/src/config/schemas/llm.ts +17 -3
  203. package/src/config/schemas/memory-v3.ts +58 -8
  204. package/src/config/seed-inference-profiles.ts +18 -0
  205. package/src/context/compactor.ts +22 -4
  206. package/src/context/post-turn-tool-result-truncation.ts +39 -1
  207. package/src/context/strip-injections.ts +8 -2
  208. package/src/daemon/conversation-agent-loop-handlers.ts +120 -42
  209. package/src/daemon/conversation-agent-loop.ts +308 -1297
  210. package/src/daemon/conversation-error.ts +31 -4
  211. package/src/daemon/conversation-history.ts +1 -1
  212. package/src/daemon/conversation-lifecycle.ts +11 -255
  213. package/src/daemon/conversation-media-retry.ts +19 -6
  214. package/src/daemon/conversation-messaging.ts +17 -0
  215. package/src/daemon/conversation-process.ts +22 -141
  216. package/src/daemon/conversation-queue-manager.ts +8 -0
  217. package/src/daemon/conversation-registry.ts +159 -0
  218. package/src/daemon/conversation-runtime-assembly.ts +361 -393
  219. package/src/daemon/conversation-store.ts +9 -90
  220. package/src/daemon/conversation-surfaces.ts +165 -11
  221. package/src/daemon/conversation-workspace.ts +17 -0
  222. package/src/daemon/conversation.ts +444 -62
  223. package/src/daemon/disk-pressure-policy.ts +0 -1
  224. package/src/daemon/external-plugins-bootstrap.ts +36 -46
  225. package/src/daemon/handlers/config-channels.ts +11 -2
  226. package/src/daemon/handlers/conversations.ts +3 -1
  227. package/src/daemon/handlers/shared.ts +9 -1
  228. package/src/daemon/handlers/skills.ts +4 -1
  229. package/src/daemon/host-app-control-proxy.ts +72 -57
  230. package/src/daemon/host-proxy-preactivation.ts +1 -3
  231. package/src/daemon/lifecycle.ts +21 -7
  232. package/src/daemon/persist-unsendable-image.ts +62 -25
  233. package/src/daemon/process-message.ts +1 -1
  234. package/src/daemon/server.ts +2 -0
  235. package/src/daemon/tool-side-effects.ts +15 -0
  236. package/src/daemon/wake-conversation-ops.ts +269 -0
  237. package/src/embedded/plugin-api.ts +2 -2
  238. package/src/export/__tests__/transcript-formatter.test.ts +5 -0
  239. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  240. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  241. package/src/heartbeat/heartbeat-service.ts +26 -0
  242. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  243. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  244. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  245. package/src/ipc/skill-routes/memory.ts +4 -2
  246. package/src/memory/__tests__/activation-session-store.test.ts +41 -0
  247. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
  248. package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
  249. package/src/memory/activation-session-store.ts +43 -0
  250. package/src/memory/conversation-crud.ts +29 -19
  251. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  252. package/src/memory/db-init.ts +6 -0
  253. package/src/memory/embedding-backend.ts +5 -22
  254. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  255. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
  256. package/src/memory/jobs-worker.ts +25 -1
  257. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  258. package/src/memory/migrations/273-onboarding-events-funnel-columns.ts +46 -0
  259. package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
  260. package/src/memory/migrations/index.ts +3 -0
  261. package/src/memory/onboarding-events-store.ts +66 -18
  262. package/src/memory/schema/acp.ts +4 -0
  263. package/src/memory/schema/infrastructure.ts +13 -0
  264. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
  265. package/src/memory/v2/__tests__/page-store.test.ts +22 -0
  266. package/src/memory/v2/consolidation-job.ts +2 -63
  267. package/src/memory/v2/types.ts +5 -0
  268. package/src/messaging/providers/telegram-bot/api.ts +14 -5
  269. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  270. package/src/notifications/access-request-copy.ts +298 -0
  271. package/src/notifications/adapters/slack.ts +3 -3
  272. package/src/notifications/adapters/telegram.ts +9 -2
  273. package/src/notifications/copy-composer.ts +49 -267
  274. package/src/notifications/decision-engine.ts +16 -35
  275. package/src/notifications/home-feed-side-effect.ts +1 -6
  276. package/src/plugin-api/constants.ts +4 -0
  277. package/src/plugin-api/index.ts +40 -11
  278. package/src/plugin-api/types.ts +108 -0
  279. package/src/plugins/defaults/compaction/compact.ts +106 -0
  280. package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
  281. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  282. package/src/plugins/defaults/compaction/manager-store.ts +57 -0
  283. package/src/plugins/defaults/compaction/package.json +1 -2
  284. package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
  285. package/src/plugins/defaults/empty-response/package.json +0 -1
  286. package/src/plugins/defaults/history-repair/package.json +0 -1
  287. package/src/plugins/defaults/index.ts +156 -26
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
  289. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
  290. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
  291. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
  292. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  293. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
  297. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
  298. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
  299. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
  300. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
  301. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
  302. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
  303. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
  304. package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
  305. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
  306. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
  307. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
  308. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
  309. package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
  310. package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
  311. package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
  312. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  313. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  314. package/src/plugins/defaults/memory-v3-shadow/injector.ts +76 -0
  315. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
  316. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
  317. package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
  318. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
  319. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
  320. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
  321. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
  322. package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
  323. package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
  324. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
  325. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
  326. package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
  327. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -1
  328. package/src/plugins/defaults/title-generate/package.json +0 -1
  329. package/src/plugins/defaults/tool-error/package.json +0 -1
  330. package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
  331. package/src/plugins/external-api.ts +12 -2
  332. package/src/plugins/pipeline.ts +6 -293
  333. package/src/plugins/registry.ts +9 -37
  334. package/src/plugins/types.ts +76 -381
  335. package/src/plugins/user-loader.ts +30 -127
  336. package/src/prompts/__tests__/system-prompt.test.ts +6 -0
  337. package/src/prompts/system-prompt.ts +61 -10
  338. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
  339. package/src/providers/anthropic/client.ts +9 -10
  340. package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
  341. package/src/providers/inference/logit-bias.ts +55 -0
  342. package/src/providers/model-catalog.ts +57 -0
  343. package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
  344. package/src/providers/openai/chat-completions-provider.ts +44 -1
  345. package/src/providers/retry.ts +57 -7
  346. package/src/providers/types.ts +6 -0
  347. package/src/runtime/__tests__/agent-wake.test.ts +555 -691
  348. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  349. package/src/runtime/agent-wake.ts +108 -209
  350. package/src/runtime/assistant-event-hub.ts +1 -1
  351. package/src/runtime/channel-approvals.ts +1 -1
  352. package/src/runtime/interactive-ui.ts +1 -1
  353. package/src/runtime/routes/__tests__/acp-routes.test.ts +466 -55
  354. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  355. package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
  356. package/src/runtime/routes/__tests__/stt-routes.test.ts +112 -0
  357. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  358. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  359. package/src/runtime/routes/acp-routes.test.ts +185 -32
  360. package/src/runtime/routes/acp-routes.ts +328 -30
  361. package/src/runtime/routes/app-management-routes.ts +3 -0
  362. package/src/runtime/routes/approval-routes.ts +1 -1
  363. package/src/runtime/routes/browser-routes.ts +1 -1
  364. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  365. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  366. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  367. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  368. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  369. package/src/runtime/routes/conversation-routes.ts +30 -2
  370. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  371. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  372. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  373. package/src/runtime/routes/host-cu-routes.ts +1 -1
  374. package/src/runtime/routes/identity-routes.ts +18 -3
  375. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  376. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  377. package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
  378. package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
  379. package/src/runtime/routes/memory-v3-routes.ts +65 -305
  380. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
  381. package/src/runtime/routes/playground/helpers.ts +1 -1
  382. package/src/runtime/routes/plugins-routes.ts +337 -35
  383. package/src/runtime/routes/stt-routes.ts +45 -12
  384. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  385. package/src/runtime/routes/work-items-routes.ts +2 -4
  386. package/src/runtime/routes/workspace-routes.ts +50 -15
  387. package/src/runtime/services/conversation-serializer.ts +1 -1
  388. package/src/runtime/verification-outbound-actions.ts +147 -2
  389. package/src/runtime/verification-templates.ts +29 -3
  390. package/src/services/published-app-updater.ts +30 -8
  391. package/src/signals/cancel.ts +2 -4
  392. package/src/subagent/manager.ts +21 -5
  393. package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
  394. package/src/telemetry/activation-funnel.ts +167 -0
  395. package/src/telemetry/types.ts +13 -0
  396. package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
  397. package/src/telemetry/usage-telemetry-reporter.ts +26 -1
  398. package/src/tools/acp/context.ts +20 -0
  399. package/src/tools/acp/list-agents.test.ts +9 -19
  400. package/src/tools/acp/list-agents.ts +3 -15
  401. package/src/tools/acp/spawn.test.ts +173 -202
  402. package/src/tools/acp/spawn.ts +37 -172
  403. package/src/tools/acp/steer.test.ts +105 -8
  404. package/src/tools/acp/steer.ts +48 -17
  405. package/src/tools/apps/executors.ts +166 -50
  406. package/src/tools/browser/browser-execution.ts +12 -2
  407. package/src/tools/filesystem/write.ts +34 -0
  408. package/src/tools/network/web-fetch.ts +65 -24
  409. package/src/tools/skills/load.ts +1 -1
  410. package/src/tools/subagent/spawn.ts +2 -4
  411. package/src/tools/ui-surface/definitions.ts +32 -5
  412. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  413. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
  414. package/docs/plugins.md +0 -832
  415. package/examples/plugins/echo/register.ts +0 -143
  416. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  417. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  418. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  419. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
  420. package/src/__tests__/pipeline-runner.test.ts +0 -554
  421. package/src/daemon/wake-target-adapter.ts +0 -253
  422. package/src/memory/v3/__tests__/assign.test.ts +0 -242
  423. package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
  424. package/src/memory/v3/__tests__/core.test.ts +0 -39
  425. package/src/memory/v3/__tests__/health.test.ts +0 -219
  426. package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
  427. package/src/memory/v3/__tests__/needle.test.ts +0 -107
  428. package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
  429. package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
  430. package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
  431. package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
  432. package/src/memory/v3/__tests__/router.test.ts +0 -332
  433. package/src/memory/v3/__tests__/selector.test.ts +0 -470
  434. package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
  435. package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
  436. package/src/memory/v3/__tests__/tree.test.ts +0 -192
  437. package/src/memory/v3/assign.ts +0 -268
  438. package/src/memory/v3/capabilities.ts +0 -124
  439. package/src/memory/v3/core.ts +0 -26
  440. package/src/memory/v3/health.ts +0 -0
  441. package/src/memory/v3/maintain-job.ts +0 -314
  442. package/src/memory/v3/needle.ts +0 -115
  443. package/src/memory/v3/orchestrate.ts +0 -126
  444. package/src/memory/v3/page-content.ts +0 -34
  445. package/src/memory/v3/provider-blocks.ts +0 -26
  446. package/src/memory/v3/reconcile.ts +0 -523
  447. package/src/memory/v3/router.ts +0 -190
  448. package/src/memory/v3/selection-log-store.ts +0 -84
  449. package/src/memory/v3/selector.ts +0 -226
  450. package/src/memory/v3/shadow-plugin.ts +0 -411
  451. package/src/memory/v3/snapshot.ts +0 -209
  452. package/src/memory/v3/tree.ts +0 -174
  453. package/src/memory/v3/types.ts +0 -59
  454. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  455. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  456. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  457. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  458. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  459. package/src/plugins/defaults/compaction/register.ts +0 -35
  460. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  461. package/src/plugins/defaults/empty-response/register.ts +0 -23
  462. package/src/plugins/defaults/history-repair/register.ts +0 -24
  463. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
  464. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  465. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  466. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  467. package/src/plugins/defaults/title-generate/register.ts +0 -35
  468. package/src/plugins/defaults/tool-error/register.ts +0 -23
  469. package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
  470. package/src/proactive-artifact/aux-message-injector.ts +0 -97
  471. package/src/proactive-artifact/decision.test.ts +0 -226
  472. package/src/proactive-artifact/decision.ts +0 -165
  473. package/src/proactive-artifact/index.ts +0 -7
  474. package/src/proactive-artifact/job.test.ts +0 -962
  475. package/src/proactive-artifact/job.ts +0 -372
  476. package/src/proactive-artifact/message-copy.ts +0 -58
  477. package/src/proactive-artifact/trigger-state.test.ts +0 -286
  478. package/src/proactive-artifact/trigger-state.ts +0 -123
  479. package/src/util/map-limit.ts +0 -27
  480. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  481. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  482. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  483. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  484. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  485. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  486. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  487. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  488. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  489. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  490. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
@@ -155,6 +155,21 @@ registerHook("app_refresh", (_name, input, _result, { ctx }) => {
155
155
  notifyAppChanged(ctx, appId, { fileChange: true });
156
156
  });
157
157
 
158
+ // app_update compiles internally (like app_refresh) but emits no events of its
159
+ // own, so without this hook an updated app leaves open surfaces rendering the
160
+ // stale dist and never re-deploys or invalidates the Library. The executor owns
161
+ // the compile; notifyAppChanged only refreshes surfaces and broadcasts.
162
+ registerHook("app_update", (_name, input, _result, { ctx }) => {
163
+ const appId = input.app_id as string | undefined;
164
+ if (!appId) return;
165
+ try {
166
+ addAppConversationId(appId, ctx.conversationId);
167
+ } catch (err) {
168
+ log.warn({ err, appId }, "Failed to track conversation ID on app_update");
169
+ }
170
+ notifyAppChanged(ctx, appId, { fileChange: true });
171
+ });
172
+
158
173
  registerHook("voice_config_update", (_name, input) => {
159
174
  const setting = input.setting as string | undefined;
160
175
  if (!setting) return;
@@ -0,0 +1,269 @@
1
+ /**
2
+ * Daemon-side side effects a wake performs against a live
3
+ * {@link Conversation}, used by `wakeAgentForOpportunity()` in
4
+ * `runtime/agent-wake.ts`.
5
+ *
6
+ * These are kept as standalone functions (rather than methods on
7
+ * `Conversation`) so the wake-specific concerns — event→wire translation,
8
+ * the wake ui_surface card, and the wake's one-shot tail persistence —
9
+ * stay out of the conversation class. They live in the daemon layer
10
+ * because they translate agent-loop events into client wire frames and
11
+ * touch the message store, so `runtime/agent-wake.ts` references only the
12
+ * erased `Conversation` type and never imports `message-protocol.ts`.
13
+ */
14
+
15
+ import type { AgentEvent } from "../agent/loop.js";
16
+ import {
17
+ addMessage,
18
+ getConversation,
19
+ provenanceFromTrustContext,
20
+ } from "../memory/conversation-crud.js";
21
+ import { syncMessageToDisk } from "../memory/conversation-disk-view.js";
22
+ import { backfillMessageIdOnLogs } from "../memory/llm-request-log-store.js";
23
+ import type { Message } from "../providers/types.js";
24
+ import { broadcastMessage } from "../runtime/assistant-event-hub.js";
25
+ import { getLogger } from "../util/logger.js";
26
+ import type { Conversation } from "./conversation.js";
27
+ import type { ServerMessage } from "./message-protocol.js";
28
+
29
+ const log = getLogger("wake-conversation-ops");
30
+
31
+ /**
32
+ * Translate a raw {@link AgentEvent} from the agent loop into the
33
+ * corresponding {@link ServerMessage} wire frame. The normal user-turn
34
+ * path does this via the full state-aware handler in
35
+ * `conversation-agent-loop-handlers.ts`; the wake path has no tool
36
+ * accounting, title generation, or activity-state tracking to worry
37
+ * about, so we only need the subset that produces client-visible
38
+ * frames. Events that have no client-visible wire shape (usage, error,
39
+ * preview/input-json deltas, etc.) are dropped — they produce no UI.
40
+ */
41
+ function translateAgentEventToServerMessage(
42
+ event: AgentEvent,
43
+ conversationId: string,
44
+ ): ServerMessage | null {
45
+ switch (event.type) {
46
+ case "text_delta":
47
+ return {
48
+ type: "assistant_text_delta",
49
+ text: event.text,
50
+ conversationId,
51
+ };
52
+ case "thinking_delta":
53
+ return {
54
+ type: "assistant_thinking_delta",
55
+ thinking: event.thinking,
56
+ conversationId,
57
+ };
58
+ case "tool_use":
59
+ return {
60
+ type: "tool_use_start",
61
+ toolName: event.name,
62
+ input: event.input,
63
+ conversationId,
64
+ toolUseId: event.id,
65
+ };
66
+ case "tool_use_preview_start":
67
+ return {
68
+ type: "tool_use_preview_start",
69
+ toolUseId: event.toolUseId,
70
+ toolName: event.toolName,
71
+ conversationId,
72
+ };
73
+ case "tool_output_chunk":
74
+ return {
75
+ type: "tool_output_chunk",
76
+ chunk: event.chunk,
77
+ conversationId,
78
+ toolUseId: event.toolUseId,
79
+ };
80
+ case "tool_result": {
81
+ const imageBlocks = event.contentBlocks?.filter(
82
+ (b): b is Extract<typeof b, { type: "image" }> => b.type === "image",
83
+ );
84
+ const imageDataList = imageBlocks?.length
85
+ ? imageBlocks.map((b) => b.source.data)
86
+ : undefined;
87
+ return {
88
+ type: "tool_result",
89
+ toolName: "",
90
+ result: event.content,
91
+ isError: event.isError,
92
+ diff: event.diff,
93
+ status: event.status,
94
+ conversationId,
95
+ imageData: imageDataList?.[0],
96
+ imageDataList,
97
+ toolUseId: event.toolUseId,
98
+ };
99
+ }
100
+ case "server_tool_start":
101
+ return {
102
+ type: "tool_use_start",
103
+ toolName: event.name,
104
+ input: event.input,
105
+ conversationId,
106
+ toolUseId: event.toolUseId,
107
+ };
108
+ case "server_tool_complete": {
109
+ let resultText = "";
110
+ if (Array.isArray(event.content) && event.content.length > 0) {
111
+ resultText = (event.content as unknown[])
112
+ .filter(
113
+ (r): r is { type: string; title: string; url: string } =>
114
+ typeof r === "object" &&
115
+ r != null &&
116
+ (r as { type?: string }).type === "web_search_result",
117
+ )
118
+ .map((r) => `${r.title}\n${r.url}`)
119
+ .join("\n\n");
120
+ }
121
+ return {
122
+ type: "tool_result",
123
+ toolName: "web_search",
124
+ result: resultText,
125
+ isError: event.isError,
126
+ conversationId,
127
+ toolUseId: event.toolUseId,
128
+ };
129
+ }
130
+ case "message_complete":
131
+ return {
132
+ type: "message_complete",
133
+ conversationId,
134
+ };
135
+ case "input_json_delta":
136
+ case "usage":
137
+ case "error":
138
+ case "provider_error":
139
+ case "max_tokens_reached":
140
+ case "context_compacting":
141
+ case "compaction_circuit_open":
142
+ case "compaction_circuit_closed":
143
+ case "compaction_completed":
144
+ case "history_stripped":
145
+ case "agent_loop_exit":
146
+ return null;
147
+ case "llm_call_started":
148
+ // The wake path persists its assistant tail via
149
+ // `persistWakeTailMessage` (an `addMessage`-shaped call) rather than
150
+ // the main event-handler's `reserve` → `updateContent` pipeline, so
151
+ // there is no row to reserve here. Translation returns null and the
152
+ // wake path's existing end-of-turn persist continues to mint the row.
153
+ return null;
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Translate a raw agent event to its wire frame and broadcast it to
159
+ * connected clients. No-op for events with no client-visible shape.
160
+ */
161
+ export function emitWakeAgentEvent(
162
+ conversation: Conversation,
163
+ event: AgentEvent,
164
+ ): void {
165
+ const frame = translateAgentEventToServerMessage(
166
+ event,
167
+ conversation.conversationId,
168
+ );
169
+ if (!frame) return;
170
+ broadcastMessage(frame);
171
+ }
172
+
173
+ /**
174
+ * Emit the live ui_surface card announcing that a wake produced output.
175
+ * The `surfaceId` matches the `ui_surface` content block injected into
176
+ * the wake's first assistant tail message so the client renders one card,
177
+ * not two.
178
+ */
179
+ export function broadcastWakeSurface(
180
+ conversation: Conversation,
181
+ source: string,
182
+ hint: string,
183
+ surfaceId: string,
184
+ ): void {
185
+ broadcastMessage({
186
+ type: "ui_surface_show",
187
+ conversationId: conversation.conversationId,
188
+ surfaceId,
189
+ surfaceType: "card",
190
+ data: {
191
+ title: "Conversation Woke",
192
+ body: hint,
193
+ metadata: [{ label: "Source", value: source }],
194
+ },
195
+ display: "inline",
196
+ });
197
+ }
198
+
199
+ /**
200
+ * Persist a single tail message produced by a wake (assistant outputs and
201
+ * intervening tool_result user messages). Builds the channel/interface
202
+ * metadata, persists via the canonical `addMessage` path, syncs the row to
203
+ * the disk view, and backfills the message id onto this turn's LLM request
204
+ * logs so wake-produced messages match the user-turn persistence path.
205
+ */
206
+ export async function persistWakeTailMessage(
207
+ conversation: Conversation,
208
+ message: Message,
209
+ ): Promise<void> {
210
+ const turnChannelCtx = conversation.getTurnChannelContext();
211
+ const turnInterfaceCtx = conversation.getTurnInterfaceContext();
212
+ const metadata: Record<string, unknown> = {
213
+ ...provenanceFromTrustContext(conversation.trustContext),
214
+ userMessageChannel: turnChannelCtx?.userMessageChannel ?? "vellum",
215
+ assistantMessageChannel:
216
+ turnChannelCtx?.assistantMessageChannel ?? "vellum",
217
+ userMessageInterface: turnInterfaceCtx?.userMessageInterface ?? "web",
218
+ assistantMessageInterface:
219
+ turnInterfaceCtx?.assistantMessageInterface ?? "web",
220
+ };
221
+ const persisted = await addMessage(
222
+ conversation.conversationId,
223
+ message.role,
224
+ JSON.stringify(message.content),
225
+ { metadata },
226
+ );
227
+ if (message.role === "assistant") {
228
+ try {
229
+ backfillMessageIdOnLogs(conversation.conversationId, persisted.id);
230
+ } catch (err) {
231
+ log.warn(
232
+ { err, conversationId: conversation.conversationId },
233
+ "wake persist: backfill messageId on LLM logs failed (non-fatal)",
234
+ );
235
+ }
236
+ }
237
+ try {
238
+ const convRow = getConversation(conversation.conversationId);
239
+ if (convRow) {
240
+ syncMessageToDisk(
241
+ conversation.conversationId,
242
+ persisted.id,
243
+ convRow.createdAt,
244
+ );
245
+ }
246
+ } catch (err) {
247
+ log.warn(
248
+ { err, conversationId: conversation.conversationId },
249
+ "wake persist: syncMessageToDisk failed (non-fatal)",
250
+ );
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Temporarily restrict the tools visible/executable during a wake by
256
+ * reusing the conversation's subagent allowlist slot. Returns a restore
257
+ * callback that reinstates the previous allowlist so the wake can release
258
+ * the scope before any queued user turn is drained.
259
+ */
260
+ export function scopeWakeAllowedTools(
261
+ conversation: Conversation,
262
+ tools: ReadonlySet<string>,
263
+ ): () => void {
264
+ const previous = conversation.subagentAllowedTools;
265
+ conversation.setSubagentAllowedTools(new Set(tools));
266
+ return () => {
267
+ conversation.setSubagentAllowedTools(previous);
268
+ };
269
+ }
@@ -8,7 +8,7 @@
8
8
  * means plugin-api ships as part of the assistant binary's regular code
9
9
  * graph; in JIT/Docker, it loads from source.
10
10
  *
11
- * The loaded namespace is then installed on `globalThis` under a versioned
11
+ * The loaded namespace is then installed on `globalThis` under a well-known
12
12
  * symbol. The boot-time shim writer (`ensurePluginApiShim`) enumerates
13
13
  * {@link PLUGIN_API_EXPORTS} and generates a tiny ESM module at
14
14
  * `<workspaceDir>/node_modules/@vellumai/plugin-api/index.js` that
@@ -26,7 +26,7 @@
26
26
  import * as pluginApi from "../plugin-api/index.js";
27
27
 
28
28
  /** Symbol key under which the plugin-api namespace is published on globalThis. */
29
- export const PLUGIN_API_REGISTRY_KEY = Symbol.for("vellum.plugin-api.v1");
29
+ export const PLUGIN_API_REGISTRY_KEY = Symbol.for("vellum.plugin-api");
30
30
 
31
31
  // Install on globalThis once at module-load time. The shim writer reads
32
32
  // `PLUGIN_API_EXPORTS` to know which bindings to re-export; the shim's
@@ -9,6 +9,7 @@ type TestMessage = {
9
9
  content: string;
10
10
  createdAt: number;
11
11
  metadata: string | null;
12
+ clientMessageId: string | null;
12
13
  };
13
14
 
14
15
  const parentMessages: TestMessage[] = [
@@ -19,6 +20,7 @@ const parentMessages: TestMessage[] = [
19
20
  content: JSON.stringify([{ type: "text", text: "go research foo" }]),
20
21
  createdAt: 1_700_000_000_000,
21
22
  metadata: null,
23
+ clientMessageId: null,
22
24
  },
23
25
  {
24
26
  id: "msg-parent-2",
@@ -34,6 +36,7 @@ const parentMessages: TestMessage[] = [
34
36
  conversationId: "child-conv-1",
35
37
  },
36
38
  }),
39
+ clientMessageId: null,
37
40
  },
38
41
  ];
39
42
 
@@ -47,6 +50,7 @@ const childMessages: TestMessage[] = [
47
50
  ]),
48
51
  createdAt: 1_700_000_002_000,
49
52
  metadata: null,
53
+ clientMessageId: null,
50
54
  },
51
55
  {
52
56
  id: "msg-child-2",
@@ -57,6 +61,7 @@ const childMessages: TestMessage[] = [
57
61
  ]),
58
62
  createdAt: 1_700_000_003_000,
59
63
  metadata: null,
64
+ clientMessageId: null,
60
65
  },
61
66
  ];
62
67
 
@@ -85,6 +85,7 @@ const stubConfig: {
85
85
  activeHoursStart: number | null;
86
86
  activeHoursEnd: number | null;
87
87
  maxConsecutiveRuns: number | null;
88
+ maxDailyRuns: number | null;
88
89
  disposition: string;
89
90
  };
90
91
  } = {
@@ -94,6 +95,7 @@ const stubConfig: {
94
95
  activeHoursStart: null,
95
96
  activeHoursEnd: null,
96
97
  maxConsecutiveRuns: null,
98
+ maxDailyRuns: null,
97
99
  disposition: "Default disposition text.",
98
100
  },
99
101
  };
@@ -195,6 +197,7 @@ mock.module("../heartbeat-run-store.js", () => ({
195
197
  markStaleRunsAsMissed: () => 0,
196
198
  markStaleRunningAsError: () => 0,
197
199
  countCompletedHeartbeatRuns: () => 10,
200
+ countCompletedRunsToday: () => 0,
198
201
  countRecentConsecutiveRuns: () => 0,
199
202
  }));
200
203
 
@@ -24,7 +24,8 @@ export type HeartbeatSkipReason =
24
24
  | "outside_active_hours"
25
25
  | "overlap"
26
26
  | "pre_first_user_message"
27
- | "max_consecutive_runs";
27
+ | "max_consecutive_runs"
28
+ | "max_daily_runs";
28
29
 
29
30
  export interface HeartbeatRunRecord {
30
31
  id: string;
@@ -217,6 +218,27 @@ export function countCompletedHeartbeatRuns(): number {
217
218
  return row?.count ?? 0;
218
219
  }
219
220
 
221
+ /**
222
+ * Count heartbeat runs that completed with status `ok` today (local midnight).
223
+ */
224
+ export function countCompletedRunsToday(): number {
225
+ const db = getDb();
226
+ const now = new Date();
227
+ const startOfDay = new Date(
228
+ now.getFullYear(),
229
+ now.getMonth(),
230
+ now.getDate(),
231
+ ).getTime();
232
+ const row = db
233
+ .select({ count: sql<number>`count(*)` })
234
+ .from(heartbeatRuns)
235
+ .where(
236
+ sql`${heartbeatRuns.status} = 'ok' AND ${heartbeatRuns.scheduledFor} >= ${startOfDay}`,
237
+ )
238
+ .get();
239
+ return row?.count ?? 0;
240
+ }
241
+
220
242
  /**
221
243
  * Count the most recent consecutive heartbeat runs with status `ok`,
222
244
  * walking backwards from newest. Stops at the first non-`ok` status
@@ -25,6 +25,7 @@ import { stripCommentLines } from "../util/strip-comment-lines.js";
25
25
  import {
26
26
  completeHeartbeatRun,
27
27
  countCompletedHeartbeatRuns,
28
+ countCompletedRunsToday,
28
29
  countRecentConsecutiveRuns,
29
30
  insertPendingHeartbeatRun,
30
31
  markStaleRunningAsError,
@@ -187,6 +188,13 @@ export class HeartbeatService {
187
188
  );
188
189
  }
189
190
 
191
+ /** Whether the daily run cap has been reached. */
192
+ get isDailyCapReached(): boolean {
193
+ const config = getConfig().heartbeat;
194
+ if (config.maxDailyRuns == null) return false;
195
+ return countCompletedRunsToday() >= config.maxDailyRuns;
196
+ }
197
+
190
198
  async runManagedWakeIfDue(
191
199
  options: ManagedWakeHeartbeatRunOptions = {},
192
200
  ): Promise<ManagedWakeHeartbeatRunResult> {
@@ -551,6 +559,24 @@ export class HeartbeatService {
551
559
  return false;
552
560
  }
553
561
 
562
+ // Daily run cap — stop burning tokens when the daily budget is exhausted.
563
+ // Force runs bypass the cap.
564
+ if (
565
+ !force &&
566
+ config.maxDailyRuns != null &&
567
+ countCompletedRunsToday() >= config.maxDailyRuns
568
+ ) {
569
+ log.debug(
570
+ { maxDailyRuns: config.maxDailyRuns },
571
+ "Daily run cap reached, skipping",
572
+ );
573
+ if (runId) skipHeartbeatRun(runId, "max_daily_runs");
574
+ if (!this.cronMode) {
575
+ this.scheduleNextRun(config.intervalMs);
576
+ }
577
+ return false;
578
+ }
579
+
554
580
  // Overlap prevention
555
581
  if (this.activeRun) {
556
582
  log.debug("Previous heartbeat run still active, skipping");
@@ -55,7 +55,7 @@ mock.module("../../browser/operations.js", () => ({
55
55
  },
56
56
  }));
57
57
 
58
- mock.module("../../daemon/conversation-store.js", () => ({
58
+ mock.module("../../daemon/conversation-registry.js", () => ({
59
59
  findConversation: (conversationId: string) => {
60
60
  mockFindConversationCalls.push(conversationId);
61
61
  return mockConversation ?? undefined;
@@ -32,9 +32,9 @@ let mockSurfaceResult: InteractiveUiResult = {
32
32
  let mockSurfaceThrows: Error | null = null;
33
33
 
34
34
  // Re-export the real module and override only findConversation.
35
- const realStore = await import("../../daemon/conversation-store.js");
36
- mock.module("../../daemon/conversation-store.js", () => ({
37
- ...realStore,
35
+ const realRegistry = await import("../../daemon/conversation-registry.js");
36
+ mock.module("../../daemon/conversation-registry.js", () => ({
37
+ ...realRegistry,
38
38
  findConversation: (_conversationId: string) => mockConversation ?? undefined,
39
39
  }));
40
40
 
@@ -137,6 +137,21 @@ describe("host.memory.addMessage", () => {
137
137
  }),
138
138
  ).rejects.toThrow();
139
139
  });
140
+
141
+ test("rejects the non-renderable system role", async () => {
142
+ // GIVEN the messages store is UI-facing (ConversationMessage), so only
143
+ // renderable turns may be persisted via this facet
144
+ // WHEN a skill attempts to add a system row
145
+ // THEN the route rejects it instead of persisting agent-context scaffolding
146
+ await expect(
147
+ memoryAddMessageRoute.handler({
148
+ conversationId: "c",
149
+ role: "system",
150
+ content: "agent-context scaffolding",
151
+ }),
152
+ ).rejects.toThrow();
153
+ expect(addMessageSpy).not.toHaveBeenCalled();
154
+ });
140
155
  });
141
156
 
142
157
  describe("host.memory.wakeAgentForOpportunity", () => {
@@ -17,7 +17,9 @@ import type { SkillIpcRoute } from "../skill-ipc-types.js";
17
17
 
18
18
  /**
19
19
  * IPC params for `addMessage()`. `role` is constrained to the
20
- * `MessageRole` union. `metadata` is a free-form record (validated
20
+ * renderable conversation roles (`user`/`assistant`): the messages store
21
+ * is UI-facing (`ConversationMessage`), so agent-context `system` rows are
22
+ * not accepted here. `metadata` is a free-form record (validated
21
23
  * downstream by `messageMetadataSchema` with a warn-and-store fallback).
22
24
  * `skipIndexing` and `clientMessageId` mirror `AddMessageOptions`.
23
25
  *
@@ -28,7 +30,7 @@ import type { SkillIpcRoute } from "../skill-ipc-types.js";
28
30
  */
29
31
  const MemoryAddMessageParams = z.object({
30
32
  conversationId: z.string().min(1),
31
- role: z.enum(["user", "assistant", "system"]),
33
+ role: z.enum(["user", "assistant"]),
32
34
  content: z.string(),
33
35
  metadata: z.record(z.string(), z.unknown()).optional(),
34
36
  skipIndexing: z.boolean().optional(),
@@ -0,0 +1,41 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ mock.module("../../util/logger.js", () => ({
4
+ getLogger: () =>
5
+ new Proxy({} as Record<string, unknown>, {
6
+ get: () => () => {},
7
+ }),
8
+ }));
9
+
10
+ import {
11
+ isActivationSession,
12
+ markActivationSession,
13
+ } from "../activation-session-store.js";
14
+ import { getDb } from "../db-connection.js";
15
+ import { initializeDb } from "../db-init.js";
16
+ import { activationSessions } from "../schema.js";
17
+
18
+ initializeDb();
19
+
20
+ describe("activation-session-store", () => {
21
+ beforeEach(() => {
22
+ getDb().delete(activationSessions).run();
23
+ });
24
+
25
+ test("marks a conversation and reports it as an activation session", () => {
26
+ markActivationSession("c1");
27
+ expect(isActivationSession("c1")).toBe(true);
28
+ });
29
+
30
+ test("unmarked conversations are not activation sessions", () => {
31
+ expect(isActivationSession("nope")).toBe(false);
32
+ });
33
+
34
+ test("marking the same conversation twice is idempotent", () => {
35
+ markActivationSession("c1");
36
+ markActivationSession("c1");
37
+ expect(isActivationSession("c1")).toBe(true);
38
+ const rows = getDb().select().from(activationSessions).all();
39
+ expect(rows.length).toBe(1);
40
+ });
41
+ });
@@ -151,6 +151,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
151
151
 
152
152
  test("enqueues consolidate when v2 is on and no checkpoint exists", () => {
153
153
  const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
154
+ writeBuffer(15);
154
155
 
155
156
  maybeEnqueueGraphMaintenanceJobs(config, Date.now());
156
157
 
@@ -177,6 +178,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
177
178
 
178
179
  test("enqueues consolidate again once the interval elapses", () => {
179
180
  const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
181
+ writeBuffer(15);
180
182
 
181
183
  const now = Date.now();
182
184
  // Stamp checkpoint to >1h ago.
@@ -193,6 +195,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
193
195
 
194
196
  test("respects a custom consolidation_interval_hours value", () => {
195
197
  const config = buildConfig({ v2Enabled: true, intervalHours: 6 });
198
+ writeBuffer(15);
196
199
 
197
200
  const now = Date.now();
198
201
  // 4h elapsed — under the configured 6h interval.
@@ -216,6 +219,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
216
219
 
217
220
  test("v1 maintenance entries are suppressed when v2 is active", () => {
218
221
  const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
222
+ writeBuffer(15);
219
223
 
220
224
  // No checkpoints set — every entry would be due if it were scheduled.
221
225
  deleteMemoryCheckpoint("graph_maintenance:decay:last_run");
@@ -426,3 +430,86 @@ describe("maybeEnqueueGraphMaintenanceJobs — buffer-size trigger", () => {
426
430
  expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
427
431
  });
428
432
  });
433
+
434
+ describe("maybeEnqueueGraphMaintenanceJobs — min buffer lines noop", () => {
435
+ test("skips scheduled consolidation when buffer is under 10 lines", () => {
436
+ // GIVEN v2 consolidation is enabled and the interval has elapsed
437
+ const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
438
+ const now = Date.now();
439
+ setMemoryCheckpoint(
440
+ CONSOLIDATE_CHECKPOINT_KEY,
441
+ String(now - 2 * 60 * 60 * 1000),
442
+ );
443
+ // AND the buffer has fewer than 10 lines
444
+ writeBuffer(5);
445
+
446
+ // WHEN the schedule runs
447
+ maybeEnqueueGraphMaintenanceJobs(config, now);
448
+
449
+ // THEN no consolidation job is enqueued
450
+ expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
451
+ // AND the checkpoint advances so the skip doesn't re-fire next tick
452
+ expect(getMemoryCheckpoint(CONSOLIDATE_CHECKPOINT_KEY)).toBe(String(now));
453
+ });
454
+
455
+ test("allows scheduled consolidation when buffer has exactly 10 lines", () => {
456
+ // GIVEN v2 consolidation is enabled and the interval has elapsed
457
+ const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
458
+ const now = Date.now();
459
+ setMemoryCheckpoint(
460
+ CONSOLIDATE_CHECKPOINT_KEY,
461
+ String(now - 2 * 60 * 60 * 1000),
462
+ );
463
+ // AND the buffer has exactly 10 lines
464
+ writeBuffer(10);
465
+
466
+ // WHEN the schedule runs
467
+ maybeEnqueueGraphMaintenanceJobs(config, now);
468
+
469
+ // THEN consolidation is enqueued
470
+ expect(countPendingJobs("memory_v2_consolidate")).toBe(1);
471
+ });
472
+
473
+ test("skips scheduled consolidation when buffer file is missing", () => {
474
+ // GIVEN v2 consolidation is enabled and the interval has elapsed
475
+ const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
476
+ const now = Date.now();
477
+ setMemoryCheckpoint(
478
+ CONSOLIDATE_CHECKPOINT_KEY,
479
+ String(now - 2 * 60 * 60 * 1000),
480
+ );
481
+ // AND no buffer file exists (0 lines)
482
+ removeBuffer();
483
+
484
+ // WHEN the schedule runs
485
+ maybeEnqueueGraphMaintenanceJobs(config, now);
486
+
487
+ // THEN no consolidation job is enqueued
488
+ expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
489
+ // AND the checkpoint advances
490
+ expect(getMemoryCheckpoint(CONSOLIDATE_CHECKPOINT_KEY)).toBe(String(now));
491
+ });
492
+
493
+ test("size-based trigger is independent of the min-lines noop", () => {
494
+ // GIVEN the time-based interval has elapsed but the buffer has fewer
495
+ // than 10 lines, while exceeding the configured maxBufferLines threshold
496
+ const config = buildConfig({
497
+ v2Enabled: true,
498
+ intervalHours: 1,
499
+ maxBufferLines: 5,
500
+ });
501
+ const now = Date.now();
502
+ setMemoryCheckpoint(
503
+ CONSOLIDATE_CHECKPOINT_KEY,
504
+ String(now - 2 * 60 * 60 * 1000),
505
+ );
506
+ writeBuffer(8);
507
+
508
+ // WHEN the schedule runs
509
+ maybeEnqueueGraphMaintenanceJobs(config, now);
510
+
511
+ // THEN consolidation is enqueued via the size trigger despite the
512
+ // time-based schedule being nooped (8 < 10 min lines, but 8 >= 5 max)
513
+ expect(countPendingJobs("memory_v2_consolidate")).toBe(1);
514
+ });
515
+ });