@code-yeongyu/senpi 2026.9.28-5 → 2026.9.28-7

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 (156) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/bundle/chunks/{anthropic-messages-GG4ZKS6A.js → anthropic-messages-QOQJCY3F.js} +1 -1
  3. package/dist/bundle/chunks/{app-server-command-3AJWPHKJ.js → app-server-command-OUTRKHMK.js} +8 -8
  4. package/dist/bundle/chunks/{chunk-XYGXUFMG.js → chunk-3HCYHGK2.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-EXUI5D5V.js → chunk-56COAYOU.js} +9 -9
  6. package/dist/bundle/chunks/{chunk-MMHQLGF2.js → chunk-AIM2YESB.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-4BDFNRBE.js → chunk-B32PXL7U.js} +1 -1
  8. package/dist/bundle/chunks/chunk-CEMOA3TF.js +2 -0
  9. package/dist/bundle/chunks/{chunk-XNIX26QC.js → chunk-DB2X7SOZ.js} +2 -2
  10. package/dist/bundle/chunks/chunk-E5F5SVZF.js +5 -0
  11. package/dist/bundle/chunks/{chunk-OHJLQ7BY.js → chunk-F2YU5BZG.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-BQGVC47Y.js → chunk-FILEUW2D.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-JGRPPPHS.js → chunk-JASECXGC.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-YLIS5N3W.js → chunk-JK5LVAOU.js} +1 -1
  15. package/dist/bundle/chunks/{chunk-KDZAVHA7.js → chunk-L4OOEWAT.js} +2 -2
  16. package/dist/bundle/chunks/{chunk-Z5NVK72W.js → chunk-LM2UMEX6.js} +1 -1
  17. package/dist/bundle/chunks/{chunk-C5I2LUOI.js → chunk-SQUUL72H.js} +1 -1
  18. package/dist/bundle/chunks/{chunk-LEAJXPT2.js → chunk-TOCWAEQB.js} +1 -1
  19. package/dist/bundle/chunks/{chunk-YEMM3LPR.js → chunk-VW3QZSX3.js} +1 -1
  20. package/dist/bundle/chunks/{chunk-7SFG425F.js → chunk-Y4CH7LYI.js} +2 -2
  21. package/dist/bundle/chunks/chunk-YJUO37UX.js +2 -0
  22. package/dist/bundle/chunks/{cli-main-NUSBF3HE.js → cli-main-26P4V2BK.js} +1 -1
  23. package/dist/bundle/chunks/github-copilot.js +1 -1
  24. package/dist/bundle/chunks/{help-fast-path-A4S4KOQ6.js → help-fast-path-BOMUI7U3.js} +1 -1
  25. package/dist/bundle/chunks/{host-command-D2CHSATG.js → host-command-ZVXNTXMN.js} +1 -1
  26. package/dist/bundle/chunks/host-lifecycle-MPLSJ5NB.js +3 -0
  27. package/dist/bundle/chunks/host-lifecycle.js +3 -3
  28. package/dist/bundle/chunks/{interactive-host-runtime-AAK47HIV.js → interactive-host-runtime-Q5EORM6B.js} +1 -1
  29. package/dist/bundle/chunks/{interactive-mode-LSTD6EUQ.js → interactive-mode-OVSKUTVC.js} +1 -1
  30. package/dist/bundle/chunks/{list-tips-S4U7TKER.js → list-tips-ARO4HRVK.js} +1 -1
  31. package/dist/bundle/chunks/{multi-session-host-5WNZ7XRV.js → multi-session-host-LJNXZRFA.js} +7 -7
  32. package/dist/bundle/chunks/{openai-completions-DVEOAQK4.js → openai-completions-BHE6TMUP.js} +1 -1
  33. package/dist/bundle/chunks/{openai-responses-7H2ADNW4.js → openai-responses-OGQ5QORF.js} +1 -1
  34. package/dist/bundle/chunks/{package-manager-cli-3HZYQYVX.js → package-manager-cli-MJCRY2JU.js} +1 -1
  35. package/dist/bundle/chunks/{rotation-stream-F36RJ6VP.js → rotation-stream-Q2EVDMUU.js} +1 -1
  36. package/dist/bundle/chunks/{rpc-mode-GMPBEVSL.js → rpc-mode-LY4HAZ5G.js} +1 -1
  37. package/dist/bundle/chunks/{schedule-command-BYWZGYOJ.js → schedule-command-3ANAEUY6.js} +1 -1
  38. package/dist/bundle/chunks/{session-picker-6VU2F5VF.js → session-picker-DRM7AGPY.js} +1 -1
  39. package/dist/bundle/chunks/session-worker.js +37 -37
  40. package/dist/bundle/cli.js +1 -1
  41. package/dist/bundle/index.js +1 -1
  42. package/dist/bundle/rpc-entry.js +1 -1
  43. package/dist/cli/args.js +2 -2
  44. package/dist/cli/args.js.map +1 -1
  45. package/dist/core/extensions/builtin/btw/index.d.ts.map +1 -1
  46. package/dist/core/extensions/builtin/btw/index.js +3 -1
  47. package/dist/core/extensions/builtin/btw/index.js.map +1 -1
  48. package/dist/core/extensions/builtin/prompt-preset/presets.d.ts.map +1 -1
  49. package/dist/core/extensions/builtin/prompt-preset/presets.js +2 -1
  50. package/dist/core/extensions/builtin/prompt-preset/presets.js.map +1 -1
  51. package/dist/core/extensions/builtin/websearch/websearch/native.d.ts +1 -0
  52. package/dist/core/extensions/builtin/websearch/websearch/native.d.ts.map +1 -1
  53. package/dist/core/extensions/builtin/websearch/websearch/native.js +8 -1
  54. package/dist/core/extensions/builtin/websearch/websearch/native.js.map +1 -1
  55. package/dist/main.d.ts +2 -2
  56. package/dist/main.d.ts.map +1 -1
  57. package/dist/main.js +25 -6
  58. package/dist/main.js.map +1 -1
  59. package/dist/modes/app-server/cli-args.d.ts +3 -0
  60. package/dist/modes/app-server/cli-args.d.ts.map +1 -1
  61. package/dist/modes/app-server/cli-args.js +25 -4
  62. package/dist/modes/app-server/cli-args.js.map +1 -1
  63. package/dist/modes/app-server/daemon/probe.d.ts +1 -0
  64. package/dist/modes/app-server/daemon/probe.d.ts.map +1 -1
  65. package/dist/modes/app-server/daemon/probe.js +4 -1
  66. package/dist/modes/app-server/daemon/probe.js.map +1 -1
  67. package/dist/modes/app-server/daemon/spawn.d.ts +26 -0
  68. package/dist/modes/app-server/daemon/spawn.d.ts.map +1 -0
  69. package/dist/modes/app-server/daemon/spawn.js +112 -0
  70. package/dist/modes/app-server/daemon/spawn.js.map +1 -0
  71. package/dist/modes/app-server/daemon.d.ts.map +1 -1
  72. package/dist/modes/app-server/daemon.js +14 -112
  73. package/dist/modes/app-server/daemon.js.map +1 -1
  74. package/dist/modes/app-server/extension-paths.d.ts +7 -0
  75. package/dist/modes/app-server/extension-paths.d.ts.map +1 -0
  76. package/dist/modes/app-server/extension-paths.js +10 -0
  77. package/dist/modes/app-server/extension-paths.js.map +1 -0
  78. package/dist/modes/app-server/index.d.ts.map +1 -1
  79. package/dist/modes/app-server/index.js +4 -1
  80. package/dist/modes/app-server/index.js.map +1 -1
  81. package/dist/modes/app-server/runtime.d.ts +5 -1
  82. package/dist/modes/app-server/runtime.d.ts.map +1 -1
  83. package/dist/modes/app-server/runtime.js +19 -3
  84. package/dist/modes/app-server/runtime.js.map +1 -1
  85. package/dist/modes/interactive/tips/catalog/computer-tips.js +1 -1
  86. package/dist/modes/interactive/tips/catalog/computer-tips.js.map +1 -1
  87. package/dist/modes/rpc/custom-capability.d.ts +5 -0
  88. package/dist/modes/rpc/custom-capability.d.ts.map +1 -1
  89. package/dist/modes/rpc/custom-capability.js +5 -0
  90. package/dist/modes/rpc/custom-capability.js.map +1 -1
  91. package/dist/modes/rpc/host-identity-env.d.ts +20 -0
  92. package/dist/modes/rpc/host-identity-env.d.ts.map +1 -0
  93. package/dist/modes/rpc/host-identity-env.js +20 -0
  94. package/dist/modes/rpc/host-identity-env.js.map +1 -0
  95. package/dist/modes/rpc/host-lifecycle.js +1 -1
  96. package/dist/modes/rpc/host-lifecycle.js.map +1 -1
  97. package/dist/modes/rpc/host-observe-request.d.ts +2 -0
  98. package/dist/modes/rpc/host-observe-request.d.ts.map +1 -1
  99. package/dist/modes/rpc/host-observe-request.js +4 -0
  100. package/dist/modes/rpc/host-observe-request.js.map +1 -1
  101. package/dist/modes/rpc/host-spawn-environment.js +1 -1
  102. package/dist/modes/rpc/host-spawn-environment.js.map +1 -1
  103. package/dist/modes/rpc/host-warm.d.ts +57 -0
  104. package/dist/modes/rpc/host-warm.d.ts.map +1 -0
  105. package/dist/modes/rpc/host-warm.js +125 -0
  106. package/dist/modes/rpc/host-warm.js.map +1 -0
  107. package/dist/modes/rpc/protocol-identity.d.ts +2 -11
  108. package/dist/modes/rpc/protocol-identity.d.ts.map +1 -1
  109. package/dist/modes/rpc/protocol-identity.js +2 -11
  110. package/dist/modes/rpc/protocol-identity.js.map +1 -1
  111. package/dist/modes/rpc/rpc-types.d.ts +24 -1
  112. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  113. package/dist/modes/rpc/rpc-types.js +2 -0
  114. package/dist/modes/rpc/rpc-types.js.map +1 -1
  115. package/dist/modes/rpc/session-command-router.d.ts +1 -1
  116. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  117. package/dist/modes/rpc/session-command-router.js +12 -1
  118. package/dist/modes/rpc/session-command-router.js.map +1 -1
  119. package/dist/modes/rpc/session-registry.d.ts +6 -2
  120. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  121. package/dist/modes/rpc/session-registry.js +6 -0
  122. package/dist/modes/rpc/session-registry.js.map +1 -1
  123. package/docs/app-server-daemon.md +1 -1
  124. package/docs/app-server.md +3 -0
  125. package/docs/rpc.md +48 -2
  126. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +24 -0
  127. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  128. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  129. package/node_modules/@earendil-works/pi-ai/dist/api/github-copilot-endpoint.d.ts +19 -0
  130. package/node_modules/@earendil-works/pi-ai/dist/api/github-copilot-endpoint.d.ts.map +1 -0
  131. package/node_modules/@earendil-works/pi-ai/dist/api/github-copilot-endpoint.js +48 -0
  132. package/node_modules/@earendil-works/pi-ai/dist/api/github-copilot-endpoint.js.map +1 -0
  133. package/node_modules/@earendil-works/pi-ai/dist/api/github-copilot-errors.d.ts.map +1 -1
  134. package/node_modules/@earendil-works/pi-ai/dist/api/github-copilot-errors.js +3 -0
  135. package/node_modules/@earendil-works/pi-ai/dist/api/github-copilot-errors.js.map +1 -1
  136. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/github-copilot.d.ts.map +1 -1
  137. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/github-copilot.js +18 -36
  138. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/github-copilot.js.map +1 -1
  139. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  140. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  141. package/node_modules/@earendil-works/pi-ai/dist/providers/devin.models.d.ts +5 -3
  142. package/node_modules/@earendil-works/pi-ai/dist/providers/devin.models.d.ts.map +1 -1
  143. package/node_modules/@earendil-works/pi-ai/dist/providers/devin.models.js +6 -5
  144. package/node_modules/@earendil-works/pi-ai/dist/providers/devin.models.js.map +1 -1
  145. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.d.ts.map +1 -1
  146. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.js +15 -1
  147. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.js.map +1 -1
  148. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  149. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  150. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  151. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  152. package/package.json +7 -7
  153. package/dist/bundle/chunks/chunk-A4UMPXQF.js +0 -2
  154. package/dist/bundle/chunks/chunk-HH3KJDWA.js +0 -2
  155. package/dist/bundle/chunks/chunk-XPJLBOWV.js +0 -5
  156. package/dist/bundle/chunks/host-lifecycle-S7O5K62Z.js +0 -3
@@ -1 +1 @@
1
- {"version":3,"file":"session-registry.js","sourceRoot":"","sources":["../../../src/modes/rpc/session-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAChG,OAAO,EAEN,mBAAmB,EAEnB,yBAAyB,GACzB,MAAM,qCAAqC,CAAC;AAG7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACrF,OAAO,EAAE,2BAA2B,EAAgC,MAAM,wBAAwB,CAAC;AACnG,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,YAAY,EAA4B,MAAM,uBAAuB,CAAC;AAuDtH,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAajD,YAAY,IAAqC,EAAE,MAAe,EAAE,MAA0C;QAC7G,KAAK,CAAC,IAAI,KAAK,aAAa,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACtE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;QACtC,IAAI,MAAM;YAAE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IAClC,CAAC;CACD;AA6CD,uFAAuF;AACvF,MAAM,UAAU,aAAa,CAAC,OAAgC;IAC7D,OAAO,MAAM,CAAC,MAAM,CAAC;QACpB,GAAG,OAAO;QACV,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChG,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnG,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,OAA0C;IAIzE,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,WAAW,IAAI,aAAa,EAAE,OAAO,EAAE,OAAO,CAAC,cAAc,IAAI,qBAAqB,EAAE,CAAC;AACjH,CAAC;AAED,oEAAoE;AACpE,MAAM,OAAO,kBAAkB;IAS9B,YAAY,OAAkC;QAR7B,YAAO,GAAG,IAAI,GAAG,EAA2B,CAAC;QAC7C,iBAAY,GAAG,IAAI,GAAG,EAAU,CAAC;QAE1C,eAAU,GAAG,CAAC,CAAC;QAMtB,IAAI,CAAC,OAAO;YACX,OAAO,CAAC,WAAW,KAAK,SAAS;gBAChC,CAAC,CAAC,OAAO;gBACT,CAAC,CAAC;oBACA,GAAG,OAAO;oBACV,aAAa,EAAE,CAAC,cAAc,EAAE,EAAE,CACjC,OAAO,CAAC,aAAa,CAAC,EAAE,GAAG,cAAc,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;iBAC/E,CAAC;QACL,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;QACnC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,MAAM,CAAC;QACnD,IAAI,CAAC,YAAY,GAAG;YACnB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YACzC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC;YAC/C,kBAAkB,EAAE,CAAC,GAAG,EAAE,EAAE;gBAC3B,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC9B,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;YAC7C,CAAC;YACD,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,WAAW,CAAC,GAAG,EAAE,KAAK,CAAC;YAC7E,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,mBAAmB,EAAE;SACtC,CAAC;IACH,CAAC;IAED,uEAAuE;IACvE,IAAI,IAAI;QACP,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAAgC,EAAE,OAA+B;QAClF,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAC9B,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,oBAAoB,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAChG,oFAAoF;QACpF,wFAAwF;QACxF,uFAAuF;QACvF,qFAAqF;QACrF,2FAA2F;QAC3F,MAAM,kBAAkB,GAAG,OAAO,CAAC,gBAAgB,CAAC;QACpD,IAAI,kBAAkB,KAAK,SAAS,EAAE,CAAC;YACtC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;gBAC3C,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ;oBAAE,SAAS;gBACvC,IAAI,KAAK,CAAC,gBAAgB,KAAK,kBAAkB;oBAAE,SAAS;gBAC5D,IAAI,WAAW,KAAK,SAAS,IAAI,KAAK,CAAC,cAAc,KAAK,WAAW;oBAAE,SAAS;gBAChF,MAAM,IAAI,uBAAuB,CAAC,mBAAmB,CAAC,CAAC;YACxD,CAAC;QACF,CAAC;QACD,IAAI,WAAW;YAAE,MAAM,IAAI,CAAC,wBAAwB,CAAC,WAAW,CAAC,CAAC;QAClE,IAAI,WAAW,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YACvD,8EAA8E;YAC9E,6EAA6E;YAC7E,yEAAyE;YACzE,qCAAqC;YACrC,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CACtC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,cAAc,KAAK,WAAW,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM,CAC7E,CAAC;YACF,IAAI,CAAC,QAAQ;gBAAE,MAAM,IAAI,uBAAuB,CAAC,qBAAqB,CAAC,CAAC;YACxE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,QAAQ,CAAC;YACjC,MAAM,SAAS,GAAG,KAAK,CAAC,kBAAkB,KAAK,IAAI,IAAI,KAAK,CAAC,WAAW,KAAK,CAAC,CAAC;YAC/E,KAAK,CAAC,WAAW,IAAI,CAAC,CAAC;YACvB,4FAA4F;YAC5F,6DAA6D;YAC7D,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,WAAW,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YAC9D,8EAA8E;YAC9E,mEAAmE;YACnE,IAAI,OAAO,EAAE,kBAAkB;gBAAE,KAAK,CAAC,kBAAkB,GAAG,IAAI,CAAC;YACjE,IAAI,CAAC,KAAK,CAAC,gBAAgB;gBAAE,MAAM,IAAI,uBAAuB,CAAC,qBAAqB,CAAC,CAAC;YACtF,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACjC,IAAI,SAAS,EAAE,CAAC;gBACf,KAAK,CAAC,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE,CACrD,KAAK,CAAC,OAAO,EAAE,mBAAmB,CAAC,iBAAiB,CAAC,CACrD,CAAC;gBACF,MAAM,KAAK,CAAC,cAAc,CAAC;YAC5B,CAAC;YACD,OAAO;gBACN,SAAS,EAAE,MAAM;gBACjB,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;gBACxC,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,QAAQ,EAAE,IAAI;aACd,CAAC;QACH,CAAC;QACD,IAAI,WAAW,EAAE,CAAC;YACjB,2FAA2F;YAC3F,oFAAoF;YACpF,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YACnC,0FAA0F;YAC1F,yFAAyF;YACzF,6FAA6F;YAC7F,4FAA4F;YAC5F,4FAA4F;YAC5F,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB;gBAC3C,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,KAAK,CAAC,WAAW,CAAC;gBACxD,CAAC,CAAC,SAAS,CAAC;YACb,IAAI,MAAM,EAAE,CAAC;gBACZ,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;gBACtC,MAAM,IAAI,uBAAuB,CAAC,qBAAqB,EAAE,SAAS,EAAE;oBACnE,KAAK,EAAE,MAAM;oBACb,cAAc,EAAE,2BAA2B;iBAC3C,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;QAED,qEAAqE;QACrE,uEAAuE;QACvE,2EAA2E;QAC3E,8CAA8C;QAC9C,MAAM,QAAQ,GAAG,WAAW,KAAK,SAAS,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC;QACtE,4EAA4E;QAC5E,6EAA6E;QAC7E,4EAA4E;QAC5E,6EAA6E;QAC7E,wEAAwE;QACxE,MAAM,iBAAiB,GAAkC,QAAQ;YAChE,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE;YAC7C,CAAC,CAAC,SAAS,CAAC;QACb,MAAM,aAAa,GAAG,aAAa,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC7F,MAAM,cAAc,GAAG,QAAQ;YAC9B,CAAC,CAAC,aAAa,CAAC,EAAE,GAAG,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,oBAAoB,EAAE,SAAS,EAAE,CAAC;YAChG,CAAC,CAAC,aAAa,CAAC;QAEjB,MAAM,MAAM,GAAG,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAoB;YAC9B,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,IAAI,aAAa,EAAE;YAC1B,OAAO,EAAE,aAAa;YACtB,GAAG,eAAe,CAAC,aAAa,CAAC;YACjC,WAAW;YACX,cAAc,EAAE,WAAW;YAC3B,GAAG,EAAE,aAAa,CAAC,GAAG;YACtB,WAAW,EAAE,CAAC;YACd,kBAAkB,EAAE,OAAO,EAAE,kBAAkB,KAAK,IAAI;YACxD,aAAa,EAAE,IAAI,CAAC,GAAG,EAAE;YACzB,cAAc,EAAE,OAAO,CAAC,OAAO,EAAE;SACjC,CAAC;QACF,KAAK,CAAC,aAAa,GAAG,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE;YAC9C,MAAM,SAAS,GAAG,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;gBACtD,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;oBAC9C,MAAM,IAAI,uBAAuB,CAAC,iBAAiB,CAAC,CAAC;gBACtD,CAAC;gBACD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;gBAC9B,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,CAAC;gBACzC,MAAM,UAAU,GAAG,WAAW,KAAK,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,MAAM,EAAE,KAAK,WAAW,CAAC;gBACxG,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;gBACjE,IAAI,MAAM,CAAC,SAAS,IAAI,CAAC,UAAU;oBAAE,OAAO,MAAM,CAAC;gBAEnD,uEAAuE;gBACvE,wEAAwE;gBACxE,wEAAwE;gBACxE,uDAAuD;gBACvD,MAAM,WAAW,GAAG,IAAI,mBAAmB,CAC1C,OAAO,CAAC,OAAO,EACf,OAAO,CAAC,QAAQ,EAChB,IAAI,CAAC,OAAO,CAAC,aAAa,EAC1B,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,EACxB,OAAO,CAAC,oBAAoB,EAC5B,OAAO,CAAC,aAAa,CACrB,CAAC;gBACF,WAAW,CAAC,gBAAgB,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;gBAClD,OAAO,CAAC,kBAAkB,EAAE,CAAC;gBAC7B,KAAK,CAAC,OAAO,GAAG,WAAW,CAAC;gBAC5B,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC3B,OAAO,MAAM,CAAC;YACf,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,cAAc,GAAG,SAAS,CAAC,IAAI,CACpC,GAAG,EAAE,CAAC,SAAS,EACf,GAAG,EAAE,CAAC,SAAS,CACf,CAAC;YACF,OAAO,SAAS,CAAC;QAClB,CAAC,CAAC;QACF,wFAAwF;QACxF,mFAAmF;QACnF,oFAAoF;QACpF,IAAI,kBAAkB,KAAK,SAAS;YAAE,KAAK,CAAC,gBAAgB,GAAG,kBAAkB,CAAC;QAClF,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC;YACJ,MAAM,iBAAiB,GAAG,kBAAkB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;YACpG,MAAM,OAAO,GAAG,WAAW;gBAC1B,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE,aAAa,CAAC,GAAG,EAAE,iBAAiB,CAAC;gBACnF,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,SAAS,EAAE,iBAAiB,CAAC,CAAC;YAC1E,KAAK,CAAC,OAAO,GAAG,MAAM,oBAAoB,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,CAC5D,yBAAyB,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE;gBACrD,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE;gBACrB,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ;gBAC/B,cAAc,EAAE,OAAO;gBACvB,iBAAiB;gBACjB,aAAa,EAAE,cAAc;aAC7B,CAAC,CACF,CAAC;YACF,KAAK,CAAC,gBAAgB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;YAChD,KAAK,CAAC,WAAW,KAAK,OAAO,CAAC,cAAc,EAAE,CAAC;YAC/C,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC;YACrB,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;QACxG,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,2EAA2E;YAC3E,0EAA0E;YAC1E,yEAAyE;YACzE,4DAA4D;YAC5D,IAAI,CAAC;gBACJ,MAAM,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC;gBACR,wEAAwE;YACzE,CAAC;oBAAS,CAAC;gBACV,IAAI,CAAC;oBACJ,MAAM,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;gBAC7B,CAAC;wBAAS,CAAC;oBACV,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;oBAC5B,IAAI,WAAW,EAAE,CAAC;wBACjB,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;wBACtC,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;oBACrD,CAAC;gBACF,CAAC;YACF,CAAC;YACD,IAAI,KAAK,YAAY,uBAAuB;gBAAE,MAAM,KAAK,CAAC;YAC1D,MAAM,IAAI,uBAAuB,CAAC,aAAa,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACtG,CAAC;IACF,CAAC;IAED;;;;;;;;;;;OAWG;IACK,KAAK,CAAC,wBAAwB,CAAC,WAAmB;QACzD,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAC9C,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,cAAc,KAAK,WAAW,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,CAC5E,CAAC;QACF,IAAI,CAAC,OAAO,EAAE,eAAe;YAAE,OAAO;QACtC,IAAI,QAAmD,CAAC;QACxD,IAAI,CAAC;YACJ,MAAM,OAAO,CAAC,IAAI,CAAC;gBAClB,OAAO,CAAC,eAAe;gBACvB,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;oBAC7B,QAAQ,GAAG,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;gBACnD,CAAC,CAAC;aACF,CAAC,CAAC;QACJ,CAAC;gBAAS,CAAC;YACV,IAAI,QAAQ;gBAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;QACtC,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACH,IAAI,CAAC,MAAc;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAED,aAAa,CAAC,MAAc,EAAE,OAAe;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,uBAAuB,CAAC,iBAAiB,CAAC,CAAC;QACjE,IACC,KAAK,CAAC,KAAK,KAAK,SAAS;YACzB,CAAC,CAAC,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,uBAAuB,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAC3F,CAAC;YACF,MAAM,IAAI,uBAAuB,CAAC,iBAAiB,CAAC,CAAC;QACtD,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,MAAM,IAAI,uBAAuB,CAAC,iBAAiB,CAAC,CAAC;QAC9G,0EAA0E;QAC1E,yEAAyE;QACzE,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC;IACd,CAAC;IAED;;;;OAIG;IACH,UAAU,CAAC,MAAc,EAAE,MAAqC,EAAE,OAA8B;QAC/F,OAAO,iBAAiB,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IACtE,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,MAAc;QACzB,OAAO,YAAY,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAChD,CAAC;IAED,iEAAiE;IACjE,KAAK,CAAC,WAAW,CAAC,MAAc;QAC/B,OAAO,kBAAkB,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC;IAED,IAAI;QACH,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;YACrD,SAAS;YACT,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;YACxC,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,cAAc,EAAE;YAC5D,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,4FAA4F;YAC5F,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC;YAC3C,MAAM,EAAE,KAAK,CAAC,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK;SAC/D,CAAC,CAAC,CAAC;IACL,CAAC;IAED,qEAAqE;IAC7D,mBAAmB;QAC1B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC;YACtD,IAAI,CAAC,OAAO;gBAAE,SAAS;YACvB,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;YAC7C,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC/E,8EAA8E;YAC9E,6EAA6E;YAC7E,4EAA4E;YAC5E,yCAAyC;YACzC,EAAE;YACF,6EAA6E;YAC7E,6EAA6E;YAC7E,2EAA2E;YAC3E,uEAAuE;YACvE,8EAA8E;YAC9E,+BAA+B;YAC/B,IAAI,WAAW,KAAK,KAAK,CAAC,WAAW,IAAI,UAAU,KAAK,KAAK,CAAC,cAAc,EAAE,CAAC;gBAC9E,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;oBAC1B,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;oBAC/C,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;gBAC9D,CAAC;gBACD,IAAI,UAAU,EAAE,CAAC;oBAChB,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;oBAClC,uFAAuF;oBACvF,qFAAqF;oBACrF,2BAA2B;oBAC3B,KAAK,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC;gBAC9E,CAAC;gBACD,KAAK,CAAC,cAAc,GAAG,UAAU,CAAC;gBAClC,KAAK,CAAC,WAAW,GAAG,WAAW,CAAC;YACjC,CAAC;YACD,KAAK,CAAC,gBAAgB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;YAChD,KAAK,CAAC,GAAG,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;QAC9B,CAAC;IACF,CAAC;IAEO,eAAe,CAAC,OAAgC;QACvD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;YACzG,MAAM,IAAI,uBAAuB,CAAC,cAAc,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,OAAO,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;YAC5C,IAAI,CAAC;gBACJ,oBAAoB,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;YAChD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,MAAM,IAAI,uBAAuB,CAAC,oBAAoB,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACxE,CAAC;QACF,CAAC;IACF,CAAC;CACD","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| \"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/** 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\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\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 (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"]}
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\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 { createRegistryWarm, type HostWarm, type PreparableRuntimeFactory } from \"./host-warm.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| \"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\t/** A factory with `prepare` also gives the registry `warm` (senpi#2314). */\n\tcreateRuntime: PreparableRuntimeFactory;\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/** 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\t/** Present only when the runtime factory can build a session's services alone (`host-warm.ts`). */\n\treadonly warm?: HostWarm;\n\n\tconstructor(options: RpcSessionRegistryOptions) {\n\t\tif (options.createRuntime.prepare)\n\t\t\tthis.warm = createRegistryWarm(options.createRuntime.prepare, {\n\t\t\t\tagentDir: options.agentDir,\n\t\t\t\t...(options.mcpRegistry !== undefined ? { mcpRegistry: options.mcpRegistry } : {}),\n\t\t\t});\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\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 (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"]}
@@ -80,7 +80,7 @@ Nothing was running:
80
80
 
81
81
  ### restart
82
82
 
83
- `restart` stops the managed daemon, then starts it again. It preserves the previous daemon flags from `settings.json` when available, so a daemon started with `--listen unix:///tmp/senpi-app-server.sock` restarts with the same listener even if the restart command omits `--listen`.
83
+ `restart` stops the managed daemon, then starts it again. It preserves the previous daemon flags from `settings.json` when available, so a daemon started with `--listen unix:///tmp/senpi-app-server.sock` restarts with the same listener even if the restart command omits `--listen`. Extensions follow the same rule: the recorded `--extension` list is reused unless the restart command names its own.
84
84
 
85
85
  ```bash
86
86
  senpi app-server daemon restart
@@ -35,6 +35,9 @@ senpi app-server --listen stdio://
35
35
  `unix:///abs/path` in the `--listen` grammar for local-control socket addresses, but this document does not cover
36
36
  daemon lifecycle or control-socket management.
37
37
 
38
+ Load extensions into every thread with repeated `--extension <path>`, the same sources the global flag accepts.
39
+ `senpi app-server daemon start` passes them to the daemon and records them, so `restart` keeps them.
40
+
38
41
  ## Protocol Overview
39
42
 
40
43
  App Server mode speaks JSON-RPC-shaped messages without a `jsonrpc` field. A request has `id`, `method`, and optional
package/docs/rpc.md CHANGED
@@ -680,6 +680,50 @@ ignores both fields and lists every session.
680
680
  Default-invisible worker sessions are invariant **I4** above: a client that does not pass `include_workers: true`
681
681
  sees the daemon exactly as it saw a host serving only that client.
682
682
 
683
+ ### Warming a host (`warm`)
684
+
685
+ A fresh host pays a one-time cost on its FIRST session: the extension module graph compiles, and the extensions'
686
+ factories load whatever they load for that session's kind and context (a task engine for a `child` role, for
687
+ example). Every later open on that host reuses it. A client that starts one host per session pays it on every session
688
+ unless it warms the host first.
689
+
690
+ #### warm
691
+
692
+ Loads what the next `open_session` with the same `cwd`, `kind` and `context` needs, without opening a session:
693
+
694
+ ```json
695
+ {"id": "w1", "type": "warm", "cwd": "/abs/project", "kind": "worker", "context": {"role": "child"}}
696
+ ```
697
+
698
+ ```json
699
+ {"id": "w1", "type": "response", "command": "warm", "success": true, "data": {"state": "warmed"}}
700
+ ```
701
+
702
+ Every field is optional and means what it means on `open_session`: `cwd` defaults to the host's cwd and must be
703
+ absolute, and `kind` and `context` are validated with the same caps and refused with the same codes
704
+ (`invalid_path`, `invalid_session_kind`, `invalid_session_context`). A socket host stamps its own identity into the
705
+ context exactly as it does for an open, so the extensions see what they will see in the real session. The host builds
706
+ that session's cwd-bound services - settings, resources, extensions with their factories run for this kind and
707
+ context - inside a provider scope of their own, then drops them.
708
+
709
+ - No session is created: nothing appears in `list_sessions` (with or without `include_workers`), no `AgentSession`
710
+ exists, and no extension receives `session_start`.
711
+ - `warm` never attaches its connection, whether or not it carries `observe: true`: a connection that has only sent
712
+ observing reads and warms is an observer, so a host that only received `warm` idles out on its normal deadline
713
+ (`SENPI_RPC_HOST_IDLE_EXIT_MS` under a supervisor, `SENPI_RPC_HOST_EMPTY_EXIT_MS` for the host itself). A warm still
714
+ loading neither holds that window open nor holds a drain.
715
+ - It takes no session lock and no open barrier, so it never waits for a live turn and a live turn never waits for
716
+ it. It does share the host's one loop: loading an extension graph is the same work an open does.
717
+ - It is idempotent per profile: `data.state` is `warmed` when this call (or a concurrent warm of the same profile it
718
+ joined) did the loading, and `already_warm` when an earlier warm had, in which case nothing runs again. A failed
719
+ load (`warm_failed: <detail>`, for example a `cwd` that does not exist) is not remembered, so it can be retried.
720
+ - A host whose sessions run in worker isolates (`--session-runtime worker`) has nothing it can warm from the host
721
+ process: it answers `state: "unsupported"` and does not advertise the capability. A draining generation refuses with
722
+ `host_draining`.
723
+
724
+ Probe `warm` in `get_protocol_info` capabilities before sending it: an older host answers an unknown command with an
725
+ error.
726
+
683
727
  ### Session auto-titling
684
728
 
685
729
  Auto-generated session titles are on by default only for interactive launches. A shared host decides titling per
@@ -740,7 +784,7 @@ Environment overrides beat the file, and invalid values fall through to the next
740
784
  The host exits only after the window elapses with NO attached client connections and NO active turns — continuously.
741
785
  Any attached connection or agent turn resets the window, so a busy host never exits. A connection attaches with its
742
786
  first request line, unless that line is an OBSERVING read: `get_protocol_info` or `list_sessions` carrying
743
- `"observe": true`. A connection that only ever sends observing reads never counts and never resets the window, so a
787
+ `"observe": true`, or a `warm` (marked or not - see "Warming a host"). A connection that only ever sends observing reads never counts and never resets the window, so a
744
788
  poller (`senpi host status [--all]`, which marks both of its reads, a doctor loop, a runtime panel) can look at every
745
789
  endpoint as often as it likes without keeping any of them alive; the first request that is anything else attaches the
746
790
  connection from then on, and `observe` on any other command is ignored. While a connection has not sent its first
@@ -871,7 +915,7 @@ What the host does enforce are lifecycle windows, and they only ever return memo
871
915
  `SENPI_RPC_HOST_EMPTY_EXIT_MS` (default 15 minutes), the host exits through its clean shutdown path (flush, socket
872
916
  removal), for stdio and `--listen` hosts alike. A connected client counts as occupancy even with no session open,
873
917
  so the host never drops a live socket under itself - except a connection whose every request so far was an
874
- observing read (`"observe": true`, the same rule as the idle-exit window above), which never holds the host open.
918
+ observing read (`"observe": true`, the same rule as the idle-exit window above) or a `warm`, which never holds the host open.
875
919
  A connection that has not sent its first request yet still counts. Supervised hosts stay clean either way: a supervisor reads a
876
920
  child exit of 0 without a signal as an intentional idle stop and exits 0 with the same cleanup, not as a crash.
877
921
 
@@ -1052,6 +1096,7 @@ containment, or containment of arbitrary native code. They are not an extension
1052
1096
  | `open_session` | `sessionPath?`, `cwd?`, `provider?`, `modelId?`, `thinkingLevel?`, `permissionPreset?`, `retain_on_disconnect?`, `kind?`, `context?`, `auto_title?` (all optional; paths MUST be absolute) | `{ sessionId, state: RpcSessionState, attached?: true }` | `sessionPath` = today's `--session` semantics (open-if-exists else create persisting there, `session-manager.ts:926-940`); `provider`/`modelId` applied only on create (resume restores the session's model — mirrors `SenpiSessionRuntime.ts:198-200`); params form the immutable launch profile (D8). When the path is already held by a fully-open session, the open ATTACHES to it: same routing handle, `attached: true`, one more attachment counted; the runtime is torn down only when the last attachment closes. Idle sessions past the eviction window are closed by the host itself. `retain_on_disconnect: true` (default false) makes a dropped connection DETACH from this session instead of closing it — see "Retained sessions" below. `kind` (default `interactive`) and the opaque `context` map are described under "Session kind and context" above; both are stored frozen for the session's life and never influence auth, model or resource resolution. |
1053
1097
  | `close_session` | `sessionId` | `{}` | Refused with `unknown_session` when the requesting connection never attached to that handle (a close releases the CALLER's attachment, and `list_sessions` publishes every handle). Otherwise aborts active work, awaits agent idle + settled persistence for up to the host grace window (default 10s), then quarantines any worker that has not exited without releasing its path reservation; its response is the LAST record tagged with that handle for the first closer — no events after (test-pinned). An admitted concurrent close joins the same teardown and receives its own successful response; output saturation rejects admission with the bounded close-overflow/resync notice described above. |
1054
1098
  | `list_sessions` | `include_workers?` (default false) | `{ sessions: [{ sessionId, durableSessionId, sessionPath, cwd, name, status, attachments, kind, context? }] }` | Includes `opening`/`closing` entries. Internally quarantined workers remain externally `closing` until exit. `attachments` is the session's live client attachment count; `0` on an `open` row is a retained session with no client attached. Every row carries `kind`. Rows with `kind: "worker"` are omitted unless `include_workers: true`, and `context` is published ONLY on that listing — a default listing carries no `context` at all. |
1099
+ | `warm` | `cwd?`, `kind?`, `context?` (as on `open_session`; `cwd` MUST be absolute) | `{ state: "warmed" \| "already_warm" \| "unsupported" }` | Loads what the next matching `open_session` needs without opening a session; never listed, never an attachment, idempotent per profile. See "Warming a host" above. Advertised as the `warm` capability by in-process hosts only. |
1055
1100
  | every existing command | + `sessionId` (REQUIRED in multi mode) | unchanged | Routed to that session. |
1056
1101
 
1057
1102
  ### Identities (D6)
@@ -1075,6 +1120,7 @@ In the response `error` field, machine-matchable:
1075
1120
  - `invalid_session_kind: <detail>` (`open_session.kind` other than `interactive` or `worker`)
1076
1121
  - `invalid_launch_profile: <detail>` (`open_session.auto_title` present but not a boolean)
1077
1122
  - `host_memory_pressure` (sent only by hosts released before #2207, which declined to CREATE a `kind: "worker"` session above `SENPI_RPC_HOST_RSS_REFUSE_MB`; `errorData { rssMb, retry_after_ms }` says when to ask again. Current hosts never refuse an open for memory; a client talking to an older generation waits and retries, it never starts a second host or a per-child process)
1123
+ - `warm_failed: <detail>` (`warm` could not load its profile, for example a `cwd` that does not exist; not remembered, so a retry loads again)
1078
1124
  - `media_not_found` (`get_media` for an unknown `toolCallId`, or a `contentIndex` that does not point at an image block)
1079
1125
 
1080
1126
  ### Tagging
@@ -12,6 +12,30 @@
12
12
 
13
13
  ### Removed
14
14
 
15
+ ## [2026.9.28-7] - 2026-09-28
16
+
17
+ ### Breaking Changes
18
+
19
+ ### Added
20
+
21
+ ### Changed
22
+
23
+ ### Fixed
24
+
25
+ ### Removed
26
+
27
+ ## [2026.9.28-6] - 2026-09-28
28
+
29
+ ### Breaking Changes
30
+
31
+ ### Added
32
+
33
+ ### Changed
34
+
35
+ ### Fixed
36
+
37
+ ### Removed
38
+
15
39
  ## [2026.9.28-5] - 2026-09-28
16
40
 
17
41
  ### Breaking Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi-codemode",
3
- "version": "2026.9.28-5",
3
+ "version": "2026.9.28-7",
4
4
  "private": true,
5
5
  "description": "Source-only senpi extension package for codemode evaluation tools",
6
6
  "type": "module",
@@ -31,14 +31,14 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@babel/parser": "8.0.4",
34
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.9.28-5",
34
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.9.28-7",
35
35
  "typebox": "1.3.34"
36
36
  },
37
37
  "peerDependencies": {
38
- "@code-yeongyu/senpi": "2026.9.28-5"
38
+ "@code-yeongyu/senpi": "2026.9.28-7"
39
39
  },
40
40
  "devDependencies": {
41
- "@code-yeongyu/senpi": "2026.9.28-5"
41
+ "@code-yeongyu/senpi": "2026.9.28-7"
42
42
  },
43
43
  "keywords": [
44
44
  "senpi",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@earendil-works/pi-agent-core",
3
- "version": "2026.9.28-5",
3
+ "version": "2026.9.28-7",
4
4
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -57,8 +57,8 @@
57
57
  },
58
58
  "dependencies": {
59
59
  "@earendil-works/chord": "0.85.1",
60
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.9.28-5",
61
- "@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.9.28-5",
60
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.9.28-7",
61
+ "@earendil-works/pi-telemetry": "npm:@code-yeongyu/senpi-telemetry@2026.9.28-7",
62
62
  "diff": "9.0.0",
63
63
  "ignore": "7.0.9",
64
64
  "typebox": "1.3.34",
@@ -0,0 +1,19 @@
1
+ export declare const GITHUB_COPILOT_INDIVIDUAL_BASE_URL = "https://api.individual.githubcopilot.com";
2
+ export interface GitHubCopilotApiEndpoint {
3
+ readonly tid: string;
4
+ readonly url: string;
5
+ }
6
+ export declare function githubCopilotTokenField(token: string, key: string): string | undefined;
7
+ export declare function githubCopilotBaseUrlFromToken(token: string): string | undefined;
8
+ export declare function parseGitHubCopilotApiEndpoint(value: unknown): string | undefined;
9
+ /**
10
+ * The account's Copilot API base URL, in order: the token response's `endpoints.api` recorded for
11
+ * this exact token (matched by `tid`, so a pooled sibling account never borrows it), the token's
12
+ * `proxy-ep`, the GitHub Enterprise Server domain, then the individual host.
13
+ */
14
+ export declare function resolveGitHubCopilotBaseUrl(input: {
15
+ token?: string;
16
+ apiEndpoint?: unknown;
17
+ enterpriseDomain?: string;
18
+ }): string;
19
+ //# sourceMappingURL=github-copilot-endpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-copilot-endpoint.d.ts","sourceRoot":"","sources":["../../src/api/github-copilot-endpoint.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,kCAAkC,6CAA6C,CAAC;AAE7F,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMtF;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG/E;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAShF;AASD;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B,GAAG,MAAM,CAOT"}
@@ -0,0 +1,48 @@
1
+ // The Copilot API host is per account plan (individual, business, enterprise, data-residency
2
+ // tenants). A request sent to another plan's host is refused with `421 Misdirected Request`,
3
+ // so the host always comes from the account's own token; the individual host is a last resort.
4
+ export const GITHUB_COPILOT_INDIVIDUAL_BASE_URL = "https://api.individual.githubcopilot.com";
5
+ export function githubCopilotTokenField(token, key) {
6
+ for (const part of token.split(";")) {
7
+ const separator = part.indexOf("=");
8
+ if (separator > 0 && part.slice(0, separator) === key)
9
+ return part.slice(separator + 1) || undefined;
10
+ }
11
+ return undefined;
12
+ }
13
+ export function githubCopilotBaseUrlFromToken(token) {
14
+ const proxyHost = githubCopilotTokenField(token, "proxy-ep");
15
+ return proxyHost ? `https://${proxyHost.replace(/^proxy\./, "api.")}` : undefined;
16
+ }
17
+ export function parseGitHubCopilotApiEndpoint(value) {
18
+ if (typeof value !== "string")
19
+ return undefined;
20
+ try {
21
+ const url = new URL(value.trim());
22
+ if (url.protocol !== "https:" || !url.hostname || url.username || url.password)
23
+ return undefined;
24
+ return `${url.origin}${url.pathname.replace(/\/+$/, "")}`;
25
+ }
26
+ catch {
27
+ return undefined;
28
+ }
29
+ }
30
+ function endpointForToken(endpoint, token) {
31
+ if (!token || typeof endpoint !== "object" || endpoint === null)
32
+ return undefined;
33
+ const tid = Reflect.get(endpoint, "tid");
34
+ const url = parseGitHubCopilotApiEndpoint(Reflect.get(endpoint, "url"));
35
+ return typeof tid === "string" && tid === githubCopilotTokenField(token, "tid") ? url : undefined;
36
+ }
37
+ /**
38
+ * The account's Copilot API base URL, in order: the token response's `endpoints.api` recorded for
39
+ * this exact token (matched by `tid`, so a pooled sibling account never borrows it), the token's
40
+ * `proxy-ep`, the GitHub Enterprise Server domain, then the individual host.
41
+ */
42
+ export function resolveGitHubCopilotBaseUrl(input) {
43
+ return (endpointForToken(input.apiEndpoint, input.token) ??
44
+ (input.token ? githubCopilotBaseUrlFromToken(input.token) : undefined) ??
45
+ (input.enterpriseDomain ? `https://copilot-api.${input.enterpriseDomain}` : undefined) ??
46
+ GITHUB_COPILOT_INDIVIDUAL_BASE_URL);
47
+ }
48
+ //# sourceMappingURL=github-copilot-endpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-copilot-endpoint.js","sourceRoot":"","sources":["../../src/api/github-copilot-endpoint.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,6FAA6F;AAC7F,+FAA+F;AAE/F,MAAM,CAAC,MAAM,kCAAkC,GAAG,0CAA0C,CAAC;AAO7F,MAAM,UAAU,uBAAuB,CAAC,KAAa,EAAE,GAAW;IACjE,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,SAAS,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,IAAI,SAAS,CAAC;IACtG,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,KAAa;IAC1D,MAAM,SAAS,GAAG,uBAAuB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAC7D,OAAO,SAAS,CAAC,CAAC,CAAC,WAAW,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,KAAc;IAC3D,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChD,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAClC,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ;YAAE,OAAO,SAAS,CAAC;QACjG,OAAO,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;AACF,CAAC;AAED,SAAS,gBAAgB,CAAC,QAAiB,EAAE,KAAyB;IACrE,IAAI,CAAC,KAAK,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAClF,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,6BAA6B,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;IACxE,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,uBAAuB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACnG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CAAC,KAI3C;IACA,OAAO,CACN,gBAAgB,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,KAAK,CAAC;QAChD,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,6BAA6B,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACtE,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,uBAAuB,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QACtF,kCAAkC,CAClC,CAAC;AACH,CAAC","sourcesContent":["// The Copilot API host is per account plan (individual, business, enterprise, data-residency\n// tenants). A request sent to another plan's host is refused with `421 Misdirected Request`,\n// so the host always comes from the account's own token; the individual host is a last resort.\n\nexport const GITHUB_COPILOT_INDIVIDUAL_BASE_URL = \"https://api.individual.githubcopilot.com\";\n\nexport interface GitHubCopilotApiEndpoint {\n\treadonly tid: string;\n\treadonly url: string;\n}\n\nexport function githubCopilotTokenField(token: string, key: string): string | undefined {\n\tfor (const part of token.split(\";\")) {\n\t\tconst separator = part.indexOf(\"=\");\n\t\tif (separator > 0 && part.slice(0, separator) === key) return part.slice(separator + 1) || undefined;\n\t}\n\treturn undefined;\n}\n\nexport function githubCopilotBaseUrlFromToken(token: string): string | undefined {\n\tconst proxyHost = githubCopilotTokenField(token, \"proxy-ep\");\n\treturn proxyHost ? `https://${proxyHost.replace(/^proxy\\./, \"api.\")}` : undefined;\n}\n\nexport function parseGitHubCopilotApiEndpoint(value: unknown): string | undefined {\n\tif (typeof value !== \"string\") return undefined;\n\ttry {\n\t\tconst url = new URL(value.trim());\n\t\tif (url.protocol !== \"https:\" || !url.hostname || url.username || url.password) return undefined;\n\t\treturn `${url.origin}${url.pathname.replace(/\\/+$/, \"\")}`;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction endpointForToken(endpoint: unknown, token: string | undefined): string | undefined {\n\tif (!token || typeof endpoint !== \"object\" || endpoint === null) return undefined;\n\tconst tid = Reflect.get(endpoint, \"tid\");\n\tconst url = parseGitHubCopilotApiEndpoint(Reflect.get(endpoint, \"url\"));\n\treturn typeof tid === \"string\" && tid === githubCopilotTokenField(token, \"tid\") ? url : undefined;\n}\n\n/**\n * The account's Copilot API base URL, in order: the token response's `endpoints.api` recorded for\n * this exact token (matched by `tid`, so a pooled sibling account never borrows it), the token's\n * `proxy-ep`, the GitHub Enterprise Server domain, then the individual host.\n */\nexport function resolveGitHubCopilotBaseUrl(input: {\n\ttoken?: string;\n\tapiEndpoint?: unknown;\n\tenterpriseDomain?: string;\n}): string {\n\treturn (\n\t\tendpointForToken(input.apiEndpoint, input.token) ??\n\t\t(input.token ? githubCopilotBaseUrlFromToken(input.token) : undefined) ??\n\t\t(input.enterpriseDomain ? `https://copilot-api.${input.enterpriseDomain}` : undefined) ??\n\t\tGITHUB_COPILOT_INDIVIDUAL_BASE_URL\n\t);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"github-copilot-errors.d.ts","sourceRoot":"","sources":["../../src/api/github-copilot-errors.ts"],"names":[],"mappings":"AA0CA;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAe/E;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAItG"}
1
+ {"version":3,"file":"github-copilot-errors.d.ts","sourceRoot":"","sources":["../../src/api/github-copilot-errors.ts"],"names":[],"mappings":"AA0CA;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAkB/E;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAItG"}
@@ -60,6 +60,9 @@ export function describeGitHubCopilotFailure(error) {
60
60
  if (isQuotaFailure(status, body, headerValue(headers, "x-ratelimit-exceeded"))) {
61
61
  return `GitHub Copilot quota exceeded (HTTP ${status}): the plan's included usage or its additional-usage limit is used up, so premium models are refused until it resets or the limit is raised. Usage is shown at https://github.com/settings/copilot.${reference}`;
62
62
  }
63
+ if (status === 421) {
64
+ return `GitHub Copilot sent this account to a different API host (HTTP 421 Misdirected Request): Business and Enterprise accounts are served from their own host, which senpi reads from the Copilot token. Run /login github-copilot so senpi stores the account's endpoint, and report it with the GitHub request id if it persists.${reference}`;
65
+ }
63
66
  if (status === 403) {
64
67
  const reason = body === undefined ? " with an empty body" : "";
65
68
  return `GitHub Copilot refused the request (HTTP 403${reason}). Copilot checks access per model and per client; if a fresh /login github-copilot does not help, report it with the GitHub request id.${reference}`;
@@ -1 +1 @@
1
- {"version":3,"file":"github-copilot-errors.js","sourceRoot":"","sources":["../../src/api/github-copilot-errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,+FAA+F;AAC/F,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC;IAC3B,gBAAgB;IAChB,qBAAqB;IACrB,uBAAuB;IACvB,wBAAwB;IACxB,gCAAgC;CAChC,CAAC,CAAC;AAEH,SAAS,WAAW,CAAC,OAAgB,EAAE,IAAY;IAClD,IAAI,OAAO,YAAY,OAAO;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC;IACtE,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACtE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACpD,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;IAC3E,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,SAAS,SAAS,CAAC,IAAwB;IAC1C,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzC,IAAI,CAAC;QACJ,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,SAAS,CAAC;QACpE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC5C,MAAM,IAAI,GACT,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3G,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;AACF,CAAC;AAED,SAAS,cAAc,CAAC,MAAc,EAAE,IAAwB,EAAE,QAA4B;IAC7F,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,IAAI,KAAK,SAAS,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7D,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAChC,OAAO,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,IAAI,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;AACvG,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,4BAA4B,CAAC,KAAc;IAC1D,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAChD,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;IACvD,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,EAAE,qBAAqB,CAAC,CAAC;IAC9D,MAAM,SAAS,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,uBAAuB,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG,CAAC;IACrG,IAAI,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC,EAAE,CAAC;QAChF,OAAO,uCAAuC,MAAM,sMAAsM,SAAS,EAAE,CAAC;IACvQ,CAAC;IACD,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,OAAO,+CAA+C,MAAM,2IAA2I,SAAS,EAAE,CAAC;IACpN,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,OAAe,EAAE,QAAgB,EAAE,KAAc;IAC7F,IAAI,QAAQ,KAAK,gBAAgB;QAAE,OAAO,OAAO,CAAC;IAClD,MAAM,IAAI,GAAG,4BAA4B,CAAC,KAAK,CAAC,CAAC;IACjD,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,IAAI,EAAE,CAAC;AAC7D,CAAC","sourcesContent":["import { normalizeProviderError } from \"../utils/error-body.ts\";\nimport { formatProviderRequestId } from \"../utils/retry.ts\";\n\n// Codes VS Code Copilot Chat treats as quota exhaustion (extensions/copilot chatMLFetcher.ts).\nconst QUOTA_CODES = new Set([\n\t\"quota_exceeded\",\n\t\"free_quota_exceeded\",\n\t\"overage_limit_reached\",\n\t\"billing_not_configured\",\n\t\"additional_spend_limit_reached\",\n]);\n\nfunction headerValue(headers: unknown, name: string): string | undefined {\n\tif (headers instanceof Headers) return headers.get(name) ?? undefined;\n\tif (typeof headers !== \"object\" || headers === null) return undefined;\n\tfor (const [key, value] of Object.entries(headers)) {\n\t\tif (key.toLowerCase() === name && typeof value === \"string\") return value;\n\t}\n\treturn undefined;\n}\n\nfunction errorCode(body: string | undefined): string | undefined {\n\tif (body === undefined) return undefined;\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(body);\n\t\tif (typeof parsed !== \"object\" || parsed === null) return undefined;\n\t\tconst nested = Reflect.get(parsed, \"error\");\n\t\tconst code =\n\t\t\ttypeof nested === \"object\" && nested !== null ? Reflect.get(nested, \"code\") : Reflect.get(parsed, \"code\");\n\t\treturn typeof code === \"string\" ? code : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction isQuotaFailure(status: number, body: string | undefined, limitKey: string | undefined): boolean {\n\tconst code = errorCode(body);\n\tif (code !== undefined && QUOTA_CODES.has(code)) return true;\n\tif (status === 402) return true;\n\treturn status === 429 && (limitKey?.includes(\"quota\") === true || /quota exceeded/i.test(body ?? \"\"));\n}\n\n/**\n * A plain-language note for a GitHub Copilot HTTP failure that the SDK message alone\n * leaves opaque (`403 status code (no body)`): which refusal it is, where to look, and\n * the GitHub request id support needs. `undefined` for failures the message already\n * explains. The wording avoids credential words so pool classification is unchanged.\n */\nexport function describeGitHubCopilotFailure(error: unknown): string | undefined {\n\tif (!(error instanceof Error)) return undefined;\n\tconst { status, body } = normalizeProviderError(error);\n\tif (status === undefined) return undefined;\n\tconst headers = Reflect.get(error, \"headers\");\n\tconst requestId = headerValue(headers, \"x-github-request-id\");\n\tconst reference = requestId === undefined ? \"\" : ` ${formatProviderRequestId(\"GitHub\", requestId)}.`;\n\tif (isQuotaFailure(status, body, headerValue(headers, \"x-ratelimit-exceeded\"))) {\n\t\treturn `GitHub Copilot quota exceeded (HTTP ${status}): the plan's included usage or its additional-usage limit is used up, so premium models are refused until it resets or the limit is raised. Usage is shown at https://github.com/settings/copilot.${reference}`;\n\t}\n\tif (status === 403) {\n\t\tconst reason = body === undefined ? \" with an empty body\" : \"\";\n\t\treturn `GitHub Copilot refused the request (HTTP 403${reason}). Copilot checks access per model and per client; if a fresh /login github-copilot does not help, report it with the GitHub request id.${reference}`;\n\t}\n\treturn undefined;\n}\n\nexport function withGitHubCopilotFailureNote(message: string, provider: string, error: unknown): string {\n\tif (provider !== \"github-copilot\") return message;\n\tconst note = describeGitHubCopilotFailure(error);\n\treturn note === undefined ? message : `${message}\\n${note}`;\n}\n"]}
1
+ {"version":3,"file":"github-copilot-errors.js","sourceRoot":"","sources":["../../src/api/github-copilot-errors.ts"],"names":[],"mappings":"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","sourcesContent":["import { normalizeProviderError } from \"../utils/error-body.ts\";\nimport { formatProviderRequestId } from \"../utils/retry.ts\";\n\n// Codes VS Code Copilot Chat treats as quota exhaustion (extensions/copilot chatMLFetcher.ts).\nconst QUOTA_CODES = new Set([\n\t\"quota_exceeded\",\n\t\"free_quota_exceeded\",\n\t\"overage_limit_reached\",\n\t\"billing_not_configured\",\n\t\"additional_spend_limit_reached\",\n]);\n\nfunction headerValue(headers: unknown, name: string): string | undefined {\n\tif (headers instanceof Headers) return headers.get(name) ?? undefined;\n\tif (typeof headers !== \"object\" || headers === null) return undefined;\n\tfor (const [key, value] of Object.entries(headers)) {\n\t\tif (key.toLowerCase() === name && typeof value === \"string\") return value;\n\t}\n\treturn undefined;\n}\n\nfunction errorCode(body: string | undefined): string | undefined {\n\tif (body === undefined) return undefined;\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(body);\n\t\tif (typeof parsed !== \"object\" || parsed === null) return undefined;\n\t\tconst nested = Reflect.get(parsed, \"error\");\n\t\tconst code =\n\t\t\ttypeof nested === \"object\" && nested !== null ? Reflect.get(nested, \"code\") : Reflect.get(parsed, \"code\");\n\t\treturn typeof code === \"string\" ? code : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction isQuotaFailure(status: number, body: string | undefined, limitKey: string | undefined): boolean {\n\tconst code = errorCode(body);\n\tif (code !== undefined && QUOTA_CODES.has(code)) return true;\n\tif (status === 402) return true;\n\treturn status === 429 && (limitKey?.includes(\"quota\") === true || /quota exceeded/i.test(body ?? \"\"));\n}\n\n/**\n * A plain-language note for a GitHub Copilot HTTP failure that the SDK message alone\n * leaves opaque (`403 status code (no body)`): which refusal it is, where to look, and\n * the GitHub request id support needs. `undefined` for failures the message already\n * explains. The wording avoids credential words so pool classification is unchanged.\n */\nexport function describeGitHubCopilotFailure(error: unknown): string | undefined {\n\tif (!(error instanceof Error)) return undefined;\n\tconst { status, body } = normalizeProviderError(error);\n\tif (status === undefined) return undefined;\n\tconst headers = Reflect.get(error, \"headers\");\n\tconst requestId = headerValue(headers, \"x-github-request-id\");\n\tconst reference = requestId === undefined ? \"\" : ` ${formatProviderRequestId(\"GitHub\", requestId)}.`;\n\tif (isQuotaFailure(status, body, headerValue(headers, \"x-ratelimit-exceeded\"))) {\n\t\treturn `GitHub Copilot quota exceeded (HTTP ${status}): the plan's included usage or its additional-usage limit is used up, so premium models are refused until it resets or the limit is raised. Usage is shown at https://github.com/settings/copilot.${reference}`;\n\t}\n\tif (status === 421) {\n\t\treturn `GitHub Copilot sent this account to a different API host (HTTP 421 Misdirected Request): Business and Enterprise accounts are served from their own host, which senpi reads from the Copilot token. Run /login github-copilot so senpi stores the account's endpoint, and report it with the GitHub request id if it persists.${reference}`;\n\t}\n\tif (status === 403) {\n\t\tconst reason = body === undefined ? \" with an empty body\" : \"\";\n\t\treturn `GitHub Copilot refused the request (HTTP 403${reason}). Copilot checks access per model and per client; if a fresh /login github-copilot does not help, report it with the GitHub request id.${reference}`;\n\t}\n\treturn undefined;\n}\n\nexport function withGitHubCopilotFailureNote(message: string, provider: string, error: unknown): string {\n\tif (provider !== \"github-copilot\") return message;\n\tconst note = describeGitHubCopilotFailure(error);\n\treturn note === undefined ? message : `${message}\\n${note}`;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"github-copilot.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/github-copilot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AAgcvF,eAAO,MAAM,kBAAkB,EAAE,SAehC,CAAC"}
1
+ {"version":3,"file":"github-copilot.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/github-copilot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AAubvF,eAAO,MAAM,kBAAkB,EAAE,SAmBhC,CAAC"}