@code-yeongyu/senpi 2026.8.29 → 2026.8.30-3

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 (346) hide show
  1. package/CHANGELOG.md +172 -0
  2. package/dist/core/agent-session.d.ts +47 -1
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +406 -39
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/bash-executor.d.ts.map +1 -1
  7. package/dist/core/bash-executor.js +155 -37
  8. package/dist/core/bash-executor.js.map +1 -1
  9. package/dist/core/compaction/compaction-execution.d.ts +14 -0
  10. package/dist/core/compaction/compaction-execution.d.ts.map +1 -0
  11. package/dist/core/compaction/compaction-execution.js +121 -0
  12. package/dist/core/compaction/compaction-execution.js.map +1 -0
  13. package/dist/core/compaction/compaction-settings.d.ts +17 -0
  14. package/dist/core/compaction/compaction-settings.d.ts.map +1 -0
  15. package/dist/core/compaction/compaction-settings.js +17 -0
  16. package/dist/core/compaction/compaction-settings.js.map +1 -0
  17. package/dist/core/compaction/compaction.d.ts +13 -26
  18. package/dist/core/compaction/compaction.d.ts.map +1 -1
  19. package/dist/core/compaction/compaction.js +7 -136
  20. package/dist/core/compaction/compaction.js.map +1 -1
  21. package/dist/core/compaction/ideal-compaction-settings.d.ts +9 -0
  22. package/dist/core/compaction/ideal-compaction-settings.d.ts.map +1 -0
  23. package/dist/core/compaction/ideal-compaction-settings.js +7 -0
  24. package/dist/core/compaction/ideal-compaction-settings.js.map +1 -0
  25. package/dist/core/compaction-settings-access.d.ts +19 -0
  26. package/dist/core/compaction-settings-access.d.ts.map +1 -0
  27. package/dist/core/compaction-settings-access.js +10 -0
  28. package/dist/core/compaction-settings-access.js.map +1 -0
  29. package/dist/core/compaction-settings-resolver.d.ts +22 -0
  30. package/dist/core/compaction-settings-resolver.d.ts.map +1 -0
  31. package/dist/core/compaction-settings-resolver.js +46 -0
  32. package/dist/core/compaction-settings-resolver.js.map +1 -0
  33. package/dist/core/credential-accounts.d.ts.map +1 -1
  34. package/dist/core/credential-accounts.js +23 -9
  35. package/dist/core/credential-accounts.js.map +1 -1
  36. package/dist/core/credential-pool/env-slots.d.ts.map +1 -1
  37. package/dist/core/credential-pool/env-slots.js +2 -0
  38. package/dist/core/credential-pool/env-slots.js.map +1 -1
  39. package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts +18 -0
  40. package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts.map +1 -0
  41. package/dist/core/extensions/builtin/compaction/context-pipeline.js +32 -0
  42. package/dist/core/extensions/builtin/compaction/context-pipeline.js.map +1 -0
  43. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +8 -2
  44. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
  45. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +164 -48
  46. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
  47. package/dist/core/extensions/builtin/compaction/emergency-prune.d.ts +17 -0
  48. package/dist/core/extensions/builtin/compaction/emergency-prune.d.ts.map +1 -0
  49. package/dist/core/extensions/builtin/compaction/emergency-prune.js +64 -0
  50. package/dist/core/extensions/builtin/compaction/emergency-prune.js.map +1 -0
  51. package/dist/core/extensions/builtin/compaction/extension-wiring.d.ts +27 -0
  52. package/dist/core/extensions/builtin/compaction/extension-wiring.d.ts.map +1 -0
  53. package/dist/core/extensions/builtin/compaction/extension-wiring.js +84 -0
  54. package/dist/core/extensions/builtin/compaction/extension-wiring.js.map +1 -0
  55. package/dist/core/extensions/builtin/compaction/idle-retry.d.ts +1 -1
  56. package/dist/core/extensions/builtin/compaction/idle-retry.d.ts.map +1 -1
  57. package/dist/core/extensions/builtin/compaction/idle-retry.js +1 -1
  58. package/dist/core/extensions/builtin/compaction/idle-retry.js.map +1 -1
  59. package/dist/core/extensions/builtin/compaction/idle.d.ts +1 -0
  60. package/dist/core/extensions/builtin/compaction/idle.d.ts.map +1 -1
  61. package/dist/core/extensions/builtin/compaction/idle.js +13 -2
  62. package/dist/core/extensions/builtin/compaction/idle.js.map +1 -1
  63. package/dist/core/extensions/builtin/compaction/index.d.ts +1 -1
  64. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  65. package/dist/core/extensions/builtin/compaction/index.js +148 -150
  66. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  67. package/dist/core/extensions/builtin/compaction/log.d.ts +1 -1
  68. package/dist/core/extensions/builtin/compaction/log.d.ts.map +1 -1
  69. package/dist/core/extensions/builtin/compaction/log.js +2 -0
  70. package/dist/core/extensions/builtin/compaction/log.js.map +1 -1
  71. package/dist/core/extensions/builtin/compaction/model-selection.d.ts +14 -0
  72. package/dist/core/extensions/builtin/compaction/model-selection.d.ts.map +1 -0
  73. package/dist/core/extensions/builtin/compaction/model-selection.js +37 -0
  74. package/dist/core/extensions/builtin/compaction/model-selection.js.map +1 -0
  75. package/dist/core/extensions/builtin/compaction/model-usability-budget.d.ts +30 -0
  76. package/dist/core/extensions/builtin/compaction/model-usability-budget.d.ts.map +1 -0
  77. package/dist/core/extensions/builtin/compaction/model-usability-budget.js +74 -0
  78. package/dist/core/extensions/builtin/compaction/model-usability-budget.js.map +1 -0
  79. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.d.ts +9 -0
  80. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.d.ts.map +1 -0
  81. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.js +2 -0
  82. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.js.map +1 -0
  83. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +3 -6
  84. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts.map +1 -1
  85. package/dist/core/extensions/builtin/compaction/openai-remote.js.map +1 -1
  86. package/dist/core/extensions/builtin/compaction/orchestration.d.ts +46 -0
  87. package/dist/core/extensions/builtin/compaction/orchestration.d.ts.map +1 -0
  88. package/dist/core/extensions/builtin/compaction/orchestration.js +103 -0
  89. package/dist/core/extensions/builtin/compaction/orchestration.js.map +1 -0
  90. package/dist/core/extensions/builtin/compaction/policy.d.ts +12 -2
  91. package/dist/core/extensions/builtin/compaction/policy.d.ts.map +1 -1
  92. package/dist/core/extensions/builtin/compaction/policy.js +52 -26
  93. package/dist/core/extensions/builtin/compaction/policy.js.map +1 -1
  94. package/dist/core/extensions/builtin/compaction/retained-message-safety.d.ts.map +1 -1
  95. package/dist/core/extensions/builtin/compaction/retained-message-safety.js +31 -8
  96. package/dist/core/extensions/builtin/compaction/retained-message-safety.js.map +1 -1
  97. package/dist/core/extensions/builtin/compaction/speculation-lead.d.ts +8 -0
  98. package/dist/core/extensions/builtin/compaction/speculation-lead.d.ts.map +1 -0
  99. package/dist/core/extensions/builtin/compaction/speculation-lead.js +17 -0
  100. package/dist/core/extensions/builtin/compaction/speculation-lead.js.map +1 -0
  101. package/dist/core/extensions/builtin/compaction/speculative-job.d.ts +22 -0
  102. package/dist/core/extensions/builtin/compaction/speculative-job.d.ts.map +1 -0
  103. package/dist/core/extensions/builtin/compaction/speculative-job.js +16 -0
  104. package/dist/core/extensions/builtin/compaction/speculative-job.js.map +1 -0
  105. package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts +22 -0
  106. package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts.map +1 -0
  107. package/dist/core/extensions/builtin/compaction/speculative-summary.js +131 -0
  108. package/dist/core/extensions/builtin/compaction/speculative-summary.js.map +1 -0
  109. package/dist/core/extensions/builtin/compaction/speculative.d.ts +8 -13
  110. package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
  111. package/dist/core/extensions/builtin/compaction/speculative.js +7 -196
  112. package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
  113. package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts +24 -0
  114. package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts.map +1 -0
  115. package/dist/core/extensions/builtin/compaction/token-budget-reminder.js +25 -0
  116. package/dist/core/extensions/builtin/compaction/token-budget-reminder.js.map +1 -0
  117. package/dist/core/extensions/builtin/compaction/tool-admission.d.ts +27 -0
  118. package/dist/core/extensions/builtin/compaction/tool-admission.d.ts.map +1 -0
  119. package/dist/core/extensions/builtin/compaction/tool-admission.js +56 -0
  120. package/dist/core/extensions/builtin/compaction/tool-admission.js.map +1 -0
  121. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts.map +1 -1
  122. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js +8 -28
  123. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js.map +1 -1
  124. package/dist/core/extensions/builtin/permission-system/external-dir.d.ts.map +1 -1
  125. package/dist/core/extensions/builtin/permission-system/external-dir.js +15 -5
  126. package/dist/core/extensions/builtin/permission-system/external-dir.js.map +1 -1
  127. package/dist/core/extensions/builtin/permission-system/parsers.d.ts.map +1 -1
  128. package/dist/core/extensions/builtin/permission-system/parsers.js +16 -1
  129. package/dist/core/extensions/builtin/permission-system/parsers.js.map +1 -1
  130. package/dist/core/extensions/builtin/terminal/manager.d.ts +5 -0
  131. package/dist/core/extensions/builtin/terminal/manager.d.ts.map +1 -1
  132. package/dist/core/extensions/builtin/terminal/manager.js +31 -3
  133. package/dist/core/extensions/builtin/terminal/manager.js.map +1 -1
  134. package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts +8 -0
  135. package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts.map +1 -0
  136. package/dist/core/extensions/builtin/terminal/monitor-permission.js +16 -0
  137. package/dist/core/extensions/builtin/terminal/monitor-permission.js.map +1 -0
  138. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +13 -0
  139. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
  140. package/dist/core/extensions/builtin/terminal/monitor-registry.js +403 -3
  141. package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
  142. package/dist/core/extensions/builtin/terminal/runtime-session.d.ts +11 -0
  143. package/dist/core/extensions/builtin/terminal/runtime-session.d.ts.map +1 -1
  144. package/dist/core/extensions/builtin/terminal/runtime-session.js +18 -2
  145. package/dist/core/extensions/builtin/terminal/runtime-session.js.map +1 -1
  146. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts.map +1 -1
  147. package/dist/core/extensions/builtin/terminal/session-bundle.js +1 -0
  148. package/dist/core/extensions/builtin/terminal/session-bundle.js.map +1 -1
  149. package/dist/core/extensions/builtin/terminal/tools/kill-bash.d.ts.map +1 -1
  150. package/dist/core/extensions/builtin/terminal/tools/kill-bash.js +5 -2
  151. package/dist/core/extensions/builtin/terminal/tools/kill-bash.js.map +1 -1
  152. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts +4 -0
  153. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
  154. package/dist/core/extensions/builtin/terminal/tools/monitor.js +40 -2
  155. package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
  156. package/dist/core/model-resolver.js +2 -2
  157. package/dist/core/model-resolver.js.map +1 -1
  158. package/dist/core/sdk.d.ts.map +1 -1
  159. package/dist/core/sdk.js +5 -0
  160. package/dist/core/sdk.js.map +1 -1
  161. package/dist/core/session-manager.d.ts.map +1 -1
  162. package/dist/core/session-manager.js +10 -1
  163. package/dist/core/session-manager.js.map +1 -1
  164. package/dist/core/settings-manager.d.ts +15 -85
  165. package/dist/core/settings-manager.d.ts.map +1 -1
  166. package/dist/core/settings-manager.js +15 -17
  167. package/dist/core/settings-manager.js.map +1 -1
  168. package/dist/core/settings-public-types.d.ts +11 -0
  169. package/dist/core/settings-public-types.d.ts.map +1 -0
  170. package/dist/core/settings-public-types.js +2 -0
  171. package/dist/core/settings-public-types.js.map +1 -0
  172. package/dist/core/settings-shapes.d.ts +36 -0
  173. package/dist/core/settings-shapes.d.ts.map +1 -0
  174. package/dist/core/settings-shapes.js +2 -0
  175. package/dist/core/settings-shapes.js.map +1 -0
  176. package/dist/core/shared-host-policy.d.ts +6 -0
  177. package/dist/core/shared-host-policy.d.ts.map +1 -0
  178. package/dist/core/shared-host-policy.js +7 -0
  179. package/dist/core/shared-host-policy.js.map +1 -0
  180. package/dist/core/terminal-settings.d.ts +22 -0
  181. package/dist/core/terminal-settings.d.ts.map +1 -0
  182. package/dist/core/terminal-settings.js +2 -0
  183. package/dist/core/terminal-settings.js.map +1 -0
  184. package/dist/core/tools/bash.d.ts.map +1 -1
  185. package/dist/core/tools/bash.js +97 -15
  186. package/dist/core/tools/bash.js.map +1 -1
  187. package/dist/core/tools/index.d.ts +1 -0
  188. package/dist/core/tools/index.d.ts.map +1 -1
  189. package/dist/core/tools/index.js +8 -0
  190. package/dist/core/tools/index.js.map +1 -1
  191. package/dist/core/tools/output-accumulator.d.ts +1 -1
  192. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  193. package/dist/core/tools/output-accumulator.js +76 -34
  194. package/dist/core/tools/output-accumulator.js.map +1 -1
  195. package/dist/index.d.ts +1 -1
  196. package/dist/index.d.ts.map +1 -1
  197. package/dist/index.js +1 -1
  198. package/dist/index.js.map +1 -1
  199. package/dist/main.d.ts.map +1 -1
  200. package/dist/main.js +17 -5
  201. package/dist/main.js.map +1 -1
  202. package/dist/modes/index.d.ts +1 -1
  203. package/dist/modes/index.d.ts.map +1 -1
  204. package/dist/modes/index.js +1 -1
  205. package/dist/modes/index.js.map +1 -1
  206. package/dist/modes/interactive/components/footer.d.ts +7 -0
  207. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  208. package/dist/modes/interactive/components/footer.js +34 -8
  209. package/dist/modes/interactive/components/footer.js.map +1 -1
  210. package/dist/modes/interactive/grok/chrome.d.ts +2 -0
  211. package/dist/modes/interactive/grok/chrome.d.ts.map +1 -1
  212. package/dist/modes/interactive/grok/chrome.js.map +1 -1
  213. package/dist/modes/interactive/interactive-host-runtime.d.ts +132 -1
  214. package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
  215. package/dist/modes/interactive/interactive-host-runtime.js +633 -117
  216. package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
  217. package/dist/modes/interactive/interactive-mode.d.ts +7 -0
  218. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  219. package/dist/modes/interactive/interactive-mode.js +114 -2
  220. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  221. package/dist/modes/rpc/connection-handler.d.ts +8 -1
  222. package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
  223. package/dist/modes/rpc/connection-handler.js +158 -49
  224. package/dist/modes/rpc/connection-handler.js.map +1 -1
  225. package/dist/modes/rpc/host-ensure.d.ts +19 -0
  226. package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
  227. package/dist/modes/rpc/host-ensure.js +79 -15
  228. package/dist/modes/rpc/host-ensure.js.map +1 -1
  229. package/dist/modes/rpc/host-lifecycle.d.ts +29 -0
  230. package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
  231. package/dist/modes/rpc/host-lifecycle.js +96 -25
  232. package/dist/modes/rpc/host-lifecycle.js.map +1 -1
  233. package/dist/modes/rpc/host-watchdog.d.ts +2 -0
  234. package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
  235. package/dist/modes/rpc/host-watchdog.js +23 -12
  236. package/dist/modes/rpc/host-watchdog.js.map +1 -1
  237. package/dist/modes/rpc/multi-session-host.js +6 -4
  238. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  239. package/dist/modes/rpc/rpc-client.d.ts +20 -4
  240. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  241. package/dist/modes/rpc/rpc-client.js +76 -17
  242. package/dist/modes/rpc/rpc-client.js.map +1 -1
  243. package/dist/modes/rpc/rpc-input-validation.d.ts +1 -0
  244. package/dist/modes/rpc/rpc-input-validation.d.ts.map +1 -1
  245. package/dist/modes/rpc/rpc-input-validation.js +66 -0
  246. package/dist/modes/rpc/rpc-input-validation.js.map +1 -1
  247. package/dist/modes/rpc/rpc-types.d.ts +86 -1
  248. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  249. package/dist/modes/rpc/rpc-types.js.map +1 -1
  250. package/dist/modes/rpc/session-binding.d.ts +1 -0
  251. package/dist/modes/rpc/session-binding.d.ts.map +1 -1
  252. package/dist/modes/rpc/session-binding.js +21 -1
  253. package/dist/modes/rpc/session-binding.js.map +1 -1
  254. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  255. package/dist/modes/rpc/session-command-router.js +1 -0
  256. package/dist/modes/rpc/session-command-router.js.map +1 -1
  257. package/dist/modes/rpc/session-event-writer.d.ts +2 -0
  258. package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
  259. package/dist/modes/rpc/session-event-writer.js +54 -7
  260. package/dist/modes/rpc/session-event-writer.js.map +1 -1
  261. package/dist/modes/rpc/session-extension-ui-requests.d.ts +1 -0
  262. package/dist/modes/rpc/session-extension-ui-requests.d.ts.map +1 -1
  263. package/dist/modes/rpc/session-extension-ui-requests.js +5 -0
  264. package/dist/modes/rpc/session-extension-ui-requests.js.map +1 -1
  265. package/dist/modes/rpc/session-registry.d.ts +10 -2
  266. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  267. package/dist/modes/rpc/session-registry.js +58 -4
  268. package/dist/modes/rpc/session-registry.js.map +1 -1
  269. package/dist/modes/rpc/socket-event-fanout.d.ts +22 -0
  270. package/dist/modes/rpc/socket-event-fanout.d.ts.map +1 -0
  271. package/dist/modes/rpc/socket-event-fanout.js +84 -0
  272. package/dist/modes/rpc/socket-event-fanout.js.map +1 -0
  273. package/docs/rpc.md +22 -2
  274. package/docs/settings.md +34 -0
  275. package/docs/windows.md +2 -0
  276. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +62 -0
  277. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  278. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-indirect-eval.js +139 -90
  279. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +4 -4
  280. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
  281. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js +40 -20
  282. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
  283. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts +3 -3
  284. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts.map +1 -1
  285. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js.map +1 -1
  286. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts +1 -1
  287. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts.map +1 -1
  288. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js +28 -9
  289. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js.map +1 -1
  290. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  291. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
  292. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +67 -38
  293. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  294. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts +20 -0
  295. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts.map +1 -0
  296. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js +443 -0
  297. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js.map +1 -0
  298. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +2 -0
  299. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
  300. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +2 -0
  301. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
  302. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +1 -1
  303. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
  304. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
  305. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  306. package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
  307. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  308. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  309. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  310. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
  311. package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
  312. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  313. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  314. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  315. package/node_modules/@earendil-works/pi-ai/dist/providers/data/together.json +1 -1
  316. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai-coding-cn.json +1 -1
  317. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai.json +1 -1
  318. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  319. package/node_modules/@earendil-works/pi-pty/dist/screen-operations.d.ts +42 -0
  320. package/node_modules/@earendil-works/pi-pty/dist/screen-operations.d.ts.map +1 -0
  321. package/node_modules/@earendil-works/pi-pty/dist/screen-operations.js +34 -0
  322. package/node_modules/@earendil-works/pi-pty/dist/screen-operations.js.map +1 -0
  323. package/node_modules/@earendil-works/pi-pty/dist/screen-text.d.ts +8 -0
  324. package/node_modules/@earendil-works/pi-pty/dist/screen-text.d.ts.map +1 -0
  325. package/node_modules/@earendil-works/pi-pty/dist/screen-text.js +51 -0
  326. package/node_modules/@earendil-works/pi-pty/dist/screen-text.js.map +1 -0
  327. package/node_modules/@earendil-works/pi-pty/dist/screen.d.ts +45 -0
  328. package/node_modules/@earendil-works/pi-pty/dist/screen.d.ts.map +1 -1
  329. package/node_modules/@earendil-works/pi-pty/dist/screen.js +178 -55
  330. package/node_modules/@earendil-works/pi-pty/dist/screen.js.map +1 -1
  331. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  332. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  333. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +7 -0
  334. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
  335. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +5 -0
  336. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
  337. package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts +1 -1
  338. package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts.map +1 -1
  339. package/node_modules/@earendil-works/pi-tui/dist/mux.js +2 -2
  340. package/node_modules/@earendil-works/pi-tui/dist/mux.js.map +1 -1
  341. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +1 -0
  342. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
  343. package/node_modules/@earendil-works/pi-tui/dist/terminal.js +19 -0
  344. package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
  345. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  346. package/package.json +7 -7
@@ -14,10 +14,11 @@
14
14
  */
15
15
  import { randomUUID } from "node:crypto";
16
16
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
17
+ import { rm } from "node:fs/promises";
17
18
  import { basename, dirname } from "node:path";
18
19
  import { isDeepStrictEqual } from "node:util";
19
20
  import { ProviderRetryWatchdogAbortError, prepareAgentToolCall } from "@earendil-works/pi-agent-core";
20
- import { contentText, SERVER_FALLBACK_ABORTED_DIAGNOSTIC } from "@earendil-works/pi-ai";
21
+ import { contentText, measureCursorHistorySerializedBytes, SERVER_FALLBACK_ABORTED_DIAGNOSTIC, } from "@earendil-works/pi-ai";
21
22
  import { cleanupSessionResources, cursorOverflowCompactionSettings, isClassifierRefusal, isContextOverflow, isCursorPayloadResourceExhausted, isCursorQuotaResourceExhausted, isCursorZeroTokenResourceExhausted, isProviderStreamStallError, isProviderTimeoutError, isRecoverableLength, isRetryableAssistantError, modelsAreEqual, resetApiProviders, shouldRetryOverflowWithoutCompact, streamSimple, } from "@earendil-works/pi-ai/compat";
22
23
  import { extract429RetryAfterMs, parseRetryAfterMsMarker } from "@earendil-works/pi-ai/utils/retry-hint";
23
24
  import { retryBackoffDelayMs } from "@earendil-works/pi-ai/utils/retry-profile/backoff";
@@ -40,6 +41,8 @@ import { buildDynamicSystemPrompt } from "./dynamic-prompt/index.js";
40
41
  import { areExperimentalFeaturesEnabled } from "./experimental.js";
41
42
  import { exportSessionToHtml } from "./export-html/index.js";
42
43
  import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
44
+ import { ModelUsabilityBudgetError, projectModelUsabilityBudget, } from "./extensions/builtin/compaction/model-usability-budget.js";
45
+ import { resolveReserveTokens } from "./extensions/builtin/compaction/policy.js";
43
46
  import { CODEX_RESPONSES_API } from "./extensions/builtin/service-tier.js";
44
47
  import { deriveExtensionRegistrationId } from "./extensions/builtin/tool-search/engine/marker.js";
45
48
  import { getToolSearchService } from "./extensions/builtin/tool-search/service.js";
@@ -47,7 +50,7 @@ import { ExecuteToolError, ExtensionRunner, wrapRegisteredTools, } from "./exten
47
50
  import { emitSessionShutdownEvent } from "./extensions/runner.js";
48
51
  import { normalizeToolExposure, RUNTIME_EXTENSION_PATH } from "./extensions/types.js";
49
52
  import { shouldWarnHighReasoning } from "./high-reasoning-warning.js";
50
- import { filterContextExcludedMessages } from "./messages.js";
53
+ import { convertToLlm, filterContextExcludedMessages, } from "./messages.js";
51
54
  import { ModelRegistry } from "./model-registry.js";
52
55
  import { getModelNarrowingPatterns, resolveModelScope } from "./model-resolver.js";
53
56
  import { PROMPT_CACHE_SAFE_WAIT_ENV, resolvePromptCacheSafeWaitSeconds } from "./prompt-cache-budget.js";
@@ -70,9 +73,21 @@ import { getSupportedThinkingLevels, supportsMax, supportsXhigh } from "./thinki
70
73
  import { resetTimings, time } from "./timings.js";
71
74
  import { createLocalBashOperations } from "./tools/bash.js";
72
75
  import { composeFilesystemPolicies } from "./tools/filesystem-policy.js";
73
- import { createAllToolDefinitions } from "./tools/index.js";
76
+ import { createAllToolDefinitions, temporarilyDisabledToolNames } from "./tools/index.js";
74
77
  import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
75
78
  import { addUsageToTotals, createUsageTotals } from "./usage-totals.js";
79
+ /** Built-in shell tools routed exclusively through eval when experimental.bashEvalOnly is enabled. */
80
+ const EVAL_ONLY_SHELL_TOOL_NAMES = ["bash", "powershell"];
81
+ /** Workflow tool routed exclusively through eval when experimental.workflowEvalOnly is enabled. */
82
+ const EVAL_ONLY_WORKFLOW_TOOL_NAMES = ["workflow"];
83
+ /** Sample eval-cell call for an eval-only tool, using the argument name that tool actually takes. */
84
+ function evalHelperCall(name) {
85
+ if (EVAL_ONLY_SHELL_TOOL_NAMES.includes(name))
86
+ return `tool.${name}({ command: "..." })`;
87
+ if (EVAL_ONLY_WORKFLOW_TOOL_NAMES.includes(name))
88
+ return `tool.${name}({ action: "..." })`;
89
+ return `tool.${name}({ ... })`;
90
+ }
76
91
  const TURN_RETRY_SUPPRESSION_PREFIX = "senpi:no-turn-retry:";
77
92
  const DEFERRED_RETRY_QUEUE_OWNERS = new WeakSet();
78
93
  /** Format the user-attributed payload for one or more explicit skill invocations. */
@@ -310,6 +325,121 @@ function isSameOverflowSource(message, model, upstreamModelId) {
310
325
  const THINKING_LEVELS_WITH_MAX = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
311
326
  /** Caps explicit skill expansion so one prompt cannot consume unbounded context. */
312
327
  export const MAX_SKILL_EXPANSIONS_PER_PROMPT = 5;
328
+ /** Cursor ingest rejects large verbatim tool payloads. The bound is UTF-8 bytes. */
329
+ export const CURSOR_TOOL_RESULT_MAX_CHARS = 2000;
330
+ export const CURSOR_TOOL_RESULT_MAX_BYTES = 50_000;
331
+ const CURSOR_TRUNCATION_MARKER = "\n...[truncated]";
332
+ export function truncateToolResultBodies(messages, maxChars = CURSOR_TOOL_RESULT_MAX_CHARS, maxBytes = CURSOR_TOOL_RESULT_MAX_BYTES, convert = (candidate) => convertToLlm(candidate)) {
333
+ if (!Array.isArray(messages) || messages.length === 0)
334
+ return { messages, changed: false };
335
+ const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
336
+ const markerChars = [...segmenter.segment(CURSOR_TRUNCATION_MARKER)].length;
337
+ const result = messages.slice();
338
+ let changed = false;
339
+ // Apply the per-result character cap first, independent of the aggregate wire cap.
340
+ for (let messageIndex = result.length - 1; messageIndex >= 0; messageIndex--) {
341
+ const message = result[messageIndex];
342
+ if (message.role !== "toolResult" || !Array.isArray(message.content))
343
+ continue;
344
+ let content = message.content;
345
+ for (let partIndex = content.length - 1; partIndex >= 0; partIndex--) {
346
+ const part = content[partIndex];
347
+ if (part.type === "image" && typeof part.data === "string")
348
+ continue;
349
+ if (part.type !== "text" || typeof part.text !== "string")
350
+ continue;
351
+ const graphemes = [...segmenter.segment(part.text)].map((item) => item.segment);
352
+ if (graphemes.length <= maxChars)
353
+ continue;
354
+ const kept = graphemes.slice(0, Math.max(0, maxChars - markerChars)).join("");
355
+ const nextText = kept + CURSOR_TRUNCATION_MARKER;
356
+ content = content === message.content ? content.slice() : content;
357
+ content[partIndex] = { ...part, text: nextText };
358
+ result[messageIndex] = { ...message, content };
359
+ changed = true;
360
+ }
361
+ }
362
+ const measure = (candidate) => {
363
+ const converted = convert(candidate);
364
+ const activeUserMessageIndex = converted.at(-1)?.role === "user" ? converted.length - 1 : -1;
365
+ return measureCursorHistorySerializedBytes(converted, activeUserMessageIndex);
366
+ };
367
+ const fits = (candidate = result) => measure(candidate) <= maxBytes;
368
+ if (fits())
369
+ return { messages: changed ? result : messages, changed };
370
+ // Empty the oldest result bodies. Search the monotonic prefix of candidates
371
+ // rather than serializing once for every result (admission must stay bounded).
372
+ const emptyToolResult = (message) => {
373
+ if (message.role !== "toolResult" || !Array.isArray(message.content))
374
+ return message;
375
+ const emptiedContent = message.content.map((part) => part.type === "text" ? { ...part, text: "" } : part.type === "image" ? { ...part, data: "" } : part);
376
+ const content = emptiedContent.filter((part, index) => {
377
+ if (index === 0)
378
+ return true;
379
+ const previous = emptiedContent[index - 1];
380
+ const empty = part.type === "text" ? part.text === "" : part.type === "image" && part.data === "";
381
+ const previousEmpty = previous.type === "text" ? previous.text === "" : previous.type === "image" && previous.data === "";
382
+ return !empty || !previousEmpty;
383
+ });
384
+ return { ...message, content };
385
+ };
386
+ const toolResultIndexes = result.flatMap((message, index) => message.role === "toolResult" && Array.isArray(message.content) ? [index] : []);
387
+ const withEmptyPrefix = (count) => {
388
+ const candidate = result.slice();
389
+ for (let i = 0; i < count; i++)
390
+ candidate[toolResultIndexes[i]] = emptyToolResult(candidate[toolResultIndexes[i]]);
391
+ return candidate;
392
+ };
393
+ let low = 0;
394
+ let high = toolResultIndexes.length;
395
+ while (low < high) {
396
+ const middle = Math.floor((low + high) / 2);
397
+ if (fits(withEmptyPrefix(middle + 1)))
398
+ high = middle;
399
+ else
400
+ low = middle + 1;
401
+ }
402
+ const emptyCount = Math.min(low + 1, toolResultIndexes.length);
403
+ if (emptyCount > 0) {
404
+ result.splice(0, result.length, ...withEmptyPrefix(emptyCount));
405
+ changed = true;
406
+ }
407
+ if (fits())
408
+ return { messages: changed ? result : messages, changed };
409
+ // If metadata alone exceeds the cap, discard the oldest complete turns. This
410
+ // search is also monotonic and avoids quadratic whole-history reserialization.
411
+ const turnRanges = [];
412
+ const isConvertedUser = (message) => convert([message])[0]?.role === "user";
413
+ for (let index = 0; index < result.length; index++) {
414
+ if (!isConvertedUser(result[index]))
415
+ continue;
416
+ const nextUser = result.findIndex((message, nextIndex) => nextIndex > index && isConvertedUser(message));
417
+ turnRanges.push([index, nextUser < 0 ? result.length : nextUser]);
418
+ }
419
+ const withoutTurns = (count) => {
420
+ if (count === 0)
421
+ return result;
422
+ const start = turnRanges[0]?.[0] ?? 0;
423
+ const end = turnRanges[count - 1]?.[1] ?? start;
424
+ return [...result.slice(0, start), ...result.slice(end)];
425
+ };
426
+ low = 0;
427
+ high = turnRanges.length;
428
+ while (low < high) {
429
+ const middle = Math.floor((low + high) / 2);
430
+ if (fits(withoutTurns(middle + 1)))
431
+ high = middle;
432
+ else
433
+ low = middle + 1;
434
+ }
435
+ const turnCount = Math.min(low + 1, turnRanges.length);
436
+ if (turnCount > 0) {
437
+ const next = withoutTurns(turnCount);
438
+ result.splice(0, result.length, ...next);
439
+ changed = true;
440
+ }
441
+ return { messages: changed ? result : messages, changed };
442
+ }
313
443
  // ============================================================================
314
444
  // AgentSession Class
315
445
  // ============================================================================
@@ -395,6 +525,10 @@ export class AgentSession {
395
525
  this._pendingBashMessages = [];
396
526
  this._turnIndex = 0;
397
527
  this._baseToolDefinitions = new Map();
528
+ /** Policy tools withheld from the model, retained so disarming can restore direct access. */
529
+ this._withheldEvalOnlyToolNames = new Set();
530
+ /** Eval-only hint names this session published, so a disarm can withdraw exactly those. */
531
+ this._publishedEvalOnlyHintNames = new Set();
398
532
  this._extensionMode = "print";
399
533
  // Tool registry for extension getTools/setTools
400
534
  this._toolRegistry = new Map();
@@ -516,6 +650,8 @@ export class AgentSession {
516
650
  this._extensionRunnerRef = config.extensionRunnerRef;
517
651
  this._initialActiveToolNames = config.initialActiveToolNames;
518
652
  this._defaultToolNames = config.defaultToolNames ? new Set(config.defaultToolNames) : undefined;
653
+ this._evalOnlyToolNamesOverride = config.evalOnlyToolNames ? new Set(config.evalOnlyToolNames) : undefined;
654
+ this._evalOnlyToolNames = this._resolveEvalOnlyToolNames();
519
655
  this._allowedToolNames = config.allowedToolNames ? new Set(config.allowedToolNames) : undefined;
520
656
  this._excludedToolNames = config.excludedToolNames ? new Set(config.excludedToolNames) : undefined;
521
657
  this._baseToolsOverride = config.baseToolsOverride;
@@ -727,6 +863,14 @@ export class AgentSession {
727
863
  (this.agent.prepareNextTurn
728
864
  ? async (_turn, signal) => await this.agent.prepareNextTurn?.(signal)
729
865
  : undefined);
866
+ const previousTransformContext = this.agent.transformContext;
867
+ this.agent.transformContext = async (messages, signal) => {
868
+ const transformed = previousTransformContext ? await previousTransformContext(messages, signal) : messages;
869
+ if (this.model?.provider === "cursor" || this.model?.provider === "cursor-cli-oauth") {
870
+ return ((await truncateToolResultBodies(transformed, CURSOR_TOOL_RESULT_MAX_CHARS, CURSOR_TOOL_RESULT_MAX_BYTES, (candidate) => this.agent.convertToLlm(candidate))).messages ?? transformed);
871
+ }
872
+ return transformed;
873
+ };
730
874
  this.agent.prepareNextTurnWithContext = async (turn, signal) => {
731
875
  // Enforce compaction only when this prepare precedes an actual provider
732
876
  // admission: a tool continuation or queued steer/follow-up messages. A
@@ -735,7 +879,8 @@ export class AgentSession {
735
879
  const compactBeforeNextAdmission = async () => {
736
880
  const provider = this.model?.provider;
737
881
  // Cursor rebuilds the full conversation each hop. Compacting here
738
- // mutates rootPrompt mid-run and poisons conversationId.
882
+ // mutates rootPrompt mid-run and poisons conversationId (#984).
883
+ // Still truncate verbatim toolResult bodies so the skip cannot send MB-scale payloads (#1043).
739
884
  if (provider === "cursor" || provider === "cursor-cli-oauth") {
740
885
  return false;
741
886
  }
@@ -816,6 +961,19 @@ export class AgentSession {
816
961
  // Event Subscription
817
962
  // =========================================================================
818
963
  /** Emit an event to all listeners */
964
+ _emitEntryAppended(entryId) {
965
+ if (this._extensionMode !== "rpc")
966
+ return;
967
+ const entry = this.sessionManager.getEntry(entryId);
968
+ if (entry)
969
+ this._emit({ type: "entry_appended", entry });
970
+ }
971
+ /** Append a transport-provided entry and publish it on the RPC event stream. */
972
+ appendSessionEntry(entry) {
973
+ this.sessionManager.appendEntry(entry);
974
+ this.agent.state.messages = this.sessionManager.buildSessionContext().messages;
975
+ this._emitEntryAppended(entry.id);
976
+ }
819
977
  _emit(event) {
820
978
  this._logSessionEvent(event);
821
979
  for (const l of this._eventListeners) {
@@ -976,6 +1134,7 @@ export class AgentSession {
976
1134
  _invalidateCompactionForModelSelection() {
977
1135
  this.abortCompaction();
978
1136
  this.abortBranchSummary();
1137
+ this._delegatedCompactionKey = undefined;
979
1138
  this._incrementMessageRevision();
980
1139
  }
981
1140
  _getIdleWaitPromise() {
@@ -1367,7 +1526,7 @@ export class AgentSession {
1367
1526
  event.message.role === "assistant" ||
1368
1527
  event.message.role === "toolResult") {
1369
1528
  // Regular LLM message - persist as SessionMessageEntry
1370
- this.sessionManager.appendMessage(event.message);
1529
+ this._emitEntryAppended(this.sessionManager.appendMessage(event.message));
1371
1530
  this._incrementMessageRevision();
1372
1531
  }
1373
1532
  // Other message types (bashExecution, compactionSummary, branchSummary) are persisted elsewhere
@@ -2001,6 +2160,9 @@ export class AgentSession {
2001
2160
  async executeTool(toolName, params, options) {
2002
2161
  let activeTools = this.getActiveToolNames();
2003
2162
  let tool = this.agent.state.tools.find((candidate) => candidate.name === toolName);
2163
+ if (!tool && this._isEvalOnlyPolicyArmed() && this._evalOnlyToolNames?.has(toolName)) {
2164
+ tool = this._toolRegistry.get(toolName);
2165
+ }
2004
2166
  if (!tool &&
2005
2167
  options?.activateInactiveTool === true &&
2006
2168
  this._toolDefinitions.has(toolName) &&
@@ -2075,6 +2237,45 @@ export class AgentSession {
2075
2237
  return false;
2076
2238
  return this._lazyToolActivators.some((activate) => activate(toolName));
2077
2239
  }
2240
+ _isEvalOnlyPolicyArmed() {
2241
+ return this._evalOnlyToolNames !== undefined && this._toolRegistry.has("eval");
2242
+ }
2243
+ /**
2244
+ * Resolve the eval-only policy from settings so a reload picks up a flag change.
2245
+ * An explicitly configured set stays authoritative for SDK embedders and tests.
2246
+ */
2247
+ _resolveEvalOnlyToolNames() {
2248
+ if (this._evalOnlyToolNamesOverride !== undefined)
2249
+ return this._evalOnlyToolNamesOverride;
2250
+ const names = [
2251
+ ...(this.settingsManager.getExperimentalBashEvalOnly() ? EVAL_ONLY_SHELL_TOOL_NAMES : []),
2252
+ ...(this.settingsManager.getExperimentalWorkflowEvalOnly() ? EVAL_ONLY_WORKFLOW_TOOL_NAMES : []),
2253
+ ];
2254
+ return names.length > 0 ? new Set(names) : undefined;
2255
+ }
2256
+ /**
2257
+ * Publish per-tool eval-only redirect hints so a direct call names its own eval helper.
2258
+ *
2259
+ * Hints published here are tracked so a disarm - or a shrinking armed set, e.g. bash+workflow
2260
+ * down to workflow only - withdraws the stale ones. Only tracked names are ever deleted, so
2261
+ * hints owned by other publishers survive.
2262
+ */
2263
+ _publishEvalOnlyToolHints() {
2264
+ const armed = this._isEvalOnlyPolicyArmed() ? (this._evalOnlyToolNames ?? new Set()) : undefined;
2265
+ for (const name of this._publishedEvalOnlyHintNames) {
2266
+ if (armed?.has(name))
2267
+ continue;
2268
+ delete this.agent.removedToolHints[name];
2269
+ }
2270
+ this._publishedEvalOnlyHintNames.clear();
2271
+ if (!armed)
2272
+ return;
2273
+ for (const name of armed) {
2274
+ this.agent.removedToolHints[name] =
2275
+ `Run ${name} inside an eval cell via ${evalHelperCall(name)}; hooks and permissions still apply.`;
2276
+ this._publishedEvalOnlyHintNames.add(name);
2277
+ }
2278
+ }
2078
2279
  /**
2079
2280
  * Set active tools by name.
2080
2281
  * Only tools in the registry can be enabled. Unknown tool names are ignored.
@@ -2102,7 +2303,23 @@ export class AgentSession {
2102
2303
  setActiveToolsByName(toolNames) {
2103
2304
  const tools = [];
2104
2305
  const validToolNames = [];
2105
- for (const name of toolNames) {
2306
+ const policyArmed = this._isEvalOnlyPolicyArmed();
2307
+ this._publishEvalOnlyToolHints();
2308
+ const policyNames = this._evalOnlyToolNames;
2309
+ let filteredToolNames = toolNames;
2310
+ if (policyArmed && policyNames) {
2311
+ for (const name of toolNames) {
2312
+ if (policyNames.has(name))
2313
+ this._withheldEvalOnlyToolNames.add(name);
2314
+ }
2315
+ filteredToolNames = toolNames.filter((name) => !policyNames.has(name));
2316
+ }
2317
+ else {
2318
+ this._withheldEvalOnlyToolNames.clear();
2319
+ }
2320
+ // Remember the unfiltered request so a later disarm can restore withheld shell tools.
2321
+ this._requestedActiveToolNames = [...new Set([...toolNames, ...this._withheldEvalOnlyToolNames])];
2322
+ for (const name of filteredToolNames) {
2106
2323
  const tool = this._toolRegistry.get(name);
2107
2324
  if (tool) {
2108
2325
  tools.push(tool);
@@ -2116,8 +2333,14 @@ export class AgentSession {
2116
2333
  this._baseSystemPrompt = this._rebuildSystemPrompt(validToolNames);
2117
2334
  this.agent.state.systemPrompt = this._systemPromptOverride ?? this._baseSystemPrompt;
2118
2335
  if (activeToolNamesChanged) {
2119
- this.abortCompaction();
2120
- this._incrementMessageRevision();
2336
+ // A tool change emitted while extensions are still binding belongs to the
2337
+ // binding itself, not to a mid-session change. The session was already
2338
+ // committed to the client, so cancelling or invalidating work it started
2339
+ // against that session would be spurious.
2340
+ if (this._extensionBindingPromptReadiness === undefined) {
2341
+ this.abortCompaction();
2342
+ this._incrementMessageRevision();
2343
+ }
2121
2344
  }
2122
2345
  }
2123
2346
  /** Whether compaction or branch summarization is currently running */
@@ -2259,9 +2482,26 @@ export class AgentSession {
2259
2482
  appendSystemPrompt: loaderAppendSystemPrompt.length > 0 ? loaderAppendSystemPrompt.join("\n\n") : undefined,
2260
2483
  };
2261
2484
  const basePrompt = loaderSystemPrompt ?? buildDynamicSystemPrompt(this._baseSystemPromptOptions);
2262
- return loaderAppendSystemPrompt.length > 0
2263
- ? `${basePrompt}\n\n${loaderAppendSystemPrompt.join("\n\n")}`
2264
- : basePrompt;
2485
+ const prompt = loaderAppendSystemPrompt.length > 0 ? `${basePrompt}\n\n${loaderAppendSystemPrompt.join("\n\n")}` : basePrompt;
2486
+ if (!this._isEvalOnlyPolicyArmed())
2487
+ return prompt;
2488
+ const armed = this._evalOnlyToolNames ?? new Set();
2489
+ const sentences = [];
2490
+ const shellHelpers = EVAL_ONLY_SHELL_TOOL_NAMES.filter((name) => armed.has(name)).map(evalHelperCall);
2491
+ if (shellHelpers.length > 0) {
2492
+ sentences.push(`Shell commands run ONLY inside eval cells via ${shellHelpers.join(" or ")}; hooks and permissions still apply.`);
2493
+ }
2494
+ if (armed.has("workflow")) {
2495
+ sentences.push(`The workflow tool runs ONLY inside eval cells via ${evalHelperCall("workflow")}; hooks and permissions still apply.`);
2496
+ }
2497
+ // SDK embedders can arm names outside the built-in groups; they still need eval guidance.
2498
+ const otherHelpers = [...armed]
2499
+ .filter((name) => !EVAL_ONLY_SHELL_TOOL_NAMES.includes(name) && name !== "workflow")
2500
+ .map(evalHelperCall);
2501
+ if (otherHelpers.length > 0) {
2502
+ sentences.push(`These tools run ONLY inside eval cells via ${otherHelpers.join(" or ")}; hooks and permissions still apply.`);
2503
+ }
2504
+ return sentences.length > 0 ? `${prompt}\n\n${sentences.join("\n\n")}` : prompt;
2265
2505
  }
2266
2506
  /**
2267
2507
  * Send a prompt to the agent.
@@ -3053,7 +3293,7 @@ export class AgentSession {
3053
3293
  }
3054
3294
  _appendCustomMessage(appMessage) {
3055
3295
  this.agent.state.messages.push(appMessage);
3056
- this.sessionManager.appendCustomMessageEntry(appMessage.customType, appMessage.content, appMessage.display, appMessage.details);
3296
+ this._emitEntryAppended(this.sessionManager.appendCustomMessageEntry(appMessage.customType, appMessage.content, appMessage.display, appMessage.details));
3057
3297
  this._incrementMessageRevision();
3058
3298
  this._emit({ type: "message_start", message: appMessage });
3059
3299
  this._emit({ type: "message_end", message: appMessage });
@@ -3298,6 +3538,43 @@ export class AgentSession {
3298
3538
  async setModel(model) {
3299
3539
  return this._setModel(model, true);
3300
3540
  }
3541
+ assertModelUsable(model = this.model, liveContextTokens = 0) {
3542
+ if (!model || model.contextWindow <= 0)
3543
+ return;
3544
+ const projection = projectModelUsabilityBudget({
3545
+ model,
3546
+ systemPrompt: this.agent.state.systemPrompt,
3547
+ tools: this.agent.state.tools,
3548
+ liveContextTokens,
3549
+ compaction: this.settingsManager.getCompactionSettings(),
3550
+ });
3551
+ if (!projection.usable)
3552
+ throw new ModelUsabilityBudgetError(projection);
3553
+ }
3554
+ _getDownswitchLiveContextTokens(model) {
3555
+ const currentModel = this.model;
3556
+ if (!currentModel)
3557
+ return 0;
3558
+ const compaction = this.settingsManager.getCompactionSettings();
3559
+ const currentBudget = projectModelUsabilityBudget({
3560
+ model: currentModel,
3561
+ systemPrompt: this.agent.state.systemPrompt,
3562
+ tools: this.agent.state.tools,
3563
+ compaction,
3564
+ });
3565
+ const targetBudget = projectModelUsabilityBudget({
3566
+ model,
3567
+ systemPrompt: this.agent.state.systemPrompt,
3568
+ tools: this.agent.state.tools,
3569
+ compaction,
3570
+ });
3571
+ const currentUsableContext = currentBudget.contextWindow - currentBudget.requiredTokens;
3572
+ const targetUsableContext = targetBudget.contextWindow - targetBudget.requiredTokens;
3573
+ if (targetUsableContext >= currentUsableContext)
3574
+ return 0;
3575
+ const fixedPrefixTokens = currentBudget.systemPromptTokens + currentBudget.activeToolSchemaTokens;
3576
+ return Math.max(0, (this.getContextUsage()?.tokens ?? 0) - fixedPrefixTokens);
3577
+ }
3301
3578
  /**
3302
3579
  * Set the model for this session without changing the global model defaults.
3303
3580
  * The selection is still persisted in this session's history.
@@ -3306,6 +3583,7 @@ export class AgentSession {
3306
3583
  return this._setModel(model, false);
3307
3584
  }
3308
3585
  async _setModel(model, updateGlobalDefaults) {
3586
+ this.assertModelUsable(model, this._getDownswitchLiveContextTokens(model));
3309
3587
  if (!(await this._modelRuntime.checkAuth(model.provider))) {
3310
3588
  throw new Error(`No API key for ${model.provider}/${model.id}`);
3311
3589
  }
@@ -3336,10 +3614,14 @@ export class AgentSession {
3336
3614
  }
3337
3615
  async _switchActiveModel(model, opts) {
3338
3616
  const previousModel = this.model;
3339
- if (opts.invalidateCompaction && this._modelSelectionChangesContext(previousModel, model)) {
3617
+ if (opts.invalidateCompaction &&
3618
+ (this._modelSelectionChangesContext(previousModel, model) ||
3619
+ previousModel?.provider !== model.provider ||
3620
+ previousModel?.id !== model.id)) {
3340
3621
  this._invalidateCompactionForModelSelection();
3341
3622
  }
3342
3623
  const thinking = this._getThinkingForModelSwitch(model, opts.ephemeralThinkingLevel);
3624
+ const liveContextTokens = this._getDownswitchLiveContextTokens(model);
3343
3625
  this.agent.state.model = model;
3344
3626
  this.agent.abortServerSideFallback =
3345
3627
  this.settingsManager.getAbortServerSideFallback() && this._retryFallback.hasConfiguredChain();
@@ -3371,7 +3653,20 @@ export class AgentSession {
3371
3653
  this._emitServiceTierChangeIfNeeded(previousTier, previousFastMode);
3372
3654
  if (!opts.emitModelSelect)
3373
3655
  return undefined;
3374
- return await this._emitModelSelect(model, previousModel, opts.modelSelectSource);
3656
+ const previousSystemPrompt = this.agent.state.systemPrompt;
3657
+ try {
3658
+ const systemPromptChange = await this._emitModelSelect(model, previousModel, opts.modelSelectSource);
3659
+ this.assertModelUsable(model, liveContextTokens);
3660
+ return systemPromptChange;
3661
+ }
3662
+ catch (error) {
3663
+ if (previousModel)
3664
+ this.agent.state.model = previousModel;
3665
+ else
3666
+ delete this.agent.state.model;
3667
+ this.agent.state.systemPrompt = previousSystemPrompt;
3668
+ throw error;
3669
+ }
3375
3670
  }
3376
3671
  _applyEphemeralThinkingLevel(level) {
3377
3672
  const previousLevel = this.agent.state.thinkingLevel;
@@ -3408,7 +3703,11 @@ export class AgentSession {
3408
3703
  nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
3409
3704
  }
3410
3705
  const next = favoriteModels[nextIndex];
3411
- const invalidatesCompaction = this._modelSelectionChangesContext(currentModel, next.model);
3706
+ const liveContextTokens = this._getDownswitchLiveContextTokens(next.model);
3707
+ this.assertModelUsable(next.model, liveContextTokens);
3708
+ const invalidatesCompaction = this._modelSelectionChangesContext(currentModel, next.model) ||
3709
+ currentModel?.provider !== next.model.provider ||
3710
+ currentModel?.id !== next.model.id;
3412
3711
  if (invalidatesCompaction) {
3413
3712
  this._invalidateCompactionForModelSelection();
3414
3713
  }
@@ -3431,16 +3730,28 @@ export class AgentSession {
3431
3730
  source: "cycle",
3432
3731
  });
3433
3732
  this._emitServiceTierChangeIfNeeded(previousTier, previousFastMode);
3434
- const systemPromptChange = await this._emitModelSelect(next.model, currentModel, "cycle");
3435
- const cycleResult = {
3436
- model: next.model,
3437
- thinkingLevel: this.thinkingLevel,
3438
- isScoped: true,
3439
- };
3440
- if (systemPromptChange) {
3441
- cycleResult.systemPromptChange = systemPromptChange;
3733
+ const previousSystemPrompt = this.agent.state.systemPrompt;
3734
+ try {
3735
+ const systemPromptChange = await this._emitModelSelect(next.model, currentModel, "cycle");
3736
+ this.assertModelUsable(next.model, liveContextTokens);
3737
+ const cycleResult = {
3738
+ model: next.model,
3739
+ thinkingLevel: this.thinkingLevel,
3740
+ isScoped: true,
3741
+ };
3742
+ if (systemPromptChange) {
3743
+ cycleResult.systemPromptChange = systemPromptChange;
3744
+ }
3745
+ return cycleResult;
3746
+ }
3747
+ catch (error) {
3748
+ if (currentModel)
3749
+ this.agent.state.model = currentModel;
3750
+ else
3751
+ delete this.agent.state.model;
3752
+ this.agent.state.systemPrompt = previousSystemPrompt;
3753
+ throw error;
3442
3754
  }
3443
- return cycleResult;
3444
3755
  }
3445
3756
  // =========================================================================
3446
3757
  // Thinking Level Management
@@ -3610,6 +3921,7 @@ export class AgentSession {
3610
3921
  setSteeringMode(mode) {
3611
3922
  this.agent.steeringMode = mode;
3612
3923
  this.settingsManager.setSteeringMode(mode);
3924
+ this._emitSessionSettingsChanged();
3613
3925
  }
3614
3926
  /**
3615
3927
  * Set follow-up message mode.
@@ -3618,6 +3930,7 @@ export class AgentSession {
3618
3930
  setFollowUpMode(mode) {
3619
3931
  this.agent.followUpMode = mode;
3620
3932
  this.settingsManager.setFollowUpMode(mode);
3933
+ this._emitSessionSettingsChanged();
3621
3934
  }
3622
3935
  // =========================================================================
3623
3936
  // Compaction
@@ -4100,7 +4413,7 @@ export class AgentSession {
4100
4413
  if (sourceChanged) {
4101
4414
  return await this._rejectCompaction(request, requestId, operationId, "stale-revision", false);
4102
4415
  }
4103
- if (this._wouldCompactionOverflow(pathEntries, compactionResult, fromExtension, model)) {
4416
+ if (await this._wouldCompactionOverflow(pathEntries, compactionResult, fromExtension, model)) {
4104
4417
  return await this._rejectCompaction(request, requestId, operationId, "would-overflow", false);
4105
4418
  }
4106
4419
  const compactionEntryId = this.sessionManager.appendCompaction(compactionResult.summary, compactionResult.firstKeptEntryId, compactionResult.tokensBefore, compactionResult.details, fromExtension, compactionResult.usage);
@@ -4141,6 +4454,7 @@ export class AgentSession {
4141
4454
  })) {
4142
4455
  throw new CompactionCancelledError();
4143
4456
  }
4457
+ this._delegatedCompactionKey = undefined;
4144
4458
  if (request.owner === "compaction" && this._compactionAbortController === request.controller) {
4145
4459
  this._compactionAbortController = undefined;
4146
4460
  }
@@ -4191,7 +4505,7 @@ export class AgentSession {
4191
4505
  finishCompactionWork();
4192
4506
  }
4193
4507
  }
4194
- _wouldCompactionOverflow(pathEntries, compactionResult, fromExtension, model) {
4508
+ async _wouldCompactionOverflow(pathEntries, compactionResult, fromExtension, model) {
4195
4509
  const currentLeaf = pathEntries[pathEntries.length - 1];
4196
4510
  if (!currentLeaf)
4197
4511
  return false;
@@ -4206,10 +4520,18 @@ export class AgentSession {
4206
4520
  details: compactionResult.details,
4207
4521
  fromHook: fromExtension,
4208
4522
  };
4209
- const simulatedMessages = buildSessionContext([...pathEntries, simulatedCompactionEntry], simulatedCompactionEntry.id).messages;
4523
+ let simulatedMessages = buildSessionContext([...pathEntries, simulatedCompactionEntry], simulatedCompactionEntry.id).messages;
4524
+ // Size the same retained context that will be admitted to Cursor. Persisted JSONL
4525
+ // remains verbatim, but the in-memory request representation is bounded first.
4526
+ if (model.provider === "cursor" || model.provider === "cursor-cli-oauth") {
4527
+ simulatedMessages = truncateToolResultBodies(simulatedMessages).messages ?? simulatedMessages;
4528
+ }
4210
4529
  const contextTokens = estimateMessagesTokens(filterContextExcludedMessages(simulatedMessages));
4211
4530
  const settings = this.settingsManager.getCompactionSettings();
4212
- return contextTokens > model.contextWindow - settings.reserveTokens;
4531
+ const reserveTokens = settings.reserveScalingEnabled === false
4532
+ ? settings.reserveTokens
4533
+ : resolveReserveTokens(model.contextWindow, settings.reserveTokens);
4534
+ return contextTokens > model.contextWindow - reserveTokens;
4213
4535
  }
4214
4536
  /**
4215
4537
  * Replaces agent state with the canonical session context while keeping exact
@@ -4237,6 +4559,9 @@ export class AgentSession {
4237
4559
  // also emitted with accepted:false so the compaction extension's circuit-breaker
4238
4560
  // bookkeeping stops being dead code; other builtin session_compact handlers guard
4239
4561
  // on event.accepted.
4562
+ if (rejectionCause === "external-owner" && this.model) {
4563
+ this._delegatedCompactionKey = { provider: this.model.provider, id: this.model.id };
4564
+ }
4240
4565
  const trimmedExtensionReason = extensionReason?.trim();
4241
4566
  const detailedMessage = trimmedExtensionReason
4242
4567
  ? `Compaction rejected: ${trimmedExtensionReason}`
@@ -4370,6 +4695,9 @@ export class AgentSession {
4370
4695
  if (!model)
4371
4696
  return;
4372
4697
  const settings = this.settingsManager.getCompactionSettings();
4698
+ const reserveTokens = settings.reserveScalingEnabled === false
4699
+ ? settings.reserveTokens
4700
+ : resolveReserveTokens(model.contextWindow, settings.reserveTokens);
4373
4701
  const isOversized = () => {
4374
4702
  const providerMessages = filterContextExcludedMessages([...this.agent.state.messages, ...messages]);
4375
4703
  const estimate = estimateContextTokens(providerMessages);
@@ -4380,7 +4708,7 @@ export class AgentSession {
4380
4708
  const contextTokens = usageMessage?.role === "assistant" && this._isAssistantFromBeforeLatestCompaction(usageMessage)
4381
4709
  ? estimateMessagesTokens(providerMessages)
4382
4710
  : estimate.tokens;
4383
- return contextTokens > model.contextWindow - settings.reserveTokens;
4711
+ return contextTokens > model.contextWindow - reserveTokens;
4384
4712
  };
4385
4713
  if (!settings.enabled || !isOversized())
4386
4714
  return;
@@ -4573,15 +4901,15 @@ export class AgentSession {
4573
4901
  return claimant !== undefined && !claimant.signal.aborted;
4574
4902
  }
4575
4903
  _isCompactionDelegated() {
4576
- const state = this._compactionLifecycle.state;
4577
4904
  const model = this.model;
4578
- return (state.status === "failed" &&
4579
- state.rejectionCause === "external-owner" &&
4580
- state.model !== undefined &&
4905
+ return (this._delegatedCompactionKey !== undefined &&
4581
4906
  model !== undefined &&
4582
- state.model.provider === model.provider);
4907
+ this._delegatedCompactionKey.provider === model.provider &&
4908
+ this._delegatedCompactionKey.id === model.id);
4583
4909
  }
4584
4910
  async _runPrePromptCompaction(lastAssistantMessage, skipAbortedCheck, reason = "pre_prompt", willRetry = false, allowSummaryOnly = false) {
4911
+ if (this._isCompactionDelegated())
4912
+ return false;
4585
4913
  const controller = new AbortController();
4586
4914
  const requestId = randomUUID();
4587
4915
  this._claimCompactionController(controller, "compaction");
@@ -4754,6 +5082,8 @@ export class AgentSession {
4754
5082
  * @returns Whether the post-run loop should call `agent.continue()`
4755
5083
  */
4756
5084
  async _runAutoCompaction(reason, willRetry) {
5085
+ if (this._isCompactionDelegated())
5086
+ return false;
4757
5087
  const finishCompactionWork = this._sessionWorkBarrier.begin();
4758
5088
  const agentMessagesAtStart = this.agent.state.messages.slice();
4759
5089
  const autoCompactionController = new AbortController();
@@ -4887,6 +5217,15 @@ export class AgentSession {
4887
5217
  */
4888
5218
  setAutoCompactionEnabled(enabled) {
4889
5219
  this.settingsManager.setCompactionEnabled(enabled);
5220
+ this._emitSessionSettingsChanged();
5221
+ }
5222
+ _emitSessionSettingsChanged() {
5223
+ this._emit({
5224
+ type: "session_settings_changed",
5225
+ steeringMode: this.steeringMode,
5226
+ followUpMode: this.followUpMode,
5227
+ autoCompactionEnabled: this.autoCompactionEnabled,
5228
+ });
4890
5229
  }
4891
5230
  /** Whether auto-compaction is enabled */
4892
5231
  get autoCompactionEnabled() {
@@ -4984,6 +5323,7 @@ export class AgentSession {
4984
5323
  : undefined;
4985
5324
  }
4986
5325
  _refreshCurrentModelFromRegistry() {
5326
+ this._delegatedCompactionKey = undefined;
4987
5327
  const currentModel = this.model;
4988
5328
  if (!currentModel) {
4989
5329
  return;
@@ -5308,8 +5648,11 @@ export class AgentSession {
5308
5648
  }),
5309
5649
  })),
5310
5650
  ].filter((tool) => isAllowedTool(tool.definition.name));
5651
+ // Withheld tools stay in _baseToolDefinitions (and therefore in _toolRegistry, which
5652
+ // getRegisteredTool serves to the Cursor exec bridge) but are dropped from the model-facing
5653
+ // definitions so they never reach the prompt. See temporarilyDisabledToolNames.
5311
5654
  const definitionRegistry = new Map(Array.from(this._baseToolDefinitions.entries())
5312
- .filter(([name]) => isAllowedTool(name))
5655
+ .filter(([name]) => isAllowedTool(name) && !temporarilyDisabledToolNames.has(name))
5313
5656
  .map(([name, definition]) => [
5314
5657
  name,
5315
5658
  {
@@ -5351,13 +5694,22 @@ export class AgentSession {
5351
5694
  toolRegistry.set(tool.name, tool);
5352
5695
  }
5353
5696
  this._toolRegistry = toolRegistry;
5697
+ this._publishEvalOnlyToolHints();
5354
5698
  const isDirectlyExposed = (name) => {
5355
5699
  const entry = this._toolDefinitions.get(name);
5356
5700
  return entry !== undefined && normalizeToolExposure(entry.definition).exposure === "direct";
5357
5701
  };
5358
- const nextActiveToolNames = (options?.activeToolNames ? [...options.activeToolNames] : [...previousActiveToolNames]).filter((name) => {
5702
+ // A withheld tool is dropped from the DEFAULT selection only. An explicit activeToolNames
5703
+ // request names the tool deliberately, and callers that do so (tests, SDK embedders,
5704
+ // filesystem-policy wiring) still expect it to activate.
5705
+ const hasExplicitActiveToolNames = options?.activeToolNames !== undefined;
5706
+ const nextActiveToolNames = (options?.activeToolNames
5707
+ ? [...options.activeToolNames]
5708
+ : [...(this._requestedActiveToolNames ?? previousActiveToolNames)]).filter((name) => {
5359
5709
  if (!isAllowedTool(name))
5360
5710
  return false;
5711
+ if (!hasExplicitActiveToolNames && temporarilyDisabledToolNames.has(name))
5712
+ return false;
5361
5713
  const previousRegistrationIds = options?.previousActiveToolRegistrationIds;
5362
5714
  if (!previousRegistrationIds)
5363
5715
  return true;
@@ -5388,6 +5740,7 @@ export class AgentSession {
5388
5740
  this.setActiveToolsByName([...new Set(nextActiveToolNames)]);
5389
5741
  }
5390
5742
  _buildRuntime(options) {
5743
+ this._delegatedCompactionKey = undefined;
5391
5744
  const autoResizeImages = this.settingsManager.getImageAutoResize();
5392
5745
  const shellCommandPrefix = this.settingsManager.getShellCommandPrefix();
5393
5746
  const shellPath = this.settingsManager.getShellPath();
@@ -5439,7 +5792,9 @@ export class AgentSession {
5439
5792
  const oldExtensionIdentities = oldExtensionRunner.getExtensionIdentities();
5440
5793
  const previousFlagValues = oldExtensionRunner.getFlagValues();
5441
5794
  const previousActiveToolRegistrationIds = new Map();
5442
- for (const name of this.getActiveToolNames()) {
5795
+ // Cover withheld eval-only tools too: the rebuild drops any seeded name missing from this
5796
+ // map, which would strand shell tools when the policy disarms during this reload.
5797
+ for (const name of this._requestedActiveToolNames ?? this.getActiveToolNames()) {
5443
5798
  const entry = this._toolDefinitions.get(name);
5444
5799
  if (entry)
5445
5800
  previousActiveToolRegistrationIds.set(name, deriveExtensionRegistrationId(entry.sourceInfo, name));
@@ -5450,6 +5805,12 @@ export class AgentSession {
5450
5805
  });
5451
5806
  time("shutdown", "reload");
5452
5807
  await this.settingsManager.reload();
5808
+ this._delegatedCompactionKey = undefined;
5809
+ // Capture the unfiltered request BEFORE the rebuild reassigns it, so a policy that
5810
+ // disarms during this reload can still restore its withheld shell tools.
5811
+ const requestedActiveToolNamesBeforeRebuild = [...(this._requestedActiveToolNames ?? this.getActiveToolNames())];
5812
+ // A flag change must take effect on reload, matching documented settings hot-reload behavior.
5813
+ this._evalOnlyToolNames = this._resolveEvalOnlyToolNames();
5453
5814
  this.syncQueueModesFromSettings();
5454
5815
  resetApiProviders();
5455
5816
  time("settings", "reload");
@@ -5474,7 +5835,7 @@ export class AgentSession {
5474
5835
  time("resources", "reload");
5475
5836
  try {
5476
5837
  this._buildRuntime({
5477
- activeToolNames: this.getActiveToolNames(),
5838
+ activeToolNames: requestedActiveToolNamesBeforeRebuild,
5478
5839
  flagValues: previousFlagValues,
5479
5840
  includeAllExtensionTools: true,
5480
5841
  previousActiveToolRegistrationIds,
@@ -6179,12 +6540,13 @@ export class AgentSession {
6179
6540
  try {
6180
6541
  const result = await executeBashWithOperations(resolvedCommand, this.sessionManager.getCwd(), options?.operations ?? createLocalBashOperations({ shellPath }), {
6181
6542
  onChunk: (delta) => {
6182
- onChunk?.(delta);
6543
+ const callbackResult = onChunk?.(delta);
6183
6544
  this._emit({
6184
6545
  type: "bash_execution_update",
6185
6546
  id: options?.id,
6186
6547
  delta,
6187
6548
  });
6549
+ return callbackResult;
6188
6550
  },
6189
6551
  signal: abortController.signal,
6190
6552
  });
@@ -6232,6 +6594,10 @@ export class AgentSession {
6232
6594
  abortController.abort();
6233
6595
  }
6234
6596
  }
6597
+ /** Remove a host-owned full-output spill after a remote observer failed. */
6598
+ async cleanupBashOutput(path) {
6599
+ await rm(path, { force: true });
6600
+ }
6235
6601
  /** Whether a bash command is currently running */
6236
6602
  get isBashRunning() {
6237
6603
  return this._bashAbortControllers.size > 0;
@@ -6433,6 +6799,7 @@ export class AgentSession {
6433
6799
  }
6434
6800
  // Update agent state (preserving exact messages still awaiting persistence)
6435
6801
  this._restoreAgentMessagesFromSession();
6802
+ this._delegatedCompactionKey = undefined;
6436
6803
  this._incrementMessageRevision();
6437
6804
  // Emit session_tree event
6438
6805
  await this._extensionRunner.emit({