@vellumai/assistant 0.8.7 → 0.8.8

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 (387) hide show
  1. package/Dockerfile +20 -4
  2. package/docker-entrypoint.sh +4 -2
  3. package/docker-init-apt-root.sh +3 -1
  4. package/docker-kata-apt-env.sh +3 -1
  5. package/docker-kata-runtime-family.sh +12 -0
  6. package/docs/architecture/memory.md +1 -1
  7. package/docs/plugins.md +75 -79
  8. package/examples/plugins/echo/README.md +6 -12
  9. package/examples/plugins/echo/register.ts +0 -41
  10. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
  11. package/openapi.yaml +3381 -348
  12. package/package.json +1 -1
  13. package/scripts/generate-openapi.ts +68 -41
  14. package/src/__tests__/agent-loop-exit-reason.test.ts +34 -39
  15. package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
  16. package/src/__tests__/agent-loop.test.ts +37 -87
  17. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  18. package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
  19. package/src/__tests__/annotate-risk-options.test.ts +2 -3
  20. package/src/__tests__/anthropic-provider.test.ts +95 -2
  21. package/src/__tests__/assistant-event-hub.test.ts +25 -0
  22. package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
  23. package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
  24. package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
  25. package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
  26. package/src/__tests__/btw-routes.test.ts +62 -3
  27. package/src/__tests__/build-persisted-content.test.ts +184 -0
  28. package/src/__tests__/catalog-files.test.ts +1 -1
  29. package/src/__tests__/clawhub-files.test.ts +1 -1
  30. package/src/__tests__/compaction-pipeline.test.ts +1 -1
  31. package/src/__tests__/compaction.benchmark.test.ts +0 -30
  32. package/src/__tests__/config-watcher.test.ts +1 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
  34. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -2
  35. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -4
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +313 -1136
  37. package/src/__tests__/conversation-agent-loop.test.ts +596 -1616
  38. package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
  39. package/src/__tests__/conversation-history-web-search.test.ts +11 -1
  40. package/src/__tests__/conversation-pairing.test.ts +4 -31
  41. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
  42. package/src/__tests__/conversation-provider-retry-repair.test.ts +26 -5
  43. package/src/__tests__/conversation-queue.test.ts +2 -0
  44. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
  45. package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
  46. package/src/__tests__/conversation-runtime-assembly.test.ts +170 -229
  47. package/src/__tests__/conversation-runtime-workspace.test.ts +3 -24
  48. package/src/__tests__/conversation-slash-commands.test.ts +8 -42
  49. package/src/__tests__/conversation-slash-queue.test.ts +6 -1
  50. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
  51. package/src/__tests__/conversation-sync-tags.test.ts +27 -15
  52. package/src/__tests__/conversation-title-service.test.ts +135 -2
  53. package/src/__tests__/conversation-workspace-injection.test.ts +6 -1
  54. package/src/__tests__/cross-provider-web-search.test.ts +214 -1
  55. package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
  56. package/src/__tests__/dm-persistence.test.ts +5 -1
  57. package/src/__tests__/empty-response-hook.test.ts +304 -0
  58. package/src/__tests__/feature-flag-test-helpers.ts +2 -2
  59. package/src/__tests__/gemini-image-service.test.ts +13 -0
  60. package/src/__tests__/helpers/mock-provider.ts +110 -0
  61. package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
  62. package/src/__tests__/history-repair-hook.test.ts +1 -0
  63. package/src/__tests__/identity-intro-cache.test.ts +12 -100
  64. package/src/__tests__/identity-routes.test.ts +248 -7
  65. package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
  66. package/src/__tests__/injector-background-turn.test.ts +2 -8
  67. package/src/__tests__/injector-chain.test.ts +106 -270
  68. package/src/__tests__/injector-disk-pressure.test.ts +3 -12
  69. package/src/__tests__/injector-document-comments.test.ts +2 -2
  70. package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
  71. package/src/__tests__/injector-v3-suppression.test.ts +31 -37
  72. package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
  73. package/src/__tests__/list-messages-page-latest.test.ts +60 -0
  74. package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
  75. package/src/__tests__/llm-usage-store.test.ts +223 -1
  76. package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
  77. package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
  78. package/src/__tests__/native-web-search.test.ts +191 -0
  79. package/src/__tests__/onboarding-template-contract.test.ts +2 -0
  80. package/src/__tests__/openai-image-service.test.ts +17 -0
  81. package/src/__tests__/openai-provider.test.ts +31 -1
  82. package/src/__tests__/persist-unsendable-image.test.ts +215 -0
  83. package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
  84. package/src/__tests__/pipeline-runner.test.ts +29 -39
  85. package/src/__tests__/pkb-autoinject.test.ts +2 -5
  86. package/src/__tests__/plugin-bootstrap.test.ts +13 -28
  87. package/src/__tests__/plugin-registry.test.ts +0 -27
  88. package/src/__tests__/plugin-types.test.ts +2 -125
  89. package/src/__tests__/process-message-display-content.test.ts +6 -2
  90. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
  91. package/src/__tests__/resolve-trust-class.test.ts +4 -4
  92. package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
  93. package/src/__tests__/schedule-routes.test.ts +603 -2
  94. package/src/__tests__/schedule-store.test.ts +41 -0
  95. package/src/__tests__/schedule-tools.test.ts +35 -0
  96. package/src/__tests__/server-history-render.test.ts +314 -1
  97. package/src/__tests__/skillssh-files.test.ts +1 -1
  98. package/src/__tests__/system-prompt.test.ts +20 -0
  99. package/src/__tests__/task-scheduler.test.ts +162 -1
  100. package/src/__tests__/terminal-tools.test.ts +6 -1
  101. package/src/__tests__/title-generate-hook.test.ts +319 -0
  102. package/src/__tests__/tool-error-hook.test.ts +278 -0
  103. package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
  104. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  105. package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
  106. package/src/__tests__/tool-result-truncation.test.ts +0 -2
  107. package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
  108. package/src/__tests__/ui-work-result-surface.test.ts +159 -0
  109. package/src/__tests__/usage-routes.test.ts +285 -1
  110. package/src/__tests__/user-plugin-loader.test.ts +2 -2
  111. package/src/__tests__/voice-session-bridge.test.ts +6 -3
  112. package/src/__tests__/web-search-backend-failure.test.ts +166 -0
  113. package/src/agent/loop.ts +346 -442
  114. package/src/api/events/assistant-thinking-delta.ts +33 -0
  115. package/src/api/events/tool-output-chunk.ts +45 -0
  116. package/src/api/events/tool-use-preview-start.ts +32 -0
  117. package/src/api/events/trace-event.ts +69 -0
  118. package/src/api/index.ts +48 -13
  119. package/src/api/responses/conversation-message.ts +368 -0
  120. package/src/avatar/__tests__/avatar-store.test.ts +34 -29
  121. package/src/cli/commands/__tests__/notifications.test.ts +58 -14
  122. package/src/cli/commands/notifications.ts +112 -60
  123. package/src/config/assistant-feature-flags.ts +22 -11
  124. package/src/config/bundled-skills/app-builder/SKILL.md +3 -20
  125. package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
  126. package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
  127. package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
  128. package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
  129. package/src/config/bundled-skills/document-editor/SKILL.md +1 -1
  130. package/src/config/bundled-skills/messaging/SKILL.md +0 -7
  131. package/src/config/feature-flag-cache.ts +3 -3
  132. package/src/config/feature-flag-registry.json +35 -3
  133. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
  134. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
  135. package/src/config/schemas/llm.ts +1 -0
  136. package/src/config/schemas/memory-v2.ts +8 -0
  137. package/src/config/schemas/memory-v3.ts +8 -0
  138. package/src/config/schemas/platform.ts +8 -0
  139. package/src/config/seed-inference-profiles.ts +2 -2
  140. package/src/config/skills.ts +13 -0
  141. package/src/context/compactor.ts +1 -1
  142. package/src/context/strip-injections.ts +122 -0
  143. package/src/context/token-estimator.ts +23 -0
  144. package/src/context/tool-result-truncation.ts +0 -23
  145. package/src/context/window-manager.ts +3 -6
  146. package/src/credential-execution/executable-discovery.ts +16 -0
  147. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
  148. package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
  149. package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
  150. package/src/daemon/assistant-attachments.ts +1 -1
  151. package/src/daemon/config-watcher.ts +2 -2
  152. package/src/daemon/context-overflow-reducer.ts +0 -1
  153. package/src/daemon/conversation-agent-loop-handlers.ts +605 -153
  154. package/src/daemon/conversation-agent-loop.ts +281 -760
  155. package/src/daemon/conversation-history.ts +5 -4
  156. package/src/daemon/conversation-lifecycle.ts +3 -4
  157. package/src/daemon/conversation-messaging.ts +7 -6
  158. package/src/daemon/conversation-process.ts +11 -16
  159. package/src/daemon/conversation-runtime-assembly.ts +130 -347
  160. package/src/daemon/conversation-slash.ts +6 -25
  161. package/src/daemon/conversation-surfaces.ts +222 -4
  162. package/src/daemon/conversation-tool-setup.ts +2 -29
  163. package/src/daemon/conversation.ts +32 -14
  164. package/src/daemon/external-plugins-bootstrap.ts +9 -10
  165. package/src/daemon/handlers/config-a2a.ts +51 -36
  166. package/src/daemon/handlers/config-slack-channel.ts +20 -14
  167. package/src/daemon/handlers/config-telegram.ts +16 -2
  168. package/src/daemon/handlers/shared.ts +156 -84
  169. package/src/daemon/handlers/skills.ts +39 -10
  170. package/src/daemon/lifecycle.ts +4 -0
  171. package/src/daemon/message-types/apps.ts +1 -29
  172. package/src/daemon/message-types/messages.ts +9 -57
  173. package/src/daemon/message-types/skills.ts +2 -0
  174. package/src/daemon/message-types/surfaces.ts +136 -3
  175. package/src/daemon/now-scratchpad.ts +21 -0
  176. package/src/daemon/orphan-reaper.test.ts +210 -0
  177. package/src/daemon/orphan-reaper.ts +240 -0
  178. package/src/daemon/persist-unsendable-image.ts +117 -0
  179. package/src/daemon/process-message.ts +1 -3
  180. package/src/daemon/trace-emitter.ts +6 -4
  181. package/src/daemon/trust-context.ts +19 -0
  182. package/src/daemon/wake-target-adapter.ts +3 -1
  183. package/src/home/home-greeting-cache.ts +24 -1
  184. package/src/ipc/gateway-client.test.ts +2 -2
  185. package/src/ipc/gateway-client.ts +3 -3
  186. package/src/media/gemini-image-service.ts +15 -0
  187. package/src/media/openai-image-service.ts +14 -0
  188. package/src/media/types.ts +34 -0
  189. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
  190. package/src/memory/auth-fallback-events-store.ts +94 -0
  191. package/src/memory/conversation-title-service.ts +65 -41
  192. package/src/memory/db-init.ts +4 -0
  193. package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
  194. package/src/memory/graph/conversation-graph-memory.ts +65 -0
  195. package/src/memory/jobs-store.ts +33 -0
  196. package/src/memory/jobs-worker.ts +31 -4
  197. package/src/memory/llm-usage-store.ts +224 -50
  198. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
  199. package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
  200. package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
  201. package/src/memory/migrations/index.ts +2 -0
  202. package/src/memory/pkb/autoinject.ts +61 -0
  203. package/src/memory/pkb/context.ts +50 -0
  204. package/src/memory/pkb/types.ts +14 -0
  205. package/src/memory/schedule-attribution-sql.ts +104 -0
  206. package/src/memory/schema/infrastructure.ts +16 -0
  207. package/src/memory/usage-grouped-buckets.ts +6 -1
  208. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -1
  209. package/src/memory/v2/consolidation-job.ts +1 -1
  210. package/src/memory/v3/__tests__/health.test.ts +16 -0
  211. package/src/memory/v3/__tests__/orchestrate.test.ts +45 -9
  212. package/src/memory/v3/__tests__/provider-blocks.test.ts +13 -0
  213. package/src/memory/v3/__tests__/router.test.ts +101 -29
  214. package/src/memory/v3/__tests__/selector.test.ts +93 -27
  215. package/src/memory/v3/__tests__/shadow-plugin.test.ts +23 -5
  216. package/src/memory/v3/health.ts +0 -0
  217. package/src/memory/v3/llm-retry.ts +32 -0
  218. package/src/memory/v3/orchestrate.ts +26 -14
  219. package/src/memory/v3/provider-blocks.ts +15 -5
  220. package/src/memory/v3/router.ts +48 -42
  221. package/src/memory/v3/selector.ts +57 -42
  222. package/src/memory/v3/shadow-plugin.ts +47 -15
  223. package/src/memory/v3/types.ts +8 -0
  224. package/src/notifications/conversation-pairing.ts +8 -15
  225. package/src/notifications/decision-engine.ts +6 -3
  226. package/src/notifications/home-feed-side-effect.ts +12 -1
  227. package/src/permissions/prompter.ts +4 -0
  228. package/src/plugin-api/constants.ts +4 -0
  229. package/src/plugin-api/index.ts +8 -1
  230. package/src/plugin-api/types.ts +151 -1
  231. package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
  232. package/src/plugins/defaults/empty-response/register.ts +8 -13
  233. package/src/plugins/defaults/index.ts +1 -15
  234. package/src/plugins/defaults/injectors/register.ts +243 -74
  235. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +91 -0
  236. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
  237. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
  238. package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
  239. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
  240. package/src/plugins/defaults/title-generate/package.json +1 -1
  241. package/src/plugins/defaults/title-generate/register.ts +18 -18
  242. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
  243. package/src/plugins/defaults/tool-error/package.json +1 -1
  244. package/src/plugins/defaults/tool-error/register.ts +9 -21
  245. package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
  246. package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
  247. package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
  248. package/src/plugins/pipeline.ts +6 -18
  249. package/src/plugins/registry.ts +8 -25
  250. package/src/plugins/types.ts +43 -474
  251. package/src/proactive-artifact/aux-message-injector.ts +3 -3
  252. package/src/proactive-artifact/job.test.ts +7 -12
  253. package/src/prompts/__tests__/system-prompt.test.ts +36 -0
  254. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +62 -0
  255. package/src/prompts/templates/BOOTSTRAP.md +2 -2
  256. package/src/prompts/templates/system-sections.ts +15 -0
  257. package/src/providers/anthropic/client.ts +37 -29
  258. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
  259. package/src/providers/openai/chat-completions-provider.ts +44 -0
  260. package/src/providers/openrouter/client.ts +1 -0
  261. package/src/providers/placeholder-sentinels.ts +35 -0
  262. package/src/runtime/__tests__/agent-wake.test.ts +5 -1
  263. package/src/runtime/agent-wake.ts +2 -2
  264. package/src/runtime/assistant-event-hub.ts +36 -6
  265. package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
  266. package/src/runtime/http-router.ts +16 -21
  267. package/src/runtime/http-types.ts +16 -70
  268. package/src/runtime/pending-interactions.ts +1 -0
  269. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
  270. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
  271. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
  272. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  273. package/src/runtime/routes/app-management-routes.ts +6 -117
  274. package/src/runtime/routes/app-routes.ts +13 -15
  275. package/src/runtime/routes/attachment-routes.ts +26 -15
  276. package/src/runtime/routes/avatar-routes.ts +26 -0
  277. package/src/runtime/routes/btw-routes.ts +29 -23
  278. package/src/runtime/routes/consolidation-routes.ts +120 -20
  279. package/src/runtime/routes/conversation-query-routes.ts +2 -0
  280. package/src/runtime/routes/conversation-routes.ts +358 -184
  281. package/src/runtime/routes/documents-routes.ts +4 -0
  282. package/src/runtime/routes/domain-routes.ts +51 -37
  283. package/src/runtime/routes/epoch-millis-range.ts +34 -0
  284. package/src/runtime/routes/events-routes.ts +28 -34
  285. package/src/runtime/routes/gateway-log-routes.ts +26 -4
  286. package/src/runtime/routes/heartbeat-routes.ts +32 -12
  287. package/src/runtime/routes/identity-intro-cache.ts +11 -34
  288. package/src/runtime/routes/identity-routes.ts +208 -17
  289. package/src/runtime/routes/image-generation-routes.ts +40 -2
  290. package/src/runtime/routes/index.ts +2 -0
  291. package/src/runtime/routes/integrations/a2a.ts +12 -10
  292. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
  293. package/src/runtime/routes/integrations/slack/channel.ts +4 -0
  294. package/src/runtime/routes/integrations/slack/share.ts +27 -6
  295. package/src/runtime/routes/integrations/telegram.ts +6 -0
  296. package/src/runtime/routes/integrations/twilio.ts +42 -0
  297. package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
  298. package/src/runtime/routes/log-export-routes.ts +8 -0
  299. package/src/runtime/routes/memory-v2-routes.ts +15 -8
  300. package/src/runtime/routes/memory-v3-routes.ts +50 -28
  301. package/src/runtime/routes/oauth-apps.ts +66 -12
  302. package/src/runtime/routes/oauth-providers.ts +44 -5
  303. package/src/runtime/routes/platform-routes.ts +81 -5
  304. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
  305. package/src/runtime/routes/playground/force-compact.ts +1 -1
  306. package/src/runtime/routes/rename-conversation-routes.ts +5 -0
  307. package/src/runtime/routes/schedule-routes.ts +152 -42
  308. package/src/runtime/routes/secret-routes.ts +14 -2
  309. package/src/runtime/routes/skills-routes.ts +43 -14
  310. package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
  311. package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
  312. package/src/runtime/routes/trust-rules-routes.ts +26 -2
  313. package/src/runtime/routes/tts-routes.ts +35 -0
  314. package/src/runtime/routes/types.ts +66 -8
  315. package/src/runtime/routes/usage-routes.ts +47 -39
  316. package/src/runtime/routes/webhook-routes.ts +41 -2
  317. package/src/runtime/routes/workspace-routes.ts +4 -0
  318. package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
  319. package/src/runtime/services/analyze-conversation.ts +2 -2
  320. package/src/schedule/schedule-store.ts +20 -1
  321. package/src/schedule/schedule-usage-store.ts +83 -0
  322. package/src/schedule/scheduler.ts +12 -5
  323. package/src/skills/catalog-files.ts +2 -2
  324. package/src/skills/catalog-install.ts +3 -0
  325. package/src/skills/categories-cache.ts +118 -0
  326. package/src/skills/clawhub-files.ts +1 -2
  327. package/src/skills/skillssh-files.ts +1 -2
  328. package/src/telemetry/types.ts +29 -1
  329. package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
  330. package/src/telemetry/usage-telemetry-reporter.ts +57 -2
  331. package/src/tools/executor.ts +1 -53
  332. package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
  333. package/src/tools/network/__tests__/web-search.test.ts +11 -3
  334. package/src/tools/network/web-search-error.test.ts +248 -0
  335. package/src/tools/network/web-search-error.ts +267 -0
  336. package/src/tools/network/web-search.ts +207 -48
  337. package/src/tools/schedule/create.ts +2 -0
  338. package/src/tools/terminal/safe-env.ts +10 -1
  339. package/src/tools/ui-surface/definitions.ts +9 -1
  340. package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
  341. package/src/tts/provider-catalog.ts +76 -1
  342. package/src/util/mutex.ts +47 -0
  343. package/src/workspace/git-service.ts +1 -42
  344. package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
  345. package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
  346. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +93 -0
  347. package/src/workspace/migrations/registry.ts +6 -0
  348. package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
  349. package/src/__tests__/empty-response-pipeline.test.ts +0 -423
  350. package/src/__tests__/llm-call-pipeline.test.ts +0 -287
  351. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
  352. package/src/__tests__/persistence-pipeline.test.ts +0 -503
  353. package/src/__tests__/title-generate-pipeline.test.ts +0 -211
  354. package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
  355. package/src/__tests__/tool-error-pipeline.test.ts +0 -241
  356. package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
  357. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
  358. package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
  359. package/src/gallery/default-gallery.ts +0 -1359
  360. package/src/gallery/gallery-manifest.ts +0 -28
  361. package/src/home/feature-gate.ts +0 -22
  362. package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
  363. package/src/plugins/defaults/empty-response/terminal.ts +0 -106
  364. package/src/plugins/defaults/injectors/package.json +0 -15
  365. package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
  366. package/src/plugins/defaults/llm-call/package.json +0 -15
  367. package/src/plugins/defaults/llm-call/register.ts +0 -45
  368. package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
  369. package/src/plugins/defaults/memory-retrieval/package.json +0 -15
  370. package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
  371. package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
  372. package/src/plugins/defaults/persistence/package.json +0 -15
  373. package/src/plugins/defaults/persistence/register.ts +0 -38
  374. package/src/plugins/defaults/persistence/terminal.ts +0 -83
  375. package/src/plugins/defaults/title-generate/terminal.ts +0 -31
  376. package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
  377. package/src/plugins/defaults/token-estimate/package.json +0 -15
  378. package/src/plugins/defaults/token-estimate/register.ts +0 -34
  379. package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
  380. package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
  381. package/src/plugins/defaults/tool-error/terminal.ts +0 -47
  382. package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
  383. package/src/plugins/defaults/tool-execute/package.json +0 -15
  384. package/src/plugins/defaults/tool-execute/register.ts +0 -49
  385. package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
  386. package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
  387. package/src/skills/category-inference.ts +0 -111
@@ -1,4 +1,6 @@
1
- import { beforeEach, describe, expect, mock, test } from "bun:test";
1
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
2
4
 
3
5
  // This test exercises v1 PKB injection. `config.memory.v2.enabled` (default
4
6
  // `true`) makes the PKB injector go silent — force it off here so the v1
@@ -44,7 +46,6 @@ import {
44
46
  assembleSlackChronologicalMessages,
45
47
  buildSubagentStatusBlock,
46
48
  buildUnifiedTurnContextBlock,
47
- findLastInjectedNowContent,
48
49
  getSlackCompactionWatermarkForPrefix,
49
50
  injectChannelCapabilityContext,
50
51
  injectChannelCommandContext,
@@ -60,29 +61,47 @@ import {
60
61
  } from "../daemon/conversation-runtime-assembly.js";
61
62
  import { buildPkbReminder } from "../daemon/pkb-reminder-builder.js";
62
63
  import type { MessageRow } from "../memory/conversation-crud.js";
64
+ import { ConversationGraphMemory } from "../memory/graph/conversation-graph-memory.js";
65
+ import { getPkbRoot } from "../memory/pkb/types.js";
63
66
  import {
64
67
  type SlackMessageMetadata,
65
68
  writeSlackMetadata,
66
69
  } from "../messaging/providers/slack/message-metadata.js";
67
70
  import { parentAlias } from "../messaging/providers/slack/render-transcript.js";
68
- import { defaultInjectorsPlugin } from "../plugins/defaults/injectors/register.js";
69
- import {
70
- registerPlugin,
71
- resetPluginRegistryForTests,
72
- } from "../plugins/registry.js";
73
71
  import type { Message } from "../providers/types.js";
74
72
  import { wrapUntrustedContent } from "../security/untrusted-content.js";
75
73
  import type { SubagentState } from "../subagent/types.js";
74
+ import { getWorkspacePromptPath } from "../util/platform.js";
75
+
76
+ // The pkb-reminder injector derives PKB-active state from the workspace itself
77
+ // — `readPkbContext()` returning content behind the personal-memory trust gate
78
+ // — rather than from a threaded flag. Tests that need the reminder to fire seed
79
+ // a default auto-injected PKB file under the workspace pkb root; tests that
80
+ // need it suppressed leave the pkb dir empty.
81
+ function seedPkbContent(): void {
82
+ const root = getPkbRoot();
83
+ mkdirSync(root, { recursive: true });
84
+ writeFileSync(join(root, "INDEX.md"), "workspace knowledge index", "utf-8");
85
+ }
76
86
 
77
- // `applyRuntimeInjections` is now driven by the default injector chain
78
- // (PR G2.1). The default-injectors plugin must be registered for the chain
79
- // to emit workspace, PKB, NOW.md, subagent, Slack, and thread-focus blocks.
80
- // Each test gets a clean registry so a test that registers its own plugin
81
- // doesn't leak into the next one.
82
- beforeEach(() => {
83
- resetPluginRegistryForTests();
84
- registerPlugin(defaultInjectorsPlugin);
85
- });
87
+ function clearPkbContent(): void {
88
+ rmSync(getPkbRoot(), { recursive: true, force: true });
89
+ }
90
+
91
+ // The now-md injector derives NOW.md state from the workspace itself —
92
+ // `readNowScratchpad()` returning content behind the personal-memory trust gate
93
+ // and the `scratchpadInjection` config toggle — rather than from a threaded
94
+ // option. Seed the file so the injector fires; clear it so suites that assert
95
+ // NOW.md is absent stay unaffected.
96
+ function seedNowScratchpad(content: string): void {
97
+ const nowPath = getWorkspacePromptPath("NOW.md");
98
+ mkdirSync(dirname(nowPath), { recursive: true });
99
+ writeFileSync(nowPath, content, "utf-8");
100
+ }
101
+
102
+ function clearNowScratchpad(): void {
103
+ rmSync(getWorkspacePromptPath("NOW.md"), { force: true });
104
+ }
86
105
 
87
106
  // ---------------------------------------------------------------------------
88
107
  // resolveChannelCapabilities
@@ -748,12 +767,33 @@ describe("applyRuntimeInjections — injection mode", () => {
748
767
  } as ChannelCapabilities,
749
768
  unifiedTurnContext:
750
769
  "<turn_context>\ncurrent_time: 2026-03-04 (Tuesday) 12:00:00 +00:00 (UTC)\ninterface: telegram\n</turn_context>",
751
- nowScratchpad: "Current focus: shipping PR 3",
752
- pkbContext: "essentials content here",
753
- pkbActive: true,
754
770
  isNonInteractive: true,
771
+ // Guardian trust so the personal-memory gate admits the actor regardless
772
+ // of the telegram channel capabilities under test, letting the reminder
773
+ // gate hinge purely on PKB content presence.
774
+ turnContext: {
775
+ requestId: "injection-mode-req",
776
+ conversationId: "injection-mode-conv",
777
+ turnIndex: 0,
778
+ trust: {
779
+ sourceChannel: "vellum" as const,
780
+ trustClass: "guardian" as const,
781
+ },
782
+ },
755
783
  };
756
784
 
785
+ // The reminder fires only when the workspace has PKB content, and the now-md
786
+ // injector only when NOW.md has content; seed both so the full-mode cases
787
+ // exercise the active branch.
788
+ beforeEach(() => {
789
+ seedPkbContent();
790
+ seedNowScratchpad("Current focus: shipping PR 3");
791
+ });
792
+ afterEach(() => {
793
+ clearPkbContent();
794
+ clearNowScratchpad();
795
+ });
796
+
757
797
  test("full mode (default) includes all injections", async () => {
758
798
  const { messages: result } = await applyRuntimeInjections(
759
799
  baseMessages,
@@ -860,12 +900,10 @@ describe("applyRuntimeInjections — injection mode", () => {
860
900
  });
861
901
  });
862
902
 
863
- // The standalone `injectNowScratchpad` helper was removed in G2.1. The
864
- // now-md default injector (registered by `defaultInjectorsPlugin`) emits
865
- // the `<NOW.md>` block as an `after-memory-prefix` placement during
866
- // `applyRuntimeInjections`. The suites below (`applyRuntimeInjections with
867
- // nowScratchpad` and the injection-mode tests) cover that behaviour
868
- // end-to-end.
903
+ // The now-md default injector emits the `<NOW.md>` block as an
904
+ // `after-memory-prefix` placement during `applyRuntimeInjections`. The suites
905
+ // below (`applyRuntimeInjections with nowScratchpad` and the injection-mode
906
+ // tests) cover that behaviour end-to-end.
869
907
 
870
908
  // ---------------------------------------------------------------------------
871
909
  // stripNowScratchpad
@@ -1192,10 +1230,14 @@ describe("applyRuntimeInjections with nowScratchpad", () => {
1192
1230
  },
1193
1231
  ];
1194
1232
 
1195
- test("injects NOW.md block when provided", async () => {
1196
- const { messages: result } = await applyRuntimeInjections(baseMessages, {
1197
- nowScratchpad: "Current focus: fix the bug",
1198
- });
1233
+ // The now-md injector sources NOW.md from the workspace itself rather than
1234
+ // from a threaded option, so seed the file to drive injection and clear it so
1235
+ // the absent-content cases see no block.
1236
+ afterEach(clearNowScratchpad);
1237
+
1238
+ test("injects NOW.md block when the file has content", async () => {
1239
+ seedNowScratchpad("Current focus: fix the bug");
1240
+ const { messages: result } = await applyRuntimeInjections(baseMessages, {});
1199
1241
 
1200
1242
  expect(result.length).toBe(1);
1201
1243
  expect(result[0].content.length).toBe(2);
@@ -1206,9 +1248,8 @@ describe("applyRuntimeInjections with nowScratchpad", () => {
1206
1248
  });
1207
1249
 
1208
1250
  test("scratchpad appears before user's original text content", async () => {
1209
- const { messages: result } = await applyRuntimeInjections(baseMessages, {
1210
- nowScratchpad: "scratchpad notes",
1211
- });
1251
+ seedNowScratchpad("scratchpad notes");
1252
+ const { messages: result } = await applyRuntimeInjections(baseMessages, {});
1212
1253
 
1213
1254
  // Scratchpad comes first (before user content)
1214
1255
  expect(
@@ -1220,16 +1261,7 @@ describe("applyRuntimeInjections with nowScratchpad", () => {
1220
1261
  );
1221
1262
  });
1222
1263
 
1223
- test("does not inject when nowScratchpad is null", async () => {
1224
- const { messages: result } = await applyRuntimeInjections(baseMessages, {
1225
- nowScratchpad: null,
1226
- });
1227
-
1228
- expect(result.length).toBe(1);
1229
- expect(result[0].content.length).toBe(1);
1230
- });
1231
-
1232
- test("does not inject when nowScratchpad is omitted", async () => {
1264
+ test("does not inject when the NOW.md file is absent", async () => {
1233
1265
  const { messages: result } = await applyRuntimeInjections(baseMessages, {});
1234
1266
 
1235
1267
  expect(result.length).toBe(1);
@@ -1237,8 +1269,8 @@ describe("applyRuntimeInjections with nowScratchpad", () => {
1237
1269
  });
1238
1270
 
1239
1271
  test("skipped in minimal mode", async () => {
1272
+ seedNowScratchpad("Current focus: fix the bug");
1240
1273
  const { messages: result } = await applyRuntimeInjections(baseMessages, {
1241
- nowScratchpad: "Current focus: fix the bug",
1242
1274
  mode: "minimal",
1243
1275
  });
1244
1276
 
@@ -1782,85 +1814,6 @@ describe("applyRuntimeInjections blocks.unifiedTurnContext", () => {
1782
1814
  });
1783
1815
  });
1784
1816
 
1785
- // ---------------------------------------------------------------------------
1786
- // findLastInjectedNowContent
1787
- // ---------------------------------------------------------------------------
1788
-
1789
- describe("findLastInjectedNowContent", () => {
1790
- test("extracts NOW.md content from the last user message", () => {
1791
- const messages: Message[] = [
1792
- {
1793
- role: "user",
1794
- content: [
1795
- {
1796
- type: "text",
1797
- text: "<NOW.md Always keep this up to date>\nCurrent focus: fix the bug\n</NOW.md>",
1798
- },
1799
- { type: "text", text: "Hello" },
1800
- ],
1801
- },
1802
- ];
1803
-
1804
- expect(findLastInjectedNowContent(messages)).toBe(
1805
- "Current focus: fix the bug",
1806
- );
1807
- });
1808
-
1809
- test("returns null when no NOW.md injection exists", () => {
1810
- const messages: Message[] = [
1811
- {
1812
- role: "user",
1813
- content: [{ type: "text", text: "Hello" }],
1814
- },
1815
- ];
1816
-
1817
- expect(findLastInjectedNowContent(messages)).toBeNull();
1818
- });
1819
-
1820
- test("returns the most recent injection when multiple exist", () => {
1821
- const messages: Message[] = [
1822
- {
1823
- role: "user",
1824
- content: [
1825
- {
1826
- type: "text",
1827
- text: "<NOW.md Always keep this up to date>\nOld focus\n</NOW.md>",
1828
- },
1829
- ],
1830
- },
1831
- { role: "assistant", content: [{ type: "text", text: "OK" }] },
1832
- {
1833
- role: "user",
1834
- content: [
1835
- {
1836
- type: "text",
1837
- text: "<NOW.md Always keep this up to date>\nNew focus\n</NOW.md>",
1838
- },
1839
- ],
1840
- },
1841
- ];
1842
-
1843
- expect(findLastInjectedNowContent(messages)).toBe("New focus");
1844
- });
1845
-
1846
- test("skips assistant messages", () => {
1847
- const messages: Message[] = [
1848
- {
1849
- role: "user",
1850
- content: [
1851
- {
1852
- type: "text",
1853
- text: "<NOW.md Always keep this up to date>\nUser focus\n</NOW.md>",
1854
- },
1855
- ],
1856
- },
1857
- { role: "assistant", content: [{ type: "text", text: "response" }] },
1858
- ];
1859
-
1860
- expect(findLastInjectedNowContent(messages)).toBe("User focus");
1861
- });
1862
- });
1863
-
1864
1817
  // ---------------------------------------------------------------------------
1865
1818
  // Subagent status injection
1866
1819
  // ---------------------------------------------------------------------------
@@ -2032,21 +1985,33 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
2032
1985
 
2033
1986
  const FLAT_REMINDER = buildPkbReminder([]);
2034
1987
 
2035
- // Use a platform-agnostic absolute workspace root so the tests work on
2036
- // macOS and Linux runners alike. `pkbRoot` sits under `pkbWorkingDir` to
2037
- // mirror production, where `pkbRoot = join(workingDir, "pkb")`.
2038
- const pkbWorkingDir = "/tmp/fake-workspace";
2039
- const pkbRoot = `${pkbWorkingDir}/pkb`;
1988
+ // The pkb-reminder injector sources the PKB root itself via `getPkbRoot()`,
1989
+ // so the in-context file paths these tests build must resolve against the
1990
+ // same per-test workspace the injector sees.
1991
+ const pkbRoot = getPkbRoot();
1992
+
1993
+ // The pkb-reminder injector reads the dense/sparse PKB query pair off the
1994
+ // conversation's live graph handle (the memory-retrieval hook records it
1995
+ // there during retrieval), not from the injection options. Register a handle
1996
+ // for the fallback conversation id `applyRuntimeInjections` synthesizes and
1997
+ // seed it with a query vector so the hint-search branch runs.
1998
+ let graphHandle: ConversationGraphMemory;
1999
+ beforeEach(() => {
2000
+ graphHandle = new ConversationGraphMemory("runtime-assembly-fallback");
2001
+ graphHandle.recordPkbQueryVectors([0.1, 0.2, 0.3], undefined);
2002
+ });
2003
+ afterEach(() => {
2004
+ graphHandle.dispose();
2005
+ });
2006
+
2007
+ // PKB content makes `readPkbContext()` non-null so the (vellum/guardian-
2008
+ // equivalent) fallback trust gate admits the reminder; cleared after each
2009
+ // test to avoid leaking into suites that assert the reminder is absent.
2010
+ beforeEach(seedPkbContent);
2011
+ afterEach(clearPkbContent);
2040
2012
 
2041
2013
  function makePkbOptions(overrides: Record<string, unknown> = {}) {
2042
2014
  return {
2043
- pkbActive: true,
2044
- pkbQueryVector: [0.1, 0.2, 0.3],
2045
- pkbScopeId: "scope-1",
2046
- pkbConversation: { messages: baseMessages },
2047
- pkbRoot,
2048
- pkbWorkingDir,
2049
- pkbAutoInjectList: [],
2050
2015
  ...overrides,
2051
2016
  };
2052
2017
  }
@@ -2082,10 +2047,11 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
2082
2047
  test("default auto-injected files (from PKB_DEFAULT_FILES) are filtered out of hints", async () => {
2083
2048
  // Regression test: when `_autoinject.md` is missing, `readPkbContext`
2084
2049
  // falls back to PKB_DEFAULT_FILES — so those files ARE in the prompt.
2085
- // The tracker must know about them too, otherwise the reminder would
2086
- // redundantly recommend e.g. `essentials.md` even though its contents
2087
- // are already injected. The agent-loop passes the effective auto-inject
2088
- // list (via `getPkbAutoInjectList`) to `applyRuntimeInjections`.
2050
+ // The injector sources the same fallback via `getPkbAutoInjectList`, so
2051
+ // it must know about them too, otherwise the reminder would redundantly
2052
+ // recommend e.g. `essentials.md` even though its contents are already
2053
+ // injected. The per-test workspace has no `_autoinject.md`, so the
2054
+ // injector resolves PKB_DEFAULT_FILES here.
2089
2055
  pkbSearchResults = [
2090
2056
  { path: "essentials.md", denseScore: 0.95 },
2091
2057
  { path: "topics/alpha.md", denseScore: 0.9 },
@@ -2094,16 +2060,7 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
2094
2060
 
2095
2061
  const { messages: result } = await applyRuntimeInjections(
2096
2062
  baseMessages,
2097
- makePkbOptions({
2098
- // Simulate the fallback the agent-loop now threads through:
2099
- // `_autoinject.md` is missing, so defaults are injected.
2100
- pkbAutoInjectList: [
2101
- "INDEX.md",
2102
- "essentials.md",
2103
- "threads.md",
2104
- "buffer.md",
2105
- ],
2106
- }),
2063
+ makePkbOptions(),
2107
2064
  );
2108
2065
  const texts = extractTexts(result);
2109
2066
  const reminder = texts.find((t) => t.startsWith("<system_reminder>"));
@@ -2143,27 +2100,26 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
2143
2100
  ];
2144
2101
  pkbSearchThrows = null;
2145
2102
 
2146
- // Build a conversation that has already read topics/beta.md via file_read.
2147
- const conversationWithRead: { messages: Message[] } = {
2148
- messages: [
2149
- ...baseMessages,
2150
- {
2151
- role: "assistant",
2152
- content: [
2153
- {
2154
- type: "tool_use",
2155
- id: "tu_1",
2156
- name: "file_read",
2157
- input: { path: `${pkbRoot}/topics/beta.md` },
2158
- },
2159
- ],
2160
- },
2161
- ],
2162
- };
2103
+ // Working messages where topics/beta.md was already read via file_read on
2104
+ // an earlier turn, followed by the current user prompt as the tail.
2105
+ const conversationWithRead: Message[] = [
2106
+ {
2107
+ role: "assistant",
2108
+ content: [
2109
+ {
2110
+ type: "tool_use",
2111
+ id: "tu_1",
2112
+ name: "file_read",
2113
+ input: { path: `${pkbRoot}/topics/beta.md` },
2114
+ },
2115
+ ],
2116
+ },
2117
+ ...baseMessages,
2118
+ ];
2163
2119
 
2164
2120
  const { messages: result } = await applyRuntimeInjections(
2165
- baseMessages,
2166
- makePkbOptions({ pkbConversation: conversationWithRead }),
2121
+ conversationWithRead,
2122
+ makePkbOptions(),
2167
2123
  );
2168
2124
  const texts = extractTexts(result);
2169
2125
  const reminder = texts.find((t) => t.startsWith("<system_reminder>"));
@@ -2201,10 +2157,13 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
2201
2157
 
2202
2158
  test("missing query vector → flat fallback, search is not attempted", async () => {
2203
2159
  pkbSearchThrows = new Error("should not be called");
2160
+ // No dense vector was recorded on the graph handle this turn, so the
2161
+ // injector must skip the hint search entirely.
2162
+ graphHandle.recordPkbQueryVectors(undefined, undefined);
2204
2163
 
2205
2164
  const { messages: result } = await applyRuntimeInjections(
2206
2165
  baseMessages,
2207
- makePkbOptions({ pkbQueryVector: undefined }),
2166
+ makePkbOptions(),
2208
2167
  );
2209
2168
  const texts = extractTexts(result);
2210
2169
  const reminder = texts.find((t) => t.startsWith("<system_reminder>"));
@@ -2318,37 +2277,28 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
2318
2277
  ];
2319
2278
  pkbSearchThrows = null;
2320
2279
 
2321
- // Pre-compaction conversation: beta was already read.
2322
- const preCompactionConversation: { messages: Message[] } = {
2323
- messages: [
2324
- ...baseMessages,
2325
- {
2326
- role: "assistant",
2327
- content: [
2328
- {
2329
- type: "tool_use",
2330
- id: "tu_pre",
2331
- name: "file_read",
2332
- input: { path: `${pkbRoot}/topics/beta.md` },
2333
- },
2334
- ],
2335
- },
2336
- ],
2337
- };
2280
+ // Pre-compaction working messages: beta was already read on an earlier
2281
+ // turn, followed by the current user prompt as the tail.
2282
+ const preCompactionMessages: Message[] = [
2283
+ {
2284
+ role: "assistant",
2285
+ content: [
2286
+ {
2287
+ type: "tool_use",
2288
+ id: "tu_pre",
2289
+ name: "file_read",
2290
+ input: { path: `${pkbRoot}/topics/beta.md` },
2291
+ },
2292
+ ],
2293
+ },
2294
+ ...baseMessages,
2295
+ ];
2338
2296
 
2339
2297
  // 1. Initial injection sees the pre-compaction state — beta should be
2340
2298
  // filtered out.
2341
2299
  const { messages: initialResult } = await applyRuntimeInjections(
2342
- baseMessages,
2343
- {
2344
- pkbActive: true,
2345
- pkbQueryVector: [0.1, 0.2],
2346
- pkbScopeId: "scope-1",
2347
- pkbConversation: preCompactionConversation,
2348
- pkbRoot,
2349
- pkbWorkingDir,
2350
- pkbAutoInjectList: [],
2351
- },
2300
+ preCompactionMessages,
2301
+ {},
2352
2302
  );
2353
2303
  // Unwrap the injected reminder from the last user message.
2354
2304
  const initialTexts = extractTexts(initialResult);
@@ -2360,41 +2310,29 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
2360
2310
  expect(initialReminder).toBeDefined();
2361
2311
  expect(initialReminder).not.toContain("- topics/beta.md");
2362
2312
 
2363
- // 2. Simulate compaction: strip all runtime injections, rebuild
2364
- // conversation to reflect the post-compaction state (tool_use blocks
2365
- // are serialized into summary text, so the only live file_read is the
2366
- // newly-read gamma).
2367
- const postCompactionConversation: { messages: Message[] } = {
2368
- messages: [
2369
- ...baseMessages,
2370
- {
2371
- role: "assistant",
2372
- content: [
2373
- {
2374
- type: "tool_use",
2375
- id: "tu_post",
2376
- name: "file_read",
2377
- input: { path: `${pkbRoot}/topics/gamma.md` },
2378
- },
2379
- ],
2380
- },
2381
- ],
2382
- };
2383
- const postCompactionMessages = stripInjectionsForCompaction(initialResult);
2313
+ // 2. After compaction the working messages reflect the post-compaction
2314
+ // state: beta's tool_use was serialized into summary text and dropped,
2315
+ // so the only live file_read is the newly-read gamma.
2316
+ const postCompactionMessages: Message[] = [
2317
+ {
2318
+ role: "assistant",
2319
+ content: [
2320
+ {
2321
+ type: "tool_use",
2322
+ id: "tu_post",
2323
+ name: "file_read",
2324
+ input: { path: `${pkbRoot}/topics/gamma.md` },
2325
+ },
2326
+ ],
2327
+ },
2328
+ ...baseMessages,
2329
+ ];
2384
2330
 
2385
- // 3. Re-inject with the new conversation — gamma (now in context)
2331
+ // 3. Re-inject over the post-compaction messages — gamma (now in context)
2386
2332
  // should be filtered, and beta (no longer "in context") should appear.
2387
2333
  const { messages: rebuiltResult } = await applyRuntimeInjections(
2388
2334
  postCompactionMessages,
2389
- {
2390
- pkbActive: true,
2391
- pkbQueryVector: [0.1, 0.2],
2392
- pkbScopeId: "scope-1",
2393
- pkbConversation: postCompactionConversation,
2394
- pkbRoot,
2395
- pkbWorkingDir,
2396
- pkbAutoInjectList: [],
2397
- },
2335
+ {},
2398
2336
  );
2399
2337
  const rebuiltTexts = extractTexts(rebuiltResult);
2400
2338
  const rebuiltReminder = rebuiltTexts.find(
@@ -5100,11 +5038,15 @@ describe("applyRuntimeInjections blocks.pkbSystemReminder", () => {
5100
5038
  },
5101
5039
  ];
5102
5040
 
5041
+ // The reminder gate hinges on PKB content presence; clear it after each test
5042
+ // so the "PKB inactive" case starts from an empty workspace pkb dir.
5043
+ afterEach(clearPkbContent);
5044
+
5103
5045
  test("captures exact reminder bytes when full mode and PKB active", async () => {
5046
+ seedPkbContent();
5104
5047
  pkbSearchResults = [];
5105
5048
  pkbSearchThrows = null;
5106
5049
  const { blocks } = await applyRuntimeInjections(baseMessages, {
5107
- pkbActive: true,
5108
5050
  mode: "full",
5109
5051
  });
5110
5052
 
@@ -5113,8 +5055,8 @@ describe("applyRuntimeInjections blocks.pkbSystemReminder", () => {
5113
5055
  });
5114
5056
 
5115
5057
  test("not captured in minimal mode", async () => {
5058
+ seedPkbContent();
5116
5059
  const { blocks } = await applyRuntimeInjections(baseMessages, {
5117
- pkbActive: true,
5118
5060
  mode: "minimal",
5119
5061
  });
5120
5062
 
@@ -5123,7 +5065,6 @@ describe("applyRuntimeInjections blocks.pkbSystemReminder", () => {
5123
5065
 
5124
5066
  test("not captured when PKB inactive", async () => {
5125
5067
  const { blocks } = await applyRuntimeInjections(baseMessages, {
5126
- pkbActive: false,
5127
5068
  mode: "full",
5128
5069
  });
5129
5070
 
@@ -1,11 +1,6 @@
1
- import { beforeEach, describe, expect, test } from "bun:test";
1
+ import { describe, expect, test } from "bun:test";
2
2
 
3
3
  import { applyRuntimeInjections } from "../daemon/conversation-runtime-assembly.js";
4
- import { defaultInjectorsPlugin } from "../plugins/defaults/injectors/register.js";
5
- import {
6
- registerPlugin,
7
- resetPluginRegistryForTests,
8
- } from "../plugins/registry.js";
9
4
  import type { Message } from "../providers/types.js";
10
5
 
11
6
  // ---------------------------------------------------------------------------
@@ -23,21 +18,11 @@ function userMsg(text: string): Message {
23
18
  const sampleContext =
24
19
  "<workspace>\nRoot: /sandbox\nDirectories: src, lib, tests\n</workspace>";
25
20
 
26
- // The standalone `injectWorkspaceTopLevelContext` helper was removed in
27
- // G2.1. The workspace-context default injector (registered by
28
- // `defaultInjectorsPlugin`) now emits the workspace block as a
21
+ // The workspace-context default injector emits the workspace block as a
29
22
  // `prepend-user-tail` placement during `applyRuntimeInjections`. The suite
30
- // below exercises that end-to-end path instead.
23
+ // below exercises that end-to-end path.
31
24
 
32
25
  describe("applyRuntimeInjections — workspace top-level context", () => {
33
- beforeEach(() => {
34
- // Post-G2.1: workspace injection is driven by the `workspace-context`
35
- // default injector, so the plugin must be registered for the chain to
36
- // produce a block. Each test gets a clean registry.
37
- resetPluginRegistryForTests();
38
- registerPlugin(defaultInjectorsPlugin);
39
- });
40
-
41
26
  test("injects workspace context when provided", async () => {
42
27
  const messages: Message[] = [userMsg("Hello")];
43
28
  const { messages: result } = await applyRuntimeInjections(messages, {
@@ -86,7 +71,6 @@ describe("applyRuntimeInjections — workspace top-level context", () => {
86
71
  html: "<div>test</div>",
87
72
  appId: "app-1",
88
73
  appName: "Example App",
89
- appFiles: [],
90
74
  },
91
75
  workspaceTopLevelContext: null,
92
76
  });
@@ -98,11 +82,6 @@ describe("applyRuntimeInjections — workspace top-level context", () => {
98
82
  });
99
83
 
100
84
  describe("applyRuntimeInjections — minimal mode skips workspace blocks", () => {
101
- beforeEach(() => {
102
- resetPluginRegistryForTests();
103
- registerPlugin(defaultInjectorsPlugin);
104
- });
105
-
106
85
  test("minimal mode skips workspace top-level context", async () => {
107
86
  const messages: Message[] = [userMsg("Hello")];
108
87
  const { messages: result } = await applyRuntimeInjections(messages, {