@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
@@ -167,6 +167,7 @@ const mockFailStalledJobs = mock(() => 0);
167
167
  const mockClaimMemoryJobs = mock(() => []);
168
168
  mock.module("../memory/jobs-store.js", () => ({
169
169
  claimMemoryJobs: mockClaimMemoryJobs,
170
+ cancelPendingAutomaticConsolidationJobs: mock(() => 0),
170
171
  completeMemoryJob: mock(() => {}),
171
172
  deferMemoryJob: mock(() => "deferred"),
172
173
  EMBED_JOB_TYPES: [],
@@ -178,7 +179,12 @@ mock.module("../memory/jobs-store.js", () => ({
178
179
  failStalledJobs: mockFailStalledJobs,
179
180
  getMemoryJobCounts: mock(() => ({})),
180
181
  hasActiveJobOfType: mock(() => false),
182
+ isAutomaticConsolidationJob: mock(() => true),
181
183
  isMemoryEnabled: () => true,
184
+ MEMORY_V2_CONSOLIDATION_JOB_TRIGGERS: {
185
+ automatic: "automatic",
186
+ manual: "manual",
187
+ },
182
188
  resetRunningJobsToPending: mock(() => 0),
183
189
  SLOW_LLM_JOB_TYPES: [],
184
190
  upsertAutoAnalysisJob: mock(() => "job-auto-analysis"),
@@ -6,7 +6,9 @@
6
6
  * and no session.processing mutation.
7
7
  */
8
8
 
9
- import { describe, expect, mock, test } from "bun:test";
9
+ import { rmSync, writeFileSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
10
12
 
11
13
  // ---------------------------------------------------------------------------
12
14
  // Mocks — must be defined before importing the module under test
@@ -76,7 +78,13 @@ mock.module("../runtime/routes/identity-intro-cache.js", () => ({
76
78
  getCachedIntro: () => null,
77
79
  readWorkspaceIdentityIntro: () => null,
78
80
  setCachedIntro: () => {},
79
- computeIdentityContentHash: () => "test-hash",
81
+ }));
82
+
83
+ const assistantFeatureFlags: Record<string, boolean> = {};
84
+
85
+ mock.module("../config/assistant-feature-flags.js", () => ({
86
+ isAssistantFeatureFlagEnabled: (key: string) =>
87
+ assistantFeatureFlags[key] ?? false,
80
88
  }));
81
89
 
82
90
  // Mock getOrCreateConversation from conversation-store so the handler
@@ -123,6 +131,7 @@ import {
123
131
  ServiceUnavailableError,
124
132
  } from "../runtime/routes/errors.js";
125
133
  import type { RouteHandlerArgs } from "../runtime/routes/types.js";
134
+ import { getWorkspaceDir } from "../util/platform.js";
126
135
 
127
136
  // ---------------------------------------------------------------------------
128
137
  // Helpers
@@ -177,6 +186,12 @@ function makeMockSession(
177
186
  const route = ROUTES.find((r) => r.endpoint === "btw" && r.method === "POST");
178
187
  if (!route) throw new Error("btw route not found in ROUTES");
179
188
 
189
+ beforeEach(() => {
190
+ for (const key of Object.keys(assistantFeatureFlags)) {
191
+ delete assistantFeatureFlags[key];
192
+ }
193
+ });
194
+
180
195
  async function callHandler(
181
196
  body: Record<string, unknown>,
182
197
  ): Promise<{ result: unknown; error?: unknown }> {
@@ -324,7 +339,25 @@ describe("POST /v1/btw", () => {
324
339
  expect(options!.config!.modelIntent).toBeUndefined();
325
340
  });
326
341
 
327
- test("greeting requests pass callSite: 'emptyStateGreeting'", async () => {
342
+ test("greeting requests return static fallback when the dynamic greetings flag is off", async () => {
343
+ mockGetOrCreateConversation.mockClear();
344
+
345
+ const { result } = await callHandler({
346
+ conversationKey: "greeting",
347
+ content: "Generate a greeting",
348
+ });
349
+ const text = await readStream(result as ReadableStream<Uint8Array>);
350
+
351
+ expect(text).toContain(
352
+ `event: btw_text_delta\ndata: {"text":"What are we working on?"}`,
353
+ );
354
+ expect(text).toContain("event: btw_complete\ndata: {}");
355
+ expect(mockGetOrCreateConversation).not.toHaveBeenCalled();
356
+ });
357
+
358
+ test("greeting requests pass callSite: 'emptyStateGreeting' when the dynamic greetings flag is on", async () => {
359
+ assistantFeatureFlags["empty-state-dynamic-greetings"] = true;
360
+
328
361
  const provider = makeMockProvider();
329
362
  const session = makeMockSession(provider);
330
363
  mockGetOrCreateConversation.mockImplementationOnce(async () => session);
@@ -356,6 +389,32 @@ describe("POST /v1/btw", () => {
356
389
  expect(options!.config!.callSite).toBe("identityIntro");
357
390
  });
358
391
 
392
+ test("identity intro requests do not synthesize a static name greeting", async () => {
393
+ const identityPath = join(getWorkspaceDir(), "IDENTITY.md");
394
+ writeFileSync(
395
+ identityPath,
396
+ "# Identity\n\n- **Name:** Example Assistant\n",
397
+ "utf-8",
398
+ );
399
+
400
+ try {
401
+ const provider = makeMockProvider();
402
+ const session = makeMockSession(provider);
403
+ mockGetOrCreateConversation.mockImplementationOnce(async () => session);
404
+
405
+ const { result } = await callHandler({
406
+ conversationKey: "identity-intro",
407
+ content: "Generate an intro",
408
+ });
409
+ const text = await readStream(result as ReadableStream<Uint8Array>);
410
+
411
+ expect(provider.sendMessage).toHaveBeenCalledTimes(1);
412
+ expect(text).not.toContain("Hi, I'm Example Assistant!");
413
+ } finally {
414
+ rmSync(identityPath, { force: true });
415
+ }
416
+ });
417
+
359
418
  // -- No persistence --
360
419
 
361
420
  test("does not persist any messages (addMessage never called)", async () => {
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Tests for `buildPersistedAssistantContent` stamping native web-search
3
+ * activity (`_activityMetadata`) onto `server_tool_use` blocks at persist time.
4
+ *
5
+ * Native Anthropic web_search resolves mid-stream — `server_tool_complete`
6
+ * fires before `message_complete` — so the captured activity is available when
7
+ * the content is persisted. Unlike external provider tools, a pure-native turn
8
+ * has no `tool_result` and never runs `annotatePersistedAssistantMessage`, so
9
+ * stamping must happen here or the WebSearchProgressCard is lost on a history
10
+ * reopen. Read-side coverage lives in server-history-render.test.ts.
11
+ */
12
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
13
+
14
+ // ── Mock platform (must precede imports that read it) ─────────────────────────
15
+ mock.module("../util/logger.js", () => ({
16
+ getLogger: () =>
17
+ new Proxy({} as Record<string, unknown>, {
18
+ get: () => () => {},
19
+ }),
20
+ }));
21
+
22
+ mock.module("../config/loader.js", () => ({
23
+ getConfig: () => ({
24
+ skills: {
25
+ entries: {},
26
+ load: { extraDirs: [], watch: false, watchDebounceMs: 0 },
27
+ install: { nodeManager: "npm" },
28
+ allowBundled: null,
29
+ remoteProviders: {
30
+ skillssh: { enabled: true },
31
+ clawhub: { enabled: true },
32
+ },
33
+ remotePolicy: {
34
+ blockSuspicious: true,
35
+ blockMalware: true,
36
+ maxSkillsShRisk: "medium",
37
+ },
38
+ },
39
+ }),
40
+ loadConfig: () => ({}),
41
+ }));
42
+
43
+ mock.module("../memory/conversation-crud.js", () => ({
44
+ addMessage: () => ({ id: "mock-msg-id" }),
45
+ getMessageById: () => null,
46
+ updateMessageContent: () => {},
47
+ provenanceFromTrustContext: () => ({}),
48
+ reserveMessage: mock(async () => ({ id: "msg-reserve" })),
49
+ }));
50
+
51
+ mock.module("../memory/llm-request-log-store.js", () => ({
52
+ recordRequestLog: () => {},
53
+ backfillMessageIdOnLogs: () => {},
54
+ }));
55
+
56
+ // ── Imports (after mocks) ─────────────────────────────────────────────────────
57
+ import { buildPersistedAssistantContent } from "../daemon/conversation-agent-loop-handlers.js";
58
+ import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
59
+ import type { ContentBlock } from "../providers/types.js";
60
+
61
+ const webSearchActivity: ToolActivityMetadata = {
62
+ webSearch: {
63
+ query: "vellum docs",
64
+ provider: "anthropic-native",
65
+ resultCount: 1,
66
+ durationMs: 88,
67
+ results: [
68
+ {
69
+ rank: 1,
70
+ title: "Vellum",
71
+ url: "https://vellum.ai",
72
+ domain: "vellum.ai",
73
+ },
74
+ ],
75
+ },
76
+ };
77
+
78
+ function findBlockById(
79
+ blocks: ContentBlock[],
80
+ id: string,
81
+ ): Record<string, unknown> {
82
+ const block = (blocks as unknown as Array<Record<string, unknown>>).find(
83
+ (b) => b.id === id,
84
+ );
85
+ if (!block) throw new Error(`block ${id} not found`);
86
+ return block;
87
+ }
88
+
89
+ // ── Tests ─────────────────────────────────────────────────────────────────────
90
+
91
+ describe("buildPersistedAssistantContent — native activityMetadata", () => {
92
+ let activity: Map<string, ToolActivityMetadata>;
93
+
94
+ beforeEach(() => {
95
+ activity = new Map();
96
+ });
97
+
98
+ test("stamps captured activity onto a pure-native server_tool_use block", () => {
99
+ // GIVEN a turn whose only tool is a native server_tool_use whose activity
100
+ // was captured at server_tool_complete (no external tool_result, so the
101
+ // annotate pass never runs for this turn)
102
+ const nativeId = "srvtu_native_search";
103
+ activity.set(nativeId, webSearchActivity);
104
+ const rawBlocks = [
105
+ { type: "text", text: "Let me search." },
106
+ {
107
+ type: "server_tool_use",
108
+ id: nativeId,
109
+ name: "web_search",
110
+ input: { query: "vellum docs" },
111
+ },
112
+ ] as unknown as ContentBlock[];
113
+
114
+ // WHEN the content is built for persistence
115
+ const built = buildPersistedAssistantContent(rawBlocks, [], activity);
116
+
117
+ // THEN the server_tool_use block carries the native activity verbatim so it
118
+ // survives a history reopen
119
+ const block = findBlockById(built, nativeId);
120
+ expect(block._activityMetadata).toEqual(webSearchActivity);
121
+ });
122
+
123
+ test("leaves a server_tool_use block untouched when no activity was captured", () => {
124
+ // GIVEN a native server_tool_use with no entry in the activity map
125
+ const nativeId = "srvtu_no_activity";
126
+ const rawBlocks = [
127
+ {
128
+ type: "server_tool_use",
129
+ id: nativeId,
130
+ name: "web_search",
131
+ input: { query: "vellum docs" },
132
+ },
133
+ ] as unknown as ContentBlock[];
134
+
135
+ // WHEN the content is built for persistence
136
+ const built = buildPersistedAssistantContent(rawBlocks, [], activity);
137
+
138
+ // THEN no _activityMetadata is written
139
+ const block = findBlockById(built, nativeId);
140
+ expect(block._activityMetadata).toBeUndefined();
141
+ });
142
+
143
+ test("does not stamp external tool_use blocks (handled by the annotate pass)", () => {
144
+ // GIVEN an external tool_use block whose id happens to have captured
145
+ // activity (external activity is stamped later in handleToolResult, not here)
146
+ const externalId = "tu_external_search";
147
+ activity.set(externalId, webSearchActivity);
148
+ const rawBlocks = [
149
+ {
150
+ type: "tool_use",
151
+ id: externalId,
152
+ name: "web_search",
153
+ input: { query: "vellum docs" },
154
+ },
155
+ ] as unknown as ContentBlock[];
156
+
157
+ // WHEN the content is built for persistence
158
+ const built = buildPersistedAssistantContent(rawBlocks, [], activity);
159
+
160
+ // THEN the tool_use block is left untouched by this function
161
+ const block = findBlockById(built, externalId);
162
+ expect(block._activityMetadata).toBeUndefined();
163
+ });
164
+
165
+ test("does not stamp when no activity map is provided", () => {
166
+ // GIVEN a native server_tool_use block AND no activity map argument
167
+ const nativeId = "srvtu_no_map";
168
+ const rawBlocks = [
169
+ {
170
+ type: "server_tool_use",
171
+ id: nativeId,
172
+ name: "web_search",
173
+ input: { query: "vellum docs" },
174
+ },
175
+ ] as unknown as ContentBlock[];
176
+
177
+ // WHEN the content is built for persistence without the optional map
178
+ const built = buildPersistedAssistantContent(rawBlocks, []);
179
+
180
+ // THEN no _activityMetadata is written
181
+ const block = findBlockById(built, nativeId);
182
+ expect(block._activityMetadata).toBeUndefined();
183
+ });
184
+ });
@@ -963,7 +963,7 @@ describe("createVellumCatalogProvider", () => {
963
963
  expect(slim!.kind).toBe("catalog");
964
964
  expect(slim!.origin).toBe("vellum");
965
965
  expect(slim!.status).toBe("available");
966
- expect(slim!.category).toBe("knowledge");
966
+ expect(slim!.category).toBe("system");
967
967
  });
968
968
 
969
969
  test("toSlimSkill returns null for unknown skill", async () => {
@@ -291,7 +291,7 @@ describe("toSlimSkill", () => {
291
291
  expect(slim!.kind).toBe("catalog");
292
292
  expect(slim!.status).toBe("available");
293
293
  expect(slim!.origin).toBe("clawhub");
294
- expect(slim!.category).toBe("knowledge");
294
+ expect(slim!.category).toBe("integrations");
295
295
 
296
296
  // Clawhub-specific fields
297
297
  const clawhub = slim as unknown as Record<string, unknown>;
@@ -113,7 +113,7 @@ describe("compaction pipeline", () => {
113
113
  const args: CompactionArgs = {
114
114
  messages: [{ role: "user", content: "hi" }],
115
115
  signal: new AbortController().signal,
116
- options: { lastCompactedAt: 42, precomputedEstimate: 1234 },
116
+ options: { precomputedEstimate: 1234 },
117
117
  };
118
118
 
119
119
  // No middleware registered — the runner invokes the terminal directly.
@@ -172,34 +172,4 @@ describe("Compaction benchmark", () => {
172
172
  expect(result.summaryCalls).toBe(1);
173
173
  expect(result.summaryCalls).toBe(counter.calls);
174
174
  });
175
-
176
- test("severe pressure triggers compaction even during cooldown", async () => {
177
- const counter = { calls: 0 };
178
- const provider = makeSummaryProvider(counter);
179
- // Use a tighter maxInputTokens so 90 turns exceeds the 95% severe threshold
180
- const config = {
181
- ...makeConfig(),
182
- maxInputTokens: 4000,
183
- targetBudgetRatio: 0.55,
184
- };
185
- const manager = new ContextWindowManager({
186
- provider,
187
- systemPrompt: "system prompt",
188
- config,
189
- });
190
-
191
- const messages = makeLongMessages(90);
192
- const estimated = estimatePromptTokens(messages, "system prompt", {
193
- providerName: "mock",
194
- });
195
- expect(estimated).toBeGreaterThan(config.maxInputTokens * 0.95);
196
-
197
- // Simulate being within cooldown by setting lastCompactedAt to now
198
- const result = await manager.maybeCompact(messages, undefined, {
199
- lastCompactedAt: Date.now(),
200
- });
201
-
202
- expect(result.compacted).toBe(true);
203
- expect(result.summaryCalls).toBeGreaterThan(0);
204
- });
205
175
  });
@@ -231,7 +231,7 @@ describe("ConfigWatcher workspace file handlers", () => {
231
231
  expect(evictCallCount).toBe(1);
232
232
  });
233
233
 
234
- test("SOUL.md change triggers identity intro invalidation", async () => {
234
+ test("SOUL.md change triggers identity intro refetch notification", async () => {
235
235
  let introCallCount = 0;
236
236
  watcher.start(
237
237
  onConversationEvict,
@@ -108,8 +108,13 @@ mock.module("../workspace/git-service.js", () => ({
108
108
  }),
109
109
  }));
110
110
 
111
- // Track all messages persisted to DB
111
+ // Track all messages persisted to DB via addMessage (single-shot writes).
112
112
  let persistedMessages: Array<{ role: string; content: string }> = [];
113
+ // Track the latest content written into each reserved row (reserve + update
114
+ // pattern). Tool results persist on arrival and finalize at the loop boundary
115
+ // through this path, so the final value per row id is what lands in the DB.
116
+ let reservedRowContent: Map<string, string> = new Map();
117
+ let reserveCounter = 0;
113
118
 
114
119
  mock.module("../memory/conversation-crud.js", () => ({
115
120
  setConversationOriginChannelIfUnset: () => {},
@@ -139,8 +144,10 @@ mock.module("../memory/conversation-crud.js", () => ({
139
144
  updateConversationTitle: () => {},
140
145
  getMessageById: () => null,
141
146
  getLastUserTimestampBefore: () => 0,
142
- reserveMessage: mock(async () => ({ id: "msg-reserve" })),
143
- updateMessageContent: mock(() => {}),
147
+ reserveMessage: mock(async () => ({ id: `msg-reserve-${++reserveCounter}` })),
148
+ updateMessageContent: mock((id: string, content: string) => {
149
+ reservedRowContent.set(id, content);
150
+ }),
144
151
  }));
145
152
 
146
153
  mock.module("../memory/conversation-queries.js", () => ({
@@ -226,8 +233,11 @@ mock.module("../agent/loop.js", () => ({
226
233
  isError: false,
227
234
  });
228
235
 
229
- // Abort happens before second tool
230
- // Synthesize cancelled result for tu_2 (what the real AgentLoop does)
236
+ // Abort happens before second tool. The real AgentLoop synthesizes
237
+ // cancelled results into history AND emits a `cancelled` tool_result
238
+ // event per tool so the orchestrator captures them for persistence and
239
+ // forwards them to the client. tu_1 (already captured via its real
240
+ // tool_result event) wins via the handler's gap-fill guard.
231
241
  const resultBlocks: ContentBlock[] = [
232
242
  {
233
243
  type: "tool_result",
@@ -243,8 +253,27 @@ mock.module("../agent/loop.js", () => ({
243
253
  },
244
254
  ];
245
255
  history.push({ role: "user", content: resultBlocks });
256
+ onEvent({
257
+ type: "tool_result",
258
+ toolUseId: "tu_1",
259
+ content: "Cancelled by user",
260
+ isError: true,
261
+ cancelled: true,
262
+ });
263
+ onEvent({
264
+ type: "tool_result",
265
+ toolUseId: "tu_2",
266
+ content: "Cancelled by user",
267
+ isError: true,
268
+ cancelled: true,
269
+ });
246
270
 
247
- return { history, exitReason: null };
271
+ return {
272
+ history,
273
+ exitReason: null,
274
+ appendedNewMessages: true,
275
+ newMessages: history.slice(messages.length),
276
+ };
248
277
  }
249
278
  },
250
279
  }));
@@ -295,6 +324,8 @@ function makeConversation(): Conversation {
295
324
  describe("abort tool result persistence", () => {
296
325
  test("abort after first of multiple tool calls still persists all required tool_result blocks", async () => {
297
326
  persistedMessages = [];
327
+ reservedRowContent = new Map();
328
+ reserveCounter = 0;
298
329
  const conversation = makeConversation();
299
330
  await conversation.loadFromDb();
300
331
 
@@ -303,19 +334,26 @@ describe("abort tool result persistence", () => {
303
334
  attachments: [],
304
335
  });
305
336
 
306
- // Find user messages in persisted data that contain tool_result
307
- const toolResultUserMessages = persistedMessages.filter((m) => {
308
- if (m.role !== "user") return false;
309
- try {
310
- const content = JSON.parse(m.content);
311
- return (
312
- Array.isArray(content) &&
313
- content.some((b: Record<string, unknown>) => b.type === "tool_result")
314
- );
315
- } catch {
316
- return false;
317
- }
318
- });
337
+ // Find persisted rows whose final content contains tool_result blocks.
338
+ // Tool results persist on arrival into one grouped row and finalize at the
339
+ // abort/loop boundary; the latest content per reserved row id is what lands
340
+ // in the DB, so one entry per row models the persisted state (a second
341
+ // entry would mean the batch was wrongly split across rows).
342
+ const toolResultUserMessages = Array.from(reservedRowContent.values())
343
+ .map((content) => ({ content }))
344
+ .filter((m) => {
345
+ try {
346
+ const content = JSON.parse(m.content);
347
+ return (
348
+ Array.isArray(content) &&
349
+ content.some(
350
+ (b: Record<string, unknown>) => b.type === "tool_result",
351
+ )
352
+ );
353
+ } catch {
354
+ return false;
355
+ }
356
+ });
319
357
 
320
358
  // There should be at least one persisted user message with tool_results
321
359
  expect(toolResultUserMessages.length).toBeGreaterThanOrEqual(1);
@@ -109,10 +109,14 @@ import { runAgentLoopImpl } from "../daemon/conversation-agent-loop.js";
109
109
  function makeCtx(
110
110
  overrides: Partial<Context> = {},
111
111
  ): AgentLoopConversationContext {
112
+ let processing = true;
112
113
  return {
113
114
  conversationId: "conv-123",
114
115
  messages: [{ role: "user", content: [{ type: "text", text: "hello" }] }],
115
- processing: true,
116
+ isProcessing: () => processing,
117
+ setProcessing: (value: boolean) => {
118
+ processing = value;
119
+ },
116
120
  abortController: new AbortController(),
117
121
  currentRequestId: "req-123",
118
122
  agentLoop: {
@@ -217,7 +221,7 @@ describe("runAgentLoopImpl disk pressure gate", () => {
217
221
  "error_terminal",
218
222
  { anchor: "global", requestId: "req-123" },
219
223
  ]);
220
- expect(ctx.processing).toBe(false);
224
+ expect(ctx.isProcessing()).toBe(false);
221
225
  expect(ctx.abortController).toBeNull();
222
226
  expect(ctx.currentRequestId).toBeUndefined();
223
227
  expect(drainQueue).toHaveBeenCalledWith("loop_complete");
@@ -96,6 +96,9 @@ mock.module("../config/loader.js", () => ({
96
96
  mock.module("../context/token-estimator.js", () => ({
97
97
  estimatePromptTokens: () => 1000,
98
98
  estimatePromptTokensRaw: () => 1000,
99
+ // The preflight overflow gate calls this calibrated wrapper directly; stub
100
+ // it alongside the others so it returns the same small value.
101
+ estimatePromptTokensWithTools: () => 1000,
99
102
  estimateToolsTokens: () => 0,
100
103
  }));
101
104
 
@@ -220,10 +223,6 @@ mock.module("../daemon/conversation-runtime-assembly.js", () => ({
220
223
  blocks: {},
221
224
  }),
222
225
  stripInjectionsForCompaction: (msgs: Message[]) => msgs,
223
- findLastInjectedNowContent: () => null,
224
- readNowScratchpad: () => null,
225
- readPkbContext: () => null,
226
- getPkbAutoInjectList: () => [],
227
226
  isSlackChannelConversation: () => false,
228
227
  getSlackCompactionWatermarkForPrefix: () => null,
229
228
  loadSlackChronologicalContext: () => null,
@@ -392,6 +391,12 @@ function makeCtx(
392
391
  { role: "user", content: [{ type: "text", text: "Hello" }] },
393
392
  ] as Message[],
394
393
  processing: true,
394
+ isProcessing(this: { processing: boolean }) {
395
+ return this.processing;
396
+ },
397
+ setProcessing(this: { processing: boolean }, value: boolean) {
398
+ this.processing = value;
399
+ },
395
400
  abortController: new AbortController(),
396
401
  currentRequestId: "test-req",
397
402
 
@@ -494,6 +499,7 @@ function makeCtx(
494
499
  injectedTokens: 0,
495
500
  }),
496
501
  retrackCachedNodes: () => {},
502
+ recordPkbQueryVectors: () => {},
497
503
  } as unknown as AgentLoopConversationContext["graphMemory"],
498
504
 
499
505
  ...overrides,