Tasks
Reference for task ledgers, task specs, lifecycle methods, workers, statuses, and errors.
import { task, tasks } from "@use-crux/core/tasks";
import type {
AddTaskInput,
JsonObject,
JsonValue,
Task,
TaskCompleteArgs,
TaskEdit,
TaskId,
TaskList,
TaskListStatus,
TaskSpecRecord,
TaskStatus,
TasksHandle,
TasksInput,
} from "@use-crux/core/tasks";
import type { PlanHandle } from "@use-crux/core/plan";Overview
tasks() creates a persistent task ledger and returns a command handle. Use dynamic ledgers for runtime-discovered IDs, or defined ledgers with task() specs for typed IDs and schema-backed completion results.
const work = await tasks({
title: "Launch tasks",
});const work = await tasks({
items: {
research: task("Research channels"),
draft: task("Draft announcement"),
},
});tasks(input?)
Creates a task ledger.
const work = await tasks({
plan: roadmap,
title: "Launch tasks",
metadata: { threadId: "thread-123" },
});interface TasksInput<TItems = undefined> {
plan?: PlanHandle | string;
title?: string;
items?: TItems;
metadata?: JsonObject;
}plan accepts a plan handle or plan ID. Metadata must be JSON-safe.
task(label, options?)
Defines an initial task spec for tasks({ items }). It does not persist anything by itself.
const spec = task("Draft announcement", {
description: "Write the announcement in markdown.",
assignee: { agent: "writer" },
result: z.object({ markdown: z.string() }),
metadata: { phase: "writing" },
});tasks.ref(taskListId)
Returns a handle for an existing task ledger without reading the store first.
const work = tasks.ref(taskListId);
const current = await work.get();tasks.list(options?)
Lists task ledgers from the configured store.
const ledgers = await tasks.list({
plan: roadmap,
limit: 20,
});interface TaskListListOptions {
plan?: PlanHandle | string;
metadata?: Record<string, JsonValue>;
limit?: number;
cursor?: string;
}tasks.tool(options?)
Creates a focused prompt tool that persists a new task ledger. After a model call, use created() to get the created handle.
const makeTasks = tasks.tool({
plan: roadmap,
title: "Execution tasks",
template: "Create short task labels with stable snake_case IDs.",
});
await generate(
prompt({
system: "Create a task ledger for the plan.",
tools: { makeTasks },
}),
{ model, input: {} },
);
const work = makeTasks.created();TasksHandle
interface TasksHandle<TItems = undefined> {
readonly id: string;
get(): Promise<TaskList | null>;
list(options?: { includeRemoved?: boolean }): Promise<Task[]>;
getTask(id: TaskId<TItems>): Promise<Task | null>;
add(
input: TItems extends TaskSpecRecord ? never : AddTaskInput,
): Promise<Task>;
edit(id: TaskId<TItems>, patch: TaskEdit): Promise<Task>;
start(id: TaskId<TItems>): Promise<Task>;
progress(id: TaskId<TItems>, message: string): Promise<Task>;
complete<TTaskId extends TaskId<TItems>>(
id: TTaskId,
...args: TaskCompleteArgs<TItems, TTaskId>
): Promise<Task>;
fail(id: TaskId<TItems>, error: string): Promise<Task>;
skip(id: TaskId<TItems>, reason?: string): Promise<Task>;
cancel(id: TaskId<TItems>, reason?: string): Promise<Task>;
remove(id: TaskId<TItems>): Promise<void>;
discard(reason?: string): Promise<void>;
asContext(options?: TasksContextOptions): Context;
asTools(): Record<string, ToolDef>;
worker(id: TaskId<TItems>, options?: TaskWorkerOptions): TaskWorker;
}Defined ledgers narrow TaskId<TItems> to the keys in items, disable dynamic add(), and infer complete() arguments from each task's result schema. Dynamic ledgers accept arbitrary string IDs and JSON-safe completion results.
Dynamic add() calls require each task ID to be unique within the ledger. Duplicate IDs reject with DuplicateTaskIdError; RecordStore adapters perform this insert atomically so concurrent writers cannot both create the same task ID.
Lifecycle
await work.add({ id: "research", label: "Research channels" });
await work.start("research");
await work.progress("research", "Checking partner launches");
await work.complete("research", { channels: ["partners"] });Allowed lifecycle methods:
start(id):pendingtoin_progressprogress(id, message): writes a progress message and keeps statuscomplete(id, result?): marks work completedfail(id, error): marks work failedskip(id, reason?): marks work skippedcancel(id, reason?): marks work cancelledremove(id): soft-removes the task from active derivationdiscard(reason?): discards the whole ledger and cancels active pending or in-progress tasks
edit() updates non-status display fields only:
await work.edit("research", {
label: "Research launch channels",
metadata: { owner: "growth" },
});Workers
worker(id) creates a task-scoped handle with context and tools bound to one task.
const worker = work.worker("research", {
guidelines: "Return source URLs in the result.",
});
prompt({
use: [roadmap.asContext(), worker.asContext()],
tools: worker.asTools(),
});Statuses
type TaskStatus =
| "pending"
| "in_progress"
| "completed"
| "failed"
| "skipped"
| "cancelled";
type TaskListStatus =
| "pending"
| "in_progress"
| "completed"
| "failed"
| "cancelled"
| "discarded";Ledger status is derived from active task rows:
- empty ledgers and all-pending ledgers are
pending; - any in-progress task makes the ledger
in_progress; - completed or skipped active tasks derive
completed; - failures derive
failedunless work is still in progress; - cancellations derive
cancelledwhen there are no failures or in-progress tasks; discard()setsdiscarded.
Removed tasks are not active.
Errors
Task lifecycle failures throw typed structural errors:
try {
await work.complete("missing");
} catch (error) {
if (error instanceof Error && error.name === "TaskNotFoundError") {
// Read structured fields from the error object.
}
}Public task errors:
TaskListNotFoundErrorTaskNotFoundErrorDuplicateTaskIdErrorTaskRemovedErrorTaskListDiscardedErrorInvalidTaskTransitionErrorTaskResultValidationErrorTaskJsonValueError
Types
interface TaskList {
id: string;
planId?: string;
title?: string;
status: TaskListStatus;
metadata?: JsonObject;
createdAt: number;
updatedAt: number;
completedAt?: number;
discardedAt?: number;
discardReason?: string;
}
interface Task {
id: string;
taskListId: string;
label: string;
description?: string;
status: TaskStatus;
progress?: string;
assignee?: { agent?: string; model?: string };
metadata?: JsonObject;
result?: JsonValue;
error?: string;
durationMs?: number;
createdAt: number;
updatedAt: number;
removedAt?: number;
}