@code-yeongyu/senpi 2026.11.10 → 2026.11.11

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 (320) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/dist/bundle/{SKILL-B4R7BVOT.md → SKILL-R7FEIQKG.md} +1 -1
  3. package/dist/bundle/chunks/{SKILL-AZ6ONO52.js → SKILL-5O2UKORG.js} +1 -1
  4. package/dist/bundle/chunks/{SKILL-B4R7BVOT.md → SKILL-R7FEIQKG.md} +1 -1
  5. package/dist/bundle/chunks/{args-77GYOPEO.js → args-HFYB6URH.js} +1 -1
  6. package/dist/bundle/chunks/{azure-openai-responses-YFZ734T3.js → azure-openai-responses-MNN2TINI.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-ZUQCYIRY.js → chunk-2ODGHBUD.js} +1 -1
  8. package/dist/bundle/chunks/chunk-3IYEGHBI.js +2 -0
  9. package/dist/bundle/chunks/{chunk-I5ASHSLZ.js → chunk-4JMGWVRH.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-YKYWL2NO.js → chunk-5F2XC6UC.js} +3 -3
  11. package/dist/bundle/chunks/{chunk-MSDBBOB2.js → chunk-5H43O4MA.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-R3OP7IA4.js → chunk-5PIZWSNG.js} +4 -4
  13. package/dist/bundle/chunks/{chunk-33MZJTQ3.js → chunk-B4WD5XHP.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-Q63YLZMN.js → chunk-BPEA5SWW.js} +2 -2
  15. package/dist/bundle/chunks/{chunk-MOVJQVMC.js → chunk-BUDJLVMF.js} +3 -3
  16. package/dist/bundle/chunks/{chunk-V5FSENW6.js → chunk-COKX65TE.js} +1 -1
  17. package/dist/bundle/chunks/{chunk-2EJ4BGBM.js → chunk-DHORIIVQ.js} +1 -1
  18. package/dist/bundle/chunks/{chunk-NH7SYIGN.js → chunk-DLPV4B3R.js} +1 -1
  19. package/dist/bundle/chunks/{chunk-RCHIMH7N.js → chunk-DQY26AX3.js} +1 -1
  20. package/dist/bundle/chunks/{chunk-Y7Z7XPUF.js → chunk-DZQA5LVN.js} +1 -1
  21. package/dist/bundle/chunks/{chunk-2CNSCMYC.js → chunk-E5O4SP5I.js} +1 -1
  22. package/dist/bundle/chunks/{chunk-3Q5ZENSD.js → chunk-E6ZBTSC6.js} +1 -1
  23. package/dist/bundle/chunks/{chunk-GVPZPVVO.js → chunk-IBDAV2AL.js} +3 -3
  24. package/dist/bundle/chunks/chunk-IREUIMFE.js +11 -0
  25. package/dist/bundle/chunks/{chunk-KU7OCWJF.js → chunk-J4ADZ2OK.js} +1 -1
  26. package/dist/bundle/chunks/{chunk-T2SMQPRE.js → chunk-K3GAGIPX.js} +1 -1
  27. package/dist/bundle/chunks/{chunk-BELNOPAI.js → chunk-L3KVP7VJ.js} +3 -3
  28. package/dist/bundle/chunks/{chunk-IYSJFQUU.js → chunk-L6DF5G6K.js} +1 -1
  29. package/dist/bundle/chunks/{chunk-7MN7YNO5.js → chunk-L7DGTA3L.js} +1 -1
  30. package/dist/bundle/chunks/chunk-LJFQYCFH.js +2 -0
  31. package/dist/bundle/chunks/{chunk-J2NGJKXL.js → chunk-LXINMDMX.js} +1 -1
  32. package/dist/bundle/chunks/{chunk-H33A33M4.js → chunk-M2VJVJMK.js} +1 -1
  33. package/dist/bundle/chunks/{chunk-2U6HXWXS.js → chunk-NNQ34ZDW.js} +1 -1
  34. package/dist/bundle/chunks/chunk-NQKCJQHB.js +2 -0
  35. package/dist/bundle/chunks/{chunk-EJB5E2JK.js → chunk-NRZAJUUO.js} +2 -2
  36. package/dist/bundle/chunks/{chunk-R2Z7CX77.js → chunk-OSNR3IRD.js} +1 -1
  37. package/dist/bundle/chunks/{chunk-O3FG6HML.js → chunk-PJI5GT7U.js} +1 -1
  38. package/dist/bundle/chunks/{chunk-FDNM2DAE.js → chunk-PUZDKFRT.js} +164 -164
  39. package/dist/bundle/chunks/{chunk-BPNSOZ2C.js → chunk-QUVSIW2R.js} +1 -1
  40. package/dist/bundle/chunks/{chunk-WXYMAUFX.js → chunk-QX7WD3IM.js} +1 -1
  41. package/dist/bundle/chunks/{chunk-Y4ONXQMR.js → chunk-RGHVPUUX.js} +1 -1
  42. package/dist/bundle/chunks/{chunk-RYPM5BZQ.js → chunk-S7NAJQB3.js} +1 -1
  43. package/dist/bundle/chunks/{chunk-2BH54YQR.js → chunk-SANWO37B.js} +1 -1
  44. package/dist/bundle/chunks/{chunk-YQZNARTX.js → chunk-SS5VTBAV.js} +1 -1
  45. package/dist/bundle/chunks/{chunk-LT353NSF.js → chunk-T4CUD7RF.js} +1 -1
  46. package/dist/bundle/chunks/{chunk-J7FW7MED.js → chunk-TCZORL3V.js} +1 -1
  47. package/dist/bundle/chunks/{chunk-KU72IKV5.js → chunk-VGSWOLIY.js} +1 -1
  48. package/dist/bundle/chunks/{chunk-DBQBJTMF.js → chunk-VUY4BZDI.js} +1 -1
  49. package/dist/bundle/chunks/chunk-XI6THO4F.js +2 -0
  50. package/dist/bundle/chunks/{chunk-FAFIGBUD.js → chunk-XUDKT4UH.js} +3 -3
  51. package/dist/bundle/chunks/{cli-main-BX5AFWJQ.js → cli-main-7II7GCIR.js} +1 -1
  52. package/dist/bundle/chunks/{credential-accounts-LVVPKFEY.js → credential-accounts-N3B5YJS7.js} +1 -1
  53. package/dist/bundle/chunks/{dispatch-EP35KKJ3.js → dispatch-YUYZQXRP.js} +1 -1
  54. package/dist/bundle/chunks/github-copilot.js +1 -1
  55. package/dist/bundle/chunks/{hand-entry-F6PWKNPJ.js → hand-entry-ONOLRMVB.js} +3 -3
  56. package/dist/bundle/chunks/{headless-host-HKGNJGYT.js → headless-host-53UMJMM6.js} +1 -1
  57. package/dist/bundle/chunks/{health-write-FBF74QJ7.js → health-write-JLSF5EVV.js} +1 -1
  58. package/dist/bundle/chunks/{help-fast-path-5SCPY3OH.js → help-fast-path-552QLMWT.js} +1 -1
  59. package/dist/bundle/chunks/{host-command-47QQKJEY.js → host-command-KWMJLPBS.js} +1 -1
  60. package/dist/bundle/chunks/{host-lifecycle-VSAH4M3R.js → host-lifecycle-4WQDCN3Y.js} +1 -1
  61. package/dist/bundle/chunks/host-lifecycle.js +5 -5
  62. package/dist/bundle/chunks/{host-session-command-SL4AN5WD.js → host-session-command-ZDYEKCUB.js} +1 -1
  63. package/dist/bundle/chunks/{host-socket-request-DMYXC56U.js → host-socket-request-QN4Q2P5E.js} +1 -1
  64. package/dist/bundle/chunks/{interactive-mode-JDMIHJMQ.js → interactive-mode-EPE4M32G.js} +1 -1
  65. package/dist/bundle/chunks/{login-health-EVXVLHB2.js → login-health-TOSQT5RG.js} +1 -1
  66. package/dist/bundle/chunks/{main-4RITUAQF.js → main-OM7BKVZG.js} +1 -1
  67. package/dist/bundle/chunks/{multi-session-host-SKDD4OWM.js → multi-session-host-DB6XMY53.js} +11 -8
  68. package/dist/bundle/chunks/openai-codex-responses-VRGOAFEG.js +10 -0
  69. package/dist/bundle/chunks/{openai-responses-NIVBWE2H.js → openai-responses-I7PXRWCM.js} +2 -2
  70. package/dist/bundle/chunks/{package-manager-cli-B5VA4KFO.js → package-manager-cli-GLU5Q2KV.js} +1 -1
  71. package/dist/bundle/chunks/{role-HQUN4XXK.js → role-AEOLJRTP.js} +1 -1
  72. package/dist/bundle/chunks/{rotation-stream-F3BEMXBE.js → rotation-stream-E3FJFHXJ.js} +1 -1
  73. package/dist/bundle/chunks/rpc-mode-SDJVBT36.js +2 -0
  74. package/dist/bundle/chunks/{rpc-monitor-source-C6VQ3BC4.js → rpc-monitor-source-PGTDSGWP.js} +1 -1
  75. package/dist/bundle/chunks/{run-attached-tui-R3RB5P7G.js → run-attached-tui-4AENRP7P.js} +1 -1
  76. package/dist/bundle/chunks/{schedule-command-FPEQCX33.js → schedule-command-5SQD7M5N.js} +1 -1
  77. package/dist/bundle/chunks/session-control-endpoint-R25A2QGS.js +3 -0
  78. package/dist/bundle/chunks/{session-picker-2FZSNFS7.js → session-picker-GZM5CVQL.js} +1 -1
  79. package/dist/bundle/chunks/{session-slot-CZGQQCOC.js → session-slot-RH7JLO3F.js} +1 -1
  80. package/dist/bundle/chunks/session-worker.js +335 -332
  81. package/dist/bundle/chunks/{state-store-S7QAESIF.js → state-store-IECVTYXC.js} +1 -1
  82. package/dist/bundle/cli.js +2 -2
  83. package/dist/bundle/host-supervisor.js +5 -5
  84. package/dist/bundle/index.js +1 -1
  85. package/dist/bundle/rpc-entry.js +1 -1
  86. package/dist/bundle/runtime-manifest.json +1 -1
  87. package/dist/cli.js +9 -3
  88. package/dist/core/agent-session-runtime.d.ts +2 -0
  89. package/dist/core/agent-session-runtime.js +5 -0
  90. package/dist/core/agent-session.d.ts +15 -3
  91. package/dist/core/agent-session.js +91 -18
  92. package/dist/core/assistant-error-redaction.d.ts +9 -0
  93. package/dist/core/assistant-error-redaction.js +27 -0
  94. package/dist/core/config-reference-redaction.js +90 -10
  95. package/dist/core/context-files.d.ts +10 -0
  96. package/dist/core/context-files.js +58 -12
  97. package/dist/core/credential-accounts.d.ts +2 -0
  98. package/dist/core/credential-accounts.js +10 -1
  99. package/dist/core/dynamic-prompt/intent-gate.js +2 -2
  100. package/dist/core/dynamic-prompt/style.js +2 -2
  101. package/dist/core/dynamic-prompt/verification.js +2 -2
  102. package/dist/core/egress/wire.d.ts +13 -0
  103. package/dist/core/extensions/builtin/anthropic-subscription/accounts.d.ts +3 -0
  104. package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.js +3 -2
  105. package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-account-test.js +3 -1
  106. package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-connection-test.d.ts +3 -1
  107. package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-connection-test.js +47 -16
  108. package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-login-slots.d.ts +1 -1
  109. package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-login-slots.js +4 -2
  110. package/dist/core/extensions/builtin/anthropic-subscription/claude-settings-env.d.ts +20 -0
  111. package/dist/core/extensions/builtin/anthropic-subscription/claude-settings-env.js +39 -0
  112. package/dist/core/extensions/builtin/anthropic-subscription/context-window.d.ts +17 -0
  113. package/dist/core/extensions/builtin/anthropic-subscription/context-window.js +23 -0
  114. package/dist/core/extensions/builtin/anthropic-subscription/errors.d.ts +1 -0
  115. package/dist/core/extensions/builtin/anthropic-subscription/errors.js +14 -5
  116. package/dist/core/extensions/builtin/anthropic-subscription/oauth-login.js +3 -3
  117. package/dist/core/extensions/builtin/anthropic-subscription/prompt-cache-ttl.d.ts +10 -4
  118. package/dist/core/extensions/builtin/anthropic-subscription/prompt-cache-ttl.js +14 -5
  119. package/dist/core/extensions/builtin/anthropic-subscription/query-environment.d.ts +3 -2
  120. package/dist/core/extensions/builtin/anthropic-subscription/query-environment.js +5 -4
  121. package/dist/core/extensions/builtin/anthropic-subscription/sdk-boundary.lazy.d.ts +2 -1
  122. package/dist/core/extensions/builtin/anthropic-subscription/sdk-boundary.lazy.js +1 -0
  123. package/dist/core/extensions/builtin/anthropic-subscription/stream.js +11 -1
  124. package/dist/core/extensions/builtin/config-reload/change-groups.js +6 -2
  125. package/dist/core/extensions/builtin/goal/continuation.d.ts +6 -0
  126. package/dist/core/extensions/builtin/goal/continuation.js +3 -2
  127. package/dist/core/extensions/builtin/goal/index.js +14 -26
  128. package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts +8 -6
  129. package/dist/core/extensions/builtin/goal/lifecycle-helpers.js +77 -39
  130. package/dist/core/extensions/builtin/goal/monitor-continuation.js +66 -7
  131. package/dist/core/extensions/builtin/goal/persistence.js +20 -0
  132. package/dist/core/extensions/builtin/goal/provider-retry.d.ts +9 -0
  133. package/dist/core/extensions/builtin/goal/provider-retry.js +16 -0
  134. package/dist/core/extensions/builtin/goal/reload-reengagement.d.ts +3 -2
  135. package/dist/core/extensions/builtin/goal/reload-reengagement.js +4 -4
  136. package/dist/core/extensions/builtin/goal/session-start-continuation.d.ts +28 -0
  137. package/dist/core/extensions/builtin/goal/session-start-continuation.js +75 -0
  138. package/dist/core/extensions/builtin/goal/store.d.ts +3 -2
  139. package/dist/core/extensions/builtin/goal/store.js +9 -2
  140. package/dist/core/extensions/builtin/goal/types.d.ts +9 -0
  141. package/dist/core/extensions/builtin/handoff-hint/index.js +15 -11
  142. package/dist/core/extensions/builtin/handoff-hint/replacement-relay.d.ts +16 -0
  143. package/dist/core/extensions/builtin/handoff-hint/replacement-relay.js +17 -0
  144. package/dist/core/extensions/builtin/html-render/tool.js +13 -3
  145. package/dist/core/extensions/builtin/imagegen/skill/SKILL.md +1 -1
  146. package/dist/core/extensions/builtin/imagegen/tool.js +4 -14
  147. package/dist/core/extensions/builtin/mcp/auth/catalog-identity.js +32 -8
  148. package/dist/core/extensions/builtin/mcp/auth/commands-auth.js +2 -1
  149. package/dist/core/extensions/builtin/mcp/auth/context.js +3 -0
  150. package/dist/core/extensions/builtin/mcp/auth/oauth-grant.d.ts +43 -0
  151. package/dist/core/extensions/builtin/mcp/auth/oauth-grant.js +196 -0
  152. package/dist/core/extensions/builtin/mcp/auth/oauth-issuer.d.ts +7 -1
  153. package/dist/core/extensions/builtin/mcp/auth/oauth-issuer.js +13 -0
  154. package/dist/core/extensions/builtin/mcp/auth/oauth-provider.d.ts +6 -17
  155. package/dist/core/extensions/builtin/mcp/auth/oauth-provider.js +67 -88
  156. package/dist/core/extensions/builtin/mcp/auth/oauth-refresh.d.ts +1 -1
  157. package/dist/core/extensions/builtin/mcp/auth/oauth-refresh.js +25 -4
  158. package/dist/core/extensions/builtin/mcp/auth/oauth.js +10 -7
  159. package/dist/core/extensions/builtin/mcp/auth/registered-redirect.d.ts +19 -0
  160. package/dist/core/extensions/builtin/mcp/auth/registered-redirect.js +55 -0
  161. package/dist/core/extensions/builtin/mcp/auth/token-store.d.ts +23 -1
  162. package/dist/core/extensions/builtin/mcp/auth/token-store.js +3 -0
  163. package/dist/core/extensions/builtin/mcp/catalog-cache.js +7 -6
  164. package/dist/core/extensions/builtin/mcp/catalog.js +3 -2
  165. package/dist/core/extensions/builtin/mcp/invocation.js +6 -0
  166. package/dist/core/extensions/builtin/mcp/service-register.js +16 -2
  167. package/dist/core/extensions/builtin/mcp/service-tools-changed.js +61 -54
  168. package/dist/core/extensions/builtin/mcp/startup-race.d.ts +2 -0
  169. package/dist/core/extensions/builtin/mcp/startup-race.js +25 -7
  170. package/dist/core/extensions/builtin/permission-system/parsers.js +1 -0
  171. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.js +3 -3
  172. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-2.js +9 -8
  173. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js +3 -3
  174. package/dist/core/extensions/builtin/prompt-preset/claude-haiku-5-5.js +3 -3
  175. package/dist/core/extensions/builtin/prompt-preset/claude-opus-5-5.js +3 -3
  176. package/dist/core/extensions/builtin/prompt-preset/claude-opus-5.js +4 -4
  177. package/dist/core/extensions/builtin/prompt-preset/claude-sonnet-5-5.js +3 -3
  178. package/dist/core/extensions/builtin/prompt-preset/deepseek-v4-1-flash.js +3 -3
  179. package/dist/core/extensions/builtin/prompt-preset/gpt-5.5.js +3 -3
  180. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +2 -2
  181. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js +3 -3
  182. package/dist/core/extensions/builtin/prompt-preset/grok-4.5.js +1 -1
  183. package/dist/core/extensions/builtin/prompt-preset/grok-4.6.js +4 -4
  184. package/dist/core/extensions/builtin/prompt-preset/grok-4.7.js +5 -5
  185. package/dist/core/extensions/builtin/prompt-preset/kimi-k3-1.js +4 -4
  186. package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js +7 -6
  187. package/dist/core/extensions/loader.js +24 -4
  188. package/dist/core/extensions/runner.d.ts +9 -1
  189. package/dist/core/extensions/runner.js +9 -4
  190. package/dist/core/extensions/types.d.ts +19 -3
  191. package/dist/core/hand/hook-advertise.d.ts +41 -0
  192. package/dist/core/hand/hook-advertise.js +98 -0
  193. package/dist/core/hand/hook-contract.d.ts +66 -0
  194. package/dist/core/hand/hook-contract.js +138 -0
  195. package/dist/core/open-timing.d.ts +2 -0
  196. package/dist/core/open-timing.js +7 -1
  197. package/dist/core/resource-loader.js +21 -3
  198. package/dist/core/retry-fallback/controller-types.d.ts +2 -0
  199. package/dist/core/retry-fallback/controller.js +9 -2
  200. package/dist/core/retry-fallback/cooldown.d.ts +7 -0
  201. package/dist/core/retry-fallback/cooldown.js +13 -1
  202. package/dist/core/retry-fallback/credential-recovery.d.ts +7 -0
  203. package/dist/core/retry-fallback/credential-recovery.js +24 -0
  204. package/dist/core/retry-fallback/log.d.ts +7 -1
  205. package/dist/core/retry-fallback/log.js +82 -5
  206. package/dist/core/retry-fallback/model-config-error.js +2 -1
  207. package/dist/core/retry-fallback/settings.d.ts +8 -5
  208. package/dist/core/retry-fallback/settings.js +9 -6
  209. package/dist/core/session-manager.d.ts +2 -0
  210. package/dist/core/session-manager.js +15 -1
  211. package/dist/core/settings-manager.d.ts +6 -0
  212. package/dist/core/settings-manager.js +8 -0
  213. package/dist/core/turn-origin.d.ts +18 -10
  214. package/dist/core/turn-origin.js +34 -30
  215. package/dist/core/ultrafast-lanes.d.ts +1 -0
  216. package/dist/core/ultrafast-lanes.js +4 -2
  217. package/dist/modes/interactive/components/footer.d.ts +23 -5
  218. package/dist/modes/interactive/components/footer.js +45 -16
  219. package/dist/modes/interactive/interactive-mode.js +9 -1
  220. package/dist/modes/interactive/session-control-rpc-commands.d.ts +1 -1
  221. package/dist/modes/interactive/session-control-rpc-commands.js +5 -0
  222. package/dist/modes/rpc/admit-external-message-command.d.ts +2 -0
  223. package/dist/modes/rpc/admit-external-message-command.js +5 -1
  224. package/dist/modes/rpc/compact-admission-validation.js +4 -1
  225. package/dist/modes/rpc/connection-authority.d.ts +2 -1
  226. package/dist/modes/rpc/connection-authority.js +11 -9
  227. package/dist/modes/rpc/connection-handler.d.ts +2 -0
  228. package/dist/modes/rpc/connection-handler.js +33 -7
  229. package/dist/modes/rpc/connection-roles.d.ts +7 -0
  230. package/dist/modes/rpc/connection-roles.js +15 -0
  231. package/dist/modes/rpc/custom-capability.d.ts +6 -0
  232. package/dist/modes/rpc/custom-capability.js +6 -0
  233. package/dist/modes/rpc/delegated-drive-origin.d.ts +6 -6
  234. package/dist/modes/rpc/delegated-drive-origin.js +7 -8
  235. package/dist/modes/rpc/delegation-declaration.d.ts +2 -0
  236. package/dist/modes/rpc/delegation-declaration.js +7 -3
  237. package/dist/modes/rpc/effect-commands.d.ts +33 -0
  238. package/dist/modes/rpc/host-capabilities.d.ts +1 -0
  239. package/dist/modes/rpc/host-capabilities.js +7 -1
  240. package/dist/modes/rpc/host-gc-evidence.d.ts +1 -1
  241. package/dist/modes/rpc/host-gc-evidence.js +9 -3
  242. package/dist/modes/rpc/host-gc.js +4 -1
  243. package/dist/modes/rpc/host-observe-request.d.ts +2 -0
  244. package/dist/modes/rpc/host-observe-request.js +6 -1
  245. package/dist/modes/rpc/host-park-ledger.d.ts +19 -0
  246. package/dist/modes/rpc/host-park-ledger.js +110 -0
  247. package/dist/modes/rpc/lifecycle-delivery.js +4 -1
  248. package/dist/modes/rpc/loopless-tool-handlers.d.ts +6 -0
  249. package/dist/modes/rpc/loopless-tool-handlers.js +14 -1
  250. package/dist/modes/rpc/media-placeholders.js +1 -0
  251. package/dist/modes/rpc/method-classes-schema-host.d.ts +42 -1
  252. package/dist/modes/rpc/method-classes-schema-host.js +24 -21
  253. package/dist/modes/rpc/method-classes-schema-session.d.ts +20 -0
  254. package/dist/modes/rpc/method-classes-schema-session.js +6 -0
  255. package/dist/modes/rpc/method-classes-schema.d.ts +62 -1
  256. package/dist/modes/rpc/method-classes-schema.js +5 -2
  257. package/dist/modes/rpc/method-classes.d.ts +4 -0
  258. package/dist/modes/rpc/method-classes.js +6 -0
  259. package/dist/modes/rpc/multi-session-host.js +1 -1
  260. package/dist/modes/rpc/project-file-commands.d.ts +32 -0
  261. package/dist/modes/rpc/project-file-commands.js +195 -0
  262. package/dist/modes/rpc/project-file-path.d.ts +34 -0
  263. package/dist/modes/rpc/project-file-path.js +93 -0
  264. package/dist/modes/rpc/rpc-error-types.d.ts +9 -1
  265. package/dist/modes/rpc/rpc-error-types.js +8 -0
  266. package/dist/modes/rpc/rpc-host-command-types.d.ts +14 -1
  267. package/dist/modes/rpc/rpc-host-response-types.d.ts +21 -0
  268. package/dist/modes/rpc/rpc-mutation-response-types.d.ts +7 -0
  269. package/dist/modes/rpc/rpc-read-response-types.d.ts +7 -0
  270. package/dist/modes/rpc/rpc-session-command-types.d.ts +10 -0
  271. package/dist/modes/rpc/rpc-session-state-types.d.ts +2 -0
  272. package/dist/modes/rpc/session-attachment-gate.js +6 -0
  273. package/dist/modes/rpc/session-binding.d.ts +1 -0
  274. package/dist/modes/rpc/session-binding.js +6 -1
  275. package/dist/modes/rpc/session-command-activity.js +1 -0
  276. package/dist/modes/rpc/session-connection-context.d.ts +2 -0
  277. package/dist/modes/rpc/session-connection-context.js +4 -0
  278. package/dist/modes/rpc/session-registry-attach.d.ts +2 -0
  279. package/dist/modes/rpc/session-registry-attach.js +15 -11
  280. package/dist/modes/rpc/session-registry-claims.js +2 -0
  281. package/dist/modes/rpc/session-registry-open.js +13 -7
  282. package/dist/modes/rpc/session-registry-opening.js +2 -1
  283. package/dist/modes/rpc/session-registry-types.d.ts +4 -0
  284. package/dist/modes/rpc/session-registry.d.ts +3 -0
  285. package/dist/modes/rpc/session-registry.js +27 -3
  286. package/dist/modes/rpc/session-router-close.js +1 -0
  287. package/dist/modes/rpc/session-router-context.d.ts +4 -2
  288. package/dist/modes/rpc/session-router-context.js +3 -0
  289. package/dist/modes/rpc/session-router-host-reads.js +1 -0
  290. package/dist/modes/rpc/session-router-occupancy.js +10 -1
  291. package/dist/modes/rpc/session-router-open-result.d.ts +12 -0
  292. package/dist/modes/rpc/session-router-open-result.js +63 -0
  293. package/dist/modes/rpc/session-router-open.d.ts +3 -0
  294. package/dist/modes/rpc/session-router-open.js +15 -63
  295. package/dist/modes/rpc/session-router-park.d.ts +15 -0
  296. package/dist/modes/rpc/session-router-park.js +176 -0
  297. package/dist/modes/rpc/session-router-routing.d.ts +1 -1
  298. package/dist/modes/rpc/session-router-routing.js +7 -0
  299. package/dist/modes/rpc/session-status-source.js +2 -2
  300. package/dist/private-fd-startup-fillers.d.ts +2 -2
  301. package/dist/private-fd-startup-fillers.js +34 -4
  302. package/dist/senpi +9 -3
  303. package/docs/engine-socket/fixtures/admission-routes/h-legacy.json +1 -1
  304. package/docs/engine-socket/fixtures/rpc-inbound-samples.json +44 -2
  305. package/docs/extensions.md +11 -1
  306. package/docs/rpc.md +106 -2
  307. package/docs/settings.md +5 -3
  308. package/docs/skills.md +1 -1
  309. package/docs/usage.md +1 -1
  310. package/native/socket-secret/prebuilds/win32-arm64/senpi_socket_secret.win32-arm64.node +0 -0
  311. package/native/socket-secret/prebuilds/win32-x64/senpi_socket_secret.win32-x64.node +0 -0
  312. package/package.json +6 -6
  313. package/dist/bundle/chunks/chunk-436DNN4Z.js +0 -2
  314. package/dist/bundle/chunks/chunk-DLAY7CIU.js +0 -2
  315. package/dist/bundle/chunks/chunk-SJI6UB23.js +0 -2
  316. package/dist/bundle/chunks/chunk-XH2MWTXA.js +0 -11
  317. package/dist/bundle/chunks/chunk-YK3ZZCHI.js +0 -2
  318. package/dist/bundle/chunks/openai-codex-responses-ABB2J43U.js +0 -10
  319. package/dist/bundle/chunks/rpc-mode-ZDUGW2IM.js +0 -2
  320. package/dist/bundle/chunks/session-control-endpoint-MEDV66PS.js +0 -3
@@ -28,7 +28,7 @@ export async function openRegistrySession(host, requested, options) {
28
28
  const requestedDurableId = profile.durableSessionId;
29
29
  if (requestedDurableId !== undefined) {
30
30
  for (const entry of host.entries.values()) {
31
- if (entry.state === "closed")
31
+ if (entry.state === "closed" || entry.speculative)
32
32
  continue;
33
33
  if (entry.durableSessionId !== requestedDurableId)
34
34
  continue;
@@ -101,7 +101,8 @@ export async function openRegistrySession(host, requested, options) {
101
101
  const { entry, storedProfile, runtimeProfile, sessionStartEvent, settleOpen } = opening;
102
102
  host.entries.set(handle, entry);
103
103
  claimSettled?.();
104
- host.options.onSizeChange?.(host.entries.size);
104
+ if (!entry.speculative)
105
+ host.options.onSizeChange?.([...host.entries.values()].filter((item) => !item.speculative).length);
105
106
  const timer = startOpenTimer(`registry-open:${handle}`);
106
107
  try {
107
108
  const newSessionOptions = {
@@ -112,7 +113,9 @@ export async function openRegistrySession(host, requested, options) {
112
113
  ? host.options.sessionFileLoad === "blocking"
113
114
  ? SessionManager.open(sessionPath, undefined, storedProfile.cwd, newSessionOptions)
114
115
  : await SessionManager.openAsync(sessionPath, undefined, storedProfile.cwd, newSessionOptions, host.options.createSlicer?.())
115
- : SessionManager.create(storedProfile.cwd, undefined, newSessionOptions);
116
+ : options?.speculative
117
+ ? SessionManager.inMemory(storedProfile.cwd, newSessionOptions)
118
+ : SessionManager.create(storedProfile.cwd, undefined, newSessionOptions);
116
119
  timer.mark("session_manager");
117
120
  entry.runtime = await runWithProviderScope(entry.scope, () => createAgentSessionRuntime(host.options.createRuntime, {
118
121
  cwd: manager.getCwd(),
@@ -125,19 +128,21 @@ export async function openRegistrySession(host, requested, options) {
125
128
  assertRequestedModelResolved(runtimeProfile, entry.runtime.diagnostics);
126
129
  ClientAdmissions.sealQueuedOnReplace(entry.runtime);
127
130
  EffectLedger.sealOnReplace(entry.runtime);
128
- entry.durableSessionId = manager.getSessionId();
131
+ const durableSessionId = manager.getSessionId();
132
+ if (!entry.speculative)
133
+ entry.durableSessionId = durableSessionId;
129
134
  entry.sessionPath ??= manager.getSessionFile();
130
135
  // The accepted baseline precedes the open, so router admission and its reply come after it (R3.C1.1).
131
136
  // Gated like a worker incarnation's lane: with the flag off no session owns one (RULING-112).
132
137
  if (host.options.workerIsolate !== true && structuredLaneEnabled()) {
133
138
  entry.acceptance = new SessionAcceptanceLane(undefined, undefined, host.options.agentDir);
134
- await entry.acceptance.generations.bind(entry.durableSessionId);
139
+ await entry.acceptance.generations.bind(durableSessionId);
135
140
  entry.acceptance.bootstrap({ transcript: manager.beginTranscriptJournal() });
136
141
  setSessionRecoveryEpoch(entry.runtime.session, entry.acceptance.recoveryEpoch);
137
142
  }
138
143
  entry.state = "open";
139
144
  settleOpen.resolve();
140
- return { sessionId: handle, durableSessionId: entry.durableSessionId, sessionPath: entry.sessionPath };
145
+ return { sessionId: handle, durableSessionId, sessionPath: entry.sessionPath };
141
146
  }
142
147
  catch (error) {
143
148
  // Runtime construction may have started extensions, watchers, and provider
@@ -156,7 +161,8 @@ export async function openRegistrySession(host, requested, options) {
156
161
  }
157
162
  finally {
158
163
  host.entries.delete(handle);
159
- host.options.onSizeChange?.(host.entries.size);
164
+ if (!entry.speculative)
165
+ host.options.onSizeChange?.([...host.entries.values()].filter((item) => !item.speculative).length);
160
166
  if (sessionPath)
161
167
  await releaseWithinGrace(host.teardownHost, handle, sessionPath);
162
168
  }
@@ -38,13 +38,14 @@ export function buildOpeningEntry(input) {
38
38
  openCompletion.catch(() => { });
39
39
  const entry = {
40
40
  state: "opening",
41
+ ...(options?.speculative ? { speculative: true } : {}),
41
42
  scope: new ProviderScope(),
42
43
  profile: storedProfile,
43
44
  ...sessionIdentity(storedProfile),
44
45
  sessionPath,
45
46
  reservationKey: sessionPath,
46
47
  cwd: storedProfile.cwd,
47
- attachments: 1,
48
+ attachments: options?.speculative ? 0 : 1,
48
49
  retainOnDisconnect: options?.retainOnDisconnect === true,
49
50
  lastCommandAt: input.now,
50
51
  lifecycleMutex: Promise.resolve(),
@@ -24,6 +24,8 @@ export type SessionRuntime = AgentSessionRuntime;
24
24
  export type RpcSessionState = "opening" | "open" | "closing" | "quarantined" | "closed";
25
25
  export interface RpcSessionEntry {
26
26
  state: RpcSessionState;
27
+ /** Built speculatively: no public identity, attachment, listing, or disk persistence until claimed. */
28
+ speculative?: boolean;
27
29
  runtime?: SessionRuntime;
28
30
  worker?: SessionWorkerClient;
29
31
  /** The in-process session's ordered acceptance lane, present only while the compact-updates flag is on. */
@@ -116,6 +118,8 @@ export interface RpcSessionRegistryOptions {
116
118
  }
117
119
  /** Host-side lifecycle policy for one `open_session`, distinct from the session's launch profile. */
118
120
  export interface RpcSessionOpenOptions {
121
+ /** Router-owned park construction; never accepted directly from the wire. */
122
+ speculative?: boolean;
119
123
  /** Refuse unless the registry already owns the path; never prepare or construct a writer. */
120
124
  attachOnly?: boolean;
121
125
  /** Keep the session alive when its last client disconnects (`open_session.retain_on_disconnect`). */
@@ -4,6 +4,7 @@ export { frozenProfile, type OpenRpcSession, type RpcSessionEntry, type RpcSessi
4
4
  /** Process-local lifecycle owner for multi-session RPC runtimes. */
5
5
  export declare class RpcSessionRegistry {
6
6
  readonly supportsCompact: (profile?: RpcSessionLaunchProfile) => boolean;
7
+ readonly supportsParkClaim = true;
7
8
  private readonly entries;
8
9
  private readonly reservations;
9
10
  private readonly pendingClaims;
@@ -20,6 +21,8 @@ export declare class RpcSessionRegistry {
20
21
  constructor(options: RpcSessionRegistryOptions);
21
22
  /** Number of live entries, including ones still opening or closing. */
22
23
  get size(): number;
24
+ /** Commit the ledger's exclusive claim after its existing runtime has acquired a persistent file. */
25
+ claimPark(handle: string, retainOnDisconnect: boolean): Promise<OpenRpcSession>;
23
26
  /** Daemon-family identities come from the endpoint's claims and generation registry. */
24
27
  holderPids(observedStarts?: ReadonlyMap<number, number | undefined>): Promise<readonly number[]>;
25
28
  openSession(requested: RpcSessionLaunchProfile, options?: RpcSessionOpenOptions): Promise<OpenRpcSession>;
@@ -16,6 +16,7 @@ export class RpcSessionRegistry {
16
16
  return this.options.agentDir;
17
17
  }
18
18
  constructor(options) {
19
+ this.supportsParkClaim = true;
19
20
  this.entries = new Map();
20
21
  this.reservations = new Set();
21
22
  this.pendingClaims = new Map();
@@ -42,8 +43,10 @@ export class RpcSessionRegistry {
42
43
  closeGraceMs: this.closeGraceMs,
43
44
  get: (handle) => this.entries.get(handle),
44
45
  delete: (handle) => {
46
+ const speculative = this.entries.get(handle)?.speculative;
45
47
  const deleted = this.entries.delete(handle);
46
- this.options.onSizeChange?.(this.entries.size);
48
+ if (!speculative)
49
+ this.options.onSizeChange?.(this.size);
47
50
  return deleted;
48
51
  },
49
52
  releaseReservation: async (key) => {
@@ -66,7 +69,26 @@ export class RpcSessionRegistry {
66
69
  }
67
70
  /** Number of live entries, including ones still opening or closing. */
68
71
  get size() {
69
- return this.entries.size;
72
+ return [...this.entries.values()].filter((entry) => !entry.speculative).length;
73
+ }
74
+ /** Commit the ledger's exclusive claim after its existing runtime has acquired a persistent file. */
75
+ async claimPark(handle, retainOnDisconnect) {
76
+ const entry = this.entries.get(handle);
77
+ if (!entry?.speculative || entry.state !== "open" || !entry.runtime)
78
+ throw new RpcSessionRegistryError("unknown_session");
79
+ await entry.runtime.persistSession();
80
+ // No await between the duplicate decision and publication: an ordinary open may have
81
+ // reserved this identity while persistence was being enabled.
82
+ for (const other of this.entries.values())
83
+ if (!other.speculative && other.durableSessionId === entry.runtime.session.sessionId)
84
+ throw new RpcSessionRegistryError("session_id_in_use");
85
+ entry.speculative = false;
86
+ entry.attachments = 1;
87
+ entry.retainOnDisconnect = retainOnDisconnect;
88
+ entry.lastCommandAt = this.now();
89
+ this.syncRuntimeMetadata();
90
+ this.options.onSizeChange?.(this.size);
91
+ return { sessionId: handle, durableSessionId: entry.runtime.session.sessionId, sessionPath: entry.sessionPath };
70
92
  }
71
93
  /** Daemon-family identities come from the endpoint's claims and generation registry. */
72
94
  holderPids(observedStarts) {
@@ -143,7 +165,9 @@ export class RpcSessionRegistry {
143
165
  list() {
144
166
  this.syncRuntimeMetadata();
145
167
  const now = this.now();
146
- return [...this.entries].map(([sessionId, entry]) => ({
168
+ return [...this.entries]
169
+ .filter(([, entry]) => !entry.speculative)
170
+ .map(([sessionId, entry]) => ({
147
171
  sessionId,
148
172
  durableSessionId: entry.durableSessionId,
149
173
  sessionPath: entry.sessionPath,
@@ -208,6 +208,7 @@ export async function dispose(host) {
208
208
  stopSweep(host);
209
209
  if (host.drainTimer !== undefined)
210
210
  clearInterval(host.drainTimer);
211
+ await host.parks.stop();
211
212
  if (host.admissions)
212
213
  await host.admissions.dispose();
213
214
  await Promise.all([...host.bindings.entries()].map(async ([sessionId, binding]) => {
@@ -6,7 +6,9 @@ import type { SessionEventWriter } from "./session-event-writer.ts";
6
6
  import type { RpcSessionRegistry } from "./session-registry.ts";
7
7
  import { SessionAttachOrder } from "./session-attach-order.ts";
8
8
  import type { RpcHostSessionDefaults, RpcSessionIdlePolicy } from "./session-router-types.ts";
9
+ import { HostParkLedger } from "./host-park-ledger.ts";
9
10
  export declare class RouterContext {
11
+ readonly parks: HostParkLedger;
10
12
  readonly admissions: CompactAdmissions | undefined;
11
13
  readonly bindings: Map<string, RpcSessionBinding>;
12
14
  readonly stopLimitBroadcast: () => void;
@@ -18,7 +20,7 @@ export declare class RouterContext {
18
20
  readonly opensByConnection: Map<string, Set<Promise<void>>>;
19
21
  /** Connections whose socket has already been detached. */
20
22
  readonly releasedConnections: Set<string>;
21
- readonly registry: Pick<RpcSessionRegistry, "openSession" | "peek" | "getForCommand" | "beginClose" | "close" | "closeMarked" | "list" | "size" | "warm" | "closeGraceMs"> & Partial<Pick<RpcSessionRegistry, "supportsCompact" | "holderPids" | "attachSession" | "agentDir">>;
23
+ readonly registry: Pick<RpcSessionRegistry, "openSession" | "peek" | "getForCommand" | "beginClose" | "close" | "closeMarked" | "list" | "size" | "warm" | "closeGraceMs"> & Partial<Pick<RpcSessionRegistry, "supportsCompact" | "supportsParkClaim" | "claimPark" | "holderPids" | "attachSession" | "agentDir">>;
22
24
  readonly writer: SessionEventWriter;
23
25
  readonly defaults: RpcHostSessionDefaults;
24
26
  readonly createBinding: typeof createRpcSessionBinding;
@@ -59,6 +61,6 @@ export declare class RouterContext {
59
61
  readonly mainHeapBytes: () => number;
60
62
  readonly kernels: () => readonly RpcHostKernelMemory[];
61
63
  } | undefined;
62
- constructor(registry: Pick<RpcSessionRegistry, "openSession" | "peek" | "getForCommand" | "beginClose" | "close" | "closeMarked" | "list" | "size" | "warm" | "closeGraceMs"> & Partial<Pick<RpcSessionRegistry, "supportsCompact" | "holderPids" | "attachSession" | "agentDir">>, writer: SessionEventWriter, defaults: RpcHostSessionDefaults, createBinding?: typeof createRpcSessionBinding, connectionOptions?: Parameters<typeof createRpcSessionBinding>[4], idle?: RpcSessionIdlePolicy);
64
+ constructor(registry: Pick<RpcSessionRegistry, "openSession" | "peek" | "getForCommand" | "beginClose" | "close" | "closeMarked" | "list" | "size" | "warm" | "closeGraceMs"> & Partial<Pick<RpcSessionRegistry, "supportsCompact" | "supportsParkClaim" | "claimPark" | "holderPids" | "attachSession" | "agentDir">>, writer: SessionEventWriter, defaults: RpcHostSessionDefaults, createBinding?: typeof createRpcSessionBinding, connectionOptions?: Parameters<typeof createRpcSessionBinding>[4], idle?: RpcSessionIdlePolicy);
63
65
  }
64
66
  //# sourceMappingURL=session-router-context.d.ts.map
@@ -12,8 +12,11 @@ import { routingIdOf } from "./session-router-errors.js";
12
12
  import { sweepIdleSessions } from "./session-router-occupancy.js";
13
13
  import { followRetainedRecord } from "./session-router-residency.js";
14
14
  import { registryStatusSource } from "./session-status-source.js";
15
+ import { HostParkLedger } from "./host-park-ledger.js";
16
+ import { closePark } from "./session-router-park.js";
15
17
  export class RouterContext {
16
18
  constructor(registry, writer, defaults, createBinding = createRpcSessionBinding, connectionOptions = {}, idle) {
19
+ this.parks = new HostParkLedger((opened) => closePark(this, opened));
17
20
  this.admissions = compactUpdatesEnabled()
18
21
  ? new CompactAdmissions(ADMISSION_LIMITS, (socketEpoch, admissionId) => this.writer.routesAdmission(socketEpoch, admissionId))
19
22
  : undefined;
@@ -14,6 +14,7 @@ import { sessionMemory } from "./session-router-observation.js";
14
14
  export function answerProtocolInfo(host, command) {
15
15
  const hostCapabilities = multiSessionHostCapabilities({
16
16
  warm: host.registry.warm !== undefined,
17
+ parkClaim: host.registry.supportsParkClaim === true,
17
18
  toolRuntime: host.defaults.toolRuntime ?? "in-process",
18
19
  negotiated: host.connectionOptions?.capabilities ?? [],
19
20
  mcpPresenters: host.writer.mcpPresenters !== undefined,
@@ -12,6 +12,15 @@ const DRAIN_SWEEP_MS = 50;
12
12
  */
13
13
  export function beginDrain(host) {
14
14
  host.draining = true;
15
+ void host.parks
16
+ .stop()
17
+ .catch((cause) => {
18
+ process.stderr.write(`senpi rpc park close failed during drain: ${String(cause)}\n`);
19
+ })
20
+ .then(() => sweepDrain(host))
21
+ .catch((cause) => {
22
+ process.stderr.write(`senpi rpc drain sweep failed after park close: ${String(cause)}\n`);
23
+ });
15
24
  stopSweep(host);
16
25
  // Armed BEFORE the first pass, so a pass that fails is retried instead of ending the drain
17
26
  // (senpi#2285). Unref'd: a draining host must never be the reason the event loop stays open.
@@ -35,7 +44,7 @@ export function sweepDrain(host) {
35
44
  parkForHandoff(host, sessionId, "session_dir_removed");
36
45
  for (const sessionId of verdicts.park)
37
46
  parkForHandoff(host, sessionId, "handoff_parked");
38
- if (host.registry.size > 0 || host.handoffParks > 0 || host.finalizations.size > 0)
47
+ if (host.registry.size > 0 || host.parks.size > 0 || host.handoffParks > 0 || host.finalizations.size > 0)
39
48
  return;
40
49
  if (host.drainTimer !== undefined) {
41
50
  clearInterval(host.drainTimer);
@@ -0,0 +1,12 @@
1
+ import type { OpenRpcSession, RpcSessionEntry } from "./session-registry.ts";
2
+ import type { RouterContext } from "./session-router-context.ts";
3
+ /** A dropped opener waits until its open or claim has either transferred or released ownership. */
4
+ export declare function holdOpenConnection(host: RouterContext, owner: string): () => void;
5
+ /** One ownership transition for both a freshly built open and a claimed speculative open. */
6
+ export declare function ownOpen(host: RouterContext, opened: OpenRpcSession, owner: string | undefined, compact?: boolean): void;
7
+ export declare function publishOpen(host: RouterContext, opened: OpenRpcSession, entry: RpcSessionEntry): void;
8
+ export declare function replyOpen(host: RouterContext, opened: OpenRpcSession, entry: RpcSessionEntry, command: {
9
+ readonly type: "open_session" | "claim_session";
10
+ readonly id?: string;
11
+ }, reply?: boolean): void;
12
+ //# sourceMappingURL=session-router-open-result.d.ts.map
@@ -0,0 +1,63 @@
1
+ import { openReplyPermissionMode } from "../../core/permission-mode.js";
2
+ import { buildRpcSessionState } from "./connection-handler.js";
3
+ /** A dropped opener waits until its open or claim has either transferred or released ownership. */
4
+ export function holdOpenConnection(host, owner) {
5
+ const barrier = Promise.withResolvers();
6
+ const opens = host.opensByConnection.get(owner) ?? new Set();
7
+ opens.add(barrier.promise);
8
+ host.opensByConnection.set(owner, opens);
9
+ return () => {
10
+ barrier.resolve();
11
+ opens.delete(barrier.promise);
12
+ if (opens.size === 0)
13
+ host.opensByConnection.delete(owner);
14
+ };
15
+ }
16
+ /** One ownership transition for both a freshly built open and a claimed speculative open. */
17
+ export function ownOpen(host, opened, owner, compact = false) {
18
+ if (owner === undefined)
19
+ return;
20
+ if (!host.writer.hasRegisteredConnectionCapabilities(owner))
21
+ host.writer.setConnectionCapabilities(owner, host.pendingCapabilities.get(owner) ?? host.connectionOptions?.capabilities ?? []);
22
+ host.writer.attachConnectionToSession(owner, opened.sessionId, compact);
23
+ const owned = host.sessionsByConnection.get(owner) ?? new Map();
24
+ owned.set(opened.sessionId, (owned.get(opened.sessionId) ?? 0) + 1);
25
+ host.sessionsByConnection.set(owner, owned);
26
+ host.attachOrder.attached(opened.sessionId, owner);
27
+ }
28
+ export function publishOpen(host, opened, entry) {
29
+ if (opened.attached)
30
+ host.writer.status.publish(opened.sessionId, "attach", "client");
31
+ else {
32
+ host.writer.publishSessionOpened({
33
+ type: "session_opened",
34
+ sessionId: opened.sessionId,
35
+ durableSessionId: opened.durableSessionId,
36
+ ...(opened.sessionPath !== undefined ? { sessionPath: opened.sessionPath } : {}),
37
+ kind: entry.kind,
38
+ cwd: entry.cwd,
39
+ context: entry.context,
40
+ });
41
+ host.writer.status.publish(opened.sessionId, "open", "client");
42
+ }
43
+ }
44
+ export function replyOpen(host, opened, entry, command, reply = true) {
45
+ const state = entry.worker?.snapshot?.state ?? (entry.runtime ? buildRpcSessionState(entry.runtime.session) : undefined);
46
+ if (!state)
47
+ throw new Error("Session state was not created");
48
+ if (reply)
49
+ host.writer.enqueue(opened.sessionId, {
50
+ id: command.id,
51
+ type: "response",
52
+ command: command.type,
53
+ success: true,
54
+ data: {
55
+ sessionId: opened.sessionId,
56
+ state,
57
+ ...openReplyPermissionMode(state),
58
+ ...(opened.attached ? { attached: true } : {}),
59
+ ...(host.defaults.sessionPresence ? { presence: host.defaults.sessionPresence.view(state.sessionId) } : {}),
60
+ },
61
+ });
62
+ }
63
+ //# sourceMappingURL=session-router-open-result.js.map
@@ -1,6 +1,7 @@
1
1
  import { type OpenAcceptance } from "./compact-open-admission.ts";
2
2
  import type { HandoverParkedEntry } from "./host-handover-parked.ts";
3
3
  import type { RpcCommand, RpcResponse } from "./rpc-types.ts";
4
+ import type { OpenRpcSession } from "./session-registry.ts";
4
5
  import type { RouterContext } from "./session-router-context.ts";
5
6
  export declare function openWithBarrier(host: RouterContext, command: Extract<RpcCommand, {
6
7
  type: "open_session";
@@ -19,5 +20,7 @@ export declare function open(host: RouterContext, command: Extract<RpcCommand, {
19
20
  readonly signal?: AbortSignal;
20
21
  readonly defaults?: boolean;
21
22
  readonly contextOverlay?: HandoverParkedEntry["contextOverlay"];
23
+ /** An invisible speculative open, published only when its ledger hands it to a caller. */
24
+ readonly park?: (opened: OpenRpcSession) => void;
22
25
  }): Promise<RpcResponse | undefined>;
23
26
  //# sourceMappingURL=session-router-open.d.ts.map
@@ -1,7 +1,5 @@
1
- import { buildRpcSessionState } from "./connection-handler.js";
2
1
  import { compactUpdatesEnabled } from "../../core/engine-socket-flag.js";
3
2
  import { startOpenTimer } from "../../core/open-timing.js";
4
- import { openReplyPermissionMode } from "../../core/permission-mode.js";
5
3
  import { compactOpen } from "./compact-open-admission.js";
6
4
  import { CompactAdmissionError } from "./compact-admission-error.js";
7
5
  import { runAdmissionProducer } from "./compact-admission-producer.js";
@@ -12,6 +10,8 @@ import { close } from "./session-router-close.js";
12
10
  import { code, detail, error } from "./session-router-errors.js";
13
11
  import { sweepDrain } from "./session-router-occupancy.js";
14
12
  import { invalidSpawnedBy, validatedOpenProfile } from "./session-router-open-profile.js";
13
+ import { holdOpenConnection, ownOpen, publishOpen, replyOpen } from "./session-router-open-result.js";
14
+ import { closePark } from "./session-router-park.js";
15
15
  import { relocationConnection } from "./session-router-relocation.js";
16
16
  import { syncRetainedRecord } from "./session-router-residency.js";
17
17
  import { liveToolMediaScope, toolMediaPersister } from "./tool-media-store.js";
@@ -24,18 +24,12 @@ function openPhysicalWithBarrier(host, command, accept, signal) {
24
24
  const owner = host.writer.currentConnection();
25
25
  if (owner === undefined)
26
26
  return open(host, command);
27
- let resolveBarrier;
28
- const barrier = new Promise((resolve) => {
29
- resolveBarrier = resolve;
30
- });
31
- const opens = host.opensByConnection.get(owner) ?? new Set();
32
27
  // Every open already accepted anywhere on this host, not just on this connection: the
33
28
  // in-process runtime serves them on ONE loop, so the wait this caller faces is the total.
34
29
  let inFlight = 0;
35
30
  for (const pending of host.opensByConnection.values())
36
31
  inFlight += pending.size;
37
- opens.add(barrier);
38
- host.opensByConnection.set(owner, opens);
32
+ const releaseBarrier = holdOpenConnection(host, owner);
39
33
  // Before the open reaches the loop: a client that later hits its deadline can then say
40
34
  // where it was queued instead of reporting a bare timeout (senpi#1844).
41
35
  if (command.id !== undefined)
@@ -49,12 +43,7 @@ function openPhysicalWithBarrier(host, command, accept, signal) {
49
43
  // A late open's physical session is released by its admission (the accept/rollback path, 4272-admission-
50
44
  // abandonment), so the late response itself owns nothing to release here.
51
45
  const opened = signal === undefined ? start() : runAdmissionProducer(signal, start, async () => { });
52
- return opened.finally(() => {
53
- resolveBarrier();
54
- opens.delete(barrier);
55
- if (opens.size === 0)
56
- host.opensByConnection.delete(owner);
57
- });
46
+ return opened.finally(releaseBarrier);
58
47
  }
59
48
  /**
60
49
  * The successor's reopen of a handoff-parked session: the same open as an `open_session`, with no
@@ -87,6 +76,7 @@ export async function open(host, command, owner, options) {
87
76
  {
88
77
  retainOnDisconnect: command.retain_on_disconnect === true,
89
78
  attachOnly: command.expectedHostInstance !== undefined,
79
+ ...(options?.park ? { speculative: true } : {}),
90
80
  });
91
81
  timer.mark("registry_open");
92
82
  options?.signal?.throwIfAborted();
@@ -119,22 +109,11 @@ export async function open(host, command, owner, options) {
119
109
  host.writer.registerAcceptance(openedSession.sessionId, lane);
120
110
  if (options?.accept)
121
111
  await options.accept(openedSession);
122
- if (owner !== undefined) {
123
- if (!host.writer.hasRegisteredConnectionCapabilities(owner))
124
- host.writer.setConnectionCapabilities(owner, host.pendingCapabilities.get(owner) ?? host.connectionOptions?.capabilities ?? []);
125
- host.writer.attachConnectionToSession(owner, openedSession.sessionId, options?.accept !== undefined);
126
- }
127
- // A client that dies without close_session (terminal closed, SIGKILL, dropped
128
- // SSH) still holds this handle's attachment and its path reservation. Remember
129
- // which connection owns it so releaseConnection() can close exactly that.
130
- if (owner !== undefined) {
131
- const owned = host.sessionsByConnection.get(owner) ?? new Map();
132
- owned.set(openedSession.sessionId, (owned.get(openedSession.sessionId) ?? 0) + 1);
133
- host.sessionsByConnection.set(owner, owned);
134
- host.attachOrder.attached(openedSession.sessionId, owner);
135
- }
112
+ ownOpen(host, openedSession, owner, options?.accept !== undefined);
136
113
  if (!host.bindings.has(openedSession.sessionId)) {
137
- host.bindings.set(openedSession.sessionId, await host.createBinding(openedSession.sessionId, entry, host.writer, () => void close(host, { type: "close_session", sessionId: openedSession.sessionId }), {
114
+ host.bindings.set(openedSession.sessionId, await host.createBinding(openedSession.sessionId, entry, host.writer, () => void (entry.speculative
115
+ ? closePark(host, openedSession)
116
+ : close(host, { type: "close_session", sessionId: openedSession.sessionId })), {
138
117
  ...host.connectionOptions,
139
118
  capabilities: owner !== undefined
140
119
  ? (host.pendingCapabilities.get(owner) ?? host.connectionOptions?.capabilities ?? [])
@@ -181,20 +160,11 @@ export async function open(host, command, owner, options) {
181
160
  });
182
161
  }
183
162
  options?.signal?.throwIfAborted();
184
- if (opened.attached)
185
- host.writer.status.publish(openedSession.sessionId, "attach", "client");
186
- else {
187
- host.writer.publishSessionOpened({
188
- type: "session_opened",
189
- sessionId: openedSession.sessionId,
190
- durableSessionId: openedSession.durableSessionId,
191
- ...(openedSession.sessionPath !== undefined ? { sessionPath: openedSession.sessionPath } : {}),
192
- kind: entry.kind,
193
- cwd: entry.cwd,
194
- context: entry.context,
195
- });
196
- host.writer.status.publish(openedSession.sessionId, "open", "client");
163
+ if (options?.park) {
164
+ options.park(openedSession);
165
+ return undefined;
197
166
  }
167
+ publishOpen(host, openedSession, entry);
198
168
  if (!options?.accept && !host.draining && owner !== undefined && host.releasedConnections.has(owner)) {
199
169
  releaseOwnerAttachment(host, owner, openedSession.sessionId);
200
170
  await releaseOwnedSession(host, openedSession.sessionId);
@@ -205,26 +175,8 @@ export async function open(host, command, owner, options) {
205
175
  if (options?.accept) {
206
176
  return undefined;
207
177
  }
208
- const replyState = entry.worker?.snapshot?.state ?? (entry.runtime ? buildRpcSessionState(entry.runtime.session) : undefined);
209
- if (!replyState)
210
- throw new Error("Session state was not created");
211
178
  timer.mark("reply");
212
- if (options?.reply !== false)
213
- host.writer.enqueue(opened.sessionId, {
214
- id: command.id,
215
- type: "response",
216
- command: "open_session",
217
- success: true,
218
- data: {
219
- sessionId: opened.sessionId,
220
- state: replyState,
221
- ...openReplyPermissionMode(replyState),
222
- ...(opened.attached ? { attached: true } : {}),
223
- ...(host.defaults.sessionPresence
224
- ? { presence: host.defaults.sessionPresence.view(replyState.sessionId) }
225
- : {}),
226
- },
227
- });
179
+ replyOpen(host, opened, entry, command, options?.reply !== false);
228
180
  return undefined;
229
181
  }
230
182
  catch (cause) {
@@ -233,7 +185,7 @@ export async function open(host, command, owner, options) {
233
185
  // rollback, and only while the registry still holds the live entry, so only that
234
186
  // rollback owes its subscribers the terminal record; an attach rollback, or one whose
235
187
  // entry is already gone, never announced the session in the first place (#3198 review, L1).
236
- if (!opened.attached && !options?.signal?.aborted) {
188
+ if (!options?.park && !opened.attached && !options?.signal?.aborted) {
237
189
  const entry = host.registry.peek(opened.sessionId);
238
190
  if (entry?.runtime !== undefined || entry?.worker !== undefined)
239
191
  host.writer.sealRolledBackOpen(opened.sessionId, entry.sessionPath);
@@ -0,0 +1,15 @@
1
+ import type { RpcCommand, RpcResponse } from "./rpc-types.ts";
2
+ import type { OpenRpcSession } from "./session-registry.ts";
3
+ import type { RouterContext } from "./session-router-context.ts";
4
+ type ParkCommand = Extract<RpcCommand, {
5
+ type: "park_session";
6
+ }>;
7
+ type ClaimCommand = Extract<RpcCommand, {
8
+ type: "claim_session";
9
+ }>;
10
+ /** Quiet teardown: speculation never owes a public terminal record or a handover park. */
11
+ export declare function closePark(host: RouterContext, opened: OpenRpcSession): Promise<void>;
12
+ export declare function parkSession(host: RouterContext, command: ParkCommand): Promise<RpcResponse>;
13
+ export declare function claimSession(host: RouterContext, command: ClaimCommand): Promise<RpcResponse | undefined>;
14
+ export {};
15
+ //# sourceMappingURL=session-router-park.d.ts.map