@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
@@ -1,4 +1,4 @@
1
- import type { ExtensionAPI } from "../../../types.ts";
1
+ import type { ExtensionAPI, ExtensionUIContext } from "../../../types.ts";
2
2
  import type { ToolSearchService } from "../../tool-search/service.ts";
3
3
  import type { McpCachedServerCatalog } from "../catalog-cache.ts";
4
4
  import type { ResolvedMcpConfig } from "../config-schema.ts";
@@ -7,6 +7,7 @@ import type { McpOutputArtifacts } from "../guard/output-guard.ts";
7
7
  import type { McpInvocationResolver } from "../invocation.ts";
8
8
  import { type McpLogger } from "../log.ts";
9
9
  import type { McpPromptServer } from "../prompts.ts";
10
+ import type { McpRequestOrigin } from "../request-origin.ts";
10
11
  import { type McpResourceServer } from "../resources.ts";
11
12
  import type { McpCatalogRegistrationOptions } from "./register.ts";
12
13
  import { type McpTierBRegistration } from "./tier-b.ts";
@@ -15,6 +16,8 @@ export type McpSessionRegistration = McpTierBRegistration & {
15
16
  promptServers: McpPromptServer[];
16
17
  };
17
18
  export interface McpDirectRegistrationEntry {
19
+ readonly requestOrigin?: () => McpRequestOrigin | undefined;
20
+ readonly bindPresentation?: (ui: ExtensionUIContext) => void;
18
21
  readonly name: string;
19
22
  readonly connection: ServerConnection;
20
23
  readonly logger: McpLogger;
@@ -24,6 +24,7 @@ export async function registerDirectMcpTools(pi, config, entries, toolSearchServ
24
24
  artifacts: entry.artifacts,
25
25
  ensureFresh: entry.ensureFresh,
26
26
  invocation: entry.invocation,
27
+ requestOrigin: entry.requestOrigin,
27
28
  outputGuard: config.settings.outputGuard,
28
29
  })
29
30
  : []
@@ -32,6 +33,7 @@ export async function registerDirectMcpTools(pi, config, entries, toolSearchServ
32
33
  artifacts: entry.artifacts,
33
34
  ensureFresh: entry.ensureFresh,
34
35
  invocation: entry.invocation,
36
+ requestOrigin: entry.requestOrigin,
35
37
  outputGuard: config.settings.outputGuard,
36
38
  });
37
39
  const identity = mcpRegistrationIdentity(catalog, entry.cachedCatalog);
@@ -39,6 +41,9 @@ export async function registerDirectMcpTools(pi, config, entries, toolSearchServ
39
41
  const cachedPrompts = entry.cachedCatalog?.prompts ?? [];
40
42
  if (cachedPrompts.length > 0) {
41
43
  promptServers.push({
44
+ ensureFresh: entry.ensureFresh,
45
+ bindPresentation: entry.bindPresentation,
46
+ requestOrigin: entry.requestOrigin,
42
47
  connection: entry.connection,
43
48
  prompts: cachedPrompts,
44
49
  requestTimeoutMs: server.config.requestTimeoutMs,
@@ -48,6 +53,8 @@ export async function registerDirectMcpTools(pi, config, entries, toolSearchServ
48
53
  const cachedResources = entry.cachedCatalog?.resources ?? [];
49
54
  if (cachedResources.length > 0) {
50
55
  resourceServers.push({
56
+ ensureFresh: entry.ensureFresh,
57
+ requestOrigin: entry.requestOrigin,
51
58
  agentDir: entry.agentDir,
52
59
  artifacts: entry.artifacts,
53
60
  connection: entry.connection,
@@ -1,6 +1,7 @@
1
1
  import { AuthError, ConnectError, isMcpSessionExpiredError, isRetriableMcpError, SessionExpiredError, } from "./errors.js";
2
2
  import { isMcpNeedsAuthError } from "./needs-auth.js";
3
3
  import { reconnectMcpNow } from "./reconnect.js";
4
+ import { McpMrtrError } from "./modern/errors.js";
4
5
  export const MCP_PING_STALE_MS = 30_000;
5
6
  export const MCP_PING_TIMEOUT_MS = 2_000;
6
7
  const healthByConnection = new WeakMap();
@@ -46,6 +47,8 @@ export async function withMcpSessionExpiryRetry(connection, operation) {
46
47
  return await operation();
47
48
  }
48
49
  catch (error) {
50
+ if (error instanceof McpMrtrError)
51
+ throw error;
49
52
  if (!isMcpSessionExpiredError(error))
50
53
  throw error;
51
54
  connection.markDegraded(sessionExpiredError(connection, error, true));
@@ -67,6 +70,8 @@ export async function withMcpRetriableFailedSendRetry(connection, operation) {
67
70
  return await operation();
68
71
  }
69
72
  catch (error) {
73
+ if (error instanceof McpMrtrError)
74
+ throw error;
70
75
  if (!isRetriableFailedSendError(error))
71
76
  throw error;
72
77
  connection.markDegraded(failedSendError(connection, error, true));
@@ -1,3 +1,4 @@
1
+ import { legacyMcpFormEligible } from "./elicitation.js";
1
2
  import { observe } from "./env-grants-store.js";
2
3
  import { effectiveGrantNames } from "./project-skill-policy.js";
3
4
  import { SharedMcpConnection } from "./shared-connection.js";
@@ -17,7 +18,12 @@ export class HostMcpRegistry {
17
18
  #shared = new Map();
18
19
  #grantObservers = new Map();
19
20
  attachShared(key, owner, options) {
20
- const identity = sharedMcpKey(options, options.agentDir);
21
+ const eligible = legacyMcpFormEligible(options.elicitationUiProvider, options.elicitationEnabled !== false);
22
+ // Enforced pools observe live readiness; it must not remain frozen in a creation-time key.
23
+ const formPolicy = options.elicitationUiProvider?.enforced?.() === true
24
+ ? `private:${options.elicitationEnabled !== false}`
25
+ : `${eligible}:false`;
26
+ const identity = `${sharedMcpKey(options, options.agentDir)}\0legacy-form:${formPolicy}`;
21
27
  let shared = this.#shared.get(identity);
22
28
  if (shared === undefined) {
23
29
  shared = new SharedMcpConnection(options, () => this.#shared.delete(identity));
@@ -61,6 +61,7 @@ export function createMcpExtension(service, sessionOwned = true) {
61
61
  service.activateSkillMcpTools(targets, pi);
62
62
  };
63
63
  pi.on("input", async (event, ctx) => {
64
+ service.bindMcpMrtrPresentation(pi, ctx.ui);
64
65
  const match = /^\s*\/skill:([A-Za-z0-9._-]+)/.exec(event.text);
65
66
  if (match)
66
67
  revealSkill(match[1]);
@@ -125,6 +126,7 @@ export function createMcpExtension(service, sessionOwned = true) {
125
126
  // Elicitation (todo 41): point mid-call forms at this session's UI.
126
127
  service.setMcpElicitationUiProvider(() => (ctx.hasUI ? ctx.ui : undefined));
127
128
  await (attachPromise ?? attach({ type: "session_start", reason: "startup" }, ctx));
129
+ service.bindMcpMrtrPresentation(pi, ctx.ui);
128
130
  }
129
131
  const skills = preview ? [] : (event.systemPromptOptions.skills ?? []);
130
132
  if (skills.length > 0) {
@@ -0,0 +1,5 @@
1
+ import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { type McpElicitationUiProvider } from "../elicitation.ts";
3
+ import { type ModernProtocolState } from "./request.ts";
4
+ export declare function createMcpRequestClient(protocol: ModernProtocolState, uiProvider: McpElicitationUiProvider | undefined, readCredential?: () => string | undefined, elicitationEnabled?: boolean): Promise<Client>;
5
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1,15 @@
1
+ import { configureMcpElicitation, legacyMcpFormEligible, mcpClientCapabilities, } from "../elicitation.js";
2
+ import { loadMcpSdkClient } from "../sdk.lazy.js";
3
+ import { mcpElapsedClock } from "../wrap.js";
4
+ import { installModernRequestAdapter } from "./request.js";
5
+ export async function createMcpRequestClient(protocol, uiProvider, readCredential = () => undefined, elicitationEnabled = true) {
6
+ const { Client } = await loadMcpSdkClient();
7
+ protocol.privateForms = uiProvider?.enforced?.() === true;
8
+ const legacyEligible = legacyMcpFormEligible(uiProvider, elicitationEnabled);
9
+ protocol.legacyFormEligible = legacyEligible;
10
+ const client = new Client({ name: "senpi-mcp-client", version: "0.0.0" }, { capabilities: mcpClientCapabilities(legacyEligible) });
11
+ await configureMcpElicitation(client, uiProvider, undefined, legacyEligible);
12
+ installModernRequestAdapter(client, protocol, mcpElapsedClock, readCredential);
13
+ return client;
14
+ }
15
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1,2 @@
1
+ export declare function queuedForm<T>(binding: object, signal: AbortSignal, run: () => Promise<T>): Promise<T>;
2
+ //# sourceMappingURL=consent-queue.d.ts.map
@@ -0,0 +1,59 @@
1
+ import { McpMrtrError } from "./errors.js";
2
+ const queues = new WeakMap();
3
+ export function queuedForm(binding, signal, run) {
4
+ let queue = queues.get(binding);
5
+ if (!queue) {
6
+ queue = { busy: false, jobs: [] };
7
+ queues.set(binding, queue);
8
+ }
9
+ const current = queue;
10
+ const pump = () => {
11
+ if (current.busy)
12
+ return;
13
+ const next = current.jobs.shift();
14
+ if (!next) {
15
+ queues.delete(binding);
16
+ return;
17
+ }
18
+ next.signal.removeEventListener("abort", next.abort);
19
+ if (next.signal.aborted) {
20
+ next.reject(new McpMrtrError("MCP_MRTR_PROCESSING_UNAVAILABLE"));
21
+ pump();
22
+ return;
23
+ }
24
+ current.busy = true;
25
+ void next.run().finally(() => {
26
+ current.busy = false;
27
+ pump();
28
+ });
29
+ };
30
+ return new Promise((resolve, reject) => {
31
+ const job = {
32
+ signal,
33
+ run: async () => {
34
+ try {
35
+ resolve(await run());
36
+ }
37
+ catch (error) {
38
+ reject(error);
39
+ }
40
+ },
41
+ reject,
42
+ abort: () => { },
43
+ };
44
+ job.abort = () => {
45
+ const index = current.jobs.indexOf(job);
46
+ if (index >= 0) {
47
+ current.jobs.splice(index, 1);
48
+ signal.removeEventListener("abort", job.abort);
49
+ reject(new McpMrtrError("MCP_MRTR_PROCESSING_UNAVAILABLE"));
50
+ }
51
+ };
52
+ current.jobs.push(job);
53
+ signal.addEventListener("abort", job.abort, { once: true });
54
+ if (signal.aborted)
55
+ job.abort();
56
+ pump();
57
+ });
58
+ }
59
+ //# sourceMappingURL=consent-queue.js.map
@@ -0,0 +1,4 @@
1
+ import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
2
+ /** Control/diagnostic payloads are private from admission, not merely after consent starts. */
3
+ export declare function sanitizeModernMessage(message: JSONRPCMessage): JSONRPCMessage | undefined;
4
+ //# sourceMappingURL=diagnostics.d.ts.map
@@ -0,0 +1,39 @@
1
+ import { McpMrtrError } from "./errors.js";
2
+ /** Control/diagnostic payloads are private from admission, not merely after consent starts. */
3
+ export function sanitizeModernMessage(message) {
4
+ if ("error" in message)
5
+ return {
6
+ jsonrpc: "2.0",
7
+ id: message.id,
8
+ error: { code: message.error.code, message: "MCP server request failed." },
9
+ };
10
+ if (!("method" in message))
11
+ return message;
12
+ if ("id" in message)
13
+ throw new McpMrtrError("MCP_MRTR_INVALID_RESULT");
14
+ if (message.method === "notifications/progress") {
15
+ const params = message.params;
16
+ if (!params ||
17
+ typeof params.progress !== "number" ||
18
+ !Number.isFinite(params.progress) ||
19
+ (params.total !== undefined && (typeof params.total !== "number" || !Number.isFinite(params.total))))
20
+ return undefined;
21
+ return {
22
+ jsonrpc: "2.0",
23
+ method: message.method,
24
+ params: {
25
+ progressToken: params.progressToken,
26
+ progress: params.progress,
27
+ ...(params.total === undefined ? {} : { total: params.total }),
28
+ },
29
+ };
30
+ }
31
+ if (message.method === "notifications/message")
32
+ return {
33
+ jsonrpc: "2.0",
34
+ method: message.method,
35
+ params: { level: "info", data: "MCP server diagnostic suppressed." },
36
+ };
37
+ return message;
38
+ }
39
+ //# sourceMappingURL=diagnostics.js.map
@@ -1,19 +1,22 @@
1
- import { configureMcpElicitation, mcpClientCapabilities } from "../elicitation.js";
2
1
  import { ConnectError } from "../errors.js";
3
- import { loadMcpSdkClient } from "../sdk.lazy.js";
4
2
  import { materializeMcpTransport, } from "../transport-sdk.js";
5
3
  import { probeHttpEra } from "./era.js";
6
- import { modernHttpFetch } from "./http-fetch.js";
4
+ import { modernCredentialSnapshot, modernHttpFetch } from "./http-fetch.js";
5
+ import { createMcpRequestClient } from "./client.js";
7
6
  import { ModernSdkBridge } from "./sdk-bridge.js";
8
7
  import { probeStdioTransport } from "./stdio-probe.js";
9
8
  import { ModernHttpTransport } from "./transport.js";
10
9
  import { MODERN_PROTOCOL_VERSION } from "./wire.js";
11
10
  const clientInfo = { name: "senpi-mcp-client", version: "0.0.0" };
11
+ const probeOptions = {
12
+ clientInfo,
13
+ clientCapabilities: {},
14
+ supportedVersions: [MODERN_PROTOCOL_VERSION],
15
+ };
12
16
  export async function materializeNegotiatedTransport(options, serverName, timeoutMs, signal) {
13
17
  signal.throwIfAborted();
14
- const clientCapabilities = mcpClientCapabilities(options.elicitationEnabled);
15
- const probeOptions = { clientInfo, clientCapabilities, supportedVersions: [MODERN_PROTOCOL_VERSION] };
16
18
  const { spec, logger } = options;
19
+ const protocol = {};
17
20
  let outcome = { kind: "legacy-fallback" };
18
21
  if (spec.kind === "http") {
19
22
  const fetch = modernHttpFetch(spec, signal);
@@ -28,14 +31,13 @@ export async function materializeNegotiatedTransport(options, serverName, timeou
28
31
  serverName,
29
32
  });
30
33
  }
31
- const { Client } = await loadMcpSdkClient();
32
- const client = new Client(clientInfo, { capabilities: clientCapabilities });
33
- await configureMcpElicitation(client, options.elicitationUiProvider, undefined, options.elicitationEnabled);
34
+ protocol.version = outcome.version;
35
+ const client = await createMcpRequestClient(protocol, options.elicitationUiProvider, () => modernCredentialSnapshot(spec.authProvider?.tokens()), options.elicitationEnabled);
34
36
  const transport = new ModernSdkBridge(new ModernHttpTransport({
35
37
  url: spec.url,
36
- fetch,
38
+ fetch: modernHttpFetch(spec, signal, false),
37
39
  clientInfo,
38
- clientCapabilities,
40
+ clientCapabilities: {},
39
41
  protocolVersion: outcome.version,
40
42
  }), outcome.discovery, outcome.version);
41
43
  const version = outcome.version;
@@ -59,12 +61,20 @@ export async function materializeNegotiatedTransport(options, serverName, timeou
59
61
  }
60
62
  }
61
63
  // Unknown endpoints (including legacy session-required 404s and auth challenges) retain SDK diagnostics.
62
- const legacy = await materializeMcpTransport(options);
64
+ if (spec.kind === "http")
65
+ protocol.legacy = true;
66
+ const legacy = await materializeMcpTransport(options, protocol);
63
67
  // A modern stdio child is detected when the SDK starts it, before its local initialize answer sets the version.
64
68
  let stdioModernVersion;
65
69
  if (spec.kind === "stdio") {
66
70
  probeStdioTransport(legacy.transport, probeOptions, logger, timeoutMs, serverName, (version) => {
67
71
  stdioModernVersion = version;
72
+ protocol.version = version;
73
+ protocol.activateRequests?.();
74
+ protocol.resolveStdioDiagnostics?.(true);
75
+ }, undefined, () => {
76
+ protocol.legacy = true;
77
+ protocol.resolveStdioDiagnostics?.(false);
68
78
  });
69
79
  }
70
80
  logVersion(legacy.transport, () => stdioModernVersion);
@@ -0,0 +1,14 @@
1
+ import type { McpMrtrPresentation } from "../../../../mcp-mrtr-presentations.ts";
2
+ import { type Value } from "./form-schema.ts";
3
+ export type MrtrInputResponse = {
4
+ action: "accept";
5
+ content: Record<string, Value>;
6
+ } | {
7
+ action: "decline" | "cancel";
8
+ };
9
+ /** No model-facing questions, RPC requests, timers, persistence, or network operations. */
10
+ export declare function collectMrtrForm(presentation: McpMrtrPresentation, serverLabel: string, params: {
11
+ message: string;
12
+ requestedSchema: unknown;
13
+ }, signal: AbortSignal): Promise<MrtrInputResponse>;
14
+ //# sourceMappingURL=elicitation.d.ts.map
@@ -0,0 +1,145 @@
1
+ import { stripVTControlCharacters } from "node:util";
2
+ import { bindMcpFormPresented } from "../../../../mcp-form-types.js";
3
+ import { McpMrtrError } from "./errors.js";
4
+ import { enums, isRecord, validValue, validateMrtrForm } from "./form-schema.js";
5
+ const own = (value, key) => Object.hasOwn(value, key);
6
+ const inert = (value) => stripVTControlCharacters(value).replace(/[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/g, "");
7
+ /** No model-facing questions, RPC requests, timers, persistence, or network operations. */
8
+ export async function collectMrtrForm(presentation, serverLabel, params, signal) {
9
+ if (!validateMrtrForm(params.requestedSchema))
10
+ throw new McpMrtrError("MCP_MRTR_INVALID_INPUT");
11
+ const schema = params.requestedSchema;
12
+ const abort = new AbortController();
13
+ const combined = AbortSignal.any([signal, presentation.signal, abort.signal]);
14
+ const opts = { signal: combined, ephemeral: true };
15
+ bindMcpFormPresented(opts, presentation.presented);
16
+ const heading = `MCP server: ${inert(serverLabel)}\n${inert(params.message)}`;
17
+ const select = async (title, choices) => {
18
+ if (combined.aborted)
19
+ return undefined;
20
+ const answer = presentation.ui.select(title, choices, opts);
21
+ const result = await answer;
22
+ return combined.aborted ? undefined : result;
23
+ };
24
+ const content = Object.create(null);
25
+ const finish = (result) => {
26
+ if (!combined.aborted)
27
+ presentation.admit?.();
28
+ return result;
29
+ };
30
+ const required = new Set(schema.required ?? []);
31
+ const entries = Object.entries(schema.properties);
32
+ const field = async (name, property) => {
33
+ const title = `${heading}\n${inert(String(property.title ?? name))}${typeof property.description === "string" ? `\n${inert(property.description)}` : ""}`;
34
+ for (;;) {
35
+ const actions = ["Enter value"];
36
+ if (!required.has(name))
37
+ actions.push("Leave unset");
38
+ if (own(property, "default"))
39
+ actions.push("Use default");
40
+ // Primitive selectors already require an explicit choice; do not conflate false with dismissal.
41
+ let action = "Enter value";
42
+ if (actions.length > 1) {
43
+ const chosen = await select(title, actions);
44
+ if (chosen === undefined)
45
+ return false;
46
+ action = chosen;
47
+ }
48
+ if (action === "Leave unset") {
49
+ delete content[name];
50
+ return true;
51
+ }
52
+ if (action === "Use default") {
53
+ content[name] = structuredClone(property.default);
54
+ return true;
55
+ }
56
+ let value;
57
+ if (property.type === "boolean") {
58
+ const chosen = await select(title, ["true", "false"]);
59
+ if (chosen === undefined)
60
+ return false;
61
+ value = chosen === "true";
62
+ }
63
+ else if (property.type === "array" || enums(property)) {
64
+ const choices = property.type === "array" && isRecord(property.items) ? enums(property.items) : enums(property);
65
+ if (choices === undefined)
66
+ return false;
67
+ const values = [];
68
+ for (;;) {
69
+ const labels = choices.map((choice, index) => `${index + 1}. ${inert(choice.label)}${values.includes(choice.value) ? " [selected]" : ""}`);
70
+ if (property.type === "array")
71
+ labels.push("Done");
72
+ const chosen = await select(title, labels);
73
+ if (chosen === undefined)
74
+ return false;
75
+ if (chosen === "Done") {
76
+ value = values;
77
+ break;
78
+ }
79
+ const index = labels.indexOf(chosen);
80
+ const choice = choices[index];
81
+ if (!choice)
82
+ continue;
83
+ if (property.type !== "array") {
84
+ value = choice.value;
85
+ break;
86
+ }
87
+ const existing = values.indexOf(choice.value);
88
+ if (existing >= 0)
89
+ values.splice(existing, 1);
90
+ else
91
+ values.push(choice.value);
92
+ }
93
+ }
94
+ else {
95
+ if (combined.aborted)
96
+ return false;
97
+ const text = await presentation.ui.input(title, undefined, opts);
98
+ if (text === undefined || combined.aborted)
99
+ return false;
100
+ value =
101
+ property.type === "number" || property.type === "integer"
102
+ ? text.trim() === ""
103
+ ? NaN
104
+ : Number(text)
105
+ : text;
106
+ }
107
+ if (validValue(property, value)) {
108
+ content[name] = value;
109
+ return true;
110
+ }
111
+ if ((await select(`${title}\nValue does not satisfy this field.`, ["Try again", "Cancel"])) !== "Try again")
112
+ return false;
113
+ }
114
+ };
115
+ try {
116
+ const begin = await select(heading, ["Continue", "Decline", "Cancel"]);
117
+ if (begin !== "Continue")
118
+ return finish({ action: begin === "Decline" ? "decline" : "cancel" });
119
+ for (const [name, property] of entries)
120
+ if (!(await field(name, property)))
121
+ return finish({ action: "cancel" });
122
+ for (;;) {
123
+ const review = entries
124
+ .map(([name]) => `${inert(name)}: ${own(content, name) ? inert(JSON.stringify(content[name])) : "(unset)"}`)
125
+ .join("\n");
126
+ const action = await select(`${heading}\nReview\n${review}`, ["Submit", "Edit field", "Decline", "Cancel"]);
127
+ if (action === "Submit" && !combined.aborted)
128
+ return finish({ action: "accept", content: structuredClone(content) });
129
+ if (action !== "Edit field")
130
+ return finish({ action: action === "Decline" ? "decline" : "cancel" });
131
+ const labels = entries.map(([name], index) => `${index + 1}. ${inert(name)}`);
132
+ const chosen = await select(`${heading}\nEdit field`, labels);
133
+ const entry = chosen === undefined ? undefined : entries[labels.indexOf(chosen)];
134
+ if (!entry || !(await field(...entry)))
135
+ return finish({ action: "cancel" });
136
+ }
137
+ }
138
+ finally {
139
+ bindMcpFormPresented(opts, undefined);
140
+ abort.abort();
141
+ for (const key of Object.keys(content))
142
+ delete content[key];
143
+ }
144
+ }
145
+ //# sourceMappingURL=elicitation.js.map
@@ -0,0 +1,21 @@
1
+ declare const messages: {
2
+ readonly MCP_MRTR_ROUND_LIMIT: "Request stopped after 8 input rounds; start a new request to continue.";
3
+ readonly MCP_MRTR_INPUT_LIMIT: "Server input exceeds the client's supported limits.";
4
+ readonly MCP_MRTR_INVALID_INPUT: "Server returned an invalid input request or form schema.";
5
+ readonly MCP_MRTR_UNSUPPORTED_INPUT: "Server requested an undeclared client feature.";
6
+ readonly MCP_MRTR_PROCESSING_UNAVAILABLE: "Elicitation processing unavailable.";
7
+ readonly MCP_MRTR_INVALID_RESULT: "Server returned an unsupported or invalid result type/shape.";
8
+ readonly MCP_MRTR_CONTEXT_CHANGED: "Request stopped because its session or server authorization changed.";
9
+ readonly MCP_MRTR_CANCELLED: "Request was canceled before completion.";
10
+ readonly MCP_MRTR_TIMEOUT: "Request exceeded its operation deadline.";
11
+ readonly MCP_MRTR_TRANSPORT_FAILED: "Request could not complete; it was not automatically replayed.";
12
+ };
13
+ export declare class McpMrtrError extends Error {
14
+ readonly source = "local";
15
+ readonly retriable = false;
16
+ readonly code: keyof typeof messages;
17
+ readonly remoteCode?: number;
18
+ constructor(code: keyof typeof messages, remoteCode?: number);
19
+ }
20
+ export {};
21
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1,23 @@
1
+ const messages = {
2
+ MCP_MRTR_ROUND_LIMIT: "Request stopped after 8 input rounds; start a new request to continue.",
3
+ MCP_MRTR_INPUT_LIMIT: "Server input exceeds the client's supported limits.",
4
+ MCP_MRTR_INVALID_INPUT: "Server returned an invalid input request or form schema.",
5
+ MCP_MRTR_UNSUPPORTED_INPUT: "Server requested an undeclared client feature.",
6
+ MCP_MRTR_PROCESSING_UNAVAILABLE: "Elicitation processing unavailable.",
7
+ MCP_MRTR_INVALID_RESULT: "Server returned an unsupported or invalid result type/shape.",
8
+ MCP_MRTR_CONTEXT_CHANGED: "Request stopped because its session or server authorization changed.",
9
+ MCP_MRTR_CANCELLED: "Request was canceled before completion.",
10
+ MCP_MRTR_TIMEOUT: "Request exceeded its operation deadline.",
11
+ MCP_MRTR_TRANSPORT_FAILED: "Request could not complete; it was not automatically replayed.",
12
+ };
13
+ export class McpMrtrError extends Error {
14
+ constructor(code, remoteCode) {
15
+ super(remoteCode === undefined ? messages[code] : `${messages[code]} (remote code ${remoteCode})`);
16
+ this.source = "local";
17
+ this.retriable = false;
18
+ this.code = code;
19
+ this.remoteCode = remoteCode;
20
+ this.name = "McpMrtrError";
21
+ }
22
+ }
23
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1,20 @@
1
+ export type Value = string | number | boolean | string[];
2
+ export type Property = Record<string, unknown>;
3
+ export type ValidatedMcpFormSchema = Form;
4
+ type Form = {
5
+ type: "object";
6
+ properties: Record<string, Property>;
7
+ required?: string[];
8
+ };
9
+ export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
10
+ export declare function enums(property: Property): Array<{
11
+ value: string;
12
+ label: string;
13
+ }> | undefined;
14
+ export declare function validValue(property: Property, value: unknown): value is Value;
15
+ /** Validate a complete submitted form, including unknown keys and omitted required fields. */
16
+ export declare function validMcpFormContent(schema: Form, content: unknown): content is Record<string, Value>;
17
+ /** Reject unsupported constraints rather than presenting a weaker schema. */
18
+ export declare function validateMrtrForm(schema: unknown): schema is Form;
19
+ export {};
20
+ //# sourceMappingURL=form-schema.d.ts.map