@code-yeongyu/senpi 2026.10.25-1 → 2026.10.25-3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/CHANGELOG.md +36 -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-RHHBXIRN.js → chunk-4WZRVZNX.js} +1 -1
  5. package/dist/bundle/chunks/chunk-6O4SEW2W.js +5 -0
  6. package/dist/bundle/chunks/{chunk-Y5OTHMXO.js → chunk-7CNOCJCN.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-YDCRA5ES.js → chunk-7W7OJDIY.js} +4 -4
  8. package/dist/bundle/chunks/chunk-AX72VNRS.js +27 -0
  9. package/dist/bundle/chunks/{chunk-FWXMMMCV.js → chunk-C5UFJ7B3.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-7IRLFYNM.js → chunk-CNL6QSUB.js} +4 -4
  11. package/dist/bundle/chunks/{chunk-BTNYLNWZ.js → chunk-FFQOEC76.js} +5 -5
  12. package/dist/bundle/chunks/{chunk-VOBD6FQG.js → chunk-FHXR4ZAC.js} +1 -1
  13. package/dist/bundle/chunks/chunk-H3HNMY2W.js +13 -0
  14. package/dist/bundle/chunks/{chunk-BMYNMUVZ.js → chunk-HVJI5T75.js} +1 -1
  15. package/dist/bundle/chunks/{chunk-EWOEVVQT.js → chunk-JA2R2ZSE.js} +1 -1
  16. package/dist/bundle/chunks/chunk-JPGT64RV.js +2 -0
  17. package/dist/bundle/chunks/chunk-KXB5WIS5.js +4 -0
  18. package/dist/bundle/chunks/chunk-L73UYAH5.js +3 -0
  19. package/dist/bundle/chunks/chunk-LTU6UFEX.js +2 -0
  20. package/dist/bundle/chunks/{chunk-EYBDBR7V.js → chunk-MJ5NNWX4.js} +1 -1
  21. package/dist/bundle/chunks/{chunk-RVYSJBZ7.js → chunk-O3Q2ET6W.js} +2 -5
  22. package/dist/bundle/chunks/chunk-O5RYI3IW.js +2 -0
  23. package/dist/bundle/chunks/{chunk-SGDOZT6B.js → chunk-ODTGC6YU.js} +1 -1
  24. package/dist/bundle/chunks/chunk-P3M2PF6K.js +27 -0
  25. package/dist/bundle/chunks/chunk-SWJXRC2I.js +5 -0
  26. package/dist/bundle/chunks/{chunk-QH2HSCMX.js → chunk-TTU2RNVX.js} +3 -3
  27. package/dist/bundle/chunks/{chunk-PXKF2RHD.js → chunk-UFJ2K7Q7.js} +1 -1
  28. package/dist/bundle/chunks/{chunk-7UUOGZKR.js → chunk-UQNRW3VU.js} +1 -1
  29. package/dist/bundle/chunks/{chunk-U67ML6CQ.js → chunk-WD6WBYC5.js} +1 -1
  30. package/dist/bundle/chunks/chunk-WJOT3DFL.js +2 -0
  31. package/dist/bundle/chunks/{chunk-7WAJCESX.js → chunk-WLHNEUGD.js} +1 -1
  32. package/dist/bundle/chunks/chunk-YI7SPFDN.js +5 -0
  33. package/dist/bundle/chunks/{chunk-URBJS24R.js → chunk-YRF4I43T.js} +35 -35
  34. package/dist/bundle/chunks/chunk-Z3UYNSVR.js +3 -0
  35. package/dist/bundle/chunks/cli-main-HEKFFB5S.js +3 -0
  36. package/dist/bundle/chunks/{client-KWTLTKVV.js → client-WTYEICLH.js} +3 -3
  37. package/dist/bundle/chunks/dispatch-NYAULNVW.js +9 -0
  38. package/dist/bundle/chunks/{help-fast-path-ZHZGXAUG.js → help-fast-path-3Z26DQSV.js} +1 -1
  39. package/dist/bundle/chunks/host-command-Z3WPHJOW.js +2 -0
  40. package/dist/bundle/chunks/host-lifecycle-ONQIMWMX.js +3 -0
  41. package/dist/bundle/chunks/host-lifecycle.js +9 -7
  42. package/dist/bundle/chunks/host-session-command-J65ENSV5.js +20 -0
  43. package/dist/bundle/chunks/{host-socket-request-ZDJGSSZO.js → host-socket-request-3CWE4265.js} +1 -1
  44. package/dist/bundle/chunks/interactive-mode-DEPQC6TX.js +2 -0
  45. package/dist/bundle/chunks/mcp-presenter-bootstrap-XUSCQVRC.js +2 -0
  46. package/dist/bundle/chunks/{multi-session-host-M7HVUMRH.js → multi-session-host-YI2WZOET.js} +12 -9
  47. package/dist/bundle/chunks/{package-manager-cli-FSR5P6YL.js → package-manager-cli-GHZPJMRF.js} +1 -1
  48. package/dist/bundle/chunks/{role-M2MTVACJ.js → role-JVSBNKGE.js} +1 -1
  49. package/dist/bundle/chunks/{rotation-stream-U7YQMC7H.js → rotation-stream-ZPVCFOVD.js} +1 -1
  50. package/dist/bundle/chunks/rpc-mode-ORHBGBBV.js +2 -0
  51. package/dist/bundle/chunks/{rpc-monitor-source-OL5KU6NO.js → rpc-monitor-source-FFEQS744.js} +1 -1
  52. package/dist/bundle/chunks/{runtime-session-CLNODAYE.js → runtime-session-V5OO7ZRM.js} +1 -1
  53. package/dist/bundle/chunks/{schedule-command-XBW4EOG5.js → schedule-command-YWG6D6ZF.js} +1 -1
  54. package/dist/bundle/chunks/session-control-endpoint-36HAQSQO.js +3 -0
  55. package/dist/bundle/chunks/{session-picker-KQUFWDQC.js → session-picker-25H5KNYP.js} +1 -1
  56. package/dist/bundle/chunks/session-worker.js +285 -271
  57. package/dist/bundle/chunks/{stdio-IEC635SY.js → stdio-YXDKKKQZ.js} +1 -1
  58. package/dist/bundle/chunks/{streamableHttp-KJI5KE4K.js → streamableHttp-DKIBUWVY.js} +1 -1
  59. package/dist/bundle/chunks/{types-LLNV23LW.js → types-BJUEQ4II.js} +1 -1
  60. package/dist/bundle/chunks/zod-compat-RJDXCU7S.js +2 -0
  61. package/dist/bundle/cli.js +2 -2
  62. package/dist/bundle/host-supervisor.js +9 -7
  63. package/dist/bundle/index.js +1 -1
  64. package/dist/bundle/rpc-entry.js +1 -1
  65. package/dist/bundle/runtime-manifest.json +1 -1
  66. package/dist/cli/args.d.ts +2 -0
  67. package/dist/cli/args.js +9 -0
  68. package/dist/cli/host-command-args.d.ts +1 -0
  69. package/dist/cli/host-command-args.js +7 -1
  70. package/dist/cli/host-command.js +3 -1
  71. package/dist/core/agent-session.d.ts +1 -0
  72. package/dist/core/agent-session.js +50 -16
  73. package/dist/core/engine-turn-limit.d.ts +2 -0
  74. package/dist/core/engine-turn-limit.js +7 -1
  75. package/dist/core/extensions/builtin/anthropic-subscription/auth-attempt.js +5 -0
  76. package/dist/core/extensions/builtin/goal/index.js +1 -0
  77. package/dist/core/extensions/builtin/goal/lifecycle-facts.d.ts +3 -0
  78. package/dist/core/extensions/builtin/goal/stop-pause.js +2 -1
  79. package/dist/core/extensions/builtin/loop/session-hold.js +2 -1
  80. package/dist/core/extensions/builtin/mcp/catalog.d.ts +3 -1
  81. package/dist/core/extensions/builtin/mcp/catalog.js +2 -0
  82. package/dist/core/extensions/builtin/mcp/connection.d.ts +1 -0
  83. package/dist/core/extensions/builtin/mcp/connection.js +46 -1
  84. package/dist/core/extensions/builtin/mcp/elicitation.d.ts +6 -1
  85. package/dist/core/extensions/builtin/mcp/elicitation.js +56 -2
  86. package/dist/core/extensions/builtin/mcp/expose/call.js +6 -2
  87. package/dist/core/extensions/builtin/mcp/expose/session.d.ts +4 -1
  88. package/dist/core/extensions/builtin/mcp/expose/session.js +7 -0
  89. package/dist/core/extensions/builtin/mcp/health.js +5 -0
  90. package/dist/core/extensions/builtin/mcp/host-registry.js +7 -1
  91. package/dist/core/extensions/builtin/mcp/index.js +2 -0
  92. package/dist/core/extensions/builtin/mcp/modern/client.d.ts +5 -0
  93. package/dist/core/extensions/builtin/mcp/modern/client.js +15 -0
  94. package/dist/core/extensions/builtin/mcp/modern/consent-queue.d.ts +2 -0
  95. package/dist/core/extensions/builtin/mcp/modern/consent-queue.js +59 -0
  96. package/dist/core/extensions/builtin/mcp/modern/diagnostics.d.ts +4 -0
  97. package/dist/core/extensions/builtin/mcp/modern/diagnostics.js +39 -0
  98. package/dist/core/extensions/builtin/mcp/modern/dispatch.js +21 -11
  99. package/dist/core/extensions/builtin/mcp/modern/elicitation.d.ts +14 -0
  100. package/dist/core/extensions/builtin/mcp/modern/elicitation.js +145 -0
  101. package/dist/core/extensions/builtin/mcp/modern/errors.d.ts +21 -0
  102. package/dist/core/extensions/builtin/mcp/modern/errors.js +23 -0
  103. package/dist/core/extensions/builtin/mcp/modern/form-schema.d.ts +20 -0
  104. package/dist/core/extensions/builtin/mcp/modern/form-schema.js +153 -0
  105. package/dist/core/extensions/builtin/mcp/modern/http-fetch.d.ts +3 -1
  106. package/dist/core/extensions/builtin/mcp/modern/http-fetch.js +8 -2
  107. package/dist/core/extensions/builtin/mcp/modern/inputs.d.ts +11 -0
  108. package/dist/core/extensions/builtin/mcp/modern/inputs.js +65 -0
  109. package/dist/core/extensions/builtin/mcp/modern/parameter-headers.d.ts +11 -0
  110. package/dist/core/extensions/builtin/mcp/modern/parameter-headers.js +109 -0
  111. package/dist/core/extensions/builtin/mcp/modern/request.d.ts +20 -0
  112. package/dist/core/extensions/builtin/mcp/modern/request.js +292 -0
  113. package/dist/core/extensions/builtin/mcp/modern/stdio-probe.d.ts +1 -1
  114. package/dist/core/extensions/builtin/mcp/modern/stdio-probe.js +54 -6
  115. package/dist/core/extensions/builtin/mcp/modern/transport.js +80 -14
  116. package/dist/core/extensions/builtin/mcp/modern/wire.js +11 -7
  117. package/dist/core/extensions/builtin/mcp/prompts.d.ts +5 -1
  118. package/dist/core/extensions/builtin/mcp/prompts.js +6 -1
  119. package/dist/core/extensions/builtin/mcp/request-origin.d.ts +47 -0
  120. package/dist/core/extensions/builtin/mcp/request-origin.js +59 -0
  121. package/dist/core/extensions/builtin/mcp/resources.d.ts +4 -1
  122. package/dist/core/extensions/builtin/mcp/resources.js +19 -4
  123. package/dist/core/extensions/builtin/mcp/sdk.lazy.d.ts +3 -1
  124. package/dist/core/extensions/builtin/mcp/sdk.lazy.js +3 -1
  125. package/dist/core/extensions/builtin/mcp/service-register.d.ts +4 -1
  126. package/dist/core/extensions/builtin/mcp/service-register.js +3 -0
  127. package/dist/core/extensions/builtin/mcp/service-types.d.ts +2 -1
  128. package/dist/core/extensions/builtin/mcp/service.d.ts +1 -0
  129. package/dist/core/extensions/builtin/mcp/service.js +79 -4
  130. package/dist/core/extensions/builtin/mcp/shared-connection.js +36 -2
  131. package/dist/core/extensions/builtin/mcp/shared-lease.d.ts +1 -0
  132. package/dist/core/extensions/builtin/mcp/shared-lease.js +9 -0
  133. package/dist/core/extensions/builtin/mcp/transport-sdk.d.ts +3 -2
  134. package/dist/core/extensions/builtin/mcp/transport-sdk.js +56 -25
  135. package/dist/core/extensions/builtin/mcp/wrap.d.ts +7 -0
  136. package/dist/core/extensions/builtin/mcp/wrap.js +6 -0
  137. package/dist/core/extensions/runner.js +3 -2
  138. package/dist/core/extensions/types.d.ts +2 -0
  139. package/dist/core/lifecycle-ledger.d.ts +1 -0
  140. package/dist/core/lifecycle-ledger.js +7 -2
  141. package/dist/core/lifecycle-record.js +3 -0
  142. package/dist/core/lifecycle-runtime-facts.js +3 -0
  143. package/dist/core/lifecycle-session-binding.d.ts +11 -1
  144. package/dist/core/lifecycle-session-binding.js +35 -4
  145. package/dist/core/lifecycle-stop.d.ts +41 -0
  146. package/dist/core/lifecycle-stop.js +92 -0
  147. package/dist/core/mcp-form-types.d.ts +89 -0
  148. package/dist/core/mcp-form-types.js +23 -0
  149. package/dist/core/mcp-mrtr-presentations.d.ts +15 -0
  150. package/dist/core/mcp-mrtr-presentations.js +17 -0
  151. package/dist/core/run-resume-turn.d.ts +1 -0
  152. package/dist/core/run-resume-turn.js +4 -3
  153. package/dist/core/session-lifecycle-types.d.ts +15 -0
  154. package/dist/core/session-lifecycle.d.ts +17 -1
  155. package/dist/core/session-lifecycle.js +40 -2
  156. package/dist/core/stop-resume-directive.d.ts +1 -1
  157. package/dist/core/stop-resume-directive.js +10 -1
  158. package/dist/main.js +5 -1
  159. package/dist/modes/interactive/components/extension-selector.d.ts +1 -0
  160. package/dist/modes/interactive/components/extension-selector.js +4 -0
  161. package/dist/modes/interactive/interactive-mode.d.ts +4 -0
  162. package/dist/modes/interactive/interactive-mode.js +35 -8
  163. package/dist/modes/interactive/mcp-local-presenter.d.ts +5 -0
  164. package/dist/modes/interactive/mcp-local-presenter.js +109 -0
  165. package/dist/modes/interactive/pause-footer.d.ts +5 -0
  166. package/dist/modes/interactive/pause-footer.js +2 -0
  167. package/dist/modes/rpc/connection-handler.d.ts +1 -0
  168. package/dist/modes/rpc/connection-handler.js +7 -1
  169. package/dist/modes/rpc/host-capabilities.d.ts +1 -0
  170. package/dist/modes/rpc/host-capabilities.js +2 -1
  171. package/dist/modes/rpc/host-ensure-start.js +13 -2
  172. package/dist/modes/rpc/host-ensure-types.d.ts +2 -0
  173. package/dist/modes/rpc/host-ensure.js +1 -0
  174. package/dist/modes/rpc/host-handoff.d.ts +2 -0
  175. package/dist/modes/rpc/host-handover-wire.d.ts +2 -0
  176. package/dist/modes/rpc/host-handover-wire.js +1 -0
  177. package/dist/modes/rpc/host-lifecycle-launch.d.ts +1 -0
  178. package/dist/modes/rpc/host-lifecycle-launch.js +43 -2
  179. package/dist/modes/rpc/host-reservations.js +3 -2
  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-stalled-evidence.js +2 -1
  183. package/dist/modes/rpc/host-successor.js +11 -1
  184. package/dist/modes/rpc/mcp-form-dialog.d.ts +45 -0
  185. package/dist/modes/rpc/mcp-form-dialog.js +159 -0
  186. package/dist/modes/rpc/mcp-form-worker.d.ts +14 -0
  187. package/dist/modes/rpc/mcp-form-worker.js +167 -0
  188. package/dist/modes/rpc/mcp-presenter-authority.d.ts +53 -0
  189. package/dist/modes/rpc/mcp-presenter-authority.js +309 -0
  190. package/dist/modes/rpc/mcp-presenter-bootstrap.d.ts +8 -0
  191. package/dist/modes/rpc/mcp-presenter-bootstrap.js +111 -0
  192. package/dist/modes/rpc/mcp-presenter-control.d.ts +24 -0
  193. package/dist/modes/rpc/mcp-presenter-control.js +330 -0
  194. package/dist/modes/rpc/multi-session-host.d.ts +3 -0
  195. package/dist/modes/rpc/multi-session-host.js +36 -1
  196. package/dist/modes/rpc/session-binding.js +6 -1
  197. package/dist/modes/rpc/session-event-writer.d.ts +14 -0
  198. package/dist/modes/rpc/session-event-writer.js +128 -1
  199. package/dist/modes/rpc/session-router-routing.js +1 -0
  200. package/dist/modes/rpc/session-worker-client.d.ts +3 -0
  201. package/dist/modes/rpc/session-worker-client.js +89 -0
  202. package/dist/modes/rpc/session-worker-protocol.d.ts +41 -0
  203. package/dist/modes/rpc/session-worker.js +9 -0
  204. package/dist/modes/rpc/tool-child/group-ownership.js +4 -2
  205. package/dist/utils/process-identity.d.ts +7 -0
  206. package/dist/utils/process-identity.js +32 -5
  207. package/docs/extensions.md +5 -0
  208. package/docs/mcp.md +20 -3
  209. package/docs/rpc.md +111 -0
  210. package/package.json +6 -6
  211. package/dist/bundle/chunks/chunk-3VOG6WFU.js +0 -27
  212. package/dist/bundle/chunks/chunk-7ASLQ243.js +0 -2
  213. package/dist/bundle/chunks/chunk-CDRFFHLL.js +0 -4
  214. package/dist/bundle/chunks/chunk-HPCS4HM4.js +0 -27
  215. package/dist/bundle/chunks/chunk-II22E3CV.js +0 -3
  216. package/dist/bundle/chunks/chunk-J5P2VL3Y.js +0 -5
  217. package/dist/bundle/chunks/chunk-SCVRKIFK.js +0 -2
  218. package/dist/bundle/chunks/chunk-XUFUKDKJ.js +0 -5
  219. package/dist/bundle/chunks/cli-main-TABBP55A.js +0 -3
  220. package/dist/bundle/chunks/dispatch-DEP4PEDZ.js +0 -10
  221. package/dist/bundle/chunks/host-command-OQR4RDTM.js +0 -2
  222. package/dist/bundle/chunks/host-lifecycle-U36U7JTE.js +0 -3
  223. package/dist/bundle/chunks/host-session-command-IGB27ZPJ.js +0 -20
  224. package/dist/bundle/chunks/interactive-mode-DGQNSXUA.js +0 -2
  225. package/dist/bundle/chunks/rpc-mode-SZAJNKKL.js +0 -2
  226. package/dist/bundle/chunks/session-control-endpoint-WEF3XTJR.js +0 -3
@@ -17,6 +17,7 @@ import { pollProtocolInfo } from "./host-readiness.js";
17
17
  import { initialHostEnvironment } from "./host-spawn-environment.js";
18
18
  import { hostLaunchProfile } from "./protocol-identity.js";
19
19
  import { createSocketSecret, socketSecretPath } from "./socket-transport.js";
20
+ import { relayPresenterBootstrap } from "./mcp-presenter-bootstrap.js";
20
21
  export const DEFAULT_READINESS_TIMEOUT_MS = 10_000;
21
22
  export { DEFAULT_STOP_TIMEOUT_MS };
22
23
  /**
@@ -55,11 +56,13 @@ export async function startHost(paths, socket, options, generation = 0) {
55
56
  let child;
56
57
  let exitedEarly;
57
58
  let childExit;
59
+ let presenterEnrollment;
58
60
  try {
59
61
  const supervisorArgs = [
60
62
  "--socket",
61
63
  socket,
62
64
  ...(options.owner ? ["--owner-fd", String(OWNER_WATCH_FD)] : []),
65
+ ...(options.presenterBootstrap ? ["--mcp-presenter-fd", "4"] : []),
63
66
  ...(options.hostArgs ?? []),
64
67
  ];
65
68
  const launch = testOptions?.spawn ?? testOptions?.launch?.(supervisorArgs) ?? defaultHostLaunch(supervisorArgs);
@@ -73,8 +76,14 @@ export async function startHost(paths, socket, options, generation = 0) {
73
76
  instanceId,
74
77
  generation,
75
78
  }),
76
- stdio: options.owner ? ["ignore", "ignore", stderr.fd, "pipe"] : ["ignore", "ignore", stderr.fd],
79
+ stdio: options.presenterBootstrap
80
+ ? ["ignore", "ignore", stderr.fd, options.owner ? "pipe" : "ignore", "pipe"]
81
+ : options.owner
82
+ ? ["ignore", "ignore", stderr.fd, "pipe"]
83
+ : ["ignore", "ignore", stderr.fd],
77
84
  });
85
+ if (options.presenterBootstrap)
86
+ presenterEnrollment = relayPresenterBootstrap(options.presenterBootstrap, child);
78
87
  childExit = new Promise((resolveExit) => {
79
88
  child.once("exit", (code, signal) => {
80
89
  exitedEarly = { code, signal };
@@ -140,8 +149,10 @@ export async function startHost(paths, socket, options, generation = 0) {
140
149
  }
141
150
  const readinessTimeoutMs = testOptions?.readinessTimeoutMs ?? DEFAULT_READINESS_TIMEOUT_MS;
142
151
  const result = await pollProtocolInfo(socket, readinessTimeoutMs, isCompatible, childExit);
143
- if (result.ready)
152
+ if (result.ready) {
153
+ await presenterEnrollment;
144
154
  return { pid: pidFile.pid, socket, reused: false, release: result.hold.release };
155
+ }
145
156
  await testOptions?.beforeReadinessTeardown?.();
146
157
  // Teardown runs for the diagnostic's sake, so it must never replace it: a stop failure here
147
158
  // (unreadable identity, a host that outlives SIGKILL) would otherwise propagate instead of the
@@ -1,5 +1,6 @@
1
1
  /** The options and result of `ensureHost`, kept apart so the ensure's helpers can name them without a cycle. */
2
2
  import type { HostLifecyclePolicyInput } from "./host-lifecycle.ts";
3
+ import type { Duplex } from "node:stream";
3
4
  /**
4
5
  * What an ensure may do to a host that is already running.
5
6
  *
@@ -10,6 +11,7 @@ import type { HostLifecyclePolicyInput } from "./host-lifecycle.ts";
10
11
  */
11
12
  export type HostUpgradePolicy = "never" | "if-engine-differs";
12
13
  export interface EnsureHostOptions {
14
+ readonly presenterBootstrap?: Duplex;
13
15
  readonly socket: string;
14
16
  readonly agentDir?: string;
15
17
  readonly keyRoot?: string;
@@ -205,6 +205,7 @@ async function upgradeGeneration(paths, socket, options, attachedPid) {
205
205
  socket,
206
206
  agentDir: options.agentDir ?? getAgentDir(),
207
207
  hostArgs: options.hostArgs ?? [],
208
+ presenterBootstrap: options.presenterBootstrap,
208
209
  ...(options.env ? { env: options.env } : {}),
209
210
  ...(options.policy ? { policy: options.policy } : {}),
210
211
  _test: {
@@ -1,3 +1,4 @@
1
+ import type { Duplex } from "node:stream";
1
2
  import type { HostLifecyclePolicyInput } from "./host-lifecycle.ts";
2
3
  /**
3
4
  * The longest a handoff holds the ensure lock: its probe of the running host, the two session counts
@@ -5,6 +6,7 @@ import type { HostLifecyclePolicyInput } from "./host-lifecycle.ts";
5
6
  */
6
7
  export declare const HANDOFF_LOCK_HOLD_MS: number;
7
8
  export interface HandoffHostOptions {
9
+ readonly presenterBootstrap?: Duplex;
8
10
  readonly socket: string;
9
11
  readonly agentDir?: string;
10
12
  /** Extra CLI arguments the successor's host child is launched with (provider pinning, extensions). */
@@ -9,6 +9,7 @@
9
9
  import type { HandoverAnswer, HandoverOutcome, HandoverView, IdleHandoverRequest } from "./host-idle-handover.ts";
10
10
  import type { RpcResponse } from "./rpc-types.ts";
11
11
  import type { RpcSessionRegistry } from "./session-registry.ts";
12
+ import type { Duplex } from "node:stream";
12
13
  export declare const BEGIN_HANDOVER_COMMAND = "begin_handover";
13
14
  export declare function parseBeginHandover(record: Readonly<Record<string, unknown>>): IdleHandoverRequest | string;
14
15
  export declare function beginHandoverResponse(id: unknown, answer: HandoverAnswer | string): Record<string, unknown>;
@@ -37,5 +38,6 @@ export declare function performIdleHandover(context: {
37
38
  readonly request: IdleHandoverRequest;
38
39
  readonly socket: string;
39
40
  readonly agentDir: string;
41
+ readonly presenterBootstrap?: Duplex;
40
42
  }): Promise<HandoverOutcome>;
41
43
  //# sourceMappingURL=host-handover-wire.d.ts.map
@@ -120,6 +120,7 @@ export async function performIdleHandover(context) {
120
120
  env[name] = null;
121
121
  Object.assign(env, request.env);
122
122
  const result = await handoffHost({
123
+ presenterBootstrap: context.presenterBootstrap,
123
124
  socket,
124
125
  agentDir,
125
126
  hostArgs: request.hostArgs,
@@ -6,6 +6,7 @@
6
6
  import { type ChildProcess } from "node:child_process";
7
7
  import type { SocketFileIdentity } from "./socket-ownership.ts";
8
8
  export interface SupervisorLaunch {
9
+ readonly presenterBootstrapFd?: number;
9
10
  readonly socket: string;
10
11
  readonly hostArgs: readonly string[];
11
12
  /** Optional runtime command used by rebranded/bundled callers. */
@@ -56,10 +56,16 @@ export function parseSupervisorArgs(argv) {
56
56
  let childArgs;
57
57
  let agentDir;
58
58
  let ownerFd;
59
+ let presenterBootstrapFd;
59
60
  let bindSocket;
60
61
  let replaceIdentity;
61
62
  for (let index = 0; index < argv.length; index++) {
62
63
  const arg = argv[index];
64
+ if (arg === "--mcp-presenter-fd" && argv[index + 1] === "4") {
65
+ presenterBootstrapFd = 4;
66
+ index++;
67
+ continue;
68
+ }
63
69
  if (arg === "--owner-fd" && index + 1 < argv.length) {
64
70
  ownerFd = Number(argv[++index]);
65
71
  if (!Number.isSafeInteger(ownerFd) || ownerFd < 3)
@@ -101,7 +107,19 @@ export function parseSupervisorArgs(argv) {
101
107
  }
102
108
  return socket === undefined
103
109
  ? undefined
104
- : { socket, hostArgs, childCommand, childArgs, agentDir, ownerFd, bindSocket, replaceIdentity };
110
+ : ownerFd === presenterBootstrapFd && ownerFd !== undefined
111
+ ? undefined
112
+ : {
113
+ socket,
114
+ hostArgs,
115
+ childCommand,
116
+ childArgs,
117
+ agentDir,
118
+ ownerFd,
119
+ bindSocket,
120
+ replaceIdentity,
121
+ presenterBootstrapFd,
122
+ };
105
123
  }
106
124
  export const OWNER_WATCH_FD = 3;
107
125
  // Strongly retain the write end until that supervisor exits, but never keep the caller alive.
@@ -188,6 +206,10 @@ export const HOST_CHILD_WATCH_FD = 3;
188
206
  export function spawnHostChild(options) {
189
207
  const { launch, internalSocket, internal, internalSecret, publicSocket } = options;
190
208
  const childLaunch = spawnableChildLaunch(resolveHostChildLaunch(launch, internalSocket));
209
+ // A shell wrapper cannot prove preservation of the private inherited handle.
210
+ const bootstrap = launch.presenterBootstrapFd !== undefined && !childLaunch.shell;
211
+ if (bootstrap)
212
+ childLaunch.args.push("--mcp-presenter-fd", "4");
191
213
  const child = spawn(childLaunch.command, childLaunch.args, {
192
214
  env: {
193
215
  ...process.env,
@@ -210,13 +232,32 @@ export function spawnHostChild(options) {
210
232
  // Slot 3 is the lifetime pipe: "pipe" gives the child a read end it can
211
233
  // wait on and keeps the write end owned by this process alone.
212
234
  shell: childLaunch.shell,
213
- stdio: [process.platform === "win32" ? "ignore" : "pipe", "ignore", "inherit", "pipe"],
235
+ stdio: [
236
+ process.platform === "win32" ? "ignore" : "pipe",
237
+ "ignore",
238
+ "inherit",
239
+ "pipe",
240
+ ...(bootstrap ? ["pipe"] : []),
241
+ ],
214
242
  // The supervisor is spawned detached, so on win32 it owns no console. A
215
243
  // console-subsystem child started from it would allocate a fresh one,
216
244
  // which Windows Terminal renders as an empty window that takes focus.
217
245
  // CREATE_NO_WINDOW gives the child a console with no window instead.
218
246
  windowsHide: true,
219
247
  });
248
+ if (launch.presenterBootstrapFd !== undefined) {
249
+ const fd = launch.presenterBootstrapFd;
250
+ // The opt-in helper stays outside the supervisor's fixed startup import budget.
251
+ void import("./mcp-presenter-bootstrap.js")
252
+ .then(({ presenterBootstrapStream, relayPresenterBootstrap }) => {
253
+ const upstream = presenterBootstrapStream(fd);
254
+ if (upstream && bootstrap)
255
+ return relayPresenterBootstrap(upstream, child);
256
+ upstream?.destroy();
257
+ child.stdio[4]?.destroy();
258
+ })
259
+ .catch(() => child.stdio[4]?.destroy());
260
+ }
220
261
  if (options.internalKey) {
221
262
  child.stdin?.on("error", () => { });
222
263
  // One write only. Keep its write end alive until this supervisor exits.
@@ -22,7 +22,7 @@
22
22
  import { createHash } from "node:crypto";
23
23
  import { mkdir, readdir, readFile, rename, rm, writeFile } from "node:fs/promises";
24
24
  import { join } from "node:path";
25
- import { processIsLive, processStartTimeMs, readProcessStartTime, sameProcessStartMs, } from "../../utils/process-identity.js";
25
+ import { processIsLive, processIsRunning, processStartTimeMs, readProcessStartTime, sameProcessStartMs, } from "../../utils/process-identity.js";
26
26
  import { createHostDaemonPaths, generationPaths, HOST_DAEMON_DIR_ENV, hostDaemonDirectoryPaths, } from "./host-daemon-paths.js";
27
27
  import { parseJson, readFileOrUndefined } from "./host-daemon-state.js";
28
28
  import { generationHeartbeatStopped } from "./host-stalled-evidence.js";
@@ -280,7 +280,8 @@ export async function claimOwnerIsLive(owner) {
280
280
  * different start time. `unproven`: the pid runs but no start time was recorded or none can be read.
281
281
  */
282
282
  export async function claimOwnerIdentity(owner) {
283
- if (!processIsLive(owner.pid))
283
+ // An exited, unreaped owner (state Z) makes no more writes: the claim is gone (senpi#4119).
284
+ if (!processIsRunning(owner.pid))
284
285
  return "gone";
285
286
  const recorded = owner.processStartTime === null ? undefined : processStartTimeMs(owner.processStartTime);
286
287
  if (recorded === undefined) {
@@ -5,6 +5,7 @@ import type { ResolvedHostLaunchSpec } from "./host-launch-spec.ts";
5
5
  import { type HostOutcome } from "./host-outcome.ts";
6
6
  import type { HostStopGeneration } from "./host-stop-generation.ts";
7
7
  export { HOST_EXIT_ERROR, HOST_EXIT_FALLBACK, HOST_EXIT_OK, HOST_EXIT_REFUSED, HOST_EXIT_USAGE, type HostOutcome, } from "./host-outcome.ts";
8
+ import type { Duplex } from "node:stream";
8
9
  /** Which daemon a request is about: the endpoint, and the agent directory holding its state. */
9
10
  export interface HostTarget {
10
11
  readonly socket: string;
@@ -50,5 +51,6 @@ export type HostRequest = {
50
51
  };
51
52
  export declare function runHostRequest(request: HostRequest, options?: {
52
53
  readonly keyRoot?: string;
54
+ readonly presenterBootstrap?: Duplex;
53
55
  }): Promise<HostOutcome>;
54
56
  //# sourceMappingURL=host-runner.d.ts.map
@@ -37,9 +37,9 @@ import { hostRequestKeyRoot } from "./host-request-key-root.js";
37
37
  import { withRpcKeyRoot } from "./rpc-auth-client.js";
38
38
  const PROBE_TIMEOUT_MS = 10_000;
39
39
  export async function runHostRequest(request, options = {}) {
40
- return withRpcKeyRoot(hostRequestKeyRoot(request, options.keyRoot), () => runHostRequestInKeyRoot(request));
40
+ return withRpcKeyRoot(hostRequestKeyRoot(request, options.keyRoot), () => runHostRequestInKeyRoot(request, options.presenterBootstrap));
41
41
  }
42
- async function runHostRequestInKeyRoot(request) {
42
+ async function runHostRequestInKeyRoot(request, presenterBootstrap) {
43
43
  if (request.action === "ensure" || request.action === "handoff" || request.action === "stop") {
44
44
  const { socket, agentDir } = request.target;
45
45
  if ((await endpointKindOfSocket(socket, agentDir)) === "tui") {
@@ -48,7 +48,7 @@ async function runHostRequestInKeyRoot(request) {
48
48
  }
49
49
  switch (request.action) {
50
50
  case "ensure":
51
- return ensureOutcome(request.target, request.spec, request.policy);
51
+ return ensureOutcome(request.target, request.spec, request.policy, presenterBootstrap);
52
52
  case "status":
53
53
  return request.all === true
54
54
  ? statusAllOutcome(request.target, request.includeWorkers)
@@ -57,7 +57,7 @@ async function runHostRequestInKeyRoot(request) {
57
57
  return stopOutcome(request);
58
58
  case "handoff":
59
59
  return request.handover === undefined
60
- ? handoffOutcome(request.target, request.spec)
60
+ ? handoffOutcome(request.target, request.spec, presenterBootstrap)
61
61
  : idleHandoverOutcome(request.target, request.spec, request.handover);
62
62
  case "shard_path":
63
63
  return shardPathOutcome(request.kind, request.owner, request.root);
@@ -70,7 +70,7 @@ async function runHostRequestInKeyRoot(request) {
70
70
  return assertNever(request);
71
71
  }
72
72
  }
73
- async function ensureOutcome(target, spec, policy) {
73
+ async function ensureOutcome(target, spec, policy, presenterBootstrap) {
74
74
  const before = await probeProtocolInfo(target.socket, PROBE_TIMEOUT_MS);
75
75
  if (policy === "fallback") {
76
76
  // `fallback` is the caller saying it can live without a host. The decision is made HERE
@@ -84,6 +84,7 @@ async function ensureOutcome(target, spec, policy) {
84
84
  try {
85
85
  ensured = await ensureHost({
86
86
  socket: target.socket,
87
+ presenterBootstrap,
87
88
  agentDir: target.agentDir,
88
89
  hostArgs: spec.hostArgs,
89
90
  env: daemonEnvOverrides(process.env, spec.env),
@@ -181,10 +182,11 @@ async function stopOutcome(request) {
181
182
  },
182
183
  };
183
184
  }
184
- async function handoffOutcome(target, spec) {
185
+ async function handoffOutcome(target, spec, presenterBootstrap) {
185
186
  const before = await probeProtocolInfo(target.socket, PROBE_TIMEOUT_MS);
186
187
  const result = await handoffHost({
187
188
  socket: target.socket,
189
+ presenterBootstrap,
188
190
  agentDir: target.agentDir,
189
191
  hostArgs: spec.hostArgs,
190
192
  env: daemonEnvOverrides(process.env, spec.env),
@@ -132,7 +132,8 @@ export async function hostChildAlive(generation) {
132
132
  if (!processIsLive(pid))
133
133
  return false;
134
134
  const observed = await readProcessIdentity(pid, process.platform, 1_000, processIsLive, "UTC");
135
- if (!processIsLive(pid))
135
+ // `absent` includes an exited, unreaped child (state Z), read in the same async snapshot (senpi#4119).
136
+ if (observed.kind === "absent" || !processIsLive(pid))
136
137
  return false;
137
138
  const recordedMs = typeof record?.processStartTime === "string" ? processStartTimeMs(record.processStartTime) : undefined;
138
139
  const observedMs = observed.kind === "present" ? processStartTimeMs(observed.identity) : undefined;
@@ -28,6 +28,7 @@ import { defaultHostLaunch, PINNED_HOST_CLIENT_CAPABILITIES } from "./host-launc
28
28
  import { DEFAULT_HOST_IDLE_EXIT_MS } from "./host-lifecycle.js";
29
29
  import { keepOwnerPipe, OWNER_WATCH_FD } from "./host-lifecycle-launch.js";
30
30
  import { probeProtocolInfo } from "./host-probe.js";
31
+ import { relayPresenterBootstrap } from "./mcp-presenter-bootstrap.js";
31
32
  import { successorHostEnvironment } from "./host-spawn-environment.js";
32
33
  import { signalGeneration } from "./host-stop.js";
33
34
  import { hostLaunchProfile } from "./protocol-identity.js";
@@ -81,6 +82,7 @@ export async function startSuccessor(context) {
81
82
  "--replace",
82
83
  `${replaced.dev}:${replaced.ino}`,
83
84
  ...(inheritOwnerPipe ? ["--owner-fd", String(OWNER_WATCH_FD)] : []),
85
+ ...(options.presenterBootstrap ? ["--mcp-presenter-fd", "4"] : []),
84
86
  ...(options.hostArgs ?? []),
85
87
  ];
86
88
  const launch = (options._test?.launch ?? options.launch)?.(argv) ?? defaultHostLaunch(argv);
@@ -96,8 +98,15 @@ export async function startSuccessor(context) {
96
98
  generation,
97
99
  instanceId,
98
100
  }),
99
- stdio: inheritOwnerPipe ? ["ignore", "ignore", stderr.fd, "pipe"] : ["ignore", "ignore", stderr.fd],
101
+ stdio: options.presenterBootstrap
102
+ ? ["ignore", "ignore", stderr.fd, inheritOwnerPipe ? "pipe" : "ignore", "pipe"]
103
+ : inheritOwnerPipe
104
+ ? ["ignore", "ignore", stderr.fd, "pipe"]
105
+ : ["ignore", "ignore", stderr.fd],
100
106
  });
107
+ const presenterEnrollment = options.presenterBootstrap
108
+ ? relayPresenterBootstrap(options.presenterBootstrap, spawned)
109
+ : undefined;
101
110
  child = spawned;
102
111
  exited = new Promise((resolve) => spawned.once("exit", () => resolve()));
103
112
  await stderr.close();
@@ -135,6 +144,7 @@ export async function startSuccessor(context) {
135
144
  // clients reach is still the predecessor, and the pointer has to name whoever owns the socket.
136
145
  await options._test?.beforeRegistration?.();
137
146
  await writeHostRegistration(paths, { ...registration, ...successorBuild(answer) });
147
+ await presenterEnrollment;
138
148
  child.unref();
139
149
  // The successor owns the socket now: the predecessor may drain. SIGUSR1 is sent only here,
140
150
  // to a pid the record proved and a host that advertised it can survive the signal. A
@@ -0,0 +1,45 @@
1
+ import type { McpFormInput, McpFormOutcome, McpFormRequest, McpFormResolved } from "../../core/mcp-form-types.ts";
2
+ export interface McpFormClock {
3
+ now(): number;
4
+ schedule(callback: () => void, delay: number): object;
5
+ cancel(timer: object): void;
6
+ }
7
+ export declare const mcpFormClock: McpFormClock;
8
+ type Termination = "presentation_lost" | "presentation_timeout" | "operation_aborted";
9
+ export type McpPresenterReply = {
10
+ readonly ok: true;
11
+ readonly form?: McpFormRequest;
12
+ } | {
13
+ readonly error: "presenter_not_authorized" | "invalid_form_response" | "form_stale" | "form_expired";
14
+ };
15
+ /** One private entry. Lifecycle closures retain this object even after its grant is removed. */
16
+ export declare class McpFormDialog {
17
+ readonly result: Promise<McpFormOutcome>;
18
+ readonly deadline: number;
19
+ private stage;
20
+ private request;
21
+ private readonly token;
22
+ private readonly cleanups;
23
+ private resolve;
24
+ private readonly input;
25
+ private readonly context;
26
+ constructor(input: McpFormInput, context: {
27
+ readonly clock: McpFormClock;
28
+ readonly envelope: Omit<McpFormRequest, "type" | "server" | "message" | "schema" | "deadlineAtMs" | "dialogToken">;
29
+ readonly current: () => boolean;
30
+ readonly remove: () => void;
31
+ readonly admit: () => void;
32
+ readonly send: (frame: McpFormRequest | McpFormResolved) => boolean;
33
+ });
34
+ start(): void;
35
+ sync(): McpFormRequest | undefined;
36
+ matches(token: string, revision: string): boolean;
37
+ presented(): McpPresenterReply;
38
+ respond(action: string, content: unknown): McpPresenterReply;
39
+ /** Internal-only: never called with a cause or entry reference decoded from RPC. */
40
+ terminate(cause: Termination): void;
41
+ private live;
42
+ private commit;
43
+ }
44
+ export {};
45
+ //# sourceMappingURL=mcp-form-dialog.d.ts.map
@@ -0,0 +1,159 @@
1
+ import { randomBytes, timingSafeEqual } from "node:crypto";
2
+ import { validMcpFormContent } from "../../core/extensions/builtin/mcp/modern/form-schema.js";
3
+ export const mcpFormClock = {
4
+ now: () => performance.now(),
5
+ schedule: (callback, delay) => setTimeout(callback, delay),
6
+ cancel: (timer) => {
7
+ if ("close" in timer && typeof timer.close === "function")
8
+ timer.close();
9
+ },
10
+ };
11
+ /** One private entry. Lifecycle closures retain this object even after its grant is removed. */
12
+ export class McpFormDialog {
13
+ constructor(input, context) {
14
+ this.stage = "prepared";
15
+ this.token = randomBytes(32);
16
+ this.cleanups = [];
17
+ this.resolve = () => { };
18
+ this.input = input;
19
+ this.context = context;
20
+ this.deadline = context.clock.now() + Math.max(0, input.deadlineMs);
21
+ this.result = new Promise((resolve) => {
22
+ this.resolve = resolve;
23
+ });
24
+ this.request = {
25
+ ...context.envelope,
26
+ type: "mcp_form_request",
27
+ dialogToken: this.token.toString("hex"),
28
+ server: structuredClone(input.server),
29
+ message: input.message,
30
+ schema: structuredClone(input.schema),
31
+ deadlineAtMs: Date.now() + Math.max(0, input.deadlineMs),
32
+ };
33
+ }
34
+ start() {
35
+ const watch = (signal, cause) => {
36
+ const stop = () => this.terminate(cause);
37
+ signal.addEventListener("abort", stop, { once: true });
38
+ this.cleanups.push(() => signal.removeEventListener("abort", stop));
39
+ if (signal.aborted)
40
+ stop();
41
+ };
42
+ watch(this.input.operationSignal, "operation_aborted");
43
+ watch(this.input.presentationSignal, "presentation_lost");
44
+ if (this.stage === "settled") {
45
+ for (const cleanup of this.cleanups.splice(0))
46
+ cleanup();
47
+ return;
48
+ }
49
+ const timer = this.context.clock.schedule(() => this.terminate("presentation_timeout"), this.input.deadlineMs);
50
+ this.cleanups.push(() => this.context.clock.cancel(timer));
51
+ if (!this.context.current() || !this.request || !this.context.send(this.request))
52
+ this.terminate("presentation_lost");
53
+ }
54
+ sync() {
55
+ if (!this.live())
56
+ return undefined;
57
+ return this.request === undefined ? undefined : structuredClone(this.request);
58
+ }
59
+ matches(token, revision) {
60
+ return (this.request?.formRevision === revision &&
61
+ /^[a-f0-9]{64}$/.test(token) &&
62
+ timingSafeEqual(this.token, Buffer.from(token, "hex")));
63
+ }
64
+ presented() {
65
+ if (!this.live())
66
+ return { error: "form_expired" };
67
+ this.stage = "presented";
68
+ return { ok: true };
69
+ }
70
+ respond(action, content) {
71
+ if (!this.live())
72
+ return { error: "form_expired" };
73
+ if (this.stage !== "presented" || !this.request)
74
+ return { error: "invalid_form_response" };
75
+ let result;
76
+ switch (action) {
77
+ case "accept":
78
+ if (!validMcpFormContent(this.request.schema, content) ||
79
+ Buffer.byteLength(JSON.stringify(content)) > 1024 * 1024)
80
+ return { error: "invalid_form_response" };
81
+ result = { action, content: structuredClone(content) };
82
+ break;
83
+ case "decline":
84
+ case "cancel":
85
+ if (content !== undefined)
86
+ return { error: "invalid_form_response" };
87
+ result = { action };
88
+ break;
89
+ default:
90
+ return { error: "invalid_form_response" };
91
+ }
92
+ if (!this.live())
93
+ return { error: "form_expired" };
94
+ this.commit({ kind: "result", result, source: "presenter_action", humanAdmission: true });
95
+ return { ok: true };
96
+ }
97
+ /** Internal-only: never called with a cause or entry reference decoded from RPC. */
98
+ terminate(cause) {
99
+ const provenance = { source: "host_termination", humanAdmission: false };
100
+ if (cause === "operation_aborted" ||
101
+ this.input.operationSignal.aborted ||
102
+ this.input.operationCurrent?.() === false) {
103
+ this.commit({ kind: "operation_aborted", ...provenance });
104
+ }
105
+ else if (this.stage === "presented") {
106
+ this.commit({ kind: "result", result: { action: "cancel" }, ...provenance });
107
+ }
108
+ else {
109
+ this.commit({ kind: "processing_failure", ...provenance });
110
+ }
111
+ }
112
+ live() {
113
+ if (this.stage === "settled")
114
+ return false;
115
+ if (this.input.operationSignal.aborted || this.input.operationCurrent?.() === false) {
116
+ this.terminate("operation_aborted");
117
+ return false;
118
+ }
119
+ if (this.context.clock.now() >= this.deadline || !this.context.current()) {
120
+ this.terminate("presentation_timeout");
121
+ return false;
122
+ }
123
+ return true;
124
+ }
125
+ commit(outcome) {
126
+ if (this.stage === "settled")
127
+ return;
128
+ this.stage = "settled";
129
+ const request = this.request;
130
+ this.request = undefined;
131
+ this.token.fill(0);
132
+ this.context.remove();
133
+ for (const cleanup of this.cleanups.splice(0))
134
+ cleanup();
135
+ // No callback, promise resolution or admission precedes the synchronous terminal claim.
136
+ if (outcome.humanAdmission)
137
+ this.context.admit();
138
+ this.resolve(outcome);
139
+ if (request && this.context.current()) {
140
+ const reason = outcome.kind === "result"
141
+ ? outcome.result.action === "accept"
142
+ ? "answered"
143
+ : outcome.result.action === "decline"
144
+ ? "declined"
145
+ : "cancelled"
146
+ : outcome.kind === "operation_aborted"
147
+ ? "operation_aborted"
148
+ : "processing_failed";
149
+ this.context.send({
150
+ type: "mcp_form_resolved",
151
+ sessionId: request.sessionId,
152
+ grantId: request.grantId,
153
+ dialogId: request.dialogId,
154
+ reason,
155
+ });
156
+ }
157
+ }
158
+ }
159
+ //# sourceMappingURL=mcp-form-dialog.js.map
@@ -0,0 +1,14 @@
1
+ import type { HostToSessionWorker, SessionWorkerToHost } from "./session-worker-protocol.ts";
2
+ /** Private worker messages never pass through WorkerEventWriter.enqueue or a snapshot. */
3
+ export declare class McpFormWorker {
4
+ private readonly send;
5
+ private readonly binds;
6
+ private readonly ready;
7
+ private readonly settlements;
8
+ private readonly bindings;
9
+ private readonly entries;
10
+ constructor(send: (message: SessionWorkerToHost) => void);
11
+ bind(durableId: string, ui: object, admit: () => void): Promise<() => void>;
12
+ receive(message: HostToSessionWorker): boolean;
13
+ }
14
+ //# sourceMappingURL=mcp-form-worker.d.ts.map