KilnDocs
Developer tools

Tasks, runs and subagents

How tasks and runs look through the API, MCP and SDK — stages, states, sources, errors, follow-ups, subagents and what an agent's run token may do.

A task is a work item seen through the API: one request with its conversation, runs and outputs. A run is one agent execution. The foreman path starts several runs per task; agents can start subagents, so runs form a tree whose first run is the task's root. When a stage finishes, the foreman adds a short update to the conversation, for example "Implement done. Next: Verify." When a run ends on a temporary platform error (the network or the egress proxy away during a restart, a provider overloaded), Kiln retries it after 5 s, 20 s and 60 s, and then runs the stage again after 1, 2, 5, 10, 20 and 30 minutes. None of this reaches the conversation or asks anyone; the task stops only when all of it fails.

Dispatching work

curl -X POST "$KILN_URL/api/v1/factories/webapp/runs" -H "authorization: Bearer $KILN_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"prompt": "Fix the rounding bug in invoices", "ticket_ref": "linear:PAY-123", "mode": "plan"}'

The reply has taskId, the first runId when one started, runUrl, state and foremanAgent. ticket_ref is <source>:<id> (linear:, jira:, github:owner/repo#12, or anything else, kept as is). branch makes the task work on that branch.

One task per incident: a report that matches an open task joins it instead of starting new work. The match is by identity first: correlation_key (an incident id or alert fingerprint), alert payload fields such as fingerprint, dedup_key or incident_id, the tracker issue, the source URL or the pull request. Reports from a different source (an alert and a Slack message) also match by content when they share enough salient words such as service names, hosts and error codes. The reply then carries deduplicated: { into, reason, score } (POST /api/v1/work-items answers 200 instead of 201), the report and its files are added to the task, and every place it was reported hears the updates. Pass "dedupe": false to always start a new task. The triage agent can also fold a task into an open one it recognises as the same work. Tune it per factory:

deduplication: { enabled: true, windowHours: 24, threshold: 0.5 }

Modes: normal; plan (the factory always writes a plan or spec first and waits for a person to approve it before anything is built); orchestrate (the foreman and implement agents are told to split the work across subagents).

Task stages

Stage Kiln states
TRIAGE received, triaging
SPEC planning, awaiting_spec
IMPLEMENT building, awaiting_preview
REVIEW reviewing, handed_off
COMPLETE complete
CANCELLED cancelled, failed

PATCH …/tasks/{id} may set any stage. COMPLETE needs decide:merge and closes the task as a person's confirmation; CANCELLED cancels it and every run and subagent still going. DELETE is a soft delete. Task outputs (PLAN, PULL_REQUEST, SCREENSHOT, FILE, EXTERNAL_REFERENCE) come newest first.

Run states and sources

State Meaning
QUEUED Waiting for a self-hosted worker.
CLAIMED A worker took it and is preparing the sandbox.
INPROGRESS The agent is working.
BLOCKED Waiting on a person: an interrupt or a paused run.
SUCCEEDED, FAILED, ERROR, CANCELLED Finished. FAILED means the task could not be done as asked; ERROR means the platform failed.

The coarse task_status filter groups them: running, blocked, failed, cancelled, complete. Each run also has a source (LINEAR, SLACK, GITHUB_WEBHOOK, API, CLI, MCP, WEB_APP, SCHEDULED_AGENT, ORCHESTRATION, SELF_IMPROVEMENT, BENCHMARK_TRIAL, CUSTOM_WEBHOOK, …), a title, creator and executor, rootRunId, and up to 20 immutable metadata keys you can filter on with metadata[key]=value.

GET /api/v1/runs filters by state, task_status, source, factory_uid, ancestor_run_id, artifact_type, q, creator, mine=true, dates and metadata, sorts with sort_by, and pages with cursor.

Errors

Errors carry a code from the catalog (GET /api/v1/errors): for example budget_exceeded, insufficient_credits, integration_not_configured, environment_setup_failed (FAILED) and internal_error, resource_unavailable, infrastructure_timeout, agent_process_failed (ERROR). Kiln retries internal_error and resource_unavailable on the same run up to three times before it reports the run.

Talking to a run

  • POST /runs/{id}/followups {message, attachments} steers a live run, or continues a finished one in a new run with the same agent, branch and task.
  • POST /runs/{id}/interrupt stops what the agent is doing; the run is BLOCKED until a follow-up.
  • GET /runs/{id}/timeline, /conversation and /transcript show what happened; /scores its scores.
  • PUT /runs/{id}/sharing {publicAccess: "VIEWER" | "EDITOR" | null} shares the run with signed-in people who cannot otherwise see the factory.
  • With the factory's followUpPermission: RUN_CREATOR, follow-ups, steering and interrupts come only from the person who started the run (the first run of a continuation chain), its task's requester, or a factory admin; others get not_authorized (403). See factory.yaml.
  • Agents stay alive for follow-ups for idleTimeoutMinutes (default 10, 60 for subagents).

Subagents and run tokens

A running agent gets KILN_RUN_TOKEN and the Kiln MCP server. With it, it may:

  • read its own task and the runs of its task or run tree, message its task, and upload files;
  • start subagents: POST /runs/{id}/spawn (MCP spawn_agent) or several at once with run-agents (run_agents). The child's spawnableBy decides who may start it; nesting stops at five levels. Three options shape a subagent:
    • name: lowercase letters, digits and dashes, unique among the runs of the tree. The agent can then use the name instead of the run id in followups, interrupt and the other run calls. A continuation keeps the name, so it always means the latest run of that line of work.
    • fork: true: the child's prompt starts with the parent's conversation so far (its request, its messages and people's steering), then the brief. Without it the child gets only the brief.
    • report: true: when the child finishes, fails, is stopped or runs out of budget, or asks a question, the parent gets a follow-up with a report: the child's name, title, summary, branch, the reason (finished, failed, stopped, out_of_budget or needs_input) and the question. A live parent is steered with it; a parent that finished continues in a new run. No polling is needed;
  • ask the requester a question (POST /runs/{id}/questions, MCP ask_user) with single- or multi-select options and a recommended option, and read the answer from the gate;
  • propose a definition change (propose_definition_change) that people review in the inbox;
  • call complete_task, which closes the task only after its pull request merged.

A run token cannot decide gates, write definitions, see other tasks or factories, merge or approve.

Edit this page on GitHub