@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
@@ -283529,8 +283529,8 @@ function isValidToolName(name3, options = {}) {
283529
283529
  }
283530
283530
 
283531
283531
  // packages/core/dist/src/generated/git-commit.js
283532
- var GIT_COMMIT_INFO = "fb972b2f8";
283533
- var CLI_VERSION = "0.64.0-preview.0";
283532
+ var GIT_COMMIT_INFO = "90997f398";
283533
+ var CLI_VERSION = "0.64.0";
283534
283534
 
283535
283535
  // packages/core/dist/src/ide/detect-ide.js
283536
283536
  var IDE_DEFINITIONS = {
@@ -307310,7 +307310,7 @@ function getVersion() {
307310
307310
  }
307311
307311
  versionPromise = (async () => {
307312
307312
  const pkgJson = await getPackageJson(__dirname4);
307313
- return "0.64.0-preview.0";
307313
+ return "0.64.0";
307314
307314
  })();
307315
307315
  return versionPromise;
307316
307316
  }
@@ -309438,6 +309438,142 @@ var BENIGN_SUBCOMMAND_TOKENS = /* @__PURE__ */ new Set([
309438
309438
  "restart",
309439
309439
  "status"
309440
309440
  ]);
309441
+ var SAFE_SHORT_FLAGS = /* @__PURE__ */ new Set([
309442
+ "-a",
309443
+ "-A",
309444
+ "-b",
309445
+ "-B",
309446
+ "-C",
309447
+ "-d",
309448
+ "-D",
309449
+ "-E",
309450
+ "-F",
309451
+ "-g",
309452
+ "-G",
309453
+ "-h",
309454
+ "-H",
309455
+ "-i",
309456
+ "-I",
309457
+ "-l",
309458
+ "-L",
309459
+ "-m",
309460
+ "-n",
309461
+ "-N",
309462
+ "-p",
309463
+ "-P",
309464
+ "-q",
309465
+ "-r",
309466
+ "-R",
309467
+ "-s",
309468
+ "-S",
309469
+ "-t",
309470
+ "-T",
309471
+ "-u",
309472
+ "-U",
309473
+ "-v",
309474
+ "-V",
309475
+ "-w",
309476
+ "-W",
309477
+ "-x",
309478
+ "-z",
309479
+ "-1",
309480
+ // Common combined POSIX inspection flags (ls, grep, ps, git)
309481
+ "-la",
309482
+ "-al",
309483
+ "-ld",
309484
+ "-dl",
309485
+ "-lh",
309486
+ "-hl",
309487
+ "-lah",
309488
+ "-alh",
309489
+ "-lrt",
309490
+ "-ltr",
309491
+ "-rn",
309492
+ "-nr",
309493
+ "-ri",
309494
+ "-ir",
309495
+ "-rni",
309496
+ "-rin",
309497
+ "-ni",
309498
+ "-in",
309499
+ "-ef",
309500
+ "-aux"
309501
+ ]);
309502
+ var SAFE_LONG_FLAGS = /* @__PURE__ */ new Set([
309503
+ "--all",
309504
+ "--oneline",
309505
+ "--stat",
309506
+ "--numstat",
309507
+ "--shortstat",
309508
+ "--name-only",
309509
+ "--name-status",
309510
+ "--graph",
309511
+ "--decorate",
309512
+ "--no-pager",
309513
+ "--color",
309514
+ "--no-color",
309515
+ "--grep",
309516
+ "--author",
309517
+ "--since",
309518
+ "--until",
309519
+ "--before",
309520
+ "--after",
309521
+ "--pretty",
309522
+ "--format",
309523
+ "--abbrev-commit",
309524
+ "--max-count",
309525
+ "--cached",
309526
+ "--staged",
309527
+ "--short",
309528
+ "--branch",
309529
+ "--porcelain",
309530
+ "--help",
309531
+ "--version",
309532
+ "--quiet",
309533
+ "--verbose",
309534
+ "--dry-run",
309535
+ "--ignore-case",
309536
+ "--line-number",
309537
+ "--recursive",
309538
+ "--extended-regexp",
309539
+ "--fixed-strings",
309540
+ "--files-with-matches",
309541
+ "--count",
309542
+ "--context",
309543
+ "--before-context",
309544
+ "--after-context"
309545
+ ]);
309546
+ var HIGH_RISK_VALUE_FLAGS = /* @__PURE__ */ new Set([
309547
+ "-c",
309548
+ "-e",
309549
+ "--eval",
309550
+ "--exec",
309551
+ "-exec",
309552
+ "-execdir",
309553
+ "--run_under",
309554
+ "--test_strategy",
309555
+ "--test_arg",
309556
+ "--test_env",
309557
+ "--action_env",
309558
+ "--jvmopt",
309559
+ "--host_jvm_args",
309560
+ "--wrapper_script_flag",
309561
+ "--upload-pack",
309562
+ "--receive-pack",
309563
+ "--require",
309564
+ "--import",
309565
+ "--loader"
309566
+ ]);
309567
+ var FILE_MODE_STRING_REGEX = /^[dcbpsl-][rwxstST-]{9}[+@.]?$/i;
309568
+ var BARE_DASHES_REGEX = /^--+$/;
309569
+ var BARE_SHELL_VARIABLE_REGEX = /^\$(?:[a-zA-Z_][a-zA-Z0-9_]*|\{[a-zA-Z_][a-zA-Z0-9_]*\})$/;
309570
+ function isHighRiskPatternToken(token2) {
309571
+ 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);
309572
+ }
309573
+ function hasUntrustedSubstring(untrustedContext, value) {
309574
+ const normalized2 = value.toLowerCase().replace(/\\/g, "/");
309575
+ return untrustedContext.untrustedTexts.some((text) => text.toLowerCase().includes(normalized2));
309576
+ }
309441
309577
  function extractUntrustedContext(history) {
309442
309578
  const untrustedTexts = [];
309443
309579
  const untrustedTokens = /* @__PURE__ */ new Set();
@@ -309468,35 +309604,19 @@ function extractUntrustedContext(history) {
309468
309604
  if (untrustedContent) {
309469
309605
  const normalizedContent = untrustedContent.replace(/\\/g, "/");
309470
309606
  untrustedTexts.push(normalizedContent);
309471
- const tokens = normalizedContent.split(/[\s,`"';()|&[\]{}<>]+/).map((t2) => t2.trim()).filter((t2) => t2.length > 1).filter((t2) => !BENIGN_SUBCOMMAND_TOKENS.has(t2.toLowerCase()));
309607
+ const contentWithoutShellMetadata = normalizedContent.replace(/^\s*Output:\s*(\(empty\))?\s*/i, "").replace(/\n(?:Exit Code|Signal|Background PIDs|Process Group PGID):.*$/gim, "");
309608
+ 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()));
309472
309609
  for (const token2 of tokens) {
309473
- const lowerToken = token2.toLowerCase();
309474
- untrustedTokens.add(lowerToken);
309475
- if (lowerToken.startsWith("--")) {
309476
- untrustedTokens.add(lowerToken.substring(2));
309477
- } else if (lowerToken.startsWith("-")) {
309478
- untrustedTokens.add(lowerToken.substring(1));
309479
- }
309480
- if (lowerToken.includes("=")) {
309481
- const eqParts = lowerToken.split("=");
309610
+ const isShortFlag = token2.startsWith("-") && !token2.startsWith("--");
309611
+ const tokenToStore = isShortFlag ? token2 : token2.toLowerCase();
309612
+ untrustedTokens.add(tokenToStore);
309613
+ if (token2.includes("=")) {
309614
+ const eqParts = token2.split("=");
309482
309615
  for (const eqPart of eqParts) {
309483
309616
  const trimmedPart = eqPart.trim();
309484
- if (trimmedPart.length > 1) {
309485
- untrustedTokens.add(trimmedPart);
309486
- if (trimmedPart.startsWith("--")) {
309487
- untrustedTokens.add(trimmedPart.substring(2));
309488
- } else if (trimmedPart.startsWith("-")) {
309489
- untrustedTokens.add(trimmedPart.substring(1));
309490
- }
309491
- }
309492
- }
309493
- }
309494
- if (lowerToken.includes("/")) {
309495
- const pathParts = lowerToken.split("/");
309496
- for (const part2 of pathParts) {
309497
- const trimmedPart = part2.trim();
309498
- if (trimmedPart.length > 1) {
309499
- untrustedTokens.add(trimmedPart);
309617
+ if (trimmedPart.length > 1 && !BARE_DASHES_REGEX.test(trimmedPart) && !SAFE_SHORT_FLAGS.has(trimmedPart) && !SAFE_LONG_FLAGS.has(trimmedPart.toLowerCase())) {
309618
+ const isPartShortFlag = trimmedPart.startsWith("-") && !trimmedPart.startsWith("--");
309619
+ untrustedTokens.add(isPartShortFlag ? trimmedPart : trimmedPart.toLowerCase());
309500
309620
  }
309501
309621
  }
309502
309622
  }
@@ -309515,7 +309635,7 @@ function findUntrustedFlags(command, untrustedContext) {
309515
309635
  let parsed;
309516
309636
  try {
309517
309637
  const normalizedCommand = process.platform === "win32" ? command.replace(/\\(?!")/g, "/") : command;
309518
- parsed = (0, import_shell_quote5.parse)(normalizedCommand);
309638
+ parsed = (0, import_shell_quote5.parse)(normalizedCommand, (key) => `$${key}`);
309519
309639
  } catch {
309520
309640
  parsed = command.trim().split(/\s+/);
309521
309641
  }
@@ -309538,45 +309658,57 @@ function findUntrustedFlags(command, untrustedContext) {
309538
309658
  const token2 = rawTokens[i3];
309539
309659
  const lowerToken = token2.toLowerCase();
309540
309660
  if (token2.startsWith("-")) {
309661
+ if (/^--?$/.test(token2)) {
309662
+ continue;
309663
+ }
309664
+ let rawFlagName = token2;
309541
309665
  let flagToCheck = lowerToken;
309666
+ let rawValToCheck;
309542
309667
  let valToCheck;
309543
309668
  if (token2.includes("=")) {
309544
- const eqIdx = lowerToken.indexOf("=");
309669
+ const eqIdx = token2.indexOf("=");
309670
+ rawFlagName = token2.substring(0, eqIdx);
309545
309671
  flagToCheck = lowerToken.substring(0, eqIdx);
309672
+ rawValToCheck = token2.substring(eqIdx + 1);
309546
309673
  valToCheck = lowerToken.substring(eqIdx + 1);
309547
309674
  }
309548
- if (untrustedContext.untrustedTokens.has(lowerToken) || untrustedContext.untrustedTokens.has(flagToCheck)) {
309675
+ const isSafeFlag = SAFE_SHORT_FLAGS.has(rawFlagName) || SAFE_LONG_FLAGS.has(flagToCheck);
309676
+ const isHighRiskFlag = HIGH_RISK_VALUE_FLAGS.has(rawFlagName) || rawFlagName.startsWith("--") && HIGH_RISK_VALUE_FLAGS.has(flagToCheck);
309677
+ const isShortFlag = rawFlagName.startsWith("-") && !rawFlagName.startsWith("--");
309678
+ const tokenToLookup = isShortFlag ? rawFlagName : lowerToken;
309679
+ const flagToLookup = isShortFlag ? rawFlagName : flagToCheck;
309680
+ if (!isSafeFlag && (untrustedContext.untrustedTokens.has(tokenToLookup) || untrustedContext.untrustedTokens.has(flagToLookup))) {
309549
309681
  detected.add(token2);
309550
- continue;
309682
+ if (token2.includes("=") || !isHighRiskFlag) {
309683
+ continue;
309684
+ }
309551
309685
  }
309552
- if (valToCheck && untrustedContext.untrustedTokens.has(valToCheck)) {
309553
- detected.add(token2);
309554
- continue;
309686
+ if (valToCheck && rawValToCheck) {
309687
+ const isValUntrusted = untrustedContext.untrustedTokens.has(valToCheck) && (isHighRiskFlag || !isSafeFlag && isHighRiskPatternToken(rawValToCheck));
309688
+ const isHighRiskValSubstring = (!isSafeFlag || isHighRiskFlag) && isHighRiskPatternToken(rawValToCheck) && hasUntrustedSubstring(untrustedContext, valToCheck);
309689
+ if (isValUntrusted || isHighRiskValSubstring) {
309690
+ detected.add(token2);
309691
+ continue;
309692
+ }
309555
309693
  }
309556
- const nextToken = rawTokens[i3 + 1];
309557
- if (nextToken && !nextToken.startsWith("-") && untrustedContext.untrustedTokens.has(nextToken.toLowerCase())) {
309558
- detected.add(token2);
309559
- detected.add(nextToken);
309694
+ if (!token2.includes("=") && isHighRiskFlag) {
309695
+ const nextToken = rawTokens[i3 + 1];
309696
+ 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))) {
309697
+ detected.add(token2);
309698
+ detected.add(nextToken);
309699
+ }
309560
309700
  }
309561
309701
  } else {
309562
- if (token2.length <= 1) {
309702
+ if (token2.length <= 1 || BARE_SHELL_VARIABLE_REGEX.test(token2)) {
309563
309703
  continue;
309564
309704
  }
309565
- const isSensitiveWord = token2.includes("/") || token2.includes(".") || token2.includes(":");
309566
- const isHighRiskPattern = token2.startsWith("http://") || token2.startsWith("https://") || token2.startsWith("/") || token2.startsWith("\\") || /^[a-zA-Z]:[\\/]/.test(token2);
309567
- if (isSensitiveWord) {
309705
+ if (isHighRiskPatternToken(token2)) {
309568
309706
  if (untrustedContext.untrustedTokens.has(lowerToken)) {
309569
309707
  detected.add(token2);
309570
309708
  continue;
309571
309709
  }
309572
- if (isHighRiskPattern) {
309573
- const normalizedLowerToken = lowerToken.replace(/\\/g, "/");
309574
- for (const text of untrustedContext.untrustedTexts) {
309575
- if (text.toLowerCase().includes(normalizedLowerToken)) {
309576
- detected.add(token2);
309577
- break;
309578
- }
309579
- }
309710
+ if (hasUntrustedSubstring(untrustedContext, lowerToken)) {
309711
+ detected.add(token2);
309580
309712
  }
309581
309713
  }
309582
309714
  }
@@ -332836,6 +332968,9 @@ function scrubPart(part) {
332836
332968
  if (part.functionResponse.id) {
332837
332969
  scrubbedResp["id"] = part.functionResponse.id;
332838
332970
  }
332971
+ if ("parts" in part.functionResponse && Array.isArray(part.functionResponse.parts)) {
332972
+ scrubbedResp["parts"] = part.functionResponse.parts.map(scrubPart);
332973
+ }
332839
332974
  scrubbed["functionResponse"] = scrubbedResp;
332840
332975
  }
332841
332976
  if ("fileData" in part) {
@@ -332866,6 +333001,8 @@ var MID_STREAM_RETRY_OPTIONS = {
332866
333001
  };
332867
333002
  var SYNTHETIC_THOUGHT_SIGNATURE2 = "skip_thought_signature_validator";
332868
333003
  var INTERRUPTED_RESPONSE_PLACEHOLDER = "[The previous response was interrupted before it completed.]";
333004
+ var INTERRUPTED_TOOL_RESPONSE_ERROR = "Response was lost or interrupted.";
333005
+ var CONTINUE_PROMPT_TEXT = "Please continue.";
332869
333006
  function isIndexedPart(part) {
332870
333007
  return "callIndex" in part;
332871
333008
  }
@@ -333107,6 +333244,7 @@ var GeminiChat = class {
333107
333244
  let userContent = createUserContent(message);
333108
333245
  const isOriginalFunctionResponse = isFunctionResponse(userContent);
333109
333246
  if (!isOriginalFunctionResponse) {
333247
+ this.closeUnansweredToolCallsTurn();
333110
333248
  this.closeUnansweredToolResponseTurn();
333111
333249
  }
333112
333250
  const { model } = this.context.config.modelConfigService.getResolvedConfig(modelConfigKey);
@@ -333133,7 +333271,7 @@ var GeminiChat = class {
333133
333271
  } else {
333134
333272
  const history = this.agentHistory.get();
333135
333273
  const lastTurn = history[history.length - 1];
333136
- if (!lastTurn || partListUnionToString(lastTurn.content.parts || []) !== userMessageContent) {
333274
+ if (!lastTurn || lastTurn.content.role !== "user" || partListUnionToString(lastTurn.content.parts || []) !== userMessageContent) {
333137
333275
  const id = this.chatRecordingService.recordMessage({
333138
333276
  model,
333139
333277
  type: "user",
@@ -333181,11 +333319,12 @@ var GeminiChat = class {
333181
333319
  } else {
333182
333320
  const history = this.agentHistory.get();
333183
333321
  const lastTurn = history[history.length - 1];
333184
- if (!lastTurn || partListUnionToString(lastTurn.content.parts || []) !== partListUnionToString(userContent.parts || [])) {
333322
+ if (!lastTurn || lastTurn.content.role !== "user" || partListUnionToString(lastTurn.content.parts || []) !== partListUnionToString(userContent.parts || [])) {
333185
333323
  this.agentHistory.push({ id, content: userContent });
333186
333324
  }
333187
333325
  }
333188
333326
  }
333327
+ this.replaceEmptyTrailingUserTurn();
333189
333328
  const requestHistory = this.getHistoryTurns(true);
333190
333329
  const streamWithRetries = async function* () {
333191
333330
  let isSuccess = false;
@@ -333297,23 +333436,77 @@ var GeminiChat = class {
333297
333436
  return streamWithRetries.call(this);
333298
333437
  }
333299
333438
  /**
333300
- * Appends a closing model turn when history ends with an unanswered tool
333301
- * response, so the next user message stays a turn of its own.
333439
+ * Appends a dedicated synthetic user turn with matching functionResponse
333440
+ * parts when history ends with an unclosed model functionCall. The
333441
+ * subsequent user prompt is then recorded as a clean turn of its own (after
333442
+ * `closeUnansweredToolResponseTurn` adds the closing model turn).
333302
333443
  */
333303
- closeUnansweredToolResponseTurn() {
333444
+ closeUnansweredToolCallsTurn() {
333304
333445
  const turns = this.agentHistory.get();
333305
333446
  const last2 = turns[turns.length - 1];
333306
- if (last2?.content.role !== "user" || !last2.content.parts?.some((part) => !!part.functionResponse)) {
333447
+ if (last2?.content.role !== "model") {
333307
333448
  return;
333308
333449
  }
333450
+ const missingResponses = buildInterruptedToolResponseParts(last2.content.parts ?? []);
333451
+ if (missingResponses.length === 0) {
333452
+ return;
333453
+ }
333454
+ const id = this.chatRecordingService.recordSyntheticMessage("user", missingResponses);
333309
333455
  this.agentHistory.push({
333310
- id: randomUUID6(),
333311
- content: {
333312
- role: "model",
333313
- parts: [{ text: INTERRUPTED_RESPONSE_PLACEHOLDER }]
333314
- }
333456
+ id,
333457
+ content: { role: "user", parts: missingResponses }
333315
333458
  });
333316
333459
  }
333460
+ /**
333461
+ * Replaces the parts of a trailing user turn that carries no content (e.g.
333462
+ * only empty text parts) with a continuation prompt, keeping the in-memory
333463
+ * history and the session log in sync. Runs once per send so that the
333464
+ * persisted history never keeps an empty user turn between model turns.
333465
+ */
333466
+ replaceEmptyTrailingUserTurn() {
333467
+ const turns = this.agentHistory.get();
333468
+ const last2 = turns[turns.length - 1];
333469
+ if (last2?.content.role !== "user" || hasNonEmptyParts(last2.content)) {
333470
+ return;
333471
+ }
333472
+ this.agentHistory.set([
333473
+ ...turns.slice(0, -1),
333474
+ {
333475
+ id: last2.id,
333476
+ content: { ...last2.content, parts: [{ text: CONTINUE_PROMPT_TEXT }] }
333477
+ }
333478
+ ]);
333479
+ this.chatRecordingService.updateMessagesFromHistory(this.agentHistory.get());
333480
+ }
333481
+ /**
333482
+ * Appends a closing model turn when history ends with an unanswered tool
333483
+ * response, so the next user message stays a turn of its own.
333484
+ */
333485
+ closeUnansweredToolResponseTurn() {
333486
+ const turns = this.agentHistory.get();
333487
+ let targetTurn = turns[turns.length - 1];
333488
+ let hadEmptyTrailingModelTurn = false;
333489
+ if (targetTurn?.content.role === "model" && (!targetTurn.content.parts || targetTurn.content.parts.length === 0)) {
333490
+ hadEmptyTrailingModelTurn = true;
333491
+ targetTurn = turns[turns.length - 2];
333492
+ }
333493
+ if (targetTurn?.content.role !== "user" || !targetTurn.content.parts?.some((part) => !!part.functionResponse)) {
333494
+ return;
333495
+ }
333496
+ if (hadEmptyTrailingModelTurn) {
333497
+ const lastTurn = turns[turns.length - 1];
333498
+ lastTurn.content.parts = [{ text: INTERRUPTED_RESPONSE_PLACEHOLDER }];
333499
+ this.chatRecordingService.updateMessagesFromHistory(turns);
333500
+ } else {
333501
+ this.agentHistory.push({
333502
+ id: randomUUID6(),
333503
+ content: {
333504
+ role: "model",
333505
+ parts: [{ text: INTERRUPTED_RESPONSE_PLACEHOLDER }]
333506
+ }
333507
+ });
333508
+ }
333509
+ }
333317
333510
  extractBinaryInjections(parts2) {
333318
333511
  const binaryParts = [];
333319
333512
  if (parts2) {
@@ -333430,10 +333623,14 @@ var GeminiChat = class {
333430
333623
  if (this.onModelChanged) {
333431
333624
  this.tools = await this.onModelChanged(modelToUse);
333432
333625
  }
333626
+ const normalizedContents = ensureTerminalUserTurn(contentsToUse);
333627
+ if (normalizedContents !== contentsToUse) {
333628
+ debugLogger.warn("Final contents do not end with a valid user turn. Normalizing contents to satisfy Gemini API invariant.");
333629
+ }
333433
333630
  lastModelToUse = modelToUse;
333434
333631
  lastConfig = config2;
333435
- lastContentsToUse = contentsToUse;
333436
- const finalContents = stripToolCallIdPrefixes(contentsToUse);
333632
+ lastContentsToUse = normalizedContents;
333633
+ const finalContents = stripToolCallIdPrefixes(normalizedContents);
333437
333634
  return this.context.config.getContentGenerator().generateContentStream({
333438
333635
  model: modelToUse,
333439
333636
  contents: finalContents,
@@ -333531,6 +333728,25 @@ var GeminiChat = class {
333531
333728
  }
333532
333729
  ensureStableToolIds(this.agentHistory.get());
333533
333730
  }
333731
+ /**
333732
+ * Removes the trailing model turn when it holds function calls that were
333733
+ * never answered (e.g. every call in the batch was declined). Earlier
333734
+ * completed tool rounds and the originating user prompt are kept, and the
333735
+ * durable IDs of the remaining turns are preserved.
333736
+ *
333737
+ * @returns true when a turn was removed.
333738
+ */
333739
+ discardTrailingUnansweredToolCallTurn() {
333740
+ const turns = this.agentHistory.get();
333741
+ const last2 = turns[turns.length - 1];
333742
+ if (last2?.content.role !== "model" || !last2.content.parts?.some((part) => !!part.functionCall)) {
333743
+ return false;
333744
+ }
333745
+ this.agentHistory.rollback(turns.length - 1);
333746
+ this.lastPromptTokenCount = estimateTokenCountSync(this.agentHistory.flatMap((c4) => c4.content.parts || []));
333747
+ this.chatRecordingService.updateMessagesFromHistory(this.agentHistory.get());
333748
+ return true;
333749
+ }
333534
333750
  setHistory(history) {
333535
333751
  const wrappedHistory = history.map((item) => {
333536
333752
  if ("id" in item && "content" in item) {
@@ -333884,8 +334100,41 @@ function isSchemaDepthError(errorMessage) {
333884
334100
  function isInvalidArgumentError(errorMessage) {
333885
334101
  return errorMessage.includes("Request contains an invalid argument");
333886
334102
  }
334103
+ function isEmptyTextPart(part) {
334104
+ if (part.text !== "") {
334105
+ return false;
334106
+ }
334107
+ return !Object.keys(part).some((key) => key !== "text" && key !== "thought" && key !== "callIndex");
334108
+ }
334109
+ function hasNonEmptyParts(content) {
334110
+ return (content.parts ?? []).some((part) => !isEmptyTextPart(part));
334111
+ }
334112
+ function buildInterruptedToolResponseParts(parts2) {
334113
+ return parts2.filter((part) => !!part?.functionCall).map((part) => ({
334114
+ functionResponse: {
334115
+ name: part.functionCall.name?.trim() || "generic_tool",
334116
+ id: part.functionCall.id,
334117
+ response: { error: INTERRUPTED_TOOL_RESPONSE_ERROR }
334118
+ }
334119
+ }));
334120
+ }
334121
+ function ensureTerminalUserTurn(contents) {
334122
+ const last2 = contents.at(-1);
334123
+ if (last2?.role === "user" && hasNonEmptyParts(last2)) {
334124
+ return contents;
334125
+ }
334126
+ if (last2?.role === "user") {
334127
+ return [
334128
+ ...contents.slice(0, -1),
334129
+ { ...last2, parts: [{ text: CONTINUE_PROMPT_TEXT }] }
334130
+ ];
334131
+ }
334132
+ const missingResponses = buildInterruptedToolResponseParts(last2?.parts ?? []);
334133
+ const parts2 = missingResponses.length > 0 ? missingResponses : [{ text: CONTINUE_PROMPT_TEXT }];
334134
+ return [...contents, { role: "user", parts: parts2 }];
334135
+ }
333887
334136
  function stripToolCallIdPrefixes(contents) {
333888
- return contents.map((content) => {
334137
+ const stripped = contents.map((content) => {
333889
334138
  const parts2 = (content.parts || []).map((part) => {
333890
334139
  const newPart = { ...part };
333891
334140
  if (newPart.functionCall) {
@@ -333893,8 +334142,7 @@ function stripToolCallIdPrefixes(contents) {
333893
334142
  const name3 = fc.name?.trim() || "generic_tool";
333894
334143
  if (fc.id && fc.id.startsWith(`${name3}__`)) {
333895
334144
  newPart.functionCall = {
333896
- name: fc.name,
333897
- args: fc.args,
334145
+ ...fc,
333898
334146
  id: fc.id.substring(name3.length + 2)
333899
334147
  };
333900
334148
  }
@@ -333904,8 +334152,9 @@ function stripToolCallIdPrefixes(contents) {
333904
334152
  const name3 = fr.name?.trim() || "generic_tool";
333905
334153
  if (fr.id && fr.id.startsWith(`${name3}__`)) {
333906
334154
  newPart.functionResponse = {
333907
- name: fr.name,
333908
- response: fr.response,
334155
+ // History parts are plain JSON objects, not class instances.
334156
+ // eslint-disable-next-line @typescript-eslint/no-misused-spread
334157
+ ...fr,
333909
334158
  id: fr.id.substring(name3.length + 2)
333910
334159
  };
333911
334160
  }
@@ -333915,18 +334164,30 @@ function stripToolCallIdPrefixes(contents) {
333915
334164
  delete newPart.text;
333916
334165
  }
333917
334166
  return newPart;
333918
- }).filter((part) => {
333919
- const hasOtherKeys = Object.keys(part).some((key) => key !== "text" && key !== "thought" && key !== "callIndex");
333920
- if (part.text !== void 0 && part.text === "" && !hasOtherKeys) {
333921
- return false;
333922
- }
333923
- return true;
333924
- });
334167
+ }).filter((part) => !isEmptyTextPart(part));
333925
334168
  return {
333926
334169
  ...content,
333927
334170
  parts: parts2
333928
334171
  };
333929
- });
334172
+ }).filter((content) => content.parts.length > 0);
334173
+ return coalesceConsecutiveContents(stripped);
334174
+ }
334175
+ function coalesceConsecutiveContents(contents) {
334176
+ const result2 = [];
334177
+ for (const item of contents) {
334178
+ const lastIdx = result2.length - 1;
334179
+ const last2 = result2[lastIdx];
334180
+ if (last2 && last2.role && last2.role === item.role) {
334181
+ const hasParts = last2.parts || item.parts;
334182
+ result2[lastIdx] = {
334183
+ ...last2,
334184
+ parts: hasParts ? [...last2.parts || [], ...item.parts || []] : void 0
334185
+ };
334186
+ } else {
334187
+ result2.push({ ...item });
334188
+ }
334189
+ }
334190
+ return result2;
333930
334191
  }
333931
334192
  function coalesceConsecutiveRoles(history) {
333932
334193
  const result2 = [];
@@ -341235,7 +341496,13 @@ var OAuthUtils = class {
341235
341496
  issuer: metadata2.issuer,
341236
341497
  tokenUrl: metadata2.token_endpoint,
341237
341498
  scopes: metadata2.scopes_supported || [],
341238
- registrationUrl: metadata2.registration_endpoint
341499
+ registrationUrl: metadata2.registration_endpoint,
341500
+ // RFC 9207 Section 3: the flag defaults to false when omitted. Discovered
341501
+ // configurations therefore always carry an explicit boolean, which lets
341502
+ // the callback policy distinguish them from an explicitly configured issuer.
341503
+ // Only a literal boolean `true` enables the flag, so loosely typed values
341504
+ // returned by some servers (e.g. "false" or "0") are not coerced to true.
341505
+ authorizationResponseIssParameterSupported: metadata2.authorization_response_iss_parameter_supported === true
341239
341506
  };
341240
341507
  }
341241
341508
  /**
@@ -341559,7 +341826,7 @@ var A2AAuthProviderFactory = class _A2AAuthProviderFactory {
341559
341826
  return provider;
341560
341827
  }
341561
341828
  case "oauth2": {
341562
- const { OAuth2AuthProvider } = await import("./oauth2-provider-FVTRRJZR.js");
341829
+ const { OAuth2AuthProvider } = await import("./oauth2-provider-CHSEPLKC.js");
341563
341830
  const provider = new OAuth2AuthProvider(authConfig, options.agentName ?? "unknown", agentCard, options.agentCardUrl);
341564
341831
  await provider.initialize();
341565
341832
  return provider;
@@ -347707,6 +347974,21 @@ var GeminiClient = class {
347707
347974
  this.updateTelemetryTokenCount();
347708
347975
  this.forceFullIdeContext = true;
347709
347976
  }
347977
+ /**
347978
+ * Removes the trailing model turn when it only holds unanswered function
347979
+ * calls. See {@link GeminiChat.discardTrailingUnansweredToolCallTurn}.
347980
+ * Returns false without side effects when the chat is not initialized.
347981
+ */
347982
+ discardTrailingUnansweredToolCallTurn() {
347983
+ if (!this.isInitialized()) {
347984
+ return false;
347985
+ }
347986
+ const removed = this.getChat().discardTrailingUnansweredToolCallTurn();
347987
+ if (removed) {
347988
+ this.updateTelemetryTokenCount();
347989
+ }
347990
+ return removed;
347991
+ }
347710
347992
  lastUsedModelId;
347711
347993
  async setTools(modelId) {
347712
347994
  if (!this.chat) {
@@ -363984,7 +364266,7 @@ var PolicyEngine = class {
363984
364266
  }
363985
364267
  return decision;
363986
364268
  }
363987
- async checkShellCommand(toolName, command, ruleDecision, serverName, dir_path, allowRedirection, rule, toolAnnotations, subagent) {
364269
+ async checkShellCommand(toolName, command, ruleDecision, serverName, dir_path, allowRedirection, rule, toolAnnotations, subagent, hasTopLevelHeuristicDowngrade = false) {
363988
364270
  if (!command) {
363989
364271
  return {
363990
364272
  decision: ruleDecision,
@@ -364020,13 +364302,17 @@ var PolicyEngine = class {
364020
364302
  }
364021
364303
  let aggregateDecision = ruleDecision;
364022
364304
  let responsibleRule = rule && ruleDecision === rule.decision ? rule : void 0;
364023
- if (this.shouldDowngradeForRedirection(command, allowRedirection, rule)) {
364305
+ const hasTopLevelRedirectionDowngrade = this.shouldDowngradeForRedirection(command, allowRedirection, rule);
364306
+ if (hasTopLevelRedirectionDowngrade) {
364024
364307
  if (aggregateDecision === PolicyDecision.ALLOW) {
364025
364308
  debugLogger.debug(`[PolicyEngine.check] Downgrading ALLOW to ASK_USER for redirected command: ${command}`);
364026
364309
  aggregateDecision = PolicyDecision.ASK_USER;
364027
364310
  responsibleRule = void 0;
364028
364311
  }
364029
364312
  }
364313
+ let evaluatedSubCommands = 0;
364314
+ let allSubCommandsExplicitlyAllowed = true;
364315
+ let lastAllowedSubRule;
364030
364316
  for (const detail of subCommands) {
364031
364317
  if (REDIRECTION_NAMES.has(detail.name)) {
364032
364318
  continue;
@@ -364035,23 +364321,31 @@ var PolicyEngine = class {
364035
364321
  const isAtomic = subCmd === command || detail.startIndex === 0 && detail.text.length === command.length;
364036
364322
  const stripped = stripShellWrapper(subCmd);
364037
364323
  if (stripped !== subCmd) {
364324
+ evaluatedSubCommands++;
364038
364325
  const wrapperResult = await this.check({ name: toolName, args: { command: stripped, dir_path } }, serverName, toolAnnotations, subagent, true);
364039
364326
  if (wrapperResult.decision === PolicyDecision.DENY)
364040
364327
  return wrapperResult;
364041
364328
  if (wrapperResult.decision === PolicyDecision.ASK_USER) {
364329
+ allSubCommandsExplicitlyAllowed = false;
364042
364330
  if (aggregateDecision === PolicyDecision.ALLOW) {
364043
364331
  responsibleRule = wrapperResult.rule;
364044
364332
  } else {
364045
364333
  responsibleRule ??= wrapperResult.rule;
364046
364334
  }
364047
364335
  aggregateDecision = PolicyDecision.ASK_USER;
364336
+ } else if (wrapperResult.decision === PolicyDecision.ALLOW && wrapperResult.rule?.decision === PolicyDecision.ALLOW && Boolean(wrapperResult.rule?.argsPattern)) {
364337
+ lastAllowedSubRule = wrapperResult.rule;
364338
+ } else {
364339
+ allSubCommandsExplicitlyAllowed = false;
364048
364340
  }
364049
364341
  }
364050
364342
  if (!isAtomic) {
364343
+ evaluatedSubCommands++;
364051
364344
  const subResult = await this.check({ name: toolName, args: { command: subCmd, dir_path } }, serverName, toolAnnotations, subagent, true);
364052
364345
  if (subResult.decision === PolicyDecision.DENY)
364053
364346
  return subResult;
364054
364347
  if (subResult.decision === PolicyDecision.ASK_USER) {
364348
+ allSubCommandsExplicitlyAllowed = false;
364055
364349
  if (aggregateDecision === PolicyDecision.ALLOW) {
364056
364350
  responsibleRule = subResult.rule;
364057
364351
  } else {
@@ -364060,13 +364354,31 @@ var PolicyEngine = class {
364060
364354
  aggregateDecision = PolicyDecision.ASK_USER;
364061
364355
  }
364062
364356
  if (subResult.decision === PolicyDecision.ALLOW && this.shouldDowngradeForRedirection(subCmd, subResult.rule?.allowRedirection ?? allowRedirection, subResult.rule)) {
364357
+ allSubCommandsExplicitlyAllowed = false;
364063
364358
  if (aggregateDecision === PolicyDecision.ALLOW) {
364064
364359
  aggregateDecision = PolicyDecision.ASK_USER;
364065
364360
  responsibleRule = void 0;
364066
364361
  }
364362
+ } else if (subResult.decision === PolicyDecision.ALLOW && subResult.rule?.decision === PolicyDecision.ALLOW && Boolean(subResult.rule?.argsPattern)) {
364363
+ lastAllowedSubRule = subResult.rule;
364364
+ } else {
364365
+ allSubCommandsExplicitlyAllowed = false;
364067
364366
  }
364367
+ } else if (stripped === subCmd) {
364368
+ allSubCommandsExplicitlyAllowed = false;
364068
364369
  }
364069
364370
  }
364371
+ const workspace = typeof this.sandboxManager.getWorkspace === "function" ? this.sandboxManager.getWorkspace() : process.cwd();
364372
+ const effectiveCwd = dir_path ? path83.resolve(workspace, dir_path) : workspace;
364373
+ const isOutsideWorkspace = effectiveCwd !== workspace && !isSubpath(workspace, effectiveCwd) && this.approvalMode !== ApprovalMode.YOLO;
364374
+ const parsedObjArgs = (0, import_shell_quote7.parse)(command);
364375
+ const parsedArgs = parsedObjArgs.map(extractStringFromParseEntry);
364376
+ const isUntrustedGit = containsGitCommand(parsedArgs) && !this.isTrustedFolder();
364377
+ const isDangerous = this.approvalMode !== ApprovalMode.YOLO && this.sandboxManager.isDangerousCommand(parsedArgs, effectiveCwd);
364378
+ if (aggregateDecision === PolicyDecision.ASK_USER && !rule?.argsPattern && !hasTopLevelRedirectionDowngrade && !hasTopLevelHeuristicDowngrade && !isOutsideWorkspace && !isUntrustedGit && !isDangerous && evaluatedSubCommands > 0 && allSubCommandsExplicitlyAllowed) {
364379
+ aggregateDecision = PolicyDecision.ALLOW;
364380
+ responsibleRule = lastAllowedSubRule;
364381
+ }
364070
364382
  return {
364071
364383
  decision: aggregateDecision,
364072
364384
  rule: aggregateDecision === ruleDecision ? rule : responsibleRule
@@ -364124,11 +364436,16 @@ var PolicyEngine = class {
364124
364436
  if (match2) {
364125
364437
  debugLogger.debug(`[PolicyEngine.check] MATCHED rule: toolName=${rule.toolName}, decision=${rule.decision}, priority=${rule.priority}, argsPattern=${rule.argsPattern?.source || "none"}`);
364126
364438
  let ruleDecision = rule.decision;
364439
+ let hasTopLevelHeuristicDowngrade = false;
364127
364440
  if (!skipHeuristics && isShellCommand && command && !("commandPrefix" in rule) && !rule.argsPattern) {
364441
+ const prevDecision = ruleDecision;
364128
364442
  ruleDecision = await this.applyShellHeuristics(command, ruleDecision, shellDirPath);
364443
+ if (prevDecision === PolicyDecision.ALLOW && ruleDecision === PolicyDecision.ASK_USER) {
364444
+ hasTopLevelHeuristicDowngrade = true;
364445
+ }
364129
364446
  }
364130
364447
  if (isShellCommand && toolName) {
364131
- const shellResult = await this.checkShellCommand(toolName, command, ruleDecision, serverName, shellDirPath, rule.allowRedirection, rule, toolAnnotations, subagent);
364448
+ const shellResult = await this.checkShellCommand(toolName, command, ruleDecision, serverName, shellDirPath, rule.allowRedirection, rule, toolAnnotations, subagent, hasTopLevelHeuristicDowngrade);
364132
364449
  decision = shellResult.decision;
364133
364450
  matchedRule = shellResult.rule;
364134
364451
  break;
@@ -364147,10 +364464,15 @@ var PolicyEngine = class {
364147
364464
  debugLogger.debug(`[PolicyEngine.check] NO MATCH - using default decision: ${this.defaultDecision}`);
364148
364465
  if (toolName && SHELL_TOOL_NAMES.includes(toolName)) {
364149
364466
  let heuristicDecision = this.defaultDecision;
364467
+ let hasTopLevelHeuristicDowngrade = false;
364150
364468
  if (!skipHeuristics && command) {
364469
+ const prevDecision = heuristicDecision;
364151
364470
  heuristicDecision = await this.applyShellHeuristics(command, heuristicDecision, shellDirPath);
364471
+ if (prevDecision === PolicyDecision.ALLOW && heuristicDecision === PolicyDecision.ASK_USER) {
364472
+ hasTopLevelHeuristicDowngrade = true;
364473
+ }
364152
364474
  }
364153
- const shellResult = await this.checkShellCommand(toolName, command, heuristicDecision, serverName, shellDirPath, false, void 0, toolAnnotations, subagent);
364475
+ const shellResult = await this.checkShellCommand(toolName, command, heuristicDecision, serverName, shellDirPath, false, void 0, toolAnnotations, subagent, hasTopLevelHeuristicDowngrade);
364154
364476
  decision = shellResult.decision;
364155
364477
  matchedRule = shellResult.rule;
364156
364478
  } else {
@@ -369889,7 +370211,7 @@ function areIssuersEqual(issuerA, issuerB) {
369889
370211
  }
369890
370212
  return normA === normB;
369891
370213
  }
369892
- function startCallbackServer(expectedState, port, expectedIssuer) {
370214
+ function startCallbackServer(expectedState, port, expectedIssuer, requireIssInResponse) {
369893
370215
  let portResolve;
369894
370216
  let portReject;
369895
370217
  const portPromise = new Promise((resolve29, reject) => {
@@ -369943,8 +370265,9 @@ function startCallbackServer(expectedState, port, expectedIssuer) {
369943
370265
  return;
369944
370266
  }
369945
370267
  if (expectedIssuer) {
369946
- if (!iss) {
369947
- debugLogger.error('OAuth callback rejected: Missing required "iss" parameter when an expected issuer is configured. Possible IdP mix-up attack (RFC 9207).');
370268
+ const requireIss = requireIssInResponse ?? true;
370269
+ if (requireIss && !iss) {
370270
+ debugLogger.error('OAuth callback rejected: Missing required "iss" parameter for the expected authorization server (RFC 9207).');
369948
370271
  res.writeHead(400, { "Content-Type": "text/html" });
369949
370272
  res.end(`
369950
370273
  <html>
@@ -369959,7 +370282,7 @@ function startCallbackServer(expectedState, port, expectedIssuer) {
369959
370282
  reject(new Error('Missing "iss" parameter in authorization response per RFC 9207'));
369960
370283
  return;
369961
370284
  }
369962
- if (!areIssuersEqual(iss, expectedIssuer)) {
370285
+ if (iss && !areIssuersEqual(iss, expectedIssuer)) {
369963
370286
  debugLogger.error("OAuth callback rejected: Issuer mismatch between authorization response and expected authorization server. Possible IdP mix-up attack (RFC 9207).");
369964
370287
  res.writeHead(400, { "Content-Type": "text/html" });
369965
370288
  res.end(`
@@ -369975,7 +370298,9 @@ function startCallbackServer(expectedState, port, expectedIssuer) {
369975
370298
  reject(new Error("Issuer mismatch in authorization response - possible OAuth mix-up attack"));
369976
370299
  return;
369977
370300
  }
369978
- debugLogger.debug("\u2713 OAuth callback issuer validated successfully.");
370301
+ if (iss) {
370302
+ debugLogger.debug("\u2713 OAuth callback issuer validated successfully.");
370303
+ }
369979
370304
  } else if (iss) {
369980
370305
  debugLogger.debug('OAuth callback received "iss" parameter (no expected issuer was configured).');
369981
370306
  }
@@ -370379,6 +370704,7 @@ var MCPOAuthProvider = class {
370379
370704
  issuer: discoveredConfig.issuer,
370380
370705
  tokenUrl: discoveredConfig.tokenUrl,
370381
370706
  scopes: config2.scopes || discoveredConfig.scopes || [],
370707
+ authorizationResponseIssParameterSupported: config2.authorizationResponseIssParameterSupported ?? discoveredConfig.authorizationResponseIssParameterSupported,
370382
370708
  // Preserve existing client credentials
370383
370709
  clientId: config2.clientId,
370384
370710
  clientSecret: config2.clientSecret
@@ -370402,6 +370728,7 @@ var MCPOAuthProvider = class {
370402
370728
  issuer: discoveredConfig.issuer,
370403
370729
  scopes: config2.scopes || discoveredConfig.scopes || [],
370404
370730
  registrationUrl: discoveredConfig.registrationUrl,
370731
+ authorizationResponseIssParameterSupported: config2.authorizationResponseIssParameterSupported ?? discoveredConfig.authorizationResponseIssParameterSupported,
370405
370732
  // Preserve existing client credentials
370406
370733
  clientId: config2.clientId,
370407
370734
  clientSecret: config2.clientSecret
@@ -370414,7 +370741,8 @@ var MCPOAuthProvider = class {
370414
370741
  const pkceParams = generatePKCEParams();
370415
370742
  const preferredPort = getPortFromUrl(config2.redirectUri);
370416
370743
  debugLogger.debug(`Starting callback server for "${serverName}" (expected issuer: ${config2.issuer || "none"})...`);
370417
- const callbackServer = startCallbackServer(pkceParams.state, preferredPort, config2.issuer);
370744
+ const requireIssInResponse = config2.authorizationResponseIssParameterSupported ?? Boolean(config2.issuer);
370745
+ const callbackServer = startCallbackServer(pkceParams.state, preferredPort, config2.issuer, requireIssInResponse);
370418
370746
  const redirectPort = await callbackServer.port;
370419
370747
  debugLogger.debug(`Callback server listening on port ${redirectPort}`);
370420
370748
  if (!config2.clientId) {
@@ -371163,7 +371491,8 @@ async function handleAutomaticOAuth(mcpServerName, mcpServerConfig, wwwAuthentic
371163
371491
  authorizationUrl: oauthConfig.authorizationUrl,
371164
371492
  issuer: oauthConfig.issuer,
371165
371493
  tokenUrl: oauthConfig.tokenUrl,
371166
- scopes: oauthConfig.scopes || []
371494
+ scopes: oauthConfig.scopes || [],
371495
+ authorizationResponseIssParameterSupported: mcpServerConfig.oauth?.authorizationResponseIssParameterSupported ?? oauthConfig.authorizationResponseIssParameterSupported
371167
371496
  };
371168
371497
  debugLogger.log(`Starting OAuth authentication for server '${mcpServerName}'...`);
371169
371498
  const authProvider = new MCPOAuthProvider(new MCPOAuthTokenStorage());
@@ -371707,7 +372036,8 @@ async function connectToMcpServer(clientVersion, mcpServerName, mcpServerConfig,
371707
372036
  authorizationUrl: oauthConfig.authorizationUrl,
371708
372037
  issuer: oauthConfig.issuer,
371709
372038
  tokenUrl: oauthConfig.tokenUrl,
371710
- scopes: oauthConfig.scopes || []
372039
+ scopes: oauthConfig.scopes || [],
372040
+ authorizationResponseIssParameterSupported: mcpServerConfig.oauth?.authorizationResponseIssParameterSupported ?? oauthConfig.authorizationResponseIssParameterSupported
371711
372041
  };
371712
372042
  const authServerUrl = mcpServerConfig.httpUrl || mcpServerConfig.url;
371713
372043
  debugLogger.log(`Starting OAuth authentication for server '${mcpServerName}'...`);
@@ -400210,6 +400540,8 @@ export {
400210
400540
  StreamEventType,
400211
400541
  SYNTHETIC_THOUGHT_SIGNATURE2 as SYNTHETIC_THOUGHT_SIGNATURE,
400212
400542
  INTERRUPTED_RESPONSE_PLACEHOLDER,
400543
+ INTERRUPTED_TOOL_RESPONSE_ERROR,
400544
+ CONTINUE_PROMPT_TEXT,
400213
400545
  isValidNonThoughtTextPart,
400214
400546
  THINKING_ONLY_NUDGE_MESSAGE,
400215
400547
  NO_RESPONSE_TEXT_NUDGE_MESSAGE,
@@ -400220,7 +400552,10 @@ export {
400220
400552
  GeminiChat,
400221
400553
  isSchemaDepthError,
400222
400554
  isInvalidArgumentError,
400555
+ buildInterruptedToolResponseParts,
400556
+ ensureTerminalUserTurn,
400223
400557
  stripToolCallIdPrefixes,
400558
+ coalesceConsecutiveContents,
400224
400559
  coalesceConsecutiveRoles,
400225
400560
  stripThoughts2 as stripThoughts,
400226
400561
  populateToolDisplay,