mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-10 14:10:37 -07:00
feat(agents): directly indicate auth required state (#20986)
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
extractIdsFromResponse,
|
||||
isTerminalState,
|
||||
A2AResultReassembler,
|
||||
AUTH_REQUIRED_MSG,
|
||||
} from './a2aUtils.js';
|
||||
import type { SendMessageResult } from './a2a-client-manager.js';
|
||||
import type {
|
||||
@@ -285,6 +286,66 @@ describe('a2aUtils', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle auth-required state with a message', () => {
|
||||
const reassembler = new A2AResultReassembler();
|
||||
|
||||
reassembler.update({
|
||||
kind: 'status-update',
|
||||
status: {
|
||||
state: 'auth-required',
|
||||
message: {
|
||||
kind: 'message',
|
||||
role: 'agent',
|
||||
parts: [{ kind: 'text', text: 'I need your permission.' }],
|
||||
} as Message,
|
||||
},
|
||||
} as unknown as SendMessageResult);
|
||||
|
||||
expect(reassembler.toString()).toContain('I need your permission.');
|
||||
expect(reassembler.toString()).toContain(AUTH_REQUIRED_MSG);
|
||||
});
|
||||
|
||||
it('should handle auth-required state without relying on metadata', () => {
|
||||
const reassembler = new A2AResultReassembler();
|
||||
|
||||
reassembler.update({
|
||||
kind: 'status-update',
|
||||
status: {
|
||||
state: 'auth-required',
|
||||
},
|
||||
} as unknown as SendMessageResult);
|
||||
|
||||
expect(reassembler.toString()).toContain(AUTH_REQUIRED_MSG);
|
||||
});
|
||||
|
||||
it('should not duplicate the auth instruction OR agent message if multiple identical auth-required chunks arrive', () => {
|
||||
const reassembler = new A2AResultReassembler();
|
||||
|
||||
const chunk = {
|
||||
kind: 'status-update',
|
||||
status: {
|
||||
state: 'auth-required',
|
||||
message: {
|
||||
kind: 'message',
|
||||
role: 'agent',
|
||||
parts: [{ kind: 'text', text: 'You need to login here.' }],
|
||||
} as Message,
|
||||
},
|
||||
} as unknown as SendMessageResult;
|
||||
|
||||
reassembler.update(chunk);
|
||||
// Simulate multiple updates with the same overall state
|
||||
reassembler.update(chunk);
|
||||
reassembler.update(chunk);
|
||||
|
||||
const output = reassembler.toString();
|
||||
// The substring should only appear exactly once
|
||||
expect(output.split(AUTH_REQUIRED_MSG).length - 1).toBe(1);
|
||||
|
||||
// Crucially, the agent's actual custom message should ALSO only appear exactly once
|
||||
expect(output.split('You need to login here.').length - 1).toBe(1);
|
||||
});
|
||||
|
||||
it('should fallback to history in a task chunk if no message or artifacts exist and task is terminal', () => {
|
||||
const reassembler = new A2AResultReassembler();
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ import type {
|
||||
} from '@a2a-js/sdk';
|
||||
import type { SendMessageResult } from './a2a-client-manager.js';
|
||||
|
||||
export const AUTH_REQUIRED_MSG = `[Authorization Required] The agent has indicated it requires authorization to proceed. Please follow the agent's instructions.`;
|
||||
|
||||
/**
|
||||
* Reassembles incremental A2A streaming updates into a coherent result.
|
||||
* Shows sequential status/messages followed by all reassembled artifacts.
|
||||
@@ -33,6 +35,7 @@ export class A2AResultReassembler {
|
||||
|
||||
switch (chunk.kind) {
|
||||
case 'status-update':
|
||||
this.appendStateInstructions(chunk.status?.state);
|
||||
this.pushMessage(chunk.status?.message);
|
||||
break;
|
||||
|
||||
@@ -65,6 +68,7 @@ export class A2AResultReassembler {
|
||||
break;
|
||||
|
||||
case 'task':
|
||||
this.appendStateInstructions(chunk.status?.state);
|
||||
this.pushMessage(chunk.status?.message);
|
||||
if (chunk.artifacts) {
|
||||
for (const art of chunk.artifacts) {
|
||||
@@ -106,6 +110,17 @@ export class A2AResultReassembler {
|
||||
}
|
||||
}
|
||||
|
||||
private appendStateInstructions(state: TaskState | undefined) {
|
||||
if (state !== 'auth-required') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent duplicate instructions if multiple chunks report auth-required
|
||||
if (!this.messageLog.includes(AUTH_REQUIRED_MSG)) {
|
||||
this.messageLog.push(AUTH_REQUIRED_MSG);
|
||||
}
|
||||
}
|
||||
|
||||
private pushMessage(message: Message | undefined) {
|
||||
if (!message) return;
|
||||
const text = extractPartsText(message.parts, '\n');
|
||||
|
||||
Reference in New Issue
Block a user