Compare commits

...

12 Commits

Author SHA1 Message Date
Jack Wotherspoon 290550358a chore: disable ask_user tool (#18215) 2026-02-03 14:14:05 -05:00
gemini-cli-robot 8349cc0d10 chore(release): v0.27.0-preview.5 2026-02-03 18:09:43 +00:00
gemini-cli-robot f55b52ab58 fix(patch): cherry-pick 1b274b0 to release/v0.27.0-preview.4-pr-17852 to patch version v0.27.0-preview.4 and create version 0.27.0-preview.5 (#18207)
Co-authored-by: Gaurav <39389231+gsquared94@users.noreply.github.com>
2026-02-03 17:46:40 +00:00
gemini-cli-robot fd85363d13 chore(release): v0.27.0-preview.4 2026-02-02 19:07:03 +00:00
gemini-cli-robot 7ce9aa518e fix(patch): cherry-pick a362b7b to release/v0.27.0-preview.3-pr-17975 to patch version v0.27.0-preview.3 and create version 0.27.0-preview.4 (#18116)
Co-authored-by: Sehoon Shon <sshon@google.com>
2026-02-02 13:43:45 -05:00
gemini-cli-robot 68bde280d0 chore(release): v0.27.0-preview.3 2026-01-29 22:35:08 +00:00
gemini-cli-robot 09800ea74e fix(patch): cherry-pick 42eedc9 to release/v0.27.0-preview.2-pr-17826 to patch version v0.27.0-preview.2 and create version 0.27.0-preview.3 (#17907)
Co-authored-by: christine betts <chrstn@uw.edu>
2026-01-29 22:10:22 +00:00
gemini-cli-robot 9cf06a3e00 chore(release): v0.27.0-preview.2 2026-01-29 17:47:54 +00:00
gemini-cli-robot 3c33fcb69c fix(patch): cherry-pick 6d36219 to release/v0.27.0-preview.1-pr-17810 to patch version v0.27.0-preview.1 and create version 0.27.0-preview.2 (#17874)
Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
2026-01-29 09:27:22 -08:00
gemini-cli-robot 6f4a2a2c2a chore(release): v0.27.0-preview.1 2026-01-28 23:01:31 +00:00
gemini-cli-robot 9954615542 fix(patch): cherry-pick 4d4e16c to release/v0.27.0-preview.0-pr-17785 to patch version v0.27.0-preview.0 and create version 0.27.0-preview.1 (#17823)
Co-authored-by: christine betts <chrstn@uw.edu>
2026-01-28 22:34:10 +00:00
gemini-cli-robot c7a044b091 chore(release): v0.27.0-preview.0 2026-01-28 01:44:42 +00:00
27 changed files with 174 additions and 86 deletions
+4 -3
View File
@@ -129,8 +129,9 @@ they appear in the UI.
### HooksConfig
| UI Label | Setting | Description | Default |
| ------------------ | --------------------------- | ------------------------------------------------ | ------- |
| Hook Notifications | `hooksConfig.notifications` | Show visual indicators when hooks are executing. | `true` |
| UI Label | Setting | Description | Default |
| ------------------ | --------------------------- | -------------------------------------------------------------------------------- | ------- |
| Enable Hooks | `hooksConfig.enabled` | Canonical toggle for the hooks system. When disabled, no hooks will be executed. | `true` |
| Hook Notifications | `hooksConfig.notifications` | Show visual indicators when hooks are executing. | `true` |
<!-- SETTINGS-AUTOGEN:END -->
+1
View File
@@ -902,6 +902,7 @@ their corresponding top-level category object in your `settings.json` file.
- **Description:** Canonical toggle for the hooks system. When disabled, no
hooks will be executed.
- **Default:** `true`
- **Requires restart:** Yes
- **`hooksConfig.disabled`** (array):
- **Description:** List of hook names (commands) that should be disabled.
+8 -31
View File
@@ -1,12 +1,12 @@
{
"name": "@google/gemini-cli",
"version": "0.27.0-nightly.20260121.97aac696f",
"version": "0.27.0-preview.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@google/gemini-cli",
"version": "0.27.0-nightly.20260121.97aac696f",
"version": "0.27.0-preview.5",
"workspaces": [
"packages/*"
],
@@ -2251,7 +2251,6 @@
"integrity": "sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@octokit/auth-token": "^6.0.0",
"@octokit/graphql": "^9.0.2",
@@ -2432,7 +2431,6 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
"license": "Apache-2.0",
"peer": true,
"engines": {
"node": ">=8.0.0"
}
@@ -2466,7 +2464,6 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz",
"integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@opentelemetry/semantic-conventions": "^1.29.0"
},
@@ -2835,7 +2832,6 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz",
"integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@opentelemetry/core": "2.0.1",
"@opentelemetry/semantic-conventions": "^1.29.0"
@@ -2869,7 +2865,6 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz",
"integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@opentelemetry/core": "2.0.1",
"@opentelemetry/resources": "2.0.1"
@@ -2922,7 +2917,6 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz",
"integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@opentelemetry/core": "2.0.1",
"@opentelemetry/resources": "2.0.1",
@@ -4128,7 +4122,6 @@
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.0.2"
}
@@ -4406,7 +4399,6 @@
"integrity": "sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.35.0",
"@typescript-eslint/types": "8.35.0",
@@ -5399,7 +5391,6 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -8409,7 +8400,6 @@
"integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -8950,7 +8940,6 @@
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"license": "MIT",
"peer": true,
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.1",
@@ -10552,7 +10541,6 @@
"resolved": "https://registry.npmjs.org/@jrichman/ink/-/ink-6.4.8.tgz",
"integrity": "sha512-v0thcXIKl9hqF/1w4HqA6MKxIcMoWSP3YtEZIAA+eeJngXpN5lGnMkb6rllB7FnOdwyEyYaFTcu1ZVr4/JZpWQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@alcalzone/ansi-tokenize": "^0.2.1",
"ansi-escapes": "^7.0.0",
@@ -14311,7 +14299,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -14322,7 +14309,6 @@
"integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"shell-quote": "^1.6.1",
"ws": "^7"
@@ -16559,7 +16545,6 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -16783,8 +16768,7 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD",
"peer": true
"license": "0BSD"
},
"node_modules/tsx": {
"version": "4.20.3",
@@ -16792,7 +16776,6 @@
"integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "~0.25.0",
"get-tsconfig": "^4.7.5"
@@ -16965,7 +16948,6 @@
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"devOptional": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -17173,7 +17155,6 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz",
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@@ -17287,7 +17268,6 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -17300,7 +17280,6 @@
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz",
"integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/chai": "^5.2.2",
"@vitest/expect": "3.2.4",
@@ -18005,7 +17984,6 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
@@ -18021,7 +17999,7 @@
},
"packages/a2a-server": {
"name": "@google/gemini-cli-a2a-server",
"version": "0.27.0-nightly.20260121.97aac696f",
"version": "0.27.0-preview.5",
"dependencies": {
"@a2a-js/sdk": "^0.3.8",
"@google-cloud/storage": "^7.16.0",
@@ -18077,7 +18055,7 @@
},
"packages/cli": {
"name": "@google/gemini-cli",
"version": "0.27.0-nightly.20260121.97aac696f",
"version": "0.27.0-preview.5",
"license": "Apache-2.0",
"dependencies": {
"@agentclientprotocol/sdk": "^0.12.0",
@@ -18164,7 +18142,7 @@
},
"packages/core": {
"name": "@google/gemini-cli-core",
"version": "0.27.0-nightly.20260121.97aac696f",
"version": "0.27.0-preview.5",
"license": "Apache-2.0",
"dependencies": {
"@a2a-js/sdk": "^0.3.8",
@@ -18300,7 +18278,6 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -18323,7 +18300,7 @@
},
"packages/test-utils": {
"name": "@google/gemini-cli-test-utils",
"version": "0.27.0-nightly.20260121.97aac696f",
"version": "0.27.0-preview.5",
"license": "Apache-2.0",
"dependencies": {
"@google/gemini-cli-core": "file:../core",
@@ -18340,7 +18317,7 @@
},
"packages/vscode-ide-companion": {
"name": "gemini-cli-vscode-ide-companion",
"version": "0.27.0-nightly.20260121.97aac696f",
"version": "0.27.0-preview.5",
"license": "LICENSE",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.23.0",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli",
"version": "0.27.0-nightly.20260121.97aac696f",
"version": "0.27.0-preview.5",
"engines": {
"node": ">=20.0.0"
},
@@ -14,7 +14,7 @@
"url": "git+https://github.com/google-gemini/gemini-cli.git"
},
"config": {
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.27.0-nightly.20260121.97aac696f"
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.27.0-preview.5"
},
"scripts": {
"start": "cross-env NODE_ENV=development node scripts/start.js",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli-a2a-server",
"version": "0.27.0-nightly.20260121.97aac696f",
"version": "0.27.0-preview.5",
"description": "Gemini CLI A2A Server",
"repository": {
"type": "git",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli",
"version": "0.27.0-nightly.20260121.97aac696f",
"version": "0.27.0-preview.5",
"description": "Gemini CLI",
"license": "Apache-2.0",
"repository": {
@@ -26,7 +26,7 @@
"dist"
],
"config": {
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.27.0-nightly.20260121.97aac696f"
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.27.0-preview.5"
},
"dependencies": {
"@agentclientprotocol/sdk": "^0.12.0",
@@ -22,6 +22,7 @@ import {
type ExtensionSetting,
} from '../../config/extensions/extensionSettings.js';
import prompts from 'prompts';
import * as fs from 'node:fs';
const {
mockExtensionManager,
@@ -79,11 +80,15 @@ vi.mock('../../config/settings.js', () => ({
}));
describe('extensions configure command', () => {
let tempWorkspaceDir: string;
beforeEach(() => {
vi.spyOn(debugLogger, 'log');
vi.spyOn(debugLogger, 'error');
vi.clearAllMocks();
tempWorkspaceDir = fs.mkdtempSync('gemini-cli-test-workspace');
vi.spyOn(process, 'cwd').mockReturnValue(tempWorkspaceDir);
// Default behaviors
mockLoadSettings.mockReturnValue({ merged: {} });
mockGetExtensionAndManager.mockResolvedValue({
@@ -141,6 +146,7 @@ describe('extensions configure command', () => {
'TEST_VAR',
promptForSetting,
'user',
tempWorkspaceDir,
);
});
@@ -186,6 +192,7 @@ describe('extensions configure command', () => {
'VAR_1',
promptForSetting,
'user',
tempWorkspaceDir,
);
});
@@ -111,6 +111,7 @@ async function configureSpecificSetting(
settingKey,
promptForSetting,
scope,
process.cwd(),
);
}
@@ -218,6 +219,7 @@ async function configureExtensionSettings(
setting.envVar,
promptForSetting,
scope,
process.cwd(),
);
}
}
@@ -306,6 +306,7 @@ System using model: \${MODEL_NAME}
'MY_VALUE',
mockRequestSetting,
ExtensionSettingScope.USER,
process.cwd(),
);
await extensionManager.restartExtension(extension);
@@ -398,6 +398,35 @@ describe('extensionSettings', () => {
expect(actualContent).toBe('VAR1="a value with spaces"\n');
});
it('should not set sensitive settings if the value is empty during initial setup', async () => {
const config: ExtensionConfig = {
name: 'test-ext',
version: '1.0.0',
settings: [
{
name: 's1',
description: 'd1',
envVar: 'SENSITIVE_VAR',
sensitive: true,
},
],
};
mockRequestSetting.mockResolvedValue('');
await maybePromptForSettings(
config,
'12345',
mockRequestSetting,
undefined,
undefined,
);
const userKeychain = new KeychainTokenStorage(
`Gemini CLI Extensions test-ext 12345`,
);
expect(await userKeychain.getSecret('SENSITIVE_VAR')).toBeNull();
});
it('should not attempt to clear secrets if keychain is unavailable', async () => {
// Arrange
const mockIsAvailable = vi.fn().mockResolvedValue(false);
@@ -738,5 +767,42 @@ describe('extensionSettings', () => {
const lines = actualContent.split('\n').filter((line) => line.length > 0);
expect(lines).toHaveLength(3); // Should only have the three variables
});
it('should delete a sensitive setting if the new value is empty', async () => {
mockRequestSetting.mockResolvedValue('');
await updateSetting(
config,
'12345',
'VAR2',
mockRequestSetting,
ExtensionSettingScope.USER,
tempWorkspaceDir,
);
const userKeychain = new KeychainTokenStorage(
`Gemini CLI Extensions test-ext 12345`,
);
expect(await userKeychain.getSecret('VAR2')).toBeNull();
});
it('should not throw if deleting a non-existent sensitive setting with empty value', async () => {
mockRequestSetting.mockResolvedValue('');
// Ensure it doesn't exist first
const userKeychain = new KeychainTokenStorage(
`Gemini CLI Extensions test-ext 12345`,
);
await userKeychain.deleteSecret('VAR2');
await updateSetting(
config,
'12345',
'VAR2',
mockRequestSetting,
ExtensionSettingScope.USER,
tempWorkspaceDir,
);
// Should complete without error
});
});
});
@@ -112,7 +112,7 @@ export async function maybePromptForSettings(
const nonSensitiveSettings: Record<string, string> = {};
for (const setting of settings) {
const value = allSettings[setting.envVar];
if (value === undefined) {
if (value === undefined || value === '') {
continue;
}
if (setting.sensitive) {
@@ -207,7 +207,7 @@ export async function updateSetting(
settingKey: string,
requestSetting: (setting: ExtensionSetting) => Promise<string>,
scope: ExtensionSettingScope,
workspaceDir?: string,
workspaceDir: string,
): Promise<void> {
const { name: extensionName, settings } = extensionConfig;
if (!settings || settings.length === 0) {
@@ -230,7 +230,15 @@ export async function updateSetting(
);
if (settingToUpdate.sensitive) {
await keychain.setSecret(settingToUpdate.envVar, newValue);
if (newValue) {
await keychain.setSecret(settingToUpdate.envVar, newValue);
} else {
try {
await keychain.deleteSecret(settingToUpdate.envVar);
} catch {
// Ignore if secret does not exist
}
}
return;
}
+2 -2
View File
@@ -1602,11 +1602,11 @@ const SETTINGS_SCHEMA = {
type: 'boolean',
label: 'Enable Hooks',
category: 'Advanced',
requiresRestart: false,
requiresRestart: true,
default: true,
description:
'Canonical toggle for the hooks system. When disabled, no hooks will be executed.',
showInDialog: false,
showInDialog: true,
},
disabled: {
type: 'array',
@@ -305,6 +305,9 @@ describe('clipboardUtils', () => {
});
it('should return null if tool is not yet detected', async () => {
// Unset session type to ensure no tool is detected automatically
delete process.env['XDG_SESSION_TYPE'];
// Don't prime the tool
const result = await clipboardUtils.saveClipboardImage(mockTargetDir);
expect(result).toBe(null);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli-core",
"version": "0.27.0-nightly.20260121.97aac696f",
"version": "0.27.0-preview.5",
"description": "Gemini CLI Core",
"license": "Apache-2.0",
"repository": {
@@ -312,6 +312,32 @@ describe('setupUser for new user', () => {
userTierName: 'paid',
});
});
it('should throw ineligible tier error when onboarding fails and ineligible tiers exist', async () => {
vi.stubEnv('GOOGLE_CLOUD_PROJECT', '');
mockLoad.mockResolvedValue({
allowedTiers: [mockPaidTier],
ineligibleTiers: [
{
reasonCode: 'UNSUPPORTED_LOCATION',
reasonMessage:
'Your current account is not eligible for Gemini Code Assist for individuals because it is not currently available in your location.',
tierId: 'free-tier',
tierName: 'Gemini Code Assist for individuals',
},
],
});
mockOnboardUser.mockResolvedValue({
done: true,
response: {
cloudaicompanionProject: {},
},
});
await expect(setupUser({} as OAuth2Client)).rejects.toThrow(
'Your current account is not eligible for Gemini Code Assist for individuals because it is not currently available in your location.',
);
});
});
describe('setupUser validation', () => {
+21 -8
View File
@@ -7,6 +7,7 @@
import type {
ClientMetadata,
GeminiUserTier,
IneligibleTier,
LoadCodeAssistResponse,
OnboardUserRequest,
} from './types.js';
@@ -35,6 +36,16 @@ export class ValidationCancelledError extends Error {
}
}
export class IneligibleTierError extends Error {
readonly ineligibleTiers: IneligibleTier[];
constructor(ineligibleTiers: IneligibleTier[]) {
const reasons = ineligibleTiers.map((t) => t.reasonMessage).join(', ');
super(reasons);
this.ineligibleTiers = ineligibleTiers;
}
}
export interface UserData {
projectId: string;
userTier: UserTierId;
@@ -127,13 +138,7 @@ export async function setupUser(
}
// If user is not setup for standard tier, inform them about all other tiers they are ineligible for.
if (loadRes.ineligibleTiers && loadRes.ineligibleTiers.length > 0) {
const reasons = loadRes.ineligibleTiers
.map((t) => t.reasonMessage)
.join(', ');
throw new Error(reasons);
}
throw new ProjectIdRequiredError();
throwIneligibleOrProjectIdError(loadRes);
}
return {
projectId: loadRes.cloudaicompanionProject,
@@ -180,7 +185,8 @@ export async function setupUser(
userTierName: tier.name,
};
}
throw new ProjectIdRequiredError();
throwIneligibleOrProjectIdError(loadRes);
}
return {
@@ -190,6 +196,13 @@ export async function setupUser(
};
}
function throwIneligibleOrProjectIdError(res: LoadCodeAssistResponse): never {
if (res.ineligibleTiers && res.ineligibleTiers.length > 0) {
throw new IneligibleTierError(res.ineligibleTiers);
}
throw new ProjectIdRequiredError();
}
function getOnboardTier(res: LoadCodeAssistResponse): GeminiUserTier {
for (const tier of res.allowedTiers || []) {
if (tier.isDefault) {
-2
View File
@@ -33,7 +33,6 @@ import { WriteFileTool } from '../tools/write-file.js';
import { WebFetchTool } from '../tools/web-fetch.js';
import { MemoryTool, setGeminiMdFilename } from '../tools/memoryTool.js';
import { WebSearchTool } from '../tools/web-search.js';
import { AskUserTool } from '../tools/ask-user.js';
import { GeminiClient } from '../core/client.js';
import { BaseLlmClient } from '../core/baseLlmClient.js';
import type { HookDefinition, HookEventName } from '../hooks/types.js';
@@ -2073,7 +2072,6 @@ export class Config {
registerCoreTool(ShellTool, this);
registerCoreTool(MemoryTool);
registerCoreTool(WebSearchTool, this);
registerCoreTool(AskUserTool);
if (this.getUseWriteTodos()) {
registerCoreTool(WriteTodosTool);
}
@@ -185,8 +185,6 @@ The following read-only tools are available in Plan Mode:
### Phase 1: Requirements Understanding
- Analyze the user's request to identify core requirements and constraints
- If critical information is missing or ambiguous, ask clarifying questions using the \`ask_user\` tool
- When using \`ask_user\`, prefer providing multiple-choice options for the user to select from when possible
- Do NOT explore the project or create a plan yet
### Phase 2: Project Exploration
+1 -3
View File
@@ -274,11 +274,10 @@ describe('Core System Prompt (prompts.ts)', () => {
it('should only list available tools in PLAN mode', () => {
vi.mocked(mockConfig.getApprovalMode).mockReturnValue(ApprovalMode.PLAN);
// Only enable a subset of tools, including ask_user
// Only enable a subset of tools
vi.mocked(mockConfig.getToolRegistry().getAllToolNames).mockReturnValue([
'glob',
'read_file',
'ask_user',
]);
const prompt = getCoreSystemPrompt(mockConfig);
@@ -286,7 +285,6 @@ describe('Core System Prompt (prompts.ts)', () => {
// Should include enabled tools
expect(prompt).toContain('`glob`');
expect(prompt).toContain('`read_file`');
expect(prompt).toContain('`ask_user`');
// Should NOT include disabled tools
expect(prompt).not.toContain('`google_web_search`');
@@ -64,12 +64,6 @@ decision = "allow"
priority = 50
modes = ["plan"]
[[rule]]
toolName = "ask_user"
decision = "allow"
priority = 50
modes = ["plan"]
# Allow write_file for .md files in plans directory
[[rule]]
toolName = "write_file"
-3
View File
@@ -6,7 +6,6 @@
import {
ACTIVATE_SKILL_TOOL_NAME,
ASK_USER_TOOL_NAME,
EDIT_TOOL_NAME,
GLOB_TOOL_NAME,
GREP_TOOL_NAME,
@@ -313,8 +312,6 @@ ${options.planModeToolsList}
### Phase 1: Requirements Understanding
- Analyze the user's request to identify core requirements and constraints
- If critical information is missing or ambiguous, ask clarifying questions using the \`${ASK_USER_TOOL_NAME}\` tool
- When using \`${ASK_USER_TOOL_NAME}\`, prefer providing multiple-choice options for the user to select from when possible
- Do NOT explore the project or create a plan yet
### Phase 2: Project Exploration
-2
View File
@@ -46,7 +46,6 @@ export const ALL_BUILTIN_TOOL_NAMES = [
LS_TOOL_NAME,
MEMORY_TOOL_NAME,
ACTIVATE_SKILL_TOOL_NAME,
ASK_USER_TOOL_NAME,
] as const;
/**
@@ -60,7 +59,6 @@ export const PLAN_MODE_TOOLS = [
READ_FILE_TOOL_NAME,
LS_TOOL_NAME,
WEB_SEARCH_TOOL_NAME,
ASK_USER_TOOL_NAME,
] as const;
/**
+11 -11
View File
@@ -101,33 +101,33 @@ describe('retryWithBackoff', () => {
expect(mockFn).toHaveBeenCalledTimes(3);
});
it('should default to 10 maxAttempts if no options are provided', async () => {
// This function will fail more than 10 times to ensure all retries are used.
const mockFn = createFailingFunction(15);
it('should default to 3 maxAttempts if no options are provided', async () => {
// This function will fail more than 3 times to ensure all retries are used.
const mockFn = createFailingFunction(5);
const promise = retryWithBackoff(mockFn);
await Promise.all([
expect(promise).rejects.toThrow('Simulated error attempt 10'),
expect(promise).rejects.toThrow('Simulated error attempt 3'),
vi.runAllTimersAsync(),
]);
expect(mockFn).toHaveBeenCalledTimes(10);
expect(mockFn).toHaveBeenCalledTimes(3);
});
it('should default to 10 maxAttempts if options.maxAttempts is undefined', async () => {
// This function will fail more than 10 times to ensure all retries are used.
const mockFn = createFailingFunction(15);
it('should default to 3 maxAttempts if options.maxAttempts is undefined', async () => {
// This function will fail more than 3 times to ensure all retries are used.
const mockFn = createFailingFunction(5);
const promise = retryWithBackoff(mockFn, { maxAttempts: undefined });
// Expect it to fail with the error from the 10th attempt.
// Expect it to fail with the error from the 3rd attempt.
await Promise.all([
expect(promise).rejects.toThrow('Simulated error attempt 10'),
expect(promise).rejects.toThrow('Simulated error attempt 3'),
vi.runAllTimersAsync(),
]);
expect(mockFn).toHaveBeenCalledTimes(10);
expect(mockFn).toHaveBeenCalledTimes(3);
});
it('should not retry if shouldRetry returns false', async () => {
+1 -1
View File
@@ -40,7 +40,7 @@ export interface RetryOptions {
}
const DEFAULT_RETRY_OPTIONS: RetryOptions = {
maxAttempts: 10,
maxAttempts: 3,
initialDelayMs: 5000,
maxDelayMs: 30000, // 30 seconds
shouldRetryOnError: isRetryableError,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli-test-utils",
"version": "0.27.0-nightly.20260121.97aac696f",
"version": "0.27.0-preview.5",
"private": true,
"main": "src/index.ts",
"license": "Apache-2.0",
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "gemini-cli-vscode-ide-companion",
"displayName": "Gemini CLI Companion",
"description": "Enable Gemini CLI with direct access to your IDE workspace.",
"version": "0.27.0-nightly.20260121.97aac696f",
"version": "0.27.0-preview.5",
"publisher": "google",
"icon": "assets/icon.png",
"repository": {
+1 -1
View File
@@ -1537,7 +1537,7 @@
"enabled": {
"title": "Enable Hooks",
"description": "Canonical toggle for the hooks system. When disabled, no hooks will be executed.",
"markdownDescription": "Canonical toggle for the hooks system. When disabled, no hooks will be executed.\n\n- Category: `Advanced`\n- Requires restart: `no`\n- Default: `true`",
"markdownDescription": "Canonical toggle for the hooks system. When disabled, no hooks will be executed.\n\n- Category: `Advanced`\n- Requires restart: `yes`\n- Default: `true`",
"default": true,
"type": "boolean"
},