@google/gemini-cli-core 0.64.0-preview.0 → 0.65.0-nightly.20261007.gef59c532f

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 (42) hide show
  1. package/dist/docs/changelogs/index.md +14 -0
  2. package/dist/docs/changelogs/latest.md +55 -34
  3. package/dist/docs/changelogs/preview.md +59 -54
  4. package/dist/docs/tools/mcp-server.md +41 -12
  5. package/dist/google-gemini-cli-core-0.65.0-nightly.20261006.gfb972b2f8.tgz +0 -0
  6. package/dist/src/generated/git-commit.d.ts +2 -2
  7. package/dist/src/generated/git-commit.js +2 -2
  8. package/dist/src/generated/git-commit.js.map +1 -1
  9. package/dist/src/mcp/oauth-callback-iss-handling.test.d.ts +6 -0
  10. package/dist/src/mcp/oauth-callback-iss-handling.test.js +251 -0
  11. package/dist/src/mcp/oauth-callback-iss-handling.test.js.map +1 -0
  12. package/dist/src/mcp/oauth-provider.d.ts +1 -0
  13. package/dist/src/mcp/oauth-provider.js +10 -1
  14. package/dist/src/mcp/oauth-provider.js.map +1 -1
  15. package/dist/src/mcp/oauth-provider.test.js +112 -0
  16. package/dist/src/mcp/oauth-provider.test.js.map +1 -1
  17. package/dist/src/mcp/oauth-utils.d.ts +6 -0
  18. package/dist/src/mcp/oauth-utils.js +6 -0
  19. package/dist/src/mcp/oauth-utils.js.map +1 -1
  20. package/dist/src/mcp/oauth-utils.test.js +32 -0
  21. package/dist/src/mcp/oauth-utils.test.js.map +1 -1
  22. package/dist/src/services/chatRecordingService.d.ts +6 -1
  23. package/dist/src/services/chatRecordingService.js +86 -22
  24. package/dist/src/services/chatRecordingService.js.map +1 -1
  25. package/dist/src/services/chatRecordingService.test.js +176 -15
  26. package/dist/src/services/chatRecordingService.test.js.map +1 -1
  27. package/dist/src/tools/mcp-client.js +5 -0
  28. package/dist/src/tools/mcp-client.js.map +1 -1
  29. package/dist/src/tools/mcp-client.test.js +49 -0
  30. package/dist/src/tools/mcp-client.test.js.map +1 -1
  31. package/dist/src/utils/oauth-flow.d.ts +2 -1
  32. package/dist/src/utils/oauth-flow.js +11 -6
  33. package/dist/src/utils/oauth-flow.js.map +1 -1
  34. package/dist/src/utils/oauth-flow.test.js +34 -2
  35. package/dist/src/utils/oauth-flow.test.js.map +1 -1
  36. package/dist/src/utils/sessionUtils.d.ts +11 -0
  37. package/dist/src/utils/sessionUtils.js +99 -13
  38. package/dist/src/utils/sessionUtils.js.map +1 -1
  39. package/dist/src/utils/sessionUtils.test.js +405 -7
  40. package/dist/src/utils/sessionUtils.test.js.map +1 -1
  41. package/dist/tsconfig.tsbuildinfo +1 -1
  42. package/package.json +1 -1
@@ -18,6 +18,20 @@ on GitHub.
18
18
  | [Preview](preview.md) | Experimental features ready for early feedback. |
19
19
  | [Stable](latest.md) | Stable, recommended for general use. |
20
20
 
21
+ ## Announcements: v0.63.0 - 2026-10-06
22
+
23
+ - **Autonomous Plan Execution:** Enabled autonomous plan execution in
24
+ non-interactive mode to support unattended agent workflows
25
+ ([#29539](https://github.com/google-gemini/gemini-cli/pull/29539) by
26
+ @urielefrenvirtusa).
27
+ - **Core Stability and Memory Lifecycle:** Bounded tool output sizes and
28
+ optimized memory management in long-running agent loops while preventing
29
+ infinite authentication loops across headless and supervisor environments
30
+ ([#29451](https://github.com/google-gemini/gemini-cli/pull/29451) by
31
+ @diegogodinezr,
32
+ [#29448](https://github.com/google-gemini/gemini-cli/pull/29448) by
33
+ @villahernandez-coder).
34
+
21
35
  ## Announcements: v0.61.0 - 2026-09-23
22
36
 
23
37
  - **Core Security Hardening:** Prevented indirect prompt injection
@@ -1,6 +1,6 @@
1
- # Latest stable release: v0.61.0
1
+ # Latest stable release: v0.63.0
2
2
 
3
- Released: September 23, 2026
3
+ Released: October 6, 2026
4
4
 
5
5
  For most users, our latest stable release is the recommended release. Install
6
6
  the latest stable version with:
@@ -11,41 +11,62 @@ npm install -g @google/gemini-cli
11
11
 
12
12
  ## Highlights
13
13
 
14
- - **Prompt Injection Defense:** Enhanced security by preventing indirect prompt
15
- injection attacks through build file modifications and untrusted command
16
- flags.
17
- - **Sandbox & State Hardening:** Hardened filesystem boundaries and isolated
18
- internal runtime state to ensure more secure execution environments.
19
- - **Agent Loop Robustness:** Guaranteed that internal state properties within
20
- `AgentLoopContext` are fully preserved during object spread operations,
21
- improving agent reliability.
22
- - **Versioned Model Preserves:** Ensured explicit versioned Flash model IDs are
23
- correctly preserved and used directly during routing and execution.
14
+ - **Autonomous Plan Execution:** Enabled autonomous plan execution in
15
+ non-interactive mode, allowing automated agent workflows to run multi-step
16
+ plans without requiring interactive user confirmation.
17
+ - **Memory Lifecycle & Output Bounding:** Bounded tool output sizes and
18
+ optimized the memory lifecycle in long-running agent loops to prevent memory
19
+ exhaustion, while formatting full output when truncation is disabled.
20
+ - **Authentication Resilience:** Prevented infinite authentication loops caused
21
+ by file contention, headless keyring environments, and supervisor state drops,
22
+ ensuring reliable credential handling across environments.
23
+ - **CLI & Connection Recovery UX:** Displayed a retry progress indicator during
24
+ connection recovery, restored paused stdin after capability detection, and
25
+ ensured automatic temporary directory cleanup upon background shell execution
26
+ exit.
24
27
 
25
28
  ## What's Changed
26
29
 
27
- - Changelog for v0.60.0-preview.0 by @gemini-cli-robot in
28
- [#29251](https://github.com/google-gemini/gemini-cli/pull/29251)
29
- - chore(release): bump version to 0.61.0-nightly.20260908.gc647533d6 by
30
+ - fix(cli): display retry progress indicator during connection recovery (#28340)
31
+ by @amelidev in
32
+ [#29468](https://github.com/google-gemini/gemini-cli/pull/29468)
33
+ - Changelog for v0.61.0-preview.1 by @gemini-cli-robot in
34
+ [#29469](https://github.com/google-gemini/gemini-cli/pull/29469)
35
+ - Changelog for v0.61.0 by @gemini-cli-robot in
36
+ [#29472](https://github.com/google-gemini/gemini-cli/pull/29472)
37
+ - fix(cli): distinguish missing MCP enablement config from malformed JSON by
38
+ @jesussamuel-byte in
39
+ [#29446](https://github.com/google-gemini/gemini-cli/pull/29446)
40
+ - fix(cli): restore paused stdin after capability detection by @ugorla-dev in
41
+ [#29487](https://github.com/google-gemini/gemini-cli/pull/29487)
42
+ - fix(core): bound tool output size and optimize memory lifecycle in
43
+ long-running agent loops by @diegogodinezr in
44
+ [#29451](https://github.com/google-gemini/gemini-cli/pull/29451)
45
+ - fix(core): remove invalid diff.external override by @urielefrenvirtusa in
46
+ [#29467](https://github.com/google-gemini/gemini-cli/pull/29467)
47
+ - chore(release): bump version to 0.63.0-nightly.20260923.gf50ba8608 by
30
48
  @gemini-cli-robot in
31
- [#29254](https://github.com/google-gemini/gemini-cli/pull/29254)
32
- - Changelog for v0.59.0 by @gemini-cli-robot in
33
- [#29253](https://github.com/google-gemini/gemini-cli/pull/29253)
34
- - fix(core): preserve explicit versioned Flash model IDs by @SandyTao520 in
35
- [#29252](https://github.com/google-gemini/gemini-cli/pull/29252)
36
- - fix(core): prevent indirect prompt injection via build file modifications and
37
- untrusted flags by @villahernandez-coder in
38
- [#29250](https://github.com/google-gemini/gemini-cli/pull/29250)
39
- - fix(sandbox): harden filesystem boundaries and isolate runtime state by
40
- @diegogodinezr in
41
- [#29214](https://github.com/google-gemini/gemini-cli/pull/29214)
42
- - fix(core): ensure AgentLoopContext properties are preserved across object
43
- spread by @diegogodinezr in
44
- [#29335](https://github.com/google-gemini/gemini-cli/pull/29335)
45
- - fix(patch): cherry-pick 62364cb to release/v0.61.0-preview.0-pr-29443 to patch
46
- version v0.61.0-preview.0 and create version 0.61.0-preview.1 by
47
- @gemini-cli-robot in
48
- [#29455](https://github.com/google-gemini/gemini-cli/pull/29455)
49
+ [#29471](https://github.com/google-gemini/gemini-cli/pull/29471)
50
+ - Changelog for v0.62.0-preview.0 by @gemini-cli-robot in
51
+ [#29470](https://github.com/google-gemini/gemini-cli/pull/29470)
52
+ - fix(core): clean up temporary directory when background shell execution exits
53
+ by @jesussamuel-byte in
54
+ [#29437](https://github.com/google-gemini/gemini-cli/pull/29437)
55
+ - fix(acp): resolve session before config initialization and avoid same-minute
56
+ filename collisions by @jesussamuel-byte in
57
+ [#29463](https://github.com/google-gemini/gemini-cli/pull/29463)
58
+ - fix(core): align policy redirection gates, path validation, and workflow
59
+ parsing by @DavidAPierce in
60
+ [#29506](https://github.com/google-gemini/gemini-cli/pull/29506)
61
+ - fix(auth): prevent infinite auth loop from file contention, headless keyring,
62
+ and supervisor state drops (#28341) by @villahernandez-coder in
63
+ [#29448](https://github.com/google-gemini/gemini-cli/pull/29448)
64
+ - fix(core): enable autonomous plan execution in non-interactive mode by
65
+ @urielefrenvirtusa in
66
+ [#29539](https://github.com/google-gemini/gemini-cli/pull/29539)
67
+ - fix(core): disable truncation when maxChars <= 0 in formatTruncatedToolOutput
68
+ by @diegogodinezr in
69
+ [#29542](https://github.com/google-gemini/gemini-cli/pull/29542)
49
70
 
50
71
  **Full Changelog**:
51
- https://github.com/google-gemini/gemini-cli/compare/v0.60.0...v0.61.0
72
+ https://github.com/google-gemini/gemini-cli/compare/v0.62.0...v0.63.0
@@ -1,6 +1,6 @@
1
- # Preview release: v0.63.0-preview.0
1
+ # Preview release: v0.64.0-preview.0
2
2
 
3
- Released: September 29, 2026
3
+ Released: October 6, 2026
4
4
 
5
5
  Our preview release includes the latest, new, and experimental features. This
6
6
  release may not be as stable as our [latest weekly release](latest.md).
@@ -13,62 +13,67 @@ npm install -g @google/gemini-cli@preview
13
13
 
14
14
  ## Highlights
15
15
 
16
- - **Autonomous Plan Execution**: Enabled autonomous plan execution in
17
- non-interactive mode, allowing automated agent workflows to run multi-step
18
- plans without requiring interactive user confirmation.
19
- - **Memory Lifecycle & Output Bounding**: Bounded tool output sizes and
20
- optimized the memory lifecycle in long-running agent loops to prevent memory
21
- leaks, while ensuring full output formatting when truncation is disabled.
22
- - **Authentication Resilience**: Prevented infinite authentication loops caused
23
- by file contention, headless keyring environments, and supervisor state drops,
24
- ensuring reliable credential handling across environments.
25
- - **CLI & Connection Recovery UX**: Displayed a retry progress indicator during
26
- connection recovery, restored paused stdin after capability detection, and
27
- ensured automatic temporary directory cleanup upon background shell execution
28
- exit.
16
+ - **Atomic File Operations & State Recovery**: Serialized file tool operations
17
+ with atomic writes and introduced atomic CLI state persistence with automatic
18
+ recovery from backup on corruption.
19
+ - **Chat Recording Optimization**: Implemented append-only delta patching and
20
+ bounded history windowing in ChatRecordingService to optimize memory and disk
21
+ efficiency during long-running sessions.
22
+ - **CLI Input & Reference Resolution**: Added support for resolving `@file:line`
23
+ references, resolved CPU hangs and quote swallowing on `@` symbols within code
24
+ blocks, and prevented ghost text wrap hangs.
25
+ - **Terminal UI & Interaction Stability**: Ensured Ctrl+C emergency aborts reach
26
+ active cancellation handlers, made selection list confirmation with Enter and
27
+ Spacebar reliable, and fixed Windows ConPTY IME cursor forwarding.
29
28
 
30
29
  ## What's Changed
31
30
 
32
- - fix(cli): display retry progress indicator during connection recovery (#28340)
33
- by @amelidev in
34
- [#29468](https://github.com/google-gemini/gemini-cli/pull/29468)
35
- - Changelog for v0.61.0-preview.1 by @gemini-cli-robot in
36
- [#29469](https://github.com/google-gemini/gemini-cli/pull/29469)
37
- - Changelog for v0.61.0 by @gemini-cli-robot in
38
- [#29472](https://github.com/google-gemini/gemini-cli/pull/29472)
39
- - fix(cli): distinguish missing MCP enablement config from malformed JSON by
40
- @jesussamuel-byte in
41
- [#29446](https://github.com/google-gemini/gemini-cli/pull/29446)
42
- - fix(cli): restore paused stdin after capability detection by @ugorla-dev in
43
- [#29487](https://github.com/google-gemini/gemini-cli/pull/29487)
44
- - fix(core): bound tool output size and optimize memory lifecycle in
45
- long-running agent loops by @diegogodinezr in
46
- [#29451](https://github.com/google-gemini/gemini-cli/pull/29451)
47
- - fix(core): remove invalid diff.external override by @urielefrenvirtusa in
48
- [#29467](https://github.com/google-gemini/gemini-cli/pull/29467)
49
- - chore(release): bump version to 0.63.0-nightly.20260923.gf50ba8608 by
31
+ - refactor(a2a-server): implement V1 to V2 settings migration logic by
32
+ @jvargassanchez-dot in
33
+ [#29450](https://github.com/google-gemini/gemini-cli/pull/29450)
34
+ - fix(acp): bridge PromptResponse.usage and emit usage_update notifications
35
+ (#29389) by @elberthc-byte in
36
+ [#29549](https://github.com/google-gemini/gemini-cli/pull/29549)
37
+ - Changelog for v0.63.0-preview.0 by @gemini-cli-robot in
38
+ [#29565](https://github.com/google-gemini/gemini-cli/pull/29565)
39
+ - fix(cli): propagate resolved folder trust state in headless mode (#29031) by
40
+ @amelidev in [#29528](https://github.com/google-gemini/gemini-cli/pull/29528)
41
+ - chore(release): bump version to 0.64.0-nightly.20260929.gd75234cae by
50
42
  @gemini-cli-robot in
51
- [#29471](https://github.com/google-gemini/gemini-cli/pull/29471)
52
- - Changelog for v0.62.0-preview.0 by @gemini-cli-robot in
53
- [#29470](https://github.com/google-gemini/gemini-cli/pull/29470)
54
- - fix(core): clean up temporary directory when background shell execution exits
55
- by @jesussamuel-byte in
56
- [#29437](https://github.com/google-gemini/gemini-cli/pull/29437)
57
- - fix(acp): resolve session before config initialization and avoid same-minute
58
- filename collisions by @jesussamuel-byte in
59
- [#29463](https://github.com/google-gemini/gemini-cli/pull/29463)
60
- - fix(core): align policy redirection gates, path validation, and workflow
61
- parsing by @DavidAPierce in
62
- [#29506](https://github.com/google-gemini/gemini-cli/pull/29506)
63
- - fix(auth): prevent infinite auth loop from file contention, headless keyring,
64
- and supervisor state drops (#28341) by @villahernandez-coder in
65
- [#29448](https://github.com/google-gemini/gemini-cli/pull/29448)
66
- - fix(core): enable autonomous plan execution in non-interactive mode by
43
+ [#29567](https://github.com/google-gemini/gemini-cli/pull/29567)
44
+ - fix(cli): prevent CPU hang and quote swallowing on @ within code (#29434) by
45
+ @elberthc-byte in
46
+ [#29557](https://github.com/google-gemini/gemini-cli/pull/29557)
47
+ - fix(core): serialize file tool operations and make writes atomic (#29078) by
48
+ @elberthc-byte in
49
+ [#29499](https://github.com/google-gemini/gemini-cli/pull/29499)
50
+ - fix(core): implement append-only delta patching and bounded history windowing
51
+ in ChatRecordingService by @jvargassanchez-dot in
52
+ [#29568](https://github.com/google-gemini/gemini-cli/pull/29568)
53
+ - fix(cli): persist state atomically and recover from backup on corruption by
67
54
  @urielefrenvirtusa in
68
- [#29539](https://github.com/google-gemini/gemini-cli/pull/29539)
69
- - fix(core): disable truncation when maxChars <= 0 in formatTruncatedToolOutput
70
- by @diegogodinezr in
71
- [#29542](https://github.com/google-gemini/gemini-cli/pull/29542)
55
+ [#29558](https://github.com/google-gemini/gemini-cli/pull/29558)
56
+ - fix(cli): ensure Ctrl+C emergency abort reaches cancellation handler during
57
+ active operations by @urielefrenvirtusa in
58
+ [#29586](https://github.com/google-gemini/gemini-cli/pull/29586)
59
+ - fix(cli): resolve @file:line references and prevent ghost text wrap hang by
60
+ @jesussamuel-byte in
61
+ [#29581](https://github.com/google-gemini/gemini-cli/pull/29581)
62
+ - fix(ui): ensure Windows ConPTY forwards IME cursor position by
63
+ @jesussamuel-byte in
64
+ [#29560](https://github.com/google-gemini/gemini-cli/pull/29560)
65
+ - fix(cli): retry directory removal on Windows locking errors during extension
66
+ updates by @jesussamuel-byte in
67
+ [#29540](https://github.com/google-gemini/gemini-cli/pull/29540)
68
+ - fix(acp): resolve session by exact id and handle listener cleanup on session
69
+ failure by @diegogodinezr in
70
+ [#29580](https://github.com/google-gemini/gemini-cli/pull/29580)
71
+ - fix(cli): preserve scroll position and partition pending height budget by
72
+ @luisfelipe-alt in
73
+ [#29520](https://github.com/google-gemini/gemini-cli/pull/29520)
74
+ - fix(cli): ensure Enter and Spacebar reliably confirm selection list options by
75
+ @ugorla-dev in
76
+ [#29502](https://github.com/google-gemini/gemini-cli/pull/29502)
72
77
 
73
78
  **Full Changelog**:
74
- https://github.com/google-gemini/gemini-cli/compare/v0.62.0-preview.0...v0.63.0-preview.0
79
+ https://github.com/google-gemini/gemini-cli/compare/v0.63.0-preview.0...v0.64.0-preview.0
@@ -326,17 +326,31 @@ This feature will not work in:
326
326
 
327
327
  <!-- prettier-ignore -->
328
328
  > [!IMPORTANT]
329
- > To protect against OAuth Identity Provider (IdP) mix-up attacks, Gemini CLI
330
- > validates the `iss` (issuer) parameter per
331
- > [RFC 9207](https://www.rfc-editor.org/rfc/rfc9207). When an expected issuer is
332
- > discovered or configured, authorization servers **must** return the `iss`
333
- > parameter in the callback redirect matching the issuer URL. Responses missing
334
- > `iss` or with mismatched issuers are rejected with HTTP 400.
329
+ > Gemini CLI validates the `iss` (issuer) parameter in OAuth authorization
330
+ > responses per [RFC 9207](https://www.rfc-editor.org/rfc/rfc9207) and the MCP
331
+ > specification:
332
+ >
333
+ > - Whenever the `iss` parameter is present in the callback redirect and an
334
+ > expected `issuer` is discovered or configured, `iss` **must** match the
335
+ > expected issuer URL.
336
+ > - When the authorization server metadata sets
337
+ > `authorization_response_iss_parameter_supported: true`, the callback
338
+ > redirect **must** include the `iss` parameter.
339
+ > - When the discovered authorization server metadata omits
340
+ > `authorization_response_iss_parameter_supported` or sets it to `false` (for
341
+ > example, providers that do not declare RFC 9207 support), callbacks without
342
+ > `iss` are accepted.
343
+ > - When you configure `issuer` explicitly in `settings.json` and Gemini CLI
344
+ > doesn't discover authorization server metadata, the callback redirect
345
+ > **must** include the `iss` parameter. To accept callbacks without `iss` for
346
+ > such a server, set `authorizationResponseIssParameterSupported` to `false`
347
+ > in the `oauth` configuration.
335
348
 
336
349
  ##### Expected authorization callback example
337
350
 
338
- When the authorization server redirects the user back to Gemini CLI, the
339
- redirect URI must include the `iss` parameter:
351
+ When the authorization server declares
352
+ `authorization_response_iss_parameter_supported: true`, the redirect URI must
353
+ include the `iss` parameter:
340
354
 
341
355
  ```http
342
356
  HTTP/1.1 302 Found
@@ -344,10 +358,14 @@ Location: http://localhost:<port>/oauth/callback?code=AUTH_CODE&state=STATE&iss=
344
358
  ```
345
359
 
346
360
  - **Valid response (accepted):** `iss` matches the configured or discovered
347
- issuer (`https://auth.example.com`).
348
- - **Missing `iss` (rejected):**
349
- `http://localhost:<port>/oauth/callback?code=AUTH_CODE&state=STATE` (fails
350
- with HTTP 400: `Missing issuer parameter in response`).
361
+ issuer (`https://auth.example.com`), or `iss` is omitted when the discovered
362
+ metadata doesn't set `authorization_response_iss_parameter_supported` to
363
+ `true`.
364
+ - **Missing `iss` when required (rejected):**
365
+ `http://localhost:<port>/oauth/callback?code=AUTH_CODE&state=STATE` when
366
+ `authorization_response_iss_parameter_supported` is `true`, or when `issuer`
367
+ is configured explicitly without discovered metadata (fails with HTTP 400:
368
+ `Missing issuer parameter in response`).
351
369
  - **Mismatched `iss` (rejected):** `iss` points to a different domain or
352
370
  includes userinfo (fails with HTTP 400: `Issuer mismatch`).
353
371
 
@@ -373,6 +391,12 @@ If your remote MCP server uses an authorization server with a known issuer URL:
373
391
  }
374
392
  ```
375
393
 
394
+ With this configuration, Gemini CLI requires the `iss` parameter in the
395
+ authorization callback. If the authorization server doesn't return `iss` in its
396
+ redirects, add `"authorizationResponseIssParameterSupported": false` to the
397
+ `oauth` block. Gemini CLI then accepts callbacks without `iss` while still
398
+ rejecting any `iss` value that doesn't match the configured `issuer`.
399
+
376
400
  #### Managing OAuth authentication
377
401
 
378
402
  Use the `/mcp auth` command to manage OAuth authentication:
@@ -398,6 +422,11 @@ Use the `/mcp auth` command to manage OAuth authentication:
398
422
  if omitted)
399
423
  - **`issuer`** (string): Authorization server issuer URL (auto-discovered if
400
424
  omitted; validated per RFC 9207)
425
+ - **`authorizationResponseIssParameterSupported`** (boolean): Whether the
426
+ authorization server returns the `iss` parameter in authorization responses
427
+ (RFC 9207). Auto-discovered from the authorization server metadata; defaults
428
+ to `true` when `issuer` is configured explicitly without discovered metadata.
429
+ Set to `false` to accept callbacks without `iss` for such a server.
401
430
  - **`tokenUrl`** (string): OAuth token endpoint (auto-discovered if omitted)
402
431
  - **`scopes`** (string[]): Required OAuth scopes
403
432
  - **`redirectUri`** (string): Custom redirect URI (defaults to an OS-assigned
@@ -3,5 +3,5 @@
3
3
  * Copyright 2026 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- export declare const GIT_COMMIT_INFO = "fb972b2f8";
7
- export declare const CLI_VERSION = "0.64.0-preview.0";
6
+ export declare const GIT_COMMIT_INFO = "ef59c532f";
7
+ export declare const CLI_VERSION = "0.65.0-nightly.20261007.gef59c532f";
@@ -5,6 +5,6 @@
5
5
  */
6
6
  // This file is auto-generated by the build script (scripts/generate-git-commit-info.js)
7
7
  // Do not edit this file manually.
8
- export const GIT_COMMIT_INFO = 'fb972b2f8';
9
- export const CLI_VERSION = '0.64.0-preview.0';
8
+ export const GIT_COMMIT_INFO = 'ef59c532f';
9
+ export const CLI_VERSION = '0.65.0-nightly.20261007.gef59c532f';
10
10
  //# sourceMappingURL=git-commit.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"git-commit.js","sourceRoot":"","sources":["../../../src/generated/git-commit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wFAAwF;AACxF,kCAAkC;AAClC,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC;AAC3C,MAAM,CAAC,MAAM,WAAW,GAAG,kBAAkB,CAAC"}
1
+ {"version":3,"file":"git-commit.js","sourceRoot":"","sources":["../../../src/generated/git-commit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wFAAwF;AACxF,kCAAkC;AAClC,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC;AAC3C,MAAM,CAAC,MAAM,WAAW,GAAG,oCAAoC,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ export {};
@@ -0,0 +1,251 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { describe, it, expect, vi, beforeEach, afterEach, afterAll, } from 'vitest';
7
+ const mockOpenBrowserSecurely = vi.hoisted(() => vi.fn());
8
+ vi.mock('../utils/secure-browser-launcher.js', () => ({
9
+ openBrowserSecurely: mockOpenBrowserSecurely,
10
+ }));
11
+ vi.mock('./oauth-token-storage.js', () => {
12
+ const mockSaveToken = vi.fn();
13
+ const mockGetCredentials = vi.fn();
14
+ const mockIsTokenExpired = vi.fn();
15
+ const mockDeleteCredentials = vi.fn();
16
+ return {
17
+ MCPOAuthTokenStorage: vi.fn(() => ({
18
+ saveToken: mockSaveToken,
19
+ getCredentials: mockGetCredentials,
20
+ isTokenExpired: mockIsTokenExpired,
21
+ deleteCredentials: mockDeleteCredentials,
22
+ })),
23
+ };
24
+ });
25
+ vi.mock('../utils/events.js', () => ({
26
+ coreEvents: {
27
+ emitFeedback: vi.fn(),
28
+ emitConsoleLog: vi.fn(),
29
+ },
30
+ }));
31
+ vi.mock('../utils/authConsent.js', () => ({
32
+ getConsentForOauth: vi.fn(() => Promise.resolve(true)),
33
+ }));
34
+ vi.mock('../utils/headless.js', () => ({
35
+ isHeadlessMode: vi.fn(() => false),
36
+ }));
37
+ vi.mock('node:dns/promises', () => ({
38
+ lookup: vi.fn(),
39
+ }));
40
+ import * as dnsPromises from 'node:dns/promises';
41
+ import ipaddr from 'ipaddr.js';
42
+ import { MCPOAuthProvider } from './oauth-provider.js';
43
+ import { MCPOAuthTokenStorage } from './oauth-token-storage.js';
44
+ import { OAuthUtils } from './oauth-utils.js';
45
+ import { startCallbackServer, REDIRECT_PATH } from '../utils/oauth-flow.js';
46
+ const realFetch = global.fetch;
47
+ const mockFetch = vi.fn();
48
+ global.fetch = mockFetch;
49
+ const createMockResponse = (options) => {
50
+ const status = options.status ?? (options.ok ? 200 : 400);
51
+ const headers = new Headers(options.headers ?? {});
52
+ if (options.contentType) {
53
+ headers.set('content-type', options.contentType);
54
+ }
55
+ return {
56
+ ok: options.ok,
57
+ status,
58
+ headers,
59
+ text: () => Promise.resolve(options.text ?? ''),
60
+ json: () => Promise.resolve(options.json ?? {}),
61
+ };
62
+ };
63
+ describe('OAuth callback iss parameter handling (RFC 9207 & MCP Spec)', () => {
64
+ const mcpServerUrl = 'https://mcp.notion.com/mcp';
65
+ const expectedIssuer = 'https://mcp.notion.com';
66
+ const mismatchIssuer = 'https://other-idp.example.com';
67
+ const mockTokenResponse = {
68
+ access_token: 'notion_access_token_123',
69
+ token_type: 'Bearer',
70
+ expires_in: 3600,
71
+ refresh_token: 'notion_refresh_token_456',
72
+ scope: 'read write',
73
+ };
74
+ beforeEach(() => {
75
+ vi.clearAllMocks();
76
+ mockOpenBrowserSecurely.mockClear();
77
+ vi.spyOn(console, 'log').mockImplementation(() => { });
78
+ vi.spyOn(console, 'warn').mockImplementation(() => { });
79
+ vi.spyOn(console, 'error').mockImplementation(() => { });
80
+ vi.mocked(dnsPromises.lookup).mockImplementation(async (hostname) => {
81
+ if (ipaddr.isValid(hostname)) {
82
+ return [{ address: hostname, family: hostname.includes(':') ? 6 : 4 }];
83
+ }
84
+ return [{ address: '93.184.216.34', family: 4 }];
85
+ });
86
+ const tokenStorage = new MCPOAuthTokenStorage();
87
+ vi.mocked(tokenStorage.saveToken).mockResolvedValue(undefined);
88
+ vi.mocked(tokenStorage.getCredentials).mockResolvedValue(null);
89
+ });
90
+ afterEach(() => {
91
+ vi.restoreAllMocks();
92
+ vi.unstubAllEnvs();
93
+ });
94
+ afterAll(() => {
95
+ // global.fetch is assigned directly (not via vi.spyOn), so
96
+ // vi.restoreAllMocks() does not revert it. Restore it explicitly to avoid
97
+ // affecting other suites that share the same worker process.
98
+ global.fetch = realFetch;
99
+ });
100
+ /**
101
+ * Simulates the browser redirect back to the local callback server and the
102
+ * token exchange for a given MCPOAuthConfig.
103
+ */
104
+ async function runOAuthFlow(oauthAuthConfig, callbackQueryParams) {
105
+ mockOpenBrowserSecurely.mockImplementation(async (authUrlStr) => {
106
+ const authUrl = new URL(authUrlStr);
107
+ const redirectUri = authUrl.searchParams.get('redirect_uri');
108
+ const state = authUrl.searchParams.get('state');
109
+ const callbackUrl = new URL(redirectUri);
110
+ callbackUrl.searchParams.set('code', 'notion_auth_code_abc');
111
+ callbackUrl.searchParams.set('state', state);
112
+ for (const [key, value] of Object.entries(callbackQueryParams)) {
113
+ callbackUrl.searchParams.set(key, value);
114
+ }
115
+ // Trigger the real local HTTP callback server asynchronously. The
116
+ // provider awaits the callback server port before opening the browser,
117
+ // so the server is already listening and no arbitrary delay is needed.
118
+ setImmediate(() => {
119
+ realFetch(callbackUrl.toString()).catch(() => { });
120
+ });
121
+ });
122
+ mockFetch.mockResolvedValueOnce(createMockResponse({
123
+ ok: true,
124
+ contentType: 'application/json',
125
+ text: JSON.stringify(mockTokenResponse),
126
+ json: mockTokenResponse,
127
+ }));
128
+ const authProvider = new MCPOAuthProvider(new MCPOAuthTokenStorage());
129
+ return authProvider.authenticate('notion', oauthAuthConfig, mcpServerUrl);
130
+ }
131
+ /**
132
+ * Simulates the OAuth authorization server metadata discovery and browser
133
+ * callback flow for an MCP server.
134
+ */
135
+ async function runOAuthFlowWithMetadata(metadataOverrides, callbackQueryParams) {
136
+ const authServerMetadata = {
137
+ issuer: expectedIssuer,
138
+ authorization_endpoint: `${expectedIssuer}/authorize`,
139
+ token_endpoint: `${expectedIssuer}/token`,
140
+ registration_endpoint: `${expectedIssuer}/register`,
141
+ response_types_supported: ['code'],
142
+ grant_types_supported: ['authorization_code', 'refresh_token'],
143
+ code_challenge_methods_supported: ['S256'],
144
+ ...metadataOverrides,
145
+ };
146
+ const discoveredConfig = OAuthUtils.metadataToOAuthConfig(authServerMetadata);
147
+ // Simulate mcp-client.ts constructing oauthAuthConfig from discoveredConfig
148
+ const oauthAuthConfig = {
149
+ enabled: true,
150
+ authorizationUrl: discoveredConfig.authorizationUrl,
151
+ issuer: discoveredConfig.issuer,
152
+ tokenUrl: discoveredConfig.tokenUrl,
153
+ scopes: discoveredConfig.scopes || [],
154
+ registrationUrl: discoveredConfig.registrationUrl,
155
+ authorizationResponseIssParameterSupported: discoveredConfig.authorizationResponseIssParameterSupported,
156
+ clientId: 'notion-dynamic-client-id',
157
+ };
158
+ return runOAuthFlow(oauthAuthConfig, callbackQueryParams);
159
+ }
160
+ /**
161
+ * Simulates a settings.json entry where the user configured the OAuth
162
+ * endpoints and issuer explicitly, so no metadata discovery takes place.
163
+ */
164
+ async function runOAuthFlowWithExplicitConfig(configOverrides, callbackQueryParams) {
165
+ const oauthAuthConfig = {
166
+ enabled: true,
167
+ authorizationUrl: `${expectedIssuer}/authorize`,
168
+ tokenUrl: `${expectedIssuer}/token`,
169
+ issuer: expectedIssuer,
170
+ clientId: 'notion-static-client-id',
171
+ scopes: ['read'],
172
+ ...configOverrides,
173
+ };
174
+ return runOAuthFlow(oauthAuthConfig, callbackQueryParams);
175
+ }
176
+ it('T1: allows callback without iss when authorization_response_iss_parameter_supported is omitted in metadata (MCP Spec Row 4)', async () => {
177
+ const token = await runOAuthFlowWithMetadata({
178
+ // Notion-style metadata: issuer is present, but authorization_response_iss_parameter_supported is omitted
179
+ }, {
180
+ // Notion callback: returns code and state, omits iss
181
+ });
182
+ expect(token.accessToken).toBe('notion_access_token_123');
183
+ });
184
+ it('T2: validates iss when authorization_response_iss_parameter_supported is true and iss is present (MCP Spec Row 1)', async () => {
185
+ // Matching iss succeeds
186
+ const token = await runOAuthFlowWithMetadata({
187
+ authorization_response_iss_parameter_supported: true,
188
+ }, {
189
+ iss: expectedIssuer,
190
+ });
191
+ expect(token.accessToken).toBe('notion_access_token_123');
192
+ // Mismatched iss is rejected
193
+ await expect(runOAuthFlowWithMetadata({
194
+ authorization_response_iss_parameter_supported: true,
195
+ }, {
196
+ iss: mismatchIssuer,
197
+ })).rejects.toThrow(/Issuer mismatch in authorization response/);
198
+ });
199
+ it('T3: rejects callback without iss when authorization_response_iss_parameter_supported is true (MCP Spec Row 2)', async () => {
200
+ await expect(runOAuthFlowWithMetadata({
201
+ authorization_response_iss_parameter_supported: true,
202
+ }, {
203
+ // Callback omits iss even though server declared support
204
+ })).rejects.toThrow('Missing "iss" parameter in authorization response per RFC 9207');
205
+ });
206
+ it('T4: validates iss when present even if authorization_response_iss_parameter_supported is omitted or false (MCP Spec Row 3)', async () => {
207
+ // Mismatched iss when authorization_response_iss_parameter_supported is omitted -> rejected
208
+ await expect(runOAuthFlowWithMetadata({}, {
209
+ iss: mismatchIssuer,
210
+ })).rejects.toThrow(/Issuer mismatch in authorization response/);
211
+ // Mismatched iss when authorization_response_iss_parameter_supported is false -> rejected
212
+ await expect(runOAuthFlowWithMetadata({
213
+ authorization_response_iss_parameter_supported: false,
214
+ }, {
215
+ iss: mismatchIssuer,
216
+ })).rejects.toThrow(/Issuer mismatch in authorization response/);
217
+ // Matching iss when authorization_response_iss_parameter_supported is omitted -> succeeds
218
+ const token = await runOAuthFlowWithMetadata({}, {
219
+ iss: expectedIssuer,
220
+ });
221
+ expect(token.accessToken).toBe('notion_access_token_123');
222
+ });
223
+ it('T5: accepts an iss-less callback at startCallbackServer level when expectedIssuer is passed with requireIssInResponse=false', async () => {
224
+ // When the discovered metadata does not advertise iss support, the provider
225
+ // passes requireIssInResponse=false. A callback omitting iss (like Notion
226
+ // MCP) must then resolve rather than reject.
227
+ const server = startCallbackServer('repro-state', undefined, expectedIssuer, false);
228
+ const port = await server.port;
229
+ const responsePromise = server.response;
230
+ const res = await realFetch(`http://localhost:${port}${REDIRECT_PATH}?code=notion-code-789&state=repro-state`);
231
+ expect(res.status).toBe(200);
232
+ const authResponse = await responsePromise;
233
+ expect(authResponse.code).toBe('notion-code-789');
234
+ expect(authResponse.state).toBe('repro-state');
235
+ expect(authResponse.iss).toBeUndefined();
236
+ });
237
+ it('T6: requires iss when issuer is configured explicitly without discovery metadata, unless authorizationResponseIssParameterSupported is false', async () => {
238
+ // Explicit issuer in settings.json, no discovery metadata, callback omits
239
+ // iss -> rejected (documented strict behavior is preserved).
240
+ await expect(runOAuthFlowWithExplicitConfig({}, {})).rejects.toThrow('Missing "iss" parameter in authorization response per RFC 9207');
241
+ // Explicit issuer with a matching iss in the callback -> succeeds.
242
+ const strictToken = await runOAuthFlowWithExplicitConfig({}, { iss: expectedIssuer });
243
+ expect(strictToken.accessToken).toBe('notion_access_token_123');
244
+ // Explicit issuer with the opt-out flag set to false and no iss -> succeeds.
245
+ const optOutToken = await runOAuthFlowWithExplicitConfig({ authorizationResponseIssParameterSupported: false }, {});
246
+ expect(optOutToken.accessToken).toBe('notion_access_token_123');
247
+ // The opt-out flag never disables the comparison when iss is present.
248
+ await expect(runOAuthFlowWithExplicitConfig({ authorizationResponseIssParameterSupported: false }, { iss: mismatchIssuer })).rejects.toThrow(/Issuer mismatch in authorization response/);
249
+ });
250
+ });
251
+ //# sourceMappingURL=oauth-callback-iss-handling.test.js.map