@vellumai/assistant 0.8.10 → 0.8.11-staging.1

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 (400) hide show
  1. package/bun.lock +62 -1
  2. package/docs/workspace-tools.md +196 -0
  3. package/examples/plugins/echo/README.md +3 -3
  4. package/knip.json +1 -0
  5. package/openapi.yaml +460 -128
  6. package/package.json +2 -1
  7. package/scripts/build-plugin-api.ts +299 -0
  8. package/src/__tests__/agent-loop-callsite-precedence.test.ts +7 -0
  9. package/src/__tests__/agent-loop-compaction-events.test.ts +197 -0
  10. package/src/__tests__/agent-loop-exit-reason.test.ts +93 -96
  11. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +2 -0
  12. package/src/__tests__/agent-loop-output-hooks.test.ts +274 -1
  13. package/src/__tests__/agent-loop-override-profile.test.ts +3 -0
  14. package/src/__tests__/agent-loop-provider-error-recording.test.ts +4 -0
  15. package/src/__tests__/agent-loop-thinking.test.ts +4 -0
  16. package/src/__tests__/agent-loop.test.ts +578 -5
  17. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -1
  18. package/src/__tests__/approval-cascade.test.ts +1 -0
  19. package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
  20. package/src/__tests__/btw-routes.test.ts +0 -1
  21. package/src/__tests__/build-persisted-content.test.ts +75 -1
  22. package/src/__tests__/catalog-install-normalize.test.ts +141 -0
  23. package/src/__tests__/ces-startup-timeout.test.ts +60 -0
  24. package/src/__tests__/compaction-events.test.ts +1 -0
  25. package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
  26. package/src/__tests__/context-overflow-reducer.test.ts +264 -124
  27. package/src/__tests__/context-window-manager-overflow-rung.test.ts +351 -0
  28. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  29. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -5
  30. package/src/__tests__/conversation-agent-loop-overflow.test.ts +284 -455
  31. package/src/__tests__/conversation-agent-loop.test.ts +131 -551
  32. package/src/__tests__/conversation-app-control-instantiation.test.ts +13 -0
  33. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
  34. package/src/__tests__/conversation-fork-crud.test.ts +259 -0
  35. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  36. package/src/__tests__/conversation-lifecycle.test.ts +257 -1
  37. package/src/__tests__/conversation-process-callsite.test.ts +1 -0
  38. package/src/__tests__/conversation-provider-retry-repair.test.ts +38 -377
  39. package/src/__tests__/conversation-queue.test.ts +1 -39
  40. package/src/__tests__/conversation-runtime-assembly.test.ts +119 -8
  41. package/src/__tests__/conversation-skill-tools.test.ts +491 -5
  42. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  43. package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
  44. package/src/__tests__/conversation-speed-override.test.ts +1 -0
  45. package/src/__tests__/conversation-store.test.ts +74 -0
  46. package/src/__tests__/conversation-surfaces-app-control.test.ts +4 -1
  47. package/src/__tests__/conversation-tool-setup-attribution.test.ts +323 -0
  48. package/src/__tests__/conversation-tool-setup-tools-disabled.test.ts +34 -0
  49. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
  50. package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
  51. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -0
  52. package/src/__tests__/corrected-target.test.ts +93 -0
  53. package/src/__tests__/credential-execution-feature-gates.test.ts +3 -5
  54. package/src/__tests__/credential-execution-tools.test.ts +23 -11
  55. package/src/__tests__/credential-security-invariants.test.ts +6 -1
  56. package/src/__tests__/db-schedule-syntax-migration.test.ts +80 -0
  57. package/src/__tests__/device-id.test.ts +70 -1
  58. package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
  59. package/src/__tests__/empty-response-hook.test.ts +242 -66
  60. package/src/__tests__/external-plugin-loader.test.ts +0 -31
  61. package/src/__tests__/get-skill-detail-audit.test.ts +43 -1
  62. package/src/__tests__/guardian-routing-invariants.test.ts +91 -0
  63. package/src/__tests__/history-repair-hook.test.ts +228 -3
  64. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  65. package/src/__tests__/host-browser-proxy.test.ts +254 -9
  66. package/src/__tests__/identity-routes.test.ts +1 -0
  67. package/src/__tests__/image-recovery-hook.test.ts +387 -0
  68. package/src/__tests__/injector-chain.test.ts +5 -4
  69. package/src/__tests__/injector-v3-suppression.test.ts +373 -47
  70. package/src/__tests__/intent-routing.test.ts +7 -0
  71. package/src/__tests__/memory-retrieval-hook.test.ts +117 -15
  72. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  73. package/src/__tests__/oauth-store.test.ts +0 -85
  74. package/src/__tests__/{context-overflow-policy.test.ts → overflow-policy.test.ts} +1 -1
  75. package/src/__tests__/parallel-tool.benchmark.test.ts +4 -0
  76. package/src/__tests__/persist-unsendable-image-downscale.test.ts +29 -9
  77. package/src/__tests__/persist-unsendable-image.test.ts +4 -4
  78. package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
  79. package/src/__tests__/plugin-bootstrap.test.ts +82 -73
  80. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  81. package/src/__tests__/plugin-types.test.ts +0 -8
  82. package/src/__tests__/provider-catalog-visibility.test.ts +1 -9
  83. package/src/__tests__/prune-old-conversations-job.test.ts +99 -0
  84. package/src/__tests__/registry.test.ts +240 -1
  85. package/src/__tests__/require-fresh-approval.test.ts +3 -0
  86. package/src/__tests__/schedule-routes.test.ts +116 -1
  87. package/src/__tests__/schedule-store.test.ts +28 -0
  88. package/src/__tests__/schedule-tools.test.ts +94 -1
  89. package/src/__tests__/server-history-render.test.ts +39 -0
  90. package/src/__tests__/skill-projection-feature-flag.test.ts +13 -0
  91. package/src/__tests__/skill-projection.benchmark.test.ts +25 -7
  92. package/src/__tests__/skills.test.ts +202 -0
  93. package/src/__tests__/slim-skill-category.test.ts +195 -0
  94. package/src/__tests__/strip-memory-injections.test.ts +33 -39
  95. package/src/__tests__/test-support/tool-invocation-seed.ts +79 -0
  96. package/src/__tests__/title-generate-hook.test.ts +9 -7
  97. package/src/__tests__/tool-audit-listener.test.ts +264 -1
  98. package/src/__tests__/tool-error-hook.test.ts +4 -3
  99. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -0
  100. package/src/__tests__/tool-executor-lifecycle-events.test.ts +273 -0
  101. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
  102. package/src/__tests__/tool-start-timestamp.test.ts +218 -0
  103. package/src/__tests__/tools-get-route.test.ts +202 -0
  104. package/src/__tests__/workspace-tool-loader.test.ts +319 -0
  105. package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
  106. package/src/agent/loop.ts +569 -319
  107. package/src/api/events/tool-result.ts +9 -0
  108. package/src/api/events/tool-use-start.ts +7 -0
  109. package/src/api/index.ts +10 -0
  110. package/src/api/responses/conversation-message.ts +135 -27
  111. package/src/api/responses/memory-v3-selection-log.ts +4 -4
  112. package/src/approvals/guardian-request-resolvers.ts +26 -0
  113. package/src/browser-session/backends/host-bridge.ts +29 -0
  114. package/src/browser-session/index.ts +1 -0
  115. package/src/browser-session/types.ts +5 -1
  116. package/src/cli/commands/__tests__/schedules.test.ts +62 -4
  117. package/src/cli/commands/__tests__/skills.test.ts +53 -0
  118. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  119. package/src/cli/commands/inference-providers.ts +0 -8
  120. package/src/cli/commands/plugins.ts +2 -2
  121. package/src/cli/commands/schedules.ts +27 -4
  122. package/src/cli/commands/skills.ts +187 -146
  123. package/src/cli/commands/tools.ts +106 -0
  124. package/src/cli/lib/__tests__/install-from-github.test.ts +256 -328
  125. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +6 -2
  126. package/src/cli/lib/__tests__/plugin-details.test.ts +10 -16
  127. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +2 -2
  128. package/src/cli/lib/__tests__/search-plugins.test.ts +145 -240
  129. package/src/cli/lib/install-from-github.ts +187 -117
  130. package/src/cli/lib/plugin-catalog-cache.ts +9 -9
  131. package/src/cli/lib/plugin-details.ts +38 -68
  132. package/src/cli/lib/plugin-marketplace.ts +42 -14
  133. package/src/cli/lib/search-plugins.ts +29 -129
  134. package/src/cli/program.ts +2 -0
  135. package/src/config/bundled-skills/acp/SKILL.md +1 -0
  136. package/src/config/bundled-skills/app-builder/SKILL.md +1 -0
  137. package/src/config/bundled-skills/app-control/SKILL.md +1 -0
  138. package/src/config/bundled-skills/computer-use/SKILL.md +1 -0
  139. package/src/config/bundled-skills/contacts/SKILL.md +1 -0
  140. package/src/config/bundled-skills/document-editor/SKILL.md +1 -0
  141. package/src/config/bundled-skills/followups/SKILL.md +1 -0
  142. package/src/config/bundled-skills/image-studio/SKILL.md +1 -0
  143. package/src/config/bundled-skills/media-processing/SKILL.md +1 -0
  144. package/src/config/bundled-skills/messaging/SKILL.md +1 -0
  145. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -0
  146. package/src/config/bundled-skills/playbooks/SKILL.md +1 -0
  147. package/src/config/bundled-skills/schedule/SKILL.md +1 -0
  148. package/src/config/bundled-skills/schedule/TOOLS.json +11 -3
  149. package/src/config/bundled-skills/sequences/SKILL.md +1 -0
  150. package/src/config/bundled-skills/settings/SKILL.md +1 -0
  151. package/src/config/bundled-skills/skill-management/SKILL.md +101 -1
  152. package/src/config/bundled-skills/subagent/SKILL.md +1 -0
  153. package/src/config/bundled-skills/transcribe/SKILL.md +1 -0
  154. package/src/config/env-registry.ts +23 -0
  155. package/src/config/feature-flag-registry.json +17 -81
  156. package/src/config/loader.ts +5 -22
  157. package/src/config/schema.ts +2 -0
  158. package/src/config/schemas/__tests__/compaction-logs.test.ts +56 -0
  159. package/src/config/schemas/__tests__/memory-v2.test.ts +0 -1
  160. package/src/config/schemas/__tests__/memory-v3.test.ts +61 -1
  161. package/src/config/schemas/compaction-logs.ts +79 -0
  162. package/src/config/schemas/memory-v2.ts +0 -8
  163. package/src/config/schemas/memory-v3.ts +104 -33
  164. package/src/config/seed-inference-profiles.ts +1 -1
  165. package/src/config/skills.ts +117 -47
  166. package/src/context/compactor.ts +11 -0
  167. package/src/context/strip-injections.ts +38 -4
  168. package/src/credential-execution/feature-gates.ts +0 -21
  169. package/src/credential-execution/startup-timeout.ts +32 -4
  170. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +18 -0
  171. package/src/daemon/conversation-agent-loop-handlers.ts +140 -91
  172. package/src/daemon/conversation-agent-loop.ts +123 -658
  173. package/src/daemon/conversation-error.ts +6 -33
  174. package/src/daemon/conversation-lifecycle.ts +1 -1
  175. package/src/daemon/conversation-runtime-assembly.ts +183 -22
  176. package/src/daemon/conversation-skill-tools.ts +137 -8
  177. package/src/daemon/conversation-slash.ts +0 -14
  178. package/src/daemon/conversation-store.ts +2 -19
  179. package/src/daemon/conversation-tool-setup.ts +87 -1
  180. package/src/daemon/conversation.ts +119 -50
  181. package/src/daemon/external-plugins-bootstrap.ts +36 -95
  182. package/src/daemon/handlers/config-channels.ts +11 -2
  183. package/src/daemon/handlers/shared.ts +9 -1
  184. package/src/daemon/handlers/skills.ts +10 -4
  185. package/src/daemon/host-app-control-proxy.ts +72 -57
  186. package/src/daemon/host-browser-proxy.ts +117 -22
  187. package/src/daemon/lifecycle.ts +34 -5
  188. package/src/daemon/message-protocol.ts +0 -7
  189. package/src/daemon/message-types/schedules.ts +1 -0
  190. package/src/daemon/message-types/skills.ts +17 -0
  191. package/src/daemon/providers-setup.ts +3 -0
  192. package/src/daemon/server.ts +3 -3
  193. package/src/daemon/tool-setup-types.ts +9 -3
  194. package/src/daemon/trust-context.ts +23 -0
  195. package/src/daemon/workspace-tools-watcher.ts +324 -0
  196. package/src/events/tool-audit-listener.ts +78 -16
  197. package/src/events/tool-metrics-listener.ts +2 -5
  198. package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +227 -0
  199. package/src/memory/__tests__/conversation-queries.test.ts +176 -0
  200. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +20 -32
  201. package/src/memory/compaction-log-writer-clickhouse.ts +418 -0
  202. package/src/memory/conversation-crud.ts +134 -3
  203. package/src/memory/conversation-queries.ts +64 -7
  204. package/src/memory/db-init.ts +14 -0
  205. package/src/memory/embedding-backend.test.ts +130 -1
  206. package/src/memory/embedding-backend.ts +79 -106
  207. package/src/memory/embedding-gemini.ts +5 -0
  208. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +12 -0
  209. package/src/memory/graph/__tests__/handle-remember-v2.test.ts +19 -0
  210. package/src/memory/graph/conversation-graph-memory.ts +36 -25
  211. package/src/memory/graph/tool-handlers.ts +3 -0
  212. package/src/memory/job-handlers/cleanup.ts +3 -1
  213. package/src/memory/jobs-store.ts +0 -28
  214. package/src/memory/jobs-worker.ts +10 -22
  215. package/src/memory/memory-marker.ts +29 -0
  216. package/src/memory/memory-retrospective-startup-cleanup.ts +1 -1
  217. package/src/memory/migrations/268-add-memory-v3-selections.ts +6 -0
  218. package/src/memory/migrations/270-schedule-description.ts +36 -0
  219. package/src/memory/migrations/275-tool-invocations-add-skill-id.test.ts +81 -0
  220. package/src/memory/migrations/275-tool-invocations-add-skill-id.ts +20 -0
  221. package/src/memory/migrations/276-tool-invocations-created-at-id-index.test.ts +68 -0
  222. package/src/memory/migrations/276-tool-invocations-created-at-id-index.ts +20 -0
  223. package/src/memory/migrations/277-add-memory-v3-ever-injected.ts +29 -0
  224. package/src/memory/migrations/278-tool-invocations-telemetry-columns.test.ts +96 -0
  225. package/src/memory/migrations/278-tool-invocations-telemetry-columns.ts +39 -0
  226. package/src/memory/migrations/279-create-skill-loaded-events.test.ts +84 -0
  227. package/src/memory/migrations/279-create-skill-loaded-events.ts +26 -0
  228. package/src/memory/migrations/280-conversations-surfaced-at.test.ts +88 -0
  229. package/src/memory/migrations/280-conversations-surfaced-at.ts +24 -0
  230. package/src/memory/migrations/index.ts +10 -0
  231. package/src/memory/migrations/registry.ts +8 -0
  232. package/src/memory/schema/conversations.ts +16 -0
  233. package/src/memory/schema/infrastructure.ts +26 -0
  234. package/src/memory/skill-loaded-events-store.test.ts +160 -0
  235. package/src/memory/skill-loaded-events-store.ts +95 -0
  236. package/src/memory/tool-executed-events-store.test.ts +219 -0
  237. package/src/memory/tool-executed-events-store.ts +102 -0
  238. package/src/memory/tool-usage-store.ts +15 -3
  239. package/src/memory/v2/__tests__/consolidation-job.test.ts +117 -12
  240. package/src/memory/v2/__tests__/consolidation-prompt-flag-gating-guard.test.ts +189 -0
  241. package/src/memory/v2/__tests__/injected-block-slugs.test.ts +90 -0
  242. package/src/memory/v2/__tests__/page-store.test.ts +33 -0
  243. package/src/memory/v2/__tests__/prompts-consolidation.test.ts +88 -15
  244. package/src/memory/v2/activation-store.ts +50 -1
  245. package/src/memory/v2/consolidation-job.ts +113 -29
  246. package/src/memory/v2/injected-block-slugs.ts +79 -0
  247. package/src/memory/v2/injection.ts +6 -1
  248. package/src/memory/v2/prompts/consolidation.ts +414 -13
  249. package/src/memory/v2/router.ts +2 -28
  250. package/src/memory/v2/static-context.ts +1 -1
  251. package/src/memory/v2/types.ts +16 -0
  252. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  253. package/src/notifications/access-request-copy.ts +298 -0
  254. package/src/notifications/adapters/slack.ts +3 -3
  255. package/src/notifications/adapters/telegram.ts +2 -1
  256. package/src/notifications/copy-composer.ts +49 -267
  257. package/src/notifications/decision-engine.ts +16 -35
  258. package/src/notifications/home-feed-side-effect.ts +1 -6
  259. package/src/oauth/oauth-store.ts +0 -9
  260. package/src/permissions/checker.test.ts +83 -1
  261. package/src/permissions/checker.ts +25 -2
  262. package/src/permissions/gateway-threshold-reader.test.ts +182 -0
  263. package/src/permissions/gateway-threshold-reader.ts +80 -0
  264. package/src/platform/client.ts +1 -3
  265. package/src/platform/feature-gate.ts +3 -12
  266. package/src/plugin-api/constants.ts +4 -2
  267. package/src/plugin-api/index.ts +63 -11
  268. package/src/plugin-api/types.ts +236 -71
  269. package/src/plugins/defaults/compaction/compact.ts +66 -2
  270. package/src/plugins/defaults/compaction/context-overflow-reducer.ts +240 -32
  271. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  272. package/src/{daemon/context-overflow-policy.ts → plugins/defaults/compaction/overflow-policy.ts} +1 -1
  273. package/src/plugins/defaults/compaction/window-manager.ts +303 -1
  274. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +173 -0
  275. package/src/plugins/defaults/empty-response/hooks/stop.ts +11 -115
  276. package/src/plugins/defaults/empty-response/nudge-state-store.ts +46 -0
  277. package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +50 -0
  278. package/src/plugins/defaults/history-repair/hooks/stop.ts +22 -0
  279. package/src/plugins/defaults/history-repair/repair-state-store.ts +51 -0
  280. package/src/plugins/defaults/history-repair/terminal.ts +39 -2
  281. package/src/plugins/defaults/image-recovery/detect.ts +25 -0
  282. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +73 -0
  283. package/src/plugins/defaults/image-recovery/hooks/stop.ts +22 -0
  284. package/src/plugins/defaults/image-recovery/image-recovery-state-store.ts +48 -0
  285. package/src/plugins/defaults/image-recovery/package.json +14 -0
  286. package/src/{daemon/persist-unsendable-image.ts → plugins/defaults/image-recovery/recover.ts} +67 -14
  287. package/src/plugins/defaults/index.ts +71 -5
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +76 -112
  289. package/src/plugins/defaults/memory-retrieval/hooks/{user-prompt-submit-temp.ts → user-prompt-submit.ts} +83 -74
  290. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +14 -8
  291. package/src/plugins/defaults/memory-retrieval/injectors.ts +2 -18
  292. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  293. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1157 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +683 -0
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +161 -140
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +160 -0
  297. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +335 -316
  298. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +145 -53
  299. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +38 -1
  300. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +18 -8
  301. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +112 -71
  302. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +192 -51
  303. package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +4 -16
  304. package/src/plugins/defaults/memory-v3-shadow/card.test.ts +173 -0
  305. package/src/plugins/defaults/memory-v3-shadow/card.ts +116 -0
  306. package/src/plugins/defaults/memory-v3-shadow/core-set.test.ts +104 -0
  307. package/src/plugins/defaults/memory-v3-shadow/core-set.ts +59 -0
  308. package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.test.ts +305 -0
  309. package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.ts +278 -0
  310. package/src/plugins/defaults/memory-v3-shadow/hot-set.test.ts +138 -0
  311. package/src/plugins/defaults/memory-v3-shadow/hot-set.ts +85 -0
  312. package/src/plugins/defaults/memory-v3-shadow/injector.ts +331 -24
  313. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +119 -13
  314. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +169 -114
  315. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +47 -16
  316. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +144 -66
  317. package/src/plugins/defaults/memory-v3-shadow/prune.test.ts +758 -0
  318. package/src/plugins/defaults/memory-v3-shadow/prune.ts +471 -0
  319. package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +68 -16
  320. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +19 -12
  321. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +96 -43
  322. package/src/plugins/defaults/memory-v3-shadow/types.ts +34 -17
  323. package/src/plugins/defaults/title-generate/hooks/stop.ts +9 -11
  324. package/src/plugins/pipeline.ts +8 -5
  325. package/src/plugins/types.ts +5 -51
  326. package/src/providers/cache-control.ts +26 -0
  327. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -2
  328. package/src/providers/model-catalog.ts +13 -1
  329. package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +147 -0
  330. package/src/providers/openai/chat-completions-provider.ts +46 -0
  331. package/src/providers/openai/responses-provider.ts +45 -0
  332. package/src/providers/registry.ts +0 -8
  333. package/src/runtime/__tests__/agent-wake.test.ts +0 -1
  334. package/src/runtime/agent-wake.ts +13 -0
  335. package/src/runtime/routes/__tests__/acp-routes.test.ts +151 -0
  336. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +12 -50
  337. package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +322 -0
  338. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +0 -62
  339. package/src/runtime/routes/__tests__/plugins-routes.test.ts +31 -11
  340. package/src/runtime/routes/acp-routes.test.ts +106 -0
  341. package/src/runtime/routes/acp-routes.ts +248 -2
  342. package/src/runtime/routes/browser-tabs-routes.ts +1 -1
  343. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  344. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +0 -14
  345. package/src/runtime/routes/consolidation-routes.ts +6 -82
  346. package/src/runtime/routes/conversation-list-routes.ts +6 -0
  347. package/src/runtime/routes/conversation-management-routes.ts +71 -0
  348. package/src/runtime/routes/identity-routes.ts +8 -0
  349. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  350. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -45
  351. package/src/runtime/routes/plugins-routes.ts +34 -45
  352. package/src/runtime/routes/schedule-routes.ts +43 -5
  353. package/src/runtime/routes/settings-routes.ts +140 -15
  354. package/src/runtime/routes/skills-routes.ts +18 -6
  355. package/src/runtime/services/__tests__/conversation-serializer.test.ts +140 -0
  356. package/src/runtime/services/conversation-serializer.ts +38 -1
  357. package/src/runtime/verification-outbound-actions.ts +147 -2
  358. package/src/runtime/verification-templates.ts +29 -3
  359. package/src/schedule/schedule-store.ts +19 -0
  360. package/src/skills/catalog-install.ts +77 -13
  361. package/src/tasks/task-scheduler.ts +1 -0
  362. package/src/telemetry/types.ts +66 -1
  363. package/src/telemetry/usage-telemetry-reporter.test.ts +542 -13
  364. package/src/telemetry/usage-telemetry-reporter.ts +213 -20
  365. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +49 -2
  366. package/src/tools/browser/__tests__/browser-status.test.ts +29 -5
  367. package/src/tools/browser/browser-execution.ts +27 -9
  368. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +380 -4
  369. package/src/tools/browser/cdp-client/__tests__/host-bridge-cdp-client.test.ts +107 -0
  370. package/src/tools/browser/cdp-client/__tests__/types.test.ts +6 -1
  371. package/src/tools/browser/cdp-client/factory.ts +217 -17
  372. package/src/tools/browser/cdp-client/host-bridge-cdp-client.ts +67 -0
  373. package/src/tools/browser/cdp-client/types.ts +22 -2
  374. package/src/tools/credential-execution/make-authenticated-request.ts +2 -1
  375. package/src/tools/credential-execution/manage-secure-command-tool.ts +169 -164
  376. package/src/tools/credential-execution/run-authenticated-command.ts +2 -1
  377. package/src/tools/executor.ts +39 -7
  378. package/src/tools/registry.ts +387 -5
  379. package/src/tools/schedule/create.ts +16 -0
  380. package/src/tools/schedule/list.ts +12 -4
  381. package/src/tools/schedule/update.ts +12 -0
  382. package/src/tools/skills/load.ts +11 -6
  383. package/src/tools/terminal/safe-env.ts +2 -0
  384. package/src/tools/types.ts +65 -9
  385. package/src/tools/workspace-tools/loader.ts +673 -0
  386. package/src/usage/attribution.ts +28 -0
  387. package/src/util/device-id.ts +17 -3
  388. package/src/util/platform.ts +16 -0
  389. package/tsconfig.plugin-api.json +13 -0
  390. package/src/__tests__/plugin-external-api.test.ts +0 -68
  391. package/src/__tests__/plugin-skill-contribution.test.ts +0 -355
  392. package/src/daemon/message-types/browser.ts +0 -10
  393. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  394. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +0 -36
  395. package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +0 -37
  396. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +0 -106
  397. package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +0 -44
  398. package/src/plugins/defaults/memory-v3-shadow/working-set.ts +0 -91
  399. package/src/plugins/external-api.ts +0 -114
  400. package/src/plugins/plugin-skill-contributions.ts +0 -292
@@ -2,10 +2,8 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
3
  import { CompactionCircuit } from "../agent/compaction-circuit.js";
4
4
  import type { AgentEvent, AgentLoopRunResult } from "../agent/loop.js";
5
- import type { UserMessageAttachment } from "../daemon/message-protocol.js";
6
5
  import { resetPluginRegistryAndRegisterDefaults } from "../plugins/defaults/index.js";
7
6
  import type { Message, ProviderResponse } from "../providers/types.js";
8
- import { ProviderError } from "../util/errors.js";
9
7
 
10
8
  mock.module("../util/logger.js", () => ({
11
9
  getLogger: () =>
@@ -82,82 +80,16 @@ mock.module("../config/loader.js", () => ({
82
80
  invalidateConfigCache: () => {},
83
81
  }));
84
82
 
85
- // Token estimator: return a small value (well within budget) so preflight
86
- // does not trigger in existing tests. Stub both the calibrated and raw
87
- // entry points — the calibrated estimate backs the orchestrator's preflight /
88
- // mid-loop overflow checkpoints, and the raw estimate backs the pre-send
89
- // calibration capture.
83
+ // Token estimator: return a small value (well within budget) so the loop's
84
+ // pre-call budget gate does not trip in these tests. Stub both the calibrated
85
+ // and raw entry points.
90
86
  mock.module("../context/token-estimator.js", () => ({
91
87
  estimatePromptTokens: () => 1000,
92
88
  estimatePromptTokensRaw: () => 1000,
93
- // The preflight overflow gate calls this calibrated wrapper directly; stub
94
- // it alongside the others so it returns the same small value.
95
89
  estimatePromptTokensWithTools: () => 1000,
96
90
  estimateToolsTokens: () => 0,
97
91
  }));
98
92
 
99
- // Overflow recovery module mocks — the convergence loop delegates to these
100
- // but these tests exercise the Conversation-level flow, not the reducer internals.
101
- // The reducer mock delegates to the compactFn to simulate a forced compaction
102
- // tier, matching the real reducer's behavior for Tier 1.
103
- mock.module(
104
- "../plugins/defaults/compaction/context-overflow-reducer.js",
105
- () => ({
106
- createInitialReducerState: () => ({
107
- appliedTiers: [],
108
- injectionMode: "full" as const,
109
- exhausted: false,
110
- }),
111
- reduceContextOverflow: async (
112
- msgs: Message[],
113
- _cfg: unknown,
114
- _state: unknown,
115
- compactFn?: (
116
- m: Message[],
117
- s: AbortSignal | undefined,
118
- o: Record<string, unknown>,
119
- ) => Promise<{
120
- compacted: boolean;
121
- messages: Message[];
122
- compactedPersistedMessages?: number;
123
- summaryText?: string;
124
- [k: string]: unknown;
125
- }>,
126
- signal?: AbortSignal,
127
- ) => {
128
- let resultMessages = msgs;
129
- let compactionResult;
130
- if (compactFn) {
131
- const cr = await compactFn(msgs, signal, { force: true });
132
- if (cr.compacted) {
133
- resultMessages = cr.messages;
134
- compactionResult = cr;
135
- }
136
- }
137
- return {
138
- messages: resultMessages,
139
- tier: "forced_compaction",
140
- state: {
141
- appliedTiers: [
142
- "forced_compaction",
143
- "tool_result_truncation",
144
- "media_stubbing",
145
- "injection_downgrade",
146
- ],
147
- injectionMode: "full",
148
- exhausted: true,
149
- },
150
- estimatedTokens: 1000,
151
- compactionResult,
152
- };
153
- },
154
- }),
155
- );
156
-
157
- mock.module("../daemon/context-overflow-policy.js", () => ({
158
- resolveOverflowAction: () => "fail_gracefully",
159
- }));
160
-
161
93
  mock.module("../prompts/system-prompt.js", () => ({
162
94
  buildSystemPrompt: () => "system prompt",
163
95
  }));
@@ -218,41 +150,20 @@ mock.module("../memory/retriever.js", () => ({
218
150
  injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
219
151
  }));
220
152
 
221
- let maybeCompactCalls: Array<{ force: boolean }> = [];
222
- let forceCompactionEnabled = false;
223
-
224
153
  mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
225
154
  ContextWindowManager: class {
226
- constructor() {}
155
+ conversationId: string | undefined;
156
+ constructor(options?: { conversationId?: string }) {
157
+ this.conversationId = options?.conversationId;
158
+ }
227
159
  updateConfig() {}
228
160
  shouldCompact() {
229
161
  return { needed: false, estimatedTokens: 0 };
230
162
  }
231
- async maybeCompact(
232
- messages: Message[],
233
- _signal?: AbortSignal,
234
- options?: { force?: boolean },
235
- ) {
236
- maybeCompactCalls.push({ force: options?.force === true });
237
- if (options?.force && forceCompactionEnabled) {
238
- return {
239
- compacted: true,
240
- messages,
241
- previousEstimatedInputTokens: 120000,
242
- estimatedInputTokens: 50000,
243
- maxInputTokens: 100000,
244
- thresholdTokens: 80000,
245
- compactedMessages: 2,
246
- compactedPersistedMessages: 2,
247
- summaryCalls: 1,
248
- summaryInputTokens: 200,
249
- summaryOutputTokens: 50,
250
- summaryModel: "mock-summary-model",
251
- summaryText: "## Goals\n- compacted",
252
- };
253
- }
163
+ async maybeCompact() {
254
164
  return { compacted: false };
255
165
  }
166
+ resetOverflowRecovery() {}
256
167
  },
257
168
  createContextSummaryMessage: () => ({
258
169
  role: "user",
@@ -263,13 +174,7 @@ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
263
174
 
264
175
  // Track how many times agentLoop.run was called
265
176
  let agentLoopRunCount = 0;
266
- let firstRunErrorMode:
267
- | "none"
268
- | "ordering"
269
- | "context_too_large"
270
- | "context_too_large_phrase"
271
- | "context_too_large_413"
272
- | "context_too_large_413_with_progress" = "ordering";
177
+ let firstRunErrorMode: "none" | "ordering" = "ordering";
273
178
 
274
179
  mock.module("../agent/loop.js", () => ({
275
180
  AgentLoop: class {
@@ -294,70 +199,11 @@ mock.module("../agent/loop.js", () => ({
294
199
  await onEvent({ type: "llm_call_started" });
295
200
  agentLoopRunCount++;
296
201
 
297
- if (
298
- agentLoopRunCount === 1 &&
299
- firstRunErrorMode === "context_too_large_413_with_progress"
300
- ) {
301
- // Simulate a run that made progress (tool-use + tool-result) before
302
- // hitting a 413 context-too-large error on the second LLM call.
303
- onEvent({
304
- type: "usage",
305
- inputTokens: 10,
306
- outputTokens: 20,
307
- model: "mock",
308
- providerDurationMs: 50,
309
- });
310
- const assistantMsg: Message = {
311
- role: "assistant",
312
- content: [
313
- {
314
- type: "tool_use",
315
- id: "tu-1",
316
- name: "bash",
317
- input: { command: "echo hi" },
318
- },
319
- ],
320
- };
321
- onEvent({ type: "message_complete", message: assistantMsg });
322
- onEvent({
323
- type: "tool_result",
324
- toolUseId: "tu-1",
325
- content: "hi",
326
- isError: false,
327
- });
328
- // Now the second LLM call fails with 413
329
- onEvent({
330
- type: "error",
331
- error: new ProviderError(
332
- "request entity too large",
333
- "mock-provider",
334
- 413,
335
- ),
336
- });
337
- const history = [...messages];
338
- history.push(assistantMsg);
339
- history.push({
340
- role: "user",
341
- content: [
342
- { type: "tool_result", tool_use_id: "tu-1", content: "hi" },
343
- ],
344
- } as Message);
345
- // Progress was made — history grew
346
- return {
347
- history,
348
- exitReason: null,
349
- appendedNewMessages: true,
350
- newMessages: history.slice(messages.length),
351
- };
352
- }
353
-
354
- // Context-too-large modes: keep failing when compaction can't help
355
- const isContextTooLargeMode =
356
- firstRunErrorMode !== "none" && firstRunErrorMode !== "ordering";
357
- const shouldError =
358
- firstRunErrorMode !== "none" &&
359
- (agentLoopRunCount === 1 ||
360
- (isContextTooLargeMode && !forceCompactionEnabled));
202
+ // Ordering-error recovery lives inside `AgentLoop.run`; with the loop
203
+ // mocked here, an unrecovered ordering error surfaces as a terminal
204
+ // `error` event so we can assert the wrapper's surface behavior — it
205
+ // routes the error to a durable conversation_error and does not re-run.
206
+ const shouldError = firstRunErrorMode === "ordering";
361
207
 
362
208
  if (shouldError) {
363
209
  onEvent({
@@ -367,34 +213,14 @@ mock.module("../agent/loop.js", () => ({
367
213
  model: "mock",
368
214
  providerDurationMs: 0,
369
215
  });
370
- const error = (() => {
371
- if (firstRunErrorMode === "ordering") {
372
- return new Error(
373
- "tool_result blocks that are not immediately after a tool_use block",
374
- );
375
- }
376
- if (firstRunErrorMode === "context_too_large_phrase") {
377
- return new Error(
378
- "The conversation is too long for the model to process.",
379
- );
380
- }
381
- if (firstRunErrorMode === "context_too_large_413") {
382
- return new ProviderError(
383
- "request entity too large",
384
- "mock-provider",
385
- 413,
386
- );
387
- }
388
- return new Error(
389
- "context_length_exceeded: request has too many input tokens",
390
- );
391
- })();
216
+ const error = new Error(
217
+ "tool_result blocks that are not immediately after a tool_use block",
218
+ );
392
219
  onEvent({ type: "error", error });
393
220
  // Return unchanged — no progress
394
221
  return {
395
222
  history: [...messages],
396
223
  exitReason: null,
397
- appendedNewMessages: false,
398
224
  newMessages: [],
399
225
  };
400
226
  }
@@ -417,7 +243,6 @@ mock.module("../agent/loop.js", () => ({
417
243
  return {
418
244
  history,
419
245
  exitReason: null,
420
- appendedNewMessages: true,
421
246
  newMessages: history.slice(messages.length),
422
247
  };
423
248
  }
@@ -474,23 +299,10 @@ function makeConversation(): Conversation {
474
299
  );
475
300
  }
476
301
 
477
- function makeImageAttachments(
478
- count: number,
479
- bytesPerImage = 20_000,
480
- ): UserMessageAttachment[] {
481
- return Array.from({ length: count }, (_, i) => ({
482
- filename: `shot-${i + 1}.png`,
483
- mimeType: "image/png",
484
- data: `${i}${"A".repeat(bytesPerImage)}`,
485
- }));
486
- }
487
-
488
- describe("provider ordering error retry", () => {
302
+ describe("terminal provider ordering error", () => {
489
303
  beforeEach(() => {
490
304
  agentLoopRunCount = 0;
491
305
  firstRunErrorMode = "ordering";
492
- maybeCompactCalls = [];
493
- forceCompactionEnabled = false;
494
306
  // The compaction pipeline runs through the plugin registry; re-register
495
307
  // every default so it has a middleware to dispatch to. Collaborators are
496
308
  // mocked above, so the default plugins' delegates go through the mocked
@@ -498,12 +310,13 @@ describe("provider ordering error retry", () => {
498
310
  resetPluginRegistryAndRegisterDefaults();
499
311
  });
500
312
 
501
- test("simulated strict provider error triggers exactly one retry", async () => {
313
+ test("surfaces a durable conversation error without re-running the loop", async () => {
314
+ // GIVEN the agent loop reports an unrecovered ordering error
502
315
  firstRunErrorMode = "ordering";
503
-
504
316
  const conversation = makeConversation();
505
317
  await conversation.loadFromDb();
506
318
 
319
+ // WHEN the conversation processes a message
507
320
  const events: Array<Record<string, unknown>> = [];
508
321
  await conversation.processMessage({
509
322
  content: "Hello",
@@ -511,43 +324,26 @@ describe("provider ordering error retry", () => {
511
324
  onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
512
325
  });
513
326
 
514
- // Should have been called exactly 2 times: original + one retry
515
- expect(agentLoopRunCount).toBe(2);
516
- });
517
-
518
- test("[experimental] retry succeeds with repaired history and no spurious error event", async () => {
519
- firstRunErrorMode = "ordering";
520
-
521
- const conversation = makeConversation();
522
- await conversation.loadFromDb();
523
-
524
- const events: Array<Record<string, unknown>> = [];
525
- await conversation.processMessage({
526
- content: "Hello",
527
- attachments: [],
528
- onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
529
- });
530
-
531
- // Should have a message_complete event (from successful retry)
532
- const messageComplete = events.find((e) => e.type === "message_complete");
533
- expect(messageComplete).toBeDefined();
534
-
535
- // Ordering error should be suppressed when retry succeeds — no error events
536
- const errorEvents = events.filter((e) => e.type === "error");
537
- expect(errorEvents.length).toBe(0);
327
+ // THEN the wrapper invokes the loop exactly once — ordering recovery is
328
+ // the loop's responsibility, not a wrapper-level re-run
329
+ expect(agentLoopRunCount).toBe(1);
538
330
 
539
- // Should also have the assistant response in memory
540
- const messages = conversation.getMessages();
541
- const lastMsg = messages[messages.length - 1];
542
- expect(lastMsg.role).toBe("assistant");
331
+ // AND the error is surfaced as a durable conversation_error consistent
332
+ // with every other terminal provider error
333
+ const conversationError = events.find(
334
+ (e) => e.type === "conversation_error",
335
+ );
336
+ expect(conversationError).toBeDefined();
337
+ expect(conversationError?.code).toBe("PROVIDER_ORDERING");
543
338
  });
544
339
 
545
- test("non-ordering errors do not trigger retry", async () => {
340
+ test("runs the loop once and emits no error on a normal turn", async () => {
341
+ // GIVEN the agent loop completes the turn without error
546
342
  firstRunErrorMode = "none";
547
-
548
343
  const conversation = makeConversation();
549
344
  await conversation.loadFromDb();
550
345
 
346
+ // WHEN the conversation processes a message
551
347
  const events: Array<Record<string, unknown>> = [];
552
348
  await conversation.processMessage({
553
349
  content: "Hello",
@@ -555,145 +351,10 @@ describe("provider ordering error retry", () => {
555
351
  onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
556
352
  });
557
353
 
558
- // Should have been called exactly 1 time (no retry for non-ordering errors)
354
+ // THEN the loop ran once and the assistant response was persisted
559
355
  expect(agentLoopRunCount).toBe(1);
560
- });
561
-
562
- test("context-too-large triggers one forced-compaction retry for image-heavy input", async () => {
563
- firstRunErrorMode = "context_too_large";
564
- forceCompactionEnabled = true;
565
-
566
- const conversation = makeConversation();
567
- await conversation.loadFromDb();
568
-
569
- const events: Array<Record<string, unknown>> = [];
570
- await conversation.processMessage({
571
- content: "Please compare these images.",
572
- attachments: makeImageAttachments(8),
573
- onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
574
- });
575
-
576
- expect(agentLoopRunCount).toBe(2);
577
- expect(maybeCompactCalls).toEqual([{ force: true }]);
578
- expect(events.some((e) => e.type === "message_complete")).toBe(true);
579
356
  expect(events.some((e) => e.type === "conversation_error")).toBe(false);
580
- });
581
-
582
- test("context-too-large exhausts reducer tiers without compaction — error surfaces after emergency attempt", async () => {
583
- firstRunErrorMode = "context_too_large";
584
- forceCompactionEnabled = false;
585
-
586
- const conversation = makeConversation();
587
- await conversation.loadFromDb();
588
-
589
- const events: Array<Record<string, unknown>> = [];
590
- await conversation.processMessage({
591
- content: "Please compare these images.",
592
- attachments: makeImageAttachments(8),
593
- onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
594
- });
595
-
596
- // The convergence loop enters and applies the reducer (which returns
597
- // exhausted:true). With the early-break removed, the loop still re-runs
598
- // the agent loop once after the reducer. Since compaction didn't help
599
- // (forceCompactionEnabled=false), the second run also fails with
600
- // context_too_large. The overflow policy (fail_gracefully) surfaces error.
601
- expect(agentLoopRunCount).toBe(2);
602
- // One maybeCompact call: the reducer's compactFn (force:true). The
603
- // orchestrator's start-of-turn auto-compact no longer runs — the loop's
604
- // pre-call budget gate owns turn-start compaction and doesn't trip here.
605
- expect(maybeCompactCalls).toEqual([{ force: true }]);
606
-
607
- expect(events.some((e) => e.type === "conversation_error")).toBe(true);
608
- });
609
-
610
- test("context-too-large still surfaces when no media payloads are available to trim", async () => {
611
- firstRunErrorMode = "context_too_large";
612
- forceCompactionEnabled = false;
613
-
614
- const conversation = makeConversation();
615
- await conversation.loadFromDb();
616
-
617
- const events: Array<Record<string, unknown>> = [];
618
- await conversation.processMessage({
619
- content: "No attachments here.",
620
- attachments: [],
621
- onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
622
- });
623
-
624
- // Same as above — convergence loop re-runs agent loop, which also fails.
625
- expect(agentLoopRunCount).toBe(2);
626
- expect(maybeCompactCalls).toEqual([{ force: true }]);
627
- expect(events.some((e) => e.type === "conversation_error")).toBe(true);
628
- });
629
-
630
- test("context-too-large phrase also triggers one forced-compaction retry", async () => {
631
- firstRunErrorMode = "context_too_large_phrase";
632
- forceCompactionEnabled = true;
633
-
634
- const conversation = makeConversation();
635
- await conversation.loadFromDb();
636
-
637
- const events: Array<Record<string, unknown>> = [];
638
- await conversation.processMessage({
639
- content: "Please compare these images.",
640
- attachments: makeImageAttachments(4),
641
- onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
642
- });
643
-
644
- expect(agentLoopRunCount).toBe(2);
645
- expect(maybeCompactCalls).toEqual([{ force: true }]);
646
- expect(events.some((e) => e.type === "message_complete")).toBe(true);
647
- expect(events.some((e) => e.type === "conversation_error")).toBe(false);
648
- });
649
-
650
- test("ProviderError with statusCode 413 triggers forced-compaction retry via classifyConversationError", async () => {
651
- firstRunErrorMode = "context_too_large_413";
652
- forceCompactionEnabled = true;
653
-
654
- const conversation = makeConversation();
655
- await conversation.loadFromDb();
656
-
657
- const events: Array<Record<string, unknown>> = [];
658
- await conversation.processMessage({
659
- content: "Please compare these images.",
660
- attachments: makeImageAttachments(8),
661
- onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
662
- });
663
-
664
- // The 413 ProviderError message "request entity too large" doesn't match
665
- // the regex patterns, but classifyConversationError recognizes statusCode 413
666
- // as CONTEXT_TOO_LARGE and sets contextTooLargeDetected = true.
667
- expect(agentLoopRunCount).toBe(2);
668
- expect(maybeCompactCalls).toEqual([{ force: true }]);
669
- expect(events.some((e) => e.type === "message_complete")).toBe(true);
670
- expect(events.some((e) => e.type === "conversation_error")).toBe(false);
671
- });
672
-
673
- test("context-too-large after progress surfaces error instead of silent failure", async () => {
674
- firstRunErrorMode = "context_too_large_413_with_progress";
675
- forceCompactionEnabled = false;
676
-
677
- const conversation = makeConversation();
678
- await conversation.loadFromDb();
679
-
680
- const events: Array<Record<string, unknown>> = [];
681
- await conversation.processMessage({
682
- content: "Run some tools then hit the limit.",
683
- attachments: [],
684
- onEvent: (msg) => events.push(msg as unknown as Record<string, unknown>),
685
- });
686
-
687
- // Two agent loop runs — first makes progress then 413, convergence loop
688
- // re-runs after reducer (which returns exhausted). Second run also fails
689
- // since compaction didn't help (forceCompactionEnabled=false).
690
- expect(agentLoopRunCount).toBe(2);
691
-
692
- // The error must be surfaced to clients via conversation_error, not silently swallowed.
693
- const conversationError = events.find(
694
- (e) => e.type === "conversation_error",
695
- ) as { code?: string } | undefined;
696
- expect(conversationError).toBeDefined();
697
- expect(conversationError?.code).toBe("CONTEXT_TOO_LARGE");
357
+ const messages = conversation.getMessages();
358
+ expect(messages[messages.length - 1].role).toBe("assistant");
698
359
  });
699
360
  });
@@ -272,6 +272,7 @@ mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
272
272
  async maybeCompact() {
273
273
  return { compacted: false };
274
274
  }
275
+ resetOverflowRecovery() {}
275
276
  },
276
277
  createContextSummaryMessage: () => ({
277
278
  role: "user",
@@ -368,7 +369,6 @@ mock.module("../agent/loop.js", () => ({
368
369
  resolveResult({
369
370
  history,
370
371
  exitReason: pending.exitReason,
371
- appendedNewMessages: history.length > messages.length,
372
372
  newMessages: history.slice(messages.length),
373
373
  }),
374
374
  reject,
@@ -2197,44 +2197,6 @@ describe("Conversation checkpoint handoff", () => {
2197
2197
  // C should have completed successfully
2198
2198
  expect(eventsC.some((e) => e.type === "message_complete")).toBe(true);
2199
2199
  });
2200
-
2201
- test("onCheckpoint callback is passed to both initial and retry runs", async () => {
2202
- const conversation = makeConversation();
2203
- await conversation.loadFromDb();
2204
-
2205
- // Start processing
2206
- const p1 = conversation.processMessage({
2207
- content: "msg-1",
2208
- attachments: [],
2209
- requestId: "req-1",
2210
- });
2211
- await waitForPendingRun(1);
2212
-
2213
- // The first run should have onCheckpoint
2214
- expect(pendingRuns[0].onCheckpoint).toBeDefined();
2215
-
2216
- // Simulate an ordering error: emit error + resolve with same length
2217
- // to trigger the retry path
2218
- const run0 = pendingRuns[0];
2219
- run0.onEvent({
2220
- type: "error",
2221
- error: new Error(
2222
- "tool_result block not immediately after tool_use block",
2223
- ),
2224
- });
2225
- // Resolve with the same messages (no new messages appended = ordering error)
2226
- run0.resolve([...run0.messages]);
2227
-
2228
- // Wait for the retry run
2229
- await waitForPendingRun(2);
2230
-
2231
- // The retry run should also have onCheckpoint
2232
- expect(pendingRuns[1].onCheckpoint).toBeDefined();
2233
-
2234
- // Complete retry cleanly
2235
- await resolveRun(1);
2236
- await p1;
2237
- });
2238
2200
  });
2239
2201
 
2240
2202
  // ---------------------------------------------------------------------------