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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (570) hide show
  1. package/Dockerfile +20 -4
  2. package/bun.lock +2 -2
  3. package/docker-entrypoint.sh +4 -2
  4. package/docker-init-apt-root.sh +3 -1
  5. package/docker-kata-apt-env.sh +3 -1
  6. package/docker-kata-runtime-family.sh +12 -0
  7. package/docs/architecture/memory.md +1 -1
  8. package/examples/plugins/echo/README.md +61 -66
  9. package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
  10. package/examples/plugins/echo/hooks/stop.ts +16 -0
  11. package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
  12. package/examples/plugins/echo/package.json +1 -2
  13. package/examples/plugins/echo/src/emit.ts +19 -0
  14. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
  15. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
  16. package/openapi.yaml +3378 -335
  17. package/package.json +2 -2
  18. package/scripts/generate-openapi.ts +68 -41
  19. package/src/__tests__/agent-loop-exit-reason.test.ts +35 -93
  20. package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
  21. package/src/__tests__/agent-loop.test.ts +37 -87
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  23. package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
  24. package/src/__tests__/annotate-risk-options.test.ts +2 -3
  25. package/src/__tests__/anthropic-provider.test.ts +95 -2
  26. package/src/__tests__/app-control-flow.test.ts +1 -1
  27. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  28. package/src/__tests__/approval-routes-http.test.ts +4 -1
  29. package/src/__tests__/assistant-event-hub.test.ts +25 -0
  30. package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
  31. package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
  32. package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
  33. package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
  34. package/src/__tests__/btw-routes.test.ts +62 -3
  35. package/src/__tests__/build-persisted-content.test.ts +184 -0
  36. package/src/__tests__/catalog-files.test.ts +1 -1
  37. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  38. package/src/__tests__/channel-approvals.test.ts +1 -1
  39. package/src/__tests__/clawhub-files.test.ts +1 -1
  40. package/src/__tests__/compaction-circuit.test.ts +258 -0
  41. package/src/__tests__/compaction-direct.test.ts +132 -0
  42. package/src/__tests__/compaction.benchmark.test.ts +0 -30
  43. package/src/__tests__/config-watcher.test.ts +1 -1
  44. package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
  45. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -5
  46. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -7
  47. package/src/__tests__/conversation-agent-loop-overflow.test.ts +316 -1143
  48. package/src/__tests__/conversation-agent-loop.test.ts +638 -1655
  49. package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
  50. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  51. package/src/__tests__/conversation-history-web-search.test.ts +11 -1
  52. package/src/__tests__/conversation-pairing.test.ts +4 -31
  53. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
  54. package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -10
  55. package/src/__tests__/conversation-queue.test.ts +2 -0
  56. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
  57. package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
  58. package/src/__tests__/conversation-runtime-assembly.test.ts +310 -300
  59. package/src/__tests__/conversation-runtime-workspace.test.ts +105 -45
  60. package/src/__tests__/conversation-slash-commands.test.ts +8 -42
  61. package/src/__tests__/conversation-slash-queue.test.ts +6 -1
  62. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  63. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
  64. package/src/__tests__/conversation-sync-tags.test.ts +27 -15
  65. package/src/__tests__/conversation-title-service.test.ts +135 -2
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +17 -16
  67. package/src/__tests__/conversation-workspace-injection.test.ts +67 -2
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +7 -6
  69. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  70. package/src/__tests__/cross-provider-web-search.test.ts +214 -1
  71. package/src/__tests__/db-acp-history.test.ts +101 -0
  72. package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
  73. package/src/__tests__/dm-persistence.test.ts +5 -1
  74. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  75. package/src/__tests__/empty-response-hook.test.ts +304 -0
  76. package/src/__tests__/feature-flag-test-helpers.ts +2 -2
  77. package/src/__tests__/file-write-tool.test.ts +63 -0
  78. package/src/__tests__/gateway-only-guard.test.ts +12 -2
  79. package/src/__tests__/gemini-image-service.test.ts +13 -0
  80. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  81. package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
  82. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  83. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  84. package/src/__tests__/heartbeat-service.test.ts +1 -0
  85. package/src/__tests__/helpers/mock-provider.ts +110 -0
  86. package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
  87. package/src/__tests__/history-repair-hook.test.ts +1 -0
  88. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  89. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  90. package/src/__tests__/identity-intro-cache.test.ts +12 -100
  91. package/src/__tests__/identity-routes.test.ts +248 -7
  92. package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
  93. package/src/__tests__/injector-background-turn.test.ts +3 -9
  94. package/src/__tests__/injector-chain.test.ts +139 -275
  95. package/src/__tests__/injector-disk-pressure.test.ts +75 -41
  96. package/src/__tests__/injector-document-comments.test.ts +3 -3
  97. package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
  98. package/src/__tests__/injector-v3-suppression.test.ts +31 -37
  99. package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
  100. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  101. package/src/__tests__/list-messages-page-latest.test.ts +60 -0
  102. package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
  103. package/src/__tests__/llm-usage-store.test.ts +223 -1
  104. package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
  105. package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
  106. package/src/__tests__/native-web-search.test.ts +191 -0
  107. package/src/__tests__/onboarding-template-contract.test.ts +2 -0
  108. package/src/__tests__/openai-image-service.test.ts +17 -0
  109. package/src/__tests__/openai-provider.test.ts +31 -1
  110. package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -284
  111. package/src/__tests__/persist-unsendable-image.test.ts +215 -0
  112. package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
  113. package/src/__tests__/pkb-autoinject.test.ts +2 -5
  114. package/src/__tests__/plugin-api-shim.test.ts +3 -6
  115. package/src/__tests__/plugin-bootstrap.test.ts +14 -40
  116. package/src/__tests__/plugin-registry.test.ts +3 -76
  117. package/src/__tests__/plugin-types.test.ts +0 -193
  118. package/src/__tests__/process-message-display-content.test.ts +6 -2
  119. package/src/__tests__/reaction-persistence.test.ts +1 -1
  120. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
  121. package/src/__tests__/resolve-trust-class.test.ts +4 -4
  122. package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
  123. package/src/__tests__/schedule-routes.test.ts +603 -2
  124. package/src/__tests__/schedule-store.test.ts +41 -0
  125. package/src/__tests__/schedule-tools.test.ts +35 -0
  126. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  127. package/src/__tests__/server-history-render.test.ts +314 -1
  128. package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
  129. package/src/__tests__/skillssh-files.test.ts +1 -1
  130. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  131. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  132. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  133. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  134. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  135. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  136. package/src/__tests__/system-prompt.test.ts +20 -0
  137. package/src/__tests__/task-scheduler.test.ts +162 -1
  138. package/src/__tests__/terminal-tools.test.ts +6 -1
  139. package/src/__tests__/title-generate-hook.test.ts +319 -0
  140. package/src/__tests__/tool-error-hook.test.ts +278 -0
  141. package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
  142. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  143. package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
  144. package/src/__tests__/tool-result-truncation.test.ts +0 -2
  145. package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
  146. package/src/__tests__/ui-work-result-surface.test.ts +159 -0
  147. package/src/__tests__/usage-routes.test.ts +285 -1
  148. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  149. package/src/__tests__/voice-session-bridge.test.ts +6 -3
  150. package/src/__tests__/web-search-backend-failure.test.ts +166 -0
  151. package/src/acp/__tests__/agent-process.test.ts +161 -0
  152. package/src/acp/__tests__/client-handler.test.ts +40 -0
  153. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  154. package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
  155. package/src/acp/__tests__/prepare-agent-env.test.ts +137 -0
  156. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  157. package/src/acp/__tests__/session-manager-resume.test.ts +736 -0
  158. package/src/acp/agent-process.ts +61 -1
  159. package/src/acp/auto-install.test.ts +196 -0
  160. package/src/acp/auto-install.ts +177 -0
  161. package/src/acp/client-handler.ts +31 -0
  162. package/src/acp/feature-gate.test.ts +48 -0
  163. package/src/acp/feature-gate.ts +34 -0
  164. package/src/acp/prepare-agent-env.ts +83 -29
  165. package/src/acp/resolve-agent.test.ts +320 -7
  166. package/src/acp/resolve-agent.ts +182 -18
  167. package/src/acp/resume-hint.ts +25 -0
  168. package/src/acp/session-manager.ts +495 -73
  169. package/src/acp/types.ts +8 -0
  170. package/src/agent/compaction-circuit.ts +60 -102
  171. package/src/agent/loop.ts +362 -485
  172. package/src/api/events/assistant-thinking-delta.ts +33 -0
  173. package/src/api/events/tool-output-chunk.ts +45 -0
  174. package/src/api/events/tool-use-preview-start.ts +32 -0
  175. package/src/api/events/trace-event.ts +69 -0
  176. package/src/api/index.ts +48 -13
  177. package/src/api/responses/conversation-message.ts +374 -0
  178. package/src/approvals/guardian-request-resolvers.ts +1 -1
  179. package/src/avatar/__tests__/avatar-store.test.ts +34 -29
  180. package/src/background-wake/next-wake.ts +1 -0
  181. package/src/cli/commands/__tests__/notifications.test.ts +58 -14
  182. package/src/cli/commands/notifications.ts +112 -60
  183. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  184. package/src/config/acp-defaults.test.ts +10 -0
  185. package/src/config/acp-defaults.ts +6 -0
  186. package/src/config/assistant-feature-flags.ts +22 -11
  187. package/src/config/bundled-skills/acp/SKILL.md +83 -31
  188. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  189. package/src/config/bundled-skills/app-builder/SKILL.md +224 -398
  190. package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
  191. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  192. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  193. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  194. package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
  195. package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
  196. package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
  197. package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
  198. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  199. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  200. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  201. package/src/config/bundled-skills/messaging/SKILL.md +0 -7
  202. package/src/config/bundled-tool-registry.ts +2 -0
  203. package/src/config/feature-flag-cache.ts +3 -3
  204. package/src/config/feature-flag-registry.json +48 -7
  205. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
  206. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
  207. package/src/config/schemas/heartbeat.ts +9 -0
  208. package/src/config/schemas/llm.ts +1 -0
  209. package/src/config/schemas/memory-v2.ts +8 -0
  210. package/src/config/schemas/memory-v3.ts +8 -0
  211. package/src/config/schemas/platform.ts +8 -0
  212. package/src/config/seed-inference-profiles.ts +2 -2
  213. package/src/config/skills.ts +13 -0
  214. package/src/context/compactor.ts +1 -1
  215. package/src/context/strip-injections.ts +128 -0
  216. package/src/context/token-estimator.ts +23 -0
  217. package/src/context/tool-result-truncation.ts +0 -23
  218. package/src/context/window-manager.ts +5 -7
  219. package/src/credential-execution/executable-discovery.ts +16 -0
  220. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
  221. package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
  222. package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
  223. package/src/daemon/assistant-attachments.ts +1 -1
  224. package/src/daemon/config-watcher.ts +2 -2
  225. package/src/daemon/context-overflow-reducer.ts +0 -1
  226. package/src/daemon/conversation-agent-loop-handlers.ts +594 -153
  227. package/src/daemon/conversation-agent-loop.ts +301 -997
  228. package/src/daemon/conversation-history.ts +5 -4
  229. package/src/daemon/conversation-lifecycle.ts +3 -4
  230. package/src/daemon/conversation-messaging.ts +7 -6
  231. package/src/daemon/conversation-process.ts +11 -16
  232. package/src/daemon/conversation-registry.ts +159 -0
  233. package/src/daemon/conversation-runtime-assembly.ts +218 -398
  234. package/src/daemon/conversation-slash.ts +6 -25
  235. package/src/daemon/conversation-store.ts +9 -90
  236. package/src/daemon/conversation-surfaces.ts +222 -4
  237. package/src/daemon/conversation-tool-setup.ts +2 -29
  238. package/src/daemon/conversation-workspace.ts +17 -0
  239. package/src/daemon/conversation.ts +32 -20
  240. package/src/daemon/external-plugins-bootstrap.ts +17 -18
  241. package/src/daemon/handlers/config-a2a.ts +51 -36
  242. package/src/daemon/handlers/config-slack-channel.ts +20 -14
  243. package/src/daemon/handlers/config-telegram.ts +16 -2
  244. package/src/daemon/handlers/conversations.ts +3 -1
  245. package/src/daemon/handlers/shared.ts +156 -84
  246. package/src/daemon/handlers/skills.ts +42 -10
  247. package/src/daemon/lifecycle.ts +25 -0
  248. package/src/daemon/message-types/apps.ts +1 -29
  249. package/src/daemon/message-types/messages.ts +9 -57
  250. package/src/daemon/message-types/skills.ts +2 -0
  251. package/src/daemon/message-types/surfaces.ts +136 -3
  252. package/src/daemon/now-scratchpad.ts +21 -0
  253. package/src/daemon/orphan-reaper.test.ts +210 -0
  254. package/src/daemon/orphan-reaper.ts +240 -0
  255. package/src/daemon/overflow-reduction-loop.ts +230 -0
  256. package/src/daemon/persist-unsendable-image.ts +117 -0
  257. package/src/daemon/process-message.ts +1 -3
  258. package/src/daemon/server.ts +2 -0
  259. package/src/daemon/trace-emitter.ts +6 -4
  260. package/src/daemon/trust-context.ts +19 -0
  261. package/src/daemon/wake-target-adapter.ts +3 -1
  262. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  263. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  264. package/src/heartbeat/heartbeat-service.ts +26 -0
  265. package/src/home/home-greeting-cache.ts +24 -1
  266. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  267. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  268. package/src/ipc/gateway-client.test.ts +2 -2
  269. package/src/ipc/gateway-client.ts +3 -3
  270. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  271. package/src/ipc/skill-routes/memory.ts +4 -2
  272. package/src/media/gemini-image-service.ts +15 -0
  273. package/src/media/openai-image-service.ts +14 -0
  274. package/src/media/types.ts +34 -0
  275. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
  276. package/src/memory/auth-fallback-events-store.ts +94 -0
  277. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  278. package/src/memory/conversation-title-service.ts +65 -41
  279. package/src/memory/db-init.ts +6 -0
  280. package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
  281. package/src/memory/graph/conversation-graph-memory.ts +65 -0
  282. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  283. package/src/memory/jobs-store.ts +33 -0
  284. package/src/memory/jobs-worker.ts +32 -5
  285. package/src/memory/llm-usage-store.ts +224 -50
  286. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
  287. package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
  288. package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
  289. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  290. package/src/memory/migrations/index.ts +3 -0
  291. package/src/memory/pkb/autoinject.ts +61 -0
  292. package/src/memory/pkb/context.ts +50 -0
  293. package/src/memory/pkb/types.ts +14 -0
  294. package/src/memory/schedule-attribution-sql.ts +104 -0
  295. package/src/memory/schema/acp.ts +4 -0
  296. package/src/memory/schema/infrastructure.ts +16 -0
  297. package/src/memory/usage-grouped-buckets.ts +6 -1
  298. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -4
  299. package/src/memory/v2/consolidation-job.ts +14 -5
  300. package/src/notifications/conversation-pairing.ts +8 -15
  301. package/src/notifications/decision-engine.ts +6 -3
  302. package/src/notifications/home-feed-side-effect.ts +12 -1
  303. package/src/permissions/prompter.ts +4 -0
  304. package/src/plugin-api/constants.ts +4 -0
  305. package/src/plugin-api/index.ts +7 -5
  306. package/src/plugin-api/types.ts +151 -1
  307. package/src/plugins/defaults/compaction/compact.ts +59 -0
  308. package/src/plugins/defaults/compaction/package.json +1 -1
  309. package/src/plugins/defaults/compaction/register.ts +8 -19
  310. package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
  311. package/src/plugins/defaults/empty-response/register.ts +8 -13
  312. package/src/plugins/defaults/index.ts +2 -18
  313. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
  314. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
  315. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
  316. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +288 -81
  317. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
  318. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +16 -0
  319. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +4 -4
  320. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
  321. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +48 -12
  322. package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
  323. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
  324. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
  325. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +104 -32
  326. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
  327. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +96 -30
  328. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +34 -16
  329. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +5 -5
  330. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +2 -2
  331. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -0
  332. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  333. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  334. package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
  335. package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
  336. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +8 -8
  337. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +26 -14
  338. package/src/plugins/defaults/{llm-call → memory-v3-shadow}/package.json +2 -2
  339. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
  340. package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
  341. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +3 -3
  342. package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
  343. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
  344. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +51 -45
  345. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
  346. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +61 -46
  347. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +69 -99
  348. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
  349. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +8 -0
  350. package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
  351. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
  352. package/src/plugins/defaults/title-generate/package.json +1 -1
  353. package/src/plugins/defaults/title-generate/register.ts +18 -18
  354. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
  355. package/src/plugins/defaults/tool-error/package.json +1 -1
  356. package/src/plugins/defaults/tool-error/register.ts +9 -21
  357. package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
  358. package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
  359. package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
  360. package/src/plugins/external-api.ts +2 -2
  361. package/src/plugins/pipeline.ts +6 -305
  362. package/src/plugins/registry.ts +10 -55
  363. package/src/plugins/types.ts +62 -797
  364. package/src/plugins/user-loader.ts +30 -127
  365. package/src/proactive-artifact/aux-message-injector.ts +4 -4
  366. package/src/proactive-artifact/job.test.ts +8 -13
  367. package/src/prompts/__tests__/system-prompt.test.ts +42 -0
  368. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
  369. package/src/prompts/templates/BOOTSTRAP.md +2 -2
  370. package/src/prompts/templates/system-sections.ts +15 -0
  371. package/src/providers/anthropic/client.ts +37 -29
  372. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
  373. package/src/providers/openai/chat-completions-provider.ts +44 -0
  374. package/src/providers/openrouter/client.ts +1 -0
  375. package/src/providers/placeholder-sentinels.ts +35 -0
  376. package/src/runtime/__tests__/agent-wake.test.ts +10 -6
  377. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  378. package/src/runtime/agent-wake.ts +2 -5
  379. package/src/runtime/assistant-event-hub.ts +37 -7
  380. package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
  381. package/src/runtime/channel-approvals.ts +1 -1
  382. package/src/runtime/http-router.ts +16 -21
  383. package/src/runtime/http-types.ts +16 -70
  384. package/src/runtime/interactive-ui.ts +1 -1
  385. package/src/runtime/pending-interactions.ts +1 -0
  386. package/src/runtime/routes/__tests__/acp-routes.test.ts +283 -55
  387. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
  388. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  389. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
  390. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
  391. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  392. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  393. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  394. package/src/runtime/routes/acp-routes.test.ts +89 -25
  395. package/src/runtime/routes/acp-routes.ts +81 -29
  396. package/src/runtime/routes/app-management-routes.ts +6 -117
  397. package/src/runtime/routes/app-routes.ts +13 -15
  398. package/src/runtime/routes/approval-routes.ts +1 -1
  399. package/src/runtime/routes/attachment-routes.ts +26 -15
  400. package/src/runtime/routes/avatar-routes.ts +26 -0
  401. package/src/runtime/routes/browser-routes.ts +1 -1
  402. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  403. package/src/runtime/routes/btw-routes.ts +29 -23
  404. package/src/runtime/routes/consolidation-routes.ts +120 -20
  405. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  406. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  407. package/src/runtime/routes/conversation-query-routes.ts +3 -1
  408. package/src/runtime/routes/conversation-routes.ts +372 -185
  409. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  410. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  411. package/src/runtime/routes/documents-routes.ts +4 -0
  412. package/src/runtime/routes/domain-routes.ts +51 -37
  413. package/src/runtime/routes/epoch-millis-range.ts +34 -0
  414. package/src/runtime/routes/events-routes.ts +28 -34
  415. package/src/runtime/routes/gateway-log-routes.ts +26 -4
  416. package/src/runtime/routes/heartbeat-routes.ts +32 -12
  417. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  418. package/src/runtime/routes/host-cu-routes.ts +1 -1
  419. package/src/runtime/routes/identity-intro-cache.ts +11 -34
  420. package/src/runtime/routes/identity-routes.ts +224 -18
  421. package/src/runtime/routes/image-generation-routes.ts +40 -2
  422. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  423. package/src/runtime/routes/index.ts +2 -0
  424. package/src/runtime/routes/integrations/a2a.ts +12 -10
  425. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
  426. package/src/runtime/routes/integrations/slack/channel.ts +4 -0
  427. package/src/runtime/routes/integrations/slack/share.ts +27 -6
  428. package/src/runtime/routes/integrations/telegram.ts +6 -0
  429. package/src/runtime/routes/integrations/twilio.ts +42 -0
  430. package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
  431. package/src/runtime/routes/log-export-routes.ts +8 -0
  432. package/src/runtime/routes/memory-v2-routes.ts +15 -8
  433. package/src/runtime/routes/memory-v3-routes.ts +66 -34
  434. package/src/runtime/routes/oauth-apps.ts +66 -12
  435. package/src/runtime/routes/oauth-providers.ts +44 -5
  436. package/src/runtime/routes/platform-routes.ts +81 -5
  437. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
  438. package/src/runtime/routes/playground/force-compact.ts +1 -1
  439. package/src/runtime/routes/playground/helpers.ts +1 -1
  440. package/src/runtime/routes/rename-conversation-routes.ts +5 -0
  441. package/src/runtime/routes/schedule-routes.ts +152 -42
  442. package/src/runtime/routes/secret-routes.ts +14 -2
  443. package/src/runtime/routes/skills-routes.ts +43 -14
  444. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  445. package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
  446. package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
  447. package/src/runtime/routes/trust-rules-routes.ts +26 -2
  448. package/src/runtime/routes/tts-routes.ts +35 -0
  449. package/src/runtime/routes/types.ts +66 -8
  450. package/src/runtime/routes/usage-routes.ts +47 -39
  451. package/src/runtime/routes/webhook-routes.ts +41 -2
  452. package/src/runtime/routes/work-items-routes.ts +2 -4
  453. package/src/runtime/routes/workspace-routes.ts +4 -0
  454. package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
  455. package/src/runtime/services/analyze-conversation.ts +2 -2
  456. package/src/runtime/services/conversation-serializer.ts +1 -1
  457. package/src/schedule/schedule-store.ts +20 -1
  458. package/src/schedule/schedule-usage-store.ts +83 -0
  459. package/src/schedule/scheduler.ts +12 -5
  460. package/src/signals/cancel.ts +2 -4
  461. package/src/skills/catalog-files.ts +2 -2
  462. package/src/skills/catalog-install.ts +3 -0
  463. package/src/skills/categories-cache.ts +118 -0
  464. package/src/skills/clawhub-files.ts +1 -2
  465. package/src/skills/skillssh-files.ts +1 -2
  466. package/src/subagent/manager.ts +17 -5
  467. package/src/telemetry/types.ts +29 -1
  468. package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
  469. package/src/telemetry/usage-telemetry-reporter.ts +57 -2
  470. package/src/tools/acp/context.ts +20 -0
  471. package/src/tools/acp/list-agents.test.ts +7 -1
  472. package/src/tools/acp/spawn.test.ts +158 -55
  473. package/src/tools/acp/spawn.ts +47 -72
  474. package/src/tools/acp/steer.test.ts +105 -8
  475. package/src/tools/acp/steer.ts +48 -17
  476. package/src/tools/apps/executors.ts +13 -8
  477. package/src/tools/executor.ts +1 -53
  478. package/src/tools/filesystem/write.ts +34 -0
  479. package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
  480. package/src/tools/network/__tests__/web-search.test.ts +11 -3
  481. package/src/tools/network/web-search-error.test.ts +248 -0
  482. package/src/tools/network/web-search-error.ts +267 -0
  483. package/src/tools/network/web-search.ts +207 -48
  484. package/src/tools/schedule/create.ts +2 -0
  485. package/src/tools/subagent/spawn.ts +2 -4
  486. package/src/tools/terminal/safe-env.ts +10 -1
  487. package/src/tools/ui-surface/definitions.ts +34 -5
  488. package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
  489. package/src/tts/provider-catalog.ts +76 -1
  490. package/src/util/mutex.ts +47 -0
  491. package/src/workspace/git-service.ts +1 -42
  492. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  493. package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
  494. package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
  495. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
  496. package/src/workspace/migrations/registry.ts +6 -0
  497. package/docs/plugins.md +0 -836
  498. package/examples/plugins/echo/register.ts +0 -184
  499. package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
  500. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  501. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  502. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  503. package/src/__tests__/empty-response-pipeline.test.ts +0 -423
  504. package/src/__tests__/llm-call-pipeline.test.ts +0 -287
  505. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
  506. package/src/__tests__/persistence-pipeline.test.ts +0 -503
  507. package/src/__tests__/pipeline-runner.test.ts +0 -564
  508. package/src/__tests__/title-generate-pipeline.test.ts +0 -211
  509. package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
  510. package/src/__tests__/tool-error-pipeline.test.ts +0 -241
  511. package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
  512. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
  513. package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
  514. package/src/gallery/default-gallery.ts +0 -1359
  515. package/src/gallery/gallery-manifest.ts +0 -28
  516. package/src/home/feature-gate.ts +0 -22
  517. package/src/memory/v3/provider-blocks.ts +0 -16
  518. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  519. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  520. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  521. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  522. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  523. package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
  524. package/src/plugins/defaults/empty-response/terminal.ts +0 -106
  525. package/src/plugins/defaults/injectors/package.json +0 -15
  526. package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
  527. package/src/plugins/defaults/llm-call/register.ts +0 -45
  528. package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
  529. package/src/plugins/defaults/memory-retrieval/package.json +0 -15
  530. package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
  531. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  532. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  533. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  534. package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
  535. package/src/plugins/defaults/persistence/package.json +0 -15
  536. package/src/plugins/defaults/persistence/register.ts +0 -38
  537. package/src/plugins/defaults/persistence/terminal.ts +0 -83
  538. package/src/plugins/defaults/title-generate/terminal.ts +0 -31
  539. package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
  540. package/src/plugins/defaults/token-estimate/package.json +0 -15
  541. package/src/plugins/defaults/token-estimate/register.ts +0 -34
  542. package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
  543. package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
  544. package/src/plugins/defaults/tool-error/terminal.ts +0 -47
  545. package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
  546. package/src/plugins/defaults/tool-execute/package.json +0 -15
  547. package/src/plugins/defaults/tool-execute/register.ts +0 -49
  548. package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
  549. package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
  550. package/src/skills/category-inference.ts +0 -111
  551. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
  552. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
  553. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  554. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  555. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
  556. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
  557. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
  558. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +0 -0
  559. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  560. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  561. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/core.ts +0 -0
  562. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  563. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  564. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  565. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  566. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  567. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  568. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
  569. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
  570. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
@@ -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 () => {
@@ -16,7 +16,7 @@ mock.module("../util/logger.js", () => ({
16
16
  }));
17
17
 
18
18
  const _conversationMocks = new Map<string, unknown>();
19
- mock.module("../daemon/conversation-store.js", () => ({
19
+ mock.module("../daemon/conversation-registry.js", () => ({
20
20
  findConversation: (id: string) => _conversationMocks.get(id),
21
21
  }));
22
22
 
@@ -11,7 +11,7 @@ mock.module("../util/logger.js", () => ({
11
11
 
12
12
  // Map conversationId → mock session so findConversation returns the right mock.
13
13
  const conversationMocks = new Map<string, unknown>();
14
- mock.module("../daemon/conversation-store.js", () => ({
14
+ mock.module("../daemon/conversation-registry.js", () => ({
15
15
  findConversation: (id: string) => conversationMocks.get(id),
16
16
  }));
17
17
 
@@ -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>;
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Tests for the `CompactionCircuit` class.
3
+ *
4
+ * The breaker logic lives as direct methods on the per-conversation
5
+ * `CompactionCircuit` state container (`agent/compaction-circuit.ts`). These
6
+ * tests assert the threshold (3 consecutive failures) and cooldown (1 hour)
7
+ * exactly match the user-visible behavior:
8
+ * (a) counter increments on each failure outcome
9
+ * (b) circuit opens after exactly 3 consecutive failures
10
+ * (c) successful compaction resets counter and clears the circuit
11
+ * (d) isOpen() reflects state and cooldown expiry
12
+ * (e) circuit re-opens after cooldown expiry when 3 more failures
13
+ * accumulate (guards the stale-timestamp regression)
14
+ * (f) isOpen() is query-only and never mutates the counter
15
+ * (g) open→closed transition emits `compaction_circuit_closed` exactly once
16
+ * (h) closed→closed transition emits nothing
17
+ */
18
+
19
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
20
+
21
+ import {
22
+ COMPACTION_CIRCUIT_COOLDOWN_MS,
23
+ COMPACTION_CIRCUIT_FAILURE_THRESHOLD,
24
+ CompactionCircuit,
25
+ } from "../agent/compaction-circuit.js";
26
+ import type { CompactionCircuitEvent } from "../plugins/types.js";
27
+
28
+ // ─── Fixtures ───────────────────────────────────────────────────────────────
29
+
30
+ const CONVERSATION_ID = "conv-breaker-test";
31
+
32
+ function collectEvents(): {
33
+ events: CompactionCircuitEvent[];
34
+ onEvent: (msg: CompactionCircuitEvent) => void;
35
+ } {
36
+ const events: CompactionCircuitEvent[] = [];
37
+ return { events, onEvent: (msg) => events.push(msg) };
38
+ }
39
+
40
+ describe("CompactionCircuit", () => {
41
+ let originalDateNow: () => number;
42
+
43
+ beforeEach(() => {
44
+ originalDateNow = Date.now;
45
+ });
46
+
47
+ afterEach(() => {
48
+ Date.now = originalDateNow;
49
+ });
50
+
51
+ test("threshold and cooldown constants match the user-visible contract", () => {
52
+ expect(COMPACTION_CIRCUIT_FAILURE_THRESHOLD).toBe(3);
53
+ expect(COMPACTION_CIRCUIT_COOLDOWN_MS).toBe(60 * 60 * 1000);
54
+ });
55
+
56
+ test("(a) counter increments on each failure outcome", async () => {
57
+ const circuit = new CompactionCircuit(CONVERSATION_ID);
58
+ const { onEvent, events } = collectEvents();
59
+
60
+ await circuit.recordOutcome(true, onEvent);
61
+ expect(circuit.consecutiveCompactionFailures).toBe(1);
62
+ expect(circuit.compactionCircuitOpenUntil).toBeNull();
63
+ expect(events).toHaveLength(0);
64
+
65
+ await circuit.recordOutcome(true, onEvent);
66
+ expect(circuit.consecutiveCompactionFailures).toBe(2);
67
+ expect(circuit.compactionCircuitOpenUntil).toBeNull();
68
+ expect(events).toHaveLength(0);
69
+ });
70
+
71
+ test("(b) circuit opens after exactly 3 consecutive failures", async () => {
72
+ const fixedNow = 1_700_000_000_000;
73
+ Date.now = () => fixedNow;
74
+
75
+ const circuit = new CompactionCircuit(CONVERSATION_ID);
76
+ const { onEvent, events } = collectEvents();
77
+
78
+ await circuit.recordOutcome(true, onEvent);
79
+ await circuit.recordOutcome(true, onEvent);
80
+ // Two failures — circuit still closed.
81
+ expect(circuit.compactionCircuitOpenUntil).toBeNull();
82
+ expect(events).toHaveLength(0);
83
+
84
+ await circuit.recordOutcome(true, onEvent);
85
+ // Third failure — circuit trips and fires the event exactly once.
86
+ expect(circuit.consecutiveCompactionFailures).toBe(3);
87
+ expect(circuit.compactionCircuitOpenUntil).toBe(fixedNow + 60 * 60 * 1000);
88
+ expect(await circuit.isOpen()).toBe(true);
89
+ expect(events).toHaveLength(1);
90
+ expect(events[0]).toEqual({
91
+ type: "compaction_circuit_open",
92
+ conversationId: CONVERSATION_ID,
93
+ reason: "3_consecutive_failures",
94
+ openUntil: fixedNow + 60 * 60 * 1000,
95
+ });
96
+
97
+ // Further failures do not re-fire the event while the circuit is open.
98
+ await circuit.recordOutcome(true, onEvent);
99
+ expect(circuit.consecutiveCompactionFailures).toBe(4);
100
+ expect(events).toHaveLength(1);
101
+ });
102
+
103
+ test("(c) successful outcome resets counter and clears circuit", async () => {
104
+ const fixedNow = 1_700_000_000_000;
105
+ Date.now = () => fixedNow;
106
+
107
+ const circuit = new CompactionCircuit(CONVERSATION_ID);
108
+ const { onEvent } = collectEvents();
109
+
110
+ // Trip the breaker.
111
+ await circuit.recordOutcome(true, onEvent);
112
+ await circuit.recordOutcome(true, onEvent);
113
+ await circuit.recordOutcome(true, onEvent);
114
+ expect(circuit.compactionCircuitOpenUntil).not.toBeNull();
115
+
116
+ // Success resets state.
117
+ await circuit.recordOutcome(false, onEvent);
118
+ expect(circuit.consecutiveCompactionFailures).toBe(0);
119
+ expect(circuit.compactionCircuitOpenUntil).toBeNull();
120
+ });
121
+
122
+ test("(d) isOpen() reflects state and expiry", async () => {
123
+ const fixedNow = 1_700_000_000_000;
124
+ Date.now = () => fixedNow;
125
+
126
+ const circuit = new CompactionCircuit(CONVERSATION_ID);
127
+ const { onEvent } = collectEvents();
128
+
129
+ // Fresh state: closed.
130
+ expect(await circuit.isOpen()).toBe(false);
131
+
132
+ // Trip the breaker.
133
+ await circuit.recordOutcome(true, onEvent);
134
+ await circuit.recordOutcome(true, onEvent);
135
+ await circuit.recordOutcome(true, onEvent);
136
+
137
+ // While open.
138
+ expect(await circuit.isOpen()).toBe(true);
139
+
140
+ // After cooldown expires the breaker reports closed again, even without
141
+ // an explicit reset — the open-until timestamp is the only source of
142
+ // truth for the gate.
143
+ Date.now = () => fixedNow + 60 * 60 * 1000 + 1;
144
+ expect(await circuit.isOpen()).toBe(false);
145
+ });
146
+
147
+ test("(e) circuit re-opens after cooldown expiry when 3 more failures accumulate", async () => {
148
+ // Regression: opening the breaker a second time must not require
149
+ // `compactionCircuitOpenUntil === null`. Once a cooldown expires, the
150
+ // gate correctly reports "closed" but a stale past-timestamp stays on the
151
+ // state, so the next 3-strike window must still trip a new cooldown. Any
152
+ // expired timestamp is treated the same as null.
153
+ const t0 = 1_700_000_000_000;
154
+ Date.now = () => t0;
155
+
156
+ const circuit = new CompactionCircuit(CONVERSATION_ID);
157
+ const { onEvent, events } = collectEvents();
158
+
159
+ // Trip the breaker the first time.
160
+ await circuit.recordOutcome(true, onEvent);
161
+ await circuit.recordOutcome(true, onEvent);
162
+ await circuit.recordOutcome(true, onEvent);
163
+ expect(circuit.compactionCircuitOpenUntil).toBe(t0 + 60 * 60 * 1000);
164
+ expect(events).toHaveLength(1);
165
+
166
+ // Advance past the cooldown window. Reset the counter — in production this
167
+ // happens when a subsequent `maybeCompact` call succeeds after the
168
+ // cooldown elapses, but the bug manifests even when the counter is reset:
169
+ // the stale `compactionCircuitOpenUntil` is what breaks re-opening.
170
+ const t1 = t0 + 60 * 60 * 1000 + 1;
171
+ Date.now = () => t1;
172
+ expect(await circuit.isOpen()).toBe(false);
173
+ circuit.consecutiveCompactionFailures = 0;
174
+ // `compactionCircuitOpenUntil` is deliberately left as the old timestamp
175
+ // to reproduce the bug condition.
176
+ expect(circuit.compactionCircuitOpenUntil).toBe(t0 + 60 * 60 * 1000);
177
+
178
+ // Three more failures must trip a fresh cooldown even though the old
179
+ // timestamp is still set.
180
+ await circuit.recordOutcome(true, onEvent);
181
+ await circuit.recordOutcome(true, onEvent);
182
+ await circuit.recordOutcome(true, onEvent);
183
+ expect(circuit.consecutiveCompactionFailures).toBe(3);
184
+ expect(circuit.compactionCircuitOpenUntil).toBe(t1 + 60 * 60 * 1000);
185
+ expect(events).toHaveLength(2);
186
+ expect(events[1]).toEqual({
187
+ type: "compaction_circuit_open",
188
+ conversationId: CONVERSATION_ID,
189
+ reason: "3_consecutive_failures",
190
+ openUntil: t1 + 60 * 60 * 1000,
191
+ });
192
+ });
193
+
194
+ test("(f) isOpen() is query-only and never mutates the counter", async () => {
195
+ // `maybeCompact()` early-return paths skip `recordOutcome` entirely and
196
+ // only gate on `isOpen()`. The query must never touch the 3-strike
197
+ // counter so those early returns can't silently reset it.
198
+ const circuit = new CompactionCircuit(CONVERSATION_ID);
199
+ const { onEvent } = collectEvents();
200
+
201
+ await circuit.recordOutcome(true, onEvent);
202
+ await circuit.recordOutcome(true, onEvent);
203
+ expect(circuit.consecutiveCompactionFailures).toBe(2);
204
+
205
+ // Query-only — must NOT touch the counter.
206
+ expect(await circuit.isOpen()).toBe(false);
207
+ expect(circuit.consecutiveCompactionFailures).toBe(2);
208
+
209
+ // A third real failure then trips the breaker as expected.
210
+ await circuit.recordOutcome(true, onEvent);
211
+ expect(circuit.consecutiveCompactionFailures).toBe(3);
212
+ expect(circuit.compactionCircuitOpenUntil).not.toBeNull();
213
+ });
214
+
215
+ test("(g) open→closed transition emits compaction_circuit_closed exactly once", async () => {
216
+ // Regression: the reset branch must notify the client on open→closed.
217
+ // Otherwise the Swift banner set from `compaction_circuit_open` stays
218
+ // visible until the original `openUntil` deadline (up to 1h),
219
+ // misrepresenting the live state.
220
+ const fixedNow = 1_700_000_000_000;
221
+ Date.now = () => fixedNow;
222
+
223
+ const circuit = new CompactionCircuit(CONVERSATION_ID);
224
+ const { onEvent, events } = collectEvents();
225
+
226
+ // Force the circuit into the open state directly — the emitted-event
227
+ // transition logic is what we're testing, not the tripping path.
228
+ circuit.compactionCircuitOpenUntil = fixedNow + 60 * 60 * 1000;
229
+ circuit.consecutiveCompactionFailures = 3;
230
+
231
+ await circuit.recordOutcome(false, onEvent);
232
+
233
+ expect(circuit.consecutiveCompactionFailures).toBe(0);
234
+ expect(circuit.compactionCircuitOpenUntil).toBeNull();
235
+ expect(events).toHaveLength(1);
236
+ expect(events[0]).toEqual({
237
+ type: "compaction_circuit_closed",
238
+ conversationId: CONVERSATION_ID,
239
+ });
240
+ });
241
+
242
+ test("(h) successful outcome against an already-closed circuit emits no event", async () => {
243
+ // Emitting `compaction_circuit_closed` on every successful compaction
244
+ // would spam the client (the breaker is closed in the common case).
245
+ // Only the open→closed transition is meaningful.
246
+ const circuit = new CompactionCircuit(CONVERSATION_ID);
247
+ const { onEvent, events } = collectEvents();
248
+
249
+ expect(circuit.compactionCircuitOpenUntil).toBeNull();
250
+ await circuit.recordOutcome(false, onEvent);
251
+ expect(circuit.compactionCircuitOpenUntil).toBeNull();
252
+ expect(events).toHaveLength(0);
253
+
254
+ // A second successful outcome while still closed — still no event.
255
+ await circuit.recordOutcome(false, onEvent);
256
+ expect(events).toHaveLength(0);
257
+ });
258
+ });