@google/gemini-cli 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.
Files changed (51) hide show
  1. package/bundle/{chunk-EDYPRCAA.js → chunk-5XKZL6EB.js} +2 -2
  2. package/bundle/{chunk-EDVFNWRG.js → chunk-7H3F2KUX.js} +1 -1
  3. package/bundle/{chunk-EFX5XL6M.js → chunk-DPNFVVVC.js} +2 -2
  4. package/bundle/{chunk-QUCRASYB.js → chunk-ENBP32XB.js} +1 -1
  5. package/bundle/{chunk-UFQDAROY.js → chunk-EV5MQULH.js} +1 -1
  6. package/bundle/{chunk-HSZIYPL6.js → chunk-FCPMU3HO.js} +3 -3
  7. package/bundle/{chunk-MLSLNORV.js → chunk-FJUB4QHG.js} +3 -3
  8. package/bundle/{chunk-HN74ORTP.js → chunk-FLKY6NH5.js} +15 -3
  9. package/bundle/{chunk-SDWOC67Z.js → chunk-HHALNKOO.js} +1 -1
  10. package/bundle/{chunk-NX7TO2DU.js → chunk-IESQO4NS.js} +1 -1
  11. package/bundle/{chunk-6OFXOGEG.js → chunk-J7SXW2FI.js} +458 -96
  12. package/bundle/{chunk-KCCVXZ52.js → chunk-KJWA3D3G.js} +25 -9
  13. package/bundle/{chunk-EBP6K2WG.js → chunk-MA35JFUP.js} +25 -9
  14. package/bundle/{chunk-ZQW7OGUO.js → chunk-NR7L3FXV.js} +427 -92
  15. package/bundle/{chunk-ZOHM7BPT.js → chunk-OQYJODOA.js} +1 -1
  16. package/bundle/{chunk-VBVCLWLA.js → chunk-P7ZJXXPR.js} +2 -2
  17. package/bundle/{chunk-2NQXIWQK.js → chunk-PS4UL4AP.js} +15 -3
  18. package/bundle/{chunk-A4TO2DY2.js → chunk-PZ4GDG24.js} +458 -96
  19. package/bundle/{chunk-ZPLFLZQA.js → chunk-QCHFD56F.js} +15 -3
  20. package/bundle/{chunk-Y2HXXBXI.js → chunk-QLNDNLK5.js} +1 -1
  21. package/bundle/{chunk-2VN53SWW.js → chunk-QSLJVHXU.js} +1 -1
  22. package/bundle/{chunk-U76XPSKR.js → chunk-V7G7CIBW.js} +25 -9
  23. package/bundle/{chunk-UBGF6JU5.js → chunk-VOLZQW4I.js} +1 -1
  24. package/bundle/{chunk-VJYKQD5D.js → chunk-WLZT2COY.js} +3 -3
  25. package/bundle/{cleanup-GGCB2LVD.js → cleanup-K7PKM5O3.js} +2 -2
  26. package/bundle/{cleanup-JRDG3OTL.js → cleanup-NAOD2SQV.js} +2 -2
  27. package/bundle/{cleanup-SSW6IXTR.js → cleanup-QRBWTWKF.js} +2 -2
  28. package/bundle/{core-IUIZE6JW.js → core-4P3HPH34.js} +11 -1
  29. package/bundle/{dist-7L3BRW6V.js → core-ICFDSJL3.js} +11 -1
  30. package/bundle/{devtoolsService-3SADPKKB.js → devtoolsService-4MGN7S5K.js} +2 -2
  31. package/bundle/{devtoolsService-MF6JKRUV.js → devtoolsService-7CS2YIL6.js} +2 -2
  32. package/bundle/{devtoolsService-XZCKFZP5.js → devtoolsService-LKOU7QN7.js} +2 -2
  33. package/bundle/{core-6HRWFQZW.js → dist-YMXCTNJD.js} +11 -1
  34. package/bundle/docs/tools/mcp-server.md +41 -12
  35. package/bundle/{gemini-QOQNG5VY.js → gemini-6ESINPDZ.js} +14 -14
  36. package/bundle/{gemini-ZMMDUAQJ.js → gemini-IPXWMEB5.js} +14 -14
  37. package/bundle/{gemini-JPMMQ3KA.js → gemini-Q2GRYIAK.js} +14 -14
  38. package/bundle/gemini.js +7 -7
  39. package/bundle/{interactiveCli-R3P4A3FC.js → interactiveCli-2T27ZS4G.js} +9 -20
  40. package/bundle/{interactiveCli-UDWGRVJL.js → interactiveCli-66E5CZDR.js} +9 -20
  41. package/bundle/{interactiveCli-3I4TG5SK.js → interactiveCli-IX4FVS6G.js} +9 -20
  42. package/bundle/{liteRtServerManager-OVPUSK2T.js → liteRtServerManager-BFSWF4GA.js} +4 -4
  43. package/bundle/{liteRtServerManager-RAV63AGE.js → liteRtServerManager-CU5D7FDE.js} +4 -4
  44. package/bundle/{liteRtServerManager-4MWCEVVM.js → liteRtServerManager-JJZYMP2G.js} +4 -4
  45. package/bundle/{oauth2-provider-XFP63FGJ.js → oauth2-provider-65ILSORU.js} +1 -1
  46. package/bundle/{oauth2-provider-FVTRRJZR.js → oauth2-provider-CHSEPLKC.js} +1 -1
  47. package/bundle/{oauth2-provider-EWOA64PR.js → oauth2-provider-N73PS5ZT.js} +1 -1
  48. package/bundle/{start-DJAIXPPY.js → start-6JMUZJ7T.js} +6 -6
  49. package/bundle/{start-VQQ2Q5KT.js → start-6KYN7M6S.js} +6 -6
  50. package/bundle/{start-5O7TBK5L.js → start-YI7IUIYU.js} +6 -6
  51. package/package.json +1 -1
@@ -283844,8 +283844,8 @@ function isValidToolName(name3, options = {}) {
283844
283844
  }
283845
283845
 
283846
283846
  // packages/core/src/generated/git-commit.ts
283847
- var GIT_COMMIT_INFO = "fb972b2f8";
283848
- var CLI_VERSION = "0.64.0-preview.0";
283847
+ var GIT_COMMIT_INFO = "90997f398";
283848
+ var CLI_VERSION = "0.64.0-preview.1";
283849
283849
 
283850
283850
  // packages/core/src/ide/detect-ide.ts
283851
283851
  var IDE_DEFINITIONS = {
@@ -307961,7 +307961,7 @@ function getVersion() {
307961
307961
  }
307962
307962
  versionPromise = (async () => {
307963
307963
  const pkgJson = await getPackageJson(__dirname4);
307964
- return "0.64.0-preview.0";
307964
+ return "0.64.0-preview.1";
307965
307965
  })();
307966
307966
  return versionPromise;
307967
307967
  }
@@ -310443,6 +310443,144 @@ var BENIGN_SUBCOMMAND_TOKENS = /* @__PURE__ */ new Set([
310443
310443
  "restart",
310444
310444
  "status"
310445
310445
  ]);
310446
+ var SAFE_SHORT_FLAGS = /* @__PURE__ */ new Set([
310447
+ "-a",
310448
+ "-A",
310449
+ "-b",
310450
+ "-B",
310451
+ "-C",
310452
+ "-d",
310453
+ "-D",
310454
+ "-E",
310455
+ "-F",
310456
+ "-g",
310457
+ "-G",
310458
+ "-h",
310459
+ "-H",
310460
+ "-i",
310461
+ "-I",
310462
+ "-l",
310463
+ "-L",
310464
+ "-m",
310465
+ "-n",
310466
+ "-N",
310467
+ "-p",
310468
+ "-P",
310469
+ "-q",
310470
+ "-r",
310471
+ "-R",
310472
+ "-s",
310473
+ "-S",
310474
+ "-t",
310475
+ "-T",
310476
+ "-u",
310477
+ "-U",
310478
+ "-v",
310479
+ "-V",
310480
+ "-w",
310481
+ "-W",
310482
+ "-x",
310483
+ "-z",
310484
+ "-1",
310485
+ // Common combined POSIX inspection flags (ls, grep, ps, git)
310486
+ "-la",
310487
+ "-al",
310488
+ "-ld",
310489
+ "-dl",
310490
+ "-lh",
310491
+ "-hl",
310492
+ "-lah",
310493
+ "-alh",
310494
+ "-lrt",
310495
+ "-ltr",
310496
+ "-rn",
310497
+ "-nr",
310498
+ "-ri",
310499
+ "-ir",
310500
+ "-rni",
310501
+ "-rin",
310502
+ "-ni",
310503
+ "-in",
310504
+ "-ef",
310505
+ "-aux"
310506
+ ]);
310507
+ var SAFE_LONG_FLAGS = /* @__PURE__ */ new Set([
310508
+ "--all",
310509
+ "--oneline",
310510
+ "--stat",
310511
+ "--numstat",
310512
+ "--shortstat",
310513
+ "--name-only",
310514
+ "--name-status",
310515
+ "--graph",
310516
+ "--decorate",
310517
+ "--no-pager",
310518
+ "--color",
310519
+ "--no-color",
310520
+ "--grep",
310521
+ "--author",
310522
+ "--since",
310523
+ "--until",
310524
+ "--before",
310525
+ "--after",
310526
+ "--pretty",
310527
+ "--format",
310528
+ "--abbrev-commit",
310529
+ "--max-count",
310530
+ "--cached",
310531
+ "--staged",
310532
+ "--short",
310533
+ "--branch",
310534
+ "--porcelain",
310535
+ "--help",
310536
+ "--version",
310537
+ "--quiet",
310538
+ "--verbose",
310539
+ "--dry-run",
310540
+ "--ignore-case",
310541
+ "--line-number",
310542
+ "--recursive",
310543
+ "--extended-regexp",
310544
+ "--fixed-strings",
310545
+ "--files-with-matches",
310546
+ "--count",
310547
+ "--context",
310548
+ "--before-context",
310549
+ "--after-context"
310550
+ ]);
310551
+ var HIGH_RISK_VALUE_FLAGS = /* @__PURE__ */ new Set([
310552
+ "-c",
310553
+ "-e",
310554
+ "--eval",
310555
+ "--exec",
310556
+ "-exec",
310557
+ "-execdir",
310558
+ "--run_under",
310559
+ "--test_strategy",
310560
+ "--test_arg",
310561
+ "--test_env",
310562
+ "--action_env",
310563
+ "--jvmopt",
310564
+ "--host_jvm_args",
310565
+ "--wrapper_script_flag",
310566
+ "--upload-pack",
310567
+ "--receive-pack",
310568
+ "--require",
310569
+ "--import",
310570
+ "--loader"
310571
+ ]);
310572
+ var FILE_MODE_STRING_REGEX = /^[dcbpsl-][rwxstST-]{9}[+@.]?$/i;
310573
+ var BARE_DASHES_REGEX = /^--+$/;
310574
+ var BARE_SHELL_VARIABLE_REGEX = /^\$(?:[a-zA-Z_][a-zA-Z0-9_]*|\{[a-zA-Z_][a-zA-Z0-9_]*\})$/;
310575
+ function isHighRiskPatternToken(token2) {
310576
+ 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);
310577
+ }
310578
+ function hasUntrustedSubstring(untrustedContext, value) {
310579
+ const normalized2 = value.toLowerCase().replace(/\\/g, "/");
310580
+ return untrustedContext.untrustedTexts.some(
310581
+ (text) => text.toLowerCase().includes(normalized2)
310582
+ );
310583
+ }
310446
310584
  function extractUntrustedContext(history) {
310447
310585
  const untrustedTexts = [];
310448
310586
  const untrustedTokens = /* @__PURE__ */ new Set();
@@ -310471,35 +310609,24 @@ function extractUntrustedContext(history) {
310471
310609
  if (untrustedContent) {
310472
310610
  const normalizedContent = untrustedContent.replace(/\\/g, "/");
310473
310611
  untrustedTexts.push(normalizedContent);
310474
- const tokens = normalizedContent.split(/[\s,`"';()|&[\]{}<>]+/).map((t2) => t2.trim()).filter((t2) => t2.length > 1).filter((t2) => !BENIGN_SUBCOMMAND_TOKENS.has(t2.toLowerCase()));
310612
+ const contentWithoutShellMetadata = normalizedContent.replace(/^\s*Output:\s*(\(empty\))?\s*/i, "").replace(
310613
+ /\n(?:Exit Code|Signal|Background PIDs|Process Group PGID):.*$/gim,
310614
+ ""
310615
+ );
310616
+ const tokens = contentWithoutShellMetadata.split(/[\s,`"';()|&[\]{}<>]+/).map((t2) => t2.trim()).filter((t2) => t2.length > 1).filter((t2) => !BARE_DASHES_REGEX.test(t2)).filter((t2) => !FILE_MODE_STRING_REGEX.test(t2)).filter((t2) => !BENIGN_SUBCOMMAND_TOKENS.has(t2.toLowerCase())).filter((t2) => !SAFE_SHORT_FLAGS.has(t2)).filter((t2) => !SAFE_LONG_FLAGS.has(t2.toLowerCase()));
310475
310617
  for (const token2 of tokens) {
310476
- const lowerToken = token2.toLowerCase();
310477
- untrustedTokens.add(lowerToken);
310478
- if (lowerToken.startsWith("--")) {
310479
- untrustedTokens.add(lowerToken.substring(2));
310480
- } else if (lowerToken.startsWith("-")) {
310481
- untrustedTokens.add(lowerToken.substring(1));
310482
- }
310483
- if (lowerToken.includes("=")) {
310484
- const eqParts = lowerToken.split("=");
310618
+ const isShortFlag = token2.startsWith("-") && !token2.startsWith("--");
310619
+ const tokenToStore = isShortFlag ? token2 : token2.toLowerCase();
310620
+ untrustedTokens.add(tokenToStore);
310621
+ if (token2.includes("=")) {
310622
+ const eqParts = token2.split("=");
310485
310623
  for (const eqPart of eqParts) {
310486
310624
  const trimmedPart = eqPart.trim();
310487
- if (trimmedPart.length > 1) {
310488
- untrustedTokens.add(trimmedPart);
310489
- if (trimmedPart.startsWith("--")) {
310490
- untrustedTokens.add(trimmedPart.substring(2));
310491
- } else if (trimmedPart.startsWith("-")) {
310492
- untrustedTokens.add(trimmedPart.substring(1));
310493
- }
310494
- }
310495
- }
310496
- }
310497
- if (lowerToken.includes("/")) {
310498
- const pathParts = lowerToken.split("/");
310499
- for (const part2 of pathParts) {
310500
- const trimmedPart = part2.trim();
310501
- if (trimmedPart.length > 1) {
310502
- untrustedTokens.add(trimmedPart);
310625
+ if (trimmedPart.length > 1 && !BARE_DASHES_REGEX.test(trimmedPart) && !SAFE_SHORT_FLAGS.has(trimmedPart) && !SAFE_LONG_FLAGS.has(trimmedPart.toLowerCase())) {
310626
+ const isPartShortFlag = trimmedPart.startsWith("-") && !trimmedPart.startsWith("--");
310627
+ untrustedTokens.add(
310628
+ isPartShortFlag ? trimmedPart : trimmedPart.toLowerCase()
310629
+ );
310503
310630
  }
310504
310631
  }
310505
310632
  }
@@ -310518,7 +310645,7 @@ function findUntrustedFlags(command, untrustedContext) {
310518
310645
  let parsed;
310519
310646
  try {
310520
310647
  const normalizedCommand = process.platform === "win32" ? command.replace(/\\(?!")/g, "/") : command;
310521
- parsed = (0, import_shell_quote5.parse)(normalizedCommand);
310648
+ parsed = (0, import_shell_quote5.parse)(normalizedCommand, (key) => `$${key}`);
310522
310649
  } catch {
310523
310650
  parsed = command.trim().split(/\s+/);
310524
310651
  }
@@ -310540,45 +310667,57 @@ function findUntrustedFlags(command, untrustedContext) {
310540
310667
  const token2 = rawTokens[i3];
310541
310668
  const lowerToken = token2.toLowerCase();
310542
310669
  if (token2.startsWith("-")) {
310670
+ if (/^--?$/.test(token2)) {
310671
+ continue;
310672
+ }
310673
+ let rawFlagName = token2;
310543
310674
  let flagToCheck = lowerToken;
310675
+ let rawValToCheck;
310544
310676
  let valToCheck;
310545
310677
  if (token2.includes("=")) {
310546
- const eqIdx = lowerToken.indexOf("=");
310678
+ const eqIdx = token2.indexOf("=");
310679
+ rawFlagName = token2.substring(0, eqIdx);
310547
310680
  flagToCheck = lowerToken.substring(0, eqIdx);
310681
+ rawValToCheck = token2.substring(eqIdx + 1);
310548
310682
  valToCheck = lowerToken.substring(eqIdx + 1);
310549
310683
  }
310550
- if (untrustedContext.untrustedTokens.has(lowerToken) || untrustedContext.untrustedTokens.has(flagToCheck)) {
310684
+ const isSafeFlag = SAFE_SHORT_FLAGS.has(rawFlagName) || SAFE_LONG_FLAGS.has(flagToCheck);
310685
+ const isHighRiskFlag = HIGH_RISK_VALUE_FLAGS.has(rawFlagName) || rawFlagName.startsWith("--") && HIGH_RISK_VALUE_FLAGS.has(flagToCheck);
310686
+ const isShortFlag = rawFlagName.startsWith("-") && !rawFlagName.startsWith("--");
310687
+ const tokenToLookup = isShortFlag ? rawFlagName : lowerToken;
310688
+ const flagToLookup = isShortFlag ? rawFlagName : flagToCheck;
310689
+ if (!isSafeFlag && (untrustedContext.untrustedTokens.has(tokenToLookup) || untrustedContext.untrustedTokens.has(flagToLookup))) {
310551
310690
  detected.add(token2);
310552
- continue;
310691
+ if (token2.includes("=") || !isHighRiskFlag) {
310692
+ continue;
310693
+ }
310553
310694
  }
310554
- if (valToCheck && untrustedContext.untrustedTokens.has(valToCheck)) {
310555
- detected.add(token2);
310556
- continue;
310695
+ if (valToCheck && rawValToCheck) {
310696
+ const isValUntrusted = untrustedContext.untrustedTokens.has(valToCheck) && (isHighRiskFlag || !isSafeFlag && isHighRiskPatternToken(rawValToCheck));
310697
+ const isHighRiskValSubstring = (!isSafeFlag || isHighRiskFlag) && isHighRiskPatternToken(rawValToCheck) && hasUntrustedSubstring(untrustedContext, valToCheck);
310698
+ if (isValUntrusted || isHighRiskValSubstring) {
310699
+ detected.add(token2);
310700
+ continue;
310701
+ }
310557
310702
  }
310558
- const nextToken = rawTokens[i3 + 1];
310559
- if (nextToken && !nextToken.startsWith("-") && untrustedContext.untrustedTokens.has(nextToken.toLowerCase())) {
310560
- detected.add(token2);
310561
- detected.add(nextToken);
310703
+ if (!token2.includes("=") && isHighRiskFlag) {
310704
+ const nextToken = rawTokens[i3 + 1];
310705
+ 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))) {
310706
+ detected.add(token2);
310707
+ detected.add(nextToken);
310708
+ }
310562
310709
  }
310563
310710
  } else {
310564
- if (token2.length <= 1) {
310711
+ if (token2.length <= 1 || BARE_SHELL_VARIABLE_REGEX.test(token2)) {
310565
310712
  continue;
310566
310713
  }
310567
- const isSensitiveWord = token2.includes("/") || token2.includes(".") || token2.includes(":");
310568
- const isHighRiskPattern = token2.startsWith("http://") || token2.startsWith("https://") || token2.startsWith("/") || token2.startsWith("\\") || /^[a-zA-Z]:[\\/]/.test(token2);
310569
- if (isSensitiveWord) {
310714
+ if (isHighRiskPatternToken(token2)) {
310570
310715
  if (untrustedContext.untrustedTokens.has(lowerToken)) {
310571
310716
  detected.add(token2);
310572
310717
  continue;
310573
310718
  }
310574
- if (isHighRiskPattern) {
310575
- const normalizedLowerToken = lowerToken.replace(/\\/g, "/");
310576
- for (const text of untrustedContext.untrustedTexts) {
310577
- if (text.toLowerCase().includes(normalizedLowerToken)) {
310578
- detected.add(token2);
310579
- break;
310580
- }
310581
- }
310719
+ if (hasUntrustedSubstring(untrustedContext, lowerToken)) {
310720
+ detected.add(token2);
310582
310721
  }
310583
310722
  }
310584
310723
  }
@@ -335229,6 +335368,9 @@ function scrubPart(part) {
335229
335368
  if (part.functionResponse.id) {
335230
335369
  scrubbedResp["id"] = part.functionResponse.id;
335231
335370
  }
335371
+ if ("parts" in part.functionResponse && Array.isArray(part.functionResponse.parts)) {
335372
+ scrubbedResp["parts"] = part.functionResponse.parts.map(scrubPart);
335373
+ }
335232
335374
  scrubbed["functionResponse"] = scrubbedResp;
335233
335375
  }
335234
335376
  if ("fileData" in part) {
@@ -335259,6 +335401,8 @@ var MID_STREAM_RETRY_OPTIONS = {
335259
335401
  };
335260
335402
  var SYNTHETIC_THOUGHT_SIGNATURE2 = "skip_thought_signature_validator";
335261
335403
  var INTERRUPTED_RESPONSE_PLACEHOLDER = "[The previous response was interrupted before it completed.]";
335404
+ var INTERRUPTED_TOOL_RESPONSE_ERROR = "Response was lost or interrupted.";
335405
+ var CONTINUE_PROMPT_TEXT = "Please continue.";
335262
335406
  function isIndexedPart(part) {
335263
335407
  return "callIndex" in part;
335264
335408
  }
@@ -335503,6 +335647,7 @@ var GeminiChat = class {
335503
335647
  let userContent = createUserContent(message);
335504
335648
  const isOriginalFunctionResponse = isFunctionResponse(userContent);
335505
335649
  if (!isOriginalFunctionResponse) {
335650
+ this.closeUnansweredToolCallsTurn();
335506
335651
  this.closeUnansweredToolResponseTurn();
335507
335652
  }
335508
335653
  const { model } = this.context.config.modelConfigService.getResolvedConfig(modelConfigKey);
@@ -335531,7 +335676,7 @@ var GeminiChat = class {
335531
335676
  } else {
335532
335677
  const history = this.agentHistory.get();
335533
335678
  const lastTurn = history[history.length - 1];
335534
- if (!lastTurn || partListUnionToString(lastTurn.content.parts || []) !== userMessageContent) {
335679
+ if (!lastTurn || lastTurn.content.role !== "user" || partListUnionToString(lastTurn.content.parts || []) !== userMessageContent) {
335535
335680
  const id = this.chatRecordingService.recordMessage({
335536
335681
  model,
335537
335682
  type: "user",
@@ -335588,11 +335733,12 @@ var GeminiChat = class {
335588
335733
  } else {
335589
335734
  const history = this.agentHistory.get();
335590
335735
  const lastTurn = history[history.length - 1];
335591
- if (!lastTurn || partListUnionToString(lastTurn.content.parts || []) !== partListUnionToString(userContent.parts || [])) {
335736
+ if (!lastTurn || lastTurn.content.role !== "user" || partListUnionToString(lastTurn.content.parts || []) !== partListUnionToString(userContent.parts || [])) {
335592
335737
  this.agentHistory.push({ id, content: userContent });
335593
335738
  }
335594
335739
  }
335595
335740
  }
335741
+ this.replaceEmptyTrailingUserTurn();
335596
335742
  const requestHistory = this.getHistoryTurns(true);
335597
335743
  const streamWithRetries = async function* () {
335598
335744
  let isSuccess = false;
@@ -335734,23 +335880,84 @@ var GeminiChat = class {
335734
335880
  return streamWithRetries.call(this);
335735
335881
  }
335736
335882
  /**
335737
- * Appends a closing model turn when history ends with an unanswered tool
335738
- * response, so the next user message stays a turn of its own.
335883
+ * Appends a dedicated synthetic user turn with matching functionResponse
335884
+ * parts when history ends with an unclosed model functionCall. The
335885
+ * subsequent user prompt is then recorded as a clean turn of its own (after
335886
+ * `closeUnansweredToolResponseTurn` adds the closing model turn).
335739
335887
  */
335740
- closeUnansweredToolResponseTurn() {
335888
+ closeUnansweredToolCallsTurn() {
335741
335889
  const turns = this.agentHistory.get();
335742
335890
  const last2 = turns[turns.length - 1];
335743
- if (last2?.content.role !== "user" || !last2.content.parts?.some((part) => !!part.functionResponse)) {
335891
+ if (last2?.content.role !== "model") {
335744
335892
  return;
335745
335893
  }
335894
+ const missingResponses = buildInterruptedToolResponseParts(
335895
+ last2.content.parts ?? []
335896
+ );
335897
+ if (missingResponses.length === 0) {
335898
+ return;
335899
+ }
335900
+ const id = this.chatRecordingService.recordSyntheticMessage(
335901
+ "user",
335902
+ missingResponses
335903
+ );
335746
335904
  this.agentHistory.push({
335747
- id: randomUUID6(),
335748
- content: {
335749
- role: "model",
335750
- parts: [{ text: INTERRUPTED_RESPONSE_PLACEHOLDER }]
335751
- }
335905
+ id,
335906
+ content: { role: "user", parts: missingResponses }
335752
335907
  });
335753
335908
  }
335909
+ /**
335910
+ * Replaces the parts of a trailing user turn that carries no content (e.g.
335911
+ * only empty text parts) with a continuation prompt, keeping the in-memory
335912
+ * history and the session log in sync. Runs once per send so that the
335913
+ * persisted history never keeps an empty user turn between model turns.
335914
+ */
335915
+ replaceEmptyTrailingUserTurn() {
335916
+ const turns = this.agentHistory.get();
335917
+ const last2 = turns[turns.length - 1];
335918
+ if (last2?.content.role !== "user" || hasNonEmptyParts(last2.content)) {
335919
+ return;
335920
+ }
335921
+ this.agentHistory.set([
335922
+ ...turns.slice(0, -1),
335923
+ {
335924
+ id: last2.id,
335925
+ content: { ...last2.content, parts: [{ text: CONTINUE_PROMPT_TEXT }] }
335926
+ }
335927
+ ]);
335928
+ this.chatRecordingService.updateMessagesFromHistory(
335929
+ this.agentHistory.get()
335930
+ );
335931
+ }
335932
+ /**
335933
+ * Appends a closing model turn when history ends with an unanswered tool
335934
+ * response, so the next user message stays a turn of its own.
335935
+ */
335936
+ closeUnansweredToolResponseTurn() {
335937
+ const turns = this.agentHistory.get();
335938
+ let targetTurn = turns[turns.length - 1];
335939
+ let hadEmptyTrailingModelTurn = false;
335940
+ if (targetTurn?.content.role === "model" && (!targetTurn.content.parts || targetTurn.content.parts.length === 0)) {
335941
+ hadEmptyTrailingModelTurn = true;
335942
+ targetTurn = turns[turns.length - 2];
335943
+ }
335944
+ if (targetTurn?.content.role !== "user" || !targetTurn.content.parts?.some((part) => !!part.functionResponse)) {
335945
+ return;
335946
+ }
335947
+ if (hadEmptyTrailingModelTurn) {
335948
+ const lastTurn = turns[turns.length - 1];
335949
+ lastTurn.content.parts = [{ text: INTERRUPTED_RESPONSE_PLACEHOLDER }];
335950
+ this.chatRecordingService.updateMessagesFromHistory(turns);
335951
+ } else {
335952
+ this.agentHistory.push({
335953
+ id: randomUUID6(),
335954
+ content: {
335955
+ role: "model",
335956
+ parts: [{ text: INTERRUPTED_RESPONSE_PLACEHOLDER }]
335957
+ }
335958
+ });
335959
+ }
335960
+ }
335754
335961
  extractBinaryInjections(parts2) {
335755
335962
  const binaryParts = [];
335756
335963
  if (parts2) {
@@ -335900,10 +336107,16 @@ var GeminiChat = class {
335900
336107
  if (this.onModelChanged) {
335901
336108
  this.tools = await this.onModelChanged(modelToUse);
335902
336109
  }
336110
+ const normalizedContents = ensureTerminalUserTurn(contentsToUse);
336111
+ if (normalizedContents !== contentsToUse) {
336112
+ debugLogger.warn(
336113
+ "Final contents do not end with a valid user turn. Normalizing contents to satisfy Gemini API invariant."
336114
+ );
336115
+ }
335903
336116
  lastModelToUse = modelToUse;
335904
336117
  lastConfig = config2;
335905
- lastContentsToUse = contentsToUse;
335906
- const finalContents = stripToolCallIdPrefixes(contentsToUse);
336118
+ lastContentsToUse = normalizedContents;
336119
+ const finalContents = stripToolCallIdPrefixes(normalizedContents);
335907
336120
  return this.context.config.getContentGenerator().generateContentStream(
335908
336121
  {
335909
336122
  model: modelToUse,
@@ -336017,6 +336230,29 @@ var GeminiChat = class {
336017
336230
  }
336018
336231
  ensureStableToolIds(this.agentHistory.get());
336019
336232
  }
336233
+ /**
336234
+ * Removes the trailing model turn when it holds function calls that were
336235
+ * never answered (e.g. every call in the batch was declined). Earlier
336236
+ * completed tool rounds and the originating user prompt are kept, and the
336237
+ * durable IDs of the remaining turns are preserved.
336238
+ *
336239
+ * @returns true when a turn was removed.
336240
+ */
336241
+ discardTrailingUnansweredToolCallTurn() {
336242
+ const turns = this.agentHistory.get();
336243
+ const last2 = turns[turns.length - 1];
336244
+ if (last2?.content.role !== "model" || !last2.content.parts?.some((part) => !!part.functionCall)) {
336245
+ return false;
336246
+ }
336247
+ this.agentHistory.rollback(turns.length - 1);
336248
+ this.lastPromptTokenCount = estimateTokenCountSync(
336249
+ this.agentHistory.flatMap((c4) => c4.content.parts || [])
336250
+ );
336251
+ this.chatRecordingService.updateMessagesFromHistory(
336252
+ this.agentHistory.get()
336253
+ );
336254
+ return true;
336255
+ }
336020
336256
  setHistory(history) {
336021
336257
  const wrappedHistory = history.map((item) => {
336022
336258
  if ("id" in item && "content" in item) {
@@ -336422,8 +336658,43 @@ function isSchemaDepthError(errorMessage) {
336422
336658
  function isInvalidArgumentError(errorMessage) {
336423
336659
  return errorMessage.includes("Request contains an invalid argument");
336424
336660
  }
336661
+ function isEmptyTextPart(part) {
336662
+ if (part.text !== "") {
336663
+ return false;
336664
+ }
336665
+ return !Object.keys(part).some(
336666
+ (key) => key !== "text" && key !== "thought" && key !== "callIndex"
336667
+ );
336668
+ }
336669
+ function hasNonEmptyParts(content) {
336670
+ return (content.parts ?? []).some((part) => !isEmptyTextPart(part));
336671
+ }
336672
+ function buildInterruptedToolResponseParts(parts2) {
336673
+ return parts2.filter((part) => !!part?.functionCall).map((part) => ({
336674
+ functionResponse: {
336675
+ name: part.functionCall.name?.trim() || "generic_tool",
336676
+ id: part.functionCall.id,
336677
+ response: { error: INTERRUPTED_TOOL_RESPONSE_ERROR }
336678
+ }
336679
+ }));
336680
+ }
336681
+ function ensureTerminalUserTurn(contents) {
336682
+ const last2 = contents.at(-1);
336683
+ if (last2?.role === "user" && hasNonEmptyParts(last2)) {
336684
+ return contents;
336685
+ }
336686
+ if (last2?.role === "user") {
336687
+ return [
336688
+ ...contents.slice(0, -1),
336689
+ { ...last2, parts: [{ text: CONTINUE_PROMPT_TEXT }] }
336690
+ ];
336691
+ }
336692
+ const missingResponses = buildInterruptedToolResponseParts(last2?.parts ?? []);
336693
+ const parts2 = missingResponses.length > 0 ? missingResponses : [{ text: CONTINUE_PROMPT_TEXT }];
336694
+ return [...contents, { role: "user", parts: parts2 }];
336695
+ }
336425
336696
  function stripToolCallIdPrefixes(contents) {
336426
- return contents.map((content) => {
336697
+ const stripped = contents.map((content) => {
336427
336698
  const parts2 = (content.parts || []).map((part) => {
336428
336699
  const newPart = { ...part };
336429
336700
  if (newPart.functionCall) {
@@ -336431,8 +336702,7 @@ function stripToolCallIdPrefixes(contents) {
336431
336702
  const name3 = fc.name?.trim() || "generic_tool";
336432
336703
  if (fc.id && fc.id.startsWith(`${name3}__`)) {
336433
336704
  newPart.functionCall = {
336434
- name: fc.name,
336435
- args: fc.args,
336705
+ ...fc,
336436
336706
  id: fc.id.substring(name3.length + 2)
336437
336707
  };
336438
336708
  }
@@ -336442,8 +336712,9 @@ function stripToolCallIdPrefixes(contents) {
336442
336712
  const name3 = fr.name?.trim() || "generic_tool";
336443
336713
  if (fr.id && fr.id.startsWith(`${name3}__`)) {
336444
336714
  newPart.functionResponse = {
336445
- name: fr.name,
336446
- response: fr.response,
336715
+ // History parts are plain JSON objects, not class instances.
336716
+ // eslint-disable-next-line @typescript-eslint/no-misused-spread
336717
+ ...fr,
336447
336718
  id: fr.id.substring(name3.length + 2)
336448
336719
  };
336449
336720
  }
@@ -336455,20 +336726,30 @@ function stripToolCallIdPrefixes(contents) {
336455
336726
  delete newPart.text;
336456
336727
  }
336457
336728
  return newPart;
336458
- }).filter((part) => {
336459
- const hasOtherKeys = Object.keys(part).some(
336460
- (key) => key !== "text" && key !== "thought" && key !== "callIndex"
336461
- );
336462
- if (part.text !== void 0 && part.text === "" && !hasOtherKeys) {
336463
- return false;
336464
- }
336465
- return true;
336466
- });
336729
+ }).filter((part) => !isEmptyTextPart(part));
336467
336730
  return {
336468
336731
  ...content,
336469
336732
  parts: parts2
336470
336733
  };
336471
- });
336734
+ }).filter((content) => content.parts.length > 0);
336735
+ return coalesceConsecutiveContents(stripped);
336736
+ }
336737
+ function coalesceConsecutiveContents(contents) {
336738
+ const result2 = [];
336739
+ for (const item of contents) {
336740
+ const lastIdx = result2.length - 1;
336741
+ const last2 = result2[lastIdx];
336742
+ if (last2 && last2.role && last2.role === item.role) {
336743
+ const hasParts = last2.parts || item.parts;
336744
+ result2[lastIdx] = {
336745
+ ...last2,
336746
+ parts: hasParts ? [...last2.parts || [], ...item.parts || []] : void 0
336747
+ };
336748
+ } else {
336749
+ result2.push({ ...item });
336750
+ }
336751
+ }
336752
+ return result2;
336472
336753
  }
336473
336754
  function coalesceConsecutiveRoles(history) {
336474
336755
  const result2 = [];
@@ -344038,7 +344319,13 @@ var OAuthUtils = class {
344038
344319
  issuer: metadata2.issuer,
344039
344320
  tokenUrl: metadata2.token_endpoint,
344040
344321
  scopes: metadata2.scopes_supported || [],
344041
- registrationUrl: metadata2.registration_endpoint
344322
+ registrationUrl: metadata2.registration_endpoint,
344323
+ // RFC 9207 Section 3: the flag defaults to false when omitted. Discovered
344324
+ // configurations therefore always carry an explicit boolean, which lets
344325
+ // the callback policy distinguish them from an explicitly configured issuer.
344326
+ // Only a literal boolean `true` enables the flag, so loosely typed values
344327
+ // returned by some servers (e.g. "false" or "0") are not coerced to true.
344328
+ authorizationResponseIssParameterSupported: metadata2.authorization_response_iss_parameter_supported === true
344042
344329
  };
344043
344330
  }
344044
344331
  /**
@@ -344439,7 +344726,7 @@ var A2AAuthProviderFactory = class _A2AAuthProviderFactory {
344439
344726
  return provider;
344440
344727
  }
344441
344728
  case "oauth2": {
344442
- const { OAuth2AuthProvider } = await import("./oauth2-provider-XFP63FGJ.js");
344729
+ const { OAuth2AuthProvider } = await import("./oauth2-provider-65ILSORU.js");
344443
344730
  const provider = new OAuth2AuthProvider(
344444
344731
  authConfig,
344445
344732
  options.agentName ?? "unknown",
@@ -351215,6 +351502,21 @@ var GeminiClient = class {
351215
351502
  this.updateTelemetryTokenCount();
351216
351503
  this.forceFullIdeContext = true;
351217
351504
  }
351505
+ /**
351506
+ * Removes the trailing model turn when it only holds unanswered function
351507
+ * calls. See {@link GeminiChat.discardTrailingUnansweredToolCallTurn}.
351508
+ * Returns false without side effects when the chat is not initialized.
351509
+ */
351510
+ discardTrailingUnansweredToolCallTurn() {
351511
+ if (!this.isInitialized()) {
351512
+ return false;
351513
+ }
351514
+ const removed = this.getChat().discardTrailingUnansweredToolCallTurn();
351515
+ if (removed) {
351516
+ this.updateTelemetryTokenCount();
351517
+ }
351518
+ return removed;
351519
+ }
351218
351520
  lastUsedModelId;
351219
351521
  async setTools(modelId) {
351220
351522
  if (!this.chat) {
@@ -368993,7 +369295,7 @@ var PolicyEngine = class {
368993
369295
  }
368994
369296
  return decision;
368995
369297
  }
368996
- async checkShellCommand(toolName, command, ruleDecision, serverName, dir_path, allowRedirection, rule, toolAnnotations, subagent) {
369298
+ async checkShellCommand(toolName, command, ruleDecision, serverName, dir_path, allowRedirection, rule, toolAnnotations, subagent, hasTopLevelHeuristicDowngrade = false) {
368997
369299
  if (!command) {
368998
369300
  return {
368999
369301
  decision: ruleDecision,
@@ -369035,7 +369337,12 @@ var PolicyEngine = class {
369035
369337
  }
369036
369338
  let aggregateDecision = ruleDecision;
369037
369339
  let responsibleRule = rule && ruleDecision === rule.decision ? rule : void 0;
369038
- if (this.shouldDowngradeForRedirection(command, allowRedirection, rule)) {
369340
+ const hasTopLevelRedirectionDowngrade = this.shouldDowngradeForRedirection(
369341
+ command,
369342
+ allowRedirection,
369343
+ rule
369344
+ );
369345
+ if (hasTopLevelRedirectionDowngrade) {
369039
369346
  if (aggregateDecision === "allow" /* ALLOW */) {
369040
369347
  debugLogger.debug(
369041
369348
  `[PolicyEngine.check] Downgrading ALLOW to ASK_USER for redirected command: ${command}`
@@ -369044,6 +369351,9 @@ var PolicyEngine = class {
369044
369351
  responsibleRule = void 0;
369045
369352
  }
369046
369353
  }
369354
+ let evaluatedSubCommands = 0;
369355
+ let allSubCommandsExplicitlyAllowed = true;
369356
+ let lastAllowedSubRule;
369047
369357
  for (const detail of subCommands) {
369048
369358
  if (REDIRECTION_NAMES.has(detail.name)) {
369049
369359
  continue;
@@ -369052,6 +369362,7 @@ var PolicyEngine = class {
369052
369362
  const isAtomic = subCmd === command || detail.startIndex === 0 && detail.text.length === command.length;
369053
369363
  const stripped = stripShellWrapper(subCmd);
369054
369364
  if (stripped !== subCmd) {
369365
+ evaluatedSubCommands++;
369055
369366
  const wrapperResult = await this.check(
369056
369367
  { name: toolName, args: { command: stripped, dir_path } },
369057
369368
  serverName,
@@ -369062,15 +369373,21 @@ var PolicyEngine = class {
369062
369373
  if (wrapperResult.decision === "deny" /* DENY */)
369063
369374
  return wrapperResult;
369064
369375
  if (wrapperResult.decision === "ask_user" /* ASK_USER */) {
369376
+ allSubCommandsExplicitlyAllowed = false;
369065
369377
  if (aggregateDecision === "allow" /* ALLOW */) {
369066
369378
  responsibleRule = wrapperResult.rule;
369067
369379
  } else {
369068
369380
  responsibleRule ??= wrapperResult.rule;
369069
369381
  }
369070
369382
  aggregateDecision = "ask_user" /* ASK_USER */;
369383
+ } else if (wrapperResult.decision === "allow" /* ALLOW */ && wrapperResult.rule?.decision === "allow" /* ALLOW */ && Boolean(wrapperResult.rule?.argsPattern)) {
369384
+ lastAllowedSubRule = wrapperResult.rule;
369385
+ } else {
369386
+ allSubCommandsExplicitlyAllowed = false;
369071
369387
  }
369072
369388
  }
369073
369389
  if (!isAtomic) {
369390
+ evaluatedSubCommands++;
369074
369391
  const subResult = await this.check(
369075
369392
  { name: toolName, args: { command: subCmd, dir_path } },
369076
369393
  serverName,
@@ -369080,6 +369397,7 @@ var PolicyEngine = class {
369080
369397
  );
369081
369398
  if (subResult.decision === "deny" /* DENY */) return subResult;
369082
369399
  if (subResult.decision === "ask_user" /* ASK_USER */) {
369400
+ allSubCommandsExplicitlyAllowed = false;
369083
369401
  if (aggregateDecision === "allow" /* ALLOW */) {
369084
369402
  responsibleRule = subResult.rule;
369085
369403
  } else {
@@ -369092,13 +369410,31 @@ var PolicyEngine = class {
369092
369410
  subResult.rule?.allowRedirection ?? allowRedirection,
369093
369411
  subResult.rule
369094
369412
  )) {
369413
+ allSubCommandsExplicitlyAllowed = false;
369095
369414
  if (aggregateDecision === "allow" /* ALLOW */) {
369096
369415
  aggregateDecision = "ask_user" /* ASK_USER */;
369097
369416
  responsibleRule = void 0;
369098
369417
  }
369418
+ } else if (subResult.decision === "allow" /* ALLOW */ && subResult.rule?.decision === "allow" /* ALLOW */ && Boolean(subResult.rule?.argsPattern)) {
369419
+ lastAllowedSubRule = subResult.rule;
369420
+ } else {
369421
+ allSubCommandsExplicitlyAllowed = false;
369099
369422
  }
369423
+ } else if (stripped === subCmd) {
369424
+ allSubCommandsExplicitlyAllowed = false;
369100
369425
  }
369101
369426
  }
369427
+ const workspace = typeof this.sandboxManager.getWorkspace === "function" ? this.sandboxManager.getWorkspace() : process.cwd();
369428
+ const effectiveCwd = dir_path ? path83.resolve(workspace, dir_path) : workspace;
369429
+ const isOutsideWorkspace = effectiveCwd !== workspace && !isSubpath(workspace, effectiveCwd) && this.approvalMode !== "yolo" /* YOLO */;
369430
+ const parsedObjArgs = (0, import_shell_quote7.parse)(command);
369431
+ const parsedArgs = parsedObjArgs.map(extractStringFromParseEntry);
369432
+ const isUntrustedGit = containsGitCommand(parsedArgs) && !this.isTrustedFolder();
369433
+ const isDangerous = this.approvalMode !== "yolo" /* YOLO */ && this.sandboxManager.isDangerousCommand(parsedArgs, effectiveCwd);
369434
+ if (aggregateDecision === "ask_user" /* ASK_USER */ && !rule?.argsPattern && !hasTopLevelRedirectionDowngrade && !hasTopLevelHeuristicDowngrade && !isOutsideWorkspace && !isUntrustedGit && !isDangerous && evaluatedSubCommands > 0 && allSubCommandsExplicitlyAllowed) {
369435
+ aggregateDecision = "allow" /* ALLOW */;
369436
+ responsibleRule = lastAllowedSubRule;
369437
+ }
369102
369438
  return {
369103
369439
  decision: aggregateDecision,
369104
369440
  rule: aggregateDecision === ruleDecision ? rule : responsibleRule
@@ -369171,12 +369507,17 @@ var PolicyEngine = class {
369171
369507
  `[PolicyEngine.check] MATCHED rule: toolName=${rule.toolName}, decision=${rule.decision}, priority=${rule.priority}, argsPattern=${rule.argsPattern?.source || "none"}`
369172
369508
  );
369173
369509
  let ruleDecision = rule.decision;
369510
+ let hasTopLevelHeuristicDowngrade = false;
369174
369511
  if (!skipHeuristics && isShellCommand && command && !("commandPrefix" in rule) && !rule.argsPattern) {
369512
+ const prevDecision = ruleDecision;
369175
369513
  ruleDecision = await this.applyShellHeuristics(
369176
369514
  command,
369177
369515
  ruleDecision,
369178
369516
  shellDirPath
369179
369517
  );
369518
+ if (prevDecision === "allow" /* ALLOW */ && ruleDecision === "ask_user" /* ASK_USER */) {
369519
+ hasTopLevelHeuristicDowngrade = true;
369520
+ }
369180
369521
  }
369181
369522
  if (isShellCommand && toolName) {
369182
369523
  const shellResult = await this.checkShellCommand(
@@ -369188,7 +369529,8 @@ var PolicyEngine = class {
369188
369529
  rule.allowRedirection,
369189
369530
  rule,
369190
369531
  toolAnnotations,
369191
- subagent
369532
+ subagent,
369533
+ hasTopLevelHeuristicDowngrade
369192
369534
  );
369193
369535
  decision = shellResult.decision;
369194
369536
  matchedRule = shellResult.rule;
@@ -369212,12 +369554,17 @@ var PolicyEngine = class {
369212
369554
  );
369213
369555
  if (toolName && SHELL_TOOL_NAMES.includes(toolName)) {
369214
369556
  let heuristicDecision = this.defaultDecision;
369557
+ let hasTopLevelHeuristicDowngrade = false;
369215
369558
  if (!skipHeuristics && command) {
369559
+ const prevDecision = heuristicDecision;
369216
369560
  heuristicDecision = await this.applyShellHeuristics(
369217
369561
  command,
369218
369562
  heuristicDecision,
369219
369563
  shellDirPath
369220
369564
  );
369565
+ if (prevDecision === "allow" /* ALLOW */ && heuristicDecision === "ask_user" /* ASK_USER */) {
369566
+ hasTopLevelHeuristicDowngrade = true;
369567
+ }
369221
369568
  }
369222
369569
  const shellResult = await this.checkShellCommand(
369223
369570
  toolName,
@@ -369228,7 +369575,8 @@ var PolicyEngine = class {
369228
369575
  false,
369229
369576
  void 0,
369230
369577
  toolAnnotations,
369231
- subagent
369578
+ subagent,
369579
+ hasTopLevelHeuristicDowngrade
369232
369580
  );
369233
369581
  decision = shellResult.decision;
369234
369582
  matchedRule = shellResult.rule;
@@ -375428,7 +375776,7 @@ function areIssuersEqual(issuerA, issuerB) {
375428
375776
  }
375429
375777
  return normA === normB;
375430
375778
  }
375431
- function startCallbackServer(expectedState, port, expectedIssuer) {
375779
+ function startCallbackServer(expectedState, port, expectedIssuer, requireIssInResponse) {
375432
375780
  let portResolve;
375433
375781
  let portReject;
375434
375782
  const portPromise = new Promise((resolve29, reject) => {
@@ -375488,9 +375836,10 @@ function startCallbackServer(expectedState, port, expectedIssuer) {
375488
375836
  return;
375489
375837
  }
375490
375838
  if (expectedIssuer) {
375491
- if (!iss) {
375839
+ const requireIss = requireIssInResponse ?? true;
375840
+ if (requireIss && !iss) {
375492
375841
  debugLogger.error(
375493
- 'OAuth callback rejected: Missing required "iss" parameter when an expected issuer is configured. Possible IdP mix-up attack (RFC 9207).'
375842
+ 'OAuth callback rejected: Missing required "iss" parameter for the expected authorization server (RFC 9207).'
375494
375843
  );
375495
375844
  res.writeHead(400, { "Content-Type": "text/html" });
375496
375845
  res.end(`
@@ -375510,7 +375859,7 @@ function startCallbackServer(expectedState, port, expectedIssuer) {
375510
375859
  );
375511
375860
  return;
375512
375861
  }
375513
- if (!areIssuersEqual(iss, expectedIssuer)) {
375862
+ if (iss && !areIssuersEqual(iss, expectedIssuer)) {
375514
375863
  debugLogger.error(
375515
375864
  "OAuth callback rejected: Issuer mismatch between authorization response and expected authorization server. Possible IdP mix-up attack (RFC 9207)."
375516
375865
  );
@@ -375532,9 +375881,11 @@ function startCallbackServer(expectedState, port, expectedIssuer) {
375532
375881
  );
375533
375882
  return;
375534
375883
  }
375535
- debugLogger.debug(
375536
- "\u2713 OAuth callback issuer validated successfully."
375537
- );
375884
+ if (iss) {
375885
+ debugLogger.debug(
375886
+ "\u2713 OAuth callback issuer validated successfully."
375887
+ );
375888
+ }
375538
375889
  } else if (iss) {
375539
375890
  debugLogger.debug(
375540
375891
  'OAuth callback received "iss" parameter (no expected issuer was configured).'
@@ -375980,6 +376331,7 @@ var MCPOAuthProvider = class {
375980
376331
  issuer: discoveredConfig.issuer,
375981
376332
  tokenUrl: discoveredConfig.tokenUrl,
375982
376333
  scopes: config2.scopes || discoveredConfig.scopes || [],
376334
+ authorizationResponseIssParameterSupported: config2.authorizationResponseIssParameterSupported ?? discoveredConfig.authorizationResponseIssParameterSupported,
375983
376335
  // Preserve existing client credentials
375984
376336
  clientId: config2.clientId,
375985
376337
  clientSecret: config2.clientSecret
@@ -376005,6 +376357,7 @@ var MCPOAuthProvider = class {
376005
376357
  issuer: discoveredConfig.issuer,
376006
376358
  scopes: config2.scopes || discoveredConfig.scopes || [],
376007
376359
  registrationUrl: discoveredConfig.registrationUrl,
376360
+ authorizationResponseIssParameterSupported: config2.authorizationResponseIssParameterSupported ?? discoveredConfig.authorizationResponseIssParameterSupported,
376008
376361
  // Preserve existing client credentials
376009
376362
  clientId: config2.clientId,
376010
376363
  clientSecret: config2.clientSecret
@@ -376021,10 +376374,12 @@ var MCPOAuthProvider = class {
376021
376374
  debugLogger.debug(
376022
376375
  `Starting callback server for "${serverName}" (expected issuer: ${config2.issuer || "none"})...`
376023
376376
  );
376377
+ const requireIssInResponse = config2.authorizationResponseIssParameterSupported ?? Boolean(config2.issuer);
376024
376378
  const callbackServer = startCallbackServer(
376025
376379
  pkceParams.state,
376026
376380
  preferredPort,
376027
- config2.issuer
376381
+ config2.issuer,
376382
+ requireIssInResponse
376028
376383
  );
376029
376384
  const redirectPort = await callbackServer.port;
376030
376385
  debugLogger.debug(`Callback server listening on port ${redirectPort}`);
@@ -376980,7 +377335,8 @@ async function handleAutomaticOAuth(mcpServerName, mcpServerConfig, wwwAuthentic
376980
377335
  authorizationUrl: oauthConfig.authorizationUrl,
376981
377336
  issuer: oauthConfig.issuer,
376982
377337
  tokenUrl: oauthConfig.tokenUrl,
376983
- scopes: oauthConfig.scopes || []
377338
+ scopes: oauthConfig.scopes || [],
377339
+ authorizationResponseIssParameterSupported: mcpServerConfig.oauth?.authorizationResponseIssParameterSupported ?? oauthConfig.authorizationResponseIssParameterSupported
376984
377340
  };
376985
377341
  debugLogger.log(
376986
377342
  `Starting OAuth authentication for server '${mcpServerName}'...`
@@ -377714,7 +378070,8 @@ async function connectToMcpServer(clientVersion, mcpServerName, mcpServerConfig,
377714
378070
  authorizationUrl: oauthConfig.authorizationUrl,
377715
378071
  issuer: oauthConfig.issuer,
377716
378072
  tokenUrl: oauthConfig.tokenUrl,
377717
- scopes: oauthConfig.scopes || []
378073
+ scopes: oauthConfig.scopes || [],
378074
+ authorizationResponseIssParameterSupported: mcpServerConfig.oauth?.authorizationResponseIssParameterSupported ?? oauthConfig.authorizationResponseIssParameterSupported
377718
378075
  };
377719
378076
  const authServerUrl = mcpServerConfig.httpUrl || mcpServerConfig.url;
377720
378077
  debugLogger.log(
@@ -407214,6 +407571,8 @@ export {
407214
407571
  StreamEventType,
407215
407572
  SYNTHETIC_THOUGHT_SIGNATURE2 as SYNTHETIC_THOUGHT_SIGNATURE,
407216
407573
  INTERRUPTED_RESPONSE_PLACEHOLDER,
407574
+ INTERRUPTED_TOOL_RESPONSE_ERROR,
407575
+ CONTINUE_PROMPT_TEXT,
407217
407576
  isValidNonThoughtTextPart,
407218
407577
  THINKING_ONLY_NUDGE_MESSAGE,
407219
407578
  NO_RESPONSE_TEXT_NUDGE_MESSAGE,
@@ -407224,7 +407583,10 @@ export {
407224
407583
  GeminiChat,
407225
407584
  isSchemaDepthError,
407226
407585
  isInvalidArgumentError,
407586
+ buildInterruptedToolResponseParts,
407587
+ ensureTerminalUserTurn,
407227
407588
  stripToolCallIdPrefixes,
407589
+ coalesceConsecutiveContents,
407228
407590
  coalesceConsecutiveRoles,
407229
407591
  stripThoughts2 as stripThoughts,
407230
407592
  populateToolDisplay,