@code-yeongyu/senpi 2026.8.30 → 2026.9.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 (373) hide show
  1. package/CHANGELOG.md +174 -2
  2. package/README.md +3 -0
  3. package/dist/core/agent-session.d.ts +28 -0
  4. package/dist/core/agent-session.d.ts.map +1 -1
  5. package/dist/core/agent-session.js +161 -14
  6. package/dist/core/agent-session.js.map +1 -1
  7. package/dist/core/compaction/compaction-execution.d.ts +14 -0
  8. package/dist/core/compaction/compaction-execution.d.ts.map +1 -0
  9. package/dist/core/compaction/compaction-execution.js +121 -0
  10. package/dist/core/compaction/compaction-execution.js.map +1 -0
  11. package/dist/core/compaction/compaction-settings.d.ts +17 -0
  12. package/dist/core/compaction/compaction-settings.d.ts.map +1 -0
  13. package/dist/core/compaction/compaction-settings.js +17 -0
  14. package/dist/core/compaction/compaction-settings.js.map +1 -0
  15. package/dist/core/compaction/compaction.d.ts +13 -26
  16. package/dist/core/compaction/compaction.d.ts.map +1 -1
  17. package/dist/core/compaction/compaction.js +7 -136
  18. package/dist/core/compaction/compaction.js.map +1 -1
  19. package/dist/core/compaction/ideal-compaction-settings.d.ts +9 -0
  20. package/dist/core/compaction/ideal-compaction-settings.d.ts.map +1 -0
  21. package/dist/core/compaction/ideal-compaction-settings.js +7 -0
  22. package/dist/core/compaction/ideal-compaction-settings.js.map +1 -0
  23. package/dist/core/compaction-settings-access.d.ts +19 -0
  24. package/dist/core/compaction-settings-access.d.ts.map +1 -0
  25. package/dist/core/compaction-settings-access.js +10 -0
  26. package/dist/core/compaction-settings-access.js.map +1 -0
  27. package/dist/core/compaction-settings-resolver.d.ts +22 -0
  28. package/dist/core/compaction-settings-resolver.d.ts.map +1 -0
  29. package/dist/core/compaction-settings-resolver.js +46 -0
  30. package/dist/core/compaction-settings-resolver.js.map +1 -0
  31. package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry.d.ts +9 -1
  32. package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry.d.ts.map +1 -1
  33. package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry.js +10 -3
  34. package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry.js.map +1 -1
  35. package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts +24 -0
  36. package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts.map +1 -0
  37. package/dist/core/extensions/builtin/compaction/context-pipeline.js +43 -0
  38. package/dist/core/extensions/builtin/compaction/context-pipeline.js.map +1 -0
  39. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +1 -1
  40. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
  41. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +8 -2
  42. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
  43. package/dist/core/extensions/builtin/compaction/emergency-prune.d.ts +17 -0
  44. package/dist/core/extensions/builtin/compaction/emergency-prune.d.ts.map +1 -0
  45. package/dist/core/extensions/builtin/compaction/emergency-prune.js +64 -0
  46. package/dist/core/extensions/builtin/compaction/emergency-prune.js.map +1 -0
  47. package/dist/core/extensions/builtin/compaction/extension-wiring.d.ts +27 -0
  48. package/dist/core/extensions/builtin/compaction/extension-wiring.d.ts.map +1 -0
  49. package/dist/core/extensions/builtin/compaction/extension-wiring.js +84 -0
  50. package/dist/core/extensions/builtin/compaction/extension-wiring.js.map +1 -0
  51. package/dist/core/extensions/builtin/compaction/idle-retry.d.ts +1 -1
  52. package/dist/core/extensions/builtin/compaction/idle-retry.d.ts.map +1 -1
  53. package/dist/core/extensions/builtin/compaction/idle-retry.js +1 -1
  54. package/dist/core/extensions/builtin/compaction/idle-retry.js.map +1 -1
  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 +152 -154
  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/model-usability-budget.d.ts +30 -0
  72. package/dist/core/extensions/builtin/compaction/model-usability-budget.d.ts.map +1 -0
  73. package/dist/core/extensions/builtin/compaction/model-usability-budget.js +74 -0
  74. package/dist/core/extensions/builtin/compaction/model-usability-budget.js.map +1 -0
  75. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.d.ts +9 -0
  76. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.d.ts.map +1 -0
  77. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.js +2 -0
  78. package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.js.map +1 -0
  79. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +3 -6
  80. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts.map +1 -1
  81. package/dist/core/extensions/builtin/compaction/openai-remote.js.map +1 -1
  82. package/dist/core/extensions/builtin/compaction/orchestration.d.ts +46 -0
  83. package/dist/core/extensions/builtin/compaction/orchestration.d.ts.map +1 -0
  84. package/dist/core/extensions/builtin/compaction/orchestration.js +103 -0
  85. package/dist/core/extensions/builtin/compaction/orchestration.js.map +1 -0
  86. package/dist/core/extensions/builtin/compaction/policy.d.ts +12 -2
  87. package/dist/core/extensions/builtin/compaction/policy.d.ts.map +1 -1
  88. package/dist/core/extensions/builtin/compaction/policy.js +52 -26
  89. package/dist/core/extensions/builtin/compaction/policy.js.map +1 -1
  90. package/dist/core/extensions/builtin/compaction/speculation-lead.d.ts +8 -0
  91. package/dist/core/extensions/builtin/compaction/speculation-lead.d.ts.map +1 -0
  92. package/dist/core/extensions/builtin/compaction/speculation-lead.js +17 -0
  93. package/dist/core/extensions/builtin/compaction/speculation-lead.js.map +1 -0
  94. package/dist/core/extensions/builtin/compaction/speculative-job.d.ts +22 -0
  95. package/dist/core/extensions/builtin/compaction/speculative-job.d.ts.map +1 -0
  96. package/dist/core/extensions/builtin/compaction/speculative-job.js +16 -0
  97. package/dist/core/extensions/builtin/compaction/speculative-job.js.map +1 -0
  98. package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts +23 -0
  99. package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts.map +1 -0
  100. package/dist/core/extensions/builtin/compaction/speculative-summary.js +132 -0
  101. package/dist/core/extensions/builtin/compaction/speculative-summary.js.map +1 -0
  102. package/dist/core/extensions/builtin/compaction/speculative.d.ts +8 -13
  103. package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
  104. package/dist/core/extensions/builtin/compaction/speculative.js +21 -197
  105. package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
  106. package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts +24 -0
  107. package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts.map +1 -0
  108. package/dist/core/extensions/builtin/compaction/token-budget-reminder.js +25 -0
  109. package/dist/core/extensions/builtin/compaction/token-budget-reminder.js.map +1 -0
  110. package/dist/core/extensions/builtin/compaction/tool-admission.d.ts +27 -0
  111. package/dist/core/extensions/builtin/compaction/tool-admission.d.ts.map +1 -0
  112. package/dist/core/extensions/builtin/compaction/tool-admission.js +56 -0
  113. package/dist/core/extensions/builtin/compaction/tool-admission.js.map +1 -0
  114. package/dist/core/extensions/builtin/hooks/trust-state-json.d.ts +5 -0
  115. package/dist/core/extensions/builtin/hooks/trust-state-json.d.ts.map +1 -0
  116. package/dist/core/extensions/builtin/hooks/trust-state-json.js +76 -0
  117. package/dist/core/extensions/builtin/hooks/trust-state-json.js.map +1 -0
  118. package/dist/core/extensions/builtin/hooks/trust-storage.d.ts +7 -1
  119. package/dist/core/extensions/builtin/hooks/trust-storage.d.ts.map +1 -1
  120. package/dist/core/extensions/builtin/hooks/trust-storage.js +60 -8
  121. package/dist/core/extensions/builtin/hooks/trust-storage.js.map +1 -1
  122. package/dist/core/extensions/builtin/hooks/trust.d.ts +1 -2
  123. package/dist/core/extensions/builtin/hooks/trust.d.ts.map +1 -1
  124. package/dist/core/extensions/builtin/hooks/trust.js +1 -72
  125. package/dist/core/extensions/builtin/hooks/trust.js.map +1 -1
  126. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.d.ts +3 -0
  127. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.d.ts.map +1 -0
  128. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.js +108 -0
  129. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.js.map +1 -0
  130. package/dist/core/extensions/builtin/prompt-preset/presets.d.ts.map +1 -1
  131. package/dist/core/extensions/builtin/prompt-preset/presets.js +11 -0
  132. package/dist/core/extensions/builtin/prompt-preset/presets.js.map +1 -1
  133. package/dist/core/extensions/builtin/prompt-preset/settings.d.ts +1 -1
  134. package/dist/core/extensions/builtin/prompt-preset/settings.d.ts.map +1 -1
  135. package/dist/core/extensions/builtin/prompt-preset/settings.js +1 -0
  136. package/dist/core/extensions/builtin/prompt-preset/settings.js.map +1 -1
  137. package/dist/core/extensions/builtin/recommended-models/index.d.ts +1 -1
  138. package/dist/core/extensions/builtin/recommended-models/index.d.ts.map +1 -1
  139. package/dist/core/extensions/builtin/recommended-models/index.js +1 -1
  140. package/dist/core/extensions/builtin/recommended-models/index.js.map +1 -1
  141. package/dist/core/extensions/builtin/terminal/runtime-session.d.ts +11 -0
  142. package/dist/core/extensions/builtin/terminal/runtime-session.d.ts.map +1 -1
  143. package/dist/core/extensions/builtin/terminal/runtime-session.js +18 -2
  144. package/dist/core/extensions/builtin/terminal/runtime-session.js.map +1 -1
  145. package/dist/core/extensions/builtin/ttsr/index.d.ts.map +1 -1
  146. package/dist/core/extensions/builtin/ttsr/index.js +4 -0
  147. package/dist/core/extensions/builtin/ttsr/index.js.map +1 -1
  148. package/dist/core/extensions/builtin/ttsr/manager.d.ts +2 -0
  149. package/dist/core/extensions/builtin/ttsr/manager.d.ts.map +1 -1
  150. package/dist/core/extensions/builtin/ttsr/manager.js +28 -1
  151. package/dist/core/extensions/builtin/ttsr/manager.js.map +1 -1
  152. package/dist/core/extensions/runner.d.ts +5 -0
  153. package/dist/core/extensions/runner.d.ts.map +1 -1
  154. package/dist/core/extensions/runner.js +7 -0
  155. package/dist/core/extensions/runner.js.map +1 -1
  156. package/dist/core/retry-fallback/controller.d.ts +14 -0
  157. package/dist/core/retry-fallback/controller.d.ts.map +1 -1
  158. package/dist/core/retry-fallback/controller.js +33 -3
  159. package/dist/core/retry-fallback/controller.js.map +1 -1
  160. package/dist/core/retry-fallback/log.js +1 -1
  161. package/dist/core/retry-fallback/log.js.map +1 -1
  162. package/dist/core/retry-fallback/settings.d.ts.map +1 -1
  163. package/dist/core/retry-fallback/settings.js +1 -0
  164. package/dist/core/retry-fallback/settings.js.map +1 -1
  165. package/dist/core/sdk.d.ts.map +1 -1
  166. package/dist/core/sdk.js +5 -0
  167. package/dist/core/sdk.js.map +1 -1
  168. package/dist/core/session-activity.d.ts +47 -0
  169. package/dist/core/session-activity.d.ts.map +1 -0
  170. package/dist/core/session-activity.js +58 -0
  171. package/dist/core/session-activity.js.map +1 -0
  172. package/dist/core/session-manager.d.ts +8 -12
  173. package/dist/core/session-manager.d.ts.map +1 -1
  174. package/dist/core/session-manager.js +118 -7
  175. package/dist/core/session-manager.js.map +1 -1
  176. package/dist/core/session-resident-store.d.ts +8 -1
  177. package/dist/core/session-resident-store.d.ts.map +1 -1
  178. package/dist/core/session-resident-store.js +12 -5
  179. package/dist/core/session-resident-store.js.map +1 -1
  180. package/dist/core/settings-manager.d.ts +8 -85
  181. package/dist/core/settings-manager.d.ts.map +1 -1
  182. package/dist/core/settings-manager.js +9 -17
  183. package/dist/core/settings-manager.js.map +1 -1
  184. package/dist/core/settings-public-types.d.ts +11 -0
  185. package/dist/core/settings-public-types.d.ts.map +1 -0
  186. package/dist/core/settings-public-types.js +2 -0
  187. package/dist/core/settings-public-types.js.map +1 -0
  188. package/dist/core/settings-shapes.d.ts +36 -0
  189. package/dist/core/settings-shapes.d.ts.map +1 -0
  190. package/dist/core/settings-shapes.js +2 -0
  191. package/dist/core/settings-shapes.js.map +1 -0
  192. package/dist/core/shared-host-policy.d.ts +6 -0
  193. package/dist/core/shared-host-policy.d.ts.map +1 -0
  194. package/dist/core/shared-host-policy.js +7 -0
  195. package/dist/core/shared-host-policy.js.map +1 -0
  196. package/dist/core/terminal-settings.d.ts +22 -0
  197. package/dist/core/terminal-settings.d.ts.map +1 -0
  198. package/dist/core/terminal-settings.js +2 -0
  199. package/dist/core/terminal-settings.js.map +1 -0
  200. package/dist/index.d.ts +1 -1
  201. package/dist/index.d.ts.map +1 -1
  202. package/dist/index.js +1 -1
  203. package/dist/index.js.map +1 -1
  204. package/dist/main.d.ts +4 -4
  205. package/dist/main.d.ts.map +1 -1
  206. package/dist/main.js +27 -11
  207. package/dist/main.js.map +1 -1
  208. package/dist/modes/app-server/daemon/process.d.ts +13 -2
  209. package/dist/modes/app-server/daemon/process.d.ts.map +1 -1
  210. package/dist/modes/app-server/daemon/process.js +58 -12
  211. package/dist/modes/app-server/daemon/process.js.map +1 -1
  212. package/dist/modes/app-server/daemon.js +65 -7
  213. package/dist/modes/app-server/daemon.js.map +1 -1
  214. package/dist/modes/index.d.ts +1 -1
  215. package/dist/modes/index.d.ts.map +1 -1
  216. package/dist/modes/index.js +1 -1
  217. package/dist/modes/index.js.map +1 -1
  218. package/dist/modes/interactive/interactive-host-runtime.d.ts +84 -1
  219. package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
  220. package/dist/modes/interactive/interactive-host-runtime.js +435 -112
  221. package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
  222. package/dist/modes/interactive/interactive-mode.d.ts +11 -1
  223. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  224. package/dist/modes/interactive/interactive-mode.js +59 -6
  225. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  226. package/dist/modes/interactive/tips/catalog/ethos-tips.js +1 -1
  227. package/dist/modes/interactive/tips/catalog/ethos-tips.js.map +1 -1
  228. package/dist/modes/rpc/connection-handler.d.ts +12 -5
  229. package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
  230. package/dist/modes/rpc/connection-handler.js +180 -25
  231. package/dist/modes/rpc/connection-handler.js.map +1 -1
  232. package/dist/modes/rpc/custom-capability.d.ts +2 -0
  233. package/dist/modes/rpc/custom-capability.d.ts.map +1 -1
  234. package/dist/modes/rpc/custom-capability.js +2 -0
  235. package/dist/modes/rpc/custom-capability.js.map +1 -1
  236. package/dist/modes/rpc/host-ensure.d.ts +17 -0
  237. package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
  238. package/dist/modes/rpc/host-ensure.js +150 -23
  239. package/dist/modes/rpc/host-ensure.js.map +1 -1
  240. package/dist/modes/rpc/host-lifecycle.d.ts +54 -0
  241. package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
  242. package/dist/modes/rpc/host-lifecycle.js +259 -69
  243. package/dist/modes/rpc/host-lifecycle.js.map +1 -1
  244. package/dist/modes/rpc/host-watchdog.d.ts +2 -2
  245. package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
  246. package/dist/modes/rpc/host-watchdog.js +89 -21
  247. package/dist/modes/rpc/host-watchdog.js.map +1 -1
  248. package/dist/modes/rpc/multi-session-host.d.ts +38 -0
  249. package/dist/modes/rpc/multi-session-host.d.ts.map +1 -1
  250. package/dist/modes/rpc/multi-session-host.js +149 -61
  251. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  252. package/dist/modes/rpc/ownership-safe-lock.d.ts +14 -0
  253. package/dist/modes/rpc/ownership-safe-lock.d.ts.map +1 -0
  254. package/dist/modes/rpc/ownership-safe-lock.js +87 -0
  255. package/dist/modes/rpc/ownership-safe-lock.js.map +1 -0
  256. package/dist/modes/rpc/rpc-client.d.ts +13 -2
  257. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  258. package/dist/modes/rpc/rpc-client.js +46 -12
  259. package/dist/modes/rpc/rpc-client.js.map +1 -1
  260. package/dist/modes/rpc/rpc-types.d.ts +21 -3
  261. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  262. package/dist/modes/rpc/rpc-types.js +1 -0
  263. package/dist/modes/rpc/rpc-types.js.map +1 -1
  264. package/dist/modes/rpc/session-binding.d.ts +2 -1
  265. package/dist/modes/rpc/session-binding.d.ts.map +1 -1
  266. package/dist/modes/rpc/session-binding.js +1 -0
  267. package/dist/modes/rpc/session-binding.js.map +1 -1
  268. package/dist/modes/rpc/session-command-router.d.ts +54 -1
  269. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  270. package/dist/modes/rpc/session-command-router.js +180 -3
  271. package/dist/modes/rpc/session-command-router.js.map +1 -1
  272. package/dist/modes/rpc/session-event-writer.d.ts +18 -0
  273. package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
  274. package/dist/modes/rpc/session-event-writer.js +88 -8
  275. package/dist/modes/rpc/session-event-writer.js.map +1 -1
  276. package/dist/modes/rpc/session-registry.d.ts +11 -1
  277. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  278. package/dist/modes/rpc/session-registry.js +25 -0
  279. package/dist/modes/rpc/session-registry.js.map +1 -1
  280. package/dist/modes/rpc/socket-transport.d.ts +13 -0
  281. package/dist/modes/rpc/socket-transport.d.ts.map +1 -0
  282. package/dist/modes/rpc/socket-transport.js +92 -0
  283. package/dist/modes/rpc/socket-transport.js.map +1 -0
  284. package/dist/modes/rpc/widget-line-renderer.d.ts +17 -0
  285. package/dist/modes/rpc/widget-line-renderer.d.ts.map +1 -0
  286. package/dist/modes/rpc/widget-line-renderer.js +73 -0
  287. package/dist/modes/rpc/widget-line-renderer.js.map +1 -0
  288. package/dist/utils/fs-watch.d.ts.map +1 -1
  289. package/dist/utils/fs-watch.js +11 -2
  290. package/dist/utils/fs-watch.js.map +1 -1
  291. package/docs/app-server.md +2 -2
  292. package/docs/containerization.md +2 -2
  293. package/docs/development.md +7 -7
  294. package/docs/extensions.md +5 -5
  295. package/docs/grok-neo.md +1 -1
  296. package/docs/index.md +1 -1
  297. package/docs/packages.md +2 -2
  298. package/docs/quickstart.md +4 -4
  299. package/docs/release-guide.md +21 -21
  300. package/docs/rpc.md +83 -6
  301. package/docs/sdk.md +1 -1
  302. package/docs/settings.md +4 -4
  303. package/docs/skills.md +2 -2
  304. package/docs/terminal-tools.md +1 -1
  305. package/docs/termux.md +1 -1
  306. package/docs/tui.md +1 -1
  307. package/examples/AGENTS.md +1 -1
  308. package/examples/sdk/README.md +1 -1
  309. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +52 -0
  310. package/node_modules/@code-yeongyu/senpi-codemode/README.md +2 -2
  311. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  312. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/skill-contribution.ts +71 -0
  313. package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +3 -1
  314. package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/SKILL.md +44 -0
  315. package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/breaking-changes.md +75 -0
  316. package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/build.md +114 -0
  317. package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/dev-tooling.md +34 -0
  318. package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/http-networking.md +113 -0
  319. package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/install.md +93 -0
  320. package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/node-compat-platforms.md +36 -0
  321. package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/performance.md +33 -0
  322. package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/runtime-apis.md +156 -0
  323. package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/security.md +52 -0
  324. package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/test.md +103 -0
  325. package/node_modules/@earendil-works/pi-agent-core/README.md +3 -0
  326. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  327. package/node_modules/@earendil-works/pi-ai/README.md +6 -3
  328. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
  329. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +24 -7
  330. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  331. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +7 -0
  332. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
  333. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +316 -2
  334. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
  335. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-conversation-rotation.d.ts +4 -0
  336. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-conversation-rotation.d.ts.map +1 -1
  337. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-conversation-rotation.js +23 -0
  338. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-conversation-rotation.js.map +1 -1
  339. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts +24 -0
  340. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts.map +1 -1
  341. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +20 -2
  342. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js.map +1 -1
  343. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  344. package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
  345. package/node_modules/@earendil-works/pi-ai/dist/providers/data/anthropic.json +1 -1
  346. package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
  347. package/node_modules/@earendil-works/pi-ai/dist/providers/data/google.json +1 -1
  348. package/node_modules/@earendil-works/pi-ai/dist/providers/data/groq.json +1 -1
  349. package/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
  350. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opengateway.json +1 -1
  351. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  352. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  353. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  354. package/node_modules/@earendil-works/pi-pty/dist/screen-operations.d.ts +42 -0
  355. package/node_modules/@earendil-works/pi-pty/dist/screen-operations.d.ts.map +1 -0
  356. package/node_modules/@earendil-works/pi-pty/dist/screen-operations.js +34 -0
  357. package/node_modules/@earendil-works/pi-pty/dist/screen-operations.js.map +1 -0
  358. package/node_modules/@earendil-works/pi-pty/dist/screen-text.d.ts +8 -0
  359. package/node_modules/@earendil-works/pi-pty/dist/screen-text.d.ts.map +1 -0
  360. package/node_modules/@earendil-works/pi-pty/dist/screen-text.js +51 -0
  361. package/node_modules/@earendil-works/pi-pty/dist/screen-text.js.map +1 -0
  362. package/node_modules/@earendil-works/pi-pty/dist/screen.d.ts +45 -0
  363. package/node_modules/@earendil-works/pi-pty/dist/screen.d.ts.map +1 -1
  364. package/node_modules/@earendil-works/pi-pty/dist/screen.js +178 -55
  365. package/node_modules/@earendil-works/pi-pty/dist/screen.js.map +1 -1
  366. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  367. package/node_modules/@earendil-works/pi-telemetry/README.md +6 -3
  368. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  369. package/node_modules/@earendil-works/pi-tui/README.md +5 -4
  370. package/node_modules/@earendil-works/pi-tui/native/darwin/README.md +2 -2
  371. package/node_modules/@earendil-works/pi-tui/native/win32/README.md +2 -2
  372. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  373. package/package.json +7 -7
@@ -4,8 +4,9 @@ import { executeBashWithOperations } from "../../core/bash-executor.js";
4
4
  import { SessionManager } from "../../core/session-manager.js";
5
5
  import { SettingsManager } from "../../core/settings-manager.js";
6
6
  import { ensureHost } from "../rpc/host-ensure.js";
7
- import { RpcClient } from "../rpc/rpc-client.js";
7
+ import { isTransportGoneError, RpcClient } from "../rpc/rpc-client.js";
8
8
  export const INTERACTIVE_HOST_FALLBACK_WARNING = "Warning: shared interactive host unavailable; continuing locally";
9
+ export const INTERACTIVE_HOST_RECONNECTING_WARNING = "Warning: shared interactive host connection lost; reconnecting";
9
10
  /**
10
11
  * Replace only the transport-facing session operations. The object returned is
11
12
  * deliberately still an AgentSessionRuntime: InteractiveMode and extensions
@@ -17,10 +18,92 @@ export async function createInteractiveHostRuntime(localRuntime, options) {
17
18
  if (!sessionPath)
18
19
  return localRuntime;
19
20
  const startHost = options.ensureHost ?? ((hostOptions) => ensureHost(hostOptions));
20
- const client = new RpcClient({ socketPath: options.socket });
21
+ let remoteSession;
22
+ let remoteRuntime;
23
+ let reconnecting;
24
+ let disconnectQueued = false;
25
+ let disposed = false;
26
+ let fallbackWarned = false;
27
+ let reconnectWarningShown = false;
28
+ const warnReconnect = (cause) => {
29
+ if (reconnectWarningShown)
30
+ return;
31
+ reconnectWarningShown = true;
32
+ options.onWarning?.({
33
+ type: "interactive_host_action_failed",
34
+ message: INTERACTIVE_HOST_RECONNECTING_WARNING,
35
+ cause,
36
+ });
37
+ };
38
+ const warnFallback = (cause) => {
39
+ if (fallbackWarned)
40
+ return;
41
+ fallbackWarned = true;
42
+ options.onWarning?.({ type: "interactive_host_fallback", message: INTERACTIVE_HOST_FALLBACK_WARNING, cause });
43
+ };
44
+ let scheduleReconnect = () => { };
45
+ const client = new RpcClient({
46
+ socketPath: options.socket,
47
+ onDisconnect: () => {
48
+ disconnectQueued = true;
49
+ scheduleReconnect();
50
+ },
51
+ });
52
+ scheduleReconnect = () => {
53
+ if (!remoteSession || reconnecting || disposed || remoteRuntime?.isFallback)
54
+ return;
55
+ remoteRuntime?.enterReconnecting();
56
+ reconnecting = (async () => {
57
+ let cause;
58
+ for (let attempt = 0; attempt < 3; attempt++) {
59
+ if (disposed)
60
+ return;
61
+ disconnectQueued = false;
62
+ try {
63
+ await startHost({ socket: options.socket, agentDir: options.agentDir });
64
+ if (disposed)
65
+ return;
66
+ await client.start();
67
+ if (disposed)
68
+ return;
69
+ await client.openSession({ sessionPath, cwd: localRuntime.cwd });
70
+ if (disposed)
71
+ return;
72
+ if (remoteRuntime)
73
+ await remoteRuntime.reRegisterClientInfo();
74
+ if (disposed)
75
+ return;
76
+ await remoteSession.refresh();
77
+ if (disposed)
78
+ return;
79
+ fallbackWarned = false;
80
+ reconnectWarningShown = false;
81
+ remoteRuntime?.enterConnected();
82
+ return;
83
+ }
84
+ catch (error) {
85
+ cause = error;
86
+ await client.stop().catch(() => { });
87
+ if (disposed)
88
+ return;
89
+ }
90
+ }
91
+ if (!disposed) {
92
+ await remoteRuntime?.enterFallback();
93
+ warnFallback(cause);
94
+ }
95
+ })().finally(() => {
96
+ reconnecting = undefined;
97
+ if (disconnectQueued && !disposed && !remoteRuntime?.isFallback)
98
+ scheduleReconnect();
99
+ });
100
+ };
21
101
  try {
22
102
  await startHost({ socket: options.socket, agentDir: options.agentDir });
23
103
  await client.start();
104
+ await client.setClientInfo(80, ["rendered_components"]);
105
+ const startupEvents = [];
106
+ const stopBuffering = client.onEvent((event) => startupEvents.push(event));
24
107
  const opened = await client.openSession({
25
108
  sessionPath,
26
109
  cwd: localRuntime.cwd,
@@ -28,7 +111,13 @@ export async function createInteractiveHostRuntime(localRuntime, options) {
28
111
  modelId: localRuntime.session.model?.id,
29
112
  thinkingLevel: localRuntime.session.thinkingLevel,
30
113
  });
31
- const remoteSession = createRemoteSessionProxy(localRuntime.session, localRuntime.services.agentDir, client, opened.state, options.onWarning);
114
+ remoteSession = createRemoteSessionProxy(localRuntime.session, localRuntime.services.agentDir, client, opened.state, options.onWarning, (cause) => {
115
+ if (remoteRuntime?.isReconnecting)
116
+ warnReconnect(cause);
117
+ else
118
+ warnFallback(cause);
119
+ }, startupEvents.filter((event) => !("sessionId" in event) || event.sessionId === opened.sessionId));
120
+ stopBuffering();
32
121
  if (opened.state.isBashRunning && !opened.attached) {
33
122
  // Only a newly opened session can have an execution orphaned by a
34
123
  // previous connection. An attach is an observer of the same live runtime;
@@ -37,15 +126,25 @@ export async function createInteractiveHostRuntime(localRuntime, options) {
37
126
  }
38
127
  if (opened.attached)
39
128
  await remoteSession.refresh();
40
- return new RemoteInteractiveRuntime(localRuntime, remoteSession, client);
129
+ remoteRuntime = new RemoteInteractiveRuntime(localRuntime, remoteSession, client, {
130
+ onTransportGone: (cause) => {
131
+ if (remoteRuntime?.isReconnecting)
132
+ warnReconnect(cause);
133
+ else
134
+ warnFallback(cause);
135
+ },
136
+ get reconnecting() {
137
+ return reconnecting;
138
+ },
139
+ dispose: () => {
140
+ disposed = true;
141
+ },
142
+ });
143
+ return remoteRuntime;
41
144
  }
42
145
  catch (cause) {
43
146
  await client.stop().catch(() => { });
44
- options.onWarning?.({
45
- type: "interactive_host_fallback",
46
- message: `${INTERACTIVE_HOST_FALLBACK_WARNING}: ${cause instanceof Error ? cause.message : String(cause)}`,
47
- cause,
48
- });
147
+ warnFallback(cause);
49
148
  return localRuntime;
50
149
  }
51
150
  }
@@ -53,21 +152,68 @@ export class RemoteInteractiveRuntime {
53
152
  #local;
54
153
  #remoteSession;
55
154
  #client;
155
+ #lifecycle;
156
+ #onTransportGone;
56
157
  #rebindSession;
57
158
  #beforeSessionInvalidate;
58
- constructor(local, remoteSession, client) {
159
+ #state = "connected";
160
+ #fallbackHandoff;
161
+ get isReconnecting() {
162
+ return this.#state === "reconnecting";
163
+ }
164
+ get isFallback() {
165
+ return this.#state === "fallback";
166
+ }
167
+ constructor(local, remoteSession, client, lifecycle) {
59
168
  this.#local = local;
60
169
  this.#remoteSession = remoteSession;
61
170
  this.#client = client;
171
+ this.#lifecycle = lifecycle;
172
+ this.#onTransportGone = lifecycle?.onTransportGone ?? (() => { });
173
+ }
174
+ async #call(call) {
175
+ try {
176
+ return await call();
177
+ }
178
+ catch (error) {
179
+ if (isTransportGoneError(error)) {
180
+ this.#onTransportGone(error);
181
+ return { cancelled: true };
182
+ }
183
+ throw error;
184
+ }
185
+ }
186
+ enterConnected() {
187
+ if (this.#state !== "disposed" && this.#state !== "fallback")
188
+ this.#state = "connected";
189
+ }
190
+ enterReconnecting() {
191
+ if (this.#state !== "disposed" && this.#state !== "fallback")
192
+ this.#state = "reconnecting";
193
+ }
194
+ async enterFallback() {
195
+ if (this.#state === "disposed" || this.#state === "fallback")
196
+ return;
197
+ this.#state = "fallback";
198
+ this.#beforeSessionInvalidate?.();
199
+ const rebind = this.#rebindSession?.();
200
+ if (rebind) {
201
+ this.#fallbackHandoff = rebind.catch((error) => {
202
+ this.#onTransportGone(error);
203
+ });
204
+ await this.#fallbackHandoff;
205
+ }
62
206
  }
63
207
  get session() {
64
- return this.#remoteSession.session;
208
+ return this.#state === "fallback" || this.#state === "disposed"
209
+ ? this.#local.session
210
+ : this.#remoteSession.session;
65
211
  }
66
212
  get services() {
67
213
  return this.#local.services;
68
214
  }
69
215
  get cwd() {
70
- return this.#remoteSession.session.sessionManager.getCwd();
216
+ return this.session.sessionManager.getCwd();
71
217
  }
72
218
  get diagnostics() {
73
219
  return this.#local.diagnostics;
@@ -80,17 +226,41 @@ export class RemoteInteractiveRuntime {
80
226
  }
81
227
  setBeforeSessionInvalidate(callback) {
82
228
  this.#beforeSessionInvalidate = callback;
229
+ this.#local.setBeforeSessionInvalidate(callback);
83
230
  }
84
231
  setRebindSession(callback) {
85
232
  this.#rebindSession = callback;
233
+ this.#local.setRebindSession(callback);
234
+ if (callback && this.#state === "fallback" && !this.#fallbackHandoff) {
235
+ this.#fallbackHandoff = callback().catch((error) => {
236
+ this.#onTransportGone(error);
237
+ });
238
+ }
86
239
  }
87
240
  setHostUiHandler(callback) {
88
241
  this.#remoteSession.setHostUiHandler(callback);
89
242
  }
243
+ #clientInfoSent = false;
244
+ #lastClientWidth = 80;
245
+ setClientInfo(width) {
246
+ this.#lastClientWidth = width;
247
+ const capabilities = this.#clientInfoSent ? undefined : ["rendered_components"];
248
+ this.#clientInfoSent = true;
249
+ void this.#client.setClientInfo(width, capabilities).catch(() => { });
250
+ }
251
+ async reRegisterClientInfo() {
252
+ await this.#client.setClientInfo(this.#lastClientWidth, ["rendered_components"]);
253
+ }
90
254
  async dispose() {
255
+ if (this.#state === "disposed")
256
+ return;
257
+ this.#state = "disposed";
258
+ this.#lifecycle?.dispose();
259
+ await this.#lifecycle?.reconnecting?.catch(() => { });
260
+ await this.#fallbackHandoff?.catch(() => { });
91
261
  const errors = [];
92
262
  for (const cleanup of [
93
- () => this.#remoteSession.abortLocalBash?.(),
263
+ () => this.#remoteSession.abortLocalBash(),
94
264
  () => this.#client.closeSession(),
95
265
  () => this.#client.stop(),
96
266
  () => this.#local.dispose(),
@@ -106,74 +276,138 @@ export class RemoteInteractiveRuntime {
106
276
  throw errors[0];
107
277
  }
108
278
  async newSession(options) {
109
- const result = await this.#client.newSession(options?.parentSession);
110
- if (!result.cancelled) {
111
- this.#beforeSessionInvalidate?.();
112
- this.#remoteSession.abortLocalBash();
113
- await this.#remoteSession.refresh();
114
- if (options?.setup) {
115
- const capture = SessionManager.inMemory(this.#remoteSession.session.sessionManager.getCwd());
116
- await options.setup(capture);
117
- for (const entry of capture.getEntries())
118
- await this.#client.appendSessionEntry(entry);
279
+ if (this.#state === "fallback")
280
+ return this.#local.newSession(options);
281
+ // Suppresses for the ENTIRE command, not just the refresh tail: a
282
+ // multi-session host emits session_replaced while completing it, so the echo
283
+ // can arrive before the refresh/rebind sequence below starts and race it -
284
+ // newSession transports setup entries between two refreshes.
285
+ return this.#remoteSession
286
+ .aroundLocalReplacement(async () => {
287
+ const result = await this.#call(() => this.#client.newSession(options?.parentSession));
288
+ if (!result.cancelled) {
289
+ this.#beforeSessionInvalidate?.();
290
+ this.#remoteSession.abortLocalBash();
119
291
  await this.#remoteSession.refresh();
292
+ if (options?.setup) {
293
+ const capture = SessionManager.inMemory(this.#remoteSession.session.sessionManager.getCwd());
294
+ await options.setup(capture);
295
+ for (const entry of capture.getEntries())
296
+ await this.#client.appendSessionEntry(entry);
297
+ await this.#remoteSession.refresh();
298
+ }
299
+ await this.#rebindSession?.();
300
+ if (options?.withSession)
301
+ await options.withSession(this.#remoteSession.createReplacedSessionContext());
120
302
  }
121
- await this.#rebindSession?.();
122
- if (options?.withSession)
123
- await options.withSession(this.#remoteSession.createReplacedSessionContext());
124
- }
125
- return result;
303
+ return result;
304
+ })
305
+ .catch((error) => {
306
+ if (isTransportGoneError(error))
307
+ return { cancelled: true };
308
+ throw error;
309
+ });
126
310
  }
127
311
  async switchSession(sessionPath, options) {
128
- const result = await this.#client.switchSession(sessionPath, options?.cwdOverride === undefined ? undefined : { cwdOverride: options.cwdOverride });
129
- if (!result.cancelled) {
130
- this.#beforeSessionInvalidate?.();
131
- this.#remoteSession.abortLocalBash();
132
- await this.#remoteSession.refresh();
133
- await this.#rebindSession?.();
134
- // The shared host already resolved trust; this callback is retained for
135
- // compatibility, but its result cannot override host-authoritative state.
136
- options?.projectTrustContextFactory?.(this.#remoteSession.session.sessionManager.getCwd());
137
- if (options?.withSession)
138
- await options.withSession(this.#remoteSession.createReplacedSessionContext());
139
- }
140
- return result;
312
+ if (this.#state === "fallback")
313
+ return this.#local.switchSession(sessionPath, options);
314
+ // See newSession: the suppression must cover the command itself, since the
315
+ // host emits session_replaced while completing it.
316
+ return this.#remoteSession
317
+ .aroundLocalReplacement(async () => {
318
+ const result = await this.#call(() => this.#client.switchSession(sessionPath, options?.cwdOverride === undefined ? undefined : { cwdOverride: options.cwdOverride }));
319
+ if (!result.cancelled) {
320
+ this.#beforeSessionInvalidate?.();
321
+ this.#remoteSession.abortLocalBash();
322
+ await this.#remoteSession.refresh();
323
+ await this.#rebindSession?.();
324
+ // The shared host already resolved trust; this callback is retained for
325
+ // compatibility, but its result cannot override host-authoritative state.
326
+ options?.projectTrustContextFactory?.(this.#remoteSession.session.sessionManager.getCwd());
327
+ if (options?.withSession)
328
+ await options.withSession(this.#remoteSession.createReplacedSessionContext());
329
+ }
330
+ return result;
331
+ })
332
+ .catch((error) => {
333
+ if (isTransportGoneError(error))
334
+ return { cancelled: true };
335
+ throw error;
336
+ });
141
337
  }
142
338
  async fork(entryId, options) {
143
- const result = await this.#client.fork(entryId, options);
144
- if (!result.cancelled) {
145
- this.#beforeSessionInvalidate?.();
146
- this.#remoteSession.abortLocalBash();
147
- await this.#refreshAndRebind();
148
- if (options?.withSession)
149
- await options.withSession(this.#remoteSession.createReplacedSessionContext());
150
- }
151
- return { cancelled: result.cancelled, selectedText: result.text };
339
+ if (this.#state === "fallback")
340
+ return this.#local.fork(entryId, options);
341
+ // See newSession: the suppression must cover the command itself.
342
+ return this.#remoteSession
343
+ .aroundLocalReplacement(async () => {
344
+ const result = await this.#call(() => this.#client.fork(entryId, options));
345
+ if (!result.cancelled) {
346
+ this.#beforeSessionInvalidate?.();
347
+ this.#remoteSession.abortLocalBash();
348
+ await this.#refreshAndRebind();
349
+ if (options?.withSession)
350
+ await options.withSession(this.#remoteSession.createReplacedSessionContext());
351
+ }
352
+ return { cancelled: result.cancelled, selectedText: result.text };
353
+ })
354
+ .catch((error) => {
355
+ if (isTransportGoneError(error))
356
+ return { cancelled: true };
357
+ throw error;
358
+ });
152
359
  }
153
360
  async importFromJsonl(inputPath, cwdOverride) {
154
- const result = await this.#client.importJsonl(inputPath, cwdOverride);
155
- if (!result.cancelled) {
156
- this.#beforeSessionInvalidate?.();
157
- this.#remoteSession.abortLocalBash();
158
- await this.#refreshAndRebind();
159
- }
160
- return result;
361
+ if (this.#state === "fallback")
362
+ return this.#local.importFromJsonl(inputPath, cwdOverride);
363
+ // See newSession: the suppression must cover the command itself.
364
+ return this.#remoteSession
365
+ .aroundLocalReplacement(async () => {
366
+ const result = await this.#call(() => this.#client.importJsonl(inputPath, cwdOverride));
367
+ if (!result.cancelled) {
368
+ this.#beforeSessionInvalidate?.();
369
+ this.#remoteSession.abortLocalBash();
370
+ await this.#refreshAndRebind();
371
+ }
372
+ return result;
373
+ })
374
+ .catch((error) => {
375
+ if (isTransportGoneError(error))
376
+ return { cancelled: true };
377
+ throw error;
378
+ });
161
379
  }
162
380
  async #refreshAndRebind() {
163
381
  await this.#remoteSession.refresh();
164
382
  await this.#rebindSession?.();
165
383
  }
166
384
  }
167
- function createRemoteSessionProxy(local, agentDir, client, initialState, onWarning) {
385
+ /** Exported for direct coverage of the wire-event handling below. */
386
+ export function createRemoteSessionProxy(local, agentDir, client, initialState, onWarning, onTransportGone, startupEvents = []) {
168
387
  // Fire-and-forget setters keep the sync AgentSession signature, but their RPC
169
388
  // failures must not vanish: the matching *_changed wire event confirms success,
170
389
  // and a rejection here is the only signal of failure.
171
390
  const reportActionFailure = (action) => (error) => {
172
- onWarning?.({
173
- type: "interactive_host_action_failed",
174
- message: `Warning: shared interactive host ${action} failed: ${error instanceof Error ? error.message : String(error)}`,
175
- cause: error,
176
- });
391
+ const transportGone = isTransportGoneError(error);
392
+ if (transportGone)
393
+ onTransportGone?.(error);
394
+ else
395
+ onWarning?.({
396
+ type: "interactive_host_action_failed",
397
+ message: `Warning: shared interactive host ${action} failed: ${error instanceof Error ? error.message : String(error)}`,
398
+ cause: error,
399
+ });
400
+ };
401
+ const transportCall = async (action, call, cancelledValue) => {
402
+ try {
403
+ return await call();
404
+ }
405
+ catch (error) {
406
+ if (!isTransportGoneError(error))
407
+ throw error;
408
+ reportActionFailure(action)(error);
409
+ return cancelledValue;
410
+ }
177
411
  };
178
412
  let state = { ...initialState };
179
413
  const waitForBashCallbacks = async (promises, allowAbandonment) => {
@@ -245,7 +479,7 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
245
479
  const remoteSessionManager = new Proxy({}, {
246
480
  get(_target, property, _receiver) {
247
481
  if (property === "appendLabelChange") {
248
- return (entryId, label) => void client.setLabel(entryId, label);
482
+ return (entryId, label) => void client.setLabel(entryId, label).catch(reportActionFailure("appendLabelChange"));
249
483
  }
250
484
  if (property === "getCwd")
251
485
  return () => state.cwd;
@@ -259,16 +493,38 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
259
493
  });
260
494
  let streamingAssistant;
261
495
  let mirroredCurrentAssistantUsage = false;
496
+ /** Non-zero while this runtime is driving its own replacement sequence. */
497
+ let localReplacementDepth = 0;
262
498
  const listeners = new Set();
263
- client.onEvent((wireEvent) => {
499
+ const handleWireEvent = (wireEvent) => {
264
500
  if (wireEvent.type === "extension_ui_request") {
265
501
  const request = wireEvent;
266
502
  if (hostUiHandler)
267
- void Promise.resolve(hostUiHandler(request)).then((response) => response && client.sendExtensionUIResponse(response));
503
+ void Promise.resolve(hostUiHandler(request))
504
+ .then((response) => response && client.sendExtensionUIResponse(response))
505
+ .catch(reportActionFailure("host UI response"));
268
506
  else
269
507
  pendingUiRequests.push(request);
270
508
  return;
271
509
  }
510
+ if (wireEvent.type === "session_replaced") {
511
+ // The host swapped the live session behind this connection - another
512
+ // attached client issued the replacement, or an extension drove one that no
513
+ // client issued. Nothing else re-reads the binding, so without this the
514
+ // proxy keeps serving the previous session's manager, settings and message
515
+ // mirror while the host has already moved on. The command response carries
516
+ // only `{ cancelled }`, so this event is the only signal available here.
517
+ //
518
+ // A multi-session host broadcasts the event to every connection including
519
+ // the one that issued the replacement, and this runtime's own replacement
520
+ // methods already run an ordered refresh/rebind - newSession additionally
521
+ // transports setup entries between two refreshes. Refreshing again from
522
+ // here would race that sequence, so self-driven replacements are skipped.
523
+ if (localReplacementDepth === 0) {
524
+ void refresh().catch(reportActionFailure("session refresh after replacement"));
525
+ }
526
+ return;
527
+ }
272
528
  if (wireEvent.type === "agent_settled")
273
529
  state = { ...state, isStreaming: false, retryAttempt: 0 };
274
530
  if (wireEvent.type === "bash_start") {
@@ -402,9 +658,18 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
402
658
  const event = hydrateMessageUpdate(wireEvent, streamingAssistant);
403
659
  for (const listener of listeners)
404
660
  listener(event);
405
- });
406
- const refresh = async () => {
407
- const nextState = await client.getState();
661
+ };
662
+ client.onEvent(handleWireEvent);
663
+ for (const event of startupEvents)
664
+ handleWireEvent(event);
665
+ const performRefresh = async () => {
666
+ // Captured before the first await: entries that arrive via `entry_appended`
667
+ // while this refresh is in flight are newer than the snapshot it reconciles
668
+ // against, and must survive the rebuild below.
669
+ const idsAtRefreshStart = new Set(sessionManager.getEntries().map((entry) => entry.id));
670
+ const nextState = await transportCall("refresh", () => client.getState());
671
+ if (!nextState)
672
+ return;
408
673
  state = { ...stateFromRpc(nextState) };
409
674
  nextQueuedInputOrder = Math.max(0, ...nextState.ordered.map((item) => item.enqueueOrder));
410
675
  let messages;
@@ -423,38 +688,83 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
423
688
  // deferred creating the file for a setup-only session.
424
689
  sessionManager = SessionManager.open(nextState.sessionFile, undefined, nextState.cwd);
425
690
  }
426
- if (nextState.entries?.length && !sessionManager.getEntries().length) {
427
- for (const entry of nextState.entries)
428
- sessionManager.appendEntry(entry);
691
+ // The host ships its complete entry list while the session file is still
692
+ // deferred (no assistant message yet, so nothing has been written), which
693
+ // makes that list the only authoritative view of the session. Reconcile the
694
+ // mirror to it exactly: `entry_appended` notifications that cross a
695
+ // concurrent refresh land on whichever manager is current, so the mirror
696
+ // can hold an arbitrary partial set rather than a prefix, and a snapshot
697
+ // taken mid-bind can simply be short. Backfilling only an empty mirror
698
+ // wedged it at whatever partial set it happened to hold.
699
+ const authoritative = nextState.entries;
700
+ if (authoritative?.length) {
701
+ const mirror = sessionManager.getEntries();
702
+ const matches = mirror.length === authoritative.length &&
703
+ mirror.every((entry, index) => entry.id === authoritative[index]?.id);
704
+ if (!matches) {
705
+ const rebuilt = SessionManager.open(nextState.sessionFile, undefined, nextState.cwd);
706
+ for (const entry of authoritative)
707
+ rebuilt.appendEntry(entry);
708
+ // A notification that crossed this refresh refers to an entry the
709
+ // snapshot predates, so it is newer than the snapshot; dropping it
710
+ // would strand the entry until an unrelated refresh happened to
711
+ // run. Keep it, in arrival order, after the authoritative list.
712
+ const authoritativeIds = new Set(authoritative.map((entry) => entry.id));
713
+ for (const entry of mirror) {
714
+ if (!idsAtRefreshStart.has(entry.id) && !authoritativeIds.has(entry.id)) {
715
+ rebuilt.appendEntry(entry);
716
+ }
717
+ }
718
+ sessionManager = rebuilt;
719
+ }
429
720
  }
430
721
  messages = sessionManager.buildSessionContext().messages;
431
722
  }
432
723
  else {
433
- messages = await client.getMessages();
724
+ messages = await transportCall("refresh", () => client.getMessages(), []);
434
725
  }
435
726
  local.agent.state.messages.splice(0, local.agent.state.messages.length, ...structuredClone(messages));
436
727
  streamingAssistant = undefined;
437
728
  };
729
+ // Refreshes must not interleave: each one reassigns sessionManager,
730
+ // settingsManager and the mirrored message list, so two in flight can commit a
731
+ // mixed snapshot of two different sessions. A replacement-driven refresh races
732
+ // exactly that way against a caller-driven one, so run them in order.
733
+ let refreshChain = Promise.resolve();
734
+ const refresh = () => {
735
+ const next = refreshChain.then(performRefresh, performRefresh);
736
+ refreshChain = next.then(() => { }, () => { });
737
+ return next;
738
+ };
438
739
  const session = new Proxy(local, {
439
740
  get(target, property, receiver) {
440
741
  if (property === "prompt")
441
- return (message, options) => client.prompt(message, {
442
- ...(options?.images ? { images: options.images } : {}),
443
- ...(options?.streamingBehavior ? { streamingBehavior: options.streamingBehavior } : {}),
444
- ...(options?.thinkingLevel ? { thinkingLevel: options.thinkingLevel } : {}),
445
- ...(options?.sessionTitlePrompt !== undefined
446
- ? { sessionTitlePrompt: options.sessionTitlePrompt }
447
- : {}),
448
- ...(options?.expandPromptTemplates !== undefined
449
- ? { expandPromptTemplates: options.expandPromptTemplates }
450
- : {}),
451
- ...(options?.promptDisposition ? { promptDisposition: options.promptDisposition } : {}),
452
- ...(options?.preflightResult ? { preflightResult: options.preflightResult } : {}),
453
- });
742
+ return async (message, options) => {
743
+ try {
744
+ await client.prompt(message, {
745
+ ...(options?.images ? { images: options.images } : {}),
746
+ ...(options?.streamingBehavior ? { streamingBehavior: options.streamingBehavior } : {}),
747
+ ...(options?.thinkingLevel ? { thinkingLevel: options.thinkingLevel } : {}),
748
+ ...(options?.sessionTitlePrompt !== undefined
749
+ ? { sessionTitlePrompt: options.sessionTitlePrompt }
750
+ : {}),
751
+ ...(options?.expandPromptTemplates !== undefined
752
+ ? { expandPromptTemplates: options.expandPromptTemplates }
753
+ : {}),
754
+ ...(options?.promptDisposition ? { promptDisposition: options.promptDisposition } : {}),
755
+ ...(options?.preflightResult ? { preflightResult: options.preflightResult } : {}),
756
+ });
757
+ }
758
+ catch (error) {
759
+ if (!isTransportGoneError(error))
760
+ throw error;
761
+ reportActionFailure("prompt")(error);
762
+ }
763
+ };
454
764
  if (property === "abort")
455
765
  return () => client.abort();
456
766
  if (property === "abortCompaction")
457
- return () => void client.abortCompaction();
767
+ return () => void client.abortCompaction().catch(reportActionFailure("abortCompaction"));
458
768
  if (property === "steer")
459
769
  return (message, images, recovery) => {
460
770
  const enqueueOrder = recovery?.enqueueOrder ?? Math.max(0, ...state.ordered.map((item) => item.enqueueOrder)) + 1;
@@ -464,7 +774,7 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
464
774
  ordered: [...state.ordered, { text: message, mode: "steer", enqueueOrder }],
465
775
  pendingMessageCount: state.pendingMessageCount + 1,
466
776
  };
467
- return client.steer(message, images, { ...recovery, enqueueOrder });
777
+ return transportCall("steer", () => client.steer(message, images, { ...recovery, enqueueOrder }));
468
778
  };
469
779
  if (property === "followUp")
470
780
  return (message, images, recovery) => {
@@ -475,7 +785,7 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
475
785
  ordered: [...state.ordered, { text: message, mode: "followUp", enqueueOrder }],
476
786
  pendingMessageCount: state.pendingMessageCount + 1,
477
787
  };
478
- return client.followUp(message, images, { ...recovery, enqueueOrder });
788
+ return transportCall("followUp", () => client.followUp(message, images, { ...recovery, enqueueOrder }));
479
789
  };
480
790
  if (property === "waitForIdle")
481
791
  return () => client.waitForIdle();
@@ -483,8 +793,8 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
483
793
  return () => target.getLastAssistantText();
484
794
  if (property === "setModel" || property === "setSessionModel")
485
795
  return async (model) => {
486
- const next = await client.setModel(model.provider, model.id);
487
- return { systemPromptName: next.systemPromptName, model: next };
796
+ const next = await transportCall("setModel", () => client.setModel(model.provider, model.id), undefined);
797
+ return next ? { systemPromptName: next.systemPromptName, model: next } : undefined;
488
798
  };
489
799
  if (property === "setSessionThinkingLevel")
490
800
  return (level) => void client
@@ -509,19 +819,19 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
509
819
  void client.setScopedModels(models).catch(reportActionFailure("setScopedModels"));
510
820
  };
511
821
  if (property === "cycleModel")
512
- return (direction) => client.cycleModel(direction);
822
+ return (direction) => transportCall("cycleModel", () => client.cycleModel(direction), undefined);
513
823
  if (property === "setThinkingLevel")
514
824
  return (level) => void client.setThinkingLevel(level).catch(reportActionFailure("setThinkingLevel"));
515
825
  if (property === "cycleThinkingLevel")
516
- return () => client.cycleThinkingLevel().then((result) => result?.level);
826
+ return () => transportCall("cycleThinkingLevel", () => client.cycleThinkingLevel(), undefined).then((result) => result?.level);
517
827
  if (property === "getAvailableThinkingLevels")
518
- return () => client.getAvailableThinkingLevels();
828
+ return () => transportCall("getAvailableThinkingLevels", () => client.getAvailableThinkingLevels(), []);
519
829
  if (property === "setSteeringMode")
520
830
  return (mode) => void client.setSteeringMode(mode).catch(reportActionFailure("setSteeringMode"));
521
831
  if (property === "setFollowUpMode")
522
832
  return (mode) => void client.setFollowUpMode(mode).catch(reportActionFailure("setFollowUpMode"));
523
833
  if (property === "compact")
524
- return (instructions) => client.compact(instructions);
834
+ return (instructions) => transportCall("compact", () => client.compact(instructions));
525
835
  if (property === "setAutoCompactionEnabled")
526
836
  return (enabled) => void client.setAutoCompaction(enabled).catch(reportActionFailure("setAutoCompaction"));
527
837
  if (property === "executeBash")
@@ -537,7 +847,7 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
537
847
  try {
538
848
  const result = await executeBashWithOperations(resolvedCommand, state.cwd, options.operations, { onChunk, signal: abortController.signal });
539
849
  if (state.sessionId === sessionAtStart) {
540
- await client.recordBashResult(command, result, options.excludeFromContext);
850
+ await transportCall("recordBashResult", () => client.recordBashResult(command, result, options.excludeFromContext));
541
851
  }
542
852
  return result;
543
853
  }
@@ -556,11 +866,11 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
556
866
  };
557
867
  bashExecutions.set(executionId, execution);
558
868
  try {
559
- const result = await client.bash(command, {
869
+ const result = await transportCall("bash", () => client.bash(command, {
560
870
  excludeFromContext: options?.excludeFromContext,
561
871
  executionId,
562
872
  operations: options?.operations,
563
- });
873
+ }), { output: "", exitCode: undefined, cancelled: true, truncated: false });
564
874
  const callbacksSettled = await waitForBashCallbacks(execution.promises, false);
565
875
  if (!callbacksSettled) {
566
876
  if (result.fullOutputPath)
@@ -588,20 +898,22 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
588
898
  if (property === "getContextUsage")
589
899
  return () => state.contextUsage;
590
900
  if (property === "getSessionStats")
591
- return () => client.getSessionStats();
901
+ return () => transportCall("getSessionStats", () => client.getSessionStats(), local.getSessionStats());
592
902
  if (property === "exportToHtml")
593
- return (outputPath, options) => client.exportHtml(outputPath, options?.themeName).then((result) => result.path);
903
+ return (outputPath, options) => transportCall("exportToHtml", () => client.exportHtml(outputPath, options?.themeName)).then((result) => result?.path);
594
904
  if (property === "setSessionName")
595
905
  return (name) => client.setSessionName(name).catch(reportActionFailure("setSessionName"));
596
906
  if (property === "navigateTree")
597
907
  return async (targetId, options) => {
598
- const result = await client.navigateTree(targetId, options);
908
+ const result = await transportCall("navigateTree", () => client.navigateTree(targetId, options), {
909
+ cancelled: true,
910
+ });
599
911
  if (!result.cancelled)
600
912
  await refresh();
601
913
  return result;
602
914
  };
603
915
  if (property === "getUserMessagesForForking")
604
- return () => client.getForkMessages();
916
+ return () => transportCall("getUserMessagesForForking", () => client.getForkMessages(), []);
605
917
  if (property === "subscribe")
606
918
  return (listener) => {
607
919
  listeners.add(listener);
@@ -647,11 +959,11 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
647
959
  if (property === "isBashRunning")
648
960
  return state.isBashRunning;
649
961
  if (property === "reload")
650
- return (_options) => client.reload();
962
+ return (_options) => transportCall("reload", () => client.reload(), { cancelled: true });
651
963
  if (property === "checkReloadVeto")
652
- return () => client.checkReloadVeto();
964
+ return () => transportCall("checkReloadVeto", () => client.checkReloadVeto(), { cancelled: true });
653
965
  if (property === "exportToJsonl")
654
- return (outputPath) => client.exportJsonl(outputPath).then((result) => result.path);
966
+ return (outputPath) => transportCall("exportToJsonl", () => client.exportJsonl(outputPath)).then((result) => result?.path);
655
967
  // The footer and other renderers read session.state.*; surface the
656
968
  // host-authoritative fields there too, not only via the direct getters.
657
969
  if (property === "state") {
@@ -703,34 +1015,45 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
703
1015
  hostUiHandler = callback;
704
1016
  if (callback)
705
1017
  for (const request of pendingUiRequests.splice(0))
706
- void Promise.resolve(callback(request)).then((response) => response && client.sendExtensionUIResponse(response));
1018
+ void Promise.resolve(callback(request))
1019
+ .then((response) => response && client.sendExtensionUIResponse(response))
1020
+ .catch(reportActionFailure("host UI response"));
707
1021
  },
708
1022
  refresh,
1023
+ aroundLocalReplacement: async (body) => {
1024
+ localReplacementDepth++;
1025
+ try {
1026
+ return await body();
1027
+ }
1028
+ finally {
1029
+ localReplacementDepth--;
1030
+ }
1031
+ },
709
1032
  abortLocalBash: () => localBashAbortController?.abort(),
710
1033
  createReplacedSessionContext: () => {
711
1034
  const context = local.createReplacedSessionContext();
712
1035
  Object.defineProperty(context, "cwd", { value: state.cwd });
713
1036
  Object.defineProperty(context, "sessionManager", { value: remoteSessionManager });
714
- context.sendMessage = (message, options) => client.sendCustomMessage(message, {
1037
+ context.sendMessage = (message, options) => transportCall("sendMessage", () => client.sendCustomMessage(message, {
715
1038
  triggerTurn: options?.triggerTurn,
716
1039
  deliverAs: options?.deliverAs,
717
- });
1040
+ }));
718
1041
  context.sendUserMessage = (content, options) => {
719
1042
  if (typeof content === "string")
720
- return client.prompt(content, {
1043
+ return transportCall("sendUserMessage", () => client.prompt(content, {
721
1044
  streamingBehavior: options?.deliverAs,
722
1045
  expandPromptTemplates: options?.expandPromptTemplates,
723
- });
1046
+ }));
724
1047
  const text = content
725
1048
  .filter((part) => part.type === "text")
726
1049
  .map((part) => part.text)
727
1050
  .join("\n");
728
1051
  const images = content.filter((part) => part.type === "image");
729
- return client.prompt(text, {
1052
+ return transportCall("sendUserMessage", () => client.prompt(text, {
730
1053
  images,
731
1054
  streamingBehavior: options?.deliverAs,
732
1055
  expandPromptTemplates: options?.expandPromptTemplates,
733
- });
1056
+ }));
734
1057
  };
735
1058
  return context;
736
1059
  },