Tasks
A task is one execution of a runtime: a single agent conversation from start to finish. Every task runs in its own isolated sandbox, so executions never share filesystem, process, or memory state, and one user’s run can never touch another’s.
Tasks come in two shapes:
- Interactive: a live session you send follow-up messages to. Between turns the session can sit
idle, holding its sandbox open and waiting for the next message until an idle timeout tears it down. - One-shot: a single prompt that runs to completion and ends.
Once a task finishes, its replayable record lives on as a conversation, the unit you investigate, judge, and experiment over.
Lifecycle
A task moves through a well-defined set of statuses:
| Status | Meaning |
|---|---|
pending | Created, not yet accepted for execution. |
queued | Accepted and waiting for a sandbox. |
scheduled | Assigned to run; sandbox is being provisioned. |
running | The agent is actively working. |
awaiting_user | The agent paused on an interrupt (for example a tool permission) and is waiting for the caller to resume. |
idle | Interactive session is alive and waiting for the next message. |
completed | Finished successfully. |
failed | Ended with an error. |
cancelling | A cancel was requested; the task is winding down. |
cancelled | Cancelled and stopped. |
One-shot tasks typically run queued → scheduled → running → completed. Interactive tasks return to idle between turns and pick up again at running when you send a new message.
A run can also pause for a human in the loop. When the agent hits an interrupt (for example a tool that needs permission), the task moves to awaiting_user and surfaces what it is blocked on: the task’s metadata lists its pending interrupts until they are answered. The caller answers each interrupt as resolved (optionally with a payload) or cancelled, and the task continues. Resolving every pending interrupt returns the task to running; answering only some keeps it awaiting_user. Resuming is idempotent — re-sending an already-resolved batch is a no-op rather than an error. See the SDK pages (JavaScript, Python) for the resume API.
Each task gets its own isolated sandbox, and there is exactly one task per
sandbox. An interactive task’s sandbox stays alive across the idle window so
follow-up turns reuse the same filesystem and context.
Task runs
Within a task, a task run is a segment of work opened against a runtime (or against an experiment). A single task can span several runs; each follow-up turn opens a new one.
A run carries a kind that expresses your intent:
| Kind | What it does |
|---|---|
prompt | Opens a fresh turn from a prompt. |
steer | Injects guidance into the turn that’s already in progress. Falls back to prompt if no turn is active. |
clear | Resets context: starts a new conversation in the same task sandbox. Takes no prompt text. |
Because runs are the unit of streaming and cancellation, you can watch a run’s output live and cancel a run mid-flight without discarding the task.
A run request with no prompt and no kind is a provision call: on a task that has already finished (completed or failed), it restarts the sandbox without opening a turn and returns the task to pending, so the environment is warm before the next prompt arrives. On a task that is still active it’s a no-op that reports the current status.
Cancelling a run
Cancellation has two modes:
| Mode | What it does |
|---|---|
abort (default) | Interrupts the in-flight turn immediately and keeps the sandbox warm. The task drops back to idle and the next prompt reuses the same container — what an interactive “stop” button wants. A bodyless cancel aborts. |
drain | Lets the active turn finish and return control, then tears the sandbox down at the next settle boundary. The task lands cancelled and the next prompt cold-boots on a fresh sandbox — the release path. Pass drain_within_seconds to force teardown if the task doesn’t settle in time. |
Draining a task that is awaiting_user keeps its pending interrupts in the task metadata, so the human-in-the-loop question survives the teardown: a later resume rehydrates it on a fresh sandbox instead of losing the exchange.
Resumable streams
A run’s live stream can be severed (a network blip, a gateway idle timeout) without losing anything. Re-attach with the last event id you saw (the SSE Last-Event-ID header or a ?since= cursor) and the run replays its recent frames; anything older than the replay window can be hydrated from the task’s conversation record, deduplicating by item id. If you re-attach before the run is live (its sandbox is still provisioning), pass wait_for_start=0 to get a 429 with a Retry-After hint instead of holding the connection open — back off and retry.
Working with tasks in code
The platform task API is available in the SDKs and CLI. JavaScript is shown first; select CLI for the equivalent shell flow.
JavaScript SDK
import { IntrospectionClient } from "@introspection-sdk/introspection-node";
const client = new IntrospectionClient({
token: process.env.INTROSPECTION_TOKEN,
});
// Open a runner against a named runtime, bound to an end-user identity.
const runner = await client.runtimes("support-agent").run({
identity: { user_id: "user_123" },
});
// Start a task, returns a handle on its initial run.
const run = await runner.tasks.start({ prompt: "Summarize my open tickets" });
// Stream the run as typed AG-UI events.
for await (const ev of run.stream()) console.log(ev);
await runner.close();
await client.shutdown();A run can be streamed or cancelled mid-flight:
// Cancel an in-progress run. The default aborts the turn and keeps the
// sandbox warm, so the task returns to `idle` and stays resumable.
await run.cancel();To release the sandbox after the active turn settles, request drain mode explicitly:
await run.cancel({ mode: "drain", drain_within_seconds: 60 });The Python SDK accepts the equivalent dictionary, and Rust uses TaskCancelOptions::Drain. Bodyless cancellation remains the backwards-compatible abort path.
For an interactive task, send a follow-up turn by opening another run on the same task. For example, a steer run nudges the agent while it works, or a clear run resets context in the same sandbox.
A RunHandle exposes stream() for the live event feed and cancel() to stop
the run. See the API Reference for the full task and run
surface, including listing, retrieving, and resuming tasks.
Related concepts
- Runtimes: the deployable agent version a task executes.
- Conversations: the immutable, replayable record a task leaves behind.
- Experiments: A/B tests that runs can be opened against.
- API Reference: the full task and run API.