@code-yeongyu/senpi 2026.10.26-1 → 2026.10.26-2

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 (148) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/bundle/chunks/{anthropic-messages-M235FJQV.js → anthropic-messages-ODSFJCXR.js} +1 -1
  3. package/dist/bundle/chunks/{args-FBNJOJSR.js → args-SWKJAYQD.js} +1 -1
  4. package/dist/bundle/chunks/{azure-openai-responses-YQRTQ7H6.js → azure-openai-responses-REGXS4XO.js} +1 -1
  5. package/dist/bundle/chunks/bedrock-converse-stream.js +1 -1
  6. package/dist/bundle/chunks/chunk-2XF6W6F6.js +410 -0
  7. package/dist/bundle/chunks/{chunk-DMFAVE5N.js → chunk-4QVORFQ2.js} +1 -1
  8. package/dist/bundle/chunks/{chunk-ZXHLH4MW.js → chunk-5W3DTM2J.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-QREKAXKX.js → chunk-6GAMRAVM.js} +5 -5
  10. package/dist/bundle/chunks/{chunk-MSHRIZGT.js → chunk-7IZH2KQX.js} +3 -3
  11. package/dist/bundle/chunks/{chunk-PH3JDRB2.js → chunk-ARSENVDY.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-5KZH6EYQ.js → chunk-CNDPCYNT.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-DYWIFF2Q.js → chunk-CYOHFREV.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-3LPWILUZ.js → chunk-DNDZPKIQ.js} +1 -1
  15. package/dist/bundle/chunks/{chunk-ZLIVZQDY.js → chunk-EQNUL2WB.js} +1 -1
  16. package/dist/bundle/chunks/{chunk-5FQMNHXV.js → chunk-ESNSRUW4.js} +1 -1
  17. package/dist/bundle/chunks/{chunk-LO5MYLGF.js → chunk-FQDARFHT.js} +1 -1
  18. package/dist/bundle/chunks/{chunk-7KOUQSBX.js → chunk-GPPOPK76.js} +3 -3
  19. package/dist/bundle/chunks/chunk-I2WFLKZ2.js +5 -0
  20. package/dist/bundle/chunks/{chunk-I7QWTWVZ.js → chunk-IHKHCWWG.js} +1 -1
  21. package/dist/bundle/chunks/{chunk-MY4ZD3EW.js → chunk-IY4G44MN.js} +1 -1
  22. package/dist/bundle/chunks/{chunk-OQ3WDMB3.js → chunk-JH7MVRNJ.js} +1 -1
  23. package/dist/bundle/chunks/chunk-KBSFTXAT.js +2 -0
  24. package/dist/bundle/chunks/{chunk-UW6M3EXY.js → chunk-KRO63T4Q.js} +1 -1
  25. package/dist/bundle/chunks/{chunk-J5FCH7TI.js → chunk-KTDIIF2F.js} +1 -1
  26. package/dist/bundle/chunks/{chunk-3WP5RFQR.js → chunk-NDNIMC5W.js} +1 -1
  27. package/dist/bundle/chunks/{chunk-CDQPA4FP.js → chunk-ODRGPC5S.js} +1 -1
  28. package/dist/bundle/chunks/{chunk-3A3XCLNH.js → chunk-PDG4XWO2.js} +1 -1
  29. package/dist/bundle/chunks/{chunk-QYFOHFPS.js → chunk-PPECSSWX.js} +1 -1
  30. package/dist/bundle/chunks/{chunk-5LWUUXMH.js → chunk-PY4ZQHTN.js} +1 -1
  31. package/dist/bundle/chunks/{chunk-EZP6AYW2.js → chunk-RPW3XQW3.js} +22 -22
  32. package/dist/bundle/chunks/{chunk-SOXQ42VS.js → chunk-RQ5JTXUC.js} +1 -1
  33. package/dist/bundle/chunks/{chunk-JFQUWEN7.js → chunk-TQ67PB7U.js} +2 -2
  34. package/dist/bundle/chunks/{chunk-6U3TBFBA.js → chunk-UJPG4ROT.js} +1 -1
  35. package/dist/bundle/chunks/{chunk-UDM7VO6O.js → chunk-XM36IFP3.js} +1 -1
  36. package/dist/bundle/chunks/{chunk-XQ45JMGN.js → chunk-Z6BREXV3.js} +1 -1
  37. package/dist/bundle/chunks/{chunk-ADQAPDPV.js → chunk-ZP6JJDYV.js} +1 -1
  38. package/dist/bundle/chunks/{cli-main-55EQA5PS.js → cli-main-Q2GA53XM.js} +1 -1
  39. package/dist/bundle/chunks/{cloudflare-workers-ai-system-one-ZJQGIIRC.js → cloudflare-workers-ai-system-one-WDHT7BBV.js} +1 -1
  40. package/dist/bundle/chunks/cursor-agent.js +1 -1
  41. package/dist/bundle/chunks/devin-agent.js +1 -1
  42. package/dist/bundle/chunks/{dispatch-C2IBP5VV.js → dispatch-5R3TYAYB.js} +3 -3
  43. package/dist/bundle/chunks/{google-generative-ai-5G4RIQA6.js → google-generative-ai-FHYFXKLA.js} +1 -1
  44. package/dist/bundle/chunks/{google-vertex-ALD4P27O.js → google-vertex-5DWE5U5K.js} +1 -1
  45. package/dist/bundle/chunks/{help-fast-path-PEIKGCKZ.js → help-fast-path-FETLIF3W.js} +1 -1
  46. package/dist/bundle/chunks/{host-command-657VCQ34.js → host-command-XEB7SHT3.js} +1 -1
  47. package/dist/bundle/chunks/{host-session-command-OV5XDOJK.js → host-session-command-FFN52LXJ.js} +1 -1
  48. package/dist/bundle/chunks/{interactive-mode-MLXSYK2H.js → interactive-mode-5DEKY3YW.js} +1 -1
  49. package/dist/bundle/chunks/{mistral-conversations-RAE4K2XY.js → mistral-conversations-I2PI7DDF.js} +1 -1
  50. package/dist/bundle/chunks/{multi-session-host-774MACJU.js → multi-session-host-OPR5VBFO.js} +3 -3
  51. package/dist/bundle/chunks/{openai-codex-responses-3CW4R7TG.js → openai-codex-responses-WSEX6SH7.js} +1 -1
  52. package/dist/bundle/chunks/{openai-completions-5UIJXQUF.js → openai-completions-YCBHTBSS.js} +1 -1
  53. package/dist/bundle/chunks/{openai-responses-2BO44MRW.js → openai-responses-GXNSQF4A.js} +1 -1
  54. package/dist/bundle/chunks/{package-manager-cli-MZHVCX2S.js → package-manager-cli-W55E3LHP.js} +1 -1
  55. package/dist/bundle/chunks/{pi-messages-RF6GER4R.js → pi-messages-O6GISIRW.js} +1 -1
  56. package/dist/bundle/chunks/{role-ETSFSDTS.js → role-SHIZQKK4.js} +1 -1
  57. package/dist/bundle/chunks/{rotation-stream-ZMQB4GPO.js → rotation-stream-2PCZWNKF.js} +1 -1
  58. package/dist/bundle/chunks/{rpc-mode-UJXOIZCW.js → rpc-mode-IDUJTSYS.js} +1 -1
  59. package/dist/bundle/chunks/{schedule-command-KA7LFFNK.js → schedule-command-B7RRSANU.js} +1 -1
  60. package/dist/bundle/chunks/{session-control-endpoint-MFWXUR7Z.js → session-control-endpoint-LMZH5JLH.js} +1 -1
  61. package/dist/bundle/chunks/{session-picker-BP33NRSL.js → session-picker-DTGBOXDR.js} +1 -1
  62. package/dist/bundle/chunks/session-worker.js +41 -41
  63. package/dist/bundle/chunks/{typesafe-system-one-GNNZ5O5U.js → typesafe-system-one-XGYJJVSE.js} +1 -1
  64. package/dist/bundle/cli.js +2 -2
  65. package/dist/bundle/index.js +1 -1
  66. package/dist/bundle/rpc-entry.js +1 -1
  67. package/dist/bundle/runtime-manifest.json +1 -1
  68. package/dist/core/agent-session.d.ts +11 -1
  69. package/dist/core/agent-session.js +19 -2
  70. package/dist/core/extensions/builtin/compaction/index.js +7 -1
  71. package/dist/core/extensions/builtin/herdr/index.js +9 -5
  72. package/dist/core/extensions/builtin/mcp/auth/oauth-issuer.d.ts +6 -0
  73. package/dist/core/extensions/builtin/mcp/auth/oauth-issuer.js +25 -0
  74. package/dist/core/extensions/builtin/mcp/auth/oauth-provider.d.ts +3 -3
  75. package/dist/core/extensions/builtin/mcp/auth/oauth-provider.js +50 -19
  76. package/dist/core/extensions/builtin/mcp/auth/oauth-refresh.js +4 -3
  77. package/dist/core/extensions/builtin/mcp/auth/oauth.js +19 -12
  78. package/dist/core/extensions/builtin/mcp/modern/dispatch.js +1 -0
  79. package/dist/core/extensions/builtin/mcp/modern/parameter-headers.d.ts +4 -1
  80. package/dist/core/extensions/builtin/mcp/modern/parameter-headers.js +33 -35
  81. package/dist/core/extensions/builtin/mcp/modern/request.js +3 -0
  82. package/dist/core/extensions/builtin/mcp/modern/transport.d.ts +2 -0
  83. package/dist/core/extensions/builtin/mcp/modern/transport.js +4 -2
  84. package/dist/core/extensions/builtin/terminal/extension-state.js +2 -1
  85. package/dist/core/extensions/builtin/vertex-model-garden/auth.d.ts +9 -0
  86. package/dist/core/extensions/builtin/vertex-model-garden/auth.js +17 -1
  87. package/dist/core/extensions/builtin/websearch/websearch/providers/google.d.ts +6 -0
  88. package/dist/core/extensions/builtin/websearch/websearch/providers/google.js +6 -1
  89. package/dist/core/extensions/types.d.ts +2 -0
  90. package/dist/core/lifecycle-children.d.ts +3 -1
  91. package/dist/core/lifecycle-children.js +79 -15
  92. package/dist/core/lifecycle-record.js +1 -24
  93. package/dist/core/lifecycle-session-binding.js +2 -1
  94. package/dist/core/lifecycle-wait-items.d.ts +17 -0
  95. package/dist/core/lifecycle-wait-items.js +72 -0
  96. package/dist/core/model-runtime.js +6 -1
  97. package/dist/core/provider-api-key-auth.d.ts +6 -1
  98. package/dist/core/provider-api-key-auth.js +11 -4
  99. package/dist/core/provider-composer.d.ts +8 -1
  100. package/dist/core/provider-composer.js +7 -3
  101. package/dist/core/sdk.js +4 -1
  102. package/dist/core/session-activity.js +2 -0
  103. package/dist/core/session-lifecycle-types.d.ts +20 -4
  104. package/dist/core/session-lifecycle.js +35 -4
  105. package/dist/core/structured-session-lane.d.ts +33 -0
  106. package/dist/core/structured-session-lane.js +62 -0
  107. package/dist/core/work-custody-record.d.ts +2 -1
  108. package/dist/core/work-custody-record.js +2 -0
  109. package/dist/core/work-obligations.d.ts +1 -1
  110. package/dist/core/work-obligations.js +8 -2
  111. package/dist/modes/json-event.d.ts +79 -1
  112. package/dist/modes/json-event.js +79 -1
  113. package/dist/modes/rpc/compact-content-index.d.ts +85 -0
  114. package/dist/modes/rpc/compact-content-index.js +254 -0
  115. package/dist/modes/rpc/compact-content-store.d.ts +6 -43
  116. package/dist/modes/rpc/compact-content-store.js +22 -157
  117. package/dist/modes/rpc/compact-index-pages.d.ts +12 -0
  118. package/dist/modes/rpc/compact-index-pages.js +31 -0
  119. package/dist/modes/rpc/compact-session-state.d.ts +11 -39
  120. package/dist/modes/rpc/compact-session-state.js +17 -26
  121. package/dist/modes/rpc/compact-spool-dir.d.ts +4 -1
  122. package/dist/modes/rpc/compact-spool-dir.js +41 -8
  123. package/dist/modes/rpc/compact-spool-storage.d.ts +53 -0
  124. package/dist/modes/rpc/compact-spool-storage.js +162 -0
  125. package/dist/modes/rpc/compact-storage-io.d.ts +2 -1
  126. package/dist/modes/rpc/compact-storage-io.js +7 -1
  127. package/dist/modes/rpc/compact-transition.d.ts +41 -0
  128. package/dist/modes/rpc/compact-transition.js +12 -0
  129. package/dist/modes/rpc/connection-handler.d.ts +2 -0
  130. package/dist/modes/rpc/connection-handler.js +12 -6
  131. package/dist/modes/rpc/host-ensure-start.js +5 -3
  132. package/dist/modes/rpc/security-presentations.js +2 -1
  133. package/dist/modes/rpc/session-binding.js +10 -0
  134. package/dist/modes/rpc/session-event-writer.d.ts +2 -0
  135. package/dist/modes/rpc/session-event-writer.js +2 -0
  136. package/dist/modes/rpc/session-record-stream.d.ts +3 -3
  137. package/dist/modes/rpc/session-record-stream.js +3 -3
  138. package/dist/modes/rpc/session-structured-lanes.d.ts +12 -0
  139. package/dist/modes/rpc/session-structured-lanes.js +24 -0
  140. package/dist/modes/rpc/session-worker-client.js +6 -5
  141. package/dist/modes/rpc/structured-session-records.d.ts +12 -0
  142. package/dist/modes/rpc/structured-session-records.js +88 -0
  143. package/docs/extensions.md +7 -1
  144. package/docs/rpc.md +17 -2
  145. package/package.json +6 -6
  146. package/dist/bundle/chunks/chunk-GQJYNFMO.js +0 -2
  147. package/dist/bundle/chunks/chunk-VICDRG3J.js +0 -410
  148. package/dist/bundle/chunks/chunk-VL7O5PO4.js +0 -5
@@ -1,6 +1,7 @@
1
1
  import { loadMcpSdkAuth } from "../sdk.lazy.js";
2
2
  import { isInvalidGrant, OAuthFlowError } from "./oauth-errors.js";
3
3
  import { oauthFetch } from "./oauth-fetch.js";
4
+ import { validatedAuthorizationServerIssuer } from "./oauth-issuer.js";
4
5
  import { assertS256Supported } from "./oauth-refresh.js";
5
6
  // Pre-flight: discover the authorization server and REFUSE (typed) if it does
6
7
  // not advertise PKCE S256. Runs before any authorize redirect.
@@ -20,7 +21,7 @@ export async function beginAuthorization(provider, options = {}) {
20
21
  }
21
22
  // Paste flow: validates single-use state itself, then exchanges the code.
22
23
  export async function completeAuthorization(provider, redirectInput, options = {}) {
23
- const { code, state, iss } = parseRedirect(redirectInput, provider.serverName);
24
+ const { code, state, iss } = parseRedirect(redirectInput, provider);
24
25
  if (!provider.consumeState(state)) {
25
26
  throw new OAuthFlowError("state_mismatch", `MCP server ${provider.serverName} authorization state did not match (possible CSRF or a stale/replayed link); restart with /mcp auth-start.`, { serverName: provider.serverName });
26
27
  }
@@ -56,15 +57,19 @@ export async function finishAuthorization(provider, code, options = {}, iss) {
56
57
  // RFC 9207: an authorization response that names another authorization server, or that omits `iss`
57
58
  // although this server promises to send it, may have been injected by a mix-up attack; never exchange its code.
58
59
  function assertAuthorizationResponseIssuer(provider, iss) {
59
- const metadata = provider.discoveryState()?.authorizationServerMetadata;
60
- if (metadata === undefined)
61
- return;
60
+ const discovery = provider.discoveryState();
61
+ if (discovery === undefined) {
62
+ throw new OAuthFlowError("needs_auth", `MCP server ${provider.serverName} has no trusted authorization discovery; restart with /mcp auth-start ${provider.serverName}.`, { serverName: provider.serverName });
63
+ }
64
+ const metadata = discovery.authorizationServerMetadata;
65
+ const issuer = validatedAuthorizationServerIssuer(discovery, provider.serverName);
62
66
  // The SDK keeps unknown metadata fields (loose schema) but does not type this RFC 9207 one.
63
- const promised = metadata
64
- .authorization_response_iss_parameter_supported === true;
67
+ const promised = metadata !== undefined &&
68
+ "authorization_response_iss_parameter_supported" in metadata &&
69
+ metadata.authorization_response_iss_parameter_supported === true;
65
70
  if (iss === undefined && !promised)
66
71
  return;
67
- if (iss !== undefined && iss === metadata.issuer)
72
+ if (iss !== undefined && iss === issuer)
68
73
  return;
69
74
  throw new OAuthFlowError("needs_auth", iss === undefined
70
75
  ? `MCP server ${provider.serverName} authorization response has no issuer although its authorization server promises one; restart with /mcp auth-start ${provider.serverName}.`
@@ -111,19 +116,19 @@ export async function clientCredentialsGrant(provider, options = {}) {
111
116
  resource: new URL(provider.serverUrl),
112
117
  fetchFn: oauthFetch(options.fetchFn),
113
118
  });
114
- await provider.saveTokens({ ...tokens, issuer: String(info.authorizationServerUrl) });
119
+ await provider.saveTokens({ ...tokens, issuer: validatedAuthorizationServerIssuer(info, provider.serverName) });
115
120
  }
116
121
  export async function logout(provider) {
117
122
  await provider.store.clear();
118
123
  }
119
124
  async function discover(provider, options) {
120
- if (options.discover !== undefined)
121
- return options.discover(provider.serverUrl);
122
125
  const cached = provider.discoveryState();
123
126
  if (cached !== undefined)
124
127
  return cached;
125
128
  const { discoverOAuthServerInfo } = await loadMcpSdkAuth();
126
- const info = await discoverOAuthServerInfo(provider.serverUrl, { fetchFn: oauthFetch(options.fetchFn) });
129
+ const info = options.discover !== undefined
130
+ ? await options.discover(provider.serverUrl)
131
+ : await discoverOAuthServerInfo(provider.serverUrl, { fetchFn: oauthFetch(options.fetchFn) });
127
132
  await provider.saveDiscoveryState(toDiscoveryState(info));
128
133
  return info;
129
134
  }
@@ -134,7 +139,8 @@ function toDiscoveryState(info) {
134
139
  resourceMetadata: info.resourceMetadata,
135
140
  };
136
141
  }
137
- function parseRedirect(input, serverName) {
142
+ function parseRedirect(input, provider) {
143
+ const serverName = provider.serverName;
138
144
  let url;
139
145
  try {
140
146
  url = new URL(input.trim());
@@ -142,6 +148,7 @@ function parseRedirect(input, serverName) {
142
148
  catch {
143
149
  throw new OAuthFlowError("needs_auth", `MCP server ${serverName} received a malformed redirect URL; paste the full http://127.0.0.1/... address from your browser.`, { serverName });
144
150
  }
151
+ assertAuthorizationResponseIssuer(provider, url.searchParams.get("iss") ?? undefined);
145
152
  const error = url.searchParams.get("error");
146
153
  if (error !== null) {
147
154
  throw new OAuthFlowError("needs_auth", `MCP server ${serverName} authorization failed: ${error}`, { serverName });
@@ -39,6 +39,7 @@ export async function materializeNegotiatedTransport(options, serverName, timeou
39
39
  clientInfo,
40
40
  clientCapabilities: {},
41
41
  protocolVersion: outcome.version,
42
+ logger,
42
43
  }), outcome.discovery, outcome.version);
43
44
  const version = outcome.version;
44
45
  logVersion(transport, () => version);
@@ -2,10 +2,13 @@
2
2
  export declare function encodeMcpHeaderValue(value: string): string;
3
3
  /** Configured headers must never supply parameter metadata absent from the request body. */
4
4
  export declare function clearMcpParameterHeaders(headers: Headers): void;
5
+ /** A local argument error, not a failed transport or an untrusted server diagnostic. */
6
+ export declare class ParameterHeaderValidationError extends Error {
7
+ }
5
8
  /** Connection-local catalog: never shared with another server or transport generation. */
6
9
  export declare class ModernParameterHeaders {
7
10
  #private;
8
- filterTools(tools: readonly unknown[], firstPage: boolean): unknown[];
11
+ filterTools(tools: readonly unknown[], firstPage: boolean, warn: (message: string) => void): unknown[];
9
12
  append(headers: Headers, name: unknown, args: unknown): void;
10
13
  }
11
14
  //# sourceMappingURL=parameter-headers.d.ts.map
@@ -18,22 +18,31 @@ export function clearMcpParameterHeaders(headers) {
18
18
  function parseParameterHeaders(schema) {
19
19
  const headers = [];
20
20
  const names = new Set();
21
+ let reason = "";
21
22
  const visit = (node, path) => {
23
+ if (Array.isArray(node))
24
+ return node.every((child) => visit(child, undefined));
22
25
  if (!isRecord(node))
23
26
  return true;
24
27
  if (Object.hasOwn(node, "x-mcp-header")) {
25
28
  const name = node["x-mcp-header"];
26
29
  const type = node.type;
27
- if (!path?.length ||
28
- typeof name !== "string" ||
29
- !/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(name) ||
30
- names.has(name.toLowerCase()) ||
31
- (type !== "string" && type !== "integer" && type !== "boolean"))
30
+ if (!path?.length)
31
+ reason = "x-mcp-header must be on a properties-only path";
32
+ else if (typeof name !== "string" || !/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(name))
33
+ reason = "x-mcp-header must be a nonempty HTTP field-name token";
34
+ else if (names.has(name.toLowerCase()))
35
+ reason = "x-mcp-header names must be case-insensitively unique";
36
+ else if (type !== "string" && type !== "integer" && type !== "boolean")
37
+ reason = "x-mcp-header type must be string, integer, or boolean";
38
+ else {
39
+ names.add(name.toLowerCase());
40
+ headers.push({ name, path, type });
41
+ }
42
+ if (reason)
32
43
  return false;
33
- names.add(name.toLowerCase());
34
- headers.push({ name, path, type });
35
44
  }
36
- // Only schema-valued keywords are traversed: examples/default/const are instance data.
45
+ // "Anywhere else" includes unknown keywords; instance-data keywords are not schemas.
37
46
  for (const [keyword, value] of Object.entries(node)) {
38
47
  if (["properties", "patternProperties", "$defs", "definitions", "dependentSchemas", "dependencies"].includes(keyword)) {
39
48
  if (!isRecord(value))
@@ -44,46 +53,31 @@ function parseParameterHeaders(schema) {
44
53
  return false;
45
54
  }
46
55
  }
47
- else if ([
48
- "items",
49
- "prefixItems",
50
- "allOf",
51
- "anyOf",
52
- "oneOf",
53
- "additionalItems",
54
- "additionalProperties",
55
- "unevaluatedItems",
56
- "unevaluatedProperties",
57
- "contains",
58
- "propertyNames",
59
- "not",
60
- "if",
61
- "then",
62
- "else",
63
- "contentSchema",
64
- ].includes(keyword)) {
65
- for (const child of Array.isArray(value) ? value : [value]) {
66
- if (!visit(child, undefined))
67
- return false;
68
- }
56
+ else if (!["examples", "default", "const", "enum"].includes(keyword)) {
57
+ if (!visit(value, undefined))
58
+ return false;
69
59
  }
70
60
  }
71
61
  return true;
72
62
  };
73
- return visit(schema, []) ? headers : undefined;
63
+ return visit(schema, []) ? headers : reason;
64
+ }
65
+ /** A local argument error, not a failed transport or an untrusted server diagnostic. */
66
+ export class ParameterHeaderValidationError extends Error {
74
67
  }
75
68
  /** Connection-local catalog: never shared with another server or transport generation. */
76
69
  export class ModernParameterHeaders {
77
70
  #tools = new Map();
78
- filterTools(tools, firstPage) {
71
+ filterTools(tools, firstPage, warn) {
79
72
  if (firstPage)
80
73
  this.#tools.clear();
81
74
  return tools.filter((tool) => {
82
75
  if (!isRecord(tool) || typeof tool.name !== "string")
83
76
  return true;
84
77
  const headers = parseParameterHeaders(tool.inputSchema);
85
- if (headers === undefined) {
78
+ if (typeof headers === "string") {
86
79
  this.#tools.delete(tool.name);
80
+ warn(`Rejected MCP tool ${JSON.stringify(tool.name)}: ${headers}`);
87
81
  return false;
88
82
  }
89
83
  this.#tools.set(tool.name, headers);
@@ -98,11 +92,15 @@ export class ModernParameterHeaders {
98
92
  for (const key of parameter.path) {
99
93
  value = isRecord(value) && Object.hasOwn(value, key) ? value[key] : undefined;
100
94
  }
95
+ if (value === undefined)
96
+ continue;
101
97
  const valid = (parameter.type === "string" && typeof value === "string") ||
102
98
  (parameter.type === "boolean" && typeof value === "boolean") ||
103
99
  (parameter.type === "integer" && typeof value === "number" && Number.isSafeInteger(value));
104
- if (valid)
105
- headers.set(`Mcp-Param-${parameter.name}`, encodeMcpHeaderValue(String(value)));
100
+ if (!valid) {
101
+ throw new ParameterHeaderValidationError(`MCP tool ${JSON.stringify(name)} argument ${JSON.stringify(parameter.path)} must be ${parameter.type === "integer" ? "a safe integer" : `a ${parameter.type}`}`);
102
+ }
103
+ headers.set(`Mcp-Param-${parameter.name}`, encodeMcpHeaderValue(String(value)));
106
104
  }
107
105
  }
108
106
  }
@@ -7,6 +7,7 @@ import { isRecord, validateMrtrForm } from "./form-schema.js";
7
7
  import { McpMrtrError } from "./errors.js";
8
8
  import { queuedForm } from "./consent-queue.js";
9
9
  import { parseInputs } from "./inputs.js";
10
+ import { ParameterHeaderValidationError } from "./parameter-headers.js";
10
11
  export const MAX_MCP_MRTR_ROUNDS = 8;
11
12
  const capableMethods = new Set(["tools/call", "resources/read", "prompts/get"]);
12
13
  const capabilitiesKey = "io.modelcontextprotocol/clientCapabilities";
@@ -259,6 +260,8 @@ export function installModernRequestAdapter(client, protocol, clock = mcpElapsed
259
260
  throw error;
260
261
  if (error instanceof AuthError)
261
262
  throw error;
263
+ if (error instanceof ParameterHeaderValidationError)
264
+ throw error;
262
265
  const remoteCode = isRecord(error) && typeof error.code === "number" ? error.code : undefined;
263
266
  throw new McpMrtrError("MCP_MRTR_TRANSPORT_FAILED", remoteCode);
264
267
  }
@@ -1,6 +1,7 @@
1
1
  import type { FetchLike, Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
2
2
  import { type JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
3
3
  import { type ModernClientInfo } from "./wire.ts";
4
+ import type { McpLogger } from "../log.ts";
4
5
  export type ModernHttpTransportOptions = {
5
6
  readonly url: string | URL;
6
7
  readonly fetch: FetchLike;
@@ -8,6 +9,7 @@ export type ModernHttpTransportOptions = {
8
9
  readonly clientCapabilities: Readonly<Record<string, unknown>>;
9
10
  readonly protocolVersion?: string;
10
11
  readonly headers?: RequestInit["headers"];
12
+ readonly logger?: Pick<McpLogger, "warn">;
11
13
  };
12
14
  export declare class ModernTransportError extends Error {
13
15
  constructor(message: string);
@@ -4,7 +4,7 @@ import { AuthError } from "../errors.js";
4
4
  import { sanitizeModernMessage } from "./diagnostics.js";
5
5
  import { McpMrtrError } from "./errors.js";
6
6
  import { getModernDispatchGuard, modernDispatchGuard } from "./request.js";
7
- import { clearMcpParameterHeaders, ModernParameterHeaders } from "./parameter-headers.js";
7
+ import { clearMcpParameterHeaders, ModernParameterHeaders, ParameterHeaderValidationError, } from "./parameter-headers.js";
8
8
  export class ModernTransportError extends Error {
9
9
  constructor(message) {
10
10
  super(message);
@@ -119,6 +119,8 @@ export class ModernHttpTransport {
119
119
  }
120
120
  }
121
121
  catch (error) {
122
+ if (error instanceof ParameterHeaderValidationError)
123
+ throw error;
122
124
  const failure = error instanceof McpMrtrError || error instanceof AuthError
123
125
  ? error
124
126
  : new McpMrtrError("MCP_MRTR_TRANSPORT_FAILED");
@@ -142,7 +144,7 @@ export class ModernHttpTransport {
142
144
  ...incoming,
143
145
  result: {
144
146
  ...result,
145
- tools: this.#parameterHeaders.filterTools(result.tools, this.#toolLists.get(incoming.id) === true),
147
+ tools: this.#parameterHeaders.filterTools(result.tools, this.#toolLists.get(incoming.id) === true, (message) => this.#options.logger?.warn(message)),
146
148
  },
147
149
  };
148
150
  }
@@ -58,8 +58,9 @@ export function bundleSinks(pi, state) {
58
58
  pi.events?.emit(WAKE_SOURCE_STATE_EVENT, {
59
59
  source: "terminal-monitors",
60
60
  activeCount: snapshot.length,
61
+ // The stable "mon_" id: runtime ids churn on restore, and item ids feed the custody signature.
61
62
  monitors: snapshot.map((entry) => ({
62
- id: entry.id,
63
+ id: entry.monitorId ?? entry.id,
63
64
  description: entry.description,
64
65
  startedAtMs: entry.startedAtMs,
65
66
  })),
@@ -8,9 +8,18 @@ export declare const VERTEX_MODEL_GARDEN_ADC_SOURCE = "gcloud application defaul
8
8
  * gcloud tooling too, so the lane is selectable but never outranks a provider the
9
9
  * user configured, and it reports nothing while the chain is absent — the failure
10
10
  * belongs at selection time, not at the first request.
11
+ *
12
+ * The target project is resolved here so it rides the ambient auth result into
13
+ * the request env: process env first, then the sibling google-vertex profile,
14
+ * which shares this ADC chain and already names the project the user entered at
15
+ * its login. With neither source the lane stays available and the request-time
16
+ * ADC fallbacks (quota project, discovered project) decide, so a missing project
17
+ * remains a first-request failure with the actionable error, not a hidden lane.
11
18
  */
12
19
  export declare function resolveVertexModelGardenAmbient(input: {
13
20
  ctx: AuthContext;
14
21
  signal: AbortSignal;
22
+ /** Read-only env of another provider's stored credential; routing metadata only, never keys. */
23
+ storedProviderEnv?(providerId: string): Promise<Record<string, string> | undefined>;
15
24
  }): Promise<AuthResult | undefined>;
16
25
  //# sourceMappingURL=auth.d.ts.map
@@ -7,6 +7,13 @@ export const VERTEX_MODEL_GARDEN_ADC_SOURCE = "gcloud application default creden
7
7
  * gcloud tooling too, so the lane is selectable but never outranks a provider the
8
8
  * user configured, and it reports nothing while the chain is absent — the failure
9
9
  * belongs at selection time, not at the first request.
10
+ *
11
+ * The target project is resolved here so it rides the ambient auth result into
12
+ * the request env: process env first, then the sibling google-vertex profile,
13
+ * which shares this ADC chain and already names the project the user entered at
14
+ * its login. With neither source the lane stays available and the request-time
15
+ * ADC fallbacks (quota project, discovered project) decide, so a missing project
16
+ * remains a first-request failure with the actionable error, not a hidden lane.
10
17
  */
11
18
  export async function resolveVertexModelGardenAmbient(input) {
12
19
  input.signal.throwIfAborted();
@@ -14,6 +21,15 @@ export async function resolveVertexModelGardenAmbient(input) {
14
21
  input.signal.throwIfAborted();
15
22
  if (!(await input.ctx.fileExists(path)))
16
23
  return undefined;
17
- return { auth: {}, source: VERTEX_MODEL_GARDEN_ADC_SOURCE, ambient: true };
24
+ input.signal.throwIfAborted();
25
+ const project = (await input.ctx.env("GOOGLE_CLOUD_PROJECT")) ??
26
+ (await input.ctx.env("GCLOUD_PROJECT")) ??
27
+ (await input.storedProviderEnv?.("google-vertex"))?.GOOGLE_CLOUD_PROJECT;
28
+ return {
29
+ auth: {},
30
+ ...(project ? { env: { GOOGLE_CLOUD_PROJECT: project } } : {}),
31
+ source: VERTEX_MODEL_GARDEN_ADC_SOURCE,
32
+ ambient: true,
33
+ };
18
34
  }
19
35
  //# sourceMappingURL=auth.js.map
@@ -1,3 +1,9 @@
1
1
  import type { ProviderModule } from "./shared.ts";
2
+ /**
3
+ * Catalog GA flash alias: it tracks the current stable flash model, so the default
4
+ * cannot age into a retired generation (gemini-2.5-flash 404s for new accounts).
5
+ * An entry-level `model` still overrides it.
6
+ */
7
+ export declare const DEFAULT_MODEL = "gemini-flash-latest";
2
8
  export declare const googleProvider: ProviderModule;
3
9
  //# sourceMappingURL=google.d.ts.map
@@ -1,6 +1,11 @@
1
1
  import { providerUrl } from "../provider-endpoints.js";
2
2
  import { appendDomainFilters, collect, contentHeaders, getArray, getNumber, getObject, getString, result, withConfigHeaders, } from "./shared.js";
3
- const DEFAULT_MODEL = "gemini-2.5-flash";
3
+ /**
4
+ * Catalog GA flash alias: it tracks the current stable flash model, so the default
5
+ * cannot age into a retired generation (gemini-2.5-flash 404s for new accounts).
6
+ * An entry-level `model` still overrides it.
7
+ */
8
+ export const DEFAULT_MODEL = "gemini-flash-latest";
4
9
  function generateContentUrl(config) {
5
10
  const root = providerUrl(config).replace(/\/+$/, "");
6
11
  if (root.endsWith(":generateContent"))
@@ -2369,6 +2369,8 @@ export interface ProviderConfig {
2369
2369
  resolve(input: {
2370
2370
  ctx: AuthContext;
2371
2371
  signal: AbortSignal;
2372
+ /** Read-only env of another provider's stored credential; routing metadata only, never keys. */
2373
+ storedProviderEnv?(providerId: string): Promise<Record<string, string> | undefined>;
2372
2374
  }): Promise<AuthResult | undefined>;
2373
2375
  };
2374
2376
  /** OAuth provider for /login support. The `id` is set automatically from the provider name. */
@@ -1,4 +1,4 @@
1
- import type { ChildLifecycleReport, SessionLifecycleState } from "./session-lifecycle-types.ts";
1
+ import type { ChildLifecycleReport, LifecycleWaits, SessionLifecycleState } from "./session-lifecycle-types.ts";
2
2
  import type { SessionEntry } from "./session-manager.ts";
3
3
  export interface ChildLifecycleStore {
4
4
  readonly entries: readonly SessionEntry[];
@@ -15,4 +15,6 @@ export declare class LifecycleChildren {
15
15
  private accept;
16
16
  }
17
17
  export declare function readLifecycleState(value: unknown): SessionLifecycleState | undefined;
18
+ /** The one reader of a durable or reported `waitingOn`; named detail never invalidates the counts. */
19
+ export declare function readWaits(value: unknown): LifecycleWaits | undefined;
18
20
  //# sourceMappingURL=lifecycle-children.d.ts.map
@@ -1,3 +1,5 @@
1
+ import { readHumanWait } from "./lifecycle-attention.js";
2
+ import { readWaitItems } from "./lifecycle-wait-items.js";
1
3
  import { isRecord } from "./work-custody-record.js";
2
4
  export class LifecycleChildren {
3
5
  constructor(store) {
@@ -60,6 +62,80 @@ export function readLifecycleState(value) {
60
62
  return undefined;
61
63
  }
62
64
  }
65
+ /** The one reader of a durable or reported `waitingOn`; named detail never invalidates the counts. */
66
+ export function readWaits(value) {
67
+ if (!isRecord(value) || typeof value.children !== "number")
68
+ return undefined;
69
+ const background = [];
70
+ if (Array.isArray(value.background)) {
71
+ for (const source of value.background) {
72
+ if (!isRecord(source) || typeof source.source !== "string" || typeof source.activeCount !== "number")
73
+ continue;
74
+ background.push({
75
+ source: source.source,
76
+ activeCount: source.activeCount,
77
+ ...readWaitItems(source.items, source.omittedItems),
78
+ ...(typeof source.retryAtMs === "number" ? { retryAtMs: source.retryAtMs } : {}),
79
+ });
80
+ }
81
+ }
82
+ const human = Array.isArray(value.human)
83
+ ? value.human.flatMap((request) => {
84
+ const wait = readHumanWait(request);
85
+ return wait ? [wait] : [];
86
+ })
87
+ : [];
88
+ const childSources = [];
89
+ if (Array.isArray(value.childSources))
90
+ for (const source of value.childSources) {
91
+ if (!isRecord(source) || typeof source.source !== "string" || typeof source.activeCount !== "number")
92
+ continue;
93
+ childSources.push({
94
+ source: source.source,
95
+ activeCount: source.activeCount,
96
+ ...readWaitItems(source.items, source.omittedItems),
97
+ });
98
+ }
99
+ const reports = [];
100
+ if (Array.isArray(value.childReports))
101
+ for (const report of value.childReports) {
102
+ if (!isRecord(report))
103
+ continue;
104
+ const state = readLifecycleState(report.state);
105
+ if (!state ||
106
+ typeof report.taskId !== "string" ||
107
+ typeof report.childSessionId !== "string" ||
108
+ typeof report.runEpoch !== "number" ||
109
+ !Number.isSafeInteger(report.runEpoch) ||
110
+ report.runEpoch < 0)
111
+ continue;
112
+ reports.push({
113
+ taskId: report.taskId,
114
+ childSessionId: report.childSessionId,
115
+ runEpoch: report.runEpoch,
116
+ state,
117
+ ...(typeof report.label === "string" ? { label: report.label } : {}),
118
+ ...(typeof report.since === "string" && Number.isFinite(Date.parse(report.since))
119
+ ? { since: report.since }
120
+ : {}),
121
+ });
122
+ }
123
+ reports.sort((a, b) => (a.taskId < b.taskId ? -1 : a.taskId > b.taskId ? 1 : 0));
124
+ const childReportsOmitted = Math.max(0, reports.length - 32) +
125
+ (typeof value.childReportsOmitted === "number" &&
126
+ Number.isSafeInteger(value.childReportsOmitted) &&
127
+ value.childReportsOmitted > 0
128
+ ? value.childReportsOmitted
129
+ : 0);
130
+ return {
131
+ children: value.children,
132
+ background,
133
+ human,
134
+ ...(childSources.length ? { childSources } : {}),
135
+ ...(reports.length ? { childReports: reports.slice(0, 32) } : {}),
136
+ ...(childReportsOmitted ? { childReportsOmitted } : {}),
137
+ };
138
+ }
63
139
  function readChildReport(value) {
64
140
  if (!isRecord(value))
65
141
  return undefined;
@@ -75,27 +151,15 @@ function readChildReport(value) {
75
151
  value.generation < 0)
76
152
  return undefined;
77
153
  const terminal = readTerminal(value.terminal);
78
- let waitingOn;
79
- if (isRecord(value.waitingOn) && typeof value.waitingOn.children === "number") {
80
- const background = [];
81
- if (Array.isArray(value.waitingOn.background))
82
- for (const item of value.waitingOn.background) {
83
- if (!isRecord(item) || typeof item.source !== "string" || typeof item.activeCount !== "number")
84
- continue;
85
- background.push({
86
- source: item.source,
87
- activeCount: item.activeCount,
88
- ...(typeof item.retryAtMs === "number" ? { retryAtMs: item.retryAtMs } : {}),
89
- });
90
- }
91
- waitingOn = { children: value.waitingOn.children, background, human: [] };
92
- }
154
+ const waitingOn = readWaits(value.waitingOn);
93
155
  return {
94
156
  taskId: value.taskId,
95
157
  childSessionId: value.childSessionId,
96
158
  runEpoch: value.runEpoch,
97
159
  generation: value.generation,
98
160
  state,
161
+ ...(typeof value.label === "string" ? { label: value.label } : {}),
162
+ ...(typeof value.since === "string" && Number.isFinite(Date.parse(value.since)) ? { since: value.since } : {}),
99
163
  ...(waitingOn ? { waitingOn } : {}),
100
164
  ...(terminal === undefined ? {} : { terminal }),
101
165
  };
@@ -1,5 +1,5 @@
1
1
  import { readHumanWait } from "./lifecycle-attention.js";
2
- import { readLifecycleState } from "./lifecycle-children.js";
2
+ import { readLifecycleState, readWaits } from "./lifecycle-children.js";
3
3
  import { readLastStop } from "./lifecycle-stop.js";
4
4
  import { isRecord } from "./work-custody-record.js";
5
5
  /** Restore the wire snapshot and its private high-water marks from owned JSONL. */
@@ -97,29 +97,6 @@ function readTurnEnd(value) {
97
97
  return undefined;
98
98
  }
99
99
  }
100
- function readWaits(value) {
101
- if (!isRecord(value) || typeof value.children !== "number")
102
- return undefined;
103
- const background = [];
104
- if (Array.isArray(value.background)) {
105
- for (const source of value.background) {
106
- if (!isRecord(source) || typeof source.source !== "string" || typeof source.activeCount !== "number")
107
- continue;
108
- background.push({
109
- source: source.source,
110
- activeCount: source.activeCount,
111
- ...(typeof source.retryAtMs === "number" ? { retryAtMs: source.retryAtMs } : {}),
112
- });
113
- }
114
- }
115
- const human = Array.isArray(value.human)
116
- ? value.human.flatMap((request) => {
117
- const wait = readHumanWait(request);
118
- return wait ? [wait] : [];
119
- })
120
- : [];
121
- return { children: value.children, background, human };
122
- }
123
100
  function readHolds(value) {
124
101
  if (!Array.isArray(value))
125
102
  return undefined;
@@ -9,6 +9,7 @@ import { lifecycleHistory } from "./lifecycle-history.js";
9
9
  import { bindLifecycleHumanInput } from "./lifecycle-human-input.js";
10
10
  import { readLifecycleRuntimeFacts } from "./lifecycle-runtime-facts.js";
11
11
  import { goalGuardAfter } from "./lifecycle-stop.js";
12
+ import { wakeSourceItems } from "./lifecycle-wait-items.js";
12
13
  import { SessionLifecycleController } from "./session-lifecycle.js";
13
14
  import { isRecord } from "./work-custody-record.js";
14
15
  /** Attaches durable lifecycle to the real session event/admission boundaries. */
@@ -310,7 +311,7 @@ export class LifecycleSessionBinding {
310
311
  return;
311
312
  if (!Number.isFinite(data.activeCount))
312
313
  return;
313
- this.controller.obligations.syncSource(data.source, data.kind === "children" ? "children" : "background", data.activeCount);
314
+ this.controller.obligations.syncSource(data.source, data.kind === "children" ? "children" : "background", data.activeCount, wakeSourceItems(data));
314
315
  }
315
316
  dispose() {
316
317
  this.disposed = true;
@@ -0,0 +1,17 @@
1
+ import type { WakeSourceStateItem } from "./extensions/builtin/monitor-state-event.ts";
2
+ import type { LifecycleWaitItem } from "./session-lifecycle-types.ts";
3
+ export interface WaitSourceItems {
4
+ readonly items?: readonly WakeSourceStateItem[];
5
+ readonly omittedItems?: number;
6
+ }
7
+ /** Optional producer detail never determines custody. Lists are snapshots, bounded independently of activeCount. */
8
+ export declare function normalizeWaitItems(value: unknown, omitted?: unknown): WaitSourceItems;
9
+ /** The one rule for a wake_source_state producer's item list: `items` when set, else the legacy `monitors`. */
10
+ export declare function wakeSourceItems(data: object): unknown;
11
+ export declare function waitItemSignature(detail: WaitSourceItems): string;
12
+ export declare function projectWaitItems(detail: WaitSourceItems): {
13
+ readonly items?: readonly LifecycleWaitItem[];
14
+ readonly omittedItems?: number;
15
+ };
16
+ export declare function readWaitItems(value: unknown, omitted?: unknown): ReturnType<typeof projectWaitItems>;
17
+ //# sourceMappingURL=lifecycle-wait-items.d.ts.map