@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091926.ebb2d62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (490) hide show
  1. package/ARCHITECTURE.md +6 -6
  2. package/bun.lock +2 -2
  3. package/docs/activation-funnel-telemetry.md +310 -0
  4. package/examples/plugins/echo/README.md +61 -60
  5. package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
  6. package/examples/plugins/echo/hooks/stop.ts +16 -0
  7. package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
  8. package/examples/plugins/echo/package.json +1 -2
  9. package/examples/plugins/echo/src/emit.ts +19 -0
  10. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
  11. package/openapi.yaml +273 -121
  12. package/package.json +2 -2
  13. package/scripts/sync-llm-catalog.ts +2 -0
  14. package/src/__tests__/activation-early-marking.test.ts +120 -0
  15. package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
  16. package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
  17. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
  18. package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
  19. package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
  20. package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
  21. package/src/__tests__/agent-loop-thinking.test.ts +36 -20
  22. package/src/__tests__/agent-loop.test.ts +441 -96
  23. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
  24. package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
  25. package/src/__tests__/anthropic-provider.test.ts +23 -8
  26. package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
  27. package/src/__tests__/app-control-flow.test.ts +1 -1
  28. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  29. package/src/__tests__/app-executors.test.ts +132 -0
  30. package/src/__tests__/approval-cascade.test.ts +6 -5
  31. package/src/__tests__/approval-routes-http.test.ts +4 -1
  32. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  33. package/src/__tests__/build-persisted-content.test.ts +75 -1
  34. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  35. package/src/__tests__/channel-approvals.test.ts +1 -1
  36. package/src/__tests__/compaction-circuit.test.ts +258 -0
  37. package/src/__tests__/compaction-direct.test.ts +146 -0
  38. package/src/__tests__/compaction-events.test.ts +7 -7
  39. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  40. package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
  41. package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
  42. package/src/__tests__/context-overflow-reducer.test.ts +265 -125
  43. package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
  44. package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
  45. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
  46. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
  47. package/src/__tests__/conversation-agent-loop-overflow.test.ts +158 -234
  48. package/src/__tests__/conversation-agent-loop.test.ts +342 -623
  49. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  50. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
  51. package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
  52. package/src/__tests__/conversation-error.test.ts +15 -1
  53. package/src/__tests__/conversation-history-web-search.test.ts +6 -1
  54. package/src/__tests__/conversation-media-retry.test.ts +1 -1
  55. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
  56. package/src/__tests__/conversation-process-callsite.test.ts +15 -15
  57. package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
  58. package/src/__tests__/conversation-queue.test.ts +10 -10
  59. package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
  60. package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
  61. package/src/__tests__/conversation-slash-queue.test.ts +7 -5
  62. package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
  63. package/src/__tests__/conversation-speed-override.test.ts +11 -10
  64. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  65. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
  66. package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
  67. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
  68. package/src/__tests__/conversation-undo.test.ts +2 -2
  69. package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
  70. package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
  71. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
  72. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  73. package/src/__tests__/corrected-target.test.ts +93 -0
  74. package/src/__tests__/credential-execution-feature-gates.test.ts +5 -4
  75. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  76. package/src/__tests__/cu-unified-flow.test.ts +36 -0
  77. package/src/__tests__/db-acp-history.test.ts +101 -0
  78. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  79. package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
  80. package/src/__tests__/empty-response-hook.test.ts +1 -1
  81. package/src/__tests__/file-write-tool.test.ts +63 -0
  82. package/src/__tests__/gateway-only-guard.test.ts +17 -2
  83. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  84. package/src/__tests__/guardian-routing-invariants.test.ts +93 -4
  85. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  86. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  87. package/src/__tests__/heartbeat-service.test.ts +1 -0
  88. package/src/__tests__/history-repair-hook.test.ts +5 -1
  89. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  90. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  91. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  92. package/src/__tests__/inference-profile-reaper.test.ts +62 -0
  93. package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
  94. package/src/__tests__/injector-background-turn.test.ts +13 -23
  95. package/src/__tests__/injector-chain.test.ts +268 -44
  96. package/src/__tests__/injector-disk-pressure.test.ts +210 -52
  97. package/src/__tests__/injector-document-comments.test.ts +97 -114
  98. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  99. package/src/__tests__/injector-v3-suppression.test.ts +4 -4
  100. package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
  101. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  102. package/src/__tests__/llm-catalog-parity.test.ts +4 -0
  103. package/src/__tests__/llm-resolver.test.ts +73 -0
  104. package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
  105. package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
  106. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  107. package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
  108. package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
  109. package/src/__tests__/persist-unsendable-image.test.ts +97 -1
  110. package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
  111. package/src/__tests__/plugin-api-shim.test.ts +6 -9
  112. package/src/__tests__/plugin-bootstrap.test.ts +91 -81
  113. package/src/__tests__/plugin-registry.test.ts +3 -49
  114. package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
  115. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  116. package/src/__tests__/plugin-types.test.ts +0 -70
  117. package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
  118. package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
  119. package/src/__tests__/published-app-updater.test.ts +138 -0
  120. package/src/__tests__/reaction-persistence.test.ts +1 -1
  121. package/src/__tests__/retry-thinking-adaptive-only.test.ts +245 -0
  122. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  123. package/src/__tests__/server-history-render.test.ts +39 -0
  124. package/src/__tests__/skill-feature-flags-integration.test.ts +31 -0
  125. package/src/__tests__/steer-tool-repair.test.ts +1 -1
  126. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  127. package/src/__tests__/subagent-detail.test.ts +25 -7
  128. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  129. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  130. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  131. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  132. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  133. package/src/__tests__/title-generate-hook.test.ts +5 -1
  134. package/src/__tests__/tool-error-hook.test.ts +1 -1
  135. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
  136. package/src/__tests__/tool-start-timestamp.test.ts +218 -0
  137. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  138. package/src/__tests__/web-fetch.test.ts +45 -0
  139. package/src/acp/__tests__/agent-process.test.ts +161 -0
  140. package/src/acp/__tests__/client-handler.test.ts +40 -0
  141. package/src/acp/__tests__/helpers/acp-config-stub.ts +0 -2
  142. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  143. package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
  144. package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
  145. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  146. package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
  147. package/src/acp/agent-process.ts +61 -1
  148. package/src/acp/auto-install.test.ts +280 -0
  149. package/src/acp/auto-install.ts +232 -0
  150. package/src/acp/client-handler.ts +31 -0
  151. package/src/acp/prepare-agent-env.ts +80 -27
  152. package/src/acp/resolve-agent.test.ts +188 -28
  153. package/src/acp/resolve-agent.ts +119 -42
  154. package/src/acp/resume-hint.ts +23 -0
  155. package/src/acp/session-manager.ts +507 -73
  156. package/src/agent/compaction-circuit.ts +60 -102
  157. package/src/agent/loop.ts +403 -251
  158. package/src/api/events/tool-result.ts +9 -0
  159. package/src/api/events/tool-use-start.ts +7 -0
  160. package/src/api/responses/conversation-message.ts +72 -13
  161. package/src/api/responses/memory-v3-selection-log.ts +19 -10
  162. package/src/approvals/guardian-request-resolvers.ts +27 -1
  163. package/src/background-wake/next-wake.ts +1 -0
  164. package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
  165. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  166. package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
  167. package/src/cli/commands/memory-v3.ts +57 -199
  168. package/src/cli/commands/plugins.ts +43 -37
  169. package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
  170. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
  171. package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
  172. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
  173. package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
  174. package/src/cli/lib/install-from-github.ts +797 -60
  175. package/src/cli/lib/plugin-catalog-cache.ts +84 -0
  176. package/src/cli/lib/plugin-details.ts +416 -0
  177. package/src/cli/lib/plugin-marketplace.ts +215 -0
  178. package/src/cli/lib/search-plugins.ts +201 -29
  179. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  180. package/src/config/acp-defaults.test.ts +10 -0
  181. package/src/config/acp-defaults.ts +9 -3
  182. package/src/config/acp-schema.ts +1 -7
  183. package/src/config/bundled-skills/acp/SKILL.md +80 -41
  184. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  185. package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
  186. package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
  187. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  188. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  189. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  190. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  191. package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
  192. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  193. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  194. package/src/config/bundled-tool-registry.ts +4 -0
  195. package/src/config/call-site-defaults.ts +0 -3
  196. package/src/config/feature-flag-registry.json +10 -56
  197. package/src/config/llm-resolver.ts +39 -7
  198. package/src/config/loader.ts +5 -22
  199. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
  200. package/src/config/schemas/call-site-catalog.ts +0 -21
  201. package/src/config/schemas/heartbeat.ts +9 -0
  202. package/src/config/schemas/llm.ts +17 -3
  203. package/src/config/schemas/memory-v3.ts +58 -8
  204. package/src/config/seed-inference-profiles.ts +18 -0
  205. package/src/context/compactor.ts +22 -4
  206. package/src/context/post-turn-tool-result-truncation.ts +39 -1
  207. package/src/context/strip-injections.ts +8 -2
  208. package/src/daemon/conversation-agent-loop-handlers.ts +120 -42
  209. package/src/daemon/conversation-agent-loop.ts +308 -1297
  210. package/src/daemon/conversation-error.ts +31 -4
  211. package/src/daemon/conversation-history.ts +1 -1
  212. package/src/daemon/conversation-lifecycle.ts +11 -255
  213. package/src/daemon/conversation-media-retry.ts +19 -6
  214. package/src/daemon/conversation-messaging.ts +17 -0
  215. package/src/daemon/conversation-process.ts +22 -141
  216. package/src/daemon/conversation-queue-manager.ts +8 -0
  217. package/src/daemon/conversation-registry.ts +159 -0
  218. package/src/daemon/conversation-runtime-assembly.ts +361 -393
  219. package/src/daemon/conversation-store.ts +9 -90
  220. package/src/daemon/conversation-surfaces.ts +165 -11
  221. package/src/daemon/conversation-workspace.ts +17 -0
  222. package/src/daemon/conversation.ts +444 -62
  223. package/src/daemon/disk-pressure-policy.ts +0 -1
  224. package/src/daemon/external-plugins-bootstrap.ts +36 -46
  225. package/src/daemon/handlers/config-channels.ts +11 -2
  226. package/src/daemon/handlers/conversations.ts +3 -1
  227. package/src/daemon/handlers/shared.ts +9 -1
  228. package/src/daemon/handlers/skills.ts +4 -1
  229. package/src/daemon/host-app-control-proxy.ts +72 -57
  230. package/src/daemon/host-proxy-preactivation.ts +1 -3
  231. package/src/daemon/lifecycle.ts +21 -7
  232. package/src/daemon/persist-unsendable-image.ts +62 -25
  233. package/src/daemon/process-message.ts +1 -1
  234. package/src/daemon/server.ts +2 -0
  235. package/src/daemon/tool-side-effects.ts +15 -0
  236. package/src/daemon/wake-conversation-ops.ts +269 -0
  237. package/src/embedded/plugin-api.ts +2 -2
  238. package/src/export/__tests__/transcript-formatter.test.ts +5 -0
  239. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  240. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  241. package/src/heartbeat/heartbeat-service.ts +26 -0
  242. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  243. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  244. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  245. package/src/ipc/skill-routes/memory.ts +4 -2
  246. package/src/memory/__tests__/activation-session-store.test.ts +41 -0
  247. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
  248. package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
  249. package/src/memory/activation-session-store.ts +43 -0
  250. package/src/memory/conversation-crud.ts +29 -19
  251. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  252. package/src/memory/db-init.ts +6 -0
  253. package/src/memory/embedding-backend.ts +5 -22
  254. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  255. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
  256. package/src/memory/jobs-worker.ts +25 -1
  257. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  258. package/src/memory/migrations/273-onboarding-events-funnel-columns.ts +46 -0
  259. package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
  260. package/src/memory/migrations/index.ts +3 -0
  261. package/src/memory/onboarding-events-store.ts +66 -18
  262. package/src/memory/schema/acp.ts +4 -0
  263. package/src/memory/schema/infrastructure.ts +13 -0
  264. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
  265. package/src/memory/v2/__tests__/page-store.test.ts +22 -0
  266. package/src/memory/v2/consolidation-job.ts +2 -63
  267. package/src/memory/v2/types.ts +5 -0
  268. package/src/messaging/providers/telegram-bot/api.ts +14 -5
  269. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  270. package/src/notifications/access-request-copy.ts +298 -0
  271. package/src/notifications/adapters/slack.ts +3 -3
  272. package/src/notifications/adapters/telegram.ts +9 -2
  273. package/src/notifications/copy-composer.ts +49 -267
  274. package/src/notifications/decision-engine.ts +16 -35
  275. package/src/notifications/home-feed-side-effect.ts +1 -6
  276. package/src/plugin-api/constants.ts +4 -0
  277. package/src/plugin-api/index.ts +40 -11
  278. package/src/plugin-api/types.ts +108 -0
  279. package/src/plugins/defaults/compaction/compact.ts +106 -0
  280. package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
  281. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  282. package/src/plugins/defaults/compaction/manager-store.ts +57 -0
  283. package/src/plugins/defaults/compaction/package.json +1 -2
  284. package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
  285. package/src/plugins/defaults/empty-response/package.json +0 -1
  286. package/src/plugins/defaults/history-repair/package.json +0 -1
  287. package/src/plugins/defaults/index.ts +156 -26
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
  289. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
  290. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
  291. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
  292. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  293. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
  297. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
  298. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
  299. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
  300. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
  301. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
  302. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
  303. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
  304. package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
  305. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
  306. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
  307. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
  308. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
  309. package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
  310. package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
  311. package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
  312. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  313. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  314. package/src/plugins/defaults/memory-v3-shadow/injector.ts +76 -0
  315. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
  316. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
  317. package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
  318. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
  319. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
  320. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
  321. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
  322. package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
  323. package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
  324. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
  325. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
  326. package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
  327. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -1
  328. package/src/plugins/defaults/title-generate/package.json +0 -1
  329. package/src/plugins/defaults/tool-error/package.json +0 -1
  330. package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
  331. package/src/plugins/external-api.ts +12 -2
  332. package/src/plugins/pipeline.ts +6 -293
  333. package/src/plugins/registry.ts +9 -37
  334. package/src/plugins/types.ts +76 -381
  335. package/src/plugins/user-loader.ts +30 -127
  336. package/src/prompts/__tests__/system-prompt.test.ts +6 -0
  337. package/src/prompts/system-prompt.ts +61 -10
  338. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
  339. package/src/providers/anthropic/client.ts +9 -10
  340. package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
  341. package/src/providers/inference/logit-bias.ts +55 -0
  342. package/src/providers/model-catalog.ts +57 -0
  343. package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
  344. package/src/providers/openai/chat-completions-provider.ts +44 -1
  345. package/src/providers/retry.ts +57 -7
  346. package/src/providers/types.ts +6 -0
  347. package/src/runtime/__tests__/agent-wake.test.ts +555 -691
  348. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  349. package/src/runtime/agent-wake.ts +108 -209
  350. package/src/runtime/assistant-event-hub.ts +1 -1
  351. package/src/runtime/channel-approvals.ts +1 -1
  352. package/src/runtime/interactive-ui.ts +1 -1
  353. package/src/runtime/routes/__tests__/acp-routes.test.ts +466 -55
  354. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  355. package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
  356. package/src/runtime/routes/__tests__/stt-routes.test.ts +112 -0
  357. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  358. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  359. package/src/runtime/routes/acp-routes.test.ts +185 -32
  360. package/src/runtime/routes/acp-routes.ts +328 -30
  361. package/src/runtime/routes/app-management-routes.ts +3 -0
  362. package/src/runtime/routes/approval-routes.ts +1 -1
  363. package/src/runtime/routes/browser-routes.ts +1 -1
  364. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  365. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  366. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  367. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  368. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  369. package/src/runtime/routes/conversation-routes.ts +30 -2
  370. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  371. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  372. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  373. package/src/runtime/routes/host-cu-routes.ts +1 -1
  374. package/src/runtime/routes/identity-routes.ts +18 -3
  375. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  376. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  377. package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
  378. package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
  379. package/src/runtime/routes/memory-v3-routes.ts +65 -305
  380. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
  381. package/src/runtime/routes/playground/helpers.ts +1 -1
  382. package/src/runtime/routes/plugins-routes.ts +337 -35
  383. package/src/runtime/routes/stt-routes.ts +45 -12
  384. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  385. package/src/runtime/routes/work-items-routes.ts +2 -4
  386. package/src/runtime/routes/workspace-routes.ts +50 -15
  387. package/src/runtime/services/conversation-serializer.ts +1 -1
  388. package/src/runtime/verification-outbound-actions.ts +147 -2
  389. package/src/runtime/verification-templates.ts +29 -3
  390. package/src/services/published-app-updater.ts +30 -8
  391. package/src/signals/cancel.ts +2 -4
  392. package/src/subagent/manager.ts +21 -5
  393. package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
  394. package/src/telemetry/activation-funnel.ts +167 -0
  395. package/src/telemetry/types.ts +13 -0
  396. package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
  397. package/src/telemetry/usage-telemetry-reporter.ts +26 -1
  398. package/src/tools/acp/context.ts +20 -0
  399. package/src/tools/acp/list-agents.test.ts +9 -19
  400. package/src/tools/acp/list-agents.ts +3 -15
  401. package/src/tools/acp/spawn.test.ts +173 -202
  402. package/src/tools/acp/spawn.ts +37 -172
  403. package/src/tools/acp/steer.test.ts +105 -8
  404. package/src/tools/acp/steer.ts +48 -17
  405. package/src/tools/apps/executors.ts +166 -50
  406. package/src/tools/browser/browser-execution.ts +12 -2
  407. package/src/tools/filesystem/write.ts +34 -0
  408. package/src/tools/network/web-fetch.ts +65 -24
  409. package/src/tools/skills/load.ts +1 -1
  410. package/src/tools/subagent/spawn.ts +2 -4
  411. package/src/tools/ui-surface/definitions.ts +32 -5
  412. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  413. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
  414. package/docs/plugins.md +0 -832
  415. package/examples/plugins/echo/register.ts +0 -143
  416. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  417. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  418. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  419. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
  420. package/src/__tests__/pipeline-runner.test.ts +0 -554
  421. package/src/daemon/wake-target-adapter.ts +0 -253
  422. package/src/memory/v3/__tests__/assign.test.ts +0 -242
  423. package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
  424. package/src/memory/v3/__tests__/core.test.ts +0 -39
  425. package/src/memory/v3/__tests__/health.test.ts +0 -219
  426. package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
  427. package/src/memory/v3/__tests__/needle.test.ts +0 -107
  428. package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
  429. package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
  430. package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
  431. package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
  432. package/src/memory/v3/__tests__/router.test.ts +0 -332
  433. package/src/memory/v3/__tests__/selector.test.ts +0 -470
  434. package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
  435. package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
  436. package/src/memory/v3/__tests__/tree.test.ts +0 -192
  437. package/src/memory/v3/assign.ts +0 -268
  438. package/src/memory/v3/capabilities.ts +0 -124
  439. package/src/memory/v3/core.ts +0 -26
  440. package/src/memory/v3/health.ts +0 -0
  441. package/src/memory/v3/maintain-job.ts +0 -314
  442. package/src/memory/v3/needle.ts +0 -115
  443. package/src/memory/v3/orchestrate.ts +0 -126
  444. package/src/memory/v3/page-content.ts +0 -34
  445. package/src/memory/v3/provider-blocks.ts +0 -26
  446. package/src/memory/v3/reconcile.ts +0 -523
  447. package/src/memory/v3/router.ts +0 -190
  448. package/src/memory/v3/selection-log-store.ts +0 -84
  449. package/src/memory/v3/selector.ts +0 -226
  450. package/src/memory/v3/shadow-plugin.ts +0 -411
  451. package/src/memory/v3/snapshot.ts +0 -209
  452. package/src/memory/v3/tree.ts +0 -174
  453. package/src/memory/v3/types.ts +0 -59
  454. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  455. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  456. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  457. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  458. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  459. package/src/plugins/defaults/compaction/register.ts +0 -35
  460. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  461. package/src/plugins/defaults/empty-response/register.ts +0 -23
  462. package/src/plugins/defaults/history-repair/register.ts +0 -24
  463. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
  464. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  465. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  466. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  467. package/src/plugins/defaults/title-generate/register.ts +0 -35
  468. package/src/plugins/defaults/tool-error/register.ts +0 -23
  469. package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
  470. package/src/proactive-artifact/aux-message-injector.ts +0 -97
  471. package/src/proactive-artifact/decision.test.ts +0 -226
  472. package/src/proactive-artifact/decision.ts +0 -165
  473. package/src/proactive-artifact/index.ts +0 -7
  474. package/src/proactive-artifact/job.test.ts +0 -962
  475. package/src/proactive-artifact/job.ts +0 -372
  476. package/src/proactive-artifact/message-copy.ts +0 -58
  477. package/src/proactive-artifact/trigger-state.test.ts +0 -286
  478. package/src/proactive-artifact/trigger-state.ts +0 -123
  479. package/src/util/map-limit.ts +0 -27
  480. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  481. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  482. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  483. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  484. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  485. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  486. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  487. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  488. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  489. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  490. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Memory v3 — synthetic capability rows (skills + `assistant` CLI commands).
3
+ *
4
+ * The assistant's invokable capabilities — installed/catalog skills and
5
+ * top-level CLI subcommands — are seeded as synthetic concept-collection rows
6
+ * (`skills/<id>`, `cli-commands/<name>`). The section-lane retrieval pipeline
7
+ * surfaces the relevant ones per turn, same as concept pages.
8
+ *
9
+ * Page summaries for synthetic slugs already resolve through the existing
10
+ * `pageSummary` lane (the v2 page index appends skill/CLI rows with a summary),
11
+ * so only full-content rendering for the live `<memory>` block needs a synthetic
12
+ * fallback — see {@link renderCapabilityContent}.
13
+ */
14
+
15
+ import {
16
+ getCliCommandCapability,
17
+ isCliCommandSlug,
18
+ } from "../../../memory/v2/cli-command-store.js";
19
+ import {
20
+ getSkillCapability,
21
+ isSkillSlug,
22
+ } from "../../../memory/v2/skill-store.js";
23
+ import type { Slug } from "./types.js";
24
+
25
+ /** True iff the slug is a synthetic skill or CLI-command capability row. */
26
+ export function isCapabilitySlug(slug: Slug): boolean {
27
+ return isSkillSlug(slug) || isCliCommandSlug(slug);
28
+ }
29
+
30
+ interface CapabilityEntry {
31
+ id: string;
32
+ content: string;
33
+ }
34
+
35
+ export interface CapabilityResolvers {
36
+ skill: (idOrSlug: string) => CapabilityEntry | null;
37
+ cli: (idOrSlug: string) => CapabilityEntry | null;
38
+ }
39
+
40
+ const defaultResolvers: CapabilityResolvers = {
41
+ skill: getSkillCapability,
42
+ cli: getCliCommandCapability,
43
+ };
44
+
45
+ /**
46
+ * Render a synthetic skill/CLI slug's full capability content for the live
47
+ * `<memory>` block, mirroring {@link renderV3PageContent}'s `# header\n<content>`
48
+ * shape. Returns:
49
+ * - the rendered block when the slug is a capability slug and resolves;
50
+ * - `""` when it is a capability slug but the cache has no entry (degrade to a
51
+ * blank section rather than throwing or falling through to an on-disk read
52
+ * that would also miss);
53
+ * - `null` when the slug is NOT a capability slug, so the caller falls through
54
+ * to its normal on-disk page rendering.
55
+ *
56
+ * `resolvers` is injectable for tests; production uses the v2 store caches.
57
+ */
58
+ export function renderCapabilityContent(
59
+ slug: Slug,
60
+ resolvers: CapabilityResolvers = defaultResolvers,
61
+ ): string | null {
62
+ if (isSkillSlug(slug)) {
63
+ const entry = resolvers.skill(slug);
64
+ return entry ? `# Skill: ${entry.id}\n${entry.content}` : "";
65
+ }
66
+ if (isCliCommandSlug(slug)) {
67
+ const entry = resolvers.cli(slug);
68
+ return entry ? `# CLI command: ${entry.id}\n${entry.content}` : "";
69
+ }
70
+ return null;
71
+ }
72
+
73
+ /**
74
+ * Resolve a slug's frontmatter-stripped body for the section index: synthetic
75
+ * skill/CLI capability slugs have no on-disk page, so they contribute their
76
+ * rendered capability content (exactly what `page-content.ts` injects for them),
77
+ * while real pages fall through to `readDiskBody`. Shared by `initLanes`'
78
+ * `pageBody` and the full-backfill body reader so the capability-or-disk dispatch
79
+ * lives in one place.
80
+ *
81
+ * `readDiskBody` is injected (rather than imported) so this helper does not pull
82
+ * the page store into `capabilities.ts` — each caller supplies its own cached or
83
+ * direct disk reader.
84
+ */
85
+ export async function capabilityOrDiskBody(
86
+ slug: Slug,
87
+ readDiskBody: (slug: Slug) => Promise<string>,
88
+ ): Promise<string> {
89
+ if (isCapabilitySlug(slug)) return renderCapabilityContent(slug) ?? "";
90
+ return readDiskBody(slug);
91
+ }
@@ -0,0 +1,97 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Memory v3 — dense retrieval lane (section-grain)
3
+ // ---------------------------------------------------------------------------
4
+ //
5
+ // Read counterpart to `section-dense-store.ts`. Embeds the turn query and runs
6
+ // a single cosine search against the `memory_v3_sections` collection, then
7
+ // dedupes the matched section points down to the top-`k` distinct articles —
8
+ // each carrying its best-scoring section ordinal. This is the dense lane of the
9
+ // section-grain retrieval design: where the v2 dense lane matches whole pages,
10
+ // this one matches the single most relevant section of a long article and hands
11
+ // the orchestrator both the article and which section matched (so the selector
12
+ // can show the matched section as the descriptor).
13
+ //
14
+ // Degrades safely: any embedding or Qdrant failure logs a warning and returns
15
+ // `[]`. The orchestrator unions the other lanes (needle, edge) plus carry-
16
+ // forward regardless, so a dense outage narrows recall but never breaks a turn.
17
+
18
+ import type { AssistantConfig } from "../../../config/types.js";
19
+ import { embedWithBackend } from "../../../memory/embedding-backend.js";
20
+ import { getLogger } from "../../../util/logger.js";
21
+ import {
22
+ getSectionDenseClient,
23
+ SECTION_COLLECTION,
24
+ } from "./section-dense-store.js";
25
+ import type { Slug } from "./types.js";
26
+
27
+ const log = getLogger("memory-v3-dense-lane");
28
+
29
+ /**
30
+ * Multiplier applied to `k` when fetching section points from Qdrant. Several
31
+ * sections can belong to the same article, so we oversample the section hits to
32
+ * leave room for the article-level dedupe to still yield `k` distinct articles.
33
+ */
34
+ export const OVERSAMPLE = 6;
35
+
36
+ /** A single dense-lane hit: an article plus the section ordinal that matched. */
37
+ export interface DenseHit {
38
+ article: Slug;
39
+ section: number;
40
+ }
41
+
42
+ /**
43
+ * Run the dense lane: embed `query`, search the section collection for the top
44
+ * `k * OVERSAMPLE` section points, then dedupe to the top-`k` distinct articles
45
+ * — each with its best-scoring section ordinal. Section points are returned by
46
+ * Qdrant in descending score order, so the first time an article is seen is its
47
+ * best section; subsequent sections of the same article are ignored.
48
+ *
49
+ * Returns `[]` on any embedding or Qdrant failure (logged at warn level).
50
+ */
51
+ export async function denseLane(
52
+ config: AssistantConfig,
53
+ query: string,
54
+ k: number,
55
+ ): Promise<DenseHit[]> {
56
+ if (k <= 0) return [];
57
+
58
+ let points: Array<{ payload?: unknown; score?: number }>;
59
+ try {
60
+ const { vectors } = await embedWithBackend(config, [query]);
61
+ const vector = vectors[0];
62
+ if (!vector || vector.length === 0) return [];
63
+
64
+ const result = await getSectionDenseClient(config).query(
65
+ SECTION_COLLECTION,
66
+ {
67
+ query: vector,
68
+ limit: k * OVERSAMPLE,
69
+ with_payload: true,
70
+ },
71
+ );
72
+ points = result.points;
73
+ } catch (err) {
74
+ log.warn({ err }, "memory v3 dense lane failed; degrading to no hits");
75
+ return [];
76
+ }
77
+
78
+ // Walk hits in score order, keeping the first (best) section per article and
79
+ // stopping once we have `k` distinct articles.
80
+ const seen = new Set<Slug>();
81
+ const hits: DenseHit[] = [];
82
+ for (const point of points) {
83
+ const payload = point.payload as
84
+ | { article?: unknown; ordinal?: unknown }
85
+ | null
86
+ | undefined;
87
+ const article = payload?.article;
88
+ const ordinal = payload?.ordinal;
89
+ if (typeof article !== "string" || typeof ordinal !== "number") continue;
90
+ if (seen.has(article)) continue;
91
+ seen.add(article);
92
+ hits.push({ article, section: ordinal });
93
+ if (hits.length >= k) break;
94
+ }
95
+
96
+ return hits;
97
+ }
@@ -0,0 +1,252 @@
1
+ import type { PageIndexEntry } from "../../../memory/v2/page-index.js";
2
+ import { parseFrontmatterFields } from "../../../skills/frontmatter.js";
3
+ import type { Slug } from "./types.js";
4
+
5
+ /**
6
+ * Edge lane for memory-v3 retrieval: a directed article link-graph used to
7
+ * expand a turn's lexical/dense seeds outward to their first-class neighbours.
8
+ *
9
+ * The graph unions THREE outbound-edge sources per article:
10
+ *
11
+ * (a) The optional `links:` frontmatter — a YAML list of
12
+ * `"<target-slug> — <description>"` strings (split on the first
13
+ * ` — `, space-emdash-space). This is the authored, first-class edge
14
+ * source: the curated description is carried through expansion so the
15
+ * orchestrator can use it directly as a select descriptor.
16
+ * (b) Inline `[[wikilink]]` targets parsed from the body.
17
+ * (c) `PageIndexEntry.edges` numeric ids resolved to slugs — the fallback
18
+ * for pages with no frontmatter `links`.
19
+ *
20
+ * All targets are resolved against the corpus slug set; unknown/dangling
21
+ * targets are dropped. A curated description is stored per (source → target)
22
+ * edge only when the `links` source supplied one; wikilink/numeric edges carry
23
+ * no description (the orchestrator falls back to a section descriptor).
24
+ *
25
+ * The build is pure given a `pageRaw` reader callback (no hard-coded I/O), so
26
+ * it is built once at lane-init and rebuilt by the maintain job, and is
27
+ * trivially unit-testable with a stub reader.
28
+ */
29
+
30
+ /** Default in-degree above which an article is treated as a hub and excluded
31
+ * from expansion (a hub neighbour is too generic to be a useful surface). */
32
+ const DEFAULT_HUB_DEGREE = 30;
33
+
34
+ const DEFAULT_SEED_COUNT = 18;
35
+ const DEFAULT_PER_SEED = 6;
36
+ const DEFAULT_CAP = 45;
37
+
38
+ /** Matches the space-emdash-space separator in a `links:` entry. */
39
+ const LINK_SEPARATOR = " — ";
40
+
41
+ /** Matches `[[target]]` wikilinks; captures the raw target before any `|`
42
+ * display-text or `#section` anchor. */
43
+ const WIKILINK_REGEX = /\[\[([^\]]+)\]\]/g;
44
+
45
+ /**
46
+ * The directed link-graph. `adjacency` maps each source slug to its outbound
47
+ * edges (target slug → curated description, or `undefined` when the edge came
48
+ * from a wikilink/numeric source). `hubs` is the set of high-in-degree slugs
49
+ * excluded from expansion. `slugs` is the resolved corpus slug set.
50
+ */
51
+ export interface EdgeGraph {
52
+ adjacency: Map<Slug, Map<Slug, string | undefined>>;
53
+ hubs: Set<Slug>;
54
+ slugs: Set<Slug>;
55
+ }
56
+
57
+ /** One article surfaced by {@link edgeExpand}, with the curated `links`
58
+ * description of the traversed edge when it carried one (else `undefined`). */
59
+ export interface EdgeNeighbor {
60
+ article: Slug;
61
+ description?: string;
62
+ }
63
+
64
+ export interface EdgeExpandOptions {
65
+ /** Predicate gating which neighbours may be surfaced (e.g. liveness /
66
+ * not-already-selected). Neighbours failing `alive` are skipped. */
67
+ alive?: (slug: Slug) => boolean;
68
+ /** Only the top `seedCount` seeds (in input order) are expanded. */
69
+ seedCount?: number;
70
+ /** Up to `perSeed` neighbours are added per expanded seed. */
71
+ perSeed?: number;
72
+ /** Hard cap on the total number of distinct surfaced articles. */
73
+ cap?: number;
74
+ }
75
+
76
+ interface BuildEdgeGraphOptions {
77
+ /** In-degree above which an article is a hub. Defaults to
78
+ * {@link DEFAULT_HUB_DEGREE}. */
79
+ hubDegree?: number;
80
+ }
81
+
82
+ /**
83
+ * Split one `links:` entry (`"<target-slug> — <description>"`) into its target
84
+ * slug and curated description on the FIRST ` — ` (space-emdash-space).
85
+ * Entries with no separator are bare target slugs and carry no description.
86
+ */
87
+ function parseLinkEntry(entry: string): {
88
+ target: Slug;
89
+ description: string | undefined;
90
+ } {
91
+ const sep = entry.indexOf(LINK_SEPARATOR);
92
+ if (sep === -1) return { target: entry.trim(), description: undefined };
93
+ return {
94
+ target: entry.slice(0, sep).trim(),
95
+ description: entry.slice(sep + LINK_SEPARATOR.length).trim() || undefined,
96
+ };
97
+ }
98
+
99
+ /** Parse inline `[[wikilink]]` targets from a body. Strips `|display` and
100
+ * `#anchor` suffixes; returns trimmed target slugs (possibly with duplicates,
101
+ * deduped by the caller's map insertion). */
102
+ function parseWikilinks(body: string): string[] {
103
+ const targets: string[] = [];
104
+ for (const match of body.matchAll(WIKILINK_REGEX)) {
105
+ let target = match[1];
106
+ const pipe = target.indexOf("|");
107
+ if (pipe !== -1) target = target.slice(0, pipe);
108
+ const hash = target.indexOf("#");
109
+ if (hash !== -1) target = target.slice(0, hash);
110
+ target = target.trim();
111
+ if (target) targets.push(target);
112
+ }
113
+ return targets;
114
+ }
115
+
116
+ /**
117
+ * Build the directed article link-graph from the page-index entries and a raw
118
+ * page reader. See the module docstring for the three unioned edge sources.
119
+ *
120
+ * Pure given `pageRaw`: the build performs no hard-coded I/O. A read that
121
+ * rejects drops that article's authored/wikilink edges but still keeps its
122
+ * numeric `PageIndexEntry.edges` fallback.
123
+ */
124
+ export async function buildEdgeGraph(
125
+ articles: readonly PageIndexEntry[],
126
+ pageRaw: (slug: Slug) => Promise<string>,
127
+ opts: BuildEdgeGraphOptions = {},
128
+ ): Promise<EdgeGraph> {
129
+ const hubDegree = opts.hubDegree ?? DEFAULT_HUB_DEGREE;
130
+
131
+ const slugs = new Set<Slug>(articles.map((a) => a.slug));
132
+ const byId = new Map<number, Slug>(articles.map((a) => [a.id, a.slug]));
133
+
134
+ const raws = await Promise.all(
135
+ articles.map((a) =>
136
+ pageRaw(a.slug).then(
137
+ (text) => text,
138
+ () => null, // read failed — fall back to numeric edges only
139
+ ),
140
+ ),
141
+ );
142
+
143
+ const adjacency = new Map<Slug, Map<Slug, string | undefined>>();
144
+ const inDegree = new Map<Slug, number>();
145
+
146
+ const addEdge = (
147
+ source: Slug,
148
+ target: Slug,
149
+ description: string | undefined,
150
+ ): void => {
151
+ if (target === source) return; // no self-edges
152
+ if (!slugs.has(target)) return; // drop unknown/dangling targets
153
+ let out = adjacency.get(source);
154
+ if (!out) {
155
+ out = new Map();
156
+ adjacency.set(source, out);
157
+ }
158
+ if (!out.has(target)) {
159
+ out.set(target, description);
160
+ inDegree.set(target, (inDegree.get(target) ?? 0) + 1);
161
+ } else if (out.get(target) === undefined && description !== undefined) {
162
+ // A later source (the authored `links`) supplies a description for an
163
+ // edge first seen without one — upgrade it. In-degree already counted.
164
+ out.set(target, description);
165
+ }
166
+ };
167
+
168
+ for (let i = 0; i < articles.length; i++) {
169
+ const article = articles[i];
170
+ const source = article.slug;
171
+ const raw = raws[i];
172
+
173
+ // (a) authored `links:` frontmatter — primary, carries descriptions.
174
+ const parsed = raw !== null ? parseFrontmatterFields(raw) : null;
175
+ const links = parsed?.fields.links;
176
+ if (Array.isArray(links)) {
177
+ for (const entry of links) {
178
+ if (typeof entry !== "string") continue;
179
+ const { target, description } = parseLinkEntry(entry);
180
+ addEdge(source, target, description);
181
+ }
182
+ }
183
+
184
+ // (b) inline `[[wikilink]]` targets from the body. `parsed.body` strips
185
+ // the frontmatter; a page without frontmatter has `parsed === null`, so
186
+ // the whole raw text is the body.
187
+ const body = parsed ? parsed.body : raw;
188
+ if (body !== null) {
189
+ for (const target of parseWikilinks(body)) {
190
+ addEdge(source, target, undefined);
191
+ }
192
+ }
193
+
194
+ // (c) numeric page-index edges resolved to slugs — fallback.
195
+ for (const targetId of article.edges) {
196
+ const target = byId.get(targetId);
197
+ if (target) addEdge(source, target, undefined);
198
+ }
199
+ }
200
+
201
+ const hubs = new Set<Slug>();
202
+ for (const [slug, degree] of inDegree) {
203
+ if (degree > hubDegree) hubs.add(slug);
204
+ }
205
+
206
+ return { adjacency, hubs, slugs };
207
+ }
208
+
209
+ /**
210
+ * Expand `seeds` outward along the graph. For each of the top `seedCount`
211
+ * seeds (in input order), surface up to `perSeed` non-hub, `alive`-passing
212
+ * neighbours, capped at `cap` total distinct articles. Seeds themselves are
213
+ * not surfaced (they are already in the pool). Each surfaced article carries
214
+ * the curated `links` description of the traversed edge when it had one.
215
+ *
216
+ * Deterministic given the input seed order and the graph's insertion order.
217
+ */
218
+ export function edgeExpand(
219
+ graph: EdgeGraph,
220
+ seeds: readonly Slug[],
221
+ opts: EdgeExpandOptions = {},
222
+ ): EdgeNeighbor[] {
223
+ const seedCount = opts.seedCount ?? DEFAULT_SEED_COUNT;
224
+ const perSeed = opts.perSeed ?? DEFAULT_PER_SEED;
225
+ const cap = opts.cap ?? DEFAULT_CAP;
226
+ const alive = opts.alive;
227
+
228
+ const seedSet = new Set<Slug>(seeds);
229
+ const surfaced = new Map<Slug, string | undefined>();
230
+
231
+ for (const seed of seeds.slice(0, seedCount)) {
232
+ if (surfaced.size >= cap) break;
233
+ const out = graph.adjacency.get(seed);
234
+ if (!out) continue;
235
+ let added = 0;
236
+ for (const [target, description] of out) {
237
+ if (added >= perSeed) break;
238
+ if (surfaced.size >= cap) break;
239
+ if (seedSet.has(target)) continue; // already in the pool
240
+ if (surfaced.has(target)) continue; // already surfaced via another seed
241
+ if (graph.hubs.has(target)) continue; // hubs excluded
242
+ if (alive && !alive(target)) continue;
243
+ surfaced.set(target, description);
244
+ added++;
245
+ }
246
+ }
247
+
248
+ return [...surfaced].map(([article, description]) => ({
249
+ article,
250
+ description,
251
+ }));
252
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * No-op `post-compact` scaffolding for the memory-v3-shadow plugin.
3
+ *
4
+ * v3 re-injection after mid-turn compaction is not yet owned by this plugin —
5
+ * the active injector still runs through the static chain. Once the
6
+ * post-compaction hook API stabilizes we will import `memoryV3Injector` from
7
+ * `../injector.js` and re-apply it here. Until then this hook does nothing.
8
+ */
9
+
10
+ import type { PluginHookFn } from "@vellumai/plugin-api";
11
+
12
+ const postCompact: PluginHookFn = async () => {};
13
+
14
+ export default postCompact;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * No-op `user-prompt-submit` scaffolding for the memory-v3-shadow plugin.
3
+ *
4
+ * The v3 injector currently runs through the static injector chain
5
+ * (`memory-retrieval/injector-chain.ts`), not through this hook. Once the
6
+ * prompt-submit hook API stabilizes we will import `memoryV3Injector` from
7
+ * `../injector.js` and run it here, so v3 injection is owned by the plugin's
8
+ * own lifecycle rather than the loop-driven chain. Until then this hook
9
+ * intentionally does nothing.
10
+ */
11
+
12
+ import type {
13
+ PluginHookFn,
14
+ UserPromptSubmitContext,
15
+ } from "@vellumai/plugin-api";
16
+
17
+ const userPromptSubmit: PluginHookFn<UserPromptSubmitContext> = async () => {};
18
+
19
+ export default userPromptSubmit;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * The memory-v3 {@link Injector}. Reads both v3 flags:
3
+ * - `memory-v3-live` on → orchestrate, log, render the working-set selection
4
+ * into a `<memory>` block, and return it at v2's dynamic-memory placement.
5
+ * - `memory-v3-shadow` on (live off) → orchestrate + log only, return `null`.
6
+ * - both off → return `null` (no orchestration).
7
+ *
8
+ * Empty selection and any failure return `null` (no v3 injection). v2
9
+ * suppression keys off BOTH the flag AND this return value, so a `null` here
10
+ * (failure or empty selection) falls back to v2 memory rather than dropping all
11
+ * memory.
12
+ *
13
+ * Orchestration and telemetry live in {@link observeTurn}; this module is the
14
+ * thin injector wrapper that renders the result for live injection.
15
+ */
16
+
17
+ import { isAssistantFeatureFlagEnabled } from "../../../config/assistant-feature-flags.js";
18
+ import { getConfig } from "../../../config/loader.js";
19
+ import { getLogger } from "../../../util/logger.js";
20
+ import {
21
+ type InjectionBlock,
22
+ type Injector,
23
+ type TurnContext,
24
+ } from "../../types.js";
25
+ import { renderV3SectionContent } from "./page-content.js";
26
+ import { renderMemoryBlock } from "./render-injection.js";
27
+ import {
28
+ MEMORY_V3_LIVE,
29
+ MEMORY_V3_SHADOW,
30
+ observeTurn,
31
+ } from "./shadow-plugin.js";
32
+ import { MEMORY_V3_BLOCK_ID } from "./types.js";
33
+
34
+ const log = getLogger("memory-v3-shadow");
35
+
36
+ export const memoryV3Injector: Injector = {
37
+ name: "memory-v3-shadow",
38
+ // High order so it sorts last; the live `<memory>` block uses the
39
+ // after-memory-prefix placement so it lands at the memory boundary regardless
40
+ // of this sort key, which only orders content-producing injectors.
41
+ order: 1000,
42
+ async produce(ctx: TurnContext): Promise<InjectionBlock | null> {
43
+ const config = getConfig();
44
+ const live = isAssistantFeatureFlagEnabled(MEMORY_V3_LIVE, config);
45
+ const shadow = isAssistantFeatureFlagEnabled(MEMORY_V3_SHADOW, config);
46
+ if (!live && !shadow) return null;
47
+
48
+ const result = await observeTurn(ctx.conversationId, ctx.turnIndex);
49
+ if (!live || !result) return null;
50
+
51
+ try {
52
+ // `renderMemoryBlock` returns "" for an empty selection; inject nothing.
53
+ const text = await renderMemoryBlock(
54
+ result.finalInjection,
55
+ result.sectionBySlug,
56
+ renderV3SectionContent,
57
+ );
58
+ if (text.length === 0) return null;
59
+ return {
60
+ id: MEMORY_V3_BLOCK_ID,
61
+ text,
62
+ // Mirror v2's dynamic `<memory>` block placement.
63
+ placement: "after-memory-prefix",
64
+ };
65
+ } catch (err) {
66
+ log.warn(
67
+ {
68
+ err: err instanceof Error ? err.message : String(err),
69
+ conversationId: ctx.conversationId,
70
+ },
71
+ "memory-v3 live render failed (non-fatal) — falling back to v2",
72
+ );
73
+ return null;
74
+ }
75
+ },
76
+ };