@dexwox-labs/a2a-client
Version:
TypeScript client implementation for Google's Agent-to-Agent (A2A) protocol - includes HTTP/WebSocket communication, circuit breakers and error handling
430 lines • 18.3 kB
JavaScript
;
/**
* @module TaskClient
* @description Client for managing tasks in the A2A protocol
*/
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
var _, done = false;
for (var i = decorators.length - 1; i >= 0; i--) {
var context = {};
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
if (kind === "accessor") {
if (result === void 0) continue;
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
if (_ = accept(result.get)) descriptor.get = _;
if (_ = accept(result.set)) descriptor.set = _;
if (_ = accept(result.init)) initializers.unshift(_);
}
else if (_ = accept(result)) {
if (kind === "field") initializers.unshift(_);
else descriptor[key] = _;
}
}
if (target) Object.defineProperty(target, contextIn.name, descriptor);
done = true;
};
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
var useValue = arguments.length > 2;
for (var i = 0; i < initializers.length; i++) {
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
}
return useValue ? value : void 0;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TaskClient = void 0;
const a2a_core_1 = require("@dexwox-labs/a2a-core");
const error_handler_1 = require("./utils/error-handler");
const http_utils_1 = require("./utils/http-utils");
const events_1 = require("events");
const types_1 = require("./types");
/**
* Client for managing tasks in the A2A protocol
*
* The TaskClient provides methods for creating, monitoring, and managing tasks
* with support for real-time status updates and push notifications.
*
* @example
* ```typescript
* const taskClient = new TaskClient({ baseUrl: 'https://a2a-server.example.com' });
*
* // Get task status
* const task = await taskClient.getTaskStatus('task-123');
* console.log(`Task status: ${task.status}`);
*
* // Cancel a task
* await taskClient.cancelTask('task-123');
* ```
*/
let TaskClient = (() => {
let _classDecorators = [(0, a2a_core_1.TraceClass)()];
let _classDescriptor;
let _classExtraInitializers = [];
let _classThis;
let _classSuper = events_1.EventEmitter;
var TaskClient = class extends _classSuper {
static { _classThis = this; }
static {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
TaskClient = _classThis = _classDescriptor.value;
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
__runInitializers(_classThis, _classExtraInitializers);
}
options;
/** @private Cache of push notification configurations by task ID */
pushConfigs = new Map();
/** @private Map of task ID to callback functions for task updates */
taskCallbacks = new Map();
/**
* Creates a new TaskClient instance
* @param options - Configuration options for the client
*/
constructor(options) {
super();
this.options = options;
}
/**
* Gets the current status of a task
*
* Retrieves the current state and details of a task by its ID. This method
* fetches the complete task object including status, input, output, and any
* error information.
*
* @param taskId - The ID of the task to check
* @returns Promise resolving to the complete task object
* @throws {A2ANetworkError} If there's a network issue contacting the server
* @throws {A2AValidationError} If the task ID is invalid or not found
*
* @example
* ```typescript
* try {
* const task = await taskClient.getTaskStatus('task-123');
* console.log(`Task status: ${task.status}`);
*
* if (task.status === 'completed') {
* console.log('Task output:', task.output);
* } else if (task.status === 'failed') {
* console.error('Task failed:', task.error);
* }
* } catch (error) {
* console.error('Error checking task status:', error.message);
* }
* ```
*/
async getTaskStatus(taskId) {
const request = {
jsonrpc: '2.0',
method: 'getTaskStatus',
params: { taskId }
};
try {
const response = await (0, http_utils_1.sendRequest)(this.options, request);
return response.result.task;
}
catch (err) {
if (err instanceof Error && err.message.includes('Network')) {
throw new error_handler_1.A2ANetworkError('Failed to get task status', {
originalError: err,
taskId
});
}
throw (0, error_handler_1.normalizeError)(err);
}
}
/**
* Cancels a running task
*
* Attempts to cancel a task that is currently in progress. This will transition
* the task to the 'canceled' state if successful. Tasks that have already
* completed or failed cannot be canceled.
*
* @param taskId - The ID of the task to cancel
* @returns Promise resolving when cancellation is complete
* @throws {A2ANetworkError} If there's a network issue contacting the server
* @throws {A2AValidationError} If the task cannot be canceled (e.g., already completed)
*
* @example
* ```typescript
* try {
* await taskClient.cancelTask('task-123');
* console.log('Task canceled successfully');
* } catch (error) {
* if (error.code === 'VALIDATION_ERROR') {
* console.error('Cannot cancel task:', error.message);
* } else {
* console.error('Error canceling task:', error.message);
* }
* }
* ```
*/
async cancelTask(taskId) {
const request = {
jsonrpc: '2.0',
method: 'cancelTask',
params: { taskId }
};
try {
await (0, http_utils_1.sendRequest)(this.options, request);
}
catch (err) {
if (err instanceof Error && err.message.includes('Network')) {
throw new error_handler_1.A2ANetworkError('Failed to cancel task', {
originalError: err,
taskId
});
}
throw (0, error_handler_1.normalizeError)(err);
}
}
/**
* Lists all tasks for the current session
* @param options Optional filters for the task list
* @returns Promise resolving to an array of tasks
* @throws A2AError if the request fails
*/
/**
* Sets push notification configuration for a task
*
* Configures server-side push notifications for task status updates. This allows
* your application to receive real-time updates about task progress without polling.
*
* @param taskId - The task ID to configure notifications for
* @param config - Push notification settings including endpoint and events to subscribe to
* @returns Promise resolving when configuration is complete
* @throws {A2ANetworkError} If there's a network issue contacting the server
* @throws {A2ATimeoutError} If the request times out
* @throws {A2AValidationError} If the configuration is invalid
*
* @example
* ```typescript
* await taskClient.setPushConfig('task-123', {
* enabled: true,
* endpoint: 'https://my-app.example.com/webhooks/tasks',
* authToken: 'secret-token-123',
* events: ['task.updated', 'task.completed', 'task.failed']
* });
*
* console.log('Push notifications configured for task');
* ```
*/
async setPushConfig(taskId, config) {
const request = {
jsonrpc: '2.0',
method: 'setPushConfig',
params: { taskId, config }
};
try {
await (0, http_utils_1.sendRequest)(this.options, request);
this.pushConfigs.set(taskId, config);
}
catch (err) {
if (err instanceof Error && err.message.includes('timeout')) {
throw new error_handler_1.A2ATimeoutError('Task operation timed out', {
originalError: err,
taskId
});
}
throw (0, error_handler_1.normalizeError)(err);
}
}
/**
* Gets push notification configuration for a task
*
* Retrieves the current push notification settings for a task. This method
* first checks the local cache and only makes a server request if needed.
*
* @param taskId - The task ID to check
* @returns Promise resolving to the push notification configuration
* @throws {A2ANetworkError} If there's a network issue contacting the server
* @throws {A2AValidationError} If the task ID is invalid or not found
*
* @example
* ```typescript
* const config = await taskClient.getPushConfig('task-123');
*
* console.log('Push notifications enabled:', config.enabled);
* console.log('Subscribed events:', config.events);
* console.log('Webhook endpoint:', config.endpoint);
* ```
*/
async getPushConfig(taskId) {
// Return cached config if available
if (this.pushConfigs.has(taskId)) {
return this.pushConfigs.get(taskId);
}
const request = {
jsonrpc: '2.0',
method: 'getPushConfig',
params: { taskId }
};
try {
const response = await (0, http_utils_1.sendRequest)(this.options, request);
this.pushConfigs.set(taskId, response.result.config);
return response.result.config;
}
catch (err) {
throw (0, error_handler_1.normalizeError)(err);
}
}
/**
* Updates the status of a task
*
* Changes a task's status from one state to another. The transition is validated
* to ensure it follows the allowed state machine transitions in the A2A protocol.
*
* @param taskId - The ID of the task to update
* @param status - Object containing the current state and target state
* @param status.from - The current state of the task
* @param status.to - The desired new state of the task
* @returns Promise resolving when the status update is complete
* @throws {A2ANetworkError} If there's a network issue contacting the server
* @throws {A2AValidationError} If the state transition is invalid
*
* @example
* ```typescript
* // Mark a task as completed
* await taskClient.updateTaskStatus('task-123', {
* from: 'working',
* to: 'completed'
* });
*
* // Mark a task as failed
* await taskClient.updateTaskStatus('task-456', {
* from: 'working',
* to: 'failed'
* });
* ```
*/
async updateTaskStatus(taskId, status) {
(0, a2a_core_1.validateTransition)(status.from, status.to);
const request = {
jsonrpc: '2.0',
method: 'updateTaskStatus',
params: { taskId, status }
};
try {
await (0, http_utils_1.sendRequest)(this.options, request);
}
catch (err) {
if (err instanceof Error && err.message.includes('Network')) {
throw new error_handler_1.A2ANetworkError('Failed to update task status', {
originalError: err,
taskId
});
}
throw (0, error_handler_1.normalizeError)(err);
}
}
/**
* Lists tasks matching the specified criteria
*
* Retrieves a list of tasks from the server, with optional filtering by status,
* limiting the number of results, and filtering by creation date.
*
* @param options - Optional filter criteria for the task list
* @param options.status - Optional filter by task status
* @param options.limit - Optional maximum number of tasks to return
* @param options.since - Optional ISO timestamp to filter tasks created after this time
* @returns Promise resolving to an array of tasks matching the criteria
* @throws {A2ANetworkError} If there's a network issue contacting the server
*
* @example
* ```typescript
* // Get all tasks
* const allTasks = await taskClient.listTasks();
*
* // Get only completed tasks
* const completedTasks = await taskClient.listTasks({ status: 'completed' });
*
* // Get the 10 most recent tasks
* const recentTasks = await taskClient.listTasks({ limit: 10 });
*
* // Get tasks created in the last hour
* const oneHourAgo = new Date(Date.now() - 3600000).toISOString();
* const recentTasks = await taskClient.listTasks({ since: oneHourAgo });
* ```
*/
async listTasks(options) {
const request = {
jsonrpc: '2.0',
method: 'listTasks',
params: options || {}
};
try {
const response = await (0, http_utils_1.sendRequest)(this.options, request);
return response.result.tasks;
}
catch (err) {
throw (0, error_handler_1.normalizeError)(err);
}
}
/**
* Registers a callback for task updates
*
* Sets up a callback function to be called whenever a specific task is updated.
* This provides a way to monitor task progress in real-time without polling.
*
* @param taskId - The ID of the task to monitor
* @param callback - Function to call when the task is updated
*
* @example
* ```typescript
* // Monitor a specific task
* taskClient.onTaskUpdate('task-123', (task) => {
* console.log(`Task ${task.id} updated:`, task.status);
*
* if (task.status === 'completed') {
* console.log('Task completed with result:', task.output);
* } else if (task.status === 'failed') {
* console.error('Task failed with error:', task.error);
* }
* });
* ```
*/
onTaskUpdate(taskId, callback) {
this.taskCallbacks.set(taskId, callback);
this.on(types_1.TASK_UPDATED, (updatedTask) => {
if (updatedTask.id === taskId) {
callback(updatedTask);
}
});
}
/**
* Handles incoming task updates and triggers callbacks
*
* This internal method processes task updates received from the server and
* triggers the appropriate callbacks and events. It's typically called by
* the MessageClient when streaming updates.
*
* @param task - The updated task object
* @internal
*/
handleTaskUpdate(task) {
// Trigger specific callback if registered
const callback = this.taskCallbacks.get(task.id);
if (callback) {
callback(task);
}
// Emit appropriate event based on task state
this.emit(types_1.TASK_UPDATED, task);
if (task.status === 'completed') {
this.emit(types_1.TASK_COMPLETED, task);
this.taskCallbacks.delete(task.id);
}
else if (task.status === 'failed' || task.status === 'canceled') {
this.emit(types_1.TASK_FAILED, task);
this.taskCallbacks.delete(task.id);
}
}
};
return TaskClient = _classThis;
})();
exports.TaskClient = TaskClient;
//# sourceMappingURL=task-client.js.map