@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,109 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { preModelCallSanitize } from "../agent/loop.js";
4
+ import type { Message } from "../providers/types.js";
5
+
6
+ /**
7
+ * `preModelCallSanitize` is the loop's single pre-send transform: it converts
8
+ * historical `web_search_tool_result` blocks to text alongside the media and
9
+ * AX-tree strips, so every provider call — first call, post-compaction, and
10
+ * recovery reruns — is sanitized in one place. These tests guard that the
11
+ * helper actually performs the web-search conversion and is idempotent.
12
+ */
13
+ describe("preModelCallSanitize", () => {
14
+ test("passes through history with nothing to sanitize", () => {
15
+ // GIVEN a plain conversation with no media, AX trees, or web-search blocks
16
+ const messages: Message[] = [
17
+ { role: "user", content: [{ type: "text", text: "Hello" }] },
18
+ { role: "assistant", content: [{ type: "text", text: "Hi" }] },
19
+ ];
20
+
21
+ // WHEN the loop sanitizes the outbound history
22
+ const result = preModelCallSanitize(messages);
23
+
24
+ // THEN the history is returned unchanged
25
+ expect(result).toEqual(messages);
26
+ });
27
+
28
+ test("converts historical web_search_tool_result blocks to text summaries", () => {
29
+ // GIVEN an assistant turn whose web_search_tool_result carries an opaque
30
+ // encrypted_content token that would be rejected if replayed
31
+ const messages: Message[] = [
32
+ { role: "user", content: [{ type: "text", text: "Search cats" }] },
33
+ {
34
+ role: "assistant",
35
+ content: [
36
+ {
37
+ type: "server_tool_use",
38
+ id: "stu_1",
39
+ name: "web_search",
40
+ input: { query: "cats" },
41
+ },
42
+ {
43
+ type: "web_search_tool_result",
44
+ tool_use_id: "stu_1",
45
+ content: [
46
+ {
47
+ type: "web_search_result",
48
+ url: "https://cats.com",
49
+ title: "Cats!",
50
+ encrypted_content: "expired_token_1",
51
+ },
52
+ ],
53
+ },
54
+ ],
55
+ },
56
+ ];
57
+
58
+ // WHEN the loop sanitizes the outbound history
59
+ const result = preModelCallSanitize(messages);
60
+
61
+ // THEN the opaque block is replaced with a plaintext title+URL summary and
62
+ // the paired server_tool_use is dropped, so no expired token is replayed
63
+ const assistantMsg = result[1];
64
+ expect(assistantMsg.content.map((b) => b.type)).toEqual(["text"]);
65
+ const summary = assistantMsg.content[0];
66
+ expect(summary.type).toBe("text");
67
+ if (summary.type === "text") {
68
+ expect(summary.text).toContain("Cats!");
69
+ expect(summary.text).toContain("https://cats.com");
70
+ expect(summary.text).not.toContain("expired_token_1");
71
+ }
72
+ });
73
+
74
+ test("is idempotent — re-sanitizing already-sanitized history is a no-op", () => {
75
+ // GIVEN history that has already been sanitized once
76
+ const messages: Message[] = [
77
+ {
78
+ role: "assistant",
79
+ content: [
80
+ {
81
+ type: "server_tool_use",
82
+ id: "stu_A",
83
+ name: "web_search",
84
+ input: { query: "alpha" },
85
+ },
86
+ {
87
+ type: "web_search_tool_result",
88
+ tool_use_id: "stu_A",
89
+ content: [
90
+ {
91
+ type: "web_search_result",
92
+ url: "https://a.example",
93
+ title: "A",
94
+ encrypted_content: "tok_A",
95
+ },
96
+ ],
97
+ },
98
+ ],
99
+ },
100
+ ];
101
+ const once = preModelCallSanitize(messages);
102
+
103
+ // WHEN it is sanitized a second time (every outbound call re-runs the helper)
104
+ const twice = preModelCallSanitize(once);
105
+
106
+ // THEN the second pass changes nothing
107
+ expect(twice).toEqual(once);
108
+ });
109
+ });
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Tests that the auto-redeploy path deploys the app's *effective* HTML
3
+ * (dist/index.html for multifile apps) rather than the empty `htmlDefinition`.
4
+ */
5
+
6
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
7
+
8
+ import type { AppDefinition } from "../memory/app-store.js";
9
+
10
+ // ── Mocks ───────────────────────────────────────────────────────────────
11
+
12
+ let mockApp: AppDefinition | null = null;
13
+ let mockIsMultifile = false;
14
+ let mockEffectiveHtml = "";
15
+ // A directory that does not exist on disk, so the multifile dist-existence
16
+ // guard reads `false` from the real fs without mocking node:fs (which would
17
+ // leak across test files).
18
+ let mockAppDir = "/tmp/__vellum_test_nonexistent__/app-1";
19
+
20
+ mock.module("../memory/app-store.js", () => ({
21
+ getApp: () => mockApp,
22
+ getAppDirPath: () => mockAppDir,
23
+ isMultifileApp: () => mockIsMultifile,
24
+ resolveEffectiveAppHtml: () => mockEffectiveHtml,
25
+ }));
26
+
27
+ let mockPublishedPage: {
28
+ id: string;
29
+ projectSlug?: string;
30
+ htmlHash: string;
31
+ } | null = null;
32
+ const updatePublishedPageSpy = mock(() => {});
33
+
34
+ mock.module("../memory/published-pages-store.js", () => ({
35
+ getActivePublishedPageByAppId: () => mockPublishedPage,
36
+ updatePublishedPage: updatePublishedPageSpy,
37
+ }));
38
+
39
+ const deploySpy = mock(async (_args: { html: string; name: string }) => ({
40
+ deploymentId: "dep-1",
41
+ url: "https://example.vercel.app",
42
+ }));
43
+
44
+ mock.module("../services/vercel-deploy.js", () => ({
45
+ deployHtmlToVercel: deploySpy,
46
+ }));
47
+
48
+ // credentialBroker.serverUse invokes execute(token) and reports success.
49
+ mock.module("../tools/credentials/broker.js", () => ({
50
+ credentialBroker: {
51
+ serverUse: async ({
52
+ execute,
53
+ }: {
54
+ execute: (token: string) => Promise<unknown>;
55
+ }) => {
56
+ await execute("test-token");
57
+ return { success: true };
58
+ },
59
+ },
60
+ }));
61
+
62
+ const { updatePublishedAppDeployment } =
63
+ await import("../services/published-app-updater.js");
64
+
65
+ function makeApp(overrides: Partial<AppDefinition> = {}): AppDefinition {
66
+ return {
67
+ id: "app-1",
68
+ name: "App",
69
+ schemaJson: "{}",
70
+ htmlDefinition: "",
71
+ createdAt: 1,
72
+ updatedAt: 2,
73
+ ...overrides,
74
+ };
75
+ }
76
+
77
+ // ── Tests ───────────────────────────────────────────────────────────────
78
+
79
+ describe("updatePublishedAppDeployment", () => {
80
+ beforeEach(() => {
81
+ mockApp = makeApp();
82
+ mockIsMultifile = false;
83
+ mockEffectiveHtml = "";
84
+ mockPublishedPage = { id: "pp-1", projectSlug: "slug", htmlHash: "old" };
85
+ mockAppDir = "/tmp/__vellum_test_nonexistent__/app-1";
86
+ deploySpy.mockClear();
87
+ updatePublishedPageSpy.mockClear();
88
+ });
89
+
90
+ test("deploys the resolved effective HTML, not the empty htmlDefinition", async () => {
91
+ // htmlDefinition is "" (as it is for every multifile app); the real
92
+ // content comes from resolveEffectiveAppHtml. isMultifile=false here keeps
93
+ // the dist guard out of the way so the assertion targets the html source.
94
+ mockApp = makeApp({ htmlDefinition: "" });
95
+ mockEffectiveHtml = "<html><body>real app</body></html>";
96
+
97
+ await updatePublishedAppDeployment("app-1");
98
+
99
+ expect(deploySpy).toHaveBeenCalledTimes(1);
100
+ expect(deploySpy.mock.calls[0][0].html).toBe(
101
+ "<html><body>real app</body></html>",
102
+ );
103
+ });
104
+
105
+ test("skips deploy when a multifile app has no compiled output", async () => {
106
+ mockIsMultifile = true;
107
+ mockApp = makeApp({ formatVersion: 2 });
108
+ mockEffectiveHtml = "<p>App compilation failed.</p>";
109
+ // mockAppDir does not exist → dist/index.html is absent.
110
+
111
+ await updatePublishedAppDeployment("app-1");
112
+
113
+ expect(deploySpy).not.toHaveBeenCalled();
114
+ });
115
+
116
+ test("skips deploy when content hash is unchanged", async () => {
117
+ const html = "<html>same</html>";
118
+ mockEffectiveHtml = html;
119
+ const { createHash } = await import("node:crypto");
120
+ mockPublishedPage = {
121
+ id: "pp-1",
122
+ htmlHash: createHash("sha256").update(html).digest("hex"),
123
+ };
124
+
125
+ await updatePublishedAppDeployment("app-1");
126
+
127
+ expect(deploySpy).not.toHaveBeenCalled();
128
+ });
129
+
130
+ test("skips deploy when the app has no active published page", async () => {
131
+ mockPublishedPage = null;
132
+ mockEffectiveHtml = "<html>x</html>";
133
+
134
+ await updatePublishedAppDeployment("app-1");
135
+
136
+ expect(deploySpy).not.toHaveBeenCalled();
137
+ });
138
+ });
@@ -27,7 +27,7 @@ mock.module("../config/env.js", () => ({
27
27
  }));
28
28
 
29
29
  const _conversationMocks = new Map<string, unknown>();
30
- mock.module("../daemon/conversation-store.js", () => ({
30
+ mock.module("../daemon/conversation-registry.js", () => ({
31
31
  findConversation: (id: string) => _conversationMocks.get(id),
32
32
  }));
33
33
 
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Verifies that `RetryProvider.normalizeSendMessageOptions` drops a disabled
3
+ * thinking config for adaptive-thinking-only models (Claude Fable), preventing
4
+ * an Anthropic 400: Fable always reasons with always-on adaptive thinking and
5
+ * rejects `thinking: { type: "disabled" }`.
6
+ *
7
+ * Effort and other parameters are unaffected, and models that genuinely support
8
+ * disabling thinking (e.g. Opus) keep their disabled config.
9
+ */
10
+
11
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
12
+
13
+ mock.module("../util/logger.js", () => ({
14
+ getLogger: () =>
15
+ new Proxy({} as Record<string, unknown>, { get: () => () => {} }),
16
+ }));
17
+
18
+ let mockLlmConfig: Record<string, unknown> = {};
19
+
20
+ mock.module("../config/loader.js", () => ({
21
+ getConfig: () => ({ llm: mockLlmConfig }),
22
+ }));
23
+
24
+ import { LLMSchema } from "../config/schemas/llm.js";
25
+ import { RetryProvider } from "../providers/retry.js";
26
+ import type {
27
+ Message,
28
+ Provider,
29
+ ProviderResponse,
30
+ SendMessageOptions,
31
+ } from "../providers/types.js";
32
+
33
+ function setLlmConfig(raw: unknown): void {
34
+ mockLlmConfig = LLMSchema.parse(raw) as Record<string, unknown>;
35
+ }
36
+
37
+ beforeEach(() => {
38
+ mockLlmConfig = LLMSchema.parse({}) as Record<string, unknown>;
39
+ });
40
+
41
+ function makePipeline(providerName: string): {
42
+ provider: Provider;
43
+ lastConfig: () => Record<string, unknown> | undefined;
44
+ } {
45
+ let captured: Record<string, unknown> | undefined;
46
+ const inner: Provider = {
47
+ name: providerName,
48
+ async sendMessage(
49
+ _messages: Message[],
50
+ options?: SendMessageOptions,
51
+ ): Promise<ProviderResponse> {
52
+ captured = options?.config as Record<string, unknown> | undefined;
53
+ return {
54
+ content: [],
55
+ model: "test",
56
+ usage: { inputTokens: 0, outputTokens: 0 },
57
+ stopReason: "stop",
58
+ };
59
+ },
60
+ };
61
+ return {
62
+ provider: new RetryProvider(inner),
63
+ lastConfig: () => captured,
64
+ };
65
+ }
66
+
67
+ const userMessage: Message = {
68
+ role: "user",
69
+ content: [{ type: "text", text: "hi" }],
70
+ };
71
+
72
+ describe("retry normalization: adaptive-only thinking models", () => {
73
+ test("drops resolved thinking: disabled for Claude Fable", async () => {
74
+ // GIVEN a profile that disables thinking for an adaptive-only Fable model
75
+ setLlmConfig({
76
+ default: {
77
+ provider: "anthropic",
78
+ model: "claude-fable-5",
79
+ thinking: { enabled: false },
80
+ effort: "high",
81
+ },
82
+ });
83
+ const { provider, lastConfig } = makePipeline("anthropic");
84
+
85
+ // WHEN a request resolves through the call-site config
86
+ await provider.sendMessage([userMessage], {
87
+ config: { callSite: "memoryExtraction" },
88
+ });
89
+
90
+ // THEN the disabled thinking config is dropped so Fable falls back to its
91
+ // always-on adaptive thinking instead of 400-ing
92
+ expect(lastConfig()?.thinking).toBeUndefined();
93
+ // AND effort is still forwarded
94
+ expect(lastConfig()?.effort).toBe("high");
95
+ });
96
+
97
+ test("drops explicit thinking: disabled from pass-through callers for Fable", async () => {
98
+ // GIVEN a pass-through caller supplying the wire-shape disabled config
99
+ const { provider, lastConfig } = makePipeline("anthropic");
100
+
101
+ // WHEN sending against a Fable model
102
+ await provider.sendMessage([userMessage], {
103
+ config: {
104
+ model: "claude-fable-5",
105
+ thinking: { type: "disabled" },
106
+ },
107
+ });
108
+
109
+ // THEN the disabled thinking config is dropped
110
+ expect(lastConfig()?.thinking).toBeUndefined();
111
+ });
112
+
113
+ test("drops disabled thinking for OpenRouter-proxied Fable", async () => {
114
+ // GIVEN a profile disabling thinking on the OpenRouter-proxied Fable id
115
+ setLlmConfig({
116
+ default: {
117
+ provider: "openrouter",
118
+ model: "anthropic/claude-fable-5",
119
+ thinking: { enabled: false },
120
+ },
121
+ });
122
+ const { provider, lastConfig } = makePipeline("openrouter");
123
+
124
+ // WHEN a request resolves through the call-site config
125
+ await provider.sendMessage([userMessage], {
126
+ config: { callSite: "memoryExtraction" },
127
+ });
128
+
129
+ // THEN the disabled thinking config is dropped
130
+ expect(lastConfig()?.thinking).toBeUndefined();
131
+ });
132
+
133
+ test("preserves adaptive thinking for Fable", async () => {
134
+ // GIVEN a profile that enables thinking for a Fable model
135
+ setLlmConfig({
136
+ default: {
137
+ provider: "anthropic",
138
+ model: "claude-fable-5",
139
+ thinking: { enabled: true },
140
+ },
141
+ });
142
+ const { provider, lastConfig } = makePipeline("anthropic");
143
+
144
+ // WHEN a request resolves through the call-site config
145
+ await provider.sendMessage([userMessage], {
146
+ config: { callSite: "memoryExtraction" },
147
+ });
148
+
149
+ // THEN adaptive thinking is preserved (only disabled is dropped)
150
+ expect(lastConfig()?.thinking).toEqual({ type: "adaptive" });
151
+ });
152
+
153
+ test("preserves disabled thinking for models that support disabling it", async () => {
154
+ // GIVEN a profile disabling thinking for a non-adaptive-only model
155
+ setLlmConfig({
156
+ default: {
157
+ provider: "anthropic",
158
+ model: "claude-opus-4-7",
159
+ thinking: { enabled: false },
160
+ },
161
+ });
162
+ const { provider, lastConfig } = makePipeline("anthropic");
163
+
164
+ // WHEN a request resolves through the call-site config
165
+ await provider.sendMessage([userMessage], {
166
+ config: { callSite: "memoryExtraction" },
167
+ });
168
+
169
+ // THEN the disabled thinking config is preserved for Opus
170
+ expect(lastConfig()?.thinking).toEqual({ type: "disabled" });
171
+ });
172
+
173
+ test("drops non-1 temperature for Fable when thinking is disabled", async () => {
174
+ // GIVEN a Fable profile that disables thinking and sets a non-1 temperature
175
+ setLlmConfig({
176
+ default: {
177
+ provider: "anthropic",
178
+ model: "claude-fable-5",
179
+ thinking: { enabled: false },
180
+ temperature: 0.7,
181
+ },
182
+ });
183
+ const { provider, lastConfig } = makePipeline("anthropic");
184
+
185
+ // WHEN a request resolves through the call-site config
186
+ await provider.sendMessage([userMessage], {
187
+ config: { callSite: "memoryExtraction" },
188
+ });
189
+
190
+ // THEN the disabled thinking is dropped (Fable falls back to adaptive) AND
191
+ // the non-1 temperature is dropped, since adaptive mode requires
192
+ // temperature: 1 — leaving it would 400 the request
193
+ expect(lastConfig()?.thinking).toBeUndefined();
194
+ expect(lastConfig()?.temperature).toBeUndefined();
195
+ });
196
+
197
+ test("drops non-1 temperature for Fable with no explicit thinking config", async () => {
198
+ // GIVEN a pass-through caller that sets a non-1 temperature and no thinking
199
+ const { provider, lastConfig } = makePipeline("anthropic");
200
+
201
+ // WHEN sending against a Fable model
202
+ await provider.sendMessage([userMessage], {
203
+ config: {
204
+ model: "claude-fable-5",
205
+ temperature: 0.5,
206
+ },
207
+ });
208
+
209
+ // THEN the temperature is dropped: Fable is always in adaptive mode, so the
210
+ // temperature: 1 constraint applies even without an explicit thinking config
211
+ expect(lastConfig()?.temperature).toBeUndefined();
212
+ });
213
+
214
+ test("drops non-1 temperature for OpenRouter-proxied Fable", async () => {
215
+ // GIVEN a pass-through caller on the OpenRouter-proxied Fable id
216
+ const { provider, lastConfig } = makePipeline("openrouter");
217
+
218
+ // WHEN sending with a non-1 temperature and no explicit thinking config
219
+ await provider.sendMessage([userMessage], {
220
+ config: {
221
+ model: "anthropic/claude-fable-5",
222
+ temperature: 0.2,
223
+ },
224
+ });
225
+
226
+ // THEN the temperature is dropped for the Anthropic-fronted Fable model
227
+ expect(lastConfig()?.temperature).toBeUndefined();
228
+ });
229
+
230
+ test("preserves temperature: 1 for Fable", async () => {
231
+ // GIVEN a Fable profile with the only adaptive-mode-valid temperature
232
+ const { provider, lastConfig } = makePipeline("anthropic");
233
+
234
+ // WHEN sending against a Fable model
235
+ await provider.sendMessage([userMessage], {
236
+ config: {
237
+ model: "claude-fable-5",
238
+ temperature: 1,
239
+ },
240
+ });
241
+
242
+ // THEN temperature: 1 is preserved (it is valid in adaptive mode)
243
+ expect(lastConfig()?.temperature).toBe(1);
244
+ });
245
+ });
@@ -91,11 +91,14 @@ mock.module("../config/loader.js", () => ({
91
91
  let _conversationFactory: (() => Conversation) | undefined;
92
92
  let _approvalGenerator: unknown;
93
93
 
94
- mock.module("../daemon/conversation-store.js", () => ({
94
+ mock.module("../daemon/conversation-registry.js", () => ({
95
95
  findConversation: () => {
96
96
  if (!_conversationFactory) return undefined;
97
97
  return _conversationFactory();
98
98
  },
99
+ }));
100
+
101
+ mock.module("../daemon/conversation-store.js", () => ({
99
102
  getOrCreateConversation: async (..._args: unknown[]) => {
100
103
  if (!_conversationFactory)
101
104
  throw new Error("_conversationFactory not set in test");
@@ -808,6 +808,45 @@ describe("renderHistoryContent contentBlocks", () => {
808
808
  );
809
809
  });
810
810
 
811
+ test("surfaces persisted thinking timing onto the thinking block", () => {
812
+ // GIVEN a thinking block the daemon stamped with internal `_startedAt` /
813
+ // `_completedAt` timing at message_complete
814
+ const output = renderHistoryContent([
815
+ {
816
+ type: "thinking",
817
+ thinking: "reasoning",
818
+ signature: "sig",
819
+ _startedAt: 1000,
820
+ _completedAt: 1500,
821
+ },
822
+ ]);
823
+
824
+ // THEN the rendered block exposes them as the wire schema's
825
+ // startedAt/completedAt so the client can show the duration + hover state
826
+ expect(output.contentBlocks).toEqual([
827
+ {
828
+ type: "thinking",
829
+ thinking: "reasoning",
830
+ startedAt: 1000,
831
+ completedAt: 1500,
832
+ },
833
+ ]);
834
+ });
835
+
836
+ test("omits thinking timing when the block carries none", () => {
837
+ // GIVEN a thinking block with no persisted timing (older daemon / thinking
838
+ // streaming disabled)
839
+ const output = renderHistoryContent([
840
+ { type: "thinking", thinking: "reasoning", signature: "sig" },
841
+ ]);
842
+
843
+ // THEN the rendered block has no startedAt/completedAt and the client hides
844
+ // the duration, exactly as a tool call with no timing
845
+ expect(output.contentBlocks).toEqual([
846
+ { type: "thinking", thinking: "reasoning" },
847
+ ]);
848
+ });
849
+
811
850
  const pdfBlock = {
812
851
  type: "file",
813
852
  source: {
@@ -5,6 +5,8 @@
5
5
  * parses it via the real frontmatter parser, and verifies that `skillFlagKey()`
6
6
  * returns the correct key and `resolveSkillStates()` correctly gates the skill.
7
7
  */
8
+ import { readFileSync } from "node:fs";
9
+ import { fileURLToPath } from "node:url";
8
10
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
9
11
 
10
12
  import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
@@ -225,3 +227,32 @@ describe("frontmatter feature-flag integration", () => {
225
227
  expect(resolvedOff.length).toBe(0);
226
228
  });
227
229
  });
230
+
231
+ // ---------------------------------------------------------------------------
232
+ // Bundled ACP skill: discoverability when ACP is disabled
233
+ // ---------------------------------------------------------------------------
234
+
235
+ describe("bundled acp skill discoverability", () => {
236
+ test("acp skill resolves with no frontmatter flag gate", () => {
237
+ // The ACP skill carries its own first-time-setup instructions and is
238
+ // always discoverable: it has no frontmatter feature-flag gate.
239
+ const skillMdPath = fileURLToPath(
240
+ new URL("../config/bundled-skills/acp/SKILL.md", import.meta.url),
241
+ );
242
+ const skillMd = readFileSync(skillMdPath, "utf8");
243
+
244
+ const skill = buildSkillSummary("acp", skillMd);
245
+ expect(skill).not.toBeNull();
246
+ expect(skill!.featureFlag).toBeUndefined();
247
+ expect(skillFlagKey(skill!)).toBeUndefined();
248
+
249
+ const config = makeConfig({
250
+ acp: { maxConcurrentSessions: 4, agents: {} },
251
+ });
252
+
253
+ const resolved = resolveSkillStates([skill!], config);
254
+ expect(resolved.length).toBe(1);
255
+ expect(resolved[0].summary.id).toBe("acp");
256
+ expect(resolved[0].state).toBe("enabled");
257
+ });
258
+ });
@@ -14,7 +14,7 @@ import type { Message } from "../providers/types.js";
14
14
  /**
15
15
  * Minimal reproduction of repairPendingToolUseBlocks logic for direct
16
16
  * unit testing. The real implementation lives in conversation-process.ts.
17
- * This mirrors it exactly to avoid needing the full ProcessConversationContext.
17
+ * This mirrors it exactly to avoid needing the full Conversation.
18
18
  */
19
19
  function repairPendingToolUseBlocks(messages: Message[]): Message[] {
20
20
  if (messages.length === 0) return messages;
@@ -174,7 +174,7 @@ import { LLMSchema } from "../config/schemas/llm.js";
174
174
  import {
175
175
  clearConversations,
176
176
  setConversation,
177
- } from "../daemon/conversation-store.js";
177
+ } from "../daemon/conversation-registry.js";
178
178
  import { CallSiteRoutingProvider } from "../providers/call-site-routing.js";
179
179
  import { SubagentManager } from "../subagent/manager.js";
180
180
 
@@ -18,6 +18,7 @@ function msg(role: string, content: unknown[]): MessageRow {
18
18
  content: JSON.stringify(content),
19
19
  createdAt: Date.now(),
20
20
  metadata: null,
21
+ clientMessageId: null,
21
22
  };
22
23
  }
23
24
 
@@ -26,10 +27,19 @@ describe("parseSubagentMessages", () => {
26
27
  const messages = [
27
28
  msg("user", [{ type: "text", text: "Do something" }]),
28
29
  msg("assistant", [
29
- { type: "tool_use", id: "t1", name: "web_search", input: { query: "test" } },
30
+ {
31
+ type: "tool_use",
32
+ id: "t1",
33
+ name: "web_search",
34
+ input: { query: "test" },
35
+ },
30
36
  ]),
31
37
  msg("user", [
32
- { type: "tool_result", tool_use_id: "t1", content: "Search results here" },
38
+ {
39
+ type: "tool_result",
40
+ tool_use_id: "t1",
41
+ content: "Search results here",
42
+ },
33
43
  ]),
34
44
  ];
35
45
 
@@ -44,7 +54,12 @@ describe("parseSubagentMessages", () => {
44
54
  const messages = [
45
55
  msg("user", [{ type: "text", text: "Do something" }]),
46
56
  msg("assistant", [
47
- { type: "tool_use", id: "t2", name: "file_read", input: { file_path: "/tmp/test.txt" } },
57
+ {
58
+ type: "tool_use",
59
+ id: "t2",
60
+ name: "file_read",
61
+ input: { file_path: "/tmp/test.txt" },
62
+ },
48
63
  ]),
49
64
  msg("user", [
50
65
  {
@@ -69,11 +84,14 @@ describe("parseSubagentMessages", () => {
69
84
  const messages = [
70
85
  msg("user", [{ type: "text", text: "Do something" }]),
71
86
  msg("assistant", [
72
- { type: "tool_use", id: "t3", name: "bash", input: { command: "echo hi" } },
73
- ]),
74
- msg("user", [
75
- { type: "tool_result", tool_use_id: "t3", content: null },
87
+ {
88
+ type: "tool_use",
89
+ id: "t3",
90
+ name: "bash",
91
+ input: { command: "echo hi" },
92
+ },
76
93
  ]),
94
+ msg("user", [{ type: "tool_result", tool_use_id: "t3", content: null }]),
77
95
  ];
78
96
 
79
97
  const result = parseSubagentMessages("sub-1", messages);