@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091853.fbaa2ae

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 (475) 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 +263 -121
  12. package/package.json +2 -2
  13. package/src/__tests__/activation-early-marking.test.ts +120 -0
  14. package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
  15. package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
  16. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
  17. package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
  18. package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
  19. package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
  20. package/src/__tests__/agent-loop-thinking.test.ts +36 -20
  21. package/src/__tests__/agent-loop.test.ts +441 -96
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
  23. package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
  24. package/src/__tests__/anthropic-provider.test.ts +23 -8
  25. package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
  26. package/src/__tests__/app-control-flow.test.ts +1 -1
  27. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  28. package/src/__tests__/app-executors.test.ts +132 -0
  29. package/src/__tests__/approval-cascade.test.ts +6 -5
  30. package/src/__tests__/approval-routes-http.test.ts +4 -1
  31. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  32. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  33. package/src/__tests__/channel-approvals.test.ts +1 -1
  34. package/src/__tests__/compaction-circuit.test.ts +258 -0
  35. package/src/__tests__/compaction-direct.test.ts +146 -0
  36. package/src/__tests__/compaction-events.test.ts +7 -7
  37. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  38. package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
  39. package/src/__tests__/context-overflow-reducer.test.ts +265 -125
  40. package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
  41. package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
  42. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
  43. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
  44. package/src/__tests__/conversation-agent-loop-overflow.test.ts +158 -234
  45. package/src/__tests__/conversation-agent-loop.test.ts +342 -623
  46. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  47. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
  48. package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
  49. package/src/__tests__/conversation-error.test.ts +15 -1
  50. package/src/__tests__/conversation-history-web-search.test.ts +6 -1
  51. package/src/__tests__/conversation-media-retry.test.ts +1 -1
  52. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
  53. package/src/__tests__/conversation-process-callsite.test.ts +15 -15
  54. package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
  55. package/src/__tests__/conversation-queue.test.ts +10 -10
  56. package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
  57. package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
  58. package/src/__tests__/conversation-slash-queue.test.ts +7 -5
  59. package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
  60. package/src/__tests__/conversation-speed-override.test.ts +11 -10
  61. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  62. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
  63. package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
  64. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
  65. package/src/__tests__/conversation-undo.test.ts +2 -2
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
  67. package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
  69. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  70. package/src/__tests__/corrected-target.test.ts +93 -0
  71. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  72. package/src/__tests__/cu-unified-flow.test.ts +36 -0
  73. package/src/__tests__/db-acp-history.test.ts +101 -0
  74. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  75. package/src/__tests__/empty-response-hook.test.ts +1 -1
  76. package/src/__tests__/file-write-tool.test.ts +63 -0
  77. package/src/__tests__/gateway-only-guard.test.ts +17 -2
  78. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  79. package/src/__tests__/guardian-routing-invariants.test.ts +93 -4
  80. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  81. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  82. package/src/__tests__/heartbeat-service.test.ts +1 -0
  83. package/src/__tests__/history-repair-hook.test.ts +5 -1
  84. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  85. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  86. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  87. package/src/__tests__/inference-profile-reaper.test.ts +62 -0
  88. package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
  89. package/src/__tests__/injector-background-turn.test.ts +13 -23
  90. package/src/__tests__/injector-chain.test.ts +268 -44
  91. package/src/__tests__/injector-disk-pressure.test.ts +210 -52
  92. package/src/__tests__/injector-document-comments.test.ts +97 -114
  93. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  94. package/src/__tests__/injector-v3-suppression.test.ts +4 -4
  95. package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
  96. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  97. package/src/__tests__/llm-resolver.test.ts +73 -0
  98. package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
  99. package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
  100. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  101. package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
  102. package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
  103. package/src/__tests__/persist-unsendable-image.test.ts +97 -1
  104. package/src/__tests__/plugin-api-shim.test.ts +6 -9
  105. package/src/__tests__/plugin-bootstrap.test.ts +91 -81
  106. package/src/__tests__/plugin-registry.test.ts +3 -49
  107. package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
  108. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  109. package/src/__tests__/plugin-types.test.ts +0 -70
  110. package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
  111. package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
  112. package/src/__tests__/published-app-updater.test.ts +138 -0
  113. package/src/__tests__/reaction-persistence.test.ts +1 -1
  114. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  115. package/src/__tests__/skill-feature-flags-integration.test.ts +31 -0
  116. package/src/__tests__/steer-tool-repair.test.ts +1 -1
  117. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  118. package/src/__tests__/subagent-detail.test.ts +25 -7
  119. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  120. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  121. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  122. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  123. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  124. package/src/__tests__/title-generate-hook.test.ts +5 -1
  125. package/src/__tests__/tool-error-hook.test.ts +1 -1
  126. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
  127. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  128. package/src/__tests__/web-fetch.test.ts +45 -0
  129. package/src/acp/__tests__/agent-process.test.ts +161 -0
  130. package/src/acp/__tests__/client-handler.test.ts +40 -0
  131. package/src/acp/__tests__/helpers/acp-config-stub.ts +0 -2
  132. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  133. package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
  134. package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
  135. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  136. package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
  137. package/src/acp/agent-process.ts +61 -1
  138. package/src/acp/auto-install.test.ts +280 -0
  139. package/src/acp/auto-install.ts +232 -0
  140. package/src/acp/client-handler.ts +31 -0
  141. package/src/acp/prepare-agent-env.ts +80 -27
  142. package/src/acp/resolve-agent.test.ts +188 -28
  143. package/src/acp/resolve-agent.ts +119 -42
  144. package/src/acp/resume-hint.ts +23 -0
  145. package/src/acp/session-manager.ts +507 -73
  146. package/src/agent/compaction-circuit.ts +60 -102
  147. package/src/agent/loop.ts +403 -251
  148. package/src/api/responses/conversation-message.ts +22 -2
  149. package/src/api/responses/memory-v3-selection-log.ts +19 -10
  150. package/src/approvals/guardian-request-resolvers.ts +27 -1
  151. package/src/background-wake/next-wake.ts +1 -0
  152. package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
  153. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  154. package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
  155. package/src/cli/commands/memory-v3.ts +57 -199
  156. package/src/cli/commands/plugins.ts +43 -37
  157. package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
  158. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
  159. package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
  160. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
  161. package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
  162. package/src/cli/lib/install-from-github.ts +797 -60
  163. package/src/cli/lib/plugin-catalog-cache.ts +84 -0
  164. package/src/cli/lib/plugin-details.ts +416 -0
  165. package/src/cli/lib/plugin-marketplace.ts +215 -0
  166. package/src/cli/lib/search-plugins.ts +201 -29
  167. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  168. package/src/config/acp-defaults.test.ts +10 -0
  169. package/src/config/acp-defaults.ts +9 -3
  170. package/src/config/acp-schema.ts +1 -7
  171. package/src/config/bundled-skills/acp/SKILL.md +80 -41
  172. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  173. package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
  174. package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
  175. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  176. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  177. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  178. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  179. package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
  180. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  181. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  182. package/src/config/bundled-tool-registry.ts +4 -0
  183. package/src/config/call-site-defaults.ts +0 -3
  184. package/src/config/feature-flag-registry.json +10 -16
  185. package/src/config/llm-resolver.ts +39 -7
  186. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
  187. package/src/config/schemas/call-site-catalog.ts +0 -21
  188. package/src/config/schemas/heartbeat.ts +9 -0
  189. package/src/config/schemas/llm.ts +17 -3
  190. package/src/config/schemas/memory-v3.ts +58 -8
  191. package/src/config/seed-inference-profiles.ts +18 -0
  192. package/src/context/compactor.ts +22 -4
  193. package/src/context/post-turn-tool-result-truncation.ts +39 -1
  194. package/src/context/strip-injections.ts +8 -2
  195. package/src/daemon/conversation-agent-loop-handlers.ts +18 -36
  196. package/src/daemon/conversation-agent-loop.ts +308 -1297
  197. package/src/daemon/conversation-error.ts +31 -4
  198. package/src/daemon/conversation-history.ts +1 -1
  199. package/src/daemon/conversation-lifecycle.ts +11 -255
  200. package/src/daemon/conversation-media-retry.ts +19 -6
  201. package/src/daemon/conversation-messaging.ts +17 -0
  202. package/src/daemon/conversation-process.ts +22 -141
  203. package/src/daemon/conversation-queue-manager.ts +8 -0
  204. package/src/daemon/conversation-registry.ts +159 -0
  205. package/src/daemon/conversation-runtime-assembly.ts +361 -393
  206. package/src/daemon/conversation-store.ts +9 -90
  207. package/src/daemon/conversation-surfaces.ts +165 -11
  208. package/src/daemon/conversation-workspace.ts +17 -0
  209. package/src/daemon/conversation.ts +444 -62
  210. package/src/daemon/disk-pressure-policy.ts +0 -1
  211. package/src/daemon/external-plugins-bootstrap.ts +36 -46
  212. package/src/daemon/handlers/config-channels.ts +11 -2
  213. package/src/daemon/handlers/conversations.ts +3 -1
  214. package/src/daemon/handlers/skills.ts +4 -1
  215. package/src/daemon/host-app-control-proxy.ts +72 -57
  216. package/src/daemon/host-proxy-preactivation.ts +1 -3
  217. package/src/daemon/lifecycle.ts +21 -7
  218. package/src/daemon/persist-unsendable-image.ts +62 -25
  219. package/src/daemon/process-message.ts +1 -1
  220. package/src/daemon/server.ts +2 -0
  221. package/src/daemon/tool-side-effects.ts +15 -0
  222. package/src/daemon/wake-conversation-ops.ts +269 -0
  223. package/src/embedded/plugin-api.ts +2 -2
  224. package/src/export/__tests__/transcript-formatter.test.ts +5 -0
  225. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  226. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  227. package/src/heartbeat/heartbeat-service.ts +26 -0
  228. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  229. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  230. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  231. package/src/ipc/skill-routes/memory.ts +4 -2
  232. package/src/memory/__tests__/activation-session-store.test.ts +41 -0
  233. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
  234. package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
  235. package/src/memory/activation-session-store.ts +43 -0
  236. package/src/memory/conversation-crud.ts +29 -19
  237. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  238. package/src/memory/db-init.ts +6 -0
  239. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  240. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
  241. package/src/memory/jobs-worker.ts +25 -1
  242. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  243. package/src/memory/migrations/273-onboarding-events-funnel-columns.ts +46 -0
  244. package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
  245. package/src/memory/migrations/index.ts +3 -0
  246. package/src/memory/onboarding-events-store.ts +66 -18
  247. package/src/memory/schema/acp.ts +4 -0
  248. package/src/memory/schema/infrastructure.ts +13 -0
  249. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
  250. package/src/memory/v2/__tests__/page-store.test.ts +22 -0
  251. package/src/memory/v2/consolidation-job.ts +2 -63
  252. package/src/memory/v2/types.ts +5 -0
  253. package/src/messaging/providers/telegram-bot/api.ts +14 -5
  254. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  255. package/src/notifications/access-request-copy.ts +298 -0
  256. package/src/notifications/adapters/slack.ts +3 -3
  257. package/src/notifications/adapters/telegram.ts +9 -2
  258. package/src/notifications/copy-composer.ts +49 -267
  259. package/src/notifications/decision-engine.ts +16 -35
  260. package/src/notifications/home-feed-side-effect.ts +1 -6
  261. package/src/plugin-api/constants.ts +4 -0
  262. package/src/plugin-api/index.ts +40 -11
  263. package/src/plugin-api/types.ts +108 -0
  264. package/src/plugins/defaults/compaction/compact.ts +106 -0
  265. package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
  266. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  267. package/src/plugins/defaults/compaction/manager-store.ts +57 -0
  268. package/src/plugins/defaults/compaction/package.json +1 -2
  269. package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
  270. package/src/plugins/defaults/empty-response/package.json +0 -1
  271. package/src/plugins/defaults/history-repair/package.json +0 -1
  272. package/src/plugins/defaults/index.ts +156 -26
  273. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
  274. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
  275. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
  276. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
  277. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  278. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
  279. package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
  280. package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
  281. package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
  282. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
  283. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
  284. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
  285. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
  286. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
  287. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
  288. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
  289. package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
  290. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
  291. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
  292. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
  293. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
  294. package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
  295. package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
  296. package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
  297. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  298. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  299. package/src/plugins/defaults/memory-v3-shadow/injector.ts +76 -0
  300. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
  301. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
  302. package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
  303. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
  304. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
  305. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
  306. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
  307. package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
  308. package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
  309. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
  310. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
  311. package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
  312. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -1
  313. package/src/plugins/defaults/title-generate/package.json +0 -1
  314. package/src/plugins/defaults/tool-error/package.json +0 -1
  315. package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
  316. package/src/plugins/external-api.ts +12 -2
  317. package/src/plugins/pipeline.ts +6 -293
  318. package/src/plugins/registry.ts +9 -37
  319. package/src/plugins/types.ts +76 -381
  320. package/src/plugins/user-loader.ts +30 -127
  321. package/src/prompts/__tests__/system-prompt.test.ts +6 -0
  322. package/src/prompts/system-prompt.ts +61 -10
  323. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
  324. package/src/providers/anthropic/client.ts +9 -10
  325. package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
  326. package/src/providers/inference/logit-bias.ts +55 -0
  327. package/src/providers/model-catalog.ts +34 -0
  328. package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
  329. package/src/providers/openai/chat-completions-provider.ts +44 -1
  330. package/src/providers/retry.ts +22 -0
  331. package/src/providers/types.ts +6 -0
  332. package/src/runtime/__tests__/agent-wake.test.ts +555 -691
  333. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  334. package/src/runtime/agent-wake.ts +108 -209
  335. package/src/runtime/assistant-event-hub.ts +1 -1
  336. package/src/runtime/channel-approvals.ts +1 -1
  337. package/src/runtime/interactive-ui.ts +1 -1
  338. package/src/runtime/routes/__tests__/acp-routes.test.ts +466 -55
  339. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  340. package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
  341. package/src/runtime/routes/__tests__/stt-routes.test.ts +112 -0
  342. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  343. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  344. package/src/runtime/routes/acp-routes.test.ts +185 -32
  345. package/src/runtime/routes/acp-routes.ts +328 -30
  346. package/src/runtime/routes/app-management-routes.ts +3 -0
  347. package/src/runtime/routes/approval-routes.ts +1 -1
  348. package/src/runtime/routes/browser-routes.ts +1 -1
  349. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  350. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  351. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  352. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  353. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  354. package/src/runtime/routes/conversation-routes.ts +30 -2
  355. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  356. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  357. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  358. package/src/runtime/routes/host-cu-routes.ts +1 -1
  359. package/src/runtime/routes/identity-routes.ts +18 -3
  360. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  361. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  362. package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
  363. package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
  364. package/src/runtime/routes/memory-v3-routes.ts +65 -305
  365. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
  366. package/src/runtime/routes/playground/helpers.ts +1 -1
  367. package/src/runtime/routes/plugins-routes.ts +337 -35
  368. package/src/runtime/routes/stt-routes.ts +45 -12
  369. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  370. package/src/runtime/routes/work-items-routes.ts +2 -4
  371. package/src/runtime/routes/workspace-routes.ts +50 -15
  372. package/src/runtime/services/conversation-serializer.ts +1 -1
  373. package/src/runtime/verification-outbound-actions.ts +147 -2
  374. package/src/runtime/verification-templates.ts +29 -3
  375. package/src/services/published-app-updater.ts +30 -8
  376. package/src/signals/cancel.ts +2 -4
  377. package/src/subagent/manager.ts +21 -5
  378. package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
  379. package/src/telemetry/activation-funnel.ts +167 -0
  380. package/src/telemetry/types.ts +13 -0
  381. package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
  382. package/src/telemetry/usage-telemetry-reporter.ts +26 -1
  383. package/src/tools/acp/context.ts +20 -0
  384. package/src/tools/acp/list-agents.test.ts +9 -19
  385. package/src/tools/acp/list-agents.ts +3 -15
  386. package/src/tools/acp/spawn.test.ts +173 -202
  387. package/src/tools/acp/spawn.ts +37 -172
  388. package/src/tools/acp/steer.test.ts +105 -8
  389. package/src/tools/acp/steer.ts +48 -17
  390. package/src/tools/apps/executors.ts +166 -50
  391. package/src/tools/browser/browser-execution.ts +12 -2
  392. package/src/tools/filesystem/write.ts +34 -0
  393. package/src/tools/network/web-fetch.ts +65 -24
  394. package/src/tools/skills/load.ts +1 -1
  395. package/src/tools/subagent/spawn.ts +2 -4
  396. package/src/tools/ui-surface/definitions.ts +32 -5
  397. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  398. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
  399. package/docs/plugins.md +0 -832
  400. package/examples/plugins/echo/register.ts +0 -143
  401. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  402. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  403. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  404. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
  405. package/src/__tests__/pipeline-runner.test.ts +0 -554
  406. package/src/daemon/wake-target-adapter.ts +0 -253
  407. package/src/memory/v3/__tests__/assign.test.ts +0 -242
  408. package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
  409. package/src/memory/v3/__tests__/core.test.ts +0 -39
  410. package/src/memory/v3/__tests__/health.test.ts +0 -219
  411. package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
  412. package/src/memory/v3/__tests__/needle.test.ts +0 -107
  413. package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
  414. package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
  415. package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
  416. package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
  417. package/src/memory/v3/__tests__/router.test.ts +0 -332
  418. package/src/memory/v3/__tests__/selector.test.ts +0 -470
  419. package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
  420. package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
  421. package/src/memory/v3/__tests__/tree.test.ts +0 -192
  422. package/src/memory/v3/assign.ts +0 -268
  423. package/src/memory/v3/capabilities.ts +0 -124
  424. package/src/memory/v3/core.ts +0 -26
  425. package/src/memory/v3/health.ts +0 -0
  426. package/src/memory/v3/maintain-job.ts +0 -314
  427. package/src/memory/v3/needle.ts +0 -115
  428. package/src/memory/v3/orchestrate.ts +0 -126
  429. package/src/memory/v3/page-content.ts +0 -34
  430. package/src/memory/v3/provider-blocks.ts +0 -26
  431. package/src/memory/v3/reconcile.ts +0 -523
  432. package/src/memory/v3/router.ts +0 -190
  433. package/src/memory/v3/selection-log-store.ts +0 -84
  434. package/src/memory/v3/selector.ts +0 -226
  435. package/src/memory/v3/shadow-plugin.ts +0 -411
  436. package/src/memory/v3/snapshot.ts +0 -209
  437. package/src/memory/v3/tree.ts +0 -174
  438. package/src/memory/v3/types.ts +0 -59
  439. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  440. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  441. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  442. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  443. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  444. package/src/plugins/defaults/compaction/register.ts +0 -35
  445. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  446. package/src/plugins/defaults/empty-response/register.ts +0 -23
  447. package/src/plugins/defaults/history-repair/register.ts +0 -24
  448. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
  449. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  450. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  451. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  452. package/src/plugins/defaults/title-generate/register.ts +0 -35
  453. package/src/plugins/defaults/tool-error/register.ts +0 -23
  454. package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
  455. package/src/proactive-artifact/aux-message-injector.ts +0 -97
  456. package/src/proactive-artifact/decision.test.ts +0 -226
  457. package/src/proactive-artifact/decision.ts +0 -165
  458. package/src/proactive-artifact/index.ts +0 -7
  459. package/src/proactive-artifact/job.test.ts +0 -962
  460. package/src/proactive-artifact/job.ts +0 -372
  461. package/src/proactive-artifact/message-copy.ts +0 -58
  462. package/src/proactive-artifact/trigger-state.test.ts +0 -286
  463. package/src/proactive-artifact/trigger-state.ts +0 -123
  464. package/src/util/map-limit.ts +0 -27
  465. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  466. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  467. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  468. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  469. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  470. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  471. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  472. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  473. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  474. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  475. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
@@ -1,190 +0,0 @@
1
- /**
2
- * Memory v3 — L1 leaf router.
3
- *
4
- * One forced-tool LLM call per turn that picks which leaves of the topic tree
5
- * to open for the next reply. The design mirrors `../v2/router.ts`:
6
- * - resolve the configured provider via `getConfiguredProvider`,
7
- * - call `provider.sendMessage` with a forced `tool_choice`,
8
- * - validate the tool input via Zod,
9
- * - map numbered IDs back to leaf paths.
10
- *
11
- * Cache strategy. The numbered leaf block is the single largest input and is
12
- * STABLE across turns — it changes only when leaves are added/removed/edited.
13
- * We render it first in the user message and tag it with an ephemeral
14
- * `cache_control` breakpoint so the provider can serve it from the prompt
15
- * cache turn after turn. The trailing recent-context / current-message block
16
- * changes every turn, so it carries no breakpoint.
17
- *
18
- * Failure handling. A *model-call* failure is not the same as the model
19
- * choosing to open everything, so the two no longer share an outcome:
20
- * - explicit `ids` → open exactly those leaves,
21
- * - explicit empty array (`ids: []`) → open nothing (deliberate abstention),
22
- * - omitted `ids` → open nothing: the router must name the leaves it wants,
23
- * never the whole tree (~137 leaves would fan out a full L2 pass per turn),
24
- * - infrastructure failure (provider unavailable, a throw that survived the
25
- * provider's own retries, no usable `tool_use`, or a schema mismatch) →
26
- * open nothing after a short re-prompt retry, degrading to the deterministic
27
- * recall lanes (always-on core, the BM25 needle, the carry-forward working
28
- * set) that the orchestrator unions in regardless.
29
- */
30
-
31
- import { z } from "zod";
32
-
33
- import {
34
- extractToolUse,
35
- getConfiguredProvider,
36
- } from "../../providers/provider-send-message.js";
37
- import type { Message, ToolDefinition } from "../../providers/types.js";
38
- import { getLogger } from "../../util/logger.js";
39
- import { retryForResult } from "./llm-retry.js";
40
- import { cachedTextBlock } from "./provider-blocks.js";
41
- import type { LeafPath, LeafTree, TurnContext } from "./types.js";
42
-
43
- const log = getLogger("memory-v3-router");
44
-
45
- /** Tool name forced via `tool_choice`. Shared constant so tests can match it. */
46
- const OPEN_LEAVES_TOOL_NAME = "open_leaves";
47
-
48
- const OpenLeavesSchema = z.object({
49
- // Optional so the field can be absent on the wire, but an omitted `ids` opens
50
- // nothing — the router must name the leaves it wants, never the whole tree.
51
- ids: z.array(z.number().int()).optional(),
52
- });
53
-
54
- const OPEN_LEAVES_TOOL: ToolDefinition = {
55
- name: OPEN_LEAVES_TOOL_NAME,
56
- description:
57
- "Open the leaves whose contents could plausibly bear on the next reply. " +
58
- "Lean toward inclusion — a missed relevant leaf is a worse error than an " +
59
- "unused one. Pass the chosen IDs explicitly; return `[]` only when nothing " +
60
- "in the tree could possibly help.",
61
- input_schema: {
62
- type: "object",
63
- properties: {
64
- ids: {
65
- type: "array",
66
- items: { type: "integer" },
67
- },
68
- },
69
- },
70
- };
71
-
72
- const SYSTEM_PROMPT = `You route a conversation turn to the leaves of a topic tree that should be opened for the next reply.
73
-
74
- Each leaf has a numbered ID, a path, and a description of what it holds. Decide which leaves to open by weighing four signals:
75
-
76
- - Topic — entities, projects, and events named or implied by the turn.
77
- - Register — the affect and mode of the message (e.g. playful, distressed, formal). A register signal is enough to open a leaf even when no entity is named.
78
- - Recent context — the immediately preceding exchange, which resolves references like "this", "that", or "the same thing" to concrete topics.
79
- - Situation — the current date and a live scratchpad of what is salient right now. A date or state cue can make a leaf relevant even when the message never names it (e.g. a person whose anniversary is today, an active thread).
80
-
81
- Include on doubt: open every leaf that could plausibly hold something useful. Missing a relevant leaf is a worse error than opening an unused one. Call \`open_leaves\` with the chosen IDs explicitly; return \`[]\` only when nothing in the tree could possibly help.`;
82
-
83
- /** Leaves sorted deterministically by path so the numbered block is stable. */
84
- function sortedLeaves(tree: LeafTree): LeafPath[] {
85
- return [...tree.leaves.keys()].sort();
86
- }
87
-
88
- /**
89
- * Render the STATIC numbered leaf block from a pre-sorted path list. Identical
90
- * across turns for any given tree, which is what makes the prompt-cache
91
- * breakpoint pay off.
92
- */
93
- function renderLeafBlockFromPaths(tree: LeafTree, paths: LeafPath[]): string {
94
- const lines = paths.map((path, i) => {
95
- const description = tree.leaves.get(path)?.description ?? "";
96
- // Collapse the (possibly multi-line) description to a single line so each
97
- // leaf is exactly one numbered entry.
98
- const oneLine = description.replace(/\s+/g, " ").trim();
99
- return `[${i + 1}] ${path} — ${oneLine}`;
100
- });
101
- return `<leaves>\n${lines.join("\n")}\n</leaves>`;
102
- }
103
-
104
- /**
105
- * Render the static numbered leaf block for a tree. Exported for the test that
106
- * locks the byte-identical cache invariant; `routeL1` renders from its already
107
- * sorted path list to avoid sorting twice.
108
- */
109
- export function renderLeafBlock(tree: LeafTree): string {
110
- return renderLeafBlockFromPaths(tree, sortedLeaves(tree));
111
- }
112
-
113
- /**
114
- * Run the L1 router for one turn. Returns the leaf paths to open — only ever the
115
- * leaves the model names explicitly. An omitted `ids`, an explicit `[]`, or an
116
- * infrastructure failure (after a short re-prompt retry) all open nothing,
117
- * degrading to the deterministic recall lanes the orchestrator unions in.
118
- */
119
- export async function routeL1(
120
- turn: TurnContext,
121
- tree: LeafTree,
122
- ): Promise<LeafPath[]> {
123
- const paths = sortedLeaves(tree);
124
- if (paths.length === 0) return [];
125
-
126
- const provider = await getConfiguredProvider("memoryV3RouteL1");
127
- if (!provider) {
128
- log.warn(
129
- "L1 router provider unavailable; degrading to deterministic lanes",
130
- );
131
- return [];
132
- }
133
-
134
- const userMsg: Message = {
135
- role: "user",
136
- content: [
137
- cachedTextBlock(renderLeafBlockFromPaths(tree, paths)),
138
- {
139
- type: "text",
140
- text:
141
- (turn.situationalContext
142
- ? `<situation>${turn.situationalContext}</situation>\n`
143
- : "") +
144
- `<recent_context>${turn.recentContext}</recent_context>\n` +
145
- `<current_message>${turn.currentMessage}</current_message>`,
146
- },
147
- ],
148
- };
149
-
150
- // One forced-tool call, retried a few times so a transient malformed response
151
- // (no usable tool_use, or tool input that fails the schema) re-prompts before
152
- // we give up. `null` from an attempt means "unusable, retry"; the provider
153
- // layer already backs off transient throws, so this loop adds no delay.
154
- const parsed = await retryForResult(async () => {
155
- const response = await provider.sendMessage([userMsg], {
156
- tools: [OPEN_LEAVES_TOOL],
157
- systemPrompt: SYSTEM_PROMPT,
158
- config: {
159
- callSite: "memoryV3RouteL1" as const,
160
- tool_choice: { type: "tool" as const, name: OPEN_LEAVES_TOOL_NAME },
161
- },
162
- });
163
- const toolBlock = extractToolUse(response);
164
- if (!toolBlock || toolBlock.name !== OPEN_LEAVES_TOOL_NAME) return null;
165
- const result = OpenLeavesSchema.safeParse(toolBlock.input);
166
- return result.success ? result.data : null;
167
- });
168
-
169
- if (parsed === null) {
170
- log.warn(
171
- "L1 router could not obtain a selection after retries; degrading to deterministic lanes",
172
- );
173
- return [];
174
- }
175
-
176
- // An omitted `ids` field means the model named no leaves — open nothing rather
177
- // than the whole tree. Only explicitly listed IDs open leaves.
178
- if (parsed.ids === undefined) return [];
179
-
180
- // Map 1-based IDs back to leaf paths, dropping out-of-range IDs without
181
- // throwing. De-duplicate while preserving model-returned order.
182
- const seen = new Set<number>();
183
- const selected: LeafPath[] = [];
184
- for (const id of parsed.ids) {
185
- if (id < 1 || id > paths.length || seen.has(id)) continue;
186
- seen.add(id);
187
- selected.push(paths[id - 1]);
188
- }
189
- return selected;
190
- }
@@ -1,84 +0,0 @@
1
- /**
2
- * Read-side store for the inspector's Memory V3 section. Reads the persisted
3
- * `memory_v3_selections` rows for a turn and re-renders the `<memory>` block
4
- * the v3 working set selected, so the inspector can show what v3 chose (and,
5
- * in live mode, what it actually injected) without re-running orchestration —
6
- * which would be wrong anyway, since the working set is stateful (carry-forward
7
- * across turns) and can't be reproduced after the fact.
8
- */
9
-
10
- import type { MemoryV3SelectionLog } from "../../api/responses/memory-v3-selection-log.js";
11
- import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
12
- import { getConfig } from "../../config/loader.js";
13
- import { getDb, getSqliteFrom } from "../db-connection.js";
14
- import { renderV3PageContent } from "./page-content.js";
15
- import { renderMemoryBlock } from "./render-injection.js";
16
- import type { Slug } from "./types.js";
17
-
18
- const MEMORY_V3_SHADOW = "memory-v3-shadow" as const;
19
- const MEMORY_V3_LIVE = "memory-v3-live" as const;
20
-
21
- interface SelectionRow {
22
- slug: string;
23
- source: string;
24
- pinned: number;
25
- }
26
-
27
- function rowsForTurn(conversationId: string, turn: number): SelectionRow[] {
28
- return getSqliteFrom(getDb())
29
- .query(
30
- /*sql*/ `
31
- SELECT slug, source, pinned FROM memory_v3_selections
32
- WHERE conversation_id = ? AND turn = ?
33
- ORDER BY rowid
34
- `,
35
- )
36
- .all(conversationId, turn) as SelectionRow[];
37
- }
38
-
39
- /**
40
- * Build the inspector's v3 selection log for one turn of a conversation.
41
- *
42
- * `turn` is the inspected message's turn (the v2-activation log's turn). The
43
- * selection is returned ONLY for that exact turn; when there are no v3 rows for
44
- * it — including when `turn` is null (the message has no v2-activation turn) —
45
- * this returns `null` rather than falling back to a different turn. A fallback
46
- * would attribute another turn's selection (e.g. a later turn's pages and
47
- * rendered block) to the inspected message, which corrupts shadow validation.
48
- *
49
- * Caveat: v3 rows are keyed by the orchestrator turn counter (`ctx.turnCount`)
50
- * while `turn` here is the v2 memory-tracker turn. They coincide for normal
51
- * turns, but if they diverge this simply yields `null` (no section) rather than
52
- * wrong data. Tying v3 rows to a message id for exact per-message attribution
53
- * regardless of counter drift is a documented follow-up.
54
- *
55
- * Selection rows are stored in `finalInjection` order (this turn's L2
56
- * selections, then carry-forward), so rendering them in row order reproduces
57
- * the block v3 would inject.
58
- */
59
- export async function getMemoryV3SelectionForInspector(
60
- conversationId: string,
61
- turn: number | null | undefined,
62
- ): Promise<MemoryV3SelectionLog | null> {
63
- if (turn == null) return null;
64
-
65
- const rows = rowsForTurn(conversationId, turn);
66
- if (rows.length === 0) return null;
67
-
68
- const config = getConfig();
69
- const selections = rows.map((r) => ({
70
- slug: r.slug,
71
- source: r.source,
72
- pinned: r.pinned === 1,
73
- }));
74
- const slugs: Slug[] = selections.map((s) => s.slug);
75
- const injectedText = await renderMemoryBlock(slugs, renderV3PageContent);
76
-
77
- return {
78
- turn,
79
- live: isAssistantFeatureFlagEnabled(MEMORY_V3_LIVE, config),
80
- shadow: isAssistantFeatureFlagEnabled(MEMORY_V3_SHADOW, config),
81
- selections,
82
- injectedText,
83
- };
84
- }
@@ -1,226 +0,0 @@
1
- /**
2
- * Memory v3 — L2 per-leaf page selector.
3
- *
4
- * After the L1 router (`./router.ts`) decides which leaves to open, the L2
5
- * selector runs ONE forced-tool LLM call PER opened leaf to pick which pages
6
- * inside that leaf are relevant for the next reply. `selectAcrossLeaves` fans
7
- * the per-leaf calls out with bounded concurrency.
8
- *
9
- * Cache strategy. Each leaf's `<pages>` block — the numbered list of its member
10
- * pages with their full summaries — is STABLE for that leaf: it changes only
11
- * when pages are added/removed or a summary is rewritten, never per turn. We
12
- * render it FIRST in the per-leaf user message and tag it with an ephemeral
13
- * `cache_control` breakpoint so the provider serves it from the prompt cache
14
- * turn after turn. The trailing recent-context / current-message block changes
15
- * every turn and carries no breakpoint. This mirrors `./router.ts`.
16
- *
17
- * Failure handling. A deliberate "select everything" and a model-call failure
18
- * are different events with different outcomes:
19
- * - explicit `ids` → select exactly those pages,
20
- * - explicit empty `ids: []` → select nothing (deliberate abstention),
21
- * - omitted `ids` → select ALL members of the leaf (the recall-safe "this
22
- * whole leaf is relevant" signal, e.g. "give me all of X"); bounded to one
23
- * leaf, so unlike the router this stays a select-all,
24
- * - infrastructure failure (provider unavailable, a throw that survived the
25
- * provider's own retries, no usable `tool_use`, or a schema mismatch) →
26
- * select nothing after a short re-prompt retry, degrading to the
27
- * deterministic recall lanes (core, needle, carry-forward working set) the
28
- * orchestrator unions in regardless.
29
- */
30
-
31
- import { z } from "zod";
32
-
33
- import {
34
- extractToolUse,
35
- getConfiguredProvider,
36
- } from "../../providers/provider-send-message.js";
37
- import type { Message, ToolDefinition } from "../../providers/types.js";
38
- import { getLogger } from "../../util/logger.js";
39
- import { mapLimit } from "../../util/map-limit.js";
40
- import { retryForResult } from "./llm-retry.js";
41
- import { cachedTextBlock } from "./provider-blocks.js";
42
- import { membersOf } from "./tree.js";
43
- import type { LeafPath, LeafTree, Slug, TurnContext } from "./types.js";
44
-
45
- const log = getLogger("memory-v3-selector");
46
-
47
- /** A page selected from an opened leaf, with whether the turn centers on it. */
48
- export interface SelectedPage {
49
- slug: Slug;
50
- pinned: boolean;
51
- }
52
-
53
- /** Tool name forced via `tool_choice`. Shared constant so tests can match it. */
54
- const SELECT_PAGES_TOOL_NAME = "select_pages";
55
-
56
- const SelectPagesSchema = z.object({
57
- // Optional: an omitted `ids` field is the recall-safe "select everything"
58
- // signal, distinct from an explicit empty array (deliberate abstention).
59
- ids: z.array(z.number().int()).optional(),
60
- pinned_ids: z.array(z.number().int()).optional(),
61
- });
62
-
63
- const SELECT_PAGES_TOOL: ToolDefinition = {
64
- name: SELECT_PAGES_TOOL_NAME,
65
- description:
66
- "Select the pages in this leaf whose content the reply would directly " +
67
- "draw on. Be selective — prefer a few precisely-relevant pages over many " +
68
- "loosely-related ones; a leaf opened on a weak signal may yield none. " +
69
- "Pass `pinned_ids` for pages the conversation is centrally about. Omit " +
70
- "`ids` only as a recall-safe fallback when you cannot judge the leaf " +
71
- "(selects every page); return `[]` when pages are present but none are " +
72
- "directly relevant.",
73
- input_schema: {
74
- type: "object",
75
- properties: {
76
- ids: {
77
- type: "array",
78
- items: { type: "integer" },
79
- },
80
- pinned_ids: {
81
- type: "array",
82
- items: { type: "integer" },
83
- },
84
- },
85
- },
86
- };
87
-
88
- const SYSTEM_PROMPT = `This leaf of the topic tree is potentially relevant to the conversation. Select ONLY the pages whose content the reply to THIS message would directly draw on.
89
-
90
- Be selective: exclude pages that are merely topically adjacent, part of the ever-present background, or only loosely related. Most opened leaves should contribute a few precisely-relevant pages, not most of their contents — a leaf opened on a weak signal may yield none.
91
-
92
- A page can also be directly relevant because of the current situation — the date or the live scratchpad — not only the message: keep a page the situation makes pertinent (e.g. a person whose anniversary is today).
93
-
94
- If the conversation is centrally ABOUT a page (rather than only peripherally relevant to it), mark that page as pinned. Call \`select_pages\` with the chosen IDs. Omit \`ids\` only as a recall-safe fallback when you cannot judge the leaf (selects every page); return \`[]\` when the pages are present but none are directly relevant.`;
95
-
96
- /**
97
- * Render the STATIC numbered `<pages>` block for a leaf from its member slugs.
98
- * Identical across turns for a given leaf, which is what makes the per-leaf
99
- * prompt-cache breakpoint pay off. Summaries are rendered in full (no
100
- * truncation) so the selector sees each page's complete description.
101
- */
102
- async function renderPagesBlock(
103
- members: Slug[],
104
- pageSummary: (slug: Slug) => Promise<string>,
105
- ): Promise<string> {
106
- const lines = await Promise.all(
107
- members.map(async (slug, i) => {
108
- const summary = await pageSummary(slug);
109
- return `[${i + 1}] ${slug} — ${summary}`;
110
- }),
111
- );
112
- return `<pages>\n${lines.join("\n")}\n</pages>`;
113
- }
114
-
115
- /**
116
- * Run the L2 selector for a single opened leaf. Returns the pages to inject.
117
- *
118
- * An omitted `ids` selects ALL members (the recall-safe "whole leaf is
119
- * relevant" signal); an explicit `[]` selects none; an infrastructure failure
120
- * (after a short re-prompt retry) selects none, degrading to the deterministic
121
- * recall lanes the orchestrator unions in.
122
- */
123
- export async function selectFromLeaf(
124
- leaf: LeafPath,
125
- turn: TurnContext,
126
- tree: LeafTree,
127
- pageSummary: (slug: Slug) => Promise<string>,
128
- ): Promise<SelectedPage[]> {
129
- const members = membersOf(tree, leaf);
130
- if (members.length === 0) return [];
131
-
132
- const allMembers = (): SelectedPage[] =>
133
- members.map((slug) => ({ slug, pinned: false }));
134
-
135
- const provider = await getConfiguredProvider("memoryV3SelectL2");
136
- if (!provider) {
137
- log.warn(
138
- { leaf },
139
- "L2 selector provider unavailable; degrading to deterministic lanes",
140
- );
141
- return [];
142
- }
143
-
144
- const userMsg: Message = {
145
- role: "user",
146
- content: [
147
- cachedTextBlock(
148
- `<leaf>${leaf}</leaf>\n` +
149
- (await renderPagesBlock(members, pageSummary)),
150
- ),
151
- {
152
- type: "text",
153
- text:
154
- (turn.situationalContext
155
- ? `<situation>${turn.situationalContext}</situation>\n`
156
- : "") +
157
- `<recent_context>${turn.recentContext}</recent_context>\n` +
158
- `<current_message>${turn.currentMessage}</current_message>`,
159
- },
160
- ],
161
- };
162
-
163
- // One forced-tool call, retried a few times so a transient malformed response
164
- // (no usable tool_use, or tool input that fails the schema) re-prompts before
165
- // we give up. `null` from an attempt means "unusable, retry"; the provider
166
- // layer already backs off transient throws, so this loop adds no delay.
167
- const parsed = await retryForResult(async () => {
168
- const response = await provider.sendMessage([userMsg], {
169
- tools: [SELECT_PAGES_TOOL],
170
- systemPrompt: SYSTEM_PROMPT,
171
- config: {
172
- callSite: "memoryV3SelectL2" as const,
173
- tool_choice: { type: "tool" as const, name: SELECT_PAGES_TOOL_NAME },
174
- },
175
- });
176
- const toolBlock = extractToolUse(response);
177
- if (!toolBlock || toolBlock.name !== SELECT_PAGES_TOOL_NAME) return null;
178
- const result = SelectPagesSchema.safeParse(toolBlock.input);
179
- return result.success ? result.data : null;
180
- });
181
-
182
- if (parsed === null) {
183
- log.warn(
184
- { leaf },
185
- "L2 selector could not obtain a selection after retries; degrading to deterministic lanes",
186
- );
187
- return [];
188
- }
189
-
190
- // Omitted `ids` is the recall-safe "this whole leaf is relevant" signal.
191
- // Bounded to one leaf, so it stays a select-all (unlike the L1 router).
192
- if (parsed.ids === undefined) return allMembers();
193
-
194
- const pinned = new Set(parsed.pinned_ids ?? []);
195
-
196
- // Map 1-based IDs back to member slugs, dropping out-of-range IDs without
197
- // throwing. De-duplicate while preserving model-returned order.
198
- const seen = new Set<number>();
199
- const selected: SelectedPage[] = [];
200
- for (const id of parsed.ids) {
201
- if (id < 1 || id > members.length || seen.has(id)) continue;
202
- seen.add(id);
203
- selected.push({ slug: members[id - 1]!, pinned: pinned.has(id) });
204
- }
205
- return selected;
206
- }
207
-
208
- /**
209
- * Run the L2 selector across every opened leaf with bounded concurrency and
210
- * flatten the per-leaf selections.
211
- *
212
- * A page assigned to more than one opened leaf may appear more than once in the
213
- * result; de-duplication across leaves is the orchestrator's job in a later PR.
214
- */
215
- export async function selectAcrossLeaves(
216
- leaves: LeafPath[],
217
- turn: TurnContext,
218
- tree: LeafTree,
219
- pageSummary: (slug: Slug) => Promise<string>,
220
- concurrency = 4,
221
- ): Promise<SelectedPage[]> {
222
- const perLeaf = await mapLimit(leaves, concurrency, (leaf) =>
223
- selectFromLeaf(leaf, turn, tree, pageSummary),
224
- );
225
- return perLeaf.flat();
226
- }