@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
@@ -1,432 +0,0 @@
1
- /**
2
- * Tests for `assistant/src/memory/v3/shadow-plugin.ts`.
3
- *
4
- * The v3 plugin is flag-gated. These tests assert:
5
- * - both flags OFF → orchestrate is never called and no DB rows are written;
6
- * - either flag ON → orchestrate runs and selection rows land in
7
- * `memory_v3_selections`;
8
- * - shadow-only (live off) → the injector returns `null` (never mutates the
9
- * turn) but still logs;
10
- * - live on → the injector returns the rendered `<memory>` block;
11
- * - an empty selection under live → `null`;
12
- * - lazy-init runs the loaders only once across multiple turns.
13
- *
14
- * All heavy dependencies (config, flag resolver, conversation reads, v2 page
15
- * index + page store, v3 loaders, orchestrate) are mocked BEFORE importing the
16
- * plugin so the module observes them at load time. A real in-memory SQLite DB
17
- * backs the write assertions via the `memory_v3_selections` migration.
18
- */
19
-
20
- import { Database } from "bun:sqlite";
21
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
22
-
23
- import { drizzle } from "drizzle-orm/bun-sqlite";
24
-
25
- import { migrateAddMemoryV3Selections } from "../../migrations/268-add-memory-v3-selections.js";
26
- import * as schema from "../../schema.js";
27
- import type { LeafTree, SelectionSource } from "../types.js";
28
-
29
- // `mock.module` is process-global and, in Bun, neither `mock.restore()` nor a
30
- // re-mock in `afterAll` reverts it for files that load LATER in the same
31
- // `bun test src/memory/v3/` run. Sibling files (orchestrate.test.ts,
32
- // tree.test.ts) import these same modules for real, so an unconditional stub
33
- // here would leak in and break them. Instead each stub below DELEGATES to the
34
- // real implementation unless this test is actively running (`shadowMockActive`,
35
- // toggled in beforeEach/afterAll).
36
- //
37
- // Snapshot the real exports into plain objects NOW: a module namespace object
38
- // is a live view, so reading `realTree.loadLeafTree` *after* the stub is
39
- // installed would resolve back to the stub (infinite recursion).
40
- const realTree = { ...(await import("../tree.js")) };
41
- const realCore = { ...(await import("../core.js")) };
42
- const realNeedle = { ...(await import("../needle.js")) };
43
- const realOrchestrate = { ...(await import("../orchestrate.js")) };
44
- const realPlatform = { ...(await import("../../../util/platform.js")) };
45
- const realPageStore = { ...(await import("../../v2/page-store.js")) };
46
- const realConversationCrud = {
47
- ...(await import("../../conversation-crud.js")),
48
- };
49
-
50
- let shadowMockActive = false;
51
-
52
- // ─── mutable test state, read by the mocks below ────────────────────────────
53
-
54
- // Per-flag toggles. The plugin reads `memory-v3-live` and `memory-v3-shadow`
55
- // independently, so the mock resolves by flag key rather than a single boolean.
56
- let liveEnabled = false;
57
- let shadowEnabled = false;
58
- let messages: Array<{ role: string; content: string }> = [];
59
- const orchestrateSpy = mock(async () => ({
60
- openedLeaves: [],
61
- currentSelections: [
62
- { slug: "domain-a/page-1", pinned: true },
63
- { slug: "domain-b/page-2", pinned: false },
64
- ],
65
- workingSetUnion: new Set<string>(["domain-a/page-1", "domain-b/page-2"]),
66
- finalInjection: ["domain-a/page-1", "domain-b/page-2", "domain-a/carried"],
67
- }));
68
- let treeLoads = 0;
69
- let coreLoads = 0;
70
- let needleBuilds = 0;
71
- let configL2Concurrency = 16;
72
-
73
- // Shared in-memory DB so writes are observable from the test. We hold the raw
74
- // sqlite handle alongside the drizzle wrapper so the test can both read rows
75
- // directly and feed the same handle through the mocked `getSqliteFrom`.
76
- let testSqlite: Database;
77
- let testDb = makeDb();
78
- function makeDb() {
79
- testSqlite = new Database(":memory:");
80
- testSqlite.exec("PRAGMA journal_mode=WAL");
81
- const db = drizzle(testSqlite, { schema });
82
- migrateAddMemoryV3Selections(db);
83
- return db;
84
- }
85
-
86
- // A tree where `domain-a/*` pages are owned by a core leaf and `domain-b/*`
87
- // are not, so attribution maps to core+l2 / l1+l2 respectively.
88
- const FAKE_TREE = {
89
- leaves: new Map([
90
- ["domain-a", { members: ["domain-a/page-1", "domain-a/carried"] }],
91
- ["domain-b", { members: ["domain-b/page-2"] }],
92
- ]),
93
- byPage: new Map(),
94
- } as unknown as LeafTree;
95
-
96
- // ─── module mocks (installed before the plugin import) ──────────────────────
97
-
98
- mock.module("../../../config/assistant-feature-flags.js", () => ({
99
- isAssistantFeatureFlagEnabled: (key: string) =>
100
- key === "memory-v3-live"
101
- ? liveEnabled
102
- : key === "memory-v3-shadow"
103
- ? shadowEnabled
104
- : false,
105
- }));
106
-
107
- mock.module("../../../config/loader.js", () => ({
108
- getConfig: () => ({
109
- memory: {
110
- v3: {
111
- workingSet: { maxPages: 150, evictWindow: 5 },
112
- l2Concurrency: configL2Concurrency,
113
- },
114
- },
115
- }),
116
- }));
117
-
118
- // Spread the real module so every export the live path transitively imports
119
- // (e.g. `getMessageById` via page-content.ts) stays present — replacing the
120
- // whole module with a bare `{ getMessages }` made those consumers fail to load.
121
- // Only `getMessages` is overridden, since that's the one the plugin reads.
122
- mock.module("../../conversation-crud.js", () => ({
123
- ...realConversationCrud,
124
- getMessages: () => messages.map((m, i) => ({ ...m, id: `m${i}` })),
125
- }));
126
-
127
- mock.module("../../db-connection.js", () => ({
128
- getDb: () => testDb,
129
- getSqliteFrom: () => testSqlite,
130
- }));
131
-
132
- mock.module("../v2/page-index.js", () => ({
133
- getPageIndex: async () => ({ bySlug: new Map() }),
134
- }));
135
-
136
- // `pageContent` (live mode) reads the full page via `readPage`/`renderPageContent`.
137
- // Stub them to return a deterministic body per slug so the rendered `<memory>`
138
- // block is assertable without touching the filesystem.
139
- mock.module("../../v2/page-store.js", () => ({
140
- ...realPageStore,
141
- readPage: async (workspaceDir: string, slug: string) =>
142
- shadowMockActive
143
- ? { slug, frontmatter: {}, body: `body for ${slug}` }
144
- : realPageStore.readPage(workspaceDir, slug),
145
- renderPageContent: (page: { slug: string; body: string }) =>
146
- shadowMockActive
147
- ? page.body
148
- : realPageStore.renderPageContent(
149
- page as Parameters<typeof realPageStore.renderPageContent>[0],
150
- ),
151
- }));
152
-
153
- mock.module("../../../util/platform.js", () => ({
154
- ...realPlatform,
155
- getWorkspaceDir: () =>
156
- shadowMockActive
157
- ? "/tmp/shadow-test-workspace"
158
- : realPlatform.getWorkspaceDir(),
159
- }));
160
-
161
- mock.module("../tree.js", () => ({
162
- ...realTree,
163
- resolveDataDir: () =>
164
- shadowMockActive ? "/tmp/shadow-test-data" : realTree.resolveDataDir(),
165
- loadLeafTree: async (dataDir: string) => {
166
- if (!shadowMockActive) return realTree.loadLeafTree(dataDir);
167
- treeLoads++;
168
- return FAKE_TREE;
169
- },
170
- membersOf: (tree: LeafTree, leaf: string) =>
171
- shadowMockActive
172
- ? ((tree.leaves.get(leaf) as unknown as { members?: string[] })
173
- ?.members ?? [])
174
- : realTree.membersOf(tree, leaf),
175
- }));
176
-
177
- mock.module("../core.js", () => ({
178
- ...realCore,
179
- loadCore: async (dataDir: string) => {
180
- if (!shadowMockActive) return realCore.loadCore(dataDir);
181
- coreLoads++;
182
- return new Set(["domain-a"]);
183
- },
184
- }));
185
-
186
- mock.module("../needle.js", () => ({
187
- ...realNeedle,
188
- buildNeedleIndex: async (
189
- ...args: Parameters<typeof realNeedle.buildNeedleIndex>
190
- ) => {
191
- if (!shadowMockActive) return realNeedle.buildNeedleIndex(...args);
192
- needleBuilds++;
193
- return { query: () => [] } as unknown as Awaited<
194
- ReturnType<typeof realNeedle.buildNeedleIndex>
195
- >;
196
- },
197
- }));
198
-
199
- mock.module("../orchestrate.js", () => ({
200
- ...realOrchestrate,
201
- orchestrate: (...args: Parameters<typeof realOrchestrate.orchestrate>) =>
202
- shadowMockActive
203
- ? orchestrateSpy(...(args as unknown as []))
204
- : realOrchestrate.orchestrate(...args),
205
- }));
206
-
207
- // Import AFTER mocks so the plugin binds to them.
208
- const {
209
- runShadowObservation,
210
- resetShadowLanesForTests,
211
- invalidateLanes,
212
- memoryV3Injector,
213
- } = await import("../shadow-plugin.js");
214
-
215
- // The module stubs above stay installed for the rest of the process (Bun can't
216
- // reliably uninstall them), but `shadowMockActive` gates their fake behavior to
217
- // this file's tests only, so later-loaded sibling files see real behavior.
218
- afterAll(() => {
219
- shadowMockActive = false;
220
- });
221
-
222
- function readRows() {
223
- return testSqlite
224
- .query(
225
- `SELECT slug, source, pinned FROM memory_v3_selections ORDER BY slug`,
226
- )
227
- .all() as Array<{ slug: string; source: SelectionSource; pinned: number }>;
228
- }
229
-
230
- beforeEach(() => {
231
- shadowMockActive = true;
232
- liveEnabled = false;
233
- shadowEnabled = false;
234
- messages = [
235
- {
236
- role: "user",
237
- content: JSON.stringify([{ type: "text", text: "hello world" }]),
238
- },
239
- ];
240
- orchestrateSpy.mockClear();
241
- treeLoads = 0;
242
- coreLoads = 0;
243
- needleBuilds = 0;
244
- configL2Concurrency = 16;
245
- testDb = makeDb();
246
- resetShadowLanesForTests();
247
- });
248
-
249
- /** Invoke the memory-v3 injector's `produce()` for a turn. */
250
- function produce(conversationId: string, turnIndex: number) {
251
- return memoryV3Injector.produce({
252
- requestId: "r1",
253
- conversationId,
254
- turnIndex,
255
- trust: {} as never,
256
- });
257
- }
258
-
259
- describe("memory-v3 shadow plugin", () => {
260
- test("shadow flag OFF → orchestrate not called, no DB writes", async () => {
261
- shadowEnabled = false;
262
- await runShadowObservation("conv-1", 0);
263
- expect(orchestrateSpy).not.toHaveBeenCalled();
264
- expect(treeLoads).toBe(0);
265
- expect(readRows()).toHaveLength(0);
266
- });
267
-
268
- test("shadow flag ON → orchestrate runs and rows are written", async () => {
269
- shadowEnabled = true;
270
- await runShadowObservation("conv-1", 2);
271
-
272
- expect(orchestrateSpy).toHaveBeenCalledTimes(1);
273
- const rows = readRows();
274
- expect(rows).toEqual([
275
- { slug: "domain-a/carried", source: "carry-forward", pinned: 0 },
276
- // page-1 belongs to the core leaf `domain-a` → core+l2, pinned.
277
- { slug: "domain-a/page-1", source: "core+l2", pinned: 1 },
278
- // page-2 belongs to non-core `domain-b` → l1+l2.
279
- { slug: "domain-b/page-2", source: "l1+l2", pinned: 0 },
280
- ]);
281
- });
282
-
283
- test("the turn passed to orchestrate carries the latest user message", async () => {
284
- shadowEnabled = true;
285
- await runShadowObservation("conv-1", 0);
286
- const turn = (
287
- orchestrateSpy.mock.calls as unknown as unknown[][]
288
- )[0]![0] as {
289
- conversationId: string;
290
- turnNumber: number;
291
- currentMessage: string;
292
- };
293
- expect(turn.conversationId).toBe("conv-1");
294
- expect(turn.turnNumber).toBe(0);
295
- expect(turn.currentMessage).toBe("hello world");
296
- });
297
-
298
- test("orchestrate receives the configured L2 concurrency", async () => {
299
- shadowEnabled = true;
300
- configL2Concurrency = 9;
301
- await runShadowObservation("conv-1", 0);
302
- const deps = (
303
- orchestrateSpy.mock.calls as unknown as unknown[][]
304
- )[0]![1] as {
305
- l2Concurrency?: number;
306
- };
307
- expect(deps.l2Concurrency).toBe(9);
308
- });
309
-
310
- test("both flags OFF → produce returns null, no orchestrate, no writes", async () => {
311
- liveEnabled = false;
312
- shadowEnabled = false;
313
- const block = await produce("conv-1", 0);
314
- expect(block).toBeNull();
315
- expect(orchestrateSpy).not.toHaveBeenCalled();
316
- expect(readRows()).toHaveLength(0);
317
- });
318
-
319
- test("shadow-only (live off) → produce returns null but still logs", async () => {
320
- liveEnabled = false;
321
- shadowEnabled = true;
322
- const block = await produce("conv-1", 0);
323
- expect(block).toBeNull();
324
- expect(orchestrateSpy).toHaveBeenCalledTimes(1);
325
- expect(readRows().length).toBeGreaterThan(0);
326
- });
327
-
328
- test("live on → produce returns the rendered <memory> block and logs", async () => {
329
- liveEnabled = true;
330
- shadowEnabled = false;
331
- const block = await produce("conv-1", 0);
332
- expect(block).not.toBeNull();
333
- expect(block!.placement).toBe("after-memory-prefix");
334
- expect(block!.text.startsWith("<memory>\n")).toBe(true);
335
- expect(block!.text.endsWith("\n</memory>")).toBe(true);
336
- // finalInjection slugs are rendered into the block in order.
337
- expect(block!.text).toContain("body for domain-a/page-1");
338
- expect(block!.text).toContain("body for domain-a/carried");
339
- // Selections are still logged in live mode.
340
- expect(readRows().length).toBeGreaterThan(0);
341
- });
342
-
343
- test("live on but empty selection → produce returns null", async () => {
344
- liveEnabled = true;
345
- shadowEnabled = false;
346
- orchestrateSpy.mockImplementationOnce(async () => ({
347
- openedLeaves: [],
348
- currentSelections: [],
349
- workingSetUnion: new Set<string>(),
350
- finalInjection: [],
351
- }));
352
- const block = await produce("conv-1", 0);
353
- expect(block).toBeNull();
354
- // Orchestration still ran (and logged nothing, since there were no rows).
355
- expect(orchestrateSpy).toHaveBeenCalledTimes(1);
356
- expect(readRows()).toHaveLength(0);
357
- });
358
-
359
- test("lazy-init runs the loaders only once across turns", async () => {
360
- shadowEnabled = true;
361
- await runShadowObservation("conv-1", 0);
362
- await runShadowObservation("conv-1", 1);
363
- await runShadowObservation("conv-1", 2);
364
- expect(treeLoads).toBe(1);
365
- expect(coreLoads).toBe(1);
366
- expect(needleBuilds).toBe(1);
367
- expect(orchestrateSpy).toHaveBeenCalledTimes(3);
368
- });
369
-
370
- test("invalidateLanes forces a one-time rebuild on the next turn", async () => {
371
- shadowEnabled = true;
372
- await runShadowObservation("conv-1", 0);
373
- await runShadowObservation("conv-1", 1);
374
- // One build so far; invalidation drops it.
375
- expect(treeLoads).toBe(1);
376
- expect(needleBuilds).toBe(1);
377
-
378
- invalidateLanes();
379
-
380
- // The next turn rebuilds (fresh tree load + fresh needle index)...
381
- await runShadowObservation("conv-1", 2);
382
- expect(treeLoads).toBe(2);
383
- expect(coreLoads).toBe(2);
384
- expect(needleBuilds).toBe(2);
385
-
386
- // ...and the rebuild is memoized again — no further builds until the next
387
- // invalidation.
388
- await runShadowObservation("conv-1", 3);
389
- expect(treeLoads).toBe(2);
390
- expect(needleBuilds).toBe(2);
391
- });
392
-
393
- test("resetShadowLanesForTests invalidates like invalidateLanes", async () => {
394
- shadowEnabled = true;
395
- await runShadowObservation("conv-1", 0);
396
- expect(treeLoads).toBe(1);
397
-
398
- resetShadowLanesForTests();
399
-
400
- await runShadowObservation("conv-1", 1);
401
- expect(treeLoads).toBe(2);
402
- });
403
-
404
- test("concurrent first turns after invalidation share a single build", async () => {
405
- shadowEnabled = true;
406
- await runShadowObservation("conv-1", 0);
407
- expect(treeLoads).toBe(1);
408
-
409
- invalidateLanes();
410
-
411
- // Both turns race the rebuild; memoization must collapse them to one build.
412
- await Promise.all([
413
- runShadowObservation("conv-1", 1),
414
- runShadowObservation("conv-1", 2),
415
- ]);
416
- expect(treeLoads).toBe(2);
417
- expect(needleBuilds).toBe(2);
418
- });
419
-
420
- test("no user message → no orchestrate, no writes", async () => {
421
- shadowEnabled = true;
422
- messages = [
423
- {
424
- role: "assistant",
425
- content: JSON.stringify([{ type: "text", text: "hi" }]),
426
- },
427
- ];
428
- await runShadowObservation("conv-1", 0);
429
- expect(orchestrateSpy).not.toHaveBeenCalled();
430
- expect(readRows()).toHaveLength(0);
431
- });
432
- });
@@ -1,168 +0,0 @@
1
- import { promises as fs } from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
5
-
6
- import { restoreDataDir, snapshotDataDir } from "../snapshot.js";
7
-
8
- let tmpRoot: string;
9
- let dataDir: string;
10
-
11
- async function readJson(file: string): Promise<unknown> {
12
- return JSON.parse(await fs.readFile(file, "utf8")) as unknown;
13
- }
14
-
15
- async function listLeaves(dir: string): Promise<Record<string, string>> {
16
- const leavesDir = path.join(dir, "leaves");
17
- const out: Record<string, string> = {};
18
- async function walk(current: string, prefix: string): Promise<void> {
19
- let entries: import("node:fs").Dirent[];
20
- try {
21
- entries = await fs.readdir(current, { withFileTypes: true });
22
- } catch {
23
- return;
24
- }
25
- for (const entry of entries) {
26
- const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
27
- const abs = path.join(current, entry.name);
28
- if (entry.isDirectory()) {
29
- await walk(abs, rel);
30
- } else if (entry.isFile()) {
31
- out[rel] = await fs.readFile(abs, "utf8");
32
- }
33
- }
34
- }
35
- await walk(leavesDir, "");
36
- return out;
37
- }
38
-
39
- async function seedDataDir(): Promise<void> {
40
- await fs.mkdir(path.join(dataDir, "leaves", "alpha"), { recursive: true });
41
- await fs.writeFile(
42
- path.join(dataDir, "leaves", "alpha", "leaf-1.md"),
43
- "leaf one body",
44
- );
45
- await fs.writeFile(
46
- path.join(dataDir, "leaves", "leaf-2.md"),
47
- "leaf two body",
48
- );
49
- await fs.writeFile(
50
- path.join(dataDir, "assignments.json"),
51
- JSON.stringify({ "leaf-1": ["topic-a"] }),
52
- );
53
- await fs.writeFile(
54
- path.join(dataDir, "core.json"),
55
- JSON.stringify({ version: 1, root: "topic-a" }),
56
- );
57
- }
58
-
59
- beforeEach(async () => {
60
- tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), "v3-snapshot-"));
61
- dataDir = path.join(tmpRoot, "data");
62
- await fs.mkdir(dataDir, { recursive: true });
63
- });
64
-
65
- afterEach(async () => {
66
- await fs.rm(tmpRoot, { recursive: true, force: true });
67
- });
68
-
69
- describe("snapshotDataDir / restoreDataDir", () => {
70
- test("roundtrips leaves, assignments, and core", async () => {
71
- await seedDataDir();
72
- const before = {
73
- leaves: await listLeaves(dataDir),
74
- assignments: await readJson(path.join(dataDir, "assignments.json")),
75
- core: await readJson(path.join(dataDir, "core.json")),
76
- };
77
-
78
- const snapshotPath = await snapshotDataDir(dataDir, { label: "snap-1" });
79
-
80
- // Mutate the live data dir after snapshotting.
81
- await fs.writeFile(
82
- path.join(dataDir, "leaves", "alpha", "leaf-1.md"),
83
- "CORRUPTED",
84
- );
85
- await fs.rm(path.join(dataDir, "leaves", "leaf-2.md"));
86
- await fs.writeFile(
87
- path.join(dataDir, "assignments.json"),
88
- JSON.stringify({ broken: true }),
89
- );
90
- await fs.writeFile(
91
- path.join(dataDir, "core.json"),
92
- JSON.stringify({ broken: true }),
93
- );
94
-
95
- await restoreDataDir(snapshotPath, dataDir);
96
-
97
- expect(await listLeaves(dataDir)).toEqual(before.leaves);
98
- expect(await readJson(path.join(dataDir, "assignments.json"))).toEqual(
99
- before.assignments,
100
- );
101
- expect(await readJson(path.join(dataDir, "core.json"))).toEqual(
102
- before.core,
103
- );
104
- });
105
-
106
- test("roundtrips the pageRefs diff", async () => {
107
- await seedDataDir();
108
- const pageRefs = new Map<string, string[]>([
109
- ["slug-one", ["leaf-1", "leaf-2"]],
110
- ["slug-two", ["leaf-3"]],
111
- ]);
112
-
113
- const snapshotPath = await snapshotDataDir(dataDir, {
114
- label: "snap-refs",
115
- pageRefs,
116
- });
117
-
118
- const { pageRefs: restored } = await restoreDataDir(snapshotPath, dataDir);
119
- expect(restored).toBeDefined();
120
- expect(restored).toEqual(pageRefs);
121
- });
122
-
123
- test("returns no pageRefs when none were captured", async () => {
124
- await seedDataDir();
125
- const snapshotPath = await snapshotDataDir(dataDir, { label: "snap-none" });
126
- const result = await restoreDataDir(snapshotPath, dataDir);
127
- expect(result.pageRefs).toBeUndefined();
128
- });
129
-
130
- test("restoring removes leaves added after the snapshot", async () => {
131
- await seedDataDir();
132
- const snapshotPath = await snapshotDataDir(dataDir, { label: "snap-add" });
133
-
134
- await fs.writeFile(
135
- path.join(dataDir, "leaves", "leaf-extra.md"),
136
- "added later",
137
- );
138
-
139
- await restoreDataDir(snapshotPath, dataDir);
140
- const leaves = await listLeaves(dataDir);
141
- expect(leaves["leaf-extra.md"]).toBeUndefined();
142
- expect(leaves["leaf-2.md"]).toBe("leaf two body");
143
- });
144
-
145
- test("retention is bounded to the last N snapshots", async () => {
146
- await seedDataDir();
147
- // Zero-padded labels sort chronologically.
148
- const labels = Array.from(
149
- { length: 8 },
150
- (_, i) => `snap-${String(i).padStart(2, "0")}`,
151
- );
152
- for (const label of labels) {
153
- await snapshotDataDir(dataDir, { label });
154
- }
155
-
156
- const snapshotsRoot = path.join(tmpRoot, "v3-snapshots");
157
- const remaining = (await fs.readdir(snapshotsRoot)).sort();
158
- expect(remaining.length).toBe(5);
159
- // Oldest three pruned, newest five kept.
160
- expect(remaining).toEqual([
161
- "snap-03",
162
- "snap-04",
163
- "snap-05",
164
- "snap-06",
165
- "snap-07",
166
- ]);
167
- });
168
- });