@code-yeongyu/senpi 2026.9.27-4 → 2026.9.28-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 (294) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/dist/bundle/chunks/{app-server-command-3GVJZFFH.js → app-server-command-H6LFICZ2.js} +1 -1
  3. package/dist/bundle/chunks/{azure-openai-responses-4SUOOZZ4.js → azure-openai-responses-FCZNBRWE.js} +1 -1
  4. package/dist/bundle/chunks/bedrock-converse-stream.js +1 -1
  5. package/dist/bundle/chunks/{chunk-PSCNHY7D.js → chunk-3GMFUEU7.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-HBG6SQU5.js → chunk-62XHQXCZ.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-EJKCBRYK.js → chunk-6BWCPOCV.js} +4 -4
  8. package/dist/bundle/chunks/{chunk-SLL375O7.js → chunk-6VD3EBQE.js} +1 -1
  9. package/dist/bundle/chunks/chunk-7YUGCTEV.js +15 -0
  10. package/dist/bundle/chunks/{chunk-FWBSGMAE.js → chunk-A4UMPXQF.js} +1 -1
  11. package/dist/bundle/chunks/chunk-BBK6SSYT.js +2 -0
  12. package/dist/bundle/chunks/{chunk-C2425O2Q.js → chunk-CNQ4JB45.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-27IFXX3B.js → chunk-DRN4FQCE.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-22MGY5JQ.js → chunk-EHSZ7UCW.js} +1 -1
  15. package/dist/bundle/chunks/chunk-FHJYIU3U.js +5 -0
  16. package/dist/bundle/chunks/chunk-GJUCSTAG.js +19 -0
  17. package/dist/bundle/chunks/chunk-KOPTADA3.js +8 -0
  18. package/dist/bundle/chunks/{chunk-VMKAYXHL.js → chunk-LSW7QSLV.js} +31 -26
  19. package/dist/bundle/chunks/{chunk-2BMD37YV.js → chunk-LUPPSEQW.js} +1 -1
  20. package/dist/bundle/chunks/{chunk-NRGMT74E.js → chunk-QOVUQZO4.js} +1 -1
  21. package/dist/bundle/chunks/{chunk-IKXPBYSO.js → chunk-SICBVLRH.js} +1 -1
  22. package/dist/bundle/chunks/chunk-SN4ZHPMN.js +135 -0
  23. package/dist/bundle/chunks/{chunk-MUTD2NOH.js → chunk-TFA3YAJK.js} +1 -1
  24. package/dist/bundle/chunks/{chunk-4T4GFAGK.js → chunk-VFSXUT7K.js} +1 -1
  25. package/dist/bundle/chunks/{cli-main-66NFBKVH.js → cli-main-GYRTYPMG.js} +1 -1
  26. package/dist/bundle/chunks/{google-generative-ai-ZTGKZTGU.js → google-generative-ai-LFCBHG3Y.js} +1 -1
  27. package/dist/bundle/chunks/{google-vertex-CCZ376YK.js → google-vertex-C474FXU7.js} +1 -1
  28. package/dist/bundle/chunks/{help-fast-path-NZ3I2UEN.js → help-fast-path-3U3YDEZ6.js} +1 -1
  29. package/dist/bundle/chunks/{host-command-RORPAC5X.js → host-command-6CJ7YM6W.js} +1 -1
  30. package/dist/bundle/chunks/{host-lifecycle-UDMJGLOR.js → host-lifecycle-RZIMOFDM.js} +1 -1
  31. package/dist/bundle/chunks/host-lifecycle.js +6 -6
  32. package/dist/bundle/chunks/interactive-host-runtime-A3GEIFTU.js +4 -0
  33. package/dist/bundle/chunks/{interactive-mode-LOAADKLH.js → interactive-mode-LX665HTP.js} +1 -1
  34. package/dist/bundle/chunks/{list-tips-S5CANO5W.js → list-tips-S4U7TKER.js} +1 -1
  35. package/dist/bundle/chunks/multi-session-host-MFTUM6XQ.js +21 -0
  36. package/dist/bundle/chunks/{openai-codex-responses-YSGZZDD5.js → openai-codex-responses-2LDIFL6Z.js} +1 -1
  37. package/dist/bundle/chunks/{openai-completions-KWHRWCBN.js → openai-completions-7KZLPKYP.js} +1 -1
  38. package/dist/bundle/chunks/{openai-images-6U5FCN5S.js → openai-images-I7DR7ERP.js} +1 -1
  39. package/dist/bundle/chunks/{openai-responses-7PWMBGYX.js → openai-responses-LCHS3AF3.js} +1 -1
  40. package/dist/bundle/chunks/{openrouter-images-QIKWQZUD.js → openrouter-images-IPNDRK6C.js} +1 -1
  41. package/dist/bundle/chunks/{package-manager-cli-5EEDA35I.js → package-manager-cli-6IAE5WG3.js} +1 -1
  42. package/dist/bundle/chunks/{rotation-stream-KZG4OVR7.js → rotation-stream-ZVJEGA2G.js} +1 -1
  43. package/dist/bundle/chunks/{rpc-mode-HOEA5NGQ.js → rpc-mode-XN3HNZMH.js} +1 -1
  44. package/dist/bundle/chunks/session-picker-FPKB4DAC.js +2 -0
  45. package/dist/bundle/chunks/session-worker.js +318 -309
  46. package/dist/bundle/cli.js +1 -1
  47. package/dist/bundle/index.js +1 -1
  48. package/dist/bundle/rpc-entry.js +1 -1
  49. package/dist/cli/args.js +1 -1
  50. package/dist/cli/args.js.map +1 -1
  51. package/dist/cli/host-command.d.ts +8 -1
  52. package/dist/cli/host-command.d.ts.map +1 -1
  53. package/dist/cli/host-command.js +66 -14
  54. package/dist/cli/host-command.js.map +1 -1
  55. package/dist/core/agent-session.d.ts +18 -1
  56. package/dist/core/agent-session.d.ts.map +1 -1
  57. package/dist/core/agent-session.js +119 -3
  58. package/dist/core/agent-session.js.map +1 -1
  59. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts +2 -2
  60. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts.map +1 -1
  61. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js +13 -5
  62. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js.map +1 -1
  63. package/dist/core/extensions/builtin/tool-search/native-search.d.ts.map +1 -1
  64. package/dist/core/extensions/builtin/tool-search/native-search.js +13 -1
  65. package/dist/core/extensions/builtin/tool-search/native-search.js.map +1 -1
  66. package/dist/core/retry-fallback/candidates.d.ts +26 -0
  67. package/dist/core/retry-fallback/candidates.d.ts.map +1 -0
  68. package/dist/core/retry-fallback/candidates.js +41 -0
  69. package/dist/core/retry-fallback/candidates.js.map +1 -0
  70. package/dist/core/retry-fallback/circuit-probes.d.ts +30 -0
  71. package/dist/core/retry-fallback/circuit-probes.d.ts.map +1 -0
  72. package/dist/core/retry-fallback/circuit-probes.js +60 -0
  73. package/dist/core/retry-fallback/circuit-probes.js.map +1 -0
  74. package/dist/core/retry-fallback/circuit.d.ts +101 -0
  75. package/dist/core/retry-fallback/circuit.d.ts.map +1 -0
  76. package/dist/core/retry-fallback/circuit.js +175 -0
  77. package/dist/core/retry-fallback/circuit.js.map +1 -0
  78. package/dist/core/retry-fallback/controller-types.d.ts +58 -0
  79. package/dist/core/retry-fallback/controller-types.d.ts.map +1 -0
  80. package/dist/core/retry-fallback/controller-types.js +2 -0
  81. package/dist/core/retry-fallback/controller-types.js.map +1 -0
  82. package/dist/core/retry-fallback/controller.d.ts +21 -49
  83. package/dist/core/retry-fallback/controller.d.ts.map +1 -1
  84. package/dist/core/retry-fallback/controller.js +88 -42
  85. package/dist/core/retry-fallback/controller.js.map +1 -1
  86. package/dist/core/sdk.d.ts.map +1 -1
  87. package/dist/core/sdk.js +25 -17
  88. package/dist/core/sdk.js.map +1 -1
  89. package/dist/core/session-failure-report.d.ts +33 -0
  90. package/dist/core/session-failure-report.d.ts.map +1 -0
  91. package/dist/core/session-failure-report.js +62 -0
  92. package/dist/core/session-failure-report.js.map +1 -0
  93. package/dist/core/settings-manager.d.ts +3 -0
  94. package/dist/core/settings-manager.d.ts.map +1 -1
  95. package/dist/core/settings-manager.js +4 -0
  96. package/dist/core/settings-manager.js.map +1 -1
  97. package/dist/core/slash-commands.d.ts.map +1 -1
  98. package/dist/core/slash-commands.js +1 -0
  99. package/dist/core/slash-commands.js.map +1 -1
  100. package/dist/index.d.ts +1 -1
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +1 -1
  103. package/dist/index.js.map +1 -1
  104. package/dist/modes/index.d.ts +1 -0
  105. package/dist/modes/index.d.ts.map +1 -1
  106. package/dist/modes/index.js +2 -0
  107. package/dist/modes/index.js.map +1 -1
  108. package/dist/modes/interactive/help-content.d.ts.map +1 -1
  109. package/dist/modes/interactive/help-content.js +3 -0
  110. package/dist/modes/interactive/help-content.js.map +1 -1
  111. package/dist/modes/interactive/interactive-host-attach.d.ts +10 -0
  112. package/dist/modes/interactive/interactive-host-attach.d.ts.map +1 -0
  113. package/dist/modes/interactive/interactive-host-attach.js +14 -0
  114. package/dist/modes/interactive/interactive-host-attach.js.map +1 -0
  115. package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
  116. package/dist/modes/interactive/interactive-host-runtime.js +3 -6
  117. package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
  118. package/dist/modes/interactive/interactive-mode.d.ts +12 -0
  119. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  120. package/dist/modes/interactive/interactive-mode.js +95 -1
  121. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  122. package/dist/modes/interactive/session-failure-info.d.ts +3 -0
  123. package/dist/modes/interactive/session-failure-info.d.ts.map +1 -0
  124. package/dist/modes/interactive/session-failure-info.js +21 -0
  125. package/dist/modes/interactive/session-failure-info.js.map +1 -0
  126. package/dist/modes/interactive/tips/catalog/model-tips.d.ts +7 -7
  127. package/dist/modes/interactive/tips/catalog/model-tips.d.ts.map +1 -1
  128. package/dist/modes/interactive/tips/catalog/model-tips.js +7 -1
  129. package/dist/modes/interactive/tips/catalog/model-tips.js.map +1 -1
  130. package/dist/modes/interactive/tips/catalog/session-tips.d.ts.map +1 -1
  131. package/dist/modes/interactive/tips/catalog/session-tips.js +1 -1
  132. package/dist/modes/interactive/tips/catalog/session-tips.js.map +1 -1
  133. package/dist/modes/rpc/host-attach-hold.d.ts +16 -0
  134. package/dist/modes/rpc/host-attach-hold.d.ts.map +1 -0
  135. package/dist/modes/rpc/host-attach-hold.js +9 -0
  136. package/dist/modes/rpc/host-attach-hold.js.map +1 -0
  137. package/dist/modes/rpc/host-cleanup-paths.d.ts +20 -0
  138. package/dist/modes/rpc/host-cleanup-paths.d.ts.map +1 -0
  139. package/dist/modes/rpc/host-cleanup-paths.js +13 -0
  140. package/dist/modes/rpc/host-cleanup-paths.js.map +1 -0
  141. package/dist/modes/rpc/host-client-occupancy.d.ts +24 -0
  142. package/dist/modes/rpc/host-client-occupancy.d.ts.map +1 -0
  143. package/dist/modes/rpc/host-client-occupancy.js +53 -0
  144. package/dist/modes/rpc/host-client-occupancy.js.map +1 -0
  145. package/dist/modes/rpc/host-daemon-paths.d.ts +61 -8
  146. package/dist/modes/rpc/host-daemon-paths.d.ts.map +1 -1
  147. package/dist/modes/rpc/host-daemon-paths.js +128 -11
  148. package/dist/modes/rpc/host-daemon-paths.js.map +1 -1
  149. package/dist/modes/rpc/host-daemon-registration.d.ts +11 -1
  150. package/dist/modes/rpc/host-daemon-registration.d.ts.map +1 -1
  151. package/dist/modes/rpc/host-daemon-registration.js +28 -15
  152. package/dist/modes/rpc/host-daemon-registration.js.map +1 -1
  153. package/dist/modes/rpc/host-endpoints.d.ts +12 -0
  154. package/dist/modes/rpc/host-endpoints.d.ts.map +1 -0
  155. package/dist/modes/rpc/host-endpoints.js +59 -0
  156. package/dist/modes/rpc/host-endpoints.js.map +1 -0
  157. package/dist/modes/rpc/host-ensure-lock.d.ts +11 -0
  158. package/dist/modes/rpc/host-ensure-lock.d.ts.map +1 -0
  159. package/dist/modes/rpc/host-ensure-lock.js +49 -0
  160. package/dist/modes/rpc/host-ensure-lock.js.map +1 -0
  161. package/dist/modes/rpc/host-ensure.d.ts +7 -0
  162. package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
  163. package/dist/modes/rpc/host-ensure.js +42 -65
  164. package/dist/modes/rpc/host-ensure.js.map +1 -1
  165. package/dist/modes/rpc/host-gc-evidence.d.ts +18 -0
  166. package/dist/modes/rpc/host-gc-evidence.d.ts.map +1 -0
  167. package/dist/modes/rpc/host-gc-evidence.js +128 -0
  168. package/dist/modes/rpc/host-gc-evidence.js.map +1 -0
  169. package/dist/modes/rpc/host-gc.d.ts +28 -0
  170. package/dist/modes/rpc/host-gc.d.ts.map +1 -0
  171. package/dist/modes/rpc/host-gc.js +110 -0
  172. package/dist/modes/rpc/host-gc.js.map +1 -0
  173. package/dist/modes/rpc/host-generations.d.ts +10 -2
  174. package/dist/modes/rpc/host-generations.d.ts.map +1 -1
  175. package/dist/modes/rpc/host-generations.js +12 -7
  176. package/dist/modes/rpc/host-generations.js.map +1 -1
  177. package/dist/modes/rpc/host-handoff.d.ts +6 -0
  178. package/dist/modes/rpc/host-handoff.d.ts.map +1 -1
  179. package/dist/modes/rpc/host-handoff.js +31 -4
  180. package/dist/modes/rpc/host-handoff.js.map +1 -1
  181. package/dist/modes/rpc/host-lifecycle-policy.d.ts +53 -0
  182. package/dist/modes/rpc/host-lifecycle-policy.d.ts.map +1 -0
  183. package/dist/modes/rpc/host-lifecycle-policy.js +64 -0
  184. package/dist/modes/rpc/host-lifecycle-policy.js.map +1 -0
  185. package/dist/modes/rpc/host-lifecycle.d.ts +1 -44
  186. package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
  187. package/dist/modes/rpc/host-lifecycle.js +23 -85
  188. package/dist/modes/rpc/host-lifecycle.js.map +1 -1
  189. package/dist/modes/rpc/host-observe-request.d.ts +17 -0
  190. package/dist/modes/rpc/host-observe-request.d.ts.map +1 -0
  191. package/dist/modes/rpc/host-observe-request.js +30 -0
  192. package/dist/modes/rpc/host-observe-request.js.map +1 -0
  193. package/dist/modes/rpc/host-probe.d.ts +12 -0
  194. package/dist/modes/rpc/host-probe.d.ts.map +1 -1
  195. package/dist/modes/rpc/host-probe.js +28 -1
  196. package/dist/modes/rpc/host-probe.js.map +1 -1
  197. package/dist/modes/rpc/host-protocol-info.d.ts +2 -0
  198. package/dist/modes/rpc/host-protocol-info.d.ts.map +1 -1
  199. package/dist/modes/rpc/host-protocol-info.js +1 -0
  200. package/dist/modes/rpc/host-protocol-info.js.map +1 -1
  201. package/dist/modes/rpc/host-readiness.d.ts +22 -0
  202. package/dist/modes/rpc/host-readiness.d.ts.map +1 -0
  203. package/dist/modes/rpc/host-readiness.js +44 -0
  204. package/dist/modes/rpc/host-readiness.js.map +1 -0
  205. package/dist/modes/rpc/host-runner.d.ts +13 -0
  206. package/dist/modes/rpc/host-runner.d.ts.map +1 -1
  207. package/dist/modes/rpc/host-runner.js +23 -4
  208. package/dist/modes/rpc/host-runner.js.map +1 -1
  209. package/dist/modes/rpc/host-status-all.d.ts +31 -0
  210. package/dist/modes/rpc/host-status-all.d.ts.map +1 -0
  211. package/dist/modes/rpc/host-status-all.js +78 -0
  212. package/dist/modes/rpc/host-status-all.js.map +1 -0
  213. package/dist/modes/rpc/host-status-rows.d.ts +35 -0
  214. package/dist/modes/rpc/host-status-rows.d.ts.map +1 -0
  215. package/dist/modes/rpc/host-status-rows.js +42 -0
  216. package/dist/modes/rpc/host-status-rows.js.map +1 -0
  217. package/dist/modes/rpc/host-status.d.ts +32 -1
  218. package/dist/modes/rpc/host-status.d.ts.map +1 -1
  219. package/dist/modes/rpc/host-status.js +33 -27
  220. package/dist/modes/rpc/host-status.js.map +1 -1
  221. package/dist/modes/rpc/host-successor.d.ts +3 -1
  222. package/dist/modes/rpc/host-successor.d.ts.map +1 -1
  223. package/dist/modes/rpc/host-successor.js +105 -42
  224. package/dist/modes/rpc/host-successor.js.map +1 -1
  225. package/dist/modes/rpc/host-watchdog.d.ts +6 -0
  226. package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
  227. package/dist/modes/rpc/host-watchdog.js +12 -13
  228. package/dist/modes/rpc/host-watchdog.js.map +1 -1
  229. package/dist/modes/rpc/loop-lag-watchdog.d.ts +16 -0
  230. package/dist/modes/rpc/loop-lag-watchdog.d.ts.map +1 -1
  231. package/dist/modes/rpc/loop-lag-watchdog.js +34 -2
  232. package/dist/modes/rpc/loop-lag-watchdog.js.map +1 -1
  233. package/dist/modes/rpc/multi-session-host.d.ts +2 -1
  234. package/dist/modes/rpc/multi-session-host.d.ts.map +1 -1
  235. package/dist/modes/rpc/multi-session-host.js +33 -6
  236. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  237. package/dist/modes/rpc/rpc-mode.d.ts +2 -2
  238. package/dist/modes/rpc/rpc-mode.js +2 -2
  239. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  240. package/dist/modes/rpc/rpc-types.d.ts +16 -0
  241. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  242. package/dist/modes/rpc/rpc-types.js.map +1 -1
  243. package/dist/modes/rpc/session-command-router.d.ts +9 -1
  244. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  245. package/dist/modes/rpc/session-command-router.js +4 -1
  246. package/dist/modes/rpc/session-command-router.js.map +1 -1
  247. package/dist/modes/rpc/session-path-key.d.ts +3 -1
  248. package/dist/modes/rpc/session-path-key.d.ts.map +1 -1
  249. package/dist/modes/rpc/session-path-key.js +3 -1
  250. package/dist/modes/rpc/session-path-key.js.map +1 -1
  251. package/docs/rpc.md +186 -19
  252. package/docs/settings.md +25 -1
  253. package/docs/usage.md +5 -2
  254. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +24 -0
  255. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  256. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  257. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  258. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  259. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.d.ts +6 -0
  260. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.d.ts.map +1 -1
  261. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.js +36 -0
  262. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.js.map +1 -1
  263. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts +1 -0
  264. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
  265. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +14 -1
  266. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
  267. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  268. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  269. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  270. package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts.map +1 -1
  271. package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js +6 -4
  272. package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js.map +1 -1
  273. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +3 -0
  274. package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
  275. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +20 -0
  276. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
  277. package/node_modules/@earendil-works/pi-tui/dist/editor-component.d.ts +2 -0
  278. package/node_modules/@earendil-works/pi-tui/dist/editor-component.d.ts.map +1 -1
  279. package/node_modules/@earendil-works/pi-tui/dist/editor-component.js.map +1 -1
  280. package/node_modules/@earendil-works/pi-tui/dist/slash-command-autocomplete.d.ts +5 -0
  281. package/node_modules/@earendil-works/pi-tui/dist/slash-command-autocomplete.d.ts.map +1 -1
  282. package/node_modules/@earendil-works/pi-tui/dist/slash-command-autocomplete.js +7 -0
  283. package/node_modules/@earendil-works/pi-tui/dist/slash-command-autocomplete.js.map +1 -1
  284. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  285. package/package.json +7 -7
  286. package/dist/bundle/chunks/chunk-3DEXCYNN.js +0 -135
  287. package/dist/bundle/chunks/chunk-5X4KSPK5.js +0 -12
  288. package/dist/bundle/chunks/chunk-KMD5ZONU.js +0 -5
  289. package/dist/bundle/chunks/chunk-PEHZYGYA.js +0 -8
  290. package/dist/bundle/chunks/chunk-PRC5XIIH.js +0 -2
  291. package/dist/bundle/chunks/chunk-QKY33652.js +0 -18
  292. package/dist/bundle/chunks/interactive-host-runtime-TIFT2SKR.js +0 -4
  293. package/dist/bundle/chunks/multi-session-host-GRHKB7Q2.js +0 -21
  294. package/dist/bundle/chunks/session-picker-FTFCQYHH.js +0 -2
@@ -16,6 +16,13 @@ export interface LoopLagWatchdogOptions {
16
16
  readonly log?: (message: string) => void;
17
17
  readonly now?: () => number;
18
18
  readonly env?: Readonly<Record<string, string | undefined>>;
19
+ /** Process CPU time in microseconds; defaults to `process.cpuUsage()`. */
20
+ readonly cpuUsage?: () => {
21
+ readonly user: number;
22
+ readonly system: number;
23
+ };
24
+ /** Live JS heap in bytes; defaults to `process.memoryUsage().heapUsed`. */
25
+ readonly heapUsed?: () => number;
19
26
  }
20
27
  /**
21
28
  * Event-loop stall detector for the shared host.
@@ -27,6 +34,11 @@ export interface LoopLagWatchdogOptions {
27
34
  * and tool whose work held the loop; past the error threshold it also emits a
28
35
  * `host_stalled` lifecycle record so attached clients (and the desktop) can show it.
29
36
  *
37
+ * Every stall also carries the process CPU time and the heap movement of the stalled
38
+ * window, so it explains itself (senpi#2211): CPU near the drift means the host was busy
39
+ * (a heap drop in the same window points at a collection); CPU near zero means the
40
+ * process did not run - the machine starved it or it sat in a blocking wait.
41
+ *
30
42
  * The watchdog only reports. It never aborts a turn, kills a session, or refuses work.
31
43
  */
32
44
  export declare class LoopLagWatchdog {
@@ -39,6 +51,10 @@ export declare class LoopLagWatchdog {
39
51
  private expectedTickAt;
40
52
  private activityMark;
41
53
  private lastWarnAt;
54
+ private readonly cpuUsage;
55
+ private readonly heapUsed;
56
+ private cpuMicrosAtTick;
57
+ private heapBytesAtTick;
42
58
  constructor(options: LoopLagWatchdogOptions);
43
59
  start(): void;
44
60
  stop(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"loop-lag-watchdog.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/loop-lag-watchdog.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAG1D,+EAA+E;AAC/E,eAAO,MAAM,oBAAoB,+BAA+B,CAAC;AACjE,0FAA0F;AAC1F,eAAO,MAAM,qBAAqB,gCAAgC,CAAC;AACnE,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,yBAAyB,OAAQ,CAAC;AAC/C,6FAA6F;AAC7F,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,6DAA6D;AAC7D,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD,MAAM,WAAW,sBAAsB;IACtC,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACrD,qDAAqD;IACrD,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;CAC5D;AAOD;;;;;;;;;;;GAWG;AACH,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwC;IAC7D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA4B;IAChD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,KAAK,CAA6C;IAC1D,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAAqB;IAEvC,YAAY,OAAO,EAAE,sBAAsB,EAO1C;IAED,KAAK,IAAI,IAAI,CAOZ;IAED,IAAI,IAAI,IAAI,CAKX;IAED;;;OAGG;IACH,IAAI,IAAI,IAAI,CAeX;CACD"}
1
+ {"version":3,"file":"loop-lag-watchdog.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/loop-lag-watchdog.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAG1D,+EAA+E;AAC/E,eAAO,MAAM,oBAAoB,+BAA+B,CAAC;AACjE,0FAA0F;AAC1F,eAAO,MAAM,qBAAqB,gCAAgC,CAAC;AACnE,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,yBAAyB,OAAQ,CAAC;AAC/C,6FAA6F;AAC7F,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,6DAA6D;AAC7D,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD,MAAM,WAAW,sBAAsB;IACtC,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACrD,qDAAqD;IACrD,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC5D,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7E,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,MAAM,CAAC;CACjC;AAaD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwC;IAC7D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA4B;IAChD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,KAAK,CAA6C;IAC1D,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA2D;IACpF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,eAAe,CAAK;IAE5B,YAAY,OAAO,EAAE,sBAAsB,EAS1C;IAED,KAAK,IAAI,IAAI,CAUZ;IAED,IAAI,IAAI,IAAI,CAKX;IAED;;;OAGG;IACH,IAAI,IAAI,IAAI,CAiCX;CACD"}
@@ -11,6 +11,10 @@ export const DEFAULT_LOOP_LAG_ERROR_MS = 5_000;
11
11
  export const LOOP_LAG_TICK_MS = 200;
12
12
  /** One warning per window, however many stalls it covers. */
13
13
  export const LOOP_LAG_WARN_INTERVAL_MS = 10_000;
14
+ const BYTES_PER_MB = 1024 * 1024;
15
+ function describeWindow(processCpuMs, heapDeltaMb) {
16
+ return `cpu=${processCpuMs}ms heap=${heapDeltaMb >= 0 ? "+" : ""}${heapDeltaMb}MB`;
17
+ }
14
18
  function describeAttribution(attribution) {
15
19
  if (attribution?.sessionId === undefined && attribution?.tool === undefined)
16
20
  return "no attributed session";
@@ -26,15 +30,24 @@ function describeAttribution(attribution) {
26
30
  * and tool whose work held the loop; past the error threshold it also emits a
27
31
  * `host_stalled` lifecycle record so attached clients (and the desktop) can show it.
28
32
  *
33
+ * Every stall also carries the process CPU time and the heap movement of the stalled
34
+ * window, so it explains itself (senpi#2211): CPU near the drift means the host was busy
35
+ * (a heap drop in the same window points at a collection); CPU near zero means the
36
+ * process did not run - the machine starved it or it sat in a blocking wait.
37
+ *
29
38
  * The watchdog only reports. It never aborts a turn, kills a session, or refuses work.
30
39
  */
31
40
  export class LoopLagWatchdog {
32
41
  constructor(options) {
33
42
  this.activityMark = 0;
43
+ this.cpuMicrosAtTick = 0;
44
+ this.heapBytesAtTick = 0;
34
45
  const env = options.env ?? process.env;
35
46
  this.emit = options.emit;
36
47
  this.log = options.log ?? ((message) => void process.stderr.write(message));
37
48
  this.now = options.now ?? Date.now;
49
+ this.cpuUsage = options.cpuUsage ?? (() => process.cpuUsage());
50
+ this.heapUsed = options.heapUsed ?? (() => process.memoryUsage().heapUsed);
38
51
  this.warnMs = parseIdleExitMs(env[LOOP_LAG_WARN_MS_ENV]) ?? DEFAULT_LOOP_LAG_WARN_MS;
39
52
  this.errorMs = parseIdleExitMs(env[LOOP_LAG_ERROR_MS_ENV]) ?? DEFAULT_LOOP_LAG_ERROR_MS;
40
53
  }
@@ -43,6 +56,9 @@ export class LoopLagWatchdog {
43
56
  return;
44
57
  this.expectedTickAt = this.now() + LOOP_LAG_TICK_MS;
45
58
  this.activityMark = sessionActivityMark();
59
+ const cpu = this.cpuUsage();
60
+ this.cpuMicrosAtTick = cpu.user + cpu.system;
61
+ this.heapBytesAtTick = this.heapUsed();
46
62
  // Unref'd: watching the loop must never be the reason the host stays alive.
47
63
  this.timer = setInterval(() => this.tick(), LOOP_LAG_TICK_MS);
48
64
  this.timer.unref?.();
@@ -64,17 +80,33 @@ export class LoopLagWatchdog {
64
80
  const previousMark = this.activityMark;
65
81
  this.expectedTickAt = now + LOOP_LAG_TICK_MS;
66
82
  this.activityMark = sessionActivityMark();
83
+ const cpu = this.cpuUsage();
84
+ const cpuMicros = cpu.user + cpu.system;
85
+ const heapBytes = this.heapUsed();
86
+ const previousCpuMicros = this.cpuMicrosAtTick;
87
+ const previousHeapBytes = this.heapBytesAtTick;
88
+ this.cpuMicrosAtTick = cpuMicros;
89
+ this.heapBytesAtTick = heapBytes;
67
90
  const driftMs = Math.round(now - expectedAt);
68
91
  recordLoopBlockedMs(driftMs);
69
92
  if (driftMs <= this.warnMs)
70
93
  return;
71
94
  const attribution = sessionActivitySince(previousMark);
95
+ const processCpuMs = Math.round((cpuMicros - previousCpuMicros) / 1000);
96
+ const heapDeltaMb = Math.round((heapBytes - previousHeapBytes) / BYTES_PER_MB);
72
97
  if (driftMs > this.errorMs)
73
- this.emit({ type: "host_stalled", driftMs, sessionId: attribution?.sessionId, tool: attribution?.tool });
98
+ this.emit({
99
+ type: "host_stalled",
100
+ driftMs,
101
+ sessionId: attribution?.sessionId,
102
+ tool: attribution?.tool,
103
+ processCpuMs,
104
+ heapDeltaMb,
105
+ });
74
106
  if (this.lastWarnAt !== undefined && now - this.lastWarnAt < LOOP_LAG_WARN_INTERVAL_MS)
75
107
  return;
76
108
  this.lastWarnAt = now;
77
- this.log(`senpi rpc host stall: event loop blocked ${driftMs}ms (${describeAttribution(attribution)})\n`);
109
+ this.log(`senpi rpc host stall: event loop blocked ${driftMs}ms (${describeAttribution(attribution)}; ${describeWindow(processCpuMs, heapDeltaMb)})\n`);
78
110
  }
79
111
  }
80
112
  //# sourceMappingURL=loop-lag-watchdog.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"loop-lag-watchdog.js","sourceRoot":"","sources":["../../../src/modes/rpc/loop-lag-watchdog.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseIdleExitMs } from \"./host-lifecycle.ts\";\nimport { recordLoopBlockedMs } from \"./loop-blocked-time.ts\";\nimport type { RpcHostStalledEvent } from \"./rpc-types.ts\";\nimport { type SessionAttribution, sessionActivityMark, sessionActivitySince } from \"./session-attribution.ts\";\n\n/** Environment override for the drift that logs a warning, in milliseconds. */\nexport const LOOP_LAG_WARN_MS_ENV = \"SENPI_RPC_LOOP_LAG_WARN_MS\";\n/** Environment override for the drift that also emits `host_stalled`, in milliseconds. */\nexport const LOOP_LAG_ERROR_MS_ENV = \"SENPI_RPC_LOOP_LAG_ERROR_MS\";\nexport const DEFAULT_LOOP_LAG_WARN_MS = 500;\nexport const DEFAULT_LOOP_LAG_ERROR_MS = 5_000;\n/** Measurement interval: short enough to bound the blamed window, cheap enough to ignore. */\nexport const LOOP_LAG_TICK_MS = 200;\n/** One warning per window, however many stalls it covers. */\nexport const LOOP_LAG_WARN_INTERVAL_MS = 10_000;\n\nexport interface LoopLagWatchdogOptions {\n\t/** Delivers one `host_stalled` lifecycle record to every connection. */\n\treadonly emit: (record: RpcHostStalledEvent) => void;\n\t/** Defaults to one stderr line; tests capture it. */\n\treadonly log?: (message: string) => void;\n\treadonly now?: () => number;\n\treadonly env?: Readonly<Record<string, string | undefined>>;\n}\n\nfunction describeAttribution(attribution: SessionAttribution | undefined): string {\n\tif (attribution?.sessionId === undefined && attribution?.tool === undefined) return \"no attributed session\";\n\treturn `sessionId=${attribution.sessionId ?? \"unknown\"}${attribution.tool ? ` tool=${attribution.tool}` : \"\"}`;\n}\n\n/**\n * Event-loop stall detector for the shared host.\n *\n * Every in-process session runs on the host's one event loop, so a session that blocks\n * it freezes every other session and the transport with it. A 200 ms timer measures how\n * late it is actually invoked: that lateness IS the time the loop spent unable to serve\n * anyone. Past the warning threshold the host says so once per 10 s, naming the session\n * and tool whose work held the loop; past the error threshold it also emits a\n * `host_stalled` lifecycle record so attached clients (and the desktop) can show it.\n *\n * The watchdog only reports. It never aborts a turn, kills a session, or refuses work.\n */\nexport class LoopLagWatchdog {\n\tprivate readonly emit: (record: RpcHostStalledEvent) => void;\n\tprivate readonly log: (message: string) => void;\n\tprivate readonly now: () => number;\n\tprivate readonly warnMs: number;\n\tprivate readonly errorMs: number;\n\tprivate timer: ReturnType<typeof setInterval> | undefined;\n\tprivate expectedTickAt: number | undefined;\n\tprivate activityMark = 0;\n\tprivate lastWarnAt: number | undefined;\n\n\tconstructor(options: LoopLagWatchdogOptions) {\n\t\tconst env = options.env ?? process.env;\n\t\tthis.emit = options.emit;\n\t\tthis.log = options.log ?? ((message) => void process.stderr.write(message));\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.warnMs = parseIdleExitMs(env[LOOP_LAG_WARN_MS_ENV]) ?? DEFAULT_LOOP_LAG_WARN_MS;\n\t\tthis.errorMs = parseIdleExitMs(env[LOOP_LAG_ERROR_MS_ENV]) ?? DEFAULT_LOOP_LAG_ERROR_MS;\n\t}\n\n\tstart(): void {\n\t\tif (this.timer !== undefined) return;\n\t\tthis.expectedTickAt = this.now() + LOOP_LAG_TICK_MS;\n\t\tthis.activityMark = sessionActivityMark();\n\t\t// Unref'd: watching the loop must never be the reason the host stays alive.\n\t\tthis.timer = setInterval(() => this.tick(), LOOP_LAG_TICK_MS);\n\t\tthis.timer.unref?.();\n\t}\n\n\tstop(): void {\n\t\tif (this.timer === undefined) return;\n\t\tclearInterval(this.timer);\n\t\tthis.timer = undefined;\n\t\tthis.expectedTickAt = undefined;\n\t}\n\n\t/**\n\t * One measurement. Public so tests drive it on an injected clock instead of\n\t * waiting for real drift. The first tick only establishes the baseline.\n\t */\n\ttick(): void {\n\t\tconst now = this.now();\n\t\tconst expectedAt = this.expectedTickAt ?? now;\n\t\tconst previousMark = this.activityMark;\n\t\tthis.expectedTickAt = now + LOOP_LAG_TICK_MS;\n\t\tthis.activityMark = sessionActivityMark();\n\t\tconst driftMs = Math.round(now - expectedAt);\n\t\trecordLoopBlockedMs(driftMs);\n\t\tif (driftMs <= this.warnMs) return;\n\t\tconst attribution = sessionActivitySince(previousMark);\n\t\tif (driftMs > this.errorMs)\n\t\t\tthis.emit({ type: \"host_stalled\", driftMs, sessionId: attribution?.sessionId, tool: attribution?.tool });\n\t\tif (this.lastWarnAt !== undefined && now - this.lastWarnAt < LOOP_LAG_WARN_INTERVAL_MS) return;\n\t\tthis.lastWarnAt = now;\n\t\tthis.log(`senpi rpc host stall: event loop blocked ${driftMs}ms (${describeAttribution(attribution)})\\n`);\n\t}\n}\n"]}
1
+ {"version":3,"file":"loop-lag-watchdog.js","sourceRoot":"","sources":["../../../src/modes/rpc/loop-lag-watchdog.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseIdleExitMs } from \"./host-lifecycle.ts\";\nimport { recordLoopBlockedMs } from \"./loop-blocked-time.ts\";\nimport type { RpcHostStalledEvent } from \"./rpc-types.ts\";\nimport { type SessionAttribution, sessionActivityMark, sessionActivitySince } from \"./session-attribution.ts\";\n\n/** Environment override for the drift that logs a warning, in milliseconds. */\nexport const LOOP_LAG_WARN_MS_ENV = \"SENPI_RPC_LOOP_LAG_WARN_MS\";\n/** Environment override for the drift that also emits `host_stalled`, in milliseconds. */\nexport const LOOP_LAG_ERROR_MS_ENV = \"SENPI_RPC_LOOP_LAG_ERROR_MS\";\nexport const DEFAULT_LOOP_LAG_WARN_MS = 500;\nexport const DEFAULT_LOOP_LAG_ERROR_MS = 5_000;\n/** Measurement interval: short enough to bound the blamed window, cheap enough to ignore. */\nexport const LOOP_LAG_TICK_MS = 200;\n/** One warning per window, however many stalls it covers. */\nexport const LOOP_LAG_WARN_INTERVAL_MS = 10_000;\n\nexport interface LoopLagWatchdogOptions {\n\t/** Delivers one `host_stalled` lifecycle record to every connection. */\n\treadonly emit: (record: RpcHostStalledEvent) => void;\n\t/** Defaults to one stderr line; tests capture it. */\n\treadonly log?: (message: string) => void;\n\treadonly now?: () => number;\n\treadonly env?: Readonly<Record<string, string | undefined>>;\n\t/** Process CPU time in microseconds; defaults to `process.cpuUsage()`. */\n\treadonly cpuUsage?: () => { readonly user: number; readonly system: number };\n\t/** Live JS heap in bytes; defaults to `process.memoryUsage().heapUsed`. */\n\treadonly heapUsed?: () => number;\n}\n\nconst BYTES_PER_MB = 1024 * 1024;\n\nfunction describeWindow(processCpuMs: number, heapDeltaMb: number): string {\n\treturn `cpu=${processCpuMs}ms heap=${heapDeltaMb >= 0 ? \"+\" : \"\"}${heapDeltaMb}MB`;\n}\n\nfunction describeAttribution(attribution: SessionAttribution | undefined): string {\n\tif (attribution?.sessionId === undefined && attribution?.tool === undefined) return \"no attributed session\";\n\treturn `sessionId=${attribution.sessionId ?? \"unknown\"}${attribution.tool ? ` tool=${attribution.tool}` : \"\"}`;\n}\n\n/**\n * Event-loop stall detector for the shared host.\n *\n * Every in-process session runs on the host's one event loop, so a session that blocks\n * it freezes every other session and the transport with it. A 200 ms timer measures how\n * late it is actually invoked: that lateness IS the time the loop spent unable to serve\n * anyone. Past the warning threshold the host says so once per 10 s, naming the session\n * and tool whose work held the loop; past the error threshold it also emits a\n * `host_stalled` lifecycle record so attached clients (and the desktop) can show it.\n *\n * Every stall also carries the process CPU time and the heap movement of the stalled\n * window, so it explains itself (senpi#2211): CPU near the drift means the host was busy\n * (a heap drop in the same window points at a collection); CPU near zero means the\n * process did not run - the machine starved it or it sat in a blocking wait.\n *\n * The watchdog only reports. It never aborts a turn, kills a session, or refuses work.\n */\nexport class LoopLagWatchdog {\n\tprivate readonly emit: (record: RpcHostStalledEvent) => void;\n\tprivate readonly log: (message: string) => void;\n\tprivate readonly now: () => number;\n\tprivate readonly warnMs: number;\n\tprivate readonly errorMs: number;\n\tprivate timer: ReturnType<typeof setInterval> | undefined;\n\tprivate expectedTickAt: number | undefined;\n\tprivate activityMark = 0;\n\tprivate lastWarnAt: number | undefined;\n\tprivate readonly cpuUsage: () => { readonly user: number; readonly system: number };\n\tprivate readonly heapUsed: () => number;\n\tprivate cpuMicrosAtTick = 0;\n\tprivate heapBytesAtTick = 0;\n\n\tconstructor(options: LoopLagWatchdogOptions) {\n\t\tconst env = options.env ?? process.env;\n\t\tthis.emit = options.emit;\n\t\tthis.log = options.log ?? ((message) => void process.stderr.write(message));\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.cpuUsage = options.cpuUsage ?? (() => process.cpuUsage());\n\t\tthis.heapUsed = options.heapUsed ?? (() => process.memoryUsage().heapUsed);\n\t\tthis.warnMs = parseIdleExitMs(env[LOOP_LAG_WARN_MS_ENV]) ?? DEFAULT_LOOP_LAG_WARN_MS;\n\t\tthis.errorMs = parseIdleExitMs(env[LOOP_LAG_ERROR_MS_ENV]) ?? DEFAULT_LOOP_LAG_ERROR_MS;\n\t}\n\n\tstart(): void {\n\t\tif (this.timer !== undefined) return;\n\t\tthis.expectedTickAt = this.now() + LOOP_LAG_TICK_MS;\n\t\tthis.activityMark = sessionActivityMark();\n\t\tconst cpu = this.cpuUsage();\n\t\tthis.cpuMicrosAtTick = cpu.user + cpu.system;\n\t\tthis.heapBytesAtTick = this.heapUsed();\n\t\t// Unref'd: watching the loop must never be the reason the host stays alive.\n\t\tthis.timer = setInterval(() => this.tick(), LOOP_LAG_TICK_MS);\n\t\tthis.timer.unref?.();\n\t}\n\n\tstop(): void {\n\t\tif (this.timer === undefined) return;\n\t\tclearInterval(this.timer);\n\t\tthis.timer = undefined;\n\t\tthis.expectedTickAt = undefined;\n\t}\n\n\t/**\n\t * One measurement. Public so tests drive it on an injected clock instead of\n\t * waiting for real drift. The first tick only establishes the baseline.\n\t */\n\ttick(): void {\n\t\tconst now = this.now();\n\t\tconst expectedAt = this.expectedTickAt ?? now;\n\t\tconst previousMark = this.activityMark;\n\t\tthis.expectedTickAt = now + LOOP_LAG_TICK_MS;\n\t\tthis.activityMark = sessionActivityMark();\n\t\tconst cpu = this.cpuUsage();\n\t\tconst cpuMicros = cpu.user + cpu.system;\n\t\tconst heapBytes = this.heapUsed();\n\t\tconst previousCpuMicros = this.cpuMicrosAtTick;\n\t\tconst previousHeapBytes = this.heapBytesAtTick;\n\t\tthis.cpuMicrosAtTick = cpuMicros;\n\t\tthis.heapBytesAtTick = heapBytes;\n\t\tconst driftMs = Math.round(now - expectedAt);\n\t\trecordLoopBlockedMs(driftMs);\n\t\tif (driftMs <= this.warnMs) return;\n\t\tconst attribution = sessionActivitySince(previousMark);\n\t\tconst processCpuMs = Math.round((cpuMicros - previousCpuMicros) / 1000);\n\t\tconst heapDeltaMb = Math.round((heapBytes - previousHeapBytes) / BYTES_PER_MB);\n\t\tif (driftMs > this.errorMs)\n\t\t\tthis.emit({\n\t\t\t\ttype: \"host_stalled\",\n\t\t\t\tdriftMs,\n\t\t\t\tsessionId: attribution?.sessionId,\n\t\t\t\ttool: attribution?.tool,\n\t\t\t\tprocessCpuMs,\n\t\t\t\theapDeltaMb,\n\t\t\t});\n\t\tif (this.lastWarnAt !== undefined && now - this.lastWarnAt < LOOP_LAG_WARN_INTERVAL_MS) return;\n\t\tthis.lastWarnAt = now;\n\t\tthis.log(\n\t\t\t`senpi rpc host stall: event loop blocked ${driftMs}ms (${describeAttribution(attribution)}; ${describeWindow(processCpuMs, heapDeltaMb)})\\n`,\n\t\t);\n\t}\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import type { CreateAgentSessionRuntimeFactory } from "../../core/agent-session-runtime.ts";
2
+ import type { SessionContext } from "../../core/extensions/types.ts";
2
3
  import type { CliRuntimeConfiguration } from "../../main.ts";
3
4
  import { type HostIdleOverrides } from "./host-idle-policy.ts";
4
5
  import { type RpcBindingFactory, SessionCommandRouter } from "./session-command-router.ts";
@@ -26,7 +27,7 @@ export { DEFAULT_HOST_EMPTY_EXIT_MS, DEFAULT_SESSION_IDLE_EVICTION_MS, RPC_HOST_
26
27
  * requests remain requester-only; foreign observation uses attach-on-open.
27
28
  */
28
29
  export declare function runMultiSessionHost(options: MultiSessionHostOptions): Promise<never>;
29
- export declare function createHostCore(options: MultiSessionHostOptions, writer: SessionEventWriter, capabilities?: string[], idle?: HostIdleOverrides): {
30
+ export declare function createHostCore(options: MultiSessionHostOptions, writer: SessionEventWriter, capabilities?: string[], idle?: HostIdleOverrides, hostContext?: SessionContext): {
30
31
  router: SessionCommandRouter;
31
32
  handle: (line: string) => Promise<void>;
32
33
  };
@@ -1 +1 @@
1
- {"version":3,"file":"multi-session-host.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/multi-session-host.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,qCAAqC,CAAC;AAS5F,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAM7D,OAAO,EAAE,KAAK,iBAAiB,EAAiD,MAAM,uBAAuB,CAAC;AAW9G,OAAO,EAAE,KAAK,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAC3F,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAsB/D,MAAM,WAAW,uBAAuB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,gCAAgC,CAAC;IAChD,mBAAmB,CAAC,EAAE,uBAAuB,CAAC;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,iBAAiB,CAAC;CAClC;AAED,OAAO,EACN,0BAA0B,EAC1B,gCAAgC,EAChC,0BAA0B,EAC1B,gCAAgC,EAChC,qBAAqB,GACrB,MAAM,uBAAuB,CAAC;AAuC/B;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,KAAK,CAAC,CAM1F;AAED,wBAAgB,cAAc,CAC7B,OAAO,EAAE,uBAAuB,EAChC,MAAM,EAAE,kBAAkB,EAC1B,YAAY,WAA+D,EAC3E,IAAI,GAAE,iBAAsB;;mBAsCA,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;EAiBlD"}
1
+ {"version":3,"file":"multi-session-host.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/multi-session-host.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,qCAAqC,CAAC;AAG5F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAOrE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAO7D,OAAO,EAAE,KAAK,iBAAiB,EAAiD,MAAM,uBAAuB,CAAC;AAW9G,OAAO,EAAE,KAAK,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAC3F,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAuB/D,MAAM,WAAW,uBAAuB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,gCAAgC,CAAC;IAChD,mBAAmB,CAAC,EAAE,uBAAuB,CAAC;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,iBAAiB,CAAC;CAClC;AAED,OAAO,EACN,0BAA0B,EAC1B,gCAAgC,EAChC,0BAA0B,EAC1B,gCAAgC,EAChC,qBAAqB,GACrB,MAAM,uBAAuB,CAAC;AAuC/B;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,KAAK,CAAC,CAM1F;AAED,wBAAgB,cAAc,CAC7B,OAAO,EAAE,uBAAuB,EAChC,MAAM,EAAE,kBAAkB,EAC1B,YAAY,WAA+D,EAC3E,IAAI,GAAE,iBAAsB,EAC5B,WAAW,CAAC,EAAE,cAAc;;mBAsCA,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;EAiBlD"}
@@ -1,12 +1,13 @@
1
1
  import { access, chmod, mkdir, unlink } from "node:fs/promises";
2
2
  import { createConnection, createServer } from "node:net";
3
- import { dirname, join } from "node:path";
3
+ import { basename, dirname, join } from "node:path";
4
4
  import { envValue } from "../../core/brand.js";
5
5
  import { HostMcpRegistry } from "../../core/extensions/builtin/mcp/host-registry.js";
6
6
  import { flushRawStdout, takeOverStdout, waitForRawStdoutBackpressure, writeRawStdout, } from "../../core/output-guard.js";
7
7
  import { killTrackedDetachedChildren } from "../../utils/shell.js";
8
8
  import { startHostChildReaper } from "./child-reaper.js";
9
9
  import { parseClientCapabilities } from "./custom-capability.js";
10
+ import { ClientOccupancy } from "./host-client-occupancy.js";
10
11
  import { GENERATION_HANDOFF_CAPABILITY } from "./host-decision.js";
11
12
  import { RPC_CLOSE_GRACE_MS_ENV, resolveHostIdlePolicy } from "./host-idle-policy.js";
12
13
  import { parseIdleExitMs } from "./host-lifecycle.js";
@@ -20,6 +21,7 @@ import { hostGeneration, hostInstanceId } from "./protocol-identity.js";
20
21
  import { rpcCommandShapeError } from "./rpc-input-validation.js";
21
22
  import { SessionCommandRouter } from "./session-command-router.js";
22
23
  import { SessionEventWriter } from "./session-event-writer.js";
24
+ import { canonicalSessionPath } from "./session-path-key.js";
23
25
  import { RpcSessionRegistry } from "./session-registry.js";
24
26
  import { PUBLIC_SOCKET_IDENTITY_FILE, readSocketIdentityFile, shieldSocketDuringClose, socketEntryReplaced, statSocketIdentity, unlinkOwnedSocket, waitForSocketIdentityFile, } from "./socket-ownership.js";
25
27
  import { socketSink } from "./socket-sink.js";
@@ -61,7 +63,7 @@ export async function runMultiSessionHost(options) {
61
63
  ? runStdioHost(options)
62
64
  : runSocketHost(options, resolveSocketPath(options.listen, options.agentDir)));
63
65
  }
64
- export function createHostCore(options, writer, capabilities = parseClientCapabilities(envValue("RPC_CLIENT_CAPABILITIES")), idle = {}) {
66
+ export function createHostCore(options, writer, capabilities = parseClientCapabilities(envValue("RPC_CLIENT_CAPABILITIES")), idle = {}, hostContext) {
65
67
  const policy = resolveHostIdlePolicy(process.env, idle);
66
68
  const router = new SessionCommandRouter(options.workerConfiguration
67
69
  ? new WorkerSessionRegistry({
@@ -83,7 +85,7 @@ export function createHostCore(options, writer, capabilities = parseClientCapabi
83
85
  instanceId: hostInstanceId(),
84
86
  onFailure: hostLog,
85
87
  }),
86
- }), writer, options, options.createBinding, { capabilities }, {
88
+ }), writer, hostContext ? { ...options, hostContext } : options, options.createBinding, { capabilities }, {
87
89
  now: policy.now,
88
90
  idleEvictionMs: policy.idleEvictionMs,
89
91
  emptyExitMs: policy.emptyExitMs,
@@ -154,8 +156,12 @@ async function runSocketHost(options, socketPath) {
154
156
  await prepareSocketPath(socketPath);
155
157
  const writer = new SessionEventWriter(() => { });
156
158
  const connections = new Map();
159
+ // The supervisor's rule for which clients hold a host open (host-client-occupancy.ts): a
160
+ // connection that has only sent observing reads (`status`) is not occupancy.
161
+ const occupancy = new ClientOccupancy(() => { });
157
162
  let draining = false;
158
163
  let handoffAnnounced = false;
164
+ const hostContext = hostSessionContext(socketPath);
159
165
  const { router, handle } = createHostCore(options, writer, [
160
166
  ...parseClientCapabilities(envValue("RPC_CLIENT_CAPABILITIES")).filter((capability) => capability !== "rendered_components"),
161
167
  // A socket host installs the SIGUSR1 drain below, so it can be handed off to a newer
@@ -166,19 +172,20 @@ async function runSocketHost(options, socketPath) {
166
172
  // Supervised hosts idle-exit via the supervisor, but a socket host that
167
173
  // outlives its supervisor (or is started bare) still self-exits when empty.
168
174
  // A connected client counts as occupancy even with no session open: exiting
169
- // under it would drop its socket and read as a crash to the supervisor.
175
+ // under it would drop its socket and read as a crash to the supervisor. Only an
176
+ // OBSERVER does not - one whose every request so far was an `observe: true` read.
170
177
  // While DRAINING the opposite is true: the successor generation owns the socket, so a
171
178
  // sessionless connection must not hold this host open.
172
179
  {
173
180
  onEmptyExit: () => void shutdown(0),
174
- canExitWhenEmpty: () => draining || connections.size === 0,
181
+ canExitWhenEmpty: () => draining || (occupancy.attachedCount === 0 && occupancy.unclassifiedCount === 0),
175
182
  onHandoffParked: async (ids) => {
176
183
  await Promise.all(ids.map(async (id) => {
177
184
  await writer.flushConnection(id);
178
185
  connections.get(id)?.close();
179
186
  }));
180
187
  },
181
- });
188
+ }, hostContext);
182
189
  const observers = startHostObservers(router, writer, {
183
190
  // The shape #1893 measured: gigabytes resident with `sessions.total 0`. Say it once, and when
184
191
  // this generation no longer owns the endpoint, leave - nobody can reach it to ask.
@@ -222,6 +229,7 @@ async function runSocketHost(options, socketPath) {
222
229
  const id = `socket-${++nextConnection}`;
223
230
  const sink = socketSink(socket);
224
231
  writer.registerConnection(id, sink);
232
+ occupancy.admit(socket);
225
233
  const detachReader = attachJsonlLineReader(socket, (line) => {
226
234
  // Do not serialize awaited commands: extension_ui_response and other
227
235
  // re-entrant frames must be able to resolve a command already awaiting them.
@@ -246,6 +254,7 @@ async function runSocketHost(options, socketPath) {
246
254
  detachReader();
247
255
  writer.unregisterConnection(id);
248
256
  connections.delete(id);
257
+ occupancy.release(socket);
249
258
  // A socket that dies without close_session still owns its sessions' attachments
250
259
  // and path reservations. Release them on the command chain so this runs after any
251
260
  // in-flight command for this connection settles, otherwise the path stays pinned
@@ -404,6 +413,24 @@ function oversizedLineError() {
404
413
  function errorMessage(cause) {
405
414
  return cause instanceof Error ? cause.message : String(cause);
406
415
  }
416
+ /**
417
+ * The identity every session on this socket host carries in its context: `host_socket`, the PUBLIC
418
+ * endpoint clients address (the supervisor's path for a supervised host, whose own listener is a
419
+ * private hop; the bound path for a bare one), realpath-canonicalized so it compares equal however
420
+ * the path was spelled; and `host_instance`, this generation. The endpoint is stable across handoffs,
421
+ * the instance is not. The FIRST generation runs this before its supervisor has created the socket's
422
+ * directory, a successor after, so the directory is canonicalized through its deepest existing
423
+ * ancestor: both then stamp the same spelling. `host_socket` is omitted where no public path exists
424
+ * (abstract sockets, a supervised win32 host, whose supervisor publishes none).
425
+ */
426
+ function hostSessionContext(socketPath) {
427
+ const supervised = readHostWatchdogConfigFromBrandEnv();
428
+ const endpoint = supervised === undefined ? socketPath : supervised.publicSocket;
429
+ if (endpoint === undefined || endpoint.startsWith("\0"))
430
+ return { host_instance: hostInstanceId() };
431
+ const directory = canonicalSessionPath(dirname(endpoint));
432
+ return { host_socket: join(directory, basename(endpoint)), host_instance: hostInstanceId() };
433
+ }
407
434
  /** The endpoint this host listens on, or nothing when it speaks stdio and shares no socket. */
408
435
  function listenSocketPath(options) {
409
436
  if (options.listen === undefined || options.listen === "stdio://")
@@ -1 +1 @@
1
- {"version":3,"file":"multi-session-host.js","sourceRoot":"","sources":["../../../src/modes/rpc/multi-session-host.ts"],"names":[],"mappings":"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","sourcesContent":["import { access, chmod, mkdir, unlink } from \"node:fs/promises\";\nimport { createConnection, createServer, type Server } from \"node:net\";\nimport { dirname, join } from \"node:path\";\nimport type { CreateAgentSessionRuntimeFactory } from \"../../core/agent-session-runtime.ts\";\nimport { envValue } from \"../../core/brand.ts\";\nimport { HostMcpRegistry } from \"../../core/extensions/builtin/mcp/host-registry.ts\";\nimport {\n\tflushRawStdout,\n\ttakeOverStdout,\n\twaitForRawStdoutBackpressure,\n\twriteRawStdout,\n} from \"../../core/output-guard.ts\";\nimport type { CliRuntimeConfiguration } from \"../../main.ts\";\nimport { killTrackedDetachedChildren } from \"../../utils/shell.ts\";\nimport { startHostChildReaper } from \"./child-reaper.ts\";\nimport type { RpcConnectionSink } from \"./connection-handler.ts\";\nimport { parseClientCapabilities } from \"./custom-capability.ts\";\nimport { GENERATION_HANDOFF_CAPABILITY } from \"./host-decision.ts\";\nimport { type HostIdleOverrides, RPC_CLOSE_GRACE_MS_ENV, resolveHostIdlePolicy } from \"./host-idle-policy.ts\";\nimport { parseIdleExitMs } from \"./host-lifecycle.ts\";\nimport { HostMemorySampler } from \"./host-memory-sampler.ts\";\nimport { runAsHostGenerationProcess } from \"./host-process-role.ts\";\nimport { createEndpointReservations } from \"./host-reservations.ts\";\nimport { armHostWatchdog, readHostWatchdogConfigFromBrandEnv } from \"./host-watchdog.ts\";\nimport { attachJsonlLineReader, MAX_RPC_LINE_CHARACTERS } from \"./jsonl.ts\";\nimport { LoopLagWatchdog } from \"./loop-lag-watchdog.ts\";\nimport { hostGeneration, hostInstanceId } from \"./protocol-identity.ts\";\nimport { rpcCommandShapeError } from \"./rpc-input-validation.ts\";\nimport type { RpcCommand, RpcResponse } from \"./rpc-types.ts\";\nimport { type RpcBindingFactory, SessionCommandRouter } from \"./session-command-router.ts\";\nimport { SessionEventWriter } from \"./session-event-writer.ts\";\nimport { RpcSessionRegistry } from \"./session-registry.ts\";\nimport {\n\tPUBLIC_SOCKET_IDENTITY_FILE,\n\treadSocketIdentityFile,\n\ttype SocketFileIdentity,\n\tshieldSocketDuringClose,\n\tsocketEntryReplaced,\n\tstatSocketIdentity,\n\tunlinkOwnedSocket,\n\twaitForSocketIdentityFile,\n} from \"./socket-ownership.ts\";\nimport { socketSink } from \"./socket-sink.ts\";\nimport {\n\tauthenticateSocket,\n\tensureSocketSecret,\n\tresolveSocketTransportAddress,\n\tSOCKET_SECRET_FILE_ENV,\n\tsocketSecretPath,\n} from \"./socket-transport.ts\";\nimport { WorkerSessionRegistry } from \"./worker-session-registry.ts\";\n\nexport interface MultiSessionHostOptions {\n\tagentDir: string;\n\tcreateRuntime: CreateAgentSessionRuntimeFactory;\n\tworkerConfiguration?: CliRuntimeConfiguration;\n\tcwd: string;\n\tpermissionPreset?: string;\n\tcreationModel?: { provider: string; modelId: string };\n\tinitialThinkingLevel?: string;\n\tlisten?: string;\n\t/** Test seam: defaults to the real shared-session binding. */\n\tcreateBinding?: RpcBindingFactory;\n}\n\nexport {\n\tDEFAULT_HOST_EMPTY_EXIT_MS,\n\tDEFAULT_SESSION_IDLE_EVICTION_MS,\n\tRPC_HOST_EMPTY_EXIT_MS_ENV,\n\tRPC_SESSION_IDLE_EVICTION_MS_ENV,\n\tresolveHostIdlePolicy,\n} from \"./host-idle-policy.ts\";\n\n/** Win32 named-pipe close can leave libuv's server callback pending after handles are destroyed. */\nconst WINDOWS_SHUTDOWN_HARD_EXIT_MS = 2_000;\n\n/**\n * Arm the host's self-observation: event-loop stall detection with per-session\n * attribution, and RSS reporting that tightens idle parking under pressure. Both run on\n * unref'd timers, and neither refuses, aborts or kills anything (#2207).\n */\nfunction startHostObservers(\n\trouter: SessionCommandRouter,\n\twriter: SessionEventWriter,\n\toptions: { onIdlePressure?: (rssMb: number) => void } = {},\n): { stop: () => void } {\n\tconst loopLag = new LoopLagWatchdog({ emit: (record) => writer.broadcastHostRecord(record) });\n\tconst memory = new HostMemorySampler({\n\t\temit: (record) => writer.broadcastHostRecord(record),\n\t\tsessions: () => router.sessionCount,\n\t\tonPressure: (pressure) => router.setMemoryPressure(pressure),\n\t\t...(options.onIdlePressure ? { onIdlePressure: options.onIdlePressure } : {}),\n\t});\n\tloopLag.start();\n\tmemory.start();\n\treturn {\n\t\tstop: () => {\n\t\t\tloopLag.stop();\n\t\t\tmemory.stop();\n\t\t},\n\t};\n}\n\ninterface Connection {\n\treadonly id: string;\n\treadonly sink: RpcConnectionSink;\n\treadonly detach: () => void;\n\treadonly close: () => void;\n}\n\n/**\n * Socket agent events are delivered only to connections attached to their\n * session, tagged with its routing sessionId. Content-free session lifecycle\n * events remain visible to every connection. Responses and extension UI\n * requests remain requester-only; foreign observation uses attach-on-open.\n */\nexport async function runMultiSessionHost(options: MultiSessionHostOptions): Promise<never> {\n\treturn runAsHostGenerationProcess(() =>\n\t\toptions.listen === undefined || options.listen === \"stdio://\"\n\t\t\t? runStdioHost(options)\n\t\t\t: runSocketHost(options, resolveSocketPath(options.listen, options.agentDir)),\n\t);\n}\n\nexport function createHostCore(\n\toptions: MultiSessionHostOptions,\n\twriter: SessionEventWriter,\n\tcapabilities = parseClientCapabilities(envValue(\"RPC_CLIENT_CAPABILITIES\")),\n\tidle: HostIdleOverrides = {},\n) {\n\tconst policy = resolveHostIdlePolicy(process.env, idle);\n\tconst router = new SessionCommandRouter(\n\t\toptions.workerConfiguration\n\t\t\t? new WorkerSessionRegistry({\n\t\t\t\t\tconfiguration: options.workerConfiguration,\n\t\t\t\t\tnow: policy.now,\n\t\t\t\t\tcloseGraceMs: idle.closeGraceMs ?? parseIdleExitMs(process.env[RPC_CLOSE_GRACE_MS_ENV]) ?? 10_000,\n\t\t\t\t})\n\t\t\t: new RpcSessionRegistry({\n\t\t\t\t\tagentDir: options.agentDir,\n\t\t\t\t\tcreateRuntime: options.createRuntime,\n\t\t\t\t\tmcpRegistry: new HostMcpRegistry(),\n\t\t\t\t\tnow: policy.now,\n\t\t\t\t\tcloseGraceMs: idle.closeGraceMs ?? parseIdleExitMs(process.env[RPC_CLOSE_GRACE_MS_ENV]) ?? 10_000,\n\t\t\t\t\t// Two generations of this daemon can be alive at once during a handoff; the claims\n\t\t\t\t\t// they publish here are what keeps them off one session file.\n\t\t\t\t\tpathReservations: createEndpointReservations({\n\t\t\t\t\t\tagentDir: options.agentDir,\n\t\t\t\t\t\tsocket: listenSocketPath(options),\n\t\t\t\t\t\tinstanceId: hostInstanceId(),\n\t\t\t\t\t\tonFailure: hostLog,\n\t\t\t\t\t}),\n\t\t\t\t}),\n\t\twriter,\n\t\toptions,\n\t\toptions.createBinding,\n\t\t{ capabilities },\n\t\t{\n\t\t\tnow: policy.now,\n\t\t\tidleEvictionMs: policy.idleEvictionMs,\n\t\t\temptyExitMs: policy.emptyExitMs,\n\t\t\tonEmptyExit: idle.onEmptyExit,\n\t\t\tonHandoffParked: idle.onHandoffParked,\n\t\t\tcanExitWhenEmpty: idle.canExitWhenEmpty,\n\t\t},\n\t);\n\tconst handle = async (line: string): Promise<void> => {\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(line);\n\t\t} catch (cause) {\n\t\t\tawait writer.enqueueControl(parseError(`Failed to parse command: ${errorMessage(cause)}`));\n\t\t\treturn;\n\t\t}\n\t\tconst shapeError = rpcCommandShapeError(parsed);\n\t\tif (shapeError) {\n\t\t\tawait writer.enqueueControl(parseError(shapeError));\n\t\t\treturn;\n\t\t}\n\t\tconst response = await router.handle(parsed as RpcCommand);\n\t\tif (response) await writer.enqueueControl(response);\n\t};\n\treturn { router, handle };\n}\n\n/** Plain-stdio host with no eagerly-created AgentSessionRuntime. */\nasync function runStdioHost(options: MultiSessionHostOptions): Promise<never> {\n\ttakeOverStdout();\n\tconst sink: RpcConnectionSink = { writeRaw: writeRawStdout, waitForBackpressure: waitForRawStdoutBackpressure };\n\tconst writer = new SessionEventWriter(sink.writeRaw, sink.waitForBackpressure);\n\t// An empty host (no session ever opened, or all closed) must not stay resident\n\t// forever: exit through the normal shutdown path once the window elapses.\n\tconst { router, handle } = createHostCore(options, writer, undefined, {\n\t\tonEmptyExit: () => void shutdown(0),\n\t});\n\tconst observers = startHostObservers(router, writer);\n\tlet shuttingDown = false;\n\tconst shutdown = async (exitCode = 0): Promise<never> => {\n\t\tif (shuttingDown) process.exit(exitCode);\n\t\tshuttingDown = true;\n\t\tobservers.stop();\n\t\tdetach();\n\t\tawait router.dispose();\n\t\tawait writer.flush();\n\t\tawait flushRawStdout();\n\t\tprocess.exit(exitCode);\n\t};\n\tconst onEnd = () => void shutdown();\n\tprocess.stdin.on(\"end\", onEnd);\n\tconst reportInputFailure = (cause: unknown): void => {\n\t\tprocess.stderr.write(`senpi rpc stdio request failed: ${errorMessage(cause)}\\n`);\n\t};\n\tconst detachReader = attachJsonlLineReader(process.stdin, (line) => void handle(line).catch(reportInputFailure), {\n\t\tmaxLineLength: MAX_RPC_LINE_CHARACTERS,\n\t\tonOversizedLine: () => void writer.enqueueControl(parseError(oversizedLineError())).catch(reportInputFailure),\n\t});\n\tconst detach = () => {\n\t\tdetachReader();\n\t\tprocess.stdin.off(\"end\", onEnd);\n\t};\n\tregisterShutdownSignals(shutdown);\n\treturn new Promise(() => {});\n}\n\nasync function runSocketHost(options: MultiSessionHostOptions, socketPath: string): Promise<never> {\n\tawait prepareSocketPath(socketPath);\n\tconst writer = new SessionEventWriter(() => {});\n\tconst connections = new Map<string, Connection>();\n\tlet draining = false;\n\tlet handoffAnnounced = false;\n\tconst { router, handle } = createHostCore(\n\t\toptions,\n\t\twriter,\n\t\t[\n\t\t\t...parseClientCapabilities(envValue(\"RPC_CLIENT_CAPABILITIES\")).filter(\n\t\t\t\t(capability) => capability !== \"rendered_components\",\n\t\t\t),\n\t\t\t// A socket host installs the SIGUSR1 drain below, so it can be handed off to a newer\n\t\t\t// generation instead of being killed. A host that does not advertise this is never\n\t\t\t// signalled - SIGUSR1 would simply terminate it, sessions and all.\n\t\t\tGENERATION_HANDOFF_CAPABILITY,\n\t\t],\n\t\t// Supervised hosts idle-exit via the supervisor, but a socket host that\n\t\t// outlives its supervisor (or is started bare) still self-exits when empty.\n\t\t// A connected client counts as occupancy even with no session open: exiting\n\t\t// under it would drop its socket and read as a crash to the supervisor.\n\t\t// While DRAINING the opposite is true: the successor generation owns the socket, so a\n\t\t// sessionless connection must not hold this host open.\n\t\t{\n\t\t\tonEmptyExit: () => void shutdown(0),\n\t\t\tcanExitWhenEmpty: () => draining || connections.size === 0,\n\t\t\tonHandoffParked: async (ids) => {\n\t\t\t\tawait Promise.all(\n\t\t\t\t\tids.map(async (id) => {\n\t\t\t\t\t\tawait writer.flushConnection(id);\n\t\t\t\t\t\tconnections.get(id)?.close();\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t},\n\t\t},\n\t);\n\tconst observers = startHostObservers(router, writer, {\n\t\t// The shape #1893 measured: gigabytes resident with `sessions.total 0`. Say it once, and when\n\t\t// this generation no longer owns the endpoint, leave - nobody can reach it to ask.\n\t\tonIdlePressure: (rssMb) => {\n\t\t\thostLog(`memory pressure with no sessions: rssMb=${rssMb}`);\n\t\t\tvoid endpointSuperseded().then((superseded) => {\n\t\t\t\tif (superseded) drainForHandoff();\n\t\t\t}, noop);\n\t\t},\n\t});\n\tlet nextConnection = 0;\n\tlet shuttingDown = false;\n\tconst secret =\n\t\tprocess.platform === \"win32\"\n\t\t\t? await ensureSocketSecret(process.env[SOCKET_SECRET_FILE_ENV] ?? socketSecretPath(socketPath))\n\t\t\t: undefined;\n\tconst watchdogConfig = readHostWatchdogConfigFromBrandEnv();\n\t// This host's OWN copy of the crash-path cleanup list. A drain empties it: once a successor\n\t// generation is registered, the daemon state files under those paths describe the successor,\n\t// and a crash of this (already replaced) host must not take them with it.\n\tconst crashCleanupPaths = [...(watchdogConfig?.cleanupPaths ?? [])];\n\t// Crash-path ownership for the supervisor's PUBLIC socket: the supervisor\n\t// records the identity of the entry it bound (inside its private scratch\n\t// directory, which no replacement supervisor writes) right after its listen,\n\t// and this host removes that path only while the identity still matches. A\n\t// blind path removal here would unlink a newer host's freshly published\n\t// entry after a takeover - the startup path already refuses to touch a\n\t// socket owned by a live server; teardown follows the same rule.\n\tconst supervisorPublicSocketPath =\n\t\tprocess.platform === \"win32\" || socketPath.startsWith(\"\\0\") ? undefined : watchdogConfig?.publicSocket;\n\tconst supervisorPublicOwnerFile =\n\t\tsupervisorPublicSocketPath && watchdogConfig?.scratchDir\n\t\t\t? join(watchdogConfig.scratchDir, PUBLIC_SOCKET_IDENTITY_FILE)\n\t\t\t: undefined;\n\tlet boundIdentity: SocketFileIdentity | undefined;\n\tlet supervisorPublicIdentity: SocketFileIdentity | undefined;\n\tconst server = createServer((socket) => {\n\t\tconst accept = (): void => {\n\t\t\tif (draining || shuttingDown) {\n\t\t\t\tsocket.destroy();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst id = `socket-${++nextConnection}`;\n\t\t\tconst sink = socketSink(socket);\n\t\t\twriter.registerConnection(id, sink);\n\t\t\tconst detachReader = attachJsonlLineReader(\n\t\t\t\tsocket,\n\t\t\t\t(line) => {\n\t\t\t\t\t// Do not serialize awaited commands: extension_ui_response and other\n\t\t\t\t\t// re-entrant frames must be able to resolve a command already awaiting them.\n\t\t\t\t\tvoid writer\n\t\t\t\t\t\t.withConnection(id, () => handle(line))\n\t\t\t\t\t\t.catch((cause) => {\n\t\t\t\t\t\t\tprocess.stderr.write(`senpi rpc connection ${id} failed: ${errorMessage(cause)}\\n`);\n\t\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tmaxLineLength: MAX_RPC_LINE_CHARACTERS,\n\t\t\t\t\tonOversizedLine: () => {\n\t\t\t\t\t\tvoid writer\n\t\t\t\t\t\t\t.withConnection(id, () => writer.enqueueControl(parseError(oversizedLineError())))\n\t\t\t\t\t\t\t.catch((cause) =>\n\t\t\t\t\t\t\t\tprocess.stderr.write(`senpi rpc connection ${id} failed: ${errorMessage(cause)}\\n`),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t\tlet detached = false;\n\t\t\tconst detach = () => {\n\t\t\t\tif (detached) return;\n\t\t\t\tdetached = true;\n\t\t\t\tdetachReader();\n\t\t\t\twriter.unregisterConnection(id);\n\t\t\t\tconnections.delete(id);\n\t\t\t\t// A socket that dies without close_session still owns its sessions' attachments\n\t\t\t\t// and path reservations. Release them on the command chain so this runs after any\n\t\t\t\t// in-flight command for this connection settles, otherwise the path stays pinned\n\t\t\t\t// by a runtime whose client is gone and later resumes attach to that orphan.\n\t\t\t\tvoid router.releaseConnection(id).catch((cause) => {\n\t\t\t\t\tprocess.stderr.write(`senpi rpc connection ${id} release failed: ${errorMessage(cause)}\\n`);\n\t\t\t\t});\n\t\t\t};\n\t\t\tconnections.set(id, { id, sink, detach, close: () => socket.destroy() });\n\t\t\tsocket.once(\"close\", detach);\n\t\t\tsocket.once(\"error\", () => detach());\n\t\t};\n\t\tif (secret) authenticateSocket(socket, secret, accept);\n\t\telse accept();\n\t});\n\tserver.on(\"error\", (cause) => {\n\t\tif (!shuttingDown) process.stderr.write(`senpi rpc socket listener failed: ${errorMessage(cause)}\\n`);\n\t});\n\t// Long-lived hosts outlive many session workers, and a terminated worker thread\n\t// takes its children's exit watchers with it (measured: every spawn API leaks\n\t// that way). The reaper claims those abandoned children; it never touches one a\n\t// live thread could still be waiting for.\n\tconst stopChildReaper = await startHostChildReaper(hostLog);\n\tconst shutdown = async (exitCode = 0, watchdogCleanup?: Promise<void>): Promise<never> => {\n\t\tif (shuttingDown) process.exit(exitCode);\n\t\tshuttingDown = true;\n\t\tobservers.stop();\n\t\tstopChildReaper();\n\t\t// On Windows, destroying named-pipe sockets does not always make libuv's\n\t\t// server.close callback fire: connected pipe instances can remain in the\n\t\t// kernel after the JavaScript handles are destroyed. Keep the normal drain\n\t\t// path, but never let that platform-specific close stall orphan the host.\n\t\ttry {\n\t\t\t// Dispose while connections are still registered so `session_closed`\n\t\t\t// `{ reason: \"host_shutdown\" }` reaches clients before the sockets die.\n\t\t\tawait router.dispose();\n\t\t\tawait writer.flush();\n\t\t\tfor (const connection of connections.values()) {\n\t\t\t\tconnection.detach();\n\t\t\t\tconnection.close();\n\t\t\t}\n\t\t\t// libuv unlinks the bound NAME when the listening handle closes - which\n\t\t\t// would delete a newer host's entry renamed over this path. Shield the\n\t\t\t// current entry for the close, then let the ownership check decide.\n\t\t\tawait shieldSocketDuringClose(socketPath, () =>\n\t\t\t\tprocess.platform === \"win32\"\n\t\t\t\t\t? Promise.race([closeServer(server), delay(WINDOWS_SHUTDOWN_HARD_EXIT_MS)])\n\t\t\t\t\t: closeServer(server),\n\t\t\t);\n\t\t\t// Ownership-checked: unlink only the entry THIS process bound. After a\n\t\t\t// takeover renamed a newer host's socket over the same path, the\n\t\t\t// identity no longer matches and the replacement stays published.\n\t\t\tawait unlinkOwnedSocket(socketPath, boundIdentity, hostLog);\n\t\t\tif (supervisorPublicSocketPath) {\n\t\t\t\tawait unlinkOwnedSocket(supervisorPublicSocketPath, supervisorPublicIdentity, hostLog);\n\t\t\t}\n\t\t\tif (watchdogCleanup) await watchdogCleanup;\n\t\t} finally {\n\t\t\t// Explicitly terminate after every shutdown trigger. Windows named-pipe\n\t\t\t// handles are not fully controllable from JS, and an unresolved cleanup\n\t\t\t// must not leave this daemon or its public endpoint alive.\n\t\t\tprocess.exit(exitCode);\n\t\t}\n\t};\n\t/**\n\t * Announce through the record writer, never inside the supervisor's raw byte proxy. This\n\t * preserves JSONL framing and FIFO before parking, including when a record spans proxy reads.\n\t * Active turns/requests keep running; durable wake-source holds resume on reopen.\n\t */\n\tconst drainForHandoff = (): void => {\n\t\tif (shuttingDown) return;\n\t\tif (draining) {\n\t\t\tif (handoffAnnounced) router.beginDrain();\n\t\t\treturn;\n\t\t}\n\t\tdraining = true;\n\t\tcrashCleanupPaths.length = 0;\n\t\thostLog(\"draining for a generation handoff\");\n\t\tvoid endpointSuperseded()\n\t\t\t.then((superseded) => {\n\t\t\t\twriter.broadcastHostRecord({\n\t\t\t\t\ttype: \"host_superseded\",\n\t\t\t\t\tinstanceId: hostInstanceId(),\n\t\t\t\t\tgeneration: hostGeneration(process.env),\n\t\t\t\t\tsuccessor: superseded ? { socket: supervisorPublicSocketPath ?? socketPath } : null,\n\t\t\t\t});\n\t\t\t\thandoffAnnounced = true;\n\t\t\t\trouter.beginDrain();\n\t\t\t})\n\t\t\t.catch((cause: unknown) => {\n\t\t\t\thostLog(`handoff announcement failed: ${String(cause)}`);\n\t\t\t});\n\t};\n\t/**\n\t * Whether the endpoint this host serves is held by another socket entry now. A supervised host\n\t * answers for the PUBLIC path its supervisor bound - its own listener is a private hop nobody\n\t * replaces - and a bare host for the path it bound itself.\n\t */\n\tconst endpointSuperseded = (): Promise<boolean> =>\n\t\tsupervisorPublicSocketPath === undefined\n\t\t\t? socketEntryReplaced(socketPath, boundIdentity)\n\t\t\t: socketEntryReplaced(supervisorPublicSocketPath, supervisorPublicIdentity);\n\tregisterShutdownSignals(shutdown);\n\tif (process.platform !== \"win32\") process.on(\"SIGUSR1\", drainForHandoff);\n\t// Arm before listen: a supervisor death during the listen transition must\n\t// still close the child and clean its private endpoint.\n\tconst watchdog =\n\t\twatchdogConfig && supervisorPublicOwnerFile\n\t\t\t? {\n\t\t\t\t\t...watchdogConfig,\n\t\t\t\t\tcleanupPaths: crashCleanupPaths,\n\t\t\t\t\t// The supervisor may die while this host is still waiting for the token\n\t\t\t\t\t// below; read it before the watchdog removes the scratch directory, or the\n\t\t\t\t\t// shutdown's ownership check has nothing to prove with and leaves the\n\t\t\t\t\t// public socket behind.\n\t\t\t\t\tbeforeCleanup: async () => {\n\t\t\t\t\t\tsupervisorPublicIdentity ??= await readSocketIdentityFile(supervisorPublicOwnerFile);\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: watchdogConfig && { ...watchdogConfig, cleanupPaths: crashCleanupPaths };\n\tarmHostWatchdog(watchdog, (reason, cleanup) => {\n\t\tprocess.stderr.write(`senpi rpc host: ${reason}; shutting down\\n`);\n\t\t// Enter shutdown before killing session-owned child processes. The Windows\n\t\t// tree killer is synchronous, while the shutdown fallback must be armed\n\t\t// before any such cleanup can delay the event loop.\n\t\tvoid shutdown(0, cleanup);\n\t\tsetImmediate(killTrackedDetachedChildren);\n\t});\n\tboundIdentity = await listen(server, socketPath, secret);\n\tif (supervisorPublicOwnerFile) {\n\t\t// The supervisor publishes its public-socket token after this internal\n\t\t// listener is ready, so a short bounded wait keeps the lifecycles in step\n\t\t// without delaying unsupervised hosts.\n\t\tsupervisorPublicIdentity = await waitForSocketIdentityFile(supervisorPublicOwnerFile);\n\t}\n\tprocess.stderr.write(`senpi rpc listening on ${formatSocketAddress(socketPath)}\\n`);\n\n\t// Opt-in only: set by the lifecycle supervisor so this host can never outlive\n\t// it, including when the supervisor is SIGKILLed and runs no handler at all.\n\treturn new Promise(() => {});\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction noop(): void {}\n\nfunction parseError(error: string): RpcResponse {\n\treturn { type: \"response\", command: \"parse\", success: false, error };\n}\n\nfunction oversizedLineError(): string {\n\treturn `RPC input line exceeds ${MAX_RPC_LINE_CHARACTERS} characters.`;\n}\n\nfunction errorMessage(cause: unknown): string {\n\treturn cause instanceof Error ? cause.message : String(cause);\n}\n\n/** The endpoint this host listens on, or nothing when it speaks stdio and shares no socket. */\nfunction listenSocketPath(options: MultiSessionHostOptions): string | undefined {\n\tif (options.listen === undefined || options.listen === \"stdio://\") return undefined;\n\treturn resolveSocketPath(options.listen, options.agentDir);\n}\n\nfunction resolveSocketPath(value: string, agentDir: string): string {\n\tif (value === \"unix://\") return join(agentDir, \"rpc\", \"rpc.sock\");\n\tif (value.startsWith(\"unix://\")) {\n\t\tconst path = value.slice(\"unix://\".length);\n\t\tif (path.length === 0) return join(agentDir, \"rpc\", \"rpc.sock\");\n\t\tif (path.startsWith(\"@\") && process.platform === \"linux\") return `\\0${path.slice(1)}`;\n\t\treturn path;\n\t}\n\treturn value;\n}\n\nfunction formatSocketAddress(socketPath: string): string {\n\treturn socketPath.startsWith(\"\\0\") ? `unix://@${socketPath.slice(1)}` : `unix://${socketPath}`;\n}\n\nasync function prepareSocketPath(socketPath: string): Promise<void> {\n\tif (process.platform === \"win32\" || socketPath.startsWith(\"\\0\")) return;\n\tawait mkdir(dirname(socketPath), { recursive: true, mode: 0o700 });\n\ttry {\n\t\tawait access(socketPath);\n\t} catch (cause) {\n\t\tif (isNodeErrorCode(cause, \"ENOENT\")) return;\n\t\tthrow cause;\n\t}\n\tif (await probeSocket(socketPath)) throw new Error(`${socketPath}: address already in use by a live server.`);\n\tawait unlink(socketPath);\n}\n\nfunction probeSocket(socketPath: string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst socket = createConnection(resolveSocketTransportAddress(socketPath, process.platform));\n\t\tconst settle = (live: boolean) => {\n\t\t\tsocket.destroy();\n\t\t\tresolve(live);\n\t\t};\n\t\tsocket.once(\"connect\", () => settle(true));\n\t\tsocket.once(\"error\", () => settle(false));\n\t\tsocket.setTimeout(1_000, () => settle(false));\n\t});\n}\n\nfunction listen(server: Server, socketPath: string, secret?: Uint8Array): Promise<SocketFileIdentity | undefined> {\n\treturn new Promise((resolve, reject) => {\n\t\tserver.once(\"error\", reject);\n\t\tserver.listen(resolveSocketTransportAddress(socketPath, process.platform, secret), async () => {\n\t\t\tserver.off(\"error\", reject);\n\t\t\ttry {\n\t\t\t\tif (process.platform !== \"win32\" && !socketPath.startsWith(\"\\0\")) {\n\t\t\t\t\tawait chmod(socketPath, 0o600);\n\t\t\t\t\t// Record which filesystem entry THIS listener created; shutdown\n\t\t\t\t\t// removes the path only while this identity still matches.\n\t\t\t\t\treturn resolve(await statSocketIdentity(socketPath));\n\t\t\t\t}\n\t\t\t\tresolve(undefined);\n\t\t\t} catch (cause) {\n\t\t\t\treject(cause);\n\t\t\t}\n\t\t});\n\t});\n}\n\nfunction closeServer(server: Server): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tserver.close((cause) => (cause ? reject(cause) : resolve()));\n\t});\n}\n\nfunction hostLog(message: string): void {\n\tprocess.stderr.write(`senpi rpc host: ${message}\\n`);\n}\n\nfunction isNodeErrorCode(cause: unknown, code: string): boolean {\n\treturn cause instanceof Error && \"code\" in cause && cause.code === code;\n}\n\nfunction registerShutdownSignals(shutdown: (exitCode?: number) => Promise<never>): void {\n\tfor (const signal of process.platform === \"win32\" ? ([\"SIGTERM\"] as const) : ([\"SIGTERM\", \"SIGHUP\"] as const)) {\n\t\tprocess.on(signal, () => {\n\t\t\tkillTrackedDetachedChildren();\n\t\t\tvoid shutdown(signal === \"SIGHUP\" ? 129 : 143);\n\t\t});\n\t}\n}\n"]}
1
+ {"version":3,"file":"multi-session-host.js","sourceRoot":"","sources":["../../../src/modes/rpc/multi-session-host.ts"],"names":[],"mappings":"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","sourcesContent":["import { access, chmod, mkdir, unlink } from \"node:fs/promises\";\nimport { createConnection, createServer, type Server } from \"node:net\";\nimport { basename, dirname, join } from \"node:path\";\nimport type { CreateAgentSessionRuntimeFactory } from \"../../core/agent-session-runtime.ts\";\nimport { envValue } from \"../../core/brand.ts\";\nimport { HostMcpRegistry } from \"../../core/extensions/builtin/mcp/host-registry.ts\";\nimport type { SessionContext } from \"../../core/extensions/types.ts\";\nimport {\n\tflushRawStdout,\n\ttakeOverStdout,\n\twaitForRawStdoutBackpressure,\n\twriteRawStdout,\n} from \"../../core/output-guard.ts\";\nimport type { CliRuntimeConfiguration } from \"../../main.ts\";\nimport { killTrackedDetachedChildren } from \"../../utils/shell.ts\";\nimport { startHostChildReaper } from \"./child-reaper.ts\";\nimport type { RpcConnectionSink } from \"./connection-handler.ts\";\nimport { parseClientCapabilities } from \"./custom-capability.ts\";\nimport { ClientOccupancy } from \"./host-client-occupancy.ts\";\nimport { GENERATION_HANDOFF_CAPABILITY } from \"./host-decision.ts\";\nimport { type HostIdleOverrides, RPC_CLOSE_GRACE_MS_ENV, resolveHostIdlePolicy } from \"./host-idle-policy.ts\";\nimport { parseIdleExitMs } from \"./host-lifecycle.ts\";\nimport { HostMemorySampler } from \"./host-memory-sampler.ts\";\nimport { runAsHostGenerationProcess } from \"./host-process-role.ts\";\nimport { createEndpointReservations } from \"./host-reservations.ts\";\nimport { armHostWatchdog, readHostWatchdogConfigFromBrandEnv } from \"./host-watchdog.ts\";\nimport { attachJsonlLineReader, MAX_RPC_LINE_CHARACTERS } from \"./jsonl.ts\";\nimport { LoopLagWatchdog } from \"./loop-lag-watchdog.ts\";\nimport { hostGeneration, hostInstanceId } from \"./protocol-identity.ts\";\nimport { rpcCommandShapeError } from \"./rpc-input-validation.ts\";\nimport type { RpcCommand, RpcResponse } from \"./rpc-types.ts\";\nimport { type RpcBindingFactory, SessionCommandRouter } from \"./session-command-router.ts\";\nimport { SessionEventWriter } from \"./session-event-writer.ts\";\nimport { canonicalSessionPath } from \"./session-path-key.ts\";\nimport { RpcSessionRegistry } from \"./session-registry.ts\";\nimport {\n\tPUBLIC_SOCKET_IDENTITY_FILE,\n\treadSocketIdentityFile,\n\ttype SocketFileIdentity,\n\tshieldSocketDuringClose,\n\tsocketEntryReplaced,\n\tstatSocketIdentity,\n\tunlinkOwnedSocket,\n\twaitForSocketIdentityFile,\n} from \"./socket-ownership.ts\";\nimport { socketSink } from \"./socket-sink.ts\";\nimport {\n\tauthenticateSocket,\n\tensureSocketSecret,\n\tresolveSocketTransportAddress,\n\tSOCKET_SECRET_FILE_ENV,\n\tsocketSecretPath,\n} from \"./socket-transport.ts\";\nimport { WorkerSessionRegistry } from \"./worker-session-registry.ts\";\n\nexport interface MultiSessionHostOptions {\n\tagentDir: string;\n\tcreateRuntime: CreateAgentSessionRuntimeFactory;\n\tworkerConfiguration?: CliRuntimeConfiguration;\n\tcwd: string;\n\tpermissionPreset?: string;\n\tcreationModel?: { provider: string; modelId: string };\n\tinitialThinkingLevel?: string;\n\tlisten?: string;\n\t/** Test seam: defaults to the real shared-session binding. */\n\tcreateBinding?: RpcBindingFactory;\n}\n\nexport {\n\tDEFAULT_HOST_EMPTY_EXIT_MS,\n\tDEFAULT_SESSION_IDLE_EVICTION_MS,\n\tRPC_HOST_EMPTY_EXIT_MS_ENV,\n\tRPC_SESSION_IDLE_EVICTION_MS_ENV,\n\tresolveHostIdlePolicy,\n} from \"./host-idle-policy.ts\";\n\n/** Win32 named-pipe close can leave libuv's server callback pending after handles are destroyed. */\nconst WINDOWS_SHUTDOWN_HARD_EXIT_MS = 2_000;\n\n/**\n * Arm the host's self-observation: event-loop stall detection with per-session\n * attribution, and RSS reporting that tightens idle parking under pressure. Both run on\n * unref'd timers, and neither refuses, aborts or kills anything (#2207).\n */\nfunction startHostObservers(\n\trouter: SessionCommandRouter,\n\twriter: SessionEventWriter,\n\toptions: { onIdlePressure?: (rssMb: number) => void } = {},\n): { stop: () => void } {\n\tconst loopLag = new LoopLagWatchdog({ emit: (record) => writer.broadcastHostRecord(record) });\n\tconst memory = new HostMemorySampler({\n\t\temit: (record) => writer.broadcastHostRecord(record),\n\t\tsessions: () => router.sessionCount,\n\t\tonPressure: (pressure) => router.setMemoryPressure(pressure),\n\t\t...(options.onIdlePressure ? { onIdlePressure: options.onIdlePressure } : {}),\n\t});\n\tloopLag.start();\n\tmemory.start();\n\treturn {\n\t\tstop: () => {\n\t\t\tloopLag.stop();\n\t\t\tmemory.stop();\n\t\t},\n\t};\n}\n\ninterface Connection {\n\treadonly id: string;\n\treadonly sink: RpcConnectionSink;\n\treadonly detach: () => void;\n\treadonly close: () => void;\n}\n\n/**\n * Socket agent events are delivered only to connections attached to their\n * session, tagged with its routing sessionId. Content-free session lifecycle\n * events remain visible to every connection. Responses and extension UI\n * requests remain requester-only; foreign observation uses attach-on-open.\n */\nexport async function runMultiSessionHost(options: MultiSessionHostOptions): Promise<never> {\n\treturn runAsHostGenerationProcess(() =>\n\t\toptions.listen === undefined || options.listen === \"stdio://\"\n\t\t\t? runStdioHost(options)\n\t\t\t: runSocketHost(options, resolveSocketPath(options.listen, options.agentDir)),\n\t);\n}\n\nexport function createHostCore(\n\toptions: MultiSessionHostOptions,\n\twriter: SessionEventWriter,\n\tcapabilities = parseClientCapabilities(envValue(\"RPC_CLIENT_CAPABILITIES\")),\n\tidle: HostIdleOverrides = {},\n\thostContext?: SessionContext,\n) {\n\tconst policy = resolveHostIdlePolicy(process.env, idle);\n\tconst router = new SessionCommandRouter(\n\t\toptions.workerConfiguration\n\t\t\t? new WorkerSessionRegistry({\n\t\t\t\t\tconfiguration: options.workerConfiguration,\n\t\t\t\t\tnow: policy.now,\n\t\t\t\t\tcloseGraceMs: idle.closeGraceMs ?? parseIdleExitMs(process.env[RPC_CLOSE_GRACE_MS_ENV]) ?? 10_000,\n\t\t\t\t})\n\t\t\t: new RpcSessionRegistry({\n\t\t\t\t\tagentDir: options.agentDir,\n\t\t\t\t\tcreateRuntime: options.createRuntime,\n\t\t\t\t\tmcpRegistry: new HostMcpRegistry(),\n\t\t\t\t\tnow: policy.now,\n\t\t\t\t\tcloseGraceMs: idle.closeGraceMs ?? parseIdleExitMs(process.env[RPC_CLOSE_GRACE_MS_ENV]) ?? 10_000,\n\t\t\t\t\t// Two generations of this daemon can be alive at once during a handoff; the claims\n\t\t\t\t\t// they publish here are what keeps them off one session file.\n\t\t\t\t\tpathReservations: createEndpointReservations({\n\t\t\t\t\t\tagentDir: options.agentDir,\n\t\t\t\t\t\tsocket: listenSocketPath(options),\n\t\t\t\t\t\tinstanceId: hostInstanceId(),\n\t\t\t\t\t\tonFailure: hostLog,\n\t\t\t\t\t}),\n\t\t\t\t}),\n\t\twriter,\n\t\thostContext ? { ...options, hostContext } : options,\n\t\toptions.createBinding,\n\t\t{ capabilities },\n\t\t{\n\t\t\tnow: policy.now,\n\t\t\tidleEvictionMs: policy.idleEvictionMs,\n\t\t\temptyExitMs: policy.emptyExitMs,\n\t\t\tonEmptyExit: idle.onEmptyExit,\n\t\t\tonHandoffParked: idle.onHandoffParked,\n\t\t\tcanExitWhenEmpty: idle.canExitWhenEmpty,\n\t\t},\n\t);\n\tconst handle = async (line: string): Promise<void> => {\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(line);\n\t\t} catch (cause) {\n\t\t\tawait writer.enqueueControl(parseError(`Failed to parse command: ${errorMessage(cause)}`));\n\t\t\treturn;\n\t\t}\n\t\tconst shapeError = rpcCommandShapeError(parsed);\n\t\tif (shapeError) {\n\t\t\tawait writer.enqueueControl(parseError(shapeError));\n\t\t\treturn;\n\t\t}\n\t\tconst response = await router.handle(parsed as RpcCommand);\n\t\tif (response) await writer.enqueueControl(response);\n\t};\n\treturn { router, handle };\n}\n\n/** Plain-stdio host with no eagerly-created AgentSessionRuntime. */\nasync function runStdioHost(options: MultiSessionHostOptions): Promise<never> {\n\ttakeOverStdout();\n\tconst sink: RpcConnectionSink = { writeRaw: writeRawStdout, waitForBackpressure: waitForRawStdoutBackpressure };\n\tconst writer = new SessionEventWriter(sink.writeRaw, sink.waitForBackpressure);\n\t// An empty host (no session ever opened, or all closed) must not stay resident\n\t// forever: exit through the normal shutdown path once the window elapses.\n\tconst { router, handle } = createHostCore(options, writer, undefined, {\n\t\tonEmptyExit: () => void shutdown(0),\n\t});\n\tconst observers = startHostObservers(router, writer);\n\tlet shuttingDown = false;\n\tconst shutdown = async (exitCode = 0): Promise<never> => {\n\t\tif (shuttingDown) process.exit(exitCode);\n\t\tshuttingDown = true;\n\t\tobservers.stop();\n\t\tdetach();\n\t\tawait router.dispose();\n\t\tawait writer.flush();\n\t\tawait flushRawStdout();\n\t\tprocess.exit(exitCode);\n\t};\n\tconst onEnd = () => void shutdown();\n\tprocess.stdin.on(\"end\", onEnd);\n\tconst reportInputFailure = (cause: unknown): void => {\n\t\tprocess.stderr.write(`senpi rpc stdio request failed: ${errorMessage(cause)}\\n`);\n\t};\n\tconst detachReader = attachJsonlLineReader(process.stdin, (line) => void handle(line).catch(reportInputFailure), {\n\t\tmaxLineLength: MAX_RPC_LINE_CHARACTERS,\n\t\tonOversizedLine: () => void writer.enqueueControl(parseError(oversizedLineError())).catch(reportInputFailure),\n\t});\n\tconst detach = () => {\n\t\tdetachReader();\n\t\tprocess.stdin.off(\"end\", onEnd);\n\t};\n\tregisterShutdownSignals(shutdown);\n\treturn new Promise(() => {});\n}\n\nasync function runSocketHost(options: MultiSessionHostOptions, socketPath: string): Promise<never> {\n\tawait prepareSocketPath(socketPath);\n\tconst writer = new SessionEventWriter(() => {});\n\tconst connections = new Map<string, Connection>();\n\t// The supervisor's rule for which clients hold a host open (host-client-occupancy.ts): a\n\t// connection that has only sent observing reads (`status`) is not occupancy.\n\tconst occupancy = new ClientOccupancy(() => {});\n\tlet draining = false;\n\tlet handoffAnnounced = false;\n\tconst hostContext = hostSessionContext(socketPath);\n\tconst { router, handle } = createHostCore(\n\t\toptions,\n\t\twriter,\n\t\t[\n\t\t\t...parseClientCapabilities(envValue(\"RPC_CLIENT_CAPABILITIES\")).filter(\n\t\t\t\t(capability) => capability !== \"rendered_components\",\n\t\t\t),\n\t\t\t// A socket host installs the SIGUSR1 drain below, so it can be handed off to a newer\n\t\t\t// generation instead of being killed. A host that does not advertise this is never\n\t\t\t// signalled - SIGUSR1 would simply terminate it, sessions and all.\n\t\t\tGENERATION_HANDOFF_CAPABILITY,\n\t\t],\n\t\t// Supervised hosts idle-exit via the supervisor, but a socket host that\n\t\t// outlives its supervisor (or is started bare) still self-exits when empty.\n\t\t// A connected client counts as occupancy even with no session open: exiting\n\t\t// under it would drop its socket and read as a crash to the supervisor. Only an\n\t\t// OBSERVER does not - one whose every request so far was an `observe: true` read.\n\t\t// While DRAINING the opposite is true: the successor generation owns the socket, so a\n\t\t// sessionless connection must not hold this host open.\n\t\t{\n\t\t\tonEmptyExit: () => void shutdown(0),\n\t\t\tcanExitWhenEmpty: () => draining || (occupancy.attachedCount === 0 && occupancy.unclassifiedCount === 0),\n\t\t\tonHandoffParked: async (ids) => {\n\t\t\t\tawait Promise.all(\n\t\t\t\t\tids.map(async (id) => {\n\t\t\t\t\t\tawait writer.flushConnection(id);\n\t\t\t\t\t\tconnections.get(id)?.close();\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t},\n\t\t},\n\t\thostContext,\n\t);\n\tconst observers = startHostObservers(router, writer, {\n\t\t// The shape #1893 measured: gigabytes resident with `sessions.total 0`. Say it once, and when\n\t\t// this generation no longer owns the endpoint, leave - nobody can reach it to ask.\n\t\tonIdlePressure: (rssMb) => {\n\t\t\thostLog(`memory pressure with no sessions: rssMb=${rssMb}`);\n\t\t\tvoid endpointSuperseded().then((superseded) => {\n\t\t\t\tif (superseded) drainForHandoff();\n\t\t\t}, noop);\n\t\t},\n\t});\n\tlet nextConnection = 0;\n\tlet shuttingDown = false;\n\tconst secret =\n\t\tprocess.platform === \"win32\"\n\t\t\t? await ensureSocketSecret(process.env[SOCKET_SECRET_FILE_ENV] ?? socketSecretPath(socketPath))\n\t\t\t: undefined;\n\tconst watchdogConfig = readHostWatchdogConfigFromBrandEnv();\n\t// This host's OWN copy of the crash-path cleanup list. A drain empties it: once a successor\n\t// generation is registered, the daemon state files under those paths describe the successor,\n\t// and a crash of this (already replaced) host must not take them with it.\n\tconst crashCleanupPaths = [...(watchdogConfig?.cleanupPaths ?? [])];\n\t// Crash-path ownership for the supervisor's PUBLIC socket: the supervisor\n\t// records the identity of the entry it bound (inside its private scratch\n\t// directory, which no replacement supervisor writes) right after its listen,\n\t// and this host removes that path only while the identity still matches. A\n\t// blind path removal here would unlink a newer host's freshly published\n\t// entry after a takeover - the startup path already refuses to touch a\n\t// socket owned by a live server; teardown follows the same rule.\n\tconst supervisorPublicSocketPath =\n\t\tprocess.platform === \"win32\" || socketPath.startsWith(\"\\0\") ? undefined : watchdogConfig?.publicSocket;\n\tconst supervisorPublicOwnerFile =\n\t\tsupervisorPublicSocketPath && watchdogConfig?.scratchDir\n\t\t\t? join(watchdogConfig.scratchDir, PUBLIC_SOCKET_IDENTITY_FILE)\n\t\t\t: undefined;\n\tlet boundIdentity: SocketFileIdentity | undefined;\n\tlet supervisorPublicIdentity: SocketFileIdentity | undefined;\n\tconst server = createServer((socket) => {\n\t\tconst accept = (): void => {\n\t\t\tif (draining || shuttingDown) {\n\t\t\t\tsocket.destroy();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst id = `socket-${++nextConnection}`;\n\t\t\tconst sink = socketSink(socket);\n\t\t\twriter.registerConnection(id, sink);\n\t\t\toccupancy.admit(socket);\n\t\t\tconst detachReader = attachJsonlLineReader(\n\t\t\t\tsocket,\n\t\t\t\t(line) => {\n\t\t\t\t\t// Do not serialize awaited commands: extension_ui_response and other\n\t\t\t\t\t// re-entrant frames must be able to resolve a command already awaiting them.\n\t\t\t\t\tvoid writer\n\t\t\t\t\t\t.withConnection(id, () => handle(line))\n\t\t\t\t\t\t.catch((cause) => {\n\t\t\t\t\t\t\tprocess.stderr.write(`senpi rpc connection ${id} failed: ${errorMessage(cause)}\\n`);\n\t\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tmaxLineLength: MAX_RPC_LINE_CHARACTERS,\n\t\t\t\t\tonOversizedLine: () => {\n\t\t\t\t\t\tvoid writer\n\t\t\t\t\t\t\t.withConnection(id, () => writer.enqueueControl(parseError(oversizedLineError())))\n\t\t\t\t\t\t\t.catch((cause) =>\n\t\t\t\t\t\t\t\tprocess.stderr.write(`senpi rpc connection ${id} failed: ${errorMessage(cause)}\\n`),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t\tlet detached = false;\n\t\t\tconst detach = () => {\n\t\t\t\tif (detached) return;\n\t\t\t\tdetached = true;\n\t\t\t\tdetachReader();\n\t\t\t\twriter.unregisterConnection(id);\n\t\t\t\tconnections.delete(id);\n\t\t\t\toccupancy.release(socket);\n\t\t\t\t// A socket that dies without close_session still owns its sessions' attachments\n\t\t\t\t// and path reservations. Release them on the command chain so this runs after any\n\t\t\t\t// in-flight command for this connection settles, otherwise the path stays pinned\n\t\t\t\t// by a runtime whose client is gone and later resumes attach to that orphan.\n\t\t\t\tvoid router.releaseConnection(id).catch((cause) => {\n\t\t\t\t\tprocess.stderr.write(`senpi rpc connection ${id} release failed: ${errorMessage(cause)}\\n`);\n\t\t\t\t});\n\t\t\t};\n\t\t\tconnections.set(id, { id, sink, detach, close: () => socket.destroy() });\n\t\t\tsocket.once(\"close\", detach);\n\t\t\tsocket.once(\"error\", () => detach());\n\t\t};\n\t\tif (secret) authenticateSocket(socket, secret, accept);\n\t\telse accept();\n\t});\n\tserver.on(\"error\", (cause) => {\n\t\tif (!shuttingDown) process.stderr.write(`senpi rpc socket listener failed: ${errorMessage(cause)}\\n`);\n\t});\n\t// Long-lived hosts outlive many session workers, and a terminated worker thread\n\t// takes its children's exit watchers with it (measured: every spawn API leaks\n\t// that way). The reaper claims those abandoned children; it never touches one a\n\t// live thread could still be waiting for.\n\tconst stopChildReaper = await startHostChildReaper(hostLog);\n\tconst shutdown = async (exitCode = 0, watchdogCleanup?: Promise<void>): Promise<never> => {\n\t\tif (shuttingDown) process.exit(exitCode);\n\t\tshuttingDown = true;\n\t\tobservers.stop();\n\t\tstopChildReaper();\n\t\t// On Windows, destroying named-pipe sockets does not always make libuv's\n\t\t// server.close callback fire: connected pipe instances can remain in the\n\t\t// kernel after the JavaScript handles are destroyed. Keep the normal drain\n\t\t// path, but never let that platform-specific close stall orphan the host.\n\t\ttry {\n\t\t\t// Dispose while connections are still registered so `session_closed`\n\t\t\t// `{ reason: \"host_shutdown\" }` reaches clients before the sockets die.\n\t\t\tawait router.dispose();\n\t\t\tawait writer.flush();\n\t\t\tfor (const connection of connections.values()) {\n\t\t\t\tconnection.detach();\n\t\t\t\tconnection.close();\n\t\t\t}\n\t\t\t// libuv unlinks the bound NAME when the listening handle closes - which\n\t\t\t// would delete a newer host's entry renamed over this path. Shield the\n\t\t\t// current entry for the close, then let the ownership check decide.\n\t\t\tawait shieldSocketDuringClose(socketPath, () =>\n\t\t\t\tprocess.platform === \"win32\"\n\t\t\t\t\t? Promise.race([closeServer(server), delay(WINDOWS_SHUTDOWN_HARD_EXIT_MS)])\n\t\t\t\t\t: closeServer(server),\n\t\t\t);\n\t\t\t// Ownership-checked: unlink only the entry THIS process bound. After a\n\t\t\t// takeover renamed a newer host's socket over the same path, the\n\t\t\t// identity no longer matches and the replacement stays published.\n\t\t\tawait unlinkOwnedSocket(socketPath, boundIdentity, hostLog);\n\t\t\tif (supervisorPublicSocketPath) {\n\t\t\t\tawait unlinkOwnedSocket(supervisorPublicSocketPath, supervisorPublicIdentity, hostLog);\n\t\t\t}\n\t\t\tif (watchdogCleanup) await watchdogCleanup;\n\t\t} finally {\n\t\t\t// Explicitly terminate after every shutdown trigger. Windows named-pipe\n\t\t\t// handles are not fully controllable from JS, and an unresolved cleanup\n\t\t\t// must not leave this daemon or its public endpoint alive.\n\t\t\tprocess.exit(exitCode);\n\t\t}\n\t};\n\t/**\n\t * Announce through the record writer, never inside the supervisor's raw byte proxy. This\n\t * preserves JSONL framing and FIFO before parking, including when a record spans proxy reads.\n\t * Active turns/requests keep running; durable wake-source holds resume on reopen.\n\t */\n\tconst drainForHandoff = (): void => {\n\t\tif (shuttingDown) return;\n\t\tif (draining) {\n\t\t\tif (handoffAnnounced) router.beginDrain();\n\t\t\treturn;\n\t\t}\n\t\tdraining = true;\n\t\tcrashCleanupPaths.length = 0;\n\t\thostLog(\"draining for a generation handoff\");\n\t\tvoid endpointSuperseded()\n\t\t\t.then((superseded) => {\n\t\t\t\twriter.broadcastHostRecord({\n\t\t\t\t\ttype: \"host_superseded\",\n\t\t\t\t\tinstanceId: hostInstanceId(),\n\t\t\t\t\tgeneration: hostGeneration(process.env),\n\t\t\t\t\tsuccessor: superseded ? { socket: supervisorPublicSocketPath ?? socketPath } : null,\n\t\t\t\t});\n\t\t\t\thandoffAnnounced = true;\n\t\t\t\trouter.beginDrain();\n\t\t\t})\n\t\t\t.catch((cause: unknown) => {\n\t\t\t\thostLog(`handoff announcement failed: ${String(cause)}`);\n\t\t\t});\n\t};\n\t/**\n\t * Whether the endpoint this host serves is held by another socket entry now. A supervised host\n\t * answers for the PUBLIC path its supervisor bound - its own listener is a private hop nobody\n\t * replaces - and a bare host for the path it bound itself.\n\t */\n\tconst endpointSuperseded = (): Promise<boolean> =>\n\t\tsupervisorPublicSocketPath === undefined\n\t\t\t? socketEntryReplaced(socketPath, boundIdentity)\n\t\t\t: socketEntryReplaced(supervisorPublicSocketPath, supervisorPublicIdentity);\n\tregisterShutdownSignals(shutdown);\n\tif (process.platform !== \"win32\") process.on(\"SIGUSR1\", drainForHandoff);\n\t// Arm before listen: a supervisor death during the listen transition must\n\t// still close the child and clean its private endpoint.\n\tconst watchdog =\n\t\twatchdogConfig && supervisorPublicOwnerFile\n\t\t\t? {\n\t\t\t\t\t...watchdogConfig,\n\t\t\t\t\tcleanupPaths: crashCleanupPaths,\n\t\t\t\t\t// The supervisor may die while this host is still waiting for the token\n\t\t\t\t\t// below; read it before the watchdog removes the scratch directory, or the\n\t\t\t\t\t// shutdown's ownership check has nothing to prove with and leaves the\n\t\t\t\t\t// public socket behind.\n\t\t\t\t\tbeforeCleanup: async () => {\n\t\t\t\t\t\tsupervisorPublicIdentity ??= await readSocketIdentityFile(supervisorPublicOwnerFile);\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: watchdogConfig && { ...watchdogConfig, cleanupPaths: crashCleanupPaths };\n\tarmHostWatchdog(watchdog, (reason, cleanup) => {\n\t\tprocess.stderr.write(`senpi rpc host: ${reason}; shutting down\\n`);\n\t\t// Enter shutdown before killing session-owned child processes. The Windows\n\t\t// tree killer is synchronous, while the shutdown fallback must be armed\n\t\t// before any such cleanup can delay the event loop.\n\t\tvoid shutdown(0, cleanup);\n\t\tsetImmediate(killTrackedDetachedChildren);\n\t});\n\tboundIdentity = await listen(server, socketPath, secret);\n\tif (supervisorPublicOwnerFile) {\n\t\t// The supervisor publishes its public-socket token after this internal\n\t\t// listener is ready, so a short bounded wait keeps the lifecycles in step\n\t\t// without delaying unsupervised hosts.\n\t\tsupervisorPublicIdentity = await waitForSocketIdentityFile(supervisorPublicOwnerFile);\n\t}\n\tprocess.stderr.write(`senpi rpc listening on ${formatSocketAddress(socketPath)}\\n`);\n\n\t// Opt-in only: set by the lifecycle supervisor so this host can never outlive\n\t// it, including when the supervisor is SIGKILLed and runs no handler at all.\n\treturn new Promise(() => {});\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction noop(): void {}\n\nfunction parseError(error: string): RpcResponse {\n\treturn { type: \"response\", command: \"parse\", success: false, error };\n}\n\nfunction oversizedLineError(): string {\n\treturn `RPC input line exceeds ${MAX_RPC_LINE_CHARACTERS} characters.`;\n}\n\nfunction errorMessage(cause: unknown): string {\n\treturn cause instanceof Error ? cause.message : String(cause);\n}\n\n/**\n * The identity every session on this socket host carries in its context: `host_socket`, the PUBLIC\n * endpoint clients address (the supervisor's path for a supervised host, whose own listener is a\n * private hop; the bound path for a bare one), realpath-canonicalized so it compares equal however\n * the path was spelled; and `host_instance`, this generation. The endpoint is stable across handoffs,\n * the instance is not. The FIRST generation runs this before its supervisor has created the socket's\n * directory, a successor after, so the directory is canonicalized through its deepest existing\n * ancestor: both then stamp the same spelling. `host_socket` is omitted where no public path exists\n * (abstract sockets, a supervised win32 host, whose supervisor publishes none).\n */\nfunction hostSessionContext(socketPath: string): SessionContext {\n\tconst supervised = readHostWatchdogConfigFromBrandEnv();\n\tconst endpoint = supervised === undefined ? socketPath : supervised.publicSocket;\n\tif (endpoint === undefined || endpoint.startsWith(\"\\0\")) return { host_instance: hostInstanceId() };\n\tconst directory = canonicalSessionPath(dirname(endpoint));\n\treturn { host_socket: join(directory, basename(endpoint)), host_instance: hostInstanceId() };\n}\n\n/** The endpoint this host listens on, or nothing when it speaks stdio and shares no socket. */\nfunction listenSocketPath(options: MultiSessionHostOptions): string | undefined {\n\tif (options.listen === undefined || options.listen === \"stdio://\") return undefined;\n\treturn resolveSocketPath(options.listen, options.agentDir);\n}\n\nfunction resolveSocketPath(value: string, agentDir: string): string {\n\tif (value === \"unix://\") return join(agentDir, \"rpc\", \"rpc.sock\");\n\tif (value.startsWith(\"unix://\")) {\n\t\tconst path = value.slice(\"unix://\".length);\n\t\tif (path.length === 0) return join(agentDir, \"rpc\", \"rpc.sock\");\n\t\tif (path.startsWith(\"@\") && process.platform === \"linux\") return `\\0${path.slice(1)}`;\n\t\treturn path;\n\t}\n\treturn value;\n}\n\nfunction formatSocketAddress(socketPath: string): string {\n\treturn socketPath.startsWith(\"\\0\") ? `unix://@${socketPath.slice(1)}` : `unix://${socketPath}`;\n}\n\nasync function prepareSocketPath(socketPath: string): Promise<void> {\n\tif (process.platform === \"win32\" || socketPath.startsWith(\"\\0\")) return;\n\tawait mkdir(dirname(socketPath), { recursive: true, mode: 0o700 });\n\ttry {\n\t\tawait access(socketPath);\n\t} catch (cause) {\n\t\tif (isNodeErrorCode(cause, \"ENOENT\")) return;\n\t\tthrow cause;\n\t}\n\tif (await probeSocket(socketPath)) throw new Error(`${socketPath}: address already in use by a live server.`);\n\tawait unlink(socketPath);\n}\n\nfunction probeSocket(socketPath: string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst socket = createConnection(resolveSocketTransportAddress(socketPath, process.platform));\n\t\tconst settle = (live: boolean) => {\n\t\t\tsocket.destroy();\n\t\t\tresolve(live);\n\t\t};\n\t\tsocket.once(\"connect\", () => settle(true));\n\t\tsocket.once(\"error\", () => settle(false));\n\t\tsocket.setTimeout(1_000, () => settle(false));\n\t});\n}\n\nfunction listen(server: Server, socketPath: string, secret?: Uint8Array): Promise<SocketFileIdentity | undefined> {\n\treturn new Promise((resolve, reject) => {\n\t\tserver.once(\"error\", reject);\n\t\tserver.listen(resolveSocketTransportAddress(socketPath, process.platform, secret), async () => {\n\t\t\tserver.off(\"error\", reject);\n\t\t\ttry {\n\t\t\t\tif (process.platform !== \"win32\" && !socketPath.startsWith(\"\\0\")) {\n\t\t\t\t\tawait chmod(socketPath, 0o600);\n\t\t\t\t\t// Record which filesystem entry THIS listener created; shutdown\n\t\t\t\t\t// removes the path only while this identity still matches.\n\t\t\t\t\treturn resolve(await statSocketIdentity(socketPath));\n\t\t\t\t}\n\t\t\t\tresolve(undefined);\n\t\t\t} catch (cause) {\n\t\t\t\treject(cause);\n\t\t\t}\n\t\t});\n\t});\n}\n\nfunction closeServer(server: Server): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tserver.close((cause) => (cause ? reject(cause) : resolve()));\n\t});\n}\n\nfunction hostLog(message: string): void {\n\tprocess.stderr.write(`senpi rpc host: ${message}\\n`);\n}\n\nfunction isNodeErrorCode(cause: unknown, code: string): boolean {\n\treturn cause instanceof Error && \"code\" in cause && cause.code === code;\n}\n\nfunction registerShutdownSignals(shutdown: (exitCode?: number) => Promise<never>): void {\n\tfor (const signal of process.platform === \"win32\" ? ([\"SIGTERM\"] as const) : ([\"SIGTERM\", \"SIGHUP\"] as const)) {\n\t\tprocess.on(signal, () => {\n\t\t\tkillTrackedDetachedChildren();\n\t\t\tvoid shutdown(signal === \"SIGHUP\" ? 129 : 143);\n\t\t});\n\t}\n}\n"]}
@@ -27,10 +27,10 @@
27
27
  *
28
28
  * | Command | Params | Success data | Notes |
29
29
  * | ---------------- | ----------------------------------------------------------------------------------------------- | ----------------------------------------------- | ----- |
30
- * | `get_protocol_info` | - | `{ protocolVersion: 1, serverVersion, capabilities, mode: "classic"|"multi", instanceId, generation, engineVersion, engineOrdinal, launch_profile }` | Answered in BOTH modes; side-effect-free capability probe. The identity fields name the host process (`instanceId`), its daemon generation, its build (`engineVersion`, and `engineOrdinal` = `[y, m, d, postRelease, buildEpoch]`) and what it was launched with (`launch_profile { profile_id, core }`). Compatibility and upgrade decisions use `protocolVersion` + `capabilities` + `engineOrdinal`; `serverVersion` is informational and is NEVER compared for compatibility. |
30
+ * | `get_protocol_info` | `observe?` (an observing read never counts as host activity) | `{ protocolVersion: 1, serverVersion, capabilities, mode: "classic"|"multi", instanceId, generation, engineVersion, engineOrdinal, launch_profile, memory_pressure? }` | Answered in BOTH modes; side-effect-free capability probe. The identity fields name the host process (`instanceId`), its daemon generation, its build (`engineVersion`, and `engineOrdinal` = `[y, m, d, postRelease, buildEpoch]`) and what it was launched with (`launch_profile { profile_id, core }`). Compatibility and upgrade decisions use `protocolVersion` + `capabilities` + `engineOrdinal`; `serverVersion` is informational and is NEVER compared for compatibility. |
31
31
  * | `open_session` | `sessionPath?`, `cwd?`, `provider?`, `modelId?`, `thinkingLevel?`, `permissionPreset?`, `retain_on_disconnect?`, `kind?`, `context?`, `auto_title?`, `durableSessionId?` (all optional; paths MUST be absolute) | `{ sessionId, state: RpcSessionState, attached?: boolean }` | `sessionPath` = today's `--session` semantics (open-if-exists else create persisting there); `provider`/`modelId` applied only on create (resume restores the session's model); params form the immutable launch profile (D8). `retain_on_disconnect: true` (default false, host capability `retain_on_disconnect`) makes a dropped connection detach instead of closing the session. `kind: "interactive"|"worker"` (default `interactive`, host capability `session_kind`) sets the session's visibility class. `context` (host capability `session_context`) is an opaque `Record<string,string>` the host never interprets: at most 32 keys matching `^[a-z][a-z0-9_]*$`, each value <= 16 KiB, <= 32 KiB of JSON in total; it reaches that session's extensions as `pi.sessionContext` and nothing else. `auto_title` (host capability `auto_title_per_session`) opts this session into or out of engine-side titling; omitted keeps the host `--auto-title-sessions` / appMode default. |
32
32
  * | `close_session` | `sessionId` | `{}` | `unknown_session` when the requesting connection never attached to that handle (a close releases the CALLER's attachment). Otherwise aborts active work and awaits teardown for the host grace window, then force-releases; the first closer's response is the LAST record tagged with that handle, while concurrent closes join and receive targeted success responses. |
33
- * | `list_sessions` | `include_workers?` (default false) | `{ sessions: [{ sessionId, durableSessionId, sessionPath, cwd, name, status, attachments, kind, context? }] }` | Includes `opening`/`closing` entries with their status; `attachments` is the live client count (`0` = retained, detached). `kind: "worker"` rows are omitted unless `include_workers: true`, and `context` is published ONLY on that listing. |
33
+ * | `list_sessions` | `include_workers?` (default false), `observe?` | `{ sessions: [{ sessionId, durableSessionId, sessionPath, cwd, name, status, attachments, kind, context? }] }` | Includes `opening`/`closing` entries with their status; `attachments` is the live client count (`0` = retained, detached). `kind: "worker"` rows are omitted unless `include_workers: true`, and `context` is published ONLY on that listing. |
34
34
  * | `get_steering_messages` / `get_follow_up_messages` / `clear_queue` | queue read or clear parameters | host-authoritative queue values | Interactive attach clients must not read bootstrap queues. |
35
35
  * | `abort_branch_summary` / `record_bash_result` / `set_label` | narrow mutation payloads | `{}` | Routes interactive runtime mutations to the owning host session. |
36
36
  * | every existing command | + `sessionId` (REQUIRED in multi mode) | unchanged | Routed to that session. |
@@ -27,10 +27,10 @@
27
27
  *
28
28
  * | Command | Params | Success data | Notes |
29
29
  * | ---------------- | ----------------------------------------------------------------------------------------------- | ----------------------------------------------- | ----- |
30
- * | `get_protocol_info` | - | `{ protocolVersion: 1, serverVersion, capabilities, mode: "classic"|"multi", instanceId, generation, engineVersion, engineOrdinal, launch_profile }` | Answered in BOTH modes; side-effect-free capability probe. The identity fields name the host process (`instanceId`), its daemon generation, its build (`engineVersion`, and `engineOrdinal` = `[y, m, d, postRelease, buildEpoch]`) and what it was launched with (`launch_profile { profile_id, core }`). Compatibility and upgrade decisions use `protocolVersion` + `capabilities` + `engineOrdinal`; `serverVersion` is informational and is NEVER compared for compatibility. |
30
+ * | `get_protocol_info` | `observe?` (an observing read never counts as host activity) | `{ protocolVersion: 1, serverVersion, capabilities, mode: "classic"|"multi", instanceId, generation, engineVersion, engineOrdinal, launch_profile, memory_pressure? }` | Answered in BOTH modes; side-effect-free capability probe. The identity fields name the host process (`instanceId`), its daemon generation, its build (`engineVersion`, and `engineOrdinal` = `[y, m, d, postRelease, buildEpoch]`) and what it was launched with (`launch_profile { profile_id, core }`). Compatibility and upgrade decisions use `protocolVersion` + `capabilities` + `engineOrdinal`; `serverVersion` is informational and is NEVER compared for compatibility. |
31
31
  * | `open_session` | `sessionPath?`, `cwd?`, `provider?`, `modelId?`, `thinkingLevel?`, `permissionPreset?`, `retain_on_disconnect?`, `kind?`, `context?`, `auto_title?`, `durableSessionId?` (all optional; paths MUST be absolute) | `{ sessionId, state: RpcSessionState, attached?: boolean }` | `sessionPath` = today's `--session` semantics (open-if-exists else create persisting there); `provider`/`modelId` applied only on create (resume restores the session's model); params form the immutable launch profile (D8). `retain_on_disconnect: true` (default false, host capability `retain_on_disconnect`) makes a dropped connection detach instead of closing the session. `kind: "interactive"|"worker"` (default `interactive`, host capability `session_kind`) sets the session's visibility class. `context` (host capability `session_context`) is an opaque `Record<string,string>` the host never interprets: at most 32 keys matching `^[a-z][a-z0-9_]*$`, each value <= 16 KiB, <= 32 KiB of JSON in total; it reaches that session's extensions as `pi.sessionContext` and nothing else. `auto_title` (host capability `auto_title_per_session`) opts this session into or out of engine-side titling; omitted keeps the host `--auto-title-sessions` / appMode default. |
32
32
  * | `close_session` | `sessionId` | `{}` | `unknown_session` when the requesting connection never attached to that handle (a close releases the CALLER's attachment). Otherwise aborts active work and awaits teardown for the host grace window, then force-releases; the first closer's response is the LAST record tagged with that handle, while concurrent closes join and receive targeted success responses. |
33
- * | `list_sessions` | `include_workers?` (default false) | `{ sessions: [{ sessionId, durableSessionId, sessionPath, cwd, name, status, attachments, kind, context? }] }` | Includes `opening`/`closing` entries with their status; `attachments` is the live client count (`0` = retained, detached). `kind: "worker"` rows are omitted unless `include_workers: true`, and `context` is published ONLY on that listing. |
33
+ * | `list_sessions` | `include_workers?` (default false), `observe?` | `{ sessions: [{ sessionId, durableSessionId, sessionPath, cwd, name, status, attachments, kind, context? }] }` | Includes `opening`/`closing` entries with their status; `attachments` is the live client count (`0` = retained, detached). `kind: "worker"` rows are omitted unless `include_workers: true`, and `context` is published ONLY on that listing. |
34
34
  * | `get_steering_messages` / `get_follow_up_messages` / `clear_queue` | queue read or clear parameters | host-authoritative queue values | Interactive attach clients must not read bootstrap queues. |
35
35
  * | `abort_branch_summary` / `record_bash_result` / `set_label` | narrow mutation payloads | `{}` | Routes interactive runtime mutations to the owning host session. |
36
36
  * | every existing command | + `sessionId` (REQUIRED in multi mode) | unchanged | Routed to that session. |