@google/gemini-cli-a2a-server 0.64.0-preview.0 → 0.64.0

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.
@@ -221367,8 +221367,8 @@ var GIT_COMMIT_INFO, CLI_VERSION;
221367
221367
  var init_git_commit = __esm({
221368
221368
  "packages/core/dist/src/generated/git-commit.js"() {
221369
221369
  "use strict";
221370
- GIT_COMMIT_INFO = "fb972b2f8";
221371
- CLI_VERSION = "0.64.0-preview.0";
221370
+ GIT_COMMIT_INFO = "90997f398";
221371
+ CLI_VERSION = "0.64.0";
221372
221372
  }
221373
221373
  });
221374
221374
 
@@ -337592,7 +337592,7 @@ function getVersion() {
337592
337592
  }
337593
337593
  versionPromise = (async () => {
337594
337594
  const pkgJson = await getPackageJson(__dirname4);
337595
- return "0.64.0-preview.0";
337595
+ return "0.64.0";
337596
337596
  })();
337597
337597
  return versionPromise;
337598
337598
  }
@@ -339930,6 +339930,13 @@ var init_planUtils = __esm({
339930
339930
  });
339931
339931
 
339932
339932
  // packages/core/dist/src/utils/untrustedContextTracker.js
339933
+ function isHighRiskPatternToken(token2) {
339934
+ return token2.startsWith("http://") || token2.startsWith("https://") || token2.startsWith("/") || token2.startsWith("\\") || token2.startsWith("./") || token2.startsWith("../") || /^[a-zA-Z]:[\\/]/.test(token2) || /\.(sh|bash|zsh|ps1|bat|cmd|exe)$/i.test(token2);
339935
+ }
339936
+ function hasUntrustedSubstring(untrustedContext, value) {
339937
+ const normalized = value.toLowerCase().replace(/\\/g, "/");
339938
+ return untrustedContext.untrustedTexts.some((text) => text.toLowerCase().includes(normalized));
339939
+ }
339933
339940
  function extractUntrustedContext(history) {
339934
339941
  const untrustedTexts = [];
339935
339942
  const untrustedTokens = /* @__PURE__ */ new Set();
@@ -339960,35 +339967,19 @@ function extractUntrustedContext(history) {
339960
339967
  if (untrustedContent) {
339961
339968
  const normalizedContent = untrustedContent.replace(/\\/g, "/");
339962
339969
  untrustedTexts.push(normalizedContent);
339963
- const tokens = normalizedContent.split(/[\s,`"';()|&[\]{}<>]+/).map((t3) => t3.trim()).filter((t3) => t3.length > 1).filter((t3) => !BENIGN_SUBCOMMAND_TOKENS.has(t3.toLowerCase()));
339970
+ const contentWithoutShellMetadata = normalizedContent.replace(/^\s*Output:\s*(\(empty\))?\s*/i, "").replace(/\n(?:Exit Code|Signal|Background PIDs|Process Group PGID):.*$/gim, "");
339971
+ const tokens = contentWithoutShellMetadata.split(/[\s,`"';()|&[\]{}<>]+/).map((t3) => t3.trim()).filter((t3) => t3.length > 1).filter((t3) => !BARE_DASHES_REGEX.test(t3)).filter((t3) => !FILE_MODE_STRING_REGEX.test(t3)).filter((t3) => !BENIGN_SUBCOMMAND_TOKENS.has(t3.toLowerCase())).filter((t3) => !SAFE_SHORT_FLAGS.has(t3)).filter((t3) => !SAFE_LONG_FLAGS.has(t3.toLowerCase()));
339964
339972
  for (const token2 of tokens) {
339965
- const lowerToken = token2.toLowerCase();
339966
- untrustedTokens.add(lowerToken);
339967
- if (lowerToken.startsWith("--")) {
339968
- untrustedTokens.add(lowerToken.substring(2));
339969
- } else if (lowerToken.startsWith("-")) {
339970
- untrustedTokens.add(lowerToken.substring(1));
339971
- }
339972
- if (lowerToken.includes("=")) {
339973
- const eqParts = lowerToken.split("=");
339973
+ const isShortFlag = token2.startsWith("-") && !token2.startsWith("--");
339974
+ const tokenToStore = isShortFlag ? token2 : token2.toLowerCase();
339975
+ untrustedTokens.add(tokenToStore);
339976
+ if (token2.includes("=")) {
339977
+ const eqParts = token2.split("=");
339974
339978
  for (const eqPart of eqParts) {
339975
339979
  const trimmedPart = eqPart.trim();
339976
- if (trimmedPart.length > 1) {
339977
- untrustedTokens.add(trimmedPart);
339978
- if (trimmedPart.startsWith("--")) {
339979
- untrustedTokens.add(trimmedPart.substring(2));
339980
- } else if (trimmedPart.startsWith("-")) {
339981
- untrustedTokens.add(trimmedPart.substring(1));
339982
- }
339983
- }
339984
- }
339985
- }
339986
- if (lowerToken.includes("/")) {
339987
- const pathParts = lowerToken.split("/");
339988
- for (const part2 of pathParts) {
339989
- const trimmedPart = part2.trim();
339990
- if (trimmedPart.length > 1) {
339991
- untrustedTokens.add(trimmedPart);
339980
+ if (trimmedPart.length > 1 && !BARE_DASHES_REGEX.test(trimmedPart) && !SAFE_SHORT_FLAGS.has(trimmedPart) && !SAFE_LONG_FLAGS.has(trimmedPart.toLowerCase())) {
339981
+ const isPartShortFlag = trimmedPart.startsWith("-") && !trimmedPart.startsWith("--");
339982
+ untrustedTokens.add(isPartShortFlag ? trimmedPart : trimmedPart.toLowerCase());
339992
339983
  }
339993
339984
  }
339994
339985
  }
@@ -340007,7 +339998,7 @@ function findUntrustedFlags(command, untrustedContext) {
340007
339998
  let parsed;
340008
339999
  try {
340009
340000
  const normalizedCommand = process.platform === "win32" ? command.replace(/\\(?!")/g, "/") : command;
340010
- parsed = (0, import_shell_quote5.parse)(normalizedCommand);
340001
+ parsed = (0, import_shell_quote5.parse)(normalizedCommand, (key) => `$${key}`);
340011
340002
  } catch {
340012
340003
  parsed = command.trim().split(/\s+/);
340013
340004
  }
@@ -340030,45 +340021,57 @@ function findUntrustedFlags(command, untrustedContext) {
340030
340021
  const token2 = rawTokens[i3];
340031
340022
  const lowerToken = token2.toLowerCase();
340032
340023
  if (token2.startsWith("-")) {
340024
+ if (/^--?$/.test(token2)) {
340025
+ continue;
340026
+ }
340027
+ let rawFlagName = token2;
340033
340028
  let flagToCheck = lowerToken;
340029
+ let rawValToCheck;
340034
340030
  let valToCheck;
340035
340031
  if (token2.includes("=")) {
340036
- const eqIdx = lowerToken.indexOf("=");
340032
+ const eqIdx = token2.indexOf("=");
340033
+ rawFlagName = token2.substring(0, eqIdx);
340037
340034
  flagToCheck = lowerToken.substring(0, eqIdx);
340035
+ rawValToCheck = token2.substring(eqIdx + 1);
340038
340036
  valToCheck = lowerToken.substring(eqIdx + 1);
340039
340037
  }
340040
- if (untrustedContext.untrustedTokens.has(lowerToken) || untrustedContext.untrustedTokens.has(flagToCheck)) {
340038
+ const isSafeFlag = SAFE_SHORT_FLAGS.has(rawFlagName) || SAFE_LONG_FLAGS.has(flagToCheck);
340039
+ const isHighRiskFlag = HIGH_RISK_VALUE_FLAGS.has(rawFlagName) || rawFlagName.startsWith("--") && HIGH_RISK_VALUE_FLAGS.has(flagToCheck);
340040
+ const isShortFlag = rawFlagName.startsWith("-") && !rawFlagName.startsWith("--");
340041
+ const tokenToLookup = isShortFlag ? rawFlagName : lowerToken;
340042
+ const flagToLookup = isShortFlag ? rawFlagName : flagToCheck;
340043
+ if (!isSafeFlag && (untrustedContext.untrustedTokens.has(tokenToLookup) || untrustedContext.untrustedTokens.has(flagToLookup))) {
340041
340044
  detected.add(token2);
340042
- continue;
340045
+ if (token2.includes("=") || !isHighRiskFlag) {
340046
+ continue;
340047
+ }
340043
340048
  }
340044
- if (valToCheck && untrustedContext.untrustedTokens.has(valToCheck)) {
340045
- detected.add(token2);
340046
- continue;
340049
+ if (valToCheck && rawValToCheck) {
340050
+ const isValUntrusted = untrustedContext.untrustedTokens.has(valToCheck) && (isHighRiskFlag || !isSafeFlag && isHighRiskPatternToken(rawValToCheck));
340051
+ const isHighRiskValSubstring = (!isSafeFlag || isHighRiskFlag) && isHighRiskPatternToken(rawValToCheck) && hasUntrustedSubstring(untrustedContext, valToCheck);
340052
+ if (isValUntrusted || isHighRiskValSubstring) {
340053
+ detected.add(token2);
340054
+ continue;
340055
+ }
340047
340056
  }
340048
- const nextToken = rawTokens[i3 + 1];
340049
- if (nextToken && !nextToken.startsWith("-") && untrustedContext.untrustedTokens.has(nextToken.toLowerCase())) {
340050
- detected.add(token2);
340051
- detected.add(nextToken);
340057
+ if (!token2.includes("=") && isHighRiskFlag) {
340058
+ const nextToken = rawTokens[i3 + 1];
340059
+ if (nextToken && !nextToken.startsWith("-") && !BARE_SHELL_VARIABLE_REGEX.test(nextToken) && !BENIGN_SUBCOMMAND_TOKENS.has(nextToken.toLowerCase()) && (untrustedContext.untrustedTokens.has(nextToken.toLowerCase()) || isHighRiskPatternToken(nextToken) && hasUntrustedSubstring(untrustedContext, nextToken))) {
340060
+ detected.add(token2);
340061
+ detected.add(nextToken);
340062
+ }
340052
340063
  }
340053
340064
  } else {
340054
- if (token2.length <= 1) {
340065
+ if (token2.length <= 1 || BARE_SHELL_VARIABLE_REGEX.test(token2)) {
340055
340066
  continue;
340056
340067
  }
340057
- const isSensitiveWord = token2.includes("/") || token2.includes(".") || token2.includes(":");
340058
- const isHighRiskPattern = token2.startsWith("http://") || token2.startsWith("https://") || token2.startsWith("/") || token2.startsWith("\\") || /^[a-zA-Z]:[\\/]/.test(token2);
340059
- if (isSensitiveWord) {
340068
+ if (isHighRiskPatternToken(token2)) {
340060
340069
  if (untrustedContext.untrustedTokens.has(lowerToken)) {
340061
340070
  detected.add(token2);
340062
340071
  continue;
340063
340072
  }
340064
- if (isHighRiskPattern) {
340065
- const normalizedLowerToken = lowerToken.replace(/\\/g, "/");
340066
- for (const text of untrustedContext.untrustedTexts) {
340067
- if (text.toLowerCase().includes(normalizedLowerToken)) {
340068
- detected.add(token2);
340069
- break;
340070
- }
340071
- }
340073
+ if (hasUntrustedSubstring(untrustedContext, lowerToken)) {
340074
+ detected.add(token2);
340072
340075
  }
340073
340076
  }
340074
340077
  }
@@ -340126,7 +340129,7 @@ function getModifiedBuildFiles(sessionKey) {
340126
340129
  }
340127
340130
  return [];
340128
340131
  }
340129
- var import_shell_quote5, UNTRUSTED_CONTEXT_REGEX, BUILD_TEST_COMMAND_ROOTS, BENIGN_SUBCOMMAND_TOKENS, MODIFIED_BUILD_FILES_SYMBOL;
340132
+ var import_shell_quote5, UNTRUSTED_CONTEXT_REGEX, BUILD_TEST_COMMAND_ROOTS, BENIGN_SUBCOMMAND_TOKENS, SAFE_SHORT_FLAGS, SAFE_LONG_FLAGS, HIGH_RISK_VALUE_FLAGS, FILE_MODE_STRING_REGEX, BARE_DASHES_REGEX, BARE_SHELL_VARIABLE_REGEX, MODIFIED_BUILD_FILES_SYMBOL;
340130
340133
  var init_untrustedContextTracker = __esm({
340131
340134
  "packages/core/dist/src/utils/untrustedContextTracker.js"() {
340132
340135
  "use strict";
@@ -340177,6 +340180,135 @@ var init_untrustedContextTracker = __esm({
340177
340180
  "restart",
340178
340181
  "status"
340179
340182
  ]);
340183
+ SAFE_SHORT_FLAGS = /* @__PURE__ */ new Set([
340184
+ "-a",
340185
+ "-A",
340186
+ "-b",
340187
+ "-B",
340188
+ "-C",
340189
+ "-d",
340190
+ "-D",
340191
+ "-E",
340192
+ "-F",
340193
+ "-g",
340194
+ "-G",
340195
+ "-h",
340196
+ "-H",
340197
+ "-i",
340198
+ "-I",
340199
+ "-l",
340200
+ "-L",
340201
+ "-m",
340202
+ "-n",
340203
+ "-N",
340204
+ "-p",
340205
+ "-P",
340206
+ "-q",
340207
+ "-r",
340208
+ "-R",
340209
+ "-s",
340210
+ "-S",
340211
+ "-t",
340212
+ "-T",
340213
+ "-u",
340214
+ "-U",
340215
+ "-v",
340216
+ "-V",
340217
+ "-w",
340218
+ "-W",
340219
+ "-x",
340220
+ "-z",
340221
+ "-1",
340222
+ // Common combined POSIX inspection flags (ls, grep, ps, git)
340223
+ "-la",
340224
+ "-al",
340225
+ "-ld",
340226
+ "-dl",
340227
+ "-lh",
340228
+ "-hl",
340229
+ "-lah",
340230
+ "-alh",
340231
+ "-lrt",
340232
+ "-ltr",
340233
+ "-rn",
340234
+ "-nr",
340235
+ "-ri",
340236
+ "-ir",
340237
+ "-rni",
340238
+ "-rin",
340239
+ "-ni",
340240
+ "-in",
340241
+ "-ef",
340242
+ "-aux"
340243
+ ]);
340244
+ SAFE_LONG_FLAGS = /* @__PURE__ */ new Set([
340245
+ "--all",
340246
+ "--oneline",
340247
+ "--stat",
340248
+ "--numstat",
340249
+ "--shortstat",
340250
+ "--name-only",
340251
+ "--name-status",
340252
+ "--graph",
340253
+ "--decorate",
340254
+ "--no-pager",
340255
+ "--color",
340256
+ "--no-color",
340257
+ "--grep",
340258
+ "--author",
340259
+ "--since",
340260
+ "--until",
340261
+ "--before",
340262
+ "--after",
340263
+ "--pretty",
340264
+ "--format",
340265
+ "--abbrev-commit",
340266
+ "--max-count",
340267
+ "--cached",
340268
+ "--staged",
340269
+ "--short",
340270
+ "--branch",
340271
+ "--porcelain",
340272
+ "--help",
340273
+ "--version",
340274
+ "--quiet",
340275
+ "--verbose",
340276
+ "--dry-run",
340277
+ "--ignore-case",
340278
+ "--line-number",
340279
+ "--recursive",
340280
+ "--extended-regexp",
340281
+ "--fixed-strings",
340282
+ "--files-with-matches",
340283
+ "--count",
340284
+ "--context",
340285
+ "--before-context",
340286
+ "--after-context"
340287
+ ]);
340288
+ HIGH_RISK_VALUE_FLAGS = /* @__PURE__ */ new Set([
340289
+ "-c",
340290
+ "-e",
340291
+ "--eval",
340292
+ "--exec",
340293
+ "-exec",
340294
+ "-execdir",
340295
+ "--run_under",
340296
+ "--test_strategy",
340297
+ "--test_arg",
340298
+ "--test_env",
340299
+ "--action_env",
340300
+ "--jvmopt",
340301
+ "--host_jvm_args",
340302
+ "--wrapper_script_flag",
340303
+ "--upload-pack",
340304
+ "--receive-pack",
340305
+ "--require",
340306
+ "--import",
340307
+ "--loader"
340308
+ ]);
340309
+ FILE_MODE_STRING_REGEX = /^[dcbpsl-][rwxstST-]{9}[+@.]?$/i;
340310
+ BARE_DASHES_REGEX = /^--+$/;
340311
+ BARE_SHELL_VARIABLE_REGEX = /^\$(?:[a-zA-Z_][a-zA-Z0-9_]*|\{[a-zA-Z_][a-zA-Z0-9_]*\})$/;
340180
340312
  MODIFIED_BUILD_FILES_SYMBOL = Symbol("sessionModifiedBuildFiles");
340181
340313
  }
340182
340314
  });
@@ -368074,6 +368206,9 @@ function scrubPart(part) {
368074
368206
  if (part.functionResponse.id) {
368075
368207
  scrubbedResp["id"] = part.functionResponse.id;
368076
368208
  }
368209
+ if ("parts" in part.functionResponse && Array.isArray(part.functionResponse.parts)) {
368210
+ scrubbedResp["parts"] = part.functionResponse.parts.map(scrubPart);
368211
+ }
368077
368212
  scrubbed["functionResponse"] = scrubbedResp;
368078
368213
  }
368079
368214
  if ("fileData" in part) {
@@ -368221,8 +368356,41 @@ function isSchemaDepthError(errorMessage) {
368221
368356
  function isInvalidArgumentError(errorMessage) {
368222
368357
  return errorMessage.includes("Request contains an invalid argument");
368223
368358
  }
368359
+ function isEmptyTextPart(part) {
368360
+ if (part.text !== "") {
368361
+ return false;
368362
+ }
368363
+ return !Object.keys(part).some((key) => key !== "text" && key !== "thought" && key !== "callIndex");
368364
+ }
368365
+ function hasNonEmptyParts(content) {
368366
+ return (content.parts ?? []).some((part) => !isEmptyTextPart(part));
368367
+ }
368368
+ function buildInterruptedToolResponseParts(parts2) {
368369
+ return parts2.filter((part) => !!part?.functionCall).map((part) => ({
368370
+ functionResponse: {
368371
+ name: part.functionCall.name?.trim() || "generic_tool",
368372
+ id: part.functionCall.id,
368373
+ response: { error: INTERRUPTED_TOOL_RESPONSE_ERROR }
368374
+ }
368375
+ }));
368376
+ }
368377
+ function ensureTerminalUserTurn(contents) {
368378
+ const last2 = contents.at(-1);
368379
+ if (last2?.role === "user" && hasNonEmptyParts(last2)) {
368380
+ return contents;
368381
+ }
368382
+ if (last2?.role === "user") {
368383
+ return [
368384
+ ...contents.slice(0, -1),
368385
+ { ...last2, parts: [{ text: CONTINUE_PROMPT_TEXT }] }
368386
+ ];
368387
+ }
368388
+ const missingResponses = buildInterruptedToolResponseParts(last2?.parts ?? []);
368389
+ const parts2 = missingResponses.length > 0 ? missingResponses : [{ text: CONTINUE_PROMPT_TEXT }];
368390
+ return [...contents, { role: "user", parts: parts2 }];
368391
+ }
368224
368392
  function stripToolCallIdPrefixes(contents) {
368225
- return contents.map((content) => {
368393
+ const stripped = contents.map((content) => {
368226
368394
  const parts2 = (content.parts || []).map((part) => {
368227
368395
  const newPart = { ...part };
368228
368396
  if (newPart.functionCall) {
@@ -368230,8 +368398,7 @@ function stripToolCallIdPrefixes(contents) {
368230
368398
  const name3 = fc.name?.trim() || "generic_tool";
368231
368399
  if (fc.id && fc.id.startsWith(`${name3}__`)) {
368232
368400
  newPart.functionCall = {
368233
- name: fc.name,
368234
- args: fc.args,
368401
+ ...fc,
368235
368402
  id: fc.id.substring(name3.length + 2)
368236
368403
  };
368237
368404
  }
@@ -368241,8 +368408,9 @@ function stripToolCallIdPrefixes(contents) {
368241
368408
  const name3 = fr2.name?.trim() || "generic_tool";
368242
368409
  if (fr2.id && fr2.id.startsWith(`${name3}__`)) {
368243
368410
  newPart.functionResponse = {
368244
- name: fr2.name,
368245
- response: fr2.response,
368411
+ // History parts are plain JSON objects, not class instances.
368412
+ // eslint-disable-next-line @typescript-eslint/no-misused-spread
368413
+ ...fr2,
368246
368414
  id: fr2.id.substring(name3.length + 2)
368247
368415
  };
368248
368416
  }
@@ -368252,18 +368420,30 @@ function stripToolCallIdPrefixes(contents) {
368252
368420
  delete newPart.text;
368253
368421
  }
368254
368422
  return newPart;
368255
- }).filter((part) => {
368256
- const hasOtherKeys = Object.keys(part).some((key) => key !== "text" && key !== "thought" && key !== "callIndex");
368257
- if (part.text !== void 0 && part.text === "" && !hasOtherKeys) {
368258
- return false;
368259
- }
368260
- return true;
368261
- });
368423
+ }).filter((part) => !isEmptyTextPart(part));
368262
368424
  return {
368263
368425
  ...content,
368264
368426
  parts: parts2
368265
368427
  };
368266
- });
368428
+ }).filter((content) => content.parts.length > 0);
368429
+ return coalesceConsecutiveContents(stripped);
368430
+ }
368431
+ function coalesceConsecutiveContents(contents) {
368432
+ const result2 = [];
368433
+ for (const item of contents) {
368434
+ const lastIdx = result2.length - 1;
368435
+ const last2 = result2[lastIdx];
368436
+ if (last2 && last2.role && last2.role === item.role) {
368437
+ const hasParts = last2.parts || item.parts;
368438
+ result2[lastIdx] = {
368439
+ ...last2,
368440
+ parts: hasParts ? [...last2.parts || [], ...item.parts || []] : void 0
368441
+ };
368442
+ } else {
368443
+ result2.push({ ...item });
368444
+ }
368445
+ }
368446
+ return result2;
368267
368447
  }
368268
368448
  function coalesceConsecutiveRoles(history) {
368269
368449
  const result2 = [];
@@ -368318,7 +368498,7 @@ function stripThoughts2(history) {
368318
368498
  };
368319
368499
  }).filter((turn) => !turn.content.parts || turn.content.parts.length > 0);
368320
368500
  }
368321
- var StreamEventType, MID_STREAM_RETRY_OPTIONS, SYNTHETIC_THOUGHT_SIGNATURE2, INTERRUPTED_RESPONSE_PLACEHOLDER, THINKING_ONLY_NUDGE_MESSAGE, NO_RESPONSE_TEXT_NUDGE_MESSAGE, InvalidStreamError, AgentExecutionStoppedError, AgentExecutionBlockedError, GeminiChat;
368501
+ var StreamEventType, MID_STREAM_RETRY_OPTIONS, SYNTHETIC_THOUGHT_SIGNATURE2, INTERRUPTED_RESPONSE_PLACEHOLDER, INTERRUPTED_TOOL_RESPONSE_ERROR, CONTINUE_PROMPT_TEXT, THINKING_ONLY_NUDGE_MESSAGE, NO_RESPONSE_TEXT_NUDGE_MESSAGE, InvalidStreamError, AgentExecutionStoppedError, AgentExecutionBlockedError, GeminiChat;
368322
368502
  var init_geminiChat = __esm({
368323
368503
  "packages/core/dist/src/core/geminiChat.js"() {
368324
368504
  "use strict";
@@ -368341,6 +368521,7 @@ var init_geminiChat = __esm({
368341
368521
  init_tokenCalculation();
368342
368522
  init_policyHelpers();
368343
368523
  init_events();
368524
+ init_debugLogger();
368344
368525
  (function(StreamEventType2) {
368345
368526
  StreamEventType2["CHUNK"] = "chunk";
368346
368527
  StreamEventType2["RETRY"] = "retry";
@@ -368355,6 +368536,8 @@ var init_geminiChat = __esm({
368355
368536
  };
368356
368537
  SYNTHETIC_THOUGHT_SIGNATURE2 = "skip_thought_signature_validator";
368357
368538
  INTERRUPTED_RESPONSE_PLACEHOLDER = "[The previous response was interrupted before it completed.]";
368539
+ INTERRUPTED_TOOL_RESPONSE_ERROR = "Response was lost or interrupted.";
368540
+ CONTINUE_PROMPT_TEXT = "Please continue.";
368358
368541
  THINKING_ONLY_NUDGE_MESSAGE = "[System: You previously generated thoughts but failed to provide a final user-facing response. Please ensure you provide your final answer or call a tool now.]";
368359
368542
  NO_RESPONSE_TEXT_NUDGE_MESSAGE = "[System: You previously returned an empty response with no text or thoughts. Please ensure you provide your final answer or call a tool now.]";
368360
368543
  InvalidStreamError = class extends Error {
@@ -368486,6 +368669,7 @@ var init_geminiChat = __esm({
368486
368669
  let userContent = createUserContent(message);
368487
368670
  const isOriginalFunctionResponse = isFunctionResponse(userContent);
368488
368671
  if (!isOriginalFunctionResponse) {
368672
+ this.closeUnansweredToolCallsTurn();
368489
368673
  this.closeUnansweredToolResponseTurn();
368490
368674
  }
368491
368675
  const { model } = this.context.config.modelConfigService.getResolvedConfig(modelConfigKey);
@@ -368512,7 +368696,7 @@ var init_geminiChat = __esm({
368512
368696
  } else {
368513
368697
  const history = this.agentHistory.get();
368514
368698
  const lastTurn = history[history.length - 1];
368515
- if (!lastTurn || partListUnionToString(lastTurn.content.parts || []) !== userMessageContent) {
368699
+ if (!lastTurn || lastTurn.content.role !== "user" || partListUnionToString(lastTurn.content.parts || []) !== userMessageContent) {
368516
368700
  const id = this.chatRecordingService.recordMessage({
368517
368701
  model,
368518
368702
  type: "user",
@@ -368560,11 +368744,12 @@ var init_geminiChat = __esm({
368560
368744
  } else {
368561
368745
  const history = this.agentHistory.get();
368562
368746
  const lastTurn = history[history.length - 1];
368563
- if (!lastTurn || partListUnionToString(lastTurn.content.parts || []) !== partListUnionToString(userContent.parts || [])) {
368747
+ if (!lastTurn || lastTurn.content.role !== "user" || partListUnionToString(lastTurn.content.parts || []) !== partListUnionToString(userContent.parts || [])) {
368564
368748
  this.agentHistory.push({ id, content: userContent });
368565
368749
  }
368566
368750
  }
368567
368751
  }
368752
+ this.replaceEmptyTrailingUserTurn();
368568
368753
  const requestHistory = this.getHistoryTurns(true);
368569
368754
  const streamWithRetries = async function* () {
368570
368755
  let isSuccess = false;
@@ -368676,23 +368861,77 @@ var init_geminiChat = __esm({
368676
368861
  return streamWithRetries.call(this);
368677
368862
  }
368678
368863
  /**
368679
- * Appends a closing model turn when history ends with an unanswered tool
368680
- * response, so the next user message stays a turn of its own.
368864
+ * Appends a dedicated synthetic user turn with matching functionResponse
368865
+ * parts when history ends with an unclosed model functionCall. The
368866
+ * subsequent user prompt is then recorded as a clean turn of its own (after
368867
+ * `closeUnansweredToolResponseTurn` adds the closing model turn).
368681
368868
  */
368682
- closeUnansweredToolResponseTurn() {
368869
+ closeUnansweredToolCallsTurn() {
368683
368870
  const turns = this.agentHistory.get();
368684
368871
  const last2 = turns[turns.length - 1];
368685
- if (last2?.content.role !== "user" || !last2.content.parts?.some((part) => !!part.functionResponse)) {
368872
+ if (last2?.content.role !== "model") {
368873
+ return;
368874
+ }
368875
+ const missingResponses = buildInterruptedToolResponseParts(last2.content.parts ?? []);
368876
+ if (missingResponses.length === 0) {
368686
368877
  return;
368687
368878
  }
368879
+ const id = this.chatRecordingService.recordSyntheticMessage("user", missingResponses);
368688
368880
  this.agentHistory.push({
368689
- id: randomUUID8(),
368690
- content: {
368691
- role: "model",
368692
- parts: [{ text: INTERRUPTED_RESPONSE_PLACEHOLDER }]
368693
- }
368881
+ id,
368882
+ content: { role: "user", parts: missingResponses }
368694
368883
  });
368695
368884
  }
368885
+ /**
368886
+ * Replaces the parts of a trailing user turn that carries no content (e.g.
368887
+ * only empty text parts) with a continuation prompt, keeping the in-memory
368888
+ * history and the session log in sync. Runs once per send so that the
368889
+ * persisted history never keeps an empty user turn between model turns.
368890
+ */
368891
+ replaceEmptyTrailingUserTurn() {
368892
+ const turns = this.agentHistory.get();
368893
+ const last2 = turns[turns.length - 1];
368894
+ if (last2?.content.role !== "user" || hasNonEmptyParts(last2.content)) {
368895
+ return;
368896
+ }
368897
+ this.agentHistory.set([
368898
+ ...turns.slice(0, -1),
368899
+ {
368900
+ id: last2.id,
368901
+ content: { ...last2.content, parts: [{ text: CONTINUE_PROMPT_TEXT }] }
368902
+ }
368903
+ ]);
368904
+ this.chatRecordingService.updateMessagesFromHistory(this.agentHistory.get());
368905
+ }
368906
+ /**
368907
+ * Appends a closing model turn when history ends with an unanswered tool
368908
+ * response, so the next user message stays a turn of its own.
368909
+ */
368910
+ closeUnansweredToolResponseTurn() {
368911
+ const turns = this.agentHistory.get();
368912
+ let targetTurn = turns[turns.length - 1];
368913
+ let hadEmptyTrailingModelTurn = false;
368914
+ if (targetTurn?.content.role === "model" && (!targetTurn.content.parts || targetTurn.content.parts.length === 0)) {
368915
+ hadEmptyTrailingModelTurn = true;
368916
+ targetTurn = turns[turns.length - 2];
368917
+ }
368918
+ if (targetTurn?.content.role !== "user" || !targetTurn.content.parts?.some((part) => !!part.functionResponse)) {
368919
+ return;
368920
+ }
368921
+ if (hadEmptyTrailingModelTurn) {
368922
+ const lastTurn = turns[turns.length - 1];
368923
+ lastTurn.content.parts = [{ text: INTERRUPTED_RESPONSE_PLACEHOLDER }];
368924
+ this.chatRecordingService.updateMessagesFromHistory(turns);
368925
+ } else {
368926
+ this.agentHistory.push({
368927
+ id: randomUUID8(),
368928
+ content: {
368929
+ role: "model",
368930
+ parts: [{ text: INTERRUPTED_RESPONSE_PLACEHOLDER }]
368931
+ }
368932
+ });
368933
+ }
368934
+ }
368696
368935
  extractBinaryInjections(parts2) {
368697
368936
  const binaryParts = [];
368698
368937
  if (parts2) {
@@ -368809,10 +369048,14 @@ var init_geminiChat = __esm({
368809
369048
  if (this.onModelChanged) {
368810
369049
  this.tools = await this.onModelChanged(modelToUse);
368811
369050
  }
369051
+ const normalizedContents = ensureTerminalUserTurn(contentsToUse);
369052
+ if (normalizedContents !== contentsToUse) {
369053
+ debugLogger.warn("Final contents do not end with a valid user turn. Normalizing contents to satisfy Gemini API invariant.");
369054
+ }
368812
369055
  lastModelToUse = modelToUse;
368813
369056
  lastConfig = config2;
368814
- lastContentsToUse = contentsToUse;
368815
- const finalContents = stripToolCallIdPrefixes(contentsToUse);
369057
+ lastContentsToUse = normalizedContents;
369058
+ const finalContents = stripToolCallIdPrefixes(normalizedContents);
368816
369059
  return this.context.config.getContentGenerator().generateContentStream({
368817
369060
  model: modelToUse,
368818
369061
  contents: finalContents,
@@ -368910,6 +369153,25 @@ var init_geminiChat = __esm({
368910
369153
  }
368911
369154
  ensureStableToolIds(this.agentHistory.get());
368912
369155
  }
369156
+ /**
369157
+ * Removes the trailing model turn when it holds function calls that were
369158
+ * never answered (e.g. every call in the batch was declined). Earlier
369159
+ * completed tool rounds and the originating user prompt are kept, and the
369160
+ * durable IDs of the remaining turns are preserved.
369161
+ *
369162
+ * @returns true when a turn was removed.
369163
+ */
369164
+ discardTrailingUnansweredToolCallTurn() {
369165
+ const turns = this.agentHistory.get();
369166
+ const last2 = turns[turns.length - 1];
369167
+ if (last2?.content.role !== "model" || !last2.content.parts?.some((part) => !!part.functionCall)) {
369168
+ return false;
369169
+ }
369170
+ this.agentHistory.rollback(turns.length - 1);
369171
+ this.lastPromptTokenCount = estimateTokenCountSync(this.agentHistory.flatMap((c2) => c2.content.parts || []));
369172
+ this.chatRecordingService.updateMessagesFromHistory(this.agentHistory.get());
369173
+ return true;
369174
+ }
368913
369175
  setHistory(history) {
368914
369176
  const wrappedHistory = history.map((item) => {
368915
369177
  if ("id" in item && "content" in item) {
@@ -376673,7 +376935,13 @@ var init_oauth_utils = __esm({
376673
376935
  issuer: metadata2.issuer,
376674
376936
  tokenUrl: metadata2.token_endpoint,
376675
376937
  scopes: metadata2.scopes_supported || [],
376676
- registrationUrl: metadata2.registration_endpoint
376938
+ registrationUrl: metadata2.registration_endpoint,
376939
+ // RFC 9207 Section 3: the flag defaults to false when omitted. Discovered
376940
+ // configurations therefore always carry an explicit boolean, which lets
376941
+ // the callback policy distinguish them from an explicitly configured issuer.
376942
+ // Only a literal boolean `true` enables the flag, so loosely typed values
376943
+ // returned by some servers (e.g. "false" or "0") are not coerced to true.
376944
+ authorizationResponseIssParameterSupported: metadata2.authorization_response_iss_parameter_supported === true
376677
376945
  };
376678
376946
  }
376679
376947
  /**
@@ -380473,7 +380741,7 @@ function areIssuersEqual(issuerA, issuerB) {
380473
380741
  }
380474
380742
  return normA === normB;
380475
380743
  }
380476
- function startCallbackServer(expectedState, port, expectedIssuer) {
380744
+ function startCallbackServer(expectedState, port, expectedIssuer, requireIssInResponse) {
380477
380745
  let portResolve;
380478
380746
  let portReject;
380479
380747
  const portPromise = new Promise((resolve23, reject) => {
@@ -380527,8 +380795,9 @@ function startCallbackServer(expectedState, port, expectedIssuer) {
380527
380795
  return;
380528
380796
  }
380529
380797
  if (expectedIssuer) {
380530
- if (!iss) {
380531
- debugLogger.error('OAuth callback rejected: Missing required "iss" parameter when an expected issuer is configured. Possible IdP mix-up attack (RFC 9207).');
380798
+ const requireIss = requireIssInResponse ?? true;
380799
+ if (requireIss && !iss) {
380800
+ debugLogger.error('OAuth callback rejected: Missing required "iss" parameter for the expected authorization server (RFC 9207).');
380532
380801
  res.writeHead(400, { "Content-Type": "text/html" });
380533
380802
  res.end(`
380534
380803
  <html>
@@ -380543,7 +380812,7 @@ function startCallbackServer(expectedState, port, expectedIssuer) {
380543
380812
  reject(new Error('Missing "iss" parameter in authorization response per RFC 9207'));
380544
380813
  return;
380545
380814
  }
380546
- if (!areIssuersEqual(iss, expectedIssuer)) {
380815
+ if (iss && !areIssuersEqual(iss, expectedIssuer)) {
380547
380816
  debugLogger.error("OAuth callback rejected: Issuer mismatch between authorization response and expected authorization server. Possible IdP mix-up attack (RFC 9207).");
380548
380817
  res.writeHead(400, { "Content-Type": "text/html" });
380549
380818
  res.end(`
@@ -380559,7 +380828,9 @@ function startCallbackServer(expectedState, port, expectedIssuer) {
380559
380828
  reject(new Error("Issuer mismatch in authorization response - possible OAuth mix-up attack"));
380560
380829
  return;
380561
380830
  }
380562
- debugLogger.debug("\u2713 OAuth callback issuer validated successfully.");
380831
+ if (iss) {
380832
+ debugLogger.debug("\u2713 OAuth callback issuer validated successfully.");
380833
+ }
380563
380834
  } else if (iss) {
380564
380835
  debugLogger.debug('OAuth callback received "iss" parameter (no expected issuer was configured).');
380565
380836
  }
@@ -387642,6 +387913,21 @@ var init_client3 = __esm({
387642
387913
  this.updateTelemetryTokenCount();
387643
387914
  this.forceFullIdeContext = true;
387644
387915
  }
387916
+ /**
387917
+ * Removes the trailing model turn when it only holds unanswered function
387918
+ * calls. See {@link GeminiChat.discardTrailingUnansweredToolCallTurn}.
387919
+ * Returns false without side effects when the chat is not initialized.
387920
+ */
387921
+ discardTrailingUnansweredToolCallTurn() {
387922
+ if (!this.isInitialized()) {
387923
+ return false;
387924
+ }
387925
+ const removed = this.getChat().discardTrailingUnansweredToolCallTurn();
387926
+ if (removed) {
387927
+ this.updateTelemetryTokenCount();
387928
+ }
387929
+ return removed;
387930
+ }
387645
387931
  lastUsedModelId;
387646
387932
  async setTools(modelId) {
387647
387933
  if (!this.chat) {
@@ -404549,7 +404835,7 @@ var init_policy_engine = __esm({
404549
404835
  }
404550
404836
  return decision;
404551
404837
  }
404552
- async checkShellCommand(toolName, command, ruleDecision, serverName, dir_path, allowRedirection, rule, toolAnnotations, subagent) {
404838
+ async checkShellCommand(toolName, command, ruleDecision, serverName, dir_path, allowRedirection, rule, toolAnnotations, subagent, hasTopLevelHeuristicDowngrade = false) {
404553
404839
  if (!command) {
404554
404840
  return {
404555
404841
  decision: ruleDecision,
@@ -404585,13 +404871,17 @@ var init_policy_engine = __esm({
404585
404871
  }
404586
404872
  let aggregateDecision = ruleDecision;
404587
404873
  let responsibleRule = rule && ruleDecision === rule.decision ? rule : void 0;
404588
- if (this.shouldDowngradeForRedirection(command, allowRedirection, rule)) {
404874
+ const hasTopLevelRedirectionDowngrade = this.shouldDowngradeForRedirection(command, allowRedirection, rule);
404875
+ if (hasTopLevelRedirectionDowngrade) {
404589
404876
  if (aggregateDecision === PolicyDecision.ALLOW) {
404590
404877
  debugLogger.debug(`[PolicyEngine.check] Downgrading ALLOW to ASK_USER for redirected command: ${command}`);
404591
404878
  aggregateDecision = PolicyDecision.ASK_USER;
404592
404879
  responsibleRule = void 0;
404593
404880
  }
404594
404881
  }
404882
+ let evaluatedSubCommands = 0;
404883
+ let allSubCommandsExplicitlyAllowed = true;
404884
+ let lastAllowedSubRule;
404595
404885
  for (const detail of subCommands) {
404596
404886
  if (REDIRECTION_NAMES.has(detail.name)) {
404597
404887
  continue;
@@ -404600,23 +404890,31 @@ var init_policy_engine = __esm({
404600
404890
  const isAtomic = subCmd === command || detail.startIndex === 0 && detail.text.length === command.length;
404601
404891
  const stripped = stripShellWrapper(subCmd);
404602
404892
  if (stripped !== subCmd) {
404893
+ evaluatedSubCommands++;
404603
404894
  const wrapperResult = await this.check({ name: toolName, args: { command: stripped, dir_path } }, serverName, toolAnnotations, subagent, true);
404604
404895
  if (wrapperResult.decision === PolicyDecision.DENY)
404605
404896
  return wrapperResult;
404606
404897
  if (wrapperResult.decision === PolicyDecision.ASK_USER) {
404898
+ allSubCommandsExplicitlyAllowed = false;
404607
404899
  if (aggregateDecision === PolicyDecision.ALLOW) {
404608
404900
  responsibleRule = wrapperResult.rule;
404609
404901
  } else {
404610
404902
  responsibleRule ??= wrapperResult.rule;
404611
404903
  }
404612
404904
  aggregateDecision = PolicyDecision.ASK_USER;
404905
+ } else if (wrapperResult.decision === PolicyDecision.ALLOW && wrapperResult.rule?.decision === PolicyDecision.ALLOW && Boolean(wrapperResult.rule?.argsPattern)) {
404906
+ lastAllowedSubRule = wrapperResult.rule;
404907
+ } else {
404908
+ allSubCommandsExplicitlyAllowed = false;
404613
404909
  }
404614
404910
  }
404615
404911
  if (!isAtomic) {
404912
+ evaluatedSubCommands++;
404616
404913
  const subResult = await this.check({ name: toolName, args: { command: subCmd, dir_path } }, serverName, toolAnnotations, subagent, true);
404617
404914
  if (subResult.decision === PolicyDecision.DENY)
404618
404915
  return subResult;
404619
404916
  if (subResult.decision === PolicyDecision.ASK_USER) {
404917
+ allSubCommandsExplicitlyAllowed = false;
404620
404918
  if (aggregateDecision === PolicyDecision.ALLOW) {
404621
404919
  responsibleRule = subResult.rule;
404622
404920
  } else {
@@ -404625,13 +404923,31 @@ var init_policy_engine = __esm({
404625
404923
  aggregateDecision = PolicyDecision.ASK_USER;
404626
404924
  }
404627
404925
  if (subResult.decision === PolicyDecision.ALLOW && this.shouldDowngradeForRedirection(subCmd, subResult.rule?.allowRedirection ?? allowRedirection, subResult.rule)) {
404926
+ allSubCommandsExplicitlyAllowed = false;
404628
404927
  if (aggregateDecision === PolicyDecision.ALLOW) {
404629
404928
  aggregateDecision = PolicyDecision.ASK_USER;
404630
404929
  responsibleRule = void 0;
404631
404930
  }
404931
+ } else if (subResult.decision === PolicyDecision.ALLOW && subResult.rule?.decision === PolicyDecision.ALLOW && Boolean(subResult.rule?.argsPattern)) {
404932
+ lastAllowedSubRule = subResult.rule;
404933
+ } else {
404934
+ allSubCommandsExplicitlyAllowed = false;
404632
404935
  }
404936
+ } else if (stripped === subCmd) {
404937
+ allSubCommandsExplicitlyAllowed = false;
404633
404938
  }
404634
404939
  }
404940
+ const workspace = typeof this.sandboxManager.getWorkspace === "function" ? this.sandboxManager.getWorkspace() : process.cwd();
404941
+ const effectiveCwd = dir_path ? path85.resolve(workspace, dir_path) : workspace;
404942
+ const isOutsideWorkspace = effectiveCwd !== workspace && !isSubpath(workspace, effectiveCwd) && this.approvalMode !== ApprovalMode.YOLO;
404943
+ const parsedObjArgs = (0, import_shell_quote7.parse)(command);
404944
+ const parsedArgs = parsedObjArgs.map(extractStringFromParseEntry);
404945
+ const isUntrustedGit = containsGitCommand(parsedArgs) && !this.isTrustedFolder();
404946
+ const isDangerous = this.approvalMode !== ApprovalMode.YOLO && this.sandboxManager.isDangerousCommand(parsedArgs, effectiveCwd);
404947
+ if (aggregateDecision === PolicyDecision.ASK_USER && !rule?.argsPattern && !hasTopLevelRedirectionDowngrade && !hasTopLevelHeuristicDowngrade && !isOutsideWorkspace && !isUntrustedGit && !isDangerous && evaluatedSubCommands > 0 && allSubCommandsExplicitlyAllowed) {
404948
+ aggregateDecision = PolicyDecision.ALLOW;
404949
+ responsibleRule = lastAllowedSubRule;
404950
+ }
404635
404951
  return {
404636
404952
  decision: aggregateDecision,
404637
404953
  rule: aggregateDecision === ruleDecision ? rule : responsibleRule
@@ -404689,11 +405005,16 @@ var init_policy_engine = __esm({
404689
405005
  if (match2) {
404690
405006
  debugLogger.debug(`[PolicyEngine.check] MATCHED rule: toolName=${rule.toolName}, decision=${rule.decision}, priority=${rule.priority}, argsPattern=${rule.argsPattern?.source || "none"}`);
404691
405007
  let ruleDecision = rule.decision;
405008
+ let hasTopLevelHeuristicDowngrade = false;
404692
405009
  if (!skipHeuristics && isShellCommand && command && !("commandPrefix" in rule) && !rule.argsPattern) {
405010
+ const prevDecision = ruleDecision;
404693
405011
  ruleDecision = await this.applyShellHeuristics(command, ruleDecision, shellDirPath);
405012
+ if (prevDecision === PolicyDecision.ALLOW && ruleDecision === PolicyDecision.ASK_USER) {
405013
+ hasTopLevelHeuristicDowngrade = true;
405014
+ }
404694
405015
  }
404695
405016
  if (isShellCommand && toolName) {
404696
- const shellResult = await this.checkShellCommand(toolName, command, ruleDecision, serverName, shellDirPath, rule.allowRedirection, rule, toolAnnotations, subagent);
405017
+ const shellResult = await this.checkShellCommand(toolName, command, ruleDecision, serverName, shellDirPath, rule.allowRedirection, rule, toolAnnotations, subagent, hasTopLevelHeuristicDowngrade);
404697
405018
  decision = shellResult.decision;
404698
405019
  matchedRule = shellResult.rule;
404699
405020
  break;
@@ -404712,10 +405033,15 @@ var init_policy_engine = __esm({
404712
405033
  debugLogger.debug(`[PolicyEngine.check] NO MATCH - using default decision: ${this.defaultDecision}`);
404713
405034
  if (toolName && SHELL_TOOL_NAMES.includes(toolName)) {
404714
405035
  let heuristicDecision = this.defaultDecision;
405036
+ let hasTopLevelHeuristicDowngrade = false;
404715
405037
  if (!skipHeuristics && command) {
405038
+ const prevDecision = heuristicDecision;
404716
405039
  heuristicDecision = await this.applyShellHeuristics(command, heuristicDecision, shellDirPath);
405040
+ if (prevDecision === PolicyDecision.ALLOW && heuristicDecision === PolicyDecision.ASK_USER) {
405041
+ hasTopLevelHeuristicDowngrade = true;
405042
+ }
404717
405043
  }
404718
- const shellResult = await this.checkShellCommand(toolName, command, heuristicDecision, serverName, shellDirPath, false, void 0, toolAnnotations, subagent);
405044
+ const shellResult = await this.checkShellCommand(toolName, command, heuristicDecision, serverName, shellDirPath, false, void 0, toolAnnotations, subagent, hasTopLevelHeuristicDowngrade);
404719
405045
  decision = shellResult.decision;
404720
405046
  matchedRule = shellResult.rule;
404721
405047
  } else {
@@ -410196,6 +410522,7 @@ var init_oauth_provider = __esm({
410196
410522
  issuer: discoveredConfig.issuer,
410197
410523
  tokenUrl: discoveredConfig.tokenUrl,
410198
410524
  scopes: config2.scopes || discoveredConfig.scopes || [],
410525
+ authorizationResponseIssParameterSupported: config2.authorizationResponseIssParameterSupported ?? discoveredConfig.authorizationResponseIssParameterSupported,
410199
410526
  // Preserve existing client credentials
410200
410527
  clientId: config2.clientId,
410201
410528
  clientSecret: config2.clientSecret
@@ -410219,6 +410546,7 @@ var init_oauth_provider = __esm({
410219
410546
  issuer: discoveredConfig.issuer,
410220
410547
  scopes: config2.scopes || discoveredConfig.scopes || [],
410221
410548
  registrationUrl: discoveredConfig.registrationUrl,
410549
+ authorizationResponseIssParameterSupported: config2.authorizationResponseIssParameterSupported ?? discoveredConfig.authorizationResponseIssParameterSupported,
410222
410550
  // Preserve existing client credentials
410223
410551
  clientId: config2.clientId,
410224
410552
  clientSecret: config2.clientSecret
@@ -410231,7 +410559,8 @@ var init_oauth_provider = __esm({
410231
410559
  const pkceParams = generatePKCEParams();
410232
410560
  const preferredPort = getPortFromUrl(config2.redirectUri);
410233
410561
  debugLogger.debug(`Starting callback server for "${serverName}" (expected issuer: ${config2.issuer || "none"})...`);
410234
- const callbackServer = startCallbackServer(pkceParams.state, preferredPort, config2.issuer);
410562
+ const requireIssInResponse = config2.authorizationResponseIssParameterSupported ?? Boolean(config2.issuer);
410563
+ const callbackServer = startCallbackServer(pkceParams.state, preferredPort, config2.issuer, requireIssInResponse);
410235
410564
  const redirectPort = await callbackServer.port;
410236
410565
  debugLogger.debug(`Callback server listening on port ${redirectPort}`);
410237
410566
  if (!config2.clientId) {
@@ -410628,7 +410957,8 @@ async function handleAutomaticOAuth(mcpServerName, mcpServerConfig, wwwAuthentic
410628
410957
  authorizationUrl: oauthConfig.authorizationUrl,
410629
410958
  issuer: oauthConfig.issuer,
410630
410959
  tokenUrl: oauthConfig.tokenUrl,
410631
- scopes: oauthConfig.scopes || []
410960
+ scopes: oauthConfig.scopes || [],
410961
+ authorizationResponseIssParameterSupported: mcpServerConfig.oauth?.authorizationResponseIssParameterSupported ?? oauthConfig.authorizationResponseIssParameterSupported
410632
410962
  };
410633
410963
  debugLogger.log(`Starting OAuth authentication for server '${mcpServerName}'...`);
410634
410964
  const authProvider = new MCPOAuthProvider(new MCPOAuthTokenStorage());
@@ -411044,7 +411374,8 @@ async function connectToMcpServer(clientVersion, mcpServerName, mcpServerConfig,
411044
411374
  authorizationUrl: oauthConfig.authorizationUrl,
411045
411375
  issuer: oauthConfig.issuer,
411046
411376
  tokenUrl: oauthConfig.tokenUrl,
411047
- scopes: oauthConfig.scopes || []
411377
+ scopes: oauthConfig.scopes || [],
411378
+ authorizationResponseIssParameterSupported: mcpServerConfig.oauth?.authorizationResponseIssParameterSupported ?? oauthConfig.authorizationResponseIssParameterSupported
411048
411379
  };
411049
411380
  const authServerUrl = mcpServerConfig.httpUrl || mcpServerConfig.url;
411050
411381
  debugLogger.log(`Starting OAuth authentication for server '${mcpServerName}'...`);