Crux
GuidesPlans & Tasks

Plans & Tasks

Give agents persistent intent documents and structured work ledgers.

Agents lose track of multi-step work. The plan lives in a context window that gets compacted, progress lives in chat history that the next process never sees, and nobody outside the conversation can inspect what the agent intends to do or how far it got. Plans & Tasks moves that state out of the transcript and into durable, inspectable records.

It gives agents two coordination surfaces:

  • plan() stores the intent document: goal, approach, constraints, and revisions.
  • tasks() stores the work ledger: task IDs, statuses, progress messages, results, and errors.
  • task() defines typed initial task specs when you want known IDs and schema-checked results.

Use either piece on its own, or combine them with prompt contexts, focused tools, workers, React hooks, and devtools.

Quick Start

plans-and-tasks.ts
import { z } from "zod";
import { prompt, generate } from "@use-crux/ai";
import { plan } from "@use-crux/core/plan";
import { task, tasks } from "@use-crux/core/tasks";

const launchPlan = await plan({
  title: "Launch guide",
  content: "Research channels, draft the announcement, review, then publish.",
  metadata: { threadId: "thread-123" },
});

const work = await tasks({
  plan: launchPlan,
  title: "Launch work",
  items: {
    research: task("Research launch channels", {
      result: z.object({ channels: z.array(z.string()) }),
    }),
    draft: task("Draft announcement", {
      result: z.object({ markdown: z.string() }),
    }),
  },
});

const worker = work.worker("research");

await generate(
  prompt({
    use: [launchPlan.asContext(), worker.asContext()],
    tools: worker.asTools(),
    system: "Complete the assigned task. Use the plan as the source of intent.",
  }),
  { model, input: {} },
);

const status = (await work.get())?.status;

When To Use What

Use plan() when the agent needs an inspectable intent document.

Use tasks() when the user or another agent needs to see discrete work states.

Use flow() when you need suspendable orchestration with named steps and resume semantics.

Use an external durable runner when you need queues, retries, schedules, concurrency control, or long-running jobs.

Imports

import { plan } from "@use-crux/core/plan";
import { task, tasks } from "@use-crux/core/tasks";

Core Model

plan() returns a command handle with a stable id. Call get() when you need the latest plan data.

const roadmap = await plan({ title: "Q1 roadmap", content: "..." });
await roadmap.update({ content: "Updated direction." });
const latest = await roadmap.get();

tasks() returns a task-ledger handle. Dynamic ledgers can add any string ID.

const work = await tasks({ plan: roadmap, title: "Roadmap tasks" });

await work.add({ id: "research", label: "Research customer asks" });
await work.start("research");
await work.progress("research", "Reading interview notes");
await work.complete("research", { themes: ["activation", "billing"] });

Defined ledgers infer task IDs from items.

const work = await tasks({
  items: {
    review: task("Review plan"),
  },
});

await work.start("review");

Composition

Plans and task ledgers expose prompt context and tools from the handle:

prompt({
  use: [roadmap.asContext(), work.asContext()],
  tools: {
    ...roadmap.asTools(),
    ...work.asTools(),
  },
});

Worker handles scope context and tools to one assigned task:

const writer = work.worker("draft", {
  guidelines: "Write in markdown and include source links.",
});

prompt({
  use: [roadmap.asContext(), writer.asContext()],
  tools: writer.asTools(),
});

Next Steps

On this page