@artinet/sdk
Version:
A TypeScript SDK for building collaborative AI agents.
242 lines (241 loc) • 9.07 kB
JavaScript
/**
* Copyright 2025 The Artinet Project
* SPDX-License-Identifier: Apache-2.0
*/
import { A2AError, DefaultPushNotificationSender, } from "@a2a-js/sdk/server";
import { A2A } from "../../types/index.js";
import { PushNotifications } from "./notifications.js";
import { logger } from "../../config/index.js";
import { SystemError, validateSchema } from "../../utils/index.js";
/**We want to avoid pulling @a2a-js/sdk logic into our business logic. */
const toServiceOptions = (context, notify) => {
return {
userId: context?.user?.userName,
extensions: context?.activatedExtensions?.map((extension) => ({
uri: extension,
})) ?? [],
notifier: notify,
};
};
const toA2AError = (error) => {
if (error instanceof A2AError) {
return error;
}
if (error instanceof SystemError) {
return new A2AError(error.code, error.message, error.data, error.taskId);
}
return error;
};
const isGetTaskPushNotificationConfigParam = (params) => {
return A2A.GetTaskPushNotificationConfigParamSchema.safeParse(params).success;
};
const paramsRequired = (params) => {
if (!params || Object.keys(params).length === 0) {
throw A2AError.invalidParams("Params Required");
}
};
const validateParams = async (schema, params) => {
paramsRequired(params);
return await validateSchema(schema, params);
};
export class Native {
_service;
_pushNotifications;
_extendAgentCard;
constructor(_service, _pushNotifications, _extendAgentCard) {
this._service = _service;
this._pushNotifications = _pushNotifications;
this._extendAgentCard = _extendAgentCard;
}
get service() {
return this._service;
}
async getAgentCard() {
return await this.service.getAgentCard();
}
async getTask(params, context) {
if (!params || Object.keys(params).length === 0) {
throw A2AError.invalidParams("Params Required");
}
return await this.service
.getTask(params, toServiceOptions(context))
.catch((error) => {
throw toA2AError(error);
});
}
async cancelTask(params, context) {
paramsRequired(params);
return await this.service
.cancelTask(params, toServiceOptions(context))
.catch((error) => {
throw toA2AError(error);
});
}
async sendMessage(params, context) {
paramsRequired(params);
return await this.service
.sendMessage(params, toServiceOptions(context, {
notify: this.notify,
register: this.registerConfig,
}))
.catch((error) => {
throw toA2AError(error);
});
}
async *sendMessageStream(params, context) {
paramsRequired(params);
try {
yield* this.service.sendMessageStream(params, toServiceOptions(context, {
notify: this.notify,
register: this.registerConfig,
}));
}
catch (error) {
throw toA2AError(error);
}
}
async *resubscribe(params, context) {
paramsRequired(params);
try {
yield* this.service.resubscribe(params, toServiceOptions(context, {
notify: this.notify,
register: this.registerConfig,
}));
}
catch (error) {
throw toA2AError(error);
}
}
get pushNotifications() {
return this._pushNotifications;
}
async pushNotificationsEnabled(taskId) {
if (!(await this._service.getAgentCard())?.capabilities?.pushNotifications ||
!this.pushNotifications) {
throw A2AError.pushNotificationNotSupported();
}
if (taskId && !(await this._service.tasks.has(taskId))) {
throw A2AError.taskNotFound(taskId ?? "Unknown task");
}
return;
}
async setTaskPushNotificationConfig(_params, _context) {
const params = await validateParams(A2A.TaskPushNotificationConfigSchema, _params);
await this.pushNotificationsEnabled(params.taskId);
const { taskId, pushNotificationConfig } = params;
pushNotificationConfig.id = pushNotificationConfig.id ?? taskId;
await this.pushNotifications.save(taskId, pushNotificationConfig);
logger.debug("Setting push notification config for task: ", {
taskId,
pushNotificationConfigId: pushNotificationConfig.id,
});
return {
taskId,
pushNotificationConfig,
};
}
async getTaskPushNotificationConfig(_params, _context) {
const params = await validateParams(A2A.GetTaskPushNotificationConfigParamsSchema, _params);
await this.pushNotificationsEnabled(params.id);
const { id: taskId } = params;
const configs = await this.pushNotifications.load(taskId);
if (!configs) {
throw A2AError.internalError(`Push notification config not found for task: ${taskId}`);
}
let configId = taskId;
if (isGetTaskPushNotificationConfigParam(params) &&
params.pushNotificationConfigId) {
configId = params.pushNotificationConfigId;
}
const pushNotificationConfig = configs.find((config) => config.id === configId);
if (!pushNotificationConfig) {
throw A2AError.internalError(`Push notification config not found for task: ${taskId} and config: ${configId}`);
}
return {
taskId,
pushNotificationConfig,
};
}
async listTaskPushNotificationConfigs(_params, _context) {
const params = await validateParams(A2A.ListTaskPushNotificationConfigParamsSchema, _params);
await this.pushNotificationsEnabled(params.id);
const { id: taskId } = params;
const configs = await this.pushNotifications.load(taskId);
if (!configs) {
return [];
}
return configs.map((pushNotificationConfig) => ({
taskId,
pushNotificationConfig,
}));
}
async deleteTaskPushNotificationConfig(_params, _context) {
const params = await validateParams(A2A.DeleteTaskPushNotificationConfigParamsSchema, _params);
await this.pushNotificationsEnabled(params.id);
const { id: taskId } = params;
await this.pushNotifications.delete(taskId);
}
registerConfig = async (taskId, config) => {
await this.pushNotificationsEnabled(taskId);
await this.pushNotifications?.save(taskId, config);
};
notify = async (task, _update, _context) => {
const enabled = await this.pushNotificationsEnabled(task.id)
.then(() => true)
.catch(() => {
logger.warn("Push notifications not enabled for task: ", {
taskId: task.id,
});
return false;
});
if (!enabled) {
return;
}
return await this.pushNotifications.send(task);
};
async getAuthenticatedExtendedAgentCard(context) {
if (!(await this.getAgentCard()).supportsAuthenticatedExtendedCard) {
throw A2AError.unsupportedOperation("Agent does not support authenticated extended card.");
}
if (!this._extendAgentCard) {
throw A2AError.authenticatedExtendedCardNotConfigured();
}
if (typeof this._extendAgentCard === "function") {
return await this._extendAgentCard(context);
}
if (context?.user?.isAuthenticated) {
return this._extendAgentCard;
}
return await this.getAgentCard();
}
static create(service, _pushNotifications, extendAgentCard) {
if (!_pushNotifications) {
const pushNotifications = new PushNotifications();
pushNotifications.sender = new DefaultPushNotificationSender(pushNotifications);
return new Native(service, pushNotifications, extendAgentCard);
}
if (_pushNotifications instanceof PushNotifications) {
return new Native(service, _pushNotifications, extendAgentCard);
}
const pushNotifications = new PushNotifications(_pushNotifications?.store);
pushNotifications.sender =
_pushNotifications?.sender ??
new DefaultPushNotificationSender(pushNotifications);
return new Native(service, pushNotifications, extendAgentCard);
}
}
/**
* native adapter for `@a2a-js/sdk`
* @param service - The service to wrap
* @param pushNotifications - (optional) arguments for creating {@link PushNotifications}
* @param extendAgentCard - (optional) The extend agent card/provider to use
* @returns A {@link A2ARequestHandler} instance
* @example
* ```typescript
* const agent = cr8("Custom Agent")
* .text("Hello!")
* .agent;
* const nativeAdapter = native(agent, pushNotifications, extendAgentCard);
* ```
*/
export const native = Native.create;