From bd0f085ae896e97373b451180f70ff551142fd7e Mon Sep 17 00:00:00 2001 From: Adam Weidman <65992621+adamfweidman@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:08:58 -0400 Subject: [PATCH] chore(a2a-server): if a2a task creation fails return error to user (#8106) Co-authored-by: cornmander --- packages/a2a-server/src/agent/executor.ts | 30 +++++++---- packages/a2a-server/src/config/config.ts | 7 +-- .../a2a-server/src/http/endpoints.test.ts | 17 +++++- .../a2a-server/src/utils/executor_utils.ts | 52 +++++++++++++++++++ 4 files changed, 91 insertions(+), 15 deletions(-) create mode 100644 packages/a2a-server/src/utils/executor_utils.ts diff --git a/packages/a2a-server/src/agent/executor.ts b/packages/a2a-server/src/agent/executor.ts index 4fdea984ae..a5360a94b7 100644 --- a/packages/a2a-server/src/agent/executor.ts +++ b/packages/a2a-server/src/agent/executor.ts @@ -36,6 +36,7 @@ import { loadSettings } from '../config/settings.js'; import { loadExtensions } from '../config/extension.js'; import { Task } from './task.js'; import { requestStorage } from '../http/requestStorage.js'; +import { pushTaskStateFailed } from '../utils/executor_utils.js'; /** * Provides a wrapper for Task. Passes data from Task to SDKTask. @@ -116,8 +117,8 @@ export class CoderAgentExecutor implements AgentExecutor { const agentSettings = persistedState._agentSettings; const config = await this.getConfig(agentSettings, sdkTask.id); - const contextId = - (metadata['_contextId'] as string) || (sdkTask.contextId as string); + const contextId: string = + (metadata['_contextId'] as string) || sdkTask.contextId; const runtimeTask = await Task.create( sdkTask.id, contextId, @@ -280,10 +281,10 @@ export class CoderAgentExecutor implements AgentExecutor { const sdkTask = requestContext.task as SDKTask | undefined; const taskId = sdkTask?.id || userMessage.taskId || uuidv4(); - const contextId = + const contextId: string = userMessage.contextId || sdkTask?.contextId || - sdkTask?.metadata?.['_contextId'] || + (sdkTask?.metadata?.['_contextId'] as string) || uuidv4(); logger.info( @@ -381,12 +382,21 @@ export class CoderAgentExecutor implements AgentExecutor { const agentSettings = userMessage.metadata?.[ 'coderAgent' ] as AgentSettings; - wrapper = await this.createTask( - taskId, - contextId as string, - agentSettings, - eventBus, - ); + try { + wrapper = await this.createTask( + taskId, + contextId, + agentSettings, + eventBus, + ); + } catch (error) { + logger.error( + `[CoderAgentExecutor] Error creating task ${taskId}:`, + error, + ); + pushTaskStateFailed(error, eventBus, taskId, contextId); + return; + } const newTaskSDK = wrapper.toSDKTask(); eventBus.publish({ ...newTaskSDK, diff --git a/packages/a2a-server/src/config/config.ts b/packages/a2a-server/src/config/config.ts index 0b6b9b8599..0ee253dc17 100644 --- a/packages/a2a-server/src/config/config.ts +++ b/packages/a2a-server/src/config/config.ts @@ -108,9 +108,10 @@ export async function loadConfig( logger.info('[Config] Using Gemini API Key'); await config.refreshAuth(AuthType.USE_GEMINI); } else { - logger.error( - `[Config] Unable to set GeneratorConfig. Please provide a GEMINI_API_KEY or set USE_CCPA.`, - ); + const errorMessage = + '[Config] Unable to set GeneratorConfig. Please provide a GEMINI_API_KEY or set USE_CCPA.'; + logger.error(errorMessage); + throw new Error(errorMessage); } return config; diff --git a/packages/a2a-server/src/http/endpoints.test.ts b/packages/a2a-server/src/http/endpoints.test.ts index 5a5116ef90..076aaec1ea 100644 --- a/packages/a2a-server/src/http/endpoints.test.ts +++ b/packages/a2a-server/src/http/endpoints.test.ts @@ -7,14 +7,17 @@ import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest'; import request from 'supertest'; import type express from 'express'; -import { createApp, updateCoderAgentCardUrl } from './app.js'; import * as fs from 'node:fs'; import * as path from 'node:path'; import * as os from 'node:os'; import type { Server } from 'node:http'; -import type { TaskMetadata } from '../types.js'; import type { AddressInfo } from 'node:net'; +import { createApp, updateCoderAgentCardUrl } from './app.js'; +import type { TaskMetadata } from '../types.js'; +import { createMockConfig } from '../utils/testing_utils.js'; +import type { Config } from '@google/gemini-cli-core'; + // Mock the logger to avoid polluting test output // Comment out to help debug vi.mock('../utils/logger.js', () => ({ @@ -56,6 +59,16 @@ vi.mock('../agent/task.js', () => { return { Task: MockTask }; }); +vi.mock('../config/config.js', async () => { + const actual = await vi.importActual('../config/config.js'); + return { + ...actual, + loadConfig: vi + .fn() + .mockImplementation(async () => createMockConfig({}) as Config), + }; +}); + describe('Agent Server Endpoints', () => { let app: express.Express; let server: Server; diff --git a/packages/a2a-server/src/utils/executor_utils.ts b/packages/a2a-server/src/utils/executor_utils.ts new file mode 100644 index 0000000000..b595a6905b --- /dev/null +++ b/packages/a2a-server/src/utils/executor_utils.ts @@ -0,0 +1,52 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import type { Message } from '@a2a-js/sdk'; +import type { ExecutionEventBus } from '@a2a-js/sdk/server'; +import { v4 as uuidv4 } from 'uuid'; + +import { CoderAgentEvent } from '../types.js'; +import type { StateChange } from '../types.js'; + +export async function pushTaskStateFailed( + error: unknown, + eventBus: ExecutionEventBus, + taskId: string, + contextId: string, +) { + const errorMessage = + error instanceof Error ? error.message : 'Agent execution error'; + const stateChange: StateChange = { + kind: CoderAgentEvent.StateChangeEvent, + }; + eventBus.publish({ + kind: 'status-update', + taskId, + contextId, + status: { + state: 'failed', + message: { + kind: 'message', + role: 'agent', + parts: [ + { + kind: 'text', + text: errorMessage, + }, + ], + messageId: uuidv4(), + taskId, + contextId, + } as Message, + }, + final: true, + metadata: { + coderAgent: stateChange, + model: 'unknown', + error: errorMessage, + }, + }); +}