@code-yeongyu/senpi 2026.8.28 → 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 (380) hide show
  1. package/CHANGELOG.md +196 -0
  2. package/dist/cli/args.d.ts +2 -0
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +4 -0
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/core/agent-session-services.d.ts.map +1 -1
  7. package/dist/core/agent-session-services.js +2 -0
  8. package/dist/core/agent-session-services.js.map +1 -1
  9. package/dist/core/agent-session.d.ts +50 -1
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +330 -29
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/auth-storage.d.ts +1 -0
  14. package/dist/core/auth-storage.d.ts.map +1 -1
  15. package/dist/core/auth-storage.js +3 -0
  16. package/dist/core/auth-storage.js.map +1 -1
  17. package/dist/core/bash-executor.d.ts.map +1 -1
  18. package/dist/core/bash-executor.js +155 -37
  19. package/dist/core/bash-executor.js.map +1 -1
  20. package/dist/core/compaction/compaction-execution.d.ts +14 -0
  21. package/dist/core/compaction/compaction-execution.d.ts.map +1 -0
  22. package/dist/core/compaction/compaction-execution.js +121 -0
  23. package/dist/core/compaction/compaction-execution.js.map +1 -0
  24. package/dist/core/compaction/compaction-settings.d.ts +17 -0
  25. package/dist/core/compaction/compaction-settings.d.ts.map +1 -0
  26. package/dist/core/compaction/compaction-settings.js +17 -0
  27. package/dist/core/compaction/compaction-settings.js.map +1 -0
  28. package/dist/core/compaction/compaction.d.ts +13 -26
  29. package/dist/core/compaction/compaction.d.ts.map +1 -1
  30. package/dist/core/compaction/compaction.js +7 -136
  31. package/dist/core/compaction/compaction.js.map +1 -1
  32. package/dist/core/compaction/ideal-compaction-settings.d.ts +9 -0
  33. package/dist/core/compaction/ideal-compaction-settings.d.ts.map +1 -0
  34. package/dist/core/compaction/ideal-compaction-settings.js +7 -0
  35. package/dist/core/compaction/ideal-compaction-settings.js.map +1 -0
  36. package/dist/core/compaction-settings-access.d.ts +19 -0
  37. package/dist/core/compaction-settings-access.d.ts.map +1 -0
  38. package/dist/core/compaction-settings-access.js +10 -0
  39. package/dist/core/compaction-settings-access.js.map +1 -0
  40. package/dist/core/compaction-settings-resolver.d.ts +22 -0
  41. package/dist/core/compaction-settings-resolver.d.ts.map +1 -0
  42. package/dist/core/compaction-settings-resolver.js +46 -0
  43. package/dist/core/compaction-settings-resolver.js.map +1 -0
  44. package/dist/core/credential-accounts.d.ts.map +1 -1
  45. package/dist/core/credential-accounts.js +45 -21
  46. package/dist/core/credential-accounts.js.map +1 -1
  47. package/dist/core/credential-pool/classify.d.ts +6 -1
  48. package/dist/core/credential-pool/classify.d.ts.map +1 -1
  49. package/dist/core/credential-pool/classify.js +22 -5
  50. package/dist/core/credential-pool/classify.js.map +1 -1
  51. package/dist/core/credential-pool/env-slots.d.ts.map +1 -1
  52. package/dist/core/credential-pool/env-slots.js +2 -0
  53. package/dist/core/credential-pool/env-slots.js.map +1 -1
  54. package/dist/core/credential-pool/failover.d.ts +8 -0
  55. package/dist/core/credential-pool/failover.d.ts.map +1 -1
  56. package/dist/core/credential-pool/failover.js +24 -6
  57. package/dist/core/credential-pool/failover.js.map +1 -1
  58. package/dist/core/credential-pool/rotation-stream.d.ts +13 -2
  59. package/dist/core/credential-pool/rotation-stream.d.ts.map +1 -1
  60. package/dist/core/credential-pool/rotation-stream.js +97 -10
  61. package/dist/core/credential-pool/rotation-stream.js.map +1 -1
  62. package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts +17 -0
  63. package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts.map +1 -0
  64. package/dist/core/extensions/builtin/compaction/context-pipeline.js +28 -0
  65. package/dist/core/extensions/builtin/compaction/context-pipeline.js.map +1 -0
  66. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +8 -2
  67. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
  68. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +163 -48
  69. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
  70. package/dist/core/extensions/builtin/compaction/emergency-prune.d.ts +17 -0
  71. package/dist/core/extensions/builtin/compaction/emergency-prune.d.ts.map +1 -0
  72. package/dist/core/extensions/builtin/compaction/emergency-prune.js +64 -0
  73. package/dist/core/extensions/builtin/compaction/emergency-prune.js.map +1 -0
  74. package/dist/core/extensions/builtin/compaction/extension-wiring.d.ts +27 -0
  75. package/dist/core/extensions/builtin/compaction/extension-wiring.d.ts.map +1 -0
  76. package/dist/core/extensions/builtin/compaction/extension-wiring.js +84 -0
  77. package/dist/core/extensions/builtin/compaction/extension-wiring.js.map +1 -0
  78. package/dist/core/extensions/builtin/compaction/idle.d.ts +1 -0
  79. package/dist/core/extensions/builtin/compaction/idle.d.ts.map +1 -1
  80. package/dist/core/extensions/builtin/compaction/idle.js +13 -2
  81. package/dist/core/extensions/builtin/compaction/idle.js.map +1 -1
  82. package/dist/core/extensions/builtin/compaction/index.d.ts +1 -1
  83. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  84. package/dist/core/extensions/builtin/compaction/index.js +121 -147
  85. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  86. package/dist/core/extensions/builtin/compaction/log.d.ts +1 -1
  87. package/dist/core/extensions/builtin/compaction/log.d.ts.map +1 -1
  88. package/dist/core/extensions/builtin/compaction/log.js +2 -0
  89. package/dist/core/extensions/builtin/compaction/log.js.map +1 -1
  90. package/dist/core/extensions/builtin/compaction/model-selection.d.ts +14 -0
  91. package/dist/core/extensions/builtin/compaction/model-selection.d.ts.map +1 -0
  92. package/dist/core/extensions/builtin/compaction/model-selection.js +37 -0
  93. package/dist/core/extensions/builtin/compaction/model-selection.js.map +1 -0
  94. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.d.ts +9 -0
  95. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.d.ts.map +1 -0
  96. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.js +2 -0
  97. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.js.map +1 -0
  98. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +3 -6
  99. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts.map +1 -1
  100. package/dist/core/extensions/builtin/compaction/openai-remote.js.map +1 -1
  101. package/dist/core/extensions/builtin/compaction/orchestration.d.ts +56 -0
  102. package/dist/core/extensions/builtin/compaction/orchestration.d.ts.map +1 -0
  103. package/dist/core/extensions/builtin/compaction/orchestration.js +136 -0
  104. package/dist/core/extensions/builtin/compaction/orchestration.js.map +1 -0
  105. package/dist/core/extensions/builtin/compaction/policy.d.ts +5 -2
  106. package/dist/core/extensions/builtin/compaction/policy.d.ts.map +1 -1
  107. package/dist/core/extensions/builtin/compaction/policy.js +43 -26
  108. package/dist/core/extensions/builtin/compaction/policy.js.map +1 -1
  109. package/dist/core/extensions/builtin/compaction/retained-message-safety.d.ts.map +1 -1
  110. package/dist/core/extensions/builtin/compaction/retained-message-safety.js +31 -8
  111. package/dist/core/extensions/builtin/compaction/retained-message-safety.js.map +1 -1
  112. package/dist/core/extensions/builtin/compaction/speculation-lead.d.ts +8 -0
  113. package/dist/core/extensions/builtin/compaction/speculation-lead.d.ts.map +1 -0
  114. package/dist/core/extensions/builtin/compaction/speculation-lead.js +17 -0
  115. package/dist/core/extensions/builtin/compaction/speculation-lead.js.map +1 -0
  116. package/dist/core/extensions/builtin/compaction/speculative-job.d.ts +22 -0
  117. package/dist/core/extensions/builtin/compaction/speculative-job.d.ts.map +1 -0
  118. package/dist/core/extensions/builtin/compaction/speculative-job.js +16 -0
  119. package/dist/core/extensions/builtin/compaction/speculative-job.js.map +1 -0
  120. package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts +22 -0
  121. package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts.map +1 -0
  122. package/dist/core/extensions/builtin/compaction/speculative-summary.js +131 -0
  123. package/dist/core/extensions/builtin/compaction/speculative-summary.js.map +1 -0
  124. package/dist/core/extensions/builtin/compaction/speculative.d.ts +8 -13
  125. package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
  126. package/dist/core/extensions/builtin/compaction/speculative.js +7 -196
  127. package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
  128. package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts +18 -0
  129. package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts.map +1 -0
  130. package/dist/core/extensions/builtin/compaction/token-budget-reminder.js +18 -0
  131. package/dist/core/extensions/builtin/compaction/token-budget-reminder.js.map +1 -0
  132. package/dist/core/extensions/builtin/compaction/tool-admission.d.ts +36 -0
  133. package/dist/core/extensions/builtin/compaction/tool-admission.d.ts.map +1 -0
  134. package/dist/core/extensions/builtin/compaction/tool-admission.js +92 -0
  135. package/dist/core/extensions/builtin/compaction/tool-admission.js.map +1 -0
  136. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts.map +1 -1
  137. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js +8 -28
  138. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js.map +1 -1
  139. package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -1
  140. package/dist/core/extensions/builtin/goal/index.js +7 -0
  141. package/dist/core/extensions/builtin/goal/index.js.map +1 -1
  142. package/dist/core/extensions/builtin/permission-system/external-dir.d.ts.map +1 -1
  143. package/dist/core/extensions/builtin/permission-system/external-dir.js +15 -5
  144. package/dist/core/extensions/builtin/permission-system/external-dir.js.map +1 -1
  145. package/dist/core/extensions/builtin/permission-system/parsers.d.ts.map +1 -1
  146. package/dist/core/extensions/builtin/permission-system/parsers.js +16 -1
  147. package/dist/core/extensions/builtin/permission-system/parsers.js.map +1 -1
  148. package/dist/core/extensions/builtin/service-tier.d.ts.map +1 -1
  149. package/dist/core/extensions/builtin/service-tier.js +13 -0
  150. package/dist/core/extensions/builtin/service-tier.js.map +1 -1
  151. package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
  152. package/dist/core/extensions/builtin/terminal/extension.js +4 -2
  153. package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
  154. package/dist/core/extensions/builtin/terminal/manager.d.ts +5 -0
  155. package/dist/core/extensions/builtin/terminal/manager.d.ts.map +1 -1
  156. package/dist/core/extensions/builtin/terminal/manager.js +31 -3
  157. package/dist/core/extensions/builtin/terminal/manager.js.map +1 -1
  158. package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts +8 -0
  159. package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts.map +1 -0
  160. package/dist/core/extensions/builtin/terminal/monitor-permission.js +16 -0
  161. package/dist/core/extensions/builtin/terminal/monitor-permission.js.map +1 -0
  162. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +13 -0
  163. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
  164. package/dist/core/extensions/builtin/terminal/monitor-registry.js +403 -3
  165. package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
  166. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts.map +1 -1
  167. package/dist/core/extensions/builtin/terminal/session-bundle.js +1 -0
  168. package/dist/core/extensions/builtin/terminal/session-bundle.js.map +1 -1
  169. package/dist/core/extensions/builtin/terminal/tools/kill-bash.d.ts.map +1 -1
  170. package/dist/core/extensions/builtin/terminal/tools/kill-bash.js +5 -2
  171. package/dist/core/extensions/builtin/terminal/tools/kill-bash.js.map +1 -1
  172. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts +4 -0
  173. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
  174. package/dist/core/extensions/builtin/terminal/tools/monitor.js +40 -2
  175. package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
  176. package/dist/core/model-config-schema.d.ts +4 -4
  177. package/dist/core/model-resolver.js +2 -2
  178. package/dist/core/model-resolver.js.map +1 -1
  179. package/dist/core/model-runtime.d.ts +2 -0
  180. package/dist/core/model-runtime.d.ts.map +1 -1
  181. package/dist/core/model-runtime.js +74 -37
  182. package/dist/core/model-runtime.js.map +1 -1
  183. package/dist/core/retry-fallback/chains.d.ts +23 -1
  184. package/dist/core/retry-fallback/chains.d.ts.map +1 -1
  185. package/dist/core/retry-fallback/chains.js +59 -3
  186. package/dist/core/retry-fallback/chains.js.map +1 -1
  187. package/dist/core/retry-fallback/controller.d.ts.map +1 -1
  188. package/dist/core/retry-fallback/controller.js +9 -2
  189. package/dist/core/retry-fallback/controller.js.map +1 -1
  190. package/dist/core/retry-fallback/settings.d.ts.map +1 -1
  191. package/dist/core/retry-fallback/settings.js +9 -0
  192. package/dist/core/retry-fallback/settings.js.map +1 -1
  193. package/dist/core/retry-fallback/validate.js +1 -1
  194. package/dist/core/retry-fallback/validate.js.map +1 -1
  195. package/dist/core/sdk.js +1 -1
  196. package/dist/core/sdk.js.map +1 -1
  197. package/dist/core/session-manager.d.ts +9 -0
  198. package/dist/core/session-manager.d.ts.map +1 -1
  199. package/dist/core/session-manager.js +40 -0
  200. package/dist/core/session-manager.js.map +1 -1
  201. package/dist/core/settings-manager.d.ts +15 -85
  202. package/dist/core/settings-manager.d.ts.map +1 -1
  203. package/dist/core/settings-manager.js +15 -17
  204. package/dist/core/settings-manager.js.map +1 -1
  205. package/dist/core/settings-public-types.d.ts +11 -0
  206. package/dist/core/settings-public-types.d.ts.map +1 -0
  207. package/dist/core/settings-public-types.js +2 -0
  208. package/dist/core/settings-public-types.js.map +1 -0
  209. package/dist/core/settings-shapes.d.ts +36 -0
  210. package/dist/core/settings-shapes.d.ts.map +1 -0
  211. package/dist/core/settings-shapes.js +2 -0
  212. package/dist/core/settings-shapes.js.map +1 -0
  213. package/dist/core/shared-host-policy.d.ts +6 -0
  214. package/dist/core/shared-host-policy.d.ts.map +1 -0
  215. package/dist/core/shared-host-policy.js +7 -0
  216. package/dist/core/shared-host-policy.js.map +1 -0
  217. package/dist/core/terminal-settings.d.ts +22 -0
  218. package/dist/core/terminal-settings.d.ts.map +1 -0
  219. package/dist/core/terminal-settings.js +2 -0
  220. package/dist/core/terminal-settings.js.map +1 -0
  221. package/dist/core/tools/bash.d.ts.map +1 -1
  222. package/dist/core/tools/bash.js +97 -15
  223. package/dist/core/tools/bash.js.map +1 -1
  224. package/dist/core/tools/index.d.ts +1 -0
  225. package/dist/core/tools/index.d.ts.map +1 -1
  226. package/dist/core/tools/index.js +8 -0
  227. package/dist/core/tools/index.js.map +1 -1
  228. package/dist/core/tools/output-accumulator.d.ts +1 -1
  229. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  230. package/dist/core/tools/output-accumulator.js +76 -34
  231. package/dist/core/tools/output-accumulator.js.map +1 -1
  232. package/dist/main.d.ts +7 -0
  233. package/dist/main.d.ts.map +1 -1
  234. package/dist/main.js +27 -6
  235. package/dist/main.js.map +1 -1
  236. package/dist/modes/interactive/components/favorite-models-selector.d.ts.map +1 -1
  237. package/dist/modes/interactive/components/favorite-models-selector.js +2 -2
  238. package/dist/modes/interactive/components/favorite-models-selector.js.map +1 -1
  239. package/dist/modes/interactive/components/footer.d.ts +7 -0
  240. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  241. package/dist/modes/interactive/components/footer.js +34 -8
  242. package/dist/modes/interactive/components/footer.js.map +1 -1
  243. package/dist/modes/interactive/grok/chrome.d.ts +2 -0
  244. package/dist/modes/interactive/grok/chrome.d.ts.map +1 -1
  245. package/dist/modes/interactive/grok/chrome.js.map +1 -1
  246. package/dist/modes/interactive/interactive-host-runtime.d.ts +121 -1
  247. package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
  248. package/dist/modes/interactive/interactive-host-runtime.js +606 -45
  249. package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
  250. package/dist/modes/interactive/interactive-mode.d.ts +7 -0
  251. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  252. package/dist/modes/interactive/interactive-mode.js +390 -239
  253. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  254. package/dist/modes/rpc/connection-handler.d.ts +8 -1
  255. package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
  256. package/dist/modes/rpc/connection-handler.js +268 -63
  257. package/dist/modes/rpc/connection-handler.js.map +1 -1
  258. package/dist/modes/rpc/host-ensure.d.ts +19 -0
  259. package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
  260. package/dist/modes/rpc/host-ensure.js +79 -15
  261. package/dist/modes/rpc/host-ensure.js.map +1 -1
  262. package/dist/modes/rpc/host-lifecycle.d.ts +29 -0
  263. package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
  264. package/dist/modes/rpc/host-lifecycle.js +96 -25
  265. package/dist/modes/rpc/host-lifecycle.js.map +1 -1
  266. package/dist/modes/rpc/host-watchdog.d.ts +2 -0
  267. package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
  268. package/dist/modes/rpc/host-watchdog.js +23 -12
  269. package/dist/modes/rpc/host-watchdog.js.map +1 -1
  270. package/dist/modes/rpc/multi-session-host.js +20 -8
  271. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  272. package/dist/modes/rpc/rpc-client.d.ts +91 -16
  273. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  274. package/dist/modes/rpc/rpc-client.js +131 -37
  275. package/dist/modes/rpc/rpc-client.js.map +1 -1
  276. package/dist/modes/rpc/rpc-input-validation.d.ts +1 -0
  277. package/dist/modes/rpc/rpc-input-validation.d.ts.map +1 -1
  278. package/dist/modes/rpc/rpc-input-validation.js +66 -0
  279. package/dist/modes/rpc/rpc-input-validation.js.map +1 -1
  280. package/dist/modes/rpc/rpc-mode.d.ts +3 -1
  281. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  282. package/dist/modes/rpc/rpc-mode.js +3 -1
  283. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  284. package/dist/modes/rpc/rpc-types.d.ts +248 -3
  285. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  286. package/dist/modes/rpc/rpc-types.js.map +1 -1
  287. package/dist/modes/rpc/session-binding.d.ts +1 -0
  288. package/dist/modes/rpc/session-binding.d.ts.map +1 -1
  289. package/dist/modes/rpc/session-binding.js +21 -1
  290. package/dist/modes/rpc/session-binding.js.map +1 -1
  291. package/dist/modes/rpc/session-command-router.d.ts +22 -0
  292. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  293. package/dist/modes/rpc/session-command-router.js +148 -16
  294. package/dist/modes/rpc/session-command-router.js.map +1 -1
  295. package/dist/modes/rpc/session-event-writer.d.ts +4 -0
  296. package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
  297. package/dist/modes/rpc/session-event-writer.js +58 -7
  298. package/dist/modes/rpc/session-event-writer.js.map +1 -1
  299. package/dist/modes/rpc/session-extension-ui-requests.d.ts +1 -0
  300. package/dist/modes/rpc/session-extension-ui-requests.d.ts.map +1 -1
  301. package/dist/modes/rpc/session-extension-ui-requests.js +5 -0
  302. package/dist/modes/rpc/session-extension-ui-requests.js.map +1 -1
  303. package/dist/modes/rpc/session-registry.d.ts +17 -2
  304. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  305. package/dist/modes/rpc/session-registry.js +67 -4
  306. package/dist/modes/rpc/session-registry.js.map +1 -1
  307. package/dist/modes/rpc/socket-event-fanout.d.ts +22 -0
  308. package/dist/modes/rpc/socket-event-fanout.d.ts.map +1 -0
  309. package/dist/modes/rpc/socket-event-fanout.js +84 -0
  310. package/dist/modes/rpc/socket-event-fanout.js.map +1 -0
  311. package/docs/rpc.md +55 -2
  312. package/docs/settings.md +34 -0
  313. package/docs/windows.md +2 -0
  314. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +74 -0
  315. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  316. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-indirect-eval.js +139 -90
  317. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +4 -4
  318. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  319. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +6 -3
  320. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
  321. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
  322. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js +40 -20
  323. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
  324. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts +3 -3
  325. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts.map +1 -1
  326. package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js.map +1 -1
  327. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts +1 -1
  328. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts.map +1 -1
  329. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js +28 -9
  330. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js.map +1 -1
  331. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  332. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
  333. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +67 -38
  334. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  335. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts +20 -0
  336. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts.map +1 -0
  337. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js +443 -0
  338. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js.map +1 -0
  339. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +2 -0
  340. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
  341. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +2 -0
  342. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
  343. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +1 -1
  344. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
  345. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
  346. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  347. package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
  348. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  349. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  350. package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
  351. package/node_modules/@earendil-works/pi-ai/dist/providers/data/baseten.json +1 -1
  352. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  353. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
  354. package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
  355. package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
  356. package/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
  357. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  358. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  359. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  360. package/node_modules/@earendil-works/pi-ai/dist/providers/data/together.json +1 -1
  361. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  362. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai-coding-cn.json +1 -1
  363. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai.json +1 -1
  364. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  365. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  366. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  367. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +7 -0
  368. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
  369. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +5 -0
  370. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
  371. package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts +1 -1
  372. package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts.map +1 -1
  373. package/node_modules/@earendil-works/pi-tui/dist/mux.js +2 -2
  374. package/node_modules/@earendil-works/pi-tui/dist/mux.js.map +1 -1
  375. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +1 -0
  376. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
  377. package/node_modules/@earendil-works/pi-tui/dist/terminal.js +19 -0
  378. package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
  379. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  380. package/package.json +7 -7
@@ -1,3 +1,8 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { rm } from "node:fs/promises";
3
+ import { executeBashWithOperations } from "../../core/bash-executor.js";
4
+ import { SessionManager } from "../../core/session-manager.js";
5
+ import { SettingsManager } from "../../core/settings-manager.js";
1
6
  import { ensureHost } from "../rpc/host-ensure.js";
2
7
  import { RpcClient } from "../rpc/rpc-client.js";
3
8
  export const INTERACTIVE_HOST_FALLBACK_WARNING = "Warning: shared interactive host unavailable; continuing locally";
@@ -23,7 +28,15 @@ export async function createInteractiveHostRuntime(localRuntime, options) {
23
28
  modelId: localRuntime.session.model?.id,
24
29
  thinkingLevel: localRuntime.session.thinkingLevel,
25
30
  });
26
- const remoteSession = createRemoteSessionProxy(localRuntime.session, client, opened.state, options.onWarning);
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();
27
40
  return new RemoteInteractiveRuntime(localRuntime, remoteSession, client);
28
41
  }
29
42
  catch (cause) {
@@ -36,25 +49,25 @@ export async function createInteractiveHostRuntime(localRuntime, options) {
36
49
  return localRuntime;
37
50
  }
38
51
  }
39
- class RemoteInteractiveRuntime {
52
+ export class RemoteInteractiveRuntime {
40
53
  #local;
41
- #session;
54
+ #remoteSession;
42
55
  #client;
43
56
  #rebindSession;
44
57
  #beforeSessionInvalidate;
45
- constructor(local, session, client) {
58
+ constructor(local, remoteSession, client) {
46
59
  this.#local = local;
47
- this.#session = session;
60
+ this.#remoteSession = remoteSession;
48
61
  this.#client = client;
49
62
  }
50
63
  get session() {
51
- return this.#session;
64
+ return this.#remoteSession.session;
52
65
  }
53
66
  get services() {
54
67
  return this.#local.services;
55
68
  }
56
69
  get cwd() {
57
- return this.#local.cwd;
70
+ return this.#remoteSession.session.sessionManager.getCwd();
58
71
  }
59
72
  get diagnostics() {
60
73
  return this.#local.diagnostics;
@@ -71,37 +84,104 @@ class RemoteInteractiveRuntime {
71
84
  setRebindSession(callback) {
72
85
  this.#rebindSession = callback;
73
86
  }
87
+ setHostUiHandler(callback) {
88
+ this.#remoteSession.setHostUiHandler(callback);
89
+ }
74
90
  async dispose() {
75
- await this.#client.closeSession();
76
- await this.#client.stop();
77
- 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];
78
107
  }
79
- async newSession() {
80
- this.#beforeSessionInvalidate?.();
81
- const result = await this.#client.newSession();
82
- if (!result.cancelled)
83
- await this.#rebindSession?.();
84
- return result;
108
+ async newSession(options) {
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();
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());
129
+ }
130
+ return result;
131
+ });
132
+ }
133
+ async switchSession(sessionPath, options) {
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
+ });
85
151
  }
86
- async switchSession(sessionPath) {
87
- this.#beforeSessionInvalidate?.();
88
- const result = await this.#client.switchSession(sessionPath);
89
- if (!result.cancelled)
90
- await this.#rebindSession?.();
91
- return result;
152
+ async fork(entryId, options) {
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
+ });
92
165
  }
93
- async fork(entryId) {
94
- this.#beforeSessionInvalidate?.();
95
- const result = await this.#client.fork(entryId);
96
- if (!result.cancelled)
97
- await this.#rebindSession?.();
98
- return { cancelled: result.cancelled, selectedText: result.text };
166
+ async importFromJsonl(inputPath, cwdOverride) {
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
+ });
99
177
  }
100
- async importFromJsonl() {
101
- throw new Error("Session import is not available while connected to the shared host");
178
+ async #refreshAndRebind() {
179
+ await this.#remoteSession.refresh();
180
+ await this.#rebindSession?.();
102
181
  }
103
182
  }
104
- function createRemoteSessionProxy(local, client, initialState, onWarning) {
183
+ /** Exported for direct coverage of the wire-event handling below. */
184
+ export function createRemoteSessionProxy(local, agentDir, client, initialState, onWarning) {
105
185
  // Fire-and-forget setters keep the sync AgentSession signature, but their RPC
106
186
  // failures must not vanish: the matching *_changed wire event confirms success,
107
187
  // and a rejection here is the only signal of failure.
@@ -112,36 +192,296 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
112
192
  cause: error,
113
193
  });
114
194
  };
115
- let state = initialState;
195
+ let state = { ...initialState };
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 = [];
251
+ let localBashAbortController;
252
+ let localBashRunning = false;
253
+ let hostBashRunning = initialState.isBashRunning;
254
+ let nextQueuedInputOrder = Math.max(0, ...initialState.ordered.map((item) => item.enqueueOrder));
255
+ let sessionManager = local.sessionManager;
256
+ let settingsManager = SettingsManager.create(initialState.cwd, agentDir, {
257
+ projectTrusted: initialState.projectTrusted,
258
+ });
259
+ const updateBashState = () => {
260
+ state = { ...state, isBashRunning: localBashRunning || hostBashRunning };
261
+ };
262
+ const remoteSessionManager = new Proxy({}, {
263
+ get(_target, property, _receiver) {
264
+ if (property === "appendLabelChange") {
265
+ return (entryId, label) => void client.setLabel(entryId, label);
266
+ }
267
+ if (property === "getCwd")
268
+ return () => state.cwd;
269
+ if (property === "getSessionName")
270
+ return () => state.sessionName;
271
+ if (property === "getUsageTotals")
272
+ return () => state.usageTotals;
273
+ const value = Reflect.get(sessionManager, property, sessionManager);
274
+ return typeof value === "function" ? value.bind(sessionManager) : value;
275
+ },
276
+ });
116
277
  let streamingAssistant;
278
+ let mirroredCurrentAssistantUsage = false;
279
+ /** Non-zero while this runtime is driving its own replacement sequence. */
280
+ let localReplacementDepth = 0;
117
281
  const listeners = new Set();
118
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
+ }
119
309
  if (wireEvent.type === "agent_settled")
120
- state = { ...state, isStreaming: false };
310
+ state = { ...state, isStreaming: false, retryAttempt: 0 };
311
+ if (wireEvent.type === "bash_start") {
312
+ hostBashRunning = true;
313
+ updateBashState();
314
+ }
315
+ if (wireEvent.type === "bash_end") {
316
+ hostBashRunning = false;
317
+ updateBashState();
318
+ }
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
+ }
121
343
  if (wireEvent.type === "agent_start")
122
344
  state = { ...state, isStreaming: true };
345
+ if (wireEvent.type === "compaction_start")
346
+ state = { ...state, isCompacting: true };
347
+ if (wireEvent.type === "compaction_end")
348
+ state = { ...state, isCompacting: false };
349
+ if (wireEvent.type === "auto_retry_start")
350
+ state = { ...state, retryAttempt: wireEvent.attempt };
351
+ if (wireEvent.type === "auto_retry_end")
352
+ state = { ...state, retryAttempt: 0 };
353
+ if (wireEvent.type === "queue_update") {
354
+ nextQueuedInputOrder = Math.max(nextQueuedInputOrder, ...wireEvent.ordered.map((item) => item.enqueueOrder));
355
+ state = {
356
+ ...state,
357
+ steering: [...wireEvent.steering],
358
+ followUp: [...wireEvent.followUp],
359
+ ordered: [...wireEvent.ordered],
360
+ pendingMessageCount: wireEvent.steering.length + wireEvent.followUp.length,
361
+ };
362
+ }
123
363
  if (wireEvent.type === "model_changed") {
124
364
  state = { ...state, model: wireEvent.model, thinkingLevel: wireEvent.thinkingLevel };
125
365
  }
126
366
  if (wireEvent.type === "thinking_level_changed")
127
367
  state = { ...state, thinkingLevel: wireEvent.level };
368
+ if (wireEvent.type === "service_tier_changed") {
369
+ state = { ...state, serviceTier: wireEvent.tier, fastMode: wireEvent.fastMode };
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
+ }
388
+ if (wireEvent.type === "session_info_changed")
389
+ state = { ...state, sessionName: wireEvent.name };
128
390
  if (wireEvent.type === "message_start") {
129
- if (wireEvent.message.role === "assistant")
391
+ if (wireEvent.message.role === "assistant") {
130
392
  streamingAssistant = structuredClone(wireEvent.message);
393
+ mirroredCurrentAssistantUsage = false;
394
+ }
131
395
  local.agent.state.messages.push(structuredClone(wireEvent.message));
132
396
  }
133
397
  if (wireEvent.type === "message_end") {
134
- if (wireEvent.message.role === "assistant")
398
+ if (wireEvent.message.role === "assistant") {
135
399
  streamingAssistant = structuredClone(wireEvent.message);
400
+ const usage = wireEvent.message.usage;
401
+ if (usage && !mirroredCurrentAssistantUsage) {
402
+ mirroredCurrentAssistantUsage = true;
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;
406
+ state = {
407
+ ...state,
408
+ contextUsage: contextWindow > 0
409
+ ? { tokens: contextTokens, contextWindow, percent: (contextTokens / contextWindow) * 100 }
410
+ : undefined,
411
+ usageTotals: {
412
+ ...state.usageTotals,
413
+ input: state.usageTotals.input + usage.input,
414
+ output: state.usageTotals.output + usage.output,
415
+ cacheRead: state.usageTotals.cacheRead + usage.cacheRead,
416
+ cacheWrite: state.usageTotals.cacheWrite + usage.cacheWrite,
417
+ cost: state.usageTotals.cost + (usage.cost?.total ?? 0),
418
+ latestCacheHitRate: latestPromptTokens > 0 ? (usage.cacheRead / latestPromptTokens) * 100 : undefined,
419
+ },
420
+ };
421
+ }
422
+ }
136
423
  const messages = local.agent.state.messages;
137
424
  const previous = messages.at(-1);
138
425
  if (previous?.role === wireEvent.message.role)
139
426
  messages[messages.length - 1] = structuredClone(wireEvent.message);
140
427
  }
428
+ if (wireEvent.type === "compaction_end" && wireEvent.accepted && !wireEvent.aborted) {
429
+ try {
430
+ local.sessionManager.reloadFromDisk?.();
431
+ if (sessionManager !== local.sessionManager)
432
+ sessionManager.reloadFromDisk?.();
433
+ local.agent.state.messages = sessionManager.buildSessionContext().messages;
434
+ }
435
+ catch {
436
+ // Non-fatal if session file is transiently locked or unavailable
437
+ }
438
+ }
141
439
  const event = hydrateMessageUpdate(wireEvent, streamingAssistant);
142
440
  for (const listener of listeners)
143
441
  listener(event);
144
442
  });
443
+ const performRefresh = async () => {
444
+ const nextState = await client.getState();
445
+ state = { ...stateFromRpc(nextState) };
446
+ nextQueuedInputOrder = Math.max(0, ...nextState.ordered.map((item) => item.enqueueOrder));
447
+ let messages;
448
+ settingsManager = SettingsManager.create(nextState.cwd, agentDir, {
449
+ projectTrusted: nextState.projectTrusted,
450
+ });
451
+ if (nextState.sessionFile) {
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
+ }
463
+ if (nextState.entries?.length && !sessionManager.getEntries().length) {
464
+ for (const entry of nextState.entries)
465
+ sessionManager.appendEntry(entry);
466
+ }
467
+ messages = sessionManager.buildSessionContext().messages;
468
+ }
469
+ else {
470
+ messages = await client.getMessages();
471
+ }
472
+ local.agent.state.messages.splice(0, local.agent.state.messages.length, ...structuredClone(messages));
473
+ streamingAssistant = undefined;
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
+ };
145
485
  const session = new Proxy(local, {
146
486
  get(target, property, receiver) {
147
487
  if (property === "prompt")
@@ -149,25 +489,74 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
149
489
  ...(options?.images ? { images: options.images } : {}),
150
490
  ...(options?.streamingBehavior ? { streamingBehavior: options.streamingBehavior } : {}),
151
491
  ...(options?.thinkingLevel ? { thinkingLevel: options.thinkingLevel } : {}),
492
+ ...(options?.sessionTitlePrompt !== undefined
493
+ ? { sessionTitlePrompt: options.sessionTitlePrompt }
494
+ : {}),
495
+ ...(options?.expandPromptTemplates !== undefined
496
+ ? { expandPromptTemplates: options.expandPromptTemplates }
497
+ : {}),
152
498
  ...(options?.promptDisposition ? { promptDisposition: options.promptDisposition } : {}),
153
499
  ...(options?.preflightResult ? { preflightResult: options.preflightResult } : {}),
154
500
  });
155
501
  if (property === "abort")
156
502
  return () => client.abort();
503
+ if (property === "abortCompaction")
504
+ return () => void client.abortCompaction();
157
505
  if (property === "steer")
158
- return (message, images) => client.steer(message, images);
506
+ return (message, images, recovery) => {
507
+ const enqueueOrder = recovery?.enqueueOrder ?? Math.max(0, ...state.ordered.map((item) => item.enqueueOrder)) + 1;
508
+ state = {
509
+ ...state,
510
+ steering: [...state.steering, message],
511
+ ordered: [...state.ordered, { text: message, mode: "steer", enqueueOrder }],
512
+ pendingMessageCount: state.pendingMessageCount + 1,
513
+ };
514
+ return client.steer(message, images, { ...recovery, enqueueOrder });
515
+ };
159
516
  if (property === "followUp")
160
- return (message, images) => client.followUp(message, images);
517
+ return (message, images, recovery) => {
518
+ const enqueueOrder = recovery?.enqueueOrder ?? Math.max(0, ...state.ordered.map((item) => item.enqueueOrder)) + 1;
519
+ state = {
520
+ ...state,
521
+ followUp: [...state.followUp, message],
522
+ ordered: [...state.ordered, { text: message, mode: "followUp", enqueueOrder }],
523
+ pendingMessageCount: state.pendingMessageCount + 1,
524
+ };
525
+ return client.followUp(message, images, { ...recovery, enqueueOrder });
526
+ };
161
527
  if (property === "waitForIdle")
162
528
  return () => client.waitForIdle();
163
529
  if (property === "getLastAssistantText")
164
530
  return () => target.getLastAssistantText();
165
- if (property === "setModel")
531
+ if (property === "setModel" || property === "setSessionModel")
166
532
  return async (model) => {
167
- await client.setModel(model.provider, model.id);
533
+ const next = await client.setModel(model.provider, model.id);
534
+ return { systemPromptName: next.systemPromptName, model: next };
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"));
168
557
  };
169
558
  if (property === "cycleModel")
170
- return () => client.cycleModel();
559
+ return (direction) => client.cycleModel(direction);
171
560
  if (property === "setThinkingLevel")
172
561
  return (level) => void client.setThinkingLevel(level).catch(reportActionFailure("setThinkingLevel"));
173
562
  if (property === "cycleThinkingLevel")
@@ -183,15 +572,81 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
183
572
  if (property === "setAutoCompactionEnabled")
184
573
  return (enabled) => void client.setAutoCompaction(enabled).catch(reportActionFailure("setAutoCompaction"));
185
574
  if (property === "executeBash")
186
- return (command) => client.bash(command);
575
+ return async (command, onChunk, options) => {
576
+ if (options?.operations && typeof options.operations.exec === "function") {
577
+ const abortController = new AbortController();
578
+ localBashAbortController = abortController;
579
+ localBashRunning = true;
580
+ updateBashState();
581
+ const sessionAtStart = state.sessionId;
582
+ const prefix = settingsManager.getShellCommandPrefix();
583
+ const resolvedCommand = prefix ? `${prefix}\n${command}` : command;
584
+ try {
585
+ const result = await executeBashWithOperations(resolvedCommand, state.cwd, options.operations, { onChunk, signal: abortController.signal });
586
+ if (state.sessionId === sessionAtStart) {
587
+ await client.recordBashResult(command, result, options.excludeFromContext);
588
+ }
589
+ return result;
590
+ }
591
+ finally {
592
+ localBashAbortController = undefined;
593
+ localBashRunning = false;
594
+ updateBashState();
595
+ }
596
+ }
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);
605
+ try {
606
+ const result = await client.bash(command, {
607
+ excludeFromContext: options?.excludeFromContext,
608
+ executionId,
609
+ operations: options?.operations,
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;
623
+ }
624
+ finally {
625
+ bashExecutions.delete(executionId);
626
+ }
627
+ };
187
628
  if (property === "abortBash")
188
- return () => void client.abortBash().catch(reportActionFailure("abortBash"));
629
+ return () => {
630
+ if (localBashAbortController)
631
+ localBashAbortController.abort();
632
+ else
633
+ void client.abortBash().catch(reportActionFailure("abortBash"));
634
+ };
635
+ if (property === "getContextUsage")
636
+ return () => state.contextUsage;
189
637
  if (property === "getSessionStats")
190
638
  return () => client.getSessionStats();
191
639
  if (property === "exportToHtml")
192
- return (outputPath) => client.exportHtml(outputPath).then((result) => result.path);
640
+ return (outputPath, options) => client.exportHtml(outputPath, options?.themeName).then((result) => result.path);
193
641
  if (property === "setSessionName")
194
- return (name) => void client.setSessionName(name).catch(reportActionFailure("setSessionName"));
642
+ return (name) => client.setSessionName(name).catch(reportActionFailure("setSessionName"));
643
+ if (property === "navigateTree")
644
+ return async (targetId, options) => {
645
+ const result = await client.navigateTree(targetId, options);
646
+ if (!result.cancelled)
647
+ await refresh();
648
+ return result;
649
+ };
195
650
  if (property === "getUserMessagesForForking")
196
651
  return () => client.getForkMessages();
197
652
  if (property === "subscribe")
@@ -205,6 +660,45 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
205
660
  };
206
661
  if (property === "isStreaming")
207
662
  return state.isStreaming;
663
+ if (property === "isIdle")
664
+ return !state.isStreaming;
665
+ if (property === "isCompacting")
666
+ return state.isCompacting;
667
+ if (property === "pendingMessageCount")
668
+ return state.pendingMessageCount;
669
+ if (property === "getSteeringMessages")
670
+ return () => state.steering;
671
+ if (property === "getFollowUpMessages")
672
+ return () => state.followUp;
673
+ if (property === "clearQueue")
674
+ return (options) => {
675
+ const result = {
676
+ steering: [...state.steering],
677
+ followUp: [...state.followUp],
678
+ ordered: [...state.ordered],
679
+ };
680
+ Object.defineProperty(result, "ordered", { value: result.ordered, enumerable: false });
681
+ void client.clearQueue(options).catch(reportActionFailure("clearQueue"));
682
+ return result;
683
+ };
684
+ if (property === "abortBranchSummary")
685
+ return () => void client.abortBranchSummary().catch(reportActionFailure("abortBranchSummary"));
686
+ if (property === "recordBashResult")
687
+ return (command, result, options) => void client
688
+ .recordBashResult(command, result, options?.excludeFromContext)
689
+ .catch(reportActionFailure("recordBashResult"));
690
+ if (property === "set_label")
691
+ return undefined;
692
+ if (property === "retryAttempt")
693
+ return state.retryAttempt;
694
+ if (property === "isBashRunning")
695
+ return state.isBashRunning;
696
+ if (property === "reload")
697
+ return (_options) => client.reload();
698
+ if (property === "checkReloadVeto")
699
+ return () => client.checkReloadVeto();
700
+ if (property === "exportToJsonl")
701
+ return (outputPath) => client.exportJsonl(outputPath).then((result) => result.path);
208
702
  // The footer and other renderers read session.state.*; surface the
209
703
  // host-authoritative fields there too, not only via the direct getters.
210
704
  if (property === "state") {
@@ -214,14 +708,35 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
214
708
  model: state.model ?? localState.model,
215
709
  thinkingLevel: state.thinkingLevel,
216
710
  isStreaming: state.isStreaming,
711
+ isCompacting: state.isCompacting,
217
712
  };
218
713
  }
219
714
  if (property === "sessionFile")
220
715
  return state.sessionFile;
221
716
  if (property === "sessionId")
222
717
  return state.sessionId;
718
+ if (property === "sessionName")
719
+ return state.sessionName;
720
+ if (property === "serviceTier")
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;
730
+ if (property === "sessionManager")
731
+ return remoteSessionManager;
732
+ if (property === "settingsManager")
733
+ return settingsManager;
223
734
  if (property === "messages")
224
735
  return target.messages;
736
+ if (property === "favoriteModels")
737
+ return state.favoriteModels;
738
+ if (property === "scopedModels")
739
+ return state.scopedModels;
225
740
  if (property === "model")
226
741
  return state.model ?? target.model;
227
742
  if (property === "thinkingLevel")
@@ -229,7 +744,53 @@ function createRemoteSessionProxy(local, client, initialState, onWarning) {
229
744
  return Reflect.get(target, property, receiver);
230
745
  },
231
746
  });
232
- return session;
747
+ return {
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
+ },
755
+ refresh,
756
+ aroundLocalReplacement: async (body) => {
757
+ localReplacementDepth++;
758
+ try {
759
+ return await body();
760
+ }
761
+ finally {
762
+ localReplacementDepth--;
763
+ }
764
+ },
765
+ abortLocalBash: () => localBashAbortController?.abort(),
766
+ createReplacedSessionContext: () => {
767
+ const context = local.createReplacedSessionContext();
768
+ Object.defineProperty(context, "cwd", { value: state.cwd });
769
+ Object.defineProperty(context, "sessionManager", { value: remoteSessionManager });
770
+ context.sendMessage = (message, options) => client.sendCustomMessage(message, {
771
+ triggerTurn: options?.triggerTurn,
772
+ deliverAs: options?.deliverAs,
773
+ });
774
+ context.sendUserMessage = (content, options) => {
775
+ if (typeof content === "string")
776
+ return client.prompt(content, {
777
+ streamingBehavior: options?.deliverAs,
778
+ expandPromptTemplates: options?.expandPromptTemplates,
779
+ });
780
+ const text = content
781
+ .filter((part) => part.type === "text")
782
+ .map((part) => part.text)
783
+ .join("\n");
784
+ const images = content.filter((part) => part.type === "image");
785
+ return client.prompt(text, {
786
+ images,
787
+ streamingBehavior: options?.deliverAs,
788
+ expandPromptTemplates: options?.expandPromptTemplates,
789
+ });
790
+ };
791
+ return context;
792
+ },
793
+ };
233
794
  }
234
795
  function hydrateMessageUpdate(event, streamingAssistant) {
235
796
  if (event.type !== "message_update" || !streamingAssistant)