@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
@@ -137,9 +137,10 @@ mock.module("../memory/retrieval-budget.js", () => ({
137
137
  computeRecallBudget: () => 0,
138
138
  }));
139
139
 
140
- mock.module("../context/window-manager.js", () => ({
140
+ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
141
141
  ContextWindowManager: class {
142
142
  constructor() {}
143
+ updateConfig() {}
143
144
  shouldCompact() {
144
145
  return { needed: false, estimatedTokens: 0 };
145
146
  }
@@ -176,10 +177,11 @@ mock.module("../agent/loop.js", () => ({
176
177
  getActiveModel() {
177
178
  return undefined;
178
179
  }
179
- async run(
180
- messages: Message[],
181
- onEvent: (event: AgentEvent) => void,
182
- ): Promise<Message[]> {
180
+ async run(options: {
181
+ messages: Message[];
182
+ onEvent: (event: AgentEvent) => void;
183
+ }): Promise<Message[]> {
184
+ const { messages, onEvent } = options;
183
185
  const assistantMessage: Message = {
184
186
  role: "assistant",
185
187
  content: [{ type: "text", text: "ok" }],
@@ -198,6 +200,7 @@ mock.module("../agent/loop.js", () => ({
198
200
  }));
199
201
 
200
202
  import { Conversation } from "../daemon/conversation.js";
203
+ import { refreshWorkspaceTopLevelContextIfNeeded } from "../daemon/conversation-workspace.js";
201
204
 
202
205
  // ---------------------------------------------------------------------------
203
206
  // Helpers
@@ -249,7 +252,7 @@ describe("Conversation workspace cache state", () => {
249
252
  });
250
253
 
251
254
  test("refreshWorkspaceTopLevelContextIfNeeded populates context and clears dirty", () => {
252
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
255
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
253
256
 
254
257
  expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
255
258
  expect(conversation.getWorkspaceTopLevelContext()).not.toBeNull();
@@ -265,10 +268,10 @@ describe("Conversation workspace cache state", () => {
265
268
  });
266
269
 
267
270
  test("refreshWorkspaceTopLevelContextIfNeeded no-ops when not dirty and cache exists", () => {
268
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
271
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
269
272
  const first = conversation.getWorkspaceTopLevelContext();
270
273
 
271
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
274
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
272
275
  const second = conversation.getWorkspaceTopLevelContext();
273
276
 
274
277
  // Same reference — no recomputation
@@ -276,7 +279,7 @@ describe("Conversation workspace cache state", () => {
276
279
  });
277
280
 
278
281
  test("markWorkspaceTopLevelDirty sets dirty flag", () => {
279
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
282
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
280
283
  expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
281
284
 
282
285
  conversation.markWorkspaceTopLevelDirty();
@@ -284,10 +287,10 @@ describe("Conversation workspace cache state", () => {
284
287
  });
285
288
 
286
289
  test("refresh after marking dirty produces fresh context", () => {
287
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
290
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
288
291
 
289
292
  conversation.markWorkspaceTopLevelDirty();
290
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
293
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
291
294
 
292
295
  expect(conversation.getWorkspaceTopLevelContext()).not.toBeNull();
293
296
  expect(conversation.getWorkspaceTopLevelContext()!).toContain(
@@ -299,7 +302,7 @@ describe("Conversation workspace cache state", () => {
299
302
  test("renders client-reported host env when set on the conversation", () => {
300
303
  conversation.hostHomeDir = "/Users/alice";
301
304
  conversation.hostUsername = "alice";
302
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
305
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
303
306
 
304
307
  const block = conversation.getWorkspaceTopLevelContext();
305
308
  expect(block).not.toBeNull();
@@ -309,7 +312,7 @@ describe("Conversation workspace cache state", () => {
309
312
 
310
313
  test("falls back to daemon os info when client host env is absent", async () => {
311
314
  const { homedir, userInfo } = await import("node:os");
312
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
315
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
313
316
 
314
317
  const block = conversation.getWorkspaceTopLevelContext();
315
318
  expect(block).not.toBeNull();
@@ -320,7 +323,7 @@ describe("Conversation workspace cache state", () => {
320
323
  test("re-renders with updated host env after marking dirty", () => {
321
324
  conversation.hostHomeDir = "/Users/alice";
322
325
  conversation.hostUsername = "alice";
323
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
326
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
324
327
  expect(conversation.getWorkspaceTopLevelContext()!).toContain(
325
328
  "Host home directory: /Users/alice",
326
329
  );
@@ -328,7 +331,7 @@ describe("Conversation workspace cache state", () => {
328
331
  conversation.hostHomeDir = "/Users/bob";
329
332
  conversation.hostUsername = "bob";
330
333
  conversation.markWorkspaceTopLevelDirty();
331
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
334
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
332
335
 
333
336
  const block = conversation.getWorkspaceTopLevelContext();
334
337
  expect(block).not.toBeNull();
@@ -344,7 +347,7 @@ describe("Conversation workspace cache state", () => {
344
347
  // Simulate a macOS turn populating host env.
345
348
  conversation.hostHomeDir = "/Users/alice";
346
349
  conversation.hostUsername = "alice";
347
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
350
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
348
351
  expect(conversation.getWorkspaceTopLevelContext()!).toContain(
349
352
  "Host home directory: /Users/alice",
350
353
  );
@@ -355,7 +358,7 @@ describe("Conversation workspace cache state", () => {
355
358
  conversation.hostHomeDir = undefined;
356
359
  conversation.hostUsername = undefined;
357
360
  conversation.markWorkspaceTopLevelDirty();
358
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
361
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
359
362
 
360
363
  const block = conversation.getWorkspaceTopLevelContext();
361
364
  expect(block).not.toBeNull();
@@ -381,7 +384,7 @@ describe("Conversation workspace cache state", () => {
381
384
  expect(conversation.hostUsername).toBe("alice");
382
385
  expect(conversation.isWorkspaceTopLevelDirty()).toBe(true);
383
386
 
384
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
387
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
385
388
  const block = conversation.getWorkspaceTopLevelContext();
386
389
  expect(block!).toContain("Host home directory: /Users/alice");
387
390
  expect(block!).toContain("Host username: alice");
@@ -455,7 +458,7 @@ describe("Conversation workspace cache state", () => {
455
458
  hostUsername: "alice",
456
459
  });
457
460
  // Render once so the dirty flag clears.
458
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
461
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
459
462
  expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
460
463
 
461
464
  // Re-apply the same values — dirty flag should remain false so we don't
@@ -476,7 +479,7 @@ describe("Conversation workspace cache state", () => {
476
479
  hostHomeDir: "/Users/alice",
477
480
  hostUsername: "alice",
478
481
  });
479
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
482
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
480
483
  expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
481
484
 
482
485
  // New values — should mark dirty so the next render picks them up.
@@ -500,7 +503,7 @@ describe("Conversation workspace cache state", () => {
500
503
 
501
504
  try {
502
505
  const tempConversation = makeConversation(workspaceRoot);
503
- tempConversation.refreshWorkspaceTopLevelContextIfNeeded();
506
+ refreshWorkspaceTopLevelContextIfNeeded(tempConversation);
504
507
 
505
508
  expect(tempConversation.getWorkspaceTopLevelContext()!).toContain(
506
509
  `Current conversation attachments: conversations/${legacyDirName}/attachments/`,
@@ -144,7 +144,7 @@ mock.module("../memory/conversation-crud.js", () => ({
144
144
  getMessageById: () => null,
145
145
  getLastUserTimestampBefore: () => 0,
146
146
  setLastNotifiedInferenceProfile: () => {},
147
- getConversationOverrideProfileFromRow: () => undefined,
147
+ resolveOverrideProfile: () => undefined,
148
148
  updateMessageMetadata: () => {},
149
149
  reserveMessage: mock(async () => ({ id: "msg-reserve" })),
150
150
  updateMessageContent: mock(() => {}),
@@ -181,8 +181,9 @@ mock.module("../memory/query-builder.js", () => ({
181
181
  mock.module("../memory/retrieval-budget.js", () => ({
182
182
  computeRecallBudget: () => 0,
183
183
  }));
184
- mock.module("../context/window-manager.js", () => ({
184
+ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
185
185
  ContextWindowManager: class {
186
+ updateConfig() {}
186
187
  shouldCompact() {
187
188
  return { needed: false, estimatedTokens: 0 };
188
189
  }
@@ -244,10 +245,11 @@ mock.module("../agent/loop.js", () => ({
244
245
  getActiveModel() {
245
246
  return undefined;
246
247
  }
247
- async run(
248
- messages: Message[],
249
- onEvent: (event: AgentEvent) => void,
250
- ): Promise<AgentLoopRunResult> {
248
+ async run(options: {
249
+ messages: Message[];
250
+ onEvent: (event: AgentEvent) => void;
251
+ }): Promise<AgentLoopRunResult> {
252
+ const { messages, onEvent } = options;
251
253
  // Prime the assistant row anchor — production code emits this from
252
254
  // `AgentLoop.run` just before `provider.sendMessage`.
253
255
  await onEvent({ type: "llm_call_started" });
@@ -295,6 +297,14 @@ mock.module("../memory/canonical-guardian-store.js", () => ({
295
297
  }));
296
298
 
297
299
  import { Conversation } from "../daemon/conversation.js";
300
+ import {
301
+ clearConversations,
302
+ findConversation,
303
+ removeSubagentConversation,
304
+ setConversation,
305
+ setSubagentConversation,
306
+ } from "../daemon/conversation-registry.js";
307
+ import { resolveWorkspaceTopLevelContext } from "../daemon/conversation-workspace.js";
298
308
  import { resetPluginRegistryAndRegisterDefaults } from "../plugins/defaults/index.js";
299
309
 
300
310
  function makeConversation(): Conversation {
@@ -309,7 +319,7 @@ function makeConversation(): Conversation {
309
319
  };
310
320
  },
311
321
  };
312
- return new Conversation(
322
+ const conversation = new Conversation(
313
323
  "conv-1",
314
324
  provider,
315
325
  "system prompt",
@@ -317,6 +327,10 @@ function makeConversation(): Conversation {
317
327
  "/tmp",
318
328
  { maxTokens: 4096 },
319
329
  );
330
+ // Mirror production: top-level conversations are registered in the store, so
331
+ // the workspace injector can resolve them by id via the conversation registry.
332
+ setConversation(conversation.conversationId, conversation);
333
+ return conversation;
320
334
  }
321
335
 
322
336
  function messageText(message: Message): string {
@@ -335,6 +349,7 @@ describe("Conversation workspace injection", () => {
335
349
  runCalls = [];
336
350
  agentLoopScript = () => {};
337
351
  scanCallCount = 0;
352
+ clearConversations();
338
353
  resetPluginRegistryAndRegisterDefaults();
339
354
  });
340
355
 
@@ -417,11 +432,58 @@ describe("Conversation workspace injection", () => {
417
432
  });
418
433
  });
419
434
 
435
+ describe("Conversation workspace injection — subagents", () => {
436
+ beforeEach(() => {
437
+ scanCallCount = 0;
438
+ clearConversations();
439
+ resetPluginRegistryAndRegisterDefaults();
440
+ });
441
+
442
+ test("workspace context resolves for subagent conversations not in the store", () => {
443
+ const provider = {
444
+ name: "mock",
445
+ async sendMessage(): Promise<ProviderResponse> {
446
+ return {
447
+ content: [],
448
+ model: "mock",
449
+ usage: { inputTokens: 0, outputTokens: 0 },
450
+ stopReason: "end_turn",
451
+ };
452
+ },
453
+ };
454
+ const subagentId = "subagent-conv-1";
455
+ const conversation = new Conversation(
456
+ subagentId,
457
+ provider,
458
+ "system prompt",
459
+ () => {},
460
+ "/tmp",
461
+ { maxTokens: 4096 },
462
+ );
463
+ setSubagentConversation(subagentId, conversation);
464
+
465
+ try {
466
+ // Subagents live only in the manager's index, never in the
467
+ // eviction-managed store, so the store lookup must not see them.
468
+ expect(findConversation(subagentId)).toBeUndefined();
469
+
470
+ // The per-conversation workspace lookup still resolves them, so subagent
471
+ // turns retain workspace grounding.
472
+ const context = resolveWorkspaceTopLevelContext(subagentId);
473
+ expect(context).not.toBeNull();
474
+ expect(context).toContain("Root: /tmp");
475
+ } finally {
476
+ removeSubagentConversation(subagentId, conversation);
477
+ }
478
+ });
479
+ });
480
+
420
481
  describe("Conversation workspace dirty-refresh E2E", () => {
421
482
  beforeEach(() => {
422
483
  runCalls = [];
423
484
  agentLoopScript = () => {};
424
485
  scanCallCount = 0;
486
+ clearConversations();
425
487
  resetPluginRegistryAndRegisterDefaults();
426
488
  });
427
489
 
@@ -141,7 +141,7 @@ mock.module("../memory/conversation-crud.js", () => ({
141
141
  getMessageById: () => null,
142
142
  getLastUserTimestampBefore: () => 0,
143
143
  setLastNotifiedInferenceProfile: () => {},
144
- getConversationOverrideProfileFromRow: () => undefined,
144
+ resolveOverrideProfile: () => undefined,
145
145
  updateMessageMetadata: () => {},
146
146
  reserveMessage: mock(async () => ({ id: "msg-reserve" })),
147
147
  updateMessageContent: mock(() => {}),
@@ -178,8 +178,9 @@ mock.module("../memory/query-builder.js", () => ({
178
178
  mock.module("../memory/retrieval-budget.js", () => ({
179
179
  computeRecallBudget: () => 0,
180
180
  }));
181
- mock.module("../context/window-manager.js", () => ({
181
+ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
182
182
  ContextWindowManager: class {
183
+ updateConfig() {}
183
184
  shouldCompact() {
184
185
  return { needed: false, estimatedTokens: 0 };
185
186
  }
@@ -228,10 +229,11 @@ mock.module("../agent/loop.js", () => ({
228
229
  getActiveModel() {
229
230
  return undefined;
230
231
  }
231
- async run(
232
- messages: Message[],
233
- onEvent: (event: AgentEvent) => void,
234
- ): Promise<Message[]> {
232
+ async run(options: {
233
+ messages: Message[];
234
+ onEvent: (event: AgentEvent) => void;
235
+ }): Promise<Message[]> {
236
+ const { messages, onEvent } = options;
235
237
  // Prime the assistant row anchor — production code emits this from
236
238
  // `AgentLoop.run` just before `provider.sendMessage`.
237
239
  await onEvent({ type: "llm_call_started" });
@@ -273,6 +275,7 @@ mock.module("../memory/canonical-guardian-store.js", () => ({
273
275
  }));
274
276
 
275
277
  import { Conversation } from "../daemon/conversation.js";
278
+ import { refreshWorkspaceTopLevelContextIfNeeded } from "../daemon/conversation-workspace.js";
276
279
 
277
280
  function makeConversation(): Conversation {
278
281
  const provider = {
@@ -310,7 +313,7 @@ describe("Conversation workspace dirty on file mutations", () => {
310
313
  await conversation.loadFromDb();
311
314
 
312
315
  // Prime the cache so dirty=false
313
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
316
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
314
317
  expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
315
318
 
316
319
  agentLoopScript = (onEvent) => {
@@ -339,7 +342,7 @@ describe("Conversation workspace dirty on file mutations", () => {
339
342
  const conversation = makeConversation();
340
343
  await conversation.loadFromDb();
341
344
 
342
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
345
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
343
346
  expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
344
347
 
345
348
  agentLoopScript = (onEvent) => {
@@ -370,7 +373,7 @@ describe("Conversation workspace dirty on file mutations", () => {
370
373
  const conversation = makeConversation();
371
374
  await conversation.loadFromDb();
372
375
 
373
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
376
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
374
377
  expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
375
378
 
376
379
  agentLoopScript = (onEvent) => {
@@ -399,7 +402,7 @@ describe("Conversation workspace dirty on file mutations", () => {
399
402
  const conversation = makeConversation();
400
403
  await conversation.loadFromDb();
401
404
 
402
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
405
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
403
406
  expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
404
407
 
405
408
  agentLoopScript = (onEvent) => {
@@ -428,7 +431,7 @@ describe("Conversation workspace dirty on file mutations", () => {
428
431
  const conversation = makeConversation();
429
432
  await conversation.loadFromDb();
430
433
 
431
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
434
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
432
435
  expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
433
436
 
434
437
  agentLoopScript = (onEvent) => {
@@ -457,7 +460,7 @@ describe("Conversation workspace dirty on file mutations", () => {
457
460
  const conversation = makeConversation();
458
461
  await conversation.loadFromDb();
459
462
 
460
- conversation.refreshWorkspaceTopLevelContextIfNeeded();
463
+ refreshWorkspaceTopLevelContextIfNeeded(conversation);
461
464
  expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
462
465
 
463
466
  agentLoopScript = (onEvent) => {
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Tests that the conversations import route never persists non-renderable
3
+ * roles. The messages store is UI-facing (`ConversationMessage`), so an
4
+ * imported export carrying agent-context `system` rows must land only its
5
+ * `user`/`assistant` turns — the `system` rows are dropped, not persisted.
6
+ */
7
+
8
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
9
+
10
+ mock.module("../util/logger.js", () => ({
11
+ getLogger: () =>
12
+ new Proxy({} as Record<string, unknown>, {
13
+ get: () => () => {},
14
+ }),
15
+ }));
16
+
17
+ mock.module("../config/loader.js", () => ({
18
+ getConfig: () => ({
19
+ ui: {},
20
+ model: "test",
21
+ provider: "test",
22
+ memory: { enabled: false },
23
+ rateLimit: { maxRequestsPerMinute: 0 },
24
+ }),
25
+ }));
26
+
27
+ mock.module("../memory/indexer.js", () => ({
28
+ indexMessageNow: async () => {},
29
+ }));
30
+
31
+ import { getMessages } from "../memory/conversation-crud.js";
32
+ import { getDb } from "../memory/db-connection.js";
33
+ import { initializeDb } from "../memory/db-init.js";
34
+ import { conversations, messages } from "../memory/schema.js";
35
+ import { ROUTES } from "../runtime/routes/conversations-import-routes.js";
36
+ import type { RouteHandlerArgs } from "../runtime/routes/types.js";
37
+
38
+ initializeDb();
39
+
40
+ function resetTables() {
41
+ const db = getDb();
42
+ db.run("DELETE FROM message_attachments");
43
+ db.run("DELETE FROM attachments");
44
+ db.run("DELETE FROM messages");
45
+ db.run("DELETE FROM conversation_keys");
46
+ db.run("DELETE FROM conversations");
47
+ }
48
+
49
+ const importHandler = ROUTES.find(
50
+ (r) => r.operationId === "conversations_import",
51
+ )!.handler;
52
+
53
+ describe("conversations import system-row filtering", () => {
54
+ beforeEach(resetTables);
55
+
56
+ test("imports renderable turns but drops system rows", async () => {
57
+ // GIVEN an export whose conversation sandwiches a system row between two
58
+ // renderable turns (e.g. agent-context scaffolding an export carried)
59
+ const body = {
60
+ conversations: [
61
+ {
62
+ sourceKey: "src-1",
63
+ title: "Imported chat",
64
+ messages: [
65
+ { role: "user", content: "first visible" },
66
+ { role: "system", content: "agent-context scaffolding" },
67
+ { role: "assistant", content: "second visible" },
68
+ ],
69
+ },
70
+ ],
71
+ };
72
+
73
+ // WHEN the conversation is imported
74
+ const result = (await importHandler({
75
+ body,
76
+ } as unknown as RouteHandlerArgs)) as {
77
+ ok: boolean;
78
+ imported: number;
79
+ messages: number;
80
+ };
81
+
82
+ // THEN the import succeeds and only counts the renderable turns
83
+ expect(result.ok).toBe(true);
84
+ expect(result.imported).toBe(1);
85
+ expect(result.messages).toBe(2);
86
+
87
+ // AND the persisted rows are exactly the user/assistant turns, never the
88
+ // system scaffolding
89
+ const db = getDb();
90
+ const conv = db.select().from(conversations).all()[0];
91
+ const rows = getMessages(conv.id);
92
+ expect(rows.map((m) => m.role)).toEqual(["user", "assistant"]);
93
+ expect(
94
+ db
95
+ .select()
96
+ .from(messages)
97
+ .all()
98
+ .some((m) => m.role === "system"),
99
+ ).toBe(false);
100
+ });
101
+ });
@@ -0,0 +1,93 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { computeCorrectedOverflowTarget } from "../plugins/defaults/compaction/corrected-target.js";
4
+
5
+ describe("computeCorrectedOverflowTarget", () => {
6
+ test("lowers the target proportionally when the estimator under-counted", () => {
7
+ /**
8
+ * When the provider's actual token count exceeds the estimator's count,
9
+ * the target is scaled down by the estimation-error ratio so a re-estimate
10
+ * of the reduced history lands under the provider's real ceiling.
11
+ */
12
+ // GIVEN a preflight budget and an estimator that under-counted (185k vs 242k actual)
13
+ const preflightBudget = 162_000;
14
+ const actualTokens = 242_000;
15
+ const estimatedTokens = 185_000;
16
+
17
+ // WHEN computing the corrected target
18
+ const result = computeCorrectedOverflowTarget({
19
+ preflightBudget,
20
+ actualTokens,
21
+ estimatedTokens,
22
+ });
23
+
24
+ // THEN the target is the budget divided by the error ratio
25
+ const expectedRatio = actualTokens / estimatedTokens;
26
+ expect(result.targetTokens).toBe(
27
+ Math.floor(preflightBudget / expectedRatio),
28
+ );
29
+ // AND the ratio is surfaced for logging
30
+ expect(result.estimationErrorRatio).toBeCloseTo(expectedRatio, 10);
31
+ // AND the corrected target is below the nominal budget
32
+ expect(result.targetTokens).toBeLessThan(preflightBudget);
33
+ });
34
+
35
+ test("returns the preflight budget unchanged when the estimator did not under-count", () => {
36
+ /**
37
+ * If the estimator matched or over-counted the provider, no correction is
38
+ * applied and the nominal preflight budget is the target.
39
+ */
40
+ // GIVEN an actual count at or below the estimate
41
+ const preflightBudget = 162_000;
42
+
43
+ // WHEN computing the corrected target
44
+ const result = computeCorrectedOverflowTarget({
45
+ preflightBudget,
46
+ actualTokens: 180_000,
47
+ estimatedTokens: 185_000,
48
+ });
49
+
50
+ // THEN the target is the unchanged preflight budget and no ratio is reported
51
+ expect(result.targetTokens).toBe(preflightBudget);
52
+ expect(result.estimationErrorRatio).toBeNull();
53
+ });
54
+
55
+ test("falls back to the preflight budget when the actual count is unavailable", () => {
56
+ /**
57
+ * Proxy-rewrapped or unparseable rejections yield a null actual count; the
58
+ * correction is skipped rather than guessed.
59
+ */
60
+ // GIVEN a null actual token count
61
+ const preflightBudget = 162_000;
62
+
63
+ // WHEN computing the corrected target
64
+ const result = computeCorrectedOverflowTarget({
65
+ preflightBudget,
66
+ actualTokens: null,
67
+ estimatedTokens: 185_000,
68
+ });
69
+
70
+ // THEN the nominal budget is used with no ratio
71
+ expect(result.targetTokens).toBe(preflightBudget);
72
+ expect(result.estimationErrorRatio).toBeNull();
73
+ });
74
+
75
+ test("falls back to the preflight budget when the estimate is zero", () => {
76
+ /**
77
+ * A zero estimate would make the ratio undefined; guard against it.
78
+ */
79
+ // GIVEN a zero estimate
80
+ const preflightBudget = 162_000;
81
+
82
+ // WHEN computing the corrected target
83
+ const result = computeCorrectedOverflowTarget({
84
+ preflightBudget,
85
+ actualTokens: 242_000,
86
+ estimatedTokens: 0,
87
+ });
88
+
89
+ // THEN the nominal budget is used with no ratio
90
+ expect(result.targetTokens).toBe(preflightBudget);
91
+ expect(result.estimationErrorRatio).toBeNull();
92
+ });
93
+ });
@@ -165,10 +165,11 @@ describe("CES flags do not affect unrelated flags", () => {
165
165
  setOverridesForTesting(overrides);
166
166
  const config = makeConfig();
167
167
 
168
- // account-deletion defaults to true in the registry and should stay true.
169
- expect(isAssistantFeatureFlagEnabled("account-deletion", config)).toBe(
170
- true,
171
- );
168
+ // Flags with defaultEnabled: true in the registry should stay true
169
+ // regardless of CES overrides.
170
+ expect(
171
+ isAssistantFeatureFlagEnabled("platform-features-in-local-mode", config),
172
+ ).toBe(true);
172
173
  });
173
174
 
174
175
  test("enabling all CES flags does not change unrelated fail-closed flags", () => {
@@ -231,6 +231,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
231
231
  "ipc/skill-routes/providers.ts", // host.providers.secureKeys.getProviderKey IPC route (out-of-process SkillHost companion)
232
232
  "daemon/external-plugins-bootstrap.ts", // reads credentials at plugin init (manifest.requiresCredential) via the CES-mediated getSecureKeyAsync path
233
233
  "plugins/external-api.ts", // globalThis runtime bridge that exposes getSecureKeyAsync to dynamically-imported workspace plugins (compiled-binary plugin loading)
234
+ "plugin-api/index.ts", // public @vellumai/plugin-api surface re-exports getSecureKeyAsync to workspace plugins via the boot-time shim (same exposure as the external-api bridge, routed through the typed public package)
234
235
  "inbound/platform-callback-registration.ts", // managed credential lookup for platform base URL, assistant ID, and API key
235
236
  "tts/providers/elevenlabs-provider.ts", // ElevenLabs TTS API key lookup
236
237
  "tts/providers/deepgram-provider.ts", // Deepgram TTS API key lookup
@@ -69,6 +69,7 @@ type SurfaceConversationContext =
69
69
  function buildMockContext(
70
70
  hostCuProxy?: InstanceType<typeof HostCuProxy>,
71
71
  trustGuardianPrincipalId: string | null = DEFAULT_PRINCIPAL,
72
+ actorPrincipalId: string | null = trustGuardianPrincipalId,
72
73
  ): SurfaceConversationContext {
73
74
  return {
74
75
  conversationId: "test-session",
@@ -80,6 +81,16 @@ function buildMockContext(
80
81
  guardianPrincipalId: trustGuardianPrincipalId,
81
82
  }
82
83
  : undefined,
84
+ authContext:
85
+ actorPrincipalId != null
86
+ ? ({ actorPrincipalId } as SurfaceConversationContext["authContext"])
87
+ : undefined,
88
+ currentTurnAuthContext:
89
+ actorPrincipalId != null
90
+ ? ({
91
+ actorPrincipalId,
92
+ } as SurfaceConversationContext["currentTurnAuthContext"])
93
+ : undefined,
83
94
  traceEmitter: { emit: () => {} },
84
95
  sendToClient: () => {},
85
96
  pendingSurfaceActions: new Map(),
@@ -665,6 +676,31 @@ describe("surfaceProxyResolver — CU tool routing", () => {
665
676
  expect(sentMessages).toHaveLength(0);
666
677
  });
667
678
 
679
+ test("rejects untargeted CU dispatch to owner client when current requester is a trusted contact", async () => {
680
+ sentMessages.length = 0;
681
+ mockHasClient = true;
682
+ mockCuClients = [
683
+ {
684
+ clientId: "owner-cu-client",
685
+ capabilities: ["host_cu"],
686
+ actorPrincipalId: "owner-user",
687
+ },
688
+ ];
689
+ proxy = new HostCuProxy();
690
+ const ctx = buildMockContext(proxy, "owner-user", "trusted-contact-user");
691
+
692
+ const result = await surfaceProxyResolver(ctx, "computer_use_click", {
693
+ element_id: 1,
694
+ reasoning: "click",
695
+ });
696
+
697
+ expect(result.isError).toBe(true);
698
+ expect(result.content).toContain("current actor");
699
+ expect(proxy.stepCount).toBe(0);
700
+ expect(proxy.actionHistory).toHaveLength(0);
701
+ expect(sentMessages).toHaveLength(0);
702
+ });
703
+
668
704
  test("auto-resolves to the unique same-user CU client when cross-user clients are present", async () => {
669
705
  // Regression: previously the dispatch counted only same-user clients
670
706
  // for the multi-client guard, so 1 same-user + 1 cross-user passed the