@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.8.10",
3
+ "version": "0.8.11-staging.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -96,6 +96,7 @@
96
96
  "@resvg/resvg-js-darwin-x64": "2.6.2"
97
97
  },
98
98
  "devDependencies": {
99
+ "@microsoft/api-extractor": "7.58.8",
99
100
  "@types/archiver": "7.0.0",
100
101
  "@types/bun": "1.3.10",
101
102
  "@types/node": "25.5.0",
@@ -0,0 +1,299 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Builds the publishable `@vellumai/plugin-api` npm package: a types-first
4
+ * package whose public `index.d.ts` is a single self-contained rollup of the
5
+ * plugin authoring surface (`src/plugin-api/index.ts`), with the workspace
6
+ * `@vellumai/*` contract types inlined and `zod` as the only external dep.
7
+ *
8
+ * Why a rollup: the authoring surface re-exports types from internal workspace
9
+ * packages that ship `.ts` source (not published to npm), so a naive emit
10
+ * leaves dangling `@vellumai/*` imports that no external consumer can resolve.
11
+ * Microsoft API Extractor inlines exactly the reachable declarations into one
12
+ * file, so a standalone plugin repo type-checks against the real contract
13
+ * without vendoring a hand-maintained shim.
14
+ *
15
+ * Why a trivial runtime: inside a live assistant workspace the host
16
+ * materializes a `node_modules/@vellumai/plugin-api` shim that re-binds the
17
+ * already-loaded namespace off `globalThis` (see `ensure-plugin-api-shim.ts`).
18
+ * The published `index.js` is the same re-bind so the npm package behaves
19
+ * identically when present, and degrades to `undefined` exports when imported
20
+ * outside a host (it is a dev-time authoring contract, not a runtime library).
21
+ *
22
+ * Usage:
23
+ * cd assistant && bun run scripts/build-plugin-api.ts --version 1.2.3
24
+ *
25
+ * Output: a ready-to-pack package directory (default
26
+ * `.plugin-api-build/package`) containing `package.json`, `index.d.ts`, and
27
+ * `index.js`. The release workflow runs `npm pack` / `npm publish` against it.
28
+ */
29
+
30
+ import { execFileSync } from "node:child_process";
31
+ import {
32
+ cpSync,
33
+ existsSync,
34
+ mkdirSync,
35
+ readdirSync,
36
+ readFileSync,
37
+ rmSync,
38
+ symlinkSync,
39
+ writeFileSync,
40
+ } from "node:fs";
41
+ import { join, resolve } from "node:path";
42
+
43
+ import {
44
+ PLUGIN_API_EXPORTS,
45
+ PLUGIN_API_REGISTRY_KEY,
46
+ } from "../src/embedded/plugin-api.js";
47
+
48
+ const ASSISTANT_DIR = resolve(import.meta.dir, "..");
49
+ const NODE_MODULES = join(ASSISTANT_DIR, "node_modules");
50
+ const TSC = join(NODE_MODULES, ".bin", "tsc");
51
+ const API_EXTRACTOR = join(NODE_MODULES, ".bin", "api-extractor");
52
+
53
+ /** The published types reference `z.*`, so the package declares the same `zod`
54
+ * version the assistant resolved — read from the installed package so it can
55
+ * never drift from the contract the rollup was generated against. */
56
+ const ZOD_VERSION = (
57
+ JSON.parse(
58
+ readFileSync(join(NODE_MODULES, "zod", "package.json"), "utf8"),
59
+ ) as { version: string }
60
+ ).version;
61
+
62
+ interface Args {
63
+ version: string;
64
+ buildDir: string;
65
+ }
66
+
67
+ function parseArgs(argv: string[]): Args {
68
+ let version = "";
69
+ let buildDir = join(ASSISTANT_DIR, ".plugin-api-build");
70
+ for (let i = 0; i < argv.length; i++) {
71
+ const arg = argv[i];
72
+ if (arg === "--version") version = argv[++i] ?? "";
73
+ else if (arg === "--out") buildDir = resolve(argv[++i] ?? "");
74
+ }
75
+ if (!version) {
76
+ throw new Error(
77
+ "Missing required --version <semver> (the release version to stamp).",
78
+ );
79
+ }
80
+ return { version, buildDir };
81
+ }
82
+
83
+ function run(bin: string, args: string[], cwd: string): void {
84
+ execFileSync(bin, args, { cwd, stdio: "inherit" });
85
+ }
86
+
87
+ /** Emit `.d.ts` for one workspace contract package into `dest`, resolving its
88
+ * dependencies through `assistant/node_modules` (the layout CI produces after
89
+ * `bun install`). Returns true if a usable `index.d.ts` was produced. */
90
+ function emitContract(pkgDir: string, dest: string): boolean {
91
+ try {
92
+ execFileSync(
93
+ TSC,
94
+ [
95
+ "--emitDeclarationOnly",
96
+ "--declaration",
97
+ "--declarationMap",
98
+ "false",
99
+ "--sourceMap",
100
+ "false",
101
+ "--rootDir",
102
+ "src",
103
+ "--outDir",
104
+ dest,
105
+ ],
106
+ { cwd: pkgDir, stdio: "pipe" },
107
+ );
108
+ } catch {
109
+ // Best-effort: a package that fails to emit standalone is only a problem
110
+ // if the public surface actually reaches it, in which case API Extractor
111
+ // fails loudly below with the offending unresolved import.
112
+ }
113
+ return existsSync(join(dest, "index.d.ts"));
114
+ }
115
+
116
+ interface ContractPackageJson {
117
+ name: string;
118
+ version?: string;
119
+ type?: string;
120
+ exports?: Record<string, unknown>;
121
+ }
122
+
123
+ /** Write a `package.json` for an emitted contract whose `types`/`exports`
124
+ * resolve to the `.d.ts` files (not the unpublished `src/*.ts`). API
125
+ * Extractor resolves a `bundledPackage`'s entry through its real
126
+ * `package.json`, so without this it would follow `exports` back to source
127
+ * and reject the `.ts`. Subpath keys map to same-named `.d.ts` siblings. */
128
+ function writeContractManifest(srcManifestPath: string, destDir: string): void {
129
+ const orig = JSON.parse(
130
+ readFileSync(srcManifestPath, "utf8"),
131
+ ) as ContractPackageJson;
132
+ const origExports = orig.exports ?? { ".": "./src/index.ts" };
133
+ const exports: Record<string, string> = {};
134
+ for (const key of Object.keys(origExports)) {
135
+ exports[key] = key === "." ? "./index.d.ts" : `${key}.d.ts`;
136
+ }
137
+ writeFileSync(
138
+ join(destDir, "package.json"),
139
+ `${JSON.stringify(
140
+ {
141
+ name: orig.name,
142
+ version: orig.version ?? "0.0.0",
143
+ type: orig.type ?? "module",
144
+ types: "./index.d.ts",
145
+ exports,
146
+ },
147
+ null,
148
+ 2,
149
+ )}\n`,
150
+ );
151
+ }
152
+
153
+ function buildRuntimeShim(): string {
154
+ const key = JSON.stringify(PLUGIN_API_REGISTRY_KEY.description ?? "");
155
+ const lines = [
156
+ "// Generated by scripts/build-plugin-api.ts — do not edit by hand.",
157
+ `const api = globalThis[Symbol.for(${key})] ?? {};`,
158
+ ...PLUGIN_API_EXPORTS.map((name) => `export const ${name} = api.${name};`),
159
+ ];
160
+ return `${lines.join("\n")}\n`;
161
+ }
162
+
163
+ function main(): void {
164
+ const { version, buildDir } = parseArgs(Bun.argv.slice(2));
165
+
166
+ const outDir = join(buildDir, "out");
167
+ const pkgDir = join(buildDir, "package");
168
+ const nodeModulesDir = join(buildDir, "node_modules");
169
+ const vellumOverlay = join(nodeModulesDir, "@vellumai");
170
+
171
+ rmSync(buildDir, { recursive: true, force: true });
172
+ mkdirSync(vellumOverlay, { recursive: true });
173
+
174
+ // 1. Emit declarations for the authoring surface and everything it reaches.
175
+ console.log("[plugin-api] emitting authoring-surface declarations…");
176
+ run(TSC, ["-p", "tsconfig.plugin-api.json"], ASSISTANT_DIR);
177
+
178
+ // 2. Emit each `@vellumai/*` contract the surface reaches into an overlay
179
+ // `node_modules`, each with a `package.json` pointing at its `.d.ts`, so
180
+ // API Extractor inlines them (as `bundledPackages`) instead of leaving
181
+ // dangling `@vellumai/*` imports an external consumer can't resolve.
182
+ console.log("[plugin-api] emitting workspace contract declarations…");
183
+ const vellumDir = join(NODE_MODULES, "@vellumai");
184
+ const bundled: string[] = [];
185
+ for (const name of readdirSync(vellumDir)) {
186
+ const dir = join(vellumDir, name);
187
+ if (!existsSync(join(dir, "tsconfig.json"))) continue;
188
+ if (!existsSync(join(dir, "src"))) continue;
189
+ const dest = join(vellumOverlay, name);
190
+ if (emitContract(dir, dest)) {
191
+ writeContractManifest(join(dir, "package.json"), dest);
192
+ bundled.push(`@vellumai/${name}`);
193
+ }
194
+ }
195
+ bundled.sort();
196
+
197
+ // `zod` is the sole external dependency of the rolled-up surface; symlink it
198
+ // so the compiler resolves it while API Extractor leaves it as an import.
199
+ symlinkSync(join(NODE_MODULES, "zod"), join(nodeModulesDir, "zod"), "dir");
200
+
201
+ // 3. Generate the API Extractor compiler tsconfig + config.
202
+ writeFileSync(
203
+ join(buildDir, "tsconfig.dts.json"),
204
+ `${JSON.stringify(
205
+ {
206
+ compilerOptions: {
207
+ target: "ES2022",
208
+ module: "NodeNext",
209
+ moduleResolution: "NodeNext",
210
+ skipLibCheck: true,
211
+ types: [],
212
+ },
213
+ include: ["dts/**/*.d.ts"],
214
+ },
215
+ null,
216
+ 2,
217
+ )}\n`,
218
+ );
219
+
220
+ writeFileSync(
221
+ join(buildDir, "api-extractor.json"),
222
+ `${JSON.stringify(
223
+ {
224
+ $schema:
225
+ "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
226
+ projectFolder: ".",
227
+ mainEntryPointFilePath: "<projectFolder>/dts/plugin-api/index.d.ts",
228
+ bundledPackages: bundled,
229
+ compiler: { tsconfigFilePath: "<projectFolder>/tsconfig.dts.json" },
230
+ dtsRollup: {
231
+ enabled: true,
232
+ untrimmedFilePath: "<projectFolder>/out/index.d.ts",
233
+ },
234
+ apiReport: { enabled: false },
235
+ docModel: { enabled: false },
236
+ tsdocMetadata: { enabled: false },
237
+ messages: {
238
+ compilerMessageReporting: { default: { logLevel: "none" } },
239
+ extractorMessageReporting: { default: { logLevel: "none" } },
240
+ },
241
+ },
242
+ null,
243
+ 2,
244
+ )}\n`,
245
+ );
246
+
247
+ // 4. Roll up the reachable declarations into a single self-contained file.
248
+ console.log("[plugin-api] rolling up declarations with API Extractor…");
249
+ run(API_EXTRACTOR, ["run", "--local", "-c", "api-extractor.json"], buildDir);
250
+
251
+ const rollup = join(outDir, "index.d.ts");
252
+ if (!existsSync(rollup)) {
253
+ throw new Error(`API Extractor did not produce ${rollup}`);
254
+ }
255
+
256
+ // 5. Assemble the publishable package directory.
257
+ mkdirSync(pkgDir, { recursive: true });
258
+ cpSync(rollup, join(pkgDir, "index.d.ts"));
259
+ writeFileSync(join(pkgDir, "index.js"), buildRuntimeShim());
260
+ writeFileSync(
261
+ join(pkgDir, "package.json"),
262
+ `${JSON.stringify(
263
+ {
264
+ name: "@vellumai/plugin-api",
265
+ version,
266
+ description:
267
+ "Public TypeScript authoring contract for Vellum assistant plugins.",
268
+ license: "MIT",
269
+ type: "module",
270
+ main: "./index.js",
271
+ types: "./index.d.ts",
272
+ exports: {
273
+ ".": {
274
+ types: "./index.d.ts",
275
+ import: "./index.js",
276
+ default: "./index.js",
277
+ },
278
+ },
279
+ files: ["index.js", "index.d.ts"],
280
+ dependencies: { zod: ZOD_VERSION },
281
+ publishConfig: { access: "public" },
282
+ repository: {
283
+ type: "git",
284
+ url: "git+https://github.com/vellum-ai/vellum-assistant.git",
285
+ directory: "assistant/src/plugin-api",
286
+ },
287
+ },
288
+ null,
289
+ 2,
290
+ )}\n`,
291
+ );
292
+
293
+ console.log(
294
+ `[plugin-api] built @vellumai/plugin-api@${version} → ${pkgDir}\n` +
295
+ `[plugin-api] inlined contracts: ${bundled.join(", ")}`,
296
+ );
297
+ }
298
+
299
+ main();
@@ -108,6 +108,7 @@ describe("AgentLoop — call-site precedence", () => {
108
108
  });
109
109
 
110
110
  await loop.run({
111
+ requestId: "test-request",
111
112
  messages: [userMessage],
112
113
  onEvent: () => {},
113
114
  trust: { sourceChannel: "vellum", trustClass: "unknown" },
@@ -139,6 +140,7 @@ describe("AgentLoop — call-site precedence", () => {
139
140
  });
140
141
 
141
142
  await loop.run({
143
+ requestId: "test-request",
142
144
  messages: [userMessage],
143
145
  onEvent: () => {},
144
146
  trust: { sourceChannel: "vellum", trustClass: "unknown" },
@@ -173,6 +175,7 @@ describe("AgentLoop — call-site precedence", () => {
173
175
  });
174
176
 
175
177
  await loop.run({
178
+ requestId: "test-request",
176
179
  messages: [userMessage],
177
180
  onEvent: () => {},
178
181
  trust: { sourceChannel: "vellum", trustClass: "unknown" },
@@ -212,6 +215,7 @@ describe("AgentLoop — call-site precedence", () => {
212
215
  });
213
216
 
214
217
  await loop.run({
218
+ requestId: "test-request",
215
219
  messages: [userMessage],
216
220
  onEvent: () => {},
217
221
  trust: { sourceChannel: "vellum", trustClass: "unknown" },
@@ -242,6 +246,7 @@ describe("AgentLoop — call-site precedence", () => {
242
246
  });
243
247
 
244
248
  await loop.run({
249
+ requestId: "test-request",
245
250
  messages: [userMessage],
246
251
  onEvent: () => {},
247
252
  trust: { sourceChannel: "vellum", trustClass: "unknown" },
@@ -280,6 +285,7 @@ describe("AgentLoop — call-site precedence", () => {
280
285
  });
281
286
 
282
287
  await loop.run({
288
+ requestId: "test-request",
283
289
  messages: [userMessage],
284
290
  onEvent: () => {},
285
291
  trust: { sourceChannel: "vellum", trustClass: "unknown" },
@@ -318,6 +324,7 @@ describe("AgentLoop — call-site precedence", () => {
318
324
  });
319
325
 
320
326
  await loop.run({
327
+ requestId: "test-request",
321
328
  messages: [userMessage],
322
329
  onEvent: () => {},
323
330
  trust: { sourceChannel: "vellum", trustClass: "unknown" },
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Tests for the loop's compaction start/end event pair.
3
+ *
4
+ * The compaction pipeline between `context_compacting` (start) and
5
+ * `compaction_completed` (end) is plugin-owned, so consumers reconstruct a
6
+ * compaction attempt purely from the pair — correlated by `compactionId`.
7
+ * These tests pin the pairing contract: shared id, trigger, timestamps, and
8
+ * pre-compaction state on the start event.
9
+ */
10
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
11
+
12
+ import type { PostCompactContext } from "@vellumai/plugin-api";
13
+
14
+ import type { AgentEvent } from "../agent/loop.js";
15
+ import { AgentLoop } from "../agent/loop.js";
16
+ import type { ContextWindowConfig } from "../config/types.js";
17
+ import type { TrustContext } from "../daemon/trust-context.js";
18
+ import { HOOKS } from "../plugin-api/constants.js";
19
+ import {
20
+ createContextWindowManager,
21
+ disposeContextWindowManager,
22
+ getContextWindowManager,
23
+ } from "../plugins/defaults/compaction/manager-store.js";
24
+ import {
25
+ registerPlugin,
26
+ resetPluginRegistryForTests,
27
+ } from "../plugins/registry.js";
28
+ import type {
29
+ Message,
30
+ Provider,
31
+ ProviderResponse,
32
+ SendMessageOptions,
33
+ ToolDefinition,
34
+ } from "../providers/types.js";
35
+
36
+ // Identity post-compact hook: keeps the history the loop handed in, standing
37
+ // in for the daemon-level re-injector these unit tests do not run.
38
+ const testPostCompactPlugin = {
39
+ manifest: { name: "test-post-compact", version: "0.0.0" },
40
+ hooks: {
41
+ [HOOKS.POST_COMPACT]: async (
42
+ _input: PostCompactContext,
43
+ ): Promise<void> => {},
44
+ },
45
+ };
46
+
47
+ function createMockProvider(responses: ProviderResponse[]): {
48
+ provider: Provider;
49
+ } {
50
+ let callIndex = 0;
51
+ const provider: Provider = {
52
+ name: "mock",
53
+ async sendMessage(
54
+ _messages: Message[],
55
+ _options?: SendMessageOptions,
56
+ ): Promise<ProviderResponse> {
57
+ const response = responses[callIndex] ?? responses[responses.length - 1];
58
+ callIndex++;
59
+ return response;
60
+ },
61
+ };
62
+ return { provider };
63
+ }
64
+
65
+ function textResponse(text: string): ProviderResponse {
66
+ return {
67
+ content: [{ type: "text", text }],
68
+ model: "mock-model",
69
+ usage: { inputTokens: 10, outputTokens: 5 },
70
+ stopReason: "end_turn",
71
+ };
72
+ }
73
+
74
+ function toolUseResponse(
75
+ id: string,
76
+ name: string,
77
+ input: Record<string, unknown>,
78
+ ): ProviderResponse {
79
+ return {
80
+ content: [{ type: "tool_use", id, name, input }],
81
+ model: "mock-model",
82
+ usage: { inputTokens: 10, outputTokens: 5 },
83
+ stopReason: "tool_use",
84
+ };
85
+ }
86
+
87
+ const dummyTools: ToolDefinition[] = [
88
+ {
89
+ name: "read_file",
90
+ description: "Read a file",
91
+ input_schema: { type: "object", properties: { path: { type: "string" } } },
92
+ },
93
+ ];
94
+
95
+ const userMessage: Message = {
96
+ role: "user",
97
+ content: [{ type: "text", text: "Hello" }],
98
+ };
99
+
100
+ function fakeCompaction(
101
+ conversationId: string,
102
+ result: { compacted: boolean; exhausted: boolean },
103
+ ): { trust: TrustContext } {
104
+ createContextWindowManager({
105
+ provider: { name: "mock-provider" } as unknown as Provider,
106
+ systemPrompt: "system",
107
+ config: {} as unknown as ContextWindowConfig,
108
+ conversationId,
109
+ });
110
+ const manager = getContextWindowManager(conversationId);
111
+ if (manager) {
112
+ manager.maybeCompact = (async () =>
113
+ result) as unknown as typeof manager.maybeCompact;
114
+ }
115
+ return { trust: { sourceChannel: "vellum", trustClass: "unknown" } };
116
+ }
117
+
118
+ type StartEvent = Extract<AgentEvent, { type: "context_compacting" }>;
119
+ type EndEvent = Extract<AgentEvent, { type: "compaction_completed" }>;
120
+
121
+ describe("AgentLoop compaction start/end event pair", () => {
122
+ beforeEach(() => {
123
+ resetPluginRegistryForTests();
124
+ registerPlugin(testPostCompactPlugin);
125
+ });
126
+
127
+ afterEach(() => {
128
+ disposeContextWindowManager("test-conversation");
129
+ });
130
+
131
+ test("budget-gate compaction emits a correlated start/end pair with full data", async () => {
132
+ const { provider } = createMockProvider([
133
+ toolUseResponse("t1", "read_file", { path: "/a.txt" }),
134
+ textResponse("done after compaction"),
135
+ ]);
136
+ const toolExecutor = async () => ({ content: "ok", isError: false });
137
+ const loop = new AgentLoop({
138
+ provider,
139
+ systemPrompt: "system",
140
+ conversationId: "test-conversation",
141
+ tools: dummyTools,
142
+ toolExecutor,
143
+ });
144
+
145
+ const events: AgentEvent[] = [];
146
+ const before = Date.now();
147
+ await loop.run({
148
+ requestId: "test-request",
149
+ messages: [userMessage],
150
+ onEvent: (event) => {
151
+ events.push(event);
152
+ },
153
+ resolveContextWindow: () => ({
154
+ maxInputTokens: 10,
155
+ overflowRecovery: { enabled: true, safetyMarginRatio: 0 },
156
+ }),
157
+ compactInPlace: true,
158
+ ...fakeCompaction("test-conversation", {
159
+ compacted: true,
160
+ exhausted: false,
161
+ }),
162
+ });
163
+ const after = Date.now();
164
+
165
+ const start = events.find(
166
+ (e): e is StartEvent => e.type === "context_compacting",
167
+ );
168
+ const end = events.find(
169
+ (e): e is EndEvent => e.type === "compaction_completed",
170
+ );
171
+ expect(start).toBeDefined();
172
+ expect(end).toBeDefined();
173
+
174
+ // Pair correlation: shared id, trigger, and start timestamp.
175
+ expect(start!.compactionId).toBe(end!.compactionId);
176
+ expect(start!.compactionId.length).toBeGreaterThan(0);
177
+ expect(start!.requestId).toBe("test-request");
178
+ expect(end!.requestId).toBe("test-request");
179
+ expect(start!.trigger).toBe("budget");
180
+ expect(end!.trigger).toBe("budget");
181
+ expect(end!.startedAt).toBe(start!.startedAt);
182
+
183
+ // Timestamps bound the attempt within the run.
184
+ expect(start!.startedAt).toBeGreaterThanOrEqual(before);
185
+ expect(end!.finishedAt).toBeGreaterThanOrEqual(end!.startedAt);
186
+ expect(end!.finishedAt).toBeLessThanOrEqual(after);
187
+
188
+ // Start carries the pre-compaction history; end carries the result and
189
+ // the stripped history the summary was built from.
190
+ expect(start!.messages.length).toBeGreaterThan(0);
191
+ expect(end!.result.compacted).toBe(true);
192
+ expect(end!.messages.length).toBeGreaterThan(0);
193
+
194
+ // Start precedes end in the event stream.
195
+ expect(events.indexOf(start!)).toBeLessThan(events.indexOf(end!));
196
+ });
197
+ });