@code-yeongyu/senpi 2026.8.29 → 2026.8.30-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 (311) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/dist/core/agent-session.d.ts +45 -1
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +329 -29
  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 +17 -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 +28 -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 +163 -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.d.ts +1 -0
  56. package/dist/core/extensions/builtin/compaction/idle.d.ts.map +1 -1
  57. package/dist/core/extensions/builtin/compaction/idle.js +13 -2
  58. package/dist/core/extensions/builtin/compaction/idle.js.map +1 -1
  59. package/dist/core/extensions/builtin/compaction/index.d.ts +1 -1
  60. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  61. package/dist/core/extensions/builtin/compaction/index.js +121 -147
  62. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  63. package/dist/core/extensions/builtin/compaction/log.d.ts +1 -1
  64. package/dist/core/extensions/builtin/compaction/log.d.ts.map +1 -1
  65. package/dist/core/extensions/builtin/compaction/log.js +2 -0
  66. package/dist/core/extensions/builtin/compaction/log.js.map +1 -1
  67. package/dist/core/extensions/builtin/compaction/model-selection.d.ts +14 -0
  68. package/dist/core/extensions/builtin/compaction/model-selection.d.ts.map +1 -0
  69. package/dist/core/extensions/builtin/compaction/model-selection.js +37 -0
  70. package/dist/core/extensions/builtin/compaction/model-selection.js.map +1 -0
  71. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.d.ts +9 -0
  72. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.d.ts.map +1 -0
  73. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.js +2 -0
  74. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.js.map +1 -0
  75. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +3 -6
  76. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts.map +1 -1
  77. package/dist/core/extensions/builtin/compaction/openai-remote.js.map +1 -1
  78. package/dist/core/extensions/builtin/compaction/orchestration.d.ts +56 -0
  79. package/dist/core/extensions/builtin/compaction/orchestration.d.ts.map +1 -0
  80. package/dist/core/extensions/builtin/compaction/orchestration.js +136 -0
  81. package/dist/core/extensions/builtin/compaction/orchestration.js.map +1 -0
  82. package/dist/core/extensions/builtin/compaction/policy.d.ts +5 -2
  83. package/dist/core/extensions/builtin/compaction/policy.d.ts.map +1 -1
  84. package/dist/core/extensions/builtin/compaction/policy.js +43 -26
  85. package/dist/core/extensions/builtin/compaction/policy.js.map +1 -1
  86. package/dist/core/extensions/builtin/compaction/retained-message-safety.d.ts.map +1 -1
  87. package/dist/core/extensions/builtin/compaction/retained-message-safety.js +31 -8
  88. package/dist/core/extensions/builtin/compaction/retained-message-safety.js.map +1 -1
  89. package/dist/core/extensions/builtin/compaction/speculation-lead.d.ts +8 -0
  90. package/dist/core/extensions/builtin/compaction/speculation-lead.d.ts.map +1 -0
  91. package/dist/core/extensions/builtin/compaction/speculation-lead.js +17 -0
  92. package/dist/core/extensions/builtin/compaction/speculation-lead.js.map +1 -0
  93. package/dist/core/extensions/builtin/compaction/speculative-job.d.ts +22 -0
  94. package/dist/core/extensions/builtin/compaction/speculative-job.d.ts.map +1 -0
  95. package/dist/core/extensions/builtin/compaction/speculative-job.js +16 -0
  96. package/dist/core/extensions/builtin/compaction/speculative-job.js.map +1 -0
  97. package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts +22 -0
  98. package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts.map +1 -0
  99. package/dist/core/extensions/builtin/compaction/speculative-summary.js +131 -0
  100. package/dist/core/extensions/builtin/compaction/speculative-summary.js.map +1 -0
  101. package/dist/core/extensions/builtin/compaction/speculative.d.ts +8 -13
  102. package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
  103. package/dist/core/extensions/builtin/compaction/speculative.js +7 -196
  104. package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
  105. package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts +18 -0
  106. package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts.map +1 -0
  107. package/dist/core/extensions/builtin/compaction/token-budget-reminder.js +18 -0
  108. package/dist/core/extensions/builtin/compaction/token-budget-reminder.js.map +1 -0
  109. package/dist/core/extensions/builtin/compaction/tool-admission.d.ts +36 -0
  110. package/dist/core/extensions/builtin/compaction/tool-admission.d.ts.map +1 -0
  111. package/dist/core/extensions/builtin/compaction/tool-admission.js +92 -0
  112. package/dist/core/extensions/builtin/compaction/tool-admission.js.map +1 -0
  113. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts.map +1 -1
  114. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js +8 -28
  115. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js.map +1 -1
  116. package/dist/core/extensions/builtin/permission-system/external-dir.d.ts.map +1 -1
  117. package/dist/core/extensions/builtin/permission-system/external-dir.js +15 -5
  118. package/dist/core/extensions/builtin/permission-system/external-dir.js.map +1 -1
  119. package/dist/core/extensions/builtin/permission-system/parsers.d.ts.map +1 -1
  120. package/dist/core/extensions/builtin/permission-system/parsers.js +16 -1
  121. package/dist/core/extensions/builtin/permission-system/parsers.js.map +1 -1
  122. package/dist/core/extensions/builtin/terminal/manager.d.ts +5 -0
  123. package/dist/core/extensions/builtin/terminal/manager.d.ts.map +1 -1
  124. package/dist/core/extensions/builtin/terminal/manager.js +31 -3
  125. package/dist/core/extensions/builtin/terminal/manager.js.map +1 -1
  126. package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts +8 -0
  127. package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts.map +1 -0
  128. package/dist/core/extensions/builtin/terminal/monitor-permission.js +16 -0
  129. package/dist/core/extensions/builtin/terminal/monitor-permission.js.map +1 -0
  130. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +13 -0
  131. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
  132. package/dist/core/extensions/builtin/terminal/monitor-registry.js +403 -3
  133. package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
  134. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts.map +1 -1
  135. package/dist/core/extensions/builtin/terminal/session-bundle.js +1 -0
  136. package/dist/core/extensions/builtin/terminal/session-bundle.js.map +1 -1
  137. package/dist/core/extensions/builtin/terminal/tools/kill-bash.d.ts.map +1 -1
  138. package/dist/core/extensions/builtin/terminal/tools/kill-bash.js +5 -2
  139. package/dist/core/extensions/builtin/terminal/tools/kill-bash.js.map +1 -1
  140. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts +4 -0
  141. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
  142. package/dist/core/extensions/builtin/terminal/tools/monitor.js +40 -2
  143. package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
  144. package/dist/core/model-resolver.js +2 -2
  145. package/dist/core/model-resolver.js.map +1 -1
  146. package/dist/core/session-manager.d.ts.map +1 -1
  147. package/dist/core/session-manager.js +10 -1
  148. package/dist/core/session-manager.js.map +1 -1
  149. package/dist/core/settings-manager.d.ts +15 -85
  150. package/dist/core/settings-manager.d.ts.map +1 -1
  151. package/dist/core/settings-manager.js +15 -17
  152. package/dist/core/settings-manager.js.map +1 -1
  153. package/dist/core/settings-public-types.d.ts +11 -0
  154. package/dist/core/settings-public-types.d.ts.map +1 -0
  155. package/dist/core/settings-public-types.js +2 -0
  156. package/dist/core/settings-public-types.js.map +1 -0
  157. package/dist/core/settings-shapes.d.ts +36 -0
  158. package/dist/core/settings-shapes.d.ts.map +1 -0
  159. package/dist/core/settings-shapes.js +2 -0
  160. package/dist/core/settings-shapes.js.map +1 -0
  161. package/dist/core/shared-host-policy.d.ts +6 -0
  162. package/dist/core/shared-host-policy.d.ts.map +1 -0
  163. package/dist/core/shared-host-policy.js +7 -0
  164. package/dist/core/shared-host-policy.js.map +1 -0
  165. package/dist/core/terminal-settings.d.ts +22 -0
  166. package/dist/core/terminal-settings.d.ts.map +1 -0
  167. package/dist/core/terminal-settings.js +2 -0
  168. package/dist/core/terminal-settings.js.map +1 -0
  169. package/dist/core/tools/bash.d.ts.map +1 -1
  170. package/dist/core/tools/bash.js +97 -15
  171. package/dist/core/tools/bash.js.map +1 -1
  172. package/dist/core/tools/index.d.ts +1 -0
  173. package/dist/core/tools/index.d.ts.map +1 -1
  174. package/dist/core/tools/index.js +8 -0
  175. package/dist/core/tools/index.js.map +1 -1
  176. package/dist/core/tools/output-accumulator.d.ts +1 -1
  177. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  178. package/dist/core/tools/output-accumulator.js +76 -34
  179. package/dist/core/tools/output-accumulator.js.map +1 -1
  180. package/dist/main.d.ts.map +1 -1
  181. package/dist/main.js +17 -5
  182. package/dist/main.js.map +1 -1
  183. package/dist/modes/interactive/components/footer.d.ts +7 -0
  184. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  185. package/dist/modes/interactive/components/footer.js +34 -8
  186. package/dist/modes/interactive/components/footer.js.map +1 -1
  187. package/dist/modes/interactive/grok/chrome.d.ts +2 -0
  188. package/dist/modes/interactive/grok/chrome.d.ts.map +1 -1
  189. package/dist/modes/interactive/grok/chrome.js.map +1 -1
  190. package/dist/modes/interactive/interactive-host-runtime.d.ts +121 -1
  191. package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
  192. package/dist/modes/interactive/interactive-host-runtime.js +336 -65
  193. package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
  194. package/dist/modes/interactive/interactive-mode.d.ts +7 -0
  195. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  196. package/dist/modes/interactive/interactive-mode.js +114 -2
  197. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  198. package/dist/modes/rpc/connection-handler.d.ts +8 -1
  199. package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
  200. package/dist/modes/rpc/connection-handler.js +145 -49
  201. package/dist/modes/rpc/connection-handler.js.map +1 -1
  202. package/dist/modes/rpc/host-ensure.d.ts +19 -0
  203. package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
  204. package/dist/modes/rpc/host-ensure.js +79 -15
  205. package/dist/modes/rpc/host-ensure.js.map +1 -1
  206. package/dist/modes/rpc/host-lifecycle.d.ts +29 -0
  207. package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
  208. package/dist/modes/rpc/host-lifecycle.js +96 -25
  209. package/dist/modes/rpc/host-lifecycle.js.map +1 -1
  210. package/dist/modes/rpc/host-watchdog.d.ts +2 -0
  211. package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
  212. package/dist/modes/rpc/host-watchdog.js +23 -12
  213. package/dist/modes/rpc/host-watchdog.js.map +1 -1
  214. package/dist/modes/rpc/multi-session-host.js +6 -4
  215. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  216. package/dist/modes/rpc/rpc-client.d.ts +10 -4
  217. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  218. package/dist/modes/rpc/rpc-client.js +47 -10
  219. package/dist/modes/rpc/rpc-client.js.map +1 -1
  220. package/dist/modes/rpc/rpc-input-validation.d.ts +1 -0
  221. package/dist/modes/rpc/rpc-input-validation.d.ts.map +1 -1
  222. package/dist/modes/rpc/rpc-input-validation.js +66 -0
  223. package/dist/modes/rpc/rpc-input-validation.js.map +1 -1
  224. package/dist/modes/rpc/rpc-types.d.ts +86 -1
  225. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  226. package/dist/modes/rpc/rpc-types.js.map +1 -1
  227. package/dist/modes/rpc/session-binding.d.ts +1 -0
  228. package/dist/modes/rpc/session-binding.d.ts.map +1 -1
  229. package/dist/modes/rpc/session-binding.js +21 -1
  230. package/dist/modes/rpc/session-binding.js.map +1 -1
  231. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  232. package/dist/modes/rpc/session-command-router.js +1 -0
  233. package/dist/modes/rpc/session-command-router.js.map +1 -1
  234. package/dist/modes/rpc/session-event-writer.d.ts +2 -0
  235. package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
  236. package/dist/modes/rpc/session-event-writer.js +54 -7
  237. package/dist/modes/rpc/session-event-writer.js.map +1 -1
  238. package/dist/modes/rpc/session-extension-ui-requests.d.ts +1 -0
  239. package/dist/modes/rpc/session-extension-ui-requests.d.ts.map +1 -1
  240. package/dist/modes/rpc/session-extension-ui-requests.js +5 -0
  241. package/dist/modes/rpc/session-extension-ui-requests.js.map +1 -1
  242. package/dist/modes/rpc/session-registry.d.ts +10 -2
  243. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  244. package/dist/modes/rpc/session-registry.js +58 -4
  245. package/dist/modes/rpc/session-registry.js.map +1 -1
  246. package/dist/modes/rpc/socket-event-fanout.d.ts +22 -0
  247. package/dist/modes/rpc/socket-event-fanout.d.ts.map +1 -0
  248. package/dist/modes/rpc/socket-event-fanout.js +84 -0
  249. package/dist/modes/rpc/socket-event-fanout.js.map +1 -0
  250. package/docs/rpc.md +18 -2
  251. package/docs/settings.md +34 -0
  252. package/docs/windows.md +2 -0
  253. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +50 -0
  254. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  255. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-indirect-eval.js +139 -90
  256. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +4 -4
  257. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
  258. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js +40 -20
  259. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
  260. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts +3 -3
  261. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts.map +1 -1
  262. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js.map +1 -1
  263. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts +1 -1
  264. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts.map +1 -1
  265. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js +28 -9
  266. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.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/anthropic-messages.d.ts.map +1 -1
  269. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +67 -38
  270. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  271. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts +20 -0
  272. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts.map +1 -0
  273. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js +443 -0
  274. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js.map +1 -0
  275. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +2 -0
  276. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
  277. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +2 -0
  278. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
  279. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +1 -1
  280. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
  281. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
  282. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  283. package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
  284. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  285. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  286. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  287. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
  288. package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
  289. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  290. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  291. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  292. package/node_modules/@earendil-works/pi-ai/dist/providers/data/together.json +1 -1
  293. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai-coding-cn.json +1 -1
  294. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai.json +1 -1
  295. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  296. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  297. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  298. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +7 -0
  299. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
  300. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +5 -0
  301. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
  302. package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts +1 -1
  303. package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts.map +1 -1
  304. package/node_modules/@earendil-works/pi-tui/dist/mux.js +2 -2
  305. package/node_modules/@earendil-works/pi-tui/dist/mux.js.map +1 -1
  306. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +1 -0
  307. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
  308. package/node_modules/@earendil-works/pi-tui/dist/terminal.js +19 -0
  309. package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
  310. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  311. package/package.json +7 -7
@@ -1,3 +1,5 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { rm } from "node:fs/promises";
1
3
  import { executeBashWithOperations } from "../../core/bash-executor.js";
2
4
  import { SessionManager } from "../../core/session-manager.js";
3
5
  import { SettingsManager } from "../../core/settings-manager.js";
@@ -27,6 +29,14 @@ export async function createInteractiveHostRuntime(localRuntime, options) {
27
29
  thinkingLevel: localRuntime.session.thinkingLevel,
28
30
  });
29
31
  const remoteSession = createRemoteSessionProxy(localRuntime.session, localRuntime.services.agentDir, client, opened.state, options.onWarning);
32
+ if (opened.state.isBashRunning && !opened.attached) {
33
+ // Only a newly opened session can have an execution orphaned by a
34
+ // previous connection. An attach is an observer of the same live runtime;
35
+ // aborting it would cancel work owned by the surviving attachment.
36
+ await client.abortBash().catch(() => { });
37
+ }
38
+ if (opened.attached)
39
+ await remoteSession.refresh();
30
40
  return new RemoteInteractiveRuntime(localRuntime, remoteSession, client);
31
41
  }
32
42
  catch (cause) {
@@ -39,7 +49,7 @@ export async function createInteractiveHostRuntime(localRuntime, options) {
39
49
  return localRuntime;
40
50
  }
41
51
  }
42
- class RemoteInteractiveRuntime {
52
+ export class RemoteInteractiveRuntime {
43
53
  #local;
44
54
  #remoteSession;
45
55
  #client;
@@ -74,71 +84,104 @@ class RemoteInteractiveRuntime {
74
84
  setRebindSession(callback) {
75
85
  this.#rebindSession = callback;
76
86
  }
87
+ setHostUiHandler(callback) {
88
+ this.#remoteSession.setHostUiHandler(callback);
89
+ }
77
90
  async dispose() {
78
- await this.#client.closeSession();
79
- await this.#client.stop();
80
- await this.#local.dispose();
91
+ const errors = [];
92
+ for (const cleanup of [
93
+ () => this.#remoteSession.abortLocalBash(),
94
+ () => this.#client.closeSession(),
95
+ () => this.#client.stop(),
96
+ () => this.#local.dispose(),
97
+ ]) {
98
+ try {
99
+ await cleanup();
100
+ }
101
+ catch (error) {
102
+ errors.push(error);
103
+ }
104
+ }
105
+ if (errors.length > 0)
106
+ throw errors[0];
81
107
  }
82
108
  async newSession(options) {
83
- const result = await this.#client.newSession(options?.parentSession);
84
- if (!result.cancelled) {
85
- this.#beforeSessionInvalidate?.();
86
- this.#remoteSession.abortLocalBash();
87
- await this.#remoteSession.refresh();
88
- if (options?.setup) {
89
- const capture = SessionManager.inMemory(this.#remoteSession.session.sessionManager.getCwd());
90
- await options.setup(capture);
91
- for (const entry of capture.getEntries())
92
- await this.#client.appendSessionEntry(entry);
109
+ // Suppresses for the ENTIRE command, not just the refresh tail: a
110
+ // multi-session host emits session_replaced while completing it, so the echo
111
+ // can arrive before the refresh/rebind sequence below starts and race it -
112
+ // newSession transports setup entries between two refreshes.
113
+ return this.#remoteSession.aroundLocalReplacement(async () => {
114
+ const result = await this.#client.newSession(options?.parentSession);
115
+ if (!result.cancelled) {
116
+ this.#beforeSessionInvalidate?.();
117
+ this.#remoteSession.abortLocalBash();
93
118
  await this.#remoteSession.refresh();
119
+ if (options?.setup) {
120
+ const capture = SessionManager.inMemory(this.#remoteSession.session.sessionManager.getCwd());
121
+ await options.setup(capture);
122
+ for (const entry of capture.getEntries())
123
+ await this.#client.appendSessionEntry(entry);
124
+ await this.#remoteSession.refresh();
125
+ }
126
+ await this.#rebindSession?.();
127
+ if (options?.withSession)
128
+ await options.withSession(this.#remoteSession.createReplacedSessionContext());
94
129
  }
95
- await this.#rebindSession?.();
96
- if (options?.withSession)
97
- await options.withSession(this.#remoteSession.createReplacedSessionContext());
98
- }
99
- return result;
130
+ return result;
131
+ });
100
132
  }
101
133
  async switchSession(sessionPath, options) {
102
- const result = await this.#client.switchSession(sessionPath, options);
103
- if (!result.cancelled) {
104
- this.#beforeSessionInvalidate?.();
105
- this.#remoteSession.abortLocalBash();
106
- await this.#remoteSession.refresh();
107
- await this.#rebindSession?.();
108
- // The shared host already resolved trust; this callback is retained for
109
- // compatibility, but its result cannot override host-authoritative state.
110
- options?.projectTrustContextFactory?.(this.#remoteSession.session.sessionManager.getCwd());
111
- if (options?.withSession)
112
- await options.withSession(this.#remoteSession.createReplacedSessionContext());
113
- }
114
- return result;
134
+ // See newSession: the suppression must cover the command itself, since the
135
+ // host emits session_replaced while completing it.
136
+ return this.#remoteSession.aroundLocalReplacement(async () => {
137
+ const result = await this.#client.switchSession(sessionPath, options?.cwdOverride === undefined ? undefined : { cwdOverride: options.cwdOverride });
138
+ if (!result.cancelled) {
139
+ this.#beforeSessionInvalidate?.();
140
+ this.#remoteSession.abortLocalBash();
141
+ await this.#remoteSession.refresh();
142
+ await this.#rebindSession?.();
143
+ // The shared host already resolved trust; this callback is retained for
144
+ // compatibility, but its result cannot override host-authoritative state.
145
+ options?.projectTrustContextFactory?.(this.#remoteSession.session.sessionManager.getCwd());
146
+ if (options?.withSession)
147
+ await options.withSession(this.#remoteSession.createReplacedSessionContext());
148
+ }
149
+ return result;
150
+ });
115
151
  }
116
152
  async fork(entryId, options) {
117
- const result = await this.#client.fork(entryId, options);
118
- if (!result.cancelled) {
119
- this.#beforeSessionInvalidate?.();
120
- this.#remoteSession.abortLocalBash();
121
- await this.#refreshAndRebind();
122
- if (options?.withSession)
123
- await options.withSession(this.#remoteSession.createReplacedSessionContext());
124
- }
125
- return { cancelled: result.cancelled, selectedText: result.text };
153
+ // See newSession: the suppression must cover the command itself.
154
+ return this.#remoteSession.aroundLocalReplacement(async () => {
155
+ const result = await this.#client.fork(entryId, options);
156
+ if (!result.cancelled) {
157
+ this.#beforeSessionInvalidate?.();
158
+ this.#remoteSession.abortLocalBash();
159
+ await this.#refreshAndRebind();
160
+ if (options?.withSession)
161
+ await options.withSession(this.#remoteSession.createReplacedSessionContext());
162
+ }
163
+ return { cancelled: result.cancelled, selectedText: result.text };
164
+ });
126
165
  }
127
166
  async importFromJsonl(inputPath, cwdOverride) {
128
- const result = await this.#client.importJsonl(inputPath, cwdOverride);
129
- if (!result.cancelled) {
130
- this.#beforeSessionInvalidate?.();
131
- this.#remoteSession.abortLocalBash();
132
- await this.#refreshAndRebind();
133
- }
134
- return result;
167
+ // See newSession: the suppression must cover the command itself.
168
+ return this.#remoteSession.aroundLocalReplacement(async () => {
169
+ const result = await this.#client.importJsonl(inputPath, cwdOverride);
170
+ if (!result.cancelled) {
171
+ this.#beforeSessionInvalidate?.();
172
+ this.#remoteSession.abortLocalBash();
173
+ await this.#refreshAndRebind();
174
+ }
175
+ return result;
176
+ });
135
177
  }
136
178
  async #refreshAndRebind() {
137
179
  await this.#remoteSession.refresh();
138
180
  await this.#rebindSession?.();
139
181
  }
140
182
  }
141
- function createRemoteSessionProxy(local, agentDir, client, initialState, onWarning) {
183
+ /** Exported for direct coverage of the wire-event handling below. */
184
+ export function createRemoteSessionProxy(local, agentDir, client, initialState, onWarning) {
142
185
  // Fire-and-forget setters keep the sync AgentSession signature, but their RPC
143
186
  // failures must not vanish: the matching *_changed wire event confirms success,
144
187
  // and a rejection here is the only signal of failure.
@@ -150,10 +193,65 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
150
193
  });
151
194
  };
152
195
  let state = { ...initialState };
153
- let bashChunk;
196
+ const waitForBashCallbacks = async (promises, allowAbandonment) => {
197
+ if (promises.size === 0)
198
+ return true;
199
+ const settled = Promise.allSettled([...promises]).then(() => true);
200
+ let timeout;
201
+ const bounded = new Promise((resolve) => {
202
+ timeout = setTimeout(resolve, allowAbandonment ? 100 : 5_000, false);
203
+ timeout.unref?.();
204
+ });
205
+ try {
206
+ return await Promise.race([settled, bounded]);
207
+ }
208
+ finally {
209
+ if (timeout)
210
+ clearTimeout(timeout);
211
+ }
212
+ };
213
+ const executionNamespace = randomUUID();
214
+ const cleanupRemoteBashOutput = async (path) => {
215
+ let cleanupTimeout;
216
+ try {
217
+ await Promise.race([
218
+ client.cleanupBashOutput(path),
219
+ new Promise((_, reject) => {
220
+ cleanupTimeout = setTimeout(() => reject(new Error("RPC cleanup timed out")), 1_000);
221
+ }),
222
+ ]);
223
+ return;
224
+ }
225
+ catch (transportError) {
226
+ // Spill paths are shared host filesystem paths. If the RPC transport is
227
+ // already unavailable, remove locally as a bounded best-effort fallback;
228
+ // a later reconnect is not required to prevent this callback failure from
229
+ // permanently leaking the host-owned artifact.
230
+ try {
231
+ await rm(path, { force: true });
232
+ return;
233
+ }
234
+ catch (localError) {
235
+ onWarning?.({
236
+ type: "interactive_host_action_failed",
237
+ message: `Warning: failed to clean up shared bash output spill: ${localError instanceof Error ? localError.message : String(localError)}`,
238
+ cause: new AggregateError([transportError, localError]),
239
+ });
240
+ }
241
+ }
242
+ finally {
243
+ if (cleanupTimeout)
244
+ clearTimeout(cleanupTimeout);
245
+ }
246
+ };
247
+ let nextBashExecutionId = 0;
248
+ const bashExecutions = new Map();
249
+ let hostUiHandler;
250
+ const pendingUiRequests = [];
154
251
  let localBashAbortController;
155
252
  let localBashRunning = false;
156
253
  let hostBashRunning = initialState.isBashRunning;
254
+ let nextQueuedInputOrder = Math.max(0, ...initialState.ordered.map((item) => item.enqueueOrder));
157
255
  let sessionManager = local.sessionManager;
158
256
  let settingsManager = SettingsManager.create(initialState.cwd, agentDir, {
159
257
  projectTrusted: initialState.projectTrusted,
@@ -166,6 +264,8 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
166
264
  if (property === "appendLabelChange") {
167
265
  return (entryId, label) => void client.setLabel(entryId, label);
168
266
  }
267
+ if (property === "getCwd")
268
+ return () => state.cwd;
169
269
  if (property === "getSessionName")
170
270
  return () => state.sessionName;
171
271
  if (property === "getUsageTotals")
@@ -176,8 +276,36 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
176
276
  });
177
277
  let streamingAssistant;
178
278
  let mirroredCurrentAssistantUsage = false;
279
+ /** Non-zero while this runtime is driving its own replacement sequence. */
280
+ let localReplacementDepth = 0;
179
281
  const listeners = new Set();
180
282
  client.onEvent((wireEvent) => {
283
+ if (wireEvent.type === "extension_ui_request") {
284
+ const request = wireEvent;
285
+ if (hostUiHandler)
286
+ void Promise.resolve(hostUiHandler(request)).then((response) => response && client.sendExtensionUIResponse(response));
287
+ else
288
+ pendingUiRequests.push(request);
289
+ return;
290
+ }
291
+ if (wireEvent.type === "session_replaced") {
292
+ // The host swapped the live session behind this connection - another
293
+ // attached client issued the replacement, or an extension drove one that no
294
+ // client issued. Nothing else re-reads the binding, so without this the
295
+ // proxy keeps serving the previous session's manager, settings and message
296
+ // mirror while the host has already moved on. The command response carries
297
+ // only `{ cancelled }`, so this event is the only signal available here.
298
+ //
299
+ // A multi-session host broadcasts the event to every connection including
300
+ // the one that issued the replacement, and this runtime's own replacement
301
+ // methods already run an ordered refresh/rebind - newSession additionally
302
+ // transports setup entries between two refreshes. Refreshing again from
303
+ // here would race that sequence, so self-driven replacements are skipped.
304
+ if (localReplacementDepth === 0) {
305
+ void refresh().catch(reportActionFailure("session refresh after replacement"));
306
+ }
307
+ return;
308
+ }
181
309
  if (wireEvent.type === "agent_settled")
182
310
  state = { ...state, isStreaming: false, retryAttempt: 0 };
183
311
  if (wireEvent.type === "bash_start") {
@@ -188,8 +316,30 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
188
316
  hostBashRunning = false;
189
317
  updateBashState();
190
318
  }
191
- if (wireEvent.type === "bash_execution_update")
192
- bashChunk?.(wireEvent.delta);
319
+ if (wireEvent.type === "bash_execution_update" && wireEvent.id) {
320
+ const execution = bashExecutions.get(wireEvent.id);
321
+ if (execution?.chunk && !execution.hasError) {
322
+ try {
323
+ const callbackPromise = Promise.resolve(execution.chunk(wireEvent.delta)).catch((error) => {
324
+ if (!execution.hasError) {
325
+ execution.error = error;
326
+ execution.hasError = true;
327
+ void client.abortBash().catch(() => { });
328
+ }
329
+ throw error;
330
+ });
331
+ execution.promises.add(callbackPromise);
332
+ void callbackPromise.catch(() => { }).finally(() => execution.promises.delete(callbackPromise));
333
+ }
334
+ catch (error) {
335
+ if (!execution.hasError) {
336
+ execution.error = error;
337
+ execution.hasError = true;
338
+ void client.abortBash().catch(() => { });
339
+ }
340
+ }
341
+ }
342
+ }
193
343
  if (wireEvent.type === "agent_start")
194
344
  state = { ...state, isStreaming: true };
195
345
  if (wireEvent.type === "compaction_start")
@@ -201,6 +351,7 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
201
351
  if (wireEvent.type === "auto_retry_end")
202
352
  state = { ...state, retryAttempt: 0 };
203
353
  if (wireEvent.type === "queue_update") {
354
+ nextQueuedInputOrder = Math.max(nextQueuedInputOrder, ...wireEvent.ordered.map((item) => item.enqueueOrder));
204
355
  state = {
205
356
  ...state,
206
357
  steering: [...wireEvent.steering],
@@ -217,6 +368,23 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
217
368
  if (wireEvent.type === "service_tier_changed") {
218
369
  state = { ...state, serviceTier: wireEvent.tier, fastMode: wireEvent.fastMode };
219
370
  }
371
+ if (wireEvent.type === "session_settings_changed") {
372
+ state = {
373
+ ...state,
374
+ steeringMode: wireEvent.steeringMode,
375
+ followUpMode: wireEvent.followUpMode,
376
+ autoCompactionEnabled: wireEvent.autoCompactionEnabled,
377
+ };
378
+ }
379
+ if (wireEvent.type === "entry_appended") {
380
+ try {
381
+ sessionManager.appendEntry(wireEvent.entry);
382
+ local.agent.state.messages = sessionManager.buildSessionContext().messages;
383
+ }
384
+ catch {
385
+ // Non-fatal if the local snapshot cannot accept a concurrent entry.
386
+ }
387
+ }
220
388
  if (wireEvent.type === "session_info_changed")
221
389
  state = { ...state, sessionName: wireEvent.name };
222
390
  if (wireEvent.type === "message_start") {
@@ -233,8 +401,13 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
233
401
  if (usage && !mirroredCurrentAssistantUsage) {
234
402
  mirroredCurrentAssistantUsage = true;
235
403
  const latestPromptTokens = usage.input + usage.cacheRead + usage.cacheWrite;
404
+ const contextWindow = state.model?.contextWindow ?? 0;
405
+ const contextTokens = usage.input + usage.cacheRead + usage.cacheWrite;
236
406
  state = {
237
407
  ...state,
408
+ contextUsage: contextWindow > 0
409
+ ? { tokens: contextTokens, contextWindow, percent: (contextTokens / contextWindow) * 100 }
410
+ : undefined,
238
411
  usageTotals: {
239
412
  ...state.usageTotals,
240
413
  input: state.usageTotals.input + usage.input,
@@ -254,7 +427,9 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
254
427
  }
255
428
  if (wireEvent.type === "compaction_end" && wireEvent.accepted && !wireEvent.aborted) {
256
429
  try {
257
- sessionManager.reloadFromDisk?.();
430
+ local.sessionManager.reloadFromDisk?.();
431
+ if (sessionManager !== local.sessionManager)
432
+ sessionManager.reloadFromDisk?.();
258
433
  local.agent.state.messages = sessionManager.buildSessionContext().messages;
259
434
  }
260
435
  catch {
@@ -265,17 +440,26 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
265
440
  for (const listener of listeners)
266
441
  listener(event);
267
442
  });
268
- const refresh = async () => {
443
+ const performRefresh = async () => {
269
444
  const nextState = await client.getState();
270
445
  state = { ...stateFromRpc(nextState) };
446
+ nextQueuedInputOrder = Math.max(0, ...nextState.ordered.map((item) => item.enqueueOrder));
271
447
  let messages;
272
448
  settingsManager = SettingsManager.create(nextState.cwd, agentDir, {
273
449
  projectTrusted: nextState.projectTrusted,
274
450
  });
275
451
  if (nextState.sessionFile) {
276
- // SessionManager.open retains the explicit path even when the host has
277
- // deferred creating the file for a setup-only session.
278
- sessionManager = SessionManager.open(nextState.sessionFile, undefined, nextState.cwd);
452
+ // Keep the caller-owned manager identity when refreshing the same session.
453
+ // Navigation and host-side mutations must update every existing mirror, not
454
+ // leave the interactive runtime holding the pre-refresh snapshot.
455
+ if (sessionManager.getSessionFile() === nextState.sessionFile) {
456
+ sessionManager.reloadFromDisk?.();
457
+ }
458
+ else {
459
+ // SessionManager.open retains the explicit path even when the host has
460
+ // deferred creating the file for a setup-only session.
461
+ sessionManager = SessionManager.open(nextState.sessionFile, undefined, nextState.cwd);
462
+ }
279
463
  if (nextState.entries?.length && !sessionManager.getEntries().length) {
280
464
  for (const entry of nextState.entries)
281
465
  sessionManager.appendEntry(entry);
@@ -288,6 +472,16 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
288
472
  local.agent.state.messages.splice(0, local.agent.state.messages.length, ...structuredClone(messages));
289
473
  streamingAssistant = undefined;
290
474
  };
475
+ // Refreshes must not interleave: each one reassigns sessionManager,
476
+ // settingsManager and the mirrored message list, so two in flight can commit a
477
+ // mixed snapshot of two different sessions. A replacement-driven refresh races
478
+ // exactly that way against a caller-driven one, so run them in order.
479
+ let refreshChain = Promise.resolve();
480
+ const refresh = () => {
481
+ const next = refreshChain.then(performRefresh, performRefresh);
482
+ refreshChain = next.then(() => { }, () => { });
483
+ return next;
484
+ };
291
485
  const session = new Proxy(local, {
292
486
  get(target, property, receiver) {
293
487
  if (property === "prompt")
@@ -295,6 +489,12 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
295
489
  ...(options?.images ? { images: options.images } : {}),
296
490
  ...(options?.streamingBehavior ? { streamingBehavior: options.streamingBehavior } : {}),
297
491
  ...(options?.thinkingLevel ? { thinkingLevel: options.thinkingLevel } : {}),
492
+ ...(options?.sessionTitlePrompt !== undefined
493
+ ? { sessionTitlePrompt: options.sessionTitlePrompt }
494
+ : {}),
495
+ ...(options?.expandPromptTemplates !== undefined
496
+ ? { expandPromptTemplates: options.expandPromptTemplates }
497
+ : {}),
298
498
  ...(options?.promptDisposition ? { promptDisposition: options.promptDisposition } : {}),
299
499
  ...(options?.preflightResult ? { preflightResult: options.preflightResult } : {}),
300
500
  });
@@ -328,13 +528,35 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
328
528
  return () => client.waitForIdle();
329
529
  if (property === "getLastAssistantText")
330
530
  return () => target.getLastAssistantText();
331
- if (property === "setModel")
531
+ if (property === "setModel" || property === "setSessionModel")
332
532
  return async (model) => {
333
533
  const next = await client.setModel(model.provider, model.id);
334
534
  return { systemPromptName: next.systemPromptName, model: next };
335
535
  };
536
+ if (property === "setSessionThinkingLevel")
537
+ return (level) => void client
538
+ .setThinkingLevel(level, { scope: "turn" })
539
+ .catch(reportActionFailure("setSessionThinkingLevel"));
540
+ if (property === "setSessionFastMode")
541
+ return (enabled) => void client.setFastMode(enabled).catch(reportActionFailure("setSessionFastMode"));
542
+ if (property === "abortRetry")
543
+ return () => void client.abortRetry().catch(reportActionFailure("abortRetry"));
544
+ if (property === "setAutoRetryEnabled")
545
+ return (enabled) => void client.setAutoRetry(enabled).catch(reportActionFailure("setAutoRetryEnabled"));
546
+ if (property === "reserveQueuedInputOrder")
547
+ return () => ++nextQueuedInputOrder;
548
+ if (property === "setFavoriteModels")
549
+ return (models) => {
550
+ state = { ...state, favoriteModels: [...models] };
551
+ void client.setFavoriteModels(models).catch(reportActionFailure("setFavoriteModels"));
552
+ };
553
+ if (property === "setScopedModels")
554
+ return (models) => {
555
+ state = { ...state, scopedModels: [...models] };
556
+ void client.setScopedModels(models).catch(reportActionFailure("setScopedModels"));
557
+ };
336
558
  if (property === "cycleModel")
337
- return () => client.cycleModel();
559
+ return (direction) => client.cycleModel(direction);
338
560
  if (property === "setThinkingLevel")
339
561
  return (level) => void client.setThinkingLevel(level).catch(reportActionFailure("setThinkingLevel"));
340
562
  if (property === "cycleThinkingLevel")
@@ -372,15 +594,35 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
372
594
  updateBashState();
373
595
  }
374
596
  }
375
- bashChunk = onChunk;
597
+ const executionId = `bash-${executionNamespace}-${++nextBashExecutionId}`;
598
+ const execution = {
599
+ chunk: onChunk,
600
+ promises: new Set(),
601
+ error: undefined,
602
+ hasError: false,
603
+ };
604
+ bashExecutions.set(executionId, execution);
376
605
  try {
377
- return await client.bash(command, {
606
+ const result = await client.bash(command, {
378
607
  excludeFromContext: options?.excludeFromContext,
608
+ executionId,
379
609
  operations: options?.operations,
380
610
  });
611
+ const callbacksSettled = await waitForBashCallbacks(execution.promises, false);
612
+ if (!callbacksSettled) {
613
+ if (result.fullOutputPath)
614
+ await cleanupRemoteBashOutput(result.fullOutputPath);
615
+ throw new Error("Bash output callback did not settle within 5000ms");
616
+ }
617
+ if (execution.hasError) {
618
+ if (result.fullOutputPath)
619
+ await cleanupRemoteBashOutput(result.fullOutputPath);
620
+ throw execution.error;
621
+ }
622
+ return result;
381
623
  }
382
624
  finally {
383
- bashChunk = undefined;
625
+ bashExecutions.delete(executionId);
384
626
  }
385
627
  };
386
628
  if (property === "abortBash")
@@ -390,10 +632,12 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
390
632
  else
391
633
  void client.abortBash().catch(reportActionFailure("abortBash"));
392
634
  };
635
+ if (property === "getContextUsage")
636
+ return () => state.contextUsage;
393
637
  if (property === "getSessionStats")
394
638
  return () => client.getSessionStats();
395
639
  if (property === "exportToHtml")
396
- return (outputPath) => client.exportHtml(outputPath).then((result) => result.path);
640
+ return (outputPath, options) => client.exportHtml(outputPath, options?.themeName).then((result) => result.path);
397
641
  if (property === "setSessionName")
398
642
  return (name) => client.setSessionName(name).catch(reportActionFailure("setSessionName"));
399
643
  if (property === "navigateTree")
@@ -416,10 +660,10 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
416
660
  };
417
661
  if (property === "isStreaming")
418
662
  return state.isStreaming;
663
+ if (property === "isIdle")
664
+ return !state.isStreaming;
419
665
  if (property === "isCompacting")
420
666
  return state.isCompacting;
421
- if (property === "isFastModeActive")
422
- return () => state.fastMode;
423
667
  if (property === "pendingMessageCount")
424
668
  return state.pendingMessageCount;
425
669
  if (property === "getSteeringMessages")
@@ -475,12 +719,24 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
475
719
  return state.sessionName;
476
720
  if (property === "serviceTier")
477
721
  return state.serviceTier;
722
+ if (property === "steeringMode")
723
+ return state.steeringMode;
724
+ if (property === "followUpMode")
725
+ return state.followUpMode;
726
+ if (property === "autoCompactionEnabled")
727
+ return state.autoCompactionEnabled;
728
+ if (property === "isFastModeActive")
729
+ return () => state.fastMode;
478
730
  if (property === "sessionManager")
479
731
  return remoteSessionManager;
480
732
  if (property === "settingsManager")
481
733
  return settingsManager;
482
734
  if (property === "messages")
483
735
  return target.messages;
736
+ if (property === "favoriteModels")
737
+ return state.favoriteModels;
738
+ if (property === "scopedModels")
739
+ return state.scopedModels;
484
740
  if (property === "model")
485
741
  return state.model ?? target.model;
486
742
  if (property === "thinkingLevel")
@@ -490,7 +746,22 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
490
746
  });
491
747
  return {
492
748
  session,
749
+ setHostUiHandler: (callback) => {
750
+ hostUiHandler = callback;
751
+ if (callback)
752
+ for (const request of pendingUiRequests.splice(0))
753
+ void Promise.resolve(callback(request)).then((response) => response && client.sendExtensionUIResponse(response));
754
+ },
493
755
  refresh,
756
+ aroundLocalReplacement: async (body) => {
757
+ localReplacementDepth++;
758
+ try {
759
+ return await body();
760
+ }
761
+ finally {
762
+ localReplacementDepth--;
763
+ }
764
+ },
494
765
  abortLocalBash: () => localBashAbortController?.abort(),
495
766
  createReplacedSessionContext: () => {
496
767
  const context = local.createReplacedSessionContext();