@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,395 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ import type { ServerMessage } from "../daemon/message-protocol.js";
4
+
5
+ // Silence the logger.
6
+ mock.module("../util/logger.js", () => ({
7
+ getLogger: () =>
8
+ new Proxy({} as Record<string, unknown>, {
9
+ get: () => () => {},
10
+ }),
11
+ }));
12
+
13
+ // Usage-data collection is enabled so recordActivationEvent writes rows.
14
+ mock.module("../config/loader.js", () => ({
15
+ getConfig: () => ({ collectUsageData: true }),
16
+ }));
17
+
18
+ let broadcastedMessages: ServerMessage[] = [];
19
+ const realEventHub = await import("../runtime/assistant-event-hub.js");
20
+ mock.module("../runtime/assistant-event-hub.js", () => ({
21
+ ...realEventHub,
22
+ broadcastMessage: (msg: ServerMessage) => broadcastedMessages.push(msg),
23
+ }));
24
+
25
+ // Stub the child-conversation launcher so the launch_conversation commit path
26
+ // runs without spinning up a real conversation.
27
+ mock.module("../daemon/conversation-launch.js", () => ({
28
+ launchConversation: async () => ({ conversationId: "spawned-conv" }),
29
+ }));
30
+
31
+ const { createSurfaceMutex, handleSurfaceAction, surfaceProxyResolver } =
32
+ await import("../daemon/conversation-surfaces.js");
33
+
34
+ import type { SurfaceConversationContext } from "../daemon/conversation-surfaces.js";
35
+ import type { SurfaceType, UiSurfaceShow } from "../daemon/message-protocol.js";
36
+ import {
37
+ isActivationSession,
38
+ markActivationSession,
39
+ } from "../memory/activation-session-store.js";
40
+ import { getDb } from "../memory/db-connection.js";
41
+ import { initializeDb } from "../memory/db-init.js";
42
+ import { queryUnreportedOnboardingEvents } from "../memory/onboarding-events-store.js";
43
+ import { activationSessions, onboardingEvents } from "../memory/schema.js";
44
+
45
+ initializeDb();
46
+
47
+ interface ProcessMessageCall {
48
+ content: string;
49
+ activeSurfaceId?: string;
50
+ }
51
+
52
+ function makeContext(
53
+ conversationId: string,
54
+ sent: ServerMessage[] = [],
55
+ ): SurfaceConversationContext & {
56
+ processMessageCalls: ProcessMessageCall[];
57
+ } {
58
+ const processMessageCalls: ProcessMessageCall[] = [];
59
+ return {
60
+ conversationId,
61
+ traceEmitter: { emit: () => {} },
62
+ sendToClient: (msg: ServerMessage) => sent.push(msg),
63
+ pendingSurfaceActions: new Map<string, { surfaceType: SurfaceType }>(),
64
+ lastSurfaceAction: new Map<
65
+ string,
66
+ { actionId: string; data?: Record<string, unknown> }
67
+ >(),
68
+ surfaceState: new Map(),
69
+ surfaceUndoStacks: new Map<string, string[]>(),
70
+ accumulatedSurfaceState: new Map<string, Record<string, unknown>>(),
71
+ surfaceActionRequestIds: new Set<string>(),
72
+ currentTurnSurfaces: [],
73
+ isProcessing: () => false,
74
+ enqueueMessage: () => ({ queued: false, requestId: "req-1" }),
75
+ getQueueDepth: () => 0,
76
+ processMessage: async (options) => {
77
+ processMessageCalls.push({
78
+ content: options.content,
79
+ activeSurfaceId: options.activeSurfaceId,
80
+ });
81
+ return "msg-1";
82
+ },
83
+ withSurface: createSurfaceMutex(),
84
+ processMessageCalls,
85
+ } as SurfaceConversationContext & {
86
+ processMessageCalls: ProcessMessageCall[];
87
+ };
88
+ }
89
+
90
+ function resetTables(): void {
91
+ getDb().delete(onboardingEvents).run();
92
+ getDb().delete(activationSessions).run();
93
+ }
94
+
95
+ /** Render a choice surface tagged (or not) with an activation_moment. */
96
+ async function showTaggedChoice(
97
+ ctx: SurfaceConversationContext,
98
+ sent: ServerMessage[],
99
+ activationMoment?: string,
100
+ ): Promise<string> {
101
+ await surfaceProxyResolver(ctx, "ui_show", {
102
+ surface_type: "choice",
103
+ title: "Pick an outcome",
104
+ data: {
105
+ options: [
106
+ { id: "inbox", title: "Clean up my inbox" },
107
+ { id: "calendar", title: "Plan my week" },
108
+ ],
109
+ },
110
+ ...(activationMoment ? { activation_moment: activationMoment } : {}),
111
+ });
112
+ const showMessage = sent.find(
113
+ (msg): msg is UiSurfaceShow => msg.type === "ui_surface_show",
114
+ ) as UiSurfaceShow;
115
+ return showMessage.surfaceId;
116
+ }
117
+
118
+ describe("activation moment emission from ui_show surface commits", () => {
119
+ beforeEach(() => {
120
+ broadcastedMessages = [];
121
+ resetTables();
122
+ });
123
+
124
+ test("committed tagged surface in a marked session writes exactly one row", async () => {
125
+ markActivationSession("conv-marked");
126
+ expect(isActivationSession("conv-marked")).toBe(true);
127
+
128
+ const sent: ServerMessage[] = [];
129
+ const ctx = makeContext("conv-marked", sent);
130
+ const surfaceId = await showTaggedChoice(ctx, sent, "moment_2");
131
+
132
+ await handleSurfaceAction(ctx, surfaceId, "inbox", {
133
+ choiceId: "inbox",
134
+ selectedIds: ["inbox"],
135
+ });
136
+
137
+ const rows = queryUnreportedOnboardingEvents(0, undefined, 10);
138
+ expect(rows).toHaveLength(1);
139
+ expect(rows[0]!.stepName).toBe("activation_moment_2_complete");
140
+ expect(rows[0]!.stepIndex).toBe(2);
141
+ expect(rows[0]!.sessionId).toBe("conv-marked");
142
+ });
143
+
144
+ test("a queue-rejected commit does NOT emit; the tag survives for the retry", async () => {
145
+ markActivationSession("conv-rejected");
146
+ const sent: ServerMessage[] = [];
147
+ const ctx = makeContext("conv-rejected", sent);
148
+ const surfaceId = await showTaggedChoice(ctx, sent, "moment_2");
149
+
150
+ // First click while the queue is full: enqueue rejects, action not
151
+ // accepted. Must NOT record a milestone, and must leave the one-shot tag
152
+ // intact so the user's retry still emits.
153
+ ctx.enqueueMessage = () => ({
154
+ queued: false,
155
+ requestId: "req-rejected",
156
+ rejected: true,
157
+ });
158
+ await handleSurfaceAction(ctx, surfaceId, "inbox", {
159
+ choiceId: "inbox",
160
+ selectedIds: ["inbox"],
161
+ });
162
+ expect(queryUnreportedOnboardingEvents(0, undefined, 10)).toHaveLength(0);
163
+
164
+ // Retry is accepted → records exactly one row.
165
+ ctx.enqueueMessage = () => ({ queued: false, requestId: "req-ok" });
166
+ await handleSurfaceAction(ctx, surfaceId, "inbox", {
167
+ choiceId: "inbox",
168
+ selectedIds: ["inbox"],
169
+ });
170
+ const rows = queryUnreportedOnboardingEvents(0, undefined, 10);
171
+ expect(rows).toHaveLength(1);
172
+ expect(rows[0]!.stepName).toBe("activation_moment_2_complete");
173
+ });
174
+
175
+ test("first_wow_executed records at SHOW time (no commit) and never double-emits", async () => {
176
+ markActivationSession("conv-wow");
177
+ const sent: ServerMessage[] = [];
178
+ const ctx = makeContext("conv-wow", sent);
179
+
180
+ // A display-only result surface tagged with the execution moment.
181
+ await surfaceProxyResolver(ctx, "ui_show", {
182
+ surface_type: "card",
183
+ title: "Inbox cleaned",
184
+ data: { text: "Archived 1,240 emails" },
185
+ activation_moment: "first_wow_executed",
186
+ });
187
+
188
+ // Recorded immediately on render — no handleSurfaceAction commit needed.
189
+ let rows = queryUnreportedOnboardingEvents(0, undefined, 10);
190
+ expect(rows).toHaveLength(1);
191
+ expect(rows[0]!.stepName).toBe("activation_first_wow_executed");
192
+ expect(rows[0]!.stepIndex).toBe(4);
193
+
194
+ // If the card later receives a commit (e.g. it carried an action), it must
195
+ // NOT double-emit — a show-timing tag is never stored for the commit path.
196
+ const showMessage = sent.find(
197
+ (msg): msg is UiSurfaceShow => msg.type === "ui_surface_show",
198
+ ) as UiSurfaceShow;
199
+ await handleSurfaceAction(ctx, showMessage.surfaceId, "expand", {});
200
+ rows = queryUnreportedOnboardingEvents(0, undefined, 10);
201
+ expect(rows).toHaveLength(1);
202
+ });
203
+
204
+ test("first_wow_executed in an UNMARKED session writes no row at show time", async () => {
205
+ const sent: ServerMessage[] = [];
206
+ const ctx = makeContext("conv-wow-unmarked", sent);
207
+ await surfaceProxyResolver(ctx, "ui_show", {
208
+ surface_type: "card",
209
+ title: "Result",
210
+ data: { text: "x" },
211
+ activation_moment: "first_wow_executed",
212
+ });
213
+ expect(queryUnreportedOnboardingEvents(0, undefined, 10)).toHaveLength(0);
214
+ });
215
+
216
+ test("does not forward the daemon-only tag to the client", async () => {
217
+ markActivationSession("conv-marked-2");
218
+ const sent: ServerMessage[] = [];
219
+ const ctx = makeContext("conv-marked-2", sent);
220
+ await showTaggedChoice(ctx, sent, "moment_2");
221
+
222
+ const showMessage = sent.find(
223
+ (msg): msg is UiSurfaceShow => msg.type === "ui_surface_show",
224
+ ) as UiSurfaceShow;
225
+ expect(
226
+ (showMessage as unknown as Record<string, unknown>).activation_moment,
227
+ ).toBeUndefined();
228
+ expect(JSON.stringify(showMessage)).not.toContain("moment_2");
229
+ });
230
+
231
+ test("untagged surface writes no row", async () => {
232
+ markActivationSession("conv-untagged");
233
+ const sent: ServerMessage[] = [];
234
+ const ctx = makeContext("conv-untagged", sent);
235
+ const surfaceId = await showTaggedChoice(ctx, sent, undefined);
236
+
237
+ await handleSurfaceAction(ctx, surfaceId, "inbox", {
238
+ choiceId: "inbox",
239
+ selectedIds: ["inbox"],
240
+ });
241
+
242
+ expect(queryUnreportedOnboardingEvents(0, undefined, 10)).toHaveLength(0);
243
+ });
244
+
245
+ test("tagged surface in an UNMARKED session writes no row", async () => {
246
+ const sent: ServerMessage[] = [];
247
+ const ctx = makeContext("conv-not-rail", sent);
248
+ const surfaceId = await showTaggedChoice(ctx, sent, "moment_2");
249
+
250
+ await handleSurfaceAction(ctx, surfaceId, "inbox", {
251
+ choiceId: "inbox",
252
+ selectedIds: ["inbox"],
253
+ });
254
+
255
+ expect(queryUnreportedOnboardingEvents(0, undefined, 10)).toHaveLength(0);
256
+ });
257
+
258
+ test("an invalid activation_moment token is ignored (no row)", async () => {
259
+ markActivationSession("conv-bad-tag");
260
+ const sent: ServerMessage[] = [];
261
+ const ctx = makeContext("conv-bad-tag", sent);
262
+ const surfaceId = await showTaggedChoice(ctx, sent, "bogus_moment");
263
+
264
+ await handleSurfaceAction(ctx, surfaceId, "inbox", {
265
+ choiceId: "inbox",
266
+ selectedIds: ["inbox"],
267
+ });
268
+
269
+ expect(queryUnreportedOnboardingEvents(0, undefined, 10)).toHaveLength(0);
270
+ });
271
+
272
+ test("FIX 1: launch_conversation commit on a tagged surface records exactly one row", async () => {
273
+ markActivationSession("conv-launch");
274
+ const sent: ServerMessage[] = [];
275
+ const ctx = makeContext("conv-launch", sent);
276
+
277
+ // A launcher card tagged with a commit-timing moment. The committed action
278
+ // carries `_action: launch_conversation`, so handleSurfaceAction takes the
279
+ // inline-launch branch (which previously returned before the emit).
280
+ await surfaceProxyResolver(ctx, "ui_show", {
281
+ surface_type: "card",
282
+ title: "Start something",
283
+ data: { text: "Kick off a draft" },
284
+ actions: [{ id: "go", label: "Go", style: "primary" }],
285
+ activation_moment: "moment_1",
286
+ });
287
+ const showMessage = sent.find(
288
+ (msg): msg is UiSurfaceShow => msg.type === "ui_surface_show",
289
+ ) as UiSurfaceShow;
290
+ const surfaceId = showMessage.surfaceId;
291
+
292
+ await handleSurfaceAction(ctx, surfaceId, "go", {
293
+ _action: "launch_conversation",
294
+ title: "Draft",
295
+ seedPrompt: "Write a draft",
296
+ });
297
+
298
+ const rows = queryUnreportedOnboardingEvents(0, undefined, 10);
299
+ expect(rows).toHaveLength(1);
300
+ expect(rows[0]!.stepName).toBe("activation_moment_1_complete");
301
+ expect(rows[0]!.sessionId).toBe("conv-launch");
302
+ });
303
+
304
+ test("FIX 2: commit-timing tag survives a surfaceState restore from history", async () => {
305
+ markActivationSession("conv-restore");
306
+ const sent: ServerMessage[] = [];
307
+ const ctx = makeContext("conv-restore", sent);
308
+ const surfaceId = await showTaggedChoice(ctx, sent, "moment_2");
309
+
310
+ // The daemon-only tag must NOT leak to the client.
311
+ const showMessage = sent.find(
312
+ (msg): msg is UiSurfaceShow => msg.type === "ui_surface_show",
313
+ ) as UiSurfaceShow;
314
+ expect(
315
+ (showMessage as unknown as Record<string, unknown>).activation_moment,
316
+ ).toBeUndefined();
317
+ expect(JSON.stringify(showMessage)).not.toContain("moment_2");
318
+
319
+ // Persist the surface into a history block exactly as the agent loop does
320
+ // (conversation-agent-loop-handlers): copy the activationMoment through.
321
+ const persisted = ctx.currentTurnSurfaces.map((s) => ({
322
+ type: "ui_surface" as const,
323
+ surfaceId: s.surfaceId,
324
+ surfaceType: s.surfaceType,
325
+ title: s.title,
326
+ data: s.data,
327
+ actions: s.actions,
328
+ ...(s.activationMoment ? { activationMoment: s.activationMoment } : {}),
329
+ }));
330
+ expect(persisted[0]!.activationMoment).toBe("moment_2");
331
+
332
+ // Simulate a reload: drop the in-memory surfaceState, then rebuild it from
333
+ // the persisted history block the same way restoreSurfaceStateFromHistory
334
+ // does (including rehydrating the daemon-only tag).
335
+ ctx.surfaceState.clear();
336
+ for (const b of persisted) {
337
+ ctx.surfaceState.set(b.surfaceId, {
338
+ surfaceType: b.surfaceType,
339
+ data: b.data,
340
+ title: b.title,
341
+ actions: b.actions,
342
+ ...(b.activationMoment ? { activationMoment: b.activationMoment } : {}),
343
+ });
344
+ }
345
+
346
+ // A commit after restore still records exactly one row.
347
+ await handleSurfaceAction(ctx, surfaceId, "inbox", {
348
+ choiceId: "inbox",
349
+ selectedIds: ["inbox"],
350
+ });
351
+
352
+ const rows = queryUnreportedOnboardingEvents(0, undefined, 10);
353
+ expect(rows).toHaveLength(1);
354
+ expect(rows[0]!.stepName).toBe("activation_moment_2_complete");
355
+ expect(rows[0]!.sessionId).toBe("conv-restore");
356
+ });
357
+
358
+ test("intermediate selection_changed does NOT emit; the terminal commit does", async () => {
359
+ markActivationSession("conv-table");
360
+ const sent: ServerMessage[] = [];
361
+ const ctx = makeContext("conv-table", sent);
362
+
363
+ // A table surface stays pending across selection_changed (non-terminal).
364
+ await surfaceProxyResolver(ctx, "ui_show", {
365
+ surface_type: "table",
366
+ title: "Tasks",
367
+ data: {
368
+ columns: [{ id: "name", label: "Name" }],
369
+ rows: [
370
+ { id: "r1", cells: { name: "Task 1" } },
371
+ { id: "r2", cells: { name: "Task 2" } },
372
+ ],
373
+ selectionMode: "multiple",
374
+ },
375
+ actions: [{ id: "run", label: "Run", style: "primary" }],
376
+ activation_moment: "moment_3",
377
+ });
378
+ const showMessage = sent.find(
379
+ (msg): msg is UiSurfaceShow => msg.type === "ui_surface_show",
380
+ ) as UiSurfaceShow;
381
+ const surfaceId = showMessage.surfaceId;
382
+
383
+ // Intermediate, non-terminal — must NOT emit.
384
+ await handleSurfaceAction(ctx, surfaceId, "selection_changed", {
385
+ selectedIds: ["r1"],
386
+ });
387
+ expect(queryUnreportedOnboardingEvents(0, undefined, 10)).toHaveLength(0);
388
+
389
+ // Terminal commit — emits exactly once.
390
+ await handleSurfaceAction(ctx, surfaceId, "run", { selectedIds: ["r1"] });
391
+ const rows = queryUnreportedOnboardingEvents(0, undefined, 10);
392
+ expect(rows).toHaveLength(1);
393
+ expect(rows[0]!.stepName).toBe("activation_moment_3_complete");
394
+ });
395
+ });
@@ -73,6 +73,7 @@ function buildMockContext(
73
73
  proxy: InstanceType<typeof HostAppControlProxy> | undefined,
74
74
  ) => void,
75
75
  trustGuardianPrincipalId: string | null = DEFAULT_PRINCIPAL,
76
+ actorPrincipalId: string | null = trustGuardianPrincipalId,
76
77
  ): SurfaceConversationContext {
77
78
  return {
78
79
  conversationId,
@@ -84,6 +85,16 @@ function buildMockContext(
84
85
  guardianPrincipalId: trustGuardianPrincipalId,
85
86
  }
86
87
  : undefined,
88
+ authContext:
89
+ actorPrincipalId != null
90
+ ? ({ actorPrincipalId } as SurfaceConversationContext["authContext"])
91
+ : undefined,
92
+ currentTurnAuthContext:
93
+ actorPrincipalId != null
94
+ ? ({
95
+ actorPrincipalId,
96
+ } as SurfaceConversationContext["currentTurnAuthContext"])
97
+ : undefined,
87
98
  traceEmitter: { emit: () => {} },
88
99
  sendToClient: () => {},
89
100
  pendingSurfaceActions: new Map(),
@@ -217,6 +228,39 @@ describe("surfaceProxyResolver — app-control tool routing", () => {
217
228
  proxy.dispose();
218
229
  });
219
230
 
231
+ test("app_control_observe rejects owner client when current requester is a trusted contact", async () => {
232
+ mockHubClients = [
233
+ {
234
+ clientId: "owner-client",
235
+ capabilities: ["host_app_control"],
236
+ actorPrincipalId: "owner-user",
237
+ },
238
+ ];
239
+ const proxy = new HostAppControlProxy("conv-1");
240
+ const ctx = buildMockContext(
241
+ proxy,
242
+ "conv-1",
243
+ undefined,
244
+ "owner-user",
245
+ "trusted-contact-user",
246
+ );
247
+ _setActiveAppControlSession({
248
+ conversationId: "conv-1",
249
+ app: "com.example.editor",
250
+ });
251
+
252
+ const result = await surfaceProxyResolver(ctx, "app_control_observe", {
253
+ tool: "observe",
254
+ app: "com.example.editor",
255
+ });
256
+
257
+ expect(result.isError).toBe(true);
258
+ expect(result.content).toContain("current actor");
259
+ expect(sentMessages).toHaveLength(0);
260
+
261
+ proxy.dispose();
262
+ });
263
+
220
264
  test("app_control_observe rejects when only another actor has a capable client", async () => {
221
265
  mockHubClients = [
222
266
  {
@@ -467,6 +511,7 @@ describe("surfaceProxyResolver — app-control tool routing", () => {
467
511
  _setActiveAppControlSession({
468
512
  conversationId: "conv-1",
469
513
  app: "com.example.editor",
514
+ targetClientId: "client-b",
470
515
  });
471
516
 
472
517
  const resultPromise = surfaceProxyResolver(ctx, "app_control_observe", {
@@ -551,6 +596,7 @@ describe("surfaceProxyResolver — app-control tool routing", () => {
551
596
  _setActiveAppControlSession({
552
597
  conversationId: "conv-1",
553
598
  app: "com.example.editor",
599
+ targetClientId: "client-mine",
554
600
  });
555
601
 
556
602
  const resultPromise = surfaceProxyResolver(ctx, "app_control_observe", {
@@ -573,7 +619,7 @@ describe("surfaceProxyResolver — app-control tool routing", () => {
573
619
  proxy.dispose();
574
620
  });
575
621
 
576
- test("single same-user client with no target proceeds without forcing targetClientId", async () => {
622
+ test("single same-user client with no target resolves to that client", async () => {
577
623
  mockHubClients = [
578
624
  {
579
625
  clientId: "only-client",
@@ -586,6 +632,7 @@ describe("surfaceProxyResolver — app-control tool routing", () => {
586
632
  _setActiveAppControlSession({
587
633
  conversationId: "conv-1",
588
634
  app: "com.example.editor",
635
+ targetClientId: "only-client",
589
636
  });
590
637
 
591
638
  const resultPromise = surfaceProxyResolver(ctx, "app_control_observe", {
@@ -2,9 +2,9 @@
2
2
  * Regression tests for app surface refresh and eventing side effects in
3
3
  * createToolExecutor (conversation-tool-setup.ts).
4
4
  *
5
- * Tests verify that app_refresh, app_create, and app_delete hooks fire
6
- * correctly, and that removed hooks (app_update, app_file_edit,
7
- * app_file_write) no longer trigger side effects.
5
+ * Tests verify that app_refresh, app_update, app_create, and app_delete hooks
6
+ * fire correctly, and that non-hooked tools (app_file_edit, app_file_write) do
7
+ * not trigger side effects.
8
8
  *
9
9
  * File-change detection for file_write/file_edit is handled by
10
10
  * AppSourceWatcher (see app-source-watcher.test.ts).
@@ -248,6 +248,76 @@ describe("session-tool-setup app refresh side effects", () => {
248
248
  });
249
249
  });
250
250
 
251
+ // ── app_update ──────────────────────────────────────────────────────
252
+
253
+ describe("app_update", () => {
254
+ test("triggers refreshSurfacesForApp and broadcast on success", async () => {
255
+ const ctx = makeCtx();
256
+ const executor = makeFakeExecutor({
257
+ content: '{"updated":true,"appId":"app-7"}',
258
+ isError: false,
259
+ });
260
+
261
+ const toolFn = createToolExecutor(
262
+ executor as unknown as ToolExecutor,
263
+ noopPrompter,
264
+ noopSecretPrompter,
265
+ ctx,
266
+ noopLifecycleHandler,
267
+ );
268
+
269
+ await toolFn("app_update", { app_id: "app-7" });
270
+
271
+ expect(refreshSpy).toHaveBeenCalledTimes(1);
272
+ expect((refreshSpy.mock.calls as unknown[][])[0][1]).toBe("app-7");
273
+ expectAppChangeBroadcast("app-7");
274
+ expect(updatePublishedSpy).toHaveBeenCalledTimes(1);
275
+ expect((updatePublishedSpy.mock.calls as unknown[][])[0][0]).toBe(
276
+ "app-7",
277
+ );
278
+ });
279
+
280
+ test("skips side effects when result is an error", async () => {
281
+ const ctx = makeCtx();
282
+ const executor = makeFakeExecutor({
283
+ content: "Error: not found",
284
+ isError: true,
285
+ });
286
+
287
+ const toolFn = createToolExecutor(
288
+ executor as unknown as ToolExecutor,
289
+ noopPrompter,
290
+ noopSecretPrompter,
291
+ ctx,
292
+ noopLifecycleHandler,
293
+ );
294
+
295
+ await toolFn("app_update", { app_id: "app-err" });
296
+
297
+ expect(refreshSpy).not.toHaveBeenCalled();
298
+ expect(broadcastSpy).not.toHaveBeenCalled();
299
+ expect(updatePublishedSpy).not.toHaveBeenCalled();
300
+ });
301
+
302
+ test("skips side effects when app_id is missing", async () => {
303
+ const ctx = makeCtx();
304
+ const executor = makeFakeExecutor({ content: "{}", isError: false });
305
+
306
+ const toolFn = createToolExecutor(
307
+ executor as unknown as ToolExecutor,
308
+ noopPrompter,
309
+ noopSecretPrompter,
310
+ ctx,
311
+ noopLifecycleHandler,
312
+ );
313
+
314
+ await toolFn("app_update", {});
315
+
316
+ expect(refreshSpy).not.toHaveBeenCalled();
317
+ expect(broadcastSpy).not.toHaveBeenCalled();
318
+ });
319
+ });
320
+
251
321
  // ── app_create side effects ─────────────────────────────────────────
252
322
 
253
323
  describe("app_create side effects", () => {
@@ -518,7 +588,6 @@ describe("session-tool-setup app refresh side effects", () => {
518
588
  "write_file",
519
589
  "shell",
520
590
  "app_list",
521
- "app_update",
522
591
  "app_file_edit",
523
592
  "app_file_write",
524
593
  ]) {
@@ -1,10 +1,10 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
 
3
+ import { findLastUndoableUserMessageIndex } from "../daemon/conversation.js";
3
4
  import {
4
5
  CONTEXT_SUMMARY_MARKER,
5
6
  createContextSummaryMessage,
6
- } from "../context/window-manager.js";
7
- import { findLastUndoableUserMessageIndex } from "../daemon/conversation.js";
7
+ } from "../plugins/defaults/compaction/window-manager.js";
8
8
  import type { Message } from "../providers/types.js";
9
9
 
10
10
  function textMessage(role: "user" | "assistant", text: string): Message {