Work
ProThe data behind agent work: plans, approvals, runs, diffs, logs and tests.
Preview
It's types and helpers, listed below.
Install
With the Candor CLI
First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/work
Types
- PlanStep
{ id: string title: string status: RunStatus /** What the step does, in a sentence. */ summary?: string | undefined agent?: AgentIdentity | undefined /** Steps this one waits for. */ dependsOn?: string[] | undefined /** The things it touches, when known. */ touches?: EntityRef[] | undefined /** Required when status is "failed". */ failure?: string | undefined duration?: string | undefined /** * Ho- PlanStepDrift
- A step where the run left the approved plan: `"added"` (not in the plan that was approved), `"skipped"` (approved, but the run didn't do it) or `"changed"` (done differently from what was approved).
{ kind: "added" | "skipped" | "changed" /** Why, in a sentence: "The billing table had no country for 212 customers." */ note?: string | undefined /** For `"changed"`: the step as it was approved, in a few words ("Backfill from billing country"). */ from?: string | undefined }- PlanAutonomyOption
- What accepting a plan can allow the agent to do without asking. Two have built-in words in `Plan`: `"askBeforeEdits"` and `"runWithoutAsking"`; your own need `label` and `detail`.
{ /** Passed to `onAccept` when the person picks it. */ id: string label?: string | undefined /** What the agent may do without asking, in a sentence. */ detail?: string | undefined /** Why this option can't be picked here, e.g. "Your organisation asks before every edit." */ locked?: string | undefined }- ClarifyingQuestion
| { id: string kind: "choice" prompt: string options: { id: string; label: string; detail?: string | undefined }[] multiple?: boolean | undefined recommended?: string | undefined /** A short label shown above the question: "Library", "Approach". */ tag?: string | undefined /** Adds an "Other" option that takes the person's own words, sent as the answer's `text`. */ other?: boolean | undefined } |- ClarifyingAnswer
- `choice` holds the chosen option ids; `text` the person's own words (a text question, or "Other").
{ questionId: string; choice?: string[] | undefined; text?: string | undefined }- ReviewLevel
- How much a person reviews before the agents act.
"R1" | "R2" | "R3" | "R4" | "R5"- ApprovalState
| "pending" // waiting for someone eligible to decide | "submitting" // a decision is being recorded | "approved" | "rejected" // by a person | "rejected-by-system" // its facts changed (the target was removed, the environment drifted) | "withdrawn" // by the requester | "expired" // its time (createdAt to expiresAt) ran out without a decision | "failed"- ApprovalImpact
{ /** The things the change touches (tables, settings, files). */ components: EntityRef[] /** Records affected; "unknown" when it can't be measured before running. */ rows: number | "unknown" }- Rollback
{ available: true; how: string } | { available: false; reason: string }- ApprovalValueChange
- One value the change sets, before and after: a role, a limit, a setting. `from: null` means it had no value ("Not set"); `from` left out means the app didn't record what it was, which is shown as that, never guessed.
{ label: string; from?: string | null | undefined; to: string | null }- ApprovalRequest
{ id: string title: string summary: string requester: { name: string } | { agent: AgentIdentity } /** Who an agent asked on behalf of, when an agent asked for a person. */ onBehalfOf?: { name: string } | undefined /** The environment's name, e.g. "Shop". */ environment: string /** What tells same-named ones apart, e.g. "dev-2". */ detail?: string | undefined production: boolean /** sha256 of the c- DiffLine
{ kind: "add" | "remove" | "context"; text: string; oldNo?: number | undefined; newNo?: number | undefined }- DiffHunk
{ header?: string | undefined; lines: DiffLine[] }- FileDiff
{ path: string /** What the file is, e.g. a field or a layout. */ entity?: EntityRef | undefined change: "create" | "update" | "delete" /** e.g. "xml", "ts", "python"; shown as a label. */ language?: string | undefined hunks: DiffHunk[] }- BlastRadiusComponent
{ entity: EntityRef change: "create" | "update" | "delete" | "read" /** Line counts when a diff exists; null when it doesn't. */ diff: { added: number; removed: number } | null }- BlastRadiusScope
| { kind: "measured" components: BlastRadiusComponent[] /** Kinds of thing the agent couldn't read, so couldn't rule out. */ unread: string[] } | { kind: "unknown"; reason: string }- LogSeverity
"debug" | "info" | "warn" | "error"- LogItem
| { kind: "line" id: string at?: string | undefined severity: LogSeverity message: string /** Who wrote it: an agent's name, a tool, the environment, "system". */ actor?: string | undefined stepId?: string | undefined } /** Lines that should be here but aren't, and why. Never silently skipped. */ | { kind: "gap"; id: string; missing: number | null; reason: string }- TestCase
{ id: string className: string method: string outcome: "pass" | "fail" | "skip" duration?: string | undefined message?: string | undefined stack?: string | undefined }- TestRun
{ status: RunStatus cases: TestCase[] /** Code coverage across the environment, 0–1. Absent until the run measured it. */ coverage?: number | undefined /** What production deploys require, 0–1. */ coverageRequired?: number | undefined duration?: string | undefined }- RunNode
{ id: string agent: AgentIdentity task: string status: RunStatus duration?: string | undefined /** The agent's own check of its work, when it ran one. */ verdict?: { outcome: "pass" | "fail" | "unsure"; note: string } | undefined children?: RunNode[] | undefined }- BackgroundTask
{ id: string title: string status: RunStatus agent?: AgentIdentity | undefined /** "Step 3 of 5", only when the service knows the total. */ progress?: { done: number; total: number } | undefined startedAt: string /** Set when the task needs the person. */ needs?: string | undefined /** When the app last heard anything from it. Running and silent for longer than the tray's `staleAfter`, it says "No- ResultColumn
{ key: string; label: string; numeric?: boolean | undefined }- ResultTable
{ columns: ResultColumn[] rows: Record<string, string | number | null>[] /** Rows the query matched; may exceed rows shown. */ total?: number | undefined query?: string | undefined }- FileFormat
- A format a file downloads as, e.g. `{ id: "pdf", label: "PDF" }`.
{ id: string; label: string }- Deliverable
- A file the work produced. Downloads go through your handler, never a link from model output.
{ id: string name: string /** In bytes. */ size?: number | undefined /** e.g. "application/pdf": picks the icon. */ mediaType?: string | undefined /** The formats it downloads as (Word, PDF); a single plain download when left out. */ formats?: FileFormat[] | undefined /** Where it stands, in your words: "Checked", "Not yet checked". */ state?: string | undefined /** "attention" for a state to look- Artifact
- A document or file to read in the artifact panel.
{ id: string title: string /** The document as markdown text, read into safe blocks (headings, lists, emphasis, code), never HTML. */ text?: string | undefined /** The document as typed blocks, when you have them already; used instead of `text`. */ blocks?: MessageBlock[] | undefined /** A file with no text to show: its icon, size and, for an image, the preview your app serves. */ file?: Attachmen- AgentActivityEntry
- One agent, what it is doing now and its state.
{ agent: AgentIdentity /** What it is doing now, in plain words; left out when it isn't working. */ doing?: string | undefined status: RunStatus /** Your word for the status ("Standing by"); defaults to the status's own label. */ statusLabel?: string | undefined /** When it started what it is doing. */ since?: string | undefined /** When the app last heard from it. Running and silent past `staleAf- ReceiptLine
- One line of a result's receipt: what it cost, how long it took, what checked it. Values are your data, as given.
{ label: string; value: string; note?: string | undefined }- DecisionChoice
- One way to answer a decision, and what it leads to.
{ id: string; label: string; detail?: string | undefined }- Decision
- A question with two answers that the work stops for (raise a limit or finish with what's done; keep or change): the facts to decide on, what carries on meanwhile, and the two choices.
{ id: string question: string /** What the person needs to decide, as label and value: "Spent so far" · "$1.62 of $2". */ facts?: { label: string; value: string }[] | undefined /** What carries on while it waits for an answer, in a sentence. */ continues?: string | undefined /** When it needs an answer by. */ deadline?: string | undefined /** What happens if nobody answers by `deadline`, in a sent