@vellumai/assistant 0.8.7 → 0.8.8-dev.202606052332.17fc8ea

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 (570) hide show
  1. package/Dockerfile +20 -4
  2. package/bun.lock +2 -2
  3. package/docker-entrypoint.sh +4 -2
  4. package/docker-init-apt-root.sh +3 -1
  5. package/docker-kata-apt-env.sh +3 -1
  6. package/docker-kata-runtime-family.sh +12 -0
  7. package/docs/architecture/memory.md +1 -1
  8. package/examples/plugins/echo/README.md +61 -66
  9. package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
  10. package/examples/plugins/echo/hooks/stop.ts +16 -0
  11. package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
  12. package/examples/plugins/echo/package.json +1 -2
  13. package/examples/plugins/echo/src/emit.ts +19 -0
  14. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
  15. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
  16. package/openapi.yaml +3378 -335
  17. package/package.json +2 -2
  18. package/scripts/generate-openapi.ts +68 -41
  19. package/src/__tests__/agent-loop-exit-reason.test.ts +35 -93
  20. package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
  21. package/src/__tests__/agent-loop.test.ts +37 -87
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  23. package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
  24. package/src/__tests__/annotate-risk-options.test.ts +2 -3
  25. package/src/__tests__/anthropic-provider.test.ts +95 -2
  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__/approval-routes-http.test.ts +4 -1
  29. package/src/__tests__/assistant-event-hub.test.ts +25 -0
  30. package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
  31. package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
  32. package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
  33. package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
  34. package/src/__tests__/btw-routes.test.ts +62 -3
  35. package/src/__tests__/build-persisted-content.test.ts +184 -0
  36. package/src/__tests__/catalog-files.test.ts +1 -1
  37. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  38. package/src/__tests__/channel-approvals.test.ts +1 -1
  39. package/src/__tests__/clawhub-files.test.ts +1 -1
  40. package/src/__tests__/compaction-circuit.test.ts +258 -0
  41. package/src/__tests__/compaction-direct.test.ts +132 -0
  42. package/src/__tests__/compaction.benchmark.test.ts +0 -30
  43. package/src/__tests__/config-watcher.test.ts +1 -1
  44. package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
  45. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -5
  46. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -7
  47. package/src/__tests__/conversation-agent-loop-overflow.test.ts +316 -1143
  48. package/src/__tests__/conversation-agent-loop.test.ts +638 -1655
  49. package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
  50. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  51. package/src/__tests__/conversation-history-web-search.test.ts +11 -1
  52. package/src/__tests__/conversation-pairing.test.ts +4 -31
  53. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
  54. package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -10
  55. package/src/__tests__/conversation-queue.test.ts +2 -0
  56. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
  57. package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
  58. package/src/__tests__/conversation-runtime-assembly.test.ts +310 -300
  59. package/src/__tests__/conversation-runtime-workspace.test.ts +105 -45
  60. package/src/__tests__/conversation-slash-commands.test.ts +8 -42
  61. package/src/__tests__/conversation-slash-queue.test.ts +6 -1
  62. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  63. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
  64. package/src/__tests__/conversation-sync-tags.test.ts +27 -15
  65. package/src/__tests__/conversation-title-service.test.ts +135 -2
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +17 -16
  67. package/src/__tests__/conversation-workspace-injection.test.ts +67 -2
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +7 -6
  69. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  70. package/src/__tests__/cross-provider-web-search.test.ts +214 -1
  71. package/src/__tests__/db-acp-history.test.ts +101 -0
  72. package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
  73. package/src/__tests__/dm-persistence.test.ts +5 -1
  74. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  75. package/src/__tests__/empty-response-hook.test.ts +304 -0
  76. package/src/__tests__/feature-flag-test-helpers.ts +2 -2
  77. package/src/__tests__/file-write-tool.test.ts +63 -0
  78. package/src/__tests__/gateway-only-guard.test.ts +12 -2
  79. package/src/__tests__/gemini-image-service.test.ts +13 -0
  80. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  81. package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
  82. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  83. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  84. package/src/__tests__/heartbeat-service.test.ts +1 -0
  85. package/src/__tests__/helpers/mock-provider.ts +110 -0
  86. package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
  87. package/src/__tests__/history-repair-hook.test.ts +1 -0
  88. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  89. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  90. package/src/__tests__/identity-intro-cache.test.ts +12 -100
  91. package/src/__tests__/identity-routes.test.ts +248 -7
  92. package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
  93. package/src/__tests__/injector-background-turn.test.ts +3 -9
  94. package/src/__tests__/injector-chain.test.ts +139 -275
  95. package/src/__tests__/injector-disk-pressure.test.ts +75 -41
  96. package/src/__tests__/injector-document-comments.test.ts +3 -3
  97. package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
  98. package/src/__tests__/injector-v3-suppression.test.ts +31 -37
  99. package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
  100. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  101. package/src/__tests__/list-messages-page-latest.test.ts +60 -0
  102. package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
  103. package/src/__tests__/llm-usage-store.test.ts +223 -1
  104. package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
  105. package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
  106. package/src/__tests__/native-web-search.test.ts +191 -0
  107. package/src/__tests__/onboarding-template-contract.test.ts +2 -0
  108. package/src/__tests__/openai-image-service.test.ts +17 -0
  109. package/src/__tests__/openai-provider.test.ts +31 -1
  110. package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -284
  111. package/src/__tests__/persist-unsendable-image.test.ts +215 -0
  112. package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
  113. package/src/__tests__/pkb-autoinject.test.ts +2 -5
  114. package/src/__tests__/plugin-api-shim.test.ts +3 -6
  115. package/src/__tests__/plugin-bootstrap.test.ts +14 -40
  116. package/src/__tests__/plugin-registry.test.ts +3 -76
  117. package/src/__tests__/plugin-types.test.ts +0 -193
  118. package/src/__tests__/process-message-display-content.test.ts +6 -2
  119. package/src/__tests__/reaction-persistence.test.ts +1 -1
  120. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
  121. package/src/__tests__/resolve-trust-class.test.ts +4 -4
  122. package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
  123. package/src/__tests__/schedule-routes.test.ts +603 -2
  124. package/src/__tests__/schedule-store.test.ts +41 -0
  125. package/src/__tests__/schedule-tools.test.ts +35 -0
  126. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  127. package/src/__tests__/server-history-render.test.ts +314 -1
  128. package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
  129. package/src/__tests__/skillssh-files.test.ts +1 -1
  130. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  131. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  132. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  133. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  134. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  135. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  136. package/src/__tests__/system-prompt.test.ts +20 -0
  137. package/src/__tests__/task-scheduler.test.ts +162 -1
  138. package/src/__tests__/terminal-tools.test.ts +6 -1
  139. package/src/__tests__/title-generate-hook.test.ts +319 -0
  140. package/src/__tests__/tool-error-hook.test.ts +278 -0
  141. package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
  142. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  143. package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
  144. package/src/__tests__/tool-result-truncation.test.ts +0 -2
  145. package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
  146. package/src/__tests__/ui-work-result-surface.test.ts +159 -0
  147. package/src/__tests__/usage-routes.test.ts +285 -1
  148. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  149. package/src/__tests__/voice-session-bridge.test.ts +6 -3
  150. package/src/__tests__/web-search-backend-failure.test.ts +166 -0
  151. package/src/acp/__tests__/agent-process.test.ts +161 -0
  152. package/src/acp/__tests__/client-handler.test.ts +40 -0
  153. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  154. package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
  155. package/src/acp/__tests__/prepare-agent-env.test.ts +137 -0
  156. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  157. package/src/acp/__tests__/session-manager-resume.test.ts +736 -0
  158. package/src/acp/agent-process.ts +61 -1
  159. package/src/acp/auto-install.test.ts +196 -0
  160. package/src/acp/auto-install.ts +177 -0
  161. package/src/acp/client-handler.ts +31 -0
  162. package/src/acp/feature-gate.test.ts +48 -0
  163. package/src/acp/feature-gate.ts +34 -0
  164. package/src/acp/prepare-agent-env.ts +83 -29
  165. package/src/acp/resolve-agent.test.ts +320 -7
  166. package/src/acp/resolve-agent.ts +182 -18
  167. package/src/acp/resume-hint.ts +25 -0
  168. package/src/acp/session-manager.ts +495 -73
  169. package/src/acp/types.ts +8 -0
  170. package/src/agent/compaction-circuit.ts +60 -102
  171. package/src/agent/loop.ts +362 -485
  172. package/src/api/events/assistant-thinking-delta.ts +33 -0
  173. package/src/api/events/tool-output-chunk.ts +45 -0
  174. package/src/api/events/tool-use-preview-start.ts +32 -0
  175. package/src/api/events/trace-event.ts +69 -0
  176. package/src/api/index.ts +48 -13
  177. package/src/api/responses/conversation-message.ts +374 -0
  178. package/src/approvals/guardian-request-resolvers.ts +1 -1
  179. package/src/avatar/__tests__/avatar-store.test.ts +34 -29
  180. package/src/background-wake/next-wake.ts +1 -0
  181. package/src/cli/commands/__tests__/notifications.test.ts +58 -14
  182. package/src/cli/commands/notifications.ts +112 -60
  183. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  184. package/src/config/acp-defaults.test.ts +10 -0
  185. package/src/config/acp-defaults.ts +6 -0
  186. package/src/config/assistant-feature-flags.ts +22 -11
  187. package/src/config/bundled-skills/acp/SKILL.md +83 -31
  188. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  189. package/src/config/bundled-skills/app-builder/SKILL.md +224 -398
  190. package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
  191. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  192. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  193. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  194. package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
  195. package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
  196. package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
  197. package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
  198. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  199. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  200. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  201. package/src/config/bundled-skills/messaging/SKILL.md +0 -7
  202. package/src/config/bundled-tool-registry.ts +2 -0
  203. package/src/config/feature-flag-cache.ts +3 -3
  204. package/src/config/feature-flag-registry.json +48 -7
  205. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
  206. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
  207. package/src/config/schemas/heartbeat.ts +9 -0
  208. package/src/config/schemas/llm.ts +1 -0
  209. package/src/config/schemas/memory-v2.ts +8 -0
  210. package/src/config/schemas/memory-v3.ts +8 -0
  211. package/src/config/schemas/platform.ts +8 -0
  212. package/src/config/seed-inference-profiles.ts +2 -2
  213. package/src/config/skills.ts +13 -0
  214. package/src/context/compactor.ts +1 -1
  215. package/src/context/strip-injections.ts +128 -0
  216. package/src/context/token-estimator.ts +23 -0
  217. package/src/context/tool-result-truncation.ts +0 -23
  218. package/src/context/window-manager.ts +5 -7
  219. package/src/credential-execution/executable-discovery.ts +16 -0
  220. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
  221. package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
  222. package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
  223. package/src/daemon/assistant-attachments.ts +1 -1
  224. package/src/daemon/config-watcher.ts +2 -2
  225. package/src/daemon/context-overflow-reducer.ts +0 -1
  226. package/src/daemon/conversation-agent-loop-handlers.ts +594 -153
  227. package/src/daemon/conversation-agent-loop.ts +301 -997
  228. package/src/daemon/conversation-history.ts +5 -4
  229. package/src/daemon/conversation-lifecycle.ts +3 -4
  230. package/src/daemon/conversation-messaging.ts +7 -6
  231. package/src/daemon/conversation-process.ts +11 -16
  232. package/src/daemon/conversation-registry.ts +159 -0
  233. package/src/daemon/conversation-runtime-assembly.ts +218 -398
  234. package/src/daemon/conversation-slash.ts +6 -25
  235. package/src/daemon/conversation-store.ts +9 -90
  236. package/src/daemon/conversation-surfaces.ts +222 -4
  237. package/src/daemon/conversation-tool-setup.ts +2 -29
  238. package/src/daemon/conversation-workspace.ts +17 -0
  239. package/src/daemon/conversation.ts +32 -20
  240. package/src/daemon/external-plugins-bootstrap.ts +17 -18
  241. package/src/daemon/handlers/config-a2a.ts +51 -36
  242. package/src/daemon/handlers/config-slack-channel.ts +20 -14
  243. package/src/daemon/handlers/config-telegram.ts +16 -2
  244. package/src/daemon/handlers/conversations.ts +3 -1
  245. package/src/daemon/handlers/shared.ts +156 -84
  246. package/src/daemon/handlers/skills.ts +42 -10
  247. package/src/daemon/lifecycle.ts +25 -0
  248. package/src/daemon/message-types/apps.ts +1 -29
  249. package/src/daemon/message-types/messages.ts +9 -57
  250. package/src/daemon/message-types/skills.ts +2 -0
  251. package/src/daemon/message-types/surfaces.ts +136 -3
  252. package/src/daemon/now-scratchpad.ts +21 -0
  253. package/src/daemon/orphan-reaper.test.ts +210 -0
  254. package/src/daemon/orphan-reaper.ts +240 -0
  255. package/src/daemon/overflow-reduction-loop.ts +230 -0
  256. package/src/daemon/persist-unsendable-image.ts +117 -0
  257. package/src/daemon/process-message.ts +1 -3
  258. package/src/daemon/server.ts +2 -0
  259. package/src/daemon/trace-emitter.ts +6 -4
  260. package/src/daemon/trust-context.ts +19 -0
  261. package/src/daemon/wake-target-adapter.ts +3 -1
  262. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  263. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  264. package/src/heartbeat/heartbeat-service.ts +26 -0
  265. package/src/home/home-greeting-cache.ts +24 -1
  266. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  267. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  268. package/src/ipc/gateway-client.test.ts +2 -2
  269. package/src/ipc/gateway-client.ts +3 -3
  270. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  271. package/src/ipc/skill-routes/memory.ts +4 -2
  272. package/src/media/gemini-image-service.ts +15 -0
  273. package/src/media/openai-image-service.ts +14 -0
  274. package/src/media/types.ts +34 -0
  275. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
  276. package/src/memory/auth-fallback-events-store.ts +94 -0
  277. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  278. package/src/memory/conversation-title-service.ts +65 -41
  279. package/src/memory/db-init.ts +6 -0
  280. package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
  281. package/src/memory/graph/conversation-graph-memory.ts +65 -0
  282. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  283. package/src/memory/jobs-store.ts +33 -0
  284. package/src/memory/jobs-worker.ts +32 -5
  285. package/src/memory/llm-usage-store.ts +224 -50
  286. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
  287. package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
  288. package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
  289. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  290. package/src/memory/migrations/index.ts +3 -0
  291. package/src/memory/pkb/autoinject.ts +61 -0
  292. package/src/memory/pkb/context.ts +50 -0
  293. package/src/memory/pkb/types.ts +14 -0
  294. package/src/memory/schedule-attribution-sql.ts +104 -0
  295. package/src/memory/schema/acp.ts +4 -0
  296. package/src/memory/schema/infrastructure.ts +16 -0
  297. package/src/memory/usage-grouped-buckets.ts +6 -1
  298. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -4
  299. package/src/memory/v2/consolidation-job.ts +14 -5
  300. package/src/notifications/conversation-pairing.ts +8 -15
  301. package/src/notifications/decision-engine.ts +6 -3
  302. package/src/notifications/home-feed-side-effect.ts +12 -1
  303. package/src/permissions/prompter.ts +4 -0
  304. package/src/plugin-api/constants.ts +4 -0
  305. package/src/plugin-api/index.ts +7 -5
  306. package/src/plugin-api/types.ts +151 -1
  307. package/src/plugins/defaults/compaction/compact.ts +59 -0
  308. package/src/plugins/defaults/compaction/package.json +1 -1
  309. package/src/plugins/defaults/compaction/register.ts +8 -19
  310. package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
  311. package/src/plugins/defaults/empty-response/register.ts +8 -13
  312. package/src/plugins/defaults/index.ts +2 -18
  313. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
  314. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
  315. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
  316. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +288 -81
  317. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
  318. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +16 -0
  319. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +4 -4
  320. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
  321. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +48 -12
  322. package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
  323. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
  324. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
  325. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +104 -32
  326. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
  327. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +96 -30
  328. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +34 -16
  329. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +5 -5
  330. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +2 -2
  331. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -0
  332. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  333. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  334. package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
  335. package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
  336. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +8 -8
  337. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +26 -14
  338. package/src/plugins/defaults/{llm-call → memory-v3-shadow}/package.json +2 -2
  339. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
  340. package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
  341. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +3 -3
  342. package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
  343. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
  344. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +51 -45
  345. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
  346. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +61 -46
  347. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +69 -99
  348. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
  349. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +8 -0
  350. package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
  351. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
  352. package/src/plugins/defaults/title-generate/package.json +1 -1
  353. package/src/plugins/defaults/title-generate/register.ts +18 -18
  354. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
  355. package/src/plugins/defaults/tool-error/package.json +1 -1
  356. package/src/plugins/defaults/tool-error/register.ts +9 -21
  357. package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
  358. package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
  359. package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
  360. package/src/plugins/external-api.ts +2 -2
  361. package/src/plugins/pipeline.ts +6 -305
  362. package/src/plugins/registry.ts +10 -55
  363. package/src/plugins/types.ts +62 -797
  364. package/src/plugins/user-loader.ts +30 -127
  365. package/src/proactive-artifact/aux-message-injector.ts +4 -4
  366. package/src/proactive-artifact/job.test.ts +8 -13
  367. package/src/prompts/__tests__/system-prompt.test.ts +42 -0
  368. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
  369. package/src/prompts/templates/BOOTSTRAP.md +2 -2
  370. package/src/prompts/templates/system-sections.ts +15 -0
  371. package/src/providers/anthropic/client.ts +37 -29
  372. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
  373. package/src/providers/openai/chat-completions-provider.ts +44 -0
  374. package/src/providers/openrouter/client.ts +1 -0
  375. package/src/providers/placeholder-sentinels.ts +35 -0
  376. package/src/runtime/__tests__/agent-wake.test.ts +10 -6
  377. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  378. package/src/runtime/agent-wake.ts +2 -5
  379. package/src/runtime/assistant-event-hub.ts +37 -7
  380. package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
  381. package/src/runtime/channel-approvals.ts +1 -1
  382. package/src/runtime/http-router.ts +16 -21
  383. package/src/runtime/http-types.ts +16 -70
  384. package/src/runtime/interactive-ui.ts +1 -1
  385. package/src/runtime/pending-interactions.ts +1 -0
  386. package/src/runtime/routes/__tests__/acp-routes.test.ts +283 -55
  387. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
  388. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  389. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
  390. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
  391. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  392. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  393. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  394. package/src/runtime/routes/acp-routes.test.ts +89 -25
  395. package/src/runtime/routes/acp-routes.ts +81 -29
  396. package/src/runtime/routes/app-management-routes.ts +6 -117
  397. package/src/runtime/routes/app-routes.ts +13 -15
  398. package/src/runtime/routes/approval-routes.ts +1 -1
  399. package/src/runtime/routes/attachment-routes.ts +26 -15
  400. package/src/runtime/routes/avatar-routes.ts +26 -0
  401. package/src/runtime/routes/browser-routes.ts +1 -1
  402. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  403. package/src/runtime/routes/btw-routes.ts +29 -23
  404. package/src/runtime/routes/consolidation-routes.ts +120 -20
  405. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  406. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  407. package/src/runtime/routes/conversation-query-routes.ts +3 -1
  408. package/src/runtime/routes/conversation-routes.ts +372 -185
  409. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  410. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  411. package/src/runtime/routes/documents-routes.ts +4 -0
  412. package/src/runtime/routes/domain-routes.ts +51 -37
  413. package/src/runtime/routes/epoch-millis-range.ts +34 -0
  414. package/src/runtime/routes/events-routes.ts +28 -34
  415. package/src/runtime/routes/gateway-log-routes.ts +26 -4
  416. package/src/runtime/routes/heartbeat-routes.ts +32 -12
  417. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  418. package/src/runtime/routes/host-cu-routes.ts +1 -1
  419. package/src/runtime/routes/identity-intro-cache.ts +11 -34
  420. package/src/runtime/routes/identity-routes.ts +224 -18
  421. package/src/runtime/routes/image-generation-routes.ts +40 -2
  422. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  423. package/src/runtime/routes/index.ts +2 -0
  424. package/src/runtime/routes/integrations/a2a.ts +12 -10
  425. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
  426. package/src/runtime/routes/integrations/slack/channel.ts +4 -0
  427. package/src/runtime/routes/integrations/slack/share.ts +27 -6
  428. package/src/runtime/routes/integrations/telegram.ts +6 -0
  429. package/src/runtime/routes/integrations/twilio.ts +42 -0
  430. package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
  431. package/src/runtime/routes/log-export-routes.ts +8 -0
  432. package/src/runtime/routes/memory-v2-routes.ts +15 -8
  433. package/src/runtime/routes/memory-v3-routes.ts +66 -34
  434. package/src/runtime/routes/oauth-apps.ts +66 -12
  435. package/src/runtime/routes/oauth-providers.ts +44 -5
  436. package/src/runtime/routes/platform-routes.ts +81 -5
  437. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
  438. package/src/runtime/routes/playground/force-compact.ts +1 -1
  439. package/src/runtime/routes/playground/helpers.ts +1 -1
  440. package/src/runtime/routes/rename-conversation-routes.ts +5 -0
  441. package/src/runtime/routes/schedule-routes.ts +152 -42
  442. package/src/runtime/routes/secret-routes.ts +14 -2
  443. package/src/runtime/routes/skills-routes.ts +43 -14
  444. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  445. package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
  446. package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
  447. package/src/runtime/routes/trust-rules-routes.ts +26 -2
  448. package/src/runtime/routes/tts-routes.ts +35 -0
  449. package/src/runtime/routes/types.ts +66 -8
  450. package/src/runtime/routes/usage-routes.ts +47 -39
  451. package/src/runtime/routes/webhook-routes.ts +41 -2
  452. package/src/runtime/routes/work-items-routes.ts +2 -4
  453. package/src/runtime/routes/workspace-routes.ts +4 -0
  454. package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
  455. package/src/runtime/services/analyze-conversation.ts +2 -2
  456. package/src/runtime/services/conversation-serializer.ts +1 -1
  457. package/src/schedule/schedule-store.ts +20 -1
  458. package/src/schedule/schedule-usage-store.ts +83 -0
  459. package/src/schedule/scheduler.ts +12 -5
  460. package/src/signals/cancel.ts +2 -4
  461. package/src/skills/catalog-files.ts +2 -2
  462. package/src/skills/catalog-install.ts +3 -0
  463. package/src/skills/categories-cache.ts +118 -0
  464. package/src/skills/clawhub-files.ts +1 -2
  465. package/src/skills/skillssh-files.ts +1 -2
  466. package/src/subagent/manager.ts +17 -5
  467. package/src/telemetry/types.ts +29 -1
  468. package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
  469. package/src/telemetry/usage-telemetry-reporter.ts +57 -2
  470. package/src/tools/acp/context.ts +20 -0
  471. package/src/tools/acp/list-agents.test.ts +7 -1
  472. package/src/tools/acp/spawn.test.ts +158 -55
  473. package/src/tools/acp/spawn.ts +47 -72
  474. package/src/tools/acp/steer.test.ts +105 -8
  475. package/src/tools/acp/steer.ts +48 -17
  476. package/src/tools/apps/executors.ts +13 -8
  477. package/src/tools/executor.ts +1 -53
  478. package/src/tools/filesystem/write.ts +34 -0
  479. package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
  480. package/src/tools/network/__tests__/web-search.test.ts +11 -3
  481. package/src/tools/network/web-search-error.test.ts +248 -0
  482. package/src/tools/network/web-search-error.ts +267 -0
  483. package/src/tools/network/web-search.ts +207 -48
  484. package/src/tools/schedule/create.ts +2 -0
  485. package/src/tools/subagent/spawn.ts +2 -4
  486. package/src/tools/terminal/safe-env.ts +10 -1
  487. package/src/tools/ui-surface/definitions.ts +34 -5
  488. package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
  489. package/src/tts/provider-catalog.ts +76 -1
  490. package/src/util/mutex.ts +47 -0
  491. package/src/workspace/git-service.ts +1 -42
  492. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  493. package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
  494. package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
  495. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
  496. package/src/workspace/migrations/registry.ts +6 -0
  497. package/docs/plugins.md +0 -836
  498. package/examples/plugins/echo/register.ts +0 -184
  499. package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
  500. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  501. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  502. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  503. package/src/__tests__/empty-response-pipeline.test.ts +0 -423
  504. package/src/__tests__/llm-call-pipeline.test.ts +0 -287
  505. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
  506. package/src/__tests__/persistence-pipeline.test.ts +0 -503
  507. package/src/__tests__/pipeline-runner.test.ts +0 -564
  508. package/src/__tests__/title-generate-pipeline.test.ts +0 -211
  509. package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
  510. package/src/__tests__/tool-error-pipeline.test.ts +0 -241
  511. package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
  512. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
  513. package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
  514. package/src/gallery/default-gallery.ts +0 -1359
  515. package/src/gallery/gallery-manifest.ts +0 -28
  516. package/src/home/feature-gate.ts +0 -22
  517. package/src/memory/v3/provider-blocks.ts +0 -16
  518. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  519. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  520. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  521. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  522. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  523. package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
  524. package/src/plugins/defaults/empty-response/terminal.ts +0 -106
  525. package/src/plugins/defaults/injectors/package.json +0 -15
  526. package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
  527. package/src/plugins/defaults/llm-call/register.ts +0 -45
  528. package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
  529. package/src/plugins/defaults/memory-retrieval/package.json +0 -15
  530. package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
  531. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  532. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  533. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  534. package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
  535. package/src/plugins/defaults/persistence/package.json +0 -15
  536. package/src/plugins/defaults/persistence/register.ts +0 -38
  537. package/src/plugins/defaults/persistence/terminal.ts +0 -83
  538. package/src/plugins/defaults/title-generate/terminal.ts +0 -31
  539. package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
  540. package/src/plugins/defaults/token-estimate/package.json +0 -15
  541. package/src/plugins/defaults/token-estimate/register.ts +0 -34
  542. package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
  543. package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
  544. package/src/plugins/defaults/tool-error/terminal.ts +0 -47
  545. package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
  546. package/src/plugins/defaults/tool-execute/package.json +0 -15
  547. package/src/plugins/defaults/tool-execute/register.ts +0 -49
  548. package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
  549. package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
  550. package/src/skills/category-inference.ts +0 -111
  551. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
  552. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
  553. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  554. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  555. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
  556. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
  557. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
  558. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +0 -0
  559. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  560. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  561. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/core.ts +0 -0
  562. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  563. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  564. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  565. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  566. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  567. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  568. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
  569. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
  570. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
@@ -22,16 +22,12 @@ mock.module("../config/loader.js", () => ({
22
22
 
23
23
  import {
24
24
  DEFAULT_INJECTOR_ORDER,
25
- defaultInjectorsPlugin,
26
- } from "../plugins/defaults/injectors/register.js";
27
- import {
28
- registerPlugin,
29
- resetPluginRegistryForTests,
30
- } from "../plugins/registry.js";
25
+ defaultInjectors,
26
+ } from "../plugins/defaults/memory-retrieval/injectors.js";
31
27
  import type { Injector, TurnContext } from "../plugins/types.js";
32
28
 
33
29
  function findInjector(name: string): Injector {
34
- const injector = defaultInjectorsPlugin.injectors?.find(
30
+ const injector = defaultInjectors.find(
35
31
  (candidate) => candidate.name === name,
36
32
  );
37
33
  if (!injector) {
@@ -57,8 +53,6 @@ const DEFAULT_INJECTION_TEXT =
57
53
 
58
54
  describe("background-turn injector", () => {
59
55
  beforeEach(() => {
60
- resetPluginRegistryForTests();
61
- registerPlugin(defaultInjectorsPlugin);
62
56
  configBackgroundInjection = DEFAULT_INJECTION_TEXT;
63
57
  });
64
58
 
@@ -1,29 +1,25 @@
1
1
  /**
2
- * Tests for the plugin-driven runtime-injection chain (PR 21 of the
3
- * `agent-plugin-system` plan).
2
+ * Tests for the static runtime-injection chain.
4
3
  *
5
4
  * Covers:
6
5
  *
7
- * 1. The ten default injectors registered by `defaultInjectorsPlugin` come
8
- * back from `getInjectors()` in the documented order
9
- * (disk-pressure-warningworkspace-context → unified-turn-context →
10
- * pkb-contextpkb-remindermemory-v2-staticnow-md
6
+ * 1. The default injectors ({@link defaultInjectors}) are listed in the
7
+ * documented order (disk-pressure-warning workspace-context
8
+ * background-turnunified-turn-context → pkb-context → pkb-reminder →
9
+ * memory-v2-staticnow-mdactive-documentsdocument-comments
11
10
  * subagent-status → slack-messages → thread-focus).
12
- * 2. A third-party-registered injector at `order: 25` slots between
13
- * `unified-turn-context` (order 20) and `pkb` (order 30), proving the
14
- * extensibility contract.
15
- * 3. `composeInjectorChain` concatenates non-null blocks with a blank-line
16
- * separator and yields an empty string when every injector opts out — the
17
- * latter matches pre-PR behavior for the golden-path conversation state
18
- * (all defaults return `null` in this PR).
19
- * 4. `applyRuntimeInjections` with an empty `turnContext` chain leaves
20
- * `blocks.injectorChainBlock` undefined, preserving the existing snapshot
21
- * for conversations that don't opt into the chain.
22
- * 5. `applyRuntimeInjections` surfaces the composed chain output on
23
- * `blocks.injectorChainBlock` when a third-party injector contributes
24
- * content.
11
+ * 2. The assembled {@link injectorChain} sorts the defaults together with the
12
+ * memory-v3 injector by ascending `order`, so memory-v3 (order 1000) lands
13
+ * last.
14
+ * 3. `composeInjectorChain` yields an empty string when every injector opts out
15
+ * the golden-path conversation state where all defaults return `null`.
16
+ * 4. `applyRuntimeInjections` splices each default injector's block into the
17
+ * correct position in the per-turn message array, and gates blocks by
18
+ * injection mode.
25
19
  */
26
20
 
21
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
22
+ import { dirname, join } from "node:path";
27
23
  import { beforeEach, describe, expect, mock, test } from "bun:test";
28
24
 
29
25
  // This test exercises v1 PKB injection. `config.memory.v2.enabled`
@@ -44,26 +40,29 @@ mock.module("../config/loader.js", () => ({
44
40
 
45
41
  const { applyRuntimeInjections, composeInjectorChain } =
46
42
  await import("../daemon/conversation-runtime-assembly.js");
47
- const { DEFAULT_INJECTOR_ORDER, defaultInjectorsPlugin } =
48
- await import("../plugins/defaults/injectors/register.js");
43
+ const { DEFAULT_INJECTOR_ORDER, defaultInjectors } =
44
+ await import("../plugins/defaults/memory-retrieval/injectors.js");
45
+ const { getInjectorChain } =
46
+ await import("../plugins/defaults/memory-retrieval/injector-chain.js");
49
47
  import {
50
- getInjectors,
51
- registerPlugin,
52
- resetPluginRegistryForTests,
53
- } from "../plugins/registry.js";
54
- import type {
55
- InjectionBlock,
56
- Injector,
57
- Plugin,
58
- TurnContext,
59
- } from "../plugins/types.js";
48
+ clearConversations,
49
+ setConversation,
50
+ } from "../daemon/conversation-registry.js";
51
+ import { buildPkbReminder } from "../daemon/pkb-reminder-builder.js";
52
+ import { getPkbRoot } from "../memory/pkb/types.js";
53
+ import type { TurnContext } from "../plugins/types.js";
60
54
  import type { Message } from "../providers/types.js";
55
+ import { getWorkspacePromptPath } from "../util/platform.js";
56
+
57
+ // `makeTurnContext` and the workspace registry seed share this id so the
58
+ // `workspace-context` injector resolves the seeded block for the turn.
59
+ const TEST_CONVERSATION_ID = "conv-test-1";
61
60
 
62
61
  /** A fake TurnContext sufficient for driving `composeInjectorChain`. */
63
62
  function makeTurnContext(): TurnContext {
64
63
  return {
65
64
  requestId: "req-test-1",
66
- conversationId: "conv-test-1",
65
+ conversationId: TEST_CONVERSATION_ID,
67
66
  turnIndex: 0,
68
67
  trust: {
69
68
  sourceChannel: "vellum",
@@ -72,26 +71,60 @@ function makeTurnContext(): TurnContext {
72
71
  };
73
72
  }
74
73
 
75
- /** Build a tiny valid plugin wrapping an array of injectors. */
76
- function wrapInPlugin(name: string, injectors: Injector[]): Plugin {
77
- return {
78
- manifest: {
79
- name,
80
- version: "0.0.1",
81
- },
82
- injectors,
83
- };
74
+ // The pkb-context and pkb-reminder injectors both derive PKB-active state from
75
+ // the workspace itself — `readPkbContext()` returning content behind the
76
+ // personal-memory trust gate — rather than from a threaded flag. Seed the file
77
+ // with exactly the content the test expects so the `<knowledge_base>` block
78
+ // renders deterministically; clear it between tests so suites that assert the
79
+ // PKB injectors are absent stay unaffected.
80
+ function seedPkbContent(content: string): void {
81
+ const root = getPkbRoot();
82
+ mkdirSync(root, { recursive: true });
83
+ writeFileSync(join(root, "INDEX.md"), content, "utf-8");
84
+ }
85
+
86
+ function clearPkbContent(): void {
87
+ rmSync(getPkbRoot(), { recursive: true, force: true });
88
+ }
89
+
90
+ // The now-md injector sources NOW.md from the workspace itself — behind the
91
+ // personal-memory trust gate and the `scratchpadInjection` config toggle —
92
+ // rather than from a threaded option. Seed the file so the injector fires;
93
+ // clear it between tests so suites that assert NOW.md is absent stay
94
+ // unaffected.
95
+ function seedNowScratchpad(content: string): void {
96
+ const nowPath = getWorkspacePromptPath("NOW.md");
97
+ mkdirSync(dirname(nowPath), { recursive: true });
98
+ writeFileSync(nowPath, content, "utf-8");
99
+ }
100
+
101
+ function clearNowScratchpad(): void {
102
+ rmSync(getWorkspacePromptPath("NOW.md"), { force: true });
103
+ }
104
+
105
+ // The workspace-context injector sources its block off the live `Conversation`
106
+ // looked up by `conversationId`. Seed a fake instance carrying a non-dirty
107
+ // workspace cache under the id `makeTurnContext()` uses so the injector emits
108
+ // the block; `clearConversations()` between tests keeps suites that assert the
109
+ // workspace block is absent unaffected.
110
+ function seedWorkspaceContext(text: string): void {
111
+ setConversation(TEST_CONVERSATION_ID, {
112
+ conversationId: TEST_CONVERSATION_ID,
113
+ workingDir: "/sandbox",
114
+ workspaceTopLevelContext: text,
115
+ workspaceTopLevelDirty: false,
116
+ } as never);
84
117
  }
85
118
 
86
119
  describe("injector chain", () => {
87
120
  beforeEach(() => {
88
- resetPluginRegistryForTests();
121
+ clearPkbContent();
122
+ clearNowScratchpad();
123
+ clearConversations();
89
124
  });
90
125
 
91
- test("defaultInjectorsPlugin registers the defaults in the documented order", () => {
92
- registerPlugin(defaultInjectorsPlugin);
93
-
94
- const names = getInjectors().map((i) => i.name);
126
+ test("defaultInjectors lists the defaults in the documented order", () => {
127
+ const names = defaultInjectors.map((i) => i.name);
95
128
  expect(names).toEqual([
96
129
  "disk-pressure-warning",
97
130
  "workspace-context",
@@ -109,10 +142,8 @@ describe("injector chain", () => {
109
142
  ]);
110
143
  });
111
144
 
112
- test("default injector order constants match the registered order values", () => {
113
- registerPlugin(defaultInjectorsPlugin);
114
-
115
- const byName = new Map(getInjectors().map((i) => [i.name, i.order]));
145
+ test("default injector order constants match the listed order values", () => {
146
+ const byName = new Map(defaultInjectors.map((i) => [i.name, i.order]));
116
147
  expect(byName.get("disk-pressure-warning")).toBe(
117
148
  DEFAULT_INJECTOR_ORDER.diskPressureWarning,
118
149
  );
@@ -143,114 +174,38 @@ describe("injector chain", () => {
143
174
  expect(byName.get("thread-focus")).toBe(DEFAULT_INJECTOR_ORDER.threadFocus);
144
175
  });
145
176
 
146
- test("a third-party injector at order 25 slots between unified-turn-context (20) and pkb-context (30)", () => {
147
- registerPlugin(defaultInjectorsPlugin);
148
-
149
- const middleInjector: Injector = {
150
- name: "plugin-25",
151
- order: 25,
152
- async produce() {
153
- return null;
154
- },
155
- };
156
- registerPlugin(wrapInPlugin("third-party", [middleInjector]));
157
-
158
- const names = getInjectors().map((i) => i.name);
159
- expect(names).toEqual([
160
- "disk-pressure-warning", // 5
161
- "workspace-context", // 10
162
- "background-turn", // 15
163
- "unified-turn-context", // 20
164
- "plugin-25", // 25 — slots in
165
- "pkb-context", // 30
166
- "pkb-reminder", // 35
167
- "memory-v2-static", // 38
168
- "now-md", // 40
169
- "active-documents", // 45
170
- "document-comments", // 46
171
- "subagent-status", // 50
172
- "slack-messages", // 60
173
- "thread-focus", // 70
177
+ test("the injector chain sorts the defaults plus memory-v3 by ascending order", () => {
178
+ // The assembled chain merges the defaults with the memory-v3 injector and
179
+ // sorts by `order`, so memory-v3 (order 1000) sits last.
180
+ const chain = getInjectorChain();
181
+ const orders = chain.map((i) => i.order);
182
+ expect(orders).toEqual([...orders].sort((a, b) => a - b));
183
+ expect(chain[chain.length - 1]?.name).toBe("memory-v3-shadow");
184
+ expect(chain.map((i) => i.name)).toEqual([
185
+ ...defaultInjectors.map((i) => i.name),
186
+ "memory-v3-shadow",
174
187
  ]);
175
188
  });
176
189
 
177
190
  test("composeInjectorChain returns empty string when every injector opts out", async () => {
178
- // The default chain is the golden-path: all ten defaults return `null`
179
- // on an empty turn context, so the composed block is an empty string.
180
- registerPlugin(defaultInjectorsPlugin);
181
-
191
+ // The default chain is the golden-path: every default returns `null` on an
192
+ // empty turn context, so the composed block is an empty string.
182
193
  const composed = await composeInjectorChain(makeTurnContext());
183
194
  expect(composed).toBe("");
184
195
  });
185
196
 
186
- test("composeInjectorChain returns empty string when registry is empty", async () => {
187
- // No plugins registered — the chain is a no-op and must return an empty
188
- // string (not throw, not undefined). Callers rely on this to treat the
189
- // chain as purely additive.
190
- const composed = await composeInjectorChain(makeTurnContext());
191
- expect(composed).toBe("");
192
- });
193
-
194
- test("composeInjectorChain concatenates non-null blocks in order with blank-line separators", async () => {
195
- const first: Injector = {
196
- name: "a",
197
- order: 5,
198
- async produce(): Promise<InjectionBlock> {
199
- return { id: "a", text: "BLOCK_A" };
200
- },
201
- };
202
- const second: Injector = {
203
- name: "b",
204
- order: 15,
205
- async produce(): Promise<InjectionBlock> {
206
- return { id: "b", text: "BLOCK_B" };
207
- },
208
- };
209
- const skipped: Injector = {
210
- name: "c",
211
- order: 25,
212
- async produce() {
213
- return null;
214
- },
215
- };
216
- // Register the higher-order one first to prove the chain sorts by `order`
217
- // rather than registration order.
218
- registerPlugin(wrapInPlugin("higher", [second]));
219
- registerPlugin(wrapInPlugin("lower", [first]));
220
- registerPlugin(wrapInPlugin("opts-out", [skipped]));
221
-
222
- const composed = await composeInjectorChain(makeTurnContext());
223
- expect(composed).toBe("BLOCK_A\n\nBLOCK_B");
224
- });
225
-
226
- test("composeInjectorChain skips blocks with empty text", async () => {
227
- const emitEmpty: Injector = {
228
- name: "empty",
229
- order: 10,
230
- async produce(): Promise<InjectionBlock> {
231
- return { id: "empty", text: "" };
232
- },
233
- };
234
- const emitReal: Injector = {
235
- name: "real",
236
- order: 20,
237
- async produce(): Promise<InjectionBlock> {
238
- return { id: "real", text: "CONTENT" };
239
- },
240
- };
241
- registerPlugin(wrapInPlugin("plugin", [emitEmpty, emitReal]));
242
-
243
- const composed = await composeInjectorChain(makeTurnContext());
244
- expect(composed).toBe("CONTENT");
245
- });
197
+ // ── Integration tests ───────────────────────────────────────────────
198
+ //
199
+ // These assertions exercise the real per-turn injection pipeline with
200
+ // the static chain active, verifying that each default injector emits
201
+ // the expected content in the correct position in the final user-tail
202
+ // content.
246
203
 
247
204
  test("applyRuntimeInjections leaves injectorChainBlock undefined when defaults opt out", async () => {
248
- // Golden-path snapshot: with only default injectors (all returning
205
+ // Golden-path snapshot: with the static chain (all defaults returning
249
206
  // `null`), `applyRuntimeInjections` reports no chain output, so the
250
207
  // historical `blocks` shape is preserved byte-for-byte for any
251
- // conversation that doesn't involve third-party injectors.
252
- registerPlugin(defaultInjectorsPlugin);
253
-
208
+ // conversation that doesn't drive a known injector.
254
209
  const runMessages: Message[] = [
255
210
  { role: "user", content: [{ type: "text", text: "hello" }] },
256
211
  ];
@@ -265,68 +220,27 @@ describe("injector chain", () => {
265
220
  expect(result.messages).toEqual(runMessages);
266
221
  });
267
222
 
268
- test("applyRuntimeInjections surfaces third-party injector output on blocks.injectorChainBlock", async () => {
269
- registerPlugin(defaultInjectorsPlugin);
270
- registerPlugin(
271
- wrapInPlugin("third-party-25", [
272
- {
273
- name: "plugin-25",
274
- order: 25,
275
- async produce(): Promise<InjectionBlock> {
276
- return { id: "plugin-25", text: "THIRD_PARTY_BLOCK" };
277
- },
278
- },
279
- ]),
280
- );
281
-
223
+ test("applyRuntimeInjections without turnContext still runs the chain under a synthesized context", async () => {
224
+ // The static chain is the canonical injection path, so
225
+ // `applyRuntimeInjections` must drive it even when the caller doesn't
226
+ // pass a `turnContext`. Call sites that rely on option fields to opt
227
+ // into injections continue to work because the synthesized fallback
228
+ // exposes `injectionInputs` built from `options`.
282
229
  const runMessages: Message[] = [
283
230
  { role: "user", content: [{ type: "text", text: "hi" }] },
284
231
  ];
285
232
 
286
233
  const result = await applyRuntimeInjections(runMessages, {
287
- turnContext: makeTurnContext(),
234
+ unifiedTurnContext: "<turn_context>\nsynthesized\n</turn_context>",
288
235
  });
289
236
 
290
- expect(result.blocks.injectorChainBlock).toBe("THIRD_PARTY_BLOCK");
291
- });
292
-
293
- test("applyRuntimeInjections without turnContext still runs the chain under a synthesized context", async () => {
294
- // Post-G2.1 semantics: the default chain is the canonical injection
295
- // path, so `applyRuntimeInjections` must drive it even when the caller
296
- // doesn't pass a `turnContext`. Test/legacy call sites that rely on
297
- // option fields to opt into injections continue to work because the
298
- // synthesized fallback exposes `injectionInputs` built from `options`.
299
- registerPlugin(defaultInjectorsPlugin);
300
- registerPlugin(
301
- wrapInPlugin("third-party-25", [
302
- {
303
- name: "plugin-25",
304
- order: 25,
305
- async produce(): Promise<InjectionBlock> {
306
- return { id: "plugin-25", text: "THIRD_PARTY_BLOCK" };
307
- },
308
- },
309
- ]),
237
+ // The unified-turn-context injector fires even without a caller-supplied
238
+ // turnContext, proving the chain runs under the synthesized context.
239
+ expect(result.blocks.unifiedTurnContext).toBe(
240
+ "<turn_context>\nsynthesized\n</turn_context>",
310
241
  );
311
-
312
- const runMessages: Message[] = [
313
- { role: "user", content: [{ type: "text", text: "hi" }] },
314
- ];
315
-
316
- const result = await applyRuntimeInjections(runMessages, {});
317
-
318
- // Third-party injector runs even without a caller-supplied turnContext.
319
- expect(result.blocks.injectorChainBlock).toBe("THIRD_PARTY_BLOCK");
320
242
  });
321
243
 
322
- // ── Integration tests ───────────────────────────────────────────────
323
- //
324
- // These assertions exercise the real per-turn injection pipeline with
325
- // the default chain active, verifying that each default injector emits
326
- // the expected content and that a third-party injector registered at a
327
- // fractional `order` slots into the correct position in the final
328
- // user-tail content.
329
-
330
244
  test("golden-path: default chain injects workspace + unified-turn + PKB + NOW + subagent in the correct positions", async () => {
331
245
  // Canonical golden-path conversation state: full mode, non-Slack
332
246
  // channel, workspace context + unified-turn + PKB + NOW + subagent
@@ -335,14 +249,21 @@ describe("injector chain", () => {
335
249
  // [workspace] ← prepend order 10 (topmost)
336
250
  // [unified-turn] ← prepend order 20
337
251
  // [now-md] ← after-memory-prefix order 40 (highest order, closest to memory)
338
- // [pkb-reminder] ← after-memory-prefix order 35 (skipped when pkbActive=false)
252
+ // [pkb-reminder] ← after-memory-prefix order 35
339
253
  // [pkb-context] ← after-memory-prefix order 30
340
254
  // [user text]
341
255
  // [subagent] ← append order 50
342
256
  //
343
257
  // No memory prefix blocks in this scenario, so after-memory-prefix
344
- // lands right at the head of the user-text cluster.
345
- registerPlugin(defaultInjectorsPlugin);
258
+ // lands right at the head of the user-text cluster. The pkb-context and
259
+ // pkb-reminder injectors both fire off the seeded PKB content under the
260
+ // guardian trust on `makeTurnContext()` — pkb-context renders the seeded
261
+ // `<knowledge_base>` body, pkb-reminder the flat `<system_reminder>`
262
+ // (no graph handle is registered, so it has no search hints).
263
+ const pkbContent = "essentials of the project";
264
+ seedPkbContent(pkbContent);
265
+ const nowContent = "Current focus: shipping G2.1";
266
+ seedNowScratchpad(nowContent);
346
267
 
347
268
  const runMessages: Message[] = [
348
269
  { role: "user", content: [{ type: "text", text: "What next?" }] },
@@ -352,18 +273,13 @@ describe("injector chain", () => {
352
273
  "<workspace>\nRoot: /sandbox\nDirectories: src, lib\n</workspace>";
353
274
  const unifiedTurn =
354
275
  "<turn_context>\ncurrent_time: 2026-04-22\ninterface: macos\n</turn_context>";
355
- const pkbContent = "essentials of the project";
356
- const nowContent = "Current focus: shipping G2.1";
357
276
  const subagentBlock =
358
277
  '<active_subagents>\n- [running] "worker" (sub-1) | elapsed: 5s\n</active_subagents>';
359
278
 
279
+ seedWorkspaceContext(workspaceText);
360
280
  const result = await applyRuntimeInjections(runMessages, {
361
281
  turnContext: makeTurnContext(),
362
- workspaceTopLevelContext: workspaceText,
363
282
  unifiedTurnContext: unifiedTurn,
364
- pkbContext: pkbContent,
365
- pkbActive: false, // disable reminder-branch to keep the snapshot small
366
- nowScratchpad: nowContent,
367
283
  subagentStatusBlock: subagentBlock,
368
284
  });
369
285
 
@@ -378,14 +294,17 @@ describe("injector chain", () => {
378
294
  // placement says it does.
379
295
  expect(texts[0]).toBe(workspaceText); // prepend order 10
380
296
  expect(texts[1]).toBe(unifiedTurn); // prepend order 20
381
- // NOW and PKB are both after-memory-prefix; NOW runs later so sits above PKB.
297
+ // NOW, pkb-reminder and pkb-context are all after-memory-prefix; higher
298
+ // order splices closer to the memory prefix, so NOW sits above the
299
+ // reminder, which sits above the knowledge_base.
382
300
  expect(texts[2]).toBe(
383
301
  `<NOW.md Always keep this up to date; keep under 10 lines>\n${nowContent}\n</NOW.md>`,
384
302
  );
385
- expect(texts[3]).toBe(`<knowledge_base>\n${pkbContent}\n</knowledge_base>`);
386
- expect(texts[4]).toBe("What next?"); // user's typed text
387
- expect(texts[5]).toBe(subagentBlock); // append order 50
388
- expect(texts).toHaveLength(6);
303
+ expect(texts[3]).toBe(buildPkbReminder([])); // pkb-reminder order 35
304
+ expect(texts[4]).toBe(`<knowledge_base>\n${pkbContent}\n</knowledge_base>`);
305
+ expect(texts[5]).toBe("What next?"); // user's typed text
306
+ expect(texts[6]).toBe(subagentBlock); // append order 50
307
+ expect(texts).toHaveLength(7);
389
308
 
390
309
  // Block metadata captures for DB persistence — one field per default
391
310
  // injector whose output the loader rehydrates from message metadata.
@@ -399,59 +318,6 @@ describe("injector chain", () => {
399
318
  );
400
319
  });
401
320
 
402
- test("third-party prepend injector at order 15 lands between workspace (10) and unified-turn-context (20) in the final message", async () => {
403
- // Proves the extensibility contract end-to-end: a plugin-registered
404
- // injector at `order: 15` with `placement: "prepend-user-tail"` slots
405
- // between the workspace prepend (order 10) and the unified-turn
406
- // prepend (order 20). Because descending-order application for
407
- // prepends puts the lowest-`order` injector topmost, workspace ends
408
- // up on top, then plugin@15, then unified-turn.
409
- registerPlugin(defaultInjectorsPlugin);
410
- registerPlugin(
411
- wrapInPlugin("third-party-15-prepend", [
412
- {
413
- name: "plugin-15",
414
- order: 15, // between workspace (10) and unified-turn (20)
415
- async produce(): Promise<InjectionBlock> {
416
- return {
417
- id: "plugin-15",
418
- text: "<plugin_block_15/>",
419
- placement: "prepend-user-tail",
420
- };
421
- },
422
- },
423
- ]),
424
- );
425
-
426
- const runMessages: Message[] = [
427
- { role: "user", content: [{ type: "text", text: "hi" }] },
428
- ];
429
-
430
- const workspaceText = "<workspace>\nRoot: /sandbox\n</workspace>";
431
- const unifiedTurn =
432
- "<turn_context>\ncurrent_time: 2026-04-22\n</turn_context>";
433
-
434
- const result = await applyRuntimeInjections(runMessages, {
435
- turnContext: makeTurnContext(),
436
- workspaceTopLevelContext: workspaceText,
437
- unifiedTurnContext: unifiedTurn,
438
- });
439
-
440
- const tail = result.messages[result.messages.length - 1];
441
- expect(tail.role).toBe("user");
442
- const texts = tail.content
443
- .filter((b): b is { type: "text"; text: string } => b.type === "text")
444
- .map((b) => b.text);
445
-
446
- // Descending-order application for prepends puts the lowest-`order`
447
- // injector topmost, so order 10 (workspace) ends up on top, then
448
- // plugin@15 below it, then unified-turn (order 20) below that.
449
- expect(texts[0]).toBe(workspaceText);
450
- expect(texts[1]).toBe("<plugin_block_15/>");
451
- expect(texts[2]).toBe(unifiedTurn);
452
- expect(texts[3]).toBe("hi");
453
- });
454
-
455
321
  test("slack-messages injector replaces runMessages when a chronological transcript is provided", async () => {
456
322
  // End-to-end verification for the `replace-run-messages` placement:
457
323
  // a Slack channel turn with a pre-rendered chronological transcript
@@ -459,8 +325,6 @@ describe("injector chain", () => {
459
325
  // after-memory/append placements run. Memory-prefix blocks from the
460
326
  // original tail are re-prepended onto the new tail so PKB / NOW
461
327
  // splices still find them.
462
- registerPlugin(defaultInjectorsPlugin);
463
-
464
328
  const originalRun: Message[] = [
465
329
  {
466
330
  role: "user",
@@ -486,8 +350,11 @@ describe("injector chain", () => {
486
350
  },
487
351
  ];
488
352
 
489
- const result = await applyRuntimeInjections(originalRun, {
490
- turnContext: makeTurnContext(),
353
+ setConversation(TEST_CONVERSATION_ID, {
354
+ conversationId: TEST_CONVERSATION_ID,
355
+ workingDir: "/sandbox",
356
+ workspaceTopLevelContext: "",
357
+ workspaceTopLevelDirty: false,
491
358
  channelCapabilities: {
492
359
  channel: "slack",
493
360
  dashboardCapable: false,
@@ -495,6 +362,9 @@ describe("injector chain", () => {
495
362
  supportsVoiceInput: false,
496
363
  chatType: "channel",
497
364
  },
365
+ } as never);
366
+ const result = await applyRuntimeInjections(originalRun, {
367
+ turnContext: makeTurnContext(),
498
368
  slackChronologicalMessages: slackTranscript,
499
369
  });
500
370
 
@@ -522,8 +392,6 @@ describe("injector chain", () => {
522
392
  // opts out in minimal mode, so the tail should carry only the turn
523
393
  // context prepend plus any non-injector hardcoded content (none
524
394
  // here).
525
- registerPlugin(defaultInjectorsPlugin);
526
-
527
395
  const result = await applyRuntimeInjections(
528
396
  [
529
397
  {
@@ -534,11 +402,7 @@ describe("injector chain", () => {
534
402
  {
535
403
  turnContext: makeTurnContext(),
536
404
  mode: "minimal",
537
- workspaceTopLevelContext: "<workspace>...</workspace>",
538
405
  unifiedTurnContext: "<turn_context>...</turn_context>",
539
- pkbContext: "kbody",
540
- pkbActive: true,
541
- nowScratchpad: "nowbody",
542
406
  subagentStatusBlock: "<active_subagents>...</active_subagents>",
543
407
  },
544
408
  );