@code-yeongyu/senpi 2026.10.25-2 → 2026.10.25-4

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 (226) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/bundle/chunks/{args-JXULI4EH.js → args-B444OQBK.js} +1 -1
  3. package/dist/bundle/chunks/{auth-4NUNUSTF.js → auth-C77UQA55.js} +1 -1
  4. package/dist/bundle/chunks/{chunk-XBUSVKWC.js → chunk-2RB5EL4X.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-KUE4AQN6.js → chunk-3QCYBOKN.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-2EFVQWXZ.js → chunk-4MOZXOJX.js} +5 -5
  7. package/dist/bundle/chunks/{chunk-MXZPAUFB.js → chunk-5JJDR6XI.js} +1 -1
  8. package/dist/bundle/chunks/chunk-6VWIMHMH.js +2 -0
  9. package/dist/bundle/chunks/{chunk-Y5OTHMXO.js → chunk-7CNOCJCN.js} +1 -1
  10. package/dist/bundle/chunks/chunk-AX72VNRS.js +27 -0
  11. package/dist/bundle/chunks/{chunk-VNN2V74Y.js → chunk-BZ7726QO.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-J2U5VDG7.js → chunk-C2MQ4QXN.js} +30 -30
  13. package/dist/bundle/chunks/{chunk-JRXKLIEQ.js → chunk-DODH3N6U.js} +5 -5
  14. package/dist/bundle/chunks/{chunk-EHQXLHRB.js → chunk-E6D4RIT7.js} +2 -2
  15. package/dist/bundle/chunks/chunk-J37GRED2.js +5 -0
  16. package/dist/bundle/chunks/chunk-JMOUU75H.js +2 -0
  17. package/dist/bundle/chunks/chunk-JPGT64RV.js +2 -0
  18. package/dist/bundle/chunks/chunk-KKD4CYCI.js +3 -0
  19. package/dist/bundle/chunks/{chunk-YN7ELQ43.js → chunk-KNVNKDER.js} +1 -1
  20. package/dist/bundle/chunks/chunk-KXB5WIS5.js +4 -0
  21. package/dist/bundle/chunks/chunk-L73UYAH5.js +3 -0
  22. package/dist/bundle/chunks/{chunk-G2UETS2R.js → chunk-LOIF6PHI.js} +4 -4
  23. package/dist/bundle/chunks/chunk-LTU6UFEX.js +2 -0
  24. package/dist/bundle/chunks/chunk-N7IN46Q5.js +27 -0
  25. package/dist/bundle/chunks/{chunk-LYGAV6KU.js → chunk-PT6JJUN2.js} +1 -1
  26. package/dist/bundle/chunks/{chunk-WFXUCVIR.js → chunk-SBGIXMX3.js} +2 -5
  27. package/dist/bundle/chunks/{chunk-W2BKZBYJ.js → chunk-STMXIBXF.js} +2 -2
  28. package/dist/bundle/chunks/chunk-TBU6VYHN.js +13 -0
  29. package/dist/bundle/chunks/{chunk-RXKNOEQQ.js → chunk-V2UYXTRR.js} +1 -1
  30. package/dist/bundle/chunks/{chunk-U67ML6CQ.js → chunk-WD6WBYC5.js} +1 -1
  31. package/dist/bundle/chunks/chunk-WJOT3DFL.js +2 -0
  32. package/dist/bundle/chunks/chunk-XMJKZK5H.js +2 -0
  33. package/dist/bundle/chunks/chunk-YI7SPFDN.js +5 -0
  34. package/dist/bundle/chunks/cli-main-YYH3IBC6.js +3 -0
  35. package/dist/bundle/chunks/{client-KWTLTKVV.js → client-WTYEICLH.js} +3 -3
  36. package/dist/bundle/chunks/dispatch-MXG5YWLV.js +9 -0
  37. package/dist/bundle/chunks/{help-fast-path-ZHZGXAUG.js → help-fast-path-3Z26DQSV.js} +1 -1
  38. package/dist/bundle/chunks/host-command-TDLLHXVI.js +2 -0
  39. package/dist/bundle/chunks/host-lifecycle-3VHHIGDL.js +3 -0
  40. package/dist/bundle/chunks/host-lifecycle.js +8 -6
  41. package/dist/bundle/chunks/host-session-command-56XF6ACS.js +20 -0
  42. package/dist/bundle/chunks/{host-socket-request-AS2OCRLP.js → host-socket-request-QL2WEZTN.js} +1 -1
  43. package/dist/bundle/chunks/interactive-mode-OEPULLZK.js +2 -0
  44. package/dist/bundle/chunks/mcp-presenter-bootstrap-XUSCQVRC.js +2 -0
  45. package/dist/bundle/chunks/{multi-session-host-VTTY5MFT.js → multi-session-host-Z2CW4SDM.js} +12 -9
  46. package/dist/bundle/chunks/{package-manager-cli-DZFOC3MF.js → package-manager-cli-N55U6PEO.js} +1 -1
  47. package/dist/bundle/chunks/rotation-stream-CBGCBYHV.js +2 -0
  48. package/dist/bundle/chunks/rpc-mode-ETXQWEB2.js +2 -0
  49. package/dist/bundle/chunks/{rpc-monitor-source-B5NQ4ZVH.js → rpc-monitor-source-AE3C6EQV.js} +1 -1
  50. package/dist/bundle/chunks/{schedule-command-SLISTG4V.js → schedule-command-VS7CZC2J.js} +1 -1
  51. package/dist/bundle/chunks/session-control-endpoint-4LEIDIVI.js +3 -0
  52. package/dist/bundle/chunks/session-picker-E4KUJDLO.js +2 -0
  53. package/dist/bundle/chunks/session-worker.js +292 -278
  54. package/dist/bundle/chunks/{stdio-IEC635SY.js → stdio-YXDKKKQZ.js} +1 -1
  55. package/dist/bundle/chunks/{streamableHttp-KJI5KE4K.js → streamableHttp-DKIBUWVY.js} +1 -1
  56. package/dist/bundle/chunks/{types-LLNV23LW.js → types-BJUEQ4II.js} +1 -1
  57. package/dist/bundle/chunks/zod-compat-RJDXCU7S.js +2 -0
  58. package/dist/bundle/cli.js +2 -2
  59. package/dist/bundle/host-supervisor.js +8 -6
  60. package/dist/bundle/index.js +1 -1
  61. package/dist/bundle/rpc-entry.js +1 -1
  62. package/dist/bundle/runtime-manifest.json +1 -1
  63. package/dist/cli/args.d.ts +2 -0
  64. package/dist/cli/args.js +9 -0
  65. package/dist/cli/host-command-args.d.ts +1 -0
  66. package/dist/cli/host-command-args.js +7 -1
  67. package/dist/cli/host-command.js +3 -1
  68. package/dist/core/agent-session.js +22 -7
  69. package/dist/core/extensions/builtin/goal/index.js +1 -0
  70. package/dist/core/extensions/builtin/goal/lifecycle-facts.d.ts +3 -0
  71. package/dist/core/extensions/builtin/mcp/catalog.d.ts +3 -1
  72. package/dist/core/extensions/builtin/mcp/catalog.js +2 -0
  73. package/dist/core/extensions/builtin/mcp/connection.d.ts +1 -0
  74. package/dist/core/extensions/builtin/mcp/connection.js +46 -1
  75. package/dist/core/extensions/builtin/mcp/elicitation.d.ts +6 -1
  76. package/dist/core/extensions/builtin/mcp/elicitation.js +54 -2
  77. package/dist/core/extensions/builtin/mcp/expose/call.js +6 -2
  78. package/dist/core/extensions/builtin/mcp/expose/session.d.ts +4 -1
  79. package/dist/core/extensions/builtin/mcp/expose/session.js +7 -0
  80. package/dist/core/extensions/builtin/mcp/health.js +5 -0
  81. package/dist/core/extensions/builtin/mcp/host-registry.js +7 -1
  82. package/dist/core/extensions/builtin/mcp/index.js +2 -0
  83. package/dist/core/extensions/builtin/mcp/modern/client.d.ts +5 -0
  84. package/dist/core/extensions/builtin/mcp/modern/client.js +15 -0
  85. package/dist/core/extensions/builtin/mcp/modern/consent-queue.d.ts +2 -0
  86. package/dist/core/extensions/builtin/mcp/modern/consent-queue.js +59 -0
  87. package/dist/core/extensions/builtin/mcp/modern/diagnostics.d.ts +4 -0
  88. package/dist/core/extensions/builtin/mcp/modern/diagnostics.js +39 -0
  89. package/dist/core/extensions/builtin/mcp/modern/dispatch.js +21 -11
  90. package/dist/core/extensions/builtin/mcp/modern/elicitation.d.ts +14 -0
  91. package/dist/core/extensions/builtin/mcp/modern/elicitation.js +145 -0
  92. package/dist/core/extensions/builtin/mcp/modern/errors.d.ts +21 -0
  93. package/dist/core/extensions/builtin/mcp/modern/errors.js +23 -0
  94. package/dist/core/extensions/builtin/mcp/modern/form-schema.d.ts +20 -0
  95. package/dist/core/extensions/builtin/mcp/modern/form-schema.js +153 -0
  96. package/dist/core/extensions/builtin/mcp/modern/http-fetch.d.ts +3 -1
  97. package/dist/core/extensions/builtin/mcp/modern/http-fetch.js +8 -2
  98. package/dist/core/extensions/builtin/mcp/modern/inputs.d.ts +11 -0
  99. package/dist/core/extensions/builtin/mcp/modern/inputs.js +65 -0
  100. package/dist/core/extensions/builtin/mcp/modern/parameter-headers.d.ts +11 -0
  101. package/dist/core/extensions/builtin/mcp/modern/parameter-headers.js +109 -0
  102. package/dist/core/extensions/builtin/mcp/modern/request.d.ts +20 -0
  103. package/dist/core/extensions/builtin/mcp/modern/request.js +292 -0
  104. package/dist/core/extensions/builtin/mcp/modern/stdio-probe.d.ts +1 -1
  105. package/dist/core/extensions/builtin/mcp/modern/stdio-probe.js +54 -6
  106. package/dist/core/extensions/builtin/mcp/modern/transport.js +80 -14
  107. package/dist/core/extensions/builtin/mcp/modern/wire.js +11 -7
  108. package/dist/core/extensions/builtin/mcp/prompts.d.ts +5 -1
  109. package/dist/core/extensions/builtin/mcp/prompts.js +6 -1
  110. package/dist/core/extensions/builtin/mcp/request-origin.d.ts +47 -0
  111. package/dist/core/extensions/builtin/mcp/request-origin.js +59 -0
  112. package/dist/core/extensions/builtin/mcp/resources.d.ts +4 -1
  113. package/dist/core/extensions/builtin/mcp/resources.js +19 -4
  114. package/dist/core/extensions/builtin/mcp/sdk.lazy.d.ts +3 -1
  115. package/dist/core/extensions/builtin/mcp/sdk.lazy.js +3 -1
  116. package/dist/core/extensions/builtin/mcp/service-register.d.ts +4 -1
  117. package/dist/core/extensions/builtin/mcp/service-register.js +3 -0
  118. package/dist/core/extensions/builtin/mcp/service-types.d.ts +2 -1
  119. package/dist/core/extensions/builtin/mcp/service.d.ts +1 -0
  120. package/dist/core/extensions/builtin/mcp/service.js +79 -4
  121. package/dist/core/extensions/builtin/mcp/shared-connection.js +36 -2
  122. package/dist/core/extensions/builtin/mcp/shared-lease.d.ts +1 -0
  123. package/dist/core/extensions/builtin/mcp/shared-lease.js +9 -0
  124. package/dist/core/extensions/builtin/mcp/transport-sdk.d.ts +3 -2
  125. package/dist/core/extensions/builtin/mcp/transport-sdk.js +56 -25
  126. package/dist/core/extensions/builtin/mcp/wrap.d.ts +7 -0
  127. package/dist/core/extensions/builtin/mcp/wrap.js +6 -0
  128. package/dist/core/extensions/runner.js +3 -2
  129. package/dist/core/extensions/types.d.ts +2 -0
  130. package/dist/core/lifecycle-ledger.d.ts +1 -0
  131. package/dist/core/lifecycle-ledger.js +7 -2
  132. package/dist/core/lifecycle-record.js +3 -0
  133. package/dist/core/lifecycle-session-binding.d.ts +11 -1
  134. package/dist/core/lifecycle-session-binding.js +29 -2
  135. package/dist/core/lifecycle-stop.d.ts +41 -0
  136. package/dist/core/lifecycle-stop.js +92 -0
  137. package/dist/core/mcp-form-types.d.ts +89 -0
  138. package/dist/core/mcp-form-types.js +23 -0
  139. package/dist/core/mcp-mrtr-presentations.d.ts +15 -0
  140. package/dist/core/mcp-mrtr-presentations.js +17 -0
  141. package/dist/core/session-control-actions.js +0 -2
  142. package/dist/core/session-lifecycle-types.d.ts +15 -0
  143. package/dist/core/session-lifecycle.d.ts +17 -1
  144. package/dist/core/session-lifecycle.js +40 -2
  145. package/dist/main.js +5 -1
  146. package/dist/modes/interactive/components/extension-selector.d.ts +1 -0
  147. package/dist/modes/interactive/components/extension-selector.js +4 -0
  148. package/dist/modes/interactive/interactive-mode.d.ts +4 -0
  149. package/dist/modes/interactive/interactive-mode.js +34 -8
  150. package/dist/modes/interactive/mcp-local-presenter.d.ts +5 -0
  151. package/dist/modes/interactive/mcp-local-presenter.js +109 -0
  152. package/dist/modes/interactive/session-control-attach.d.ts +20 -0
  153. package/dist/modes/interactive/session-control-attach.js +13 -0
  154. package/dist/modes/interactive/session-control-commands.js +16 -6
  155. package/dist/modes/interactive/session-control-registry.d.ts +2 -1
  156. package/dist/modes/interactive/session-control-registry.js +3 -2
  157. package/dist/modes/interactive/session-control-rpc-commands.d.ts +4 -2
  158. package/dist/modes/interactive/session-control-rpc-commands.js +114 -4
  159. package/dist/modes/interactive/session-control-server.js +9 -3
  160. package/dist/modes/rpc/connection-handler.d.ts +1 -0
  161. package/dist/modes/rpc/connection-handler.js +13 -41
  162. package/dist/modes/rpc/custom-capability.d.ts +6 -0
  163. package/dist/modes/rpc/custom-capability.js +6 -0
  164. package/dist/modes/rpc/get-entries-command.d.ts +13 -0
  165. package/dist/modes/rpc/get-entries-command.js +61 -0
  166. package/dist/modes/rpc/get-entries-page.d.ts +22 -0
  167. package/dist/modes/rpc/get-entries-page.js +41 -2
  168. package/dist/modes/rpc/host-capabilities.d.ts +1 -0
  169. package/dist/modes/rpc/host-capabilities.js +6 -2
  170. package/dist/modes/rpc/host-ensure-start.js +13 -2
  171. package/dist/modes/rpc/host-ensure-types.d.ts +2 -0
  172. package/dist/modes/rpc/host-ensure.js +1 -0
  173. package/dist/modes/rpc/host-handoff.d.ts +2 -0
  174. package/dist/modes/rpc/host-handover-wire.d.ts +2 -0
  175. package/dist/modes/rpc/host-handover-wire.js +1 -0
  176. package/dist/modes/rpc/host-lifecycle-launch.d.ts +1 -0
  177. package/dist/modes/rpc/host-lifecycle-launch.js +43 -2
  178. package/dist/modes/rpc/host-observe-request.d.ts +2 -1
  179. package/dist/modes/rpc/host-observe-request.js +4 -1
  180. package/dist/modes/rpc/host-runner.d.ts +2 -0
  181. package/dist/modes/rpc/host-runner.js +8 -6
  182. package/dist/modes/rpc/host-successor.js +11 -1
  183. package/dist/modes/rpc/mcp-form-dialog.d.ts +45 -0
  184. package/dist/modes/rpc/mcp-form-dialog.js +159 -0
  185. package/dist/modes/rpc/mcp-form-worker.d.ts +14 -0
  186. package/dist/modes/rpc/mcp-form-worker.js +167 -0
  187. package/dist/modes/rpc/mcp-presenter-authority.d.ts +53 -0
  188. package/dist/modes/rpc/mcp-presenter-authority.js +309 -0
  189. package/dist/modes/rpc/mcp-presenter-bootstrap.d.ts +8 -0
  190. package/dist/modes/rpc/mcp-presenter-bootstrap.js +111 -0
  191. package/dist/modes/rpc/mcp-presenter-control.d.ts +24 -0
  192. package/dist/modes/rpc/mcp-presenter-control.js +330 -0
  193. package/dist/modes/rpc/multi-session-host.d.ts +3 -0
  194. package/dist/modes/rpc/multi-session-host.js +36 -1
  195. package/dist/modes/rpc/rpc-read-response-types.d.ts +2 -0
  196. package/dist/modes/rpc/rpc-session-command-types.d.ts +8 -1
  197. package/dist/modes/rpc/session-binding.js +6 -1
  198. package/dist/modes/rpc/session-event-writer.d.ts +14 -0
  199. package/dist/modes/rpc/session-event-writer.js +128 -1
  200. package/dist/modes/rpc/session-router-routing.js +1 -0
  201. package/dist/modes/rpc/session-worker-client.d.ts +3 -0
  202. package/dist/modes/rpc/session-worker-client.js +89 -0
  203. package/dist/modes/rpc/session-worker-protocol.d.ts +41 -0
  204. package/dist/modes/rpc/session-worker.js +9 -0
  205. package/docs/extensions.md +5 -0
  206. package/docs/mcp.md +20 -3
  207. package/docs/rpc.md +151 -8
  208. package/package.json +6 -6
  209. package/dist/bundle/chunks/chunk-3LNSLJ7N.js +0 -2
  210. package/dist/bundle/chunks/chunk-B6TPQDBW.js +0 -5
  211. package/dist/bundle/chunks/chunk-CDRFFHLL.js +0 -4
  212. package/dist/bundle/chunks/chunk-HPCS4HM4.js +0 -27
  213. package/dist/bundle/chunks/chunk-KJZAY5YH.js +0 -27
  214. package/dist/bundle/chunks/chunk-MSQCTH6V.js +0 -2
  215. package/dist/bundle/chunks/chunk-OI4NMCKP.js +0 -3
  216. package/dist/bundle/chunks/chunk-SCVRKIFK.js +0 -2
  217. package/dist/bundle/chunks/cli-main-SWKFMTZQ.js +0 -3
  218. package/dist/bundle/chunks/dispatch-DEP4PEDZ.js +0 -10
  219. package/dist/bundle/chunks/host-command-BC6UWP33.js +0 -2
  220. package/dist/bundle/chunks/host-lifecycle-TN5NEGNF.js +0 -3
  221. package/dist/bundle/chunks/host-session-command-MGVBLBRV.js +0 -20
  222. package/dist/bundle/chunks/interactive-mode-SCSROHD5.js +0 -2
  223. package/dist/bundle/chunks/rotation-stream-U7YQMC7H.js +0 -2
  224. package/dist/bundle/chunks/rpc-mode-TOXYD4XA.js +0 -2
  225. package/dist/bundle/chunks/session-control-endpoint-HFFMBALG.js +0 -3
  226. package/dist/bundle/chunks/session-picker-465FVXQG.js +0 -2
package/docs/rpc.md CHANGED
@@ -159,6 +159,17 @@ The shared `SessionLifecycle` type is exported by `@code-yeongyu/senpi`:
159
159
  with reason `provider_error`, `tool_error`, `task_error`, `lost`, or `interrupted`.
160
160
  - A standing agent self-settle intent adds `settleDecision: { decisionId }` to the
161
161
  persisted `done` seal, independently of the completion label.
162
+ - Optional `lastStop: { cause, runId, at, epoch, deadline?, guard?, todo?, abortSource?, persisted? }`
163
+ says why the run `runId` stopped. `cause` is `deadline`, `goal_guard`, `todo_guard`, `aborted`,
164
+ or `no_wake`; `deadline` is `inactivity` or `max_runtime`, `guard` is the goal guard reason as
165
+ recorded, `todo` is `cap`, and `abortSource` is `user`, `system`, or `provider`. `persisted: false`
166
+ appears only when the deadline-stop entry could not be written and is independent of the
167
+ snapshot's `persisted`. `lastStop` describes the last run's historical stop, so a `no_wake`
168
+ stop remains even if a wake source later appears. It is cleared when the next run starts
169
+ and never names a later run. A fact refresh with no bound turn end never sets `lastStop`;
170
+ between runs, a refresh while the last end is still bound may set or update that run's stop.
171
+ Goal guards are captured during their bound end's own processing, never inherited from
172
+ another run. Provider errors and policy rejections carry no `lastStop`, even if a deadline fired.
162
173
 
163
174
  Clients must not notify or settle while `restoring` is true or `persisted` is false.
164
175
  Use `completionId` to deduplicate root completion/failure, not `agent_settled` or
@@ -1946,13 +1957,17 @@ Environment overrides beat the file, and invalid values fall through to the next
1946
1957
 
1947
1958
  The host exits only after the window elapses with NO attached client connections and NO active turns — continuously.
1948
1959
  Any attached connection or agent turn resets the window, so a busy host never exits. A connection attaches with its
1949
- first request line, unless that line is an OBSERVING read: `get_protocol_info` or `list_sessions` carrying
1950
- `"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
1960
+ first request line, unless that line is an OBSERVING read: a side-effect-free read carrying `"observe": true` -
1961
+ `get_protocol_info`, `list_sessions`, `subscribe_sessions`, `unsubscribe_sessions`, `get_effect_status`,
1962
+ `get_pending_inputs`, `get_subscription_limits`, and the session reads `get_state` and `get_entries` (capability
1963
+ `observe_reads`) - 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
1951
1964
  poller (`senpi host status [--all]`, which marks both of its reads, a doctor loop, a runtime panel) can look at every
1952
1965
  endpoint as often as it likes without keeping any of them alive; the first request that is anything else attaches the
1953
1966
  connection from then on, and `observe` on any other command is ignored. While a connection has not sent its first
1954
1967
  line yet the host does not exit, and the window keeps running. Hosts that predate the field ignore it, so a marked read
1955
- works against every generation.
1968
+ works against every generation. A watcher can therefore read a session's state and history (`get_state`,
1969
+ `get_entries` with `observe: true`) as often as it likes: the host's attached count stays at 0 and its idle window
1970
+ keeps running.
1956
1971
 
1957
1972
  A connection from one of the host's OWN sessions is DEPENDENT and never counts either. A host exports its generation
1958
1973
  id (`SENPI_RPC_HOST_INSTANCE_ID`) to the sessions it runs, and `RpcClient` sends that id as `"origin_host_instance"`
@@ -2159,8 +2174,10 @@ one with `pi.session.registerControlEndpoint({ inboxDir, drain, isSessionReferen
2159
2174
  [pi.session](extensions.md#pisession)). OmO's thread component registers one at startup; a plain `senpi` with no
2160
2175
  registrant opens no socket and writes no registry directory.
2161
2176
 
2162
- - **Platform.** POSIX terminals only. On win32 the registration answers
2163
- `{ status: "unsupported", reason: "unsupported_platform" }` and registers nothing. On a multi-session host the
2177
+ - **Platform.** Every platform. On win32 the socket path below is the endpoint's logical address: the secret is
2178
+ written beside it, and the listener binds the named pipe the host transport derives from the path and the secret
2179
+ (`\\.\pipe\senpi-rpc-<32hex>`), the address every client already connects to after reading the secret. It is
2180
+ registered and listed like a POSIX terminal. On a multi-session host the
2164
2181
  same call binds nothing - the host's public socket is the endpoint - see "Waking a host session (`wake`)".
2165
2182
  - **Socket and auth.** The endpoint listens on `<agentDir>/rpc/tui/t-<16hex>.sock` (16 hex of the SHA-256 of a
2166
2183
  fresh instance id). When that path is longer than a Unix socket path allows, it moves to
@@ -2181,13 +2198,16 @@ registrant opens no socket and writes no registry directory.
2181
2198
  - **Commands.** Anything not listed below is answered `unsupported` as data. No command opens another session or
2182
2199
  runs a command; input goes through the terminal's admission (`steer`/`follow_up`/`prompt` below), so the
2183
2200
  user's draft still holds it.
2184
- - `get_protocol_info`: `mode: "tui"`, `endpoint_kind: "tui"`, `capabilities: ["tui_control", "session_status"]`, `generation: 0`, the endpoint's
2201
+ - `get_protocol_info`: `mode: "tui"`, `endpoint_kind: "tui"`, `capabilities: ["tui_control", "session_status",
2202
+ "tui_observer", "observe_reads", "entries_tail"]` (`tui_observer`: the observe-and-drive set below), `generation: 0`, the endpoint's
2185
2203
  `instanceId`, and `commands`: every command this endpoint accepts. A terminal from before the session
2186
2204
  controls below lists no `commands`.
2187
2205
  - `list_sessions`: exactly one row, `kind: "interactive"`, `surface: "tui"`, `attachments: 1`.
2188
2206
  - `get_state`: the RPC session state plus `turn_epoch`, `blocking_question`, `compacting`,
2189
2207
  `editor_has_draft` and `state_version`.
2190
2208
  - `get_messages`.
2209
+ - `get_entries { since?, before?, limit?, tail?, maxBytes? }`: the host's answer, pages and tail pages included
2210
+ (see `get_entries`).
2191
2211
  - `set_session_name { name }`: a blank name is refused.
2192
2212
  - `subscribe { cursor? }`: a feed of `session_control_event { seq, kind, data }` records, where `kind` is
2193
2213
  `state`, `report` (an assistant message's text), `question` (pending question ids) or `completion` (the
@@ -2209,10 +2229,25 @@ registrant opens no socket and writes no registry directory.
2209
2229
  `queued`; `held_draft` (the user is composing) and `turn_conflict` are refusals with a reason and an action.
2210
2230
  Unlike a host's input, the message lands as a remote delivery (a `session_control_delivery` entry the
2211
2231
  terminal renders as "remote message"), not a user message: there are no prompt templates, slash commands or
2212
- skills, no admission receipt, and a repeated `clientMessageId` answers `duplicate: true`. Frames carrying
2232
+ skills. Input sent with a `clientMessageId` is answered with an admission receipt,
2233
+ `admission { clientMessageId, durableSessionId, disposition }`; a retry with the same id and the same input is
2234
+ answered from it, with `replayed: true` (and `duplicate: true`) and the original `disposition`, and delivers
2235
+ nothing; the same id with different input is refused `client_message_id_conflict`. Frames carrying
2213
2236
  `images`, `thinkingLevel`, `expandPromptTemplates`, `unknownCommandAsText`, `sessionTitlePrompt` or
2214
2237
  `enqueueOrder` are refused `unsupported_field`, naming them.
2215
2238
  - `get_effect_status { effectId, sessionPath? }`: the same effect ledger as a host.
2239
+ - `set_client_info { width, capabilities? }`: declares the connection's roles (`interactive_user`,
2240
+ `task_parent`), fixed once declared (`connection_role_fixed`), as on a host.
2241
+ - `attach_session { sessionId, durableSessionId, expectedHostInstance? }`: a live attachment, as on a host:
2242
+ `sessionId` is the terminal's session id from `list_sessions`, `durableSessionId` the same id, and
2243
+ `expectedHostInstance`, when sent, this endpoint's `instanceId`. Answers `{ sessionId, attached: true }` after the
2244
+ replay of the in-flight snapshot, then the session's records follow; another session is `unknown_session`, an
2245
+ identity that does not match is `session_identity_mismatch`.
2246
+ - `release_attachment { sessionId }`: drops this connection's attachment, answered `{ deferred: false }`;
2247
+ `session_not_attached` when it holds none.
2248
+ - `abort { intent?, effectId?, awaitIdle?, clearQueue?, graceMs? }`: the host's `abort` and effect receipts.
2249
+ `intent: "stop"` is refused `stop_requires_interactive_client: declare interactive_user with set_client_info first`
2250
+ until the connection declared `interactive_user`.
2216
2251
  - `wake { delivery_ids? }`: one drain pass, answered `{ admitted: [{ delivery_id, kind }] }`.
2217
2252
  - `extension_ui_response`: answers only a question this session asked and still waits on
2218
2253
  (`unknown_request` / `question_incomplete` / `invalid_response` otherwise). `uiRequestId` names the
@@ -2235,7 +2270,6 @@ registrant opens no socket and writes no registry directory.
2235
2270
  settles before the answer. Answers `{ interrupted: true, turnId }` with the `turn_epoch` it stopped. An idle
2236
2271
  session answers `{ interrupted: false }`; a `turnId` that is not the running turn answers
2237
2272
  `{ interrupted: false, turnId }` with the running one and leaves it alone. The session stays open.
2238
- - `prompt`, `steer` and `follow_up` are `unsupported`.
2239
2273
  - **Admission.** A message from another session enters only through the registrant's drain, which calls
2240
2274
  `pi.session.admitExternalMessage({ delivery_id, text, deliverAs, expected_turn_id? })`. One synchronous call
2241
2275
  decides and acts:
@@ -4446,6 +4480,15 @@ Response:
4446
4480
 
4447
4481
  An unpaged request (neither bound) answers exactly as before while the whole history fits in 48 MiB. A longer history is refused with `errorCode: "response_too_large"` instead of an answer that would close the connection; read it in pages.
4448
4482
 
4483
+ **Tail pages** (capability `entries_tail`). Pass `tail` (a positive integer) to read the NEWEST entries first: the answer holds the newest `tail` entries of the range, in append order, under the same `maxBytes` rule (8 MiB when only `tail` is given, capped at 48 MiB), and always at least the newest one. While older entries remain it carries `hasMore: true` and `next_before`, the id of the oldest entry it returned; ask again with `before: next_before` to walk back until `hasMore` is `false`. `before` (an entry id) limits any read to entries strictly before it; an id that names no entry is refused with `errorCode: "not_found"`. `since` is unchanged, and `since` and `before` may bound one range together. `tail` and `limit` cannot be combined (`invalid_argument`): `limit` pages forward from the range's start, `tail` backward from its end.
4484
+
4485
+ ```json
4486
+ {"type": "get_entries", "tail": 200, "observe": true}
4487
+ {"type": "get_entries", "tail": 200, "before": "<next_before>", "observe": true}
4488
+ ```
4489
+
4490
+ `get_entries` and `get_state` may carry `observe: true`: on a host such a read never counts as an attachment (see the idle-exit window).
4491
+
4449
4492
  A single entry larger than 48 MiB cannot travel in any answer, so a read whose next entry is that large (paged or not) is refused with `response_too_large` naming the entry. While a walk has more pages, the host keeps the session's full history in memory for the next page; it lets it go 30 s after the last page request, so a client that stops mid-walk does not pin the history.
4450
4493
 
4451
4494
  #### get_tree
@@ -5425,6 +5468,106 @@ A provider holds logins of one kind. If it had an OAuth login, the key replaces
5425
5468
 
5426
5469
  ## Extension UI Protocol
5427
5470
 
5471
+ Without presenter enforcement, MCP 2026-07-28 multi-round-trip form answering is terminal-only in v1.
5472
+ RPC-originated modern requests advertise no elicitation and terminate locally
5473
+ if input is required; they never fabricate a decline or retry incomplete input.
5474
+ Client roles and capability declarations do not enable this form channel.
5475
+ Existing legacy MCP dialogs and their RPC request/response shapes remain unchanged.
5476
+
5477
+ ### Opt-in private MCP forms
5478
+
5479
+ A multi-session RPC host or interactive terminal can opt in with `--mcp-presenter-enforcement`. This is a
5480
+ deployment option, not a project setting. It defaults to off; existing desktop and
5481
+ gateway clients keep their current behavior until they implement the private
5482
+ presenter protocol. Without this option, terminal forms retain their existing queue,
5483
+ legacy origin rules, and legacy stderr diagnostics. Enforced terminal forms also
5484
+ queue, and a collector failure ends only its own form, not the terminal's grant.
5485
+ All UI contexts in one terminal session binding share its presenter authority.
5486
+ Without enforcement, each real select/input answer retains its existing human
5487
+ admission timing; the form collector does not add a second final admission.
5488
+ Ephemeral selects also keep the scheduled `requestRender()` path without enforcement.
5489
+ With enforcement, only the authority's winning whole-form action admits once.
5490
+ The host advertises `mcp_form_presenter_v1` only when enforcement is enabled;
5491
+ the capability reports protocol support, not authority to present or answer a form.
5492
+
5493
+ The native launch owner enrolls an Ed25519 public key through an inherited private
5494
+ bootstrap channel. Descriptor 4 is reserved for this channel; descriptor 3 remains
5495
+ the separate owner-lifetime channel. Enrollment and management proofs sign the
5496
+ host's exact returned transcript. No secret is passed in an argument, environment
5497
+ variable, launch specification, or session state. Reusing a host does not enroll the
5498
+ caller, and a successor requires fresh enrollment and fresh grants.
5499
+ An invalid or lost bootstrap channel leaves the host without a presenter; it does
5500
+ not terminate the shared daemon or its lifetime channel. The helper remains lazy
5501
+ to preserve the supervisor's startup import budget, with import and relay failures
5502
+ contained at that boundary. Conditional idle handover
5503
+ is owned by the running host after the CLI returns: that host relays the successor's
5504
+ fresh challenge over the existing authenticated launch-owner channel. The CLI's
5505
+ bootstrap stream is never serialized into `begin_handover`.
5506
+
5507
+ `mcp_presenter_context` returns the caller's current connection nonce and attachment
5508
+ context. These identifiers confer no authority. Acquisition and revocation use the
5509
+ separate authenticated management endpoint, with a fresh challenge for each
5510
+ `mcp_presenter_assign`, `mcp_presenter_acquire`, or `mcp_presenter_revoke` command.
5511
+ Enrollment alone assigns no sessions. Before acquisition, the trusted native
5512
+ supervisor signs `mcp_presenter_assign` with `sessionId`, the current `bindingEpoch`,
5513
+ and `purpose: "mcp_form"` for a session it opened, or one the user explicitly adopted
5514
+ through the trusted host-owner UI. Ordinary RPC attachment cannot assign a session.
5515
+ Assignment belongs only to that enrolled principal and live binding; re-enrollment
5516
+ or binding replacement requires a new assignment. It is not persisted.
5517
+ An acquisition also requires the connection's `interactive_user` and
5518
+ `mcp_form_presenter_v1` capabilities, and binds the
5519
+ current host, session, binding epoch, connection nonce, attachment ID and epoch, and
5520
+ `purpose: "mcp_form"`. Only one holder is active for a session binding.
5521
+ Concurrent forms, including worker-origin forms, wait in the host's abort-aware
5522
+ FIFO for that holder. Waiting consumes the original deadline; timeout, cancellation,
5523
+ or revocation before display never fabricates a human response.
5524
+ Enforced legacy connections observe presenter readiness and reconnect when their
5525
+ negotiated form capability becomes stale, including a grant issued after connect.
5526
+ Revoking the last ready holder removes the capability on the new connection.
5527
+ A shared physical connection advertises forms when at least one attached owner is
5528
+ ready; each request still requires its own origin's grant. An ungranted owner cannot
5529
+ borrow another owner's presenter. Readiness changes during initialization are
5530
+ rechecked before the connection is published. Modern MRTR connections do not
5531
+ reconnect for this purpose because their capability metadata is per request.
5532
+
5533
+ Legacy elicitation has no reliable parent tool-call identifier in the received SDK
5534
+ handler metadata. With multiple `tools/call` operations in flight on one physical
5535
+ legacy client, enforced elicitation fails closed with the same fixed JSON-RPC
5536
+ processing error as an unavailable presenter: code `-32603`, message
5537
+ `Elicitation processing unavailable`, and no `data`.
5538
+ No form is displayed and no human answer is admitted. Without enforcement, legacy
5539
+ clients keep the SDK request method, origin handling, and concurrent-call behavior.
5540
+
5541
+ Worker binding waits at most five seconds for the host's acknowledgement; expiry
5542
+ fails with `private_form_unavailable`, removes the pending waiter, and releases any
5543
+ late host binding. Worker readiness events feed the same legacy renegotiation path.
5544
+ Under enforcement, server stderr remains available as redacted diagnostic records
5545
+ with allowlisted system error codes. Arbitrary text is omitted because a server can
5546
+ echo private messages, schema labels, or answers; ordinary legacy stderr is unchanged.
5547
+
5548
+ The granted connection receives `mcp_form_request` privately. Its envelope contains
5549
+ `sessionId`, `bindingEpoch`, `attachmentEpoch`, `grantId`, `dialogId`, `dialogToken`,
5550
+ and `formRevision`; the request also carries `server` (`label` and `source`),
5551
+ `message`, `schema`, and `deadlineAtMs`. The presenter returns that exact envelope
5552
+ with `mcp_form_presented` only after rendering, then `mcp_form_response` with
5553
+ `action: "accept" | "decline" | "cancel"` and accepted `content` when applicable.
5554
+ `mcp_form_sync` uses the current session, binding epoch, attachment epoch, and grant
5555
+ to recover the holder's still-live form. It is not a public replay API.
5556
+
5557
+ Unknown and stolen-dialog submissions from a non-holder both return
5558
+ `presenter_not_authorized` before private dialog lookup. The host validates a whole
5559
+ form before admitting a human answer, and a synchronous terminal latch accepts only
5560
+ one outcome. `mcp_form_resolved` carries the session, grant, dialog, and a reason:
5561
+ `answered`, `declined`, `cancelled`, `processing_failed`, or `operation_aborted`.
5562
+
5563
+ No presenter, or loss before actual presentation, is a processing failure. Only
5564
+ explicit human refusal produces decline. Dismissal after presentation is cancel;
5565
+ host timeout or presentation loss after presentation also cancels, without human
5566
+ admission. Operation abort terminates the operation. Private records, schemas,
5567
+ tokens, drafts, and answers never enter general extension UI events, session
5568
+ snapshots, replay, or ordinary output queues. Native desktop registration, WSL
5569
+ relay, and a gateway form page require separate client implementation.
5570
+
5428
5571
  Extensions can request user interaction via `ctx.ui.select()`, `ctx.ui.confirm()`, etc. In RPC mode, these are translated into a request/response sub-protocol on top of the base command/event flow.
5429
5572
 
5430
5573
  There are two categories of extension UI methods:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi",
3
- "version": "2026.10.25-2",
3
+ "version": "2026.10.25-4",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -71,9 +71,9 @@
71
71
  },
72
72
  "dependencies": {
73
73
  "@earendil-works/chord": "0.99.1",
74
- "@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.10.25-2",
75
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.25-2",
76
- "@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.10.25-2",
74
+ "@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.10.25-4",
75
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.10.25-4",
76
+ "@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.10.25-4",
77
77
  "@silvia-odwyer/photon-node": "0.3.4",
78
78
  "chalk": "6.0.0",
79
79
  "cross-spawn": "7.0.6",
@@ -92,8 +92,8 @@
92
92
  "yaml": "2.9.1",
93
93
  "@anthropic-ai/claude-agent-sdk": "0.3.306",
94
94
  "@anthropic-ai/sdk": "0.127.0",
95
- "@code-yeongyu/senpi-codemode": "2026.10.25-2",
96
- "@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.10.25-2",
95
+ "@code-yeongyu/senpi-codemode": "2026.10.25-4",
96
+ "@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.10.25-4",
97
97
  "@modelcontextprotocol/sdk": "1.32.1",
98
98
  "@mozilla/readability": "0.6.0",
99
99
  "linkedom": "0.18.13",
@@ -1,2 +0,0 @@
1
- import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
2
- import{rateLimitModelFamily,usageLimitResetMs}from"./chunk-MSQCTH6V.js";import{signalSpawnedChild}from"./chunk-KLAX467B.js";import{rememberResolvedReferenceValue}from"./chunk-DLAY7CIU.js";import{normalizeProviderError}from"./chunk-TAV6QZFM.js";import{extract429RetryAfterMs}from"./chunk-PELNNWGO.js";import{getShellConfig}from"./chunk-BWA4FVFW.js";import{Compile,getOverflowPatterns}from"./chunk-E6WYH3ZL.js";import{ConfigReferenceError,PROVIDER_NOT_CONFIGURED_PREFIX,USAGE_LIMIT_EXHAUSTION,isOAuthRefreshUnavailableError,listSlots}from"./chunk-6AOVUTUR.js";import{typebox_exports}from"./chunk-MIENF2YK.js";function rendezvousOrder(key,slots,hasher){return[...slots].map(slot=>({slot,score:hasher(`${key}\0${slot.name}`)})).sort((left,right)=>right.score>left.score?1:right.score<left.score?-1:0).map(({slot})=>slot)}function applyAccountOrder(order,ranked){if(order===void 0||order.length===0)return[...ranked];let byName=new Map(ranked.map(slot=>[slot.name,slot])),front=[];for(let name of order){let slot=byName.get(name);slot!==void 0&&!front.includes(slot)&&front.push(slot)}return[...front,...ranked.filter(slot=>!front.includes(slot))]}function withoutOrderedAccount(credential,name){if(credential.order===void 0||!credential.order.includes(name))return credential;let order=credential.order.filter(entry=>entry!==name),next={...credential};return order.length===0?delete next.order:next.order=order,next}var PRE_COMMIT_EVENT_TYPES=new Set(["start","text_start","thinking_start","toolcall_start"]);function isCommittedRotationOutput(event){return!PRE_COMMIT_EVENT_TYPES.has(event.type)}function isRotationStreamStart(event){return event.type==="start"}function rotationErrorFromEvent(event){if(event.type!=="error")return;let failure=new Error(event.error.errorMessage??"provider stream error"),status=event.error.providerDiagnostic?.httpStatus;return status===void 0?failure:Object.assign(failure,{status})}var DEFAULT_SLOT_BLOCK_MS=6e4,MAX_SLOT_BLOCK_MS=2880*60*1e3;function escapeRegExp(text){return text.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var ACCOUNT_USAGE_LIMIT_TEXT=new RegExp([...USAGE_LIMIT_EXHAUSTION.markers.map(escapeRegExp),String.raw`\b(?:hit|reached)\s+your\b[^.]*?\blimit\b`,String.raw`\busage\s+limit\s+reached\b`,String.raw`\b(?:Go|Free)UsageLimitError\b`,String.raw`\bblocking_limit\b`,String.raw`\brapid_refill_breaker\b`,String.raw`\bquota\s+exceeded\b`].join("|"),"i");function isAccountUsageLimitText(text){return ACCOUNT_USAGE_LIMIT_TEXT.test(text)}var COOLDOWN_BASE_MS=DEFAULT_SLOT_BLOCK_MS,COOLDOWN_CAP_MS=MAX_SLOT_BLOCK_MS,RETRY_SAME_MAX_ATTEMPTS=2,ACCOUNT_POOL_REFUSAL_NOTE="No request was sent: the account pool refused it locally.";function rateLimitCooldown(failureCount,serverHintMs,policy={}){let cap=policy.capMs??COOLDOWN_CAP_MS,base=policy.baseMs??COOLDOWN_BASE_MS,backoff=Math.min(cap,base*2**Math.max(0,failureCount)),floored=serverHintMs===void 0?backoff:Math.max(backoff,serverHintMs);return{cooldownMs:Math.min(cap,floored),retryAfterWasCapped:floored>cap}}function usageLimitCooldown(resetMs,policy={}){let cap=policy.capMs??COOLDOWN_CAP_MS;return{cooldownMs:Math.min(cap,resetMs),retryAfterWasCapped:resetMs>cap}}var INVALID_KEY_TEXT=/invalid[ _-]?(?:api[ _-]?)?key|authentication[_ ]?error|invalid x-api-key|unauthorized/i,ACCOUNT_SCOPED_403_TEXT=/account|credential|token|api[ _-]?key|organization|subscription/i,RATE_LIMIT_TEXT=/rate[ _-]?limit|too many requests|resource_exhausted/i,BILLING_TEXT=/billing|credits?[ _-]?(?:required|exhausted|balance)|insufficient[ _-]?(?:funds|quota|credit)|payment[ _-]?required|quota[ _-]?exhausted/i,OVERLOAD_TEXT=/overloaded/i,NETWORK_TEXT=/econnreset|econnrefused|etimedout|enotfound|socket hang up|fetch failed|network error|request timed out/i,WEBSOCKET_REQUEST_FAULT_TEXT=/websocket closed 100[89]\b/i,WEBSOCKET_TRANSPORT_FAULT_TEXT=/websocket (?:closed|error|connect timeout|liveness timeout)/i,FAIL_FAST_TEXT=/context[ _-]?(?:length|window)|maximum context|invalid[ _-]?model|model[ _-]?not[ _-]?found|malformed[ _-]?stream|premature[ _-]?(?:close|stream)/i,ABORT_TEXT=/\baborted?\b/i,TEMPORARY_FAULT_TEXT=/time[ds]?[ _-]?out|timed out|disconnect|reset before|connection reset|gateway|temporarily|unavailable/i;function isAbort(error,message){return error instanceof Error&&error.name==="AbortError"?!0:ABORT_TEXT.test(message)}function isOverflowText(text){return getOverflowPatterns().some(pattern=>pattern.test(text))}function classifyCredentialFailure(error,context={}){if(isOAuthRefreshUnavailableError(error))return{kind:"retry_same",maxAttempts:RETRY_SAME_MAX_ATTEMPTS};let normalized=normalizeProviderError(error),text=normalized.messageCarriesBody?normalized.message:`${normalized.message} ${normalized.body??""}`,status=normalized.status,failureCount=context.failureCount??0;if(isAbort(error,text))return{kind:"fail_request"};if(text.includes(ACCOUNT_POOL_REFUSAL_NOTE))return{kind:"fail_request"};let providerFault=status===408||status!==void 0&&status>=500&&status<600||status===void 0&&(TEMPORARY_FAULT_TEXT.test(text)||NETWORK_TEXT.test(text)||OVERLOAD_TEXT.test(text)),usageLimit=isAccountUsageLimitText(text)&&!isOverflowText(text),limitText=RATE_LIMIT_TEXT.test(text)||usageLimit;if(text.includes(PROVIDER_NOT_CONFIGURED_PREFIX))return{kind:"failover",block:{reason:"auth_error"}};if(status===401||!providerFault&&status!==429&&INVALID_KEY_TEXT.test(text))return{kind:"failover",block:{reason:"auth_error"}};if(status===403&&!limitText)return ACCOUNT_SCOPED_403_TEXT.test(text)?{kind:"failover",block:{reason:"auth_error"}}:{kind:"fail_request"};if(status===402||!providerFault&&BILLING_TEXT.test(text))return{kind:"failover",block:{reason:"account_disabled"}};if(status===429||limitText){let nowMs=context.nowMs??Date.now(),hint=extract429RetryAfterMs({status:status??429,bodyText:text},nowMs)??(usageLimit?normalized.retryAfterMs??usageLimitResetMs(text,nowMs):void 0),modelFamily=rateLimitModelFamily(text),policy={baseMs:context.cooldownBaseMs,capMs:context.cooldownCapMs};return{kind:"failover",block:{reason:"rate_limit",...usageLimit&&hint!==void 0&&hint>0?usageLimitCooldown(hint,policy):rateLimitCooldown(failureCount,hint,policy),...modelFamily===void 0?{}:{modelFamily}}}}return isOverflowText(text)||status===400||status===404||FAIL_FAST_TEXT.test(text)?{kind:"fail_request"}:WEBSOCKET_REQUEST_FAULT_TEXT.test(text)?{kind:"fail_request"}:WEBSOCKET_TRANSPORT_FAULT_TEXT.test(text)?{kind:"retry_same",maxAttempts:RETRY_SAME_MAX_ATTEMPTS}:status===529||OVERLOAD_TEXT.test(text)||status!==void 0&&status>=500&&status<600?{kind:"retry_same",maxAttempts:RETRY_SAME_MAX_ATTEMPTS}:status===408||NETWORK_TEXT.test(text)?{kind:"retry_same",maxAttempts:RETRY_SAME_MAX_ATTEMPTS}:{kind:"fail_request"}}var CredentialFailoverError=class extends Error{constructor(action,original,options={}){let detail=original instanceof Error?original.message:String(original);super(detail,{cause:original}),this.name="CredentialFailoverError",this.action=action,this.original=original,this.retryAt=options.retryAt}};function isAvailable(slot,now){return!(slot.blockReason==="account_disabled"||slot.blockedUntil!==void 0&&slot.blockedUntil>now)}function coolingProbe(slots,attempted){if(attempted.size>0)return[];let soonest=slots.filter(slot=>slot.blockReason==="rate_limit"&&slot.blockedUntil!==void 0).reduce((best,slot)=>best===void 0||(slot.blockedUntil??0)<(best.blockedUntil??0)?slot:best,void 0);return soonest===void 0?[]:[soonest]}function noSlotsMessage(slots,now){let blocked=slots.map(slot=>{let reason=slot.blockReason??"blocked";return slot.blockedUntil!==void 0&&slot.blockedUntil>now?`${slot.name} (${reason} until ${new Date(slot.blockedUntil).toISOString()})`:reason==="account_disabled"?`${slot.name} (account_disabled until re-login)`:`${slot.name} (${reason})`});return blocked.length===0?"No credential slots available":`No credential slots available: ${blocked.join(", ")}`}function soonestRetryAt(slots,now){let deadlines=slots.map(slot=>slot.blockedUntil).filter(value=>value!==void 0&&value>now);return deadlines.length===0?void 0:Math.min(...deadlines)}async function settle(stream){let iterator=stream[Symbol.asyncIterator]();try{await iterator.return?.(void 0)}catch{}}async function*runCredentialFailover(options){let now=options.now??Date.now,classify=options.classify??classifyCredentialFailure,attempted=new Set,retriesBySlot=new Map,lastError,lastOriginal,lastTerminalEvent,startAnnounced=!1;for(;;){let slots=await options.listSlots(),available=slots.filter(slot2=>!attempted.has(slot2.name)&&isAvailable(slot2,now())),candidates=available.length>0?available:coolingProbe(slots,attempted);if(candidates.length===0){if(lastTerminalEvent!==void 0){yield lastTerminalEvent;return}let retryAt=soonestRetryAt(slots,now());throw lastError!==void 0&&lastOriginal!==void 0?new CredentialFailoverError(lastError.action,lastOriginal,retryAt===void 0?{}:{retryAt}):new CredentialFailoverError({kind:"fail_request"},new Error(noSlotsMessage(slots,now())),retryAt===void 0?{}:{retryAt})}let slot=options.select(candidates),committedOutput=!1,terminalEvent,attemptStream;try{attemptStream=await options.runAttempt(slot);for await(let event of attemptStream){let failure=options.errorFromEvent?.(event);if(failure!==void 0)throw terminalEvent=event,failure;if(options.isStreamStart?.(event)){if(startAnnounced)continue;startAnnounced=!0}committedOutput||=options.isCommittedOutput(event),yield event}await options.onSuccess?.(slot);return}catch(error){attemptStream&&await settle(attemptStream);let failureCount=(slot.failureCount??0)+(retriesBySlot.get(slot.name)??0),action=classify(error,{failureCount});if(lastOriginal=error,lastTerminalEvent=terminalEvent,action.kind==="retry_same"&&!committedOutput){let used=(retriesBySlot.get(slot.name)??0)+1;if(retriesBySlot.set(slot.name,used),used<action.maxAttempts)continue;if(terminalEvent!==void 0){yield terminalEvent;return}throw new CredentialFailoverError(action,error)}if(action.kind!=="failover"){if(terminalEvent!==void 0){yield terminalEvent;return}throw new CredentialFailoverError(action,error)}await options.persistBlock(slot,action.block),attempted.add(slot.name);let failure=new CredentialFailoverError(action,error);if(lastError=failure,await options.onRotate?.({slot,block:action.block,attempt:attempted.size,committedOutput}),committedOutput){if(terminalEvent!==void 0){yield terminalEvent;return}throw failure}}}}function slotKind(slot){return slot.access!==void 0||slot.refresh!==void 0?"oauth":slot.key!==void 0?"api_key":void 0}function unusedSlotKinds(credential){if(!credential||!Array.isArray(credential.accounts))return[];let kinds=new Set;for(let slot of listSlots(credential)){let kind=slotKind(slot);kind!==void 0&&kind!==credential.type&&kinds.add(kind)}return[...kinds]}function slotsOfOwnKind(credential){return listSlots(credential).filter(slot=>slotKind(slot)!==otherKind(credential.type))}var otherKind=kind=>kind==="oauth"?"api_key":"oauth";var FileReferenceSchema=typebox_exports.Object({$file:typebox_exports.String({minLength:1})},{additionalProperties:!1}),EnvReferenceSchema=typebox_exports.Object({$env:typebox_exports.String({pattern:"^[A-Za-z_][A-Za-z0-9_]*$"})},{additionalProperties:!1}),ConcatReferenceSchema=typebox_exports.Object({$concat:typebox_exports.Array(typebox_exports.Union([typebox_exports.String(),FileReferenceSchema,EnvReferenceSchema]))},{additionalProperties:!1}),ConfigReferenceSchema=typebox_exports.Union([FileReferenceSchema,EnvReferenceSchema,ConcatReferenceSchema]),ConfigValueSchema=typebox_exports.Union([typebox_exports.String(),ConfigReferenceSchema]),NonemptyConfigValueSchema=typebox_exports.Union([typebox_exports.String({minLength:1}),ConfigReferenceSchema]),validateReference=Compile(ConfigReferenceSchema);function isConfigReference(value){return validateReference.Check(value)}function assertConfigValue(value,field){if(typeof value!="string"&&!isConfigReference(value))throw new ConfigReferenceError(field,value,"malformed")}function sleep(ms,signal){return new Promise((resolve,reject)=>{if(signal?.aborted){reject(new Error("Aborted"));return}let timeout=setTimeout(resolve,ms);signal?.addEventListener("abort",()=>{clearTimeout(timeout),reject(new Error("Aborted"))})})}var warnedVariables=new Set;function warnOpaqueConfigEnvironment(name,value){value!==void 0&&/\{(?:file|env):/.test(value)&&!warnedVariables.has(name)&&(warnedVariables.add(name),console.warn(`${name}: environment value contains reference-looking text; inserted literally`))}function warnMissingConfigEnvironment(name){warnedVariables.has(name)||(warnedVariables.add(name),console.warn(`${name}: configuration environment variable is unset`))}import{dirname as dirname2}from"node:path";import{constants}from"node:fs";import{lstat,open,readlink}from"node:fs/promises";import{homedir}from"node:os";import{dirname,isAbsolute,join,parse,sep}from"node:path";var MAX_BYTES=65536,readableWarnings=new Set;function fail(input,reason){throw new ConfigReferenceError(input.field,input.reference,reason)}function checkOwner(input,stat){process.platform!=="win32"&&stat.uid!==0&&stat.uid!==process.getuid?.()&&fail(input,"unsafe-owner")}function checkWritable(input,stat,ancestor){process.platform==="win32"||stat.isSymbolicLink()||(stat.mode&18)===0||ancestor&&stat.isDirectory()&&(stat.mode&512)!==0||fail(input,"unsafe-permissions")}function sameFile(left,right){return left.dev===right.dev&&left.ino===right.ino}function absolutePath(input){let declared=input.reference.$file;return declared.startsWith("~/")||process.platform==="win32"&&declared.startsWith("~\\")?`${homedir()}${sep}${declared.slice(2)}`:isAbsolute(declared)?declared:`${input.baseDir}${sep}${declared}`}async function walk(input){let absolute=absolutePath(input),current=parse(absolute).root,pending=absolute.slice(current.length).split(sep).filter(Boolean),stat=await lstat(current),links=0;checkOwner(input,stat),checkWritable(input,stat,!0);for(let part=pending.shift();part!==void 0;part=pending.shift()){if(part===".")continue;stat.isDirectory()||fail(input,"non-regular");let next=part===".."?dirname(current):join(current,part),nextStat=await lstat(next);if(checkOwner(input,nextStat),process.platform!=="win32"&&(stat.mode&18)!==0&&!nextStat.isSymbolicLink()&&(nextStat.mode&18)!==0&&fail(input,"unsafe-permissions"),checkWritable(input,nextStat,pending.length>0),nextStat.isSymbolicLink()){++links>40&&fail(input,"unreadable");let target=await readlink(next);sameFile(nextStat,await lstat(next))||fail(input,"changed");let targetPath=isAbsolute(target)?target:`${current}${sep}${target}`;current=parse(targetPath).root,pending=[...targetPath.slice(current.length).split(sep).filter(Boolean),...pending],stat=await lstat(current),checkOwner(input,stat),checkWritable(input,stat,!0)}else current=next,stat=nextStat}return stat.isFile()||fail(input,"non-regular"),{path:current,stat}}async function readConfigReferenceFile(input){try{let walked=await walk(input),flags=constants.O_RDONLY|(constants.O_NOFOLLOW??0)|(constants.O_NONBLOCK??0),handle=await open(walked.path,flags);try{let stat=await handle.stat();sameFile(walked.stat,stat)||fail(input,"changed"),stat.isFile()||fail(input,"non-regular"),checkOwner(input,stat),checkWritable(input,stat,!1),stat.size>MAX_BYTES&&fail(input,"oversize");let buffer=Buffer.alloc(MAX_BYTES+1),length=0;for(;length<buffer.length;){let{bytesRead}=await handle.read(buffer,length,buffer.length-length,length);if(bytesRead===0)break;length+=bytesRead}length>MAX_BYTES&&fail(input,"oversize");let value;try{value=new TextDecoder("utf-8",{fatal:!0}).decode(buffer.subarray(0,length)).trim()}catch{fail(input,"invalid-utf8")}return value.length===0&&fail(input,"empty"),process.platform!=="win32"&&(stat.mode&36)!==0&&!readableWarnings.has(walked.path)&&(readableWarnings.add(walked.path),console.warn(`${input.field}: reference file is readable by other users`)),value}finally{await handle.close()}}catch(error){if(error instanceof ConfigReferenceError)throw error;let code=Reflect.get(Object(error),"code");fail(input,code==="ENOENT"||code==="ENOTDIR"?"missing":"unreadable")}}var origins=new WeakMap;function rememberConfigReferenceOrigin(value,context){typeof value!="string"&&origins.set(value,context)}function configReferenceContext(value,context){return context??(typeof value=="string"?void 0:origins.get(value))}function assertConfigReferenceAuthority(value,context){if(typeof value=="string")return;let source=configReferenceContext(value,context);if(source?.authority!=="trusted")throw new ConfigReferenceError(source?.field??"configuration value",value,"forbidden");assertConfigValue(value,source.field)}async function resolveConfigReference(value,context,env){let source=configReferenceContext(value,context);if(assertConfigReferenceAuthority(value,source),source===void 0)throw new ConfigReferenceError("configuration value",value,"forbidden");if("$file"in value){let text=await readConfigReferenceFile({field:source.field,reference:value,baseDir:source.baseDir??dirname2(source.origin)});return rememberResolvedReferenceValue(text),text}if("$env"in value){let result2=(env??source.env??process.env)[value.$env];if(warnOpaqueConfigEnvironment(value.$env,result2),result2===void 0)throw new ConfigReferenceError(source.field,value,"unset");if(result2==="")throw new ConfigReferenceError(source.field,value,"empty");return rememberResolvedReferenceValue(result2),result2}let parts=[];for(let part of value.$concat)parts.push(typeof part=="string"?part:await resolveConfigReference(part,source,env));let result=parts.join("");if(result==="")throw new ConfigReferenceError(source.field,value,"empty");return rememberResolvedReferenceValue(result),result}import{spawn}from"child_process";var COMMAND_EXECUTION_MAX_ATTEMPTS=3,COMMAND_EXECUTION_BACKOFF_MS=[250,1e3],COMMAND_TIMEOUT_MS=1e4,COMMAND_MAX_OUTPUT_BYTES=1024*1024;function runCommandProcess(file,args,options){return new Promise(resolveOutcome=>{let spawnOptions={shell:options.shell,windowsHide:!0,...options.env===void 0?{}:{env:{...process.env,...options.env}}},child=options.stdin===void 0?spawn(file,[...args],{...spawnOptions,stdio:["ignore","pipe","ignore"]}):spawn(file,[...args],{...spawnOptions,stdio:["pipe","pipe","ignore"]}),stdout="",settled=!1,finish=outcome=>{settled||(settled=!0,clearTimeout(timer),resolveOutcome(outcome))},timer=setTimeout(()=>{signalSpawnedChild(child,"SIGTERM"),child.unref(),finish({executed:!0,value:void 0})},COMMAND_TIMEOUT_MS);child.stdout.setEncoding("utf-8"),child.stdout.on("data",chunk=>{stdout+=chunk,!(stdout.length<=COMMAND_MAX_OUTPUT_BYTES)&&(signalSpawnedChild(child,"SIGTERM"),finish({executed:!0,value:void 0}))}),child.on("error",error=>finish({executed:error.code!=="ENOENT",value:void 0})),child.on("close",code=>{let value=stdout.trim();finish({executed:!0,value:code===0&&value?value:void 0})}),child.stdin?.end(options.stdin)})}async function executeWithConfiguredShell(command,env){try{let{shell,args,commandTransport}=getShellConfig(),commandFromStdin=commandTransport==="stdin";return await runCommandProcess(shell,commandFromStdin?args:[...args,command],{shell:!1,...commandFromStdin?{stdin:command}:{},...env===void 0?{}:{env}})}catch{return{executed:!1,value:void 0}}}async function executeCommandOnce(command,env){if(process.platform==="win32"){let configured=await executeWithConfiguredShell(command,env);if(configured.executed)return configured.value}return(await runCommandProcess(command,[],{shell:!0,...env===void 0?{}:{env}})).value}async function runConfigCommand(commandConfig,env){let command=commandConfig.slice(1);for(let attempt=0;attempt<COMMAND_EXECUTION_MAX_ATTEMPTS;attempt++){let value=await executeCommandOnce(command,env);if(value!==void 0)return value;let backoffMs=COMMAND_EXECUTION_BACKOFF_MS[attempt];backoffMs!==void 0&&await sleep(backoffMs)}}var commandResultCache=new Map,ENV_VAR_NAME_RE=/^[A-Za-z_][A-Za-z0-9_]*$/,ENV_VAR_NAME_PREFIX_RE=/^[A-Za-z_][A-Za-z0-9_]*/;function appendLiteral(parts,value){if(!value)return;let previousPart=parts[parts.length-1];if(previousPart?.type==="literal"){previousPart.value+=value;return}parts.push({type:"literal",value})}function parseConfigValueTemplate(config){let parts=[],index=0;for(;index<config.length;){let dollarIndex=config.indexOf("$",index);if(dollarIndex<0){appendLiteral(parts,config.slice(index));break}appendLiteral(parts,config.slice(index,dollarIndex));let nextChar=config[dollarIndex+1];if(nextChar==="$"||nextChar==="!"){appendLiteral(parts,nextChar),index=dollarIndex+2;continue}if(nextChar==="{"){let endIndex=config.indexOf("}",dollarIndex+2);if(endIndex<0){appendLiteral(parts,"$"),index=dollarIndex+1;continue}let name=config.slice(dollarIndex+2,endIndex);ENV_VAR_NAME_RE.test(name)?parts.push({type:"env",name}):appendLiteral(parts,config.slice(dollarIndex,endIndex+1)),index=endIndex+1;continue}let match=config.slice(dollarIndex+1).match(ENV_VAR_NAME_PREFIX_RE);if(match){parts.push({type:"env",name:match[0]}),index=dollarIndex+1+match[0].length;continue}appendLiteral(parts,"$"),index=dollarIndex+1}return parts}function parseConfigValueReference(config){return config.startsWith("!")?{type:"command",config}:{type:"template",parts:parseConfigValueTemplate(config)}}function resolveEnvConfigValue(name,env){let value=env?.[name]||process.env[name]||void 0;return warnOpaqueConfigEnvironment(name,value),value}function getTemplateEnvVarNames(parts){let names=[];for(let part of parts)part.type!=="env"||names.includes(part.name)||names.push(part.name);return names}function resolveTemplate(parts,env){let resolved="";for(let part of parts){if(part.type==="literal"){resolved+=part.value;continue}let envValue=resolveEnvConfigValue(part.name,env);if(envValue===void 0){warnMissingConfigEnvironment(part.name);return}resolved+=envValue}return resolved}function getConfigValueEnvVarNames(config){if(typeof config!="string")return"$env"in config?[config.$env]:"$concat"in config?[...new Set(config.$concat.flatMap(part=>typeof part!="string"&&"$env"in part?[part.$env]:[]))]:[];let reference=parseConfigValueReference(config);return reference.type==="template"?getTemplateEnvVarNames(reference.parts):[]}function getMissingConfigValueEnvVarNames(config,env){return getConfigValueEnvVarNames(config).filter(name=>resolveEnvConfigValue(name,env)===void 0)}function isCommandConfigValue(config){return typeof config=="string"&&parseConfigValueReference(config).type==="command"}function isConfigValueConfigured(config,env){return typeof config!="string"||getMissingConfigValueEnvVarNames(config,env).length===0}function resolveConfigValue(config,env,context){if(typeof config!="string")return resolveConfigReference(config,context,env);let reference=parseConfigValueReference(config);return reference.type==="command"?env===void 0?executeCommand(reference.config):runConfigCommand(reference.config,env):Promise.resolve(resolveTemplate(reference.parts,env))}function executeCommand(commandConfig){let cached=commandResultCache.get(commandConfig);if(cached)return cached;let result=runConfigCommand(commandConfig);return commandResultCache.set(commandConfig,result),result}function resolveConfigValueUncached(config,env,context){if(typeof config!="string")return resolveConfigReference(config,context,env);let reference=parseConfigValueReference(config);return reference.type==="command"?runConfigCommand(reference.config,env):Promise.resolve(resolveTemplate(reference.parts,env))}async function resolveConfigValueOrThrow(config,description,env,context){if(typeof config!="string")return resolveConfigReference(config,context,env);let resolvedValue=await resolveConfigValueUncached(config,env,context);if(resolvedValue!==void 0)return resolvedValue;let reference=parseConfigValueReference(config);if(reference.type==="command")throw new Error(`Failed to resolve ${description} from shell command: ${reference.config.slice(1)}`);if(reference.type==="template"){let missingEnvVars=getMissingConfigValueEnvVarNames(config,env);if(missingEnvVars.length===1)throw new Error(`Failed to resolve ${description} from environment variable: ${missingEnvVars[0]}`);if(missingEnvVars.length>1)throw new Error(`Failed to resolve ${description} from environment variables: ${missingEnvVars.join(", ")}`)}throw new Error(`Failed to resolve ${description}`)}async function resolveHeadersOrThrow(headers,description,env){if(!headers)return;for(let value of Object.values(headers))assertConfigReferenceAuthority(value);let resolved={};for(let[key,value]of Object.entries(headers))resolved[key]=await resolveConfigValueOrThrow(value,`${description} header "${key}"`,env);return Object.keys(resolved).length>0?resolved:void 0}export{slotKind,unusedSlotKinds,slotsOfOwnKind,rendezvousOrder,applyAccountOrder,withoutOrderedAccount,ConfigValueSchema,NonemptyConfigValueSchema,isConfigReference,warnOpaqueConfigEnvironment,warnMissingConfigEnvironment,rememberConfigReferenceOrigin,assertConfigReferenceAuthority,resolveConfigReference,sleep,getConfigValueEnvVarNames,isCommandConfigValue,isConfigValueConfigured,resolveConfigValue,resolveConfigValueOrThrow,resolveHeadersOrThrow,isCommittedRotationOutput,isRotationStreamStart,rotationErrorFromEvent,MAX_SLOT_BLOCK_MS,ACCOUNT_POOL_REFUSAL_NOTE,classifyCredentialFailure,CredentialFailoverError,runCredentialFailover};
@@ -1,5 +0,0 @@
1
- import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
2
- import{attachMcpGrantPresentation,effectStatusRecord,sessionQuestionBridges}from"./chunk-JRXKLIEQ.js";import{RPC_ERROR_INVALID_SESSION_CONTEXT,RPC_ERROR_INVALID_SUBSCRIPTION,RPC_ERROR_SESSION_NOT_ATTACHED,RPC_ERROR_SUBSCRIPTION_LIMIT,RPC_ERROR_UNKNOWN_SUBSCRIPTION,RPC_ERROR_UNSUPPORTED_TRANSPORT}from"./chunk-GY5MR5KR.js";import{omitInlineMedia}from"./chunk-EHQXLHRB.js";import{MEDIA_PLACEHOLDERS_CAPABILITY,hostGeneration,hostInstanceId}from"./chunk-W2BKZBYJ.js";import{serializeJsonLine}from"./chunk-WFXUCVIR.js";import{AsyncLocalStorage}from"node:async_hooks";var RpcSecurityPresentations=class{#sessions=new Map;#attachments=new Map;constructor(fanout,inConnection,flush){this.fanout=fanout,this.inConnection=inConnection,this.flush=flush}bind(sessionId,durableId,notices,select){let previous=this.#sessions.get(sessionId);this.#stop(previous);let state={durableId,select,snapshot:notices.snapshot,registrations:new Map,grants:new Map,replayed:previous?.durableId===durableId?previous.replayed:new Map};this.#sessions.set(sessionId,state),state.stop=notices.subscribe(snapshot=>this.acceptSnapshot(sessionId,snapshot));for(let id of this.fanout.attachedConnections(sessionId))this.#register(id,state);return()=>{this.#sessions.get(sessionId)===state&&(this.#stop(state),this.#sessions.delete(sessionId))}}acceptSnapshot(sessionId,snapshot){let state=this.#sessions.get(sessionId);state===void 0&&(state={snapshot,registrations:new Map,grants:new Map,replayed:new Map},this.#sessions.set(sessionId,state)),state.snapshot=snapshot;for(let id of this.fanout.attachedConnections(sessionId))this.#replay(sessionId,id,snapshot)}attach(id,sessionId){let attached=this.#attachments.get(id)??new Set;if(attached.has(sessionId))return;attached.add(sessionId),this.#attachments.set(id,attached);let state=this.#sessions.get(sessionId);state!==void 0&&(this.#register(id,state),this.#replay(sessionId,id,state.snapshot))}detach(id,sessionId){this.#attachments.get(id)?.delete(sessionId),this.#attachments.get(id)?.size===0&&this.#attachments.delete(id);let state=this.#sessions.get(sessionId);state?.registrations.get(id)?.(),state?.registrations.delete(id),state?.replayed.delete(id),state!==void 0&&this.#cancelFor(state,id)}dropConnection(id){for(let sessionId of[...this.#attachments.get(id)??[]])this.detach(id,sessionId)}capabilitiesChanged(id){for(let sessionId of this.#attachments.get(id)??[]){let state=this.#sessions.get(sessionId);state!==void 0&&(this.#register(id,state),this.fanout.connectionHas(id,"grant_select")||this.#cancelFor(state,id))}}routeWorkerGrant(sessionId,requestId,preferred,fallbackCapabilities,cancel){let attached=this.fanout.attachedConnections(sessionId),connection=attached.includes(preferred??"")&&this.fanout.connectionHas(preferred,"grant_select")?preferred:attached.find(id=>this.fanout.connectionHas(id,"grant_select"));if(!(connection!==void 0||this.fanout.isEmpty()&&fallbackCapabilities.includes("grant_select")))return cancel(),{accepted:!1};let state=this.#sessions.get(sessionId);return state===void 0&&(state={snapshot:{generation:0,notices:[]},registrations:new Map,grants:new Map,replayed:new Map},this.#sessions.set(sessionId,state)),state.grants.set(requestId,{connection,cancel}),{accepted:!0,connection}}acceptWorkerResponse(sessionId,requestId,connection){let state=this.#sessions.get(sessionId),pending=state?.grants.get(requestId);if(pending!==void 0)return pending.connection!==connection||connection!==void 0&&!this.fanout.connectionHas(connection,"grant_select")?!1:(state?.grants.delete(requestId),!0)}settleGrant(sessionId,requestId){this.#sessions.get(sessionId)?.grants.delete(requestId)}dropSession(sessionId){this.#stop(this.#sessions.get(sessionId)),this.#sessions.delete(sessionId);for(let[id,sessions]of this.#attachments)sessions.delete(sessionId),sessions.size===0&&this.#attachments.delete(id)}#register(id,state){if(!this.fanout.connectionHas(id,"grant_select")){state.registrations.get(id)?.(),state.registrations.delete(id);return}if(state.registrations.has(id)||state.select===void 0||state.durableId===void 0)return;let select=state.select;state.registrations.set(id,attachMcpGrantPresentation(state.durableId,(title,choices,opts)=>this.inConnection(id,()=>select(title,choices,opts))))}#replay(sessionId,id,snapshot){let target=this.fanout.get(id);if(target===void 0)return;let state=this.#sessions.get(sessionId),revision=JSON.stringify(snapshot);if(state?.replayed.get(id)!==revision){state?.replayed.set(id,revision);for(let notice of snapshot.notices)target.actor.enqueue(serializeJsonLine({type:"extension_ui_request",id:crypto.randomUUID(),sessionId,method:"notify",notifyType:"warning",message:notice.message}));this.flush()}}#cancelFor(state,id){for(let[requestId,pending]of state.grants)pending.connection===id&&(state.grants.delete(requestId),pending.cancel())}#stop(state){state?.stop?.();for(let stop of state?.registrations.values()??[])stop();for(let pending of state?.grants.values()??[])pending.cancel()}};var blockedMsTotal=0;function recordLoopBlockedMs(driftMs){driftMs>0&&(blockedMsTotal+=driftMs)}function loopBlockedMark(){return blockedMsTotal}function loopBlockedMsSince(mark){return blockedMsTotal-mark}var EVENT_RESYNC_CAPABILITY="event_resync";function isCorrelatedReply(value){return typeof value=="object"&&value!==null&&"type"in value&&value.type==="response"&&"id"in value&&typeof value.id=="string"}var SocketPeerLivenessError=class extends Error{constructor(){super("socket peer missed its liveness ping"),this.name="SocketPeerLivenessError"}},SocketPeerRecovery=class{constructor(write,snapshot){this.awaitingPing=!1,this.pingBuffered=!1,this.replyLost=!1,this.write=write,this.snapshot=snapshot,this.pingBuffered=!0,this.write(serializeJsonLine({type:"rpc_ping"}))}get pending(){return this.reason!==void 0}shed(reason){this.reason===void 0&&(this.reason=reason,this.readSnapshot=this.snapshot(reason))}acknowledge(){this.awaitingPing=!1}loseReply(){this.replyLost=!0}ping(){if(this.awaitingPing)throw new SocketPeerLivenessError;this.awaitingPing=!0,this.pingBuffered||(this.pingBuffered=!0,this.write(serializeJsonLine({type:"rpc_ping"})))}takeSnapshot(){let reason=this.reason;this.reason=void 0,this.awaitingPing=!1,this.pingBuffered=!1;let snapshot=this.readSnapshot;this.readSnapshot=void 0;let gap=reason===void 0?[]:[{type:"resync_required",reason,replyLost:this.replyLost}];return this.replyLost=!1,[...gap,...snapshot?.()??[]]}};var DEFAULT_QUEUE_BYTES=64*1024*1024,DEFAULT_STALL_MS=3e4,SocketEventQueueStallError=class extends Error{constructor(pendingBytes,stallMs){super(`socket event queue stalled: peer did not drain ${pendingBytes} queued bytes within ${stallMs}ms`),this.name="SocketEventQueueStallError",this.pendingBytes=pendingBytes,this.stallMs=stallMs}},SocketEventQueueOverflowError=class extends Error{constructor(queuedBytes,incomingBytes,maxQueueBytes,incomingPreview){super(`socket event queue overflow: ${queuedBytes} queued + ${incomingBytes} incoming > ${maxQueueBytes} (incoming: ${incomingPreview})`),this.name="SocketEventQueueOverflowError",this.queuedBytes=queuedBytes,this.incomingBytes=incomingBytes,this.maxQueueBytes=maxQueueBytes,this.incomingPreview=incomingPreview}},SocketEventSinkActor=class{constructor(sink,onFailure,maxQueueBytes=DEFAULT_QUEUE_BYTES,stallMs=DEFAULT_STALL_MS){this.queue=[],this.queuedBytes=0,this.closed=!1,this.recoveryEnabled=!1,this.stopped=new AbortController,this.sink=sink,this.onFailure=onFailure,this.maxQueueBytes=maxQueueBytes,this.stallMs=stallMs}enableRecovery(snapshot){this.recoveryEnabled=!0,this.recovery??=new SocketPeerRecovery(line=>this.sink.writeRaw(line),snapshot),this.recovery.acknowledge()}disableRecovery(){this.recoveryEnabled=!1,this.recovery?.pending||(this.recovery=void 0)}enqueue(line,key,onWritten,demotedLine,reply=!1){if(this.closed)return;if(this.recovery?.pending&&!reply){onWritten?.();return}let bytes=Buffer.byteLength(line);if(this.recovery?.pending){this.queuedBytes+bytes<=this.maxQueueBytes?(this.queue.push({line,bytes,onWritten,reply}),this.queuedBytes+=bytes):(this.recovery.loseReply(),onWritten?.());return}if(key!==void 0){let existing=this.queue.find(entry=>entry.key===key);existing&&(existing.demotedLine!==void 0&&(this.queuedBytes-=existing.bytes,existing.line=existing.demotedLine,existing.bytes=Buffer.byteLength(existing.line),this.queuedBytes+=existing.bytes,existing.demotedLine=void 0),existing.key=void 0)}if(this.queuedBytes+bytes>this.maxQueueBytes){if(this.recovery&&this.recoveryEnabled){this.shed("overflow"),this.enqueue(line,void 0,onWritten,void 0,reply),this.drain();return}let overflow=new SocketEventQueueOverflowError(this.queuedBytes,bytes,this.maxQueueBytes,line.slice(0,120));this.closed=!0,this.queue.length=0,this.queuedBytes=0;try{this.sink.writeRaw(`${JSON.stringify({type:"overflow",error:"overflow, resync required"})}
3
- `)}catch(cause){this.onFailure(cause)}this.onFailure(overflow);return}this.queue.push({line,bytes,key,demotedLine,onWritten,reply}),this.queuedBytes+=bytes,this.drain()}async flush(){for(;this.draining;)await this.draining;if(this.failure!==void 0)throw this.failure}waitForAcceptance(){return Promise.resolve()}close(){this.closed=!0,this.queue.length=0,this.queuedBytes=0,this.stopped.abort()}shed(reason){if(this.recovery?.pending)return;let replies=[];for(let entry of this.queue)entry.reply?replies.push(entry):entry.onWritten?.();this.queue.length=0;for(let reply of replies)this.queue.push(reply);this.queuedBytes=replies.reduce((bytes,entry)=>bytes+entry.bytes,0),this.recovery?.shed(reason)}waitForDrainOrStall(writtenBytes){return new Promise((resolve,reject)=>{let timer,blockedMark=loopBlockedMark(),cleanup=()=>{clearTimeout(timer),this.stopped.signal.removeEventListener("abort",onStop)},onStop=()=>{cleanup(),resolve()},onDeadline=()=>{let blockedMs=loopBlockedMsSince(blockedMark);if(blockedMs>0){blockedMark=loopBlockedMark(),timer=setTimeout(onDeadline,Math.min(blockedMs,this.stallMs));return}if(this.recovery&&this.recoveryEnabled){try{this.shed("stall"),this.recovery.ping(),blockedMark=loopBlockedMark(),timer=setTimeout(onDeadline,this.stallMs)}catch(cause){cleanup(),reject(cause)}return}let stall=new SocketEventQueueStallError(this.queuedBytes+writtenBytes,this.stallMs);try{this.sink.writeRaw(`${JSON.stringify({type:"overflow",error:"stalled, resync required"})}
4
- `)}catch{}cleanup(),reject(stall)};timer=setTimeout(onDeadline,this.stallMs),this.stopped.signal.addEventListener("abort",onStop,{once:!0}),this.sink.waitForBackpressure().then(()=>{cleanup(),resolve()},cause=>{cleanup(),reject(cause)})})}drain(){return this.draining?this.draining:(this.draining=(async()=>{try{for(;!this.closed&&(this.queue.length>0||this.recovery?.pending);){if(this.recovery?.pending&&this.queue.length===0){if(await this.waitForDrainOrStall(0),this.closed)break;let snapshot=this.recovery.takeSnapshot();this.recoveryEnabled||(this.recovery=void 0);for(let record of snapshot){if(this.closed||this.recovery?.pending)break;let line=serializeJsonLine(record);this.sink.writeRaw(line),await this.waitForDrainOrStall(Buffer.byteLength(line))}continue}let entry=this.queue.shift();this.queuedBytes-=entry.bytes,this.sink.writeRaw(entry.line),await this.waitForDrainOrStall(entry.bytes),entry.onWritten?.()}}catch(cause){this.failure=cause,this.closed=!0,this.queue.length=0,this.queuedBytes=0,this.onFailure(cause)}})().finally(()=>{this.draining=void 0,!this.closed&&(this.queue.length>0||this.recovery?.pending)&&this.drain()}),this.draining)}};var BROADCAST_LIFECYCLE_RECORDS=new Set(["agent_start","agent_settled","agent_idle","session_opened","session_closed","session_lifecycle"]);function snapshotPlaceholderLine(record){if(record.placeholderLine!==void 0)return record.placeholderLine;let redacted=record.source===void 0?void 0:omitInlineMedia(record.source);return record.placeholderLine=redacted===void 0||redacted===record.source?record.line:serializeJsonLine(redacted),record.placeholderLine}var SessionEventFanout=class{constructor(){this.connections=new Map,this.sessionSnapshots=new Map,this.lifecycleSnapshots=new Map,this.pendingQuestions=new Map,this.connectionCapabilities=new Map,this.connectionSessions=new Map,this.registeredCapabilityConnections=new Set}registerConnection(id,connection,options={}){let actor=new SocketEventSinkActor(connection,cause=>{this.connections.get(id)?.actor===actor&&(this.connections.delete(id),this.connectionCapabilities.delete(id),this.connectionSessions.delete(id),this.registeredCapabilityConnections.delete(id),this.onConnectionClosed?.(id)),process.stderr.write(`senpi rpc connection ${id} event queue failed; closing: ${cause instanceof Error?cause.message:String(cause)}
5
- `),connection.close?.()},options.maxQueueBytes,options.stallMs);this.connections.set(id,{connection,actor}),this.connectionCapabilities.set(id,new Set),this.connectionSessions.set(id,new Set)}unregisterConnection(id){let registered=this.connections.get(id);registered&&(registered.actor.close(),this.connections.delete(id),this.connectionCapabilities.delete(id),this.connectionSessions.delete(id),this.registeredCapabilityConnections.delete(id))}attachConnectionToSession(id,sessionId){if(!this.connections.has(id))return;let sessions=this.connectionSessions.get(id)??new Set;if(!sessions.has(sessionId)){sessions.add(sessionId),this.connectionSessions.set(id,sessions),this.replaySnapshot(id,sessionId);for(let frame of this.pendingQuestions.get(sessionId)?.values()??[])this.connections.get(id)?.actor.enqueue(serializeJsonLine({...frame,remainingMs:typeof frame.deadlineAtMs=="number"?Math.max(0,frame.deadlineAtMs-Date.now()):0}))}}detachConnectionFromSession(id,sessionId){this.connectionSessions.get(id)?.delete(sessionId)}setConnectionCapabilities(id,capabilities){let registered=this.connections.get(id);registered&&(this.connectionCapabilities.set(id,new Set(capabilities)),this.registeredCapabilityConnections.add(id),capabilities.includes(EVENT_RESYNC_CAPABILITY)?registered.actor.enableRecovery(reason=>{let attached=[...this.connectionSessions.get(id)??[]];return()=>this.resyncSnapshot(id,reason,attached)}):registered.actor.disableRecovery())}clearConnectionCapabilities(id){this.connections.has(id)&&(this.connectionCapabilities.set(id,new Set),this.registeredCapabilityConnections.delete(id))}hasRegisteredConnectionCapabilities(id){return this.registeredCapabilityConnections.has(id)}getConnectionCapabilities(id){if(this.registeredCapabilityConnections.has(id))return[...this.connectionCapabilities.get(id)??[]]}connectionHas(id,capability){return id===void 0?!1:this.connectionCapabilities.get(id)?.has(capability)??!1}targets(sessionId,targetId,isTargeted,recordType){return isTargeted?[targetId]:typeof recordType=="string"&&BROADCAST_LIFECYCLE_RECORDS.has(recordType)?this.connections.size>0?[...this.connections.keys()]:[void 0]:this.connections.size>0?[...this.connections.keys()].filter(id=>this.connectionSessions.get(id)?.has(sessionId)):[void 0]}get(id){return this.connections.get(id)}values(){return this.connections.values()}isEmpty(){return this.connections.size===0}broadcast(line){for(let{actor}of this.connections.values())actor.enqueue(line)}connectionIds(){return[...this.connections.keys()]}attachedConnections(sessionId){return[...this.connections.keys()].filter(id=>this.connectionSessions.get(id)?.has(sessionId))}pendingQuestionCount(sessionId){return this.pendingQuestions.get(sessionId)?.size??0}deliverToSession(sessionId,line){for(let[id,{actor}]of this.connections)this.connectionSessions.get(id)?.has(sessionId)&&actor.enqueue(line)}rememberSnapshot(sessionId,value,line,placeholderLine,source){if(value.type==="session_lifecycle"){this.lifecycleSnapshots.set(sessionId,{line,placeholderLine,source});return}if(value.type==="extension_ui_request"&&value.method==="question"&&typeof value.id=="string"){let pending=this.pendingQuestions.get(sessionId)??new Map;pending.set(value.id,{...value}),this.pendingQuestions.set(sessionId,pending);return}if(value.type==="question_resolved"&&typeof value.id=="string"){this.pendingQuestions.get(sessionId)?.delete(value.id);return}if(value.type==="question_updated"&&typeof value.id=="string"){let frame=this.pendingQuestions.get(sessionId)?.get(value.id);frame&&Object.assign(frame,{deadlineAtMs:value.deadlineAtMs,remainingMs:value.remainingMs});return}let event=value.assistantMessageEvent,record={line,placeholderLine,source};value.type==="message_start"||value.type==="message_update"&&event?.type==="text_start"?this.sessionSnapshots.set(sessionId,[record]):this.sessionSnapshots.has(sessionId)&&this.sessionSnapshots.get(sessionId)?.push(record),value.type==="message_end"&&this.sessionSnapshots.delete(sessionId)}forgetSession(sessionId){this.pendingQuestions.delete(sessionId),this.sessionSnapshots.delete(sessionId),this.lifecycleSnapshots.delete(sessionId)}replaySnapshot(id,sessionId){let actor=this.connections.get(id)?.actor;if(!actor)return;let placeholders=this.connectionHas(id,MEDIA_PLACEHOLDERS_CAPABILITY),lifecycle=this.lifecycleSnapshots.get(sessionId);lifecycle&&actor.enqueue(placeholders?snapshotPlaceholderLine(lifecycle):lifecycle.line);for(let record of this.sessionSnapshots.get(sessionId)??[])actor.enqueue(placeholders?snapshotPlaceholderLine(record):record.line)}resyncSnapshot(id,reason,attached){let placeholders=this.connectionHas(id,MEDIA_PLACEHOLDERS_CAPABILITY),current=this.connectionSessions.get(id)??new Set;return[...new Set([...attached,...current])].map(sessionId=>{let latest=(this.sessionSnapshots.get(sessionId)??[]).findLast(record2=>record2.source?.message!=null),frozen=latest===void 0?void 0:JSON.parse(latest.line),record={type:"resync_required",sessionId,reason,message:typeof frozen=="object"&&frozen!==null&&"message"in frozen?frozen.message:null,...current.has(sessionId)?{}:{closed:!0}};return placeholders?omitInlineMedia(record):record})}};var SessionOpenTurns=class{constructor(){this.open=new Map}note(sessionId,type){if(type==="agent_start")this.open.set(sessionId,(this.open.get(sessionId)??0)+1);else if(type==="agent_settled"){let left=(this.open.get(sessionId)??0)-1;left>0?this.open.set(sessionId,left):this.open.delete(sessionId)}}take(sessionId){let owed=this.open.get(sessionId)??0;return this.open.delete(sessionId),owed}};var SessionRecordAccounting=class{constructor(){this.count=0,this.bytes=0}append(queue,value,key,resolve,reject){let line=serializeJsonLine(value),byteLength=Buffer.byteLength(line),node={value,line,byteLength,key,previous:queue.tail,resolve,reject};return queue.tail?queue.tail.next=node:queue.head=node,queue.tail=node,this.count++,this.bytes+=byteLength,node}replace(node,value){let line=serializeJsonLine(value),byteLength=Buffer.byteLength(line);this.bytes+=byteLength-node.byteLength,node.value=value,node.line=line,node.byteLength=byteLength}unlink(queue,node){node.previous?node.previous.next=node.next:queue.head=node.next,node.next?node.next.previous=node.previous:queue.tail=node.previous,node.key&&queue.latestByKey.get(node.key)===node&&queue.latestByKey.delete(node.key),node.previous=void 0,node.next=void 0,this.count--,this.bytes-=node.byteLength}};function liveStatus(runtime,fanout){return fanout.pendingQuestions>0||runtime.pendingQuestions>0?"waiting_input":runtime.isCompacting?"compacting":runtime.isStreaming?"running":"idle"}function statusOf(entry,runtime,fanout,terminal){if(terminal!==void 0)return terminal.status;switch(entry.state){case"opening":return"opening";case"closing":case"quarantined":case"closed":return"closed";case"open":return runtime===void 0?"opening":liveStatus(runtime,fanout);default:{let exhaustive=entry.state;throw new Error(`unexpected session state ${String(exhaustive)}`)}}}function deriveSessionStatus(entry,runtime,fanout,terminal,cause){let status=statusOf(entry,runtime,fanout,terminal),reason=terminal?.reason??(status==="idle"&&cause==="abort"?"aborted":void 0);return{status,...reason!==void 0?{reason}:{},kind:entry.kind,cwd:entry.cwd,attachments:Math.max(0,entry.attachments),...entry.durableSessionId!==void 0?{durableSessionId:entry.durableSessionId}:{},...entry.sessionPath!==void 0?{sessionPath:entry.sessionPath}:{},...entry.name!==void 0?{name:entry.name}:{},...runtime?.model!==void 0?{model:{provider:runtime.model.provider,id:runtime.model.id}}:{},...runtime?.thinkingLevel!==void 0?{thinkingLevel:runtime.thinkingLevel}:{},pendingQueue:{steering:runtime?.steering??0,followUp:runtime?.followUp??0},context:entry.context}}function terminalOfLifecycle(record){let reason=typeof record.reason=="string"?record.reason:void 0;if(record.type==="session_parked")return{status:"unloaded",reason:reason==="unloaded"?"unload_session":"idle"};if(record.type==="session_closed")return reason==="handoff_parked"?{status:"unloaded",reason:"handoff"}:reason===void 0?{status:"closed"}:{status:"closed",reason}}function causeOfRecord(record){switch(record.type){case"agent_start":return"prompt";case"agent_end":return"agent_end";case"agent_idle":return"agent_idle";case"session_abort":return"abort";case"queue_update":return"queue";case"model_changed":case"thinking_level_changed":return"set_model";case"session_replaced":return"replace";case"session_parked":return"park";case"session_closed":return"close";case"question_resolved":return"question";case"extension_ui_request":return record.method==="question"?"question":void 0;case"compaction_start":case"compaction_end":return"compaction";default:return}}function sessionOpenedLines(record){let{context,...plain}=record;return{plain:serializeJsonLine(plain),withContext:serializeJsonLine(context===void 0?plain:{...plain,context})}}var RECORD_TYPE_PATTERN=/^[a-z][a-z0-9_]{0,63}$/;function refusal(reason,action){return{refusal:{reason,action}}}function isRecord(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function parseSelector(value){if(value===void 0||value==="all")return{selector:{kind:"all"}};let action='send sessions: "all", { ids: [<sessionId | durableSessionId | sessionPath>] } or { childrenOf: <durableSessionId> }';if(!isRecord(value))return refusal('sessions must be "all" or an object',action);if("ids"in value&&!("childrenOf"in value)){let ids=value.ids;return!Array.isArray(ids)||ids.length===0||ids.length>1024?refusal("sessions.ids must be a non-empty array of at most 1024 strings",action):ids.every(id=>typeof id=="string"&&id.length>0)?{selector:{kind:"ids",ids:new Set(ids)}}:refusal("every sessions.ids entry must be a non-empty string",action)}if("childrenOf"in value&&!("ids"in value)){let parent=value.childrenOf;return typeof parent!="string"||parent.length===0?refusal("sessions.childrenOf must be a non-empty durable session id",action):{selector:{kind:"childrenOf",parent}}}return refusal("sessions must name exactly one of ids or childrenOf",action)}function parseMirror(value){if(value===void 0||value==="none")return{mirror:{kind:"none"}};if(value==="all")return{mirror:{kind:"all"}};let action='send mirror: "none", "all" or an array of record types such as ["message_update"]';return!Array.isArray(value)||value.length===0||value.length>64?refusal('mirror must be "none", "all" or a non-empty array of at most 64 record types',action):value.every(type=>typeof type=="string"&&RECORD_TYPE_PATTERN.test(type))?{mirror:{kind:"types",types:new Set(value)}}:refusal(`every mirror entry must be a record type matching ${RECORD_TYPE_PATTERN.source}`,action)}function optionalBoolean(value,field){return value===void 0||typeof value=="boolean"?value:{reason:`${field} must be a boolean`,action:`send ${field}: true or false, or omit it`}}function parseSubscription(request){let selector=parseSelector(request.sessions);if("refusal"in selector)return selector;let mirror=parseMirror(request.mirror);if("refusal"in mirror)return mirror;let includeWorkers=optionalBoolean(request.include_workers,"include_workers");if(typeof includeWorkers=="object")return{refusal:includeWorkers};let includeContext=optionalBoolean(request.include_context,"include_context");if(typeof includeContext=="object")return{refusal:includeContext};let effects=optionalBoolean(request.effects,"effects");return typeof effects=="object"?{refusal:effects}:{options:{selector:selector.selector,includeWorkers:includeWorkers??selector.selector.kind==="childrenOf",includeContext:includeContext??!1,mirror:mirror.mirror,effects:effects??selector.selector.kind==="childrenOf"}}}function subscriptionMatches(options,sessionId,entry){if(entry.kind==="worker"&&!options.includeWorkers)return!1;let selector=options.selector;switch(selector.kind){case"all":return!0;case"ids":return selector.ids.has(sessionId)||entry.durableSessionId!==void 0&&selector.ids.has(entry.durableSessionId)||entry.sessionPath!==void 0&&selector.ids.has(entry.sessionPath);case"childrenOf":{let lineage=entry.openContext??entry.context;return(lineage.parent_session_id??lineage.root_session_id)===selector.parent}default:{let exhaustive=selector;throw new Error(`unexpected selector ${String(exhaustive)}`)}}}function mirrorsRecord(mirror,recordType){switch(mirror.kind){case"none":return!1;case"all":return!0;case"types":return mirror.types.has(recordType);default:{let exhaustive=mirror;throw new Error(`unexpected mirror ${String(exhaustive)}`)}}}var MAX_SUBSCRIPTIONS_PER_CONNECTION=64,SessionStatusStream=class{constructor(delivery){this.subscriptions=new Map,this.pendings=new Map,this.terminals=new Map,this.lastViews=new Map,this.lastChanges=new Map,this.nextSubscription=0,this.delivery=delivery}setSource(source){this.source=source}setLastEffectProvider(provider){this.lastEffect=provider}get size(){return this.subscriptions.size}connectionSize(connectionId){let count=0;for(let pending of this.pendings.values())pending.connectionId===connectionId&&count++;for(let subscription of this.subscriptions.values())subscription.connectionId===connectionId&&count++;return count}subscribe(connectionId,options){let pending={id:`sub-${++this.nextSubscription}`,connectionId,options,seq:0,lines:[]};this.pendings.set(pending.id,pending);let snapshot=[];for(let sessionId of this.source?.sessionIds()??[]){let view=this.currentView(sessionId);if(view===void 0||!subscriptionMatches(options,sessionId,view.entry))continue;let derived=this.derive(sessionId,view);snapshot.push(this.recordFor(pending,sessionId,derived,this.lastChangeOf(sessionId)))}return{subscriptionId:pending.id,snapshot,seq:pending.seq}}activate(subscriptionId){let pending=this.pendings.get(subscriptionId);if(pending===void 0)return;this.pendings.delete(subscriptionId);let subscription={id:pending.id,connectionId:pending.connectionId,options:pending.options,seq:pending.seq};this.subscriptions.set(subscription.id,subscription);for(let line of pending.lines)this.delivery.enqueue(subscription.connectionId,line);pending.lines.length=0}unsubscribe(connectionId,subscriptionId){let pending=this.pendings.get(subscriptionId);return pending!==void 0?pending.connectionId!==connectionId?!1:(this.pendings.delete(subscriptionId),!0):this.subscriptions.get(subscriptionId)?.connectionId!==connectionId?!1:(this.subscriptions.delete(subscriptionId),!0)}dropConnection(connectionId){for(let[id,pending]of this.pendings)pending.connectionId===connectionId&&this.pendings.delete(id);for(let[id,subscription]of this.subscriptions)subscription.connectionId===connectionId&&this.subscriptions.delete(id)}publish(sessionId,cause,by,contextOnly=!1){let change={at:(this.delivery.now?.()??new Date).toISOString(),cause,by};if(this.lastChanges.set(sessionId,change),this.subscriptions.size===0&&this.pendings.size===0)return;let view=this.currentView(sessionId);if(view===void 0)return;let derived=this.derive(sessionId,view,cause);for(let pending of this.pendings.values())contextOnly&&!pending.options.includeContext||subscriptionMatches(pending.options,sessionId,view.entry)&&(pending.seq+=1,pending.lines.push(serializeJsonLine(this.recordFor(pending,sessionId,derived,change))));for(let subscription of this.subscriptions.values()){if(contextOnly&&!subscription.options.includeContext||!subscriptionMatches(subscription.options,sessionId,view.entry))continue;subscription.seq+=1;let record=this.recordFor(subscription,sessionId,derived,change);this.delivery.enqueue(subscription.connectionId,serializeJsonLine(record))||this.subscriptions.delete(subscription.id)}}publishEffect(sessionId,status){if(this.subscriptions.size===0&&this.pendings.size===0)return;let view=this.currentView(sessionId);if(view===void 0)return;let at=status.updatedAt??status.acceptedAt??(this.delivery.now?.()??new Date).toISOString();for(let pending of this.pendings.values())!pending.options.effects||!subscriptionMatches(pending.options,sessionId,view.entry)||(pending.seq+=1,pending.lines.push(serializeJsonLine(effectStatusRecord(pending,sessionId,status,at))));for(let subscription of this.subscriptions.values()){if(!subscription.options.effects||!subscriptionMatches(subscription.options,sessionId,view.entry))continue;subscription.seq+=1;let line=serializeJsonLine(effectStatusRecord(subscription,sessionId,status,at));this.delivery.enqueue(subscription.connectionId,line)||this.subscriptions.delete(subscription.id)}}seal(sessionId,terminal,cause,by){this.terminals.set(sessionId,terminal),this.publish(sessionId,cause,by)}forget(sessionId){this.terminals.delete(sessionId),this.lastViews.delete(sessionId),this.lastChanges.delete(sessionId)}mirrorTargets(sessionId,recordType){let subscriptions=[...this.pendings.values(),...this.subscriptions.values()].filter(subscription=>mirrorsRecord(subscription.options.mirror,recordType));if(subscriptions.length===0)return[];let entry=this.source?.entryView(sessionId)??this.lastViews.get(sessionId)?.entry;if(entry===void 0)return[];let targets=new Set;for(let subscription of subscriptions)subscriptionMatches(subscription.options,sessionId,entry)&&targets.add(subscription.connectionId);return[...targets]}matchingConnections(sessionId){let view=this.currentView(sessionId),matches=new Map;if(view===void 0)return matches;for(let subscription of this.subscriptions.values())subscriptionMatches(subscription.options,sessionId,view.entry)&&matches.set(subscription.connectionId,(matches.get(subscription.connectionId)??!1)||subscription.options.includeContext);return matches}currentView(sessionId){let live=this.source?.view(sessionId);return live!==void 0&&this.lastViews.set(sessionId,live),live??this.lastViews.get(sessionId)}derive(sessionId,view,cause){return deriveSessionStatus(view.entry,view.runtime,{pendingQuestions:this.delivery.pendingQuestions(sessionId)},this.terminals.get(sessionId),cause)}lastChangeOf(sessionId){return this.lastChanges.get(sessionId)??{at:(this.delivery.now?.()??new Date).toISOString(),cause:"open",by:"host"}}recordFor(subscription,sessionId,derived,lastChange){let{context,...rest}=derived,lastEffect=this.lastEffect?.(sessionId);return{type:"session_status",subscriptionId:subscription.id,seq:subscription.seq,sessionId,...rest,...subscription.options.includeContext?{context}:{},...lastEffect!==void 0?{lastEffect}:{},lastChange}}};var SurfaceDigestDedupe=class{constructor(){this.connections=new Map}announce(connectionId,sessionId,previous,digest){let sessions=this.connections.get(connectionId);if(sessions===void 0&&(sessions=new Map,this.connections.set(connectionId,sessions)),previous===digest)return!1;let tell=!0;for(let[other,announced]of sessions)if(!(other===sessionId||announced.last!==digest)&&announced.previous===previous){tell=!1;break}return sessions.set(sessionId,{last:digest,previous}),tell}dropConnection(connectionId){this.connections.delete(connectionId)}forgetSession(sessionId){for(let sessions of this.connections.values())sessions.delete(sessionId)}};var settleActors=actors=>Promise.all(actors.map(actor=>actor.flush().catch(()=>{}))).then(()=>{}),acceptActors=actors=>Promise.all(actors.map(actor=>actor.waitForAcceptance())).then(()=>{}),MAX_SHARED_STDIO_QUEUE_BYTES=64*1024*1024,MAX_SHARED_STDIO_QUEUE_RECORDS=4096,MESSAGE_KEY="message",COMPACT_DELTA_TYPES=new Set(["text_delta","thinking_delta","toolcall_delta"]);function compactDelta(value){if(value.type!=="message_update"||!Object.hasOwn(value,"message"))return;let event=value.assistantMessageEvent;if(typeof event!="object"||event===null)return;let typedEvent=event;if(!(typeof typedEvent.type!="string"||!COMPACT_DELTA_TYPES.has(typedEvent.type)||typeof typedEvent.contentIndex!="number"||typeof typedEvent.delta!="string"))return{type:typedEvent.type,contentIndex:typedEvent.contentIndex,delta:typedEvent.delta}}function demoteToDeltaOnly(value){let event=value.assistantMessageEvent;return{...value,message:null,assistantMessageEvent:{...event,partial:null}}}function toolUpdateKey(value){return value.type==="tool_execution_update"&&typeof value.toolCallId=="string"?`tool:${value.toolCallId}`:void 0}var SessionEventWriter=class{get pendingCloseRecordCount(){return this.reservedCloseRecords}get pendingCloseByteLength(){return this.reservedCloseBytes}constructor(writeRaw,waitOrSchedule,scheduleFlush){this.records=new SessionRecordAccounting,this.queues=new Map,this.fanout=new SessionEventFanout,this.connectionContext=new AsyncLocalStorage,this.securityPresentations=new RpcSecurityPresentations(this.fanout,(id,task)=>this.withConnection(id,task),()=>this.requestFlush()),this.workerSecurityBridge=!1,this.controlQueue={latestByKey:new Map,ready:!1},this.readyQueues=[],this.sealedSessions=new Set,this.workerSessions=new Set,this.openTurns=new SessionOpenTurns,this.status=new SessionStatusStream({enqueue:(connectionId,line)=>{let actor=this.fanout.get(connectionId)?.actor;return actor===void 0?!1:(actor.enqueue(line),this.requestFlush(),!0)},pendingQuestions:sessionId=>this.fanout.pendingQuestionCount(sessionId)}),this.surfaceDedupe=new SurfaceDigestDedupe,this.mediaPersisters=new Map,this.flushScheduled=!1,this.controlOverflowReported=!1,this.closeOverflowReported=!1,this.closeOverflowActors=new WeakSet,this.reservedCloseRecords=0,this.reservedCloseBytes=0,this.fanout.onConnectionClosed=id=>this.securityPresentations.dropConnection(id),this.writeRaw=writeRaw,waitOrSchedule&&scheduleFlush===void 0&&waitOrSchedule.length>0?this.scheduleFlush=waitOrSchedule:(this.waitForBackpressure=waitOrSchedule,this.scheduleFlush=scheduleFlush??(flush=>queueMicrotask(()=>{flush()})))}get bufferedRecordCount(){return this.records.count+(this.inFlight?1:0)}get bufferedByteLength(){return this.records.bytes+(this.inFlight?.node.byteLength??0)}isSessionSealed(sessionId){return this.sealedSessions.has(sessionId)}registerConnection(id,connection,options={}){this.fanout.registerConnection(id,connection,options)}unregisterConnection(id){this.securityPresentations.dropConnection(id),this.fanout.unregisterConnection(id),this.status.dropConnection(id),this.surfaceDedupe.dropConnection(id)}attachConnectionToSession(id,sessionId){this.fanout.attachConnectionToSession(id,sessionId),this.hasConnection(id)&&this.securityPresentations.attach(id,sessionId)}detachConnectionFromSession(id,sessionId){this.fanout.detachConnectionFromSession(id,sessionId),this.securityPresentations.detach(id,sessionId)}hasConnection(id){return this.fanout.get(id)!==void 0}setConnectionCapabilities(id,capabilities){this.fanout.setConnectionCapabilities(id,capabilities),this.securityPresentations.capabilitiesChanged(id)}clearConnectionCapabilities(id){this.fanout.clearConnectionCapabilities(id),this.securityPresentations.capabilitiesChanged(id)}bindSecurityPresentations(sessionId,durableId,notices,ui){if(!this.fanout.isEmpty())return this.securityPresentations.bind(sessionId,durableId,notices,ui.select)}acceptWorkerSecuritySnapshot(sessionId,snapshot){this.securityPresentations.acceptSnapshot(sessionId,snapshot)}routeWorkerGrant(sessionId,requestId,preferred,capabilities,cancel){return this.securityPresentations.routeWorkerGrant(sessionId,requestId,preferred,capabilities,cancel)}acceptWorkerGrantResponse(sessionId,id,connection){return this.securityPresentations.acceptWorkerResponse(sessionId,id,connection)}settleGrantDialog(sessionId,id){this.securityPresentations.settleGrant(sessionId,id)}hasRegisteredConnectionCapabilities(id){return this.fanout.hasRegisteredConnectionCapabilities(id)}getConnectionCapabilities(id){return this.fanout.getConnectionCapabilities(id)}setSessionMedia(sessionId,persister){persister===void 0?this.mediaPersisters.delete(sessionId):this.mediaPersisters.set(sessionId,persister)}setSessionKind(sessionId,kind){kind==="worker"?this.workerSessions.add(sessionId):this.workerSessions.delete(sessionId)}async flushConnection(id){let target=this.fanout.get(id);target&&await settleActors([target.actor])}withConnection(id,task){return this.connectionContext.run(id,task)}currentConnection(){return this.connectionContext.getStore()}enqueue(sessionId,value){if(this.sealedSessions.has(sessionId))return!1;let targetId=this.connectionContext.getStore(),record=value,isTargeted=record.type==="response"||record.type==="bash_execution_update"||record.type==="host_request"||record.type==="relocation_due"||record.type==="extension_ui_request"&&["select","confirm","input","editor"].includes(String(record.method)),tagged={...value,sessionId},line=serializeJsonLine(tagged);if(this.fanout.isEmpty()&&this.exceedsStdioCapacity(line))return this.closeSession(sessionId,{type:"response",command:"close_session",success:!1,error:"session_output_overflow, resync required"},"error"),!1;this.openTurns.note(sessionId,record.type);let attached=this.fanout.targets(sessionId,targetId,isTargeted,record.type),mirrors=isTargeted?[]:this.status.mirrorTargets(sessionId,String(record.type)),targets=mirrors.length===0?attached:[...new Set([...attached,...mirrors])],redacted=targets.some(target=>this.fanout.connectionHas(target,MEDIA_PLACEHOLDERS_CAPABILITY))?omitInlineMedia(tagged,this.mediaPersisters.get(sessionId)):tagged,placeholderLine=redacted===tagged?void 0:serializeJsonLine(redacted);isTargeted||this.fanout.rememberSnapshot(sessionId,tagged,line,placeholderLine,tagged);for(let target of targets){if(target!==void 0&&!this.fanout.get(target))continue;let registered=target===void 0?void 0:this.fanout.get(target),wants=placeholderLine!==void 0&&this.fanout.connectionHas(target,MEDIA_PLACEHOLDERS_CAPABILITY);if(registered){let keyed=compactDelta(tagged)!==void 0&&tagged.message!==null;registered.actor.enqueue(wants?placeholderLine:line,keyed?MESSAGE_KEY:void 0,void 0,keyed?serializeJsonLine(demoteToDeltaOnly(tagged)):void 0,isCorrelatedReply(tagged))}else this.appendSessionRecord(sessionId,wants?redacted:tagged,target)}let cause=isTargeted?void 0:causeOfRecord(record);return cause!==void 0&&this.status.publish(sessionId,cause,this.changeActor("agent")),this.afterRecord?.(sessionId),this.requestFlush(),!0}changeActor(fallback){return this.connectionContext.getStore()===void 0?fallback:"client"}publishSessionOpened(record){if(this.fanout.isEmpty()){let{context:_context,...wire}=record;this.appendSessionRecord(record.sessionId,{...wire}),this.requestFlush();return}let lines=sessionOpenedLines(record),subscribers=this.status.matchingConnections(record.sessionId),recipients=record.kind==="worker"?new Set([...this.fanout.attachedConnections(record.sessionId),...subscribers.keys()]):new Set(this.fanout.connectionIds());for(let id of recipients)this.fanout.get(id)?.actor.enqueue(subscribers.get(id)===!0?lines.withContext:lines.plain);this.requestFlush()}publishLoadedSurfacesChanged(sessionId,previous,digest){if(this.fanout.isEmpty()){this.enqueue(sessionId,{type:"loaded_surfaces_changed"});return}if(this.sealedSessions.has(sessionId))return;let line=serializeJsonLine({type:"loaded_surfaces_changed",sessionId});for(let id of this.fanout.attachedConnections(sessionId))this.surfaceDedupe.announce(id,sessionId,previous,digest)&&this.fanout.get(id)?.actor.enqueue(line);this.requestFlush()}waitForSessionBackpressure(sessionId){return this.fanout.isEmpty()?this.flush():acceptActors(this.sessionActors(sessionId))}sessionActors(sessionId){return[...new Set([...this.fanout.targets(sessionId,this.currentConnection(),!1,void 0),this.currentConnection()])].flatMap(target=>{let registered=target===void 0?void 0:this.fanout.get(target);return registered?[registered.actor]:[]})}enqueueControl(value,targetOverride){if(this.failure!==void 0)return Promise.reject(this.failure);let targetId=targetOverride??this.connectionContext.getStore(),registered=targetId===void 0?void 0:this.fanout.get(targetId);if(registered)return registered.actor.enqueue(serializeJsonLine(value),void 0,void 0,void 0,isCorrelatedReply(value)),this.requestFlush(),Promise.resolve();if(this.exceedsStdioCapacity(serializeJsonLine(value)))return this.controlOverflowReported||(this.controlOverflowReported=!0,this.records.append(this.controlQueue,{type:"overflow",error:"rpc_control_output_overflow, resync required"}),this.markReady(this.controlQueue),this.requestFlush()),Promise.reject(new Error("rpc_control_output_overflow, resync required"));let queue=targetId===void 0?this.controlQueue:this.connectionQueue(targetId),completion=new Promise((resolve,reject)=>{this.records.append(queue,{...value},void 0,resolve,reject)});return this.markReady(queue),this.requestFlush(),completion}sendOpenQueued(connection,record){let wire={type:"queued",for_request:record.for_request,position:record.position,in_flight:record.in_flight},target=this.fanout.get(connection);target!==void 0&&(target.actor.enqueue(serializeJsonLine(wire)),this.requestFlush())}broadcastHostRecord(record){let wire;switch(record.type){case"host_superseded":wire={type:"host_superseded",instanceId:record.instanceId,generation:record.generation,successor:record.successor};break;case"host_stalled":wire={type:"host_stalled",driftMs:record.driftMs,...record.sessionId!==void 0?{sessionId:record.sessionId}:{},...record.tool!==void 0?{tool:record.tool}:{}};break;case"host_memory_pressure":wire={type:"host_memory_pressure",rssMb:record.rssMb,...record.footprintMb!==void 0?{footprintMb:record.footprintMb}:{},...record.measure!==void 0?{measure:record.measure}:{},sessions:record.sessions,...record.main!==void 0?{main:record.main}:{},...record.kernels!==void 0?{kernels:record.kernels}:{}};break;case"host_trimmed":wire={type:"host_trimmed",footprintBeforeMb:record.footprintBeforeMb,footprintAfterMb:record.footprintAfterMb,measure:record.measure,collected:record.collected,sessions:record.sessions,pauseMs:record.pauseMs};break;case"subscription_limits":wire={type:"subscription_limits",provider:record.provider,account:record.account,windows:record.windows,reportedAt:record.reportedAt,...record.plan===void 0?{}:{plan:record.plan},...record.error===void 0?{}:{error:record.error}};break;default:{let exhaustive=record;throw new Error(`unexpected host record ${exhaustive}`)}}this.fanout.isEmpty()?(this.records.append(this.controlQueue,wire),this.markReady(this.controlQueue)):this.fanout.broadcast(serializeJsonLine(wire)),this.requestFlush()}closeSession(sessionId,response,reason,sessionPath){if(!this.settleBeforeSeal(sessionId))return;this.sealedSessions.add(sessionId),this.securityPresentations.dropSession(sessionId),this.fanout.forgetSession(sessionId),this.mediaPersisters.delete(sessionId);let targetId=this.connectionContext.getStore(),lifecycle={type:"session_closed",sessionId,...reason&&{reason},...sessionPath&&{sessionPath}};this.fanout.isEmpty()?this.appendSessionRecord(sessionId,lifecycle):this.workerSessions.has(sessionId)?this.fanout.deliverToSession(sessionId,serializeJsonLine(lifecycle)):this.fanout.broadcast(serializeJsonLine(lifecycle)),this.sealStatus(sessionId,lifecycle);let taggedResponse={...response,sessionId},registered=targetId===void 0?void 0:this.fanout.get(targetId);registered?registered.actor.enqueue(serializeJsonLine(taggedResponse),void 0,void 0,void 0,isCorrelatedReply(response)):this.appendSessionRecord(sessionId,taggedResponse,targetId),this.requestFlush()}parkSession(sessionId,sessionPath,reason){this.sealLifecycle(sessionId,{type:"session_parked",sessionId,sessionPath,reason})}releaseSession(sessionId,sessionPath){this.sealLifecycle(sessionId,{type:"session_closed",sessionId,reason:"released",sessionPath})}sealRolledBackOpen(sessionId,sessionPath){this.sealLifecycle(sessionId,{type:"session_closed",sessionId,reason:"error",...sessionPath!==void 0&&{sessionPath}})}sealLifecycle(sessionId,lifecycle,attachedOnly=!1){this.settleBeforeSeal(sessionId)&&(this.sealedSessions.add(sessionId),this.securityPresentations.dropSession(sessionId),this.fanout.forgetSession(sessionId),this.mediaPersisters.delete(sessionId),this.fanout.isEmpty()?this.appendSessionRecord(sessionId,lifecycle):attachedOnly||this.workerSessions.has(sessionId)?this.fanout.deliverToSession(sessionId,serializeJsonLine(lifecycle)):this.fanout.broadcast(serializeJsonLine(lifecycle)),this.sealStatus(sessionId,lifecycle),this.requestFlush())}sealStatus(sessionId,lifecycle){let terminal=terminalOfLifecycle(lifecycle),cause=causeOfRecord(lifecycle);terminal!==void 0&&cause!==void 0&&this.status.seal(sessionId,terminal,cause,this.changeActor("host"))}reserveCloseResponse(sessionId,response,records=2){let bytes=Buffer.byteLength(serializeJsonLine({...response,sessionId}))+(records===2?Buffer.byteLength(serializeJsonLine({type:"session_closed",sessionId,reason:"client_close"})):0);if(this.bufferedRecordCount+this.reservedCloseRecords+records>MAX_SHARED_STDIO_QUEUE_RECORDS||this.bufferedByteLength+this.reservedCloseBytes+bytes>MAX_SHARED_STDIO_QUEUE_BYTES){let overflow={type:"overflow",command:"close_session",error:"rpc_close_output_overflow, resync required"},targetId=this.currentConnection();if(targetId!==void 0){let actor=this.fanout.get(targetId)?.actor;actor&&!this.closeOverflowActors.has(actor)&&(this.closeOverflowActors.add(actor),actor.enqueue(serializeJsonLine(overflow),void 0,()=>this.closeOverflowActors.delete(actor)))}else this.closeOverflowReported||(this.closeOverflowReported=!0,this.records.append(this.controlQueue,overflow),this.markReady(this.controlQueue),this.requestFlush());return}this.reservedCloseRecords+=records,this.reservedCloseBytes+=bytes;let active=!0,release=()=>{active&&(active=!1,this.reservedCloseRecords-=records,this.reservedCloseBytes-=bytes)};return{release,complete:(terminal,reason)=>{active&&(release(),terminal&&!this.sealedSessions.has(sessionId)?this.closeSession(sessionId,response,reason):this.appendClosedResponse(sessionId,response))}}}enqueueClosedResponse(sessionId,response){this.reserveCloseResponse(sessionId,response,1)?.complete(!1)}appendClosedResponse(sessionId,response){let targetId=this.connectionContext.getStore(),taggedResponse={...response,sessionId},registered=targetId===void 0?void 0:this.fanout.get(targetId);registered?registered.actor.enqueue(serializeJsonLine(taggedResponse),void 0,void 0,void 0,isCorrelatedReply(response)):this.appendSessionRecord(sessionId,taggedResponse,targetId),this.requestFlush()}settleBeforeSeal(sessionId){if(this.sealedSessions.has(sessionId))return!1;for(let owed=this.openTurns.take(sessionId);owed>0;owed-=1)this.enqueue(sessionId,{type:"agent_settled",reason:"session_closed"});return!this.sealedSessions.has(sessionId)}forgetSession(sessionId){this.sealedSessions.delete(sessionId),this.openTurns.take(sessionId),this.workerSessions.delete(sessionId),this.fanout.forgetSession(sessionId),this.mediaPersisters.delete(sessionId),this.status.forget(sessionId),this.surfaceDedupe.forgetSession(sessionId)}flush(){if(this.failure!==void 0)return Promise.reject(this.failure);if(this.flushScheduled=!1,this.drainPromise)return this.drainPromise;if(this.readyQueues.length===0)return settleActors([...this.fanout.values()].map(({actor})=>actor));let resolveDrain,rejectDrain,drain=new Promise((resolve,reject)=>{resolveDrain=resolve,rejectDrain=reject});return this.drainPromise=drain,this.drainUntilEmpty().then(resolveDrain,rejectDrain),drain.then(()=>{this.drainPromise===drain&&(this.drainPromise=void 0),this.readyQueues.length>0&&this.requestFlush()},cause=>{this.drainPromise===drain&&(this.drainPromise=void 0),this.fail(cause)}),drain}async drainUntilEmpty(){do await this.drainReadyQueues();while(this.readyQueues.length>0);await settleActors([...this.fanout.values()].map(({actor})=>actor)),this.controlOverflowReported=!1,this.reservedCloseRecords===0&&(this.closeOverflowReported=!1)}async drainReadyQueues(){for(;this.readyQueues.length>0;){let queue=this.readyQueues.shift();queue.ready=!1;let node=queue.head;if(node){this.records.unlink(queue,node),this.inFlight={queue,node};try{let connection=queue.targetId?this.fanout.get(queue.targetId)?.connection:void 0,writeRaw=connection?.writeRaw??this.writeRaw,waitForBackpressure=connection?.waitForBackpressure??this.waitForBackpressure;!connection&&queue.targetId||(writeRaw(node.line),waitForBackpressure&&await waitForBackpressure()),node.resolve?.()}catch(cause){throw node.reject?.(cause),cause}finally{this.inFlight=void 0}if(queue.head)this.markReady(queue);else if(queue.sessionId||queue.targetId)for(let[key,candidate]of this.queues)candidate===queue&&this.queues.delete(key)}}}exceedsStdioCapacity(line){return this.bufferedRecordCount+this.reservedCloseRecords>=MAX_SHARED_STDIO_QUEUE_RECORDS||this.bufferedByteLength+this.reservedCloseBytes+Buffer.byteLength(line)>MAX_SHARED_STDIO_QUEUE_BYTES}connectionQueue(targetId){let key=`connection:${targetId}`,queue=this.queues.get(key);return queue||(queue={targetId,latestByKey:new Map,ready:!1},this.queues.set(key,queue)),queue}appendSessionRecord(sessionId,value,targetId){let key=`${targetId??"default"}:${sessionId}`,queue=this.queues.get(key);if(queue||(queue={sessionId,targetId,latestByKey:new Map,ready:!1},this.queues.set(key,queue)),compactDelta(value)){let previousFull=queue.latestByKey.get(MESSAGE_KEY);previousFull&&this.demoteAndMerge(queue,previousFull);let node=this.records.append(queue,value,MESSAGE_KEY);queue.latestByKey.set(MESSAGE_KEY,node),this.markReady(queue);return}let toolKey=toolUpdateKey(value);if(toolKey){let previous=queue.latestByKey.get(toolKey);previous&&this.records.unlink(queue,previous);let node=this.records.append(queue,value,toolKey);queue.latestByKey.set(toolKey,node),this.markReady(queue);return}queue.latestByKey.clear(),this.records.append(queue,value),this.markReady(queue)}demoteAndMerge(queue,node){this.records.replace(node,demoteToDeltaOnly(node.value));let current=compactDelta(node.value),preceding=node.previous,previous=preceding?compactDelta(preceding.value):void 0;if(preceding&&previous&&current&&preceding.value.message===null&&previous.type===current.type&&previous.contentIndex===current.contentIndex){let precedingEvent=preceding.value.assistantMessageEvent;this.records.replace(preceding,{...preceding.value,assistantMessageEvent:{...precedingEvent,delta:previous.delta+current.delta}}),this.records.unlink(queue,node)}}markReady(queue){queue.ready||this.inFlight?.queue===queue||!queue.head||(queue.ready=!0,this.readyQueues.push(queue))}requestFlush(){this.failure!==void 0||this.flushScheduled||this.drainPromise||(this.flushScheduled=!0,this.scheduleFlush(()=>this.flush()))}fail(cause){if(this.failure===void 0){this.failure=cause;for(let queue of this.allQueues())for(let node=queue.head;node;node=node.next)node.reject?.(cause)}}*allQueues(){yield*this.queues.values(),yield this.controlQueue}};function socketSink(socket){let needsDrain=!1,cut=!1;return{writeRaw(chunk){!cut&&!socket.destroyed&&(needsDrain=!socket.write(chunk))},close(){if(cut||(cut=!0,socket.destroyed))return;socket.end();let grace=setTimeout(()=>socket.destroy(),5e3);grace.unref(),socket.once("close",()=>clearTimeout(grace))},waitForBackpressure(){return socket.destroyed||!needsDrain?Promise.resolve():(needsDrain=!1,new Promise(resolve=>{let done=()=>{socket.off("drain",done),socket.off("close",done),resolve()};socket.once("drain",done),socket.once("close",done)}))}}}var SESSION_CONTEXT_PATCH_LIMITS={keyPattern:/^[a-z][a-z0-9_]{0,63}$/,valueChars:256,overlayBytes:4096},RESERVED_SESSION_CONTEXT_KEYS=new Set(["host_socket","host_instance","shard_key","tree_key","parent_session_id","root_session_id"]);function refuse(reason,action){return{refusal:{reason,action}}}function applyContextPatch(overlay,patch){let shape='send patch as an object of lowercase keys to string, number, boolean or null values, e.g. { "task_status": "running" }';if(typeof patch!="object"||patch===null||Array.isArray(patch))return refuse("patch must be an object",shape);let entries=Object.entries(patch);if(entries.length===0)return refuse("patch has no keys",shape);let merged={...overlay};for(let[key,value]of entries){if(!SESSION_CONTEXT_PATCH_LIMITS.keyPattern.test(key))return refuse(`key "${key}" must match ${SESSION_CONTEXT_PATCH_LIMITS.keyPattern.source}`,"rename the key to lowercase letters, digits and underscores");if(RESERVED_SESSION_CONTEXT_KEYS.has(key))return refuse(`key "${key}" is set by the host and cannot be patched`,"drop that key from the patch; the host keeps it current");if(typeof value=="string"&&value.length>SESSION_CONTEXT_PATCH_LIMITS.valueChars)return refuse(`value for "${key}" is ${value.length} characters, at most ${SESSION_CONTEXT_PATCH_LIMITS.valueChars} are accepted`,"shorten the value or store it elsewhere and publish a reference");if(typeof value=="number"&&!Number.isFinite(value))return refuse(`value for "${key}" must be a finite number`,shape);if(value!==null&&typeof value!="string"&&typeof value!="number"&&typeof value!="boolean")return refuse(`value for "${key}" must be a string, number, boolean or null`,shape);merged[key]=value}let bytes=Buffer.byteLength(JSON.stringify(merged));return bytes>SESSION_CONTEXT_PATCH_LIMITS.overlayBytes?refuse(`patched context would be ${bytes} bytes, at most ${SESSION_CONTEXT_PATCH_LIMITS.overlayBytes} are accepted`,"remove keys with null or shorten values before adding more"):{overlay:merged}}function publishedSessionContext(base,overlay){if(overlay===void 0)return base;let published={...base};for(let[key,value]of Object.entries(overlay))value===null?delete published[key]:published[key]=value;return published}function modelOf(model){return model===void 0?void 0:{provider:model.provider,id:model.id}}function sessionRuntimeView(session,pendingQuestions){let model=modelOf(session.model);return{isStreaming:session.isStreaming===!0,isCompacting:session.isCompacting===!0,...model!==void 0?{model}:{},...session.thinkingLevel!==void 0?{thinkingLevel:session.thinkingLevel}:{},steering:typeof session.getSteeringMessages=="function"?session.getSteeringMessages().length:0,followUp:typeof session.getFollowUpMessages=="function"?session.getFollowUpMessages().length:0,pendingQuestions:pendingQuestions??sessionQuestionBridges.get(session)?.pendingQuestions()?.length??0}}function runtimeView(entry){let session=entry.runtime?.session;if(session!==void 0)return sessionRuntimeView(session);let state=entry.worker?.snapshot?.state;if(state===void 0)return;let model=modelOf(state.model??void 0);return{isStreaming:state.isStreaming,isCompacting:state.isCompacting,...model!==void 0?{model}:{},...state.thinkingLevel!==void 0?{thinkingLevel:state.thinkingLevel}:{},steering:state.steering.length,followUp:state.followUp.length,pendingQuestions:state.pendingQuestions?.length??0}}function entryView(entry){let session=entry.runtime?.session,state=entry.worker?.snapshot?.state,durableSessionId=session?.sessionId??state?.sessionId??entry.durableSessionId,sessionPath=session?.sessionFile??state?.sessionFile??entry.sessionPath,name=session?.sessionManager.getSessionName()??state?.sessionName;return{state:entry.state,kind:entry.kind,cwd:entry.cwd,attachments:entry.attachments,...durableSessionId!==void 0?{durableSessionId}:{},...sessionPath!==void 0?{sessionPath}:{},...name!==void 0?{name}:{},context:publishedSessionContext(entry.context,entry.contextOverlay),openContext:entry.context}}function registryStatusSource(registry){return{sessionIds:()=>registry.list().map(row=>row.sessionId),entryView:sessionId=>{let entry=registry.peek(sessionId);return entry===void 0?void 0:entryView(entry)},view:sessionId=>{let entry=registry.peek(sessionId);if(entry===void 0)return;let runtime=runtimeView(entry);return runtime===void 0?{entry:entryView(entry)}:{entry:entryView(entry),runtime}}}}function refusal2(id,command,code,detail){return{id,type:"response",command,success:!1,error:`${code}: ${detail.reason}`,errorCode:code,errorData:{reason:detail.reason,action:detail.action}}}function answerSubscribeSessions(command,connectionId,stream){if(connectionId===void 0)return{answer:refusal2(command.id,command.type,RPC_ERROR_UNSUPPORTED_TRANSPORT,{reason:"stdio has no per-connection identity to deliver a subscription to",action:"connect to the host socket (senpi --mode rpc --listen) and subscribe there"})};if(stream.connectionSize(connectionId)>=MAX_SUBSCRIPTIONS_PER_CONNECTION)return{answer:refusal2(command.id,command.type,RPC_ERROR_SUBSCRIPTION_LIMIT,{reason:`this connection already holds ${MAX_SUBSCRIPTIONS_PER_CONNECTION} subscriptions`,action:"unsubscribe_sessions the ones you no longer read, then subscribe again"})};let parsed=parseSubscription(command);if("refusal"in parsed)return{answer:refusal2(command.id,command.type,RPC_ERROR_INVALID_SUBSCRIPTION,parsed.refusal)};let subscribed=stream.subscribe(connectionId,parsed.options);return{answer:{id:command.id,type:"response",command:"subscribe_sessions",success:!0,data:{subscriptionId:subscribed.subscriptionId,snapshot:subscribed.snapshot,seq:subscribed.seq,hostGeneration:String(hostGeneration(process.env)),hostInstanceId:hostInstanceId()}},activate:subscribed.subscriptionId}}function answerUnsubscribeSessions(command,connectionId,stream){return connectionId===void 0?refusal2(command.id,command.type,RPC_ERROR_UNSUPPORTED_TRANSPORT,{reason:"stdio has no per-connection identity holding any subscription",action:"connect to the host socket (senpi --mode rpc --listen) and manage subscriptions there"}):typeof command.subscriptionId=="string"&&stream.unsubscribe(connectionId,command.subscriptionId)?{id:command.id,type:"response",command:"unsubscribe_sessions",success:!0}:refusal2(command.id,command.type,RPC_ERROR_UNKNOWN_SUBSCRIPTION,{reason:"this connection holds no subscription with that id",action:"send the subscriptionId your subscribe_sessions answer returned, on the same connection"})}function notAttachedRefusal(command){return refusal2(command.id,command.type,RPC_ERROR_SESSION_NOT_ATTACHED,{reason:"this connection is not attached to that session",action:"attach first with open_session { sessionPath }, then patch its context"})}function answerSetSessionContext(command,entry,stream){let applied=applyContextPatch(entry.contextOverlay,command.patch);return"refusal"in applied?refusal2(command.id,command.type,RPC_ERROR_INVALID_SESSION_CONTEXT,applied.refusal):(entry.contextOverlay=applied.overlay,stream.publish(command.sessionId,"context","client",!0),{id:command.id,type:"response",command:"set_session_context",success:!0,data:{context:publishedSessionContext(entry.context,entry.contextOverlay)}})}export{applyContextPatch,publishedSessionContext,recordLoopBlockedMs,sessionRuntimeView,registryStatusSource,refusal2 as refusal,answerSubscribeSessions,answerUnsubscribeSessions,notAttachedRefusal,answerSetSessionContext,SessionEventWriter,socketSink};
@@ -1,4 +0,0 @@
1
- import { createRequire as __piCreateRequire } from "node:module"; const require = __piCreateRequire(import.meta.url); { const __piWorkerThreads = require("node:worker_threads"); if (typeof __piWorkerThreads.markAsUncloneable !== "function") { __piWorkerThreads.markAsUncloneable = () => {}; } }
2
- import{redactResolvedReferenceValues}from"./chunk-DLAY7CIU.js";import{getAgentDir}from"./chunk-7YO7VQZW.js";var McpError=class extends Error{constructor(kind,name,message,options={}){super(message),this.name=name,this.kind=kind,this.phase=options.phase,this.retriable=options.retriable,this.serverName=options.serverName,options.cause!==void 0&&Object.defineProperty(this,"cause",{configurable:!0,value:options.cause,writable:!1}),Object.setPrototypeOf(this,new.target.prototype)}},ConnectError=class extends McpError{constructor(message,options={}){super("connect","ConnectError",message,options)}};var ToolExecError=class extends McpError{constructor(message,options={}){super("tool_exec","ToolExecError",message,options)}},AuthError=class extends McpError{constructor(message,options={}){super("auth","AuthError",message,options)}},TimeoutError=class extends McpError{constructor(message,options={}){super("timeout","TimeoutError",message,options)}},SessionExpiredError=class extends McpError{constructor(message,options={}){super("session_expired","SessionExpiredError",message,options)}},RETRIABLE_STATUS_CODES=new Set([404,502,503]),RETRIABLE_NUMERIC_CODES=new Set([-32001]),RETRIABLE_TEXT=["econnrefused","connection refused","transport closed"];function isRetriableMcpError(error){if(error instanceof McpError&&error.retriable===!0||isMcpSessionExpiredError(error)||hasRetriableNumericSignal(error,new Set))return!0;let text=collectText(error,new Set).join(" ").toLowerCase();return RETRIABLE_TEXT.some(needle=>text.includes(needle))?!0:/\b(?:404|502|503)\b/.test(text)}function isMcpSessionExpiredError(error){if(hasSessionExpiredNumericSignal(error,new Set))return!0;let text=collectText(error,new Set).join(" ").toLowerCase();return/\b404\b/.test(text)||text.includes("-32000")&&text.includes("session")||text.includes("session expired")||text.includes("session not found")||text.includes("mcp-session-id")}function hasRetriableNumericSignal(value,seen){if(typeof value!="object"||value===null||seen.has(value))return!1;seen.add(value);let code=numberFromUnknown(getProperty(value,"code"));if(code!==void 0&&RETRIABLE_NUMERIC_CODES.has(code))return!0;let status=numberFromUnknown(getProperty(value,"status"));if(status!==void 0&&RETRIABLE_STATUS_CODES.has(status))return!0;let statusCode=numberFromUnknown(getProperty(value,"statusCode"));return statusCode!==void 0&&RETRIABLE_STATUS_CODES.has(statusCode)?!0:hasRetriableNumericSignal(getProperty(value,"response"),seen)||hasRetriableNumericSignal(getProperty(value,"cause"),seen)}function hasSessionExpiredNumericSignal(value,seen){if(typeof value!="object"||value===null||seen.has(value))return!1;seen.add(value);let code=numberFromUnknown(getProperty(value,"code"));if(code===-32001)return!0;if(code===-32e3){let message=getProperty(value,"message");if(typeof message=="string"&&message.toLowerCase().includes("session"))return!0}return numberFromUnknown(getProperty(value,"status"))===404||numberFromUnknown(getProperty(value,"statusCode"))===404?!0:hasSessionExpiredNumericSignal(getProperty(value,"response"),seen)||hasSessionExpiredNumericSignal(getProperty(value,"cause"),seen)}function collectText(value,seen){if(typeof value=="string")return[value];if(typeof value=="number")return[String(value)];if(typeof value!="object"||value===null)return[];if(seen.has(value))return[];seen.add(value);let parts=[];value instanceof Error&&parts.push(value.message,value.name);for(let key of["message","code","status","statusCode"]){let property=getProperty(value,key);(typeof property=="string"||typeof property=="number")&&parts.push(String(property))}return parts.push(...collectText(getProperty(value,"response"),seen)),parts.push(...collectText(getProperty(value,"cause"),seen)),parts}function numberFromUnknown(value){if(typeof value=="number")return value;if(typeof value!="string"||value.trim()==="")return;let parsed=Number(value);return Number.isFinite(parsed)?parsed:void 0}function getProperty(value,key){if(!(typeof value!="object"||value===null))return value[key]}function memoizeLoader(load){let loaded,loading;return()=>loaded!==void 0?Promise.resolve(loaded):(loading??=load().then(module=>(loaded=module,module)).finally(()=>{loading=void 0}),loading)}var loadMcpSdkClient=memoizeLoader(()=>import("./client-KWTLTKVV.js")),loadMcpSdkStdioTransport=memoizeLoader(()=>import("./stdio-IEC635SY.js")),loadMcpSdkTypes=memoizeLoader(()=>import("./types-LLNV23LW.js")),authModule,loadMcpSdkAuth=memoizeLoader(async()=>{let module=await import("./auth-4NUNUSTF.js");return authModule=module,module}),loadMcpSdkStreamableHttpTransport=memoizeLoader(async()=>{let[module]=await Promise.all([import("./streamableHttp-KJI5KE4K.js"),loadMcpSdkAuth()]);return module});function isMcpSdkUnauthorizedError(error){return authModule!==void 0&&error instanceof authModule.UnauthorizedError}import{chmodSync,closeSync,openSync,renameSync,rmSync,statSync}from"node:fs";var LOG_SINK_RETRY_MS=5e3,ROTATION_LOCK_STALE_MS=1e4;function errorCode(error){return typeof error=="object"&&error!==null&&"code"in error&&typeof error.code=="string"?error.code:void 0}function exceedsCap(filePath,incomingBytes,maxBytes){try{return statSync(filePath).size+incomingBytes>maxBytes}catch(error){if(errorCode(error)==="ENOENT")return!1;throw error}}function clearStaleLock(lockPath){try{Date.now()-statSync(lockPath).mtimeMs>ROTATION_LOCK_STALE_MS&&rmSync(lockPath,{force:!0})}catch(error){if(errorCode(error)!=="ENOENT")throw error}}function rotateLogIfNeeded(filePath,incomingBytes,maxBytes){if(!exceedsCap(filePath,incomingBytes,maxBytes))return;let lockPath=`${filePath}.rotate-lock`,lock;try{lock=openSync(lockPath,"wx",384)}catch(error){if(errorCode(error)!=="EEXIST")throw error;clearStaleLock(lockPath);return}try{if(!exceedsCap(filePath,incomingBytes,maxBytes))return;try{renameSync(filePath,`${filePath}.1`)}catch(error){if(errorCode(error)==="ENOENT")return;throw error}chmodSync(`${filePath}.1`,384)}finally{closeSync(lock),rmSync(lockPath,{force:!0})}}import{closeSync as closeSync3,fchmodSync,mkdirSync as mkdirSync2,openSync as openSync3,writeSync as writeSync2}from"node:fs";import{dirname,join as join2}from"node:path";import{createHmac,randomBytes,randomUUID}from"node:crypto";import{closeSync as closeSync2,constants,fstatSync,fsyncSync,linkSync,mkdirSync,openSync as openSync2,readSync,rmSync as rmSync2,writeSync}from"node:fs";import{join}from"node:path";var KEY_FILE="config-identity.key",KEY_BYTES=32,EPHEMERAL_PREFIX="ephemeral:",processKey=randomBytes(KEY_BYTES),installationKeys=new Map,warnedPaths=new Set;function keyedIdentity(agentDir,payload){let key=installationKey(agentDir??getAgentDir());return key===void 0?`${EPHEMERAL_PREFIX}${hmac(processKey,payload)}`:hmac(key,payload)}function isEphemeralIdentity(identity){return identity?.startsWith(EPHEMERAL_PREFIX)===!0}function keyedFingerprint(value){return hmac(installationKey(getAgentDir())??processKey,value).slice(0,8)}function hmac(key,payload){return createHmac("sha256",key).update(payload).digest("hex")}function installationKey(agentDir){return installationKeys.has(agentDir)||installationKeys.set(agentDir,loadOrPublishKey(agentDir)),installationKeys.get(agentDir)}function loadOrPublishKey(agentDir){let path=join(agentDir,KEY_FILE),existing=readKey(path);return existing.kind==="valid"?existing.key:existing.kind==="missing"?publishKey(agentDir,path):void 0}function readKey(path){let fd;try{fd=openSync2(path,constants.O_RDONLY|(constants.O_NOFOLLOW??0))}catch(error){return isErrno(error,"ENOENT")?{kind:"missing"}:invalidKey(path,"cannot open safely","repair access and ensure it is a regular file, not a symlink")}try{let stat=fstatSync(fd);if(!stat.isFile())return invalidKey(path,"not a regular file","restore the original private key file");if(stat.size!==KEY_BYTES)return invalidKey(path,`wrong size (${stat.size} bytes)`,"restore the original 32-byte key from backup");if(process.platform!=="win32"){if(stat.uid!==process.getuid?.())return invalidKey(path,"wrong owner","chown the key file to your current user");if((stat.mode&511)!==384)return invalidKey(path,"wrong mode","chmod 600 the key file")}let key=Buffer.alloc(KEY_BYTES);return readSync(fd,key,0,KEY_BYTES,0)===KEY_BYTES?{kind:"valid",key}:invalidKey(path,"incomplete read","restore the original 32-byte key from backup")}finally{closeSync2(fd)}}function invalidKey(path,problem,repair){return warnedPaths.has(path)||(warnedPaths.add(path),console.warn(`MCP identity key ${path}: ${problem}; persistent catalog cache is disabled. To fix: ${repair}, then restart senpi. The file was left unchanged.`)),{kind:"invalid"}}function publishKey(agentDir,path){let temporary=`${path}.${process.pid}.${randomUUID()}.tmp`;try{mkdirSync(agentDir,{recursive:!0,mode:448});let key=randomBytes(KEY_BYTES),fd=openSync2(temporary,constants.O_CREAT|constants.O_EXCL|constants.O_WRONLY,384);try{writeSync(fd,key),fsyncSync(fd)}finally{closeSync2(fd)}return linkSync(temporary,path),syncDirectory(agentDir),key}catch(error){if(!isErrno(error,"EEXIST"))return;let published=readKey(path);return published.kind==="valid"?published.key:void 0}finally{rmSync2(temporary,{force:!0})}}function syncDirectory(directory){if(process.platform==="win32")return;let fd=openSync2(directory,constants.O_RDONLY);try{fsyncSync(fd)}finally{closeSync2(fd)}}function isErrno(error,code){return error instanceof Error&&"code"in error&&error.code===code}var DEFAULT_MAX_FILE_BYTES=1024*1024,MAX_RING_LINES=200,SENSITIVE_KEY_PATTERN=/(?:^|[_-])(?:api[_-]?key|key|token|secret|password|client_secret|auth|authorization)(?:$|[_-])/i,QUERY_SECRET_PATTERN=/([?&][^=\s&"'<>]*(?:key|token|secret|password|client_secret|auth)[^=\s&"'<>]*=)([^&\s"'<>]+)/gi,AUTHORIZATION_VALUE_PATTERN=/((?:"|')?\bAuthorization(?:"|')?\s*[:=]\s*(?:"|')?(?:[A-Za-z][A-Za-z0-9+.-]*\s+)?)(?!<redacted:)([^"',\s}\]&]+)/gi,BEARER_VALUE_PATTERN=/(\bBearer\s+)(?!<redacted:)([A-Za-z0-9._~+/=-]+)/gi,KEY_VALUE_PATTERN=/((?:"|')?[^"'\s:=,{}]*(?:api[_-]?key|key|token|secret|password|client_secret|auth)[^"'\s:=,{}]*(?:"|')?\s*[:=]\s*(?:"|')?)([^"',\s}\]&]+)/gi,MCP_LOG_LEVELS={emergency:{level:"emergency",severity:0},alert:{level:"alert",severity:1},critical:{level:"critical",severity:2},crit:{level:"critical",severity:2},error:{level:"error",severity:3},err:{level:"error",severity:3},warning:{level:"warning",severity:4},warn:{level:"warning",severity:4},notice:{level:"notice",severity:5},informational:{level:"info",severity:6},info:{level:"info",severity:6},debug:{level:"debug",severity:7}};function createMcpLogger(server,options={}){return new FileMcpLogger(server,options)}function getMcpLogDir(){return join2(getAgentDir(),"logs","mcp")}function fingerprintSecret(secret){return keyedFingerprint(secret)}function redactMcpLogText(text){return redactMcpLogTextWithContext(text,{seen:new WeakSet,secrets:new Set})}function redactMcpLogTextWithContext(text,context){return text.replace(AUTHORIZATION_VALUE_PATTERN,(_match,prefix,secret)=>`${prefix}${redactionForSecret(secret,context)}`).replace(BEARER_VALUE_PATTERN,(_match,prefix,secret)=>`${prefix}${redactionForSecret(secret,context)}`).replace(QUERY_SECRET_PATTERN,(_match,prefix,secret)=>`${prefix}${redactionForSecret(secret,context)}`).replace(KEY_VALUE_PATTERN,(match,prefix,secret)=>secret.startsWith("<redacted:")||/\bAuthorization\b/i.test(prefix)?match:`${prefix}${redactionForSecret(secret,context)}`)}function mapMcpLogLevel(level){return{...MCP_LOG_LEVELS[level]??MCP_LOG_LEVELS.info}}var FileMcpLogger=class{#server;#maxFileBytes;#ring=[];#fileSinkRetryAt=0;constructor(server,options){this.#server=server,this.#maxFileBytes=options.maxFileBytes??DEFAULT_MAX_FILE_BYTES,this.filePath=join2(options.logDir??getMcpLogDir(),`${sanitizeServerName(server)}.log`)}debug(message,data){this.log("debug",message,data)}info(message,data){this.log("info",message,data)}warn(message,data){this.log("warning",message,data)}error(message,data){this.log("error",message,data)}stderr(message,data){this.log("info",message,data,"stderr")}log(level,message,data,channel="server"){let line=this.#formatLine(level,message,data,channel);this.#pushRing(line),this.#writeFile(line)}getRingBuffer(){return[...this.#ring]}#formatLine(level,message,data,channel){let context={seen:new WeakSet,secrets:new Set},mapped=mapMcpLogLevel(level),redactedData=data===void 0?void 0:redactMcpLogData(data,context),entry={timestamp:new Date().toISOString(),server:this.#server,level:mapped.level,severity:mapped.severity,channel,message:redactMcpLogTextWithContext(message,context),data:redactedData};return redactResolvedReferenceValues(redactKnownSecrets(JSON.stringify(entry),context))}#pushRing(line){this.#ring.push(line),this.#ring.length>MAX_RING_LINES&&this.#ring.splice(0,this.#ring.length-MAX_RING_LINES)}#writeFile(line){if(!(Date.now()<this.#fileSinkRetryAt))try{mkdirSync2(dirname(this.filePath),{recursive:!0,mode:448});let bytes=Buffer.byteLength(`${line}
3
- `);this.#maxFileBytes>0&&rotateLogIfNeeded(this.filePath,bytes,this.#maxFileBytes);let fd=openSync3(this.filePath,"a",384);try{writeSync2(fd,`${line}
4
- `),fchmodSync(fd,384)}finally{closeSync3(fd)}}catch{this.#fileSinkRetryAt=Date.now()+LOG_SINK_RETRY_MS,this.#pushRing(this.#formatLine("warning","file sink disabled after write failure",void 0,"file"))}}};function redactMcpLogData(value,context){return typeof value=="string"?redactMcpLogTextWithContext(value,context):typeof value=="bigint"?value.toString():typeof value!="object"||value===null?value:context.seen.has(value)?"[Circular]":(context.seen.add(value),value instanceof Error?redactMcpLogError(value,context):Array.isArray(value)?value.map(item=>redactMcpLogData(item,context)):redactMcpLogEntries(value,context))}function redactMcpLogError(error,context){let redacted=redactMcpLogEntries(error,context);return redacted.name=redactMcpLogTextWithContext(error.name,context),redacted.message=redactKnownSecrets(redactMcpLogTextWithContext(error.message,context),context),error.stack!==void 0&&(redacted.stack=redactKnownSecrets(redactMcpLogTextWithContext(error.stack,context),context)),redacted}function redactMcpLogEntries(value,context){let redacted={};for(let[key,item]of Object.entries(value))SENSITIVE_KEY_PATTERN.test(key)?redacted[key]=redactionForSecret(stringifySecretValue(item),context):redacted[key]=redactMcpLogData(item,context);return redacted}function stringifySecretValue(value){if(typeof value=="string")return value;if(typeof value=="number"||typeof value=="boolean"||typeof value=="bigint")return String(value);let seen=new WeakSet;return JSON.stringify(value,(_key,item)=>typeof item=="bigint"?item.toString():typeof item!="object"||item===null?item:seen.has(item)?"[Circular]":(seen.add(item),item))??String(value)}function redactionForSecret(secret,context){return secret.length>0&&!secret.startsWith("<redacted:")&&context.secrets.add(secret),redactionFor(secret)}function redactKnownSecrets(text,context){for(let secret of[...context.secrets].sort((left,right)=>right.length-left.length))text=text.split(secret).join(redactionFor(secret));return text}function redactionFor(secret){return`<redacted:${fingerprintSecret(secret)}>`}function sanitizeServerName(server){return server.replace(/[^A-Za-z0-9._-]+/g,"_")||"server"}function wrapAsync(scope,fn,sink){return async(...args)=>{try{await fn(...args)}catch(error){let caughtError=error instanceof Error?error:normalizeCaughtError(error);await reportMcpAsyncError(scope,caughtError,sink)}}}function safeTimer(scope,delayMs,fn,sink){let wrapped=wrapAsync(scope,fn,sink),timer=setTimeout(()=>{wrapped()},delayMs);return timer.unref(),timer}function safeInterval(scope,delayMs,fn,sink){let wrapped=wrapAsync(scope,fn,sink),timer=setInterval(()=>{wrapped()},delayMs);return timer.unref(),timer}function safeDelay(delayMs){return new Promise(resolve=>{setTimeout(resolve,delayMs)})}function safeOn(emitter,eventName,scope,listener,sink){let wrapped=wrapAsync(scope,listener,sink);return emitter.on(eventName,wrapped),()=>{emitter.off(eventName,wrapped)}}function safeEventBusOn(bus,channel,handler){return bus.on(channel,handler)}async function reportMcpAsyncError(scope,error,sink){let normalized=normalizeError(error);if(logError(scope,normalized,sink.logger),!!sink.notify)try{await sink.notify(`MCP ${scope} failed: ${normalized.message}`,normalized)}catch(notifyError){let caughtNotifyError=notifyError instanceof Error?notifyError:normalizeCaughtError(notifyError);logError(`${scope}.notify`,caughtNotifyError,sink.logger)}}function logError(scope,error,logger){try{logger.error(scope,serializeError(error))}catch{let redactedScope=redactMcpLogText(scope);console.error(`MCP ${redactedScope} logger failed; suppressed async error details`)}}function serializeError(error){let serialized={name:error.name,message:error.message};return error.stack&&(serialized.stack=error.stack),serialized}function normalizeError(error){return error instanceof Error?error:normalizeCaughtError(error)}function normalizeCaughtError(error){return typeof error=="string"?new Error(error):new Error(safeStringify(error))}function safeStringify(value){try{return JSON.stringify(value)??String(value)}catch{return String(value)}}var OAuthFlowError=class extends AuthError{constructor(oauthKind,message,options={}){super(message,options),this.name="OAuthFlowError",this.oauthKind=oauthKind,this.terminal=TERMINAL_KINDS.has(oauthKind)}},TERMINAL_KINDS=new Set(["s256_unsupported","needs_auth","invalid_grant","state_mismatch","expired_code","no_verifier","headless","missing_env"]),TERMINAL_OAUTH_CODES=new Set(["invalid_grant","invalid_token","invalid_client","unauthorized_client"]),TRANSIENT_OAUTH_CODES=new Set(["temporarily_unavailable","server_error","slow_down"]);function isInvalidGrant(error){let code=oauthErrorCode(error);if(code!==void 0)return TERMINAL_OAUTH_CODES.has(code);let text=errorText(error).toLowerCase();return text.includes("invalid_grant")||text.includes("invalid_token")}function isTransientTokenError(error){if(isInvalidGrant(error))return!1;let code=oauthErrorCode(error);if(code!==void 0&&TRANSIENT_OAUTH_CODES.has(code))return!0;let text=errorText(error).toLowerCase();return/\b(?:500|502|503|504)\b/.test(text)||text.includes("econnrefused")||text.includes("network")}function oauthErrorCode(error){if(typeof error!="object"||error===null)return;let code=error.errorCode;return typeof code=="string"?code:void 0}function errorText(error){return error instanceof Error?`${error.name} ${error.message}`:String(error)}var MAX_CAUSE_DEPTH=5;function isMcpNeedsAuthError(error,depth=0){return isMcpSdkUnauthorizedError(error)?!0:error instanceof OAuthFlowError?error.terminal:depth<MAX_CAUSE_DEPTH&&error!==null&&typeof error=="object"&&"cause"in error?isMcpNeedsAuthError(error.cause,depth+1):!1}import{execFile}from"node:child_process";import{promisify}from"node:util";var execFileAsync=promisify(execFile);async function collectProcessTree(rootPid){let seen=new Set,queue=[rootPid];for(let index=0;index<queue.length;index++){let pid=queue[index];if(!seen.has(pid)){seen.add(pid);for(let childPid of await childPids(pid))seen.has(childPid)||queue.push(childPid)}}return[...seen]}async function reapProcessTree(rootPid,options){let knownPids=new Set(await collectProcessTree(rootPid));if(someProcessAlive(knownPids)){killPids(knownPids,"SIGTERM"),await waitForDead(knownPids,options.termWaitMs);for(let pid of await collectProcessTree(rootPid))knownPids.add(pid);someProcessAlive(knownPids)&&(killPids(knownPids,"SIGKILL"),await waitForDead(knownPids,options.killWaitMs))}}function isProcessAlive(pid){try{return process.kill(pid,0),!0}catch(error){return!1}}function delay(ms){return safeDelay(ms)}async function childPids(parentPid){if(!["darwin","linux"].includes(process.platform))return[];try{let{stdout}=await execFileAsync("pgrep",["-P",String(parentPid),"."],{timeout:1e3});return stdout.split(/\s+/).map(Number).filter(pid=>Number.isInteger(pid)&&pid>0)}catch(error){return[]}}function killPids(pids,signal){for(let pid of[...pids].reverse())if(!(!Number.isInteger(pid)||pid<=1))try{process.kill(pid,signal)}catch(error){}}async function waitForDead(pids,timeoutMs){let deadline=Date.now()+timeoutMs;for(;Date.now()<deadline;){if(!someProcessAlive(pids))return;await delay(25)}}function someProcessAlive(pids){for(let pid of pids)if(isProcessAlive(pid))return!0;return!1}export{LOG_SINK_RETRY_MS,rotateLogIfNeeded,keyedIdentity,isEphemeralIdentity,createMcpLogger,fingerprintSecret,redactMcpLogText,wrapAsync,safeTimer,safeInterval,safeDelay,safeOn,safeEventBusOn,reportMcpAsyncError,ConnectError,ToolExecError,AuthError,TimeoutError,SessionExpiredError,isRetriableMcpError,isMcpSessionExpiredError,OAuthFlowError,isInvalidGrant,isTransientTokenError,loadMcpSdkClient,loadMcpSdkStdioTransport,loadMcpSdkTypes,loadMcpSdkAuth,loadMcpSdkStreamableHttpTransport,isMcpNeedsAuthError,reapProcessTree,delay};