@vellumai/assistant 0.8.7 → 0.8.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (387) hide show
  1. package/Dockerfile +20 -4
  2. package/docker-entrypoint.sh +4 -2
  3. package/docker-init-apt-root.sh +3 -1
  4. package/docker-kata-apt-env.sh +3 -1
  5. package/docker-kata-runtime-family.sh +12 -0
  6. package/docs/architecture/memory.md +1 -1
  7. package/docs/plugins.md +75 -79
  8. package/examples/plugins/echo/README.md +6 -12
  9. package/examples/plugins/echo/register.ts +0 -41
  10. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
  11. package/openapi.yaml +3381 -348
  12. package/package.json +1 -1
  13. package/scripts/generate-openapi.ts +68 -41
  14. package/src/__tests__/agent-loop-exit-reason.test.ts +34 -39
  15. package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
  16. package/src/__tests__/agent-loop.test.ts +37 -87
  17. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  18. package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
  19. package/src/__tests__/annotate-risk-options.test.ts +2 -3
  20. package/src/__tests__/anthropic-provider.test.ts +95 -2
  21. package/src/__tests__/assistant-event-hub.test.ts +25 -0
  22. package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
  23. package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
  24. package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
  25. package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
  26. package/src/__tests__/btw-routes.test.ts +62 -3
  27. package/src/__tests__/build-persisted-content.test.ts +184 -0
  28. package/src/__tests__/catalog-files.test.ts +1 -1
  29. package/src/__tests__/clawhub-files.test.ts +1 -1
  30. package/src/__tests__/compaction-pipeline.test.ts +1 -1
  31. package/src/__tests__/compaction.benchmark.test.ts +0 -30
  32. package/src/__tests__/config-watcher.test.ts +1 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
  34. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -2
  35. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -4
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +313 -1136
  37. package/src/__tests__/conversation-agent-loop.test.ts +596 -1616
  38. package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
  39. package/src/__tests__/conversation-history-web-search.test.ts +11 -1
  40. package/src/__tests__/conversation-pairing.test.ts +4 -31
  41. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
  42. package/src/__tests__/conversation-provider-retry-repair.test.ts +26 -5
  43. package/src/__tests__/conversation-queue.test.ts +2 -0
  44. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
  45. package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
  46. package/src/__tests__/conversation-runtime-assembly.test.ts +170 -229
  47. package/src/__tests__/conversation-runtime-workspace.test.ts +3 -24
  48. package/src/__tests__/conversation-slash-commands.test.ts +8 -42
  49. package/src/__tests__/conversation-slash-queue.test.ts +6 -1
  50. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
  51. package/src/__tests__/conversation-sync-tags.test.ts +27 -15
  52. package/src/__tests__/conversation-title-service.test.ts +135 -2
  53. package/src/__tests__/conversation-workspace-injection.test.ts +6 -1
  54. package/src/__tests__/cross-provider-web-search.test.ts +214 -1
  55. package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
  56. package/src/__tests__/dm-persistence.test.ts +5 -1
  57. package/src/__tests__/empty-response-hook.test.ts +304 -0
  58. package/src/__tests__/feature-flag-test-helpers.ts +2 -2
  59. package/src/__tests__/gemini-image-service.test.ts +13 -0
  60. package/src/__tests__/helpers/mock-provider.ts +110 -0
  61. package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
  62. package/src/__tests__/history-repair-hook.test.ts +1 -0
  63. package/src/__tests__/identity-intro-cache.test.ts +12 -100
  64. package/src/__tests__/identity-routes.test.ts +248 -7
  65. package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
  66. package/src/__tests__/injector-background-turn.test.ts +2 -8
  67. package/src/__tests__/injector-chain.test.ts +106 -270
  68. package/src/__tests__/injector-disk-pressure.test.ts +3 -12
  69. package/src/__tests__/injector-document-comments.test.ts +2 -2
  70. package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
  71. package/src/__tests__/injector-v3-suppression.test.ts +31 -37
  72. package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
  73. package/src/__tests__/list-messages-page-latest.test.ts +60 -0
  74. package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
  75. package/src/__tests__/llm-usage-store.test.ts +223 -1
  76. package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
  77. package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
  78. package/src/__tests__/native-web-search.test.ts +191 -0
  79. package/src/__tests__/onboarding-template-contract.test.ts +2 -0
  80. package/src/__tests__/openai-image-service.test.ts +17 -0
  81. package/src/__tests__/openai-provider.test.ts +31 -1
  82. package/src/__tests__/persist-unsendable-image.test.ts +215 -0
  83. package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
  84. package/src/__tests__/pipeline-runner.test.ts +29 -39
  85. package/src/__tests__/pkb-autoinject.test.ts +2 -5
  86. package/src/__tests__/plugin-bootstrap.test.ts +13 -28
  87. package/src/__tests__/plugin-registry.test.ts +0 -27
  88. package/src/__tests__/plugin-types.test.ts +2 -125
  89. package/src/__tests__/process-message-display-content.test.ts +6 -2
  90. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
  91. package/src/__tests__/resolve-trust-class.test.ts +4 -4
  92. package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
  93. package/src/__tests__/schedule-routes.test.ts +603 -2
  94. package/src/__tests__/schedule-store.test.ts +41 -0
  95. package/src/__tests__/schedule-tools.test.ts +35 -0
  96. package/src/__tests__/server-history-render.test.ts +314 -1
  97. package/src/__tests__/skillssh-files.test.ts +1 -1
  98. package/src/__tests__/system-prompt.test.ts +20 -0
  99. package/src/__tests__/task-scheduler.test.ts +162 -1
  100. package/src/__tests__/terminal-tools.test.ts +6 -1
  101. package/src/__tests__/title-generate-hook.test.ts +319 -0
  102. package/src/__tests__/tool-error-hook.test.ts +278 -0
  103. package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
  104. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  105. package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
  106. package/src/__tests__/tool-result-truncation.test.ts +0 -2
  107. package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
  108. package/src/__tests__/ui-work-result-surface.test.ts +159 -0
  109. package/src/__tests__/usage-routes.test.ts +285 -1
  110. package/src/__tests__/user-plugin-loader.test.ts +2 -2
  111. package/src/__tests__/voice-session-bridge.test.ts +6 -3
  112. package/src/__tests__/web-search-backend-failure.test.ts +166 -0
  113. package/src/agent/loop.ts +346 -442
  114. package/src/api/events/assistant-thinking-delta.ts +33 -0
  115. package/src/api/events/tool-output-chunk.ts +45 -0
  116. package/src/api/events/tool-use-preview-start.ts +32 -0
  117. package/src/api/events/trace-event.ts +69 -0
  118. package/src/api/index.ts +48 -13
  119. package/src/api/responses/conversation-message.ts +368 -0
  120. package/src/avatar/__tests__/avatar-store.test.ts +34 -29
  121. package/src/cli/commands/__tests__/notifications.test.ts +58 -14
  122. package/src/cli/commands/notifications.ts +112 -60
  123. package/src/config/assistant-feature-flags.ts +22 -11
  124. package/src/config/bundled-skills/app-builder/SKILL.md +3 -20
  125. package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
  126. package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
  127. package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
  128. package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
  129. package/src/config/bundled-skills/document-editor/SKILL.md +1 -1
  130. package/src/config/bundled-skills/messaging/SKILL.md +0 -7
  131. package/src/config/feature-flag-cache.ts +3 -3
  132. package/src/config/feature-flag-registry.json +35 -3
  133. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
  134. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
  135. package/src/config/schemas/llm.ts +1 -0
  136. package/src/config/schemas/memory-v2.ts +8 -0
  137. package/src/config/schemas/memory-v3.ts +8 -0
  138. package/src/config/schemas/platform.ts +8 -0
  139. package/src/config/seed-inference-profiles.ts +2 -2
  140. package/src/config/skills.ts +13 -0
  141. package/src/context/compactor.ts +1 -1
  142. package/src/context/strip-injections.ts +122 -0
  143. package/src/context/token-estimator.ts +23 -0
  144. package/src/context/tool-result-truncation.ts +0 -23
  145. package/src/context/window-manager.ts +3 -6
  146. package/src/credential-execution/executable-discovery.ts +16 -0
  147. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
  148. package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
  149. package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
  150. package/src/daemon/assistant-attachments.ts +1 -1
  151. package/src/daemon/config-watcher.ts +2 -2
  152. package/src/daemon/context-overflow-reducer.ts +0 -1
  153. package/src/daemon/conversation-agent-loop-handlers.ts +605 -153
  154. package/src/daemon/conversation-agent-loop.ts +281 -760
  155. package/src/daemon/conversation-history.ts +5 -4
  156. package/src/daemon/conversation-lifecycle.ts +3 -4
  157. package/src/daemon/conversation-messaging.ts +7 -6
  158. package/src/daemon/conversation-process.ts +11 -16
  159. package/src/daemon/conversation-runtime-assembly.ts +130 -347
  160. package/src/daemon/conversation-slash.ts +6 -25
  161. package/src/daemon/conversation-surfaces.ts +222 -4
  162. package/src/daemon/conversation-tool-setup.ts +2 -29
  163. package/src/daemon/conversation.ts +32 -14
  164. package/src/daemon/external-plugins-bootstrap.ts +9 -10
  165. package/src/daemon/handlers/config-a2a.ts +51 -36
  166. package/src/daemon/handlers/config-slack-channel.ts +20 -14
  167. package/src/daemon/handlers/config-telegram.ts +16 -2
  168. package/src/daemon/handlers/shared.ts +156 -84
  169. package/src/daemon/handlers/skills.ts +39 -10
  170. package/src/daemon/lifecycle.ts +4 -0
  171. package/src/daemon/message-types/apps.ts +1 -29
  172. package/src/daemon/message-types/messages.ts +9 -57
  173. package/src/daemon/message-types/skills.ts +2 -0
  174. package/src/daemon/message-types/surfaces.ts +136 -3
  175. package/src/daemon/now-scratchpad.ts +21 -0
  176. package/src/daemon/orphan-reaper.test.ts +210 -0
  177. package/src/daemon/orphan-reaper.ts +240 -0
  178. package/src/daemon/persist-unsendable-image.ts +117 -0
  179. package/src/daemon/process-message.ts +1 -3
  180. package/src/daemon/trace-emitter.ts +6 -4
  181. package/src/daemon/trust-context.ts +19 -0
  182. package/src/daemon/wake-target-adapter.ts +3 -1
  183. package/src/home/home-greeting-cache.ts +24 -1
  184. package/src/ipc/gateway-client.test.ts +2 -2
  185. package/src/ipc/gateway-client.ts +3 -3
  186. package/src/media/gemini-image-service.ts +15 -0
  187. package/src/media/openai-image-service.ts +14 -0
  188. package/src/media/types.ts +34 -0
  189. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
  190. package/src/memory/auth-fallback-events-store.ts +94 -0
  191. package/src/memory/conversation-title-service.ts +65 -41
  192. package/src/memory/db-init.ts +4 -0
  193. package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
  194. package/src/memory/graph/conversation-graph-memory.ts +65 -0
  195. package/src/memory/jobs-store.ts +33 -0
  196. package/src/memory/jobs-worker.ts +31 -4
  197. package/src/memory/llm-usage-store.ts +224 -50
  198. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
  199. package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
  200. package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
  201. package/src/memory/migrations/index.ts +2 -0
  202. package/src/memory/pkb/autoinject.ts +61 -0
  203. package/src/memory/pkb/context.ts +50 -0
  204. package/src/memory/pkb/types.ts +14 -0
  205. package/src/memory/schedule-attribution-sql.ts +104 -0
  206. package/src/memory/schema/infrastructure.ts +16 -0
  207. package/src/memory/usage-grouped-buckets.ts +6 -1
  208. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -1
  209. package/src/memory/v2/consolidation-job.ts +1 -1
  210. package/src/memory/v3/__tests__/health.test.ts +16 -0
  211. package/src/memory/v3/__tests__/orchestrate.test.ts +45 -9
  212. package/src/memory/v3/__tests__/provider-blocks.test.ts +13 -0
  213. package/src/memory/v3/__tests__/router.test.ts +101 -29
  214. package/src/memory/v3/__tests__/selector.test.ts +93 -27
  215. package/src/memory/v3/__tests__/shadow-plugin.test.ts +23 -5
  216. package/src/memory/v3/health.ts +0 -0
  217. package/src/memory/v3/llm-retry.ts +32 -0
  218. package/src/memory/v3/orchestrate.ts +26 -14
  219. package/src/memory/v3/provider-blocks.ts +15 -5
  220. package/src/memory/v3/router.ts +48 -42
  221. package/src/memory/v3/selector.ts +57 -42
  222. package/src/memory/v3/shadow-plugin.ts +47 -15
  223. package/src/memory/v3/types.ts +8 -0
  224. package/src/notifications/conversation-pairing.ts +8 -15
  225. package/src/notifications/decision-engine.ts +6 -3
  226. package/src/notifications/home-feed-side-effect.ts +12 -1
  227. package/src/permissions/prompter.ts +4 -0
  228. package/src/plugin-api/constants.ts +4 -0
  229. package/src/plugin-api/index.ts +8 -1
  230. package/src/plugin-api/types.ts +151 -1
  231. package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
  232. package/src/plugins/defaults/empty-response/register.ts +8 -13
  233. package/src/plugins/defaults/index.ts +1 -15
  234. package/src/plugins/defaults/injectors/register.ts +243 -74
  235. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +91 -0
  236. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
  237. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
  238. package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
  239. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
  240. package/src/plugins/defaults/title-generate/package.json +1 -1
  241. package/src/plugins/defaults/title-generate/register.ts +18 -18
  242. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
  243. package/src/plugins/defaults/tool-error/package.json +1 -1
  244. package/src/plugins/defaults/tool-error/register.ts +9 -21
  245. package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
  246. package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
  247. package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
  248. package/src/plugins/pipeline.ts +6 -18
  249. package/src/plugins/registry.ts +8 -25
  250. package/src/plugins/types.ts +43 -474
  251. package/src/proactive-artifact/aux-message-injector.ts +3 -3
  252. package/src/proactive-artifact/job.test.ts +7 -12
  253. package/src/prompts/__tests__/system-prompt.test.ts +36 -0
  254. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +62 -0
  255. package/src/prompts/templates/BOOTSTRAP.md +2 -2
  256. package/src/prompts/templates/system-sections.ts +15 -0
  257. package/src/providers/anthropic/client.ts +37 -29
  258. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
  259. package/src/providers/openai/chat-completions-provider.ts +44 -0
  260. package/src/providers/openrouter/client.ts +1 -0
  261. package/src/providers/placeholder-sentinels.ts +35 -0
  262. package/src/runtime/__tests__/agent-wake.test.ts +5 -1
  263. package/src/runtime/agent-wake.ts +2 -2
  264. package/src/runtime/assistant-event-hub.ts +36 -6
  265. package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
  266. package/src/runtime/http-router.ts +16 -21
  267. package/src/runtime/http-types.ts +16 -70
  268. package/src/runtime/pending-interactions.ts +1 -0
  269. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
  270. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
  271. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
  272. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  273. package/src/runtime/routes/app-management-routes.ts +6 -117
  274. package/src/runtime/routes/app-routes.ts +13 -15
  275. package/src/runtime/routes/attachment-routes.ts +26 -15
  276. package/src/runtime/routes/avatar-routes.ts +26 -0
  277. package/src/runtime/routes/btw-routes.ts +29 -23
  278. package/src/runtime/routes/consolidation-routes.ts +120 -20
  279. package/src/runtime/routes/conversation-query-routes.ts +2 -0
  280. package/src/runtime/routes/conversation-routes.ts +358 -184
  281. package/src/runtime/routes/documents-routes.ts +4 -0
  282. package/src/runtime/routes/domain-routes.ts +51 -37
  283. package/src/runtime/routes/epoch-millis-range.ts +34 -0
  284. package/src/runtime/routes/events-routes.ts +28 -34
  285. package/src/runtime/routes/gateway-log-routes.ts +26 -4
  286. package/src/runtime/routes/heartbeat-routes.ts +32 -12
  287. package/src/runtime/routes/identity-intro-cache.ts +11 -34
  288. package/src/runtime/routes/identity-routes.ts +208 -17
  289. package/src/runtime/routes/image-generation-routes.ts +40 -2
  290. package/src/runtime/routes/index.ts +2 -0
  291. package/src/runtime/routes/integrations/a2a.ts +12 -10
  292. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
  293. package/src/runtime/routes/integrations/slack/channel.ts +4 -0
  294. package/src/runtime/routes/integrations/slack/share.ts +27 -6
  295. package/src/runtime/routes/integrations/telegram.ts +6 -0
  296. package/src/runtime/routes/integrations/twilio.ts +42 -0
  297. package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
  298. package/src/runtime/routes/log-export-routes.ts +8 -0
  299. package/src/runtime/routes/memory-v2-routes.ts +15 -8
  300. package/src/runtime/routes/memory-v3-routes.ts +50 -28
  301. package/src/runtime/routes/oauth-apps.ts +66 -12
  302. package/src/runtime/routes/oauth-providers.ts +44 -5
  303. package/src/runtime/routes/platform-routes.ts +81 -5
  304. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
  305. package/src/runtime/routes/playground/force-compact.ts +1 -1
  306. package/src/runtime/routes/rename-conversation-routes.ts +5 -0
  307. package/src/runtime/routes/schedule-routes.ts +152 -42
  308. package/src/runtime/routes/secret-routes.ts +14 -2
  309. package/src/runtime/routes/skills-routes.ts +43 -14
  310. package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
  311. package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
  312. package/src/runtime/routes/trust-rules-routes.ts +26 -2
  313. package/src/runtime/routes/tts-routes.ts +35 -0
  314. package/src/runtime/routes/types.ts +66 -8
  315. package/src/runtime/routes/usage-routes.ts +47 -39
  316. package/src/runtime/routes/webhook-routes.ts +41 -2
  317. package/src/runtime/routes/workspace-routes.ts +4 -0
  318. package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
  319. package/src/runtime/services/analyze-conversation.ts +2 -2
  320. package/src/schedule/schedule-store.ts +20 -1
  321. package/src/schedule/schedule-usage-store.ts +83 -0
  322. package/src/schedule/scheduler.ts +12 -5
  323. package/src/skills/catalog-files.ts +2 -2
  324. package/src/skills/catalog-install.ts +3 -0
  325. package/src/skills/categories-cache.ts +118 -0
  326. package/src/skills/clawhub-files.ts +1 -2
  327. package/src/skills/skillssh-files.ts +1 -2
  328. package/src/telemetry/types.ts +29 -1
  329. package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
  330. package/src/telemetry/usage-telemetry-reporter.ts +57 -2
  331. package/src/tools/executor.ts +1 -53
  332. package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
  333. package/src/tools/network/__tests__/web-search.test.ts +11 -3
  334. package/src/tools/network/web-search-error.test.ts +248 -0
  335. package/src/tools/network/web-search-error.ts +267 -0
  336. package/src/tools/network/web-search.ts +207 -48
  337. package/src/tools/schedule/create.ts +2 -0
  338. package/src/tools/terminal/safe-env.ts +10 -1
  339. package/src/tools/ui-surface/definitions.ts +9 -1
  340. package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
  341. package/src/tts/provider-catalog.ts +76 -1
  342. package/src/util/mutex.ts +47 -0
  343. package/src/workspace/git-service.ts +1 -42
  344. package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
  345. package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
  346. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +93 -0
  347. package/src/workspace/migrations/registry.ts +6 -0
  348. package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
  349. package/src/__tests__/empty-response-pipeline.test.ts +0 -423
  350. package/src/__tests__/llm-call-pipeline.test.ts +0 -287
  351. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
  352. package/src/__tests__/persistence-pipeline.test.ts +0 -503
  353. package/src/__tests__/title-generate-pipeline.test.ts +0 -211
  354. package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
  355. package/src/__tests__/tool-error-pipeline.test.ts +0 -241
  356. package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
  357. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
  358. package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
  359. package/src/gallery/default-gallery.ts +0 -1359
  360. package/src/gallery/gallery-manifest.ts +0 -28
  361. package/src/home/feature-gate.ts +0 -22
  362. package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
  363. package/src/plugins/defaults/empty-response/terminal.ts +0 -106
  364. package/src/plugins/defaults/injectors/package.json +0 -15
  365. package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
  366. package/src/plugins/defaults/llm-call/package.json +0 -15
  367. package/src/plugins/defaults/llm-call/register.ts +0 -45
  368. package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
  369. package/src/plugins/defaults/memory-retrieval/package.json +0 -15
  370. package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
  371. package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
  372. package/src/plugins/defaults/persistence/package.json +0 -15
  373. package/src/plugins/defaults/persistence/register.ts +0 -38
  374. package/src/plugins/defaults/persistence/terminal.ts +0 -83
  375. package/src/plugins/defaults/title-generate/terminal.ts +0 -31
  376. package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
  377. package/src/plugins/defaults/token-estimate/package.json +0 -15
  378. package/src/plugins/defaults/token-estimate/register.ts +0 -34
  379. package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
  380. package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
  381. package/src/plugins/defaults/tool-error/terminal.ts +0 -47
  382. package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
  383. package/src/plugins/defaults/tool-execute/package.json +0 -15
  384. package/src/plugins/defaults/tool-execute/register.ts +0 -49
  385. package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
  386. package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
  387. package/src/skills/category-inference.ts +0 -111
@@ -1,503 +0,0 @@
1
- /**
2
- * Unit tests for the `persistence` pipeline wrapping (PR 27).
3
- *
4
- * Exercises the three behaviors the plan calls out:
5
- *
6
- * 1. The default `persistence` pipeline delegates to the `memory/
7
- * conversation-crud.ts` functions (`addMessage`, `updateMessageMetadata`,
8
- * `deleteMessageById`) so running the pipeline produces the same DB rows
9
- * the direct call would have produced.
10
- * 2. A custom plugin can redirect persistence to a mock in-memory store by
11
- * short-circuiting every op — the real DB is never touched.
12
- *
13
- * Uses the real SQLite DB wired up via `test-preload.ts` (which points the
14
- * workspace dir at a per-file temp directory). `resetPluginRegistryForTests`
15
- * isolates the plugin registry between cases so the default plugin
16
- * registered at module load from `external-plugins-bootstrap.ts` doesn't
17
- * leak across tests that want a clean slate.
18
- */
19
-
20
- import { afterAll, beforeEach, describe, expect, test } from "bun:test";
21
-
22
- import type { TrustContext } from "../daemon/trust-context.js";
23
- import {
24
- addMessage,
25
- createConversation,
26
- getMessageById,
27
- getMessages,
28
- reserveMessage,
29
- updateMessageContent,
30
- updateMessageMetadata,
31
- } from "../memory/conversation-crud.js";
32
- import { getDb } from "../memory/db-connection.js";
33
- import { initializeDb } from "../memory/db-init.js";
34
- import { defaultPersistencePlugin } from "../plugins/defaults/persistence/register.js";
35
- import { defaultPersistenceTerminal } from "../plugins/defaults/persistence/terminal.js";
36
- import { DEFAULT_TIMEOUTS, runPipeline } from "../plugins/pipeline.js";
37
- import {
38
- getMiddlewaresFor,
39
- registerPlugin,
40
- resetPluginRegistryForTests,
41
- } from "../plugins/registry.js";
42
- import type {
43
- Middleware,
44
- PersistAddResult,
45
- PersistArgs,
46
- PersistDeleteResult,
47
- PersistReserveResult,
48
- PersistResult,
49
- Plugin,
50
- TurnContext,
51
- } from "../plugins/types.js";
52
-
53
- initializeDb();
54
-
55
- function resetTables(): void {
56
- const db = getDb();
57
- db.run("DELETE FROM message_attachments");
58
- db.run("DELETE FROM attachments");
59
- db.run("DELETE FROM memory_segments");
60
- db.run("DELETE FROM memory_embeddings");
61
- db.run("DELETE FROM messages");
62
- db.run("DELETE FROM conversations");
63
- }
64
-
65
- // ─── Fixtures ───────────────────────────────────────────────────────────────
66
-
67
- const trust: TrustContext = {
68
- sourceChannel: "vellum",
69
- trustClass: "guardian",
70
- };
71
-
72
- function makeCtx(overrides: Partial<TurnContext> = {}): TurnContext {
73
- return {
74
- requestId: "req-test",
75
- conversationId: "conv-test",
76
- turnIndex: 0,
77
- trust,
78
- ...overrides,
79
- };
80
- }
81
-
82
- // ─── Tests ──────────────────────────────────────────────────────────────────
83
-
84
- describe("persistence pipeline", () => {
85
- beforeEach(() => {
86
- resetPluginRegistryForTests();
87
- resetTables();
88
- });
89
-
90
- // Clear the registry on the way out too so later test files in the same
91
- // `bun test` run don't inherit persistence middleware from our final test.
92
- afterAll(() => {
93
- resetPluginRegistryForTests();
94
- });
95
-
96
- test("default plugin: add op persists a message identical to direct addMessage", async () => {
97
- registerPlugin(defaultPersistencePlugin);
98
-
99
- const conv = createConversation();
100
-
101
- // Baseline: what the direct call produces.
102
- const direct = await addMessage(conv.id, "user", "direct-content", {
103
- metadata: { role: "baseline" },
104
- skipIndexing: true,
105
- });
106
- const directRow = getMessageById(direct.id, conv.id);
107
-
108
- // Through the pipeline: must produce a row with identical columns
109
- // (modulo id / createdAt timestamps, which are unique per insert).
110
- const result = (await runPipeline<PersistArgs, PersistResult>(
111
- "persistence",
112
- getMiddlewaresFor("persistence"),
113
- defaultPersistenceTerminal,
114
- {
115
- op: "add",
116
- conversationId: conv.id,
117
- role: "user",
118
- content: "pipeline-content",
119
- metadata: { role: "pipeline" },
120
- addOptions: { skipIndexing: true },
121
- },
122
- makeCtx({ conversationId: conv.id }),
123
- DEFAULT_TIMEOUTS.persistence,
124
- )) as PersistAddResult;
125
-
126
- expect(result.op).toBe("add");
127
- expect(result.message.id).toBeTruthy();
128
- expect(result.message.conversationId).toBe(conv.id);
129
- expect(result.message.role).toBe("user");
130
- expect(result.message.content).toBe("pipeline-content");
131
-
132
- const pipelineRow = getMessageById(result.message.id, conv.id);
133
- expect(pipelineRow).not.toBeNull();
134
- // Column-by-column parity with the direct baseline: role / content /
135
- // metadata string are the fields under the plugin's control.
136
- expect(pipelineRow?.role).toBe(directRow?.role);
137
- expect(typeof pipelineRow?.metadata).toBe(typeof directRow?.metadata);
138
- });
139
-
140
- test("default plugin: update op merges metadata in place", async () => {
141
- registerPlugin(defaultPersistencePlugin);
142
-
143
- const conv = createConversation();
144
- const msg = await addMessage(conv.id, "user", "to-update", {
145
- metadata: { initial: true },
146
- skipIndexing: true,
147
- });
148
-
149
- // Through the pipeline: result is the `update` envelope (no payload).
150
- const result = await runPipeline<PersistArgs, PersistResult>(
151
- "persistence",
152
- getMiddlewaresFor("persistence"),
153
- defaultPersistenceTerminal,
154
- {
155
- op: "update",
156
- messageId: msg.id,
157
- updates: { extra: "added" },
158
- },
159
- makeCtx({ conversationId: conv.id }),
160
- DEFAULT_TIMEOUTS.persistence,
161
- );
162
- expect(result).toEqual({ op: "update" });
163
-
164
- // Direct equivalent call on a second message for parity comparison.
165
- const baseline = await addMessage(conv.id, "user", "to-update-direct", {
166
- metadata: { initial: true },
167
- skipIndexing: true,
168
- });
169
- updateMessageMetadata(baseline.id, { extra: "added" });
170
-
171
- const pipelineRow = getMessageById(msg.id, conv.id);
172
- const baselineRow = getMessageById(baseline.id, conv.id);
173
-
174
- // Both rows must end up with identical merged metadata shape.
175
- expect(JSON.parse(pipelineRow!.metadata!)).toEqual({
176
- initial: true,
177
- extra: "added",
178
- });
179
- expect(JSON.parse(baselineRow!.metadata!)).toEqual(
180
- JSON.parse(pipelineRow!.metadata!),
181
- );
182
- });
183
-
184
- test("default plugin: delete op removes the message and returns segment IDs", async () => {
185
- registerPlugin(defaultPersistencePlugin);
186
-
187
- const conv = createConversation();
188
- const msg = await addMessage(conv.id, "user", "to-delete", {
189
- skipIndexing: true,
190
- });
191
-
192
- const result = (await runPipeline<PersistArgs, PersistResult>(
193
- "persistence",
194
- getMiddlewaresFor("persistence"),
195
- defaultPersistenceTerminal,
196
- { op: "delete", messageId: msg.id },
197
- makeCtx({ conversationId: conv.id }),
198
- DEFAULT_TIMEOUTS.persistence,
199
- )) as PersistDeleteResult;
200
-
201
- expect(result.op).toBe("delete");
202
- expect(result.segmentIds).toEqual([]);
203
- expect(result.deletedSummaryIds).toEqual([]);
204
-
205
- // The row must be gone.
206
- expect(getMessageById(msg.id, conv.id)).toBeNull();
207
- expect(getMessages(conv.id)).toHaveLength(0);
208
- });
209
-
210
- test("default plugin: reserve op pre-allocates an empty assistant row", async () => {
211
- registerPlugin(defaultPersistencePlugin);
212
-
213
- const conv = createConversation();
214
-
215
- const result = (await runPipeline<PersistArgs, PersistResult>(
216
- "persistence",
217
- getMiddlewaresFor("persistence"),
218
- defaultPersistenceTerminal,
219
- {
220
- op: "reserve",
221
- conversationId: conv.id,
222
- role: "assistant",
223
- metadata: { reservedFor: "anchor" },
224
- },
225
- makeCtx({ conversationId: conv.id }),
226
- DEFAULT_TIMEOUTS.persistence,
227
- )) as PersistReserveResult;
228
-
229
- expect(result.op).toBe("reserve");
230
- expect(result.message.id).toBeTruthy();
231
- expect(result.message.role).toBe("assistant");
232
- // Reserve places an empty JSON array so consumers that parse content
233
- // observe a no-content payload.
234
- expect(result.message.content).toBe("[]");
235
-
236
- // Row must exist with the expected shape and live in the conversation.
237
- const row = getMessageById(result.message.id, conv.id);
238
- expect(row).not.toBeNull();
239
- expect(row?.content).toBe("[]");
240
- expect(JSON.parse(row!.metadata!)).toEqual({ reservedFor: "anchor" });
241
- expect(getMessages(conv.id).map((m) => m.id)).toContain(result.message.id);
242
- });
243
-
244
- test("default plugin: updateContent op overwrites an existing row's content", async () => {
245
- registerPlugin(defaultPersistencePlugin);
246
-
247
- const conv = createConversation();
248
-
249
- // Reserve first, then overwrite — exactly the B3 sequence consumers will
250
- // follow.
251
- const reserved = await reserveMessage(conv.id, "assistant");
252
- expect(getMessageById(reserved.id, conv.id)?.content).toBe("[]");
253
-
254
- const result = await runPipeline<PersistArgs, PersistResult>(
255
- "persistence",
256
- getMiddlewaresFor("persistence"),
257
- defaultPersistenceTerminal,
258
- {
259
- op: "updateContent",
260
- messageId: reserved.id,
261
- content: JSON.stringify([{ type: "text", text: "Hello" }]),
262
- },
263
- makeCtx({ conversationId: conv.id }),
264
- DEFAULT_TIMEOUTS.persistence,
265
- );
266
- expect(result).toEqual({ op: "updateContent" });
267
-
268
- // Content updated in place; row id unchanged.
269
- const row = getMessageById(reserved.id, conv.id);
270
- expect(row).not.toBeNull();
271
- expect(JSON.parse(row!.content)).toEqual([{ type: "text", text: "Hello" }]);
272
-
273
- // Direct-call parity: a fresh reserve + direct updateMessageContent
274
- // should land the same content shape.
275
- const baselineReserved = await reserveMessage(conv.id, "assistant");
276
- updateMessageContent(
277
- baselineReserved.id,
278
- JSON.stringify([{ type: "text", text: "Hello" }]),
279
- );
280
- const baselineRow = getMessageById(baselineReserved.id, conv.id);
281
- expect(baselineRow?.content).toBe(row?.content);
282
- });
283
-
284
- test("custom plugin: short-circuits every op onto a mock in-memory store", async () => {
285
- type Stored = {
286
- id: string;
287
- conversationId: string;
288
- role: string;
289
- content: string;
290
- metadata: Record<string, unknown>;
291
- };
292
- const mockStore = new Map<string, Stored>();
293
- let nextId = 1;
294
-
295
- const redirect: Middleware<PersistArgs, PersistResult> =
296
- async function redirectPersist(args, _next, _ctx) {
297
- switch (args.op) {
298
- case "add": {
299
- const id = `mock-${nextId++}`;
300
- mockStore.set(id, {
301
- id,
302
- conversationId: args.conversationId,
303
- role: args.role,
304
- content: args.content,
305
- metadata: { ...(args.metadata ?? {}) },
306
- });
307
- return {
308
- op: "add",
309
- message: {
310
- id,
311
- conversationId: args.conversationId,
312
- role: args.role,
313
- content: args.content,
314
- createdAt: 123,
315
- },
316
- };
317
- }
318
- case "reserve": {
319
- const id = `mock-${nextId++}`;
320
- mockStore.set(id, {
321
- id,
322
- conversationId: args.conversationId,
323
- role: args.role,
324
- content: "[]",
325
- metadata: { ...(args.metadata ?? {}) },
326
- });
327
- return {
328
- op: "reserve",
329
- message: {
330
- id,
331
- conversationId: args.conversationId,
332
- role: args.role,
333
- content: "[]",
334
- createdAt: 123,
335
- },
336
- };
337
- }
338
- case "updateContent": {
339
- const existing = mockStore.get(args.messageId);
340
- if (existing) {
341
- existing.content = args.content;
342
- }
343
- return { op: "updateContent" };
344
- }
345
- case "update": {
346
- const existing = mockStore.get(args.messageId);
347
- if (existing) {
348
- existing.metadata = { ...existing.metadata, ...args.updates };
349
- }
350
- return { op: "update" };
351
- }
352
- case "delete": {
353
- mockStore.delete(args.messageId);
354
- return { op: "delete", segmentIds: [], deletedSummaryIds: [] };
355
- }
356
- }
357
- };
358
-
359
- const customPlugin: Plugin = {
360
- manifest: {
361
- name: "mock-persistence",
362
- version: "0.0.1",
363
- },
364
- middleware: { persistence: redirect },
365
- };
366
-
367
- // Register the custom plugin FIRST so it composes as the outermost
368
- // wrapper — it short-circuits before the default plugin ever runs,
369
- // keeping the real DB untouched.
370
- registerPlugin(customPlugin);
371
- registerPlugin(defaultPersistencePlugin);
372
-
373
- const conv = createConversation();
374
- const dbRowsBefore = getMessages(conv.id).length;
375
-
376
- const addResult = (await runPipeline<PersistArgs, PersistResult>(
377
- "persistence",
378
- getMiddlewaresFor("persistence"),
379
- defaultPersistenceTerminal,
380
- {
381
- op: "add",
382
- conversationId: conv.id,
383
- role: "user",
384
- content: "mock-content",
385
- metadata: { origin: "mock" },
386
- },
387
- makeCtx({ conversationId: conv.id }),
388
- DEFAULT_TIMEOUTS.persistence,
389
- )) as PersistAddResult;
390
-
391
- expect(addResult.message.id).toBe("mock-1");
392
- expect(mockStore.size).toBe(1);
393
-
394
- const reserveResult = (await runPipeline<PersistArgs, PersistResult>(
395
- "persistence",
396
- getMiddlewaresFor("persistence"),
397
- defaultPersistenceTerminal,
398
- {
399
- op: "reserve",
400
- conversationId: conv.id,
401
- role: "assistant",
402
- metadata: { reservedFor: "mock-anchor" },
403
- },
404
- makeCtx({ conversationId: conv.id }),
405
- DEFAULT_TIMEOUTS.persistence,
406
- )) as PersistReserveResult;
407
- expect(reserveResult.op).toBe("reserve");
408
- expect(reserveResult.message.id).toBe("mock-2");
409
- expect(reserveResult.message.content).toBe("[]");
410
- expect(mockStore.get("mock-2")?.content).toBe("[]");
411
-
412
- await runPipeline<PersistArgs, PersistResult>(
413
- "persistence",
414
- getMiddlewaresFor("persistence"),
415
- defaultPersistenceTerminal,
416
- {
417
- op: "updateContent",
418
- messageId: "mock-2",
419
- content: '[{"type":"text","text":"finalized"}]',
420
- },
421
- makeCtx({ conversationId: conv.id }),
422
- DEFAULT_TIMEOUTS.persistence,
423
- );
424
- expect(mockStore.get("mock-2")?.content).toBe(
425
- '[{"type":"text","text":"finalized"}]',
426
- );
427
-
428
- await runPipeline<PersistArgs, PersistResult>(
429
- "persistence",
430
- getMiddlewaresFor("persistence"),
431
- defaultPersistenceTerminal,
432
- {
433
- op: "update",
434
- messageId: "mock-1",
435
- updates: { extra: "added" },
436
- },
437
- makeCtx({ conversationId: conv.id }),
438
- DEFAULT_TIMEOUTS.persistence,
439
- );
440
- expect(mockStore.get("mock-1")?.metadata).toEqual({
441
- origin: "mock",
442
- extra: "added",
443
- });
444
-
445
- const delResult = (await runPipeline<PersistArgs, PersistResult>(
446
- "persistence",
447
- getMiddlewaresFor("persistence"),
448
- defaultPersistenceTerminal,
449
- { op: "delete", messageId: "mock-1" },
450
- makeCtx({ conversationId: conv.id }),
451
- DEFAULT_TIMEOUTS.persistence,
452
- )) as PersistDeleteResult;
453
- expect(delResult.op).toBe("delete");
454
- expect(mockStore.has("mock-1")).toBe(false);
455
-
456
- // The real DB must not have been touched by any of the ops.
457
- expect(getMessages(conv.id)).toHaveLength(dbRowsBefore);
458
- });
459
-
460
- test("user plugin registered AFTER the default still runs (no shadowing)", async () => {
461
- // Production registration order: defaults load first via the side-effect
462
- // imports in `defaults/index.ts`, then user plugins register on top via
463
- // `bootstrapPlugins()`. The user's middleware ends up at a deeper onion
464
- // layer than the default. If the default's middleware were to bypass
465
- // `next` and call the terminal directly, the user middleware would never
466
- // run — this test guards against that regression.
467
- registerPlugin(defaultPersistencePlugin);
468
-
469
- let userMiddlewareRan = false;
470
- const userMiddleware: Middleware<PersistArgs, PersistResult> = async (
471
- args,
472
- next,
473
- ) => {
474
- userMiddlewareRan = true;
475
- return next(args);
476
- };
477
- registerPlugin({
478
- manifest: {
479
- name: "late-user-plugin",
480
- version: "0.0.1",
481
- },
482
- middleware: { persistence: userMiddleware },
483
- });
484
-
485
- const conv = createConversation();
486
- await runPipeline<PersistArgs, PersistResult>(
487
- "persistence",
488
- getMiddlewaresFor("persistence"),
489
- defaultPersistenceTerminal,
490
- {
491
- op: "add",
492
- conversationId: conv.id,
493
- role: "user",
494
- content: "shadow-check",
495
- addOptions: { skipIndexing: true },
496
- },
497
- makeCtx({ conversationId: conv.id }),
498
- DEFAULT_TIMEOUTS.persistence,
499
- );
500
-
501
- expect(userMiddlewareRan).toBe(true);
502
- });
503
- });