@code-yeongyu/senpi 2026.9.5 → 2026.9.7-2

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 (307) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/dist/core/agent-session.d.ts +60 -5
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +262 -78
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/compaction/compaction.d.ts +6 -2
  7. package/dist/core/compaction/compaction.d.ts.map +1 -1
  8. package/dist/core/compaction/compaction.js +6 -1
  9. package/dist/core/compaction/compaction.js.map +1 -1
  10. package/dist/core/compaction/stuck-overflow.d.ts +11 -0
  11. package/dist/core/compaction/stuck-overflow.d.ts.map +1 -0
  12. package/dist/core/compaction/stuck-overflow.js +17 -0
  13. package/dist/core/compaction/stuck-overflow.js.map +1 -0
  14. package/dist/core/extensions/builtin/cache-keepalive/index.d.ts.map +1 -1
  15. package/dist/core/extensions/builtin/cache-keepalive/index.js +3 -21
  16. package/dist/core/extensions/builtin/cache-keepalive/index.js.map +1 -1
  17. package/dist/core/extensions/builtin/claude-sdk-oauth/prompt-bridge.d.ts.map +1 -1
  18. package/dist/core/extensions/builtin/claude-sdk-oauth/prompt-bridge.js +2 -0
  19. package/dist/core/extensions/builtin/claude-sdk-oauth/prompt-bridge.js.map +1 -1
  20. package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.d.ts.map +1 -1
  21. package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js +36 -19
  22. package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js.map +1 -1
  23. package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.d.ts.map +1 -1
  24. package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.js +21 -10
  25. package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.js.map +1 -1
  26. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts +4 -17
  27. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts.map +1 -1
  28. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js +23 -33
  29. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js.map +1 -1
  30. package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-pump.d.ts.map +1 -1
  31. package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-pump.js +15 -2
  32. package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-pump.js.map +1 -1
  33. package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.d.ts.map +1 -1
  34. package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js +1 -0
  35. package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js.map +1 -1
  36. package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.d.ts.map +1 -1
  37. package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.js +5 -3
  38. package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.js.map +1 -1
  39. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.d.ts.map +1 -1
  40. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.js +8 -3
  41. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.js.map +1 -1
  42. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-claim.d.ts +1 -1
  43. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-claim.d.ts.map +1 -1
  44. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-claim.js +2 -3
  45. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-claim.js.map +1 -1
  46. package/dist/core/extensions/builtin/claude-sdk-oauth/tools.d.ts +6 -5
  47. package/dist/core/extensions/builtin/claude-sdk-oauth/tools.d.ts.map +1 -1
  48. package/dist/core/extensions/builtin/claude-sdk-oauth/tools.js +6 -5
  49. package/dist/core/extensions/builtin/claude-sdk-oauth/tools.js.map +1 -1
  50. package/dist/core/extensions/builtin/compaction/circuit-breaker.d.ts +8 -0
  51. package/dist/core/extensions/builtin/compaction/circuit-breaker.d.ts.map +1 -1
  52. package/dist/core/extensions/builtin/compaction/circuit-breaker.js +8 -0
  53. package/dist/core/extensions/builtin/compaction/circuit-breaker.js.map +1 -1
  54. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +4 -0
  55. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
  56. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +40 -20
  57. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
  58. package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.d.ts +18 -0
  59. package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.d.ts.map +1 -0
  60. package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.js +20 -0
  61. package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.js.map +1 -0
  62. package/dist/core/extensions/builtin/compaction/index.js +2 -2
  63. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  64. package/dist/core/extensions/builtin/compaction/lane-policy.d.ts +12 -0
  65. package/dist/core/extensions/builtin/compaction/lane-policy.d.ts.map +1 -1
  66. package/dist/core/extensions/builtin/compaction/lane-policy.js +47 -18
  67. package/dist/core/extensions/builtin/compaction/lane-policy.js.map +1 -1
  68. package/dist/core/extensions/builtin/compaction/openai-remote-convert.d.ts.map +1 -1
  69. package/dist/core/extensions/builtin/compaction/openai-remote-convert.js +2 -0
  70. package/dist/core/extensions/builtin/compaction/openai-remote-convert.js.map +1 -1
  71. package/dist/core/extensions/builtin/compaction/retained-message-safety.d.ts.map +1 -1
  72. package/dist/core/extensions/builtin/compaction/retained-message-safety.js +14 -1
  73. package/dist/core/extensions/builtin/compaction/retained-message-safety.js.map +1 -1
  74. package/dist/core/extensions/builtin/goal/cache-warm-renderer.js +6 -4
  75. package/dist/core/extensions/builtin/goal/cache-warm-renderer.js.map +1 -1
  76. package/dist/core/extensions/builtin/goal/cache-warm.d.ts +19 -1
  77. package/dist/core/extensions/builtin/goal/cache-warm.d.ts.map +1 -1
  78. package/dist/core/extensions/builtin/goal/cache-warm.js +23 -10
  79. package/dist/core/extensions/builtin/goal/cache-warm.js.map +1 -1
  80. package/dist/core/extensions/builtin/goal/continuation-recovery.d.ts +1 -0
  81. package/dist/core/extensions/builtin/goal/continuation-recovery.d.ts.map +1 -1
  82. package/dist/core/extensions/builtin/goal/continuation-recovery.js +2 -0
  83. package/dist/core/extensions/builtin/goal/continuation-recovery.js.map +1 -1
  84. package/dist/core/extensions/builtin/goal/continuation.d.ts +3 -1
  85. package/dist/core/extensions/builtin/goal/continuation.d.ts.map +1 -1
  86. package/dist/core/extensions/builtin/goal/continuation.js +4 -0
  87. package/dist/core/extensions/builtin/goal/continuation.js.map +1 -1
  88. package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts +3 -0
  89. package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts.map +1 -1
  90. package/dist/core/extensions/builtin/goal/lifecycle-helpers.js +14 -3
  91. package/dist/core/extensions/builtin/goal/lifecycle-helpers.js.map +1 -1
  92. package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts +1 -1
  93. package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts.map +1 -1
  94. package/dist/core/extensions/builtin/goal/monitor-continuation.js +7 -3
  95. package/dist/core/extensions/builtin/goal/monitor-continuation.js.map +1 -1
  96. package/dist/core/extensions/builtin/hooks/plugin-manifest.js +2 -2
  97. package/dist/core/extensions/builtin/hooks/plugin-manifest.js.map +1 -1
  98. package/dist/core/extensions/builtin/hooks/safety.js +2 -2
  99. package/dist/core/extensions/builtin/hooks/safety.js.map +1 -1
  100. package/dist/core/extensions/builtin/permission-system/external-dir.d.ts.map +1 -1
  101. package/dist/core/extensions/builtin/permission-system/external-dir.js +5 -22
  102. package/dist/core/extensions/builtin/permission-system/external-dir.js.map +1 -1
  103. package/dist/core/extensions/builtin/permission-system/parsers.d.ts.map +1 -1
  104. package/dist/core/extensions/builtin/permission-system/parsers.js +5 -7
  105. package/dist/core/extensions/builtin/permission-system/parsers.js.map +1 -1
  106. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts +2 -2
  107. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts.map +1 -1
  108. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js +8 -2
  109. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js.map +1 -1
  110. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
  111. package/dist/core/extensions/builtin/terminal/monitor-registry.js +14 -7
  112. package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
  113. package/dist/core/messages.d.ts +7 -0
  114. package/dist/core/messages.d.ts.map +1 -1
  115. package/dist/core/messages.js +2 -0
  116. package/dist/core/messages.js.map +1 -1
  117. package/dist/core/package-identity.d.ts +12 -0
  118. package/dist/core/package-identity.d.ts.map +1 -0
  119. package/dist/core/package-identity.js +57 -0
  120. package/dist/core/package-identity.js.map +1 -0
  121. package/dist/core/resource-loader.d.ts +0 -2
  122. package/dist/core/resource-loader.d.ts.map +1 -1
  123. package/dist/core/resource-loader.js +8 -60
  124. package/dist/core/resource-loader.js.map +1 -1
  125. package/dist/core/retry-fallback/controller.d.ts.map +1 -1
  126. package/dist/core/retry-fallback/controller.js +5 -4
  127. package/dist/core/retry-fallback/controller.js.map +1 -1
  128. package/dist/core/sdk.d.ts.map +1 -1
  129. package/dist/core/sdk.js +5 -0
  130. package/dist/core/sdk.js.map +1 -1
  131. package/dist/core/session-manager.d.ts +12 -1
  132. package/dist/core/session-manager.d.ts.map +1 -1
  133. package/dist/core/session-manager.js +29 -3
  134. package/dist/core/session-manager.js.map +1 -1
  135. package/dist/core/settings-manager.d.ts +6 -2
  136. package/dist/core/settings-manager.d.ts.map +1 -1
  137. package/dist/core/settings-manager.js +4 -1
  138. package/dist/core/settings-manager.js.map +1 -1
  139. package/dist/core/tools/bounded-realpath.d.ts +28 -0
  140. package/dist/core/tools/bounded-realpath.d.ts.map +1 -0
  141. package/dist/core/tools/bounded-realpath.js +53 -0
  142. package/dist/core/tools/bounded-realpath.js.map +1 -0
  143. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  144. package/dist/core/tools/file-mutation-queue.js +15 -13
  145. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  146. package/dist/core/tools/filesystem-policy.d.ts +7 -0
  147. package/dist/core/tools/filesystem-policy.d.ts.map +1 -1
  148. package/dist/core/tools/filesystem-policy.js +25 -15
  149. package/dist/core/tools/filesystem-policy.js.map +1 -1
  150. package/dist/core/trust-manager.d.ts.map +1 -1
  151. package/dist/core/trust-manager.js +13 -2
  152. package/dist/core/trust-manager.js.map +1 -1
  153. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  154. package/dist/modes/interactive/interactive-mode.js +5 -1
  155. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  156. package/dist/modes/interactive/tips/catalog/memory-tips.d.ts.map +1 -1
  157. package/dist/modes/interactive/tips/catalog/memory-tips.js +6 -0
  158. package/dist/modes/interactive/tips/catalog/memory-tips.js.map +1 -1
  159. package/dist/modes/interactive/tips/catalog/subagent-tips.d.ts.map +1 -1
  160. package/dist/modes/interactive/tips/catalog/subagent-tips.js +6 -0
  161. package/dist/modes/interactive/tips/catalog/subagent-tips.js.map +1 -1
  162. package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
  163. package/dist/modes/rpc/host-lifecycle.js +31 -5
  164. package/dist/modes/rpc/host-lifecycle.js.map +1 -1
  165. package/dist/modes/rpc/host-watchdog.d.ts +11 -0
  166. package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
  167. package/dist/modes/rpc/host-watchdog.js +16 -2
  168. package/dist/modes/rpc/host-watchdog.js.map +1 -1
  169. package/dist/modes/rpc/multi-session-host.d.ts +3 -7
  170. package/dist/modes/rpc/multi-session-host.d.ts.map +1 -1
  171. package/dist/modes/rpc/multi-session-host.js +57 -29
  172. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  173. package/dist/modes/rpc/rpc-types.d.ts +1 -2
  174. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  175. package/dist/modes/rpc/rpc-types.js +0 -1
  176. package/dist/modes/rpc/rpc-types.js.map +1 -1
  177. package/dist/modes/rpc/session-registry.d.ts +1 -4
  178. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  179. package/dist/modes/rpc/session-registry.js +0 -15
  180. package/dist/modes/rpc/session-registry.js.map +1 -1
  181. package/dist/modes/rpc/socket-ownership.d.ts +46 -0
  182. package/dist/modes/rpc/socket-ownership.d.ts.map +1 -0
  183. package/dist/modes/rpc/socket-ownership.js +178 -0
  184. package/dist/modes/rpc/socket-ownership.js.map +1 -0
  185. package/dist/utils/fs-watch.d.ts +17 -0
  186. package/dist/utils/fs-watch.d.ts.map +1 -1
  187. package/dist/utils/fs-watch.js +38 -10
  188. package/dist/utils/fs-watch.js.map +1 -1
  189. package/dist/utils/paths.d.ts +33 -0
  190. package/dist/utils/paths.d.ts.map +1 -1
  191. package/dist/utils/paths.js +109 -3
  192. package/dist/utils/paths.js.map +1 -1
  193. package/docs/compaction-guide.md +2 -2
  194. package/docs/compaction.md +2 -2
  195. package/docs/development.md +3 -2
  196. package/docs/providers.md +1 -1
  197. package/docs/rpc.md +6 -7
  198. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +74 -0
  199. package/node_modules/@code-yeongyu/senpi-codemode/README.md +34 -5
  200. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  201. package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/protocol.ts +1 -0
  202. package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/reserved.ts +2 -0
  203. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/runtime-factory.ts +3 -0
  204. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/session-manager.ts +7 -0
  205. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/AGENTS.md +7 -0
  206. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/kernel.ts +6 -2
  207. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/context-manager.ts +73 -129
  208. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/inline-worker.ts +2 -2
  209. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/interrupt-bounds.ts +66 -0
  210. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-contract.ts +3 -0
  211. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/run-queue.ts +18 -3
  212. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-core.js +59 -0
  213. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-indirect-eval.js +10 -6
  214. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-runtime.js +16 -0
  215. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-shell-capture.d.ts +25 -0
  216. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-shell-capture.js +70 -8
  217. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-slot.ts +106 -0
  218. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-startup.ts +70 -0
  219. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel-contract.ts +3 -0
  220. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/transport.ts +11 -3
  221. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/kernel.ts +6 -2
  222. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/session-env.ts +56 -0
  223. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/runtime-asset.ts +40 -14
  224. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-contract.ts +3 -0
  225. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-kernel.ts +9 -1
  226. package/node_modules/@code-yeongyu/senpi-codemode/src/output/output-meta.ts +26 -2
  227. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +10 -5
  228. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-execution.ts +9 -11
  229. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-contract.ts +45 -0
  230. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-manager.ts +43 -71
  231. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-notification.ts +4 -5
  232. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-snapshot.ts +2 -0
  233. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-status.ts +30 -0
  234. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-eval-result.ts +1 -0
  235. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-notification-queue.ts +2 -2
  236. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/image.ts +12 -3
  237. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/interrupt-note.ts +29 -15
  238. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +2 -0
  239. package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts.map +1 -1
  240. package/node_modules/@earendil-works/pi-agent-core/dist/agent.js +3 -1
  241. package/node_modules/@earendil-works/pi-agent-core/dist/agent.js.map +1 -1
  242. package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.d.ts.map +1 -1
  243. package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.js +17 -0
  244. package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.js.map +1 -1
  245. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
  246. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.js +1 -0
  247. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.js.map +1 -1
  248. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.d.ts.map +1 -1
  249. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +1 -0
  250. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js.map +1 -1
  251. package/node_modules/@earendil-works/pi-agent-core/dist/harness/reducer.d.ts.map +1 -1
  252. package/node_modules/@earendil-works/pi-agent-core/dist/harness/reducer.js +6 -0
  253. package/node_modules/@earendil-works/pi-agent-core/dist/harness/reducer.js.map +1 -1
  254. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/context.d.ts +2 -1
  255. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/context.d.ts.map +1 -1
  256. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/context.js +5 -1
  257. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/context.js.map +1 -1
  258. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.d.ts.map +1 -1
  259. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.js +1 -0
  260. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.js.map +1 -1
  261. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/types.d.ts +7 -1
  262. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/types.d.ts.map +1 -1
  263. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/types.js.map +1 -1
  264. package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts +2 -0
  265. package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts.map +1 -1
  266. package/node_modules/@earendil-works/pi-agent-core/dist/types.js.map +1 -1
  267. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  268. package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js +2 -0
  269. package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js.map +1 -1
  270. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.d.ts.map +1 -1
  271. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js +15 -0
  272. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js.map +1 -1
  273. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +4 -4
  274. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js.map +1 -1
  275. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  276. package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
  277. package/node_modules/@earendil-works/pi-ai/dist/providers/data/azure-openai-responses.json +1 -1
  278. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cerebras.json +1 -1
  279. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  280. package/node_modules/@earendil-works/pi-ai/dist/providers/data/github-copilot.json +1 -1
  281. package/node_modules/@earendil-works/pi-ai/dist/providers/data/moonshotai-cn.json +1 -1
  282. package/node_modules/@earendil-works/pi-ai/dist/providers/data/moonshotai.json +1 -1
  283. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai-codex.json +1 -1
  284. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai.json +1 -1
  285. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  286. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  287. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opengateway.json +1 -1
  288. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  289. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  290. package/node_modules/@earendil-works/pi-ai/dist/providers/faux.d.ts.map +1 -1
  291. package/node_modules/@earendil-works/pi-ai/dist/providers/faux.js +2 -0
  292. package/node_modules/@earendil-works/pi-ai/dist/providers/faux.js.map +1 -1
  293. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +8 -1
  294. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  295. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  296. package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.d.ts.map +1 -1
  297. package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.js +2 -0
  298. package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.js.map +1 -1
  299. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts +1 -1
  300. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts.map +1 -1
  301. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +14 -3
  302. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js.map +1 -1
  303. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  304. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  305. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  306. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  307. package/package.json +7 -7
@@ -35,6 +35,7 @@ import { executeBashWithOperations } from "./bash-executor.js";
35
35
  import { envValue } from "./brand.js";
36
36
  import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, estimateTokens, generateBranchSummary, prepareCompaction, resolveThresholdContextTokens, shouldCompact, } from "./compaction/index.js";
37
37
  import { CompactionLifecycleCoordinator } from "./compaction/lifecycle.js";
38
+ import { isTurnStuckOnContextOverflow } from "./compaction/stuck-overflow.js";
38
39
  import { isWarmSummaryAnchorValid } from "./compaction/warm-anchor.js";
39
40
  import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
40
41
  import { buildDynamicSystemPrompt } from "./dynamic-prompt/index.js";
@@ -42,7 +43,7 @@ import { areExperimentalFeaturesEnabled } from "./experimental.js";
42
43
  import { exportSessionToHtml } from "./export-html/index.js";
43
44
  import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
44
45
  import { ModelUsabilityBudgetError, projectModelUsabilityBudget, } from "./extensions/builtin/compaction/model-usability-budget.js";
45
- import { resolveReserveTokens } from "./extensions/builtin/compaction/policy.js";
46
+ import { resolveEffectiveReserveTokens, resolveReserveTokens, shouldTriggerCompaction, } from "./extensions/builtin/compaction/policy.js";
46
47
  import { WAKE_SOURCE_STATE_EVENT } from "./extensions/builtin/monitor-state-event.js";
47
48
  import { CODEX_RESPONSES_API } from "./extensions/builtin/service-tier.js";
48
49
  import { deriveExtensionRegistrationId } from "./extensions/builtin/tool-search/engine/marker.js";
@@ -208,6 +209,8 @@ function removeSkillInvocationTokens(text, tokens) {
208
209
  let result = "";
209
210
  for (const token of tokens) {
210
211
  result += text.slice(cursor, token.start);
212
+ if (token.position === "inline")
213
+ result += `[skill: ${token.name}]`;
211
214
  cursor = token.end;
212
215
  if (token.position === "inline" &&
213
216
  (result.endsWith(" ") || result.endsWith("\t")) &&
@@ -755,6 +758,25 @@ export class AgentSession {
755
758
  return { model };
756
759
  }
757
760
  }
761
+ /**
762
+ * Resolve the model used for compaction summarization. When the user sets a
763
+ * `compaction.model` override ("provider/model"), that model is used for the
764
+ * summarization call instead of the session model — this is what lets an
765
+ * SDK-owned lane (claude-sdk-oauth) compact on a cheaper/different model.
766
+ * Any resolution failure (unset, malformed, unknown model) falls back to the
767
+ * session model so compaction never silently breaks.
768
+ */
769
+ _resolveCompactionModel(sessionModel) {
770
+ const override = this._getCompactionSettings().model;
771
+ if (!override)
772
+ return sessionModel;
773
+ const slash = override.indexOf("/");
774
+ if (slash <= 0 || slash === override.length - 1)
775
+ return sessionModel;
776
+ const provider = override.slice(0, slash);
777
+ const modelId = override.slice(slash + 1);
778
+ return this._modelRuntime.getModel(provider, modelId) ?? sessionModel;
779
+ }
758
780
  async _getCompactionRequestAuth(model) {
759
781
  const auth = await this._getSummarizationRequestAuth(model);
760
782
  return {
@@ -1115,6 +1137,31 @@ export class AgentSession {
1115
1137
  }
1116
1138
  _incrementMessageRevision() {
1117
1139
  this._messageRevision++;
1140
+ // A revision bump alone is not evidence that the rejected context changed:
1141
+ // releasing here let a synthetic bump retry the unchanged oversized context
1142
+ // (#7921 case 6). Release only once the context actually shrank.
1143
+ this._releaseBlockedPostCompactionAdmissionIfReduced();
1144
+ }
1145
+ /** Byte-derived size of the context an admission decision would carry. */
1146
+ _blockedAdmissionContentTokens() {
1147
+ return estimateMessagesTokens(filterContextExcludedMessages(this.agent.state.messages));
1148
+ }
1149
+ /** A compaction that genuinely reduced the context clears the blocked state. */
1150
+ _releaseBlockedPostCompactionAdmissionIfReduced() {
1151
+ const blocked = this._blockedPostCompactionAssistant;
1152
+ if (blocked === undefined)
1153
+ return;
1154
+ if (this._blockedAdmissionContentTokens() < blocked.contentTokens) {
1155
+ this._blockedPostCompactionAssistant = undefined;
1156
+ }
1157
+ }
1158
+ /**
1159
+ * Unconditional release for manual `/compact`, the user's explicit remedy. An
1160
+ * ordinary user prompt is deliberately not a release: it only adds context, so
1161
+ * it cannot make the rejected context admissible, and the pre-prompt gate
1162
+ * already owns its own fail-closed rejection for that route.
1163
+ */
1164
+ _releaseBlockedPostCompactionAdmission() {
1118
1165
  this._blockedPostCompactionAssistant = undefined;
1119
1166
  }
1120
1167
  getMessageRevision() {
@@ -1163,7 +1210,7 @@ export class AgentSession {
1163
1210
  systemPrompt: this.agent.state.systemPrompt,
1164
1211
  tools: this.agent.state.tools,
1165
1212
  liveContextTokens: this._getDownswitchLiveContextTokens(model),
1166
- compaction: this.settingsManager.getCompactionSettings(),
1213
+ compaction: this._getCompactionSettings(),
1167
1214
  });
1168
1215
  return projection.usable ? undefined : projection;
1169
1216
  }
@@ -1353,13 +1400,22 @@ export class AgentSession {
1353
1400
  const messages = filterContextExcludedMessages(this.agent.state.messages);
1354
1401
  return resolveThresholdContextTokens(directContextTokens, estimateMessagesTokens(messages));
1355
1402
  }
1403
+ /**
1404
+ * `compaction.enabled=false` disables proactive (threshold) compaction only.
1405
+ * A provider-confirmed overflow is an error the session cannot progress past
1406
+ * by any other route, so overflow recovery stays armed regardless (#1422).
1407
+ */
1356
1408
  _getAutoCompactionReason(message) {
1357
- const settings = this.settingsManager.getCompactionSettings();
1358
- if (!settings.enabled || message.stopReason === "aborted") {
1409
+ const settings = this._getCompactionSettings();
1410
+ if (message.stopReason === "aborted") {
1359
1411
  return undefined;
1360
1412
  }
1361
1413
  const model = this.model;
1362
- if (!model || this._isAssistantFromBeforeLatestCompaction(message)) {
1414
+ // Narrowed to the stale usage number itself: pre-boundary provenance does
1415
+ // not exempt a context whose own content already exceeds the policy.
1416
+ if (!model)
1417
+ return undefined;
1418
+ if (this._isAssistantFromBeforeLatestCompaction(message) && !this._exceedsPolicyByContentEstimate()) {
1363
1419
  return undefined;
1364
1420
  }
1365
1421
  const sameModel = isSameOverflowSource(message, model, this._modelRuntime.getCompatibilityRequestConfig(model).upstreamModelId);
@@ -1367,11 +1423,15 @@ export class AgentSession {
1367
1423
  const currentContextNeedsCompaction = contextUsage !== undefined &&
1368
1424
  contextUsage.tokens !== null &&
1369
1425
  shouldCompact(contextUsage.tokens, contextUsage.contextWindow, settings);
1370
- if (isContextOverflow(message, model.contextWindow) && (sameModel || currentContextNeedsCompaction)) {
1371
- return "overflow";
1426
+ const isOverflow = (isContextOverflow(message, model.contextWindow) && (sameModel || currentContextNeedsCompaction)) ||
1427
+ this._isCursorPayloadOverflow(message);
1428
+ if (isOverflow) {
1429
+ if (settings.enabled || isTurnStuckOnContextOverflow(message, model.contextWindow))
1430
+ return "overflow";
1431
+ return undefined;
1372
1432
  }
1373
- if (this._isCursorPayloadOverflow(message)) {
1374
- return "overflow";
1433
+ if (!settings.enabled) {
1434
+ return undefined;
1375
1435
  }
1376
1436
  let contextTokens;
1377
1437
  const directContextTokens = message.usage ? calculateContextTokens(message.usage) : 0;
@@ -1391,13 +1451,31 @@ export class AgentSession {
1391
1451
  if (compactionEntry &&
1392
1452
  usageMessage?.role === "assistant" &&
1393
1453
  this._isAssistantFromBeforeLatestCompaction(usageMessage)) {
1394
- return undefined;
1454
+ // Drop only the stale usage number; the messages themselves still count.
1455
+ const contentTokens = estimateMessagesTokens(messages);
1456
+ return shouldCompact(contentTokens, model.contextWindow, settings) ? "threshold" : undefined;
1395
1457
  }
1396
1458
  }
1397
1459
  contextTokens = estimate.tokens;
1398
1460
  }
1399
1461
  return shouldCompact(contextTokens, model.contextWindow, settings) ? "threshold" : undefined;
1400
1462
  }
1463
+ /**
1464
+ * Content measured by bytes, ignoring every provider usage number. The
1465
+ * stale-usage exemptions below only claim that a usage figure measured before
1466
+ * the accepted compaction boundary is not evidence of current pressure; they
1467
+ * must not exempt the messages themselves, because fresh post-boundary content
1468
+ * (a large tool result, late steering) is measurable without any usage report
1469
+ * (#7921 case 5).
1470
+ */
1471
+ _exceedsPolicyByContentEstimate() {
1472
+ const model = this.model;
1473
+ if (!model)
1474
+ return false;
1475
+ const settings = this._getCompactionSettings();
1476
+ const contextTokens = estimateMessagesTokens(filterContextExcludedMessages(this.agent.state.messages));
1477
+ return shouldCompact(contextTokens, model.contextWindow, settings);
1478
+ }
1401
1479
  _hasPendingPostCompactionUsageExemption(message) {
1402
1480
  return (this._skipNextPostCompactionAssistantCheck &&
1403
1481
  !this._assistantsPendingAtCompaction.has(message) &&
@@ -3621,7 +3699,7 @@ export class AgentSession {
3621
3699
  systemPrompt: this.agent.state.systemPrompt,
3622
3700
  tools: this.agent.state.tools,
3623
3701
  liveContextTokens,
3624
- compaction: this.settingsManager.getCompactionSettings(),
3702
+ compaction: this._getCompactionSettings(),
3625
3703
  includeSpeculationLead: options?.includeSpeculationLead,
3626
3704
  admission: options?.admission,
3627
3705
  });
@@ -3632,7 +3710,7 @@ export class AgentSession {
3632
3710
  const currentModel = this.model;
3633
3711
  if (!currentModel)
3634
3712
  return 0;
3635
- const compaction = this.settingsManager.getCompactionSettings();
3713
+ const compaction = this._getCompactionSettings();
3636
3714
  const currentBudget = projectModelUsabilityBudget({
3637
3715
  model: currentModel,
3638
3716
  systemPrompt: this.agent.state.systemPrompt,
@@ -3711,17 +3789,18 @@ export class AgentSession {
3711
3789
  const thinking = this._getThinkingForModelSwitch(model, opts.ephemeralThinkingLevel);
3712
3790
  const liveContextTokens = this._getDownswitchLiveContextTokens(model);
3713
3791
  this.agent.state.model = model;
3714
- this.agent.abortServerSideFallback =
3715
- this.settingsManager.getAbortServerSideFallback() && this._retryFallback.hasConfiguredChain();
3716
- if (opts.appendSessionEntry) {
3717
- this.sessionManager.appendModelChange(model.provider, model.id, opts.entryReason, previousModel?.provider, previousModel?.id);
3718
- }
3719
- if (opts.persistDefault) {
3720
- this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
3792
+ if (!(model.id === "gpt-6-astra" && (model.provider === "openai" || model.provider === "openai-codex"))) {
3793
+ this.agent.state.reasoningBaseline = undefined;
3721
3794
  }
3722
3795
  const scopedMatch = this._scopedModels.find((sm) => modelsAreEqual(sm.model, model));
3723
3796
  const previousTier = this._currentServiceTier;
3724
3797
  const previousFastMode = this.isFastModeActive();
3798
+ const previousThinkingLevel = this.agent.state.thinkingLevel;
3799
+ const previousThinkingSelection = this.agent.state.thinkingSelection;
3800
+ const previousReasoningBaseline = this.agent.state.reasoningBaseline;
3801
+ const previousAbortServerSideFallback = this.agent.abortServerSideFallback;
3802
+ this.agent.abortServerSideFallback =
3803
+ this.settingsManager.getAbortServerSideFallback() && this._retryFallback.hasConfiguredChain();
3725
3804
  this._currentServiceTier = this._resolveServiceTier(model, scopedMatch?.serviceTier);
3726
3805
  if (opts.ephemeralThinkingLevel !== undefined) {
3727
3806
  this._applyEphemeralThinkingLevel(thinking.level);
@@ -3730,21 +3809,25 @@ export class AgentSession {
3730
3809
  this._setThinkingLevel(thinking.level, false, thinking.selection);
3731
3810
  }
3732
3811
  this._emitHighReasoningWarningIfNeeded();
3733
- // Post-switch: the level reported here is the one actually in force (clamped, or restored
3734
- // from this model's memory), not the level requested for the previous model.
3735
- this._emit({
3736
- type: "model_changed",
3737
- model,
3738
- thinkingLevel: this.thinkingLevel,
3739
- source: opts.modelSelectSource,
3740
- });
3741
- this._emitServiceTierChangeIfNeeded(previousTier, previousFastMode);
3742
- if (!opts.emitModelSelect)
3743
- return undefined;
3744
3812
  const previousSystemPrompt = this.agent.state.systemPrompt;
3745
3813
  try {
3746
- const systemPromptChange = await this._emitModelSelect(model, previousModel, opts.modelSelectSource);
3814
+ const systemPromptChange = opts.emitModelSelect
3815
+ ? await this._emitModelSelect(model, previousModel, opts.modelSelectSource)
3816
+ : undefined;
3747
3817
  this.assertModelUsable(model, liveContextTokens);
3818
+ if (opts.appendSessionEntry) {
3819
+ this.sessionManager.appendModelChange(model.provider, model.id, opts.entryReason, previousModel?.provider, previousModel?.id);
3820
+ }
3821
+ if (opts.persistDefault)
3822
+ this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
3823
+ // Emit only after all admission hooks have accepted the candidate.
3824
+ this._emit({
3825
+ type: "model_changed",
3826
+ model,
3827
+ thinkingLevel: this.thinkingLevel,
3828
+ source: opts.modelSelectSource,
3829
+ });
3830
+ this._emitServiceTierChangeIfNeeded(previousTier, previousFastMode);
3748
3831
  return systemPromptChange;
3749
3832
  }
3750
3833
  catch (error) {
@@ -3753,6 +3836,11 @@ export class AgentSession {
3753
3836
  else
3754
3837
  delete this.agent.state.model;
3755
3838
  this.agent.state.systemPrompt = previousSystemPrompt;
3839
+ this.agent.state.thinkingLevel = previousThinkingLevel;
3840
+ this.agent.state.thinkingSelection = previousThinkingSelection;
3841
+ this.agent.state.reasoningBaseline = previousReasoningBaseline;
3842
+ this.agent.abortServerSideFallback = previousAbortServerSideFallback;
3843
+ this._currentServiceTier = previousTier;
3756
3844
  throw error;
3757
3845
  }
3758
3846
  }
@@ -3929,6 +4017,14 @@ export class AgentSession {
3929
4017
  }
3930
4018
  if (isChanging || selectionChanged) {
3931
4019
  this.sessionManager.appendThinkingLevelChange(effectiveLevel, effectiveSelection);
4020
+ const model = this.model;
4021
+ if (isChanging &&
4022
+ model?.id === "gpt-6-astra" &&
4023
+ (model.provider === "openai" || model.provider === "openai-codex")) {
4024
+ this.agent.state.reasoningBaseline ??= previousLevel;
4025
+ this.sessionManager.appendConfigurationUpdate(effectiveLevel);
4026
+ this.agent.state.messages = this.sessionManager.buildSessionContext().messages;
4027
+ }
3932
4028
  if (updateGlobalDefault && (this.supportsThinking() || effectiveLevel !== "off")) {
3933
4029
  this.settingsManager.setDefaultThinkingLevel(effectiveLevel);
3934
4030
  }
@@ -4236,8 +4332,11 @@ export class AgentSession {
4236
4332
  const model = this.model;
4237
4333
  if (!model)
4238
4334
  throw new Error(formatNoModelSelectedMessage());
4335
+ // Manual compaction is the user's explicit remedy for a blocked admission
4336
+ // (#7921 case 6).
4337
+ this._releaseBlockedPostCompactionAdmission();
4239
4338
  const pathEntries = this.sessionManager.getBranch();
4240
- const settings = cursorOverflowCompactionSettings(this.settingsManager.getCompactionSettings(), model.provider, "manual");
4339
+ const settings = cursorOverflowCompactionSettings(this._getCompactionSettings(), model.provider, "manual");
4241
4340
  if (!prepareCompaction(pathEntries, settings)) {
4242
4341
  const requestId = randomUUID();
4243
4342
  const lastEntry = pathEntries[pathEntries.length - 1];
@@ -4477,7 +4576,7 @@ export class AgentSession {
4477
4576
  throw new CompactionCancelledError();
4478
4577
  }
4479
4578
  const pathEntries = this.sessionManager.getBranch();
4480
- const settings = cursorOverflowCompactionSettings(this.settingsManager.getCompactionSettings(), this.model?.provider, request.reason);
4579
+ const settings = cursorOverflowCompactionSettings(this._getCompactionSettings(), this.model?.provider, request.reason);
4481
4580
  let compactionResult = request.precomputed;
4482
4581
  let fromExtension = request.precomputed !== undefined;
4483
4582
  if (!compactionResult) {
@@ -4519,7 +4618,13 @@ export class AgentSession {
4519
4618
  }
4520
4619
  }
4521
4620
  if (!compactionResult) {
4522
- const { model: requestModel, apiKey, headers, extraBody, env, } = await this._getCompactionRequestAuth(model);
4621
+ // A configured compaction.model override redirects only the
4622
+ // summarization call to that model; every other part of the session
4623
+ // (lifecycle record, preparation, branch) still tracks the session
4624
+ // model. Falls back to the session model when the override is unset
4625
+ // or cannot be resolved.
4626
+ const compactionModel = this._resolveCompactionModel(model);
4627
+ const { model: requestModel, apiKey, headers, extraBody, env, } = await this._getCompactionRequestAuth(compactionModel);
4523
4628
  compactionResult = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, extraBody, request.customInstructions, signal, env, request.reason);
4524
4629
  }
4525
4630
  }
@@ -4550,11 +4655,20 @@ export class AgentSession {
4550
4655
  if (await this._wouldCompactionOverflow(pathEntries, compactionResult, fromExtension, model)) {
4551
4656
  return await this._rejectCompaction(request, requestId, operationId, "would-overflow", false);
4552
4657
  }
4658
+ const latestConfigurationEffort = this.sessionManager
4659
+ .getBranch()
4660
+ .findLast((entry) => entry.type === "configuration_update")?.reasoning.effort;
4553
4661
  const compactionEntryId = this.sessionManager.appendCompaction(compactionResult.summary, compactionResult.firstKeptEntryId, compactionResult.tokensBefore, compactionResult.details, fromExtension, compactionResult.usage);
4554
4662
  const savedEntry = this.sessionManager.getEntry(compactionEntryId);
4555
4663
  if (savedEntry?.type !== "compaction") {
4556
4664
  throw new Error("Compaction entry was not saved");
4557
4665
  }
4666
+ const modelAfterCompaction = this.model;
4667
+ if (latestConfigurationEffort !== undefined &&
4668
+ modelAfterCompaction?.id === "gpt-6-astra" &&
4669
+ (modelAfterCompaction.provider === "openai" || modelAfterCompaction.provider === "openai-codex")) {
4670
+ this.sessionManager.appendConfigurationUpdate(latestConfigurationEffort);
4671
+ }
4558
4672
  const sessionContext = this.sessionManager.buildSessionContext();
4559
4673
  const currentAgentMessages = this.agent.state.messages;
4560
4674
  const hasUnchangedPrefix = agentMessagesAtStart.every((message, index) => currentAgentMessages[index] === message);
@@ -4665,7 +4779,7 @@ export class AgentSession {
4665
4779
  simulatedMessages = truncateToolResultBodies(simulatedMessages).messages ?? simulatedMessages;
4666
4780
  }
4667
4781
  const contextTokens = estimateMessagesTokens(filterContextExcludedMessages(simulatedMessages));
4668
- const settings = this.settingsManager.getCompactionSettings();
4782
+ const settings = this._getCompactionSettings();
4669
4783
  const reserveTokens = settings.reserveScalingEnabled === false
4670
4784
  ? settings.reserveTokens
4671
4785
  : resolveReserveTokens(model.contextWindow, settings.reserveTokens);
@@ -4778,13 +4892,12 @@ export class AgentSession {
4778
4892
  * @returns Whether the post-run loop should call `agent.continue()` for overflow recovery or queued messages
4779
4893
  */
4780
4894
  async _enforceCompactionBeforeProvider(assistantMessage, skipAbortedCheck, inlineReason, retryAfterCompaction = false) {
4895
+ this._releaseBlockedPostCompactionAdmissionIfReduced();
4781
4896
  const blockedAdmission = this._blockedPostCompactionAssistant;
4782
- if (blockedAdmission !== undefined &&
4783
- blockedAdmission.assistant === assistantMessage &&
4784
- blockedAdmission.revision === this._messageRevision) {
4897
+ if (blockedAdmission !== undefined && blockedAdmission.assistant === assistantMessage) {
4785
4898
  throw new RequiredCompactionError();
4786
4899
  }
4787
- const settings = this.settingsManager.getCompactionSettings();
4900
+ const settings = this._getCompactionSettings();
4788
4901
  const model = this.model;
4789
4902
  const contextTokens = estimateContextTokens(filterContextExcludedMessages(this.sessionManager.buildSessionContext().messages)).tokens;
4790
4903
  const compacted = assistantMessage
@@ -4813,12 +4926,28 @@ export class AgentSession {
4813
4926
  }
4814
4927
  throw new RequiredCompactionError();
4815
4928
  }
4929
+ /**
4930
+ * Pending queued input the provider will also carry on this turn. Steering and
4931
+ * follow-up text enqueued after the admission projection was assembled - by a
4932
+ * `before_agent_start` handler, an extension action, or the user racing the
4933
+ * gate - is drained into the same run, so the final gate must measure it
4934
+ * (#7921 case 5).
4935
+ */
4936
+ _pendingQueuedInputMessages() {
4937
+ const timestamp = Date.now();
4938
+ return [...this._steeringMessages, ...this._followUpMessages].map((text) => ({
4939
+ role: "user",
4940
+ content: [{ type: "text", text }],
4941
+ timestamp,
4942
+ }));
4943
+ }
4816
4944
  /**
4817
4945
  * The normal pre-prompt check only estimates persisted session context. This
4818
4946
  * final gate also includes turn-local messages which the provider will see:
4819
- * the current prompt, next-turn custom messages, and before_agent_start
4820
- * additions. Compaction rewrites only session context, so callers retain and
4821
- * reapply their already-assembled one-shot additions after it succeeds.
4947
+ * the current prompt, next-turn custom messages, before_agent_start additions,
4948
+ * and steering or follow-up input queued after that projection was assembled.
4949
+ * Compaction rewrites only session context, so callers retain and reapply
4950
+ * their already-assembled one-shot additions after it succeeds.
4822
4951
  */
4823
4952
  async _enforceFinalProviderAdmission(messages) {
4824
4953
  // User-only prompts are deliberately admitted without this gate: prompt
@@ -4826,18 +4955,24 @@ export class AgentSession {
4826
4955
  // (issues #531/#886), so oversized user prompts rely on threshold
4827
4956
  // compaction — which also samples the local transcript estimate — and on
4828
4957
  // provider-overflow recovery. This gate closes the separate gap opened by
4829
- // turn-local custom additions, which the pre-prompt check cannot observe.
4830
- if (!messages.some((message) => message.role === "custom"))
4958
+ // turn-local custom additions and by late queued input, neither of which the
4959
+ // pre-prompt check can observe.
4960
+ const lateQueuedMessages = this._pendingQueuedInputMessages();
4961
+ if (!messages.some((message) => message.role === "custom") && lateQueuedMessages.length === 0)
4831
4962
  return;
4832
4963
  const model = this.model;
4833
4964
  if (!model)
4834
4965
  return;
4835
- const settings = this.settingsManager.getCompactionSettings();
4836
- const reserveTokens = settings.reserveScalingEnabled === false
4837
- ? settings.reserveTokens
4838
- : resolveReserveTokens(model.contextWindow, settings.reserveTokens);
4966
+ const settings = this._getCompactionSettings();
4967
+ const reserveTokens = resolveEffectiveReserveTokens(model.contextWindow, settings);
4839
4968
  const isOversized = () => {
4840
- const providerMessages = filterContextExcludedMessages([...this.agent.state.messages, ...messages]);
4969
+ const providerMessages = filterContextExcludedMessages([
4970
+ ...this.agent.state.messages,
4971
+ ...messages,
4972
+ // Re-read the queues on every sample: this closure is evaluated again
4973
+ // after compaction, when more input may have arrived.
4974
+ ...this._pendingQueuedInputMessages(),
4975
+ ]);
4841
4976
  const estimate = estimateContextTokens(providerMessages);
4842
4977
  const usageMessage = estimate.lastUsageIndex === null ? undefined : providerMessages[estimate.lastUsageIndex];
4843
4978
  // Kept assistant usage can describe the pre-compaction request. Once a
@@ -4866,9 +5001,7 @@ export class AgentSession {
4866
5001
  }
4867
5002
  }
4868
5003
  async _checkCompaction(assistantMessage, skipAbortedCheck = true, inlineReason, retryAfterCompaction = false) {
4869
- const settings = this.settingsManager.getCompactionSettings();
4870
- if (!settings.enabled)
4871
- return false;
5004
+ const settings = this._getCompactionSettings();
4872
5005
  // Skip if message was aborted (user cancelled) - unless skipAbortedCheck is false
4873
5006
  if (skipAbortedCheck && assistantMessage.stopReason === "aborted")
4874
5007
  return false;
@@ -4883,7 +5016,12 @@ export class AgentSession {
4883
5016
  // compaction boundary. This prevents a stale pre-compaction usage/error
4884
5017
  // from retriggering compaction on the first prompt after compaction.
4885
5018
  const compactionEntry = getLatestCompactionEntry(this.sessionManager.getBranch());
4886
- if (this._isAssistantFromBeforeLatestCompaction(assistantMessage)) {
5019
+ // The inline (pre-prompt) caller re-samples the current context itself and
5020
+ // owns the fail-closed rejection, so only the automatic route needs the
5021
+ // narrowed exemption here: fresh post-boundary content still counts even
5022
+ // though this message's own usage predates the boundary (#7921 case 5).
5023
+ if (this._isAssistantFromBeforeLatestCompaction(assistantMessage) &&
5024
+ (inlineReason !== undefined || !this._exceedsPolicyByContentEstimate())) {
4887
5025
  return false;
4888
5026
  }
4889
5027
  // Case 1: Overflow - LLM returned context overflow error.
@@ -4897,6 +5035,9 @@ export class AgentSession {
4897
5035
  const isOverflow = (isContextOverflow(assistantMessage, contextWindow) && (sameModel || currentContextNeedsCompaction)) ||
4898
5036
  recoverableLength ||
4899
5037
  this._isCursorPayloadOverflow(assistantMessage);
5038
+ if (isOverflow && !settings.enabled && !isTurnStuckOnContextOverflow(assistantMessage, contextWindow)) {
5039
+ return false;
5040
+ }
4900
5041
  if (isOverflow &&
4901
5042
  assistantMessage.stopReason === "stop" &&
4902
5043
  this._consumePostCompactionUsageExemption(assistantMessage)) {
@@ -4915,7 +5056,7 @@ export class AgentSession {
4915
5056
  getLatestCompactionEntry(this.sessionManager.getBranch()) !== null) {
4916
5057
  this._blockedPostCompactionAssistant = {
4917
5058
  assistant: assistantMessage,
4918
- revision: this._messageRevision,
5059
+ contentTokens: this._blockedAdmissionContentTokens(),
4919
5060
  };
4920
5061
  }
4921
5062
  return compacted;
@@ -4967,6 +5108,10 @@ export class AgentSession {
4967
5108
  }
4968
5109
  return compacted;
4969
5110
  }
5111
+ // Stuck-overflow recovery above runs regardless of the flag; threshold
5112
+ // compaction below is the proactive path the user switched off (#1422).
5113
+ if (!settings.enabled)
5114
+ return false;
4970
5115
  // The first ordinary response can carry provider usage calculated before
4971
5116
  // compaction. Consume that exemption only after proving this is not an
4972
5117
  // overflow, then retain the decision for later admission re-sampling.
@@ -4977,6 +5122,7 @@ export class AgentSession {
4977
5122
  // This ensures sessions that hit persistent API errors (e.g. 529) or malformed zero-usage
4978
5123
  // responses can still compact and do not reset context accounting.
4979
5124
  let contextTokens;
5125
+ let staleUsageContentTokens;
4980
5126
  if (inlineReason) {
4981
5127
  const messages = filterContextExcludedMessages(this.sessionManager.buildSessionContext().messages);
4982
5128
  contextTokens = estimateContextTokens(messages).tokens;
@@ -4997,10 +5143,13 @@ export class AgentSession {
4997
5143
  if (compactionEntry &&
4998
5144
  usageMsg.role === "assistant" &&
4999
5145
  this._isAssistantFromBeforeLatestCompaction(usageMsg)) {
5000
- return false;
5146
+ // Drop only the stale usage number; the messages themselves still count.
5147
+ staleUsageContentTokens = estimateMessagesTokens(messages);
5148
+ if (!shouldCompact(staleUsageContentTokens, contextWindow, settings))
5149
+ return false;
5001
5150
  }
5002
5151
  }
5003
- contextTokens = estimate.tokens;
5152
+ contextTokens = staleUsageContentTokens ?? estimate.tokens;
5004
5153
  }
5005
5154
  }
5006
5155
  if (shouldCompact(contextTokens, contextWindow, settings)) {
@@ -5015,7 +5164,7 @@ export class AgentSession {
5015
5164
  getLatestCompactionEntry(this.sessionManager.getBranch()) !== null) {
5016
5165
  this._blockedPostCompactionAssistant = {
5017
5166
  assistant: assistantMessage,
5018
- revision: this._messageRevision,
5167
+ contentTokens: this._blockedAdmissionContentTokens(),
5019
5168
  };
5020
5169
  }
5021
5170
  return compacted;
@@ -5099,7 +5248,7 @@ export class AgentSession {
5099
5248
  }
5100
5249
  async _revalidateScheduledContinuationAdmission() {
5101
5250
  const model = this.model;
5102
- const settings = this.settingsManager.getCompactionSettings();
5251
+ const settings = this._getCompactionSettings();
5103
5252
  if (!model || !settings.enabled)
5104
5253
  return;
5105
5254
  const canonicalMessages = filterContextExcludedMessages(this.sessionManager.buildSessionContext().messages);
@@ -5108,11 +5257,27 @@ export class AgentSession {
5108
5257
  const contextTokens = usageMessage?.role === "assistant" && this._isAssistantFromBeforeLatestCompaction(usageMessage)
5109
5258
  ? estimateMessagesTokens(canonicalMessages)
5110
5259
  : estimate.tokens;
5111
- if (!shouldCompact(contextTokens, model.contextWindow, settings))
5260
+ // An explicit user prompt passes the proactive policy in the compaction
5261
+ // extension's before_agent_start, which automatic continuations never emit.
5262
+ // Sampling the same predicate here makes both routes compact at the same
5263
+ // usage instead of letting a continuation ride past the threshold up to the
5264
+ // hard valve (#7921 case 4).
5265
+ const atHardLimit = shouldCompact(contextTokens, model.contextWindow, settings);
5266
+ const overProactiveThreshold = shouldTriggerCompaction({
5267
+ tokens: contextTokens,
5268
+ contextWindow: model.contextWindow,
5269
+ percent: model.contextWindow > 0 ? (contextTokens / model.contextWindow) * 100 : 0,
5270
+ }, model.contextWindow, settings);
5271
+ if (!atHardLimit && !overProactiveThreshold)
5112
5272
  return;
5113
5273
  const compacted = await this._runPrePromptCompaction(this._findLastAssistantMessage(), true, "pre_prompt");
5114
5274
  if (!compacted) {
5115
- if (this._isCompactionOnCooldown() || this._isCompactionDelegated() || this._hasSupersedingCompactionClaim()) {
5275
+ // Proactive pressure alone must never brick an automatic continuation:
5276
+ // only the hard reserve valve stays fail-closed (#531/#886).
5277
+ if (!atHardLimit ||
5278
+ this._isCompactionOnCooldown() ||
5279
+ this._isCompactionDelegated() ||
5280
+ this._hasSupersedingCompactionClaim()) {
5116
5281
  return;
5117
5282
  }
5118
5283
  throw new RequiredCompactionError();
@@ -5279,7 +5444,7 @@ export class AgentSession {
5279
5444
  }
5280
5445
  if (!this._ownsCompactionController(autoCompactionController, "auto"))
5281
5446
  return false;
5282
- const preparation = prepareCompaction(this.sessionManager.getBranch(), cursorOverflowCompactionSettings(this.settingsManager.getCompactionSettings(), this.model?.provider, reason), reason === "overflow");
5447
+ const preparation = prepareCompaction(this.sessionManager.getBranch(), cursorOverflowCompactionSettings(this._getCompactionSettings(), this.model?.provider, reason), reason === "overflow");
5283
5448
  if (!preparation) {
5284
5449
  return endBeforeExecution();
5285
5450
  }
@@ -5363,12 +5528,21 @@ export class AgentSession {
5363
5528
  }
5364
5529
  }
5365
5530
  /**
5366
- * Toggle auto-compaction setting.
5531
+ * Toggle auto-compaction for this session only. Persisting the flag is a
5532
+ * settings-editor concern; a session-level command (RPC `set_auto_compaction`,
5533
+ * one thread of a multi-session host) must never rewrite the global setting
5534
+ * for every other session on the machine (#1422).
5367
5535
  */
5368
5536
  setAutoCompactionEnabled(enabled) {
5369
- this.settingsManager.setCompactionEnabled(enabled);
5537
+ this._autoCompactionSessionOverride = enabled;
5370
5538
  this._emitSessionSettingsChanged();
5371
5539
  }
5540
+ _getCompactionSettings() {
5541
+ const settings = this.settingsManager.getCompactionSettings();
5542
+ if (this._autoCompactionSessionOverride === undefined)
5543
+ return settings;
5544
+ return { ...settings, enabled: this._autoCompactionSessionOverride };
5545
+ }
5372
5546
  _emitSessionSettingsChanged() {
5373
5547
  this._emit({
5374
5548
  type: "session_settings_changed",
@@ -5377,9 +5551,9 @@ export class AgentSession {
5377
5551
  autoCompactionEnabled: this.autoCompactionEnabled,
5378
5552
  });
5379
5553
  }
5380
- /** Whether auto-compaction is enabled */
5554
+ /** Whether auto-compaction is enabled for this session */
5381
5555
  get autoCompactionEnabled() {
5382
- return this.settingsManager.getCompactionEnabled();
5556
+ return this._autoCompactionSessionOverride ?? this.settingsManager.getCompactionEnabled();
5383
5557
  }
5384
5558
  async bindExtensions(bindings) {
5385
5559
  const finishBindingWork = this._sessionWorkBarrier.begin();
@@ -5616,7 +5790,7 @@ export class AgentSession {
5616
5790
  this._extensionShutdownHandler?.();
5617
5791
  },
5618
5792
  getContextUsage: () => this.getContextUsage(),
5619
- getCompactionSettings: () => this.settingsManager.getCompactionSettings(),
5793
+ getCompactionSettings: () => this._getCompactionSettings(),
5620
5794
  getPromptCacheSafeWaitSeconds: () => this.resolvePromptCacheSafeWaitSeconds(),
5621
5795
  getPromptCacheGoalBackstopMaxSeconds: () => this.settingsManager.getPromptCacheGoalBackstopMaxSeconds(),
5622
5796
  getPromptCacheKeepAliveSettings: () => this.settingsManager.getPromptCacheKeepAliveSettings(),
@@ -6214,6 +6388,18 @@ export class AgentSession {
6214
6388
  let switchedFallback = false;
6215
6389
  let is429TierRouted = false;
6216
6390
  let hintTierDelayMs;
6391
+ const tryFallback = async (reason, failure) => {
6392
+ try {
6393
+ return await this._retryFallback.tryFallback(reason, failure);
6394
+ }
6395
+ catch (error) {
6396
+ if (error instanceof ModelUsabilityBudgetError) {
6397
+ this._resolveRetry();
6398
+ return false;
6399
+ }
6400
+ throw error;
6401
+ }
6402
+ };
6217
6403
  if (sameModelRemint) {
6218
6404
  this._retryAttempt++;
6219
6405
  if (this._retryAttempt > retryProfile.turn.maxRetries) {
@@ -6236,9 +6422,7 @@ export class AgentSession {
6236
6422
  // Billing-class failures never recover on this account, so the fallback
6237
6423
  // switch pins as the session model instead of reverting after the cooldown.
6238
6424
  const reason = isBillingErrorMessage(errorMessage) ? "billing" : "hard-error";
6239
- switchedFallback = await this._retryFallback.tryFallback(reason, {
6240
- errorMessage,
6241
- });
6425
+ switchedFallback = await tryFallback(reason, { errorMessage });
6242
6426
  if (!switchedFallback) {
6243
6427
  const exhaustedChainKey = this._retryFallback.exhaustedChainKey;
6244
6428
  if (exhaustedChainKey) {
@@ -6271,7 +6455,7 @@ export class AgentSession {
6271
6455
  this._resolveRetry();
6272
6456
  return "not-handled";
6273
6457
  }
6274
- switchedFallback = await this._retryFallback.tryFallback("refusal", {});
6458
+ switchedFallback = await tryFallback("refusal", {});
6275
6459
  if (!switchedFallback) {
6276
6460
  const exhaustedChainKey = this._retryFallback.exhaustedChainKey;
6277
6461
  if (exhaustedChainKey) {
@@ -6317,7 +6501,7 @@ export class AgentSession {
6317
6501
  is429TierRouted = true;
6318
6502
  this._retryAttempt++;
6319
6503
  if (this._retryAttempt > retryProfile.turn.maxRetries) {
6320
- switchedFallback = await this._retryFallback.tryFallback("transient", {
6504
+ switchedFallback = await tryFallback("transient", {
6321
6505
  errorMessage,
6322
6506
  retryAfterMs: this._getProviderRetryDelayMs(errorMessage),
6323
6507
  });
@@ -6354,7 +6538,7 @@ export class AgentSession {
6354
6538
  if (tier === "no-hint-fast-fallback") {
6355
6539
  // Fall back immediately when a candidate exists; otherwise degrade
6356
6540
  // to same-model in-turn retries instead of failing the turn.
6357
- switchedFallback = await this._retryFallback.tryFallback("transient", { errorMessage });
6541
+ switchedFallback = await tryFallback("transient", { errorMessage });
6358
6542
  if (switchedFallback) {
6359
6543
  this._retryAttempt = 1;
6360
6544
  }
@@ -6369,7 +6553,7 @@ export class AgentSession {
6369
6553
  this._retryAttempt++;
6370
6554
  if (this._retryAttempt > retryProfile.turn.maxRetries) {
6371
6555
  // Budget exhausted within tier1; fall back.
6372
- switchedFallback = await this._retryFallback.tryFallback("transient", {
6556
+ switchedFallback = await tryFallback("transient", {
6373
6557
  errorMessage,
6374
6558
  retryAfterMs: this._getProviderRetryDelayMs(errorMessage),
6375
6559
  });
@@ -6410,7 +6594,7 @@ export class AgentSession {
6410
6594
  if (inTurnResult.demoteToProbeBack) {
6411
6595
  // Cumulative hinted wait exceeded cap; demote to tier2 fallback path.
6412
6596
  const remainingHintMs = Math.max(0, (this._hintDeadlineMs ?? Date.now()) - Date.now());
6413
- switchedFallback = await this._retryFallback.tryFallback("transient", {
6597
+ switchedFallback = await tryFallback("transient", {
6414
6598
  errorMessage,
6415
6599
  retryAfterMs: remainingHintMs,
6416
6600
  });
@@ -6446,7 +6630,7 @@ export class AgentSession {
6446
6630
  else {
6447
6631
  // tier2-fallback-probe-back or tier3-fallback-only: immediate fallback.
6448
6632
  const remainingHintMs = hintMs ?? 0;
6449
- switchedFallback = await this._retryFallback.tryFallback("transient", {
6633
+ switchedFallback = await tryFallback("transient", {
6450
6634
  errorMessage,
6451
6635
  retryAfterMs: remainingHintMs,
6452
6636
  });
@@ -6469,7 +6653,7 @@ export class AgentSession {
6469
6653
  this._retryAttempt++;
6470
6654
  }
6471
6655
  if (!is429TierRouted && this._retryAttempt > retryProfile.turn.maxRetries) {
6472
- switchedFallback = await this._retryFallback.tryFallback("transient", {
6656
+ switchedFallback = await tryFallback("transient", {
6473
6657
  errorMessage,
6474
6658
  retryAfterMs: this._getProviderRetryDelayMs(errorMessage),
6475
6659
  });
@@ -6513,7 +6697,7 @@ export class AgentSession {
6513
6697
  // branch above may have already switched on this same error, and hopping again
6514
6698
  // here would skip that candidate's own retry budget.
6515
6699
  if (!switchedFallback) {
6516
- switchedFallback = await this._retryFallback.tryFallback("transient", {
6700
+ switchedFallback = await tryFallback("transient", {
6517
6701
  errorMessage,
6518
6702
  retryAfterMs: providerDelayMs,
6519
6703
  });
@@ -6595,7 +6779,7 @@ export class AgentSession {
6595
6779
  // the prior retry checks. Revalidate canonical session context immediately
6596
6780
  // before the continuation so a rejected compaction never admits that model.
6597
6781
  const model = this.model;
6598
- const compactionSettings = this.settingsManager.getCompactionSettings();
6782
+ const compactionSettings = this._getCompactionSettings();
6599
6783
  const contextTokens = estimateContextTokens(filterContextExcludedMessages(this.sessionManager.buildSessionContext().messages)).tokens;
6600
6784
  if (compactionSettings.enabled &&
6601
6785
  model &&