@code-yeongyu/senpi 2026.9.26 → 2026.9.27-3

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 (404) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/dist/bun-runtime.d.ts +18 -2
  3. package/dist/bun-runtime.d.ts.map +1 -1
  4. package/dist/bun-runtime.js +60 -8
  5. package/dist/bun-runtime.js.map +1 -1
  6. package/dist/bundle/chunks/anthropic-messages-3FGAXYJS.js +8 -0
  7. package/dist/bundle/chunks/{app-server-command-ZRCKWY2X.js → app-server-command-ZVB35UIJ.js} +1 -1
  8. package/dist/bundle/chunks/azure-openai-responses-4SUOOZZ4.js +2 -0
  9. package/dist/bundle/chunks/bun-extension-importer-R4HXF3GD.js +14 -0
  10. package/dist/bundle/chunks/{chunk-KMJXSXRX.js → chunk-357I4IUU.js} +2 -2
  11. package/dist/bundle/chunks/chunk-3SIQIY57.js +8 -0
  12. package/dist/bundle/chunks/chunk-45SZOLCU.js +2 -0
  13. package/dist/bundle/chunks/{chunk-77ACEMYL.js → chunk-6J6RDMBY.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-BSOJHK4W.js → chunk-7FXXRPB3.js} +2 -1
  15. package/dist/bundle/chunks/{chunk-FD34HSJJ.js → chunk-C6LQNMRB.js} +1 -1
  16. package/dist/bundle/chunks/chunk-ELM77A26.js +2 -0
  17. package/dist/bundle/chunks/chunk-GZVCRV7I.js +3 -0
  18. package/dist/bundle/chunks/{chunk-TTY5BHCG.js → chunk-H2SGCPMD.js} +1 -1
  19. package/dist/bundle/chunks/chunk-KLBKUBDV.js +2 -0
  20. package/dist/bundle/chunks/chunk-L6QV7NZI.js +2 -0
  21. package/dist/bundle/chunks/{chunk-ZKZAATFM.js → chunk-LDB5UMH2.js} +2 -2
  22. package/dist/bundle/chunks/{chunk-HZAKOVHN.js → chunk-LGKKU4VM.js} +1 -1
  23. package/dist/bundle/chunks/{chunk-6Q6DGME2.js → chunk-NMDALHXH.js} +1 -1
  24. package/dist/bundle/chunks/chunk-NNILBBOH.js +18 -0
  25. package/dist/bundle/chunks/{chunk-SUQDU5KM.js → chunk-OQRUK2DK.js} +1 -1
  26. package/dist/bundle/chunks/{chunk-OZCB6BP3.js → chunk-OZN2KUPR.js} +7 -7
  27. package/dist/bundle/chunks/{chunk-X3T45623.js → chunk-QK4QKRTF.js} +1 -1
  28. package/dist/bundle/chunks/{chunk-WM5FVFAP.js → chunk-QPZVJQC5.js} +1 -1
  29. package/dist/bundle/chunks/{chunk-Y54V65MS.js → chunk-QYAO24GB.js} +1 -1
  30. package/dist/bundle/chunks/{chunk-MKAM7IRH.js → chunk-RULGEHQM.js} +1 -1
  31. package/dist/bundle/chunks/{chunk-QGC46CGK.js → chunk-RYI6XR6Z.js} +63 -58
  32. package/dist/bundle/chunks/chunk-U3VOXCGP.js +2 -0
  33. package/dist/bundle/chunks/chunk-W3D3ZYDS.js +2 -0
  34. package/dist/bundle/chunks/{chunk-UBPMXBOJ.js → chunk-XKLR7C5B.js} +1 -1
  35. package/dist/bundle/chunks/{chunk-CLZVNQW3.js → chunk-Y7EIVDHZ.js} +1 -1
  36. package/dist/bundle/chunks/chunk-ZIVYE5YK.js +2 -0
  37. package/dist/bundle/chunks/cli-main-RONHLFWZ.js +3 -0
  38. package/dist/bundle/chunks/google-generative-ai-ZTGKZTGU.js +2 -0
  39. package/dist/bundle/chunks/google-vertex-CCZ376YK.js +2 -0
  40. package/dist/bundle/chunks/help-fast-path-5LRG6FCG.js +2 -0
  41. package/dist/bundle/chunks/host-command-JVPGQR74.js +2 -0
  42. package/dist/bundle/chunks/host-lifecycle-R4HLXLBM.js +3 -0
  43. package/dist/bundle/chunks/host-lifecycle.js +4 -4
  44. package/dist/bundle/chunks/interactive-host-runtime-FES2G62G.js +4 -0
  45. package/dist/bundle/chunks/interactive-mode-45EP2FVL.js +2 -0
  46. package/dist/bundle/chunks/{list-tips-EBRNBPBM.js → list-tips-XXMRDHN6.js} +1 -1
  47. package/dist/bundle/chunks/{multi-session-host-7YX2I7UK.js → multi-session-host-EFCMD5OT.js} +6 -6
  48. package/dist/bundle/chunks/{openai-completions-4CLXDDHJ.js → openai-completions-OP6VFNUR.js} +1 -1
  49. package/dist/bundle/chunks/openai-images-6U5FCN5S.js +2 -0
  50. package/dist/bundle/chunks/openai-responses-7PWMBGYX.js +2 -0
  51. package/dist/bundle/chunks/openrouter-images-QIKWQZUD.js +2 -0
  52. package/dist/bundle/chunks/{package-manager-cli-3ISYTEL2.js → package-manager-cli-5XJS3TYM.js} +1 -1
  53. package/dist/bundle/chunks/rotation-stream-KW7PTEC4.js +2 -0
  54. package/dist/bundle/chunks/rpc-mode-ZHYECU24.js +2 -0
  55. package/dist/bundle/chunks/session-picker-QHIYVQP4.js +2 -0
  56. package/dist/bundle/chunks/session-worker.js +281 -270
  57. package/dist/bundle/chunks/{state-store-Q5EWEAFG.js → state-store-D4GGGT3J.js} +1 -1
  58. package/dist/bundle/cli.js +1 -1
  59. package/dist/bundle/index.js +1 -1
  60. package/dist/bundle/rpc-entry.js +1 -1
  61. package/dist/cli/args.d.ts +1 -0
  62. package/dist/cli/args.d.ts.map +1 -1
  63. package/dist/cli/args.js +4 -0
  64. package/dist/cli/args.js.map +1 -1
  65. package/dist/cli/continue-moved.d.ts +15 -0
  66. package/dist/cli/continue-moved.d.ts.map +1 -0
  67. package/dist/cli/continue-moved.js +26 -0
  68. package/dist/cli/continue-moved.js.map +1 -0
  69. package/dist/cli/cross-project-session.d.ts +22 -0
  70. package/dist/cli/cross-project-session.d.ts.map +1 -0
  71. package/dist/cli/cross-project-session.js +38 -0
  72. package/dist/cli/cross-project-session.js.map +1 -0
  73. package/dist/core/agent-session-runtime.d.ts +3 -0
  74. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  75. package/dist/core/agent-session-runtime.js +43 -10
  76. package/dist/core/agent-session-runtime.js.map +1 -1
  77. package/dist/core/agent-session.d.ts.map +1 -1
  78. package/dist/core/agent-session.js +19 -5
  79. package/dist/core/agent-session.js.map +1 -1
  80. package/dist/core/extensions/builtin/anthropic-subscription/session-registry-pump.d.ts.map +1 -1
  81. package/dist/core/extensions/builtin/anthropic-subscription/session-registry-pump.js +5 -1
  82. package/dist/core/extensions/builtin/anthropic-subscription/session-registry-pump.js.map +1 -1
  83. package/dist/core/extensions/builtin/anthropic-subscription/session-turn-claim.d.ts +13 -1
  84. package/dist/core/extensions/builtin/anthropic-subscription/session-turn-claim.d.ts.map +1 -1
  85. package/dist/core/extensions/builtin/anthropic-subscription/session-turn-claim.js +25 -2
  86. package/dist/core/extensions/builtin/anthropic-subscription/session-turn-claim.js.map +1 -1
  87. package/dist/core/extensions/builtin/anthropic-subscription/session-turn-types.d.ts +1 -0
  88. package/dist/core/extensions/builtin/anthropic-subscription/session-turn-types.d.ts.map +1 -1
  89. package/dist/core/extensions/builtin/anthropic-subscription/session-turn-types.js.map +1 -1
  90. package/dist/core/extensions/builtin/index.d.ts.map +1 -1
  91. package/dist/core/extensions/builtin/index.js +2 -0
  92. package/dist/core/extensions/builtin/index.js.map +1 -1
  93. package/dist/core/extensions/builtin/mcp/catalog.d.ts +3 -0
  94. package/dist/core/extensions/builtin/mcp/catalog.d.ts.map +1 -1
  95. package/dist/core/extensions/builtin/mcp/catalog.js +9 -0
  96. package/dist/core/extensions/builtin/mcp/catalog.js.map +1 -1
  97. package/dist/core/extensions/builtin/mcp/connection-types.d.ts +2 -0
  98. package/dist/core/extensions/builtin/mcp/connection-types.d.ts.map +1 -1
  99. package/dist/core/extensions/builtin/mcp/connection-types.js.map +1 -1
  100. package/dist/core/extensions/builtin/mcp/connection.d.ts +1 -1
  101. package/dist/core/extensions/builtin/mcp/connection.d.ts.map +1 -1
  102. package/dist/core/extensions/builtin/mcp/connection.js +3 -2
  103. package/dist/core/extensions/builtin/mcp/connection.js.map +1 -1
  104. package/dist/core/extensions/builtin/mcp/expose/session.d.ts +3 -0
  105. package/dist/core/extensions/builtin/mcp/expose/session.d.ts.map +1 -1
  106. package/dist/core/extensions/builtin/mcp/expose/session.js +9 -2
  107. package/dist/core/extensions/builtin/mcp/expose/session.js.map +1 -1
  108. package/dist/core/extensions/builtin/mcp/service-register.d.ts.map +1 -1
  109. package/dist/core/extensions/builtin/mcp/service-register.js +7 -1
  110. package/dist/core/extensions/builtin/mcp/service-register.js.map +1 -1
  111. package/dist/core/extensions/builtin/mcp/service-tools-changed.d.ts +18 -0
  112. package/dist/core/extensions/builtin/mcp/service-tools-changed.d.ts.map +1 -0
  113. package/dist/core/extensions/builtin/mcp/service-tools-changed.js +61 -0
  114. package/dist/core/extensions/builtin/mcp/service-tools-changed.js.map +1 -0
  115. package/dist/core/extensions/builtin/mcp/service-types.d.ts +13 -0
  116. package/dist/core/extensions/builtin/mcp/service-types.d.ts.map +1 -1
  117. package/dist/core/extensions/builtin/mcp/service-types.js.map +1 -1
  118. package/dist/core/extensions/builtin/mcp/service.d.ts.map +1 -1
  119. package/dist/core/extensions/builtin/mcp/service.js +4 -37
  120. package/dist/core/extensions/builtin/mcp/service.js.map +1 -1
  121. package/dist/core/extensions/builtin/mcp/shared-lease.js +1 -1
  122. package/dist/core/extensions/builtin/mcp/shared-lease.js.map +1 -1
  123. package/dist/core/extensions/builtin/mcp/startup-race.d.ts.map +1 -1
  124. package/dist/core/extensions/builtin/mcp/startup-race.js +11 -1
  125. package/dist/core/extensions/builtin/mcp/startup-race.js.map +1 -1
  126. package/dist/core/extensions/builtin/permission-system/index.d.ts.map +1 -1
  127. package/dist/core/extensions/builtin/permission-system/index.js +5 -2
  128. package/dist/core/extensions/builtin/permission-system/index.js.map +1 -1
  129. package/dist/core/extensions/builtin/permission-system/parsers.d.ts +5 -0
  130. package/dist/core/extensions/builtin/permission-system/parsers.d.ts.map +1 -1
  131. package/dist/core/extensions/builtin/permission-system/parsers.js +13 -0
  132. package/dist/core/extensions/builtin/permission-system/parsers.js.map +1 -1
  133. package/dist/core/extensions/builtin/repository-identity.d.ts +6 -0
  134. package/dist/core/extensions/builtin/repository-identity.d.ts.map +1 -0
  135. package/dist/core/extensions/builtin/repository-identity.js +33 -0
  136. package/dist/core/extensions/builtin/repository-identity.js.map +1 -0
  137. package/dist/core/extensions/bun-extension-importer.d.ts.map +1 -1
  138. package/dist/core/extensions/bun-extension-importer.js +10 -4
  139. package/dist/core/extensions/bun-extension-importer.js.map +1 -1
  140. package/dist/core/extensions/bun-extension-registry.js +6 -5
  141. package/dist/core/extensions/bun-extension-registry.js.map +1 -1
  142. package/dist/core/extensions/index.d.ts +1 -1
  143. package/dist/core/extensions/index.d.ts.map +1 -1
  144. package/dist/core/extensions/index.js.map +1 -1
  145. package/dist/core/extensions/kernel-prelude.d.ts +13 -0
  146. package/dist/core/extensions/kernel-prelude.d.ts.map +1 -0
  147. package/dist/core/extensions/kernel-prelude.js +38 -0
  148. package/dist/core/extensions/kernel-prelude.js.map +1 -0
  149. package/dist/core/extensions/types.d.ts +44 -2
  150. package/dist/core/extensions/types.d.ts.map +1 -1
  151. package/dist/core/extensions/types.js.map +1 -1
  152. package/dist/core/messages.d.ts.map +1 -1
  153. package/dist/core/messages.js +4 -2
  154. package/dist/core/messages.js.map +1 -1
  155. package/dist/core/model-resolver.js +1 -1
  156. package/dist/core/model-resolver.js.map +1 -1
  157. package/dist/core/moved-sessions.d.ts +10 -0
  158. package/dist/core/moved-sessions.d.ts.map +1 -0
  159. package/dist/core/moved-sessions.js +84 -0
  160. package/dist/core/moved-sessions.js.map +1 -0
  161. package/dist/core/process-crash-record.d.ts +62 -0
  162. package/dist/core/process-crash-record.d.ts.map +1 -0
  163. package/dist/core/process-crash-record.js +216 -0
  164. package/dist/core/process-crash-record.js.map +1 -0
  165. package/dist/core/provider-rejected-images.d.ts +18 -0
  166. package/dist/core/provider-rejected-images.d.ts.map +1 -0
  167. package/dist/core/provider-rejected-images.js +107 -0
  168. package/dist/core/provider-rejected-images.js.map +1 -0
  169. package/dist/core/repository-identity.d.ts +18 -0
  170. package/dist/core/repository-identity.d.ts.map +1 -0
  171. package/dist/core/repository-identity.js +75 -0
  172. package/dist/core/repository-identity.js.map +1 -0
  173. package/dist/core/resume-target.d.ts +20 -0
  174. package/dist/core/resume-target.d.ts.map +1 -0
  175. package/dist/core/resume-target.js +30 -0
  176. package/dist/core/resume-target.js.map +1 -0
  177. package/dist/core/session-discovery.d.ts.map +1 -1
  178. package/dist/core/session-discovery.js +1 -0
  179. package/dist/core/session-discovery.js.map +1 -1
  180. package/dist/core/session-hold-errors.d.ts +20 -0
  181. package/dist/core/session-hold-errors.d.ts.map +1 -0
  182. package/dist/core/session-hold-errors.js +28 -0
  183. package/dist/core/session-hold-errors.js.map +1 -0
  184. package/dist/core/session-holders.d.ts +22 -0
  185. package/dist/core/session-holders.d.ts.map +1 -0
  186. package/dist/core/session-holders.js +234 -0
  187. package/dist/core/session-holders.js.map +1 -0
  188. package/dist/core/session-manager.d.ts +5 -0
  189. package/dist/core/session-manager.d.ts.map +1 -1
  190. package/dist/core/session-manager.js.map +1 -1
  191. package/dist/core/session-rebind.d.ts +23 -0
  192. package/dist/core/session-rebind.d.ts.map +1 -0
  193. package/dist/core/session-rebind.js +127 -0
  194. package/dist/core/session-rebind.js.map +1 -0
  195. package/dist/core/session-summary-index-file.d.ts.map +1 -1
  196. package/dist/core/session-summary-index-file.js +5 -2
  197. package/dist/core/session-summary-index-file.js.map +1 -1
  198. package/dist/core/session-summary.d.ts +3 -0
  199. package/dist/core/session-summary.d.ts.map +1 -1
  200. package/dist/core/session-summary.js +7 -0
  201. package/dist/core/session-summary.js.map +1 -1
  202. package/dist/core/session-title-generator.d.ts.map +1 -1
  203. package/dist/core/session-title-generator.js +21 -3
  204. package/dist/core/session-title-generator.js.map +1 -1
  205. package/dist/core/session-title-reasoning.d.ts +14 -0
  206. package/dist/core/session-title-reasoning.d.ts.map +1 -0
  207. package/dist/core/session-title-reasoning.js +31 -0
  208. package/dist/core/session-title-reasoning.js.map +1 -0
  209. package/dist/core/tools/path-utils.d.ts.map +1 -1
  210. package/dist/core/tools/path-utils.js +13 -0
  211. package/dist/core/tools/path-utils.js.map +1 -1
  212. package/dist/index.d.ts +1 -1
  213. package/dist/index.d.ts.map +1 -1
  214. package/dist/index.js.map +1 -1
  215. package/dist/legacy-dir-copy.d.ts +9 -0
  216. package/dist/legacy-dir-copy.d.ts.map +1 -0
  217. package/dist/legacy-dir-copy.js +69 -0
  218. package/dist/legacy-dir-copy.js.map +1 -0
  219. package/dist/legacy-senpi-dir-migration.d.ts.map +1 -1
  220. package/dist/legacy-senpi-dir-migration.js +64 -48
  221. package/dist/legacy-senpi-dir-migration.js.map +1 -1
  222. package/dist/main.d.ts +6 -0
  223. package/dist/main.d.ts.map +1 -1
  224. package/dist/main.js +129 -22
  225. package/dist/main.js.map +1 -1
  226. package/dist/migrations-state.d.ts +1 -1
  227. package/dist/migrations-state.d.ts.map +1 -1
  228. package/dist/migrations-state.js +5 -1
  229. package/dist/migrations-state.js.map +1 -1
  230. package/dist/migrations.d.ts.map +1 -1
  231. package/dist/migrations.js +4 -0
  232. package/dist/migrations.js.map +1 -1
  233. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  234. package/dist/modes/interactive/components/session-selector.js +5 -3
  235. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  236. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  237. package/dist/modes/interactive/interactive-mode.js +10 -4
  238. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  239. package/dist/modes/interactive/resume-rebind.d.ts +12 -0
  240. package/dist/modes/interactive/resume-rebind.d.ts.map +1 -0
  241. package/dist/modes/interactive/resume-rebind.js +37 -0
  242. package/dist/modes/interactive/resume-rebind.js.map +1 -0
  243. package/dist/modes/interactive/runtime-notice-presenter.d.ts +8 -0
  244. package/dist/modes/interactive/runtime-notice-presenter.d.ts.map +1 -0
  245. package/dist/modes/interactive/runtime-notice-presenter.js +69 -0
  246. package/dist/modes/interactive/runtime-notice-presenter.js.map +1 -0
  247. package/dist/modes/interactive/runtime-notice.d.ts +50 -0
  248. package/dist/modes/interactive/runtime-notice.d.ts.map +1 -0
  249. package/dist/modes/interactive/runtime-notice.js +104 -0
  250. package/dist/modes/interactive/runtime-notice.js.map +1 -0
  251. package/dist/modes/interactive/tips/catalog/computer-tips.d.ts +7 -0
  252. package/dist/modes/interactive/tips/catalog/computer-tips.d.ts.map +1 -0
  253. package/dist/modes/interactive/tips/catalog/computer-tips.js +36 -0
  254. package/dist/modes/interactive/tips/catalog/computer-tips.js.map +1 -0
  255. package/dist/modes/interactive/tips/registry.d.ts.map +1 -1
  256. package/dist/modes/interactive/tips/registry.js +2 -0
  257. package/dist/modes/interactive/tips/registry.js.map +1 -1
  258. package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
  259. package/dist/modes/rpc/connection-handler.js +3 -0
  260. package/dist/modes/rpc/connection-handler.js.map +1 -1
  261. package/dist/modes/rpc/host-child-exit.d.ts.map +1 -1
  262. package/dist/modes/rpc/host-child-exit.js +4 -0
  263. package/dist/modes/rpc/host-child-exit.js.map +1 -1
  264. package/dist/modes/rpc/host-crash-record.d.ts +8 -0
  265. package/dist/modes/rpc/host-crash-record.d.ts.map +1 -1
  266. package/dist/modes/rpc/host-crash-record.js.map +1 -1
  267. package/dist/modes/rpc/host-crash-record.test.js +11 -0
  268. package/dist/modes/rpc/host-crash-record.test.js.map +1 -1
  269. package/dist/modes/rpc/rpc-client.d.ts +2 -2
  270. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  271. package/dist/modes/rpc/rpc-client.js +14 -6
  272. package/dist/modes/rpc/rpc-client.js.map +1 -1
  273. package/dist/modes/rpc/rpc-request-deadline.d.ts +17 -0
  274. package/dist/modes/rpc/rpc-request-deadline.d.ts.map +1 -0
  275. package/dist/modes/rpc/rpc-request-deadline.js +27 -0
  276. package/dist/modes/rpc/rpc-request-deadline.js.map +1 -0
  277. package/dist/modes/rpc/rpc-types.d.ts +9 -2
  278. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  279. package/dist/modes/rpc/rpc-types.js.map +1 -1
  280. package/dist/modes/rpc/session-command-router.d.ts +3 -6
  281. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  282. package/dist/modes/rpc/session-command-router.js +9 -22
  283. package/dist/modes/rpc/session-command-router.js.map +1 -1
  284. package/dist/modes/rpc/session-path-key.d.ts +10 -0
  285. package/dist/modes/rpc/session-path-key.d.ts.map +1 -0
  286. package/dist/modes/rpc/session-path-key.js +33 -0
  287. package/dist/modes/rpc/session-path-key.js.map +1 -0
  288. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  289. package/dist/modes/rpc/session-registry.js +6 -10
  290. package/dist/modes/rpc/session-registry.js.map +1 -1
  291. package/dist/modes/rpc/session-sweep.d.ts +18 -0
  292. package/dist/modes/rpc/session-sweep.d.ts.map +1 -0
  293. package/dist/modes/rpc/session-sweep.js +32 -0
  294. package/dist/modes/rpc/session-sweep.js.map +1 -0
  295. package/dist/pi-dir-restore.d.ts +4 -0
  296. package/dist/pi-dir-restore.d.ts.map +1 -0
  297. package/dist/pi-dir-restore.js +92 -0
  298. package/dist/pi-dir-restore.js.map +1 -0
  299. package/docs/environment-variables.md +2 -0
  300. package/docs/extensions.md +27 -1
  301. package/docs/quickstart.md +7 -1
  302. package/docs/rpc.md +8 -3
  303. package/docs/sdk.md +29 -0
  304. package/docs/sessions.md +13 -0
  305. package/docs/usage.md +1 -0
  306. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +38 -0
  307. package/node_modules/@code-yeongyu/senpi-codemode/README.md +7 -0
  308. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  309. package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/protocol.ts +9 -0
  310. package/node_modules/@code-yeongyu/senpi-codemode/src/bridges/schema-bridge.ts +2 -0
  311. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/kernel-preludes.ts +30 -0
  312. package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +15 -1
  313. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/context-manager.ts +1 -1
  314. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/local-module-loader.ts +16 -2
  315. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel.ts +6 -1
  316. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py +15 -2
  317. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/transport.ts +7 -1
  318. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/kernel-prelude-plan.ts +19 -0
  319. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt-template.ts +2 -1
  320. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +4 -0
  321. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool-options.ts +11 -1
  322. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool.ts +1 -0
  323. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/run-eval-cell.ts +1 -0
  324. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +3 -1
  325. package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts +2 -2
  326. package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts.map +1 -1
  327. package/node_modules/@earendil-works/pi-agent-core/dist/agent.js +7 -0
  328. package/node_modules/@earendil-works/pi-agent-core/dist/agent.js.map +1 -1
  329. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.d.ts.map +1 -1
  330. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.js +3 -1
  331. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.js.map +1 -1
  332. package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts +3 -1
  333. package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts.map +1 -1
  334. package/node_modules/@earendil-works/pi-agent-core/dist/types.js.map +1 -1
  335. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  336. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
  337. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +10 -7
  338. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  339. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts.map +1 -1
  340. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +9 -1
  341. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
  342. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
  343. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  344. package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
  345. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  346. package/node_modules/@earendil-works/pi-ai/dist/provider-diagnostic.d.ts +33 -0
  347. package/node_modules/@earendil-works/pi-ai/dist/provider-diagnostic.d.ts.map +1 -0
  348. package/node_modules/@earendil-works/pi-ai/dist/provider-diagnostic.js +56 -0
  349. package/node_modules/@earendil-works/pi-ai/dist/provider-diagnostic.js.map +1 -0
  350. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  351. package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
  352. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  353. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  354. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  355. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  356. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +3 -0
  357. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  358. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  359. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-diagnostic-carrier.d.ts +5 -0
  360. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-diagnostic-carrier.d.ts.map +1 -0
  361. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-diagnostic-carrier.js +18 -0
  362. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-diagnostic-carrier.js.map +1 -0
  363. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-diagnostic-sources.d.ts +26 -0
  364. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-diagnostic-sources.d.ts.map +1 -0
  365. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-diagnostic-sources.js +103 -0
  366. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-diagnostic-sources.js.map +1 -0
  367. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-diagnostic-vocabulary.d.ts +36 -0
  368. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-diagnostic-vocabulary.d.ts.map +1 -0
  369. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-diagnostic-vocabulary.js +129 -0
  370. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-diagnostic-vocabulary.js.map +1 -0
  371. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-retry.d.ts.map +1 -1
  372. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-retry.js +5 -4
  373. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-retry.js.map +1 -1
  374. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  375. package/node_modules/@earendil-works/pi-pty/native/prebuilds/darwin-arm64/senpi_pty.darwin-arm64.node +0 -0
  376. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  377. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  378. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  379. package/package.json +7 -7
  380. package/dist/bundle/chunks/anthropic-messages-UYDVRFAS.js +0 -8
  381. package/dist/bundle/chunks/azure-openai-responses-DODWR4W6.js +0 -2
  382. package/dist/bundle/chunks/bun-extension-importer-H4IJCWWQ.js +0 -14
  383. package/dist/bundle/chunks/chunk-34HMVB33.js +0 -2
  384. package/dist/bundle/chunks/chunk-3WBSBDBX.js +0 -13
  385. package/dist/bundle/chunks/chunk-L6JO4M6J.js +0 -3
  386. package/dist/bundle/chunks/chunk-SFYHCFRO.js +0 -2
  387. package/dist/bundle/chunks/chunk-T3JBT2IK.js +0 -8
  388. package/dist/bundle/chunks/chunk-TTHMOL5G.js +0 -2
  389. package/dist/bundle/chunks/chunk-UYCGOK6M.js +0 -2
  390. package/dist/bundle/chunks/chunk-ZTVQY5V6.js +0 -2
  391. package/dist/bundle/chunks/cli-main-H6JAXAXV.js +0 -3
  392. package/dist/bundle/chunks/google-generative-ai-VIEFECKJ.js +0 -2
  393. package/dist/bundle/chunks/google-vertex-Q64CKFID.js +0 -2
  394. package/dist/bundle/chunks/help-fast-path-ZGKPSDFO.js +0 -2
  395. package/dist/bundle/chunks/host-command-BMHJ4YUD.js +0 -2
  396. package/dist/bundle/chunks/host-lifecycle-2GZAGSGW.js +0 -3
  397. package/dist/bundle/chunks/interactive-host-runtime-YI75GFD5.js +0 -4
  398. package/dist/bundle/chunks/interactive-mode-HM7UQYRC.js +0 -2
  399. package/dist/bundle/chunks/openai-images-JA5N5DUU.js +0 -2
  400. package/dist/bundle/chunks/openai-responses-IBFGBAJP.js +0 -2
  401. package/dist/bundle/chunks/openrouter-images-MW5TXV7L.js +0 -2
  402. package/dist/bundle/chunks/rotation-stream-NATO6IMN.js +0 -2
  403. package/dist/bundle/chunks/rpc-mode-6PY4TN5F.js +0 -2
  404. package/dist/bundle/chunks/session-picker-BP7THCWF.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"session-registry.js","sourceRoot":"","sources":["../../../src/modes/rpc/session-registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, realpathSync } from \"node:fs\";\nimport { basename, dirname, isAbsolute, resolve } from \"node:path\";\nimport { ProviderScope, runWithProviderScope } from \"@earendil-works/pi-ai/node/provider-scope\";\nimport {\n\ttype AgentSessionLaunchProfile,\n\tAgentSessionRuntime,\n\ttype CreateAgentSessionRuntimeFactory,\n\tcreateAgentSessionRuntime,\n} from \"../../core/agent-session-runtime.ts\";\nimport type { HostMcpRegistry } from \"../../core/extensions/builtin/mcp/host-registry.ts\";\nimport type { SessionContext, SessionKind, SessionStartEvent } from \"../../core/extensions/types.ts\";\nimport { EMPTY_SESSION_CONTEXT } from \"../../core/extensions/types.ts\";\nimport { assertValidSessionId, SessionManager } from \"../../core/session-manager.ts\";\nimport { SESSION_PATH_RETRY_AFTER_MS, type SessionPathReservations } from \"./host-reservations.ts\";\nimport { beginSessionClose, closeMarkedSession, closeSession, type SessionTeardownHost } from \"./session-teardown.ts\";\nimport type { SessionWorkerClient } from \"./session-worker-client.ts\";\n\n/** The immutable flags selected when a routing session is opened. */\nexport interface RpcSessionLaunchProfile extends AgentSessionLaunchProfile {\n\tsessionPath?: string;\n\t/**\n\t * Durable session id chosen by the caller, applied ONLY when this open creates the session.\n\t * An existing session file keeps the id in its header, so a resume never rewrites identity.\n\t */\n\tdurableSessionId?: string;\n}\n\nexport type SessionRuntime = AgentSessionRuntime;\nexport type RpcSessionState = \"opening\" | \"open\" | \"closing\" | \"quarantined\" | \"closed\";\n\nexport interface RpcSessionEntry {\n\tstate: RpcSessionState;\n\truntime?: SessionRuntime;\n\tworker?: SessionWorkerClient;\n\t/** Visibility class chosen by the open, frozen for the entry's life. */\n\treadonly kind: SessionKind;\n\t/** Frozen opaque labels the open attached; `{}` when it attached none. */\n\treadonly context: SessionContext;\n\t/** Resolves replacement against the runtime currently owned by this entry. */\n\tswitchSession?: SessionRuntime[\"switchSession\"];\n\t/** Rebind callback installed by the shared RPC connection handler. */\n\trebindSession?: Parameters<SessionRuntime[\"setRebindSession\"]>[0];\n\tscope: ProviderScope;\n\tprofile: Readonly<RpcSessionLaunchProfile>;\n\tdurableSessionId?: string;\n\tsessionPath?: string;\n\t/** Canonical reservation key for path-opened sessions; matches the reservations set. */\n\treservationKey?: string;\n\t/** Key granted for the spelling this session was opened with; cleared once superseded. */\n\trequestedPathKey?: string;\n\t/** Current runtime cwd, which can change when a session is replaced. */\n\tcwd: string;\n\t/** Live attachments (open + later attaches). The runtime is disposed only when the last one closes. */\n\tattachments: number;\n\t/**\n\t * Opt-in retention: a dropped connection only DETACHES from this session. The\n\t * entry stays open at zero attachments (still listed, still running its turn,\n\t * still holding its path) until an explicit close_session or idle eviction.\n\t * Requested per `open_session`; an attach may turn it on, never off.\n\t */\n\tretainOnDisconnect?: boolean;\n\t/** Timestamp of the last routed command / observed activity; drives idle eviction. */\n\tlastCommandAt: number;\n\tlifecycleMutex: Promise<void>;\n\tcloseCompletion?: Promise<void>;\n\tcloseResolve?: () => void;\n\tcloseStarted?: boolean;\n}\n\nexport class RpcSessionRegistryError extends Error {\n\treadonly code:\n\t\t| \"unknown_session\"\n\t\t| \"session_closing\"\n\t\t| \"session_path_in_use\"\n\t\t| \"session_id_in_use\"\n\t\t| \"session_reservation_limit\"\n\t\t| \"invalid_path\"\n\t\t| \"invalid_session_id\"\n\t\t| \"host_memory_pressure\"\n\t\t| \"open_failed\";\n\t/** Machine-readable context for the wire (`errorData`): who holds a path, when to retry. */\n\treadonly detail?: Readonly<Record<string, unknown>>;\n\n\tconstructor(code: RpcSessionRegistryError[\"code\"], reason?: string, detail?: Readonly<Record<string, unknown>>) {\n\t\tsuper(code === \"open_failed\" && reason ? `${code}: ${reason}` : code);\n\t\tthis.code = code;\n\t\tthis.name = \"RpcSessionRegistryError\";\n\t\tif (detail) this.detail = detail;\n\t}\n}\n\nexport interface RpcSessionRegistryOptions {\n\tagentDir: string;\n\tcreateRuntime: CreateAgentSessionRuntimeFactory;\n\tmcpRegistry?: HostMcpRegistry;\n\t/** Injectable clock (defaults to Date.now) so idle bookkeeping is testable. */\n\tnow?: () => number;\n\t/** Maximum time to wait for graceful runtime teardown before forced release. */\n\tcloseGraceMs?: number;\n\t/**\n\t * Cross-GENERATION path claims. During a handoff two hosts are alive at once, and only a\n\t * claim outside either process can keep them off one JSONL. Absent for an embedded registry\n\t * that is the only host of its agent directory.\n\t */\n\tpathReservations?: SessionPathReservations;\n}\n\n/**\n * Why the host currently declines to CREATE a worker session: it is above its RSS refuse\n * watermark. Carried verbatim to the client as `errorData` so it knows when to retry.\n */\nexport interface WorkerAdmissionRefusal {\n\treadonly rssMb: number;\n\treadonly retry_after_ms: number;\n}\n\n/** Host-side lifecycle policy for one `open_session`, distinct from the session's launch profile. */\nexport interface RpcSessionOpenOptions {\n\t/** Keep the session alive when its last client disconnects (`open_session.retain_on_disconnect`). */\n\tretainOnDisconnect?: boolean;\n}\n\n/** One `list_sessions` row. `context` is published only to a listing that asked for workers. */\nexport interface RpcSessionRow {\n\tsessionId: string;\n\tdurableSessionId?: string;\n\tsessionPath?: string;\n\tcwd: string;\n\tname?: string;\n\tstatus: Exclude<RpcSessionState, \"quarantined\">;\n\tattachments: number;\n\tkind: SessionKind;\n\tcontext: SessionContext;\n}\n\nexport interface OpenRpcSession {\n\tsessionId: string;\n\tdurableSessionId: string;\n\tsessionPath?: string;\n\t/** True when this open attached to an already-open session instead of creating one. */\n\tattached?: boolean;\n}\n\nfunction canonicalPath(path: string): string {\n\tconst absolutePath = resolve(path);\n\tif (existsSync(absolutePath)) return realpathSync(absolutePath);\n\treturn `${realpathSync(dirname(absolutePath))}/${basename(absolutePath)}`;\n}\n\n/** Freezes an open's launch inputs, including the nested objects a client supplied. */\nexport function frozenProfile(profile: RpcSessionLaunchProfile): Readonly<RpcSessionLaunchProfile> {\n\treturn Object.freeze({\n\t\t...profile,\n\t\t...(profile.creationModel ? { creationModel: Object.freeze({ ...profile.creationModel }) } : {}),\n\t\t...(profile.sessionContext ? { sessionContext: Object.freeze({ ...profile.sessionContext }) } : {}),\n\t});\n}\n\n/**\n * The visibility class and labels an entry keeps for its life, normalized once here so no\n * lifecycle, listing or delivery decision has to re-apply the defaults. Reads the already\n * frozen profile, so the entry and the runtime share one frozen context object.\n */\nexport function sessionIdentity(profile: Readonly<RpcSessionLaunchProfile>): {\n\treadonly kind: SessionKind;\n\treadonly context: SessionContext;\n} {\n\treturn { kind: profile.sessionKind ?? \"interactive\", context: profile.sessionContext ?? EMPTY_SESSION_CONTEXT };\n}\n\n/** Process-local lifecycle owner for multi-session RPC runtimes. */\nexport class RpcSessionRegistry {\n\tprivate readonly entries = new Map<string, RpcSessionEntry>();\n\tprivate readonly reservations = new Set<string>();\n\tprivate readonly teardownHost: SessionTeardownHost;\n\tprivate nextHandle = 0;\n\tprivate readonly options: RpcSessionRegistryOptions;\n\tprivate readonly now: () => number;\n\treadonly closeGraceMs: number;\n\tprivate workerRefusal: WorkerAdmissionRefusal | undefined;\n\n\tconstructor(options: RpcSessionRegistryOptions) {\n\t\tthis.options =\n\t\t\toptions.mcpRegistry === undefined\n\t\t\t\t? options\n\t\t\t\t: {\n\t\t\t\t\t\t...options,\n\t\t\t\t\t\tcreateRuntime: (runtimeOptions) =>\n\t\t\t\t\t\t\toptions.createRuntime({ ...runtimeOptions, mcpRegistry: options.mcpRegistry }),\n\t\t\t\t\t};\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.closeGraceMs = options.closeGraceMs ?? 10_000;\n\t\tthis.teardownHost = {\n\t\t\tcloseGraceMs: this.closeGraceMs,\n\t\t\tget: (handle) => this.entries.get(handle),\n\t\t\tdelete: (handle) => this.entries.delete(handle),\n\t\t\treleaseReservation: (key) => {\n\t\t\t\tthis.reservations.delete(key);\n\t\t\t\tthis.options.pathReservations?.release(key);\n\t\t\t},\n\t\t\tmarkDetached: (key) => this.options.pathReservations?.setAttached(key, false),\n\t\t\tsync: () => this.syncRuntimeMetadata(),\n\t\t};\n\t}\n\n\t/** Number of live entries, including ones still opening or closing. */\n\tget size(): number {\n\t\treturn this.entries.size;\n\t}\n\n\t/**\n\t * While set, an open that would CREATE a worker session is refused with\n\t * `host_memory_pressure`; attaches to a live path and interactive opens are unaffected.\n\t * The only memory-driven refusal on the in-process path - never an occupancy count.\n\t */\n\tsetWorkerAdmission(refusal: WorkerAdmissionRefusal | undefined): void {\n\t\tthis.workerRefusal = refusal;\n\t}\n\n\tasync openSession(profile: RpcSessionLaunchProfile, options?: RpcSessionOpenOptions): Promise<OpenRpcSession> {\n\t\tthis.validateProfile(profile);\n\t\tthis.syncRuntimeMetadata();\n\t\tconst sessionPath = profile.sessionPath ? canonicalPath(profile.sessionPath) : undefined;\n\t\t// Taken SYNCHRONOUSLY, before any await, exactly like the path reservation below: a\n\t\t// concurrent open naming the same durable id must find this one already recorded rather\n\t\t// than a window between the decision and the record of it. Two LIVE sessions may never\n\t\t// share a durable id - every per-session artifact a client keys by it would collide.\n\t\t// Re-opening the SAME file is an attach/resume, not a collision: the id is the file's own.\n\t\tconst requestedDurableId = profile.durableSessionId;\n\t\tif (requestedDurableId !== undefined) {\n\t\t\tfor (const entry of this.entries.values()) {\n\t\t\t\tif (entry.state === \"closed\") continue;\n\t\t\t\tif (entry.durableSessionId !== requestedDurableId) continue;\n\t\t\t\tif (sessionPath !== undefined && entry.reservationKey === sessionPath) continue;\n\t\t\t\tthrow new RpcSessionRegistryError(\"session_id_in_use\");\n\t\t\t}\n\t\t}\n\t\tif (sessionPath) await this.settleClosingReservation(sessionPath);\n\t\tif (sessionPath && this.reservations.has(sessionPath)) {\n\t\t\t// Attach-on-open: a live session outlives individual client attachments, so a\n\t\t\t// resume (or a second surface) for an already-hosted path joins the existing\n\t\t\t// runtime instead of failing. An entry still opening keeps the exclusive\n\t\t\t// reservation and rejects as before.\n\t\t\tconst existing = [...this.entries].find(\n\t\t\t\t([, entry]) => entry.reservationKey === sessionPath && entry.state === \"open\",\n\t\t\t);\n\t\t\tif (!existing) throw new RpcSessionRegistryError(\"session_path_in_use\");\n\t\t\tconst [handle, entry] = existing;\n\t\t\tconst wasParked = entry.retainOnDisconnect === true && entry.attachments === 0;\n\t\t\tentry.attachments += 1;\n\t\t\t// The claim carries the attachment state another generation decides on: a path this host is\n\t\t\t// actively serving a client on is never reclaimable from it.\n\t\t\tthis.options.pathReservations?.setAttached(sessionPath, true);\n\t\t\t// Retention is a property of the live session: any attach may ask for it, and\n\t\t\t// no attach may revoke it for the clients that already rely on it.\n\t\t\tif (options?.retainOnDisconnect) entry.retainOnDisconnect = true;\n\t\t\tif (!entry.durableSessionId) throw new RpcSessionRegistryError(\"session_path_in_use\");\n\t\t\tentry.lastCommandAt = this.now();\n\t\t\tif (wasParked) {\n\t\t\t\tentry.lifecycleMutex = entry.lifecycleMutex.then(() =>\n\t\t\t\t\tentry.runtime?.emitAttachmentEvent(\"session_resumed\"),\n\t\t\t\t);\n\t\t\t\tawait entry.lifecycleMutex;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsessionId: handle,\n\t\t\t\tdurableSessionId: entry.durableSessionId,\n\t\t\t\tsessionPath: entry.sessionPath,\n\t\t\t\tattached: true,\n\t\t\t};\n\t\t}\n\t\tif (this.workerRefusal && profile.sessionKind === \"worker\")\n\t\t\tthrow new RpcSessionRegistryError(\"host_memory_pressure\", undefined, { ...this.workerRefusal });\n\t\tif (sessionPath) {\n\t\t\t// Taken SYNCHRONOUSLY, before any await: a concurrent open for the same path must find the\n\t\t\t// reservation already held, not a window between the decision and the record of it.\n\t\t\tthis.reservations.add(sessionPath);\n\t\t\t// Another generation of this daemon may still be writing this file. Its claim is the only\n\t\t\t// thing this process can see across a handoff, and a live one means \"retry\", not \"gone\".\n\t\t\t// Only AWAIT when there is a cross-generation claim to take: `await undefined` still costs a\n\t\t\t// microtask, and an embedded registry (no second generation) must reach the \"opening\" entry\n\t\t\t// in the same tick a caller that raced it would look, exactly as it did before generations.\n\t\t\tconst holder = this.options.pathReservations\n\t\t\t\t? await this.options.pathReservations.claim(sessionPath)\n\t\t\t\t: undefined;\n\t\t\tif (holder) {\n\t\t\t\tthis.reservations.delete(sessionPath);\n\t\t\t\tthrow new RpcSessionRegistryError(\"session_path_in_use\", undefined, {\n\t\t\t\t\towner: holder,\n\t\t\t\t\tretry_after_ms: SESSION_PATH_RETRY_AFTER_MS,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Resume vs create parity (D1 + omo SenpiSessionRuntime.ts:198-200):\n\t\t// Create-only launch semantics mirror classic startup flags. A resumed\n\t\t// session restores its persisted model and thinking level instead of being\n\t\t// overridden by the new open_session request.\n\t\tconst isResume = sessionPath !== undefined && existsSync(sessionPath);\n\t\t// Re-opening an existing session file is a resume, exactly like interactive\n\t\t// /resume (AgentSessionRuntime.switchSession). Without the event the session\n\t\t// starts with reason \"startup\" and every extension that only rebuilds state\n\t\t// on a resume - the ask-user dangling-question hook - stays unreachable from\n\t\t// the RPC restart path. A session created by this open stays \"startup\".\n\t\tconst sessionStartEvent: SessionStartEvent | undefined = isResume\n\t\t\t? { type: \"session_start\", reason: \"resume\" }\n\t\t\t: undefined;\n\t\tconst storedProfile = frozenProfile({ ...profile, ...(sessionPath ? { sessionPath } : {}) });\n\t\tconst runtimeProfile = isResume\n\t\t\t? frozenProfile({ ...storedProfile, creationModel: undefined, initialThinkingLevel: undefined })\n\t\t\t: storedProfile;\n\n\t\tconst handle = `rpc-${++this.nextHandle}`;\n\t\tconst entry: RpcSessionEntry = {\n\t\t\tstate: \"opening\",\n\t\t\tscope: new ProviderScope(),\n\t\t\tprofile: storedProfile,\n\t\t\t...sessionIdentity(storedProfile),\n\t\t\tsessionPath,\n\t\t\treservationKey: sessionPath,\n\t\t\tcwd: storedProfile.cwd,\n\t\t\tattachments: 1,\n\t\t\tretainOnDisconnect: options?.retainOnDisconnect === true,\n\t\t\tlastCommandAt: this.now(),\n\t\t\tlifecycleMutex: Promise.resolve(),\n\t\t};\n\t\tentry.switchSession = (sessionPath, options) => {\n\t\t\tconst operation = entry.lifecycleMutex.then(async () => {\n\t\t\t\tif (entry.state !== \"open\" || !entry.runtime) {\n\t\t\t\t\tthrow new RpcSessionRegistryError(\"unknown_session\");\n\t\t\t\t}\n\t\t\t\tconst runtime = entry.runtime;\n\t\t\t\tconst cwdOverride = options?.cwdOverride;\n\t\t\t\tconst cwdChanged = cwdOverride !== undefined && runtime.session.sessionManager.getCwd() !== cwdOverride;\n\t\t\t\tconst result = await runtime.switchSession(sessionPath, options);\n\t\t\t\tif (result.cancelled || !cwdChanged) return result;\n\n\t\t\t\t// A multi-session binding outlives an individual replacement. Keep the\n\t\t\t\t// entry's runtime object aligned with the replacement so every attached\n\t\t\t\t// client resolves getters and future commands against the new cwd-bound\n\t\t\t\t// runtime, not the object created during open_session.\n\t\t\t\tconst replacement = new AgentSessionRuntime(\n\t\t\t\t\truntime.session,\n\t\t\t\t\truntime.services,\n\t\t\t\t\tthis.options.createRuntime,\n\t\t\t\t\t[...runtime.diagnostics],\n\t\t\t\t\truntime.modelFallbackMessage,\n\t\t\t\t\truntime.launchProfile,\n\t\t\t\t);\n\t\t\t\treplacement.setRebindSession(entry.rebindSession);\n\t\t\t\tentry.runtime = replacement;\n\t\t\t\tthis.syncRuntimeMetadata();\n\t\t\t\treturn result;\n\t\t\t});\n\t\t\tentry.lifecycleMutex = operation.then(\n\t\t\t\t() => undefined,\n\t\t\t\t() => undefined,\n\t\t\t);\n\t\t\treturn operation;\n\t\t};\n\t\t// Recorded before the first await so the synchronous collision guard above sees an open\n\t\t// that is still being built. `manager.getSessionId()` overwrites it below with the\n\t\t// authoritative value, which on a resume is the header's id, not the requested one.\n\t\tif (requestedDurableId !== undefined) entry.durableSessionId = requestedDurableId;\n\t\tthis.entries.set(handle, entry);\n\t\ttry {\n\t\t\tconst newSessionOptions = requestedDurableId !== undefined ? { id: requestedDurableId } : undefined;\n\t\t\tconst manager = sessionPath\n\t\t\t\t? SessionManager.open(sessionPath, undefined, storedProfile.cwd, newSessionOptions)\n\t\t\t\t: SessionManager.create(storedProfile.cwd, undefined, newSessionOptions);\n\t\t\tentry.runtime = await runWithProviderScope(entry.scope, () =>\n\t\t\t\tcreateAgentSessionRuntime(this.options.createRuntime, {\n\t\t\t\t\tcwd: manager.getCwd(),\n\t\t\t\t\tagentDir: this.options.agentDir,\n\t\t\t\t\tsessionManager: manager,\n\t\t\t\t\tsessionStartEvent,\n\t\t\t\t\tlaunchProfile: runtimeProfile,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tentry.durableSessionId = manager.getSessionId();\n\t\t\tentry.sessionPath ??= manager.getSessionFile();\n\t\t\tentry.state = \"open\";\n\t\t\treturn { sessionId: handle, durableSessionId: entry.durableSessionId, sessionPath: entry.sessionPath };\n\t\t} catch (error) {\n\t\t\t// Runtime construction may have started extensions, watchers, and provider\n\t\t\t// registrations before it rejects. Keep the reservation and entry private\n\t\t\t// until all of those resources have been torn down, then release them as\n\t\t\t// one rollback so the path can immediately be opened again.\n\t\t\ttry {\n\t\t\t\tawait entry.runtime?.dispose();\n\t\t\t} catch {\n\t\t\t\t// The original construction error remains the externally visible cause.\n\t\t\t} finally {\n\t\t\t\ttry {\n\t\t\t\t\tawait entry.scope.close?.();\n\t\t\t\t} finally {\n\t\t\t\t\tthis.entries.delete(handle);\n\t\t\t\t\tif (sessionPath) {\n\t\t\t\t\t\tthis.reservations.delete(sessionPath);\n\t\t\t\t\t\tthis.options.pathReservations?.release(sessionPath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (error instanceof RpcSessionRegistryError) throw error;\n\t\t\tthrow new RpcSessionRegistryError(\"open_failed\", error instanceof Error ? error.message : undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Waits out a teardown already in flight for this path before the open decides.\n\t *\n\t * A close FREES the path, but the entry keeps its reservation until its runtime is\n\t * disposed, so an open landing inside that window used to be refused with\n\t * `session_path_in_use` for a session that no longer exists - making \"reopen the\n\t * path I just closed\" a race against disposal latency, which no client can time.\n\t * The open now waits for the teardown it would have been refused by and then opens\n\t * the file fresh. Bounded by the same grace window that bounds the teardown itself\n\t * (`closeMarkedSession` force-releases at that deadline), so a wedged disposal\n\t * still ends in the ordinary refusal instead of an open that never answers.\n\t */\n\tprivate async settleClosingReservation(sessionPath: string): Promise<void> {\n\t\tconst closing = [...this.entries.values()].find(\n\t\t\t(entry) => entry.reservationKey === sessionPath && entry.state === \"closing\",\n\t\t);\n\t\tif (!closing?.closeCompletion) return;\n\t\tlet deadline: ReturnType<typeof setTimeout> | undefined;\n\t\ttry {\n\t\t\tawait Promise.race([\n\t\t\t\tclosing.closeCompletion,\n\t\t\t\tnew Promise<void>((resolve) => {\n\t\t\t\t\tdeadline = setTimeout(resolve, this.closeGraceMs);\n\t\t\t\t}),\n\t\t\t]);\n\t\t} finally {\n\t\t\tif (deadline) clearTimeout(deadline);\n\t\t}\n\t}\n\n\t/**\n\t * Read-only lookup with no state transitions or attachment accounting.\n\t * Exists so lifecycle decisions (e.g. deferring a dropped connection's\n\t * release while a turn is still streaming) can inspect the live entry\n\t * without claiming it.\n\t */\n\tpeek(handle: string): RpcSessionEntry | undefined {\n\t\treturn this.entries.get(handle);\n\t}\n\n\tgetForCommand(handle: string, command: string): RpcSessionEntry {\n\t\tconst entry = this.entries.get(handle);\n\t\tif (!entry) throw new RpcSessionRegistryError(\"unknown_session\");\n\t\tif (\n\t\t\tentry.state === \"closing\" &&\n\t\t\t![\"abort\", \"abort_bash\", \"extension_ui_response\", \"extension_ui_progress\"].includes(command)\n\t\t) {\n\t\t\tthrow new RpcSessionRegistryError(\"session_closing\");\n\t\t}\n\t\tif (entry.state !== \"open\" && entry.state !== \"closing\") throw new RpcSessionRegistryError(\"unknown_session\");\n\t\t// Every routed command counts as activity for idle eviction. Lookups that\n\t\t// must not refresh idleness (sweeps, listing) use peek()/list() instead.\n\t\tentry.lastCommandAt = this.now();\n\t\treturn entry;\n\t}\n\n\t/**\n\t * Starts a close synchronously and returns the live entry for routing decisions.\n\t * `detach` marks a client going away rather than the session ending, which a\n\t * retained entry answers by staying open at zero attachments.\n\t */\n\tbeginClose(handle: string, onRole?: (finalizer: boolean) => void, options?: { detach?: boolean }): RpcSessionEntry {\n\t\treturn beginSessionClose(this.teardownHost, handle, onRole, options);\n\t}\n\n\tasync close(handle: string): Promise<void> {\n\t\treturn closeSession(this.teardownHost, handle);\n\t}\n\n\t/** Completes a close previously made visible by beginClose(). */\n\tasync closeMarked(handle: string): Promise<void> {\n\t\treturn closeMarkedSession(this.teardownHost, handle);\n\t}\n\n\tlist(): RpcSessionRow[] {\n\t\tthis.syncRuntimeMetadata();\n\t\treturn [...this.entries].map(([sessionId, entry]) => ({\n\t\t\tsessionId,\n\t\t\tdurableSessionId: entry.durableSessionId,\n\t\t\tsessionPath: entry.sessionPath,\n\t\t\tcwd: entry.cwd,\n\t\t\tname: entry.runtime?.session.sessionManager.getSessionName(),\n\t\t\tkind: entry.kind,\n\t\t\tcontext: entry.context,\n\t\t\t// A closing entry has already released its last attachment; never publish that as negative.\n\t\t\tattachments: Math.max(0, entry.attachments),\n\t\t\tstatus: entry.state === \"quarantined\" ? \"closing\" : entry.state,\n\t\t}));\n\t}\n\n\t/** Reconcile path and durable identity after runtime replacement. */\n\tprivate syncRuntimeMetadata(): void {\n\t\tfor (const entry of this.entries.values()) {\n\t\t\tconst manager = entry.runtime?.session.sessionManager;\n\t\t\tif (!manager) continue;\n\t\t\tconst currentPath = manager.getSessionFile();\n\t\t\tconst currentKey = currentPath ? canonicalPath(currentPath) : undefined;\n\t\t\t// Preserve the originally canonicalized key while the runtime still points at\n\t\t\t// the same path. SessionManager may expose a symlink-resolved spelling after\n\t\t\t// opening a file that did not exist yet; treating that as replacement would\n\t\t\t// break ordinary attach-on-open aliases.\n\t\t\t//\n\t\t\t// A moved path is not the only reason to reconcile: a session opened WITHOUT\n\t\t\t// `sessionPath` lands its created file straight into `sessionPath` and never\n\t\t\t// takes a reservation, so comparing paths alone leaves that file unclaimed\n\t\t\t// forever and a later open of it builds a SECOND runtime over the same\n\t\t\t// transcript. Reconcile whenever the canonical key we hold is not the key the\n\t\t\t// runtime is actually writing.\n\t\t\tif (currentPath !== entry.sessionPath || currentKey !== entry.reservationKey) {\n\t\t\t\tif (entry.reservationKey) {\n\t\t\t\t\tthis.reservations.delete(entry.reservationKey);\n\t\t\t\t\tthis.options.pathReservations?.release(entry.reservationKey);\n\t\t\t\t}\n\t\t\t\tif (currentKey) {\n\t\t\t\t\tthis.reservations.add(currentKey);\n\t\t\t\t\t// A replacement moved this session to another file; the claim follows it, carrying the\n\t\t\t\t\t// attachment state it is now held at. A file a live foreign generation holds is left\n\t\t\t\t\t// alone by claim() itself.\n\t\t\t\t\tvoid this.options.pathReservations?.claim(currentKey, entry.attachments > 0);\n\t\t\t\t}\n\t\t\t\tentry.reservationKey = currentKey;\n\t\t\t\tentry.sessionPath = currentPath;\n\t\t\t}\n\t\t\tentry.durableSessionId = manager.getSessionId();\n\t\t\tentry.cwd = manager.getCwd();\n\t\t}\n\t}\n\n\tprivate validateProfile(profile: RpcSessionLaunchProfile): void {\n\t\tif (!isAbsolute(profile.cwd) || (profile.sessionPath !== undefined && !isAbsolute(profile.sessionPath))) {\n\t\t\tthrow new RpcSessionRegistryError(\"invalid_path\");\n\t\t}\n\t\tif (profile.durableSessionId !== undefined) {\n\t\t\ttry {\n\t\t\t\tassertValidSessionId(profile.durableSessionId);\n\t\t\t} catch (cause) {\n\t\t\t\tthrow new RpcSessionRegistryError(\"invalid_session_id\", String(cause));\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"session-registry.js","sourceRoot":"","sources":["../../../src/modes/rpc/session-registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { isAbsolute } from \"node:path\";\nimport { ProviderScope, runWithProviderScope } from \"@earendil-works/pi-ai/node/provider-scope\";\nimport {\n\ttype AgentSessionLaunchProfile,\n\tAgentSessionRuntime,\n\ttype CreateAgentSessionRuntimeFactory,\n\tcreateAgentSessionRuntime,\n} from \"../../core/agent-session-runtime.ts\";\nimport type { HostMcpRegistry } from \"../../core/extensions/builtin/mcp/host-registry.ts\";\nimport type { SessionContext, SessionKind, SessionStartEvent } from \"../../core/extensions/types.ts\";\nimport { EMPTY_SESSION_CONTEXT } from \"../../core/extensions/types.ts\";\nimport { assertValidSessionId, SessionManager } from \"../../core/session-manager.ts\";\nimport { SESSION_PATH_RETRY_AFTER_MS, type SessionPathReservations } from \"./host-reservations.ts\";\nimport { canonicalSessionPath } from \"./session-path-key.ts\";\nimport { beginSessionClose, closeMarkedSession, closeSession, type SessionTeardownHost } from \"./session-teardown.ts\";\nimport type { SessionWorkerClient } from \"./session-worker-client.ts\";\n\n/** The immutable flags selected when a routing session is opened. */\nexport interface RpcSessionLaunchProfile extends AgentSessionLaunchProfile {\n\tsessionPath?: string;\n\t/**\n\t * Durable session id chosen by the caller, applied ONLY when this open creates the session.\n\t * An existing session file keeps the id in its header, so a resume never rewrites identity.\n\t */\n\tdurableSessionId?: string;\n}\n\nexport type SessionRuntime = AgentSessionRuntime;\nexport type RpcSessionState = \"opening\" | \"open\" | \"closing\" | \"quarantined\" | \"closed\";\n\nexport interface RpcSessionEntry {\n\tstate: RpcSessionState;\n\truntime?: SessionRuntime;\n\tworker?: SessionWorkerClient;\n\t/** Visibility class chosen by the open, frozen for the entry's life. */\n\treadonly kind: SessionKind;\n\t/** Frozen opaque labels the open attached; `{}` when it attached none. */\n\treadonly context: SessionContext;\n\t/** Resolves replacement against the runtime currently owned by this entry. */\n\tswitchSession?: SessionRuntime[\"switchSession\"];\n\t/** Rebind callback installed by the shared RPC connection handler. */\n\trebindSession?: Parameters<SessionRuntime[\"setRebindSession\"]>[0];\n\tscope: ProviderScope;\n\tprofile: Readonly<RpcSessionLaunchProfile>;\n\tdurableSessionId?: string;\n\tsessionPath?: string;\n\t/** Canonical reservation key for path-opened sessions; matches the reservations set. */\n\treservationKey?: string;\n\t/** Key granted for the spelling this session was opened with; cleared once superseded. */\n\trequestedPathKey?: string;\n\t/** Current runtime cwd, which can change when a session is replaced. */\n\tcwd: string;\n\t/** Live attachments (open + later attaches). The runtime is disposed only when the last one closes. */\n\tattachments: number;\n\t/**\n\t * Opt-in retention: a dropped connection only DETACHES from this session. The\n\t * entry stays open at zero attachments (still listed, still running its turn,\n\t * still holding its path) until an explicit close_session or idle eviction.\n\t * Requested per `open_session`; an attach may turn it on, never off.\n\t */\n\tretainOnDisconnect?: boolean;\n\t/** Timestamp of the last routed command / observed activity; drives idle eviction. */\n\tlastCommandAt: number;\n\tlifecycleMutex: Promise<void>;\n\tcloseCompletion?: Promise<void>;\n\tcloseResolve?: () => void;\n\tcloseStarted?: boolean;\n}\n\nexport class RpcSessionRegistryError extends Error {\n\treadonly code:\n\t\t| \"unknown_session\"\n\t\t| \"session_closing\"\n\t\t| \"session_path_in_use\"\n\t\t| \"session_id_in_use\"\n\t\t| \"session_reservation_limit\"\n\t\t| \"invalid_path\"\n\t\t| \"invalid_session_id\"\n\t\t| \"host_memory_pressure\"\n\t\t| \"open_failed\";\n\t/** Machine-readable context for the wire (`errorData`): who holds a path, when to retry. */\n\treadonly detail?: Readonly<Record<string, unknown>>;\n\n\tconstructor(code: RpcSessionRegistryError[\"code\"], reason?: string, detail?: Readonly<Record<string, unknown>>) {\n\t\tsuper(code === \"open_failed\" && reason ? `${code}: ${reason}` : code);\n\t\tthis.code = code;\n\t\tthis.name = \"RpcSessionRegistryError\";\n\t\tif (detail) this.detail = detail;\n\t}\n}\n\nexport interface RpcSessionRegistryOptions {\n\tagentDir: string;\n\tcreateRuntime: CreateAgentSessionRuntimeFactory;\n\tmcpRegistry?: HostMcpRegistry;\n\t/** Injectable clock (defaults to Date.now) so idle bookkeeping is testable. */\n\tnow?: () => number;\n\t/** Maximum time to wait for graceful runtime teardown before forced release. */\n\tcloseGraceMs?: number;\n\t/**\n\t * Cross-GENERATION path claims. During a handoff two hosts are alive at once, and only a\n\t * claim outside either process can keep them off one JSONL. Absent for an embedded registry\n\t * that is the only host of its agent directory.\n\t */\n\tpathReservations?: SessionPathReservations;\n}\n\n/**\n * Why the host currently declines to CREATE a worker session: it is above its RSS refuse\n * watermark. Carried verbatim to the client as `errorData` so it knows when to retry.\n */\nexport interface WorkerAdmissionRefusal {\n\treadonly rssMb: number;\n\treadonly retry_after_ms: number;\n}\n\n/** Host-side lifecycle policy for one `open_session`, distinct from the session's launch profile. */\nexport interface RpcSessionOpenOptions {\n\t/** Keep the session alive when its last client disconnects (`open_session.retain_on_disconnect`). */\n\tretainOnDisconnect?: boolean;\n}\n\n/** One `list_sessions` row. `context` is published only to a listing that asked for workers. */\nexport interface RpcSessionRow {\n\tsessionId: string;\n\tdurableSessionId?: string;\n\tsessionPath?: string;\n\tcwd: string;\n\tname?: string;\n\tstatus: Exclude<RpcSessionState, \"quarantined\">;\n\tattachments: number;\n\tkind: SessionKind;\n\tcontext: SessionContext;\n}\n\nexport interface OpenRpcSession {\n\tsessionId: string;\n\tdurableSessionId: string;\n\tsessionPath?: string;\n\t/** True when this open attached to an already-open session instead of creating one. */\n\tattached?: boolean;\n}\n\n/** Freezes an open's launch inputs, including the nested objects a client supplied. */\nexport function frozenProfile(profile: RpcSessionLaunchProfile): Readonly<RpcSessionLaunchProfile> {\n\treturn Object.freeze({\n\t\t...profile,\n\t\t...(profile.creationModel ? { creationModel: Object.freeze({ ...profile.creationModel }) } : {}),\n\t\t...(profile.sessionContext ? { sessionContext: Object.freeze({ ...profile.sessionContext }) } : {}),\n\t});\n}\n\n/**\n * The visibility class and labels an entry keeps for its life, normalized once here so no\n * lifecycle, listing or delivery decision has to re-apply the defaults. Reads the already\n * frozen profile, so the entry and the runtime share one frozen context object.\n */\nexport function sessionIdentity(profile: Readonly<RpcSessionLaunchProfile>): {\n\treadonly kind: SessionKind;\n\treadonly context: SessionContext;\n} {\n\treturn { kind: profile.sessionKind ?? \"interactive\", context: profile.sessionContext ?? EMPTY_SESSION_CONTEXT };\n}\n\n/** Process-local lifecycle owner for multi-session RPC runtimes. */\nexport class RpcSessionRegistry {\n\tprivate readonly entries = new Map<string, RpcSessionEntry>();\n\tprivate readonly reservations = new Set<string>();\n\tprivate readonly teardownHost: SessionTeardownHost;\n\tprivate nextHandle = 0;\n\tprivate readonly options: RpcSessionRegistryOptions;\n\tprivate readonly now: () => number;\n\treadonly closeGraceMs: number;\n\tprivate workerRefusal: WorkerAdmissionRefusal | undefined;\n\n\tconstructor(options: RpcSessionRegistryOptions) {\n\t\tthis.options =\n\t\t\toptions.mcpRegistry === undefined\n\t\t\t\t? options\n\t\t\t\t: {\n\t\t\t\t\t\t...options,\n\t\t\t\t\t\tcreateRuntime: (runtimeOptions) =>\n\t\t\t\t\t\t\toptions.createRuntime({ ...runtimeOptions, mcpRegistry: options.mcpRegistry }),\n\t\t\t\t\t};\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.closeGraceMs = options.closeGraceMs ?? 10_000;\n\t\tthis.teardownHost = {\n\t\t\tcloseGraceMs: this.closeGraceMs,\n\t\t\tget: (handle) => this.entries.get(handle),\n\t\t\tdelete: (handle) => this.entries.delete(handle),\n\t\t\treleaseReservation: (key) => {\n\t\t\t\tthis.reservations.delete(key);\n\t\t\t\tthis.options.pathReservations?.release(key);\n\t\t\t},\n\t\t\tmarkDetached: (key) => this.options.pathReservations?.setAttached(key, false),\n\t\t\tsync: () => this.syncRuntimeMetadata(),\n\t\t};\n\t}\n\n\t/** Number of live entries, including ones still opening or closing. */\n\tget size(): number {\n\t\treturn this.entries.size;\n\t}\n\n\t/**\n\t * While set, an open that would CREATE a worker session is refused with\n\t * `host_memory_pressure`; attaches to a live path and interactive opens are unaffected.\n\t * The only memory-driven refusal on the in-process path - never an occupancy count.\n\t */\n\tsetWorkerAdmission(refusal: WorkerAdmissionRefusal | undefined): void {\n\t\tthis.workerRefusal = refusal;\n\t}\n\n\tasync openSession(profile: RpcSessionLaunchProfile, options?: RpcSessionOpenOptions): Promise<OpenRpcSession> {\n\t\tthis.validateProfile(profile);\n\t\tthis.syncRuntimeMetadata();\n\t\tconst sessionPath = profile.sessionPath ? canonicalSessionPath(profile.sessionPath) : undefined;\n\t\t// Taken SYNCHRONOUSLY, before any await, exactly like the path reservation below: a\n\t\t// concurrent open naming the same durable id must find this one already recorded rather\n\t\t// than a window between the decision and the record of it. Two LIVE sessions may never\n\t\t// share a durable id - every per-session artifact a client keys by it would collide.\n\t\t// Re-opening the SAME file is an attach/resume, not a collision: the id is the file's own.\n\t\tconst requestedDurableId = profile.durableSessionId;\n\t\tif (requestedDurableId !== undefined) {\n\t\t\tfor (const entry of this.entries.values()) {\n\t\t\t\tif (entry.state === \"closed\") continue;\n\t\t\t\tif (entry.durableSessionId !== requestedDurableId) continue;\n\t\t\t\tif (sessionPath !== undefined && entry.reservationKey === sessionPath) continue;\n\t\t\t\tthrow new RpcSessionRegistryError(\"session_id_in_use\");\n\t\t\t}\n\t\t}\n\t\tif (sessionPath) await this.settleClosingReservation(sessionPath);\n\t\tif (sessionPath && this.reservations.has(sessionPath)) {\n\t\t\t// Attach-on-open: a live session outlives individual client attachments, so a\n\t\t\t// resume (or a second surface) for an already-hosted path joins the existing\n\t\t\t// runtime instead of failing. An entry still opening keeps the exclusive\n\t\t\t// reservation and rejects as before.\n\t\t\tconst existing = [...this.entries].find(\n\t\t\t\t([, entry]) => entry.reservationKey === sessionPath && entry.state === \"open\",\n\t\t\t);\n\t\t\tif (!existing) throw new RpcSessionRegistryError(\"session_path_in_use\");\n\t\t\tconst [handle, entry] = existing;\n\t\t\tconst wasParked = entry.retainOnDisconnect === true && entry.attachments === 0;\n\t\t\tentry.attachments += 1;\n\t\t\t// The claim carries the attachment state another generation decides on: a path this host is\n\t\t\t// actively serving a client on is never reclaimable from it.\n\t\t\tthis.options.pathReservations?.setAttached(sessionPath, true);\n\t\t\t// Retention is a property of the live session: any attach may ask for it, and\n\t\t\t// no attach may revoke it for the clients that already rely on it.\n\t\t\tif (options?.retainOnDisconnect) entry.retainOnDisconnect = true;\n\t\t\tif (!entry.durableSessionId) throw new RpcSessionRegistryError(\"session_path_in_use\");\n\t\t\tentry.lastCommandAt = this.now();\n\t\t\tif (wasParked) {\n\t\t\t\tentry.lifecycleMutex = entry.lifecycleMutex.then(() =>\n\t\t\t\t\tentry.runtime?.emitAttachmentEvent(\"session_resumed\"),\n\t\t\t\t);\n\t\t\t\tawait entry.lifecycleMutex;\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tsessionId: handle,\n\t\t\t\tdurableSessionId: entry.durableSessionId,\n\t\t\t\tsessionPath: entry.sessionPath,\n\t\t\t\tattached: true,\n\t\t\t};\n\t\t}\n\t\tif (this.workerRefusal && profile.sessionKind === \"worker\")\n\t\t\tthrow new RpcSessionRegistryError(\"host_memory_pressure\", undefined, { ...this.workerRefusal });\n\t\tif (sessionPath) {\n\t\t\t// Taken SYNCHRONOUSLY, before any await: a concurrent open for the same path must find the\n\t\t\t// reservation already held, not a window between the decision and the record of it.\n\t\t\tthis.reservations.add(sessionPath);\n\t\t\t// Another generation of this daemon may still be writing this file. Its claim is the only\n\t\t\t// thing this process can see across a handoff, and a live one means \"retry\", not \"gone\".\n\t\t\t// Only AWAIT when there is a cross-generation claim to take: `await undefined` still costs a\n\t\t\t// microtask, and an embedded registry (no second generation) must reach the \"opening\" entry\n\t\t\t// in the same tick a caller that raced it would look, exactly as it did before generations.\n\t\t\tconst holder = this.options.pathReservations\n\t\t\t\t? await this.options.pathReservations.claim(sessionPath)\n\t\t\t\t: undefined;\n\t\t\tif (holder) {\n\t\t\t\tthis.reservations.delete(sessionPath);\n\t\t\t\tthrow new RpcSessionRegistryError(\"session_path_in_use\", undefined, {\n\t\t\t\t\towner: holder,\n\t\t\t\t\tretry_after_ms: SESSION_PATH_RETRY_AFTER_MS,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Resume vs create parity (D1 + omo SenpiSessionRuntime.ts:198-200):\n\t\t// Create-only launch semantics mirror classic startup flags. A resumed\n\t\t// session restores its persisted model and thinking level instead of being\n\t\t// overridden by the new open_session request.\n\t\tconst isResume = sessionPath !== undefined && existsSync(sessionPath);\n\t\t// Re-opening an existing session file is a resume, exactly like interactive\n\t\t// /resume (AgentSessionRuntime.switchSession). Without the event the session\n\t\t// starts with reason \"startup\" and every extension that only rebuilds state\n\t\t// on a resume - the ask-user dangling-question hook - stays unreachable from\n\t\t// the RPC restart path. A session created by this open stays \"startup\".\n\t\tconst sessionStartEvent: SessionStartEvent | undefined = isResume\n\t\t\t? { type: \"session_start\", reason: \"resume\" }\n\t\t\t: undefined;\n\t\tconst storedProfile = frozenProfile({ ...profile, ...(sessionPath ? { sessionPath } : {}) });\n\t\tconst runtimeProfile = isResume\n\t\t\t? frozenProfile({ ...storedProfile, creationModel: undefined, initialThinkingLevel: undefined })\n\t\t\t: storedProfile;\n\n\t\tconst handle = `rpc-${++this.nextHandle}`;\n\t\tconst entry: RpcSessionEntry = {\n\t\t\tstate: \"opening\",\n\t\t\tscope: new ProviderScope(),\n\t\t\tprofile: storedProfile,\n\t\t\t...sessionIdentity(storedProfile),\n\t\t\tsessionPath,\n\t\t\treservationKey: sessionPath,\n\t\t\tcwd: storedProfile.cwd,\n\t\t\tattachments: 1,\n\t\t\tretainOnDisconnect: options?.retainOnDisconnect === true,\n\t\t\tlastCommandAt: this.now(),\n\t\t\tlifecycleMutex: Promise.resolve(),\n\t\t};\n\t\tentry.switchSession = (sessionPath, options) => {\n\t\t\tconst operation = entry.lifecycleMutex.then(async () => {\n\t\t\t\tif (entry.state !== \"open\" || !entry.runtime) {\n\t\t\t\t\tthrow new RpcSessionRegistryError(\"unknown_session\");\n\t\t\t\t}\n\t\t\t\tconst runtime = entry.runtime;\n\t\t\t\tconst cwdOverride = options?.cwdOverride;\n\t\t\t\tconst cwdChanged = cwdOverride !== undefined && runtime.session.sessionManager.getCwd() !== cwdOverride;\n\t\t\t\tconst result = await runtime.switchSession(sessionPath, options);\n\t\t\t\tif (result.cancelled || !cwdChanged) return result;\n\n\t\t\t\t// A multi-session binding outlives an individual replacement. Keep the\n\t\t\t\t// entry's runtime object aligned with the replacement so every attached\n\t\t\t\t// client resolves getters and future commands against the new cwd-bound\n\t\t\t\t// runtime, not the object created during open_session.\n\t\t\t\tconst replacement = new AgentSessionRuntime(\n\t\t\t\t\truntime.session,\n\t\t\t\t\truntime.services,\n\t\t\t\t\tthis.options.createRuntime,\n\t\t\t\t\t[...runtime.diagnostics],\n\t\t\t\t\truntime.modelFallbackMessage,\n\t\t\t\t\truntime.launchProfile,\n\t\t\t\t);\n\t\t\t\treplacement.setRebindSession(entry.rebindSession);\n\t\t\t\truntime.releaseSessionHold();\n\t\t\t\tentry.runtime = replacement;\n\t\t\t\tthis.syncRuntimeMetadata();\n\t\t\t\treturn result;\n\t\t\t});\n\t\t\tentry.lifecycleMutex = operation.then(\n\t\t\t\t() => undefined,\n\t\t\t\t() => undefined,\n\t\t\t);\n\t\t\treturn operation;\n\t\t};\n\t\t// Recorded before the first await so the synchronous collision guard above sees an open\n\t\t// that is still being built. `manager.getSessionId()` overwrites it below with the\n\t\t// authoritative value, which on a resume is the header's id, not the requested one.\n\t\tif (requestedDurableId !== undefined) entry.durableSessionId = requestedDurableId;\n\t\tthis.entries.set(handle, entry);\n\t\ttry {\n\t\t\tconst newSessionOptions = requestedDurableId !== undefined ? { id: requestedDurableId } : undefined;\n\t\t\tconst manager = sessionPath\n\t\t\t\t? SessionManager.open(sessionPath, undefined, storedProfile.cwd, newSessionOptions)\n\t\t\t\t: SessionManager.create(storedProfile.cwd, undefined, newSessionOptions);\n\t\t\tentry.runtime = await runWithProviderScope(entry.scope, () =>\n\t\t\t\tcreateAgentSessionRuntime(this.options.createRuntime, {\n\t\t\t\t\tcwd: manager.getCwd(),\n\t\t\t\t\tagentDir: this.options.agentDir,\n\t\t\t\t\tsessionManager: manager,\n\t\t\t\t\tsessionStartEvent,\n\t\t\t\t\tlaunchProfile: runtimeProfile,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tentry.durableSessionId = manager.getSessionId();\n\t\t\tentry.sessionPath ??= manager.getSessionFile();\n\t\t\tentry.state = \"open\";\n\t\t\treturn { sessionId: handle, durableSessionId: entry.durableSessionId, sessionPath: entry.sessionPath };\n\t\t} catch (error) {\n\t\t\t// Runtime construction may have started extensions, watchers, and provider\n\t\t\t// registrations before it rejects. Keep the reservation and entry private\n\t\t\t// until all of those resources have been torn down, then release them as\n\t\t\t// one rollback so the path can immediately be opened again.\n\t\t\ttry {\n\t\t\t\tawait entry.runtime?.dispose();\n\t\t\t} catch {\n\t\t\t\t// The original construction error remains the externally visible cause.\n\t\t\t} finally {\n\t\t\t\ttry {\n\t\t\t\t\tawait entry.scope.close?.();\n\t\t\t\t} finally {\n\t\t\t\t\tthis.entries.delete(handle);\n\t\t\t\t\tif (sessionPath) {\n\t\t\t\t\t\tthis.reservations.delete(sessionPath);\n\t\t\t\t\t\tthis.options.pathReservations?.release(sessionPath);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (error instanceof RpcSessionRegistryError) throw error;\n\t\t\tthrow new RpcSessionRegistryError(\"open_failed\", error instanceof Error ? error.message : undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Waits out a teardown already in flight for this path before the open decides.\n\t *\n\t * A close FREES the path, but the entry keeps its reservation until its runtime is\n\t * disposed, so an open landing inside that window used to be refused with\n\t * `session_path_in_use` for a session that no longer exists - making \"reopen the\n\t * path I just closed\" a race against disposal latency, which no client can time.\n\t * The open now waits for the teardown it would have been refused by and then opens\n\t * the file fresh. Bounded by the same grace window that bounds the teardown itself\n\t * (`closeMarkedSession` force-releases at that deadline), so a wedged disposal\n\t * still ends in the ordinary refusal instead of an open that never answers.\n\t */\n\tprivate async settleClosingReservation(sessionPath: string): Promise<void> {\n\t\tconst closing = [...this.entries.values()].find(\n\t\t\t(entry) => entry.reservationKey === sessionPath && entry.state === \"closing\",\n\t\t);\n\t\tif (!closing?.closeCompletion) return;\n\t\tlet deadline: ReturnType<typeof setTimeout> | undefined;\n\t\ttry {\n\t\t\tawait Promise.race([\n\t\t\t\tclosing.closeCompletion,\n\t\t\t\tnew Promise<void>((resolve) => {\n\t\t\t\t\tdeadline = setTimeout(resolve, this.closeGraceMs);\n\t\t\t\t}),\n\t\t\t]);\n\t\t} finally {\n\t\t\tif (deadline) clearTimeout(deadline);\n\t\t}\n\t}\n\n\t/**\n\t * Read-only lookup with no state transitions or attachment accounting.\n\t * Exists so lifecycle decisions (e.g. deferring a dropped connection's\n\t * release while a turn is still streaming) can inspect the live entry\n\t * without claiming it.\n\t */\n\tpeek(handle: string): RpcSessionEntry | undefined {\n\t\treturn this.entries.get(handle);\n\t}\n\n\tgetForCommand(handle: string, command: string): RpcSessionEntry {\n\t\tconst entry = this.entries.get(handle);\n\t\tif (!entry) throw new RpcSessionRegistryError(\"unknown_session\");\n\t\tif (\n\t\t\tentry.state === \"closing\" &&\n\t\t\t![\"abort\", \"abort_bash\", \"extension_ui_response\", \"extension_ui_progress\"].includes(command)\n\t\t) {\n\t\t\tthrow new RpcSessionRegistryError(\"session_closing\");\n\t\t}\n\t\tif (entry.state !== \"open\" && entry.state !== \"closing\") throw new RpcSessionRegistryError(\"unknown_session\");\n\t\t// Every routed command counts as activity for idle eviction. Lookups that\n\t\t// must not refresh idleness (sweeps, listing) use peek()/list() instead.\n\t\tentry.lastCommandAt = this.now();\n\t\treturn entry;\n\t}\n\n\t/**\n\t * Starts a close synchronously and returns the live entry for routing decisions.\n\t * `detach` marks a client going away rather than the session ending, which a\n\t * retained entry answers by staying open at zero attachments.\n\t */\n\tbeginClose(handle: string, onRole?: (finalizer: boolean) => void, options?: { detach?: boolean }): RpcSessionEntry {\n\t\treturn beginSessionClose(this.teardownHost, handle, onRole, options);\n\t}\n\n\tasync close(handle: string): Promise<void> {\n\t\treturn closeSession(this.teardownHost, handle);\n\t}\n\n\t/** Completes a close previously made visible by beginClose(). */\n\tasync closeMarked(handle: string): Promise<void> {\n\t\treturn closeMarkedSession(this.teardownHost, handle);\n\t}\n\n\tlist(): RpcSessionRow[] {\n\t\tthis.syncRuntimeMetadata();\n\t\treturn [...this.entries].map(([sessionId, entry]) => ({\n\t\t\tsessionId,\n\t\t\tdurableSessionId: entry.durableSessionId,\n\t\t\tsessionPath: entry.sessionPath,\n\t\t\tcwd: entry.cwd,\n\t\t\tname: entry.runtime?.session.sessionManager.getSessionName(),\n\t\t\tkind: entry.kind,\n\t\t\tcontext: entry.context,\n\t\t\t// A closing entry has already released its last attachment; never publish that as negative.\n\t\t\tattachments: Math.max(0, entry.attachments),\n\t\t\tstatus: entry.state === \"quarantined\" ? \"closing\" : entry.state,\n\t\t}));\n\t}\n\n\t/** Reconcile path and durable identity after runtime replacement. */\n\tprivate syncRuntimeMetadata(): void {\n\t\tfor (const entry of this.entries.values()) {\n\t\t\tconst manager = entry.runtime?.session.sessionManager;\n\t\t\tif (!manager) continue;\n\t\t\tconst currentPath = manager.getSessionFile();\n\t\t\tconst currentKey = currentPath ? canonicalSessionPath(currentPath) : undefined;\n\t\t\t// Preserve the originally canonicalized key while the runtime still points at\n\t\t\t// the same path. SessionManager may expose a symlink-resolved spelling after\n\t\t\t// opening a file that did not exist yet; treating that as replacement would\n\t\t\t// break ordinary attach-on-open aliases.\n\t\t\t//\n\t\t\t// A moved path is not the only reason to reconcile: a session opened WITHOUT\n\t\t\t// `sessionPath` lands its created file straight into `sessionPath` and never\n\t\t\t// takes a reservation, so comparing paths alone leaves that file unclaimed\n\t\t\t// forever and a later open of it builds a SECOND runtime over the same\n\t\t\t// transcript. Reconcile whenever the canonical key we hold is not the key the\n\t\t\t// runtime is actually writing.\n\t\t\tif (currentPath !== entry.sessionPath || currentKey !== entry.reservationKey) {\n\t\t\t\tif (entry.reservationKey) {\n\t\t\t\t\tthis.reservations.delete(entry.reservationKey);\n\t\t\t\t\tthis.options.pathReservations?.release(entry.reservationKey);\n\t\t\t\t}\n\t\t\t\tif (currentKey) {\n\t\t\t\t\tthis.reservations.add(currentKey);\n\t\t\t\t\t// A replacement moved this session to another file; the claim follows it, carrying the\n\t\t\t\t\t// attachment state it is now held at. A file a live foreign generation holds is left\n\t\t\t\t\t// alone by claim() itself.\n\t\t\t\t\tvoid this.options.pathReservations?.claim(currentKey, entry.attachments > 0);\n\t\t\t\t}\n\t\t\t\tentry.reservationKey = currentKey;\n\t\t\t\tentry.sessionPath = currentPath;\n\t\t\t}\n\t\t\tentry.durableSessionId = manager.getSessionId();\n\t\t\tentry.cwd = manager.getCwd();\n\t\t}\n\t}\n\n\tprivate validateProfile(profile: RpcSessionLaunchProfile): void {\n\t\tif (!isAbsolute(profile.cwd) || (profile.sessionPath !== undefined && !isAbsolute(profile.sessionPath))) {\n\t\t\tthrow new RpcSessionRegistryError(\"invalid_path\");\n\t\t}\n\t\tif (profile.durableSessionId !== undefined) {\n\t\t\ttry {\n\t\t\t\tassertValidSessionId(profile.durableSessionId);\n\t\t\t} catch (cause) {\n\t\t\t\tthrow new RpcSessionRegistryError(\"invalid_session_id\", String(cause));\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import type { RpcSessionRegistry } from "./session-registry.ts";
2
+ export interface SweepVerdicts {
3
+ /** Open sessions idle past the window: a retained one parks, any other closes as `idle_evicted`. */
4
+ readonly idle: readonly string[];
5
+ /**
6
+ * Sessions no client holds whose transcript directory is gone. Nothing can reopen them by path
7
+ * and they can never persist again, so they close as `session_dir_removed` (senpi#2206).
8
+ */
9
+ readonly orphaned: readonly string[];
10
+ }
11
+ /**
12
+ * One occupancy sweep's decisions. "Idle" is the COMPLETE session-owned activity contract
13
+ * (`AgentSession.isSessionBusy`: agent run, bash, background terminal jobs and other published
14
+ * wake sources, compaction, barrier-held session work): a busy session restarts its idle clock
15
+ * instead, so work that outlives a turn is never killed. A non-finite window evicts nothing idle.
16
+ */
17
+ export declare function selectSweepEvictions(registry: Pick<RpcSessionRegistry, "list" | "peek">, now: number, idleEvictionMs: number): SweepVerdicts;
18
+ //# sourceMappingURL=session-sweep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-sweep.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/session-sweep.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,MAAM,WAAW,aAAa;IAC7B,oGAAoG;IACpG,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CACnC,QAAQ,EAAE,IAAI,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAAC,EACnD,GAAG,EAAE,MAAM,EACX,cAAc,EAAE,MAAM,GACpB,aAAa,CAmBf"}
@@ -0,0 +1,32 @@
1
+ import { sessionDirectoryRemoved } from "./session-path-key.js";
2
+ /**
3
+ * One occupancy sweep's decisions. "Idle" is the COMPLETE session-owned activity contract
4
+ * (`AgentSession.isSessionBusy`: agent run, bash, background terminal jobs and other published
5
+ * wake sources, compaction, barrier-held session work): a busy session restarts its idle clock
6
+ * instead, so work that outlives a turn is never killed. A non-finite window evicts nothing idle.
7
+ */
8
+ export function selectSweepEvictions(registry, now, idleEvictionMs) {
9
+ const idle = [];
10
+ const orphaned = [];
11
+ for (const { sessionId, status, sessionPath } of registry.list()) {
12
+ if (status !== "open")
13
+ continue;
14
+ const entry = registry.peek(sessionId);
15
+ if (!entry)
16
+ continue;
17
+ if (entry.attachments === 0 && sessionPath !== undefined && sessionDirectoryRemoved(sessionPath)) {
18
+ orphaned.push(sessionId);
19
+ continue;
20
+ }
21
+ if (!Number.isFinite(idleEvictionMs))
22
+ continue;
23
+ if (entry.worker?.busy || entry.runtime?.session.isSessionBusy) {
24
+ entry.lastCommandAt = now;
25
+ continue;
26
+ }
27
+ if (now - entry.lastCommandAt >= idleEvictionMs)
28
+ idle.push(sessionId);
29
+ }
30
+ return { idle, orphaned };
31
+ }
32
+ //# sourceMappingURL=session-sweep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-sweep.js","sourceRoot":"","sources":["../../../src/modes/rpc/session-sweep.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAahE;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CACnC,QAAmD,EACnD,GAAW,EACX,cAAsB;IAEtB,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;QAClE,IAAI,MAAM,KAAK,MAAM;YAAE,SAAS;QAChC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,IAAI,KAAK,CAAC,WAAW,KAAK,CAAC,IAAI,WAAW,KAAK,SAAS,IAAI,uBAAuB,CAAC,WAAW,CAAC,EAAE,CAAC;YAClG,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACzB,SAAS;QACV,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC;YAAE,SAAS;QAC/C,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,IAAI,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC;YAChE,KAAK,CAAC,aAAa,GAAG,GAAG,CAAC;YAC1B,SAAS;QACV,CAAC;QACD,IAAI,GAAG,GAAG,KAAK,CAAC,aAAa,IAAI,cAAc;YAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC3B,CAAC","sourcesContent":["import { sessionDirectoryRemoved } from \"./session-path-key.ts\";\nimport type { RpcSessionRegistry } from \"./session-registry.ts\";\n\nexport interface SweepVerdicts {\n\t/** Open sessions idle past the window: a retained one parks, any other closes as `idle_evicted`. */\n\treadonly idle: readonly string[];\n\t/**\n\t * Sessions no client holds whose transcript directory is gone. Nothing can reopen them by path\n\t * and they can never persist again, so they close as `session_dir_removed` (senpi#2206).\n\t */\n\treadonly orphaned: readonly string[];\n}\n\n/**\n * One occupancy sweep's decisions. \"Idle\" is the COMPLETE session-owned activity contract\n * (`AgentSession.isSessionBusy`: agent run, bash, background terminal jobs and other published\n * wake sources, compaction, barrier-held session work): a busy session restarts its idle clock\n * instead, so work that outlives a turn is never killed. A non-finite window evicts nothing idle.\n */\nexport function selectSweepEvictions(\n\tregistry: Pick<RpcSessionRegistry, \"list\" | \"peek\">,\n\tnow: number,\n\tidleEvictionMs: number,\n): SweepVerdicts {\n\tconst idle: string[] = [];\n\tconst orphaned: string[] = [];\n\tfor (const { sessionId, status, sessionPath } of registry.list()) {\n\t\tif (status !== \"open\") continue;\n\t\tconst entry = registry.peek(sessionId);\n\t\tif (!entry) continue;\n\t\tif (entry.attachments === 0 && sessionPath !== undefined && sessionDirectoryRemoved(sessionPath)) {\n\t\t\torphaned.push(sessionId);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!Number.isFinite(idleEvictionMs)) continue;\n\t\tif (entry.worker?.busy || entry.runtime?.session.isSessionBusy) {\n\t\t\tentry.lastCommandAt = now;\n\t\t\tcontinue;\n\t\t}\n\t\tif (now - entry.lastCommandAt >= idleEvictionMs) idle.push(sessionId);\n\t}\n\treturn { idle, orphaned };\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import { type ScanMigrationName } from "./migrations-state.ts";
2
+ /** Copies back what an earlier start moved out of `~/.pi`, only into an agent/mom dir holding no user state. */
3
+ export declare function restoreDrainedPiDirs(completed: ReadonlySet<ScanMigrationName>, homeDir?: string): void;
4
+ //# sourceMappingURL=pi-dir-restore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi-dir-restore.d.ts","sourceRoot":"","sources":["../src/pi-dir-restore.ts"],"names":[],"mappings":"AAWA,OAAO,EAA6B,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAkE1F,gHAAgH;AAChH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,WAAW,CAAC,iBAAiB,CAAC,EAAE,OAAO,GAAE,MAAkB,GAAG,IAAI,CAejH"}
@@ -0,0 +1,92 @@
1
+ import { existsSync, lstatSync, readdirSync, readFileSync, rmSync, statSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import chalk from "chalk";
5
+ import { CONFIG_DIR_NAME, getAgentDir } from "./config.js";
6
+ import { copyMissingEntries, isRegenerableEntry, isWithinOrSamePath, pathsPointToSameLocation, } from "./legacy-dir-copy.js";
7
+ import { MIGRATIONS_STATE_FILENAME } from "./migrations-state.js";
8
+ /** Upstream pi's agent-dir entries; anything else in our agent dir is our own state and never goes to pi. */
9
+ const UPSTREAM_PI_AGENT_ENTRIES = new Set([
10
+ "auth.json",
11
+ "settings.json",
12
+ "models.json",
13
+ "models-store.json",
14
+ "keybindings.json",
15
+ "sessions",
16
+ "extensions",
17
+ "skills",
18
+ "prompts",
19
+ "themes",
20
+ "npm",
21
+ "git",
22
+ "experimental",
23
+ "AGENTS.md",
24
+ "CLAUDE.md",
25
+ "SYSTEM.md",
26
+ "APPEND_SYSTEM.md",
27
+ ]);
28
+ function isDirectory(path) {
29
+ return existsSync(path) && statSync(path).isDirectory();
30
+ }
31
+ /**
32
+ * Before this release, the first start moved `~/.pi/agent` and `~/.pi/mom` into this product's
33
+ * directories. A recorded run of that migration, or engine state written by a build older than the
34
+ * migrations state file, is the evidence that it may have happened on this agent dir.
35
+ */
36
+ function movingMigrationMayHaveRun(agentDir, completed) {
37
+ if (completed.has("migrateLegacySenpiDirs"))
38
+ return true;
39
+ if (existsSync(join(agentDir, MIGRATIONS_STATE_FILENAME)))
40
+ return false;
41
+ return isDirectory(agentDir) && readdirSync(agentDir).some((entry) => UPSTREAM_PI_AGENT_ENTRIES.has(entry));
42
+ }
43
+ /** pi writes `{}` into `auth.json` and `models-store.json` when it starts with an empty directory. */
44
+ function isEmptyJsonStub(path) {
45
+ const stat = lstatSync(path);
46
+ if (!stat.isFile() || stat.size > 16)
47
+ return false;
48
+ const text = readFileSync(path, "utf-8").trim();
49
+ return text === "{}" || text === "[]";
50
+ }
51
+ function holdsNoUserState(dir) {
52
+ if (!existsSync(dir))
53
+ return true;
54
+ return readdirSync(dir).every((entry) => isRegenerableEntry(entry) || isEmptyJsonStub(join(dir, entry)));
55
+ }
56
+ function restoreDir(from, to, include) {
57
+ if (!isDirectory(from) || !holdsNoUserState(to))
58
+ return;
59
+ if (existsSync(to) && pathsPointToSameLocation(from, to))
60
+ return;
61
+ for (const entry of existsSync(to) ? readdirSync(to) : []) {
62
+ if (include(entry) && existsSync(join(from, entry)) && isEmptyJsonStub(join(to, entry))) {
63
+ rmSync(join(to, entry));
64
+ }
65
+ }
66
+ const restored = copyMissingEntries(from, to, include);
67
+ if (restored.length > 0) {
68
+ console.log(chalk.green(`Restored ${to} from ${from}: an earlier start had moved it there.`));
69
+ console.log(chalk.dim("Both directories now hold independent copies."));
70
+ }
71
+ }
72
+ /** Copies back what an earlier start moved out of `~/.pi`, only into an agent/mom dir holding no user state. */
73
+ export function restoreDrainedPiDirs(completed, homeDir = homedir()) {
74
+ if (CONFIG_DIR_NAME === ".pi")
75
+ return;
76
+ const agentDir = getAgentDir();
77
+ const brandDir = join(homeDir, CONFIG_DIR_NAME);
78
+ const piDir = join(homeDir, ".pi");
79
+ if (!isWithinOrSamePath(agentDir, brandDir) || !isDirectory(piDir))
80
+ return;
81
+ if (!movingMigrationMayHaveRun(agentDir, completed))
82
+ return;
83
+ try {
84
+ restoreDir(agentDir, join(piDir, "agent"), (entry) => UPSTREAM_PI_AGENT_ENTRIES.has(entry));
85
+ restoreDir(join(brandDir, "mom"), join(piDir, "mom"), (entry) => !isRegenerableEntry(entry));
86
+ }
87
+ catch (error) {
88
+ const reason = error instanceof Error ? error.message : String(error);
89
+ console.warn(chalk.yellow(`Could not restore ${piDir}: ${reason}`));
90
+ }
91
+ }
92
+ //# sourceMappingURL=pi-dir-restore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pi-dir-restore.js","sourceRoot":"","sources":["../src/pi-dir-restore.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, lstatSync, readdirSync, readFileSync, rmSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport chalk from \"chalk\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"./config.ts\";\nimport {\n\tcopyMissingEntries,\n\tisRegenerableEntry,\n\tisWithinOrSamePath,\n\tpathsPointToSameLocation,\n} from \"./legacy-dir-copy.ts\";\nimport { MIGRATIONS_STATE_FILENAME, type ScanMigrationName } from \"./migrations-state.ts\";\n\n/** Upstream pi's agent-dir entries; anything else in our agent dir is our own state and never goes to pi. */\nconst UPSTREAM_PI_AGENT_ENTRIES: ReadonlySet<string> = new Set([\n\t\"auth.json\",\n\t\"settings.json\",\n\t\"models.json\",\n\t\"models-store.json\",\n\t\"keybindings.json\",\n\t\"sessions\",\n\t\"extensions\",\n\t\"skills\",\n\t\"prompts\",\n\t\"themes\",\n\t\"npm\",\n\t\"git\",\n\t\"experimental\",\n\t\"AGENTS.md\",\n\t\"CLAUDE.md\",\n\t\"SYSTEM.md\",\n\t\"APPEND_SYSTEM.md\",\n]);\n\nfunction isDirectory(path: string): boolean {\n\treturn existsSync(path) && statSync(path).isDirectory();\n}\n\n/**\n * Before this release, the first start moved `~/.pi/agent` and `~/.pi/mom` into this product's\n * directories. A recorded run of that migration, or engine state written by a build older than the\n * migrations state file, is the evidence that it may have happened on this agent dir.\n */\nfunction movingMigrationMayHaveRun(agentDir: string, completed: ReadonlySet<ScanMigrationName>): boolean {\n\tif (completed.has(\"migrateLegacySenpiDirs\")) return true;\n\tif (existsSync(join(agentDir, MIGRATIONS_STATE_FILENAME))) return false;\n\treturn isDirectory(agentDir) && readdirSync(agentDir).some((entry) => UPSTREAM_PI_AGENT_ENTRIES.has(entry));\n}\n\n/** pi writes `{}` into `auth.json` and `models-store.json` when it starts with an empty directory. */\nfunction isEmptyJsonStub(path: string): boolean {\n\tconst stat = lstatSync(path);\n\tif (!stat.isFile() || stat.size > 16) return false;\n\tconst text = readFileSync(path, \"utf-8\").trim();\n\treturn text === \"{}\" || text === \"[]\";\n}\n\nfunction holdsNoUserState(dir: string): boolean {\n\tif (!existsSync(dir)) return true;\n\treturn readdirSync(dir).every((entry) => isRegenerableEntry(entry) || isEmptyJsonStub(join(dir, entry)));\n}\n\nfunction restoreDir(from: string, to: string, include: (entry: string) => boolean): void {\n\tif (!isDirectory(from) || !holdsNoUserState(to)) return;\n\tif (existsSync(to) && pathsPointToSameLocation(from, to)) return;\n\tfor (const entry of existsSync(to) ? readdirSync(to) : []) {\n\t\tif (include(entry) && existsSync(join(from, entry)) && isEmptyJsonStub(join(to, entry))) {\n\t\t\trmSync(join(to, entry));\n\t\t}\n\t}\n\tconst restored = copyMissingEntries(from, to, include);\n\tif (restored.length > 0) {\n\t\tconsole.log(chalk.green(`Restored ${to} from ${from}: an earlier start had moved it there.`));\n\t\tconsole.log(chalk.dim(\"Both directories now hold independent copies.\"));\n\t}\n}\n\n/** Copies back what an earlier start moved out of `~/.pi`, only into an agent/mom dir holding no user state. */\nexport function restoreDrainedPiDirs(completed: ReadonlySet<ScanMigrationName>, homeDir: string = homedir()): void {\n\tif (CONFIG_DIR_NAME === \".pi\") return;\n\tconst agentDir = getAgentDir();\n\tconst brandDir = join(homeDir, CONFIG_DIR_NAME);\n\tconst piDir = join(homeDir, \".pi\");\n\tif (!isWithinOrSamePath(agentDir, brandDir) || !isDirectory(piDir)) return;\n\tif (!movingMigrationMayHaveRun(agentDir, completed)) return;\n\n\ttry {\n\t\trestoreDir(agentDir, join(piDir, \"agent\"), (entry) => UPSTREAM_PI_AGENT_ENTRIES.has(entry));\n\t\trestoreDir(join(brandDir, \"mom\"), join(piDir, \"mom\"), (entry) => !isRegenerableEntry(entry));\n\t} catch (error) {\n\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\tconsole.warn(chalk.yellow(`Could not restore ${piDir}: ${reason}`));\n\t}\n}\n"]}
@@ -87,6 +87,8 @@ These variables are read by Pi itself:
87
87
  | `PI_PACKAGE_DIR` | Override the package directory, useful for Nix/Guix store paths |
88
88
  | `PI_OFFLINE` | Disable startup network operations, including update checks, package updates, and install/update telemetry |
89
89
  | `PI_SKIP_VERSION_CHECK` | Disable the `pi.dev` latest-version request |
90
+ | `SENPI_RUNTIME` | `node` keeps senpi on Node.js and hides the Node.js runtime notice; `bun` re-execs under any installed Bun. Without it, a Bun-global install runs on its Bun and any other installed package runs on a discovered Bun 1.4.0 or newer |
91
+ | `PI_SKIP_RUNTIME_NOTICE` | Hide the one-time "Running on Node.js" notice without changing the runtime |
90
92
  | `PI_TELEMETRY` | Override install/update telemetry and provider attribution headers: `1`/`true`/`yes` or `0`/`false`/`no` |
91
93
  | `PI_CACHE_RETENTION` | Set to `long` to opt into extended provider prompt caching where supported; direct Anthropic defaults to 5 minutes |
92
94
  | `PI_SHARE_VIEWER_URL` | Override the base URL used by `/share` |
@@ -936,6 +936,17 @@ pi.on("thinking_level_select", async (event, ctx) => {
936
936
 
937
937
  Use this to update extension UI when `pi.setThinkingLevel()`, model changes, or built-in thinking-level controls change the active thinking level.
938
938
 
939
+ #### tool_activated
940
+
941
+ Fired after the active tool set gains tools. This covers `pi.setActiveTools()`, a `tool_search` promotion, and a by-name call that activates a deferred tool. The event is notification-only: handler return values are ignored.
942
+
943
+ ```typescript
944
+ pi.on("tool_activated", async (event, ctx) => {
945
+ // event.toolNames - only the tools that just became active
946
+ if (event.toolNames.includes("my_tool")) await warmUp(ctx);
947
+ });
948
+ ```
949
+
939
950
  ### Tool Events
940
951
 
941
952
  #### tool_call
@@ -1676,6 +1687,21 @@ Use `promptSnippet` to opt a custom tool into a one-line entry in `Available too
1676
1687
 
1677
1688
  **Important:** `promptGuidelines` bullets are appended flat to the `Guidelines` section with no tool name prefix. Each guideline must name the tool it refers to — avoid "Use this tool when..." because the LLM cannot tell which tool "this" means. Write "Use my_tool when..." instead.
1678
1689
 
1690
+ Use `kernelPrelude` to give the `eval` kernels globals that call your tool while it is active. It takes `javascript` and `python` statements, one `documentation` line for the eval prompt's helper list, and the `exports` those statements define. Each snippet runs before a cell only when one of its exports is missing, and a deactivated tool's exports are removed before the next cell. Exports must not shadow built-in kernel helpers such as `display` or `tool`.
1691
+
1692
+ Use `permissionParser` to classify your tool's calls for the permission system. It receives the call input and the working directory and returns requests (`permission`, `patterns`, `always`), so a rule like `my_tool:read=allow` can grant one tier while another stays gated. Without it, every call is one request named after the tool. A built-in parser for the same tool name always wins.
1693
+
1694
+ ```typescript
1695
+ pi.registerTool({
1696
+ name: "my_tool",
1697
+ // ...
1698
+ permissionParser: (input) => {
1699
+ const tier = input.action === "add" ? "exec" : "read";
1700
+ return [{ permission: "my_tool", patterns: [tier], always: [tier] }];
1701
+ },
1702
+ });
1703
+ ```
1704
+
1679
1705
  See [dynamic-tools.ts](../examples/extensions/dynamic-tools.ts) for a full example.
1680
1706
 
1681
1707
  ```typescript
@@ -2126,7 +2152,7 @@ pi.setActiveTools([...new Set([...active, "my_custom_tool"])]); // Keep current
2126
2152
  pi.setActiveTools(["read", "bash"]); // Switch to read-only
2127
2153
  ```
2128
2154
 
2129
- `pi.getAllTools()` returns `name`, `description`, `parameters`, `promptGuidelines`, and `sourceInfo`.
2155
+ `pi.getAllTools()` returns `name`, `description`, `parameters`, `promptGuidelines`, `kernelPrelude`, `permissionParser`, and `sourceInfo`.
2130
2156
 
2131
2157
  Typical `sourceInfo.source` values:
2132
2158
  - `builtin` for built-in tools
@@ -13,12 +13,18 @@ Senpi is distributed as an npm package:
13
13
  bun add -g @code-yeongyu/senpi
14
14
  ```
15
15
 
16
+ Senpi runs on Bun whenever it can: a `bun add -g` install always does, and an npm, pnpm, or Yarn
17
+ install hands itself to Bun 1.4.0 or newer when one is installed. If senpi still starts on Node.js
18
+ (no Bun, or an older one), the interactive UI shows a "Running on Node.js" notice once per version
19
+ with the commands to install or upgrade Bun and reinstall senpi with it. Set `SENPI_RUNTIME=node`
20
+ to stay on Node.js without the notice.
21
+
16
22
  ### Uninstall
17
23
 
18
24
  Use the package manager that installed senpi:
19
25
 
20
26
  ```bash
21
- # bun add -g
27
+ # npm
22
28
  npm uninstall -g @code-yeongyu/senpi
23
29
 
24
30
  # pnpm
package/docs/rpc.md CHANGED
@@ -735,8 +735,10 @@ REPORT: nothing here aborts a turn, kills a session, or refuses an `open_session
735
735
  `position` is 1-based and `in_flight` counts opens already accepted across the whole host,
736
736
  since every session shares one loop. It is addressed to the opener only, carries the request
737
737
  id under `for_request` rather than the response-id field, and is dropped if that connection
738
- has disconnected. A client that later times out can report where it was queued instead of a
739
- bare deadline.
738
+ has disconnected. The bundled `RpcClient` treats it as the host's acknowledgement: an
739
+ acknowledged open waits up to 10 minutes for its response instead of the 30 s request
740
+ deadline (a busy host was measured answering after 57 s), and a timeout after it names the
741
+ queue position instead of a bare deadline. A lost transport still rejects at once.
740
742
  `SENPI_RPC_LOOP_LAG_ERROR_MS` (default 5000) additionally broadcasts a `host_stalled` record
741
743
  (`{ type, driftMs, sessionId?, tool? }`) to every connection, like the other content-free lifecycle records.
742
744
  - **Stall attribution**: each routed command is dispatched inside an `AsyncLocalStorage` scope carrying its routing
@@ -1135,6 +1137,8 @@ The `model` field is a full [Model](#model) object or `null`. The `sessionName`
1135
1137
 
1136
1138
  `serviceTier` is the tier a request would carry right now (`"auto"`, `"flex"`, or `"priority"`), omitted when no tier applies. `fastMode` is `true` when the active model is served at the priority ("fast") tier — either because fast mode is on for this session or because the model selection itself pins `priority`. The two never disagree: whenever `fastMode` is `true`, `serviceTier` is `"priority"`.
1137
1139
 
1140
+ `lastProviderDiagnostic` is present after a failed turn whose provider supplied structured evidence: `{"category": "rate_limit", "httpStatus": 429, "code": "rate_limit_error", "evidence": "structured_code"}`. `category` is one of `auth`, `rate_limit`, `quota`, `context_limit`, `invalid_request`, `provider_unavailable`, `unknown`; `httpStatus` is absent for errors delivered inside a streamed response. It describes the same failure as the latest assistant `errorMessage` and is replaced or cleared with it. The failed assistant message in `message_end`, `agent_end`, `get_messages` and the session file carries the same object as `providerDiagnostic`. See [Provider failure diagnostics](sdk.md#provider-failure-diagnostics) for how it is derived.
1141
+
1138
1142
  #### get_messages
1139
1143
 
1140
1144
  Get all messages in the conversation.
@@ -2154,7 +2158,7 @@ Events are streamed to stdout as JSON lines during agent operation. Events do no
2154
2158
  | `loaded_surfaces_changed` | Loaded skills, extensions, or MCP inventory changed; re-read `get_commands` and `get_loaded_surfaces` |
2155
2159
  | `model_changed` | Active model changed (any source), with the thinking level in force afterwards |
2156
2160
  | `service_tier_changed` | Effective service tier or fast-mode state changed |
2157
- | `session_closed` | Multi-session host: a routing handle ended. Optional `reason`: `client_close`, `idle_evicted`, `host_shutdown`, `replaced`, `handoff_parked`, `error` |
2161
+ | `session_closed` | Multi-session host: a routing handle ended. Optional `reason`: `client_close`, `idle_evicted`, `host_shutdown`, `replaced`, `handoff_parked`, `session_dir_removed`, `error` |
2158
2162
  | `session_parked` | Multi-session host: a retained session was released to disk at the idle window (`sessionId`, `sessionPath`). Replaces `session_closed` for that handle |
2159
2163
  | `host_stalled` | Multi-session host: the event loop was blocked past `SENPI_RPC_LOOP_LAG_ERROR_MS`, with the drift and the session/tool blamed for it |
2160
2164
  | `host_memory_pressure` | Multi-session host: RSS is above `SENPI_RPC_HOST_RSS_WARN_MB`, with the live session count |
@@ -2180,6 +2184,7 @@ When a multi-session host ends a routing handle it may name why:
2180
2184
  | --- | --- |
2181
2185
  | `client_close` | An attached client sent `close_session` |
2182
2186
  | `idle_evicted` | The idle sweep ended a session that was not retained |
2187
+ | `session_dir_removed` | No client held the session and its transcript directory was deleted; the sweep ended it, whatever the idle window |
2183
2188
  | `host_shutdown` | The host process is exiting (SIGTERM, idle-exit, empty-host). A retained session is closed, not parked |
2184
2189
  | `replaced` | The routing handle ended because the live session behind it was replaced |
2185
2190
  | `handoff_parked` | A generation handoff drained this host; reopen with `open_session { sessionPath }` |
package/docs/sdk.md CHANGED
@@ -252,6 +252,7 @@ const state = session.agent.state;
252
252
  // state.tools: AgentTool[] - available tools
253
253
  // state.streamingMessage?: AgentMessage - current partial assistant message
254
254
  // state.errorMessage?: string - latest assistant error
255
+ // state.providerDiagnostic?: ProviderDiagnostic - structured family of that error, when known
255
256
 
256
257
  // Replace messages (useful for branching or restoration)
257
258
  session.agent.state.messages = messages; // copies the top-level array
@@ -263,6 +264,34 @@ session.agent.state.tools = tools; // copies the top-level array
263
264
  await session.agent.waitForIdle();
264
265
  ```
265
266
 
267
+ #### Provider failure diagnostics
268
+
269
+ A failed turn is an assistant message with `stopReason: "error"` and a human-readable `errorMessage`. When the provider rejected the request with structured evidence, the same message also carries an optional `providerDiagnostic`, so callers can branch on the failure family without parsing `errorMessage`:
270
+
271
+ ```typescript
272
+ // Shape of ProviderDiagnostic, exported from "@earendil-works/pi-ai":
273
+ // {
274
+ // category: "auth" | "rate_limit" | "quota" | "context_limit" | "invalid_request" | "provider_unavailable" | "unknown";
275
+ // httpStatus?: number; // 400..599; absent for an error delivered inside an HTTP 200 stream
276
+ // code?: string; // provider token from a closed allowlist, e.g. "rate_limit_error", "insufficient_quota"
277
+ // evidence: "structured_status" | "structured_code";
278
+ // }
279
+ import type { ProviderDiagnostic } from "@earendil-works/pi-ai";
280
+
281
+ session.subscribe((event) => {
282
+ if (event.type !== "message_end" || event.message.role !== "assistant") return;
283
+ if (event.message.providerDiagnostic?.category === "auth") promptForLogin();
284
+ });
285
+
286
+ // After the run settles, the latest failure is also on the agent state
287
+ const diagnostic: ProviderDiagnostic | undefined = session.agent.state.providerDiagnostic;
288
+ ```
289
+
290
+ - Only the Anthropic Messages and OpenAI-compatible Chat Completions adapters mint it today, from the HTTP status and error code of the SDK error their own transport call raised, an Anthropic `event: error` SSE envelope, or an OpenAI-compatible in-stream `error` chunk. Message text, response headers, request ids, and errors thrown by caller callbacks such as `onPayload` never produce one.
291
+ - A recognized `code` decides the category (`evidence: "structured_code"`); without one, the status alone decides it (`evidence: "structured_status"`: 401 is `auth`, 400 `invalid_request`, 5xx `provider_unavailable`, and any other 4xx such as a bare 402/403/429 is `unknown`). A code that contradicts the status, or a status outside 400..599, yields no diagnostic. `billing_error` maps to `unknown`, not `quota`.
292
+ - The field is at most 512 bytes. It is additive: `errorMessage`, retries, model fallback and CLI exit codes are identical with or without it. It is absent on aborted turns and on failures without structured provider evidence.
293
+ - Values that crossed a boundary (a persisted session, a wire message) can be revalidated with `sanitizeProviderDiagnostic(value)` from `@earendil-works/pi-ai`, which returns a fresh canonical copy or `undefined`.
294
+
266
295
  ### Events
267
296
 
268
297
  Subscribe to events to receive streaming output and lifecycle notifications.
package/docs/sessions.md CHANGED
@@ -13,10 +13,23 @@ senpi --no-session # Ephemeral mode; do not save
13
13
  senpi --name "my task" # Set session display name at startup
14
14
  senpi --session <path|id> # Use a specific session file or partial session ID
15
15
  senpi --fork <path|id> # Fork a session file or partial session ID into a new session
16
+ senpi --rebind <path|id> # Move a session of this repository, recorded at another path, here
16
17
  ```
17
18
 
18
19
  Use `/session` in interactive mode to see the current session file, session ID, message count, tokens, and cost.
19
20
 
21
+ ### Moved or re-cloned repositories
22
+
23
+ Sessions are filed by the directory they started in. Each session also records which git repository that directory belonged to (its root commit and `origin` remote), so it can still be recognised after the repository moves.
24
+
25
+ When `--session <id>` (or a pick in `--resume`) finds a session filed under another path and the current directory is the same git repository, senpi shows both paths and offers to move the session here. Answering `y` rebinds it: the session file moves to the current project, its recorded working directory is updated, and it keeps its id, history, goal, loops, and monitors. The old path no longer lists it. Use `--fork <id>` to copy it into a new session instead. A session from a different repository gets the fork prompt as before.
26
+
27
+ The in-session `/resume` selector asks the same question when you pick such a session; answering No opens it where it is. The current-folder view of `/resume` and `--resume` also lists this repository's sessions whose old path no longer exists, marked "moved from <old path>", and `--continue` in a project with no session of its own offers the newest of them.
28
+
29
+ A session that another senpi process still has open is never moved: the move stops and names that process (pid and directory) so you can quit it first. Each open session is advertised under `session-holders/` next to the session file; a record left by a process that has exited is ignored.
30
+
31
+ `--rebind <path|id>` does the same without asking, for scripts. It refuses when the two directories are provably different repositories. Without an interactive terminal, `--session` never prompts: it prints the exact `--rebind` and `--fork` commands and exits with a non-zero status.
32
+
20
33
  For the JSONL file format and SessionManager API, see [Session Format](session-format.md).
21
34
 
22
35
  ## Session Commands
package/docs/usage.md CHANGED
@@ -226,6 +226,7 @@ cat README.md | senpi -p "Summarize this text"
226
226
  | `-r`, `--resume` | Browse and select a session |
227
227
  | `--session <path\|id>` | Use a specific session file or partial UUID |
228
228
  | `--fork <path\|id>` | Fork a session file or partial UUID into a new session |
229
+ | `--rebind <path\|id>` | Move a session of this repository, recorded at another path (moved or re-cloned), into this directory and continue it |
229
230
  | `--session-dir <dir>` | Custom session storage directory |
230
231
  | `--no-session` | Ephemeral mode; do not save |
231
232
  | `--name <name>`, `-n <name>` | Set session display name at startup |