@a2a-js/sdk 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -12,6 +12,7 @@ export declare class DefaultRequestHandler implements A2ARequestHandler {
|
|
|
12
12
|
constructor(agentCard: AgentCard, taskStore: TaskStore, agentExecutor: AgentExecutor, eventBusManager?: ExecutionEventBusManager);
|
|
13
13
|
getAgentCard(): Promise<AgentCard>;
|
|
14
14
|
private _createRequestContext;
|
|
15
|
+
private _processEvents;
|
|
15
16
|
sendMessage(params: MessageSendParams): Promise<Message | Task>;
|
|
16
17
|
sendMessageStream(params: MessageSendParams): AsyncGenerator<Message | Task | TaskStatusUpdateEvent | TaskArtifactUpdateEvent, void, undefined>;
|
|
17
18
|
getTask(params: TaskQueryParams): Promise<Task>;
|
|
@@ -4,6 +4,7 @@ import { A2AError } from "../error.js";
|
|
|
4
4
|
import { DefaultExecutionEventBusManager } from "../events/execution_event_bus_manager.js";
|
|
5
5
|
import { ExecutionEventQueue } from "../events/execution_event_queue.js";
|
|
6
6
|
import { ResultManager } from "../result_manager.js";
|
|
7
|
+
const terminalStates = ["completed", "failed", "canceled", "rejected"];
|
|
7
8
|
export class DefaultRequestHandler {
|
|
8
9
|
agentCard;
|
|
9
10
|
taskStore;
|
|
@@ -29,6 +30,10 @@ export class DefaultRequestHandler {
|
|
|
29
30
|
if (!task) {
|
|
30
31
|
throw A2AError.taskNotFound(incomingMessage.taskId);
|
|
31
32
|
}
|
|
33
|
+
if (terminalStates.includes(task.status.state)) {
|
|
34
|
+
// Throw an error that conforms to the JSON-RPC Invalid Request error specification.
|
|
35
|
+
throw A2AError.invalidRequest(`Task ${task.id} is in a terminal state (${task.status.state}) and cannot be modified.`);
|
|
36
|
+
}
|
|
32
37
|
}
|
|
33
38
|
if (incomingMessage.referenceTaskIds && incomingMessage.referenceTaskIds.length > 0) {
|
|
34
39
|
referenceTasks = [];
|
|
@@ -51,11 +56,41 @@ export class DefaultRequestHandler {
|
|
|
51
56
|
const contextId = incomingMessage.contextId || uuidv4();
|
|
52
57
|
return new RequestContext(messageForContext, taskId, contextId, task, referenceTasks);
|
|
53
58
|
}
|
|
59
|
+
async _processEvents(taskId, resultManager, eventQueue, options) {
|
|
60
|
+
let firstResultSent = false;
|
|
61
|
+
try {
|
|
62
|
+
for await (const event of eventQueue.events()) {
|
|
63
|
+
await resultManager.processEvent(event);
|
|
64
|
+
if (options?.firstResultResolver && !firstResultSent) {
|
|
65
|
+
if (event.kind === 'message' || event.kind === 'task') {
|
|
66
|
+
options.firstResultResolver(event);
|
|
67
|
+
firstResultSent = true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (options?.firstResultRejector && !firstResultSent) {
|
|
72
|
+
options.firstResultRejector(A2AError.internalError('Execution finished before a message or task was produced.'));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
console.error(`Event processing loop failed for task ${taskId}:`, error);
|
|
77
|
+
if (options?.firstResultRejector && !firstResultSent) {
|
|
78
|
+
options.firstResultRejector(error);
|
|
79
|
+
}
|
|
80
|
+
// re-throw error for blocking case to catch
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
this.eventBusManager.cleanupByTaskId(taskId);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
54
87
|
async sendMessage(params) {
|
|
55
88
|
const incomingMessage = params.message;
|
|
56
89
|
if (!incomingMessage.messageId) {
|
|
57
90
|
throw A2AError.invalidParams('message.messageId is required.');
|
|
58
91
|
}
|
|
92
|
+
// Default to blocking behavior if 'blocking' is not explicitly false.
|
|
93
|
+
const isBlocking = params.configuration?.blocking !== false;
|
|
59
94
|
const taskId = incomingMessage.taskId || uuidv4();
|
|
60
95
|
// Instantiate ResultManager before creating RequestContext
|
|
61
96
|
const resultManager = new ResultManager(this.taskStore);
|
|
@@ -64,12 +99,14 @@ export class DefaultRequestHandler {
|
|
|
64
99
|
// Use the (potentially updated) contextId from requestContext
|
|
65
100
|
const finalMessageForAgent = requestContext.userMessage;
|
|
66
101
|
const eventBus = this.eventBusManager.createOrGetByTaskId(taskId);
|
|
102
|
+
// EventQueue should be attached to the bus, before the agent execution begins.
|
|
67
103
|
const eventQueue = new ExecutionEventQueue(eventBus);
|
|
68
|
-
// Start agent execution (non-blocking)
|
|
104
|
+
// Start agent execution (non-blocking).
|
|
105
|
+
// It runs in the background and publishes events to the eventBus.
|
|
69
106
|
this.agentExecutor.execute(requestContext, eventBus).catch(err => {
|
|
70
107
|
console.error(`Agent execution failed for message ${finalMessageForAgent.messageId}:`, err);
|
|
71
|
-
// Publish a synthetic error event
|
|
72
|
-
//
|
|
108
|
+
// Publish a synthetic error event, which will be handled by the ResultManager
|
|
109
|
+
// and will also settle the firstResultPromise for non-blocking calls.
|
|
73
110
|
const errorTask = {
|
|
74
111
|
id: requestContext.task?.id || uuidv4(), // Use existing task ID or generate new
|
|
75
112
|
contextId: finalMessageForAgent.contextId,
|
|
@@ -93,7 +130,7 @@ export class DefaultRequestHandler {
|
|
|
93
130
|
errorTask.history?.push(finalMessageForAgent);
|
|
94
131
|
}
|
|
95
132
|
}
|
|
96
|
-
eventBus.publish(errorTask);
|
|
133
|
+
eventBus.publish(errorTask);
|
|
97
134
|
eventBus.publish({
|
|
98
135
|
kind: "status-update",
|
|
99
136
|
taskId: errorTask.id,
|
|
@@ -103,17 +140,24 @@ export class DefaultRequestHandler {
|
|
|
103
140
|
});
|
|
104
141
|
eventBus.finished();
|
|
105
142
|
});
|
|
106
|
-
|
|
107
|
-
//
|
|
108
|
-
await
|
|
143
|
+
if (isBlocking) {
|
|
144
|
+
// In blocking mode, wait for the full processing to complete.
|
|
145
|
+
await this._processEvents(taskId, resultManager, eventQueue);
|
|
146
|
+
const finalResult = resultManager.getFinalResult();
|
|
147
|
+
if (!finalResult) {
|
|
148
|
+
throw A2AError.internalError('Agent execution finished without a result, and no task context found.');
|
|
149
|
+
}
|
|
150
|
+
return finalResult;
|
|
109
151
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
152
|
+
else {
|
|
153
|
+
// In non-blocking mode, return a promise that will be settled by fullProcessing.
|
|
154
|
+
return new Promise((resolve, reject) => {
|
|
155
|
+
this._processEvents(taskId, resultManager, eventQueue, {
|
|
156
|
+
firstResultResolver: resolve,
|
|
157
|
+
firstResultRejector: reject,
|
|
158
|
+
});
|
|
159
|
+
});
|
|
113
160
|
}
|
|
114
|
-
// Cleanup after processing is complete for taskId
|
|
115
|
-
this.eventBusManager.cleanupByTaskId(taskId);
|
|
116
|
-
return finalResult;
|
|
117
161
|
}
|
|
118
162
|
async *sendMessageStream(params) {
|
|
119
163
|
const incomingMessage = params.message;
|
package/package.json
CHANGED