Fleet
Pools, workers, worker versions (including an agent version's LLM vendor, model and effort), and the agent-skill and image catalogues that staff stages.
The fleet is what staffs your stages: a pool leases a task to a worker, which runs a specific worker version. Agents can carry skills, and every worker runs from a container image. This page is the reference for all of them.
Viewing the fleet needs read; configuring it needs admin. See
Authentication. Replace https://cadence.example.com
with your instance's address. For how these fit together, see
Pools and workers.
Pools
A pool pins exactly one worker version and leases tasks to it. POST/GET/PATCH /api/v1/spaces/:space_key/pools[/:id] and .../pools/:id/repin; MCP
pools_create, pools_get, pools_list, pools_update, pools_repin,
pools_archive, pools_delete; the web interface (pause, resume, repin).
Listing and getting need read; the rest need admin.
Fields:
| Field | Notes |
|---|---|
worker_version_id | The one version this pool runs. Required. |
size | How many workers the pool runs. Zero or more. |
debug | Runs the pool in debug mode. Defaults to false. |
max_lifetime_seconds | A cap on a lease's lifetime, counted from when the lease was dispatched. Without one, the space's default lease lifetime applies; without that, a platform default for the worker kind applies. See When a lease reaches its max lifetime. |
lease_scopes | Which tasks a lease may reach (see below). Defaults to task and parent. |
lease_scopes is any of:
task— the leased task and its children.parent— the leased task's parent and its children.dependencies— the task's direct dependency neighbours.workflow— every task on the lease's workflow.space— every task in the space.
Any other value is refused.
Over the API, pool create takes kind — AgentPool, RunnerPool, or
ClassifierPool — with size and worker_version_id; a create with any other
kind is refused with invalid_kind. Over MCP, pools_create takes no kind:
it derives the kind from the pinned version, so an agent version makes an
AgentPool, a runner version a RunnerPool, and a classifier version a
ClassifierPool. Pool update accepts only size and worker_version_id.
debug, max_lifetime_seconds, lease_scopes, and pause state are set over MCP
or the web interface. Pausing a pool stops it dispatching new leases
while keeping its size.
A pool has no LLM vendor of its own. An agent pool's workers draw on the vendor
named by the llm_vendor of the agent version
the lease runs, and are served LLM_TOKEN and LLM_PROVIDER from that vendor's
registration — the pool's space's registration, otherwise the instance-wide one.
Repinning a pool to a version with another vendor changes the vendor its workers
draw on. A worker does not start while no token of that vendor can be served:
the dispatch records a spin-up failure and holds the task until a person acts. See
LLM providers.
pools_create and pools_update no longer take llm_vendor, and the pool form in
the browser no longer has an LLM vendor field. Both tools' input schemas declare no
other arguments, so an MCP client that checks a call against the schema refuses an
llm_vendor argument before sending it; Cadence itself ignores one that arrives
and sets no vendor. To change the vendor, repin the pool to a version that names
another vendor.
A classifier version carries no LLM vendor. A classifier draws on a
question-answering provider, and that provider's key reaches it as a
credential-sourced environment variable declared on the pinned version, the way
any worker's secret does (see Credentials). Like a
runner pool, a classifier pool takes the shorter platform lifetime leash, not the
longer one an agent pool gets when no max_lifetime_seconds or space default is
set.
Pinning a pool to a version it cannot use is refused, and what you see depends on
the surface. Over the API, updating a pool (PATCH .../pools/:id) to a version that
does not exist returns unknown_worker_version_id; a version that exists but is the
wrong kind or is not yet ready is refused with invalid_request, on create, update,
and repin alike. Over MCP, pools_create and pools_update report the message
unknown worker_version_id for a version that does not exist.
Archiving a pool with in-flight leases is refused with pool has in-flight leases; pause and drain before retiring. A pool that has ever run a lease cannot be
deleted — pool has lease history and cannot be deleted; archive it instead —
only archived.
When a lease reaches its max lifetime
A lease's max lifetime is the cap resolved from the max_lifetime_seconds row
above, counted from when the lease was dispatched, not from when the task was
queued.
In the last 120 seconds before the cap, the lease's heartbeat answers expire,
unless the lease is being cancelled, when it answers cancel. expire is the
worker's warning to wind down. The worker hears it only if it sends a heartbeat in
that window. See the command-line client's heartbeat
for the control words and what a worker does with expire.
Cadence checks every lease against its cap once a minute, so it kills a lease
within about a minute after the cap passes. This applies whether the lease is
running, paused or being cancelled. The lease ends killed, and Cadence does not
retry it. If the lease was working a task, Cadence also holds the task with this
reason:
dispatch blocked — manual intervention needed: <pool> (lease killed: exceeded its max lifetime of <N>s)<pool> is the pool's name and <N> the cap in seconds. If the task already
carries a blocking hold, Cadence leaves that hold in place and adds no second one.
While a blocking hold stands, Cadence does not lease the task again, and Cadence
never clears a blocking hold itself. To get the task worked again, see
Hold a task and clear a hold.
Workers
A worker is a named agent, runner, or classifier. POST/GET/PATCH /api/v1/spaces/:space_key/workers[/:id]; MCP workers_register, workers_list,
workers_update, workers_delete (delete is MCP-only). Listing needs read;
registering, updating, and deleting need admin.
A worker has a kind — Agent, Runner, or Classifier — a key (lowercase letters, digits,
-, and _, starting with a letter) unique in the space, a name, and a
description. Registering is idempotent on the space and key: registering the
same key again returns the existing worker.
You can update a worker's name, description, and key. Any other field is
refused with unpermitted_fields. Renaming the key while a lease is active is
refused with worker has an active lease — drain or cancel before renaming.
Deleting is refused for a worker with lease history or one pinned by a pool.
Worker versions
A worker version is a frozen, runnable configuration of a worker. POST/PATCH /api/v1/spaces/:space_key/workers/:worker_id/versions[/:id], .../publish,
.../fork; POST /api/v1/worker_versions/:id/archive; DELETE /api/v1/worker_versions/:id; MCP worker_versions_create_draft,
worker_versions_update_draft, worker_versions_publish, worker_versions_fork,
worker_versions_archive, worker_versions_discard, worker_versions_list,
worker_versions_get. Listing and getting need read; the rest need admin.
A version moves through three states: draft, ready, and archived. You build
a draft, then publish it to ready; publishing freezes its declarative fields,
so a published version is immutable and a change means a new draft.
An agent version carries image_id, env, size (cpu and memory),
services, system_prompt (with system_prompt_mode of replace or append),
llm_vendor, model, an optional effort, mcp_servers, skill_version_ids,
initial_prompt, and the hooks up, run, and down. A runner version
carries a command. An agent version needs initial_prompt, an llm_vendor, and
a model defined for that vendor to be published — see Choosing a vendor, model
and effort. A runner version needs command.
A classifier version carries image_id, env, size, services, the hooks
up, run, and down, and — in place of a prompt or a command — two JSON
documents, input and output. Each defaults to an empty object, so a classifier
version needs neither an initial_prompt nor a command to be published:
inputnames what Cadence hands the worker:attachments, a list of{ name }objects naming which of the task's attachments to hand it, andquestions, a list carried through to the provider untouched.outputnames how the worker's answer reaches the task:checks, a map of Check code to a Liquid expression, andproperties, a map of Property key to a Liquid expression. Each expression is rendered against the worker's reported result — see How anoutputexpression is rendered.
A classifier version does not take the other kinds' fields: system_prompt,
system_prompt_mode, llm_vendor, model, effort, and mcp_servers.
worker_versions_create_draft drops any of them it is given and creates the draft
without them; worker_versions_update_draft refuses them (for example, System prompt is not allowed on a Classifier). initial_prompt and command must be
blank. An output checks map is refused if it names a Check
code the space does not have; each expression must be valid Liquid template text.
Both documents join the version's other declarative fields in being frozen once
the version is published.
How an output expression is rendered
Each checks and properties expression is Liquid template text, rendered over a
single variable, result — the JSON object the worker reports as the details
of its completion (cadence worker complete --details <json>, or the details
object on POST /api/v1/leases/:id/complete; see the CLI's
complete). Its keys are whatever the worker put there —
the answers it composed from the task and the version's questions. result is the
only variable in scope: an expression sees the reported result and nothing else —
not the task, the questions, or the attachments.
Write a properties expression to render to the value to set. Write a checks
expression to render to pending, passed, or failed — a check that renders to
anything else fails the whole application (below). An expression that renders to an
empty string sets nothing: that entry is skipped, which lets an expression decline
to write on a given result.
For a worker that reports { "verdict": "spam", "confidence": 0.98 }, this
output records the verdict as a property and passes a check only when the verdict
is not spam:
"output": {
"properties": {
"triage_verdict": "{{ result.verdict }}"
},
"checks": {
"not_spam": "{% if result.verdict == 'spam' %}failed{% else %}passed{% endif %}"
}
}Rendering is strict: an expression that is malformed, or that names something the
reported result does not carry, fails to render, and a report whose details is
not a JSON object is rejected the same way — either failure fails the whole
application, as below.
When a classifier pool's lease reports success, Cadence renders the pinned
version's output against the reported result and writes what it names to the
task: each properties entry as a task Property,
each checks entry as a CheckResult. This is applied
all-or-nothing — if any entry cannot be rendered or applied, nothing is written
and the lease completes as failure instead, which holds the task for an operator.
An entry whose expression renders to an empty string is skipped. Cadence records a
classifier_outcome_applied activity on the task naming the properties and checks
it wrote. See
What moves a task without a person.
Environment variables are declared per version, each with a name and a source:
a literal value, or a credential — the name of a live
credential Cadence can resolve for the worker's space:
a credential defined in the space, or an instance-scope credential set up for the
whole instance. A credential source is refused only when no live credential of
that name can be resolved.
The lifecycle refuses in these cases:
not_ready— archiving a version that is notready.pinned— archiving a version a pool still pins; repin the pool first.active_leases— archiving a version with active leases; drain them first.not_draft— discarding a version that is not adraft; archive areadyone instead.
Choosing a vendor, model and effort
An agent version names three things about the model its workers run:
llm_vendor— the vendor that sells the model: one ofanthropic,openai,google,eurouter, ortensorx. Required to publish.model— the model string the vendor's API expects. It must be a model defined for that vendor. A model is identified by vendor and model string together: the same string can appear under two vendors with different efforts.effort— optional; how hard the model reasons. It must be one of the efforts the model's definition lists.nullor""means the model's own default. A model that defines no efforts refuses any effort.
Set them with worker_versions_create_draft or worker_versions_update_draft
(llm_vendor, model, effort), or over the API on POST/PATCH /api/v1/spaces/:space_key/workers/:worker_id/versions[/:id]. Worker-version JSON,
over MCP and the API, returns llm_vendor and effort; each is null when unset.
A runner or classifier version does not take either. Over MCP,
worker_versions_create_draft drops them and creates the draft without them, and
worker_versions_update_draft refuses them — for example, Llm vendor is not allowed on a Runner. Over the API the fields are refused with
unpermitted_fields.
Find the defined vendors and models
Call llm_models_list to see every model you can choose. It returns one row
per model, sorted by vendor and then model:
| Field | Notes |
|---|---|
vendor | The vendor that sells it. |
model | The model string to set as model. |
title | A readable name, the one the browser shows. |
efforts | The efforts you can set, from least to most. Empty when effort cannot be set. |
default_effort | The effort used when you set none. null when the vendor documents none. |
deprecated | true for a model that can be saved on a draft but not published. |
Deprecated models are included. Pass vendor to list one vendor's models; an
unknown vendor is refused with unknown vendor "<vendor>" (known: anthropic, openai, google, eurouter, tensorx). The tool is read-only, needs no space, and
can be called by a signed-in user or from a lease. The list changes as vendors
add and retire models, so read it rather than relying on a copy.
What a draft refuses
A draft's vendor and model are checked when the draft is created from scratch, when a new draft's vendor or model differs from the version it was forked from, and whenever an edit changes either. Its effort is checked whenever it is set and the draft's vendor, model or effort was chosen or changed. The refusals name what you can choose instead:
- No vendor:
Llm vendor must be chosen (defined: anthropic, openai, google, eurouter, tensorx). - An unknown vendor:
Llm vendor "<vendor>" is not defined (defined: …). - A model not defined for the vendor:
Model "<model>" is not defined for <vendor> (defined: …), listing that vendor's models that are not deprecated. - An effort the model does not list:
Effort "<effort>" is not an effort of "<model>" (allowed: …). - An effort on a model that defines none:
Effort "<effort>" can't be set: "<model>" defines no efforts. - An effort when the model is not defined for the vendor, or no vendor is set:
Effort "<effort>" can't be set: "<model>" has no defined efforts for <vendor>. With no vendor set,<vendor>readsno vendor.
A deprecated model is accepted on a draft. Publishing refuses it.
What publishing refuses
worker_versions_publish checks the vendor, model and effort in full, whatever
the draft went through, and refuses:
- No vendor or an unknown vendor:
LLM vendor must be chosen (defined: …)orLLM vendor "<vendor>" is not defined (defined: …). - A model not defined for the vendor:
model "<model>" is not defined for <vendor> (defined: …). - A deprecated model:
model "<model>" is deprecated for <vendor> (choose one of: …). - An effort the model no longer lists:
effort "<effort>" is not an effort of "<model>" (allowed: …). - An effort on a model that defines none:
effort "<effort>" can't be set: "<model>" defines no efforts. - An effort when the model is not defined for the vendor, or no vendor is set:
effort "<effort>" can't be set: "<model>" has no defined efforts for <vendor>, where<vendor>readsno vendorwhen none is set.
When more than one applies, publishing names them all in one refusal, separated by
; .
Forking copies llm_vendor and effort along with model. A fork of an older
version that names no vendor is a draft that cannot be published until you
choose a vendor, and a fork of a version whose model has since been deprecated
cannot be published until you choose another model.
What the choice means for a pool
The version's vendor decides which LLM-provider
registration its workers
draw on. A pool pinned to the version runs no work unless the registration of that
vendor that serves its space has a token it can serve: live, readable, and not held
back by its limit or by the vendor. That registration is the space's own if the
space has one of that vendor; the instance-wide one is used only when the space has
none. A space registration with no token to serve stops the pool even when the
instance's registration has tokens. If no token can be served, the worker does not
start: the dispatch records worker_spin_up_failed, Cadence holds the task, and
nothing retries. A person has to fix the cause — see LLM providers § How a token
reaches a worker — and then
clear the hold on each held task. See Hold a task and clear a hold §
3.
When effort is set, Cadence hands it to the worker in its lease payload; when it
is not, the payload carries no effort. What the worker does with it is up to the
worker's image. See What an agent reads about its
model.
In the browser
The worker version's new and edit form offers three selects:
- LLM vendor, listing the vendors by title.
- Model, listing the chosen vendor's models that are not deprecated. It
reloads when you change the vendor. A draft's current model stays selectable,
marked
(deprecated)or(not defined), so saving the form never switches it for you. - Effort, offering
Model default (<default>)— orModel defaultwhen the model documents none — and the model's efforts. It reloads when you change the model.
The version's page shows the vendor beside the model — (vendor: None) when
unset — and the effort, or the model's default it falls back to.
Agent skills
A skill is a unit of instruction an agent version can carry. This is the space's
own skill catalogue: GET/POST /api/v1/spaces/:space_key/agent_skills, PATCH .../agent_skills/:id, and nested versions; MCP agent_skills_register,
agent_skills_list, agent_skills_update, agent_skills_delete, and
agent_skill_versions_create_draft, ..._update_draft, ..._publish,
..._fork, ..._archive, ..._discard, ..._list, ..._get. Listing and
getting need read; the rest need admin.
A skill has a key, a name, a description, and a deprecated flag. Its
versions follow the same draft → ready → archived lifecycle as worker
versions, each carrying content and a description. An agent version pins skill
versions by id.
Images
An image is a container image a worker version runs from. This is the space's own
image catalogue: GET/POST /api/v1/spaces/:space_key/images, PATCH .../images/:id; MCP images_register, images_list, images_update,
images_delete. Listing needs read; the rest need admin.
An image has a ref — a registry-and-tag or @sha256: digest reference — a
name, a description, and a deprecated flag. The ref is immutable: to swap
it, register a new image. Deleting an image that a worker version still references
is refused.