Node-agent internal: claim this host's due agent-plane job runs. (Not operator-facing.)
const url = 'http://localhost:8080/v1/agent/jobs/pending';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}use reqwest;
#[tokio::main]pub async fn main() { let url = "http://localhost:8080/v1/agent/jobs/pending";
let mut headers = reqwest::header::HeaderMap::new(); headers.insert("Authorization", "Bearer <token>".parse().unwrap());
let client = reqwest::Client::new(); let response = client.get(url) .headers(headers) .send() .await;
let results = response.unwrap() .json::<serde_json::Value>() .await .unwrap();
dbg!(results);}curl --request GET \ --url http://localhost:8080/v1/agent/jobs/pending \ --header 'Authorization: Bearer <token>'THE CLAIM IS THE RESPONSE. Returning a run and marking it running is one statement (FOR UPDATE … SKIP LOCKED), so two polls arriving together take DISJOINT sets rather than both executing the same warm-up. Capped at 5 runs per poll: a host coming back after an outage with a dozen jobs due must not start all of them at once, and the rest are still pending because the work is a durable row. A claim that is then never reported is not lost work — the dispatcher’s reaper aborts it after deadline_secs and materializes a fresh pending row. An empty list is the steady state, and is also what this returns when the jobs master switch is off: the switch is re-read HERE as well as in the dispatcher, because the dispatcher is what stops rows being CREATED and this is what stops an already-materialized row being HANDED OUT.
Authorizations
Section titled “Authorizations”Responses
Section titled “Responses”OK.
object
Capped at 5 per poll; [] is the steady state and also the answer when the jobs master switch is off.
One run claimed by the calling host. Returning it and marking it running is ONE statement, which is what makes concurrent polls take disjoint sets.
object
The registry job id, e.g. “template.warmup”.
The opaque per-job JSON the control plane stored when it materialized the run (for an event trigger, whatever the event carried). The framework NEVER interprets it; the agent hands it to the runner. {} rather than null when there is none. Bounded at 4096 bytes by a CHECK.
object
After this long with no report the dispatcher ABORTS the run and re-materializes it. Sent so the agent can bound its own execution rather than discover the abort by racing it.
Examplegenerated
{ "runs": [ { "run_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "job_id": "example", "params": {}, "deadline_secs": 1 } ]}Missing/invalid/expired/revoked token.
object
object
E.g. validation_failed, unauthorized, forbidden, not_found, conflict, session_quota_exceeded, home_in_use, home_not_provisioned, parent_app_disabled, profile_ineligible, profile_not_launchable_for_app, no_host_available, capacity_exhausted, restart_required, rate_limited, internal. Open string, not an enum: new codes are additive and an unknown one falls through to a client’s generic per-status branch.
Present on restart_required.
Steam library discovery Phase 3, ADDITIVE: present on home_in_use when the guard could name the CONFLICTING live session - the one already holding the home. It is here so the client can offer “go to your running session” with a link instead of a dead-end toast. OMITTED rather than empty when the conflict is known but the session is not, so a client branches on presence and never renders a link to nowhere. Load-bearing once derived tiles exist: the lock is held by the PARENT’s home, so a user who clicks a game tile can be refused because a DIFFERENT app (the Steam launcher, or another game from the same install) is running, and without the session id the refusal reads as a bug. See control-api.md §Derived tiles.
Steam library discovery Phase 3, ADDITIVE: present on the 409 conflict from DELETE /v1/apps/{id} when the app has derived tiles and ?delete_derived=true was not sent. A LIST, not a count - the point of the confirmation is that the admin sees what they are about to destroy. Capped; an empty array means the tiles could not be listed, never that there are none.
object
Examplegenerated
{ "error": { "code": "example", "message": "example", "live_sessions": 1, "session_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "derived_tiles": [ { "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "name": "example" } ] }}Could not claim pending jobs.
object
object
E.g. validation_failed, unauthorized, forbidden, not_found, conflict, session_quota_exceeded, home_in_use, home_not_provisioned, parent_app_disabled, profile_ineligible, profile_not_launchable_for_app, no_host_available, capacity_exhausted, restart_required, rate_limited, internal. Open string, not an enum: new codes are additive and an unknown one falls through to a client’s generic per-status branch.
Present on restart_required.
Steam library discovery Phase 3, ADDITIVE: present on home_in_use when the guard could name the CONFLICTING live session - the one already holding the home. It is here so the client can offer “go to your running session” with a link instead of a dead-end toast. OMITTED rather than empty when the conflict is known but the session is not, so a client branches on presence and never renders a link to nowhere. Load-bearing once derived tiles exist: the lock is held by the PARENT’s home, so a user who clicks a game tile can be refused because a DIFFERENT app (the Steam launcher, or another game from the same install) is running, and without the session id the refusal reads as a bug. See control-api.md §Derived tiles.
Steam library discovery Phase 3, ADDITIVE: present on the 409 conflict from DELETE /v1/apps/{id} when the app has derived tiles and ?delete_derived=true was not sent. A LIST, not a count - the point of the confirmation is that the admin sees what they are about to destroy. Capped; an empty array means the tiles could not be listed, never that there are none.
object
Examplegenerated
{ "error": { "code": "example", "message": "example", "live_sessions": 1, "session_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "derived_tiles": [ { "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "name": "example" } ] }}