Compare commits

...

35 Commits

Author SHA1 Message Date
Michael Bleigh d564eb7ffa refactor(core): make LegacyAgentSession dependencies optional
Allows LegacyAgentProtocol to fall back to the global Config object to resolve its dependencies, making it easier to instantiate in the CLI.
2026-04-03 14:05:12 -07:00
Michael Bleigh 430a52fc27 fix: use agentSessionInteractiveEnabled 2026-04-03 14:04:52 -07:00
Michael Bleigh 72ec782fe0 fix: trim PR and move interactive flag to ADK settings
- Reverted unrelated changes to nonInteractiveCli.test.ts and snapshots.
- Reverted unrelated history truncation config additions.
- Renamed useAgentProtocol to interactiveAgentSessionEnabled and moved it to the ADKSettings group as requested.
2026-04-03 13:44:36 -07:00
Michael Bleigh 9b40e96f80 fix: restore nonInteractiveCli.ts and update AgentSession logic
- Reverted packages/cli/src/nonInteractiveCli.ts to the main branch state, restoring the fallback while(true) loop behavior.
- Added debugLogger.error call for unknown event types in the AgentSession event loop.
2026-04-03 13:33:10 -07:00
Michael Bleigh 8a00e41ba8 fix: address PR feedback for non-interactive and event translator
- Restore 'never' return type for runTerminalExitHandler

- Add TODO for usage event and debugLogger for unknown events

- Check for 'status' in isStructuredError type guard

- Fix unused property build error for experimentalAgentHistorySummarization
2026-04-03 09:44:35 -07:00
Michael Bleigh 35905c29ab Merge branch 'main' of github.com:google-gemini/gemini-cli into mb/agents/01-core-types-fixes 2026-04-03 09:36:50 -07:00
Michael Bleigh 839c6da07c Merge branch 'main' into mb/agents/01-core-types-fixes 2026-04-02 17:10:53 -07:00
Michael Bleigh 6c2f005428 Merge branch 'pr4-cli' into mb/agents/01-core-types-fixes 2026-04-02 11:04:29 -07:00
Adam Weidman 05baea6627 fix(cli): fix tests and remove unused imports in non-interactive agent session 2026-04-01 14:36:46 -07:00
Adam Weidman ab2f9bd9a7 Merge branch 'origin/main' into pr4-cli 2026-04-01 14:22:11 -07:00
Adam Weidman 9dfe9eeb89 feat(cli): split nonInteractiveCli and implement flag toggle (proper fix) 2026-04-01 12:39:21 -07:00
Adam Weidman cd34c9666e fixes 2026-04-01 10:40:11 -07:00
Adam Weidman bb459defe9 fix(core): preserve first-turn display content 2026-04-01 10:40:11 -07:00
Adam Weidman 1088e1febf fix(cli): preserve max-turns non-interactive parity 2026-04-01 10:40:11 -07:00
Adam Weidman 6795567d28 fix(cli): align non-interactive loop warning handling 2026-04-01 10:40:11 -07:00
Adam Weidman 7c7150f487 !feat(cli): harden non-interactive agent session handling 2026-04-01 10:40:11 -07:00
Adam Weidman 24a4a0da1e !feat(cli): address non-interactive review follow-ups 2026-04-01 10:40:11 -07:00
Adam Weidman f7b08330f7 !feat(cli): adopt protocol-backed agent session streaming 2026-04-01 10:40:11 -07:00
Adam Weidman c3b5bcb84c fix(cli): consume the stream returned by send 2026-04-01 10:40:10 -07:00
Adam Weidman 6d8cee7620 refactor(cli): handle max turns from stream end 2026-04-01 10:40:10 -07:00
Adam Weidman f34b146a05 feat(cli): migrate nonInteractiveCli to LegacyAgentSession 2026-04-01 10:40:10 -07:00
Adam Weidman e02072abf8 feat(config): add experimental.adk.agentSessionNoninteractiveEnabled setting 2026-04-01 10:33:49 -07:00
Michael Bleigh 52f2479007 feat(core): add agent protocol UI types and experimental flag
Also fixes a minor type error in nonInteractiveCli and mock ToolRegistry in tests that the new protocol exposes.
2026-03-30 14:08:55 -07:00
Michael Bleigh 3b0906f319 Merge branch 'main' into pr4-cli 2026-03-30 12:39:30 -07:00
Adam Weidman 8e9961a791 fixes 2026-03-29 17:43:07 -04:00
Michael Bleigh 6a80311a37 Merge branch 'main' into pr4-cli 2026-03-27 11:08:15 -07:00
Adam Weidman 6fb7bcf868 fix(core): preserve first-turn display content 2026-03-26 11:51:15 -04:00
Adam Weidman 56656dfbc9 fix(cli): preserve max-turns non-interactive parity 2026-03-23 18:47:11 -04:00
Adam Weidman 93322430a0 fix(cli): align non-interactive loop warning handling 2026-03-23 18:38:56 -04:00
Adam Weidman 25c5d44678 !feat(cli): harden non-interactive agent session handling 2026-03-23 18:38:56 -04:00
Adam Weidman 2dfe237738 !feat(cli): address non-interactive review follow-ups 2026-03-23 18:38:56 -04:00
Adam Weidman f77e4716fa !feat(cli): adopt protocol-backed agent session streaming 2026-03-23 18:38:56 -04:00
Adam Weidman 3e0b8aa958 fix(cli): consume the stream returned by send 2026-03-23 18:38:56 -04:00
Adam Weidman b268e93a1d refactor(cli): handle max turns from stream end 2026-03-23 18:38:56 -04:00
Adam Weidman 218adcd6c3 feat(cli): migrate nonInteractiveCli to LegacyAgentSession 2026-03-23 18:38:56 -04:00
10 changed files with 97 additions and 23 deletions
+6
View File
@@ -1605,6 +1605,12 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `false`
- **Requires restart:** Yes
- **`experimental.adk.agentSessionInteractiveEnabled`** (boolean):
- **Description:** Enable the agent session implementation for the interactive
CLI.
- **Default:** `false`
- **Requires restart:** Yes
- **`experimental.enableAgents`** (boolean):
- **Description:** Enable local and remote subagents.
- **Default:** `true`
+10
View File
@@ -1970,6 +1970,16 @@ const SETTINGS_SCHEMA = {
description: 'Enable non-interactive agent sessions.',
showInDialog: false,
},
agentSessionInteractiveEnabled: {
type: 'boolean',
label: 'Interactive Agent Session Enabled',
category: 'Experimental',
requiresRestart: true,
default: false,
description:
'Enable the agent session implementation for the interactive CLI.',
showInDialog: false,
},
},
},
enableAgents: {
@@ -37,6 +37,7 @@ import {
LegacyAgentSession,
ToolErrorType,
geminiPartsToContentParts,
debugLogger,
} from '@google/gemini-cli-core';
import type { Part } from '@google/genai';
@@ -599,6 +600,7 @@ export async function runNonInteractive({
// Explicitly ignore these non-interactive events
break;
default:
debugLogger.error('Unknown agent event type:', event);
event satisfies never;
break;
}
@@ -78,7 +78,7 @@ exports[`<ToolGroupMessage /> > Golden Snapshots > renders header when scrolled
exports[`<ToolGroupMessage /> > Golden Snapshots > renders mixed tool calls including update_topic 1`] = `
"
Testing Topic: This is the description
Testing Topic: This is the summary
╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ read_file Read a file │
@@ -142,12 +142,6 @@ exports[`<ToolGroupMessage /> > Golden Snapshots > renders two tool groups where
"
`;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders update_topic tool call using TopicMessage > update_topic_tool 1`] = `
"
Testing Topic: This is the description
"
`;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders with limited terminal height 1`] = `
"╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ tool-with-result Tool with output │
@@ -432,6 +432,7 @@ function isStructuredError(error: unknown): error is StructuredError {
return (
typeof error === 'object' &&
error !== null &&
'status' in error &&
'message' in error &&
typeof error.message === 'string'
);
@@ -17,6 +17,9 @@ import type {
ToolCallRequestInfo,
} from '../scheduler/types.js';
import { CoreToolCallStatus } from '../scheduler/types.js';
import type { GeminiClient } from '../core/client.js';
import type { Scheduler } from '../scheduler/scheduler.js';
import type { Config } from '../config/config.js';
// ---------------------------------------------------------------------------
// Mock helpers
@@ -24,7 +27,7 @@ import { CoreToolCallStatus } from '../scheduler/types.js';
function createMockDeps(
overrides?: Partial<LegacyAgentSessionDeps>,
): LegacyAgentSessionDeps {
): Required<LegacyAgentSessionDeps> {
const mockClient = {
sendMessageStream: vi.fn(),
getChat: vi.fn().mockReturnValue({
@@ -40,18 +43,22 @@ function createMockDeps(
const mockConfig = {
getMaxSessionTurns: vi.fn().mockReturnValue(-1),
getModel: vi.fn().mockReturnValue('gemini-2.5-pro'),
getGeminiClient: vi.fn().mockReturnValue(mockClient),
getMessageBus: vi.fn().mockImplementation(() => ({
subscribe: vi.fn(),
unsubscribe: vi.fn(),
})),
};
return {
client: mockClient as unknown as LegacyAgentSessionDeps['client'],
scheduler: mockScheduler as unknown as LegacyAgentSessionDeps['scheduler'],
config: mockConfig as unknown as LegacyAgentSessionDeps['config'],
client: mockClient as unknown as GeminiClient,
scheduler: mockScheduler as unknown as Scheduler,
config: mockConfig as unknown as Config,
promptId: 'test-prompt',
streamId: 'test-stream',
getPreferredEditor: vi.fn().mockReturnValue(undefined),
...overrides,
};
} as Required<LegacyAgentSessionDeps>;
}
async function* makeStream(
@@ -129,7 +136,7 @@ async function collectEvents(
// ---------------------------------------------------------------------------
describe('LegacyAgentSession', () => {
let deps: LegacyAgentSessionDeps;
let deps: Required<LegacyAgentSessionDeps>;
beforeEach(() => {
deps = createMockDeps();
@@ -14,10 +14,11 @@ import type { Part } from '@google/genai';
import type { GeminiClient } from '../core/client.js';
import type { Config } from '../config/config.js';
import type { ToolCallRequestInfo } from '../scheduler/types.js';
import type { Scheduler } from '../scheduler/scheduler.js';
import { Scheduler } from '../scheduler/scheduler.js';
import { recordToolCallInteractions } from '../code_assist/telemetry.js';
import { ToolErrorType, isFatalToolError } from '../tools/tool-error.js';
import { debugLogger } from '../utils/debugLogger.js';
import type { EditorType } from '../utils/editor.js';
import {
buildToolResponseData,
contentPartsToGeminiParts,
@@ -45,14 +46,15 @@ function isAbortLikeError(err: unknown): boolean {
}
export interface LegacyAgentSessionDeps {
client: GeminiClient;
scheduler: Scheduler;
config: Config;
promptId: string;
client?: GeminiClient;
scheduler?: Scheduler;
promptId?: string;
streamId?: string;
getPreferredEditor?: () => EditorType | undefined;
}
class LegacyAgentProtocol implements AgentProtocol {
export class LegacyAgentProtocol implements AgentProtocol {
private _events: AgentEvent[] = [];
private _subscribers = new Set<(event: AgentEvent) => void>();
private _translationState: TranslationState;
@@ -69,10 +71,16 @@ class LegacyAgentProtocol implements AgentProtocol {
constructor(deps: LegacyAgentSessionDeps) {
this._translationState = createTranslationState(deps.streamId);
this._nextStreamIdOverride = deps.streamId;
this._client = deps.client;
this._scheduler = deps.scheduler;
this._config = deps.config;
this._promptId = deps.promptId;
this._client = deps.client ?? deps.config.getGeminiClient();
this._promptId = deps.promptId ?? deps.config.promptId ?? '';
this._scheduler =
deps.scheduler ??
new Scheduler({
context: deps.config,
schedulerId: 'legacy-agent-scheduler',
getPreferredEditor: deps.getPreferredEditor ?? (() => undefined),
});
}
get events(): readonly AgentEvent[] {
+31
View File
@@ -4,6 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import type { Kind } from '../tools/tools.js';
export type WithMeta = { _meta?: Record<string, unknown> };
export type Unsubscribe = () => void;
@@ -180,6 +182,16 @@ export interface ToolRequest {
name: string;
/** The arguments for the tool. */
args: Record<string, unknown>;
/** UI specific metadata */
_meta?: {
legacyState?: {
displayName?: string;
isOutputMarkdown?: boolean;
description?: string;
kind?: Kind;
};
[key: string]: unknown;
};
}
/**
@@ -192,6 +204,18 @@ export interface ToolUpdate {
displayContent?: ContentPart[];
content?: ContentPart[];
data?: Record<string, unknown>;
/** UI specific metadata */
_meta?: {
legacyState?: {
status?: string;
progressMessage?: string;
progress?: number;
progressTotal?: number;
pid?: number;
description?: string;
};
[key: string]: unknown;
};
}
export interface ToolResponse {
@@ -205,6 +229,13 @@ export interface ToolResponse {
data?: Record<string, unknown>;
/** When true, the tool call encountered an error that will be sent to the model. */
isError?: boolean;
/** UI specific metadata */
_meta?: {
legacyState?: {
outputFile?: string;
};
[key: string]: unknown;
};
}
export type ElicitationRequest = {
+8
View File
@@ -225,6 +225,7 @@ export interface GemmaModelRouterSettings {
export interface ADKSettings {
agentSessionNoninteractiveEnabled?: boolean;
agentSessionInteractiveEnabled?: boolean;
}
export interface ExtensionSetting {
@@ -892,6 +893,7 @@ export class Config implements McpContext, AgentLoopContext {
private readonly gemmaModelRouter: GemmaModelRouterSettings;
private readonly agentSessionNoninteractiveEnabled: boolean;
private readonly agentSessionInteractiveEnabled: boolean;
private readonly continueOnFailedApiCall: boolean;
private readonly retryFetchErrors: boolean;
@@ -1314,6 +1316,8 @@ export class Config implements McpContext, AgentLoopContext {
this.agentSessionNoninteractiveEnabled =
params.adk?.agentSessionNoninteractiveEnabled ?? false;
this.agentSessionInteractiveEnabled =
params.adk?.agentSessionInteractiveEnabled ?? false;
this.retryFetchErrors = params.retryFetchErrors ?? true;
this.maxAttempts = Math.min(
params.maxAttempts ?? DEFAULT_MAX_ATTEMPTS,
@@ -3377,6 +3381,10 @@ export class Config implements McpContext, AgentLoopContext {
return this.agentSessionNoninteractiveEnabled;
}
getAgentSessionInteractiveEnabled(): boolean {
return this.agentSessionInteractiveEnabled;
}
/**
* Get override settings for a specific agent.
* Reads from agents.overrides.<agentName>.
+7
View File
@@ -2775,6 +2775,13 @@
"markdownDescription": "Enable non-interactive agent sessions.\n\n- Category: `Experimental`\n- Requires restart: `yes`\n- Default: `false`",
"default": false,
"type": "boolean"
},
"agentSessionInteractiveEnabled": {
"title": "Interactive Agent Session Enabled",
"description": "Enable the agent session implementation for the interactive CLI.",
"markdownDescription": "Enable the agent session implementation for the interactive CLI.\n\n- Category: `Experimental`\n- Requires restart: `yes`\n- Default: `false`",
"default": false,
"type": "boolean"
}
},
"additionalProperties": false