@vellumai/assistant 0.8.10 → 0.8.11-staging.1

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 (400) hide show
  1. package/bun.lock +62 -1
  2. package/docs/workspace-tools.md +196 -0
  3. package/examples/plugins/echo/README.md +3 -3
  4. package/knip.json +1 -0
  5. package/openapi.yaml +460 -128
  6. package/package.json +2 -1
  7. package/scripts/build-plugin-api.ts +299 -0
  8. package/src/__tests__/agent-loop-callsite-precedence.test.ts +7 -0
  9. package/src/__tests__/agent-loop-compaction-events.test.ts +197 -0
  10. package/src/__tests__/agent-loop-exit-reason.test.ts +93 -96
  11. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +2 -0
  12. package/src/__tests__/agent-loop-output-hooks.test.ts +274 -1
  13. package/src/__tests__/agent-loop-override-profile.test.ts +3 -0
  14. package/src/__tests__/agent-loop-provider-error-recording.test.ts +4 -0
  15. package/src/__tests__/agent-loop-thinking.test.ts +4 -0
  16. package/src/__tests__/agent-loop.test.ts +578 -5
  17. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -1
  18. package/src/__tests__/approval-cascade.test.ts +1 -0
  19. package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
  20. package/src/__tests__/btw-routes.test.ts +0 -1
  21. package/src/__tests__/build-persisted-content.test.ts +75 -1
  22. package/src/__tests__/catalog-install-normalize.test.ts +141 -0
  23. package/src/__tests__/ces-startup-timeout.test.ts +60 -0
  24. package/src/__tests__/compaction-events.test.ts +1 -0
  25. package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
  26. package/src/__tests__/context-overflow-reducer.test.ts +264 -124
  27. package/src/__tests__/context-window-manager-overflow-rung.test.ts +351 -0
  28. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  29. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -5
  30. package/src/__tests__/conversation-agent-loop-overflow.test.ts +284 -455
  31. package/src/__tests__/conversation-agent-loop.test.ts +131 -551
  32. package/src/__tests__/conversation-app-control-instantiation.test.ts +13 -0
  33. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
  34. package/src/__tests__/conversation-fork-crud.test.ts +259 -0
  35. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  36. package/src/__tests__/conversation-lifecycle.test.ts +257 -1
  37. package/src/__tests__/conversation-process-callsite.test.ts +1 -0
  38. package/src/__tests__/conversation-provider-retry-repair.test.ts +38 -377
  39. package/src/__tests__/conversation-queue.test.ts +1 -39
  40. package/src/__tests__/conversation-runtime-assembly.test.ts +119 -8
  41. package/src/__tests__/conversation-skill-tools.test.ts +491 -5
  42. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  43. package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
  44. package/src/__tests__/conversation-speed-override.test.ts +1 -0
  45. package/src/__tests__/conversation-store.test.ts +74 -0
  46. package/src/__tests__/conversation-surfaces-app-control.test.ts +4 -1
  47. package/src/__tests__/conversation-tool-setup-attribution.test.ts +323 -0
  48. package/src/__tests__/conversation-tool-setup-tools-disabled.test.ts +34 -0
  49. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
  50. package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
  51. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -0
  52. package/src/__tests__/corrected-target.test.ts +93 -0
  53. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -5
  54. package/src/__tests__/credential-execution-tools.test.ts +23 -11
  55. package/src/__tests__/credential-security-invariants.test.ts +6 -1
  56. package/src/__tests__/db-schedule-syntax-migration.test.ts +80 -0
  57. package/src/__tests__/device-id.test.ts +70 -1
  58. package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
  59. package/src/__tests__/empty-response-hook.test.ts +242 -66
  60. package/src/__tests__/external-plugin-loader.test.ts +0 -31
  61. package/src/__tests__/get-skill-detail-audit.test.ts +43 -1
  62. package/src/__tests__/guardian-routing-invariants.test.ts +91 -0
  63. package/src/__tests__/history-repair-hook.test.ts +228 -3
  64. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  65. package/src/__tests__/host-browser-proxy.test.ts +254 -9
  66. package/src/__tests__/identity-routes.test.ts +1 -0
  67. package/src/__tests__/image-recovery-hook.test.ts +387 -0
  68. package/src/__tests__/injector-chain.test.ts +5 -4
  69. package/src/__tests__/injector-v3-suppression.test.ts +373 -47
  70. package/src/__tests__/intent-routing.test.ts +7 -0
  71. package/src/__tests__/memory-retrieval-hook.test.ts +117 -15
  72. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  73. package/src/__tests__/oauth-store.test.ts +0 -85
  74. package/src/__tests__/{context-overflow-policy.test.ts → overflow-policy.test.ts} +1 -1
  75. package/src/__tests__/parallel-tool.benchmark.test.ts +4 -0
  76. package/src/__tests__/persist-unsendable-image-downscale.test.ts +29 -9
  77. package/src/__tests__/persist-unsendable-image.test.ts +4 -4
  78. package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
  79. package/src/__tests__/plugin-bootstrap.test.ts +82 -73
  80. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  81. package/src/__tests__/plugin-types.test.ts +0 -8
  82. package/src/__tests__/provider-catalog-visibility.test.ts +1 -9
  83. package/src/__tests__/prune-old-conversations-job.test.ts +99 -0
  84. package/src/__tests__/registry.test.ts +240 -1
  85. package/src/__tests__/require-fresh-approval.test.ts +3 -0
  86. package/src/__tests__/schedule-routes.test.ts +116 -1
  87. package/src/__tests__/schedule-store.test.ts +28 -0
  88. package/src/__tests__/schedule-tools.test.ts +94 -1
  89. package/src/__tests__/server-history-render.test.ts +39 -0
  90. package/src/__tests__/skill-projection-feature-flag.test.ts +13 -0
  91. package/src/__tests__/skill-projection.benchmark.test.ts +25 -7
  92. package/src/__tests__/skills.test.ts +202 -0
  93. package/src/__tests__/slim-skill-category.test.ts +195 -0
  94. package/src/__tests__/strip-memory-injections.test.ts +33 -39
  95. package/src/__tests__/test-support/tool-invocation-seed.ts +79 -0
  96. package/src/__tests__/title-generate-hook.test.ts +9 -7
  97. package/src/__tests__/tool-audit-listener.test.ts +264 -1
  98. package/src/__tests__/tool-error-hook.test.ts +4 -3
  99. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -0
  100. package/src/__tests__/tool-executor-lifecycle-events.test.ts +273 -0
  101. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
  102. package/src/__tests__/tool-start-timestamp.test.ts +218 -0
  103. package/src/__tests__/tools-get-route.test.ts +202 -0
  104. package/src/__tests__/workspace-tool-loader.test.ts +319 -0
  105. package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
  106. package/src/agent/loop.ts +569 -319
  107. package/src/api/events/tool-result.ts +9 -0
  108. package/src/api/events/tool-use-start.ts +7 -0
  109. package/src/api/index.ts +10 -0
  110. package/src/api/responses/conversation-message.ts +135 -27
  111. package/src/api/responses/memory-v3-selection-log.ts +4 -4
  112. package/src/approvals/guardian-request-resolvers.ts +26 -0
  113. package/src/browser-session/backends/host-bridge.ts +29 -0
  114. package/src/browser-session/index.ts +1 -0
  115. package/src/browser-session/types.ts +5 -1
  116. package/src/cli/commands/__tests__/schedules.test.ts +62 -4
  117. package/src/cli/commands/__tests__/skills.test.ts +53 -0
  118. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  119. package/src/cli/commands/inference-providers.ts +0 -8
  120. package/src/cli/commands/plugins.ts +2 -2
  121. package/src/cli/commands/schedules.ts +27 -4
  122. package/src/cli/commands/skills.ts +187 -146
  123. package/src/cli/commands/tools.ts +106 -0
  124. package/src/cli/lib/__tests__/install-from-github.test.ts +256 -328
  125. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +6 -2
  126. package/src/cli/lib/__tests__/plugin-details.test.ts +10 -16
  127. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +2 -2
  128. package/src/cli/lib/__tests__/search-plugins.test.ts +145 -240
  129. package/src/cli/lib/install-from-github.ts +187 -117
  130. package/src/cli/lib/plugin-catalog-cache.ts +9 -9
  131. package/src/cli/lib/plugin-details.ts +38 -68
  132. package/src/cli/lib/plugin-marketplace.ts +42 -14
  133. package/src/cli/lib/search-plugins.ts +29 -129
  134. package/src/cli/program.ts +2 -0
  135. package/src/config/bundled-skills/acp/SKILL.md +1 -0
  136. package/src/config/bundled-skills/app-builder/SKILL.md +1 -0
  137. package/src/config/bundled-skills/app-control/SKILL.md +1 -0
  138. package/src/config/bundled-skills/computer-use/SKILL.md +1 -0
  139. package/src/config/bundled-skills/contacts/SKILL.md +1 -0
  140. package/src/config/bundled-skills/document-editor/SKILL.md +1 -0
  141. package/src/config/bundled-skills/followups/SKILL.md +1 -0
  142. package/src/config/bundled-skills/image-studio/SKILL.md +1 -0
  143. package/src/config/bundled-skills/media-processing/SKILL.md +1 -0
  144. package/src/config/bundled-skills/messaging/SKILL.md +1 -0
  145. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -0
  146. package/src/config/bundled-skills/playbooks/SKILL.md +1 -0
  147. package/src/config/bundled-skills/schedule/SKILL.md +1 -0
  148. package/src/config/bundled-skills/schedule/TOOLS.json +11 -3
  149. package/src/config/bundled-skills/sequences/SKILL.md +1 -0
  150. package/src/config/bundled-skills/settings/SKILL.md +1 -0
  151. package/src/config/bundled-skills/skill-management/SKILL.md +101 -1
  152. package/src/config/bundled-skills/subagent/SKILL.md +1 -0
  153. package/src/config/bundled-skills/transcribe/SKILL.md +1 -0
  154. package/src/config/env-registry.ts +23 -0
  155. package/src/config/feature-flag-registry.json +17 -81
  156. package/src/config/loader.ts +5 -22
  157. package/src/config/schema.ts +2 -0
  158. package/src/config/schemas/__tests__/compaction-logs.test.ts +56 -0
  159. package/src/config/schemas/__tests__/memory-v2.test.ts +0 -1
  160. package/src/config/schemas/__tests__/memory-v3.test.ts +61 -1
  161. package/src/config/schemas/compaction-logs.ts +79 -0
  162. package/src/config/schemas/memory-v2.ts +0 -8
  163. package/src/config/schemas/memory-v3.ts +104 -33
  164. package/src/config/seed-inference-profiles.ts +1 -1
  165. package/src/config/skills.ts +117 -47
  166. package/src/context/compactor.ts +11 -0
  167. package/src/context/strip-injections.ts +38 -4
  168. package/src/credential-execution/feature-gates.ts +0 -21
  169. package/src/credential-execution/startup-timeout.ts +32 -4
  170. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +18 -0
  171. package/src/daemon/conversation-agent-loop-handlers.ts +140 -91
  172. package/src/daemon/conversation-agent-loop.ts +123 -658
  173. package/src/daemon/conversation-error.ts +6 -33
  174. package/src/daemon/conversation-lifecycle.ts +1 -1
  175. package/src/daemon/conversation-runtime-assembly.ts +183 -22
  176. package/src/daemon/conversation-skill-tools.ts +137 -8
  177. package/src/daemon/conversation-slash.ts +0 -14
  178. package/src/daemon/conversation-store.ts +2 -19
  179. package/src/daemon/conversation-tool-setup.ts +87 -1
  180. package/src/daemon/conversation.ts +119 -50
  181. package/src/daemon/external-plugins-bootstrap.ts +36 -95
  182. package/src/daemon/handlers/config-channels.ts +11 -2
  183. package/src/daemon/handlers/shared.ts +9 -1
  184. package/src/daemon/handlers/skills.ts +10 -4
  185. package/src/daemon/host-app-control-proxy.ts +72 -57
  186. package/src/daemon/host-browser-proxy.ts +117 -22
  187. package/src/daemon/lifecycle.ts +34 -5
  188. package/src/daemon/message-protocol.ts +0 -7
  189. package/src/daemon/message-types/schedules.ts +1 -0
  190. package/src/daemon/message-types/skills.ts +17 -0
  191. package/src/daemon/providers-setup.ts +3 -0
  192. package/src/daemon/server.ts +3 -3
  193. package/src/daemon/tool-setup-types.ts +9 -3
  194. package/src/daemon/trust-context.ts +23 -0
  195. package/src/daemon/workspace-tools-watcher.ts +324 -0
  196. package/src/events/tool-audit-listener.ts +78 -16
  197. package/src/events/tool-metrics-listener.ts +2 -5
  198. package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +227 -0
  199. package/src/memory/__tests__/conversation-queries.test.ts +176 -0
  200. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +20 -32
  201. package/src/memory/compaction-log-writer-clickhouse.ts +418 -0
  202. package/src/memory/conversation-crud.ts +134 -3
  203. package/src/memory/conversation-queries.ts +64 -7
  204. package/src/memory/db-init.ts +14 -0
  205. package/src/memory/embedding-backend.test.ts +130 -1
  206. package/src/memory/embedding-backend.ts +79 -106
  207. package/src/memory/embedding-gemini.ts +5 -0
  208. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +12 -0
  209. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +19 -0
  210. package/src/memory/graph/conversation-graph-memory.ts +36 -25
  211. package/src/memory/graph/tool-handlers.ts +3 -0
  212. package/src/memory/job-handlers/cleanup.ts +3 -1
  213. package/src/memory/jobs-store.ts +0 -28
  214. package/src/memory/jobs-worker.ts +10 -22
  215. package/src/memory/memory-marker.ts +29 -0
  216. package/src/memory/memory-retrospective-startup-cleanup.ts +1 -1
  217. package/src/memory/migrations/268-add-memory-v3-selections.ts +6 -0
  218. package/src/memory/migrations/270-schedule-description.ts +36 -0
  219. package/src/memory/migrations/275-tool-invocations-add-skill-id.test.ts +81 -0
  220. package/src/memory/migrations/275-tool-invocations-add-skill-id.ts +20 -0
  221. package/src/memory/migrations/276-tool-invocations-created-at-id-index.test.ts +68 -0
  222. package/src/memory/migrations/276-tool-invocations-created-at-id-index.ts +20 -0
  223. package/src/memory/migrations/277-add-memory-v3-ever-injected.ts +29 -0
  224. package/src/memory/migrations/278-tool-invocations-telemetry-columns.test.ts +96 -0
  225. package/src/memory/migrations/278-tool-invocations-telemetry-columns.ts +39 -0
  226. package/src/memory/migrations/279-create-skill-loaded-events.test.ts +84 -0
  227. package/src/memory/migrations/279-create-skill-loaded-events.ts +26 -0
  228. package/src/memory/migrations/280-conversations-surfaced-at.test.ts +88 -0
  229. package/src/memory/migrations/280-conversations-surfaced-at.ts +24 -0
  230. package/src/memory/migrations/index.ts +10 -0
  231. package/src/memory/migrations/registry.ts +8 -0
  232. package/src/memory/schema/conversations.ts +16 -0
  233. package/src/memory/schema/infrastructure.ts +26 -0
  234. package/src/memory/skill-loaded-events-store.test.ts +160 -0
  235. package/src/memory/skill-loaded-events-store.ts +95 -0
  236. package/src/memory/tool-executed-events-store.test.ts +219 -0
  237. package/src/memory/tool-executed-events-store.ts +102 -0
  238. package/src/memory/tool-usage-store.ts +15 -3
  239. package/src/memory/v2/__tests__/consolidation-job.test.ts +117 -12
  240. package/src/memory/v2/__tests__/consolidation-prompt-flag-gating-guard.test.ts +189 -0
  241. package/src/memory/v2/__tests__/injected-block-slugs.test.ts +90 -0
  242. package/src/memory/v2/__tests__/page-store.test.ts +33 -0
  243. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +88 -15
  244. package/src/memory/v2/activation-store.ts +50 -1
  245. package/src/memory/v2/consolidation-job.ts +113 -29
  246. package/src/memory/v2/injected-block-slugs.ts +79 -0
  247. package/src/memory/v2/injection.ts +6 -1
  248. package/src/memory/v2/prompts/consolidation.ts +414 -13
  249. package/src/memory/v2/router.ts +2 -28
  250. package/src/memory/v2/static-context.ts +1 -1
  251. package/src/memory/v2/types.ts +16 -0
  252. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  253. package/src/notifications/access-request-copy.ts +298 -0
  254. package/src/notifications/adapters/slack.ts +3 -3
  255. package/src/notifications/adapters/telegram.ts +2 -1
  256. package/src/notifications/copy-composer.ts +49 -267
  257. package/src/notifications/decision-engine.ts +16 -35
  258. package/src/notifications/home-feed-side-effect.ts +1 -6
  259. package/src/oauth/oauth-store.ts +0 -9
  260. package/src/permissions/checker.test.ts +83 -1
  261. package/src/permissions/checker.ts +25 -2
  262. package/src/permissions/gateway-threshold-reader.test.ts +182 -0
  263. package/src/permissions/gateway-threshold-reader.ts +80 -0
  264. package/src/platform/client.ts +1 -3
  265. package/src/platform/feature-gate.ts +3 -12
  266. package/src/plugin-api/constants.ts +4 -2
  267. package/src/plugin-api/index.ts +63 -11
  268. package/src/plugin-api/types.ts +236 -71
  269. package/src/plugins/defaults/compaction/compact.ts +66 -2
  270. package/src/plugins/defaults/compaction/context-overflow-reducer.ts +240 -32
  271. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  272. package/src/{daemon/context-overflow-policy.ts → plugins/defaults/compaction/overflow-policy.ts} +1 -1
  273. package/src/plugins/defaults/compaction/window-manager.ts +303 -1
  274. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +173 -0
  275. package/src/plugins/defaults/empty-response/hooks/stop.ts +11 -115
  276. package/src/plugins/defaults/empty-response/nudge-state-store.ts +46 -0
  277. package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +50 -0
  278. package/src/plugins/defaults/history-repair/hooks/stop.ts +22 -0
  279. package/src/plugins/defaults/history-repair/repair-state-store.ts +51 -0
  280. package/src/plugins/defaults/history-repair/terminal.ts +39 -2
  281. package/src/plugins/defaults/image-recovery/detect.ts +25 -0
  282. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +73 -0
  283. package/src/plugins/defaults/image-recovery/hooks/stop.ts +22 -0
  284. package/src/plugins/defaults/image-recovery/image-recovery-state-store.ts +48 -0
  285. package/src/plugins/defaults/image-recovery/package.json +14 -0
  286. package/src/{daemon/persist-unsendable-image.ts → plugins/defaults/image-recovery/recover.ts} +67 -14
  287. package/src/plugins/defaults/index.ts +71 -5
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +76 -112
  289. package/src/plugins/defaults/memory-retrieval/hooks/{user-prompt-submit-temp.ts → user-prompt-submit.ts} +83 -74
  290. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +14 -8
  291. package/src/plugins/defaults/memory-retrieval/injectors.ts +2 -18
  292. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  293. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1157 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +683 -0
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +161 -140
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +160 -0
  297. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +335 -316
  298. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +145 -53
  299. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +38 -1
  300. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +18 -8
  301. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +112 -71
  302. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +192 -51
  303. package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +4 -16
  304. package/src/plugins/defaults/memory-v3-shadow/card.test.ts +173 -0
  305. package/src/plugins/defaults/memory-v3-shadow/card.ts +116 -0
  306. package/src/plugins/defaults/memory-v3-shadow/core-set.test.ts +104 -0
  307. package/src/plugins/defaults/memory-v3-shadow/core-set.ts +59 -0
  308. package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.test.ts +305 -0
  309. package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.ts +278 -0
  310. package/src/plugins/defaults/memory-v3-shadow/hot-set.test.ts +138 -0
  311. package/src/plugins/defaults/memory-v3-shadow/hot-set.ts +85 -0
  312. package/src/plugins/defaults/memory-v3-shadow/injector.ts +331 -24
  313. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +119 -13
  314. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +169 -114
  315. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +47 -16
  316. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +144 -66
  317. package/src/plugins/defaults/memory-v3-shadow/prune.test.ts +758 -0
  318. package/src/plugins/defaults/memory-v3-shadow/prune.ts +471 -0
  319. package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +68 -16
  320. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +19 -12
  321. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +96 -43
  322. package/src/plugins/defaults/memory-v3-shadow/types.ts +34 -17
  323. package/src/plugins/defaults/title-generate/hooks/stop.ts +9 -11
  324. package/src/plugins/pipeline.ts +8 -5
  325. package/src/plugins/types.ts +5 -51
  326. package/src/providers/cache-control.ts +26 -0
  327. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -2
  328. package/src/providers/model-catalog.ts +13 -1
  329. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +147 -0
  330. package/src/providers/openai/chat-completions-provider.ts +46 -0
  331. package/src/providers/openai/responses-provider.ts +45 -0
  332. package/src/providers/registry.ts +0 -8
  333. package/src/runtime/__tests__/agent-wake.test.ts +0 -1
  334. package/src/runtime/agent-wake.ts +13 -0
  335. package/src/runtime/routes/__tests__/acp-routes.test.ts +151 -0
  336. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +12 -50
  337. package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +322 -0
  338. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +0 -62
  339. package/src/runtime/routes/__tests__/plugins-routes.test.ts +31 -11
  340. package/src/runtime/routes/acp-routes.test.ts +106 -0
  341. package/src/runtime/routes/acp-routes.ts +248 -2
  342. package/src/runtime/routes/browser-tabs-routes.ts +1 -1
  343. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  344. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +0 -14
  345. package/src/runtime/routes/consolidation-routes.ts +6 -82
  346. package/src/runtime/routes/conversation-list-routes.ts +6 -0
  347. package/src/runtime/routes/conversation-management-routes.ts +71 -0
  348. package/src/runtime/routes/identity-routes.ts +8 -0
  349. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  350. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -45
  351. package/src/runtime/routes/plugins-routes.ts +34 -45
  352. package/src/runtime/routes/schedule-routes.ts +43 -5
  353. package/src/runtime/routes/settings-routes.ts +140 -15
  354. package/src/runtime/routes/skills-routes.ts +18 -6
  355. package/src/runtime/services/__tests__/conversation-serializer.test.ts +140 -0
  356. package/src/runtime/services/conversation-serializer.ts +38 -1
  357. package/src/runtime/verification-outbound-actions.ts +147 -2
  358. package/src/runtime/verification-templates.ts +29 -3
  359. package/src/schedule/schedule-store.ts +19 -0
  360. package/src/skills/catalog-install.ts +77 -13
  361. package/src/tasks/task-scheduler.ts +1 -0
  362. package/src/telemetry/types.ts +66 -1
  363. package/src/telemetry/usage-telemetry-reporter.test.ts +542 -13
  364. package/src/telemetry/usage-telemetry-reporter.ts +213 -20
  365. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +49 -2
  366. package/src/tools/browser/__tests__/browser-status.test.ts +29 -5
  367. package/src/tools/browser/browser-execution.ts +27 -9
  368. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +380 -4
  369. package/src/tools/browser/cdp-client/__tests__/host-bridge-cdp-client.test.ts +107 -0
  370. package/src/tools/browser/cdp-client/__tests__/types.test.ts +6 -1
  371. package/src/tools/browser/cdp-client/factory.ts +217 -17
  372. package/src/tools/browser/cdp-client/host-bridge-cdp-client.ts +67 -0
  373. package/src/tools/browser/cdp-client/types.ts +22 -2
  374. package/src/tools/credential-execution/make-authenticated-request.ts +2 -1
  375. package/src/tools/credential-execution/manage-secure-command-tool.ts +169 -164
  376. package/src/tools/credential-execution/run-authenticated-command.ts +2 -1
  377. package/src/tools/executor.ts +39 -7
  378. package/src/tools/registry.ts +387 -5
  379. package/src/tools/schedule/create.ts +16 -0
  380. package/src/tools/schedule/list.ts +12 -4
  381. package/src/tools/schedule/update.ts +12 -0
  382. package/src/tools/skills/load.ts +11 -6
  383. package/src/tools/terminal/safe-env.ts +2 -0
  384. package/src/tools/types.ts +65 -9
  385. package/src/tools/workspace-tools/loader.ts +673 -0
  386. package/src/usage/attribution.ts +28 -0
  387. package/src/util/device-id.ts +17 -3
  388. package/src/util/platform.ts +16 -0
  389. package/tsconfig.plugin-api.json +13 -0
  390. package/src/__tests__/plugin-external-api.test.ts +0 -68
  391. package/src/__tests__/plugin-skill-contribution.test.ts +0 -355
  392. package/src/daemon/message-types/browser.ts +0 -10
  393. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  394. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +0 -36
  395. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +0 -37
  396. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +0 -106
  397. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +0 -44
  398. package/src/plugins/defaults/memory-v3-shadow/working-set.ts +0 -91
  399. package/src/plugins/external-api.ts +0 -114
  400. package/src/plugins/plugin-skill-contributions.ts +0 -292
@@ -4,136 +4,100 @@
4
4
  * After the agent loop compacts a conversation mid-turn it must re-apply the
5
5
  * runtime injections compaction stripped — the NOW.md scratchpad, PKB context,
6
6
  * memory-v2 static block, workspace top-level context, and Slack chronological
7
- * snapshot — onto the compacted history before the turn continues. This hook
8
- * is the memory system's home for that transform: it receives the message
9
- * history plus the resolved runtime-injection options and returns the edited
10
- * history (and the blocks it captured), with no dependency on the agent loop's
11
- * closure state.
7
+ * snapshot — onto the compacted history before the turn continues. This hook is
8
+ * the memory system's home for that transform: it re-applies the injections via
9
+ * {@link applyRuntimeInjections}, writes the edited history back onto the
10
+ * context, and re-tracks the memory graph's cached nodes against the re-injected
11
+ * history. The injection blocks the transform captures are not needed by the
12
+ * re-injection caller, so only the messages propagate.
12
13
  *
13
- * It re-applies the runtime injections via {@link applyRuntimeInjections} and
14
- * re-tracks the memory graph's cached nodes against the re-injected history.
15
- * The remaining orchestrator-side step (the post-injection bookkeeping the
16
- * loop records) is expected to migrate here as the hook subsumes the loop's
17
- * re-injection ceremony.
14
+ * Every per-turn input the live conversation can supply is self-resolved from
15
+ * it (looked up by id) rather than threaded in by the loop:
16
+ * - The trust snapshot comes from the conversation's turn-start trust snapshot
17
+ * ({@link Conversation.currentTurnTrustContext}), and the inbound actor
18
+ * context from the turn-start actor snapshot
19
+ * ({@link Conversation.currentTurnInboundActorContext}) — both frozen at turn
20
+ * start, not read live. Post-compaction runs mid-turn in a later tool
21
+ * iteration, where the live trust can be overwritten by a concurrent
22
+ * request's actor and the contact/member registry the actor context derives
23
+ * from can be mutated by contact tools; reading the frozen snapshots re-emits
24
+ * the values the turn's initial assembly resolved.
25
+ * - The memory graph handle comes from the plugin's own conversation-keyed
26
+ * registry ({@link getLiveGraphMemory}).
27
+ * - The call site and transcript inputs self-resolve inside
28
+ * {@link applyRuntimeInjections}.
18
29
  *
19
- * The memory graph handle is sourced internally from the plugin's own
20
- * conversation-keyed registry ({@link getLiveGraphMemory}) rather than being
21
- * threaded in by the loop — it is memory-retrieval-specific state, not
22
- * something the generic loop should carry.
30
+ * The loop supplies only the irreducible turn-identity fields on the public
31
+ * {@link PostCompactContext}.
23
32
  */
24
33
 
34
+ import type { PluginHookFn, PostCompactContext } from "@vellumai/plugin-api";
35
+
36
+ import { getConfig } from "../../../../config/loader.js";
37
+ import { findConversationOrSubagent } from "../../../../daemon/conversation-registry.js";
25
38
  import {
26
39
  applyRuntimeInjections,
27
- type InboundActorContext,
28
- type InjectionMode,
29
- type RuntimeInjectionResult,
40
+ resolveTurnModelProfileLabel,
30
41
  } from "../../../../daemon/conversation-runtime-assembly.js";
31
42
  import {
43
+ FALLBACK_TURN_TRUST,
32
44
  resolveTrustClass,
33
- type TrustContext,
34
45
  } from "../../../../daemon/trust-context.js";
35
46
  import { getLiveGraphMemory } from "../../../../memory/graph/conversation-graph-memory.js";
36
- import type { Message } from "../../../../providers/types.js";
37
-
38
- /**
39
- * Everything the post-compaction hook needs, supplied by the agent loop from
40
- * its own working state. Re-injection inputs migrate loop-ward by growing this
41
- * type; the loop hands the hook an object of this shape when it calls
42
- * {@link postCompact} directly.
43
- *
44
- * The turn-identity fields are flat here: `conversationId` is the key the
45
- * re-injection resolves the live conversation through (and the only one that,
46
- * together with `requestId` and `trust`, cannot be recovered from that live
47
- * conversation), while `turnIndex` and `callSite` are intentionally omitted so
48
- * {@link applyRuntimeInjections} self-resolves them from the live conversation.
49
- * The memory graph handle is likewise sourced internally via
50
- * {@link getLiveGraphMemory} rather than threaded in.
51
- */
52
- export interface PostCompactContext {
53
- /** Compacted message history to re-inject onto. */
54
- history: Message[];
55
- /**
56
- * Stable ID for the current request, forwarded onto the injector turn
57
- * context. The one turn-identity field that cannot be recovered from the
58
- * live conversation, so the loop supplies it.
59
- */
60
- requestId: string | undefined;
61
- /**
62
- * Conversation the turn is scoped to. The key the re-injection resolves the
63
- * live conversation (and its channel/trust/transcript state) through, so the
64
- * loop always supplies it.
65
- */
66
- conversationId: string;
67
- /**
68
- * Trust classification and channel identity for the inbound actor, forwarded
69
- * onto the injector turn context and used to gate the memory-graph re-track.
70
- * Supplied by the loop as the turn-start snapshot rather than re-resolved
71
- * mid-turn.
72
- */
73
- trust: TrustContext;
74
- /**
75
- * Whether the in-flight turn has no human present to answer clarification
76
- * questions. Resolved once by the agent loop at turn start (from its
77
- * `isInteractive` option, which can fall back to mutable client/headless
78
- * state that flips mid-turn on SSE reconnect) and handed to the hook here, so
79
- * re-injection uses the loop's turn-start snapshot rather than re-reading
80
- * live conversation state.
81
- */
82
- isNonInteractive: boolean;
83
- /**
84
- * Injection volume for the re-applied blocks. Owned by the agent loop, which
85
- * drops to `"minimal"` when overflow reduction trims the prompt; handed to
86
- * the hook here so re-injection matches the loop's committed decision instead
87
- * of re-deriving it.
88
- */
89
- mode: InjectionMode;
90
- /**
91
- * The `model_profile:` turn-context label, or `null` when the active
92
- * inference profile is unchanged since the last notified one. The agent loop
93
- * resolves it once at turn start from config and the turn's profile override,
94
- * and notifies the model only on change — a decision that flips mid-turn once
95
- * the notification is persisted, so it cannot be re-derived. Handed to the
96
- * hook here so re-injection re-emits the loop's turn-start value.
97
- */
98
- modelProfile: string | null;
99
- /**
100
- * Inbound actor identity and trust fields for the unified `<turn_context>`
101
- * block, or `null` on guardian turns (which suppress the actor section). The
102
- * agent loop resolves it once at turn start via the actor-trust resolver,
103
- * whose contact/member registry inputs can be mutated mid-turn by contact
104
- * tools, so it cannot be re-derived. Handed to the hook here so re-injection
105
- * re-emits the loop's turn-start value.
106
- */
107
- actorContext: InboundActorContext | null;
108
- }
109
47
 
110
- export default async function postCompact(
111
- ctx: PostCompactContext,
112
- ): Promise<RuntimeInjectionResult> {
113
- const { history, requestId, conversationId, trust, ...options } = ctx;
114
- // The remaining `options` carry the non-identity injection inputs
115
- // (`isNonInteractive`, `mode`, `modelProfile`, `actorContext`). Forward the
116
- // turn-identity fields the live conversation can't supply (`requestId`,
117
- // `conversationId`, `trust`); `turnIndex` and `callSite` self-resolve from
118
- // the live conversation so the re-injection matches the turn's initial
119
- // assembly.
48
+ const postCompact: PluginHookFn<PostCompactContext> = async (ctx) => {
49
+ const {
50
+ history,
51
+ requestId,
52
+ conversationId,
53
+ isNonInteractive,
54
+ modelProfileKey,
55
+ injectionMode,
56
+ } = ctx;
57
+ const mode = injectionMode ?? "full";
58
+ const config = getConfig();
59
+ const conversation = findConversationOrSubagent(conversationId);
60
+ // Trust and the inbound actor context are read from the conversation's
61
+ // turn-start snapshots rather than live state: post-compaction runs mid-turn
62
+ // in a later tool iteration, where the live trust can be overwritten by a
63
+ // concurrent request's actor and the contact/member registry the actor
64
+ // context derives from can be mutated by contact tools. The snapshots are the
65
+ // values the turn's initial assembly resolved.
66
+ const trust =
67
+ conversation?.currentTurnTrustContext ??
68
+ conversation?.trustContext ??
69
+ FALLBACK_TURN_TRUST;
70
+ const actorContext = conversation?.currentTurnInboundActorContext ?? null;
71
+ // Render the `model_profile:` label from the turn's resolved profile key,
72
+ // using the call site self-resolved from the live conversation — the same
73
+ // derivation the first-call user-prompt-submit assembly uses.
74
+ const modelProfile = resolveTurnModelProfileLabel(
75
+ modelProfileKey,
76
+ conversation?.currentCallSite ?? "mainAgent",
77
+ config.llm,
78
+ conversationId,
79
+ );
120
80
  const result = await applyRuntimeInjections(history, {
121
- ...options,
81
+ isNonInteractive,
82
+ modelProfile,
83
+ actorContext,
84
+ mode,
122
85
  requestId,
123
86
  conversationId,
124
87
  trust,
125
88
  });
89
+ // Write the re-injected history back onto the threaded context; the loop
90
+ // reads it from there once the hook settles.
91
+ ctx.history = result.messages;
126
92
  // Re-track the nodes the memory graph last injected so they survive against
127
- // the re-injected history. Untrusted actors and minimal-mode turns never
128
- // received a memory-graph injection, so there is nothing to re-track. The
129
- // actor's trust class is derived from the turn's own trust context (the same
130
- // value the injector chain resolves). The live graph handle is looked up
131
- // from the plugin's own registry by the turn's conversation id — the same
132
- // instance the turn's retrieval mutated, so re-tracking sees the real
133
- // cached-node state.
93
+ // the re-injected history. Untrusted actors never received a memory-graph
94
+ // injection, so there is nothing to re-track. The live graph handle is looked
95
+ // up from the plugin's own registry by the turn's conversation id — the same
96
+ // instance the turn's retrieval mutated.
134
97
  const isTrustedActor = resolveTrustClass(trust) === "guardian";
135
- if (isTrustedActor && options.mode !== "minimal") {
98
+ if (isTrustedActor) {
136
99
  getLiveGraphMemory(conversationId)?.retrackCachedNodes();
137
100
  }
138
- return result;
139
- }
101
+ };
102
+
103
+ export default postCompact;
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Default `user-prompt-submit-temp` hook: prepares a turn's prompt before the
3
- * first provider call by running memory-graph retrieval and then applying the
2
+ * Default `user-prompt-submit` hook: prepares a turn's prompt before the first
3
+ * provider call by running memory-graph retrieval and then applying the
4
4
  * runtime injections that assemble the turn.
5
5
  *
6
6
  * **Memory graph** via {@link ConversationGraphMemory.prepareMemory} —
@@ -22,88 +22,52 @@
22
22
  * post-compaction hook, which re-applies the same injections after a mid-loop
23
23
  * compaction.
24
24
  *
25
- * This fires at the early "prompt submitted, before context assembly" moment,
26
- * ahead of the canonical late `user-prompt-submit` hook (history repair,
27
- * title), which normalizes the assembled history after the overflow-reduction
28
- * transform that runs between the two moments. The `-temp` suffix marks this as
29
- * a transitional staging point that folds into `user-prompt-submit` once
30
- * compaction is cleared from the gap between the two call sites.
25
+ * Registered first in the `user-prompt-submit` chain, so it heads the chain
26
+ * ahead of history repair and title generation: those later hooks see the
27
+ * fully assembled, memory-injected history.
31
28
  */
32
29
 
33
- import type { PluginHookFn } from "@vellumai/plugin-api";
34
- import type { Logger } from "pino";
30
+ import type {
31
+ PluginHookFn,
32
+ UserPromptSubmitContext,
33
+ } from "@vellumai/plugin-api";
35
34
 
36
35
  import { getConfig } from "../../../../config/loader.js";
37
36
  import { findConversationOrSubagent } from "../../../../daemon/conversation-registry.js";
38
37
  import {
39
38
  applyRuntimeInjections,
40
39
  resolveTurnInboundActorContext,
40
+ resolveTurnModelProfileLabel,
41
41
  type RuntimeInjectionResult,
42
42
  } from "../../../../daemon/conversation-runtime-assembly.js";
43
- import type { ServerMessage } from "../../../../daemon/message-protocol.js";
44
43
  import type { MemoryRecalled } from "../../../../daemon/message-types/memory.js";
45
44
  import { resolveTrustClass } from "../../../../daemon/trust-context.js";
46
45
  import { updateMessageMetadata } from "../../../../memory/conversation-crud.js";
47
46
  import { recordMemoryRecallLog } from "../../../../memory/memory-recall-log-store.js";
48
- import type { Message } from "../../../../providers/types.js";
47
+ import { broadcastMessage } from "../../../../runtime/assistant-event-hub.js";
49
48
  import type { GraphMemoryResult } from "../../../types.js";
50
-
51
- /**
52
- * Context threaded through the `user-prompt-submit-temp` hook. The
53
- * conversation-scoped retrieval state (graph handle, abort signal, trust
54
- * class) is self-resolved from the live conversation by id; the readonly
55
- * fields here carry the event sink and the turn-start injection snapshot,
56
- * while `latestMessages` straddles input and output — the loop seeds it with
57
- * the pre-injection array and the hook overwrites it with the injected result.
58
- */
59
- export interface MemoryRetrievalHookContext {
60
- /** Event sink used by the graph retriever and `memory_recalled` emission. */
61
- readonly onEvent: (msg: ServerMessage) => void;
62
- /** Conversation the turn belongs to — keys the recall-log row. */
63
- readonly conversationId: string;
64
- /** User message the injected memory block is persisted onto. */
65
- readonly userMessageId: string;
66
- /** Turn-scoped logger for non-fatal persistence warnings. */
67
- readonly logger: Logger;
68
- /**
69
- * Working message list for the turn. Seeded by the loop with the
70
- * pre-injection messages and consumed as the retrieval input; the hook
71
- * overwrites it with the fully injected result (the memory-graph block, when
72
- * the actor is trusted, plus the runtime injections). Read back by the loop.
73
- */
74
- latestMessages: Message[];
75
- /**
76
- * Stable ID for the current request, forwarded onto the injector turn
77
- * context. The one turn-identity field the live conversation can't recover.
78
- */
79
- readonly requestId: string | undefined;
80
- /**
81
- * Whether the in-flight turn has no human present to answer clarification
82
- * questions. Resolved once at turn start and threaded in so injection uses
83
- * the turn-start snapshot rather than live state that can flip mid-turn.
84
- */
85
- readonly isNonInteractive: boolean;
86
- /**
87
- * The `model_profile:` turn-context label, or `null` when the active
88
- * inference profile is unchanged since the last notified one. Resolved once
89
- * at turn start, so it is threaded in rather than re-derived.
90
- */
91
- readonly modelProfile: string | null;
92
- }
49
+ import { MEMORY_V3_INJECTED_BLOCK_METADATA_KEY } from "../../memory-v3-shadow/ever-injected-store.js";
93
50
 
94
51
  /**
95
52
  * Persist and broadcast the retrieval's side effects: the injected block on
96
53
  * the user message's metadata (so it survives reloads), a recall-log row, and
97
- * the `memory_recalled` debug event. All three are best-effort — a failure to
98
- * persist must not abort the turn.
54
+ * the `memory_recalled` debug event. All three are best-effort — a failure
55
+ * must not abort the turn.
56
+ *
57
+ * The injected-block persist runs HERE, immediately after retrieval — not
58
+ * after runtime assembly. v2's activation store marks the block's pages
59
+ * `everInjected` during retrieval, so deferring the metadata write would open
60
+ * a window (which includes memory-v3's selector LLM call when the shadow flag
61
+ * is on) where a crash/abort leaves pages claimed-but-never-persisted: absent
62
+ * from history on reload AND suppressed until compaction. When memory-v3
63
+ * supersedes (strips) the block later this turn, {@link
64
+ * persistInjectionBlocks} REMOVES this key again in its combined update — a
65
+ * transient both-keys state mid-turn is acceptable; a v2 loss window is not.
99
66
  */
100
67
  function recordRecallSideEffects(
101
68
  graphResult: GraphMemoryResult,
102
- ctx: MemoryRetrievalHookContext,
69
+ ctx: UserPromptSubmitContext,
103
70
  ): void {
104
- // Persist the injected block text in message metadata so it survives
105
- // conversation reloads (eviction, restart, fork). loadFromDb re-injects
106
- // from metadata.
107
71
  if (graphResult.injectedBlockText) {
108
72
  try {
109
73
  updateMessageMetadata(ctx.userMessageId, {
@@ -174,7 +138,7 @@ function recordRecallSideEffects(
174
138
  recency: c.recencyBoost,
175
139
  })),
176
140
  };
177
- ctx.onEvent(memoryRecalledEvent);
141
+ broadcastMessage(memoryRecalledEvent);
178
142
  }
179
143
  }
180
144
 
@@ -186,23 +150,50 @@ function recordRecallSideEffects(
186
150
  * correspond to `userMessageId`. All present blocks are written in a single
187
151
  * update to avoid doubling SQLite SELECT+UPDATE work each turn. Best-effort — a
188
152
  * persistence failure must not abort the turn.
153
+ *
154
+ * The two `<memory>` layers end the turn mutually exclusive per row:
155
+ * - v2's block (`memoryInjectedBlock`) was already persisted right after
156
+ * retrieval (see {@link recordRecallSideEffects} — no loss window). When
157
+ * memory-v3 superseded it this turn (`blocks.memoryV3Active`), assembly
158
+ * stripped the v2 block from the tail, so this combined update REMOVES the
159
+ * key again (persisting it would rehydrate a block that is not in the live
160
+ * history — a reload cache-bust and duplicated memory). `v2BlockPersisted`
161
+ * tells this function whether there is anything to remove.
162
+ * - `blocks.memoryV3InjectedBlock` (the frozen net-new card block, unwrapped)
163
+ * persists under `MEMORY_V3_INJECTED_BLOCK_METADATA_KEY`; `loadFromDb`
164
+ * re-wraps and splices it on load, freezing the cards into history.
189
165
  */
190
166
  function persistInjectionBlocks(
191
167
  blocks: RuntimeInjectionResult["blocks"],
192
- ctx: MemoryRetrievalHookContext,
168
+ ctx: UserPromptSubmitContext,
169
+ v2BlockPersisted: boolean,
193
170
  ): void {
171
+ const removeV2Block = Boolean(blocks.memoryV3Active) && v2BlockPersisted;
194
172
  if (
195
173
  !blocks.unifiedTurnContext &&
196
174
  !blocks.pkbSystemReminder &&
197
175
  !blocks.workspaceBlock &&
198
176
  !blocks.nowScratchpadBlock &&
199
177
  !blocks.pkbContextBlock &&
200
- !blocks.memoryV2StaticBlock
178
+ !blocks.memoryV2StaticBlock &&
179
+ !blocks.memoryV3InjectedBlock &&
180
+ !removeV2Block
201
181
  ) {
202
182
  return;
203
183
  }
204
184
  try {
205
185
  const metadataUpdates: Record<string, unknown> = {};
186
+ if (removeV2Block) {
187
+ // An explicit `undefined` overrides the value persisted after retrieval
188
+ // and is dropped by `updateMessageMetadata`'s JSON.stringify, deleting
189
+ // the key — the metadata schema types the field `string | absent`, so
190
+ // removal must drop the key rather than write null.
191
+ metadataUpdates.memoryInjectedBlock = undefined;
192
+ }
193
+ if (blocks.memoryV3InjectedBlock) {
194
+ metadataUpdates[MEMORY_V3_INJECTED_BLOCK_METADATA_KEY] =
195
+ blocks.memoryV3InjectedBlock;
196
+ }
206
197
  if (blocks.unifiedTurnContext) {
207
198
  metadataUpdates.turnContextBlock = blocks.unifiedTurnContext;
208
199
  }
@@ -243,13 +234,14 @@ function persistInjectionBlocks(
243
234
  * into `prepareMemory`.
244
235
  */
245
236
  const userPromptSubmitMemoryRetrieval: PluginHookFn<
246
- MemoryRetrievalHookContext
237
+ UserPromptSubmitContext
247
238
  > = async (ctx) => {
248
239
  // The conversation-scoped retrieval state — graph handle, abort signal, and
249
240
  // trust class — is resolved from the live conversation by id rather than
250
241
  // threaded in, mirroring how `applyRuntimeInjections` self-resolves its
251
242
  // per-turn inputs.
252
243
  const conversation = findConversationOrSubagent(ctx.conversationId);
244
+ const config = getConfig();
253
245
  const abortSignal = conversation?.abortController?.signal;
254
246
  const isTrustedActor =
255
247
  resolveTrustClass(conversation?.trustContext) === "guardian";
@@ -258,15 +250,24 @@ const userPromptSubmitMemoryRetrieval: PluginHookFn<
258
250
  conversation?.assistantId,
259
251
  );
260
252
 
253
+ let v2BlockPersisted = false;
261
254
  if (conversation && isTrustedActor && abortSignal) {
255
+ // Retrieval progress (`memory_status`) and the `memory_recalled` summary
256
+ // publish to the shared `broadcastMessage` hub — the sink every turn
257
+ // publisher converges to — rather than a threaded event callback. This
258
+ // keeps any raw client-emit capability off the hook contract.
262
259
  const graphResult = await conversation.graphMemory.prepareMemory(
263
260
  ctx.latestMessages,
264
- getConfig(),
261
+ config,
265
262
  abortSignal,
266
- ctx.onEvent,
263
+ broadcastMessage,
267
264
  );
268
265
 
269
266
  recordRecallSideEffects(graphResult, ctx);
267
+ // The v2 block is persisted inside `recordRecallSideEffects`. If
268
+ // memory-v3 supersedes it later this turn, `persistInjectionBlocks`
269
+ // removes the key in its combined post-assembly update.
270
+ v2BlockPersisted = Boolean(graphResult.injectedBlockText);
270
271
 
271
272
  ctx.latestMessages = graphResult.runMessages;
272
273
  // Select dense+sparse as a matched pair so RRF fusion combines two signals
@@ -300,21 +301,29 @@ const userPromptSubmitMemoryRetrieval: PluginHookFn<
300
301
  // input — the Slack chronological transcript, the unified `<turn_context>`
301
302
  // block, channel/voice/transport hints, and the turn's trust/index/call-site
302
303
  // — from the live conversation, so we hand in only the request id and
303
- // conversation id plus the fields resolved once at turn start
304
- // (`isNonInteractive`, `modelProfile`). The unified `<turn_context>` actor
305
- // input is self-resolved from the live conversation's trust context. This
306
- // first-call assembly always runs at `"full"` volume; overflow reduction only
307
- // downgrades the mode on later re-injection.
304
+ // conversation id plus the field resolved once at turn start
305
+ // (`isNonInteractive`). The `model_profile` label is rendered here from the
306
+ // turn's resolved profile key, using the call site self-resolved from the
307
+ // live conversation. The unified `<turn_context>` actor input is
308
+ // self-resolved from the live conversation's trust context. This first-call
309
+ // assembly always runs at `"full"` volume; overflow reduction only downgrades
310
+ // the mode on later re-injection.
311
+ const modelProfile = resolveTurnModelProfileLabel(
312
+ ctx.modelProfileKey,
313
+ conversation?.currentCallSite ?? "mainAgent",
314
+ config.llm,
315
+ ctx.conversationId,
316
+ );
308
317
  const injection = await applyRuntimeInjections(ctx.latestMessages, {
309
318
  isNonInteractive: ctx.isNonInteractive,
310
- modelProfile: ctx.modelProfile,
319
+ modelProfile,
311
320
  actorContext,
312
321
  mode: "full",
313
322
  requestId: ctx.requestId,
314
323
  conversationId: ctx.conversationId,
315
324
  });
316
325
  ctx.latestMessages = injection.messages;
317
- persistInjectionBlocks(injection.blocks, ctx);
326
+ persistInjectionBlocks(injection.blocks, ctx, v2BlockPersisted);
318
327
  };
319
328
 
320
329
  export default userPromptSubmitMemoryRetrieval;
@@ -8,10 +8,11 @@
8
8
  * injectors out of the plugin registry.
9
9
  *
10
10
  * The chain combines the default injectors ({@link defaultInjectors}) with the
11
- * memory-v3 injector ({@link memoryV3Injector}). The sort mirrors the previous
12
- * registry aggregation (`Array.prototype.sort` is stable, so injectors sharing
13
- * an `order` keep their listed order), so the produced sequence — and therefore
14
- * the injected content — is identical.
11
+ * memory-v3 injectors ({@link memoryV3Injector} — frozen net-new cards — and
12
+ * {@link memoryV3SpotlightInjector} — the ephemeral section spotlight). The
13
+ * sort mirrors the previous registry aggregation (`Array.prototype.sort` is
14
+ * stable, so injectors sharing an `order` keep their listed order), so the
15
+ * produced sequence — and therefore the injected content — is identical.
15
16
  *
16
17
  * The chain is assembled lazily on first access and memoized, so the sort runs
17
18
  * once per process rather than per turn and module evaluation stays free of any
@@ -19,7 +20,10 @@
19
20
  */
20
21
 
21
22
  import type { Injector } from "../../types.js";
22
- import { memoryV3Injector } from "../memory-v3-shadow/injector.js";
23
+ import {
24
+ memoryV3Injector,
25
+ memoryV3SpotlightInjector,
26
+ } from "../memory-v3-shadow/injector.js";
23
27
  import { defaultInjectors } from "./injectors.js";
24
28
 
25
29
  let cachedChain: Injector[] | null = null;
@@ -27,9 +31,11 @@ let cachedChain: Injector[] | null = null;
27
31
  /** The order-sorted runtime injector chain, assembled once and memoized. */
28
32
  export function getInjectorChain(): Injector[] {
29
33
  if (cachedChain === null) {
30
- cachedChain = [...defaultInjectors, memoryV3Injector].sort(
31
- (a, b) => a.order - b.order,
32
- );
34
+ cachedChain = [
35
+ ...defaultInjectors,
36
+ memoryV3Injector,
37
+ memoryV3SpotlightInjector,
38
+ ].sort((a, b) => a.order - b.order);
33
39
  }
34
40
  return cachedChain;
35
41
  }
@@ -53,7 +53,7 @@ import { readNowScratchpad } from "../../../daemon/now-scratchpad.js";
53
53
  import { getInContextPkbPaths } from "../../../daemon/pkb-context-tracker.js";
54
54
  import { buildPkbReminder } from "../../../daemon/pkb-reminder-builder.js";
55
55
  import {
56
- resolveTrustClass,
56
+ isPersonalMemoryAllowed,
57
57
  type TrustContext,
58
58
  } from "../../../daemon/trust-context.js";
59
59
  import { listComments } from "../../../documents/document-comments-store.js";
@@ -62,10 +62,7 @@ import { getPkbAutoInjectList } from "../../../memory/pkb/autoinject.js";
62
62
  import { readPkbContext } from "../../../memory/pkb/context.js";
63
63
  import { searchPkbFiles } from "../../../memory/pkb/pkb-search.js";
64
64
  import { getPkbRoot, PKB_WORKSPACE_SCOPE } from "../../../memory/pkb/types.js";
65
- import {
66
- readMemoryV2StaticContent,
67
- shouldExposePersonalMemory,
68
- } from "../../../memory/v2/static-context.js";
65
+ import { readMemoryV2StaticContent } from "../../../memory/v2/static-context.js";
69
66
  import type { Message } from "../../../providers/types.js";
70
67
  import { getLogger } from "../../../util/logger.js";
71
68
  import { getSandboxWorkingDir } from "../../../util/platform.js";
@@ -360,19 +357,6 @@ const pkbReminderInjector: Injector = {
360
357
  },
361
358
  };
362
359
 
363
- /**
364
- * Whether personal-memory content (PKB, NOW.md) may be surfaced this turn: the
365
- * trust gate admits the actor (guardian-class, or an internal/local flow). All
366
- * memory-domain injectors share this gate so they apply identical exposure
367
- * rules without it being threaded in from the agent loop.
368
- */
369
- function isPersonalMemoryAllowed(trust: TrustContext): boolean {
370
- return shouldExposePersonalMemory({
371
- sourceChannel: trust.sourceChannel,
372
- isTrustedActor: resolveTrustClass(trust) === "guardian",
373
- });
374
- }
375
-
376
360
  /**
377
361
  * Read the auto-injected PKB content for the turn, gated behind the
378
362
  * personal-memory trust gate. Returns the content string when the gate admits
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "default-memory-retrieval",
3
+ "version": "1.0.0",
4
+ "description": "First-party default plugin running memory-graph retrieval and runtime injection at the front of the user-prompt-submit chain.",
5
+ "private": true,
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "engines": {
9
+ "node": ">=20.12.0"
10
+ },
11
+ "peerDependencies": {
12
+ "@vellumai/plugin-api": "^0.8.0"
13
+ }
14
+ }