@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
@@ -22,7 +22,6 @@ import type { Message } from "../providers/types.js";
22
22
 
23
23
  mock.module("../memory/conversation-crud.js", () => ({
24
24
  getConversationOverrideProfile: () => undefined,
25
- reserveMessage: mock(async () => ({ id: "msg-reserve" })),
26
25
  }));
27
26
 
28
27
  mock.module("../config/loader.js", () => ({
@@ -62,36 +61,37 @@ mock.module("../daemon/disk-pressure-guard.js", () => ({
62
61
  }),
63
62
  }));
64
63
 
64
+ import type { Conversation } from "../daemon/conversation.js";
65
65
  import {
66
66
  __resetWakeChainForTests,
67
67
  wakeAgentForOpportunity,
68
- type WakeTarget,
69
68
  } from "../runtime/agent-wake.js";
70
69
 
71
- function makeTarget(): WakeTarget {
72
- const history: Message[] = [];
70
+ function makeTarget(): Conversation {
71
+ const messages: Message[] = [];
73
72
  let processing = false;
74
- return {
73
+ const target = {
75
74
  conversationId: "conv-wake-callsite",
76
75
  agentLoop: {
77
- run: (async (messages: Message[]) => ({
76
+ run: async ({ messages }: { messages: Message[] }) => ({
78
77
  history: messages,
79
78
  exitReason: null,
80
79
  appendedNewMessages: false,
81
80
  newMessages: [],
82
- })) as WakeTarget["agentLoop"]["run"],
81
+ }),
83
82
  },
84
- getMessages: () => history,
85
- pushMessage: (msg) => {
86
- history.push(msg);
87
- },
88
- emitAgentEvent: () => {},
83
+ messages,
84
+ getMessages: () => messages,
89
85
  isProcessing: () => processing,
90
- markProcessing: (on) => {
86
+ setProcessing: (on: boolean) => {
91
87
  processing = on;
92
88
  },
93
- persistTailMessage: async () => {},
89
+ setTrustContext: () => {},
90
+ getTurnChannelContext: () => null,
91
+ getTurnInterfaceContext: () => null,
92
+ drainQueue: async () => {},
94
93
  };
94
+ return target as unknown as Conversation;
95
95
  }
96
96
 
97
97
  beforeEach(() => {
@@ -48,8 +48,9 @@ mock.module("../config/loader.js", () => ({
48
48
  }),
49
49
  }));
50
50
 
51
- import type { AgentEvent, AgentLoopRunOptions } from "../agent/loop.js";
51
+ import type { AgentLoopRunOptions } from "../agent/loop.js";
52
52
  import { LLMSchema } from "../config/schemas/llm.js";
53
+ import type { Conversation } from "../daemon/conversation.js";
53
54
  import { RetryProvider } from "../providers/retry.js";
54
55
  import type {
55
56
  Message,
@@ -60,7 +61,6 @@ import type {
60
61
  import {
61
62
  __resetWakeChainForTests,
62
63
  wakeAgentForOpportunity,
63
- type WakeTarget,
64
64
  } from "../runtime/agent-wake.js";
65
65
 
66
66
  interface RunArgs {
@@ -69,44 +69,40 @@ interface RunArgs {
69
69
  }
70
70
 
71
71
  function makeTarget(): {
72
- target: WakeTarget;
72
+ target: Conversation;
73
73
  runArgs: RunArgs[];
74
74
  } {
75
75
  const runArgs: RunArgs[] = [];
76
- const history: Message[] = [
76
+ const messages: Message[] = [
77
77
  { role: "user", content: [{ type: "text", text: "hi" }] },
78
78
  ];
79
79
  let processing = false;
80
80
 
81
- const target: WakeTarget = {
81
+ const target = {
82
82
  conversationId: "conv-wake-override",
83
83
  agentLoop: {
84
- run: (async (
85
- messages: Message[],
86
- _onEvent: (event: AgentEvent) => void | Promise<void>,
87
- options?: AgentLoopRunOptions,
88
- ) => {
84
+ run: async (options: AgentLoopRunOptions) => {
89
85
  runArgs.push({
90
- messages: [...messages],
86
+ messages: [...options.messages],
91
87
  options,
92
88
  });
93
89
  // Return the input verbatim → silent no-op (no assistant tail).
94
90
  // Wake never yields at a checkpoint, so the pause-reason is null.
95
- return { history: messages, exitReason: null };
96
- }) as WakeTarget["agentLoop"]["run"],
97
- },
98
- getMessages: () => history,
99
- pushMessage: (msg) => {
100
- history.push(msg);
91
+ return { history: options.messages, exitReason: null };
92
+ },
101
93
  },
102
- emitAgentEvent: () => {},
94
+ messages,
95
+ getMessages: () => messages,
103
96
  isProcessing: () => processing,
104
- markProcessing: (on) => {
97
+ setProcessing: (on: boolean) => {
105
98
  processing = on;
106
99
  },
107
- persistTailMessage: async () => {},
100
+ setTrustContext: () => {},
101
+ getTurnChannelContext: () => null,
102
+ getTurnInterfaceContext: () => null,
103
+ drainQueue: async () => {},
108
104
  };
109
- return { target, runArgs };
105
+ return { target: target as unknown as Conversation, runArgs };
110
106
  }
111
107
 
112
108
  beforeEach(() => {
@@ -2,7 +2,11 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
3
  import type { Anthropic } from "@anthropic-ai/sdk";
4
4
 
5
- import type { Message, ToolDefinition } from "../providers/types.js";
5
+ import type {
6
+ ContentBlock,
7
+ Message,
8
+ ToolDefinition,
9
+ } from "../providers/types.js";
6
10
 
7
11
  // ---------------------------------------------------------------------------
8
12
  // Mock Anthropic SDK — must be before importing the provider
@@ -102,7 +106,6 @@ mock.module("@anthropic-ai/sdk", () => ({
102
106
  }));
103
107
 
104
108
  // Import after mocking
105
- import { cachedTextBlock } from "../memory/v3/provider-blocks.js";
106
109
  import { AnthropicProvider } from "../providers/anthropic/client.js";
107
110
  import {
108
111
  isPlaceholderSentinelText,
@@ -138,6 +141,18 @@ function toolResultMsg(toolUseId: string, content: string): Message {
138
141
  };
139
142
  }
140
143
 
144
+ // A stable prefix text block carrying a 1h-TTL cache_control breakpoint, as a
145
+ // caller may stamp before the provider sees it. The internal ContentBlock type
146
+ // omits cache_control (only the provider transforms it onto the wire), so reach
147
+ // it via a cast.
148
+ function cachedPrefixBlock(text: string): ContentBlock {
149
+ return {
150
+ type: "text",
151
+ text,
152
+ cache_control: { type: "ephemeral", ttl: "1h" },
153
+ } as unknown as ContentBlock;
154
+ }
155
+
141
156
  const sampleTools: ToolDefinition[] = [
142
157
  {
143
158
  name: "file_read",
@@ -325,13 +340,13 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
325
340
  });
326
341
 
327
342
  test("preserves a caller's 1h block cache_control and sends the extended-cache beta (non-Haiku)", async () => {
328
- // v3's `cachedTextBlock` stamps a stable prefix block with a 1h TTL; the
329
- // non-Haiku path must forward it unchanged and send the beta header.
343
+ // A caller that stamps a stable prefix block with a 1h TTL: the non-Haiku
344
+ // path must forward it unchanged and send the beta header.
330
345
  await provider.sendMessage([
331
346
  {
332
347
  role: "user",
333
348
  content: [
334
- cachedTextBlock("stable pages block"),
349
+ cachedPrefixBlock("stable pages block"),
335
350
  { type: "text", text: "volatile current message" },
336
351
  ],
337
352
  },
@@ -349,7 +364,7 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
349
364
 
350
365
  test("strips ttl from a caller's block cache_control and omits the beta for Haiku", async () => {
351
366
  // Haiku doesn't support the extended-cache-ttl beta, so a caller-stamped
352
- // 1h ttl (e.g. from `cachedTextBlock`) must be stripped before sending.
367
+ // 1h ttl on a message block must be stripped before sending.
353
368
  const haiku = new AnthropicProvider(
354
369
  "sk-ant-test",
355
370
  "claude-haiku-4-5-20251001",
@@ -358,7 +373,7 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
358
373
  {
359
374
  role: "user",
360
375
  content: [
361
- cachedTextBlock("stable pages block"),
376
+ cachedPrefixBlock("stable pages block"),
362
377
  { type: "text", text: "volatile current message" },
363
378
  ],
364
379
  },
@@ -385,7 +400,7 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
385
400
  {
386
401
  role: "user",
387
402
  content: [
388
- cachedTextBlock("stable pages block"),
403
+ cachedPrefixBlock("stable pages block"),
389
404
  { type: "text", text: "volatile current message" },
390
405
  ],
391
406
  },
@@ -81,6 +81,7 @@ mock.module("../bundler/app-compiler.js", () => ({
81
81
  import * as appCreateScript from "../config/bundled-skills/app-builder/tools/app-create.js";
82
82
  import * as appDeleteScript from "../config/bundled-skills/app-builder/tools/app-delete.js";
83
83
  import * as appRefreshScript from "../config/bundled-skills/app-builder/tools/app-refresh.js";
84
+ import * as appUpdateScript from "../config/bundled-skills/app-builder/tools/app-update.js";
84
85
 
85
86
  // ---------------------------------------------------------------------------
86
87
  // Tests
@@ -171,4 +172,24 @@ describe("app-builder skill tool scripts", () => {
171
172
  expect(parsed.appId).toBe("app-1");
172
173
  });
173
174
  });
175
+
176
+ // ---- app-update --------------------------------------------------------
177
+
178
+ describe("app-update", () => {
179
+ test("exports a run function", () => {
180
+ expect(typeof appUpdateScript.run).toBe("function");
181
+ });
182
+
183
+ test("delegates to executeAppUpdate and returns result", async () => {
184
+ const result = await appUpdateScript.run(
185
+ { app_id: "app-1", name: "Renamed" },
186
+ makeContext(),
187
+ );
188
+ expect(result.isError).toBe(false);
189
+ const parsed = JSON.parse(result.content);
190
+ expect(parsed.updated).toBe(true);
191
+ expect(parsed.appId).toBe("app-1");
192
+ expect(parsed.name).toBe("Renamed");
193
+ });
194
+ });
174
195
  });
@@ -103,7 +103,7 @@ const { ROUTES } = await import("../runtime/routes/host-app-control-routes.js");
103
103
  const { surfaceProxyResolver } =
104
104
  await import("../daemon/conversation-surfaces.js");
105
105
  const { setConversation, clearConversations } =
106
- await import("../daemon/conversation-store.js");
106
+ await import("../daemon/conversation-registry.js");
107
107
  type SurfaceConversationContext =
108
108
  import("../daemon/conversation-surfaces.js").SurfaceConversationContext;
109
109
 
@@ -19,6 +19,7 @@ const ALLOWLIST = new Set([
19
19
  "assistant/src/memory/app-store.ts", // defines getAppsDir
20
20
  "assistant/src/memory/app-git-service.ts", // uses getAppsDir for git repo root, not per-app paths
21
21
  "assistant/src/daemon/app-source-watcher.ts", // uses getAppsDir for recursive fs.watch root, not per-app paths
22
+ "assistant/src/tools/filesystem/write.ts", // uses getAppsDir as an exemption root for the artifact-HTML guard, not per-app paths
22
23
  ]);
23
24
 
24
25
  function isTestFile(filePath: string): boolean {
@@ -32,6 +32,7 @@ import type { AppStore } from "../tools/apps/executors.js";
32
32
  import {
33
33
  executeAppCreate,
34
34
  executeAppRefresh,
35
+ executeAppUpdate,
35
36
  } from "../tools/apps/executors.js";
36
37
 
37
38
  // ---------------------------------------------------------------------------
@@ -89,6 +90,46 @@ function mockStore(
89
90
  // ---------------------------------------------------------------------------
90
91
 
91
92
  describe("executeAppCreate", () => {
93
+ test("falls back to the preview title when the name is omitted or blank", async () => {
94
+ let createdParams: Record<string, unknown> | undefined;
95
+ const app = makeMultifileApp({ name: "Coffee Tracker" });
96
+ const store: AppStore = {
97
+ ...mockStore(app, {}),
98
+ createApp: (params) => {
99
+ createdParams = params as unknown as Record<string, unknown>;
100
+ return app;
101
+ },
102
+ };
103
+
104
+ const result = await executeAppCreate(
105
+ { name: " ", preview: { title: "Coffee Tracker" } },
106
+ store,
107
+ );
108
+
109
+ expect(result.isError).toBe(false);
110
+ expect(createdParams?.name).toBe("Coffee Tracker");
111
+ });
112
+
113
+ test("defaults the name to 'New App' when neither name nor preview title is given", async () => {
114
+ let createdParams: Record<string, unknown> | undefined;
115
+ const app = makeMultifileApp({ name: "New App" });
116
+ const store: AppStore = {
117
+ ...mockStore(app, {}),
118
+ createApp: (params) => {
119
+ createdParams = params as unknown as Record<string, unknown>;
120
+ return app;
121
+ },
122
+ };
123
+
124
+ const result = await executeAppCreate(
125
+ {} as unknown as Parameters<typeof executeAppCreate>[0],
126
+ store,
127
+ );
128
+
129
+ expect(result.isError).toBe(false);
130
+ expect(createdParams?.name).toBe("New App");
131
+ });
132
+
92
133
  test("creates multifile app with src/ scaffold", async () => {
93
134
  const files: Record<string, string> = {};
94
135
  let createdParams: Record<string, unknown> | undefined;
@@ -408,3 +449,94 @@ describe("executeAppRefresh", () => {
408
449
  expect(parsed.error).toContain("not found");
409
450
  });
410
451
  });
452
+
453
+ // ---------------------------------------------------------------------------
454
+ // executeAppUpdate
455
+ // ---------------------------------------------------------------------------
456
+
457
+ describe("executeAppUpdate", () => {
458
+ test("updates metadata and recompiles a multifile app", async () => {
459
+ const app = makeMultifileApp({ name: "Old", description: "old desc" });
460
+ let updateArgs: unknown;
461
+ const store: AppStore = {
462
+ ...mockStore(app),
463
+ updateApp: (_id, updates) => {
464
+ updateArgs = updates;
465
+ return { ...app, ...updates };
466
+ },
467
+ };
468
+
469
+ const result = await executeAppUpdate(
470
+ { app_id: app.id, name: "New Name", description: "new desc" },
471
+ store,
472
+ );
473
+
474
+ expect(result.isError).toBe(false);
475
+ expect(updateArgs).toEqual({ name: "New Name", description: "new desc" });
476
+ const parsed = JSON.parse(result.content);
477
+ expect(parsed.updated).toBe(true);
478
+ expect(parsed.name).toBe("New Name");
479
+ expect(parsed.description).toBe("new desc");
480
+ expect(parsed.compiled).toBe(true);
481
+ });
482
+
483
+ test("writes source_files before recompiling", async () => {
484
+ const files: Record<string, string> = {};
485
+ const app = makeMultifileApp();
486
+ const store: AppStore = {
487
+ ...mockStore(app, files),
488
+ updateApp: (_id, updates) => ({ ...app, ...updates }),
489
+ };
490
+
491
+ const result = await executeAppUpdate(
492
+ { app_id: app.id, source_files: { "src/App.tsx": "// new code" } },
493
+ store,
494
+ );
495
+
496
+ expect(result.isError).toBe(false);
497
+ expect(files["src/App.tsx"]).toBe("// new code");
498
+ expect(JSON.parse(result.content).compiled).toBe(true);
499
+ });
500
+
501
+ test("legacy app: updates without compiling", async () => {
502
+ const app = makeLegacyApp({ name: "Legacy" });
503
+ const store: AppStore = {
504
+ ...mockStore(app),
505
+ updateApp: (_id, updates) => ({ ...app, ...updates }),
506
+ };
507
+
508
+ const result = await executeAppUpdate(
509
+ { app_id: app.id, name: "Renamed" },
510
+ store,
511
+ );
512
+
513
+ expect(result.isError).toBe(false);
514
+ const parsed = JSON.parse(result.content);
515
+ expect(parsed.updated).toBe(true);
516
+ expect(parsed.name).toBe("Renamed");
517
+ expect(parsed.compiled).toBeUndefined();
518
+ });
519
+
520
+ test("returns error for unknown app", async () => {
521
+ const store = mockStore(makeMultifileApp());
522
+ const result = await executeAppUpdate({ app_id: "nope" }, store);
523
+
524
+ expect(result.isError).toBe(true);
525
+ expect(JSON.parse(result.content).error).toContain("not found");
526
+ });
527
+
528
+ test("rejects invalid source_files", async () => {
529
+ const app = makeMultifileApp();
530
+ const store = mockStore(app);
531
+ const result = await executeAppUpdate(
532
+ {
533
+ app_id: app.id,
534
+ source_files: { "src/App.tsx": 123 as unknown as string },
535
+ },
536
+ store,
537
+ );
538
+
539
+ expect(result.isError).toBe(true);
540
+ expect(JSON.parse(result.content).error).toContain("must be a string");
541
+ });
542
+ });
@@ -173,9 +173,10 @@ mock.module("../memory/retriever.js", () => ({
173
173
  injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
174
174
  }));
175
175
 
176
- mock.module("../context/window-manager.js", () => ({
176
+ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
177
177
  ContextWindowManager: class {
178
178
  constructor() {}
179
+ updateConfig() {}
179
180
  shouldCompact() {
180
181
  return { needed: false, estimatedTokens: 0 };
181
182
  }
@@ -208,10 +209,10 @@ mock.module("../agent/loop.js", () => ({
208
209
  getActiveModel() {
209
210
  return undefined;
210
211
  }
211
- async run(
212
- _messages: Message[],
213
- _onEvent: (event: AgentEvent) => void,
214
- ): Promise<Message[]> {
212
+ async run(_options: {
213
+ messages: Message[];
214
+ onEvent: (event: AgentEvent) => void;
215
+ }): Promise<Message[]> {
215
216
  return [];
216
217
  }
217
218
  },
@@ -119,12 +119,15 @@ mock.module("../permissions/trust-store.js", () => ({
119
119
  // ---------------------------------------------------------------------------
120
120
  let _conversationFactory: (() => Conversation) | undefined;
121
121
 
122
- mock.module("../daemon/conversation-store.js", () => ({
122
+ mock.module("../daemon/conversation-registry.js", () => ({
123
123
  findConversation: () => {
124
124
  // Return the current test session for any conversation ID lookup.
125
125
  if (!_conversationFactory) return undefined;
126
126
  return _conversationFactory();
127
127
  },
128
+ }));
129
+
130
+ mock.module("../daemon/conversation-store.js", () => ({
128
131
  getOrCreateConversation: async () => {
129
132
  if (!_conversationFactory)
130
133
  throw new Error("_conversationFactory not set in test");
@@ -117,7 +117,7 @@ mock.module("../memory/conversation-crud.js", () => ({
117
117
  findMostRecentRetrospectiveFor: mock(() => null),
118
118
  forkConversation: mock(() => ({ id: "conv-fork" })),
119
119
  getConversationOverrideProfile: () => undefined,
120
- getConversationOverrideProfileFromRow: () => undefined,
120
+ resolveOverrideProfile: () => undefined,
121
121
  getConversationMemoryScopeId: () => "default",
122
122
  getConversationOriginChannel: () => null,
123
123
  getConversationOriginInterface: () => null,
@@ -54,7 +54,10 @@ mock.module("../memory/llm-request-log-store.js", () => ({
54
54
  }));
55
55
 
56
56
  // ── Imports (after mocks) ─────────────────────────────────────────────────────
57
- import { buildPersistedAssistantContent } from "../daemon/conversation-agent-loop-handlers.js";
57
+ import {
58
+ buildPersistedAssistantContent,
59
+ stampThinkingTiming,
60
+ } from "../daemon/conversation-agent-loop-handlers.js";
58
61
  import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
59
62
  import type { ContentBlock } from "../providers/types.js";
60
63
 
@@ -182,3 +185,74 @@ describe("buildPersistedAssistantContent — native activityMetadata", () => {
182
185
  expect(block._activityMetadata).toBeUndefined();
183
186
  });
184
187
  });
188
+
189
+ describe("stampThinkingTiming", () => {
190
+ test("stamps internal timing onto thinking blocks by position", () => {
191
+ // GIVEN a turn that interleaves text and two thinking blocks AND the
192
+ // per-block timing captured while streaming (one entry per thinking block,
193
+ // in stream order)
194
+ const content = [
195
+ { type: "thinking", thinking: "first", signature: "s1" },
196
+ { type: "text", text: "answer" },
197
+ { type: "thinking", thinking: "second", signature: "s2" },
198
+ ] as unknown as ContentBlock[];
199
+ const timings = [
200
+ { startedAt: 100, completedAt: 250 },
201
+ { startedAt: 400, completedAt: 480 },
202
+ ];
203
+
204
+ // WHEN the content is stamped before persistence
205
+ const stamped = stampThinkingTiming(content, timings) as unknown as Array<
206
+ Record<string, unknown>
207
+ >;
208
+
209
+ // THEN each thinking block carries the `_`-prefixed timing for its position
210
+ expect(stamped[0]).toMatchObject({
211
+ type: "thinking",
212
+ _startedAt: 100,
213
+ _completedAt: 250,
214
+ });
215
+ expect(stamped[2]).toMatchObject({
216
+ type: "thinking",
217
+ _startedAt: 400,
218
+ _completedAt: 480,
219
+ });
220
+ // AND the interleaved text block is left untouched
221
+ expect(stamped[1]).toEqual({ type: "text", text: "answer" });
222
+ });
223
+
224
+ test("leaves thinking blocks unstamped when no timing was captured", () => {
225
+ // GIVEN thinking content but an empty timing list (thinking streaming was
226
+ // disabled, so no per-block timing was recorded this turn)
227
+ const content = [
228
+ { type: "thinking", thinking: "first", signature: "s1" },
229
+ ] as unknown as ContentBlock[];
230
+
231
+ // WHEN the content is stamped with no timing
232
+ const stamped = stampThinkingTiming(content, []);
233
+
234
+ // THEN the original content is returned unchanged so the UI hides duration,
235
+ // exactly as a tool call with no timing
236
+ expect(stamped).toBe(content);
237
+ expect(stamped[0]).not.toHaveProperty("_startedAt");
238
+ });
239
+
240
+ test("stamps only the thinking blocks that have a matching timing entry", () => {
241
+ // GIVEN two thinking blocks but only one captured timing entry (e.g. the
242
+ // second block opened after the timing array was already finalized)
243
+ const content = [
244
+ { type: "thinking", thinking: "first", signature: "s1" },
245
+ { type: "thinking", thinking: "second", signature: "s2" },
246
+ ] as unknown as ContentBlock[];
247
+ const timings = [{ startedAt: 100, completedAt: 250 }];
248
+
249
+ // WHEN the content is stamped
250
+ const stamped = stampThinkingTiming(content, timings) as unknown as Array<
251
+ Record<string, unknown>
252
+ >;
253
+
254
+ // THEN the first block is stamped and the unmatched second block is left as-is
255
+ expect(stamped[0]).toMatchObject({ _startedAt: 100, _completedAt: 250 });
256
+ expect(stamped[1]).not.toHaveProperty("_startedAt");
257
+ });
258
+ });
@@ -16,7 +16,7 @@ mock.module("../util/logger.js", () => ({
16
16
  }));
17
17
 
18
18
  const _conversationMocks = new Map<string, unknown>();
19
- mock.module("../daemon/conversation-store.js", () => ({
19
+ mock.module("../daemon/conversation-registry.js", () => ({
20
20
  findConversation: (id: string) => _conversationMocks.get(id),
21
21
  }));
22
22
 
@@ -11,7 +11,7 @@ mock.module("../util/logger.js", () => ({
11
11
 
12
12
  // Map conversationId → mock session so findConversation returns the right mock.
13
13
  const conversationMocks = new Map<string, unknown>();
14
- mock.module("../daemon/conversation-store.js", () => ({
14
+ mock.module("../daemon/conversation-registry.js", () => ({
15
15
  findConversation: (id: string) => conversationMocks.get(id),
16
16
  }));
17
17