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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (490) hide show
  1. package/ARCHITECTURE.md +6 -6
  2. package/bun.lock +2 -2
  3. package/docs/activation-funnel-telemetry.md +310 -0
  4. package/examples/plugins/echo/README.md +61 -60
  5. package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
  6. package/examples/plugins/echo/hooks/stop.ts +16 -0
  7. package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
  8. package/examples/plugins/echo/package.json +1 -2
  9. package/examples/plugins/echo/src/emit.ts +19 -0
  10. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
  11. package/openapi.yaml +273 -121
  12. package/package.json +2 -2
  13. package/scripts/sync-llm-catalog.ts +2 -0
  14. package/src/__tests__/activation-early-marking.test.ts +120 -0
  15. package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
  16. package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
  17. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
  18. package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
  19. package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
  20. package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
  21. package/src/__tests__/agent-loop-thinking.test.ts +36 -20
  22. package/src/__tests__/agent-loop.test.ts +441 -96
  23. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
  24. package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
  25. package/src/__tests__/anthropic-provider.test.ts +23 -8
  26. package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
  27. package/src/__tests__/app-control-flow.test.ts +1 -1
  28. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  29. package/src/__tests__/app-executors.test.ts +132 -0
  30. package/src/__tests__/approval-cascade.test.ts +6 -5
  31. package/src/__tests__/approval-routes-http.test.ts +4 -1
  32. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  33. package/src/__tests__/build-persisted-content.test.ts +75 -1
  34. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  35. package/src/__tests__/channel-approvals.test.ts +1 -1
  36. package/src/__tests__/compaction-circuit.test.ts +258 -0
  37. package/src/__tests__/compaction-direct.test.ts +146 -0
  38. package/src/__tests__/compaction-events.test.ts +7 -7
  39. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  40. package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
  41. package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
  42. package/src/__tests__/context-overflow-reducer.test.ts +265 -125
  43. package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
  44. package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
  45. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
  46. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
  47. package/src/__tests__/conversation-agent-loop-overflow.test.ts +158 -234
  48. package/src/__tests__/conversation-agent-loop.test.ts +342 -623
  49. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  50. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
  51. package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
  52. package/src/__tests__/conversation-error.test.ts +15 -1
  53. package/src/__tests__/conversation-history-web-search.test.ts +6 -1
  54. package/src/__tests__/conversation-media-retry.test.ts +1 -1
  55. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
  56. package/src/__tests__/conversation-process-callsite.test.ts +15 -15
  57. package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
  58. package/src/__tests__/conversation-queue.test.ts +10 -10
  59. package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
  60. package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
  61. package/src/__tests__/conversation-slash-queue.test.ts +7 -5
  62. package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
  63. package/src/__tests__/conversation-speed-override.test.ts +11 -10
  64. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  65. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
  66. package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
  67. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
  68. package/src/__tests__/conversation-undo.test.ts +2 -2
  69. package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
  70. package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
  71. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
  72. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  73. package/src/__tests__/corrected-target.test.ts +93 -0
  74. package/src/__tests__/credential-execution-feature-gates.test.ts +5 -4
  75. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  76. package/src/__tests__/cu-unified-flow.test.ts +36 -0
  77. package/src/__tests__/db-acp-history.test.ts +101 -0
  78. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  79. package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
  80. package/src/__tests__/empty-response-hook.test.ts +1 -1
  81. package/src/__tests__/file-write-tool.test.ts +63 -0
  82. package/src/__tests__/gateway-only-guard.test.ts +17 -2
  83. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  84. package/src/__tests__/guardian-routing-invariants.test.ts +93 -4
  85. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  86. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  87. package/src/__tests__/heartbeat-service.test.ts +1 -0
  88. package/src/__tests__/history-repair-hook.test.ts +5 -1
  89. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  90. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  91. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  92. package/src/__tests__/inference-profile-reaper.test.ts +62 -0
  93. package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
  94. package/src/__tests__/injector-background-turn.test.ts +13 -23
  95. package/src/__tests__/injector-chain.test.ts +268 -44
  96. package/src/__tests__/injector-disk-pressure.test.ts +210 -52
  97. package/src/__tests__/injector-document-comments.test.ts +97 -114
  98. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  99. package/src/__tests__/injector-v3-suppression.test.ts +4 -4
  100. package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
  101. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  102. package/src/__tests__/llm-catalog-parity.test.ts +4 -0
  103. package/src/__tests__/llm-resolver.test.ts +73 -0
  104. package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
  105. package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
  106. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  107. package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
  108. package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
  109. package/src/__tests__/persist-unsendable-image.test.ts +97 -1
  110. package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
  111. package/src/__tests__/plugin-api-shim.test.ts +6 -9
  112. package/src/__tests__/plugin-bootstrap.test.ts +91 -81
  113. package/src/__tests__/plugin-registry.test.ts +3 -49
  114. package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
  115. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  116. package/src/__tests__/plugin-types.test.ts +0 -70
  117. package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
  118. package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
  119. package/src/__tests__/published-app-updater.test.ts +138 -0
  120. package/src/__tests__/reaction-persistence.test.ts +1 -1
  121. package/src/__tests__/retry-thinking-adaptive-only.test.ts +245 -0
  122. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  123. package/src/__tests__/server-history-render.test.ts +39 -0
  124. package/src/__tests__/skill-feature-flags-integration.test.ts +31 -0
  125. package/src/__tests__/steer-tool-repair.test.ts +1 -1
  126. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  127. package/src/__tests__/subagent-detail.test.ts +25 -7
  128. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  129. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  130. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  131. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  132. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  133. package/src/__tests__/title-generate-hook.test.ts +5 -1
  134. package/src/__tests__/tool-error-hook.test.ts +1 -1
  135. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
  136. package/src/__tests__/tool-start-timestamp.test.ts +218 -0
  137. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  138. package/src/__tests__/web-fetch.test.ts +45 -0
  139. package/src/acp/__tests__/agent-process.test.ts +161 -0
  140. package/src/acp/__tests__/client-handler.test.ts +40 -0
  141. package/src/acp/__tests__/helpers/acp-config-stub.ts +0 -2
  142. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  143. package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
  144. package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
  145. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  146. package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
  147. package/src/acp/agent-process.ts +61 -1
  148. package/src/acp/auto-install.test.ts +280 -0
  149. package/src/acp/auto-install.ts +232 -0
  150. package/src/acp/client-handler.ts +31 -0
  151. package/src/acp/prepare-agent-env.ts +80 -27
  152. package/src/acp/resolve-agent.test.ts +188 -28
  153. package/src/acp/resolve-agent.ts +119 -42
  154. package/src/acp/resume-hint.ts +23 -0
  155. package/src/acp/session-manager.ts +507 -73
  156. package/src/agent/compaction-circuit.ts +60 -102
  157. package/src/agent/loop.ts +403 -251
  158. package/src/api/events/tool-result.ts +9 -0
  159. package/src/api/events/tool-use-start.ts +7 -0
  160. package/src/api/responses/conversation-message.ts +72 -13
  161. package/src/api/responses/memory-v3-selection-log.ts +19 -10
  162. package/src/approvals/guardian-request-resolvers.ts +27 -1
  163. package/src/background-wake/next-wake.ts +1 -0
  164. package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
  165. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  166. package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
  167. package/src/cli/commands/memory-v3.ts +57 -199
  168. package/src/cli/commands/plugins.ts +43 -37
  169. package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
  170. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
  171. package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
  172. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
  173. package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
  174. package/src/cli/lib/install-from-github.ts +797 -60
  175. package/src/cli/lib/plugin-catalog-cache.ts +84 -0
  176. package/src/cli/lib/plugin-details.ts +416 -0
  177. package/src/cli/lib/plugin-marketplace.ts +215 -0
  178. package/src/cli/lib/search-plugins.ts +201 -29
  179. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  180. package/src/config/acp-defaults.test.ts +10 -0
  181. package/src/config/acp-defaults.ts +9 -3
  182. package/src/config/acp-schema.ts +1 -7
  183. package/src/config/bundled-skills/acp/SKILL.md +80 -41
  184. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  185. package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
  186. package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
  187. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  188. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  189. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  190. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  191. package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
  192. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  193. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  194. package/src/config/bundled-tool-registry.ts +4 -0
  195. package/src/config/call-site-defaults.ts +0 -3
  196. package/src/config/feature-flag-registry.json +10 -56
  197. package/src/config/llm-resolver.ts +39 -7
  198. package/src/config/loader.ts +5 -22
  199. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
  200. package/src/config/schemas/call-site-catalog.ts +0 -21
  201. package/src/config/schemas/heartbeat.ts +9 -0
  202. package/src/config/schemas/llm.ts +17 -3
  203. package/src/config/schemas/memory-v3.ts +58 -8
  204. package/src/config/seed-inference-profiles.ts +18 -0
  205. package/src/context/compactor.ts +22 -4
  206. package/src/context/post-turn-tool-result-truncation.ts +39 -1
  207. package/src/context/strip-injections.ts +8 -2
  208. package/src/daemon/conversation-agent-loop-handlers.ts +120 -42
  209. package/src/daemon/conversation-agent-loop.ts +308 -1297
  210. package/src/daemon/conversation-error.ts +31 -4
  211. package/src/daemon/conversation-history.ts +1 -1
  212. package/src/daemon/conversation-lifecycle.ts +11 -255
  213. package/src/daemon/conversation-media-retry.ts +19 -6
  214. package/src/daemon/conversation-messaging.ts +17 -0
  215. package/src/daemon/conversation-process.ts +22 -141
  216. package/src/daemon/conversation-queue-manager.ts +8 -0
  217. package/src/daemon/conversation-registry.ts +159 -0
  218. package/src/daemon/conversation-runtime-assembly.ts +361 -393
  219. package/src/daemon/conversation-store.ts +9 -90
  220. package/src/daemon/conversation-surfaces.ts +165 -11
  221. package/src/daemon/conversation-workspace.ts +17 -0
  222. package/src/daemon/conversation.ts +444 -62
  223. package/src/daemon/disk-pressure-policy.ts +0 -1
  224. package/src/daemon/external-plugins-bootstrap.ts +36 -46
  225. package/src/daemon/handlers/config-channels.ts +11 -2
  226. package/src/daemon/handlers/conversations.ts +3 -1
  227. package/src/daemon/handlers/shared.ts +9 -1
  228. package/src/daemon/handlers/skills.ts +4 -1
  229. package/src/daemon/host-app-control-proxy.ts +72 -57
  230. package/src/daemon/host-proxy-preactivation.ts +1 -3
  231. package/src/daemon/lifecycle.ts +21 -7
  232. package/src/daemon/persist-unsendable-image.ts +62 -25
  233. package/src/daemon/process-message.ts +1 -1
  234. package/src/daemon/server.ts +2 -0
  235. package/src/daemon/tool-side-effects.ts +15 -0
  236. package/src/daemon/wake-conversation-ops.ts +269 -0
  237. package/src/embedded/plugin-api.ts +2 -2
  238. package/src/export/__tests__/transcript-formatter.test.ts +5 -0
  239. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  240. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  241. package/src/heartbeat/heartbeat-service.ts +26 -0
  242. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  243. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  244. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  245. package/src/ipc/skill-routes/memory.ts +4 -2
  246. package/src/memory/__tests__/activation-session-store.test.ts +41 -0
  247. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
  248. package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
  249. package/src/memory/activation-session-store.ts +43 -0
  250. package/src/memory/conversation-crud.ts +29 -19
  251. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  252. package/src/memory/db-init.ts +6 -0
  253. package/src/memory/embedding-backend.ts +5 -22
  254. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  255. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
  256. package/src/memory/jobs-worker.ts +25 -1
  257. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  258. package/src/memory/migrations/273-onboarding-events-funnel-columns.ts +46 -0
  259. package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
  260. package/src/memory/migrations/index.ts +3 -0
  261. package/src/memory/onboarding-events-store.ts +66 -18
  262. package/src/memory/schema/acp.ts +4 -0
  263. package/src/memory/schema/infrastructure.ts +13 -0
  264. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
  265. package/src/memory/v2/__tests__/page-store.test.ts +22 -0
  266. package/src/memory/v2/consolidation-job.ts +2 -63
  267. package/src/memory/v2/types.ts +5 -0
  268. package/src/messaging/providers/telegram-bot/api.ts +14 -5
  269. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  270. package/src/notifications/access-request-copy.ts +298 -0
  271. package/src/notifications/adapters/slack.ts +3 -3
  272. package/src/notifications/adapters/telegram.ts +9 -2
  273. package/src/notifications/copy-composer.ts +49 -267
  274. package/src/notifications/decision-engine.ts +16 -35
  275. package/src/notifications/home-feed-side-effect.ts +1 -6
  276. package/src/plugin-api/constants.ts +4 -0
  277. package/src/plugin-api/index.ts +40 -11
  278. package/src/plugin-api/types.ts +108 -0
  279. package/src/plugins/defaults/compaction/compact.ts +106 -0
  280. package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
  281. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  282. package/src/plugins/defaults/compaction/manager-store.ts +57 -0
  283. package/src/plugins/defaults/compaction/package.json +1 -2
  284. package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
  285. package/src/plugins/defaults/empty-response/package.json +0 -1
  286. package/src/plugins/defaults/history-repair/package.json +0 -1
  287. package/src/plugins/defaults/index.ts +156 -26
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
  289. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
  290. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
  291. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
  292. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  293. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
  297. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
  298. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
  299. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
  300. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
  301. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
  302. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
  303. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
  304. package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
  305. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
  306. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
  307. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
  308. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
  309. package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
  310. package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
  311. package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
  312. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  313. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  314. package/src/plugins/defaults/memory-v3-shadow/injector.ts +76 -0
  315. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
  316. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
  317. package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
  318. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
  319. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
  320. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
  321. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
  322. package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
  323. package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
  324. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
  325. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
  326. package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
  327. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -1
  328. package/src/plugins/defaults/title-generate/package.json +0 -1
  329. package/src/plugins/defaults/tool-error/package.json +0 -1
  330. package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
  331. package/src/plugins/external-api.ts +12 -2
  332. package/src/plugins/pipeline.ts +6 -293
  333. package/src/plugins/registry.ts +9 -37
  334. package/src/plugins/types.ts +76 -381
  335. package/src/plugins/user-loader.ts +30 -127
  336. package/src/prompts/__tests__/system-prompt.test.ts +6 -0
  337. package/src/prompts/system-prompt.ts +61 -10
  338. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
  339. package/src/providers/anthropic/client.ts +9 -10
  340. package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
  341. package/src/providers/inference/logit-bias.ts +55 -0
  342. package/src/providers/model-catalog.ts +57 -0
  343. package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
  344. package/src/providers/openai/chat-completions-provider.ts +44 -1
  345. package/src/providers/retry.ts +57 -7
  346. package/src/providers/types.ts +6 -0
  347. package/src/runtime/__tests__/agent-wake.test.ts +555 -691
  348. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  349. package/src/runtime/agent-wake.ts +108 -209
  350. package/src/runtime/assistant-event-hub.ts +1 -1
  351. package/src/runtime/channel-approvals.ts +1 -1
  352. package/src/runtime/interactive-ui.ts +1 -1
  353. package/src/runtime/routes/__tests__/acp-routes.test.ts +466 -55
  354. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  355. package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
  356. package/src/runtime/routes/__tests__/stt-routes.test.ts +112 -0
  357. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  358. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  359. package/src/runtime/routes/acp-routes.test.ts +185 -32
  360. package/src/runtime/routes/acp-routes.ts +328 -30
  361. package/src/runtime/routes/app-management-routes.ts +3 -0
  362. package/src/runtime/routes/approval-routes.ts +1 -1
  363. package/src/runtime/routes/browser-routes.ts +1 -1
  364. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  365. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  366. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  367. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  368. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  369. package/src/runtime/routes/conversation-routes.ts +30 -2
  370. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  371. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  372. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  373. package/src/runtime/routes/host-cu-routes.ts +1 -1
  374. package/src/runtime/routes/identity-routes.ts +18 -3
  375. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  376. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  377. package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
  378. package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
  379. package/src/runtime/routes/memory-v3-routes.ts +65 -305
  380. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
  381. package/src/runtime/routes/playground/helpers.ts +1 -1
  382. package/src/runtime/routes/plugins-routes.ts +337 -35
  383. package/src/runtime/routes/stt-routes.ts +45 -12
  384. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  385. package/src/runtime/routes/work-items-routes.ts +2 -4
  386. package/src/runtime/routes/workspace-routes.ts +50 -15
  387. package/src/runtime/services/conversation-serializer.ts +1 -1
  388. package/src/runtime/verification-outbound-actions.ts +147 -2
  389. package/src/runtime/verification-templates.ts +29 -3
  390. package/src/services/published-app-updater.ts +30 -8
  391. package/src/signals/cancel.ts +2 -4
  392. package/src/subagent/manager.ts +21 -5
  393. package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
  394. package/src/telemetry/activation-funnel.ts +167 -0
  395. package/src/telemetry/types.ts +13 -0
  396. package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
  397. package/src/telemetry/usage-telemetry-reporter.ts +26 -1
  398. package/src/tools/acp/context.ts +20 -0
  399. package/src/tools/acp/list-agents.test.ts +9 -19
  400. package/src/tools/acp/list-agents.ts +3 -15
  401. package/src/tools/acp/spawn.test.ts +173 -202
  402. package/src/tools/acp/spawn.ts +37 -172
  403. package/src/tools/acp/steer.test.ts +105 -8
  404. package/src/tools/acp/steer.ts +48 -17
  405. package/src/tools/apps/executors.ts +166 -50
  406. package/src/tools/browser/browser-execution.ts +12 -2
  407. package/src/tools/filesystem/write.ts +34 -0
  408. package/src/tools/network/web-fetch.ts +65 -24
  409. package/src/tools/skills/load.ts +1 -1
  410. package/src/tools/subagent/spawn.ts +2 -4
  411. package/src/tools/ui-surface/definitions.ts +32 -5
  412. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  413. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
  414. package/docs/plugins.md +0 -832
  415. package/examples/plugins/echo/register.ts +0 -143
  416. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  417. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  418. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  419. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
  420. package/src/__tests__/pipeline-runner.test.ts +0 -554
  421. package/src/daemon/wake-target-adapter.ts +0 -253
  422. package/src/memory/v3/__tests__/assign.test.ts +0 -242
  423. package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
  424. package/src/memory/v3/__tests__/core.test.ts +0 -39
  425. package/src/memory/v3/__tests__/health.test.ts +0 -219
  426. package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
  427. package/src/memory/v3/__tests__/needle.test.ts +0 -107
  428. package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
  429. package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
  430. package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
  431. package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
  432. package/src/memory/v3/__tests__/router.test.ts +0 -332
  433. package/src/memory/v3/__tests__/selector.test.ts +0 -470
  434. package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
  435. package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
  436. package/src/memory/v3/__tests__/tree.test.ts +0 -192
  437. package/src/memory/v3/assign.ts +0 -268
  438. package/src/memory/v3/capabilities.ts +0 -124
  439. package/src/memory/v3/core.ts +0 -26
  440. package/src/memory/v3/health.ts +0 -0
  441. package/src/memory/v3/maintain-job.ts +0 -314
  442. package/src/memory/v3/needle.ts +0 -115
  443. package/src/memory/v3/orchestrate.ts +0 -126
  444. package/src/memory/v3/page-content.ts +0 -34
  445. package/src/memory/v3/provider-blocks.ts +0 -26
  446. package/src/memory/v3/reconcile.ts +0 -523
  447. package/src/memory/v3/router.ts +0 -190
  448. package/src/memory/v3/selection-log-store.ts +0 -84
  449. package/src/memory/v3/selector.ts +0 -226
  450. package/src/memory/v3/shadow-plugin.ts +0 -411
  451. package/src/memory/v3/snapshot.ts +0 -209
  452. package/src/memory/v3/tree.ts +0 -174
  453. package/src/memory/v3/types.ts +0 -59
  454. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  455. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  456. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  457. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  458. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  459. package/src/plugins/defaults/compaction/register.ts +0 -35
  460. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  461. package/src/plugins/defaults/empty-response/register.ts +0 -23
  462. package/src/plugins/defaults/history-repair/register.ts +0 -24
  463. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
  464. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  465. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  466. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  467. package/src/plugins/defaults/title-generate/register.ts +0 -35
  468. package/src/plugins/defaults/tool-error/register.ts +0 -23
  469. package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
  470. package/src/proactive-artifact/aux-message-injector.ts +0 -97
  471. package/src/proactive-artifact/decision.test.ts +0 -226
  472. package/src/proactive-artifact/decision.ts +0 -165
  473. package/src/proactive-artifact/index.ts +0 -7
  474. package/src/proactive-artifact/job.test.ts +0 -962
  475. package/src/proactive-artifact/job.ts +0 -372
  476. package/src/proactive-artifact/message-copy.ts +0 -58
  477. package/src/proactive-artifact/trigger-state.test.ts +0 -286
  478. package/src/proactive-artifact/trigger-state.ts +0 -123
  479. package/src/util/map-limit.ts +0 -27
  480. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  481. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  482. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  483. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  484. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  485. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  486. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  487. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  488. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  489. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  490. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Activation funnel vocabulary — the single source of truth for the
3
+ * activation-rail telemetry funnel.
4
+ *
5
+ * The activation rail emits milestone ("funnel") events into the existing
6
+ * onboarding telemetry substrate (`type: "onboarding"`). This module defines
7
+ * the step vocabulary (step_name → step_index), the funnel version, the
8
+ * cohort arm tag, and the deterministic `daemon_event_id` used for dbt-side
9
+ * dedup. Downstream PRs (store, reporter, emit handler, turn hook) import
10
+ * from here so the vocabulary is never duplicated.
11
+ */
12
+
13
+ /** Funnel version stamped on every activation event (per JARVIS-1033). */
14
+ export const ACTIVATION_FUNNEL_VERSION = "activation_v1_2026_06";
15
+
16
+ /**
17
+ * Filename of the bootstrap template that drives the activation rail. The web
18
+ * prechat-context sets this as `onboardingContext.bootstrapTemplate` (mirrored
19
+ * there as `ACTIVATION_RAIL_BOOTSTRAP_TEMPLATE`); the daemon recognizes it to
20
+ * mark the conversation as an activation session. Single source of truth on the
21
+ * daemon side so the literal isn't duplicated across modules.
22
+ */
23
+ export const ACTIVATION_RAIL_BOOTSTRAP_TEMPLATE =
24
+ "BOOTSTRAP-ACTIVATION-RAIL.md";
25
+
26
+ /**
27
+ * Cohort arm tag for Stream-A emission. The daemon only runs the rail for
28
+ * treatment users, so activation events are tagged with the treatment arm
29
+ * name ("variant-a"), mirroring `pre-chat-onboarding-experiment-2026-06-06`.
30
+ * Stream B later threads the assigned arm in place of this constant.
31
+ */
32
+ export const ACTIVATION_AB_VARIANT = "variant-a";
33
+
34
+ /**
35
+ * Activation funnel steps keyed by short id. `stepName` is the wire value
36
+ * sent to telemetry; `stepIndex` is the 1-based ordinal position. This is
37
+ * the single source of truth for the step vocabulary.
38
+ */
39
+ export const ACTIVATION_STEPS = {
40
+ moment1: { stepName: "activation_moment_1_complete", stepIndex: 1 },
41
+ moment2: { stepName: "activation_moment_2_complete", stepIndex: 2 },
42
+ moment3: { stepName: "activation_moment_3_complete", stepIndex: 3 },
43
+ firstWowExecuted: { stepName: "activation_first_wow_executed", stepIndex: 4 },
44
+ firstWowInteracted: {
45
+ stepName: "activation_first_wow_interacted",
46
+ stepIndex: 5,
47
+ },
48
+ } as const;
49
+
50
+ /** Union of valid activation step names. */
51
+ export type ActivationStepName =
52
+ (typeof ACTIVATION_STEPS)[keyof typeof ACTIVATION_STEPS]["stepName"];
53
+
54
+ /** step_name → step_index, derived once from the vocabulary. */
55
+ const STEP_INDEX_BY_NAME = new Map<ActivationStepName, number>(
56
+ Object.values(ACTIVATION_STEPS).map((step) => [
57
+ step.stepName,
58
+ step.stepIndex,
59
+ ]),
60
+ );
61
+
62
+ /** Type guard: is `value` one of the known activation step names? */
63
+ export function isActivationStepName(
64
+ value: string,
65
+ ): value is ActivationStepName {
66
+ return STEP_INDEX_BY_NAME.has(value as ActivationStepName);
67
+ }
68
+
69
+ /**
70
+ * Model-facing activation-moment tokens. These are the values the model passes
71
+ * as the optional `ui_show` `activation_moment` tag; the daemon maps each token
72
+ * to its `ACTIVATION_STEPS[...].stepName` and records the milestone when the
73
+ * tagged surface is committed. Kept short/stable so the schema enum is friendly
74
+ * for the model — the canonical wire vocabulary stays `ActivationStepName`.
75
+ */
76
+ export type ActivationMomentParam =
77
+ | "moment_1"
78
+ | "moment_2"
79
+ | "moment_3"
80
+ | "first_wow_executed"
81
+ | "first_wow_interacted";
82
+
83
+ /**
84
+ * `ActivationMomentParam` token → canonical `ActivationStepName`, derived from
85
+ * `ACTIVATION_STEPS` so the vocabulary stays single-sourced. The token order
86
+ * mirrors `ACTIVATION_STEPS` (indices 1–5).
87
+ */
88
+ const STEP_NAME_BY_MOMENT_PARAM = {
89
+ moment_1: ACTIVATION_STEPS.moment1.stepName,
90
+ moment_2: ACTIVATION_STEPS.moment2.stepName,
91
+ moment_3: ACTIVATION_STEPS.moment3.stepName,
92
+ first_wow_executed: ACTIVATION_STEPS.firstWowExecuted.stepName,
93
+ first_wow_interacted: ACTIVATION_STEPS.firstWowInteracted.stepName,
94
+ } as const satisfies Record<ActivationMomentParam, ActivationStepName>;
95
+
96
+ /** The five valid model-facing moment tokens (e.g. for a schema enum). */
97
+ export const ACTIVATION_MOMENT_PARAMS = Object.keys(
98
+ STEP_NAME_BY_MOMENT_PARAM,
99
+ ) as ActivationMomentParam[];
100
+
101
+ /** Type guard: is `value` one of the model-facing activation-moment tokens? */
102
+ export function isActivationMomentParam(
103
+ value: string,
104
+ ): value is ActivationMomentParam {
105
+ return Object.prototype.hasOwnProperty.call(STEP_NAME_BY_MOMENT_PARAM, value);
106
+ }
107
+
108
+ /** Map a model-facing moment token to its canonical activation step name. */
109
+ export function activationStepNameForMomentParam(
110
+ param: ActivationMomentParam,
111
+ ): ActivationStepName {
112
+ return STEP_NAME_BY_MOMENT_PARAM[param];
113
+ }
114
+
115
+ /**
116
+ * When each tagged moment is emitted relative to its `ui_show` surface:
117
+ * - `"show"` — at surface render time. `first_wow_executed` is an *execution*
118
+ * signal: the rail's result/`work_result` surface is display-only and may
119
+ * receive no user commit, so a commit-time emit would never fire (and if the
120
+ * card has an action, deferring to the click would conflate "executed" with
121
+ * "interacted" and corrupt funnel timing).
122
+ * - `"commit"` — on user commit/interaction. The other four moments represent
123
+ * resolving an intake, picking an offer/task, or clicking a result action —
124
+ * all genuine commit events.
125
+ */
126
+ export const ACTIVATION_MOMENT_EMIT_AT = {
127
+ moment_1: "commit",
128
+ moment_2: "commit",
129
+ moment_3: "commit",
130
+ first_wow_executed: "show",
131
+ first_wow_interacted: "commit",
132
+ } as const satisfies Record<ActivationMomentParam, "show" | "commit">;
133
+
134
+ /** True when the moment records at surface-render time rather than on commit. */
135
+ export function activationMomentEmitsAtShow(
136
+ param: ActivationMomentParam,
137
+ ): boolean {
138
+ return ACTIVATION_MOMENT_EMIT_AT[param] === "show";
139
+ }
140
+
141
+ /** Look up the 1-based funnel index for a step name. */
142
+ export function activationStepIndex(stepName: ActivationStepName): number {
143
+ // Non-null: ActivationStepName is derived from ACTIVATION_STEPS, so the map
144
+ // always contains the key.
145
+ return STEP_INDEX_BY_NAME.get(stepName)!;
146
+ }
147
+
148
+ /**
149
+ * Deterministic `daemon_event_id` for an activation event. Keying on
150
+ * `${funnel_version}:${sessionId}:${stepName}` lets the existing dbt
151
+ * dedup (keyed on `daemon_event_id`, earliest-wins) collapse a moment that
152
+ * fires more than once into a single row.
153
+ *
154
+ * `funnelVersion` defaults to the current `ACTIVATION_FUNNEL_VERSION` but
155
+ * callers that have a per-row stored version (e.g. the telemetry reporter
156
+ * flushing rows recorded under an older version) MUST pass the row's own
157
+ * `funnel_version` so the id stays stable across a version bump — otherwise
158
+ * queued/offline v1 rows would be keyed with the new binary's version and
159
+ * stop collapsing with already-ingested v1 rows from the same session.
160
+ */
161
+ export function buildActivationDaemonEventId(
162
+ sessionId: string,
163
+ stepName: ActivationStepName,
164
+ funnelVersion: string = ACTIVATION_FUNNEL_VERSION,
165
+ ): string {
166
+ return `${funnelVersion}:${sessionId}:${stepName}`;
167
+ }
@@ -183,6 +183,19 @@ export interface OnboardingTelemetryEvent extends TelemetryEventBase {
183
183
  google_connected?: boolean;
184
184
  google_scopes?: string[];
185
185
  ab_variant?: string;
186
+ /**
187
+ * Activation-funnel fields (mirror the web funnel shape and the platform
188
+ * serializer). The platform accepts an onboarding event via either the
189
+ * legacy `screen` path or the all-funnel-fields path (`session_id` +
190
+ * `step_name` + `step_index` + `completed_at` + `funnel_version` +
191
+ * `ab_variant`).
192
+ */
193
+ session_id?: string;
194
+ step_name?: string;
195
+ step_index?: number;
196
+ completed_at?: string;
197
+ funnel_version?: string;
198
+ user_id?: string;
186
199
  }
187
200
 
188
201
  /**
@@ -131,6 +131,11 @@ const mockQueryUnreportedOnboardingEvents = mock(
131
131
  googleScopesJson: string | null;
132
132
  priorAssistantsJson: string | null;
133
133
  abVariant: string | null;
134
+ sessionId: string | null;
135
+ stepName: string | null;
136
+ stepIndex: number | null;
137
+ completedAt: string | null;
138
+ funnelVersion: string | null;
134
139
  }[],
135
140
  );
136
141
 
@@ -152,6 +157,10 @@ import { getDb } from "../memory/db-connection.js";
152
157
  import { initializeDb } from "../memory/db-init.js";
153
158
  import { authFallbackEvents } from "../memory/schema.js";
154
159
  import type { UsageEvent } from "../usage/types.js";
160
+ import {
161
+ ACTIVATION_FUNNEL_VERSION,
162
+ buildActivationDaemonEventId,
163
+ } from "./activation-funnel.js";
155
164
  import { UsageTelemetryReporter } from "./usage-telemetry-reporter.js";
156
165
 
157
166
  initializeDb();
@@ -201,6 +210,34 @@ function makeUsageEvent(
201
210
  };
202
211
  }
203
212
 
213
+ type OnboardingEventFixture = ReturnType<
214
+ typeof mockQueryUnreportedOnboardingEvents
215
+ >[number];
216
+
217
+ function makeOnboardingEvent(
218
+ overrides: Partial<OnboardingEventFixture> = {},
219
+ ): OnboardingEventFixture {
220
+ eventIdCounter += 1;
221
+ return {
222
+ id: `onb-${eventIdCounter}`,
223
+ createdAt: 1700000000000 + eventIdCounter * 1000,
224
+ screen: "tools",
225
+ toolsJson: null,
226
+ tasksJson: null,
227
+ tone: null,
228
+ googleConnected: null,
229
+ googleScopesJson: null,
230
+ priorAssistantsJson: null,
231
+ abVariant: null,
232
+ sessionId: null,
233
+ stepName: null,
234
+ stepIndex: null,
235
+ completedAt: null,
236
+ funnelVersion: null,
237
+ ...overrides,
238
+ };
239
+ }
240
+
204
241
  const originalFetch = globalThis.fetch;
205
242
  let mockFetch: ReturnType<typeof mock>;
206
243
 
@@ -1184,4 +1221,121 @@ describe("UsageTelemetryReporter", () => {
1184
1221
  expect(idCalls.length).toBeGreaterThanOrEqual(1);
1185
1222
  expect(idCalls[idCalls.length - 1][1]).toBe(lastRow.id);
1186
1223
  });
1224
+
1225
+ // -------------------------------------------------------------------------
1226
+ // Onboarding / activation funnel events
1227
+ // -------------------------------------------------------------------------
1228
+
1229
+ test("activation onboarding row serializes funnel fields + deterministic daemon_event_id", async () => {
1230
+ mockQueryUnreportedUsageEvents.mockReturnValue([]);
1231
+ const sessionId = "sess-activation-1";
1232
+ const stepName = "activation_moment_1_complete";
1233
+ mockQueryUnreportedOnboardingEvents.mockReturnValue([
1234
+ makeOnboardingEvent({
1235
+ id: "onb-activation-1",
1236
+ screen: stepName,
1237
+ abVariant: "variant-a",
1238
+ sessionId,
1239
+ stepName,
1240
+ stepIndex: 1,
1241
+ completedAt: "2026-06-06T00:00:00.000Z",
1242
+ funnelVersion: ACTIVATION_FUNNEL_VERSION,
1243
+ }),
1244
+ ]);
1245
+ mockFetch.mockImplementation(() =>
1246
+ Promise.resolve(new Response('{"accepted":1}', { status: 200 })),
1247
+ );
1248
+
1249
+ const reporter = new UsageTelemetryReporter();
1250
+ await reporter.flush();
1251
+
1252
+ expect(mockFetch).toHaveBeenCalledTimes(1);
1253
+ const body = JSON.parse(
1254
+ (mockFetch.mock.calls[0] as [string, RequestInit])[1].body as string,
1255
+ );
1256
+ expect(body.events.length).toBe(1);
1257
+ expect(body.events[0]).toMatchObject({
1258
+ type: "onboarding",
1259
+ session_id: sessionId,
1260
+ step_name: stepName,
1261
+ step_index: 1,
1262
+ completed_at: "2026-06-06T00:00:00.000Z",
1263
+ funnel_version: "activation_v1_2026_06",
1264
+ daemon_event_id: buildActivationDaemonEventId(sessionId, stepName),
1265
+ });
1266
+ });
1267
+
1268
+ test("activation daemon_event_id is keyed on the row's stored funnel_version, not the binary constant", async () => {
1269
+ mockQueryUnreportedUsageEvents.mockReturnValue([]);
1270
+ const sessionId = "sess-activation-old";
1271
+ const stepName = "activation_moment_1_complete";
1272
+ // A row recorded under an OLDER funnel version, queued across an upgrade.
1273
+ const oldVersion = "activation_v0_2026_05";
1274
+ expect(oldVersion).not.toBe(ACTIVATION_FUNNEL_VERSION);
1275
+ mockQueryUnreportedOnboardingEvents.mockReturnValue([
1276
+ makeOnboardingEvent({
1277
+ id: "onb-activation-old",
1278
+ screen: stepName,
1279
+ abVariant: "variant-a",
1280
+ sessionId,
1281
+ stepName,
1282
+ stepIndex: 1,
1283
+ completedAt: "2026-05-01T00:00:00.000Z",
1284
+ funnelVersion: oldVersion,
1285
+ }),
1286
+ ]);
1287
+ mockFetch.mockImplementation(() =>
1288
+ Promise.resolve(new Response('{"accepted":1}', { status: 200 })),
1289
+ );
1290
+
1291
+ const reporter = new UsageTelemetryReporter();
1292
+ await reporter.flush();
1293
+
1294
+ const body = JSON.parse(
1295
+ (mockFetch.mock.calls[0] as [string, RequestInit])[1].body as string,
1296
+ );
1297
+ expect(body.events[0]).toMatchObject({
1298
+ funnel_version: oldVersion,
1299
+ daemon_event_id: buildActivationDaemonEventId(
1300
+ sessionId,
1301
+ stepName,
1302
+ oldVersion,
1303
+ ),
1304
+ });
1305
+ // Guard: the id must carry the row's version, not the binary constant.
1306
+ expect(body.events[0].daemon_event_id).toBe(
1307
+ `${oldVersion}:${sessionId}:${stepName}`,
1308
+ );
1309
+ });
1310
+
1311
+ test("legacy onboarding row keeps daemon_event_id: e.id and omits funnel fields", async () => {
1312
+ mockQueryUnreportedUsageEvents.mockReturnValue([]);
1313
+ mockQueryUnreportedOnboardingEvents.mockReturnValue([
1314
+ makeOnboardingEvent({
1315
+ id: "onb-legacy-1",
1316
+ screen: "tools",
1317
+ toolsJson: JSON.stringify(["calendar"]),
1318
+ }),
1319
+ ]);
1320
+ mockFetch.mockImplementation(() =>
1321
+ Promise.resolve(new Response('{"accepted":1}', { status: 200 })),
1322
+ );
1323
+
1324
+ const reporter = new UsageTelemetryReporter();
1325
+ await reporter.flush();
1326
+
1327
+ expect(mockFetch).toHaveBeenCalledTimes(1);
1328
+ const body = JSON.parse(
1329
+ (mockFetch.mock.calls[0] as [string, RequestInit])[1].body as string,
1330
+ );
1331
+ expect(body.events.length).toBe(1);
1332
+ const e = body.events[0];
1333
+ expect(e.type).toBe("onboarding");
1334
+ expect(e.daemon_event_id).toBe("onb-legacy-1");
1335
+ expect(e.session_id).toBeUndefined();
1336
+ expect(e.step_name).toBeUndefined();
1337
+ expect(e.step_index).toBeUndefined();
1338
+ expect(e.completed_at).toBeUndefined();
1339
+ expect(e.funnel_version).toBeUndefined();
1340
+ });
1187
1341
  });
@@ -28,6 +28,10 @@ import { VellumPlatformClient } from "../platform/client.js";
28
28
  import { getDeviceId } from "../util/device-id.js";
29
29
  import { getLogger } from "../util/logger.js";
30
30
  import { APP_VERSION } from "../version.js";
31
+ import {
32
+ type ActivationStepName,
33
+ buildActivationDaemonEventId,
34
+ } from "./activation-funnel.js";
31
35
  import type { TelemetryEvent, TurnTelemetryClientInfo } from "./types.js";
32
36
 
33
37
  const log = getLogger("usage-telemetry");
@@ -337,7 +341,22 @@ export class UsageTelemetryReporter {
337
341
  ...onboardingEvents.map(
338
342
  (e): TelemetryEvent => ({
339
343
  type: "onboarding",
340
- daemon_event_id: e.id,
344
+ // Wire-only override for activation rows: a deterministic id keyed
345
+ // on funnel_version/session/step lets dbt collapse a moment that
346
+ // fires more than once. Key on the ROW's stored `funnelVersion`
347
+ // (not the binary's current constant) so rows recorded under an
348
+ // older version — flushed offline or after an upgrade — keep a
349
+ // stable id and still collapse with already-ingested rows. The
350
+ // SQLite watermark cursor still uses `e.id`/`e.createdAt`, so this
351
+ // override is checkpoint-safe.
352
+ daemon_event_id:
353
+ e.sessionId && e.stepName && e.funnelVersion
354
+ ? buildActivationDaemonEventId(
355
+ e.sessionId,
356
+ e.stepName as ActivationStepName,
357
+ e.funnelVersion,
358
+ )
359
+ : e.id,
341
360
  recorded_at: e.createdAt,
342
361
  screen: e.screen,
343
362
  ...(e.toolsJson ? { tools: JSON.parse(e.toolsJson) } : {}),
@@ -350,6 +369,12 @@ export class UsageTelemetryReporter {
350
369
  ? { google_scopes: JSON.parse(e.googleScopesJson) }
351
370
  : {}),
352
371
  ...(e.abVariant ? { ab_variant: e.abVariant } : {}),
372
+ // Activation funnel fields — only present on activation rows.
373
+ ...(e.sessionId ? { session_id: e.sessionId } : {}),
374
+ ...(e.stepName ? { step_name: e.stepName } : {}),
375
+ ...(e.stepIndex != null ? { step_index: e.stepIndex } : {}),
376
+ ...(e.completedAt ? { completed_at: e.completedAt } : {}),
377
+ ...(e.funnelVersion ? { funnel_version: e.funnelVersion } : {}),
353
378
  // Onboarding events fall back to the envelope `assistant_version`
354
379
  // — same upload-time attribution risk as before this PR. Adding
355
380
  // the record-time column to `onboarding_events` (#30733) is a
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Shared ToolContext accessors for the ACP tools.
3
+ */
4
+
5
+ import type { ServerMessage } from "../../daemon/message-protocol.js";
6
+ import type { ToolContext } from "../types.js";
7
+
8
+ /**
9
+ * Narrows `ToolContext.sendToClient` to the `ServerMessage` sender the ACP
10
+ * session manager expects. ToolContext types the callback against an
11
+ * index-signature message shape (`{ type: string; [key: string]: unknown }`)
12
+ * that ServerMessage's union members don't structurally satisfy, so the
13
+ * cast lives here once instead of being duplicated at every ACP tool call
14
+ * site.
15
+ */
16
+ export function getSendToClient(
17
+ context: ToolContext,
18
+ ): ((msg: ServerMessage) => void) | undefined {
19
+ return context.sendToClient as ((msg: ServerMessage) => void) | undefined;
20
+ }
@@ -33,31 +33,17 @@ beforeEach(() => {
33
33
  // ---------------------------------------------------------------------------
34
34
 
35
35
  describe("executeAcpListAgents", () => {
36
- test("returns disabled hint when ACP is disabled", async () => {
37
- config.setConfig({ enabled: false });
38
-
39
- const result = await executeAcpListAgents({}, makeContext());
40
-
41
- expect(result.isError).toBe(false);
42
- const parsed = JSON.parse(result.content as string);
43
- expect(parsed.enabled).toBe(false);
44
- // Pulls from the shared ACP_DISABLED_HINT constant exported by
45
- // resolve-agent.ts. The exact wording is checked in resolve-agent.test.ts.
46
- expect(parsed.hint).toContain("acp.enabled");
47
- expect(parsed.hint).toContain("config.json");
48
- });
49
-
50
- test("enabled, no user config: both defaults present with source 'default' and available based on Bun.which", async () => {
36
+ test("no user config: all defaults present with source 'default' and available based on Bun.which", async () => {
51
37
  config.setConfig({ agents: {} });
52
38
 
53
39
  const result = await executeAcpListAgents({}, makeContext());
54
40
 
55
41
  expect(result.isError).toBe(false);
56
42
  const parsed = JSON.parse(result.content as string);
57
- expect(parsed.enabled).toBe(true);
58
43
  expect(parsed.agents.map((a: { id: string }) => a.id)).toEqual([
59
44
  "claude",
60
45
  "codex",
46
+ "gemini",
61
47
  ]);
62
48
  for (const entry of parsed.agents) {
63
49
  expect(entry.source).toBe("default");
@@ -67,7 +53,7 @@ describe("executeAcpListAgents", () => {
67
53
  }
68
54
  });
69
55
 
70
- test("enabled, user overrides claude: claude has source 'config' and the user's command", async () => {
56
+ test("user overrides claude: claude has source 'config' and the user's command", async () => {
71
57
  config.setConfig({
72
58
  agents: {
73
59
  claude: {
@@ -82,7 +68,6 @@ describe("executeAcpListAgents", () => {
82
68
 
83
69
  expect(result.isError).toBe(false);
84
70
  const parsed = JSON.parse(result.content as string);
85
- expect(parsed.enabled).toBe(true);
86
71
 
87
72
  const claude = parsed.agents.find((a: { id: string }) => a.id === "claude");
88
73
  expect(claude.source).toBe("config");
@@ -106,7 +91,12 @@ describe("executeAcpListAgents", () => {
106
91
  const codex = parsed.agents.find((a: { id: string }) => a.id === "codex");
107
92
  expect(codex.available).toBe(false);
108
93
  expect(codex.unavailableReason).toBe("'codex-acp' is not on PATH");
109
- expect(codex.setupHint).toBe("npm i -g @zed-industries/codex-acp");
94
+ expect(codex.setupHint).toBe("bun add -g @zed-industries/codex-acp");
95
+
96
+ const gemini = parsed.agents.find((a: { id: string }) => a.id === "gemini");
97
+ expect(gemini.available).toBe(false);
98
+ expect(gemini.unavailableReason).toBe("'gemini' is not on PATH");
99
+ expect(gemini.setupHint).toBe("bun add -g @google/gemini-cli");
110
100
 
111
101
  const claude = parsed.agents.find((a: { id: string }) => a.id === "claude");
112
102
  expect(claude.available).toBe(true);
@@ -1,31 +1,19 @@
1
- import { ACP_DISABLED_HINT, listAcpAgents } from "../../acp/resolve-agent.js";
1
+ import { listAcpAgents } from "../../acp/resolve-agent.js";
2
2
  import type { ToolContext, ToolExecutionResult } from "../types.js";
3
3
 
4
4
  /**
5
5
  * Lists ACP coding agents available to spawn (configured + bundled defaults),
6
6
  * marking each with its source (`config` vs `default`), whether the agent's
7
7
  * binary is on PATH, and an install hint when missing.
8
- *
9
- * When `acp.enabled: false`, returns a single hint instructing the user to
10
- * enable ACP — no agent list is surfaced because none can run.
11
8
  */
12
9
  export async function executeAcpListAgents(
13
10
  _input: Record<string, unknown>,
14
11
  _context: ToolContext,
15
12
  ): Promise<ToolExecutionResult> {
16
- const { enabled, agents } = listAcpAgents();
17
- if (!enabled) {
18
- return {
19
- content: JSON.stringify({
20
- enabled: false,
21
- hint: ACP_DISABLED_HINT,
22
- }),
23
- isError: false,
24
- };
25
- }
13
+ const { agents } = listAcpAgents();
26
14
 
27
15
  return {
28
- content: JSON.stringify({ enabled, agents }),
16
+ content: JSON.stringify({ agents }),
29
17
  isError: false,
30
18
  };
31
19
  }