diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts index 6a4bd09470..8059cac435 100755 --- a/packages/cli/src/config/config.ts +++ b/packages/cli/src/config/config.ts @@ -699,7 +699,6 @@ export async function loadCliConfig( await resolveWorkspacePolicyState({ cwd, trustedFolder, - interactive, }); const policyEngineConfig = await createPolicyEngineConfig( diff --git a/packages/cli/src/config/policy.test.ts b/packages/cli/src/config/policy.test.ts index 1a773d56a7..38481d9593 100644 --- a/packages/cli/src/config/policy.test.ts +++ b/packages/cli/src/config/policy.test.ts @@ -9,7 +9,7 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; import * as os from 'node:os'; import { resolveWorkspacePolicyState } from './policy.js'; -import { writeToStderr } from '@google/gemini-cli-core'; +import { debugLogger } from '@google/gemini-cli-core'; // Mock debugLogger to avoid noise in test output vi.mock('@google/gemini-cli-core', async (importOriginal) => { @@ -54,7 +54,6 @@ describe('resolveWorkspacePolicyState', () => { const result = await resolveWorkspacePolicyState({ cwd: workspaceDir, trustedFolder: false, - interactive: true, }); expect(result).toEqual({ @@ -68,28 +67,18 @@ describe('resolveWorkspacePolicyState', () => { fs.mkdirSync(policiesDir, { recursive: true }); fs.writeFileSync(path.join(policiesDir, 'policy.toml'), 'rules = []'); - // First call to establish integrity (interactive accept) + // First call will now auto-accept const firstResult = await resolveWorkspacePolicyState({ cwd: workspaceDir, trustedFolder: true, - interactive: true, }); - expect(firstResult.policyUpdateConfirmationRequest).toBeDefined(); + expect(firstResult.workspacePoliciesDir).toBe(policiesDir); + expect(firstResult.policyUpdateConfirmationRequest).toBeUndefined(); - // Establish integrity manually as if accepted - const { PolicyIntegrityManager } = await import('@google/gemini-cli-core'); - const integrityManager = new PolicyIntegrityManager(); - await integrityManager.acceptIntegrity( - 'workspace', - workspaceDir, - firstResult.policyUpdateConfirmationRequest!.newHash, - ); - - // Second call should match + // Second call should also match const result = await resolveWorkspacePolicyState({ cwd: workspaceDir, trustedFolder: true, - interactive: true, }); expect(result.workspacePoliciesDir).toBe(policiesDir); @@ -100,30 +89,26 @@ describe('resolveWorkspacePolicyState', () => { const result = await resolveWorkspacePolicyState({ cwd: workspaceDir, trustedFolder: true, - interactive: true, }); expect(result.workspacePoliciesDir).toBeUndefined(); expect(result.policyUpdateConfirmationRequest).toBeUndefined(); }); - it('should return confirmation request if changed in interactive mode', async () => { + it('should auto-accept if changed in interactive mode', async () => { fs.mkdirSync(policiesDir, { recursive: true }); fs.writeFileSync(path.join(policiesDir, 'policy.toml'), 'rules = []'); const result = await resolveWorkspacePolicyState({ cwd: workspaceDir, trustedFolder: true, - interactive: true, }); - expect(result.workspacePoliciesDir).toBeUndefined(); - expect(result.policyUpdateConfirmationRequest).toEqual({ - scope: 'workspace', - identifier: workspaceDir, - policyDir: policiesDir, - newHash: expect.any(String), - }); + expect(result.workspacePoliciesDir).toBe(policiesDir); + expect(result.policyUpdateConfirmationRequest).toBeUndefined(); + expect(debugLogger.warn).toHaveBeenCalledWith( + expect.stringContaining('Automatically accepting and loading'), + ); }); it('should warn and auto-accept if changed in non-interactive mode', async () => { @@ -133,12 +118,11 @@ describe('resolveWorkspacePolicyState', () => { const result = await resolveWorkspacePolicyState({ cwd: workspaceDir, trustedFolder: true, - interactive: false, }); expect(result.workspacePoliciesDir).toBe(policiesDir); expect(result.policyUpdateConfirmationRequest).toBeUndefined(); - expect(writeToStderr).toHaveBeenCalledWith( + expect(debugLogger.warn).toHaveBeenCalledWith( expect.stringContaining('Automatically accepting and loading'), ); }); @@ -152,7 +136,6 @@ describe('resolveWorkspacePolicyState', () => { const result = await resolveWorkspacePolicyState({ cwd: tempDir, trustedFolder: true, - interactive: true, }); expect(result.workspacePoliciesDir).toBeUndefined(); @@ -176,9 +159,7 @@ describe('resolveWorkspacePolicyState', () => { const result = await resolveWorkspacePolicyState({ cwd: symlinkDir, trustedFolder: true, - interactive: true, }); - expect(result.workspacePoliciesDir).toBeUndefined(); expect(result.policyUpdateConfirmationRequest).toBeUndefined(); } finally { diff --git a/packages/cli/src/config/policy.ts b/packages/cli/src/config/policy.ts index 3b85d0b4b6..1f18ed726b 100644 --- a/packages/cli/src/config/policy.ts +++ b/packages/cli/src/config/policy.ts @@ -16,7 +16,7 @@ import { IntegrityStatus, Storage, type PolicyUpdateConfirmationRequest, - writeToStderr, + debugLogger, } from '@google/gemini-cli-core'; import { type Settings } from './settings.js'; @@ -57,14 +57,14 @@ export interface WorkspacePolicyState { export async function resolveWorkspacePolicyState(options: { cwd: string; trustedFolder: boolean; - interactive: boolean; }): Promise { - const { cwd, trustedFolder, interactive } = options; + const { cwd, trustedFolder } = options; let workspacePoliciesDir: string | undefined; - let policyUpdateConfirmationRequest: + // TODO: Restore policyUpdateConfirmationRequest when re-enabling interactive policy acceptance. + const policyUpdateConfirmationRequest: | PolicyUpdateConfirmationRequest - | undefined; + | undefined = undefined; if (trustedFolder) { const storage = new Storage(cwd); @@ -91,25 +91,20 @@ export async function resolveWorkspacePolicyState(options: { ) { // No workspace policies found workspacePoliciesDir = undefined; - } else if (interactive) { - // Policies changed or are new, and we are in interactive mode - policyUpdateConfirmationRequest = { - scope: 'workspace', - identifier: cwd, - policyDir: potentialWorkspacePoliciesDir, - newHash: integrityResult.hash, - }; } else { - // Non-interactive mode: warn and automatically accept/load + // Policies changed or are new. + // Automatically accept and load for now to reduce friction. + // We keep the infrastructure (PolicyUpdateConfirmationRequest etc.) + // but bypass the interactive dialog. await integrityManager.acceptIntegrity( 'workspace', cwd, integrityResult.hash, ); workspacePoliciesDir = potentialWorkspacePoliciesDir; - // debugLogger.warn here doesn't show up in the terminal. It is showing up only in debug mode on the debug console - writeToStderr( - 'WARNING: Workspace policies changed or are new. Automatically accepting and loading them in non-interactive mode.\n', + + debugLogger.warn( + 'Workspace policies changed or are new. Automatically accepting and loading them.', ); } } diff --git a/packages/cli/src/config/workspace-policy-cli.test.ts b/packages/cli/src/config/workspace-policy-cli.test.ts index 98cbe05bce..c39a7df14c 100644 --- a/packages/cli/src/config/workspace-policy-cli.test.ts +++ b/packages/cli/src/config/workspace-policy-cli.test.ts @@ -164,7 +164,7 @@ describe('Workspace-Level Policy CLI Integration', () => { ); }); - it('should set policyUpdateConfirmationRequest if integrity MISMATCH in interactive mode', async () => { + it('should automatically accept if integrity MISMATCH in interactive mode', async () => { vi.mocked(isWorkspaceTrusted).mockReturnValue({ isTrusted: true, source: 'file', @@ -186,24 +186,23 @@ describe('Workspace-Level Policy CLI Integration', () => { cwd: MOCK_CWD, }); - expect(config.getPolicyUpdateConfirmationRequest()).toEqual({ - scope: 'workspace', - identifier: MOCK_CWD, - policyDir: expect.stringContaining(path.join('.gemini', 'policies')), - newHash: 'new-hash', - }); - // In interactive mode without accept flag, it waits for user confirmation (handled by UI), - // so it currently DOES NOT pass the directory to createPolicyEngineConfig yet. - // The UI will handle the confirmation and reload/update. + expect(config.getPolicyUpdateConfirmationRequest()).toBeUndefined(); + expect(mockAcceptIntegrity).toHaveBeenCalledWith( + 'workspace', + MOCK_CWD, + 'new-hash', + ); expect(ServerConfig.createPolicyEngineConfig).toHaveBeenCalledWith( expect.objectContaining({ - workspacePoliciesDir: undefined, + workspacePoliciesDir: expect.stringContaining( + path.join('.gemini', 'policies'), + ), }), expect.anything(), ); }); - it('should set policyUpdateConfirmationRequest if integrity is NEW with files (first time seen) in interactive mode', async () => { + it('should automatically accept if integrity is NEW with files (first time seen) in interactive mode', async () => { vi.mocked(isWorkspaceTrusted).mockReturnValue({ isTrusted: true, source: 'file', @@ -222,16 +221,18 @@ describe('Workspace-Level Policy CLI Integration', () => { cwd: MOCK_CWD, }); - expect(config.getPolicyUpdateConfirmationRequest()).toEqual({ - scope: 'workspace', - identifier: MOCK_CWD, - policyDir: expect.stringContaining(path.join('.gemini', 'policies')), - newHash: 'new-hash', - }); + expect(config.getPolicyUpdateConfirmationRequest()).toBeUndefined(); + expect(mockAcceptIntegrity).toHaveBeenCalledWith( + 'workspace', + MOCK_CWD, + 'new-hash', + ); expect(ServerConfig.createPolicyEngineConfig).toHaveBeenCalledWith( expect.objectContaining({ - workspacePoliciesDir: undefined, + workspacePoliciesDir: expect.stringContaining( + path.join('.gemini', 'policies'), + ), }), expect.anything(), );