@prestyj/cli 5.10.0 → 5.12.0

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 (703) hide show
  1. package/README.md +3 -4
  2. package/assets/skills/compliance-guard/SKILL.md +251 -0
  3. package/assets/skills/compliance-guard/references/artifacts.md +186 -0
  4. package/assets/skills/compliance-guard/references/eu-uk.md +151 -0
  5. package/assets/skills/compliance-guard/references/exposure-triage.md +161 -0
  6. package/assets/skills/compliance-guard/references/lawsuit-vectors.md +141 -0
  7. package/assets/skills/compliance-guard/references/provenance.md +42 -0
  8. package/assets/skills/compliance-guard/references/sector-gates.md +173 -0
  9. package/assets/skills/compliance-guard/references/security-baseline.md +130 -0
  10. package/assets/skills/compliance-guard/references/trigger-map.md +163 -0
  11. package/assets/skills/compliance-guard/references/us.md +164 -0
  12. package/dist/app-sidecar-auth-broadcast.test.d.ts +2 -0
  13. package/dist/app-sidecar-auth-broadcast.test.d.ts.map +1 -0
  14. package/dist/app-sidecar-auth-broadcast.test.js +348 -0
  15. package/dist/app-sidecar-auth-broadcast.test.js.map +1 -0
  16. package/dist/app-sidecar-sessions.d.ts +5 -2
  17. package/dist/app-sidecar-sessions.d.ts.map +1 -1
  18. package/dist/app-sidecar-sessions.js +34 -8
  19. package/dist/app-sidecar-sessions.js.map +1 -1
  20. package/dist/app-sidecar-sessions.test.js +96 -1
  21. package/dist/app-sidecar-sessions.test.js.map +1 -1
  22. package/dist/app-sidecar.js +616 -173
  23. package/dist/app-sidecar.js.map +1 -1
  24. package/dist/chat-agents/jiwa.test.js +1 -1
  25. package/dist/cli/auth.d.ts.map +1 -1
  26. package/dist/cli/auth.js +33 -36
  27. package/dist/cli/auth.js.map +1 -1
  28. package/dist/cli/command-routing.d.ts +1 -1
  29. package/dist/cli/command-routing.d.ts.map +1 -1
  30. package/dist/cli/command-routing.js +1 -0
  31. package/dist/cli/command-routing.js.map +1 -1
  32. package/dist/cli/command-routing.test.js +1 -0
  33. package/dist/cli/command-routing.test.js.map +1 -1
  34. package/dist/cli.d.ts.map +1 -1
  35. package/dist/cli.js +179 -48
  36. package/dist/cli.js.map +1 -1
  37. package/dist/config.d.ts +18 -5
  38. package/dist/config.d.ts.map +1 -1
  39. package/dist/config.js +45 -76
  40. package/dist/config.js.map +1 -1
  41. package/dist/config.test.js +30 -13
  42. package/dist/config.test.js.map +1 -1
  43. package/dist/core/agent-notifications.d.ts +62 -0
  44. package/dist/core/agent-notifications.d.ts.map +1 -0
  45. package/dist/core/agent-notifications.js +92 -0
  46. package/dist/core/agent-notifications.js.map +1 -0
  47. package/dist/core/agent-notifications.test.d.ts +2 -0
  48. package/dist/core/agent-notifications.test.d.ts.map +1 -0
  49. package/dist/core/agent-notifications.test.js +97 -0
  50. package/dist/core/agent-notifications.test.js.map +1 -0
  51. package/dist/core/agent-session-checkpoint-flush.test.d.ts +2 -0
  52. package/dist/core/agent-session-checkpoint-flush.test.d.ts.map +1 -0
  53. package/dist/core/agent-session-checkpoint-flush.test.js +201 -0
  54. package/dist/core/agent-session-checkpoint-flush.test.js.map +1 -0
  55. package/dist/core/agent-session-compaction.test.js +323 -6
  56. package/dist/core/agent-session-compaction.test.js.map +1 -1
  57. package/dist/core/agent-session-crash-durability.test.d.ts +2 -0
  58. package/dist/core/agent-session-crash-durability.test.d.ts.map +1 -0
  59. package/dist/core/agent-session-crash-durability.test.js +102 -0
  60. package/dist/core/agent-session-crash-durability.test.js.map +1 -0
  61. package/dist/core/agent-session-marker-anchors.test.js +181 -2
  62. package/dist/core/agent-session-marker-anchors.test.js.map +1 -1
  63. package/dist/core/agent-session-model-switch.test.d.ts +2 -0
  64. package/dist/core/agent-session-model-switch.test.d.ts.map +1 -0
  65. package/dist/core/agent-session-model-switch.test.js +161 -0
  66. package/dist/core/agent-session-model-switch.test.js.map +1 -0
  67. package/dist/core/agent-session-notifications.test.d.ts +2 -0
  68. package/dist/core/agent-session-notifications.test.d.ts.map +1 -0
  69. package/dist/core/agent-session-notifications.test.js +114 -0
  70. package/dist/core/agent-session-notifications.test.js.map +1 -0
  71. package/dist/core/agent-session-process-gate.test.d.ts +2 -0
  72. package/dist/core/agent-session-process-gate.test.d.ts.map +1 -0
  73. package/dist/core/agent-session-process-gate.test.js +98 -0
  74. package/dist/core/agent-session-process-gate.test.js.map +1 -0
  75. package/dist/core/agent-session-queue.test.js +65 -0
  76. package/dist/core/agent-session-queue.test.js.map +1 -1
  77. package/dist/core/agent-session-review-coverage.test.js +80 -23
  78. package/dist/core/agent-session-review-coverage.test.js.map +1 -1
  79. package/dist/core/agent-session-slash-restore.test.d.ts +2 -0
  80. package/dist/core/agent-session-slash-restore.test.d.ts.map +1 -0
  81. package/dist/core/agent-session-slash-restore.test.js +121 -0
  82. package/dist/core/agent-session-slash-restore.test.js.map +1 -0
  83. package/dist/core/agent-session-tool-tiers.test.d.ts +2 -0
  84. package/dist/core/agent-session-tool-tiers.test.d.ts.map +1 -0
  85. package/dist/core/agent-session-tool-tiers.test.js +138 -0
  86. package/dist/core/agent-session-tool-tiers.test.js.map +1 -0
  87. package/dist/core/agent-session.d.ts +257 -13
  88. package/dist/core/agent-session.d.ts.map +1 -1
  89. package/dist/core/agent-session.js +1118 -241
  90. package/dist/core/agent-session.js.map +1 -1
  91. package/dist/core/agents.d.ts +29 -1
  92. package/dist/core/agents.d.ts.map +1 -1
  93. package/dist/core/agents.js +47 -97
  94. package/dist/core/agents.js.map +1 -1
  95. package/dist/core/agents.test.js +87 -7
  96. package/dist/core/agents.test.js.map +1 -1
  97. package/dist/core/api-benchmark.d.ts +3 -3
  98. package/dist/core/api-benchmark.js +7 -7
  99. package/dist/core/auth-providers.d.ts +37 -1
  100. package/dist/core/auth-providers.d.ts.map +1 -1
  101. package/dist/core/auth-providers.js +83 -3
  102. package/dist/core/auth-providers.js.map +1 -1
  103. package/dist/core/auto-update.test.js +1 -1
  104. package/dist/core/autopilot-gate.d.ts +2 -2
  105. package/dist/core/autopilot-gate.js +1 -1
  106. package/dist/core/bash-spawn-benchmark.js +6 -6
  107. package/dist/core/bundled-agents.d.ts +3 -0
  108. package/dist/core/bundled-agents.d.ts.map +1 -0
  109. package/dist/core/bundled-agents.js +252 -0
  110. package/dist/core/bundled-agents.js.map +1 -0
  111. package/dist/core/cache-warm-benchmark.d.ts +2 -2
  112. package/dist/core/cache-warm-benchmark.js +5 -5
  113. package/dist/core/code-retrieval-chunkers.d.ts +27 -0
  114. package/dist/core/code-retrieval-chunkers.d.ts.map +1 -0
  115. package/dist/core/code-retrieval-chunkers.js +264 -0
  116. package/dist/core/code-retrieval-chunkers.js.map +1 -0
  117. package/dist/core/code-retrieval-chunkers.test.d.ts +2 -0
  118. package/dist/core/code-retrieval-chunkers.test.d.ts.map +1 -0
  119. package/dist/core/code-retrieval-chunkers.test.js +203 -0
  120. package/dist/core/code-retrieval-chunkers.test.js.map +1 -0
  121. package/dist/core/code-retrieval.d.ts +6 -0
  122. package/dist/core/code-retrieval.d.ts.map +1 -1
  123. package/dist/core/code-retrieval.js +38 -1
  124. package/dist/core/code-retrieval.js.map +1 -1
  125. package/dist/core/compaction/active-context.test.js +21 -0
  126. package/dist/core/compaction/active-context.test.js.map +1 -1
  127. package/dist/core/compaction/compactor.d.ts +108 -8
  128. package/dist/core/compaction/compactor.d.ts.map +1 -1
  129. package/dist/core/compaction/compactor.js +423 -119
  130. package/dist/core/compaction/compactor.js.map +1 -1
  131. package/dist/core/compaction/compactor.test.js +360 -8
  132. package/dist/core/compaction/compactor.test.js.map +1 -1
  133. package/dist/core/compaction/policy.d.ts +17 -0
  134. package/dist/core/compaction/policy.d.ts.map +1 -0
  135. package/dist/core/compaction/policy.js +22 -0
  136. package/dist/core/compaction/policy.js.map +1 -0
  137. package/dist/core/compaction/policy.test.d.ts +2 -0
  138. package/dist/core/compaction/policy.test.d.ts.map +1 -0
  139. package/dist/core/compaction/policy.test.js +35 -0
  140. package/dist/core/compaction/policy.test.js.map +1 -0
  141. package/dist/core/compaction/query-aware-selector.d.ts +33 -0
  142. package/dist/core/compaction/query-aware-selector.d.ts.map +1 -0
  143. package/dist/core/compaction/query-aware-selector.js +202 -0
  144. package/dist/core/compaction/query-aware-selector.js.map +1 -0
  145. package/dist/core/compaction/query-aware-selector.test.d.ts +2 -0
  146. package/dist/core/compaction/query-aware-selector.test.d.ts.map +1 -0
  147. package/dist/core/compaction/query-aware-selector.test.js +96 -0
  148. package/dist/core/compaction/query-aware-selector.test.js.map +1 -0
  149. package/dist/core/compaction/tool-result-pruner.d.ts +24 -11
  150. package/dist/core/compaction/tool-result-pruner.d.ts.map +1 -1
  151. package/dist/core/compaction/tool-result-pruner.js +98 -26
  152. package/dist/core/compaction/tool-result-pruner.js.map +1 -1
  153. package/dist/core/compaction/tool-result-pruner.test.js +178 -6
  154. package/dist/core/compaction/tool-result-pruner.test.js.map +1 -1
  155. package/dist/core/encode-cwd.test.js +1 -1
  156. package/dist/core/event-bus.d.ts +23 -0
  157. package/dist/core/event-bus.d.ts.map +1 -1
  158. package/dist/core/event-bus.js +10 -0
  159. package/dist/core/event-bus.js.map +1 -1
  160. package/dist/core/extensions/loader.d.ts.map +1 -1
  161. package/dist/core/extensions/loader.js +28 -8
  162. package/dist/core/extensions/loader.js.map +1 -1
  163. package/dist/core/extensions/plugin-bundles.d.ts +23 -0
  164. package/dist/core/extensions/plugin-bundles.d.ts.map +1 -0
  165. package/dist/core/extensions/plugin-bundles.js +228 -0
  166. package/dist/core/extensions/plugin-bundles.js.map +1 -0
  167. package/dist/core/extensions/plugin-bundles.test.d.ts +2 -0
  168. package/dist/core/extensions/plugin-bundles.test.d.ts.map +1 -0
  169. package/dist/core/extensions/plugin-bundles.test.js +110 -0
  170. package/dist/core/extensions/plugin-bundles.test.js.map +1 -0
  171. package/dist/core/fast-apply-benchmark.d.ts +2 -2
  172. package/dist/core/fast-apply-benchmark.js +6 -6
  173. package/dist/core/foreign-session-import.d.ts +120 -0
  174. package/dist/core/foreign-session-import.d.ts.map +1 -0
  175. package/dist/core/foreign-session-import.js +598 -0
  176. package/dist/core/foreign-session-import.js.map +1 -0
  177. package/dist/core/foreign-session-import.test.d.ts +2 -0
  178. package/dist/core/foreign-session-import.test.d.ts.map +1 -0
  179. package/dist/core/foreign-session-import.test.js +220 -0
  180. package/dist/core/foreign-session-import.test.js.map +1 -0
  181. package/dist/core/foreign-transcript-blocks.d.ts +51 -0
  182. package/dist/core/foreign-transcript-blocks.d.ts.map +1 -0
  183. package/dist/core/foreign-transcript-blocks.js +151 -0
  184. package/dist/core/foreign-transcript-blocks.js.map +1 -0
  185. package/dist/core/goal-lifecycle-smoke.test.js +2 -2
  186. package/dist/core/goal-store.js +1 -1
  187. package/dist/core/goal-store.test.js +2 -2
  188. package/dist/core/goal-verifier.test.js +2 -2
  189. package/dist/core/goal-worker-dev-server-lifecycle.test.js +1 -1
  190. package/dist/core/goal-worker.test.js +2 -2
  191. package/dist/core/hashline-edit-benchmark.d.ts +2 -2
  192. package/dist/core/hashline-edit-benchmark.js +7 -7
  193. package/dist/core/ideal-review.d.ts +20 -1
  194. package/dist/core/ideal-review.d.ts.map +1 -1
  195. package/dist/core/ideal-review.js +37 -2
  196. package/dist/core/ideal-review.js.map +1 -1
  197. package/dist/core/ideal-review.test.js +99 -3
  198. package/dist/core/ideal-review.test.js.map +1 -1
  199. package/dist/core/local-endpoint-store.test.js +1 -1
  200. package/dist/core/logger.d.ts +1 -1
  201. package/dist/core/logger.js +2 -2
  202. package/dist/core/logger.js.map +1 -1
  203. package/dist/core/loop-breaker.d.ts.map +1 -1
  204. package/dist/core/loop-breaker.js +1 -0
  205. package/dist/core/loop-breaker.js.map +1 -1
  206. package/dist/core/lsp/client-navigation.test.d.ts +2 -0
  207. package/dist/core/lsp/client-navigation.test.d.ts.map +1 -0
  208. package/dist/core/lsp/client-navigation.test.js +135 -0
  209. package/dist/core/lsp/client-navigation.test.js.map +1 -0
  210. package/dist/core/lsp/client.d.ts +84 -0
  211. package/dist/core/lsp/client.d.ts.map +1 -1
  212. package/dist/core/lsp/client.js +190 -3
  213. package/dist/core/lsp/client.js.map +1 -1
  214. package/dist/core/lsp/integration.test.js +2 -2
  215. package/dist/core/lsp/manager-navigation.test.d.ts +2 -0
  216. package/dist/core/lsp/manager-navigation.test.d.ts.map +1 -0
  217. package/dist/core/lsp/manager-navigation.test.js +121 -0
  218. package/dist/core/lsp/manager-navigation.test.js.map +1 -0
  219. package/dist/core/lsp/manager.d.ts +81 -9
  220. package/dist/core/lsp/manager.d.ts.map +1 -1
  221. package/dist/core/lsp/manager.js +183 -64
  222. package/dist/core/lsp/manager.js.map +1 -1
  223. package/dist/core/lsp/manager.test.js +18 -0
  224. package/dist/core/lsp/manager.test.js.map +1 -1
  225. package/dist/core/lsp/pool.d.ts +113 -0
  226. package/dist/core/lsp/pool.d.ts.map +1 -0
  227. package/dist/core/lsp/pool.js +255 -0
  228. package/dist/core/lsp/pool.js.map +1 -0
  229. package/dist/core/lsp/pool.test.d.ts +2 -0
  230. package/dist/core/lsp/pool.test.d.ts.map +1 -0
  231. package/dist/core/lsp/pool.test.js +286 -0
  232. package/dist/core/lsp/pool.test.js.map +1 -0
  233. package/dist/core/lsp/servers.d.ts.map +1 -1
  234. package/dist/core/lsp/servers.js +43 -3
  235. package/dist/core/lsp/servers.js.map +1 -1
  236. package/dist/core/lsp/servers.test.d.ts +2 -0
  237. package/dist/core/lsp/servers.test.d.ts.map +1 -0
  238. package/dist/core/lsp/servers.test.js +67 -0
  239. package/dist/core/lsp/servers.test.js.map +1 -0
  240. package/dist/core/lsp/windows.test.js +6 -18
  241. package/dist/core/lsp/windows.test.js.map +1 -1
  242. package/dist/core/mcp/catalog-cache.d.ts +63 -0
  243. package/dist/core/mcp/catalog-cache.d.ts.map +1 -0
  244. package/dist/core/mcp/catalog-cache.js +163 -0
  245. package/dist/core/mcp/catalog-cache.js.map +1 -0
  246. package/dist/core/mcp/catalog-cache.test.d.ts +2 -0
  247. package/dist/core/mcp/catalog-cache.test.d.ts.map +1 -0
  248. package/dist/core/mcp/catalog-cache.test.js +99 -0
  249. package/dist/core/mcp/catalog-cache.test.js.map +1 -0
  250. package/dist/core/mcp/client-session-recovery.test.d.ts +2 -0
  251. package/dist/core/mcp/client-session-recovery.test.d.ts.map +1 -0
  252. package/dist/core/mcp/client-session-recovery.test.js +231 -0
  253. package/dist/core/mcp/client-session-recovery.test.js.map +1 -0
  254. package/dist/core/mcp/client.d.ts +158 -0
  255. package/dist/core/mcp/client.d.ts.map +1 -1
  256. package/dist/core/mcp/client.js +478 -45
  257. package/dist/core/mcp/client.js.map +1 -1
  258. package/dist/core/mcp/content.d.ts +19 -0
  259. package/dist/core/mcp/content.d.ts.map +1 -0
  260. package/dist/core/mcp/content.js +172 -0
  261. package/dist/core/mcp/content.js.map +1 -0
  262. package/dist/core/mcp/content.test.d.ts +2 -0
  263. package/dist/core/mcp/content.test.d.ts.map +1 -0
  264. package/dist/core/mcp/content.test.js +135 -0
  265. package/dist/core/mcp/content.test.js.map +1 -0
  266. package/dist/core/mcp/deferred-catalog.d.ts +1 -1
  267. package/dist/core/mcp/deferred-catalog.js +1 -1
  268. package/dist/core/mcp/elicitation-bridge.d.ts +41 -0
  269. package/dist/core/mcp/elicitation-bridge.d.ts.map +1 -0
  270. package/dist/core/mcp/elicitation-bridge.js +52 -0
  271. package/dist/core/mcp/elicitation-bridge.js.map +1 -0
  272. package/dist/core/mcp/elicitation.test.d.ts +2 -0
  273. package/dist/core/mcp/elicitation.test.d.ts.map +1 -0
  274. package/dist/core/mcp/elicitation.test.js +158 -0
  275. package/dist/core/mcp/elicitation.test.js.map +1 -0
  276. package/dist/core/mcp/index.d.ts +5 -1
  277. package/dist/core/mcp/index.d.ts.map +1 -1
  278. package/dist/core/mcp/index.js +2 -0
  279. package/dist/core/mcp/index.js.map +1 -1
  280. package/dist/core/mcp/legacy-negotiation.test.d.ts +2 -0
  281. package/dist/core/mcp/legacy-negotiation.test.d.ts.map +1 -0
  282. package/dist/core/mcp/legacy-negotiation.test.js +80 -0
  283. package/dist/core/mcp/legacy-negotiation.test.js.map +1 -0
  284. package/dist/core/mcp/oauth-provider.d.ts +1 -2
  285. package/dist/core/mcp/oauth-provider.d.ts.map +1 -1
  286. package/dist/core/mcp/oauth-provider.js +6 -2
  287. package/dist/core/mcp/oauth-provider.js.map +1 -1
  288. package/dist/core/mcp/oauth-store.d.ts +1 -1
  289. package/dist/core/mcp/oauth-store.d.ts.map +1 -1
  290. package/dist/core/mcp/oauth-store.js.map +1 -1
  291. package/dist/core/mcp/oauth-store.test.js +2 -2
  292. package/dist/core/mcp/resolve-stdio.test.js +5 -5
  293. package/dist/core/mcp/session-elicitation-wiring.test.d.ts +2 -0
  294. package/dist/core/mcp/session-elicitation-wiring.test.d.ts.map +1 -0
  295. package/dist/core/mcp/session-elicitation-wiring.test.js +138 -0
  296. package/dist/core/mcp/session-elicitation-wiring.test.js.map +1 -0
  297. package/dist/core/mcp/shared-pool.d.ts +158 -0
  298. package/dist/core/mcp/shared-pool.d.ts.map +1 -0
  299. package/dist/core/mcp/shared-pool.js +235 -0
  300. package/dist/core/mcp/shared-pool.js.map +1 -0
  301. package/dist/core/mcp/shared-pool.test.d.ts +2 -0
  302. package/dist/core/mcp/shared-pool.test.d.ts.map +1 -0
  303. package/dist/core/mcp/shared-pool.test.js +425 -0
  304. package/dist/core/mcp/shared-pool.test.js.map +1 -0
  305. package/dist/core/mcp/store.test.js +5 -5
  306. package/dist/core/mcp/types.d.ts +12 -0
  307. package/dist/core/mcp/types.d.ts.map +1 -1
  308. package/dist/core/mcp/when-connected.test.d.ts +2 -0
  309. package/dist/core/mcp/when-connected.test.d.ts.map +1 -0
  310. package/dist/core/mcp/when-connected.test.js +46 -0
  311. package/dist/core/mcp/when-connected.test.js.map +1 -0
  312. package/dist/core/nolan-context.d.ts.map +1 -1
  313. package/dist/core/nolan-context.js +8 -0
  314. package/dist/core/nolan-context.js.map +1 -1
  315. package/dist/core/nolan-context.test.js +40 -0
  316. package/dist/core/nolan-context.test.js.map +1 -1
  317. package/dist/core/nolan-model.test.js +1 -1
  318. package/dist/core/nolan-prompt.js +8 -5
  319. package/dist/core/nolan-prompt.js.map +1 -1
  320. package/dist/core/nolan-prompt.test.js +5 -0
  321. package/dist/core/nolan-prompt.test.js.map +1 -1
  322. package/dist/core/oauth/xai.d.ts +2 -0
  323. package/dist/core/oauth/xai.d.ts.map +1 -0
  324. package/dist/core/oauth/xai.js +4 -0
  325. package/dist/core/oauth/xai.js.map +1 -0
  326. package/dist/core/persistent-shell.d.ts +3 -2
  327. package/dist/core/persistent-shell.d.ts.map +1 -1
  328. package/dist/core/persistent-shell.js +11 -4
  329. package/dist/core/persistent-shell.js.map +1 -1
  330. package/dist/core/persistent-shell.test.js +6 -6
  331. package/dist/core/process-gate.d.ts +45 -0
  332. package/dist/core/process-gate.d.ts.map +1 -0
  333. package/dist/core/process-gate.js +79 -0
  334. package/dist/core/process-gate.js.map +1 -0
  335. package/dist/core/process-gate.test.d.ts +2 -0
  336. package/dist/core/process-gate.test.d.ts.map +1 -0
  337. package/dist/core/process-gate.test.js +87 -0
  338. package/dist/core/process-gate.test.js.map +1 -0
  339. package/dist/core/process-manager-dev-server-repro.test.js +51 -12
  340. package/dist/core/process-manager-dev-server-repro.test.js.map +1 -1
  341. package/dist/core/process-manager-notifications.test.d.ts +2 -0
  342. package/dist/core/process-manager-notifications.test.d.ts.map +1 -0
  343. package/dist/core/process-manager-notifications.test.js +227 -0
  344. package/dist/core/process-manager-notifications.test.js.map +1 -0
  345. package/dist/core/process-manager.d.ts +72 -1
  346. package/dist/core/process-manager.d.ts.map +1 -1
  347. package/dist/core/process-manager.js +247 -4
  348. package/dist/core/process-manager.js.map +1 -1
  349. package/dist/core/progress/git-xp.test.js +2 -2
  350. package/dist/core/progress/ranks.d.ts +14 -3
  351. package/dist/core/progress/ranks.d.ts.map +1 -1
  352. package/dist/core/progress/ranks.js +181 -12
  353. package/dist/core/progress/ranks.js.map +1 -1
  354. package/dist/core/progress/ranks.test.js +48 -7
  355. package/dist/core/progress/ranks.test.js.map +1 -1
  356. package/dist/core/progress/rebuild.test.js +1 -1
  357. package/dist/core/progress/store.test.js +1 -1
  358. package/dist/core/progress/types.d.ts +2 -1
  359. package/dist/core/progress/types.d.ts.map +1 -1
  360. package/dist/core/project-discovery.d.ts +31 -6
  361. package/dist/core/project-discovery.d.ts.map +1 -1
  362. package/dist/core/project-discovery.js +349 -79
  363. package/dist/core/project-discovery.js.map +1 -1
  364. package/dist/core/project-discovery.test.js +128 -4
  365. package/dist/core/project-discovery.test.js.map +1 -1
  366. package/dist/core/prompt-commands.js +3 -3
  367. package/dist/core/radio.d.ts +1 -1
  368. package/dist/core/radio.d.ts.map +1 -1
  369. package/dist/core/radio.js +90 -6
  370. package/dist/core/radio.js.map +1 -1
  371. package/dist/core/radio.test.js +15 -2
  372. package/dist/core/radio.test.js.map +1 -1
  373. package/dist/core/regrounding.d.ts.map +1 -1
  374. package/dist/core/regrounding.js +1 -0
  375. package/dist/core/regrounding.js.map +1 -1
  376. package/dist/core/run-claim.d.ts +37 -0
  377. package/dist/core/run-claim.d.ts.map +1 -0
  378. package/dist/core/run-claim.js +46 -0
  379. package/dist/core/run-claim.js.map +1 -0
  380. package/dist/core/run-claim.test.d.ts +2 -0
  381. package/dist/core/run-claim.test.d.ts.map +1 -0
  382. package/dist/core/run-claim.test.js +107 -0
  383. package/dist/core/run-claim.test.js.map +1 -0
  384. package/dist/core/run-journal.test.d.ts +2 -0
  385. package/dist/core/run-journal.test.d.ts.map +1 -0
  386. package/dist/core/run-journal.test.js +195 -0
  387. package/dist/core/run-journal.test.js.map +1 -0
  388. package/dist/core/run-lifecycle.d.ts +22 -3
  389. package/dist/core/run-lifecycle.d.ts.map +1 -1
  390. package/dist/core/run-lifecycle.js +12 -3
  391. package/dist/core/run-lifecycle.js.map +1 -1
  392. package/dist/core/sandbox-domains.d.ts +14 -0
  393. package/dist/core/sandbox-domains.d.ts.map +1 -0
  394. package/dist/core/sandbox-domains.js +76 -0
  395. package/dist/core/sandbox-domains.js.map +1 -0
  396. package/dist/core/sandbox-feedback.d.ts +22 -0
  397. package/dist/core/sandbox-feedback.d.ts.map +1 -0
  398. package/dist/core/sandbox-feedback.js +69 -0
  399. package/dist/core/sandbox-feedback.js.map +1 -0
  400. package/dist/core/sandbox-feedback.test.d.ts +2 -0
  401. package/dist/core/sandbox-feedback.test.d.ts.map +1 -0
  402. package/dist/core/sandbox-feedback.test.js +32 -0
  403. package/dist/core/sandbox-feedback.test.js.map +1 -0
  404. package/dist/core/sandbox.d.ts +91 -0
  405. package/dist/core/sandbox.d.ts.map +1 -0
  406. package/dist/core/sandbox.js +254 -0
  407. package/dist/core/sandbox.js.map +1 -0
  408. package/dist/core/sandbox.test.d.ts +2 -0
  409. package/dist/core/sandbox.test.d.ts.map +1 -0
  410. package/dist/core/sandbox.test.js +157 -0
  411. package/dist/core/sandbox.test.js.map +1 -0
  412. package/dist/core/semantic-search-benchmark.d.ts +2 -2
  413. package/dist/core/semantic-search-benchmark.js +5 -5
  414. package/dist/core/session-compaction.d.ts +6 -0
  415. package/dist/core/session-compaction.d.ts.map +1 -1
  416. package/dist/core/session-compaction.js +11 -1
  417. package/dist/core/session-compaction.js.map +1 -1
  418. package/dist/core/session-compaction.test.js +19 -2
  419. package/dist/core/session-compaction.test.js.map +1 -1
  420. package/dist/core/session-export.d.ts.map +1 -1
  421. package/dist/core/session-export.js +12 -4
  422. package/dist/core/session-export.js.map +1 -1
  423. package/dist/core/session-history.d.ts +95 -6
  424. package/dist/core/session-history.d.ts.map +1 -1
  425. package/dist/core/session-history.js +261 -17
  426. package/dist/core/session-history.js.map +1 -1
  427. package/dist/core/session-history.test.js +173 -2
  428. package/dist/core/session-history.test.js.map +1 -1
  429. package/dist/core/session-manager.d.ts +223 -6
  430. package/dist/core/session-manager.d.ts.map +1 -1
  431. package/dist/core/session-manager.js +579 -74
  432. package/dist/core/session-manager.js.map +1 -1
  433. package/dist/core/session-manager.test.js +154 -10
  434. package/dist/core/session-manager.test.js.map +1 -1
  435. package/dist/core/session-notification-visibility.test.d.ts +2 -0
  436. package/dist/core/session-notification-visibility.test.d.ts.map +1 -0
  437. package/dist/core/session-notification-visibility.test.js +113 -0
  438. package/dist/core/session-notification-visibility.test.js.map +1 -0
  439. package/dist/core/session-preview.d.ts +5 -4
  440. package/dist/core/session-preview.d.ts.map +1 -1
  441. package/dist/core/session-preview.js +12 -5
  442. package/dist/core/session-preview.js.map +1 -1
  443. package/dist/core/session-restore-display.test.js +25 -0
  444. package/dist/core/session-restore-display.test.js.map +1 -1
  445. package/dist/core/session-storage-fd-leak.test.d.ts +2 -0
  446. package/dist/core/session-storage-fd-leak.test.d.ts.map +1 -0
  447. package/dist/core/session-storage-fd-leak.test.js +116 -0
  448. package/dist/core/session-storage-fd-leak.test.js.map +1 -0
  449. package/dist/core/session-storage.d.ts +22 -2
  450. package/dist/core/session-storage.d.ts.map +1 -1
  451. package/dist/core/session-storage.js +72 -9
  452. package/dist/core/session-storage.js.map +1 -1
  453. package/dist/core/session-storage.test.js +8 -3
  454. package/dist/core/session-storage.test.js.map +1 -1
  455. package/dist/core/settings-manager.d.ts +10 -2
  456. package/dist/core/settings-manager.d.ts.map +1 -1
  457. package/dist/core/settings-manager.js +36 -1
  458. package/dist/core/settings-manager.js.map +1 -1
  459. package/dist/core/shell-path.js +1 -1
  460. package/dist/core/shell.d.ts +1 -1
  461. package/dist/core/shell.js +2 -2
  462. package/dist/core/shell.test.js +2 -2
  463. package/dist/core/shutdown.d.ts +71 -0
  464. package/dist/core/shutdown.d.ts.map +1 -0
  465. package/dist/core/shutdown.js +144 -0
  466. package/dist/core/shutdown.js.map +1 -0
  467. package/dist/core/shutdown.test.d.ts +2 -0
  468. package/dist/core/shutdown.test.d.ts.map +1 -0
  469. package/dist/core/shutdown.test.js +160 -0
  470. package/dist/core/shutdown.test.js.map +1 -0
  471. package/dist/core/sidecar-error-reporter.js +1 -1
  472. package/dist/core/sidecar-error-reporter.test.js +2 -2
  473. package/dist/core/skills-routing.test.js +96 -0
  474. package/dist/core/skills-routing.test.js.map +1 -1
  475. package/dist/core/skills.d.ts.map +1 -1
  476. package/dist/core/skills.js +3 -0
  477. package/dist/core/skills.js.map +1 -1
  478. package/dist/core/steering.d.ts +12 -0
  479. package/dist/core/steering.d.ts.map +1 -1
  480. package/dist/core/steering.js +16 -0
  481. package/dist/core/steering.js.map +1 -1
  482. package/dist/core/subagent-manager.d.ts +22 -0
  483. package/dist/core/subagent-manager.d.ts.map +1 -1
  484. package/dist/core/subagent-manager.js +84 -13
  485. package/dist/core/subagent-manager.js.map +1 -1
  486. package/dist/core/subagent-manager.test.js +57 -15
  487. package/dist/core/subagent-manager.test.js.map +1 -1
  488. package/dist/core/subagent-store.test.js +3 -3
  489. package/dist/core/task-store.js +1 -1
  490. package/dist/core/verification-evidence.d.ts +17 -0
  491. package/dist/core/verification-evidence.d.ts.map +1 -0
  492. package/dist/core/verification-evidence.js +283 -0
  493. package/dist/core/verification-evidence.js.map +1 -0
  494. package/dist/core/verification-evidence.test.d.ts +2 -0
  495. package/dist/core/verification-evidence.test.d.ts.map +1 -0
  496. package/dist/core/verification-evidence.test.js +103 -0
  497. package/dist/core/verification-evidence.test.js.map +1 -0
  498. package/dist/index.js +1 -1
  499. package/dist/index.js.map +1 -1
  500. package/dist/interactive.d.ts.map +1 -1
  501. package/dist/interactive.js +17 -3
  502. package/dist/interactive.js.map +1 -1
  503. package/dist/modes/acp-mode.d.ts +122 -0
  504. package/dist/modes/acp-mode.d.ts.map +1 -0
  505. package/dist/modes/acp-mode.js +1366 -0
  506. package/dist/modes/acp-mode.js.map +1 -0
  507. package/dist/modes/acp-mode.test.d.ts +2 -0
  508. package/dist/modes/acp-mode.test.d.ts.map +1 -0
  509. package/dist/modes/acp-mode.test.js +1065 -0
  510. package/dist/modes/acp-mode.test.js.map +1 -0
  511. package/dist/modes/agent-home-mode.d.ts.map +1 -1
  512. package/dist/modes/agent-home-mode.js +15 -11
  513. package/dist/modes/agent-home-mode.js.map +1 -1
  514. package/dist/modes/index.d.ts +1 -0
  515. package/dist/modes/index.d.ts.map +1 -1
  516. package/dist/modes/index.js +1 -0
  517. package/dist/modes/index.js.map +1 -1
  518. package/dist/modes/json-mode.d.ts +36 -0
  519. package/dist/modes/json-mode.d.ts.map +1 -1
  520. package/dist/modes/json-mode.js +39 -0
  521. package/dist/modes/json-mode.js.map +1 -1
  522. package/dist/modes/json-mode.test.d.ts +2 -0
  523. package/dist/modes/json-mode.test.d.ts.map +1 -0
  524. package/dist/modes/json-mode.test.js +84 -0
  525. package/dist/modes/json-mode.test.js.map +1 -0
  526. package/dist/modes/serve-mode.d.ts +1 -1
  527. package/dist/modes/serve-mode.d.ts.map +1 -1
  528. package/dist/modes/serve-mode.js +14 -9
  529. package/dist/modes/serve-mode.js.map +1 -1
  530. package/dist/modes/subagent-worker-mode.d.ts +8 -0
  531. package/dist/modes/subagent-worker-mode.d.ts.map +1 -1
  532. package/dist/modes/subagent-worker-mode.js +91 -11
  533. package/dist/modes/subagent-worker-mode.js.map +1 -1
  534. package/dist/modes/subagent-worker-mode.test.d.ts +2 -0
  535. package/dist/modes/subagent-worker-mode.test.d.ts.map +1 -0
  536. package/dist/modes/subagent-worker-mode.test.js +52 -0
  537. package/dist/modes/subagent-worker-mode.test.js.map +1 -0
  538. package/dist/session.d.ts +29 -0
  539. package/dist/session.d.ts.map +1 -1
  540. package/dist/session.js +41 -0
  541. package/dist/session.js.map +1 -1
  542. package/dist/session.test.js +1 -1
  543. package/dist/system-prompt.d.ts +37 -1
  544. package/dist/system-prompt.d.ts.map +1 -1
  545. package/dist/system-prompt.js +123 -11
  546. package/dist/system-prompt.js.map +1 -1
  547. package/dist/system-prompt.test.js +90 -11
  548. package/dist/system-prompt.test.js.map +1 -1
  549. package/dist/system-prompt.tiering.test.d.ts +2 -0
  550. package/dist/system-prompt.tiering.test.d.ts.map +1 -0
  551. package/dist/system-prompt.tiering.test.js +105 -0
  552. package/dist/system-prompt.tiering.test.js.map +1 -0
  553. package/dist/tools/bash.d.ts +2 -1
  554. package/dist/tools/bash.d.ts.map +1 -1
  555. package/dist/tools/bash.js +55 -16
  556. package/dist/tools/bash.js.map +1 -1
  557. package/dist/tools/code-nav.d.ts +30 -0
  558. package/dist/tools/code-nav.d.ts.map +1 -0
  559. package/dist/tools/code-nav.js +320 -0
  560. package/dist/tools/code-nav.js.map +1 -0
  561. package/dist/tools/code-nav.test.d.ts +2 -0
  562. package/dist/tools/code-nav.test.d.ts.map +1 -0
  563. package/dist/tools/code-nav.test.js +136 -0
  564. package/dist/tools/code-nav.test.js.map +1 -0
  565. package/dist/tools/edit.test.js +1 -1
  566. package/dist/tools/generate-image.d.ts +1 -1
  567. package/dist/tools/goals.d.ts +4 -4
  568. package/dist/tools/goals.test.js +2 -2
  569. package/dist/tools/grep-parity.test.d.ts +2 -0
  570. package/dist/tools/grep-parity.test.d.ts.map +1 -0
  571. package/dist/tools/grep-parity.test.js +92 -0
  572. package/dist/tools/grep-parity.test.js.map +1 -0
  573. package/dist/tools/grep-recall.test.d.ts +2 -0
  574. package/dist/tools/grep-recall.test.d.ts.map +1 -0
  575. package/dist/tools/grep-recall.test.js +114 -0
  576. package/dist/tools/grep-recall.test.js.map +1 -0
  577. package/dist/tools/grep-spawn.test.d.ts +2 -0
  578. package/dist/tools/grep-spawn.test.d.ts.map +1 -0
  579. package/dist/tools/grep-spawn.test.js +66 -0
  580. package/dist/tools/grep-spawn.test.js.map +1 -0
  581. package/dist/tools/grep.d.ts +13 -3
  582. package/dist/tools/grep.d.ts.map +1 -1
  583. package/dist/tools/grep.js +342 -41
  584. package/dist/tools/grep.js.map +1 -1
  585. package/dist/tools/grep.test.js +17 -14
  586. package/dist/tools/grep.test.js.map +1 -1
  587. package/dist/tools/index.d.ts +20 -2
  588. package/dist/tools/index.d.ts.map +1 -1
  589. package/dist/tools/index.js +28 -18
  590. package/dist/tools/index.js.map +1 -1
  591. package/dist/tools/index.test.d.ts +2 -0
  592. package/dist/tools/index.test.d.ts.map +1 -0
  593. package/dist/tools/index.test.js +55 -0
  594. package/dist/tools/index.test.js.map +1 -0
  595. package/dist/tools/plan-mode.test.js +1 -1
  596. package/dist/tools/prompt-hints.d.ts +9 -0
  597. package/dist/tools/prompt-hints.d.ts.map +1 -1
  598. package/dist/tools/prompt-hints.js +61 -8
  599. package/dist/tools/prompt-hints.js.map +1 -1
  600. package/dist/tools/read-only-bash.d.ts +3 -0
  601. package/dist/tools/read-only-bash.d.ts.map +1 -1
  602. package/dist/tools/read-only-bash.js +7 -9
  603. package/dist/tools/read-only-bash.js.map +1 -1
  604. package/dist/tools/safe-env.js +1 -1
  605. package/dist/tools/search-code.d.ts.map +1 -1
  606. package/dist/tools/search-code.js +7 -6
  607. package/dist/tools/search-code.js.map +1 -1
  608. package/dist/tools/search-code.test.js +8 -2
  609. package/dist/tools/search-code.test.js.map +1 -1
  610. package/dist/tools/skill.js +2 -1
  611. package/dist/tools/skill.js.map +1 -1
  612. package/dist/tools/subagent-control.d.ts.map +1 -1
  613. package/dist/tools/subagent-control.js +25 -6
  614. package/dist/tools/subagent-control.js.map +1 -1
  615. package/dist/tools/subagent-control.test.js +34 -1
  616. package/dist/tools/subagent-control.test.js.map +1 -1
  617. package/dist/tools/subagent-shared.d.ts +26 -1
  618. package/dist/tools/subagent-shared.d.ts.map +1 -1
  619. package/dist/tools/subagent-shared.js +40 -7
  620. package/dist/tools/subagent-shared.js.map +1 -1
  621. package/dist/tools/subagent-shared.test.js +43 -9
  622. package/dist/tools/subagent-shared.test.js.map +1 -1
  623. package/dist/tools/subagent.d.ts.map +1 -1
  624. package/dist/tools/subagent.js +54 -16
  625. package/dist/tools/subagent.js.map +1 -1
  626. package/dist/tools/subagent.test.js +68 -3
  627. package/dist/tools/subagent.test.js.map +1 -1
  628. package/dist/tools/task-output.d.ts.map +1 -1
  629. package/dist/tools/task-output.js +3 -1
  630. package/dist/tools/task-output.js.map +1 -1
  631. package/dist/tools/task-send.test.js +19 -10
  632. package/dist/tools/task-send.test.js.map +1 -1
  633. package/dist/tools/tool-search.d.ts +12 -1
  634. package/dist/tools/tool-search.d.ts.map +1 -1
  635. package/dist/tools/tool-search.js +24 -4
  636. package/dist/tools/tool-search.js.map +1 -1
  637. package/dist/tools/tool-search.test.d.ts +2 -0
  638. package/dist/tools/tool-search.test.d.ts.map +1 -0
  639. package/dist/tools/tool-search.test.js +69 -0
  640. package/dist/tools/tool-search.test.js.map +1 -0
  641. package/dist/tools/tool-tiers.d.ts +42 -0
  642. package/dist/tools/tool-tiers.d.ts.map +1 -0
  643. package/dist/tools/tool-tiers.js +78 -0
  644. package/dist/tools/tool-tiers.js.map +1 -0
  645. package/dist/tools/tool-tiers.test.d.ts +2 -0
  646. package/dist/tools/tool-tiers.test.d.ts.map +1 -0
  647. package/dist/tools/tool-tiers.test.js +48 -0
  648. package/dist/tools/tool-tiers.test.js.map +1 -0
  649. package/dist/tools/web-fetch.d.ts +1 -1
  650. package/dist/tools/web-fetch.d.ts.map +1 -1
  651. package/dist/tools/web-fetch.js +5 -2
  652. package/dist/tools/web-fetch.js.map +1 -1
  653. package/dist/tools/write.test.js +1 -1
  654. package/dist/types.d.ts +7 -0
  655. package/dist/types.d.ts.map +1 -1
  656. package/dist/ui/App.d.ts.map +1 -1
  657. package/dist/ui/App.js +27 -1
  658. package/dist/ui/App.js.map +1 -1
  659. package/dist/ui/components/Banner.d.ts.map +1 -1
  660. package/dist/ui/components/Banner.js +24 -16
  661. package/dist/ui/components/Banner.js.map +1 -1
  662. package/dist/ui/components/ToolExecution.js +42 -34
  663. package/dist/ui/components/ToolExecution.js.map +1 -1
  664. package/dist/ui/components/force-color.d.ts +2 -0
  665. package/dist/ui/components/force-color.d.ts.map +1 -0
  666. package/dist/ui/components/force-color.js +11 -0
  667. package/dist/ui/components/force-color.js.map +1 -0
  668. package/dist/ui/components/themed-output.test.d.ts +2 -0
  669. package/dist/ui/components/themed-output.test.d.ts.map +1 -0
  670. package/dist/ui/components/themed-output.test.js +111 -0
  671. package/dist/ui/components/themed-output.test.js.map +1 -0
  672. package/dist/ui/error-item.d.ts +1 -1
  673. package/dist/ui/error-item.js +3 -3
  674. package/dist/ui/error-item.js.map +1 -1
  675. package/dist/ui/hooks/useAgentLoop.d.ts +3 -0
  676. package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
  677. package/dist/ui/hooks/useAgentLoop.js +45 -6
  678. package/dist/ui/hooks/useAgentLoop.js.map +1 -1
  679. package/dist/ui/login.js +1 -1
  680. package/dist/ui/login.js.map +1 -1
  681. package/dist/ui/render.d.ts +1 -1
  682. package/dist/ui/render.js +6 -6
  683. package/dist/ui/theme/dark-ansi.json +6 -0
  684. package/dist/ui/theme/dark-daltonized.json +6 -0
  685. package/dist/ui/theme/dark.json +6 -0
  686. package/dist/ui/theme/light-ansi.json +6 -0
  687. package/dist/ui/theme/light-daltonized.json +6 -0
  688. package/dist/ui/theme/light.json +6 -0
  689. package/dist/ui/theme/theme.d.ts +6 -0
  690. package/dist/ui/theme/theme.d.ts.map +1 -1
  691. package/dist/ui/tui-terminal-recorder.test.js +1 -1
  692. package/dist/utils/git.test.js +3 -3
  693. package/dist/utils/github.test.js +3 -3
  694. package/dist/utils/github.test.js.map +1 -1
  695. package/dist/utils/image.d.ts +26 -3
  696. package/dist/utils/image.d.ts.map +1 -1
  697. package/dist/utils/image.js +83 -14
  698. package/dist/utils/image.js.map +1 -1
  699. package/dist/utils/image.test.d.ts +2 -0
  700. package/dist/utils/image.test.d.ts.map +1 -0
  701. package/dist/utils/image.test.js +139 -0
  702. package/dist/utils/image.test.js.map +1 -0
  703. package/package.json +7 -6
@@ -0,0 +1,96 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { selectMessagesInBudget } from "./compactor.js";
3
+ import { estimateConversationTokens } from "./token-estimator.js";
4
+ import { findLatestHumanQuery, selectQueryAwareContext } from "./query-aware-selector.js";
5
+ function message(role, content) {
6
+ return { role, content };
7
+ }
8
+ describe("selectQueryAwareContext", () => {
9
+ it("keeps older evidence relevant to the latest request ahead of unrelated recent context", () => {
10
+ const messages = [
11
+ message("user", "Build the account settings flow."),
12
+ message("assistant", "AuthService validates the OAuth callback verifier in src/auth/callback.ts."),
13
+ message("assistant", "Adjusted unrelated dashboard colors and spacing."),
14
+ message("user", "Fix the OAuth callback verifier mismatch in AuthService."),
15
+ ];
16
+ const budget = estimateConversationTokens([messages[0], messages[1], messages[3]]);
17
+ const result = selectQueryAwareContext(messages, "Fix the OAuth callback verifier mismatch in AuthService.", budget, { fallback: selectMessagesInBudget });
18
+ expect(result.strategy).toBe("query_aware");
19
+ expect(result.messages).toEqual([messages[0], messages[1], messages[3]]);
20
+ expect(result.messages).not.toContain(messages[2]);
21
+ });
22
+ it("never exceeds the hard token budget", () => {
23
+ const messages = [
24
+ message("user", `Original request ${"a".repeat(1_000)}`),
25
+ message("assistant", `OAuth callback evidence ${"b".repeat(1_000)}`),
26
+ message("assistant", `OAuth verifier definition ${"c".repeat(1_000)}`),
27
+ message("user", "Investigate the OAuth verifier."),
28
+ ];
29
+ const budget = 320;
30
+ const result = selectQueryAwareContext(messages, "Investigate the OAuth verifier.", budget, {
31
+ fallback: selectMessagesInBudget,
32
+ });
33
+ expect(result.selectedTokens).toBeLessThanOrEqual(budget);
34
+ expect(estimateConversationTokens(result.messages)).toBeLessThanOrEqual(budget);
35
+ });
36
+ it("uses the existing selector deterministically when the query has no signal", () => {
37
+ const messages = [
38
+ message("user", "first"),
39
+ message("assistant", "middle"),
40
+ message("user", "latest"),
41
+ ];
42
+ const fallback = vi.fn(selectMessagesInBudget);
43
+ const first = selectQueryAwareContext(messages, "please continue", 100, { fallback });
44
+ const second = selectQueryAwareContext(messages, "please continue", 100, { fallback });
45
+ expect(first).toEqual(second);
46
+ expect(first).toMatchObject({ strategy: "fallback", fallbackReason: "empty_query" });
47
+ expect(first.messages).toEqual(selectMessagesInBudget(messages, 100));
48
+ expect(fallback).toHaveBeenCalledTimes(2);
49
+ });
50
+ it("falls back without throwing when retrieval fails or returns invalid rankings", () => {
51
+ const messages = [
52
+ message("user", "Original OAuth request"),
53
+ message("assistant", "OAuth implementation evidence"),
54
+ message("user", "Fix OAuth verifier"),
55
+ ];
56
+ const expected = selectMessagesInBudget(messages, 100);
57
+ const failed = selectQueryAwareContext(messages, "Fix OAuth verifier", 100, {
58
+ fallback: selectMessagesInBudget,
59
+ retrieve: () => {
60
+ throw new Error("retriever unavailable");
61
+ },
62
+ });
63
+ const invalid = selectQueryAwareContext(messages, "Fix OAuth verifier", 100, {
64
+ fallback: selectMessagesInBudget,
65
+ retrieve: () => [{ index: 99, score: 1 }],
66
+ });
67
+ expect(failed).toMatchObject({
68
+ strategy: "fallback",
69
+ fallbackReason: "retrieval_failed",
70
+ messages: expected,
71
+ });
72
+ expect(invalid).toMatchObject({
73
+ strategy: "fallback",
74
+ fallbackReason: "invalid_ranking",
75
+ messages: expected,
76
+ });
77
+ });
78
+ });
79
+ describe("findLatestHumanQuery", () => {
80
+ it("ignores runtime continuation messages", () => {
81
+ const messages = [
82
+ {
83
+ role: "user",
84
+ content: "Fix the OAuth verifier",
85
+ provenance: { source: "human", kind: "prompt", visibility: "transcript" },
86
+ },
87
+ {
88
+ role: "user",
89
+ content: "Continue after compaction",
90
+ provenance: { source: "runtime", kind: "continuation", visibility: "hidden" },
91
+ },
92
+ ];
93
+ expect(findLatestHumanQuery(messages)).toBe("Fix the OAuth verifier");
94
+ });
95
+ });
96
+ //# sourceMappingURL=query-aware-selector.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-aware-selector.test.js","sourceRoot":"","sources":["../../../src/core/compaction/query-aware-selector.test.ts"],"names":[],"mappings":"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"}
@@ -1,34 +1,47 @@
1
1
  import type { Message } from "@prestyj/ai";
2
2
  /**
3
- * Cheap context pruning for stale tool outputs (opencode-style, no LLM call).
3
+ * Cheap context pruning for stale tool payloads (opencode-style, no LLM call).
4
4
  *
5
- * Two signals, one pass:
5
+ * Three signals, one pass:
6
6
  * 1. Superseded reads — an old `read` of a file that was re-read later carries
7
7
  * zero information; the newest read wins.
8
8
  * 2. Old tool-output overflow — walking backwards, the most recent
9
- * `protectTokens` worth of tool output (plus everything in the last
10
- * `protectTurns` user turns) is kept verbatim; anything older is stubbed.
9
+ * `protectTokens` worth of tool output plus the last `protectToolBatches`
10
+ * provider tool batches are kept verbatim; anything older is stubbed.
11
+ * 3. Large completed tool arguments — old write/edit payloads are capped once
12
+ * their result has returned; the files and tool result are the durable truth.
13
+ *
14
+ * `PROTECTED_TOOLS` output is never pruned: it is behavioural instruction rather
15
+ * than reproducible data, so a "re-run the tool" stub is not recoverable — the
16
+ * agent has no signal that it ever loaded the skill.
17
+ *
18
+ * Tool batches—not human turns—are the protection boundary. Autonomous research
19
+ * can execute dozens of provider/tool cycles for one user prompt; protecting the
20
+ * whole human turn lets reproducible reads refill the context immediately after
21
+ * compaction.
11
22
  *
12
23
  * Cache stability: pruning mutates history, which invalidates the provider's
13
- * prompt-cache prefix once. To avoid churning the cache every turn, nothing is
14
- * pruned unless at least `minimumTokens` would be freed in one batch — so
15
- * prunes are rare, large, and the stubbed prefix is stable afterwards.
16
- * Stubs are idempotent (marked with a prefix) and never re-pruned.
24
+ * prompt-cache prefix. To limit churn, nothing is pruned unless at least
25
+ * `minimumTokens` would be freed in one batch. Stubs and compacted arguments are
26
+ * idempotent, so later passes leave the rewritten prefix stable.
17
27
  */
18
28
  export declare const PRUNE_PROTECT_TOKENS = 40000;
19
29
  export declare const PRUNE_MINIMUM_TOKENS = 20000;
20
- export declare const PRUNE_PROTECT_TURNS = 2;
30
+ export declare const PRUNE_PROTECT_TOOL_BATCHES = 2;
31
+ /** Tools whose output is instruction, not reproducible data — never pruned. */
32
+ export declare const PRUNE_PROTECTED_TOOLS: Set<string>;
21
33
  export interface PruneOptions {
22
34
  /** Recent tool-output token budget kept verbatim. Default 40k. */
23
35
  protectTokens?: number;
24
36
  /** Minimum freed tokens required to apply a prune batch. Default 20k. */
25
37
  minimumTokens?: number;
26
- /** Number of most-recent user turns whose tool outputs are never pruned. Default 2. */
27
- protectTurns?: number;
38
+ /** Number of most-recent provider tool batches never pruned. Default 2. */
39
+ protectToolBatches?: number;
28
40
  }
29
41
  export interface PruneResult {
30
42
  pruned: boolean;
31
43
  prunedResults: number;
44
+ compactedToolCalls: number;
32
45
  freedTokens: number;
33
46
  }
34
47
  /** Mutates `messages` in place; message/array identity is preserved so usage
@@ -1 +1 @@
1
- {"version":3,"file":"tool-result-pruner.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/tool-result-pruner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAc,MAAM,aAAa,CAAC;AAGvD;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAC3C,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAC3C,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAIrC,MAAM,WAAW,YAAY;IAC3B,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAQD;iEACiE;AACjE,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,IAAI,GAAE,YAAiB,GAAG,WAAW,CAqF/F"}
1
+ {"version":3,"file":"tool-result-pruner.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/tool-result-pruner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,OAAO,EAAc,MAAM,aAAa,CAAC;AAIpE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAC3C,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAC3C,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C,+EAA+E;AAC/E,eAAO,MAAM,qBAAqB,aAAqB,CAAC;AAWxD,MAAM,WAAW,YAAY;IAC3B,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;CACrB;AAeD;iEACiE;AACjE,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,IAAI,GAAE,YAAiB,GAAG,WAAW,CA+I/F"}
@@ -1,30 +1,50 @@
1
+ import { compactHistoricalToolCallArgs } from "./compactor.js";
1
2
  import { estimateTokens } from "./token-estimator.js";
2
3
  /**
3
- * Cheap context pruning for stale tool outputs (opencode-style, no LLM call).
4
+ * Cheap context pruning for stale tool payloads (opencode-style, no LLM call).
4
5
  *
5
- * Two signals, one pass:
6
+ * Three signals, one pass:
6
7
  * 1. Superseded reads — an old `read` of a file that was re-read later carries
7
8
  * zero information; the newest read wins.
8
9
  * 2. Old tool-output overflow — walking backwards, the most recent
9
- * `protectTokens` worth of tool output (plus everything in the last
10
- * `protectTurns` user turns) is kept verbatim; anything older is stubbed.
10
+ * `protectTokens` worth of tool output plus the last `protectToolBatches`
11
+ * provider tool batches are kept verbatim; anything older is stubbed.
12
+ * 3. Large completed tool arguments — old write/edit payloads are capped once
13
+ * their result has returned; the files and tool result are the durable truth.
14
+ *
15
+ * `PROTECTED_TOOLS` output is never pruned: it is behavioural instruction rather
16
+ * than reproducible data, so a "re-run the tool" stub is not recoverable — the
17
+ * agent has no signal that it ever loaded the skill.
18
+ *
19
+ * Tool batches—not human turns—are the protection boundary. Autonomous research
20
+ * can execute dozens of provider/tool cycles for one user prompt; protecting the
21
+ * whole human turn lets reproducible reads refill the context immediately after
22
+ * compaction.
11
23
  *
12
24
  * Cache stability: pruning mutates history, which invalidates the provider's
13
- * prompt-cache prefix once. To avoid churning the cache every turn, nothing is
14
- * pruned unless at least `minimumTokens` would be freed in one batch — so
15
- * prunes are rare, large, and the stubbed prefix is stable afterwards.
16
- * Stubs are idempotent (marked with a prefix) and never re-pruned.
25
+ * prompt-cache prefix. To limit churn, nothing is pruned unless at least
26
+ * `minimumTokens` would be freed in one batch. Stubs and compacted arguments are
27
+ * idempotent, so later passes leave the rewritten prefix stable.
17
28
  */
18
29
  export const PRUNE_PROTECT_TOKENS = 40_000;
19
30
  export const PRUNE_MINIMUM_TOKENS = 20_000;
20
- export const PRUNE_PROTECT_TURNS = 2;
31
+ export const PRUNE_PROTECT_TOOL_BATCHES = 2;
32
+ /** Tools whose output is instruction, not reproducible data — never pruned. */
33
+ export const PRUNE_PROTECTED_TOOLS = new Set(["skill"]);
21
34
  const PRUNE_MARKER = "[Pruned:";
35
+ /**
36
+ * Prefix of the token-overflow stub specifically. Reaching one of these while
37
+ * walking backwards proves the whole older prefix was already considered, which
38
+ * a superseded-read stub does NOT prove — read dedup fires independently of the
39
+ * token budget, so an older verbatim result can sit behind one.
40
+ */
41
+ const PRUNE_OVERFLOW_MARKER = `${PRUNE_MARKER} old tool output`;
22
42
  /** Mutates `messages` in place; message/array identity is preserved so usage
23
43
  * anchors and React refs stay valid. Returns what was freed. */
24
44
  export function pruneStaleToolResults(messages, opts = {}) {
25
45
  const protectTokens = opts.protectTokens ?? PRUNE_PROTECT_TOKENS;
26
46
  const minimumTokens = opts.minimumTokens ?? PRUNE_MINIMUM_TOKENS;
27
- const protectTurns = opts.protectTurns ?? PRUNE_PROTECT_TURNS;
47
+ const protectToolBatches = opts.protectToolBatches ?? PRUNE_PROTECT_TOOL_BATCHES;
28
48
  // toolCallId → tool name + args (for read-dedup path identification).
29
49
  const callInfo = new Map();
30
50
  for (const msg of messages) {
@@ -37,22 +57,37 @@ export function pruneStaleToolResults(messages, opts = {}) {
37
57
  }
38
58
  }
39
59
  const seenReadPaths = new Set();
40
- const candidates = [];
41
- let userTurns = 0;
60
+ const resultCandidates = [];
61
+ const toolBatchByCallId = new Map();
62
+ let recentToolBatches = 0;
42
63
  let recentToolTokens = 0;
43
- for (let msgIndex = messages.length - 1; msgIndex >= 0; msgIndex--) {
64
+ scan: for (let msgIndex = messages.length - 1; msgIndex >= 0; msgIndex--) {
44
65
  const msg = messages[msgIndex];
45
- if (msg.role === "user")
46
- userTurns++;
47
66
  if (msg.role !== "tool" || !Array.isArray(msg.content))
48
67
  continue;
68
+ recentToolBatches++;
69
+ const protectedBatch = recentToolBatches <= protectToolBatches;
49
70
  for (let partIndex = msg.content.length - 1; partIndex >= 0; partIndex--) {
50
71
  const result = msg.content[partIndex];
51
- if (result.type !== "tool_result" || typeof result.content !== "string")
72
+ if (result.type !== "tool_result")
73
+ continue;
74
+ toolBatchByCallId.set(result.toolCallId, recentToolBatches);
75
+ if (typeof result.content !== "string")
52
76
  continue;
53
- if (result.content.startsWith(PRUNE_MARKER))
77
+ // An overflow stub outside the protect zone means an earlier pass blew
78
+ // the token budget here and pruned every prunable result older than it in
79
+ // that same batch. History only grows at the tail, so nothing older can
80
+ // have become prunable since: stop instead of re-scanning the whole
81
+ // transcript every turn. Other stub kinds prove nothing about what lies
82
+ // behind them, so they are merely skipped.
83
+ if (result.content.startsWith(PRUNE_MARKER)) {
84
+ if (!protectedBatch && result.content.startsWith(PRUNE_OVERFLOW_MARKER))
85
+ break scan;
54
86
  continue;
87
+ }
55
88
  const info = callInfo.get(result.toolCallId);
89
+ if (info && PRUNE_PROTECTED_TOOLS.has(info.name))
90
+ continue;
56
91
  // Dedup key includes the range: a partial read (offset/limit) covers
57
92
  // different content than a full read or another range, so only an
58
93
  // identical path+range read supersedes an older one.
@@ -62,13 +97,12 @@ export function pruneStaleToolResults(messages, opts = {}) {
62
97
  const readKey = readPath !== undefined
63
98
  ? `${readPath}#${String(info?.args.offset ?? "")}:${String(info?.args.limit ?? "")}`
64
99
  : undefined;
65
- const protectedTurn = userTurns < protectTurns;
66
100
  // Newest read of each path+range wins — remember it even inside the
67
101
  // protect zone so an older duplicate outside the zone still counts as
68
102
  // superseded.
69
103
  if (readKey !== undefined) {
70
- if (seenReadPaths.has(readKey) && !protectedTurn) {
71
- candidates.push({
104
+ if (seenReadPaths.has(readKey) && !protectedBatch) {
105
+ resultCandidates.push({
72
106
  result,
73
107
  freedTokens: estimateTokens(result.content),
74
108
  stub: `${PRUNE_MARKER} this read of ${readPath} was superseded by a newer read later in the conversation.]`,
@@ -77,13 +111,13 @@ export function pruneStaleToolResults(messages, opts = {}) {
77
111
  }
78
112
  seenReadPaths.add(readKey);
79
113
  }
80
- if (protectedTurn)
114
+ if (protectedBatch)
81
115
  continue;
82
116
  const resultTokens = estimateTokens(result.content);
83
117
  recentToolTokens += resultTokens;
84
118
  if (recentToolTokens <= protectTokens)
85
119
  continue;
86
- candidates.push({
120
+ resultCandidates.push({
87
121
  result,
88
122
  freedTokens: resultTokens,
89
123
  stub: `${PRUNE_MARKER} old tool output (${result.content.length} chars) removed to save ` +
@@ -91,13 +125,51 @@ export function pruneStaleToolResults(messages, opts = {}) {
91
125
  });
92
126
  }
93
127
  }
94
- const freedTokens = candidates.reduce((sum, candidate) => sum + candidate.freedTokens, 0);
128
+ // Completed write/edit-style calls can carry entire files in their arguments.
129
+ // Once outside the protected batches, cap those payloads before they alone can
130
+ // trigger an LLM compaction. Unresolved calls are deliberately left untouched.
131
+ const toolArgCandidates = [];
132
+ for (const message of messages) {
133
+ if (message.role !== "assistant" || !Array.isArray(message.content))
134
+ continue;
135
+ const compactedMessage = compactHistoricalToolCallArgs([message], (toolCallId) => {
136
+ const batch = toolBatchByCallId.get(toolCallId);
137
+ return batch !== undefined && batch > protectToolBatches;
138
+ })[0];
139
+ if (compactedMessage === message)
140
+ continue;
141
+ const originalContent = message.content;
142
+ const compactedContent = compactedMessage.content;
143
+ const originalTokens = estimateTokens(JSON.stringify(originalContent));
144
+ const compactedTokens = estimateTokens(JSON.stringify(compactedContent));
145
+ const compactedToolCalls = compactedContent.filter((part, index) => part !== originalContent[index]).length;
146
+ toolArgCandidates.push({
147
+ message,
148
+ content: compactedMessage.content,
149
+ compactedToolCalls,
150
+ freedTokens: Math.max(0, originalTokens - compactedTokens),
151
+ });
152
+ }
153
+ const freedTokens = [...resultCandidates, ...toolArgCandidates].reduce((sum, candidate) => sum + candidate.freedTokens, 0);
95
154
  if (freedTokens < minimumTokens) {
96
- return { pruned: false, prunedResults: 0, freedTokens: 0 };
155
+ return {
156
+ pruned: false,
157
+ prunedResults: 0,
158
+ compactedToolCalls: 0,
159
+ freedTokens: 0,
160
+ };
97
161
  }
98
- for (const candidate of candidates) {
162
+ for (const candidate of resultCandidates) {
99
163
  candidate.result.content = candidate.stub;
100
164
  }
101
- return { pruned: true, prunedResults: candidates.length, freedTokens };
165
+ for (const candidate of toolArgCandidates) {
166
+ candidate.message.content = candidate.content;
167
+ }
168
+ return {
169
+ pruned: true,
170
+ prunedResults: resultCandidates.length,
171
+ compactedToolCalls: toolArgCandidates.reduce((sum, candidate) => sum + candidate.compactedToolCalls, 0),
172
+ freedTokens,
173
+ };
102
174
  }
103
175
  //# sourceMappingURL=tool-result-pruner.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool-result-pruner.js","sourceRoot":"","sources":["../../../src/core/compaction/tool-result-pruner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tool-result-pruner.js","sourceRoot":"","sources":["../../../src/core/compaction/tool-result-pruner.ts"],"names":[],"mappings":"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"}
@@ -17,8 +17,19 @@ function resultOf(messages, toolCallId) {
17
17
  }
18
18
  throw new Error(`missing tool result ${toolCallId}`);
19
19
  }
20
+ function callOf(messages, toolCallId) {
21
+ for (const msg of messages) {
22
+ if (msg.role !== "assistant" || !Array.isArray(msg.content))
23
+ continue;
24
+ for (const part of msg.content) {
25
+ if (part.type === "tool_call" && part.id === toolCallId)
26
+ return part;
27
+ }
28
+ }
29
+ throw new Error(`missing tool call ${toolCallId}`);
30
+ }
20
31
  describe("pruneStaleToolResults", () => {
21
- it("stubs superseded reads outside the protect turns", () => {
32
+ it("stubs superseded reads outside the protected tool batches", () => {
22
33
  const messages = [
23
34
  { role: "user", content: "turn 1" },
24
35
  ...toolTurn("old-read", "read", { file_path: "src/a.ts" }, "x".repeat(40_000)),
@@ -27,7 +38,10 @@ describe("pruneStaleToolResults", () => {
27
38
  ...toolTurn("new-read", "read", { file_path: "src/a.ts" }, "fresh content"),
28
39
  { role: "user", content: "turn 3" },
29
40
  ];
30
- const result = pruneStaleToolResults(messages, { minimumTokens: 1_000 });
41
+ const result = pruneStaleToolResults(messages, {
42
+ minimumTokens: 1_000,
43
+ protectToolBatches: 1,
44
+ });
31
45
  expect(result.pruned).toBe(true);
32
46
  expect(result.prunedResults).toBe(1);
33
47
  expect(resultOf(messages, "old-read").content).toContain("superseded by a newer read");
@@ -63,6 +77,7 @@ describe("pruneStaleToolResults", () => {
63
77
  const result = pruneStaleToolResults(messages, {
64
78
  protectTokens: 10_000,
65
79
  minimumTokens: 1_000,
80
+ protectToolBatches: 1,
66
81
  });
67
82
  expect(result.pruned).toBe(true);
68
83
  // "recent" fills the 10k protect budget; "oldest" overflows it.
@@ -70,7 +85,7 @@ describe("pruneStaleToolResults", () => {
70
85
  expect(resultOf(messages, "oldest").content).toContain("old tool output");
71
86
  expect(resultOf(messages, "oldest").content).toContain("Re-run the tool");
72
87
  });
73
- it("never touches tool output inside the protected recent turns", () => {
88
+ it("never touches tool output inside the protected recent batches", () => {
74
89
  const messages = [
75
90
  { role: "user", content: "turn 1" },
76
91
  ...toolTurn("latest", "bash", { command: "ls" }, "z".repeat(200_000)),
@@ -82,6 +97,35 @@ describe("pruneStaleToolResults", () => {
82
97
  expect(result.pruned).toBe(false);
83
98
  expect(resultOf(messages, "latest").content).toBe("z".repeat(200_000));
84
99
  });
100
+ it("caps large completed tool arguments outside the protected batches", () => {
101
+ const oldContent = "old".repeat(40_000);
102
+ const recentContent = "recent".repeat(20_000);
103
+ const messages = [
104
+ { role: "user", content: "implement several files" },
105
+ ...toolTurn("old-write", "write", { file_path: "old.ts", content: oldContent }, "ok"),
106
+ ...toolTurn("recent-write", "write", { file_path: "recent.ts", content: recentContent }, "ok"),
107
+ ...toolTurn("latest", "grep", { pattern: "done" }, "done"),
108
+ ];
109
+ const first = pruneStaleToolResults(messages, {
110
+ protectTokens: 100_000,
111
+ minimumTokens: 1,
112
+ protectToolBatches: 2,
113
+ });
114
+ const compacted = callOf(messages, "old-write").args.content;
115
+ expect(first.pruned).toBe(true);
116
+ expect(first.prunedResults).toBe(0);
117
+ expect(first.compactedToolCalls).toBe(1);
118
+ expect(compacted.length).toBeLessThan(oldContent.length);
119
+ expect(compacted).toContain("more characters truncated");
120
+ expect(callOf(messages, "recent-write").args.content).toBe(recentContent);
121
+ const second = pruneStaleToolResults(messages, {
122
+ protectTokens: 100_000,
123
+ minimumTokens: 1,
124
+ protectToolBatches: 2,
125
+ });
126
+ expect(second.pruned).toBe(false);
127
+ expect(callOf(messages, "old-write").args.content).toBe(compacted);
128
+ });
85
129
  it("holds the batch until the minimum freed-token threshold is met", () => {
86
130
  const messages = [
87
131
  { role: "user", content: "turn 1" },
@@ -92,10 +136,57 @@ describe("pruneStaleToolResults", () => {
92
136
  const result = pruneStaleToolResults(messages, {
93
137
  protectTokens: 100,
94
138
  minimumTokens: 20_000,
139
+ protectToolBatches: 0,
95
140
  });
96
141
  expect(result.pruned).toBe(false);
97
142
  expect(resultOf(messages, "small").content).toBe("s".repeat(2_000));
98
143
  });
144
+ it("bounds tool output during one long autonomous user turn", () => {
145
+ const messages = [{ role: "user", content: "research then implement" }];
146
+ for (let batch = 1; batch <= 12; batch++) {
147
+ messages.push(...toolTurn(`batch-${batch}`, "read", { file_path: `repo/${batch}.ts` }, "x".repeat(100_000)));
148
+ pruneStaleToolResults(messages, {
149
+ protectTokens: 40_000,
150
+ minimumTokens: 20_000,
151
+ protectToolBatches: 2,
152
+ });
153
+ }
154
+ const verbatimResults = Array.from({ length: 12 }, (_, index) => resultOf(messages, `batch-${index + 1}`)).filter((result) => !result.content.toString().startsWith("[Pruned:"));
155
+ // Two protected batches plus one older result fitting inside the 40k-token budget.
156
+ expect(verbatimResults).toHaveLength(3);
157
+ expect(resultOf(messages, "batch-10").content).toBe("x".repeat(100_000));
158
+ expect(resultOf(messages, "batch-11").content).toBe("x".repeat(100_000));
159
+ expect(resultOf(messages, "batch-12").content).toBe("x".repeat(100_000));
160
+ expect(resultOf(messages, "batch-1").content).toContain("old tool output");
161
+ });
162
+ it("treats parallel tool results as one provider batch", () => {
163
+ const messages = [
164
+ { role: "user", content: "research in parallel" },
165
+ {
166
+ role: "assistant",
167
+ content: [
168
+ { type: "tool_call", id: "parallel-a", name: "grep", args: { pattern: "a" } },
169
+ { type: "tool_call", id: "parallel-b", name: "grep", args: { pattern: "b" } },
170
+ ],
171
+ },
172
+ {
173
+ role: "tool",
174
+ content: [
175
+ { type: "tool_result", toolCallId: "parallel-a", content: "a".repeat(100_000) },
176
+ { type: "tool_result", toolCallId: "parallel-b", content: "b".repeat(100_000) },
177
+ ],
178
+ },
179
+ ...toolTurn("latest", "read", { file_path: "src/latest.ts" }, "latest"),
180
+ ];
181
+ const result = pruneStaleToolResults(messages, {
182
+ protectTokens: 1,
183
+ minimumTokens: 1,
184
+ protectToolBatches: 2,
185
+ });
186
+ expect(result.pruned).toBe(false);
187
+ expect(resultOf(messages, "parallel-a").content).toBe("a".repeat(100_000));
188
+ expect(resultOf(messages, "parallel-b").content).toBe("b".repeat(100_000));
189
+ });
99
190
  it("is idempotent: stubs are never re-pruned or re-counted", () => {
100
191
  const messages = [
101
192
  { role: "user", content: "turn 1" },
@@ -103,13 +194,90 @@ describe("pruneStaleToolResults", () => {
103
194
  { role: "user", content: "turn 2" },
104
195
  { role: "user", content: "turn 3" },
105
196
  ];
106
- const first = pruneStaleToolResults(messages, { protectTokens: 100, minimumTokens: 1_000 });
197
+ const first = pruneStaleToolResults(messages, {
198
+ protectTokens: 100,
199
+ minimumTokens: 1_000,
200
+ protectToolBatches: 0,
201
+ });
107
202
  const stub = resultOf(messages, "old").content;
108
- const second = pruneStaleToolResults(messages, { protectTokens: 100, minimumTokens: 1 });
203
+ const second = pruneStaleToolResults(messages, {
204
+ protectTokens: 100,
205
+ minimumTokens: 1,
206
+ protectToolBatches: 0,
207
+ });
109
208
  expect(first.pruned).toBe(true);
110
209
  expect(second.pruned).toBe(false);
111
210
  expect(resultOf(messages, "old").content).toBe(stub);
112
211
  });
212
+ it("never prunes skill output, which is instruction rather than reproducible data", () => {
213
+ const messages = [
214
+ { role: "user", content: "turn 1" },
215
+ ...toolTurn("skill-load", "skill", { skill: "evidence-led-ui" }, "s".repeat(120_000)),
216
+ { role: "user", content: "turn 2" },
217
+ ...toolTurn("old-bash", "bash", { command: "ls" }, "o".repeat(120_000)),
218
+ { role: "user", content: "turn 3" },
219
+ { role: "user", content: "turn 4" },
220
+ ];
221
+ const result = pruneStaleToolResults(messages, {
222
+ protectTokens: 100,
223
+ minimumTokens: 1_000,
224
+ protectToolBatches: 0,
225
+ });
226
+ expect(result.pruned).toBe(true);
227
+ expect(result.prunedResults).toBe(1);
228
+ expect(resultOf(messages, "skill-load").content).toBe("s".repeat(120_000));
229
+ expect(resultOf(messages, "old-bash").content).toContain("old tool output");
230
+ });
231
+ it("stops scanning at the first stub outside the protected batches", () => {
232
+ const messages = [
233
+ { role: "user", content: "turn 1" },
234
+ ...toolTurn("oldest", "bash", { command: "ls" }, "o".repeat(120_000)),
235
+ { role: "user", content: "turn 2" },
236
+ ...toolTurn("middle", "bash", { command: "pwd" }, "m".repeat(120_000)),
237
+ { role: "user", content: "turn 3" },
238
+ ];
239
+ const opts = { protectTokens: 100, minimumTokens: 1_000, protectToolBatches: 0 };
240
+ expect(pruneStaleToolResults(messages, opts).prunedResults).toBe(2);
241
+ // Fresh output arrives; the older half is already stubbed, so the second
242
+ // pass must stop at "middle" rather than re-walking the whole transcript.
243
+ messages.push(...toolTurn("newest", "bash", { command: "env" }, "n".repeat(120_000)));
244
+ const second = pruneStaleToolResults(messages, opts);
245
+ expect(second.pruned).toBe(true);
246
+ expect(second.prunedResults).toBe(1);
247
+ expect(resultOf(messages, "newest").content).toContain("old tool output");
248
+ expect(resultOf(messages, "oldest").content).toContain("old tool output");
249
+ });
250
+ it("does not let a superseded-read stub strand older output behind it", () => {
251
+ // A read dedup stub is written regardless of the token budget, so it can
252
+ // sit in front of results that are still verbatim. Stopping the backwards
253
+ // walk at one would strand them permanently.
254
+ const messages = [
255
+ { role: "user", content: "turn 1" },
256
+ ...toolTurn("old-bash", "bash", { command: "ls" }, "o".repeat(400_000)),
257
+ { role: "user", content: "turn 2" },
258
+ ...toolTurn("read-1", "read", { file_path: "/a.ts" }, "a".repeat(1_000)),
259
+ { role: "user", content: "turn 3" },
260
+ ...toolTurn("read-2", "read", { file_path: "/a.ts" }, "a".repeat(1_000)),
261
+ { role: "user", content: "turn 4" },
262
+ ];
263
+ // A protect budget this large spares the bash output, so only the
264
+ // superseded read is stubbed on the first pass.
265
+ const first = pruneStaleToolResults(messages, {
266
+ protectTokens: 500_000,
267
+ minimumTokens: 100,
268
+ protectToolBatches: 0,
269
+ });
270
+ expect(first.prunedResults).toBe(1);
271
+ expect(resultOf(messages, "old-bash").content).toBe("o".repeat(400_000));
272
+ // Now the budget is tight: the older bash output must still be reachable.
273
+ const second = pruneStaleToolResults(messages, {
274
+ protectTokens: 100,
275
+ minimumTokens: 1_000,
276
+ protectToolBatches: 0,
277
+ });
278
+ expect(second.pruned).toBe(true);
279
+ expect(resultOf(messages, "old-bash").content).toContain("old tool output");
280
+ });
113
281
  it("skips structured (non-string) tool results", () => {
114
282
  const messages = [
115
283
  { role: "user", content: "turn 1" },
@@ -140,7 +308,11 @@ describe("pruneStaleToolResults", () => {
140
308
  ];
141
309
  const anchor = messages[3];
142
310
  const toolMsg = messages[2];
143
- pruneStaleToolResults(messages, { protectTokens: 100, minimumTokens: 1_000 });
311
+ pruneStaleToolResults(messages, {
312
+ protectTokens: 100,
313
+ minimumTokens: 1_000,
314
+ protectToolBatches: 0,
315
+ });
144
316
  expect(messages[3]).toBe(anchor);
145
317
  expect(messages[2]).toBe(toolMsg);
146
318
  });