> ## Documentation Index
> Fetch the complete documentation index at: https://docs.slaunt.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Thread and run lifecycle state machine

> Slaunt's persisted thread state machine: transitions, idempotency, state and conflict errors, automatic runs and telemetry gaps.

Each thread has a persisted state machine, and the lifecycle tools move it. Quaero is built from these transitions: an agent with no running run isn't shown, and one that stops reporting goes stale. Most lifecycle errors come from calling a tool in the wrong state, which the table below explains.

| From | Event | To |
| - | - | - |
| `UNBOUND` | `thread_begin` | `THREAD_READY` |
| `THREAD_READY` | `run_begin` | `RUNNING` |
| `RUNNING` | `run_step` | `RUNNING` |
| `RUNNING` | `run_end` | `IDLE` |
| `IDLE` | `run_begin` | `RUNNING` |
| `IDLE` | `thread_begin` | `THREAD_READY` (rotate into a new chat) |
| `IDLE` | `thread_close` | `CLOSED` |
| `CLOSED` | `thread_begin` | `THREAD_READY` |

Anything else is rejected. In practice: one run at a time per thread; you can't start a new thread mid-run; `thread_close` is only valid between runs.

## Idempotency

Network retries are safe. `thread_begin` requires a `request_id` (max 256 chars); the other lifecycle tools default it to the JSON-RPC `id`. Sending the same `request_id` again returns the original result instead of applying the transition twice. Generate a new one for each new attempt, not each retry.

## Errors

An invalid transition returns `-32602` with the current state and what's allowed from it. Don't retry it; fix the call.

```json theme={null}
{"code":-32602,"message":"run_begin is invalid while thread state is RUNNING","data":{"state":"RUNNING","allowed":["run_step","run_end"],"state_version":3}}
```

Two writers racing on one thread (e.g. a hook and the model) get `-32009`. This one is safe to retry.

```json theme={null}
{"code":-32009,"message":"Thread state changed concurrently; expected version 3, current version is 4","data":{"state":"RUNNING","state_version":4,"expected_state_version":3,"retryable":true}}
```

## Other behavior

* **Automatic runs:** a provider tool call after `thread_begin` can start a minimal run on its own, but Quaero then has no task description. Call `run_begin` when you know the task.
* **Telemetry gaps:** a `RUNNING` run with no `run_step` for about 45 seconds shows as stale (`telemetry_gap_open: true`). Send steps at real milestones, not as a heartbeat.
* **Stops:** when someone stops the agent, `runtime.stop_requested` is set on the next lifecycle response. End the run with `outcome: "killed"` or `"interrupted"`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.