mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-20 18:14:29 -07:00
feat(core): persist subagent agentId in tool call records (#25092)
This commit is contained in:
@@ -114,7 +114,7 @@ export function createUnauthorizedToolError(toolName: string): string {
|
||||
export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
|
||||
readonly definition: LocalAgentDefinition<TOutput>;
|
||||
|
||||
private readonly agentId: string;
|
||||
readonly agentId: string;
|
||||
private readonly toolRegistry: ToolRegistry;
|
||||
private readonly promptRegistry: PromptRegistry;
|
||||
private readonly resourceRegistry: ResourceRegistry;
|
||||
|
||||
@@ -79,6 +79,7 @@ describe('LocalSubagentInvocation', () => {
|
||||
mockExecutorInstance = {
|
||||
run: vi.fn(),
|
||||
definition: testDefinition,
|
||||
agentId: 'test-agent-id',
|
||||
} as unknown as Mocked<LocalAgentExecutor<z.ZodUnknown>>;
|
||||
|
||||
MockLocalAgentExecutor.create.mockResolvedValue(
|
||||
|
||||
@@ -25,12 +25,14 @@ import {
|
||||
isToolActivityError,
|
||||
} from './types.js';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import type { z } from 'zod';
|
||||
import type { MessageBus } from '../confirmation-bus/message-bus.js';
|
||||
import {
|
||||
sanitizeThoughtContent,
|
||||
sanitizeToolArgs,
|
||||
sanitizeErrorMessage,
|
||||
} from '../utils/agent-sanitization-utils.js';
|
||||
import { debugLogger } from '../utils/debugLogger.js';
|
||||
|
||||
const INPUT_PREVIEW_MAX_LENGTH = 50;
|
||||
const DESCRIPTION_MAX_LENGTH = 200;
|
||||
@@ -108,6 +110,7 @@ export class LocalSubagentInvocation extends BaseToolInvocation<
|
||||
updateOutput?: (output: ToolLiveOutput) => void,
|
||||
): Promise<ToolResult> {
|
||||
const recentActivity: SubagentActivityItem[] = [];
|
||||
let executor: LocalAgentExecutor<z.ZodUnknown> | undefined;
|
||||
|
||||
try {
|
||||
if (updateOutput) {
|
||||
@@ -273,7 +276,7 @@ export class LocalSubagentInvocation extends BaseToolInvocation<
|
||||
}
|
||||
};
|
||||
|
||||
const executor = await LocalAgentExecutor.create(
|
||||
executor = await LocalAgentExecutor.create(
|
||||
this.definition,
|
||||
this.context,
|
||||
onActivity,
|
||||
@@ -319,11 +322,14 @@ ${output.result}`;
|
||||
return {
|
||||
llmContent: [{ text: resultContent }],
|
||||
returnDisplay: progress,
|
||||
data: { agentId: executor.agentId },
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
|
||||
debugLogger.error(`Subagent '${this.definition.name}' failed:`, error);
|
||||
|
||||
const isAbort =
|
||||
(error instanceof Error && error.name === 'AbortError') ||
|
||||
errorMessage.includes('Aborted');
|
||||
@@ -369,6 +375,7 @@ ${output.result}`;
|
||||
return {
|
||||
llmContent: `Subagent '${this.definition.name}' failed. Error: ${errorMessage}`,
|
||||
returnDisplay: progress,
|
||||
data: executor ? { agentId: executor.agentId } : undefined,
|
||||
// We omit the 'error' property so that the UI renders our rich returnDisplay
|
||||
// instead of the raw error message. The llmContent still informs the agent of the failure.
|
||||
};
|
||||
|
||||
@@ -1050,6 +1050,10 @@ export class GeminiChat {
|
||||
result: call.response?.responseParts || null,
|
||||
status: call.status,
|
||||
timestamp: new Date().toISOString(),
|
||||
agentId:
|
||||
typeof call.response?.data?.['agentId'] === 'string'
|
||||
? call.response.data['agentId']
|
||||
: undefined,
|
||||
resultDisplay,
|
||||
description:
|
||||
'invocation' in call ? call.invocation?.getDescription() : undefined,
|
||||
|
||||
@@ -536,6 +536,34 @@ describe('ChatRecordingService', () => {
|
||||
.toolCalls,
|
||||
).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should record agentId when provided', async () => {
|
||||
chatRecordingService.recordMessage({
|
||||
type: 'gemini',
|
||||
content: '',
|
||||
model: 'gemini-pro',
|
||||
});
|
||||
|
||||
const toolCall: ToolCallRecord = {
|
||||
id: 'tool-1',
|
||||
name: 'testTool',
|
||||
args: {},
|
||||
status: CoreToolCallStatus.Success,
|
||||
timestamp: new Date().toISOString(),
|
||||
agentId: 'test-agent-id',
|
||||
};
|
||||
chatRecordingService.recordToolCalls('gemini-pro', [toolCall]);
|
||||
|
||||
const sessionFile = chatRecordingService.getConversationFilePath()!;
|
||||
const conversation = (await loadConversationRecord(
|
||||
sessionFile,
|
||||
)) as ConversationRecord;
|
||||
const geminiMsg = conversation.messages[0] as MessageRecord & {
|
||||
type: 'gemini';
|
||||
};
|
||||
expect(geminiMsg.toolCalls).toHaveLength(1);
|
||||
expect(geminiMsg.toolCalls![0].agentId).toBe('test-agent-id');
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteSession', () => {
|
||||
|
||||
@@ -45,6 +45,7 @@ export interface ToolCallRecord {
|
||||
result?: PartListUnion | null;
|
||||
status: Status;
|
||||
timestamp: string;
|
||||
agentId?: string;
|
||||
// UI-specific fields for display purposes
|
||||
displayName?: string;
|
||||
description?: string;
|
||||
|
||||
Reference in New Issue
Block a user