Skip to main content
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. 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.
Two writers racing on one thread (e.g. a hook and the model) get -32009. This one is safe to retry.

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".