feat: add agent task schema and CRUD API (closes #96, closes #97)

This commit is contained in:
Jason Woltje
2026-01-29 23:26:22 -06:00
parent 59aec28d5c
commit da4fb72902
13 changed files with 2534 additions and 4 deletions

View File

@@ -0,0 +1,224 @@
import { Injectable, NotFoundException } from "@nestjs/common";
import { PrismaService } from "../prisma/prisma.service";
import { AgentTaskStatus, AgentTaskPriority } from "@prisma/client";
import type {
CreateAgentTaskDto,
UpdateAgentTaskDto,
QueryAgentTasksDto,
} from "./dto";
/**
* Service for managing agent tasks
*/
@Injectable()
export class AgentTasksService {
constructor(private readonly prisma: PrismaService) {}
/**
* Create a new agent task
*/
async create(
workspaceId: string,
userId: string,
createAgentTaskDto: CreateAgentTaskDto
) {
const data: any = {
...createAgentTaskDto,
workspaceId,
createdById: userId,
status: createAgentTaskDto.status || AgentTaskStatus.PENDING,
priority: createAgentTaskDto.priority || AgentTaskPriority.MEDIUM,
agentConfig: createAgentTaskDto.agentConfig || {},
};
// Set startedAt if status is RUNNING
if (data.status === AgentTaskStatus.RUNNING) {
data.startedAt = new Date();
}
// Set completedAt if status is COMPLETED or FAILED
if (
data.status === AgentTaskStatus.COMPLETED ||
data.status === AgentTaskStatus.FAILED
) {
data.completedAt = new Date();
if (!data.startedAt) {
data.startedAt = new Date();
}
}
const agentTask = await this.prisma.agentTask.create({
data,
include: {
createdBy: {
select: { id: true, name: true, email: true },
},
},
});
return agentTask;
}
/**
* Get paginated agent tasks with filters
*/
async findAll(query: QueryAgentTasksDto) {
const page = query.page || 1;
const limit = query.limit || 50;
const skip = (page - 1) * limit;
// Build where clause
const where: any = {
workspaceId: query.workspaceId,
};
if (query.status) {
where.status = query.status;
}
if (query.priority) {
where.priority = query.priority;
}
if (query.agentType) {
where.agentType = query.agentType;
}
if (query.createdById) {
where.createdById = query.createdById;
}
// Execute queries in parallel
const [data, total] = await Promise.all([
this.prisma.agentTask.findMany({
where,
include: {
createdBy: {
select: { id: true, name: true, email: true },
},
},
orderBy: {
createdAt: "desc",
},
skip,
take: limit,
}),
this.prisma.agentTask.count({ where }),
]);
return {
data,
meta: {
total,
page,
limit,
totalPages: Math.ceil(total / limit),
},
};
}
/**
* Get a single agent task by ID
*/
async findOne(id: string, workspaceId: string) {
const agentTask = await this.prisma.agentTask.findUnique({
where: {
id,
workspaceId,
},
include: {
createdBy: {
select: { id: true, name: true, email: true },
},
},
});
if (!agentTask) {
throw new NotFoundException(`Agent task with ID ${id} not found`);
}
return agentTask;
}
/**
* Update an agent task
*/
async update(
id: string,
workspaceId: string,
updateAgentTaskDto: UpdateAgentTaskDto
) {
// Verify agent task exists
const existingTask = await this.prisma.agentTask.findUnique({
where: { id, workspaceId },
});
if (!existingTask) {
throw new NotFoundException(`Agent task with ID ${id} not found`);
}
const data: any = { ...updateAgentTaskDto };
// Handle startedAt based on status changes
if (updateAgentTaskDto.status) {
if (
updateAgentTaskDto.status === AgentTaskStatus.RUNNING &&
existingTask.status === AgentTaskStatus.PENDING &&
!existingTask.startedAt
) {
data.startedAt = new Date();
}
// Handle completedAt based on status changes
if (
(updateAgentTaskDto.status === AgentTaskStatus.COMPLETED ||
updateAgentTaskDto.status === AgentTaskStatus.FAILED) &&
existingTask.status !== AgentTaskStatus.COMPLETED &&
existingTask.status !== AgentTaskStatus.FAILED
) {
data.completedAt = new Date();
if (!existingTask.startedAt) {
data.startedAt = new Date();
}
}
}
const agentTask = await this.prisma.agentTask.update({
where: {
id,
workspaceId,
},
data,
include: {
createdBy: {
select: { id: true, name: true, email: true },
},
},
});
return agentTask;
}
/**
* Delete an agent task
*/
async remove(id: string, workspaceId: string) {
// Verify agent task exists
const agentTask = await this.prisma.agentTask.findUnique({
where: { id, workspaceId },
});
if (!agentTask) {
throw new NotFoundException(`Agent task with ID ${id} not found`);
}
await this.prisma.agentTask.delete({
where: {
id,
workspaceId,
},
});
return { message: "Agent task deleted successfully" };
}
}