@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,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
+ };