var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/server/index.ts var server_exports = {}; __export(server_exports, { A2AError: () => A2AError, DefaultExecutionEventBus: () => DefaultExecutionEventBus, DefaultExecutionEventBusManager: () => DefaultExecutionEventBusManager, DefaultPushNotificationSender: () => DefaultPushNotificationSender, DefaultRequestHandler: () => DefaultRequestHandler, ExecutionEventQueue: () => ExecutionEventQueue, InMemoryPushNotificationStore: () => InMemoryPushNotificationStore, InMemoryTaskStore: () => InMemoryTaskStore, JsonRpcTransportHandler: () => JsonRpcTransportHandler, RequestContext: () => RequestContext, ResultManager: () => ResultManager, ServerCallContext: () => ServerCallContext, UnauthenticatedUser: () => UnauthenticatedUser }); module.exports = __toCommonJS(server_exports); // src/server/agent_execution/request_context.ts var RequestContext = class { userMessage; taskId; contextId; task; referenceTasks; context; constructor(userMessage, taskId, contextId, task, referenceTasks, context) { this.userMessage = userMessage; this.taskId = taskId; this.contextId = contextId; this.task = task; this.referenceTasks = referenceTasks; this.context = context; } }; // src/server/events/execution_event_bus.ts var CustomEventImpl = typeof CustomEvent !== "undefined" ? CustomEvent : class CustomEventPolyfill extends Event { detail; constructor(type, eventInitDict) { super(type, eventInitDict); this.detail = eventInitDict?.detail ?? null; } }; function isAgentExecutionCustomEvent(e) { return e instanceof CustomEventImpl; } var DefaultExecutionEventBus = class extends EventTarget { // Separate storage for each event type - both use the interface's Listener type // but are invoked differently (with event payload vs. no arguments) eventListeners = /* @__PURE__ */ new Map(); finishedListeners = /* @__PURE__ */ new Map(); publish(event) { this.dispatchEvent(new CustomEventImpl("event", { detail: event })); } finished() { this.dispatchEvent(new Event("finished")); } /** * EventEmitter-compatible 'on' method. * Wraps the listener to extract event detail from CustomEvent. * Supports multiple registrations of the same listener (like EventEmitter). * @param eventName The event name to listen for. * @param listener The callback function to invoke when the event is emitted. * @returns This instance for method chaining. */ on(eventName, listener) { if (eventName === "event") { this.addEventListenerInternal(listener); } else { this.addFinishedListenerInternal(listener); } return this; } /** * EventEmitter-compatible 'off' method. * Uses the stored wrapped listener for proper removal. * Removes at most one instance of a listener per call (like EventEmitter). * @param eventName The event name to stop listening for. * @param listener The callback function to remove. * @returns This instance for method chaining. */ off(eventName, listener) { if (eventName === "event") { this.removeEventListenerInternal(listener); } else { this.removeFinishedListenerInternal(listener); } return this; } /** * EventEmitter-compatible 'once' method. * Listener is automatically removed after first invocation. * Supports multiple registrations of the same listener (like EventEmitter). * @param eventName The event name to listen for once. * @param listener The callback function to invoke when the event is emitted. * @returns This instance for method chaining. */ once(eventName, listener) { if (eventName === "event") { this.addEventListenerOnceInternal(listener); } else { this.addFinishedListenerOnceInternal(listener); } return this; } /** * EventEmitter-compatible 'removeAllListeners' method. * Removes all listeners for a specific event or all events. * @param eventName Optional event name to remove listeners for. If omitted, removes all. * @returns This instance for method chaining. */ removeAllListeners(eventName) { if (eventName === void 0 || eventName === "event") { for (const wrappedListeners of this.eventListeners.values()) { for (const wrapped of wrappedListeners) { this.removeEventListener("event", wrapped); } } this.eventListeners.clear(); } if (eventName === void 0 || eventName === "finished") { for (const wrappedListeners of this.finishedListeners.values()) { for (const wrapped of wrappedListeners) { this.removeEventListener("finished", wrapped); } } this.finishedListeners.clear(); } return this; } // ======================== // Helper methods for listener tracking // ======================== /** * Adds a wrapped listener to the tracking map. */ trackListener(listenerMap, listener, wrapped) { const existing = listenerMap.get(listener); if (existing) { existing.push(wrapped); } else { listenerMap.set(listener, [wrapped]); } } /** * Removes a wrapped listener from the tracking map (for once cleanup). */ untrackWrappedListener(listenerMap, listener, wrapped) { const wrappedList = listenerMap.get(listener); if (wrappedList && wrappedList.length > 0) { const index = wrappedList.indexOf(wrapped); if (index !== -1) { wrappedList.splice(index, 1); if (wrappedList.length === 0) { listenerMap.delete(listener); } } } } // ======================== // Internal methods for 'event' listeners // ======================== addEventListenerInternal(listener) { const wrapped = (e) => { if (!isAgentExecutionCustomEvent(e)) { throw new Error('Internal error: expected CustomEvent for "event" type'); } listener.call(this, e.detail); }; this.trackListener(this.eventListeners, listener, wrapped); this.addEventListener("event", wrapped); } removeEventListenerInternal(listener) { const wrappedList = this.eventListeners.get(listener); if (wrappedList && wrappedList.length > 0) { const wrapped = wrappedList.pop(); if (wrappedList.length === 0) { this.eventListeners.delete(listener); } this.removeEventListener("event", wrapped); } } addEventListenerOnceInternal(listener) { const wrapped = (e) => { if (!isAgentExecutionCustomEvent(e)) { throw new Error('Internal error: expected CustomEvent for "event" type'); } this.untrackWrappedListener(this.eventListeners, listener, wrapped); listener.call(this, e.detail); }; this.trackListener(this.eventListeners, listener, wrapped); this.addEventListener("event", wrapped, { once: true }); } // ======================== // Internal methods for 'finished' listeners // ======================== // The interface declares listeners as (event: AgentExecutionEvent) => void, // but for 'finished' events they are invoked with no arguments (EventEmitter behavior). // We use Function.prototype.call to invoke with `this` as the event bus (matching // EventEmitter semantics) and no arguments, which is type-safe. addFinishedListenerInternal(listener) { const wrapped = () => { listener.call(this); }; this.trackListener(this.finishedListeners, listener, wrapped); this.addEventListener("finished", wrapped); } removeFinishedListenerInternal(listener) { const wrappedList = this.finishedListeners.get(listener); if (wrappedList && wrappedList.length > 0) { const wrapped = wrappedList.pop(); if (wrappedList.length === 0) { this.finishedListeners.delete(listener); } this.removeEventListener("finished", wrapped); } } addFinishedListenerOnceInternal(listener) { const wrapped = () => { this.untrackWrappedListener(this.finishedListeners, listener, wrapped); listener.call(this); }; this.trackListener(this.finishedListeners, listener, wrapped); this.addEventListener("finished", wrapped, { once: true }); } }; // src/server/events/execution_event_bus_manager.ts var DefaultExecutionEventBusManager = class { taskIdToBus = /* @__PURE__ */ new Map(); /** * Creates or retrieves an existing ExecutionEventBus based on the taskId. * @param taskId The ID of the task. * @returns An instance of ExecutionEventBus. */ createOrGetByTaskId(taskId) { if (!this.taskIdToBus.has(taskId)) { this.taskIdToBus.set(taskId, new DefaultExecutionEventBus()); } return this.taskIdToBus.get(taskId); } /** * Retrieves an existing ExecutionEventBus based on the taskId. * @param taskId The ID of the task. * @returns An instance of ExecutionEventBus or undefined if not found. */ getByTaskId(taskId) { return this.taskIdToBus.get(taskId); } /** * Removes the event bus for a given taskId. * This should be called when an execution flow is complete to free resources. * @param taskId The ID of the task. */ cleanupByTaskId(taskId) { const bus = this.taskIdToBus.get(taskId); if (bus) { bus.removeAllListeners(); } this.taskIdToBus.delete(taskId); } }; // src/server/events/execution_event_queue.ts var ExecutionEventQueue = class { eventBus; eventQueue = []; resolvePromise; stopped = false; constructor(eventBus) { this.eventBus = eventBus; this.eventBus.on("event", this.handleEvent); this.eventBus.on("finished", this.handleFinished); } handleEvent = (event) => { if (this.stopped) return; this.eventQueue.push(event); if (this.resolvePromise) { this.resolvePromise(); this.resolvePromise = void 0; } }; handleFinished = () => { this.stop(); }; /** * Provides an async generator that yields events from the event bus. * Stops when a Message event is received or a TaskStatusUpdateEvent with final=true is received. */ async *events() { while (!this.stopped || this.eventQueue.length > 0) { if (this.eventQueue.length > 0) { const event = this.eventQueue.shift(); yield event; if (event.kind === "message" || event.kind === "status-update" && event.final) { this.handleFinished(); break; } } else if (!this.stopped) { await new Promise((resolve) => { this.resolvePromise = resolve; }); } } } /** * Stops the event queue from processing further events. */ stop() { this.stopped = true; if (this.resolvePromise) { this.resolvePromise(); this.resolvePromise = void 0; } this.eventBus.off("event", this.handleEvent); this.eventBus.off("finished", this.handleFinished); } }; // src/server/request_handler/default_request_handler.ts var import_uuid = require("uuid"); // src/server/error.ts var A2AError = class _A2AError extends Error { code; data; taskId; // Optional task ID context constructor(code, message, data, taskId) { super(message); this.name = "A2AError"; this.code = code; this.data = data; this.taskId = taskId; } /** * Formats the error into a standard JSON-RPC error object structure. */ toJSONRPCError() { const errorObject = { code: this.code, message: this.message }; if (this.data !== void 0) { errorObject.data = this.data; } return errorObject; } // Static factory methods for common errors static parseError(message, data) { return new _A2AError(-32700, message, data); } static invalidRequest(message, data) { return new _A2AError(-32600, message, data); } static methodNotFound(method) { return new _A2AError(-32601, `Method not found: ${method}`); } static invalidParams(message, data) { return new _A2AError(-32602, message, data); } static internalError(message, data) { return new _A2AError(-32603, message, data); } static taskNotFound(taskId) { return new _A2AError(-32001, `Task not found: ${taskId}`, void 0, taskId); } static taskNotCancelable(taskId) { return new _A2AError(-32002, `Task not cancelable: ${taskId}`, void 0, taskId); } static pushNotificationNotSupported() { return new _A2AError(-32003, "Push Notification is not supported"); } static unsupportedOperation(operation) { return new _A2AError(-32004, `Unsupported operation: ${operation}`); } static authenticatedExtendedCardNotConfigured() { return new _A2AError(-32007, `Extended card not configured.`); } }; // src/server/result_manager.ts var ResultManager = class { taskStore; serverCallContext; currentTask; latestUserMessage; // To add to history if a new task is created finalMessageResult; // Stores the message if it's the final result constructor(taskStore, serverCallContext) { this.taskStore = taskStore; this.serverCallContext = serverCallContext; } setContext(latestUserMessage) { this.latestUserMessage = latestUserMessage; } /** * Processes an agent execution event and updates the task store. * @param event The agent execution event. */ async processEvent(event) { if (event.kind === "message") { this.finalMessageResult = event; } else if (event.kind === "task") { const taskEvent = event; this.currentTask = { ...taskEvent }; if (this.latestUserMessage) { if (!this.currentTask.history?.find( (msg) => msg.messageId === this.latestUserMessage.messageId )) { this.currentTask.history = [this.latestUserMessage, ...this.currentTask.history || []]; } } await this.saveCurrentTask(); } else if (event.kind === "status-update") { const updateEvent = event; if (this.currentTask && this.currentTask.id === updateEvent.taskId) { this.currentTask.status = updateEvent.status; if (updateEvent.status.message) { if (!this.currentTask.history?.find( (msg) => msg.messageId === updateEvent.status.message.messageId )) { this.currentTask.history = [ ...this.currentTask.history || [], updateEvent.status.message ]; } } await this.saveCurrentTask(); } else if (!this.currentTask && updateEvent.taskId) { const loaded = await this.taskStore.load(updateEvent.taskId, this.serverCallContext); if (loaded) { this.currentTask = loaded; this.currentTask.status = updateEvent.status; if (updateEvent.status.message) { if (!this.currentTask.history?.find( (msg) => msg.messageId === updateEvent.status.message.messageId )) { this.currentTask.history = [ ...this.currentTask.history || [], updateEvent.status.message ]; } } await this.saveCurrentTask(); } else { console.warn( `ResultManager: Received status update for unknown task ${updateEvent.taskId}` ); } } } else if (event.kind === "artifact-update") { const artifactEvent = event; if (this.currentTask && this.currentTask.id === artifactEvent.taskId) { if (!this.currentTask.artifacts) { this.currentTask.artifacts = []; } const existingArtifactIndex = this.currentTask.artifacts.findIndex( (art) => art.artifactId === artifactEvent.artifact.artifactId ); if (existingArtifactIndex !== -1) { if (artifactEvent.append) { const existingArtifact = this.currentTask.artifacts[existingArtifactIndex]; existingArtifact.parts.push(...artifactEvent.artifact.parts); if (artifactEvent.artifact.description) existingArtifact.description = artifactEvent.artifact.description; if (artifactEvent.artifact.name) existingArtifact.name = artifactEvent.artifact.name; if (artifactEvent.artifact.metadata) existingArtifact.metadata = { ...existingArtifact.metadata, ...artifactEvent.artifact.metadata }; } else { this.currentTask.artifacts[existingArtifactIndex] = artifactEvent.artifact; } } else { this.currentTask.artifacts.push(artifactEvent.artifact); } await this.saveCurrentTask(); } else if (!this.currentTask && artifactEvent.taskId) { const loaded = await this.taskStore.load(artifactEvent.taskId, this.serverCallContext); if (loaded) { this.currentTask = loaded; if (!this.currentTask.artifacts) this.currentTask.artifacts = []; const existingArtifactIndex = this.currentTask.artifacts.findIndex( (art) => art.artifactId === artifactEvent.artifact.artifactId ); if (existingArtifactIndex !== -1) { if (artifactEvent.append) { this.currentTask.artifacts[existingArtifactIndex].parts.push( ...artifactEvent.artifact.parts ); } else { this.currentTask.artifacts[existingArtifactIndex] = artifactEvent.artifact; } } else { this.currentTask.artifacts.push(artifactEvent.artifact); } await this.saveCurrentTask(); } else { console.warn( `ResultManager: Received artifact update for unknown task ${artifactEvent.taskId}` ); } } } } async saveCurrentTask() { if (this.currentTask) { await this.taskStore.save(this.currentTask, this.serverCallContext); } } /** * Gets the final result, which could be a Message or a Task. * This should be called after the event stream has been fully processed. * @returns The final Message or the current Task. */ getFinalResult() { if (this.finalMessageResult) { return this.finalMessageResult; } return this.currentTask; } /** * Gets the task currently being managed by this ResultManager instance. * This task could be one that was started with or one created during agent execution. * @returns The current Task or undefined if no task is active. */ getCurrentTask() { return this.currentTask; } }; // src/server/push_notification/push_notification_store.ts var InMemoryPushNotificationStore = class { store = /* @__PURE__ */ new Map(); async save(taskId, pushNotificationConfig) { const configs = this.store.get(taskId) || []; if (!pushNotificationConfig.id) { pushNotificationConfig.id = taskId; } const existingIndex = configs.findIndex((config) => config.id === pushNotificationConfig.id); if (existingIndex !== -1) { configs.splice(existingIndex, 1); } configs.push(pushNotificationConfig); this.store.set(taskId, configs); } async load(taskId) { const configs = this.store.get(taskId); return configs || []; } async delete(taskId, configId) { if (configId === void 0) { configId = taskId; } const configs = this.store.get(taskId); if (!configs) { return; } const configIndex = configs.findIndex((config) => config.id === configId); if (configIndex !== -1) { configs.splice(configIndex, 1); } if (configs.length === 0) { this.store.delete(taskId); } else { this.store.set(taskId, configs); } } }; // src/server/push_notification/default_push_notification_sender.ts var DefaultPushNotificationSender = class { pushNotificationStore; notificationChain; options; constructor(pushNotificationStore, options = {}) { this.pushNotificationStore = pushNotificationStore; this.notificationChain = /* @__PURE__ */ new Map(); this.options = { timeout: 5e3, tokenHeaderName: "X-A2A-Notification-Token", ...options }; } async send(task) { const pushConfigs = await this.pushNotificationStore.load(task.id); if (!pushConfigs || pushConfigs.length === 0) { return; } const lastPromise = this.notificationChain.get(task.id) ?? Promise.resolve(); const newPromise = lastPromise.then(async () => { const dispatches = pushConfigs.map(async (pushConfig) => { try { await this._dispatchNotification(task, pushConfig); } catch (error) { console.error( `Error sending push notification for task_id=${task.id} to URL: ${pushConfig.url}. Error:`, error ); } }); await Promise.all(dispatches); }); this.notificationChain.set(task.id, newPromise); return newPromise.finally(() => { if (this.notificationChain.get(task.id) === newPromise) { this.notificationChain.delete(task.id); } }); } async _dispatchNotification(task, pushConfig) { const url = pushConfig.url; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.options.timeout); try { const headers = { "Content-Type": "application/json" }; if (pushConfig.token) { headers[this.options.tokenHeaderName] = pushConfig.token; } const response = await fetch(url, { method: "POST", headers, body: JSON.stringify(task), signal: controller.signal }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } console.info(`Push notification sent for task_id=${task.id} to URL: ${url}`); } finally { clearTimeout(timeoutId); } } }; // src/extensions.ts var Extensions = { /** * Creates new {@link Extensions} from `current` and `additional`. * If `current` already contains `additional` it is returned unmodified. */ createFrom: (current, additional) => { if (current?.includes(additional)) { return current; } return [...current ?? [], additional]; }, /** * Creates {@link Extensions} from comma separated extensions identifiers as per * https://a2a-protocol.org/latest/specification/#326-service-parameters. * Parses the output of `toServiceParameter`. */ parseServiceParameter: (value) => { if (!value) { return []; } const unique = new Set( value.split(",").map((ext) => ext.trim()).filter((ext) => ext.length > 0) ); return Array.from(unique); }, /** * Converts {@link Extensions} to comma separated extensions identifiers as per * https://a2a-protocol.org/latest/specification/#326-service-parameters. */ toServiceParameter: (value) => { return value.join(","); } }; // src/server/context.ts var ServerCallContext = class { _requestedExtensions; _user; _activatedExtensions; constructor(requestedExtensions, user) { this._requestedExtensions = requestedExtensions; this._user = user; } get user() { return this._user; } get activatedExtensions() { return this._activatedExtensions; } get requestedExtensions() { return this._requestedExtensions; } addActivatedExtension(uri) { this._activatedExtensions = Extensions.createFrom(this._activatedExtensions, uri); } }; // src/server/request_handler/default_request_handler.ts var terminalStates = ["completed", "failed", "canceled", "rejected"]; var DefaultRequestHandler = class { agentCard; taskStore; agentExecutor; eventBusManager; pushNotificationStore; pushNotificationSender; extendedAgentCardProvider; constructor(agentCard, taskStore, agentExecutor, eventBusManager = new DefaultExecutionEventBusManager(), pushNotificationStore, pushNotificationSender, extendedAgentCardProvider) { this.agentCard = agentCard; this.taskStore = taskStore; this.agentExecutor = agentExecutor; this.eventBusManager = eventBusManager; this.extendedAgentCardProvider = extendedAgentCardProvider; if (agentCard.capabilities.pushNotifications) { this.pushNotificationStore = pushNotificationStore || new InMemoryPushNotificationStore(); this.pushNotificationSender = pushNotificationSender || new DefaultPushNotificationSender(this.pushNotificationStore); } } async getAgentCard() { return this.agentCard; } async getAuthenticatedExtendedAgentCard(context) { if (!this.agentCard.supportsAuthenticatedExtendedCard) { throw A2AError.unsupportedOperation("Agent does not support authenticated extended card."); } if (!this.extendedAgentCardProvider) { throw A2AError.authenticatedExtendedCardNotConfigured(); } if (typeof this.extendedAgentCardProvider === "function") { return this.extendedAgentCardProvider(context); } if (context?.user?.isAuthenticated) { return this.extendedAgentCardProvider; } return this.agentCard; } async _createRequestContext(incomingMessage, context) { let task; let referenceTasks; if (incomingMessage.taskId) { task = await this.taskStore.load(incomingMessage.taskId, context); if (!task) { throw A2AError.taskNotFound(incomingMessage.taskId); } if (terminalStates.includes(task.status.state)) { throw A2AError.invalidRequest( `Task ${task.id} is in a terminal state (${task.status.state}) and cannot be modified.` ); } task.history = [...task.history || [], incomingMessage]; await this.taskStore.save(task, context); } const taskId = incomingMessage.taskId || (0, import_uuid.v4)(); if (incomingMessage.referenceTaskIds && incomingMessage.referenceTaskIds.length > 0) { referenceTasks = []; for (const refId of incomingMessage.referenceTaskIds) { const refTask = await this.taskStore.load(refId, context); if (refTask) { referenceTasks.push(refTask); } else { console.warn(`Reference task ${refId} not found.`); } } } const contextId = incomingMessage.contextId || task?.contextId || (0, import_uuid.v4)(); if (context?.requestedExtensions) { const agentCard = await this.getAgentCard(); const exposedExtensions = new Set( agentCard.capabilities.extensions?.map((ext) => ext.uri) || [] ); const validExtensions = context.requestedExtensions.filter( (extension) => exposedExtensions.has(extension) ); context = new ServerCallContext(validExtensions, context.user); } const messageForContext = { ...incomingMessage, contextId, taskId }; return new RequestContext(messageForContext, taskId, contextId, task, referenceTasks, context); } async _processEvents(taskId, resultManager, eventQueue, context, options) { let firstResultSent = false; try { for await (const event of eventQueue.events()) { await resultManager.processEvent(event); try { await this._sendPushNotificationIfNeeded(event, context); } catch (error) { console.error(`Error sending push notification: ${error}`); } if (options?.firstResultResolver && !firstResultSent) { let firstResult; if (event.kind === "message") { firstResult = event; } else { firstResult = resultManager.getCurrentTask(); } if (firstResult) { options.firstResultResolver(firstResult); firstResultSent = true; } } } if (options?.firstResultRejector && !firstResultSent) { options.firstResultRejector( A2AError.internalError("Execution finished before a message or task was produced.") ); } } catch (error) { console.error(`Event processing loop failed for task ${taskId}:`, error); this._handleProcessingError( error, resultManager, firstResultSent, taskId, options?.firstResultRejector ); } finally { this.eventBusManager.cleanupByTaskId(taskId); } } async sendMessage(params, context) { const incomingMessage = params.message; if (!incomingMessage.messageId) { throw A2AError.invalidParams("message.messageId is required."); } const isBlocking = params.configuration?.blocking !== false; const resultManager = new ResultManager(this.taskStore, context); resultManager.setContext(incomingMessage); const requestContext = await this._createRequestContext(incomingMessage, context); const taskId = requestContext.taskId; const finalMessageForAgent = requestContext.userMessage; if (params.configuration?.pushNotificationConfig && this.agentCard.capabilities.pushNotifications) { await this.pushNotificationStore?.save(taskId, params.configuration.pushNotificationConfig); } const eventBus = this.eventBusManager.createOrGetByTaskId(taskId); const eventQueue = new ExecutionEventQueue(eventBus); this.agentExecutor.execute(requestContext, eventBus).catch((err) => { console.error(`Agent execution failed for message ${finalMessageForAgent.messageId}:`, err); const errorTask = { id: requestContext.task?.id || (0, import_uuid.v4)(), // Use existing task ID or generate new contextId: finalMessageForAgent.contextId, status: { state: "failed", message: { kind: "message", role: "agent", messageId: (0, import_uuid.v4)(), parts: [{ kind: "text", text: `Agent execution error: ${err.message}` }], taskId: requestContext.task?.id, contextId: finalMessageForAgent.contextId }, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, history: requestContext.task?.history ? [...requestContext.task.history] : [], kind: "task" }; if (finalMessageForAgent) { if (!errorTask.history?.find((m) => m.messageId === finalMessageForAgent.messageId)) { errorTask.history?.push(finalMessageForAgent); } } eventBus.publish(errorTask); eventBus.publish({ // And publish a final status update kind: "status-update", taskId: errorTask.id, contextId: errorTask.contextId, status: errorTask.status, final: true }); eventBus.finished(); }); if (isBlocking) { await this._processEvents(taskId, resultManager, eventQueue, context); const finalResult = resultManager.getFinalResult(); if (!finalResult) { throw A2AError.internalError( "Agent execution finished without a result, and no task context found." ); } return finalResult; } else { return new Promise((resolve, reject) => { this._processEvents(taskId, resultManager, eventQueue, context, { firstResultResolver: resolve, firstResultRejector: reject }); }); } } async *sendMessageStream(params, context) { const incomingMessage = params.message; if (!incomingMessage.messageId) { throw A2AError.invalidParams("message.messageId is required for streaming."); } const resultManager = new ResultManager(this.taskStore, context); resultManager.setContext(incomingMessage); const requestContext = await this._createRequestContext(incomingMessage, context); const taskId = requestContext.taskId; const finalMessageForAgent = requestContext.userMessage; const eventBus = this.eventBusManager.createOrGetByTaskId(taskId); const eventQueue = new ExecutionEventQueue(eventBus); if (params.configuration?.pushNotificationConfig && this.agentCard.capabilities.pushNotifications) { await this.pushNotificationStore?.save(taskId, params.configuration.pushNotificationConfig); } this.agentExecutor.execute(requestContext, eventBus).catch((err) => { console.error( `Agent execution failed for stream message ${finalMessageForAgent.messageId}:`, err ); const errorTaskStatus = { kind: "status-update", taskId: requestContext.task?.id || (0, import_uuid.v4)(), // Use existing or a placeholder contextId: finalMessageForAgent.contextId, status: { state: "failed", message: { kind: "message", role: "agent", messageId: (0, import_uuid.v4)(), parts: [{ kind: "text", text: `Agent execution error: ${err.message}` }], taskId: requestContext.task?.id, contextId: finalMessageForAgent.contextId }, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, final: true // This will terminate the stream for the client }; eventBus.publish(errorTaskStatus); }); try { for await (const event of eventQueue.events()) { await resultManager.processEvent(event); await this._sendPushNotificationIfNeeded(event, context); yield event; } } finally { this.eventBusManager.cleanupByTaskId(taskId); } } async getTask(params, context) { const task = await this.taskStore.load(params.id, context); if (!task) { throw A2AError.taskNotFound(params.id); } if (params.historyLength !== void 0 && params.historyLength >= 0) { if (task.history) { task.history = task.history.slice(-params.historyLength); } } else { task.history = []; } return task; } async cancelTask(params, context) { const task = await this.taskStore.load(params.id, context); if (!task) { throw A2AError.taskNotFound(params.id); } const nonCancelableStates = ["completed", "failed", "canceled", "rejected"]; if (nonCancelableStates.includes(task.status.state)) { throw A2AError.taskNotCancelable(params.id); } const eventBus = this.eventBusManager.getByTaskId(params.id); if (eventBus) { const eventQueue = new ExecutionEventQueue(eventBus); await this.agentExecutor.cancelTask(params.id, eventBus); await this._processEvents( params.id, new ResultManager(this.taskStore, context), eventQueue, context ); } else { task.status = { state: "canceled", message: { // Optional: Add a system message indicating cancellation kind: "message", role: "agent", messageId: (0, import_uuid.v4)(), parts: [{ kind: "text", text: "Task cancellation requested by user." }], taskId: task.id, contextId: task.contextId }, timestamp: (/* @__PURE__ */ new Date()).toISOString() }; task.history = [...task.history || [], task.status.message]; await this.taskStore.save(task, context); } const latestTask = await this.taskStore.load(params.id, context); if (!latestTask) { throw A2AError.internalError(`Task ${params.id} not found after cancellation.`); } if (latestTask.status.state != "canceled") { throw A2AError.taskNotCancelable(params.id); } return latestTask; } async setTaskPushNotificationConfig(params, context) { if (!this.agentCard.capabilities.pushNotifications) { throw A2AError.pushNotificationNotSupported(); } const task = await this.taskStore.load(params.taskId, context); if (!task) { throw A2AError.taskNotFound(params.taskId); } const { taskId, pushNotificationConfig } = params; if (!pushNotificationConfig.id) { pushNotificationConfig.id = taskId; } await this.pushNotificationStore?.save(taskId, pushNotificationConfig); return params; } async getTaskPushNotificationConfig(params, context) { if (!this.agentCard.capabilities.pushNotifications) { throw A2AError.pushNotificationNotSupported(); } const task = await this.taskStore.load(params.id, context); if (!task) { throw A2AError.taskNotFound(params.id); } const configs = await this.pushNotificationStore?.load(params.id) || []; if (configs.length === 0) { throw A2AError.internalError(`Push notification config not found for task ${params.id}.`); } let configId; if ("pushNotificationConfigId" in params && params.pushNotificationConfigId) { configId = params.pushNotificationConfigId; } else { configId = params.id; } const config = configs.find((c) => c.id === configId); if (!config) { throw A2AError.internalError( `Push notification config with id '${configId}' not found for task ${params.id}.` ); } return { taskId: params.id, pushNotificationConfig: config }; } async listTaskPushNotificationConfigs(params, context) { if (!this.agentCard.capabilities.pushNotifications) { throw A2AError.pushNotificationNotSupported(); } const task = await this.taskStore.load(params.id, context); if (!task) { throw A2AError.taskNotFound(params.id); } const configs = await this.pushNotificationStore?.load(params.id) || []; return configs.map((config) => ({ taskId: params.id, pushNotificationConfig: config })); } async deleteTaskPushNotificationConfig(params, context) { if (!this.agentCard.capabilities.pushNotifications) { throw A2AError.pushNotificationNotSupported(); } const task = await this.taskStore.load(params.id, context); if (!task) { throw A2AError.taskNotFound(params.id); } const { id: taskId, pushNotificationConfigId } = params; await this.pushNotificationStore?.delete(taskId, pushNotificationConfigId); } async *resubscribe(params, context) { if (!this.agentCard.capabilities.streaming) { throw A2AError.unsupportedOperation("Streaming (and thus resubscription) is not supported."); } const task = await this.taskStore.load(params.id, context); if (!task) { throw A2AError.taskNotFound(params.id); } yield task; const finalStates = ["completed", "failed", "canceled", "rejected"]; if (finalStates.includes(task.status.state)) { return; } const eventBus = this.eventBusManager.getByTaskId(params.id); if (!eventBus) { console.warn(`Resubscribe: No active event bus for task ${params.id}.`); return; } const eventQueue = new ExecutionEventQueue(eventBus); try { for await (const event of eventQueue.events()) { if (event.kind === "status-update" && event.taskId === params.id) { yield event; } else if (event.kind === "artifact-update" && event.taskId === params.id) { yield event; } else if (event.kind === "task" && event.id === params.id) { yield event; } } } finally { eventQueue.stop(); } } async _sendPushNotificationIfNeeded(event, context) { if (!this.agentCard.capabilities.pushNotifications) { return; } let taskId = ""; if (event.kind == "task") { const task2 = event; taskId = task2.id; } else { taskId = event.taskId; } if (!taskId) { console.error(`Task ID not found for event ${event.kind}.`); return; } const task = await this.taskStore.load(taskId, context); if (!task) { console.error(`Task ${taskId} not found.`); return; } this.pushNotificationSender?.send(task); } async _handleProcessingError(error, resultManager, firstResultSent, taskId, firstResultRejector) { if (firstResultRejector && !firstResultSent) { firstResultRejector(error); return; } if (!firstResultRejector) { throw error; } const currentTask = resultManager.getCurrentTask(); const errorMessage = error instanceof Error && error.message || "Unknown error"; if (currentTask) { const statusUpdateFailed = { taskId: currentTask.id, contextId: currentTask.contextId, status: { state: "failed", message: { kind: "message", role: "agent", messageId: (0, import_uuid.v4)(), parts: [{ kind: "text", text: `Event processing loop failed: ${errorMessage}` }], taskId: currentTask.id, contextId: currentTask.contextId }, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, kind: "status-update", final: true }; try { await resultManager.processEvent(statusUpdateFailed); } catch (error2) { console.error( `Event processing loop failed for task ${taskId}: ${error2 instanceof Error && error2.message || "Unknown error"}` ); } } else { console.error(`Event processing loop failed for task ${taskId}: ${errorMessage}`); } } }; // src/server/store.ts var InMemoryTaskStore = class { store = /* @__PURE__ */ new Map(); async load(taskId) { const entry = this.store.get(taskId); return entry ? { ...entry } : void 0; } async save(task) { this.store.set(task.id, { ...task }); } }; // src/server/transports/jsonrpc/jsonrpc_transport_handler.ts var JsonRpcTransportHandler = class { requestHandler; constructor(requestHandler) { this.requestHandler = requestHandler; } /** * Handles an incoming JSON-RPC request. * For streaming methods, it returns an AsyncGenerator of JSONRPCResult. * For non-streaming methods, it returns a Promise of a single JSONRPCMessage (Result or ErrorResponse). */ async handle(requestBody, context) { let rpcRequest; try { if (typeof requestBody === "string") { rpcRequest = JSON.parse(requestBody); } else if (typeof requestBody === "object" && requestBody !== null) { rpcRequest = requestBody; } else { throw A2AError.parseError("Invalid request body type."); } if (!this.isRequestValid(rpcRequest)) { throw A2AError.invalidRequest("Invalid JSON-RPC Request."); } } catch (error) { const a2aError = error instanceof A2AError ? error : A2AError.parseError( error instanceof SyntaxError && error.message || "Failed to parse JSON request." ); return { jsonrpc: "2.0", id: rpcRequest?.id !== void 0 ? rpcRequest.id : null, error: a2aError.toJSONRPCError() }; } const { method, id: requestId = null } = rpcRequest; try { if (method !== "agent/getAuthenticatedExtendedCard" && !this.paramsAreValid(rpcRequest.params)) { throw A2AError.invalidParams(`Invalid method parameters.`); } if (method === "message/stream" || method === "tasks/resubscribe") { const params = rpcRequest.params; const agentCard = await this.requestHandler.getAgentCard(); if (!agentCard.capabilities.streaming) { throw A2AError.unsupportedOperation(`Method ${method} requires streaming capability.`); } const agentEventStream = method === "message/stream" ? this.requestHandler.sendMessageStream(params, context) : this.requestHandler.resubscribe(params, context); return (async function* jsonRpcEventStream() { try { for await (const event of agentEventStream) { yield { jsonrpc: "2.0", id: requestId, // Use the original request ID for all streamed responses result: event }; } } catch (streamError) { console.error( `Error in agent event stream for ${method} (request ${requestId}):`, streamError ); throw streamError; } })(); } else { let result; switch (method) { case "message/send": result = await this.requestHandler.sendMessage(rpcRequest.params, context); break; case "tasks/get": result = await this.requestHandler.getTask(rpcRequest.params, context); break; case "tasks/cancel": result = await this.requestHandler.cancelTask(rpcRequest.params, context); break; case "tasks/pushNotificationConfig/set": result = await this.requestHandler.setTaskPushNotificationConfig( rpcRequest.params, context ); break; case "tasks/pushNotificationConfig/get": result = await this.requestHandler.getTaskPushNotificationConfig( rpcRequest.params, context ); break; case "tasks/pushNotificationConfig/delete": await this.requestHandler.deleteTaskPushNotificationConfig(rpcRequest.params, context); result = null; break; case "tasks/pushNotificationConfig/list": result = await this.requestHandler.listTaskPushNotificationConfigs( rpcRequest.params, context ); break; case "agent/getAuthenticatedExtendedCard": result = await this.requestHandler.getAuthenticatedExtendedAgentCard(context); break; default: throw A2AError.methodNotFound(method); } return { jsonrpc: "2.0", id: requestId, result }; } } catch (error) { let a2aError; if (error instanceof A2AError) { a2aError = error; } else { a2aError = A2AError.internalError( error instanceof Error && error.message || "An unexpected error occurred." ); } return { jsonrpc: "2.0", id: requestId, error: a2aError.toJSONRPCError() }; } } // Validates the basic structure of a JSON-RPC request isRequestValid(rpcRequest) { if (rpcRequest.jsonrpc !== "2.0") { return false; } if ("id" in rpcRequest) { const id = rpcRequest.id; const isString = typeof id === "string"; const isInteger = typeof id === "number" && Number.isInteger(id); const isNull = id === null; if (!isString && !isInteger && !isNull) { return false; } } if (!rpcRequest.method || typeof rpcRequest.method !== "string") { return false; } return true; } // Validates that params is an object with non-empty string keys paramsAreValid(params) { if (typeof params !== "object" || params === null || Array.isArray(params)) { return false; } for (const key of Object.keys(params)) { if (key === "") { return false; } } return true; } }; // src/server/authentication/user.ts var UnauthenticatedUser = class { get isAuthenticated() { return false; } get userName() { return ""; } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { A2AError, DefaultExecutionEventBus, DefaultExecutionEventBusManager, DefaultPushNotificationSender, DefaultRequestHandler, ExecutionEventQueue, InMemoryPushNotificationStore, InMemoryTaskStore, JsonRpcTransportHandler, RequestContext, ResultManager, ServerCallContext, UnauthenticatedUser });