Skip to content

Commit c4758ba

Browse files
authored
feat(core): wire AgentSession invocations into agent-tool (google-gemini#26948)
1 parent 96a8d1d commit c4758ba

2 files changed

Lines changed: 151 additions & 0 deletions

File tree

packages/core/src/agents/agent-tool.test.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,17 @@ import type { Config } from '../config/config.js';
1212
import type { MessageBus } from '../confirmation-bus/message-bus.js';
1313
import { LocalSubagentInvocation } from './local-invocation.js';
1414
import { RemoteAgentInvocation } from './remote-invocation.js';
15+
import { LocalSessionInvocation } from './local-session-invocation.js';
16+
import { RemoteSessionInvocation } from './remote-session-invocation.js';
1517
import { BrowserAgentInvocation } from './browser/browserAgentInvocation.js';
1618
import { BROWSER_AGENT_NAME } from './browser/browserAgentDefinition.js';
1719
import { AgentRegistry } from './registry.js';
1820
import type { LocalAgentDefinition, RemoteAgentDefinition } from './types.js';
1921

2022
vi.mock('./local-invocation.js');
2123
vi.mock('./remote-invocation.js');
24+
vi.mock('./local-session-invocation.js');
25+
vi.mock('./remote-session-invocation.js');
2226
vi.mock('./browser/browserAgentInvocation.js');
2327

2428
describe('AgentTool', () => {
@@ -141,4 +145,122 @@ describe('AgentTool', () => {
141145
'Invoke Browser Agent',
142146
);
143147
});
148+
149+
describe('agentSessionSubagentEnabled feature flag', () => {
150+
it('should use LocalSessionInvocation when flag is enabled for local agent', async () => {
151+
vi.spyOn(mockConfig, 'isAgentSessionSubagentEnabled').mockReturnValue(
152+
true,
153+
);
154+
tool = new AgentTool(mockConfig, mockMessageBus);
155+
156+
const params = {
157+
agent_name: 'TestLocalAgent',
158+
prompt: 'Do something',
159+
};
160+
const invocation = tool['createInvocation'](params, mockMessageBus);
161+
await invocation.shouldConfirmExecute(new AbortController().signal);
162+
163+
expect(LocalSessionInvocation).toHaveBeenCalledWith(
164+
testLocalDefinition,
165+
mockConfig,
166+
{ objective: 'Do something' },
167+
mockMessageBus,
168+
undefined,
169+
);
170+
expect(LocalSubagentInvocation).not.toHaveBeenCalled();
171+
});
172+
173+
it('should use RemoteSessionInvocation when flag is enabled for remote agent', async () => {
174+
vi.spyOn(mockConfig, 'isAgentSessionSubagentEnabled').mockReturnValue(
175+
true,
176+
);
177+
tool = new AgentTool(mockConfig, mockMessageBus);
178+
179+
const params = {
180+
agent_name: 'TestRemoteAgent',
181+
prompt: 'Search something',
182+
};
183+
const invocation = tool['createInvocation'](params, mockMessageBus);
184+
await invocation.shouldConfirmExecute(new AbortController().signal);
185+
186+
expect(RemoteSessionInvocation).toHaveBeenCalledWith(
187+
testRemoteDefinition,
188+
mockConfig,
189+
{ query: 'Search something' },
190+
mockMessageBus,
191+
undefined,
192+
);
193+
expect(RemoteAgentInvocation).not.toHaveBeenCalled();
194+
});
195+
196+
it('should use legacy invocations when flag is disabled (default)', async () => {
197+
vi.spyOn(mockConfig, 'isAgentSessionSubagentEnabled').mockReturnValue(
198+
false,
199+
);
200+
tool = new AgentTool(mockConfig, mockMessageBus);
201+
202+
const localParams = {
203+
agent_name: 'TestLocalAgent',
204+
prompt: 'Do something',
205+
};
206+
const localInv = tool['createInvocation'](localParams, mockMessageBus);
207+
await localInv.shouldConfirmExecute(new AbortController().signal);
208+
209+
expect(LocalSubagentInvocation).toHaveBeenCalled();
210+
expect(LocalSessionInvocation).not.toHaveBeenCalled();
211+
212+
vi.clearAllMocks();
213+
214+
const remoteParams = {
215+
agent_name: 'TestRemoteAgent',
216+
prompt: 'Search',
217+
};
218+
const remoteInv = tool['createInvocation'](remoteParams, mockMessageBus);
219+
await remoteInv.shouldConfirmExecute(new AbortController().signal);
220+
221+
expect(RemoteAgentInvocation).toHaveBeenCalled();
222+
expect(RemoteSessionInvocation).not.toHaveBeenCalled();
223+
});
224+
225+
it('should thread onAgentEvent to session invocations', async () => {
226+
vi.spyOn(mockConfig, 'isAgentSessionSubagentEnabled').mockReturnValue(
227+
true,
228+
);
229+
const onEvent = vi.fn();
230+
tool = new AgentTool(mockConfig, mockMessageBus, onEvent);
231+
232+
const params = {
233+
agent_name: 'TestLocalAgent',
234+
prompt: 'Do something',
235+
};
236+
const invocation = tool['createInvocation'](params, mockMessageBus);
237+
await invocation.shouldConfirmExecute(new AbortController().signal);
238+
239+
expect(LocalSessionInvocation).toHaveBeenCalledWith(
240+
testLocalDefinition,
241+
mockConfig,
242+
{ objective: 'Do something' },
243+
mockMessageBus,
244+
{ onAgentEvent: onEvent },
245+
);
246+
});
247+
248+
it('should always use BrowserAgentInvocation for browser agent regardless of flag', async () => {
249+
vi.spyOn(mockConfig, 'isAgentSessionSubagentEnabled').mockReturnValue(
250+
true,
251+
);
252+
tool = new AgentTool(mockConfig, mockMessageBus);
253+
254+
const params = {
255+
agent_name: BROWSER_AGENT_NAME,
256+
prompt: 'Open page',
257+
};
258+
const invocation = tool['createInvocation'](params, mockMessageBus);
259+
await invocation.shouldConfirmExecute(new AbortController().signal);
260+
261+
expect(BrowserAgentInvocation).toHaveBeenCalled();
262+
expect(LocalSessionInvocation).not.toHaveBeenCalled();
263+
expect(RemoteSessionInvocation).not.toHaveBeenCalled();
264+
});
265+
});
144266
});

packages/core/src/agents/agent-tool.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ import type { MessageBus } from '../confirmation-bus/message-bus.js';
1818
import type { AgentDefinition, AgentInputs } from './types.js';
1919
import { LocalSubagentInvocation } from './local-invocation.js';
2020
import { RemoteAgentInvocation } from './remote-invocation.js';
21+
import { LocalSessionInvocation } from './local-session-invocation.js';
22+
import { RemoteSessionInvocation } from './remote-session-invocation.js';
2123
import { BROWSER_AGENT_NAME } from './browser/browserAgentDefinition.js';
2224
import { BrowserAgentInvocation } from './browser/browserAgentInvocation.js';
25+
import type { AgentEvent } from '../agent/types.js';
2326
import { formatUserHintsForModel } from '../utils/fastAckHelper.js';
2427
import { isRecord } from '../utils/markdownUtils.js';
2528
import { runInDevTraceSpan } from '../telemetry/trace.js';
@@ -46,6 +49,7 @@ export class AgentTool extends BaseDeclarativeTool<
4649
constructor(
4750
private readonly context: AgentLoopContext,
4851
messageBus: MessageBus,
52+
private readonly onAgentEvent?: (event: AgentEvent) => void,
4953
) {
5054
super(
5155
AGENT_TOOL_NAME,
@@ -100,6 +104,7 @@ export class AgentTool extends BaseDeclarativeTool<
100104
this.context,
101105
_toolName,
102106
_toolDisplayName,
107+
this.onAgentEvent,
103108
);
104109
}
105110

@@ -133,6 +138,7 @@ class DelegateInvocation extends BaseToolInvocation<
133138
private readonly context: AgentLoopContext,
134139
_toolName?: string,
135140
_toolDisplayName?: string,
141+
private readonly onAgentEvent?: (event: AgentEvent) => void,
136142
) {
137143
super(
138144
params,
@@ -160,14 +166,37 @@ class DelegateInvocation extends BaseToolInvocation<
160166
);
161167
}
162168

169+
const useSession = this.context.config.isAgentSessionSubagentEnabled();
170+
const options = this.onAgentEvent
171+
? { onAgentEvent: this.onAgentEvent }
172+
: undefined;
173+
163174
if (this.definition.kind === 'remote') {
175+
if (useSession) {
176+
return new RemoteSessionInvocation(
177+
this.definition,
178+
this.context,
179+
agentArgs,
180+
this.messageBus,
181+
options,
182+
);
183+
}
164184
return new RemoteAgentInvocation(
165185
this.definition,
166186
this.context,
167187
agentArgs,
168188
this.messageBus,
169189
);
170190
} else {
191+
if (useSession) {
192+
return new LocalSessionInvocation(
193+
this.definition,
194+
this.context,
195+
agentArgs,
196+
this.messageBus,
197+
options,
198+
);
199+
}
171200
return new LocalSubagentInvocation(
172201
this.definition,
173202
this.context,

0 commit comments

Comments
 (0)