@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
@@ -0,0 +1,555 @@
1
+ /**
2
+ * Memory v3 — `memory_v3_maintain` job handler.
3
+ *
4
+ * A flag-gated, best-effort self-maintenance pass over the v3 section dense
5
+ * store and the in-memory lanes. It runs three independent stages, in order:
6
+ *
7
+ * 1. **Section re-embed** — diff the page index by `modifiedAt` against the
8
+ * last successful pass (the high-water mark below), and for every page that
9
+ * is new or edited since then, re-chunk it into sections
10
+ * (`buildSectionIndex`) and refresh its dense points
11
+ * (`deleteSectionsForArticle` + `upsertSections`). This keeps the
12
+ * section-grain Qdrant collection in sync with on-disk page edits so the
13
+ * dense lane retrieves against current content. The high-water mark is
14
+ * advanced only after the pass completes with zero page failures (and is
15
+ * captured before any potential mtime bumps) so a page is not re-embedded
16
+ * forever, yet a page whose embed failed (and whose sections were therefore
17
+ * left deleted) stays above the mark and is retried next pass.
18
+ * 2. **Deleted-page prune** — diff the dense store's stored articles
19
+ * (`listSectionArticles`) against the live page-index slugs and
20
+ * `deleteSectionsForArticle` for any article that is no longer in the
21
+ * index. A deleted page's slug never reaches the re-embed delta selector
22
+ * (it only names live pages), so without this its section points would
23
+ * linger in Qdrant and the dense lane could still surface the deleted page.
24
+ * Synthetic capability rows are in the page index, so they are never pruned.
25
+ * 3. **Lane invalidation** — `invalidateLanes()` so the next turn rebuilds the
26
+ * in-memory section index, needle, and edge graph from the freshly-updated
27
+ * pages.
28
+ *
29
+ * Best-effort by construction: each stage is wrapped so a failure in one is
30
+ * logged and recorded in the outcome but does NOT abort the others. A single
31
+ * page whose embed fails does not abort the rest of the re-embed stage, and a
32
+ * single prune delete that throws does not abort the rest of the prune stage.
33
+ * The job is a no-op (returns a disabled outcome) unless `memory-v3-shadow` OR
34
+ * `memory-v3-live` is enabled — the same flags that gate the v3 plugin.
35
+ *
36
+ * Dependency-injectable: `deps` lets tests substitute the page-index reader,
37
+ * section builder, dense-store ops (including the prune-stage
38
+ * `listSectionArticles`/`listIndexedSlugs` collaborators), and `invalidateLanes`
39
+ * without process-global module mocks.
40
+ */
41
+
42
+ import { isAssistantFeatureFlagEnabled } from "../../../config/assistant-feature-flags.js";
43
+ import type { AssistantConfig } from "../../../config/types.js";
44
+ import {
45
+ getMemoryCheckpoint,
46
+ setMemoryCheckpoint,
47
+ } from "../../../memory/checkpoints.js";
48
+ import type { MemoryJob } from "../../../memory/jobs-store.js";
49
+ import { getPageIndex } from "../../../memory/v2/page-index.js";
50
+ import { readPage } from "../../../memory/v2/page-store.js";
51
+ import { getLogger } from "../../../util/logger.js";
52
+ import { getWorkspaceDir } from "../../../util/platform.js";
53
+ import { capabilityOrDiskBody } from "./capabilities.js";
54
+ import {
55
+ deleteSectionsForArticle as realDeleteSectionsForArticle,
56
+ ensureSectionCollection as realEnsureSectionCollection,
57
+ listSectionArticles as realListSectionArticles,
58
+ upsertSections as realUpsertSections,
59
+ } from "./section-dense-store.js";
60
+ import { buildSectionIndex as realBuildSectionIndex } from "./sections.js";
61
+ import { invalidateLanes as realInvalidateLanes } from "./shadow-plugin.js";
62
+ import type { Slug } from "./types.js";
63
+
64
+ const MEMORY_V3_SHADOW = "memory-v3-shadow" as const;
65
+ const MEMORY_V3_LIVE = "memory-v3-live" as const;
66
+
67
+ /**
68
+ * Durable checkpoint holding the epoch-ms high-water mark of the last successful
69
+ * re-embed pass. Pages whose mtime is past this mark are re-chunked + re-embedded
70
+ * into the section dense store; the mark is advanced only after a pass completes,
71
+ * captured before the pass so a transient embed write does not re-trigger itself.
72
+ *
73
+ * This is a FRESH key, distinct from the tree-era `enriched_through_ms` that the
74
+ * old classify-union maintainer advanced. On upgrade the new key is absent, so
75
+ * `computeChangedPages` sees a high-water of 0 and re-embeds EVERY page on the
76
+ * first run — seeding the otherwise-empty `memory_v3_sections` collection.
77
+ * Reusing the old (already-advanced) key would skip all historical pages and
78
+ * leave dense retrieval blind to existing memory until each page was next edited.
79
+ *
80
+ * Distinct too from `memory_v3_maintain_last_run` (the enqueue-cadence checkpoint
81
+ * in `jobs-worker.ts`), which advances on every backstop enqueue rather than on
82
+ * an actual maintenance run.
83
+ */
84
+ const MAINTAIN_EMBED_HIGH_WATER_KEY =
85
+ "memory_v3_maintain:sections_embedded_through_ms" as const;
86
+
87
+ const log = getLogger("memory-v3-maintain");
88
+
89
+ /** Page-index projection the changed-page selector reads. */
90
+ export interface ChangedPageCandidate {
91
+ slug: Slug;
92
+ /** File mtime in epoch ms; 0 for synthetic skill/CLI rows (excluded). */
93
+ modifiedAt: number;
94
+ }
95
+
96
+ /** Injectable collaborators; defaults wire the real implementations. */
97
+ export interface MaintainJobDeps {
98
+ /**
99
+ * The slugs whose sections to re-embed this pass: pages new or edited since
100
+ * the last successful pass. See {@link computeChangedPages}.
101
+ */
102
+ selectChangedPages: () => Promise<Slug[]>;
103
+ /** Re-chunk pages into the flat section index (pure; reads page bodies). */
104
+ buildSectionIndex: typeof realBuildSectionIndex;
105
+ /** Read a page's frontmatter-stripped body for `buildSectionIndex`. */
106
+ readPageBody: (slug: Slug) => Promise<string>;
107
+ /** Clear an article's stale section points before re-upserting. */
108
+ deleteSectionsForArticle: typeof realDeleteSectionsForArticle;
109
+ /** Embed + upsert an article's current sections into the dense store. */
110
+ upsertSections: typeof realUpsertSections;
111
+ /**
112
+ * Persist the high-water mark after a re-embed pass with zero failures. The
113
+ * value is captured before the pass's writes (see the key docstring); the
114
+ * caller skips this when any page failed so failed pages retry next pass.
115
+ */
116
+ commitEmbedHighWater: (highWaterMs: number) => void;
117
+ /**
118
+ * Every distinct `article` slug that currently has section points in the
119
+ * dense store. The prune stage diffs this against the live page-index slugs
120
+ * to find articles whose points linger after the page was deleted.
121
+ */
122
+ listSectionArticles: () => Promise<string[]>;
123
+ /**
124
+ * The slugs currently in the page index (the live set the lanes are rebuilt
125
+ * from). The prune stage keeps these and deletes any section article absent
126
+ * from this set. Includes synthetic capability rows so they are never pruned.
127
+ */
128
+ listIndexedSlugs: () => Promise<Slug[]>;
129
+ /** Drop the memoized v3 lanes so the next turn rebuilds them. */
130
+ invalidateLanes: () => void;
131
+ /** Active assistant config (for the dense-store/embedding calls). */
132
+ config: AssistantConfig;
133
+ }
134
+
135
+ /**
136
+ * Injectable collaborators for the one-time {@link backfillAllSections} pass.
137
+ * Distinct from {@link MaintainJobDeps}: the backfill embeds EVERY page
138
+ * (including synthetic capability rows the incremental selector excludes), so it
139
+ * uses an all-pages selector, a capability-aware body reader, and an explicit
140
+ * collection-ensure step rather than the change-delta machinery.
141
+ */
142
+ export interface BackfillJobDeps {
143
+ /** All page-index slugs, including synthetic skill/CLI capability rows. */
144
+ selectAllPages: () => Promise<Slug[]>;
145
+ /** Create the section collection if absent before the first upsert. */
146
+ ensureSectionCollection: typeof realEnsureSectionCollection;
147
+ /** Re-chunk pages into the flat section index (pure; reads page bodies). */
148
+ buildSectionIndex: typeof realBuildSectionIndex;
149
+ /** Read a page's body; capability slugs resolve their rendered content. */
150
+ readPageBody: (slug: Slug) => Promise<string>;
151
+ /** Clear an article's stale section points before re-upserting. */
152
+ deleteSectionsForArticle: typeof realDeleteSectionsForArticle;
153
+ /** Embed + upsert an article's current sections into the dense store. */
154
+ upsertSections: typeof realUpsertSections;
155
+ /**
156
+ * Persist the high-water mark after the backfill completes with zero
157
+ * failures. Skipped when any page failed so failed pages retry next pass.
158
+ */
159
+ commitEmbedHighWater: (highWaterMs: number) => void;
160
+ /** Epoch-ms stamped as the new high-water mark; injectable for tests. */
161
+ nowMs: () => number;
162
+ /** Active assistant config (for the dense-store/embedding calls). */
163
+ config: AssistantConfig;
164
+ }
165
+
166
+ /** Counts surfaced by {@link backfillAllSections}. */
167
+ export interface BackfillOutcome {
168
+ /** Pages whose sections were (re-)embedded this pass. */
169
+ articles: number;
170
+ /** Total section points upserted across all articles. */
171
+ sections: number;
172
+ /** Pages whose embed threw (and was contained). */
173
+ failures: number;
174
+ }
175
+
176
+ /** Per-stage outcome surfaced in the structured log line. */
177
+ export interface MaintainOutcome {
178
+ /** True when both v3 flags were off and the job no-opped. */
179
+ disabled: boolean;
180
+ /** Pages whose sections were re-chunked + re-embedded this pass. */
181
+ reembedded: number;
182
+ /** Pages whose re-embed threw (and was contained). */
183
+ reembedFailures: number;
184
+ /**
185
+ * Deleted-page articles whose lingering section points were pruned this pass
186
+ * (present in the dense store but absent from the page index).
187
+ */
188
+ pruned: number;
189
+ /** Prune deletes that threw (and were contained). */
190
+ pruneFailures: number;
191
+ /** Whether the in-memory lanes were invalidated. */
192
+ invalidated: boolean;
193
+ /** Stages that threw (and were contained). */
194
+ failures: string[];
195
+ }
196
+
197
+ /**
198
+ * Pick the pages the re-embed stage should refresh this pass: every page whose
199
+ * mtime is past `prevHighWaterMs` (new or edited since the last successful run).
200
+ * Synthetic skill/CLI rows (`modifiedAt === 0`) are excluded — they are
201
+ * capability entries with no on-disk page to chunk. On the first run
202
+ * (`prevHighWaterMs` null) every real page is selected so a fresh or
203
+ * freshly-backfilled install seeds the section dense store once.
204
+ */
205
+ export function computeChangedPages(
206
+ pages: ReadonlyArray<ChangedPageCandidate>,
207
+ prevHighWaterMs: number | null,
208
+ ): Slug[] {
209
+ const changed: Slug[] = [];
210
+ for (const page of pages) {
211
+ if (page.modifiedAt <= 0) continue; // synthetic capability row
212
+ if (prevHighWaterMs === null || page.modifiedAt > prevHighWaterMs) {
213
+ changed.push(page.slug);
214
+ }
215
+ }
216
+ return changed;
217
+ }
218
+
219
+ /** Read the persisted high-water mark, treating missing/garbage as first-run. */
220
+ function readEmbedHighWater(): number | null {
221
+ const raw = getMemoryCheckpoint(MAINTAIN_EMBED_HIGH_WATER_KEY);
222
+ if (raw === null) return null;
223
+ const parsed = Number.parseInt(raw, 10);
224
+ return Number.isNaN(parsed) ? null : parsed;
225
+ }
226
+
227
+ /** Default selector: page index → {@link computeChangedPages}. */
228
+ async function selectChangedPagesFromWorkspace(
229
+ workspaceDir: string,
230
+ ): Promise<Slug[]> {
231
+ const index = await getPageIndex(workspaceDir);
232
+ return computeChangedPages(index.entries, readEmbedHighWater());
233
+ }
234
+
235
+ function commitEmbedHighWater(highWaterMs: number): void {
236
+ setMemoryCheckpoint(MAINTAIN_EMBED_HIGH_WATER_KEY, String(highWaterMs));
237
+ }
238
+
239
+ /** Read a page's frontmatter-stripped body; missing/failed reads degrade to "". */
240
+ async function readPageBodyFromWorkspace(
241
+ workspaceDir: string,
242
+ slug: Slug,
243
+ ): Promise<string> {
244
+ try {
245
+ const page = await readPage(workspaceDir, slug);
246
+ return page?.body ?? "";
247
+ } catch {
248
+ return "";
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Capability-aware page-body reader for the full backfill: synthetic skill/CLI
254
+ * slugs have no on-disk page, so they contribute their rendered capability
255
+ * content (exactly what `page-content.ts` injects for them) while real pages
256
+ * read from disk. Shares the capability-or-disk dispatch with `shadow-plugin.ts`
257
+ * `initLanes`' `pageBody` via {@link capabilityOrDiskBody}.
258
+ */
259
+ async function backfillPageBodyFromWorkspace(
260
+ workspaceDir: string,
261
+ slug: Slug,
262
+ ): Promise<string> {
263
+ return capabilityOrDiskBody(slug, (s) =>
264
+ readPageBodyFromWorkspace(workspaceDir, s),
265
+ );
266
+ }
267
+
268
+ /** All page-index slugs, including synthetic skill/CLI capability rows. */
269
+ async function selectAllPagesFromWorkspace(
270
+ workspaceDir: string,
271
+ ): Promise<Slug[]> {
272
+ const index = await getPageIndex(workspaceDir);
273
+ return index.entries.map((entry) => entry.slug);
274
+ }
275
+
276
+ function defaultDeps(config: AssistantConfig): MaintainJobDeps {
277
+ const workspaceDir = getWorkspaceDir();
278
+ return {
279
+ selectChangedPages: () => selectChangedPagesFromWorkspace(workspaceDir),
280
+ buildSectionIndex: realBuildSectionIndex,
281
+ readPageBody: (slug) => readPageBodyFromWorkspace(workspaceDir, slug),
282
+ deleteSectionsForArticle: realDeleteSectionsForArticle,
283
+ upsertSections: realUpsertSections,
284
+ commitEmbedHighWater,
285
+ listSectionArticles: () => realListSectionArticles(config),
286
+ listIndexedSlugs: () => selectAllPagesFromWorkspace(workspaceDir),
287
+ invalidateLanes: realInvalidateLanes,
288
+ config,
289
+ };
290
+ }
291
+
292
+ function defaultBackfillDeps(config: AssistantConfig): BackfillJobDeps {
293
+ const workspaceDir = getWorkspaceDir();
294
+ return {
295
+ selectAllPages: () => selectAllPagesFromWorkspace(workspaceDir),
296
+ ensureSectionCollection: realEnsureSectionCollection,
297
+ buildSectionIndex: realBuildSectionIndex,
298
+ readPageBody: (slug) => backfillPageBodyFromWorkspace(workspaceDir, slug),
299
+ deleteSectionsForArticle: realDeleteSectionsForArticle,
300
+ upsertSections: realUpsertSections,
301
+ commitEmbedHighWater,
302
+ nowMs: () => Date.now(),
303
+ config,
304
+ };
305
+ }
306
+
307
+ /**
308
+ * Re-chunk + re-embed each changed page into the section dense store. Each page
309
+ * is refreshed independently: a single page whose delete/build/upsert throws is
310
+ * logged and counted in `reembedFailures` without aborting the rest. Returns the
311
+ * counts of successfully refreshed and failed pages.
312
+ */
313
+ async function reembedChangedPages(
314
+ slugs: Slug[],
315
+ deps: MaintainJobDeps,
316
+ ): Promise<{ reembedded: number; reembedFailures: number }> {
317
+ let reembedded = 0;
318
+ let reembedFailures = 0;
319
+ for (const slug of slugs) {
320
+ try {
321
+ const index = await deps.buildSectionIndex([slug], deps.readPageBody);
322
+ await deps.deleteSectionsForArticle(deps.config, slug);
323
+ await deps.upsertSections(deps.config, index.sections);
324
+ reembedded += 1;
325
+ } catch (err) {
326
+ reembedFailures += 1;
327
+ log.warn(
328
+ { slug, err: err instanceof Error ? err.message : String(err) },
329
+ "memory-v3 maintain: page re-embed failed (non-fatal)",
330
+ );
331
+ }
332
+ }
333
+ return { reembedded, reembedFailures };
334
+ }
335
+
336
+ /**
337
+ * Prune section points for articles that no longer exist in the page index. A
338
+ * deleted concept page leaves `getPageIndex` (so the needle and edge lanes both
339
+ * drop it on the next rebuild) but its section points linger in Qdrant, where
340
+ * the dense lane could still surface it. The incremental selector
341
+ * ({@link computeChangedPages}) never names a slug that is gone, so a deleted
342
+ * page's stale points are only reachable by reading the collection back and
343
+ * diffing against the live slug set.
344
+ *
345
+ * Each deletion is independent: a single `deleteSectionsForArticle` throw is
346
+ * logged and counted in `pruneFailures` without aborting the rest. Synthetic
347
+ * capability rows are in the page index, so they are never pruned.
348
+ */
349
+ async function pruneDeletedPages(
350
+ deps: MaintainJobDeps,
351
+ ): Promise<{ pruned: number; pruneFailures: number }> {
352
+ const [storedArticles, indexedSlugs] = await Promise.all([
353
+ deps.listSectionArticles(),
354
+ deps.listIndexedSlugs(),
355
+ ]);
356
+ const live = new Set(indexedSlugs);
357
+ const deleted = storedArticles.filter((article) => !live.has(article));
358
+
359
+ let pruned = 0;
360
+ let pruneFailures = 0;
361
+ for (const article of deleted) {
362
+ try {
363
+ await deps.deleteSectionsForArticle(deps.config, article);
364
+ pruned += 1;
365
+ } catch (err) {
366
+ pruneFailures += 1;
367
+ log.warn(
368
+ { article, err: err instanceof Error ? err.message : String(err) },
369
+ "memory-v3 maintain: deleted-page section prune failed (non-fatal)",
370
+ );
371
+ }
372
+ }
373
+ return { pruned, pruneFailures };
374
+ }
375
+
376
+ /**
377
+ * One-time full backfill of the section dense store. Unlike {@link maintainJob}
378
+ * (which only re-embeds pages changed since the high-water mark and EXCLUDES
379
+ * synthetic capability rows), this embeds EVERY page in the index — including
380
+ * synthetic skill/CLI rows, whose `modifiedAt` is 0 and which the incremental
381
+ * selector therefore never picks. It exists for the operator transition before
382
+ * A/B/cutover: the `memory_v3_sections` collection starts empty, so on an
383
+ * existing install most pages were never embedded into the dense lane.
384
+ *
385
+ * Each article is refreshed independently (delete its stale points, then
386
+ * re-upsert its current sections); a single article whose build/delete/upsert
387
+ * throws is logged and counted in `failures` without aborting the rest. When the
388
+ * pass completes with zero failures the high-water mark is advanced to the
389
+ * timestamp captured BEFORE the pass, so the next incremental maintain run
390
+ * deltas from here instead of re-embedding the whole corpus again. The timestamp
391
+ * is captured up-front (as in `maintainJob`) so an embed write that bumps a
392
+ * page's mtime does not re-trigger it next pass. If any page failed the
393
+ * checkpoint is held (not advanced): a failed page was delete-then-upsert'd and
394
+ * so has no sections, and advancing past its mtime would hide it from the
395
+ * incremental selector forever — holding the mark lets the next pass re-embed it.
396
+ */
397
+ export async function backfillAllSections(
398
+ config: AssistantConfig,
399
+ deps: BackfillJobDeps = defaultBackfillDeps(config),
400
+ ): Promise<BackfillOutcome> {
401
+ // Capture the high-water stamp before any writes (see the function docstring).
402
+ const startedAtMs = deps.nowMs();
403
+
404
+ const slugs = await deps.selectAllPages();
405
+ await deps.ensureSectionCollection(deps.config);
406
+
407
+ let articles = 0;
408
+ let sections = 0;
409
+ let failures = 0;
410
+ for (const slug of slugs) {
411
+ try {
412
+ const index = await deps.buildSectionIndex([slug], deps.readPageBody);
413
+ await deps.deleteSectionsForArticle(deps.config, slug);
414
+ await deps.upsertSections(deps.config, index.sections);
415
+ articles += 1;
416
+ sections += index.sections.length;
417
+ } catch (err) {
418
+ failures += 1;
419
+ log.warn(
420
+ { slug, err: err instanceof Error ? err.message : String(err) },
421
+ "memory-v3 backfill: page embed failed (non-fatal)",
422
+ );
423
+ }
424
+ }
425
+
426
+ // Advance the maintain high-water mark only when every page embedded cleanly,
427
+ // so the next incremental pass deltas from here rather than re-embedding
428
+ // everything. A failed page was delete-then-upsert'd, so it now has no
429
+ // sections; advancing past its mtime would hide it from `computeChangedPages`
430
+ // forever. Holding the checkpoint keeps failed pages above the mark so the
431
+ // next pass re-embeds them — re-embedding the succeeded pages too is
432
+ // idempotent and cheap.
433
+ if (failures === 0) {
434
+ deps.commitEmbedHighWater(startedAtMs);
435
+ } else {
436
+ log.info(
437
+ { articles, sections, failures },
438
+ "memory-v3 backfill: embed checkpoint held (page failures) — failed pages retry next pass",
439
+ );
440
+ }
441
+
442
+ log.info(
443
+ { articles, sections, failures },
444
+ "memory-v3 section backfill complete",
445
+ );
446
+
447
+ return { articles, sections, failures };
448
+ }
449
+
450
+ /**
451
+ * Run the v3 self-maintenance pass. Each stage is independently contained: a
452
+ * thrown error is logged and recorded in `failures` without aborting the rest.
453
+ * No-ops when both v3 flags are off.
454
+ */
455
+ export async function maintainJob(
456
+ _job: MemoryJob,
457
+ config: AssistantConfig,
458
+ deps: MaintainJobDeps = defaultDeps(config),
459
+ ): Promise<MaintainOutcome> {
460
+ const outcome: MaintainOutcome = {
461
+ disabled: false,
462
+ reembedded: 0,
463
+ reembedFailures: 0,
464
+ pruned: 0,
465
+ pruneFailures: 0,
466
+ invalidated: false,
467
+ failures: [],
468
+ };
469
+
470
+ const enabled =
471
+ isAssistantFeatureFlagEnabled(MEMORY_V3_SHADOW, config) ||
472
+ isAssistantFeatureFlagEnabled(MEMORY_V3_LIVE, config);
473
+ if (!enabled) {
474
+ outcome.disabled = true;
475
+ return outcome;
476
+ }
477
+
478
+ // Stage 1: re-chunk + re-embed pages changed since the last pass. Capture the
479
+ // high-water mark BEFORE the pass so an embed write that bumps a page's mtime
480
+ // does not re-trigger it next pass; advance it only when every page succeeded.
481
+ try {
482
+ const startedAtMs = Date.now();
483
+ const changed = await deps.selectChangedPages();
484
+ const { reembedded, reembedFailures } = await reembedChangedPages(
485
+ changed,
486
+ deps,
487
+ );
488
+ outcome.reembedded = reembedded;
489
+ outcome.reembedFailures = reembedFailures;
490
+ // Only advance the high-water mark when nothing failed. A failed page is
491
+ // processed as delete-then-upsert, so a transient embed/Qdrant error leaves
492
+ // its sections deleted; if we advanced past its mtime, `computeChangedPages`
493
+ // would never re-select it (mtime <= high-water) and it would stay missing.
494
+ // Holding the checkpoint keeps every failed page above the mark so it
495
+ // retries next pass — the handful of already-succeeded pages re-embedding
496
+ // again is idempotent and cheap.
497
+ if (reembedFailures === 0) {
498
+ deps.commitEmbedHighWater(startedAtMs);
499
+ } else {
500
+ log.info(
501
+ { reembedded, reembedFailures },
502
+ "memory-v3 maintain: embed checkpoint held (page failures) — failed pages retry next pass",
503
+ );
504
+ }
505
+ } catch (err) {
506
+ outcome.failures.push("reembed");
507
+ log.warn(
508
+ { err: err instanceof Error ? err.message : String(err) },
509
+ "memory-v3 maintain: section re-embed failed (non-fatal)",
510
+ );
511
+ }
512
+
513
+ // Stage 2: prune section points for pages deleted from the index. A deleted
514
+ // page's slug never appears in `selectChangedPages` (the delta selector only
515
+ // names live pages), so its lingering points are cleared by diffing the dense
516
+ // store's stored articles against the live page-index slugs. Contained: a
517
+ // failure is logged + recorded in `failures` without aborting invalidation.
518
+ try {
519
+ const { pruned, pruneFailures } = await pruneDeletedPages(deps);
520
+ outcome.pruned = pruned;
521
+ outcome.pruneFailures = pruneFailures;
522
+ } catch (err) {
523
+ outcome.failures.push("prune");
524
+ log.warn(
525
+ { err: err instanceof Error ? err.message : String(err) },
526
+ "memory-v3 maintain: deleted-page prune failed (non-fatal)",
527
+ );
528
+ }
529
+
530
+ // Stage 3: rebuild section index + needle + edge graph on the next turn.
531
+ try {
532
+ deps.invalidateLanes();
533
+ outcome.invalidated = true;
534
+ } catch (err) {
535
+ outcome.failures.push("invalidate");
536
+ log.warn(
537
+ { err: err instanceof Error ? err.message : String(err) },
538
+ "memory-v3 maintain: lane invalidation failed (non-fatal)",
539
+ );
540
+ }
541
+
542
+ log.info(
543
+ {
544
+ reembedded: outcome.reembedded,
545
+ reembedFailures: outcome.reembedFailures,
546
+ pruned: outcome.pruned,
547
+ pruneFailures: outcome.pruneFailures,
548
+ invalidated: outcome.invalidated,
549
+ failures: outcome.failures,
550
+ },
551
+ "memory-v3 maintain pass complete",
552
+ );
553
+
554
+ return outcome;
555
+ }