@code-yeongyu/senpi 2026.9.27-4 → 2026.9.28

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 (271) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/bundle/chunks/{app-server-command-3GVJZFFH.js → app-server-command-TOPTGVQB.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-SLL375O7.js → chunk-7DY2JPUX.js} +1 -1
  8. package/dist/bundle/chunks/chunk-7YUGCTEV.js +15 -0
  9. package/dist/bundle/chunks/{chunk-FWBSGMAE.js → chunk-A4UMPXQF.js} +1 -1
  10. package/dist/bundle/chunks/chunk-BBK6SSYT.js +2 -0
  11. package/dist/bundle/chunks/{chunk-C2425O2Q.js → chunk-CNQ4JB45.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-27IFXX3B.js → chunk-DRN4FQCE.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-22MGY5JQ.js → chunk-EHSZ7UCW.js} +1 -1
  14. package/dist/bundle/chunks/chunk-FHJYIU3U.js +5 -0
  15. package/dist/bundle/chunks/chunk-GJUCSTAG.js +19 -0
  16. package/dist/bundle/chunks/{chunk-VMKAYXHL.js → chunk-KIOTFUG6.js} +25 -20
  17. package/dist/bundle/chunks/{chunk-2BMD37YV.js → chunk-LUPPSEQW.js} +1 -1
  18. package/dist/bundle/chunks/chunk-OAIVTS5X.js +8 -0
  19. package/dist/bundle/chunks/{chunk-NRGMT74E.js → chunk-QOVUQZO4.js} +1 -1
  20. package/dist/bundle/chunks/chunk-SN4ZHPMN.js +135 -0
  21. package/dist/bundle/chunks/{chunk-IKXPBYSO.js → chunk-UJFLZ2M2.js} +1 -1
  22. package/dist/bundle/chunks/{chunk-4T4GFAGK.js → chunk-VFSXUT7K.js} +1 -1
  23. package/dist/bundle/chunks/{cli-main-66NFBKVH.js → cli-main-ARR54ET6.js} +1 -1
  24. package/dist/bundle/chunks/{google-generative-ai-ZTGKZTGU.js → google-generative-ai-LFCBHG3Y.js} +1 -1
  25. package/dist/bundle/chunks/{google-vertex-CCZ376YK.js → google-vertex-C474FXU7.js} +1 -1
  26. package/dist/bundle/chunks/{help-fast-path-NZ3I2UEN.js → help-fast-path-3U3YDEZ6.js} +1 -1
  27. package/dist/bundle/chunks/{host-command-RORPAC5X.js → host-command-6CJ7YM6W.js} +1 -1
  28. package/dist/bundle/chunks/{host-lifecycle-UDMJGLOR.js → host-lifecycle-RZIMOFDM.js} +1 -1
  29. package/dist/bundle/chunks/host-lifecycle.js +6 -6
  30. package/dist/bundle/chunks/interactive-host-runtime-A3GEIFTU.js +4 -0
  31. package/dist/bundle/chunks/{interactive-mode-LOAADKLH.js → interactive-mode-ITSFLGF6.js} +1 -1
  32. package/dist/bundle/chunks/{list-tips-S5CANO5W.js → list-tips-FPXDXN7R.js} +1 -1
  33. package/dist/bundle/chunks/multi-session-host-B4WMZE2G.js +21 -0
  34. package/dist/bundle/chunks/{openai-codex-responses-YSGZZDD5.js → openai-codex-responses-2LDIFL6Z.js} +1 -1
  35. package/dist/bundle/chunks/{openai-completions-KWHRWCBN.js → openai-completions-7KZLPKYP.js} +1 -1
  36. package/dist/bundle/chunks/{openai-images-6U5FCN5S.js → openai-images-I7DR7ERP.js} +1 -1
  37. package/dist/bundle/chunks/{openai-responses-7PWMBGYX.js → openai-responses-LCHS3AF3.js} +1 -1
  38. package/dist/bundle/chunks/{openrouter-images-QIKWQZUD.js → openrouter-images-IPNDRK6C.js} +1 -1
  39. package/dist/bundle/chunks/{package-manager-cli-5EEDA35I.js → package-manager-cli-DXHQ6RKA.js} +1 -1
  40. package/dist/bundle/chunks/{rotation-stream-KZG4OVR7.js → rotation-stream-ZVJEGA2G.js} +1 -1
  41. package/dist/bundle/chunks/{rpc-mode-HOEA5NGQ.js → rpc-mode-PFPNP65F.js} +1 -1
  42. package/dist/bundle/chunks/session-picker-KOPOV6Q4.js +2 -0
  43. package/dist/bundle/chunks/session-worker.js +311 -302
  44. package/dist/bundle/cli.js +1 -1
  45. package/dist/bundle/index.js +1 -1
  46. package/dist/bundle/rpc-entry.js +1 -1
  47. package/dist/cli/args.js +1 -1
  48. package/dist/cli/args.js.map +1 -1
  49. package/dist/cli/host-command.d.ts +8 -1
  50. package/dist/cli/host-command.d.ts.map +1 -1
  51. package/dist/cli/host-command.js +66 -14
  52. package/dist/cli/host-command.js.map +1 -1
  53. package/dist/core/agent-session.d.ts +18 -1
  54. package/dist/core/agent-session.d.ts.map +1 -1
  55. package/dist/core/agent-session.js +119 -3
  56. package/dist/core/agent-session.js.map +1 -1
  57. package/dist/core/retry-fallback/candidates.d.ts +26 -0
  58. package/dist/core/retry-fallback/candidates.d.ts.map +1 -0
  59. package/dist/core/retry-fallback/candidates.js +41 -0
  60. package/dist/core/retry-fallback/candidates.js.map +1 -0
  61. package/dist/core/retry-fallback/circuit-probes.d.ts +30 -0
  62. package/dist/core/retry-fallback/circuit-probes.d.ts.map +1 -0
  63. package/dist/core/retry-fallback/circuit-probes.js +60 -0
  64. package/dist/core/retry-fallback/circuit-probes.js.map +1 -0
  65. package/dist/core/retry-fallback/circuit.d.ts +101 -0
  66. package/dist/core/retry-fallback/circuit.d.ts.map +1 -0
  67. package/dist/core/retry-fallback/circuit.js +175 -0
  68. package/dist/core/retry-fallback/circuit.js.map +1 -0
  69. package/dist/core/retry-fallback/controller-types.d.ts +58 -0
  70. package/dist/core/retry-fallback/controller-types.d.ts.map +1 -0
  71. package/dist/core/retry-fallback/controller-types.js +2 -0
  72. package/dist/core/retry-fallback/controller-types.js.map +1 -0
  73. package/dist/core/retry-fallback/controller.d.ts +21 -49
  74. package/dist/core/retry-fallback/controller.d.ts.map +1 -1
  75. package/dist/core/retry-fallback/controller.js +88 -42
  76. package/dist/core/retry-fallback/controller.js.map +1 -1
  77. package/dist/core/sdk.d.ts.map +1 -1
  78. package/dist/core/sdk.js +25 -17
  79. package/dist/core/sdk.js.map +1 -1
  80. package/dist/core/session-failure-report.d.ts +33 -0
  81. package/dist/core/session-failure-report.d.ts.map +1 -0
  82. package/dist/core/session-failure-report.js +62 -0
  83. package/dist/core/session-failure-report.js.map +1 -0
  84. package/dist/core/settings-manager.d.ts +3 -0
  85. package/dist/core/settings-manager.d.ts.map +1 -1
  86. package/dist/core/settings-manager.js +4 -0
  87. package/dist/core/settings-manager.js.map +1 -1
  88. package/dist/core/slash-commands.d.ts.map +1 -1
  89. package/dist/core/slash-commands.js +1 -0
  90. package/dist/core/slash-commands.js.map +1 -1
  91. package/dist/index.d.ts +1 -1
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +1 -1
  94. package/dist/index.js.map +1 -1
  95. package/dist/modes/index.d.ts +1 -0
  96. package/dist/modes/index.d.ts.map +1 -1
  97. package/dist/modes/index.js +2 -0
  98. package/dist/modes/index.js.map +1 -1
  99. package/dist/modes/interactive/help-content.d.ts.map +1 -1
  100. package/dist/modes/interactive/help-content.js +3 -0
  101. package/dist/modes/interactive/help-content.js.map +1 -1
  102. package/dist/modes/interactive/interactive-host-attach.d.ts +10 -0
  103. package/dist/modes/interactive/interactive-host-attach.d.ts.map +1 -0
  104. package/dist/modes/interactive/interactive-host-attach.js +14 -0
  105. package/dist/modes/interactive/interactive-host-attach.js.map +1 -0
  106. package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
  107. package/dist/modes/interactive/interactive-host-runtime.js +3 -6
  108. package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
  109. package/dist/modes/interactive/interactive-mode.d.ts +7 -0
  110. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  111. package/dist/modes/interactive/interactive-mode.js +66 -1
  112. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  113. package/dist/modes/interactive/session-failure-info.d.ts +3 -0
  114. package/dist/modes/interactive/session-failure-info.d.ts.map +1 -0
  115. package/dist/modes/interactive/session-failure-info.js +21 -0
  116. package/dist/modes/interactive/session-failure-info.js.map +1 -0
  117. package/dist/modes/interactive/tips/catalog/model-tips.d.ts +7 -7
  118. package/dist/modes/interactive/tips/catalog/model-tips.d.ts.map +1 -1
  119. package/dist/modes/interactive/tips/catalog/model-tips.js +7 -1
  120. package/dist/modes/interactive/tips/catalog/model-tips.js.map +1 -1
  121. package/dist/modes/interactive/tips/catalog/session-tips.d.ts.map +1 -1
  122. package/dist/modes/interactive/tips/catalog/session-tips.js +1 -1
  123. package/dist/modes/interactive/tips/catalog/session-tips.js.map +1 -1
  124. package/dist/modes/rpc/host-attach-hold.d.ts +16 -0
  125. package/dist/modes/rpc/host-attach-hold.d.ts.map +1 -0
  126. package/dist/modes/rpc/host-attach-hold.js +9 -0
  127. package/dist/modes/rpc/host-attach-hold.js.map +1 -0
  128. package/dist/modes/rpc/host-cleanup-paths.d.ts +20 -0
  129. package/dist/modes/rpc/host-cleanup-paths.d.ts.map +1 -0
  130. package/dist/modes/rpc/host-cleanup-paths.js +13 -0
  131. package/dist/modes/rpc/host-cleanup-paths.js.map +1 -0
  132. package/dist/modes/rpc/host-client-occupancy.d.ts +24 -0
  133. package/dist/modes/rpc/host-client-occupancy.d.ts.map +1 -0
  134. package/dist/modes/rpc/host-client-occupancy.js +53 -0
  135. package/dist/modes/rpc/host-client-occupancy.js.map +1 -0
  136. package/dist/modes/rpc/host-daemon-paths.d.ts +61 -8
  137. package/dist/modes/rpc/host-daemon-paths.d.ts.map +1 -1
  138. package/dist/modes/rpc/host-daemon-paths.js +128 -11
  139. package/dist/modes/rpc/host-daemon-paths.js.map +1 -1
  140. package/dist/modes/rpc/host-daemon-registration.d.ts +11 -1
  141. package/dist/modes/rpc/host-daemon-registration.d.ts.map +1 -1
  142. package/dist/modes/rpc/host-daemon-registration.js +28 -15
  143. package/dist/modes/rpc/host-daemon-registration.js.map +1 -1
  144. package/dist/modes/rpc/host-endpoints.d.ts +12 -0
  145. package/dist/modes/rpc/host-endpoints.d.ts.map +1 -0
  146. package/dist/modes/rpc/host-endpoints.js +59 -0
  147. package/dist/modes/rpc/host-endpoints.js.map +1 -0
  148. package/dist/modes/rpc/host-ensure-lock.d.ts +11 -0
  149. package/dist/modes/rpc/host-ensure-lock.d.ts.map +1 -0
  150. package/dist/modes/rpc/host-ensure-lock.js +49 -0
  151. package/dist/modes/rpc/host-ensure-lock.js.map +1 -0
  152. package/dist/modes/rpc/host-ensure.d.ts +7 -0
  153. package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
  154. package/dist/modes/rpc/host-ensure.js +42 -65
  155. package/dist/modes/rpc/host-ensure.js.map +1 -1
  156. package/dist/modes/rpc/host-gc-evidence.d.ts +18 -0
  157. package/dist/modes/rpc/host-gc-evidence.d.ts.map +1 -0
  158. package/dist/modes/rpc/host-gc-evidence.js +128 -0
  159. package/dist/modes/rpc/host-gc-evidence.js.map +1 -0
  160. package/dist/modes/rpc/host-gc.d.ts +28 -0
  161. package/dist/modes/rpc/host-gc.d.ts.map +1 -0
  162. package/dist/modes/rpc/host-gc.js +110 -0
  163. package/dist/modes/rpc/host-gc.js.map +1 -0
  164. package/dist/modes/rpc/host-generations.d.ts +10 -2
  165. package/dist/modes/rpc/host-generations.d.ts.map +1 -1
  166. package/dist/modes/rpc/host-generations.js +12 -7
  167. package/dist/modes/rpc/host-generations.js.map +1 -1
  168. package/dist/modes/rpc/host-handoff.d.ts +6 -0
  169. package/dist/modes/rpc/host-handoff.d.ts.map +1 -1
  170. package/dist/modes/rpc/host-handoff.js +31 -4
  171. package/dist/modes/rpc/host-handoff.js.map +1 -1
  172. package/dist/modes/rpc/host-lifecycle-policy.d.ts +53 -0
  173. package/dist/modes/rpc/host-lifecycle-policy.d.ts.map +1 -0
  174. package/dist/modes/rpc/host-lifecycle-policy.js +64 -0
  175. package/dist/modes/rpc/host-lifecycle-policy.js.map +1 -0
  176. package/dist/modes/rpc/host-lifecycle.d.ts +1 -44
  177. package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
  178. package/dist/modes/rpc/host-lifecycle.js +23 -85
  179. package/dist/modes/rpc/host-lifecycle.js.map +1 -1
  180. package/dist/modes/rpc/host-observe-request.d.ts +17 -0
  181. package/dist/modes/rpc/host-observe-request.d.ts.map +1 -0
  182. package/dist/modes/rpc/host-observe-request.js +30 -0
  183. package/dist/modes/rpc/host-observe-request.js.map +1 -0
  184. package/dist/modes/rpc/host-probe.d.ts +12 -0
  185. package/dist/modes/rpc/host-probe.d.ts.map +1 -1
  186. package/dist/modes/rpc/host-probe.js +28 -1
  187. package/dist/modes/rpc/host-probe.js.map +1 -1
  188. package/dist/modes/rpc/host-protocol-info.d.ts +2 -0
  189. package/dist/modes/rpc/host-protocol-info.d.ts.map +1 -1
  190. package/dist/modes/rpc/host-protocol-info.js +1 -0
  191. package/dist/modes/rpc/host-protocol-info.js.map +1 -1
  192. package/dist/modes/rpc/host-readiness.d.ts +22 -0
  193. package/dist/modes/rpc/host-readiness.d.ts.map +1 -0
  194. package/dist/modes/rpc/host-readiness.js +44 -0
  195. package/dist/modes/rpc/host-readiness.js.map +1 -0
  196. package/dist/modes/rpc/host-runner.d.ts +13 -0
  197. package/dist/modes/rpc/host-runner.d.ts.map +1 -1
  198. package/dist/modes/rpc/host-runner.js +23 -4
  199. package/dist/modes/rpc/host-runner.js.map +1 -1
  200. package/dist/modes/rpc/host-status-all.d.ts +31 -0
  201. package/dist/modes/rpc/host-status-all.d.ts.map +1 -0
  202. package/dist/modes/rpc/host-status-all.js +78 -0
  203. package/dist/modes/rpc/host-status-all.js.map +1 -0
  204. package/dist/modes/rpc/host-status-rows.d.ts +35 -0
  205. package/dist/modes/rpc/host-status-rows.d.ts.map +1 -0
  206. package/dist/modes/rpc/host-status-rows.js +42 -0
  207. package/dist/modes/rpc/host-status-rows.js.map +1 -0
  208. package/dist/modes/rpc/host-status.d.ts +32 -1
  209. package/dist/modes/rpc/host-status.d.ts.map +1 -1
  210. package/dist/modes/rpc/host-status.js +33 -27
  211. package/dist/modes/rpc/host-status.js.map +1 -1
  212. package/dist/modes/rpc/host-successor.d.ts +3 -1
  213. package/dist/modes/rpc/host-successor.d.ts.map +1 -1
  214. package/dist/modes/rpc/host-successor.js +105 -42
  215. package/dist/modes/rpc/host-successor.js.map +1 -1
  216. package/dist/modes/rpc/host-watchdog.d.ts +6 -0
  217. package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
  218. package/dist/modes/rpc/host-watchdog.js +12 -13
  219. package/dist/modes/rpc/host-watchdog.js.map +1 -1
  220. package/dist/modes/rpc/loop-lag-watchdog.d.ts +16 -0
  221. package/dist/modes/rpc/loop-lag-watchdog.d.ts.map +1 -1
  222. package/dist/modes/rpc/loop-lag-watchdog.js +34 -2
  223. package/dist/modes/rpc/loop-lag-watchdog.js.map +1 -1
  224. package/dist/modes/rpc/multi-session-host.d.ts +2 -1
  225. package/dist/modes/rpc/multi-session-host.d.ts.map +1 -1
  226. package/dist/modes/rpc/multi-session-host.js +33 -6
  227. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  228. package/dist/modes/rpc/rpc-mode.d.ts +2 -2
  229. package/dist/modes/rpc/rpc-mode.js +2 -2
  230. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  231. package/dist/modes/rpc/rpc-types.d.ts +16 -0
  232. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  233. package/dist/modes/rpc/rpc-types.js.map +1 -1
  234. package/dist/modes/rpc/session-command-router.d.ts +9 -1
  235. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  236. package/dist/modes/rpc/session-command-router.js +4 -1
  237. package/dist/modes/rpc/session-command-router.js.map +1 -1
  238. package/dist/modes/rpc/session-path-key.d.ts +3 -1
  239. package/dist/modes/rpc/session-path-key.d.ts.map +1 -1
  240. package/dist/modes/rpc/session-path-key.js +3 -1
  241. package/dist/modes/rpc/session-path-key.js.map +1 -1
  242. package/docs/rpc.md +186 -19
  243. package/docs/settings.md +25 -1
  244. package/docs/usage.md +5 -2
  245. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +12 -0
  246. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  247. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  248. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  249. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  250. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.d.ts +6 -0
  251. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.d.ts.map +1 -1
  252. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.js +36 -0
  253. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.js.map +1 -1
  254. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts +1 -0
  255. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
  256. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +14 -1
  257. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
  258. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  259. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  260. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  261. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  262. package/package.json +7 -7
  263. package/dist/bundle/chunks/chunk-3DEXCYNN.js +0 -135
  264. package/dist/bundle/chunks/chunk-5X4KSPK5.js +0 -12
  265. package/dist/bundle/chunks/chunk-KMD5ZONU.js +0 -5
  266. package/dist/bundle/chunks/chunk-PEHZYGYA.js +0 -8
  267. package/dist/bundle/chunks/chunk-PRC5XIIH.js +0 -2
  268. package/dist/bundle/chunks/chunk-QKY33652.js +0 -18
  269. package/dist/bundle/chunks/interactive-host-runtime-TIFT2SKR.js +0 -4
  270. package/dist/bundle/chunks/multi-session-host-GRHKB7Q2.js +0 -21
  271. package/dist/bundle/chunks/session-picker-FTFCQYHH.js +0 -2
@@ -0,0 +1,53 @@
1
+ /**
2
+ * The lifecycle supervisor's policy half: which cold-start mode and idle-exit window apply
3
+ * (`resolveHostPolicy`, environment over settings.json over the defaults), and the pure decision
4
+ * core that turns the current activity snapshot into active / idle / exit (`IdleExitDecider`).
5
+ *
6
+ * Nothing here touches a socket, a process or the filesystem; `host-lifecycle.ts` owns all of that
7
+ * and re-exports these names so every existing importer keeps resolving them at their original home.
8
+ */
9
+ export type HostColdStart = "transient" | "persistent";
10
+ /** Environment override for the cold-start policy: `transient` or `persistent`. */
11
+ export declare const HOST_COLD_START_ENV = "SENPI_RPC_HOST_COLD_START";
12
+ /** Environment override for the idle-exit window in milliseconds. */
13
+ export declare const HOST_IDLE_EXIT_MS_ENV = "SENPI_RPC_HOST_IDLE_EXIT_MS";
14
+ /** Default idle-exit window: 15 minutes of continuous no-connection, no-turn idle. */
15
+ export declare const DEFAULT_HOST_IDLE_EXIT_MS: number;
16
+ /** Soft handoff deadline: rescan and report, never interrupt turns or in-flight requests. */
17
+ export declare const HANDOFF_GRACE_MS_ENV = "SENPI_RPC_HANDOFF_GRACE_MS";
18
+ export declare const DEFAULT_HANDOFF_GRACE_MS: number;
19
+ /** The policy fields ensureHost() records in rpc-host-daemon/settings.json. */
20
+ export interface HostLifecyclePolicyInput {
21
+ readonly coldStart?: HostColdStart;
22
+ readonly idleExitMs?: number;
23
+ }
24
+ export interface HostLifecyclePolicy {
25
+ readonly coldStart: HostColdStart;
26
+ readonly idleExitMs: number;
27
+ }
28
+ export declare function parseColdStart(value: string | undefined): HostColdStart | undefined;
29
+ export declare function parseIdleExitMs(value: string | undefined): number | undefined;
30
+ /**
31
+ * Resolves the effective host policy. Precedence: environment overrides beat
32
+ * settings.json, which beats the documented defaults (transient, 15 minutes).
33
+ * Invalid values at either source fall through to the next source.
34
+ */
35
+ export declare function resolveHostPolicy(settings: unknown, env: Readonly<Record<string, string | undefined>>): HostLifecyclePolicy;
36
+ export interface HostActivity {
37
+ readonly connections: number;
38
+ readonly activeTurns: number;
39
+ }
40
+ export type IdleExitDecision = "active" | "idle" | "exit";
41
+ /**
42
+ * Pure idle-window decision core. `update()` must be called with the CURRENT
43
+ * activity state; the window only counts continuously idle time and any
44
+ * activity resets it, so a busy host can never cross the threshold.
45
+ */
46
+ export declare class IdleExitDecider {
47
+ private idleSince;
48
+ private readonly now;
49
+ readonly idleExitMs: number;
50
+ constructor(idleExitMs: number, now?: () => number);
51
+ update(activity: HostActivity): IdleExitDecision;
52
+ }
53
+ //# sourceMappingURL=host-lifecycle-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-lifecycle-policy.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/host-lifecycle-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,YAAY,CAAC;AAEvD,mFAAmF;AACnF,eAAO,MAAM,mBAAmB,8BAA8B,CAAC;AAC/D,qEAAqE;AACrE,eAAO,MAAM,qBAAqB,gCAAgC,CAAC;AACnE,sFAAsF;AACtF,eAAO,MAAM,yBAAyB,QAAc,CAAC;AACrD,6FAA6F;AAC7F,eAAO,MAAM,oBAAoB,+BAA+B,CAAC;AACjE,eAAO,MAAM,wBAAwB,QAAc,CAAC;AAEpD,+EAA+E;AAC/E,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC;IACnC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAEnF;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAI7E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAChC,QAAQ,EAAE,OAAO,EACjB,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GAC/C,mBAAmB,CASrB;AAED,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAE1D;;;;GAIG;AACH,qBAAa,eAAe;IAC3B,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B,YAAY,UAAU,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,MAAiB,EAG3D;IAED,MAAM,CAAC,QAAQ,EAAE,YAAY,GAAG,gBAAgB,CAa/C;CACD"}
@@ -0,0 +1,64 @@
1
+ /** Environment override for the cold-start policy: `transient` or `persistent`. */
2
+ export const HOST_COLD_START_ENV = "SENPI_RPC_HOST_COLD_START";
3
+ /** Environment override for the idle-exit window in milliseconds. */
4
+ export const HOST_IDLE_EXIT_MS_ENV = "SENPI_RPC_HOST_IDLE_EXIT_MS";
5
+ /** Default idle-exit window: 15 minutes of continuous no-connection, no-turn idle. */
6
+ export const DEFAULT_HOST_IDLE_EXIT_MS = 15 * 60_000;
7
+ /** Soft handoff deadline: rescan and report, never interrupt turns or in-flight requests. */
8
+ export const HANDOFF_GRACE_MS_ENV = "SENPI_RPC_HANDOFF_GRACE_MS";
9
+ export const DEFAULT_HANDOFF_GRACE_MS = 10 * 60_000;
10
+ export function parseColdStart(value) {
11
+ return value === "transient" || value === "persistent" ? value : undefined;
12
+ }
13
+ export function parseIdleExitMs(value) {
14
+ if (value === undefined || !/^\d+$/.test(value.trim()))
15
+ return undefined;
16
+ const parsed = Number(value.trim());
17
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
18
+ }
19
+ /**
20
+ * Resolves the effective host policy. Precedence: environment overrides beat
21
+ * settings.json, which beats the documented defaults (transient, 15 minutes).
22
+ * Invalid values at either source fall through to the next source.
23
+ */
24
+ export function resolveHostPolicy(settings, env) {
25
+ const record = isRecord(settings) ? settings : {};
26
+ const coldStart = parseColdStart(env[HOST_COLD_START_ENV]) ?? parseColdStart(asOptionalString(record.coldStart)) ?? "transient";
27
+ const idleExitMs = parseIdleExitMs(env[HOST_IDLE_EXIT_MS_ENV]) ??
28
+ parseIdleExitMs(asOptionalString(record.idleExitMs)) ??
29
+ DEFAULT_HOST_IDLE_EXIT_MS;
30
+ return { coldStart, idleExitMs };
31
+ }
32
+ /**
33
+ * Pure idle-window decision core. `update()` must be called with the CURRENT
34
+ * activity state; the window only counts continuously idle time and any
35
+ * activity resets it, so a busy host can never cross the threshold.
36
+ */
37
+ export class IdleExitDecider {
38
+ constructor(idleExitMs, now = Date.now) {
39
+ this.idleExitMs = idleExitMs;
40
+ this.now = now;
41
+ }
42
+ update(activity) {
43
+ // Any attachment or active turn both holds the host open and resets the
44
+ // window, so only CONTINUOUS idle can ever cross the threshold.
45
+ if (activity.connections > 0 || activity.activeTurns > 0) {
46
+ this.idleSince = undefined;
47
+ return "active";
48
+ }
49
+ if (this.idleExitMs === Number.POSITIVE_INFINITY)
50
+ return "idle";
51
+ if (this.idleSince === undefined) {
52
+ this.idleSince = this.now();
53
+ return "idle";
54
+ }
55
+ return this.now() - this.idleSince >= this.idleExitMs ? "exit" : "idle";
56
+ }
57
+ }
58
+ function isRecord(value) {
59
+ return typeof value === "object" && value !== null && !Array.isArray(value);
60
+ }
61
+ function asOptionalString(value) {
62
+ return typeof value === "string" ? value : typeof value === "number" ? String(value) : undefined;
63
+ }
64
+ //# sourceMappingURL=host-lifecycle-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-lifecycle-policy.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-lifecycle-policy.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The lifecycle supervisor's policy half: which cold-start mode and idle-exit window apply\n * (`resolveHostPolicy`, environment over settings.json over the defaults), and the pure decision\n * core that turns the current activity snapshot into active / idle / exit (`IdleExitDecider`).\n *\n * Nothing here touches a socket, a process or the filesystem; `host-lifecycle.ts` owns all of that\n * and re-exports these names so every existing importer keeps resolving them at their original home.\n */\nexport type HostColdStart = \"transient\" | \"persistent\";\n\n/** Environment override for the cold-start policy: `transient` or `persistent`. */\nexport const HOST_COLD_START_ENV = \"SENPI_RPC_HOST_COLD_START\";\n/** Environment override for the idle-exit window in milliseconds. */\nexport const HOST_IDLE_EXIT_MS_ENV = \"SENPI_RPC_HOST_IDLE_EXIT_MS\";\n/** Default idle-exit window: 15 minutes of continuous no-connection, no-turn idle. */\nexport const DEFAULT_HOST_IDLE_EXIT_MS = 15 * 60_000;\n/** Soft handoff deadline: rescan and report, never interrupt turns or in-flight requests. */\nexport const HANDOFF_GRACE_MS_ENV = \"SENPI_RPC_HANDOFF_GRACE_MS\";\nexport const DEFAULT_HANDOFF_GRACE_MS = 10 * 60_000;\n\n/** The policy fields ensureHost() records in rpc-host-daemon/settings.json. */\nexport interface HostLifecyclePolicyInput {\n\treadonly coldStart?: HostColdStart;\n\treadonly idleExitMs?: number;\n}\n\nexport interface HostLifecyclePolicy {\n\treadonly coldStart: HostColdStart;\n\treadonly idleExitMs: number;\n}\n\nexport function parseColdStart(value: string | undefined): HostColdStart | undefined {\n\treturn value === \"transient\" || value === \"persistent\" ? value : undefined;\n}\n\nexport function parseIdleExitMs(value: string | undefined): number | undefined {\n\tif (value === undefined || !/^\\d+$/.test(value.trim())) return undefined;\n\tconst parsed = Number(value.trim());\n\treturn Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;\n}\n\n/**\n * Resolves the effective host policy. Precedence: environment overrides beat\n * settings.json, which beats the documented defaults (transient, 15 minutes).\n * Invalid values at either source fall through to the next source.\n */\nexport function resolveHostPolicy(\n\tsettings: unknown,\n\tenv: Readonly<Record<string, string | undefined>>,\n): HostLifecyclePolicy {\n\tconst record = isRecord(settings) ? settings : {};\n\tconst coldStart =\n\t\tparseColdStart(env[HOST_COLD_START_ENV]) ?? parseColdStart(asOptionalString(record.coldStart)) ?? \"transient\";\n\tconst idleExitMs =\n\t\tparseIdleExitMs(env[HOST_IDLE_EXIT_MS_ENV]) ??\n\t\tparseIdleExitMs(asOptionalString(record.idleExitMs)) ??\n\t\tDEFAULT_HOST_IDLE_EXIT_MS;\n\treturn { coldStart, idleExitMs };\n}\n\nexport interface HostActivity {\n\treadonly connections: number;\n\treadonly activeTurns: number;\n}\n\nexport type IdleExitDecision = \"active\" | \"idle\" | \"exit\";\n\n/**\n * Pure idle-window decision core. `update()` must be called with the CURRENT\n * activity state; the window only counts continuously idle time and any\n * activity resets it, so a busy host can never cross the threshold.\n */\nexport class IdleExitDecider {\n\tprivate idleSince: number | undefined;\n\tprivate readonly now: () => number;\n\treadonly idleExitMs: number;\n\n\tconstructor(idleExitMs: number, now: () => number = Date.now) {\n\t\tthis.idleExitMs = idleExitMs;\n\t\tthis.now = now;\n\t}\n\n\tupdate(activity: HostActivity): IdleExitDecision {\n\t\t// Any attachment or active turn both holds the host open and resets the\n\t\t// window, so only CONTINUOUS idle can ever cross the threshold.\n\t\tif (activity.connections > 0 || activity.activeTurns > 0) {\n\t\t\tthis.idleSince = undefined;\n\t\t\treturn \"active\";\n\t\t}\n\t\tif (this.idleExitMs === Number.POSITIVE_INFINITY) return \"idle\";\n\t\tif (this.idleSince === undefined) {\n\t\t\tthis.idleSince = this.now();\n\t\t\treturn \"idle\";\n\t\t}\n\t\treturn this.now() - this.idleSince >= this.idleExitMs ? \"exit\" : \"idle\";\n\t}\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction asOptionalString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" ? value : typeof value === \"number\" ? String(value) : undefined;\n}\n"]}
@@ -1,25 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  export { classifyChildExit } from "./host-child-exit.ts";
3
+ export { DEFAULT_HANDOFF_GRACE_MS, DEFAULT_HOST_IDLE_EXIT_MS, HANDOFF_GRACE_MS_ENV, HOST_COLD_START_ENV, HOST_IDLE_EXIT_MS_ENV, type HostActivity, type HostColdStart, type HostLifecyclePolicy, type HostLifecyclePolicyInput, IdleExitDecider, type IdleExitDecision, parseColdStart, parseIdleExitMs, resolveHostPolicy, } from "./host-lifecycle-policy.ts";
3
4
  import { type SocketFileIdentity } from "./socket-ownership.ts";
4
- export type HostColdStart = "transient" | "persistent";
5
- /** Environment override for the cold-start policy: `transient` or `persistent`. */
6
- export declare const HOST_COLD_START_ENV = "SENPI_RPC_HOST_COLD_START";
7
- /** Environment override for the idle-exit window in milliseconds. */
8
- export declare const HOST_IDLE_EXIT_MS_ENV = "SENPI_RPC_HOST_IDLE_EXIT_MS";
9
- /** Default idle-exit window: 15 minutes of continuous no-connection, no-turn idle. */
10
- export declare const DEFAULT_HOST_IDLE_EXIT_MS: number;
11
- /** Soft handoff deadline: rescan and report, never interrupt turns or in-flight requests. */
12
- export declare const HANDOFF_GRACE_MS_ENV = "SENPI_RPC_HANDOFF_GRACE_MS";
13
- export declare const DEFAULT_HANDOFF_GRACE_MS: number;
14
- /** The policy fields ensureHost() records in rpc-host-daemon/settings.json. */
15
- export interface HostLifecyclePolicyInput {
16
- readonly coldStart?: HostColdStart;
17
- readonly idleExitMs?: number;
18
- }
19
- export interface HostLifecyclePolicy {
20
- readonly coldStart: HostColdStart;
21
- readonly idleExitMs: number;
22
- }
23
5
  /**
24
6
  * The internal hop must stay short enough for sun_path (104 bytes on macOS)
25
7
  * regardless of where the public socket lives, and private against other local
@@ -34,31 +16,6 @@ export declare function createInternalSocketPath(baseDir?: string, platform?: No
34
16
  dir?: string;
35
17
  secretPath?: string;
36
18
  }>;
37
- export declare function parseColdStart(value: string | undefined): HostColdStart | undefined;
38
- export declare function parseIdleExitMs(value: string | undefined): number | undefined;
39
- /**
40
- * Resolves the effective host policy. Precedence: environment overrides beat
41
- * settings.json, which beats the documented defaults (transient, 15 minutes).
42
- * Invalid values at either source fall through to the next source.
43
- */
44
- export declare function resolveHostPolicy(settings: unknown, env: Readonly<Record<string, string | undefined>>): HostLifecyclePolicy;
45
- export interface HostActivity {
46
- readonly connections: number;
47
- readonly activeTurns: number;
48
- }
49
- export type IdleExitDecision = "active" | "idle" | "exit";
50
- /**
51
- * Pure idle-window decision core. `update()` must be called with the CURRENT
52
- * activity state; the window only counts continuously idle time and any
53
- * activity resets it, so a busy host can never cross the threshold.
54
- */
55
- export declare class IdleExitDecider {
56
- private idleSince;
57
- private readonly now;
58
- readonly idleExitMs: number;
59
- constructor(idleExitMs: number, now?: () => number);
60
- update(activity: HostActivity): IdleExitDecision;
61
- }
62
19
  export interface SupervisorLaunch {
63
20
  readonly socket: string;
64
21
  readonly hostArgs: readonly string[];
@@ -1 +1 @@
1
- {"version":3,"file":"host-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/host-lifecycle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"host-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/host-lifecycle.ts"],"names":[],"mappings":";AAgEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAIzD,OAAO,EACN,wBAAwB,EACxB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,eAAe,EACf,KAAK,gBAAgB,EACrB,cAAc,EACd,eAAe,EACf,iBAAiB,GACjB,MAAM,4BAA4B,CAAC;AAgBpC,OAAO,EAGN,KAAK,kBAAkB,EAKvB,MAAM,uBAAuB,CAAC;AAuB/B;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,SAAW,EAClB,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC1C,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAsBhE;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,kEAAkE;IAClE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,qFAAqF;IACrF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,kBAAkB,CAAC;CAC9C;AAED,gGAAgG;AAChG,eAAO,MAAM,wBAAwB,mCAAmC,CAAC;AASzE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CASjG;AAED,sGAAsG;AACtG,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,gBAAgB,GAAG,SAAS,CA4CzF;AAQD,0FAA0F;AAC1F,wBAAgB,kBAAkB,IAAI,MAAM,CAW3C;AA4BD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACrC,MAAM,EAAE,gBAAgB,EACxB,cAAc,EAAE,MAAM,EACtB,QAAQ,GAAE,OAAqB,GAC7B;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAmBrC;AAWD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CACnC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,EAC3C,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC1C;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAUrD;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4W/E"}
@@ -50,9 +50,15 @@ import { fileURLToPath } from "node:url";
50
50
  import { getAgentDir, isBunBinary, isBundledNode } from "../../config.js";
51
51
  import { processIsLive, readProcessStartTime } from "../app-server/daemon/process.js";
52
52
  import { classifyChildExit, noteChildExit } from "./host-child-exit.js";
53
+ import { ClientOccupancy } from "./host-client-occupancy.js";
54
+ import { DEFAULT_HANDOFF_GRACE_MS, HANDOFF_GRACE_MS_ENV, IdleExitDecider, parseIdleExitMs, resolveHostPolicy, } from "./host-lifecycle-policy.js";
53
55
  // The exit verdict moved to ./host-child-exit.ts with the crash recording it now feeds; it stays
54
56
  // exported from here so every existing importer keeps resolving it at its original home.
55
57
  export { classifyChildExit } from "./host-child-exit.js";
58
+ // The cold-start/idle-exit policy and its decision core live in ./host-lifecycle-policy.ts; they stay
59
+ // exported from here so every existing importer keeps resolving them at their original home.
60
+ export { DEFAULT_HANDOFF_GRACE_MS, DEFAULT_HOST_IDLE_EXIT_MS, HANDOFF_GRACE_MS_ENV, HOST_COLD_START_ENV, HOST_IDLE_EXIT_MS_ENV, IdleExitDecider, parseColdStart, parseIdleExitMs, resolveHostPolicy, } from "./host-lifecycle-policy.js";
61
+ import { hostCrashCleanupPaths } from "./host-cleanup-paths.js";
56
62
  import { createHostDaemonPaths, generationPaths, HOST_DAEMON_DIR_ENV } from "./host-daemon-paths.js";
57
63
  import { releaseGeneration } from "./host-daemon-registration.js";
58
64
  import { watchForSupersession } from "./host-supersession.js";
@@ -62,15 +68,6 @@ import { activeTurnsForIdleDecision, createObserverLink } from "./observer-link.
62
68
  import { HOST_INSTANCE_ID_ENV } from "./protocol-identity.js";
63
69
  import { MAX_SOCKET_PATH_BYTES, PUBLIC_SOCKET_IDENTITY_FILE, shieldSocketDuringClose, statSocketIdentity, unlinkOwnedSocket, writeSocketIdentityFile, } from "./socket-ownership.js";
64
70
  import { authenticateSocket, createSocketSecret, ensureSocketSecret, resolveSocketTransportAddress, SOCKET_SECRET_FILE_ENV, sendSocketHandshake, socketSecretPath, } from "./socket-transport.js";
65
- /** Environment override for the cold-start policy: `transient` or `persistent`. */
66
- export const HOST_COLD_START_ENV = "SENPI_RPC_HOST_COLD_START";
67
- /** Environment override for the idle-exit window in milliseconds. */
68
- export const HOST_IDLE_EXIT_MS_ENV = "SENPI_RPC_HOST_IDLE_EXIT_MS";
69
- /** Default idle-exit window: 15 minutes of continuous no-connection, no-turn idle. */
70
- export const DEFAULT_HOST_IDLE_EXIT_MS = 15 * 60_000;
71
- /** Soft handoff deadline: rescan and report, never interrupt turns or in-flight requests. */
72
- export const HANDOFF_GRACE_MS_ENV = "SENPI_RPC_HANDOFF_GRACE_MS";
73
- export const DEFAULT_HANDOFF_GRACE_MS = 10 * 60_000;
74
71
  const CHILD_STOP_TIMEOUT_MS = 5_000;
75
72
  /** Win32 named-pipe shutdown can leave supervisor handles live after close starts. */
76
73
  const WINDOWS_SUPERVISOR_SHUTDOWN_HARD_EXIT_MS = 2_000;
@@ -109,54 +106,6 @@ export async function createInternalSocketPath(baseDir = tmpdir(), platform = pr
109
106
  }), { mode: 0o600 });
110
107
  return { socket: join(dir, "host.sock"), dir, secretPath: join(dir, ".secret") };
111
108
  }
112
- export function parseColdStart(value) {
113
- return value === "transient" || value === "persistent" ? value : undefined;
114
- }
115
- export function parseIdleExitMs(value) {
116
- if (value === undefined || !/^\d+$/.test(value.trim()))
117
- return undefined;
118
- const parsed = Number(value.trim());
119
- return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
120
- }
121
- /**
122
- * Resolves the effective host policy. Precedence: environment overrides beat
123
- * settings.json, which beats the documented defaults (transient, 15 minutes).
124
- * Invalid values at either source fall through to the next source.
125
- */
126
- export function resolveHostPolicy(settings, env) {
127
- const record = isRecord(settings) ? settings : {};
128
- const coldStart = parseColdStart(env[HOST_COLD_START_ENV]) ?? parseColdStart(asOptionalString(record.coldStart)) ?? "transient";
129
- const idleExitMs = parseIdleExitMs(env[HOST_IDLE_EXIT_MS_ENV]) ??
130
- parseIdleExitMs(asOptionalString(record.idleExitMs)) ??
131
- DEFAULT_HOST_IDLE_EXIT_MS;
132
- return { coldStart, idleExitMs };
133
- }
134
- /**
135
- * Pure idle-window decision core. `update()` must be called with the CURRENT
136
- * activity state; the window only counts continuously idle time and any
137
- * activity resets it, so a busy host can never cross the threshold.
138
- */
139
- export class IdleExitDecider {
140
- constructor(idleExitMs, now = Date.now) {
141
- this.idleExitMs = idleExitMs;
142
- this.now = now;
143
- }
144
- update(activity) {
145
- // Any attachment or active turn both holds the host open and resets the
146
- // window, so only CONTINUOUS idle can ever cross the threshold.
147
- if (activity.connections > 0 || activity.activeTurns > 0) {
148
- this.idleSince = undefined;
149
- return "active";
150
- }
151
- if (this.idleExitMs === Number.POSITIVE_INFINITY)
152
- return "idle";
153
- if (this.idleSince === undefined) {
154
- this.idleSince = this.now();
155
- return "idle";
156
- }
157
- return this.now() - this.idleSince >= this.idleExitMs ? "exit" : "idle";
158
- }
159
- }
160
109
  /** Hidden internal launch route: wire-invisible, never advertised by the public CLI surface. */
161
110
  export const INTERNAL_SUPERVISOR_FLAG = "--internal-rpc-host-supervisor";
162
111
  /**
@@ -362,7 +311,7 @@ export async function runHostSupervisor(launch) {
362
311
  const internalSocket = internal.socket;
363
312
  const internalSecretPath = internal.secretPath ?? socketSecretPath(internalSocket);
364
313
  const internalSecret = process.platform === "win32" ? await createSocketSecret(internalSecretPath) : undefined;
365
- const clientSockets = new Set();
314
+ const clients = new ClientOccupancy(() => decider.update(currentActivity()));
366
315
  const busySessions = new Map();
367
316
  // Declared before anything that can reach `currentActivity()`. A client accepted during startup
368
317
  // asks for the activity snapshot, and a `const` read before its initializer runs is a
@@ -413,18 +362,14 @@ export async function runHostSupervisor(launch) {
413
362
  [HOST_WATCH_PPID_ENV]: String(process.pid),
414
363
  ...(internal.dir ? { [HOST_SCRATCH_DIR_ENV]: internal.dir } : {}),
415
364
  ...(internalSecret ? { [SOCKET_SECRET_FILE_ENV]: internalSecretPath } : {}),
416
- [HOST_CLEANUP_PATHS_ENV]: [
417
- // A successor writes no registration of its own until the ensure that spawned it does,
418
- // and the files under these paths still describe the generation being replaced.
419
- ...(successor ? [] : [paths.pointerFile, generation.pidFile, paths.settingsFile]),
420
- // POSIX public sockets are removed ownership-checked by the host child
421
- // (token: the scratch-directory sidecar plus HOST_PUBLIC_SOCKET_ENV),
422
- // never by path from a crash-path cleanup: a blind removal here would
423
- // unlink a newer host's freshly published entry after a takeover.
424
- // Windows named pipes have no filesystem entry to own, so they stay
425
- // listed for the crash-path cleanup.
426
- ...(process.platform === "win32" ? [publicSocket] : []),
427
- ].join("\n"),
365
+ [HOST_CLEANUP_PATHS_ENV]: hostCrashCleanupPaths({
366
+ pointerFile: paths.pointerFile,
367
+ generationPidFile: generation.pidFile,
368
+ settingsFile: paths.settingsFile,
369
+ publicSocket,
370
+ successor: Boolean(successor),
371
+ platform: process.platform,
372
+ }).join("\n"),
428
373
  ...(process.platform === "win32" ? {} : { [HOST_PUBLIC_SOCKET_ENV]: publicSocket }),
429
374
  },
430
375
  // Slot 3 is the lifetime pipe: "pipe" gives the child a read end it can
@@ -462,12 +407,12 @@ export async function runHostSupervisor(launch) {
462
407
  const internal = createConnection(resolveSocketTransportAddress(internalSocket, process.platform, internalSecret));
463
408
  if (internalSecret)
464
409
  sendSocketHandshake(internal, internalSecret);
465
- clientSockets.add(client);
466
- // A readiness exchange can begin and end between ticks. Record the
467
- // attachment now, before a later tick can reuse the preceding idle window.
468
- decider.update(currentActivity());
410
+ // A readiness exchange can begin and end between ticks: the client is recorded the
411
+ // moment its first request line arrives, before a later tick can reuse the preceding
412
+ // idle window. An observing read (`status`) is never recorded (host-client-occupancy.ts).
413
+ clients.admit(client);
469
414
  const detach = () => {
470
- clientSockets.delete(client);
415
+ clients.release(client);
471
416
  decider.update(currentActivity());
472
417
  internal.destroy();
473
418
  client.destroy();
@@ -496,12 +441,12 @@ export async function runHostSupervisor(launch) {
496
441
  const decider = new IdleExitDecider(policy.coldStart === "persistent" ? Number.POSITIVE_INFINITY : policy.idleExitMs);
497
442
  const tickIntervalMs = Math.max(20, Math.min(1_000, policy.idleExitMs / 4));
498
443
  const ticker = setInterval(() => {
499
- if (!draining && decider.update(currentActivity()) === "exit")
444
+ if (!draining && decider.update(currentActivity()) === "exit" && clients.unclassifiedCount === 0)
500
445
  void shutdown("idle", 0);
501
446
  }, tickIntervalMs);
502
447
  function currentActivity() {
503
448
  return {
504
- connections: clientSockets.size,
449
+ connections: clients.attachedCount,
505
450
  activeTurns: activeTurnsForIdleDecision({
506
451
  healthy: observerLink.healthy(),
507
452
  unhealthySince: observerLink.unhealthySince(),
@@ -561,8 +506,7 @@ export async function runHostSupervisor(launch) {
561
506
  : undefined;
562
507
  try {
563
508
  writeStderrLine(`senpi rpc host supervisor: ${reason} shutdown`);
564
- for (const client of clientSockets)
565
- client.destroy();
509
+ clients.destroyAll();
566
510
  // libuv unlinks the bound NAME when the listening handle closes - which
567
511
  // would delete a newer host's entry renamed over this path. Shield the
568
512
  // current entry for the close, then let the ownership check decide. A drained
@@ -913,12 +857,6 @@ function errorMessage(cause) {
913
857
  function isNodeErrorCode(cause, code) {
914
858
  return cause instanceof Error && "code" in cause && cause.code === code;
915
859
  }
916
- function isRecord(value) {
917
- return typeof value === "object" && value !== null && !Array.isArray(value);
918
- }
919
- function asOptionalString(value) {
920
- return typeof value === "string" ? value : typeof value === "number" ? String(value) : undefined;
921
- }
922
860
  function isEntryScript() {
923
861
  const entry = process.argv[1];
924
862
  if (!entry)