@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
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Regression tests for the image-too-large persistence path (JARVIS-1037).
3
+ *
4
+ * An image the provider can never accept — over the per-side pixel cap or the
5
+ * per-image byte cap, and not shrinkable on this host — must be durably swapped
6
+ * for a text note in its stored message. If it stays in the stored content,
7
+ * every later turn rehydrates it from the DB and the model reports seeing both
8
+ * the rejected image and any smaller re-upload. `persistUnsendableImageDowngrades`
9
+ * makes the swap durable so the rejected upload cannot resurface.
10
+ *
11
+ * Uses the real SQLite DB wired up via `test-preload.ts` (per-file temp
12
+ * workspace).
13
+ */
14
+
15
+ import { beforeEach, describe, expect, test } from "bun:test";
16
+
17
+ import { persistUnsendableImageDowngrades } from "../daemon/persist-unsendable-image.js";
18
+ import {
19
+ addMessage,
20
+ createConversation,
21
+ getMessages,
22
+ } from "../memory/conversation-crud.js";
23
+ import { getDb } from "../memory/db-connection.js";
24
+ import { initializeDb } from "../memory/db-init.js";
25
+ import type { ContentBlock } from "../providers/types.js";
26
+
27
+ initializeDb();
28
+
29
+ function resetTables(): void {
30
+ const db = getDb();
31
+ db.run("DELETE FROM message_attachments");
32
+ db.run("DELETE FROM attachments");
33
+ db.run("DELETE FROM memory_segments");
34
+ db.run("DELETE FROM memory_embeddings");
35
+ db.run("DELETE FROM messages");
36
+ db.run("DELETE FROM conversations");
37
+ }
38
+
39
+ /**
40
+ * Build a minimal PNG whose IHDR declares the given dimensions. Only the
41
+ * 8-byte signature and the width/height fields (read by `parseImageDimensions`)
42
+ * need to be correct; the rest is padding. `optimizeImageForTransport` cannot
43
+ * downscale this off macOS (no `sips`), so it stays a no-op — exactly the
44
+ * host condition that produces an unsendable stored image.
45
+ */
46
+ function makePngBase64(width: number, height: number, padBytes = 0): string {
47
+ const header = Buffer.from(
48
+ Uint8Array.from([
49
+ 0x89,
50
+ 0x50,
51
+ 0x4e,
52
+ 0x47,
53
+ 0x0d,
54
+ 0x0a,
55
+ 0x1a,
56
+ 0x0a, // PNG signature
57
+ 0x00,
58
+ 0x00,
59
+ 0x00,
60
+ 0x0d, // IHDR length (13)
61
+ 0x49,
62
+ 0x48,
63
+ 0x44,
64
+ 0x52, // "IHDR"
65
+ (width >>> 24) & 0xff,
66
+ (width >>> 16) & 0xff,
67
+ (width >>> 8) & 0xff,
68
+ width & 0xff,
69
+ (height >>> 24) & 0xff,
70
+ (height >>> 16) & 0xff,
71
+ (height >>> 8) & 0xff,
72
+ height & 0xff,
73
+ 0x08,
74
+ 0x06,
75
+ 0x00,
76
+ 0x00,
77
+ 0x00, // bit depth / color type / etc.
78
+ ]),
79
+ ).toString("base64");
80
+ return padBytes > 0 ? header + "A".repeat(padBytes) : header;
81
+ }
82
+
83
+ function imageBlock(data: string): ContentBlock {
84
+ return {
85
+ type: "image",
86
+ source: { type: "base64", media_type: "image/png", data },
87
+ };
88
+ }
89
+
90
+ function storedContent(conversationId: string): ContentBlock[][] {
91
+ return getMessages(conversationId).map(
92
+ (row) => JSON.parse(row.content) as ContentBlock[],
93
+ );
94
+ }
95
+
96
+ const PROVIDER_MAX_IMAGE_DIMENSION = 8000;
97
+
98
+ describe("persistUnsendableImageDowngrades", () => {
99
+ beforeEach(() => {
100
+ resetTables();
101
+ });
102
+
103
+ /** A stored image past the provider pixel cap is swapped for a text note. */
104
+ test("replaces an oversized image block with a text note", async () => {
105
+ // GIVEN a message holding text plus an image past the pixel cap
106
+ const conv = createConversation();
107
+ const oversized = makePngBase64(PROVIDER_MAX_IMAGE_DIMENSION + 1000, 6000);
108
+ await addMessage(
109
+ conv.id,
110
+ "user",
111
+ JSON.stringify([
112
+ { type: "text", text: "look at this" },
113
+ imageBlock(oversized),
114
+ ]),
115
+ { skipIndexing: true },
116
+ );
117
+
118
+ // WHEN the downgrade is persisted
119
+ const rewritten = persistUnsendableImageDowngrades(conv.id);
120
+
121
+ // THEN one message is rewritten with no image block left
122
+ expect(rewritten).toBe(1);
123
+ const [content] = storedContent(conv.id);
124
+ expect(content.some((b) => b.type === "image")).toBe(false);
125
+ // AND the original text is preserved alongside the substituted note
126
+ expect(content.filter((b) => b.type === "text")).toHaveLength(2);
127
+ });
128
+
129
+ /** The JARVIS-1037 scenario: the rejected original must not resurface next
130
+ * to a valid re-upload. */
131
+ test("re-uploaded smaller image survives while the rejected original is removed", async () => {
132
+ // GIVEN turn 1 contains an oversized upload that was rejected
133
+ const conv = createConversation();
134
+ await addMessage(
135
+ conv.id,
136
+ "user",
137
+ JSON.stringify([imageBlock(makePngBase64(12000, 9000))]),
138
+ { skipIndexing: true },
139
+ );
140
+ // AND turn 2 contains a properly sized re-upload
141
+ await addMessage(
142
+ conv.id,
143
+ "user",
144
+ JSON.stringify([imageBlock(makePngBase64(800, 600))]),
145
+ { skipIndexing: true },
146
+ );
147
+
148
+ // WHEN the downgrade is persisted
149
+ const rewritten = persistUnsendableImageDowngrades(conv.id);
150
+
151
+ // THEN only the rejected original is removed
152
+ expect(rewritten).toBe(1);
153
+ const [first, second] = storedContent(conv.id);
154
+ expect(first.some((b) => b.type === "image")).toBe(false);
155
+ // AND the valid re-upload is left intact
156
+ expect(second.some((b) => b.type === "image")).toBe(true);
157
+ });
158
+
159
+ /** Sendable images are never disturbed by the recovery path. */
160
+ test("leaves a normally-sized image untouched", async () => {
161
+ // GIVEN a message with an image well within provider limits
162
+ const conv = createConversation();
163
+ await addMessage(
164
+ conv.id,
165
+ "user",
166
+ JSON.stringify([imageBlock(makePngBase64(1024, 768))]),
167
+ { skipIndexing: true },
168
+ );
169
+
170
+ // WHEN the downgrade is persisted
171
+ const rewritten = persistUnsendableImageDowngrades(conv.id);
172
+
173
+ // THEN nothing is rewritten and the image remains
174
+ expect(rewritten).toBe(0);
175
+ const [content] = storedContent(conv.id);
176
+ expect(content.some((b) => b.type === "image")).toBe(true);
177
+ });
178
+
179
+ /** The byte-size cap is enforced independently of pixel dimensions. */
180
+ test("removes an image whose payload exceeds the per-image byte cap", async () => {
181
+ // GIVEN an image within the pixel cap but with a payload over 5 MB
182
+ const conv = createConversation();
183
+ const huge = makePngBase64(1000, 1000, 6 * 1024 * 1024);
184
+ await addMessage(conv.id, "user", JSON.stringify([imageBlock(huge)]), {
185
+ skipIndexing: true,
186
+ });
187
+
188
+ // WHEN the downgrade is persisted
189
+ const rewritten = persistUnsendableImageDowngrades(conv.id);
190
+
191
+ // THEN the oversized-payload image is removed
192
+ expect(rewritten).toBe(1);
193
+ const [content] = storedContent(conv.id);
194
+ expect(content.some((b) => b.type === "image")).toBe(false);
195
+ });
196
+
197
+ /** Re-running after a rewrite is a safe no-op (no image blocks remain). */
198
+ test("is idempotent — a second run rewrites nothing", async () => {
199
+ // GIVEN a conversation whose oversized image has already been downgraded
200
+ const conv = createConversation();
201
+ await addMessage(
202
+ conv.id,
203
+ "user",
204
+ JSON.stringify([imageBlock(makePngBase64(10000, 10000))]),
205
+ { skipIndexing: true },
206
+ );
207
+ expect(persistUnsendableImageDowngrades(conv.id)).toBe(1);
208
+
209
+ // WHEN the downgrade runs a second time
210
+ const secondRun = persistUnsendableImageDowngrades(conv.id);
211
+
212
+ // THEN nothing further is rewritten
213
+ expect(secondRun).toBe(0);
214
+ });
215
+ });
@@ -149,6 +149,7 @@ function makeDeps(): EventHandlerDeps {
149
149
  userMessageInterface: "macos",
150
150
  assistantMessageInterface: "macos",
151
151
  } as EventHandlerDeps["turnInterfaceContext"],
152
+ applyCompaction: async () => {},
152
153
  } as EventHandlerDeps;
153
154
  }
154
155
 
@@ -6,7 +6,7 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
6
6
  import {
7
7
  getPkbAutoInjectList,
8
8
  readAutoinjectList,
9
- } from "../daemon/conversation-runtime-assembly.js";
9
+ } from "../memory/pkb/autoinject.js";
10
10
 
11
11
  const PKB_DEFAULT_FILES = [
12
12
  "INDEX.md",
@@ -80,10 +80,7 @@ describe("readAutoinjectList", () => {
80
80
  "INDEX.md\ncustom-topic.md\n",
81
81
  "utf-8",
82
82
  );
83
- expect(readAutoinjectList(pkbDir)).toEqual([
84
- "INDEX.md",
85
- "custom-topic.md",
86
- ]);
83
+ expect(readAutoinjectList(pkbDir)).toEqual(["INDEX.md", "custom-topic.md"]);
87
84
  });
88
85
 
89
86
  test("strips blank lines and whitespace", () => {
@@ -44,10 +44,7 @@ describe("buildShimSource", () => {
44
44
  });
45
45
 
46
46
  test("handles an empty export list (today's types-only surface)", () => {
47
- const source = buildShimSource(
48
- [],
49
- Symbol.for("vellum.plugin-api.v1"),
50
- );
47
+ const source = buildShimSource([], Symbol.for("vellum.plugin-api.v1"));
51
48
  expect(source).toBe(
52
49
  `const api = globalThis[Symbol.for("vellum.plugin-api.v1")];\n`,
53
50
  );
@@ -109,7 +106,7 @@ describe("ensurePluginApiShim", () => {
109
106
  const pluginDir = join(workspaceDir, "plugins", "fake-plugin");
110
107
  await mkdir(pluginDir, { recursive: true });
111
108
  await writeFile(
112
- join(pluginDir, "register.js"),
109
+ join(pluginDir, "probe.js"),
113
110
  `import * as api from "@vellumai/plugin-api";\nexport { api };\n`,
114
111
  );
115
112
 
@@ -118,7 +115,7 @@ describe("ensurePluginApiShim", () => {
118
115
  // → plugin-api namespace. If any link in that chain is broken, this
119
116
  // import throws.
120
117
  const mod: { api: Record<string, unknown> } = await import(
121
- join(pluginDir, "register.js")
118
+ join(pluginDir, "probe.js")
122
119
  );
123
120
  expect(mod.api).toBeDefined();
124
121
  });
@@ -38,14 +38,11 @@ import { RiskLevel } from "../permissions/types.js";
38
38
  import { registerDefaultPlugins } from "../plugins/defaults/index.js";
39
39
  import {
40
40
  closeRegistration,
41
- getInjectors,
42
- getMiddlewaresFor,
43
41
  getRegisteredPlugins,
44
42
  registerPlugin,
45
43
  resetPluginRegistryForTests,
46
44
  } from "../plugins/registry.js";
47
45
  import {
48
- type PipelineMiddlewareMap,
49
46
  type Plugin,
50
47
  PluginExecutionError,
51
48
  type PluginInitContext,
@@ -342,8 +339,8 @@ describe("plugin bootstrap", () => {
342
339
  // THEN the defaults are still registered, ahead of the user plugin, so the
343
340
  // middleware onion order is unchanged
344
341
  const names = getRegisteredPlugins().map((p) => p.manifest.name);
345
- expect(names).toContain("default-llm-call");
346
- expect(names.indexOf("default-llm-call")).toBeLessThan(
342
+ expect(names).toContain("default-compaction");
343
+ expect(names.indexOf("default-compaction")).toBeLessThan(
347
344
  names.indexOf("user-after-defaults"),
348
345
  );
349
346
  });
@@ -486,47 +483,24 @@ describe("plugin bootstrap", () => {
486
483
  expect(initFired).toBe(false);
487
484
  });
488
485
 
489
- test("requiresFlag disabled: plugin middleware and injectors are dropped from the registry", async () => {
490
- // Regression: prior to the unregisterPlugin() call on the flag-gated skip
491
- // path, `getMiddlewaresFor()` and `getInjectors()` iterated over every
492
- // entry in `registeredPlugins` so a gated-off plugin's middleware and
493
- // injectors still ran on every pipeline invocation and system-prompt
494
- // assembly even though `init()` had never fired to set up the state they
495
- // depended on.
496
- setOverridesForTesting({ "plugin-middleware-disabled": false });
497
-
498
- const gatedMiddleware: PipelineMiddlewareMap["llmCall"] = async (
499
- args,
500
- next,
501
- ) => next(args);
486
+ test("requiresFlag disabled: the skipped plugin is removed from the registry", async () => {
487
+ // Regression: a flag-gated skip must call `unregisterPlugin()` so the
488
+ // gated-off plugin does not linger in `registeredPlugins` with its
489
+ // `init()` never having fired to set up the state it depends on.
490
+ setOverridesForTesting({ "plugin-registry-disabled": false });
491
+
502
492
  const plugin = buildPlugin(
503
- "gated-middleware",
504
- {
505
- middleware: { llmCall: gatedMiddleware },
506
- injectors: [
507
- {
508
- name: "gated-middleware-injector",
509
- order: 100,
510
- async produce() {
511
- return null;
512
- },
513
- },
514
- ],
515
- },
516
- { requiresFlag: ["plugin-middleware-disabled"] },
493
+ "gated-registry",
494
+ {},
495
+ { requiresFlag: ["plugin-registry-disabled"] },
517
496
  );
518
497
  registerPlugin(plugin);
519
498
 
520
499
  await bootstrapPlugins();
521
500
 
522
- // Neither the middleware slot nor the injector list should expose the
523
- // flag-gated plugin's contributions. The default plugins also contribute
524
- // llmCall middleware / injectors, so we key on identity rather than
525
- // asserting empty lists.
526
- expect(getMiddlewaresFor("llmCall")).not.toContain(gatedMiddleware);
527
- expect(
528
- getInjectors().some((i) => i.name === "gated-middleware-injector"),
529
- ).toBe(false);
501
+ // The gated-off plugin must not survive in the registry snapshot.
502
+ const names = getRegisteredPlugins().map((p) => p.manifest.name);
503
+ expect(names).not.toContain("gated-registry");
530
504
  });
531
505
 
532
506
  test("requiresFlag disabled: no shutdown hook entry installed for the skipped plugin", async () => {
@@ -2,28 +2,19 @@
2
2
  * Tests for the plugin registry (PR 13).
3
3
  *
4
4
  * Covers successful registration, required-field and duplicate-name
5
- * validation, capability-version negotiation error messaging, injector
6
- * ordering, and middleware collection order.
5
+ * validation, capability-version negotiation error messaging, and
6
+ * registration order.
7
7
  */
8
8
 
9
9
  import { beforeEach, describe, expect, test } from "bun:test";
10
10
 
11
11
  import {
12
12
  closeRegistration,
13
- getInjectors,
14
- getMiddlewaresFor,
15
13
  getRegisteredPlugins,
16
14
  registerPlugin,
17
15
  resetPluginRegistryForTests,
18
16
  } from "../plugins/registry.js";
19
- import {
20
- type CompactionArgs,
21
- type CompactionResult,
22
- type Injector,
23
- type Middleware,
24
- type Plugin,
25
- PluginExecutionError,
26
- } from "../plugins/types.js";
17
+ import { type Plugin, PluginExecutionError } from "../plugins/types.js";
27
18
 
28
19
  /** Build a minimal, valid plugin with the given name and optional extras. */
29
20
  function buildPlugin(
@@ -149,70 +140,6 @@ describe("plugin registry", () => {
149
140
  expect(() => registerPlugin(bad)).toThrow(/manifest\.version is required/);
150
141
  });
151
142
 
152
- test("getInjectors returns injectors sorted by order ascending", () => {
153
- const high: Injector = {
154
- name: "high-order",
155
- order: 20,
156
- async produce() {
157
- return null;
158
- },
159
- };
160
- const low: Injector = {
161
- name: "low-order",
162
- order: 10,
163
- async produce() {
164
- return null;
165
- },
166
- };
167
-
168
- // Register the higher-order plugin first so registration order alone
169
- // would produce the wrong sequence — the test proves sort-by-order wins.
170
- registerPlugin(buildPlugin("high", { injectors: [high] }));
171
- registerPlugin(buildPlugin("low", { injectors: [low] }));
172
-
173
- const injectors = getInjectors();
174
- expect(injectors.map((i) => i.name)).toEqual(["low-order", "high-order"]);
175
- });
176
-
177
- test("getMiddlewaresFor returns middleware in registration order", () => {
178
- const firstMw: Middleware<CompactionArgs, CompactionResult> = async (
179
- args,
180
- next,
181
- ) => next(args);
182
- const secondMw: Middleware<CompactionArgs, CompactionResult> = async (
183
- args,
184
- next,
185
- ) => next(args);
186
-
187
- registerPlugin(
188
- buildPlugin("plugin-first", { middleware: { compaction: firstMw } }),
189
- );
190
- registerPlugin(
191
- buildPlugin("plugin-second", { middleware: { compaction: secondMw } }),
192
- );
193
-
194
- const middlewares = getMiddlewaresFor("compaction");
195
- expect(middlewares).toHaveLength(2);
196
- // Identity comparison proves the middleware instances come back in
197
- // registration order — outer→inner composition semantics belong to the
198
- // pipeline runner (PR 12), not the registry.
199
- expect(middlewares[0]).toBe(firstMw);
200
- expect(middlewares[1]).toBe(secondMw);
201
- });
202
-
203
- test("getMiddlewaresFor skips plugins without a middleware for the pipeline", () => {
204
- const mw: Middleware<CompactionArgs, CompactionResult> = async (
205
- args,
206
- next,
207
- ) => next(args);
208
- registerPlugin(buildPlugin("bare"));
209
- registerPlugin(buildPlugin("has-mw", { middleware: { compaction: mw } }));
210
-
211
- const middlewares = getMiddlewaresFor("compaction");
212
- expect(middlewares).toHaveLength(1);
213
- expect(middlewares[0]).toBe(mw);
214
- });
215
-
216
143
  test("getRegisteredPlugins reflects registration order", () => {
217
144
  registerPlugin(buildPlugin("one"));
218
145
  registerPlugin(buildPlugin("two"));
@@ -12,40 +12,11 @@ import { describe, expect, test } from "bun:test";
12
12
 
13
13
  import type { TrustContext } from "../daemon/trust-context.js";
14
14
  import { RiskLevel } from "../permissions/types.js";
15
- import type {
16
- ToolResultTruncateArgs,
17
- ToolResultTruncateResult,
18
- } from "../plugins/defaults/tool-result-truncate/types.js";
19
15
  import {
20
- type CircuitBreakerArgs,
21
- type CircuitBreakerResult,
22
- type CompactionArgs,
23
- type CompactionResult,
24
- type EmptyResponseArgs,
25
- type EmptyResponseResult,
26
- type EstimateArgs,
27
- type EstimateResult,
28
- type Injector,
29
- type LLMCallArgs,
30
- type LLMCallResult,
31
- type MemoryArgs,
32
- type MemoryResult,
33
- type Middleware,
34
- type OverflowReduceArgs,
35
- type OverflowReduceResult,
36
- type PersistArgs,
37
- type PersistResult,
38
16
  type Plugin,
39
17
  PluginExecutionError,
40
18
  type PluginInitContext,
41
19
  type PluginManifest,
42
- PluginTimeoutError,
43
- type TitleArgs,
44
- type TitleResult,
45
- type ToolErrorArgs,
46
- type ToolErrorDecision,
47
- type ToolExecuteArgs,
48
- type ToolExecuteResult,
49
20
  type TurnContext,
50
21
  } from "../plugins/types.js";
51
22
  import type { Tool } from "../tools/types.js";
@@ -59,7 +30,6 @@ const sampleTurnContext: TurnContext = {
59
30
  requestId: "req-abc",
60
31
  conversationId: "conv-xyz",
61
32
  turnIndex: 0,
62
- pluginName: "sample-plugin",
63
33
  trust: sampleTrust,
64
34
  };
65
35
 
@@ -73,134 +43,6 @@ describe("plugin core types", () => {
73
43
  config: { parse: (input: unknown) => input },
74
44
  };
75
45
 
76
- // Generic passthrough — typed per slot below because per-pipeline
77
- // arg/result types have diverged from the early `{input: unknown}` /
78
- // `{output: unknown}` placeholders as individual pipeline wrap-up PRs
79
- // land.
80
- const passthrough: Middleware<
81
- { input: unknown },
82
- { output: unknown }
83
- > = async (args, next, _ctx) => next(args);
84
- // `llmCall` has concrete arg/result types (upgraded in PR 15).
85
- const llmCallPassthrough: Middleware<LLMCallArgs, LLMCallResult> = async (
86
- args,
87
- next,
88
- _ctx,
89
- ) => next(args);
90
-
91
- // `toolExecute` has concrete arg/result types (refined in PR 16).
92
- const toolExecutePassthrough: Middleware<
93
- ToolExecuteArgs,
94
- ToolExecuteResult
95
- > = async (args, next, _ctx) => next(args);
96
-
97
- // `toolResultTruncate` has a concrete args/result shape (PR 17) so we
98
- // need a dedicated passthrough for that slot.
99
- const truncatePassthrough: Middleware<
100
- ToolResultTruncateArgs,
101
- ToolResultTruncateResult
102
- > = async (args, _next, _ctx) => ({
103
- content: args.content,
104
- truncated: false,
105
- });
106
-
107
- // The `emptyResponse` slot has concrete args/result types; use a
108
- // dedicated passthrough so the `satisfies Plugin` check stays honest.
109
- const emptyResponsePassthrough: Middleware<
110
- EmptyResponseArgs,
111
- EmptyResponseResult
112
- > = async (args, next, _ctx) => next(args);
113
-
114
- // The `toolError` slot has concrete args/result types (PR 19); use a
115
- // dedicated passthrough so the shape-only test keeps compiling as types
116
- // get tightened.
117
- const toolErrorPassthrough: Middleware<
118
- ToolErrorArgs,
119
- ToolErrorDecision
120
- > = async (args, next, _ctx) => next(args);
121
-
122
- // `memoryRetrieval` has a concrete typed signature (MemoryArgs →
123
- // MemoryResult) introduced in PR 20, so it can't use the generic
124
- // `{ input }` passthrough above.
125
- const memoryPassthrough: Middleware<MemoryArgs, MemoryResult> = async (
126
- args,
127
- next,
128
- _ctx,
129
- ) => next(args);
130
-
131
- // `tokenEstimate` has a concrete arg/result shape (refined in the
132
- // tokenEstimate-pipeline PR), so its middleware can't share the generic
133
- // `{ input, output }` passthrough. A slot-specific passthrough keeps the
134
- // shape-only assertion honest across type-refinement PRs.
135
- const tokenEstimatePassthrough: Middleware<
136
- EstimateArgs,
137
- EstimateResult
138
- > = async (args, next, _ctx) => next(args);
139
-
140
- // `overflowReduce` has a concrete arg/result shape (PR 23). Uses a
141
- // dedicated passthrough that returns a structurally-correct result so
142
- // `satisfies Plugin` keeps verifying the signature.
143
- const overflowReducePassthrough: Middleware<
144
- OverflowReduceArgs,
145
- OverflowReduceResult
146
- > = async (args, _next, _ctx) => ({
147
- messages: args.messages,
148
- runMessages: args.runMessages,
149
- injectionMode: "full",
150
- reducerState: {
151
- appliedTiers: [],
152
- injectionMode: "full",
153
- exhausted: true,
154
- },
155
- reducerCompacted: false,
156
- attempts: 0,
157
- });
158
-
159
- // Slot-specific passthrough for the `compaction` pipeline — PR 25
160
- // narrowed its args/result away from the generic `{ input: unknown }`
161
- // placeholder, so the generic `passthrough` no longer satisfies its
162
- // middleware signature. This dedicated middleware keeps the shape-only
163
- // assertion for the slot without forcing every other slot to narrow.
164
- const compactionPassthrough: Middleware<
165
- CompactionArgs,
166
- CompactionResult
167
- > = async (args, next, _ctx) => next(args);
168
-
169
- // `circuitBreaker` carries a concrete arg shape (pipeline wrapping
170
- // landed ahead of the other slots) so it needs its own passthrough
171
- // rather than reusing the generic placeholder.
172
- const circuitPassthrough: Middleware<
173
- CircuitBreakerArgs,
174
- CircuitBreakerResult
175
- > = async (args, next, _ctx) => next(args);
176
-
177
- // `persistence` has concrete discriminated-union arg/result types
178
- // (upgraded from the initial `{ input }/{ output }` placeholder in PR 27)
179
- // so it gets its own passthrough rather than sharing the generic one
180
- // above.
181
- const persistPassthrough: Middleware<PersistArgs, PersistResult> = async (
182
- args,
183
- next,
184
- _ctx,
185
- ) => next(args);
186
-
187
- // The `titleGenerate` slot now has concrete arg/result types (PR 28)
188
- // rather than the placeholder `{ input/output: unknown }` shape, so it
189
- // needs its own passthrough implementation.
190
- const titlePassthrough: Middleware<TitleArgs, TitleResult> = async (
191
- args,
192
- next,
193
- _ctx,
194
- ) => next(args);
195
-
196
- const injector: Injector = {
197
- name: "sample-injector",
198
- order: 10,
199
- async produce(_ctx) {
200
- return { id: "sample-block", text: "hello", meta: { kind: "demo" } };
201
- },
202
- };
203
-
204
46
  const sampleTool: Tool = {
205
47
  name: "sample-tool",
206
48
  description: "Sample plugin tool",
@@ -244,45 +86,10 @@ describe("plugin core types", () => {
244
86
  body: "## Sample\n\nPlugin-provided skill body.",
245
87
  },
246
88
  ],
247
- injectors: [injector],
248
- middleware: {
249
- turn: passthrough,
250
- llmCall: llmCallPassthrough,
251
- toolExecute: toolExecutePassthrough,
252
- memoryRetrieval: memoryPassthrough,
253
- tokenEstimate: tokenEstimatePassthrough,
254
- compaction: compactionPassthrough,
255
- overflowReduce: overflowReducePassthrough,
256
- persistence: persistPassthrough,
257
- titleGenerate: titlePassthrough,
258
- toolResultTruncate: truncatePassthrough,
259
- emptyResponse: emptyResponsePassthrough,
260
- toolError: toolErrorPassthrough,
261
- circuitBreaker: circuitPassthrough,
262
- },
263
89
  } satisfies Plugin;
264
90
 
265
91
  // Minimal runtime check so the test body is non-empty.
266
92
  expect(plugin.manifest.name).toBe("sample-plugin");
267
- expect(plugin.middleware.turn).toBe(passthrough);
268
- });
269
-
270
- test("PluginTimeoutError carries pipeline, plugin, and elapsed fields", () => {
271
- const err = new PluginTimeoutError("compaction", "sample-plugin", 30000);
272
- expect(err).toBeInstanceOf(Error);
273
- expect(err.name).toBe("PluginTimeoutError");
274
- expect(err.pipeline).toBe("compaction");
275
- expect(err.pluginName).toBe("sample-plugin");
276
- expect(err.elapsedMs).toBe(30000);
277
- expect(err.message).toContain("compaction");
278
- expect(err.message).toContain("30000");
279
- expect(err.message).toContain("sample-plugin");
280
- });
281
-
282
- test("PluginTimeoutError omits plugin suffix when unknown", () => {
283
- const err = new PluginTimeoutError("llmCall", undefined, 1234);
284
- expect(err.pluginName).toBeUndefined();
285
- expect(err.message).not.toContain("offending plugin");
286
93
  });
287
94
 
288
95
  test("PluginExecutionError carries the plugin name and message", () => {