mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-24 12:04:56 -07:00
feat(core): implement interactive and non-interactive consent for OAuth (#17699)
This commit is contained in:
@@ -305,4 +305,59 @@ describe('CoreEventEmitter', () => {
|
||||
expect(listener).toHaveBeenCalledWith(payload);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ConsentRequest Event', () => {
|
||||
it('should emit consent request immediately when a listener is present', () => {
|
||||
const listener = vi.fn();
|
||||
events.on(CoreEvent.ConsentRequest, listener);
|
||||
|
||||
const payload = {
|
||||
prompt: 'Do you consent?',
|
||||
onConfirm: vi.fn(),
|
||||
};
|
||||
|
||||
events.emitConsentRequest(payload);
|
||||
|
||||
expect(listener).toHaveBeenCalledTimes(1);
|
||||
expect(listener).toHaveBeenCalledWith(payload);
|
||||
});
|
||||
|
||||
it('should buffer consent requests when no listener is present', () => {
|
||||
const listener = vi.fn();
|
||||
const payload = {
|
||||
prompt: 'Buffered consent?',
|
||||
onConfirm: vi.fn(),
|
||||
};
|
||||
|
||||
// Emit while no listeners attached
|
||||
events.emitConsentRequest(payload);
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
|
||||
// Attach listener and drain
|
||||
events.on(CoreEvent.ConsentRequest, listener);
|
||||
events.drainBacklogs();
|
||||
|
||||
expect(listener).toHaveBeenCalledTimes(1);
|
||||
expect(listener).toHaveBeenCalledWith(payload);
|
||||
});
|
||||
|
||||
it('should respect the backlog size limit for consent requests', () => {
|
||||
const listener = vi.fn();
|
||||
const MAX_BACKLOG_SIZE = 10000;
|
||||
|
||||
for (let i = 0; i < MAX_BACKLOG_SIZE + 10; i++) {
|
||||
events.emitConsentRequest({
|
||||
prompt: `Consent ${i}`,
|
||||
onConfirm: vi.fn(),
|
||||
});
|
||||
}
|
||||
|
||||
events.on(CoreEvent.ConsentRequest, listener);
|
||||
events.drainBacklogs();
|
||||
|
||||
expect(listener).toHaveBeenCalledTimes(MAX_BACKLOG_SIZE);
|
||||
// Verify strictly that the FIRST call was Consent 10 (0-9 dropped)
|
||||
expect(listener.mock.calls[0][0]).toMatchObject({ prompt: 'Consent 10' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -111,6 +111,14 @@ export interface RetryAttemptPayload {
|
||||
model: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Payload for the 'consent-request' event.
|
||||
*/
|
||||
export interface ConsentRequestPayload {
|
||||
prompt: string;
|
||||
onConfirm: (confirmed: boolean) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Payload for the 'agents-discovered' event.
|
||||
*/
|
||||
@@ -133,6 +141,7 @@ export enum CoreEvent {
|
||||
AgentsRefreshed = 'agents-refreshed',
|
||||
AdminSettingsChanged = 'admin-settings-changed',
|
||||
RetryAttempt = 'retry-attempt',
|
||||
ConsentRequest = 'consent-request',
|
||||
AgentsDiscovered = 'agents-discovered',
|
||||
}
|
||||
|
||||
@@ -151,6 +160,7 @@ export interface CoreEvents extends ExtensionEvents {
|
||||
[CoreEvent.AgentsRefreshed]: never[];
|
||||
[CoreEvent.AdminSettingsChanged]: never[];
|
||||
[CoreEvent.RetryAttempt]: [RetryAttemptPayload];
|
||||
[CoreEvent.ConsentRequest]: [ConsentRequestPayload];
|
||||
[CoreEvent.AgentsDiscovered]: [AgentsDiscoveredPayload];
|
||||
}
|
||||
|
||||
@@ -274,6 +284,13 @@ export class CoreEventEmitter extends EventEmitter<CoreEvents> {
|
||||
this.emit(CoreEvent.RetryAttempt, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests consent from the user via the UI.
|
||||
*/
|
||||
emitConsentRequest(payload: ConsentRequestPayload): void {
|
||||
this._emitOrQueue(CoreEvent.ConsentRequest, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies subscribers that new unacknowledged agents have been discovered.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user