Add tasks: the crew's shared to-do list
Bots and workspaces each keep a list of work tracked until it's done, and the bots move it along themselves as they work. - tasks (one collection per bot or per workspace), task_notes (the progress log) and task_media (a deliverable or a reference file, its bytes a private media row served at /media/private/<id>) - statuses todo/doing/blocked/done/cancelled, with blocked_reason while only the user can unblock it - visibility as SQL in src/utils/task-access: a user's own bots and workspaces, a run's own list plus its workspaces, everything for the coordinator; every tool and endpoint resolves through it - list_tasks, create_task and update_task on every run, so a chat reply, a task and a routine all read the list, log progress and file what they produced - TasksNote on every run and TaskReminderNote in chat, so a greeting or a proposed next step brings pending work up in one measured line - tasks group on the workspace dashboard and a tasks modal in the conversation header, sharing one form, row and detail panel - a bot's or workspace's own chat uploads are read before the delete so none are stranded; bot files keep their folder Also corrects the README's RAM guidance and table alignment.
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import { z } from "zod";
|
||||
import { AppData } from "@/src/data/app-data";
|
||||
import { TaskMediaKinds, TaskStatuses } from "@/src/dict/task-dict";
|
||||
|
||||
const status_values = TaskStatuses.map((status) => status.value) as [string, ...string[]];
|
||||
const media_kind_values = TaskMediaKinds.map((kind) => kind.value) as [string, ...string[]];
|
||||
|
||||
/**
|
||||
* One collection: a bot's own list or a workspace's shared one. The helper
|
||||
* refuses both or neither (`taskScopeValues`).
|
||||
*/
|
||||
const scope = {
|
||||
bot_id: z.number().int().positive().optional(),
|
||||
workspace_id: z.number().int().positive().optional(),
|
||||
};
|
||||
|
||||
export const CreateTaskRequestSchema = z.object({
|
||||
...scope,
|
||||
title: z.string().trim().min(1).max(AppData.TaskTitleMaxLength),
|
||||
description: z.string().max(AppData.TaskDescriptionMaxLength).optional(),
|
||||
status: z.enum(status_values).optional(),
|
||||
});
|
||||
|
||||
export const UpdateTaskRequestSchema = z.object({
|
||||
task_id: z.number().int().positive(),
|
||||
title: z.string().trim().min(1).max(AppData.TaskTitleMaxLength).optional(),
|
||||
description: z.string().max(AppData.TaskDescriptionMaxLength).optional(),
|
||||
status: z.enum(status_values).optional(),
|
||||
blocked_reason: z.string().max(AppData.TaskNoteMaxLength).nullable().optional(),
|
||||
});
|
||||
|
||||
export const DeleteTaskRequestSchema = z.object({
|
||||
task_id: z.number().int().positive(),
|
||||
});
|
||||
|
||||
export const TaskNoteRequestSchema = z.object({
|
||||
task_id: z.number().int().positive(),
|
||||
text: z.string().trim().min(1).max(AppData.TaskNoteMaxLength),
|
||||
});
|
||||
|
||||
/**
|
||||
* Linking a file: one from the run's folders (or the user's own uploads)
|
||||
* or a fresh upload, already saved as a private `media` row.
|
||||
*/
|
||||
export const TaskFileRequestSchema = z.object({
|
||||
task_id: z.number().int().positive(),
|
||||
file_id: z.number().int().positive().optional(),
|
||||
media_id: z.number().int().positive().optional(),
|
||||
kind: z.enum(media_kind_values).optional(),
|
||||
});
|
||||
|
||||
export const DetachTaskFileRequestSchema = z
|
||||
.object({
|
||||
task_id: z.number().int().positive(),
|
||||
task_media_id: z.number().int().positive().optional(),
|
||||
media_id: z.number().int().positive().optional(),
|
||||
})
|
||||
.refine((body) => Boolean(body.task_media_id || body.media_id), {
|
||||
message: "Which file?",
|
||||
});
|
||||
|
||||
/**
|
||||
* Uploading one deliverable straight onto a task, like a chat attachment.
|
||||
*/
|
||||
export const UploadTaskFileRequestSchema = z.object({
|
||||
task_id: z.number().int().positive(),
|
||||
data_url: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
kind: z.enum(media_kind_values).optional(),
|
||||
});
|
||||
|
||||
export type CreateTaskRequest = z.infer<typeof CreateTaskRequestSchema>;
|
||||
export type UpdateTaskRequest = z.infer<typeof UpdateTaskRequestSchema>;
|
||||
export type TaskNoteRequest = z.infer<typeof TaskNoteRequestSchema>;
|
||||
export type TaskFileRequest = z.infer<typeof TaskFileRequestSchema>;
|
||||
Reference in New Issue
Block a user