@artinet/sdk
Version:
A TypeScript SDK for building collaborative AI agents.
118 lines (117 loc) • 4.25 kB
JavaScript
/**
* Copyright 2025 The Artinet Project
* SPDX-License-Identifier: Apache-2.0
*/
import { getCurrentTimestamp, formatJson } from "../../../utils/utils.js";
import { validateSchema } from "../../../utils/schema-validation.js";
import { upsertArtifact } from "./artifact.js";
import { A2A } from "../../../types/index.js";
import { logger } from "../../../config/index.js";
const isMessageInHistory = (task, message) => {
return task.history?.find((msg) => msg.messageId === message.messageId);
};
const updateHistory = (task, updateMessage) => {
if (!isMessageInHistory(task, updateMessage)) {
task.history = [...(task.history ?? []), updateMessage];
}
};
export const handleMessageUpdate = async ({ task, update, }) => {
const validated = await validateSchema(A2A.MessageSchema, update);
if (validated.taskId && task.id !== validated.taskId) {
throw new Error(`updateMessage: Invalid task id: incoming: ${validated.taskId} expected: ${task.id} ${formatJson({
cause: {
validated,
actual: task.id,
task,
},
})}`);
}
updateHistory(task, validated);
return task;
};
export const handleTaskUpdate = async ({ context, task, update, }) => {
const validated = await validateSchema(A2A.TaskSchema, update);
if (task.id !== validated.id) {
throw new Error(`updateTask: Invalid task id: incoming: ${validated.id} expected: ${task.id}`, {
cause: validated,
});
}
task = { ...task, ...validated };
if (context.userMessage) {
updateHistory(task, context.userMessage);
}
return task;
};
export const handleStatusUpdate = async ({ task, update, }) => {
const validated = await validateSchema(A2A.TaskStatusUpdateEventSchema, update);
if (validated.taskId && task.id !== validated.taskId) {
throw new Error(`updateTaskStatusUpdate: Invalid task id: incoming: ${validated.taskId} expected: ${task.id}`, { cause: validated });
}
task.status = validated.status;
task.status.timestamp = getCurrentTimestamp();
if (validated.status.message) {
updateHistory(task, validated.status.message);
}
return task;
};
export const handleArtifactUpdate = async ({ task, update }) => {
const validated = await validateSchema(A2A.TaskArtifactUpdateEventSchema, update);
if (validated.taskId && task.id !== validated.taskId) {
throw new Error(`updateTaskArtifactUpdate: Invalid task id: incoming: ${validated.taskId} expected: ${task.id}`, {
cause: validated,
});
}
validated.taskId = task.id;
task.artifacts = upsertArtifact(task.artifacts ?? [], validated);
return task;
};
// The onus is now on the caller to handle errors when processing updates
// and to decide whether the updated task should be saved or not
export const handleUpdate = async ({ context, task, update, }) => {
if (!update || !update.kind) {
throw new Error("updateState: Invalid update", { cause: update });
}
logger.debug(`handleUpdate:`, {
contextId: context?.contextId,
taskId: task?.id,
});
task = await validateSchema(A2A.TaskSchema, task);
if (!context || !context.contextId) {
throw new Error("updateState: Invalid context", { cause: context });
}
switch (update.kind) {
case A2A.Kind.message: {
return handleMessageUpdate({
context,
task,
update,
});
}
case A2A.Kind.task: {
return handleTaskUpdate({
context,
task,
update,
});
}
case A2A.Kind["status-update"]: {
return handleStatusUpdate({
context,
task,
update,
});
}
case A2A.Kind["artifact-update"]: {
return handleArtifactUpdate({
context,
task,
update,
});
}
default: {
throw new Error(`updateState: Invalid update kind: {"kind": "unknown"}`, {
cause: update,
});
}
}
};