mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-06-04 08:04:48 -07:00
fix(cli): revert legacy agent session stream crash on terminal events to match main snapshot
This commit is contained in:
Generated
+3
-6
@@ -486,8 +486,7 @@
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.11.0.tgz",
|
||||
"integrity": "sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==",
|
||||
"license": "(Apache-2.0 AND BSD-3-Clause)",
|
||||
"peer": true
|
||||
"license": "(Apache-2.0 AND BSD-3-Clause)"
|
||||
},
|
||||
"node_modules/@bundled-es-modules/cookie": {
|
||||
"version": "2.0.1",
|
||||
@@ -1490,7 +1489,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.4.tgz",
|
||||
"integrity": "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@grpc/proto-loader": "^0.7.13",
|
||||
"@js-sdsl/ordered-map": "^4.4.2"
|
||||
@@ -7413,8 +7411,7 @@
|
||||
"version": "0.0.1581282",
|
||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1581282.tgz",
|
||||
"integrity": "sha512-nv7iKtNZQshSW2hKzYNr46nM/Cfh5SEvE2oV0/SEGgc9XupIY5ggf84Cz8eJIkBce7S3bmTAauFD6aysMpnqsQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/dezalgo": {
|
||||
"version": "1.0.4",
|
||||
@@ -16250,6 +16247,7 @@
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"dev": true,
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
},
|
||||
@@ -17867,7 +17865,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz",
|
||||
"integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@grpc/proto-loader": "^0.8.0",
|
||||
"@js-sdsl/ordered-map": "^4.4.2"
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
exports[`runNonInteractive > should emit appropriate error event in streaming JSON mode: 'loop detected' 1`] = `
|
||||
"{"type":"init","timestamp":"<TIMESTAMP>","session_id":"test-session-id","model":"test-model"}
|
||||
{"type":"message","timestamp":"<TIMESTAMP>","role":"user","content":"Loop test"}
|
||||
{"type":"result","timestamp":"<TIMESTAMP>","status":"error","error":{"type":"Error","message":"[API Error: Loop detected]"},"stats":{"total_tokens":0,"input_tokens":0,"output_tokens":0,"cached":0,"input":0,"duration_ms":<DURATION>,"tool_calls":0,"models":{}}}
|
||||
{"type":"error","timestamp":"<TIMESTAMP>","severity":"warning","message":"Loop detected, stopping execution"}
|
||||
{"type":"result","timestamp":"<TIMESTAMP>","status":"success","stats":{"total_tokens":0,"input_tokens":0,"output_tokens":0,"cached":0,"input":0,"duration_ms":<DURATION>,"tool_calls":0,"models":{}}}
|
||||
"
|
||||
`;
|
||||
|
||||
@@ -12,7 +13,6 @@ exports[`runNonInteractive > should emit appropriate error event in streaming JS
|
||||
{"type":"message","timestamp":"<TIMESTAMP>","role":"user","content":"Max turns test"}
|
||||
{"type":"error","timestamp":"<TIMESTAMP>","severity":"error","message":"Maximum session turns exceeded"}
|
||||
{"type":"result","timestamp":"<TIMESTAMP>","status":"success","stats":{"total_tokens":0,"input_tokens":0,"output_tokens":0,"cached":0,"input":0,"duration_ms":<DURATION>,"tool_calls":0,"models":{}}}
|
||||
{"type":"result","timestamp":"<TIMESTAMP>","status":"success","stats":{"total_tokens":0,"input_tokens":0,"output_tokens":0,"cached":0,"input":0,"duration_ms":<DURATION>,"tool_calls":0,"models":{}}}
|
||||
"
|
||||
`;
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ function ensureStreamStart(state: TranslationState, out: AgentEvent[]): void {
|
||||
* Converts @google/genai Part[] to ContentPart[].
|
||||
* Text parts become text ContentParts; inline data becomes media ContentParts.
|
||||
*/
|
||||
function mapResponseParts(parts: Part[]): ContentPart[] {
|
||||
export function mapResponseParts(parts: Part[]): ContentPart[] {
|
||||
const result: ContentPart[] = [];
|
||||
for (const part of parts) {
|
||||
if (part.text !== undefined) {
|
||||
@@ -285,7 +285,29 @@ export function translateEvent(
|
||||
name: state.pendingToolNames.get(event.value.callId) ?? 'unknown',
|
||||
content: mapResponseParts(event.value.responseParts),
|
||||
isError: event.value.error !== undefined,
|
||||
...(event.value.data ? { data: event.value.data } : {}),
|
||||
...(event.value.resultDisplay !== undefined
|
||||
? {
|
||||
displayContent: [
|
||||
{
|
||||
type: 'text',
|
||||
text:
|
||||
typeof event.value.resultDisplay === 'string'
|
||||
? event.value.resultDisplay
|
||||
: JSON.stringify(event.value.resultDisplay),
|
||||
},
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
...(event.value.data || event.value.errorType
|
||||
? {
|
||||
data: {
|
||||
...(event.value.data || {}),
|
||||
...(event.value.errorType
|
||||
? { errorType: event.value.errorType }
|
||||
: {}),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
}),
|
||||
);
|
||||
state.pendingToolNames.delete(event.value.callId);
|
||||
|
||||
@@ -1067,7 +1067,7 @@ describe('LegacyAgentSession', () => {
|
||||
expect(resp.isError).toBe(true);
|
||||
expect(resp.name).toBe('write_file');
|
||||
expect(resp.content).toEqual([
|
||||
{ type: 'text', text: 'Permission denied: /bad' },
|
||||
{ type: 'text', text: 'Permission denied' },
|
||||
]);
|
||||
expect(resp.displayContent).toEqual([
|
||||
{ type: 'text', text: 'Cannot write to /bad' },
|
||||
@@ -1140,7 +1140,7 @@ describe('LegacyAgentSession', () => {
|
||||
// LoopDetected emits error{fatal:true} + stream_end{failed}
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
it('LoopDetected emits fatal error and stream_end(failed)', async () => {
|
||||
it('LoopDetected emits non-fatal error and stream_end(failed)', async () => {
|
||||
const client = makeAsyncClient([
|
||||
[
|
||||
{ type: GeminiEventType.ModelInfo, value: 'gemini-2.5-pro' },
|
||||
@@ -1163,8 +1163,8 @@ describe('LegacyAgentSession', () => {
|
||||
const error = events.find(
|
||||
(e) => e.type === 'error',
|
||||
) as AgentEvent<'error'>;
|
||||
expect(error.fatal).toBe(true);
|
||||
expect(error.message).toBe('Loop detected');
|
||||
expect(error.fatal).toBe(false);
|
||||
expect(error.message).toBe('Loop detected, stopping execution');
|
||||
|
||||
const end = events[events.length - 1] as AgentEvent<'stream_end'>;
|
||||
expect(end.reason).toBe('failed');
|
||||
|
||||
@@ -21,6 +21,7 @@ import { debugLogger } from '../utils/debugLogger.js';
|
||||
import {
|
||||
translateEvent,
|
||||
createTranslationState,
|
||||
mapResponseParts,
|
||||
type TranslationState,
|
||||
} from './event-translator.js';
|
||||
import type {
|
||||
@@ -218,7 +219,8 @@ export class LegacyAgentSession implements AgentSession {
|
||||
if (
|
||||
event.type === GeminiEventType.AgentExecutionStopped ||
|
||||
event.type === GeminiEventType.LoopDetected ||
|
||||
event.type === GeminiEventType.AgentExecutionBlocked
|
||||
event.type === GeminiEventType.AgentExecutionBlocked ||
|
||||
event.type === GeminiEventType.MaxSessionTurns
|
||||
) {
|
||||
this._streamDone = true;
|
||||
return;
|
||||
@@ -248,7 +250,9 @@ export class LegacyAgentSession implements AgentSession {
|
||||
this.makeInternalEvent('tool_response', {
|
||||
requestId: request.callId,
|
||||
name: request.name,
|
||||
content: mapCompletedToolResponseParts(response.responseParts),
|
||||
content: response.error
|
||||
? [{ type: 'text', text: response.error.message }]
|
||||
: mapResponseParts(response.responseParts),
|
||||
isError: response.error !== undefined,
|
||||
...(response.resultDisplay !== undefined
|
||||
? {
|
||||
@@ -441,20 +445,3 @@ function contentPartsToGeminiParts(parts: ContentPart[]): Part[] {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Convert @google/genai Part[] → AgentEvent ContentPart[] */
|
||||
function mapCompletedToolResponseParts(parts: Part[]): ContentPart[] {
|
||||
const result: ContentPart[] = [];
|
||||
for (const part of parts) {
|
||||
if (part.text !== undefined) {
|
||||
result.push({ type: 'text', text: part.text });
|
||||
} else if (part.inlineData) {
|
||||
result.push({
|
||||
type: 'media',
|
||||
data: part.inlineData.data,
|
||||
mimeType: part.inlineData.mimeType,
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user