@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
@@ -1,18 +1,47 @@
1
- import { describe, expect, test } from "bun:test";
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
3
  import { estimatePromptTokens } from "../context/token-estimator.js";
4
+ import type {
5
+ CompactionContext,
6
+ EmergencyCompactionContext,
7
+ } from "../plugins/defaults/compaction/compact.js";
8
+ import type { Message } from "../providers/types.js";
9
+
10
+ const SYSTEM_PROMPT = "You are a helpful assistant.";
11
+
12
+ // The reducer's compaction rungs call `defaultCompact` /
13
+ // `defaultEmergencyCompact`, which resolve the conversation's
14
+ // ContextWindowManager from the compaction store. Tests drive these impls to
15
+ // exercise rung progression without a live manager.
16
+ let compactImpl: (context: CompactionContext) => Promise<ContextWindowResult>;
17
+ let emergencyImpl: (
18
+ context: EmergencyCompactionContext,
19
+ ) => Promise<ContextWindowResult>;
20
+
21
+ mock.module("../plugins/defaults/compaction/compact.js", () => ({
22
+ defaultCompact: (context: CompactionContext) => compactImpl(context),
23
+ defaultEmergencyCompact: (context: EmergencyCompactionContext) =>
24
+ emergencyImpl(context),
25
+ }));
26
+
4
27
  import {
5
28
  createInitialReducerState,
6
29
  reduceContextOverflow,
7
30
  type ReducerConfig,
8
31
  type ReducerState,
9
32
  } from "../plugins/defaults/compaction/context-overflow-reducer.js";
10
- import type {
11
- ContextWindowCompactOptions,
12
- ContextWindowResult,
33
+ import {
34
+ type ContextWindowResult,
35
+ createContextSummaryMessage,
13
36
  } from "../plugins/defaults/compaction/window-manager.js";
14
- import { createContextSummaryMessage } from "../plugins/defaults/compaction/window-manager.js";
15
- import type { Message } from "../providers/types.js";
37
+
38
+ // The emergency rung (rung 0) runs first on every reduction step. Default it
39
+ // to a no-op so the ladder falls straight through to forced compaction — the
40
+ // rungs most tests exercise. Tests covering the emergency rung override
41
+ // `emergencyImpl` via useEmergencyCompacting().
42
+ beforeEach(() => {
43
+ emergencyImpl = async ({ messages }) => noOpResult(messages);
44
+ });
16
45
 
17
46
  function msg(role: "user" | "assistant", text: string): Message {
18
47
  return { role, content: [{ type: "text", text }] };
@@ -48,8 +77,6 @@ function imageMsg(): Message {
48
77
  };
49
78
  }
50
79
 
51
- const SYSTEM_PROMPT = "You are a helpful assistant.";
52
-
53
80
  function makeConfig(overrides?: Partial<ReducerConfig>): ReducerConfig {
54
81
  return {
55
82
  providerName: "mock",
@@ -69,80 +96,97 @@ function makeConfig(overrides?: Partial<ReducerConfig>): ReducerConfig {
69
96
  },
70
97
  },
71
98
  targetTokens: 1000,
99
+ conversationId: "conv-overflow-test",
100
+ previousEstimatedInputTokens: 2000,
101
+ maxMiddleTierAttempts: 4,
102
+ allowAutoCompressLatestTurn: false,
72
103
  ...overrides,
73
104
  };
74
105
  }
75
106
 
76
- /**
77
- * Create a mock compaction function that replaces messages with a summary.
78
- */
79
- function makeCompactFn(
80
- summaryText = "## Goals\n- compacted summary",
81
- ): (
107
+ /** A compaction result that replaces `messages` with a single summary. */
108
+ function compactedResult(
82
109
  messages: Message[],
83
- signal: AbortSignal | undefined,
84
- options: ContextWindowCompactOptions,
85
- ) => Promise<ContextWindowResult> {
86
- return async (messages, _signal, _options) => {
87
- const summaryMsg = createContextSummaryMessage(summaryText);
88
- const compactedMessages = [summaryMsg];
89
- const estimatedInputTokens = estimatePromptTokens(
110
+ summaryText: string,
111
+ ): ContextWindowResult {
112
+ const compactedMessages = [createContextSummaryMessage(summaryText)];
113
+ return {
114
+ messages: compactedMessages,
115
+ compacted: true,
116
+ previousEstimatedInputTokens: estimatePromptTokens(
117
+ messages,
118
+ SYSTEM_PROMPT,
119
+ {
120
+ providerName: "mock",
121
+ },
122
+ ),
123
+ estimatedInputTokens: estimatePromptTokens(
90
124
  compactedMessages,
91
125
  SYSTEM_PROMPT,
92
- { providerName: "mock" },
93
- );
94
- return {
95
- messages: compactedMessages,
96
- compacted: true,
97
- previousEstimatedInputTokens: estimatePromptTokens(
98
- messages,
99
- SYSTEM_PROMPT,
100
- { providerName: "mock" },
101
- ),
102
- estimatedInputTokens,
103
- maxInputTokens: 2000,
104
- thresholdTokens: 1200,
105
- compactedMessages: messages.length,
106
- compactedPersistedMessages: messages.length,
107
- summaryCalls: 1,
108
- summaryInputTokens: 100,
109
- summaryOutputTokens: 50,
110
- summaryModel: "mock-model",
111
- summaryText,
112
- };
113
- };
126
+ {
127
+ providerName: "mock",
128
+ },
129
+ ),
130
+ maxInputTokens: 2000,
131
+ thresholdTokens: 1200,
132
+ compactedMessages: messages.length,
133
+ compactedPersistedMessages: messages.length,
134
+ summaryCalls: 1,
135
+ summaryInputTokens: 100,
136
+ summaryOutputTokens: 50,
137
+ summaryModel: "mock-model",
138
+ summaryText,
139
+ } satisfies ContextWindowResult;
140
+ }
141
+
142
+ /** A compaction result that leaves `messages` untouched (no progress). */
143
+ function noOpResult(messages: Message[]): ContextWindowResult {
144
+ const estimatedInputTokens = estimatePromptTokens(messages, SYSTEM_PROMPT, {
145
+ providerName: "mock",
146
+ });
147
+ return {
148
+ messages,
149
+ compacted: false,
150
+ previousEstimatedInputTokens: estimatedInputTokens,
151
+ estimatedInputTokens,
152
+ maxInputTokens: 2000,
153
+ thresholdTokens: 1200,
154
+ compactedMessages: 0,
155
+ compactedPersistedMessages: 0,
156
+ summaryCalls: 0,
157
+ summaryInputTokens: 0,
158
+ summaryOutputTokens: 0,
159
+ summaryModel: "",
160
+ summaryText: "",
161
+ reason: "unable to compact",
162
+ } satisfies ContextWindowResult;
114
163
  }
115
164
 
116
165
  /**
117
- * Create a compact function that does not compact (simulates compaction
118
- * being unable to reduce further).
166
+ * Install a compaction stub that replaces messages with a summary.
119
167
  */
120
- function makeNoOpCompactFn(): (
121
- messages: Message[],
122
- signal: AbortSignal | undefined,
123
- options: ContextWindowCompactOptions,
124
- ) => Promise<ContextWindowResult> {
125
- return async (messages, _signal, _options) => {
126
- const estimatedInputTokens = estimatePromptTokens(messages, SYSTEM_PROMPT, {
127
- providerName: "mock",
128
- });
129
- return {
130
- messages,
131
- compacted: false,
132
- previousEstimatedInputTokens: estimatedInputTokens,
133
- estimatedInputTokens,
134
- maxInputTokens: 2000,
135
- thresholdTokens: 1200,
136
- compactedMessages: 0,
137
- compactedPersistedMessages: 0,
138
- summaryCalls: 0,
139
- summaryInputTokens: 0,
140
- summaryOutputTokens: 0,
141
- summaryModel: "",
142
- summaryText: "",
143
- reason: "unable to compact",
144
- };
145
- };
168
+ function useCompacting(summaryText = "## Goals\n- compacted summary"): void {
169
+ compactImpl = async ({ messages }: CompactionContext) =>
170
+ compactedResult(messages, summaryText);
171
+ }
172
+
173
+ /**
174
+ * Install a compaction stub that cannot reduce (simulates compaction being
175
+ * unable to make progress).
176
+ */
177
+ function useNoOpCompacting(): void {
178
+ compactImpl = async ({ messages }: CompactionContext) => noOpResult(messages);
179
+ }
180
+
181
+ /**
182
+ * Install an emergency-compaction stub that replaces messages with a summary,
183
+ * letting the emergency rung (rung 0) make progress on the first step.
184
+ */
185
+ function useEmergencyCompacting(
186
+ summaryText = "## Goals\n- emergency summary",
187
+ ): void {
188
+ emergencyImpl = async ({ messages }: EmergencyCompactionContext) =>
189
+ compactedResult(messages, summaryText);
146
190
  }
147
191
 
148
192
  describe("context-overflow-reducer", () => {
@@ -164,7 +208,7 @@ describe("context-overflow-reducer", () => {
164
208
  ];
165
209
 
166
210
  const config = makeConfig();
167
- const compactFn = makeCompactFn();
211
+ useCompacting();
168
212
 
169
213
  let state: ReducerState | undefined;
170
214
  let prevTokens = Infinity;
@@ -177,7 +221,6 @@ describe("context-overflow-reducer", () => {
177
221
  currentMessages,
178
222
  config,
179
223
  state,
180
- compactFn,
181
224
  );
182
225
 
183
226
  // Each step should either reduce tokens or advance tier state
@@ -210,7 +253,7 @@ describe("context-overflow-reducer", () => {
210
253
  ];
211
254
 
212
255
  const config = makeConfig();
213
- const compactFn = makeCompactFn();
256
+ useCompacting();
214
257
 
215
258
  let currentMessages = messages;
216
259
  let state: ReducerState | undefined;
@@ -221,7 +264,6 @@ describe("context-overflow-reducer", () => {
221
264
  currentMessages,
222
265
  config,
223
266
  state,
224
- compactFn,
225
267
  );
226
268
  tokenHistory.push(result.estimatedTokens);
227
269
  currentMessages = result.messages;
@@ -243,7 +285,7 @@ describe("context-overflow-reducer", () => {
243
285
  ];
244
286
 
245
287
  const config = makeConfig();
246
- const compactFn = makeCompactFn();
288
+ useCompacting();
247
289
 
248
290
  // Exhaust all tiers first
249
291
  let currentMessages = messages;
@@ -253,7 +295,6 @@ describe("context-overflow-reducer", () => {
253
295
  currentMessages,
254
296
  config,
255
297
  state,
256
- compactFn,
257
298
  );
258
299
  currentMessages = result.messages;
259
300
  state = result.state;
@@ -264,7 +305,6 @@ describe("context-overflow-reducer", () => {
264
305
  currentMessages,
265
306
  config,
266
307
  state,
267
- compactFn,
268
308
  );
269
309
 
270
310
  expect(finalResult.state.exhausted).toBe(true);
@@ -283,14 +323,9 @@ describe("context-overflow-reducer", () => {
283
323
  ];
284
324
 
285
325
  const config = makeConfig();
286
- const noOpCompact = makeNoOpCompactFn();
326
+ useNoOpCompacting();
287
327
 
288
- const result = await reduceContextOverflow(
289
- messages,
290
- config,
291
- undefined,
292
- noOpCompact,
293
- );
328
+ const result = await reduceContextOverflow(messages, config, undefined);
294
329
 
295
330
  expect(result.tier).toBe("forced_compaction");
296
331
  expect(result.state.appliedTiers).toContain("forced_compaction");
@@ -311,20 +346,14 @@ describe("context-overflow-reducer", () => {
311
346
  ];
312
347
 
313
348
  const config = makeConfig();
314
- const compactFn = makeNoOpCompactFn();
349
+ useNoOpCompacting();
315
350
 
316
351
  // Skip compaction, apply tool-result truncation
317
- const step1 = await reduceContextOverflow(
318
- messages,
319
- config,
320
- undefined,
321
- compactFn,
322
- );
352
+ const step1 = await reduceContextOverflow(messages, config, undefined);
323
353
  const step2 = await reduceContextOverflow(
324
354
  step1.messages,
325
355
  config,
326
356
  step1.state,
327
- compactFn,
328
357
  );
329
358
 
330
359
  expect(step2.tier).toBe("tool_result_truncation");
@@ -352,7 +381,7 @@ describe("context-overflow-reducer", () => {
352
381
  ];
353
382
 
354
383
  const config = makeConfig();
355
- const compactFn = makeNoOpCompactFn();
384
+ useNoOpCompacting();
356
385
 
357
386
  let currentMessages = messages;
358
387
  let state: ReducerState | undefined;
@@ -361,7 +390,6 @@ describe("context-overflow-reducer", () => {
361
390
  currentMessages,
362
391
  config,
363
392
  state,
364
- compactFn,
365
393
  );
366
394
  currentMessages = result.messages;
367
395
  state = result.state;
@@ -399,7 +427,7 @@ describe("context-overflow-reducer", () => {
399
427
  ];
400
428
 
401
429
  const config = makeConfig();
402
- const compactFn = makeCompactFn();
430
+ useCompacting();
403
431
 
404
432
  // Run 1
405
433
  const run1Tiers: string[] = [];
@@ -407,7 +435,7 @@ describe("context-overflow-reducer", () => {
407
435
  let state1: ReducerState | undefined;
408
436
  let msgs1 = messages;
409
437
  while (!state1?.exhausted) {
410
- const r = await reduceContextOverflow(msgs1, config, state1, compactFn);
438
+ const r = await reduceContextOverflow(msgs1, config, state1);
411
439
  run1Tiers.push(r.tier);
412
440
  run1Tokens.push(r.estimatedTokens);
413
441
  msgs1 = r.messages;
@@ -420,7 +448,7 @@ describe("context-overflow-reducer", () => {
420
448
  let state2: ReducerState | undefined;
421
449
  let msgs2 = messages;
422
450
  while (!state2?.exhausted) {
423
- const r = await reduceContextOverflow(msgs2, config, state2, compactFn);
451
+ const r = await reduceContextOverflow(msgs2, config, state2);
424
452
  run2Tiers.push(r.tier);
425
453
  run2Tokens.push(r.estimatedTokens);
426
454
  msgs2 = r.messages;
@@ -440,7 +468,7 @@ describe("context-overflow-reducer", () => {
440
468
  ];
441
469
 
442
470
  const config = makeConfig();
443
- const compactFn = makeCompactFn();
471
+ useCompacting();
444
472
 
445
473
  let state: ReducerState | undefined;
446
474
  let currentMessages = messages;
@@ -451,7 +479,6 @@ describe("context-overflow-reducer", () => {
451
479
  currentMessages,
452
480
  config,
453
481
  state,
454
- compactFn,
455
482
  );
456
483
  injectionModes.push(result.state.injectionMode);
457
484
  currentMessages = result.messages;
@@ -512,22 +539,16 @@ describe("context-overflow-reducer", () => {
512
539
  const config = makeConfig({
513
540
  targetTokens: 500_000,
514
541
  });
515
- const compactFn = makeNoOpCompactFn();
542
+ useNoOpCompacting();
516
543
 
517
544
  // Run through forced_compaction and tool_result_truncation first
518
- const step1 = await reduceContextOverflow(
519
- messages,
520
- config,
521
- undefined,
522
- compactFn,
523
- );
545
+ const step1 = await reduceContextOverflow(messages, config, undefined);
524
546
  expect(step1.tier).toBe("forced_compaction");
525
547
 
526
548
  const step2 = await reduceContextOverflow(
527
549
  step1.messages,
528
550
  config,
529
551
  step1.state,
530
- compactFn,
531
552
  );
532
553
  expect(step2.tier).toBe("tool_result_truncation");
533
554
 
@@ -536,7 +557,6 @@ describe("context-overflow-reducer", () => {
536
557
  step2.messages,
537
558
  config,
538
559
  step2.state,
539
- compactFn,
540
560
  );
541
561
  expect(step3.tier).toBe("media_stubbing");
542
562
 
@@ -570,14 +590,9 @@ describe("context-overflow-reducer", () => {
570
590
  ];
571
591
 
572
592
  const config = makeConfig();
573
- const compactFn = makeCompactFn();
593
+ useCompacting();
574
594
 
575
- const result = await reduceContextOverflow(
576
- messages,
577
- config,
578
- undefined,
579
- compactFn,
580
- );
595
+ const result = await reduceContextOverflow(messages, config, undefined);
581
596
 
582
597
  expect(result.tier).toBe("forced_compaction");
583
598
  expect(result.compactionResult).toBeDefined();
@@ -594,24 +609,149 @@ describe("context-overflow-reducer", () => {
594
609
  ];
595
610
 
596
611
  const config = makeConfig();
597
- const compactFn = makeCompactFn();
612
+ useCompacting();
598
613
 
599
614
  // Skip past compaction tier
600
- const step1 = await reduceContextOverflow(
601
- messages,
602
- config,
603
- undefined,
604
- compactFn,
605
- );
615
+ const step1 = await reduceContextOverflow(messages, config, undefined);
606
616
  const step2 = await reduceContextOverflow(
607
617
  step1.messages,
608
618
  config,
609
619
  step1.state,
610
- compactFn,
611
620
  );
612
621
 
613
622
  expect(step2.tier).toBe("tool_result_truncation");
614
623
  expect(step2.compactionResult).toBeUndefined();
615
624
  });
616
625
  });
626
+
627
+ describe("emergency compaction rung", () => {
628
+ test("emergency rung runs first and forwards its compaction result", async () => {
629
+ const messages: Message[] = [
630
+ msg("user", "do something"),
631
+ toolUseMsg("tu_1", "bash"),
632
+ toolResultMsg("tu_1", "x".repeat(8000)),
633
+ ];
634
+
635
+ const config = makeConfig();
636
+ useCompacting();
637
+ useEmergencyCompacting();
638
+
639
+ const result = await reduceContextOverflow(messages, config, undefined);
640
+
641
+ expect(result.tier).toBe("emergency_compaction");
642
+ expect(result.state.appliedTiers).toEqual(["emergency_compaction"]);
643
+ expect(result.compactionResult?.compacted).toBe(true);
644
+ expect(result.state.exhausted).toBe(false);
645
+ });
646
+
647
+ test("when emergency cannot reduce, the same step falls through to forced compaction", async () => {
648
+ const messages: Message[] = [
649
+ msg("user", "Hello"),
650
+ msg("assistant", "World"),
651
+ ];
652
+
653
+ const config = makeConfig();
654
+ useCompacting();
655
+
656
+ // Default emergency stub is a no-op, so the step falls through.
657
+ const result = await reduceContextOverflow(messages, config, undefined);
658
+
659
+ expect(result.tier).toBe("forced_compaction");
660
+ expect(result.state.appliedTiers).toEqual([
661
+ "emergency_compaction",
662
+ "forced_compaction",
663
+ ]);
664
+ });
665
+ });
666
+
667
+ describe("terminal auto-compress rung", () => {
668
+ test("auto-compress runs as the final rung when the policy permits it", async () => {
669
+ const messages: Message[] = [
670
+ msg("user", "Hello"),
671
+ msg("assistant", "World"),
672
+ ];
673
+
674
+ const config = makeConfig({ allowAutoCompressLatestTurn: true });
675
+ useCompacting();
676
+
677
+ let state: ReducerState | undefined;
678
+ let currentMessages = messages;
679
+ const tiers: string[] = [];
680
+ while (!state?.exhausted) {
681
+ const result = await reduceContextOverflow(
682
+ currentMessages,
683
+ config,
684
+ state,
685
+ );
686
+ tiers.push(result.tier);
687
+ currentMessages = result.messages;
688
+ state = result.state;
689
+ }
690
+
691
+ expect(tiers[tiers.length - 1]).toBe("auto_compress_latest_turn");
692
+ expect(state!.appliedTiers).toContain("auto_compress_latest_turn");
693
+ });
694
+
695
+ test("auto-compress is skipped when the policy disallows it", async () => {
696
+ const messages: Message[] = [
697
+ msg("user", "Hello"),
698
+ msg("assistant", "World"),
699
+ ];
700
+
701
+ const config = makeConfig({ allowAutoCompressLatestTurn: false });
702
+ useCompacting();
703
+
704
+ let state: ReducerState | undefined;
705
+ let currentMessages = messages;
706
+ const tiers: string[] = [];
707
+ while (!state?.exhausted) {
708
+ const result = await reduceContextOverflow(
709
+ currentMessages,
710
+ config,
711
+ state,
712
+ );
713
+ tiers.push(result.tier);
714
+ currentMessages = result.messages;
715
+ state = result.state;
716
+ }
717
+
718
+ expect(tiers).not.toContain("auto_compress_latest_turn");
719
+ expect(tiers[tiers.length - 1]).toBe("injection_downgrade");
720
+ });
721
+ });
722
+
723
+ describe("middle-tier attempt budget", () => {
724
+ test("the ladder escalates once maxMiddleTierAttempts middle rungs run", async () => {
725
+ const messages: Message[] = [
726
+ msg("user", "Hello"),
727
+ msg("assistant", "World"),
728
+ ];
729
+
730
+ const config = makeConfig({
731
+ maxMiddleTierAttempts: 2,
732
+ allowAutoCompressLatestTurn: false,
733
+ });
734
+ useCompacting();
735
+
736
+ let state: ReducerState | undefined;
737
+ let currentMessages = messages;
738
+ const tiers: string[] = [];
739
+ while (!state?.exhausted) {
740
+ const result = await reduceContextOverflow(
741
+ currentMessages,
742
+ config,
743
+ state,
744
+ );
745
+ tiers.push(result.tier);
746
+ currentMessages = result.messages;
747
+ state = result.state;
748
+ }
749
+
750
+ // Only two middle tiers run before the ladder is exhausted; media
751
+ // stubbing and injection downgrade are never reached.
752
+ expect(tiers).toEqual(["forced_compaction", "tool_result_truncation"]);
753
+ expect(tiers).not.toContain("media_stubbing");
754
+ expect(tiers).not.toContain("injection_downgrade");
755
+ });
756
+ });
617
757
  });