@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
@@ -121,6 +121,24 @@ describe("createResolveToolsCallback — config.tools.exclude", () => {
121
121
  ).toEqual(["bash", "file_read"]);
122
122
  });
123
123
 
124
+ test("memory.enabled=false hides remember but keeps recall available", () => {
125
+ const stub: Partial<AssistantConfig> = {
126
+ memory: { enabled: false } as AssistantConfig["memory"],
127
+ tools: { exclude: [] },
128
+ };
129
+ getConfigSpy = spyOn(configLoader, "getConfig").mockReturnValue(
130
+ stub as AssistantConfig,
131
+ );
132
+ const resolver = createResolveToolsCallback(
133
+ [def("remember"), def("recall"), def("file_read")],
134
+ makeCtx(),
135
+ );
136
+
137
+ const names = resolver!([]).map((d) => d.name);
138
+
139
+ expect(names).toEqual(["recall", "file_read"]);
140
+ });
141
+
124
142
  test("excluded tool stays excluded under disk-pressure cleanup mode", () => {
125
143
  // `bash` is a cleanup-safe tool and would normally survive cleanup mode;
126
144
  // the exclude filter must still suppress it.
@@ -17,6 +17,10 @@ import type {
17
17
  import { getConfig } from "../config/loader.js";
18
18
  import { recordEstimate } from "../context/estimator-calibration.js";
19
19
  import { getCalibrationProviderKey } from "../context/token-estimator.js";
20
+ import {
21
+ recordCompactionEndBestEffort,
22
+ recordCompactionStartBestEffort,
23
+ } from "../memory/compaction-log-writer-clickhouse.js";
20
24
  import { projectAssistantMessage } from "../memory/conversation-attention-store.js";
21
25
  import {
22
26
  deleteMessageById,
@@ -51,7 +55,6 @@ import type {
51
55
  ImageContent,
52
56
  Message,
53
57
  } from "../providers/types.js";
54
- import { isContextOverflowError } from "../providers/types.js";
55
58
  import {
56
59
  getCurrentSeq,
57
60
  recordPersistedSeq,
@@ -81,10 +84,8 @@ import type { AssistantSurface } from "./conversation-agent-loop.js";
81
84
  import {
82
85
  buildConversationErrorMessage,
83
86
  classifyConversationError,
84
- isContextTooLarge,
85
87
  maxTokensReachedClassification,
86
88
  } from "./conversation-error.js";
87
- import { isProviderOrderingError } from "./conversation-slash.js";
88
89
  import { resolveTurnTimezoneContext } from "./date-context.js";
89
90
  import type {
90
91
  CardSurfaceData,
@@ -159,23 +160,6 @@ export interface EventHandlerState {
159
160
  exchangeLlmCallCount: number;
160
161
  readonly exchangeRawResponses: unknown[];
161
162
  model: string;
162
- orderingErrorDetected: boolean;
163
- deferredOrderingError: string | null;
164
- contextTooLargeDetected: boolean;
165
- /**
166
- * Set when the provider rejects with an image-dimension error. The agent
167
- * loop strips or downscales oversized image blocks from ctx.messages and
168
- * retries once before surfacing an error to the user.
169
- */
170
- imageTooLargeDetected: boolean;
171
- /**
172
- * The provider error object when context_too_large is detected, preserved
173
- * so `parseActualTokensFromError` can prefer the typed
174
- * `ContextOverflowError` fields over the string-regex fallback. The
175
- * message is always reachable via `.message` on this object — no separate
176
- * field is needed.
177
- */
178
- contextTooLargeError: unknown;
179
163
  providerErrorUserMessage: string | null;
180
164
  lastAssistantMessageId: string | undefined;
181
165
  /**
@@ -282,6 +266,15 @@ export interface EventHandlerState {
282
266
  * debounce so a mid-turn snapshot reflects what the user is watching live.
283
267
  */
284
268
  currentMessageContent: ContentBlock[];
269
+ /**
270
+ * Per-thinking-block timing for the in-flight LLM call, in stream order:
271
+ * entry `i` is the `i`-th thinking block mirrored into `currentMessageContent`.
272
+ * `startedAt` is stamped when a thinking block opens; `completedAt` is the
273
+ * last reasoning delta fused into it. Used to stamp `_startedAt`/`_completedAt`
274
+ * onto the authoritative thinking blocks at `message_complete`, mirroring how
275
+ * tool calls persist timing. Reset alongside `currentMessageContent`.
276
+ */
277
+ currentThinkingTimestamps: { startedAt: number; completedAt: number }[];
285
278
  /**
286
279
  * `seq` of the most recent streamed content delta mirrored into
287
280
  * `currentMessageContent`. Recorded as the conversation's persisted `seq`
@@ -309,14 +302,14 @@ export interface EventHandlerDeps {
309
302
  /**
310
303
  * Commit a successful inline compaction to durable state. Invoked from the
311
304
  * `compaction_completed` dispatch case (when `result.compacted`) with the
312
- * loop's compaction result and the stripped pre-compaction `basis`. Supplied
305
+ * loop's compaction result and the stripped pre-compaction `messages`. Supplied
313
306
  * by the orchestrator because the body writes Conversation DB-record fields,
314
307
  * projects Slack provenance, and emits transport the loop is intentionally
315
308
  * blind to.
316
309
  */
317
310
  readonly applyCompaction: (
318
311
  result: ContextWindowResult,
319
- basis: Message[],
312
+ messages: Message[],
320
313
  ) => Promise<void>;
321
314
  }
322
315
 
@@ -337,11 +330,6 @@ export function createEventHandlerState(): EventHandlerState {
337
330
  exchangeLlmCallCount: 0,
338
331
  exchangeRawResponses: [],
339
332
  model: "",
340
- orderingErrorDetected: false,
341
- deferredOrderingError: null,
342
- contextTooLargeDetected: false,
343
- imageTooLargeDetected: false,
344
- contextTooLargeError: null,
345
333
  providerErrorUserMessage: null,
346
334
  lastAssistantMessageId: undefined,
347
335
  assistantRowAwaitingFinalization: false,
@@ -371,6 +359,7 @@ export function createEventHandlerState(): EventHandlerState {
371
359
  pendingPartialFlushTimer: undefined,
372
360
  pendingPartialFlushPromise: undefined,
373
361
  currentMessageContent: [],
362
+ currentThinkingTimestamps: [],
374
363
  lastPersistedContentSeq: undefined,
375
364
  };
376
365
  }
@@ -420,6 +409,37 @@ export function buildPersistedAssistantContent(
420
409
  });
421
410
  }
422
411
 
412
+ /**
413
+ * Stamp `_startedAt`/`_completedAt` onto the content's thinking blocks from the
414
+ * per-block timing captured while streaming, matched by position: the `i`-th
415
+ * thinking block in `content` takes `timings[i]`. The `_`-prefixed fields are
416
+ * vellum-internal metadata (never sent to providers), mirroring how tool calls
417
+ * persist timing; `renderHistoryContent` reads them back onto the wire schema's
418
+ * `startedAt`/`completedAt`.
419
+ *
420
+ * Timing is only captured when reasoning is streamed (`streamThinking`), so a
421
+ * turn with thinking disabled — or any block without a matching entry — is left
422
+ * unstamped and the UI hides its duration, exactly as a tool call with no
423
+ * timing does.
424
+ */
425
+ export function stampThinkingTiming(
426
+ content: ContentBlock[],
427
+ timings: ReadonlyArray<{ startedAt: number; completedAt: number }>,
428
+ ): ContentBlock[] {
429
+ if (timings.length === 0) return content;
430
+ let thinkingIdx = 0;
431
+ return content.map((block) => {
432
+ if (block.type !== "thinking") return block;
433
+ const timing = timings[thinkingIdx++];
434
+ if (!timing) return block;
435
+ return {
436
+ ...block,
437
+ _startedAt: timing.startedAt,
438
+ _completedAt: timing.completedAt,
439
+ } as ContentBlock;
440
+ });
441
+ }
442
+
423
443
  /** Append a streamed text chunk to `state.currentMessageContent`, fusing into tail text block. */
424
444
  function appendTextToCurrentMessage(
425
445
  state: EventHandlerState,
@@ -446,15 +466,19 @@ function appendThinkingToCurrentMessage(
446
466
  thinking: string,
447
467
  ): void {
448
468
  if (thinking.length === 0) return;
469
+ const now = Date.now();
449
470
  const tail = state.currentMessageContent.at(-1);
450
471
  if (tail && tail.type === "thinking") {
451
472
  tail.thinking = tail.thinking + thinking;
473
+ const timing = state.currentThinkingTimestamps.at(-1);
474
+ if (timing) timing.completedAt = now;
452
475
  } else {
453
476
  state.currentMessageContent.push({
454
477
  type: "thinking",
455
478
  thinking,
456
479
  signature: "",
457
480
  });
481
+ state.currentThinkingTimestamps.push({ startedAt: now, completedAt: now });
458
482
  }
459
483
  }
460
484
 
@@ -465,6 +489,7 @@ function resetPartialPersistAccumulator(state: EventHandlerState): void {
465
489
  state.pendingPartialFlushTimer = undefined;
466
490
  }
467
491
  state.currentMessageContent = [];
492
+ state.currentThinkingTimestamps = [];
468
493
  state.lastPersistedContentSeq = undefined;
469
494
  state.pendingPartialFlushPromise = undefined;
470
495
  }
@@ -852,9 +877,14 @@ export function handleToolUse(
852
877
  if (event.name === "app_create" || event.name === "app_refresh") {
853
878
  state.appBuildToolUsedThisRun = true;
854
879
  }
855
- state.toolCallTimestamps.set(event.id, { startedAt: Date.now() });
880
+ const startedAt = Date.now();
881
+ state.toolCallTimestamps.set(event.id, { startedAt });
856
882
  state.currentToolUseId = event.id;
857
883
  state.currentTurnToolUseIds.push(event.id);
884
+ // Stamp the start time onto the already-durable tool_use block so a snapshot
885
+ // fetched mid-tool (refresh / reconnect) carries it and clients can render a
886
+ // running timer without having seen the live `tool_use_start` event.
887
+ recordToolStartOnPersistedMessage(state, event.id, startedAt);
858
888
  const statusText = computeToolUseStatusText(event.name, event.input);
859
889
  deps.ctx.emitActivityState("tool_running", "tool_use_start", {
860
890
  requestId: deps.reqId,
@@ -867,6 +897,7 @@ export function handleToolUse(
867
897
  conversationId: deps.ctx.conversationId,
868
898
  toolUseId: event.id,
869
899
  messageId: state.lastAssistantMessageId,
900
+ startedAt,
870
901
  });
871
902
  // `message_complete` always precedes tool events (see handleMessageComplete),
872
903
  // so this tool_use block is already durable in the assistant row. The
@@ -1248,8 +1279,9 @@ export async function handleToolResult(
1248
1279
  });
1249
1280
 
1250
1281
  // Record tool completion timestamp
1282
+ const completedAt = Date.now();
1251
1283
  const ts = state.toolCallTimestamps.get(event.toolUseId);
1252
- if (ts) ts.completedAt = Date.now();
1284
+ if (ts) ts.completedAt = completedAt;
1253
1285
  state.currentToolUseId = undefined;
1254
1286
 
1255
1287
  // Capture risk metadata when present. autoApproved is true when the tool
@@ -1368,6 +1400,7 @@ export async function handleToolResult(
1368
1400
  approvalReason: event.approvalReason,
1369
1401
  riskThreshold: event.riskThreshold,
1370
1402
  activityMetadata: event.activityMetadata,
1403
+ completedAt,
1371
1404
  });
1372
1405
 
1373
1406
  // Capture the seq synchronously (before the persist await) so it reflects the
@@ -1385,6 +1418,44 @@ export async function handleToolResult(
1385
1418
  }
1386
1419
  }
1387
1420
 
1421
+ /**
1422
+ * Stamp `_startedAt` onto the in-flight tool_use block in the persisted
1423
+ * assistant message the moment the tool begins. The block is already durable
1424
+ * (message_complete precedes tool events), so without this a `/messages`
1425
+ * snapshot fetched mid-tool would carry no start time and clients could not
1426
+ * render a running elapsed-time counter until the whole turn finished. The
1427
+ * full timing + risk annotation still happens in
1428
+ * `annotatePersistedAssistantMessage` once every tool in the turn completes.
1429
+ */
1430
+ function recordToolStartOnPersistedMessage(
1431
+ state: EventHandlerState,
1432
+ toolUseId: string,
1433
+ startedAt: number,
1434
+ ): void {
1435
+ const messageId = state.lastAssistantMessageId;
1436
+ if (!messageId) return;
1437
+
1438
+ const row = getMessageById(messageId);
1439
+ if (!row) return;
1440
+
1441
+ let content: ContentBlock[];
1442
+ try {
1443
+ content = JSON.parse(row.content) as ContentBlock[];
1444
+ } catch {
1445
+ return;
1446
+ }
1447
+
1448
+ for (const block of content) {
1449
+ if (block.type !== "tool_use") continue;
1450
+ const rec = block as unknown as Record<string, unknown>;
1451
+ if (rec.id !== toolUseId) continue;
1452
+ if (rec._startedAt === startedAt) return;
1453
+ rec._startedAt = startedAt;
1454
+ updateMessageContent(messageId, JSON.stringify(content));
1455
+ return;
1456
+ }
1457
+ }
1458
+
1388
1459
  /**
1389
1460
  * After all tools for the current turn complete, fetch the persisted assistant
1390
1461
  * message, annotate its tool_use blocks with timing and confirmation metadata,
@@ -1508,61 +1579,32 @@ function handleError(
1508
1579
  deps: EventHandlerDeps,
1509
1580
  event: Extract<AgentEvent, { type: "error" }>,
1510
1581
  ): void {
1511
- if (isProviderOrderingError(event.error.message)) {
1512
- state.orderingErrorDetected = true;
1513
- state.deferredOrderingError = event.error.message;
1514
- } else if (isContextOverflowError(event.error)) {
1515
- // Typed path — the provider client already classified this as overflow.
1516
- state.contextTooLargeDetected = true;
1517
- state.contextTooLargeError = event.error;
1518
- } else if (isContextTooLarge(event.error.message)) {
1519
- state.contextTooLargeDetected = true;
1520
- state.contextTooLargeError = event.error;
1521
- } else {
1522
- const classified = classifyConversationError(event.error, {
1523
- phase: "agent_loop",
1524
- });
1525
- if (classified.code === "CONTEXT_TOO_LARGE") {
1526
- state.contextTooLargeDetected = true;
1527
- state.contextTooLargeError = event.error;
1528
- } else if (classified.code === "IMAGE_TOO_LARGE") {
1529
- // Trigger silent recovery: the agent loop will strip/downscale images
1530
- // in ctx.messages and retry once before surfacing an error.
1531
- state.imageTooLargeDetected = true;
1532
- } else if (
1533
- classified.code === "PROVIDER_ORDERING" ||
1534
- classified.code === "PROVIDER_WEB_SEARCH"
1535
- ) {
1536
- // Ordering errors detected via classifyConversationError (e.g. from ProviderError
1537
- // with statusCode 400 and ordering message) — trigger the retry path.
1538
- state.orderingErrorDetected = true;
1539
- state.deferredOrderingError = event.error.message;
1540
- } else {
1541
- if (classified.errorCategory === "provider_api_error") {
1542
- log.error(
1543
- {
1544
- conversationId: deps.ctx.conversationId,
1545
- errorCode: classified.code,
1546
- errorCategory: classified.errorCategory,
1547
- statusCode:
1548
- event.error instanceof ProviderError
1549
- ? event.error.statusCode
1550
- : undefined,
1551
- provider:
1552
- event.error instanceof ProviderError
1553
- ? event.error.provider
1554
- : undefined,
1555
- errorMessage: event.error.message,
1556
- },
1557
- "Provider rejected request with unclassified 4xx error",
1558
- );
1559
- }
1560
- deps.onEvent(
1561
- buildConversationErrorMessage(deps.ctx.conversationId, classified),
1562
- );
1563
- state.providerErrorUserMessage = classified.userMessage;
1564
- }
1582
+ const classified = classifyConversationError(event.error, {
1583
+ phase: "agent_loop",
1584
+ });
1585
+ if (classified.errorCategory === "provider_api_error") {
1586
+ log.error(
1587
+ {
1588
+ conversationId: deps.ctx.conversationId,
1589
+ errorCode: classified.code,
1590
+ errorCategory: classified.errorCategory,
1591
+ statusCode:
1592
+ event.error instanceof ProviderError
1593
+ ? event.error.statusCode
1594
+ : undefined,
1595
+ provider:
1596
+ event.error instanceof ProviderError
1597
+ ? event.error.provider
1598
+ : undefined,
1599
+ errorMessage: event.error.message,
1600
+ },
1601
+ "Provider rejected request with unclassified 4xx error",
1602
+ );
1565
1603
  }
1604
+ deps.onEvent(
1605
+ buildConversationErrorMessage(deps.ctx.conversationId, classified),
1606
+ );
1607
+ state.providerErrorUserMessage = classified.userMessage;
1566
1608
  }
1567
1609
 
1568
1610
  export function handleMaxTokensReached(
@@ -1730,10 +1772,13 @@ export async function handleMessageComplete(
1730
1772
  // redacted) via the shared helper. The partial-persist flush uses
1731
1773
  // the same helper with `surfaces=[]` so a mid-turn snapshot lands in
1732
1774
  // the same shape as the finalize.
1733
- const contentForPersistence = buildPersistedAssistantContent(
1734
- event.message.content as ContentBlock[],
1735
- deps.ctx.currentTurnSurfaces,
1736
- state.toolActivityMetadata,
1775
+ const contentForPersistence = stampThinkingTiming(
1776
+ buildPersistedAssistantContent(
1777
+ event.message.content as ContentBlock[],
1778
+ deps.ctx.currentTurnSurfaces,
1779
+ state.toolActivityMetadata,
1780
+ ),
1781
+ state.currentThinkingTimestamps,
1737
1782
  );
1738
1783
 
1739
1784
  // The row was reserved at `llm_call_started` (with channel metadata
@@ -1766,6 +1811,7 @@ export async function handleMessageComplete(
1766
1811
  // Reset the partial-persist mirror so subsequent calls in this turn
1767
1812
  // start with an empty running view.
1768
1813
  state.currentMessageContent = [];
1814
+ state.currentThinkingTimestamps = [];
1769
1815
  state.lastPersistedContentSeq = undefined;
1770
1816
 
1771
1817
  // ── Indexing + attention projection ──
@@ -2269,6 +2315,7 @@ export async function dispatchAgentEvent(
2269
2315
  break;
2270
2316
  }
2271
2317
  case "context_compacting":
2318
+ recordCompactionStartBestEffort(deps.ctx.conversationId, event);
2272
2319
  deps.ctx.emitActivityState("thinking", "context_compacting", {
2273
2320
  requestId: deps.reqId,
2274
2321
  statusText: "Compacting context",
@@ -2283,7 +2330,7 @@ export async function dispatchAgentEvent(
2283
2330
  deps.onEvent(event);
2284
2331
  break;
2285
2332
  case "compaction_completed":
2286
- // Always commit the loop-stripped `basis` as the durable message base
2333
+ // Always commit the loop-stripped `messages` as the durable message base
2287
2334
  // so re-injection re-applies onto the stripped history even when the
2288
2335
  // pipeline ran but did not compact. When it did compact, commit the
2289
2336
  // durable result (DB-record fields, Slack provenance, SSE) — which
@@ -2292,9 +2339,10 @@ export async function dispatchAgentEvent(
2292
2339
  // so the committed history is in place in time. A failed durable
2293
2340
  // commit re-throws below to abort the turn rather than re-injecting
2294
2341
  // against half-applied state.
2295
- deps.ctx.messages = event.basis;
2342
+ recordCompactionEndBestEffort(deps.ctx.conversationId, event);
2343
+ deps.ctx.messages = event.messages;
2296
2344
  if (event.result.compacted) {
2297
- await deps.applyCompaction(event.result, event.basis);
2345
+ await deps.applyCompaction(event.result, event.messages);
2298
2346
  }
2299
2347
  break;
2300
2348
  case "history_stripped":
@@ -2354,7 +2402,8 @@ export async function dispatchAgentEvent(
2354
2402
  );
2355
2403
  // Re-throw errors from critical handlers that must not be silently swallowed:
2356
2404
  // - message_complete: persists assistant message to DB, sets state flags
2357
- // - error: sets recovery flags (contextTooLargeDetected, orderingErrorDetected)
2405
+ // - error: triggers image recovery or surfaces the user-facing error
2406
+ // message
2358
2407
  // - usage: records token accounting
2359
2408
  // - compaction_completed: durable compaction commit; aborting the turn is
2360
2409
  // safer than re-injecting against a half-applied compaction