{"openapi":"3.1.0","info":{"title":"Aya Hub Agent API","description":"The execution-pull protocol a worker (`aya-helper`) polls, and the service-to-service integration API an external product calls, and the remote chat API a cloud agent uses to read sessions and message them over the internet, on Aya Hub. See `docs/api/execution-pull.md` for the lease lifecycle a schema alone cannot express. Browser/phone routes (`/api/sessions/...`, pairing, push) are out of scope.","license":{"name":"FSL-1.1-ALv2","identifier":"FSL-1.1-ALv2"},"version":"4.0"},"servers":[{"url":"https://hub.example.test","description":"An Aya Hub deployment."}],"paths":{"/.well-known/aya-relay-keys.json":{"get":{"tags":["relay"],"summary":"The Ed25519 public keys (JWKS) that verify relay tickets. No credential required.","operationId":"keys","responses":{"200":{"description":"JWKS with the current and, during a rotation, the previous key.","content":{"application/json":{"schema":{}}}},"404":{"description":"The relay is not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"relay unavailable"}}}}},"security":[{}]}},"/.well-known/aya-webhook-keys.json":{"get":{"tags":["issuer"],"summary":"The Ed25519 public keys a provisioner verifies the hub's signed webhooks against (Standard Webhooks v1a).","operationId":"webhook_keys","responses":{"200":{"description":"Webhook public keys.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookKeys"}}}},"404":{"description":"Webhook signing is not configured.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{}]}},"/.well-known/jwks.json":{"get":{"tags":["issuer"],"summary":"The public keys that verify tokens this hub issued: the active key, any key published ahead of activation, and retiring keys.","operationId":"jwks","responses":{"200":{"description":"JSON Web Key Set.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Jwks"}}}},"404":{"description":"The workload issuer is not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{}]}},"/.well-known/oauth-authorization-server":{"get":{"tags":["issuer"],"summary":"The same document as `/.well-known/openid-configuration`, for RFC 8414 clients.","operationId":"oauth_authorization_server","responses":{"200":{"description":"Authorization server metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiscoveryDocument"}}}},"404":{"description":"The workload issuer is not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{}]}},"/.well-known/openid-configuration":{"get":{"tags":["issuer"],"summary":"The workload issuer's OpenID Connect discovery document.","operationId":"openid_configuration","responses":{"200":{"description":"Discovery document.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiscoveryDocument"}}}},"404":{"description":"The workload issuer is not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{}]}},"/api/agent/capabilities":{"post":{"tags":["agent-capabilities"],"summary":"Reports what a root's harness can currently do — tools, MCP servers, skills, commands, and subagents — replacing any prior report for the same root_id.","description":"Uses axum's `Json<ReportCapabilitiesRequest>` extractor — see\n`chat_routes.rs::agent_events`'s own doc for the 415/400/422\n`JsonRejection` split this runs before the handler body, with a\nplain-text body rather than `crate::openapi::ErrorResponse`'s JSON shape.","operationId":"agent_report_capabilities","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportCapabilitiesRequest"}}},"required":true},"responses":{"200":{"description":"Snapshot stored.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"400":{"description":"Request body is not syntactically valid JSON (axum `JsonRejection::JsonSyntaxError`). Plain-text body. The root id (`root_id` / `root.id`) is also refused with a JSON `{\"error\":\"invalid root id\"}` 400 unless it is 1-128 bytes of `[A-Za-z0-9._:-]`.","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"OIDC credential lacks the `agent:report` scope, or the root is bound to another principal.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"root is bound to another principal"}}}},"413":{"description":"Body exceeds axum's default 2 MiB body limit — no route-specific override here. Produced by the `DefaultBodyLimit` layer during body extraction, BEFORE this handler ever runs. Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"415":{"description":"Missing or non-JSON `Content-Type` header (axum `JsonRejection::MissingJsonContentType`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"Body is syntactically valid JSON but does not deserialize into `ReportCapabilitiesRequest` (axum `JsonRejection::JsonDataError`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route unconditionally answers 503 (`chat_routes::agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["agent:report"]}]}},"/api/agent/index/fingerprints":{"get":{"tags":["agent-index"],"summary":"Per-`native_id` `[fingerprint, message_count]` pairs for one root — what a\nhelper diffs its own local sessions against before deciding what\n`POST /api/agent/index/sessions` needs to publish (HUB.md §4.1). The\nresponse is a JSON object keyed by `native_id`, so it is documented as a\nfree-form object rather than a named schema (see this crate's OpenAPI\nself-verification notes for why: dynamic keys have no fixed property\nlist, and each value is a 2-element tuple — `[fp: number | null, count:\nnumber]` — which JSON Schema can only express with `prefixItems`).","operationId":"index_fingerprints","parameters":[{"name":"root","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Object keyed by `native_id`, each value `[fingerprint, message_count]`.","content":{"application/json":{"schema":{},"example":{"native-1":[123,4],"native-2":[null,0]}}}},"400":{"description":"The `root` query value is not 1-128 bytes of `[A-Za-z0-9._:-]`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"invalid root id"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"OIDC credential lacks the `agent:report` scope, or the root is bound to another principal.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"root is bound to another principal"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route (and every other `/api/agent/index/*` route) unconditionally answers 503 (`chat_routes::agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["agent:report"]}]}},"/api/agent/index/sessions":{"post":{"tags":["agent-index"],"summary":"One publish batch of sessions (HUB.md §4.1) — the write path a helper\ncalls after diffing its own sessions against `GET\n/api/agent/index/fingerprints`. Body size is capped well above what a\ncompliant helper ever sends (see [`INDEX_SESSIONS_BODY_LIMIT`]'s own\ndoc); exceeding it is a `413`, surfaced by\n`axum::extract::DefaultBodyLimit` (a plain-text body, not this\noperation's JSON error schema) BEFORE this handler ever runs — the same\nframework-level-rejection shape as the 415/422 rows below, now\ndocumented the same way rather than omitted (a prior version of this\ncomment reasoned \"surfaced by the layer, not the handler, so it's not\nin the list below\" — inconsistent with 415/422, which ARE listed\ndespite the identical reasoning).","description":"Uses axum's `Json<PublishSessionsRequest>` extractor — see\n`chat_routes.rs::agent_events`'s own doc for the 415/400/422\n`JsonRejection` split this runs before the handler body, with a\nplain-text body rather than `crate::openapi::ErrorResponse`'s JSON shape.","operationId":"publish_sessions","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishSessionsRequest"}}},"required":true},"responses":{"200":{"description":"Batch applied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishOutcome"}}}},"400":{"description":"Request body is not syntactically valid JSON (axum `JsonRejection::JsonSyntaxError`). Plain-text body. The root id (`root_id` / `root.id`) is also refused with a JSON `{\"error\":\"invalid root id\"}` 400 unless it is 1-128 bytes of `[A-Za-z0-9._:-]`.","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"OIDC credential lacks the `agent:report` scope, or the root is bound to another principal.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"root is bound to another principal"}}}},"413":{"description":"Body exceeds `INDEX_SESSIONS_BODY_LIMIT` (32 MiB). Produced by the `DefaultBodyLimit` layer during body extraction, BEFORE this handler ever runs. Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"415":{"description":"Missing or non-JSON `Content-Type` header (axum `JsonRejection::MissingJsonContentType`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"Body is syntactically valid JSON but does not deserialize into `PublishSessionsRequest` (axum `JsonRejection::JsonDataError`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route (and every other `/api/agent/index/*` route) unconditionally answers 503 (`chat_routes::agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["agent:report"]}]}},"/api/agent/jobs/next":{"get":{"tags":["agent-jobs"],"summary":"Claims one pending generic job for the polling worker, or 204 when none is ready for the given roots.","operationId":"agent_next_job","parameters":[{"name":"roots","in":"query","description":"Comma-separated root ids this worker can execute against. Absent or\npresent-but-empty both match nothing (see [`parse_roots`]'s own doc).","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"description":"A job was claimed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimedJob"}}}},"204":{"description":"No pending job currently matches `roots`."},"400":{"description":"`roots` names more than 64 entries (`{\"error\":\"too many roots\"}`) or an entry that is not 1-128 bytes of `[A-Za-z0-9._:-]` (`{\"error\":\"invalid root id\"}`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"OIDC credential lacks the `agent:claim` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error, including a claimed job whose stored params failed to parse (the job is failed server-side in that case, not left claimed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route (and every other `/api/agent/jobs/*` route) unconditionally answers 503 (`chat_routes::agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["agent:claim"]}]}},"/api/agent/jobs/{id}/result":{"post":{"tags":["agent-jobs"],"summary":"Reports the terminal outcome of a claimed job.","description":"Uses axum's `Json<AgentJobResultRequest>` extractor — see\n`chat_routes.rs::agent_events`'s own doc for the 415/400/422\n`JsonRejection` split this runs before the handler body, with a\nplain-text body rather than `crate::openapi::ErrorResponse`'s JSON shape.","operationId":"agent_job_result","parameters":[{"name":"id","in":"path","description":"Job id, as returned by `GET /api/agent/jobs/next`.","required":true,"schema":{"type":"integer","format":"int64"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentJobResultRequest"}}},"required":true},"responses":{"200":{"description":"Recorded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"400":{"description":"Request body is not syntactically valid JSON (axum `JsonRejection::JsonSyntaxError`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Missing bearer token, or one that fails verification — `agent_auth.rs`'s own middleware, shared by every enabled `/api/agent/*` route.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"unauthorized"}}}},"403":{"description":"Bearer token verified, but lacks the scope this route requires (`agent:report`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"missing required scope"}}}},"404":{"description":"No job with this id exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"`claim_token` does not match the current claim.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Body exceeds axum's default 2 MiB body limit — no route-specific override here. Produced by the `DefaultBodyLimit` layer during body extraction, BEFORE this handler ever runs. Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"415":{"description":"Missing or non-JSON `Content-Type` header (axum `JsonRejection::MissingJsonContentType`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"Body is syntactically valid JSON but does not deserialize into `AgentJobResultRequest` (axum `JsonRejection::JsonDataError`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route (and every other `/api/agent/jobs/*` route) unconditionally answers 503 (`chat_routes::agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["agent:report"]}]}},"/api/agent/reservation":{"get":{"tags":["agent-turns"],"summary":"Reads the reservation the calling worker belongs to.","operationId":"agent_reservation","responses":{"200":{"description":"The reservation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReservationView"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing the `agent:report` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"The caller is not a reserved worker.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"410":{"description":"The reservation has ended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route unconditionally answers 503 (`agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["agent:report"]}]}},"/api/agent/turns":{"get":{"tags":["agent-turns"],"summary":"Lists turns, newest-first, optionally filtered by status. Requires the\n`agent:report` scope (`agent_auth::required_scope`).","operationId":"list_turns","parameters":[{"name":"status","in":"query","description":"Comma-separated status list (e.g. `pending,claimed`). Absent matches\nevery status; present-but-empty (`status=`) matches none.","required":false,"schema":{"type":["string","null"]}},{"name":"limit","in":"query","required":false,"schema":{"type":["integer","null"],"format":"int64"}},{"name":"offset","in":"query","required":false,"schema":{"type":["integer","null"],"format":"int64"}}],"responses":{"200":{"description":"Page of turns, newest-first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TurnListResponse"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"OIDC credential lacks the `agent:report` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route unconditionally answers 503 (`chat_routes::agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["agent:report"]}]}},"/api/agent/turns/next":{"get":{"tags":["agent-turns"],"summary":"Claims one pending execution turn for the polling worker, or 204 when none is ready.","description":"Model tiers step 3 (`docs/overhaul/model-tiers.md` §1 \"Helper at CLAIM\"):\na turn whose `provider` is `null` on the wire is a TIERED turn — its\nprovider was deliberately left for a helper to resolve. Such a turn is\nclaimable by this caller whenever `providers` (this query's own\n`servable_providers`) intersects the response's `candidates` array by AT\nLEAST ONE entry, not necessarily all of them; ordinary root/provider\nmatching (this doc's own paragraph above) still applies identically\notherwise. The claiming helper — never this route — makes the actual\nprovider/model pick from `tier`/`candidates` (`aya_drive::route_\ncandidates`).","operationId":"agent_next","parameters":[{"name":"providers","in":"query","description":"Comma-separated providers the worker can drive. Only narrows what it\nis eligible for.","required":false,"schema":{"type":["string","null"]}},{"name":"roots","in":"query","description":"Comma-separated root ids the worker wants to serve. Only narrows: a\nsource that binds roots to principals serves just the listed roots\nthe caller owns (naming an unbound root may bind it to the caller),\nand drops the rest. Absent means every root bound to the caller.","required":false,"schema":{"type":["string","null"]}},{"name":"profile_id","in":"query","description":"Opaque worker profile identifier. Sources can use this to scope a\nsource-owned affinity without learning local harness policy.","required":false,"schema":{"type":["string","null"]}},{"name":"purpose","in":"query","description":"Opaque source-owned execution purpose. A worker may use this to claim\nonly one class of prepared work without learning how that work is\ninterpreted by its source.","required":false,"schema":{"type":["string","null"]}},{"name":"aya-claim-capabilities","in":"header","description":"Comma-separated optional kinds of work this worker can run. `work-checkout`: it prepares a work run's `work_checkout` and runs the turn there. A turn needing a capability the claim does not name is skipped and stays queued for a claim that does; unknown names are ignored.","required":false,"schema":{"type":["string","null"]}}],"responses":{"200":{"description":"A turn was claimed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimedTurn"}}}},"204":{"description":"No pending turn currently matches this claimant's filters."},"400":{"description":"`roots` names more than 64 entries (`{\"error\":\"too many roots\"}`) or an entry that is not 1-128 bytes of `[A-Za-z0-9._:-]` (`{\"error\":\"invalid root id\"}`). `providers` names more than 32 distinct entries, or an entry that is not 1-64 bytes of `[A-Za-z0-9._-]` (`{\"error\":\"invalid providers\"}`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"OIDC credential lacks the `agent:claim` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"410":{"description":"The caller's reservation has ended; the worker must stop.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route (and every other `/api/agent/turns/*` route) unconditionally answers 503 (`agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["agent:claim"]}]}},"/api/agent/turns/{id}/done":{"post":{"tags":["agent-turns"],"summary":"Reports success, failure, an ordinary capped requeue, or an uncapped parked requeue when park_until_ms is supplied.","description":"Uses axum's `Json<CompletionRequest>` extractor — see `agent_events`'s own\ndoc for the 415/400/422 `JsonRejection` split this runs before the\nhandler body, with a plain-text body rather than\n[`crate::openapi::ErrorResponse`]'s JSON shape.","operationId":"agent_done","parameters":[{"name":"id","in":"path","description":"Turn id, as returned by `GET /api/agent/turns/next`.","required":true,"schema":{"type":"integer","format":"int64"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompletionRequest"}}},"required":true},"responses":{"200":{"description":"Recorded (idempotent ONLY for an exact retry: the same `claim_token` from the same claiming principal against an already-done turn also answers 200 without re-applying it).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"400":{"description":"Request body is not syntactically valid JSON (axum `JsonRejection::JsonSyntaxError`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Missing bearer token, or one that fails verification — `agent_auth.rs`'s own middleware, shared by every enabled `/api/agent/*` route.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"unauthorized"}}}},"403":{"description":"Bearer token verified, but lacks the scope this route requires (`agent:report`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"missing required scope"}}}},"404":{"description":"No turn with this id exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"`claim_token` does not match the current live claim, OR the turn is already done and this call is not an exact retry by the winning claimant (different token, or same token from a different principal).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Body exceeds axum's default 2 MiB body limit — no route-specific override here. Produced by the `DefaultBodyLimit` layer during body extraction, BEFORE this handler ever runs. Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"415":{"description":"Missing or non-JSON `Content-Type` header (axum `JsonRejection::MissingJsonContentType`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"Body is syntactically valid JSON but does not deserialize into `CompletionRequest` (axum `JsonRejection::JsonDataError`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route (and every other `/api/agent/turns/*` route) unconditionally answers 503 (`agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["agent:report"]}]}},"/api/agent/turns/{id}/events":{"post":{"tags":["agent-turns"],"summary":"Appends one batch of events to a claimed turn's stream.","description":"Uses axum's `Json<EventAppendRequest>` extractor, which runs BEFORE this\nfunction's body and can itself reject the request — a real 415/400/422\nsplit, not one this handler's own logic produces (axum 0.8's\n`JsonRejection`, `axum-core-0.5.6/src/extract/rejection.rs`): missing or\nnon-JSON `Content-Type` is 415, syntactically invalid JSON is 400, and\nvalid JSON that doesn't match `EventAppendRequest`'s shape is 422. Each\nrejection's body is a bare `text/plain` string (the `(status,\n$body).into_response()` the `define_rejection!` macro builds), never this\ncrate's `{\"error\": ...}` shape — see [`crate::openapi::ErrorResponse`]'s\nown doc for why that type is NOT reused for these three.","operationId":"agent_events","parameters":[{"name":"id","in":"path","description":"Turn id, as returned by `GET /api/agent/turns/next`.","required":true,"schema":{"type":"integer","format":"int64"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventAppendRequest"}}},"required":true},"responses":{"200":{"description":"Events appended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"400":{"description":"EITHER `batch_id` is empty or longer than 256 bytes (this handler's own check, JSON `{\"error\": ...}` body) OR the request body is not syntactically valid JSON (axum `JsonRejection::JsonSyntaxError`, plain-text body) — two media types, one status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}},"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Missing bearer token, or one that fails verification — `agent_auth.rs`'s own middleware, shared by every enabled `/api/agent/*` route.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"unauthorized"}}}},"403":{"description":"Bearer token verified, but lacks the scope this route requires (`agent:report`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"missing required scope"}}}},"404":{"description":"No turn with this id exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"`claim_token` does not match the current live claim (superseded by a reclaim, or the turn isn't `claimed`), or this `batch_id` was already used for a different payload.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Body exceeds axum's default 2 MiB body limit — no route-specific override here. Produced by the `DefaultBodyLimit` layer during body extraction, BEFORE this handler ever runs. Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"415":{"description":"Missing or non-JSON `Content-Type` header (axum `JsonRejection::MissingJsonContentType`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"Body is syntactically valid JSON but does not deserialize into `EventAppendRequest` (axum `JsonRejection::JsonDataError`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route (and every other `/api/agent/turns/*` route) unconditionally answers 503 (`agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["agent:report"]}]}},"/api/agent/turns/{id}/history":{"get":{"tags":["agent-turns"],"summary":"Every recorded state transition for one turn, oldest-first (pending →\nclaimed → done/expired, including any reclaims/requeues in between). Same\n`agent:report` scope requirement as [`list_turns`] — see this module's\ndoc.","operationId":"turn_history","parameters":[{"name":"id","in":"path","description":"Turn id.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"Transition history, oldest-first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TurnHistoryResponse"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"OIDC credential lacks the `agent:report` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No turn with this id exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route unconditionally answers 503 (`chat_routes::agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["agent:report"]}]}},"/api/agent/wake":{"get":{"tags":["agent-turns"],"summary":"Waits for new claimable work, or for the caller's reservation to end.","operationId":"agent_wake","parameters":[{"name":"cursor","in":"query","description":"The cursor from the previous answer. Absent returns the current one\nat once, as does one behind it; one equal to it waits.","required":false,"schema":{"type":["integer","null"],"format":"int64","minimum":0}},{"name":"wait_ms","in":"query","description":"How long to wait for a change, capped at 25000.","required":false,"schema":{"type":["integer","null"],"format":"int64","minimum":0}}],"responses":{"200":{"description":"Something changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WakeResponse"}}}},"204":{"description":"Nothing changed within `wait_ms`."},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing the `agent:claim` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"410":{"description":"The caller's reservation has ended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route unconditionally answers 503 (`agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["agent:claim"]}]}},"/api/agent/workers/self/commands/claim":{"post":{"tags":["agent-workers"],"summary":"Claims one pending control command for this principal's resolved `self`\ninstance, or `204` when none is pending. `409` when this principal has\nnever heartbeated (no `self` to resolve).","operationId":"agent_claim_worker_command","responses":{"200":{"description":"A control command was claimed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimedWorkerCommand"}}}},"204":{"description":"No pending command for this principal's resolved instance."},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"OIDC credential lacks the `execution:control` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"This principal has never heartbeated, or its only known instance is not currently present — there is no `self` instance to claim for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route (and every other `/api/agent/workers/*` route) unconditionally answers 503 (`chat_routes::agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["execution:control"]}]}},"/api/agent/workers/self/commands/{id}/code":{"get":{"tags":["agent-workers"],"summary":"Reads a one-time code exactly once. `204` until a code has been submitted\n(`POST /api/workers/{instance_id}/commands/{id}/code`, browser-facing)\nAND not yet delivered; `200 {\"code\": \"...\"}` exactly once thereafter.","operationId":"agent_take_worker_command_code","parameters":[{"name":"id","in":"path","description":"Operation id, as returned by `POST /api/agent/workers/self/commands/claim`.","required":true,"schema":{"type":"string"}},{"name":"X-AYA-Claim-Token","in":"header","description":"The claim token from the claim response.","required":true,"schema":{"type":"string"}},{"name":"X-AYA-Claim-Generation","in":"header","description":"The claim generation from the claim response.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"The one-time code, delivered exactly once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkerCommandCode"}}}},"204":{"description":"No code has been submitted yet, or it was already delivered."},"400":{"description":"Missing or malformed `X-AYA-Claim-Token`/`X-AYA-Claim-Generation` headers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"OIDC credential lacks the `execution:control` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No command with this id exists for this principal's resolved instance.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The claim fence does not match the command's current claim, the claim's lease has expired (reclaim it via `POST .../commands/claim` and use the fresh fence), or this principal has never heartbeated (or its only known instance is not currently present).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route (and every other `/api/agent/workers/*` route) unconditionally answers 503 (`chat_routes::agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["execution:control"]}]}},"/api/agent/workers/self/commands/{id}/complete":{"post":{"tags":["agent-workers"],"summary":"Reports the terminal outcome of a provider-auth-control command.","operationId":"agent_complete_worker_command","parameters":[{"name":"id","in":"path","description":"Operation id, as returned by `POST /api/agent/workers/self/commands/claim`.","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentWorkerCommandCompleteRequest"}}},"required":true},"responses":{"200":{"description":"Recorded — idempotent for the winning fence.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"400":{"description":"Request body is not syntactically valid JSON (axum `JsonRejection::JsonSyntaxError`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"OIDC credential lacks the `execution:control` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No command with this id exists for this principal's resolved instance.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The claim fence does not match the command's current claim, the claim's lease has expired (reclaim it via `POST .../commands/claim` and use the fresh fence), or this principal has never heartbeated (or its only known instance is not currently present).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Body exceeds axum's default 2 MiB body limit — no route-specific override here. Produced by the `DefaultBodyLimit` layer during body extraction, BEFORE this handler ever runs. Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"415":{"description":"Missing or non-JSON `Content-Type` header (axum `JsonRejection::MissingJsonContentType`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"Body is syntactically valid JSON but does not deserialize into `AgentWorkerCommandCompleteRequest` (axum `JsonRejection::JsonDataError`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route (and every other `/api/agent/workers/*` route) unconditionally answers 503 (`chat_routes::agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["execution:control"]}]}},"/api/agent/workers/self/commands/{id}/events":{"post":{"tags":["agent-workers"],"summary":"Reports progress for a claimed provider-auth-control command.","operationId":"agent_post_worker_command_event","parameters":[{"name":"id","in":"path","description":"Operation id, as returned by `POST /api/agent/workers/self/commands/claim`.","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentWorkerCommandEventRequest"}}},"required":true},"responses":{"200":{"description":"Recorded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"400":{"description":"Request body is not syntactically valid JSON (axum `JsonRejection::JsonSyntaxError`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"OIDC credential lacks the `execution:control` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No command with this id exists for this principal's resolved instance.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The claim fence does not match the command's current claim, the claim's lease has expired (reclaim it via `POST .../commands/claim` and use the fresh fence), or this principal has never heartbeated (or its only known instance is not currently present).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Body exceeds axum's default 2 MiB body limit — no route-specific override here. Produced by the `DefaultBodyLimit` layer during body extraction, BEFORE this handler ever runs. Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"415":{"description":"Missing or non-JSON `Content-Type` header (axum `JsonRejection::MissingJsonContentType`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"Body is syntactically valid JSON but does not deserialize into `AgentWorkerCommandEventRequest` (axum `JsonRejection::JsonDataError`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route (and every other `/api/agent/workers/*` route) unconditionally answers 503 (`chat_routes::agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["execution:control"]}]}},"/api/agent/workers/self/commands/{id}/heartbeat":{"post":{"tags":["agent-workers"],"summary":"Renews a provider-auth-control command's lease.","operationId":"agent_renew_worker_command_lease","parameters":[{"name":"id","in":"path","description":"Operation id, as returned by `POST /api/agent/workers/self/commands/claim`.","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentWorkerCommandFenceRequest"}}},"required":true},"responses":{"200":{"description":"Lease extended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OkResponse"}}}},"400":{"description":"Request body is not syntactically valid JSON (axum `JsonRejection::JsonSyntaxError`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"OIDC credential lacks the `execution:control` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No command with this id exists for this principal's resolved instance.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The claim fence does not match the command's current claim, the claim's lease has expired (reclaim it via `POST .../commands/claim` and use the fresh fence), or this principal has never heartbeated (or its only known instance is not currently present).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Body exceeds axum's default 2 MiB body limit — no route-specific override here. Produced by the `DefaultBodyLimit` layer during body extraction, BEFORE this handler ever runs. Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"415":{"description":"Missing or non-JSON `Content-Type` header (axum `JsonRejection::MissingJsonContentType`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"Body is syntactically valid JSON but does not deserialize into `AgentWorkerCommandFenceRequest` (axum `JsonRejection::JsonDataError`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route (and every other `/api/agent/workers/*` route) unconditionally answers 503 (`chat_routes::agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["execution:control"]}]}},"/api/agent/workers/self/heartbeat":{"put":{"tags":["agent-workers"],"summary":"Upserts this principal's worker-registry row. `204 No Content` on\nsuccess — see this module's own doc for why no body is needed (`hub_client.\nrs::put_worker_heartbeat` only ever checks `status.is_success()`).","operationId":"agent_worker_heartbeat","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentWorkerHeartbeatRequest"}}},"required":true},"responses":{"204":{"description":"Heartbeat recorded."},"400":{"description":"Request body is not syntactically valid JSON (axum `JsonRejection::JsonSyntaxError`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"OIDC credential lacks the `execution:heartbeat` scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"`instance_id` is already bound to a different principal's heartbeats, or this principal already has a DIFFERENT instance present (heartbeated within the presence window) — the principal-to-instance binding is one-to-one while the bound instance is present.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"410":{"description":"The heartbeat is from a reservation worker whose reservation has ended; it must stop.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Body exceeds axum's default 2 MiB body limit — no route-specific override here. Produced by the `DefaultBodyLimit` layer during body extraction, BEFORE this handler ever runs. Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"415":{"description":"Missing or non-JSON `Content-Type` header (axum `JsonRejection::MissingJsonContentType`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"Body is syntactically valid JSON but does not deserialize into `AgentWorkerHeartbeatRequest` (axum `JsonRejection::JsonDataError`). Plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent authentication is not configured on this hub — this route (and every other `/api/agent/workers/*` route) unconditionally answers 503 (`chat_routes::agent_disabled`), with no auth check performed at all.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_agent":["execution:heartbeat"]}]}},"/api/enroll/v1/enroll":{"post":{"tags":["enrollment"],"summary":"Enrolls a freshly provisioned worker against its reservation.","operationId":"enroll","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollRequest"}}},"required":true},"responses":{"200":{"description":"Enrolled. A repeat of a completed enrollment with the same token and the same public key answers the same enrollment again.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollResponse"}}}},"400":{"description":"A malformed request: an unparsable body, an invalid public key, or a field out of bounds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Constant refusal: the token is unknown, expired, used (by another key) or superseded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"},"example":{"error":"enrollment refused"}}}},"403":{"description":"Attestation failed; the reservation fails and its instance is destroyed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Workers are not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"A duplicate key, machine or nonce, a self-description that does not meet the pool's gates, or a root owned by someone else.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"410":{"description":"The reservation was cancelled or ended, or its token revoked; the worker must shut down.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"413":{"description":"The body is larger than 64 KiB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited per peer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"503":{"description":"Not decidable yet: the reservation is not yet provisioning (the provisioner has not reported the instance), or the attestation cannot be checked yet. Retry with the same token after `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{}]}},"/api/enroll/v1/static/checkin":{"post":{"tags":["enrollment"],"summary":"A static-pool machine collects the enrollment token of its assigned reservation.","description":"The credential is checked once per request. Coarse limits apply per peer\nbefore that; failed checks are also limited per machine\n(`fail:static:{pool}/{machine}`), a bucket the correct credential never\ndraws on, so nobody can lock a machine out by guessing.","operationId":"static_checkin","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaticCheckinRequest"}}},"required":true},"responses":{"200":{"description":"An enrollment token for the assigned reservation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaticCheckinResponse"}}}},"204":{"description":"Nothing is assigned to this machine yet."},"400":{"description":"A malformed request, or a field out of bounds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Unknown machine or wrong credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Workers are not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"413":{"description":"The body is larger than 64 KiB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited per peer, or too many failed checks for this machine.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{}]}},"/api/integrations/turns":{"post":{"tags":["integrations"],"summary":"Queues one read-only idea-review turn, idempotently re-reporting an identical resubmission under the same request_id.","description":"This handler parses the body itself (`axum::body::Bytes`, not a typed\n`Json<T>` extractor) so it can enforce [`MAX_BODY_BYTES`] and return one\nuniform `400`/`413` for every malformed-body case rather than axum's\ndefault `JsonRejection` status split — see `parse_argv`\n(`jobs_routes.rs`) for the same reasoning applied to a different route.\n`#[utoipa::path]` still documents the real typed shape\n([`IntegrationTurnRequest`]) it requires once past those checks.","operationId":"post_turn","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationTurnRequest"}}},"required":true},"responses":{"202":{"description":"Queued (or idempotently re-reported for a request_id/input_hash already seen).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationQueued"}}}},"400":{"description":"Malformed or invalid request (unsupported purpose/input_version, oversized/duplicate/invalid fields).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing `review:run` scope, or the subject is not a service principal (does not start with `client-`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The review API is not configured on this hub at all — checked before authentication, so this is the response with or without a token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"integration API is unavailable"}}}},"409":{"description":"Same request_id was already queued with a DIFFERENT input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Body exceeds `MAX_BODY_BYTES`. Produced by the `DefaultBodyLimit` layer during body extraction, BEFORE this handler ever runs (its own `body.len()` check inside the handler never gets a chance to fire under the current configuration, where the layer's limit and the handler's own constant are identical) — a plain-text body, not this operation's JSON error schema.","content":{"text/plain":{"schema":{"type":"string"}}}},"500":{"description":"Internal error queuing the turn.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"integration request could not be queued"}}}},"503":{"description":"Agent execution is not configured on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_integration":["review:run"]}]}},"/api/integrations/turns/{request_id}":{"get":{"tags":["integrations"],"summary":"Polls the state of a previously queued idea-review turn. `state` is one\nof `queued`, `running`, `ready` (`result` present, see [`ReviewResult`]),\nor `failed` (`failure` present) — see\n[`crate::openapi::IntegrationTurnStatus`]'s own doc for why this is\ndocumented as one response shape with state-dependent optional fields\nrather than four separate schemas.","operationId":"get_turn","parameters":[{"name":"request_id","in":"path","description":"The `request_id` originally passed to `POST /api/integrations/turns`.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Current state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationTurnStatus"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing `review:run` scope, or the subject is not a service principal.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Either no turn was ever queued for this subject/request_id (`{\"error\": \"integration request not found\"}`), or the review API is not configured on this hub at all (`{\"error\": \"integration API is unavailable\"}`) — the latter checked before authentication, so it's the response with or without a token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"integration request not found"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"integration request could not be read"}}}}},"security":[{"oidc_integration":["review:run"]}]}},"/api/integrations/work-runs":{"post":{"tags":["integrations"],"summary":"Queues one service work run against a hub-configured work profile, idempotently re-reporting an identical resubmission under the same request_id.","description":"This handler parses the body itself (`axum::body::Bytes`, not a typed\n`Json<T>` extractor) so it can enforce [`MAX_BODY_BYTES`] and return one\nuniform `400`/`413` for every malformed-body case — see\n[`crate::integration_routes::post_turn`]'s own doc for the identical\nreasoning. `#[utoipa::path]` still documents the real typed shape\n([`WorkRunRequest`]) it requires once past those checks.","operationId":"post_work_run","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkRunRequest"}}},"required":true},"responses":{"202":{"description":"Queued (or idempotently re-reported for a request_id/input_hash already seen).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkRunQueued"}}}},"400":{"description":"Malformed or invalid request (wrong input_version, oversized/blank/unsafe field, a `branch` outside the target profile's `branch_prefixes`, or target_ref names no configured work profile).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing `work:run` scope, or the subject is not a service principal (does not start with `client-`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Work runs are not configured on this hub at all (no WORK_PROFILES_JSON) — checked before authentication, so this is the response with or without a token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"work API is unavailable"}}}},"409":{"description":"Same request_id was already queued with a DIFFERENT input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Body exceeds `MAX_BODY_BYTES`. Produced by the `DefaultBodyLimit` layer during body extraction, BEFORE this handler ever runs (its own `body.len()` check inside the handler never gets a chance to fire under the current configuration, where the layer's limit and the handler's own constant are identical) — a plain-text body, not this operation's JSON error schema.","content":{"text/plain":{"schema":{"type":"string"}}}},"500":{"description":"Internal error queuing the run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"work request could not be queued"}}}},"503":{"description":"Work runs are configured, but agent execution is not configured on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_integration":["work:run"]}]}},"/api/integrations/work-runs/{request_id}":{"get":{"tags":["integrations"],"summary":"Polls a queued or completed service work run, including the state of its own result callback to WORK_REPORT_URL.","operationId":"get_work_run","parameters":[{"name":"request_id","in":"path","description":"The `request_id` originally passed to `POST /api/integrations/work-runs`.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Current state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkRunStatus"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing `work:run` scope, or the subject is not a service principal.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Either no work run was ever queued for this subject/request_id (`{\"error\": \"work request not found\"}`), or work runs are not configured on this hub at all (`{\"error\": \"work API is unavailable\"}`) — the latter checked before authentication, so it's the response with or without a token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"work request not found"}}}},"500":{"description":"Internal error reading the run.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"work request could not be read"}}}}},"security":[{"oidc_integration":["work:run"]}]}},"/api/provision/v1/reservations/{id}/enrollment-token":{"post":{"tags":["provisioning"],"summary":"Fetches the single-use enrollment token for a reservation.","operationId":"enrollment_token","parameters":[{"name":"id","in":"path","description":"The reservation id from the create webhook.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Enrollment token. `expires_at_ms` is in Unix milliseconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollmentTokenResponse"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"The token carries no `workers:provision:<pool>` scope at all (decided before any lookup).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"No such reservation, a reservation in a pool the token lacks `workers:provision:<pool>` for (indistinguishable on purpose), or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"The reservation is not in a state that accepts a token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"410":{"description":"The reservation was cancelled or ended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_workload":["workers:provision:{pool}"]}]}},"/api/provision/v1/reservations/{id}/status":{"post":{"tags":["provisioning"],"summary":"Reports a provisioning status change.","operationId":"status","parameters":[{"name":"id","in":"path","description":"The reservation id.","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisionStatusRequest"}}},"required":true},"responses":{"204":{"description":"Accepted (including an idempotent repeat)."},"400":{"description":"A malformed body: unknown fields, an unknown state, or an `instance_id` longer than 256 bytes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"The token carries no `workers:provision:<pool>` scope at all (decided before any lookup).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"No such reservation, a reservation in a pool the token lacks `workers:provision:<pool>` for (indistinguishable on purpose), or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"That transition is not allowed from the reservation's current state, the callback names another instance, or its nonce is refused. A valid nonce is kept even when the transition is refused.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"413":{"description":"The body is larger than 16 KiB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_workload":["workers:provision:{pool}"]}]}},"/api/relay/audit":{"post":{"tags":["relay"],"summary":"Stores a batch of relay audit events.","operationId":"audit","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditBody"}}},"required":true},"responses":{"200":{"description":"Events stored.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditAccepted"}}}},"400":{"description":"Malformed body or more than 500 events.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing `relay:audit` or a non-`client-` subject.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The relay is not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"relay unavailable"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_relay":["relay:audit"]}]}},"/api/relay/revocations":{"get":{"tags":["relay"],"summary":"The append-only revocation log after a cursor, 500 entries per page.","operationId":"revocations","parameters":[{"name":"since","in":"query","description":"Return entries after this cursor (default 0).","required":false,"schema":{"type":["integer","null"],"format":"int64"}}],"responses":{"200":{"description":"A page of revocation entries, the new cursor, whether more remain, the log's head and its epoch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevocationsBody"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing `relay:sync` or a non-`client-` subject.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The relay is not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"relay unavailable"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_relay":["relay:sync"]}]}},"/api/relay/sessions/logout":{"post":{"tags":["relay"],"summary":"Revokes a relay session the relay logged out. Idempotent: a repeated report adds nothing.","operationId":"session_logout","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionLogoutBody"}}},"required":true},"responses":{"204":{"description":"The session is revoked."},"400":{"description":"Malformed body or an invalid `sid`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing `relay:sync` or a non-`client-` subject.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The relay is not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"relay unavailable"}}}},"429":{"description":"The relay client's request budget is spent; see `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_relay":["relay:sync"]}]}},"/api/remote/v1/auth-info":{"get":{"tags":["remote"],"summary":"Names the issuer, token resource and scopes a client needs. No credential required.","operationId":"auth_info","responses":{"200":{"description":"Auth discovery.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoteAuthInfo"}}}},"404":{"description":"The remote API is not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"remote API is unavailable"}}}}},"security":[{}]}},"/api/remote/v1/chats":{"post":{"tags":["remote"],"summary":"Starts a new chat on a root, idempotently per request_id.","operationId":"new_chat","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoteNewChatRequest"}}},"required":true},"responses":{"202":{"description":"Queued, or the original turn when this `request_id` was already accepted with the same input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoteChatResponse"}}}},"400":{"description":"Invalid request: bad `request_id`, empty or oversized text, an unknown provider or root, a root that belongs to a different provider (for a reservation root: not the harness it names), a `tier` or `working_directory` with a reservation root, both or neither of `root_id` and `reservation_id`, an unknown `working_directory`, no connected machine serving the provider, or a `permission_mode` other than `read-only`/`allow-edits` or one the provider does not support.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing `chat:drive`, a non-`client-` subject, or a subject outside the allowlist; or the target is a reservation root (`res-<reservation>.<harness>`) of a reservation that is not the caller's (an unknown reservation answers identically).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The remote API is not enabled on this hub (checked before authentication).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"remote API is unavailable"}}}},"409":{"description":"This `request_id` was already used with different input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"410":{"description":"The target is a reservation root whose reservation has ended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Body exceeds 256 KiB. Produced by the body-limit layer before this handler runs; a plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Per-principal rate limit exceeded, or the caller already has the maximum number of pending or running turns. Both carry `Retry-After`: retry after that many seconds.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent execution is not configured on this hub, or the target is a reservation (a `reservation_id`, or a `root_id` of the form `res-<reservation>.<harness>`) and this hub does not run the workers feature (`{\"error\":\"reserved roots need the workers feature\"}`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_remote":["chat:drive"]}]}},"/api/remote/v1/machines":{"get":{"tags":["remote"],"summary":"Lists machines, their roots and whether each is online, plus the known harnesses.","operationId":"machines","responses":{"200":{"description":"Machines.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoteMachines"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing `chat:read`, a non-`client-` subject, or a subject outside the allowlist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The remote API is not enabled on this hub (checked before authentication).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"remote API is unavailable"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_remote":["chat:read"]}]}},"/api/remote/v1/pools":{"get":{"tags":["workers-admin"],"summary":"Lists pools.","operationId":"list_pools","responses":{"200":{"description":"Pools.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoolList"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Workers are not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/api/remote/v1/pools/{id}":{"get":{"tags":["workers-admin"],"summary":"Reads a pool document with its version.","operationId":"get_pool","parameters":[{"name":"id","in":"path","description":"The pool id.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The pool.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoolView"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"No such pool, or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]},"put":{"tags":["workers-admin"],"summary":"Creates or replaces a pool (GitOps style: the whole document, optimistic version).","description":"A hub serves one org (D1): `org` must be `default`, and a stored pool's org never changes. Limits and budgets in the document count this pool only (D2).","operationId":"put_pool","parameters":[{"name":"id","in":"path","description":"The pool id.","required":true,"schema":{"type":"string"}},{"name":"If-Match","in":"header","description":"`\"<version>\"` of the stored document; required when the pool exists.","required":false,"schema":{"type":["string","null"]}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoolSpec"}}},"required":true},"responses":{"200":{"description":"Stored.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoolView"}}}},"400":{"description":"The document is invalid, including an `org` other than `default`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Workers are not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"The stored pool belongs to another org; a pool's org never changes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"412":{"description":"`If-Match` does not match the stored version.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]},"delete":{"tags":["workers-admin"],"summary":"Retires a pool: no new reservations, existing ones run to their end.","operationId":"delete_pool","parameters":[{"name":"id","in":"path","description":"The pool id.","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Retired."},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"No such pool, or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/api/remote/v1/principals/{sub}/deprovision":{"post":{"tags":["workers-admin"],"summary":"Ends everything a departed principal holds: reservations, workers, pending turns and tokens.","operationId":"deprovision","parameters":[{"name":"sub","in":"path","description":"The principal (token `sub`) that left.","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReasonBody"}}},"required":true},"responses":{"200":{"description":"Done. Safe to repeat.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeprovisionResult"}}}},"400":{"description":"Invalid body, a malformed principal, or a missing reason.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Workers are not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/api/remote/v1/principals/{sub}/reinstate":{"post":{"tags":["workers-admin"],"summary":"Lifts a deprovisioning so the principal can reserve workers again.","description":"Only the deny of every remote scope is lifted. Clients the deprovisioning disabled stay disabled, and the team it removed is not restored.","operationId":"reinstate","parameters":[{"name":"sub","in":"path","description":"The principal (token `sub`) to let back in.","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Reinstated."},"400":{"description":"Invalid principal.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Workers are not enabled on this hub, or the principal was not deprovisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/api/remote/v1/relay/tickets":{"post":{"tags":["relay"],"summary":"Mints a short-lived bearer ticket for opening a worker service through the relay.","operationId":"remote_ticket","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoteTicketRequest"}}},"required":true},"responses":{"200":{"description":"Ticket.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TicketResponse"}}}},"400":{"description":"Invalid worker, service, capabilities or lease.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing `relay:open`, a non-`client-` subject, a subject outside the remote API allowlist or `RELAY_API_ALLOWED_SUBJECTS`, a deprovisioned principal, or denied by policy.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The relay or the remote API is not enabled on this hub, or the worker is unknown.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"relay unavailable"}}}},"429":{"description":"The principal's remote read budget is spent; see `Retry-After`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"The hub cannot tell whether the principal is deprovisioned; retry shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_relay":["relay:open"]}]}},"/api/remote/v1/reservations":{"get":{"tags":["reservations"],"summary":"Lists the caller's reservations, newest first, at most 200.","operationId":"list_reservations","parameters":[{"name":"state","in":"query","description":"Only reservations in this state.","required":false,"schema":{"type":["string","null"]}},{"name":"include_ended","in":"query","description":"Include ended reservations. Default false.","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Reservations. `truncated` is true when more match than the 200 returned; the state filter is applied before the cap, so older matches are never hidden by newer non-matching ones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReservationList"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:reserve`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Workers are not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:reserve"]}]},"post":{"tags":["reservations"],"summary":"Requests an ephemeral worker from a pool.","operationId":"create_reservation","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateReservationRequest"}}},"required":true},"responses":{"200":{"description":"An earlier request with the same `request_id`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReservationView"}}}},"201":{"description":"Created; poll it until `Active`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReservationView"}}}},"400":{"description":"Invalid request, or `source` does not satisfy the pool's schema.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:reserve`, the caller may not use this pool, or reservation budget exhausted: a monthly runtime budget (user, team or org) is spent, and the error names which. Not retryable this month.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"No such pool, or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"`request_id` was used with different parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"A pool limit or the queue bound is reached, or the request is rate limited. The body carries `retry_after_secs` only for a pool limit or the queue bound; every 429 carries the `Retry-After` header, which is the only retry hint on a rate-limit 429.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:reserve"]}]}},"/api/remote/v1/reservations/{id}":{"get":{"tags":["reservations"],"summary":"Reads one of the caller's reservations.","operationId":"get_reservation","parameters":[{"name":"id","in":"path","description":"The reservation id.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The reservation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReservationView"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:reserve`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"No such reservation (including another principal's), or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:reserve"]}]},"delete":{"tags":["reservations"],"summary":"Cancels or releases a reservation. Safe to repeat.","operationId":"release_reservation","parameters":[{"name":"id","in":"path","description":"The reservation id.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Ending or ended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReservationView"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:reserve`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"No such reservation (including another principal's), or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:reserve"]}]}},"/api/remote/v1/roots/{root_id}/projects":{"get":{"tags":["remote"],"summary":"Lists a root's project directories with session counts.","operationId":"root_projects","parameters":[{"name":"root_id","in":"path","description":"A root id from `GET /machines`.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Projects.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoteProjects"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing `chat:read`, a non-`client-` subject, or a subject outside the allowlist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The remote API is not enabled on this hub (checked before authentication).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"remote API is unavailable"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_remote":["chat:read"]}]}},"/api/remote/v1/search":{"get":{"tags":["remote"],"summary":"Searches message text across sessions.","operationId":"search","parameters":[{"name":"q","in":"query","description":"1-500 characters.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Hits, best first.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RemoteSearchHit"}}}}},"400":{"description":"`q` is missing, empty or longer than 500 characters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing `chat:read`, a non-`client-` subject, or a subject outside the allowlist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The remote API is not enabled on this hub (checked before authentication).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"remote API is unavailable"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_remote":["chat:read"]}]}},"/api/remote/v1/sessions":{"get":{"tags":["remote"],"summary":"Lists sessions.","operationId":"list_sessions","parameters":[{"name":"provider","in":"query","description":"Only this harness (`claude`, `codex`, …).","required":false,"schema":{"type":"string"}},{"name":"root_id","in":"query","description":"Only this root.","required":false,"schema":{"type":"string"}},{"name":"project","in":"query","description":"Only this project directory.","required":false,"schema":{"type":"string"}},{"name":"title_contains","in":"query","description":"Case-insensitive title substring.","required":false,"schema":{"type":"string"}},{"name":"include_archived","in":"query","description":"`true` or `1` includes archived sessions.","required":false,"schema":{"type":"boolean"}},{"name":"updated_after","in":"query","description":"Unix ms lower bound on `updated_at`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Page size, default 50, at most 200.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Rows to skip.","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"Sessions.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RemoteSession"}}}}},"400":{"description":"A numeric parameter did not parse.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing `chat:read`, a non-`client-` subject, or a subject outside the allowlist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"The remote API is not enabled on this hub (checked before authentication).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"remote API is unavailable"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_remote":["chat:read"]}]}},"/api/remote/v1/sessions/{pk}":{"get":{"tags":["remote"],"summary":"Fetches one session.","operationId":"get_session","parameters":[{"name":"pk","in":"path","description":"A session's `pk` from `GET /sessions`.","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"The session.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoteSession"}}}},"400":{"description":"`pk` is not a number.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing `chat:read`, a non-`client-` subject, or a subject outside the allowlist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such session, or the remote API is not enabled on this hub (the latter is checked before authentication).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_remote":["chat:read"]}]}},"/api/remote/v1/sessions/{pk}/messages":{"get":{"tags":["remote"],"summary":"Reads a page of a session's messages, newest first.","operationId":"get_messages","parameters":[{"name":"pk","in":"path","description":"A session's `pk`.","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"cursor","in":"query","description":"The previous page's `next_cursor`, JSON-encoded, or `pk` / `ts:pk`.","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","description":"Page size, default 50, at most 100.","required":false,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"A page of messages.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoteMessagesPage"}}}},"400":{"description":"Bad `pk`, `cursor` or `limit`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing `chat:read`, a non-`client-` subject, or a subject outside the allowlist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such session, or the remote API is not enabled on this hub (the latter is checked before authentication).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_remote":["chat:read"]}]},"post":{"tags":["remote"],"summary":"Queues a message as a session's next turn, idempotently per request_id.","operationId":"send_message","parameters":[{"name":"pk","in":"path","description":"The session to message.","required":true,"schema":{"type":"integer","format":"int64"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoteSendRequest"}}},"required":true},"responses":{"202":{"description":"Queued, or the original turn when this `request_id` was already accepted with the same input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoteSendResponse"}}}},"400":{"description":"Invalid request: bad `request_id`, empty or oversized text, a `permission_mode` other than `read-only`/`allow-edits` or one this chat's harness does not support, or a bad `effort`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing `chat:drive`, a non-`client-` subject, or a subject outside the allowlist; or the target is a reservation root (`res-<reservation>.<harness>`) of a reservation that is not the caller's (an unknown reservation answers identically).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such session, or the remote API is not enabled on this hub (the latter is checked before authentication).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"This `request_id` was already used with different input.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"410":{"description":"The target is a reservation root whose reservation has ended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Body exceeds 256 KiB. Produced by the body-limit layer before this handler runs; a plain-text body.","content":{"text/plain":{"schema":{"type":"string"}}}},"429":{"description":"Per-principal rate limit exceeded, or the caller already has the maximum number of pending or running turns. Both carry `Retry-After`: retry after that many seconds.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"503":{"description":"Agent execution is not configured on this hub, or the session lives on a reservation root (`res-<reservation>.<harness>`) and this hub does not run the workers feature (`{\"error\":\"reserved roots need the workers feature\"}`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_remote":["chat:drive"]}]}},"/api/remote/v1/sessions/{pk}/move":{"post":{"tags":["reservations"],"summary":"Moves a session to an active reservation.","description":"Only the session's owner may move it: on a reservation root (including a\nsession already moved onto one) the reservation's requester; on an\nordinary root the subject whose turn created the session through this\nhub. Anyone else, including a session that came from a machine's own\nindex, gets the same 404 as a session that does not exist. There is no\noperator override.","operationId":"move_session","parameters":[{"name":"pk","in":"path","description":"The session's primary key.","required":true,"schema":{"type":"integer","format":"int64"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionMoveRequest"}}},"required":true},"responses":{"200":{"description":"Moved. For `summary`, `turn_id` starts the new chat; the new session is not linked to the old one in the hub's session routes, so follow the turn to learn it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionMoveResponse"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:reserve` or `chat:drive`, or a `summary` move without `chat:read` (it reads the transcript).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"No such session or reservation; a session that is not the caller's (it lives on a reservation that is not the caller's, or the caller's turn did not create it), which answers identically; or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"The reservation is not active, or does not support a native move.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"410":{"description":"The session lives on the caller's reservation, which has ended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:reserve","chat:drive"]}]}},"/api/remote/v1/turns/{id}/events":{"get":{"tags":["remote"],"summary":"Reads (and long-polls) a turn's events.","operationId":"turn_events","parameters":[{"name":"id","in":"path","description":"A `turn_id` returned by `POST /sessions/{pk}/messages` or `POST /chats`.","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"since","in":"query","description":"Return events after this index; default -1 (from the start).","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"wait_ms","in":"query","description":"Long-poll up to this long for something new; default 0, at most 25000.","required":false,"schema":{"type":"integer","format":"int64","minimum":0}}],"responses":{"200":{"description":"The turn's current state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemoteTurnEvents"}}}},"400":{"description":"Bad `id`, `since` or `wait_ms`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid bearer credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing `chat:read`, a non-`client-` subject, or a subject outside the allowlist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such turn, or one a different caller requested, or the remote API is not enabled on this hub (the last is checked before authentication).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Per-principal rate limit exceeded. Retry after the number of seconds in `Retry-After`.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Whole seconds until a request will be admitted."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"rate limited"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"oidc_remote":["chat:read"]}]}},"/api/remote/v1/workers/clients":{"get":{"tags":["workers-admin"],"summary":"Lists the issuer's clients (never their secrets).","operationId":"list_clients","responses":{"200":{"description":"Clients.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientList"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Workers are not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]},"post":{"tags":["workers-admin"],"summary":"Registers a service or static-machine client of the workload issuer.","description":"The client belongs to the hub's one org, `default` (D1).","operationId":"create_client","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientCreateRequest"}}},"required":true},"responses":{"201":{"description":"Created. A secret, if any, is in this response only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientCreated"}}}},"400":{"description":"Invalid client id, grants or key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Workers are not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"The client id exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/api/remote/v1/workers/clients/{id}/disable":{"post":{"tags":["workers-admin"],"summary":"Disables a client and revokes every token it holds. Safe to repeat.","operationId":"disable_client","parameters":[{"name":"id","in":"path","description":"The client id.","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReasonBody"}}},"required":true},"responses":{"200":{"description":"Disabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientView"}}}},"400":{"description":"Invalid body, or a missing reason.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"No such client, or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/api/remote/v1/workers/clients/{id}/rotate-secret":{"post":{"tags":["workers-admin"],"summary":"Replaces a service client's secret; the old one stops working at once.","operationId":"rotate_client_secret","parameters":[{"name":"id","in":"path","description":"The client id.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"New secret, shown once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretRotated"}}}},"400":{"description":"The client does not use a secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"No such client, or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"The client is not active (disabled or revoked).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/api/remote/v1/workers/issuer/keys/rotate":{"post":{"tags":["workers-admin"],"summary":"Rotates the issuer's signing key: the staged next key becomes active and a new one is staged.","operationId":"rotate_issuer_keys","parameters":[{"name":"emergency","in":"query","description":"Retire the active key now and publish the new one immediately instead\nof 48 hours ahead.","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Rotated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeysRotated"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Workers are not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"The key source cannot rotate (file-backed keys rotate by replacing the file).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/api/remote/v1/workers/place-alias-history":{"get":{"tags":["workers-admin"],"summary":"Every alias set, rename and remove, newest first.","operationId":"place_alias_history","parameters":[{"name":"limit","in":"query","description":"1 to 1000; default 100.","required":false,"schema":{"type":["integer","null"],"minimum":0}}],"responses":{"200":{"description":"The history.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AliasHistory"}}}},"400":{"description":"`limit` is not 1 to 1000.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Workers are not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/api/remote/v1/workers/place-aliases/{alias}":{"put":{"tags":["workers-admin"],"summary":"Gives a bound machine an alias (a display and input name; the id never changes).","description":"Idempotent: setting an alias the machine already has, spelled the same, changes nothing.","operationId":"put_place_alias","parameters":[{"name":"alias","in":"path","description":"The alias, stored as typed and matched case-insensitively.","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceAliasPut"}}},"required":true},"responses":{"200":{"description":"Set.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceView"}}}},"400":{"description":"An invalid alias or machine name.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"The place is not a bound machine, or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"The alias belongs to another machine (the message names it), or is a machine's own name.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]},"delete":{"tags":["workers-admin"],"summary":"Removes an alias.","operationId":"delete_place_alias","parameters":[{"name":"alias","in":"path","description":"The alias, matched case-insensitively.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Removed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceAliasRemoved"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"No such alias, or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/api/remote/v1/workers/place-aliases/{alias}/rename":{"post":{"tags":["workers-admin"],"summary":"Renames an alias. The old name stops resolving; the history keeps it.","operationId":"rename_place_alias","parameters":[{"name":"alias","in":"path","description":"The current alias, matched case-insensitively.","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceAliasRename"}}},"required":true},"responses":{"200":{"description":"Renamed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceView"}}}},"400":{"description":"An invalid new alias.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"No such alias, or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"The new alias belongs to another machine, or is a machine's own name.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/api/remote/v1/workers/places":{"get":{"tags":["workers-admin"],"summary":"Lists every machine place the hub holds a binding, alias or conflict for.","operationId":"list_places","responses":{"200":{"description":"Places, sorted by id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceList"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Workers are not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/api/remote/v1/workers/places/{name}":{"get":{"tags":["workers-admin"],"summary":"Resolves a machine id, name or alias to its canonical place.","operationId":"get_place","parameters":[{"name":"name","in":"path","description":"A machine id (`machine:games`), a bare machine name or an alias; case-insensitive.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The place.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceView"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"No such place or alias, or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/api/remote/v1/workers/places/{name}/binding":{"put":{"tags":["workers-admin"],"summary":"Binds a machine name to an enrolled identity (the operator override of first-report binding).","description":"The only way to change a binding: it replaces any binding and clears the name's recorded conflicts. The machine's aliases stay with the id.","operationId":"put_place_binding","parameters":[{"name":"name","in":"path","description":"A machine id (`machine:games`) or a bare machine name.","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceBindingPut"}}},"required":true},"responses":{"200":{"description":"Bound.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceView"}}}},"400":{"description":"Not a machine place, not its canonical name (the message names it), or a malformed identity.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"No such enrolled identity, or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"The name is an alias of another machine.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]},"delete":{"tags":["workers-admin"],"summary":"Forgets a machine name's binding and its conflicts; the next report binds it again.","operationId":"delete_place_binding","parameters":[{"name":"name","in":"path","description":"A machine id (`machine:games`) or a bare machine name.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Unbound.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceUnbound"}}}},"400":{"description":"Not a machine place, or not its canonical name.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"The place is not bound, or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"500":{"description":"Internal error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/api/remote/v1/workers/pools/{id}/machines/{mid}":{"put":{"tags":["workers-admin"],"summary":"Registers or updates a machine of a static pool.","operationId":"put_machine","parameters":[{"name":"id","in":"path","description":"The (static) pool id.","required":true,"schema":{"type":"string"}},{"name":"mid","in":"path","description":"The machine id.","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MachinePut"}}},"required":true},"responses":{"200":{"description":"Stored.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MachineView"}}}},"400":{"description":"Invalid body, or the pool is not static.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"No such pool, or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"Another machine of this pool already uses that credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/api/remote/v1/workers/pools/{id}/machines/{mid}/reset-complete":{"post":{"tags":["workers-admin"],"summary":"Marks a machine that needed a reset as clean and available again.","operationId":"reset_machine","parameters":[{"name":"id","in":"path","description":"The (static) pool id.","required":true,"schema":{"type":"string"}},{"name":"mid","in":"path","description":"The machine id.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Available.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MachineView"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"No such machine, or workers are not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"The machine is not waiting for a reset.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/api/remote/v1/workers/root-bindings/{root}":{"put":{"tags":["workers-admin"],"summary":"Rebinds an ordinary root to a principal (the operator override of first-come binding).","description":"The binding is stored with source `admin`: it survives a restart, `ROOT_BINDINGS_JSON` never rewrites or removes it, and it does not make the principal eligible for root-less (unrooted) turns. A root named by `ROOT_BINDINGS_JSON` is pinned and cannot be rebound here.","operationId":"put_root_binding","parameters":[{"name":"root","in":"path","description":"The root id.","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RootBindingPut"}}},"required":true},"responses":{"200":{"description":"Bound.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RootBindingView"}}}},"400":{"description":"Invalid root id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Workers are not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"409":{"description":"A reservation root (`res-<id>.<harness>`) is bound only by enrollment, or the root is pinned by `ROOT_BINDINGS_JSON` (`{\"error\":\"pinned by ROOT_BINDINGS_JSON\"}`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/api/remote/v1/workers/teams/{principal}":{"put":{"tags":["workers-admin"],"summary":"Assigns a principal to a team, which the pool limits and budgets count against.","description":"A reservation's team is the one it was admitted under (D3). The response says how many of the principal's reservations are still live under their admission-time team; only new reservations count against the new one.","operationId":"put_team","parameters":[{"name":"principal","in":"path","description":"The principal (token `sub`).","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamPut"}}},"required":true},"responses":{"200":{"description":"Stored.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamView"}}}},"400":{"description":"Invalid body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Workers are not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]},"delete":{"tags":["workers-admin"],"summary":"Removes a principal's team assignment, and with it any pool access the team gave.","description":"Reservations already running keep the team they were admitted under (D3).","operationId":"delete_team","parameters":[{"name":"principal","in":"path","description":"The principal (token `sub`).","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Removed."},"400":{"description":"Invalid principal.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"description":"Missing or invalid bearer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"403":{"description":"Missing `workers:admin`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"404":{"description":"Workers are not enabled on this hub, or the principal has no team.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{"oidc_remote":["workers:admin"]}]}},"/oauth/authorize":{"get":{"tags":["issuer"],"summary":"Always refused: this issuer only supports the client credentials grant.","operationId":"authorize","responses":{"400":{"description":"`unsupported_response_type`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"404":{"description":"The workload issuer is not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"security":[{}]}},"/oauth/token":{"post":{"tags":["issuer"],"summary":"Exchanges client credentials for a short-lived access token for one resource.","operationId":"token","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/TokenRequest"}}},"required":true},"responses":{"200":{"description":"Access token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"400":{"description":"`invalid_request`, `unsupported_grant_type`, `invalid_scope` or `invalid_target`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"401":{"description":"`invalid_client`: unknown or malformed client id, wrong secret, bad or replayed assertion, or a disabled or revoked client.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"404":{"description":"The workload issuer is not enabled on this hub.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"413":{"description":"`invalid_request`: the form body is larger than 16 KiB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}},"429":{"description":"Rate limited: every request by peer, then a failed authentication by the claimed client and a successful one by the client, each with its own budget.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"500":{"description":"`server_error`: the endpoint failed, for example because the token would be longer than the 8 KiB verifiers accept.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}}},"security":[{}]}}},"components":{"schemas":{"Access":{"type":"object","properties":{"principals":{"type":"array","items":{"type":"string"},"description":"Exact principals (`client-<id>`, or a human subject)."},"groups":{"type":"array","items":{"type":"string"}}},"additionalProperties":false},"AgentJobResultRequest":{"type":"object","required":["claim_token","ok"],"properties":{"claim_token":{"type":"string"},"ok":{"type":"boolean"},"error":{"type":["string","null"]},"result":{}}},"AgentWorkerCommandCompleteRequest":{"type":"object","required":["claim_token","claim_generation","ok"],"properties":{"claim_token":{"type":"string"},"claim_generation":{"type":"integer","format":"int64"},"ok":{"type":"boolean"},"error_code":{"type":["string","null"]}}},"AgentWorkerCommandEventRequest":{"type":"object","description":"Body of `POST .../events` — mirrors `hub_client.rs::post_control_event`'s\nwire shape. `event` mirrors `hub_client.rs::ControlEvent` field-for-field\n(see that type's own doc: \"It has no transcript, credential, account,\npath, or raw CLI-error field\").","required":["claim_token","claim_generation","event"],"properties":{"claim_token":{"type":"string"},"claim_generation":{"type":"integer","format":"int64"},"event":{"$ref":"#/components/schemas/AgentWorkerControlEvent"}}},"AgentWorkerCommandFenceRequest":{"type":"object","required":["claim_token","claim_generation"],"properties":{"claim_token":{"type":"string"},"claim_generation":{"type":"integer","format":"int64"}}},"AgentWorkerControlEvent":{"type":"object","required":["state"],"properties":{"state":{"type":"string"},"verification_url":{"type":["string","null"]},"user_code":{"type":["string","null"]},"expires_at":{"type":["string","null"]},"error_code":{"type":["string","null"]}}},"AgentWorkerHeartbeatRequest":{"type":"object","description":"Body of `PUT /api/agent/workers/self/heartbeat` — field-for-field the\nhelper's `worker_heartbeat.rs::WorkerHeartbeatPayload` (see that type's\nown doc: \"do not extend this type with hostnames, paths, raw command\nfailures, or credentials\"). `profiles`/`capabilities`/`provider_sessions`\nstay `serde_json::Value` here too — this route has no more reason to\nunderstand their shape than `crate::workers::WorkerStore` does (see that\nmodule's own doc).","required":["instance_id"],"properties":{"instance_id":{"type":"string"},"version":{"type":["string","null"]},"profiles":{},"capabilities":{},"slots":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AgentWorkerSlotsWire"}]},"health":{"type":["string","null"]},"provider_sessions":{},"sent_at":{"type":["string","null"]},"description":{"type":["object","null"],"description":"Sent only by a reservation worker's own hub heartbeat; ignored from\nanyone else. The hub narrows it to the worker's enrollment."},"place":{"type":["string","null"],"description":"This computer's own name, `machine:<hostname>` (`aya_core::place`).\nOptional. Recorded only for an enrolled static box (an active\n`static_worker` client) and bound to it first come; a conflict is\nrecorded, never merged. Ignored from anyone else and when malformed;\nit never changes the response."}}},"AgentWorkerSlotsWire":{"type":"object","properties":{"total":{"type":["integer","null"],"format":"int64"},"busy":{"type":["integer","null"],"format":"int64"}}},"AliasEventView":{"type":"object","required":["seq","at_ms","kind","place","actor"],"properties":{"seq":{"type":"integer","format":"int64"},"at_ms":{"type":"integer","format":"int64"},"kind":{"type":"string","description":"`set`, `rename` or `remove`."},"place":{"$ref":"#/components/schemas/PlaceRef"},"old":{"type":["string","null"]},"new":{"type":["string","null"]},"actor":{"type":"string"}}},"AliasHistory":{"type":"object","required":["events","truncated"],"properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/AliasEventView"},"description":"Newest first."},"truncated":{"type":"boolean"}}},"AssignedRoot":{"type":"object","required":["root_id","provider","path"],"properties":{"root_id":{"type":"string"},"provider":{"type":"string"},"path":{"type":"string","description":"Workspace path on the worker, from the pool's `workspace_path`."}}},"AttestationEvidence":{"oneOf":[{"type":"object","required":["machine_id","credential","kind"],"properties":{"machine_id":{"type":"string"},"credential":{"type":"string"},"kind":{"type":"string","enum":["static_psk"]}}},{"type":"object","required":["nonce","kind"],"properties":{"nonce":{"type":"string"},"kind":{"type":"string","enum":["it_nonce"]}}},{"type":"object","required":["token","kind"],"properties":{"token":{"type":"string"},"kind":{"type":"string","enum":["fly_oidc"]}}},{"type":"object","required":["sandbox_id","kind"],"properties":{"sandbox_id":{"type":"string"},"kind":{"type":"string","enum":["e2b"]}}}],"description":"Evidence the worker offers that it is the machine the backend created.\n`Debug` redacts the secret-bearing fields."},"AttestationSpec":{"type":"object","required":["kind"],"properties":{"kind":{"type":"string"},"config":{"type":"object"}},"additionalProperties":false},"Audiences":{"type":"object","required":["hub_agent"],"properties":{"hub_agent":{"type":"string"},"relay":{"type":["string","null"]},"external":{"type":"array","items":{"type":"string"}}}},"AuditAccepted":{"type":"object","required":["accepted"],"properties":{"accepted":{"type":"integer","description":"How many events were stored.","minimum":0}}},"AuditBody":{"type":"object","description":"Body of `POST /api/relay/audit`.","required":["events"],"properties":{"events":{"type":"array","items":{"type":"object"},"description":"Opaque JSON event objects; events over 16 KiB are dropped."}}},"BackendSpec":{"type":"object","required":["kind"],"properties":{"kind":{"type":"string","description":"`static`, `webhook` (IT), `fake` (tests); `fly` and `e2b` are designed\nbut not implemented in A1."},"queue_owner":{"$ref":"#/components/schemas/QueueOwner"},"config":{"type":"object"}},"additionalProperties":false},"Budget":{"type":"object","description":"Runtime budgets in hours per calendar month (UTC). `None` is unlimited.","properties":{"period":{"$ref":"#/components/schemas/BudgetPeriod"},"org_runtime_hours":{"type":["integer","null"],"format":"int64","minimum":0},"team_runtime_hours":{"type":["integer","null"],"format":"int64","minimum":0},"user_runtime_hours":{"type":["integer","null"],"format":"int64","minimum":0},"cost_per_hour_units":{"type":"integer","format":"int64","minimum":0},"kill_grace_secs":{"type":"integer","format":"int64","minimum":0}},"additionalProperties":false},"BudgetPeriod":{"type":"string","enum":["month"]},"CallbackState":{"type":"string","description":"What a provisioning backend reports through the status callback.","enum":["queued","provisioning","created","failed","destroyed"]},"Capabilities":{"type":"object","description":"What a driven session's agent can DO right now — its tools, MCP\nservers, skills, and slash commands (CONTRACT.md §21: \"the app can\ndrive sessions but cannot tell the user what the agent on the other end\ncan DO\"). Every provider maps into this one shape; the UI renders it\nwithout knowing which provider, or which of the two Claude sources\n(§21), produced it.","required":["provider","tools","mcp_servers","skills","commands","agents","source"],"properties":{"provider":{"type":"string","description":"`PROVIDER_CLAUDE` / `PROVIDER_HERMES` — which adapter produced\nthis snapshot."},"model":{"type":["string","null"]},"tools":{"type":"array","items":{"$ref":"#/components/schemas/CapabilityItem"}},"mcp_servers":{"type":"array","items":{"$ref":"#/components/schemas/McpServer"}},"skills":{"type":"array","items":{"$ref":"#/components/schemas/CapabilityItem"}},"commands":{"type":"array","items":{"$ref":"#/components/schemas/CapabilityItem"},"description":"Hermes exposes none of these over its HTTP API (CONTRACT.md §21) —\nstays empty for that provider always, never invented."},"agents":{"type":"array","items":{"$ref":"#/components/schemas/CapabilityItem"},"description":"Subagent types the session can delegate to. Claude advertises these\nin its init record (5 observed on a real machine); they are a\ngenuine \"what can this session do\" capability, not chrome, which is\nwhy they get a field rather than being dropped for lack of a slot."},"observed_at_ms":{"type":["integer","null"],"format":"int64","description":"Unix milliseconds this snapshot was taken/observed. This is\nload-bearing, not decoration: paired with `source`, it is what lets\na caller say \"as of 20 minutes ago\" and mean it, rather than\nsilently presenting stale, turn-cached data as current. `None` only\nwhen a caller genuinely has no timestamp to attach — never omit\nthis just because the data came from a while ago."},"source":{"$ref":"#/components/schemas/CapabilitySource","description":"How this snapshot was obtained — see `CapabilitySource`. Load-\nbearing alongside `observed_at_ms` for the same reason: a UI must be\nable to tell a live-probed snapshot apart from one that's merely\ncached from whenever a turn last ran."},"changes":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/CapabilityChanges","description":"What changed the last time a probe of the same scope (root, chat\nfolder) found something different from the probe before it, so a\nhot-reloaded MCP server or toolset shows up as a change. `None` until\ntwo probes differed, and from a source that keeps no history."}]}}},"CapabilityChanges":{"type":"object","description":"Servers and tools that appeared or went away between two capability\nprobes of the same scope (CONTRACT.md §23e). Names only.","required":["seen_at_ms","mcp_servers_added","mcp_servers_removed","tools_added","tools_removed"],"properties":{"seen_at_ms":{"type":"integer","format":"int64","description":"Unix milliseconds of the probe that first saw this change."},"mcp_servers_added":{"type":"array","items":{"type":"string"}},"mcp_servers_removed":{"type":"array","items":{"type":"string"}},"tools_added":{"type":"array","items":{"type":"string"}},"tools_removed":{"type":"array","items":{"type":"string"}}}},"CapabilityItem":{"type":"object","description":"One capability a driven agent can invoke — a tool, a skill, or a slash\ncommand, depending on which `Capabilities` field it lives in.","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":["string","null"]},"enabled":{"type":["boolean","null"],"description":"Whether this item is currently switched on, for sources that\ndistinguish \"enabled\" from merely \"available\" (Hermes toolsets).\n`None` means the source doesn't report this at all — NOT the same\nas `Some(false)`."}}},"CapabilitySource":{"type":"string","description":"How a `Capabilities` snapshot was obtained. Exists so a UI can render\n\"as of 20 minutes ago\" truthfully instead of presenting a cached-from-a-\nturn manifest as current (CONTRACT.md §21) — see `Capabilities::source`.","enum":["live_probe","observed_in_turn","merged"]},"ClaimFilter":{"type":"object","description":"Capability filters sent while asking an execution source for work.\n\n`None` preserves an absent query parameter; `Some(\"\")` preserves a\npresent-but-empty parameter, which means the worker serves nothing on that\naxis for Aya Hub's existing queue semantics.","properties":{"providers":{"type":["string","null"],"description":"Comma-separated providers the worker can drive. Only narrows what it\nis eligible for."},"roots":{"type":["string","null"],"description":"Comma-separated root ids the worker wants to serve. Only narrows: a\nsource that binds roots to principals serves just the listed roots\nthe caller owns (naming an unbound root may bind it to the caller),\nand drops the rest. Absent means every root bound to the caller."},"profile_id":{"type":["string","null"],"description":"Opaque worker profile identifier. Sources can use this to scope a\nsource-owned affinity without learning local harness policy."},"purpose":{"type":["string","null"],"description":"Opaque source-owned execution purpose. A worker may use this to claim\nonly one class of prepared work without learning how that work is\ninterpreted by its source."}}},"ClaimedJob":{"type":"object","description":"Mirrors `jobs_routes.rs::agent_next_job`'s `200` response body. `params`\nis the job's own kind-specific payload (e.g. `{\"session_pk\":...,\n\"provider\":...,\"native_id\":...}` for `git_diff`; the same plus `\"argv\"`\nfor `terminal_exec`) — free-form here because `jobs.rs::JobQueue` is\ndeliberately kind-agnostic (see that module's own doc) and this spec\ncovers only the two kinds `jobs_routes.rs` happens to create today, not a\nclosed set a future kind must fit.","required":["job_id","kind","params","claim_token"],"properties":{"job_id":{"type":"string"},"kind":{"type":"string"},"params":{},"claim_token":{"type":"string"}}},"ClaimedTurn":{"type":"object","description":"A claimed execution turn returned by an execution source.\n\n`claim_token` fences all subsequent event and completion reports.\n\nEvery `Option` field below is serialized as an explicit JSON `null` when\nabsent, to retain the existing Aya Hub wire shape — but only some of them\nmay be OMITTED (key and all) by a producer. `provider`, `native_id`, and\n`model` lack `#[serde(default)]`, so this type's own `Deserialize` impl\nrejects a payload that drops the key entirely (a bare missing key is not\nthe same as an explicit `null` without `#[serde(default)]` — see\n`serde`'s field-default rules); their `#[schema(required = true)]` below\nmakes the generated schema say so too: required (the key must be present)\nAND nullable (the value may be `null`). The rest carry `#[serde(default)]`\nand stay genuinely omittable — utoipa already excludes a `#[serde(default)]`\nfield from `required` with no override needed (see `ToSchema`'s\n\"field nullability and required rules\" doc in utoipa-gen).","required":["turn_id","provider","native_id","text","model","claim_token"],"properties":{"turn_id":{"type":"string"},"provider":{"type":["string","null"],"description":"Source-directed queues set this. Worker-routed queues leave it null;\nthe polling Aya source overlays its own local execution profile."},"native_id":{"type":["string","null"]},"text":{"type":"string"},"model":{"type":["string","null"]},"permission_mode":{"type":["string","null"]},"fork_of_native_id":{"type":["string","null"]},"working_directory":{"type":["string","null"]},"effort":{"type":["string","null"]},"root_id":{"type":["string","null"]},"claim_token":{"type":"string"},"claim_generation":{"type":"integer","format":"int64","description":"An additive field. Older sources omit it and use zero as their legacy\nsentinel; real fenced claims begin at generation one."},"tier":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Tier","description":"Model tiers step 2 (`docs/overhaul/model-tiers.md` §1 \"Helper at\nCLAIM\" / §3 step 2): the work tier a source resolved at CREATE time.\n`Some` when the source wants THIS helper to perform tier resolution\n— either `provider` was left unpinned (the helper picks BOTH\nprovider and model from `candidates`), or `provider` is pinned but\nno `model` was set (the helper picks only a model within that\nprovider — review P1 finding 1, 2026-09-02: a pinned provider does\nNOT mean tier routing has nothing left to do). `None` (the default\n— an older source simply never sends this key) preserves today's\nexact behavior. Additive, like `permission_mode`/`fork_of_native_id`/\netc. above, but — unlike those — `skip_serializing_if` so an older\nfixture/consumer never sees a wire key it has no concept of at all.\n`deserialize_with` (finding 9, two-reviewer pass): an unrecognized\ntier string maps to `None` rather than rejecting the whole claim —\nsee [`deserialize_optional_tier`]'s own doc."}]},"candidates":{"type":["array","null"],"items":{"$ref":"#/components/schemas/TierCandidate"},"description":"The tier's candidate list, narrowed to whatever the source itself\nalready knew was advertised at CREATE time (design §1's\n`candidate_subset` — the hub CREATE-time subset stored on the turn).\nA helper routes WITHIN this list (never expands it) against its own\ncurrently-advertised providers and window state — see\n`aya_drive::route_candidates`. `None` is a source that never sent a\ncandidate subset at all (a hub predating design §3 step 3, or a\nworker-routed source), which a helper falls back to routing over its\nOWN configured tier table for; `Some(vec![])` (finding 8,\ntwo-reviewer pass) — an explicit empty subset, meaning the SOURCE\nsaw nothing advertised at create time — gets the identical fallback\ntreatment, since the claiming helper's own live table is the honest\nsecond look a stale CREATE-time snapshot can't provide. See\n`poll.rs`'s claim-time router for exactly where that fallback is\napplied."},"reservation_id":{"type":["string","null"],"description":"Set only when the turn is addressed to an ephemeral worker's\nreservation (`rsv_…`). Additive and omitted when absent, so older\nsources and helpers never see the key."},"work_checkout":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/WorkCheckout","description":"Set only for a work run whose profile asks for a fresh checkout: the\nworker prepares that working copy and runs the turn in it instead of\n`working_directory`. Additive and omitted when absent, so older\nsources and helpers never see the key. A worker that predates it\nwould ignore it and run in `working_directory`, so a source hands\nsuch a turn only to a claim naming [`CLAIM_CAPABILITY_WORK_CHECKOUT`]\nin its [`CLAIM_CAPABILITIES_HEADER`]."}]}}},"ClaimedWorkerCommand":{"type":"object","description":"What [`WorkerStore::claim_next_command`] hands back to a polling worker —\nfield-for-field the wire shape `hub_client.rs::ClaimedControlCommand`\ndeserializes, so this type is serialized directly as the `200` response\nbody of `POST /api/agent/workers/self/commands/claim`\n(`workers_routes.rs::agent_claim_worker_command`) rather than mirrored a\nsecond time as an ad hoc documentation-only type, the same convention\n`openapi.rs` already uses for `aya_execution_protocol::ClaimedTurn`.","required":["operation_id","provider","provider_instance_id","action","claim_token","claim_generation","lease_expires_at"],"properties":{"operation_id":{"type":"string"},"provider":{"type":"string"},"provider_instance_id":{"type":"string"},"action":{"type":"string"},"claim_token":{"type":"string"},"claim_generation":{"type":"integer","format":"int64"},"lease_expires_at":{"type":"string","description":"RFC3339 — see [`WorkerStore::claim_next_command`]'s own doc for why\nthis is formatted once, at claim time, rather than left as a raw\nmillisecond count for the route layer to format."}}},"ClientCreateRequest":{"type":"object","required":["client_id","kind","grants"],"properties":{"client_id":{"type":"string","description":"`[A-Za-z0-9._-]{1,128}`; the `w-` prefix is reserved for enrolled workers."},"kind":{"$ref":"#/components/schemas/ClientKindWire"},"display_name":{"type":["string","null"]},"grants":{"type":"array","items":{"$ref":"#/components/schemas/GrantWire"}},"public_jwk":{"type":["object","null"],"description":"Ed25519 public key (`OKP` JWK) for `private_key_jwt`, instead of a secret."}},"additionalProperties":false},"ClientCreated":{"type":"object","required":["client"],"properties":{"client":{"$ref":"#/components/schemas/ClientView"},"client_secret":{"type":["string","null"],"description":"Shown once. Absent for a `private_key_jwt` client."}}},"ClientKindWire":{"type":"string","enum":["service","static_worker","worker"]},"ClientList":{"type":"object","required":["clients"],"properties":{"clients":{"type":"array","items":{"$ref":"#/components/schemas/ClientView"}}}},"ClientView":{"type":"object","required":["client_id","kind","grants","status","created_at_ms"],"properties":{"client_id":{"type":"string"},"kind":{"$ref":"#/components/schemas/ClientKindWire"},"display_name":{"type":["string","null"]},"grants":{"type":"array","items":{"$ref":"#/components/schemas/GrantWire"}},"status":{"type":"string","description":"`active`, `disabled` or `revoked`."},"pool_id":{"type":["string","null"]},"reservation_id":{"type":["string","null"]},"created_at_ms":{"type":"integer","format":"int64"}}},"CompletionRequest":{"type":"object","description":"Terminal outcome reported for one claimed execution lease.","required":["claim_token","ok"],"properties":{"claim_token":{"type":"string"},"ok":{"type":"boolean"},"error":{"type":["string","null"]},"created_native_id":{"type":["string","null"]},"created_root_id":{"type":["string","null"]},"requeue":{"type":"boolean"},"park_until_ms":{"type":["integer","null"],"format":"int64","description":"Model tiers step 2 (two-reviewer pass, finding 3): \"park instead of\nretry-to-failure.\" Only meaningful alongside `requeue: true`. `Some`\ntells the source WHEN this turn's candidates are expected to reopen\n(`aya_drive::RouteDecision::AllExhausted`'s own `earliest_reset_ms`,\nwhen known) — the source sets this turn's next-eligible time to\n`max(now + <its own short backoff>, park_until_ms)` and does NOT\ncount it as a retry attempt, since waiting for a KNOWN window reset\nis not the same failure mode the attempt cap exists to bound (a\nturn stuck losing an ordinary busy-session race repeatedly). `None`\n(the default — an older/non-tier requeue) preserves today's exact\nrequeue behavior: the short backoff only, attempts incremented\nnormally. Additive, nullable like `error`/`created_native_id`/\n`created_root_id` above (no `skip_serializing_if` — an older\nconsumer already tolerates this shape of explicit-null field)."},"resolved_provider":{"type":["string","null"],"description":"Model tiers step 3 follow-up (coordinator addition, 2026-09-02):\ncloses the \"tiered claim resolves locally, hub never learns which\nprovider\" gap `docs/overhaul/model-tiers.md` §6 flagged as a known\nlimitation. `Some` when the claiming worker performed tier\nresolution (see `ClaimedTurn::tier`'s own doc for the two shapes\nthat triggers it): for a provider-unresolved tiered claim, the\nSELECTED provider; for a provider-pinned tiered claim whose model\nwas unresolved (review P1 finding 1, 2026-09-02), the SAME\nalready-known provider, repeated. `None` for a claim with an\nexplicit provider AND model, and for a claim with no `tier` at all.\nThe source only validates/persists this value against stored\ncandidates when its own stored provider was `NULL`; the pinned\ncase's repeated value is accepted but not re-validated — see\n`aya-hub`'s `TurnQueue::mark_done_for`. Additive, nullable like\n`error`/`created_native_id`/`created_root_id`/`park_until_ms`\nabove, no `skip_serializing_if` (an older consumer already\ntolerates this shape of explicit-null field)."},"resolved_model":{"type":["string","null"],"description":"The model half of the same resolved candidate — `Some` only\nalongside `resolved_provider: Some(_)` (never independently),\n`None` when the resolved candidate's own `model` was `None` (the\nharness's own default — see `TierCandidate::model`'s doc) EVEN\nTHOUGH `resolved_provider` is `Some`. Reported for both shapes\n`resolved_provider` covers. Unlike `resolved_provider`, never\nvalidated against stored candidates — unconditionally fills a\nstill-unset model. Same additive/nullable shape as every other\nfield on this struct."}}},"ContentBlock":{"oneOf":[{"type":"object","required":["text","type"],"properties":{"text":{"type":"string"},"type":{"type":"string","enum":["text"]}}},{"type":"object","required":["text","type"],"properties":{"text":{"type":"string"},"type":{"type":"string","enum":["thinking"]}}},{"type":"object","required":["call_id","tool_name","input","type"],"properties":{"call_id":{"type":"string"},"tool_name":{"type":"string"},"input":{},"mcp":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/McpTool","description":"Set when the harness says this is an MCP tool call, so no reader\nhas to parse one harness's tool naming (CONTRACT.md §23i)."}]},"type":{"type":"string","enum":["tool_call"]}}},{"type":"object","required":["call_id","output","is_error","type"],"properties":{"call_id":{"type":"string"},"output":{},"is_error":{"type":"boolean"},"type":{"type":"string","enum":["tool_result"]}}},{"type":"object","required":["label","data","type"],"properties":{"label":{"type":"string"},"data":{},"type":{"type":"string","enum":["attachment"]}}},{"type":"object","required":["raw","type"],"properties":{"raw":{},"type":{"type":"string","enum":["other"]}}}],"description":"Typed message content. `Other` is the drift escape hatch — unknown block\nshapes are preserved, never dropped."},"CreateReservationRequest":{"type":"object","required":["pool","request_id","kind"],"properties":{"pool":{"type":"string"},"request_id":{"type":"string","description":"Caller-chosen idempotency key, unique per requesting principal."},"kind":{"$ref":"#/components/schemas/ReservationKind"},"ttl_secs":{"type":["integer","null"],"format":"int64","description":"Shortens, never extends, the pool's TTL for this kind.","minimum":0},"harnesses":{"type":["array","null"],"items":{"type":"string"},"description":"A subset of the pool's harnesses; default is all of them."},"source":{"type":["object","null"],"description":"Validated against the pool's restricted source schema."},"labels":{"type":"object","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}}},"additionalProperties":false},"DeprovisionResult":{"type":"object","required":["principal","reservations_ended","clients_disabled","turns_ended"],"properties":{"principal":{"type":"string"},"reservations_ended":{"type":"integer","format":"int32","minimum":0},"clients_disabled":{"type":"integer","format":"int32","minimum":0},"turns_ended":{"type":"integer","format":"int32","minimum":0}}},"DiscoveryDocument":{"type":"object","required":["issuer","token_endpoint","jwks_uri","authorization_endpoint","grant_types_supported","response_types_supported","token_endpoint_auth_methods_supported","token_endpoint_auth_signing_alg_values_supported","id_token_signing_alg_values_supported","subject_types_supported","scopes_supported"],"properties":{"issuer":{"type":"string"},"token_endpoint":{"type":"string"},"jwks_uri":{"type":"string"},"authorization_endpoint":{"type":"string","description":"A placeholder, present only because OIDC discovery clients require\nthe field: this issuer supports only the `client_credentials` grant,\nso `GET /oauth/authorize` always refuses with 400\n`unsupported_response_type`."},"grant_types_supported":{"type":"array","items":{"type":"string"},"description":"Always `[\"client_credentials\"]`."},"response_types_supported":{"type":"array","items":{"type":"string"},"description":"Always `[\"none\"]`: no authorization response type is supported (see\n`authorization_endpoint`)."},"token_endpoint_auth_methods_supported":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_signing_alg_values_supported":{"type":"array","items":{"type":"string"}},"id_token_signing_alg_values_supported":{"type":"array","items":{"type":"string"}},"subject_types_supported":{"type":"array","items":{"type":"string"}},"scopes_supported":{"type":"array","items":{"type":"string"},"description":"The fixed agent-boundary scopes. Not listed, because they are not a\nfixed set: the provisioning scopes `workers:provision:<pool>` (one per\npool, granted only to registered provisioner clients) and relay\nscopes (named by the relay and granted through pool grants)."}}},"DoctorCheck":{"type":"object","required":["name","ok"],"properties":{"name":{"type":"string"},"ok":{"type":"boolean"}}},"DoctorResult":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"},"checks":{"type":"array","items":{"$ref":"#/components/schemas/DoctorCheck"}}}},"EnrollRequest":{"type":"object","required":["enrollment_token","public_jwk","attestation","agent_version","protocols"],"properties":{"enrollment_token":{"type":"string","description":"The single-use enrollment token. It authorizes this operation and is\nsent here in the body, not in an `Authorization` header."},"public_jwk":{"$ref":"#/components/schemas/OkpJwk"},"attestation":{"$ref":"#/components/schemas/AttestationEvidence"},"description":{"$ref":"#/components/schemas/WorkerDescription"},"machine_id_sha256":{"type":["string","null"]},"agent_version":{"type":"string"},"protocols":{"type":"array","items":{"type":"integer","format":"int32","minimum":0}}}},"EnrollResponse":{"type":"object","required":["worker_id","client_id","issuer","token_endpoint","audiences","reservation","roots","heartbeat_interval_secs","dead_man_secs","protocol"],"properties":{"worker_id":{"type":"string"},"client_id":{"type":"string"},"issuer":{"type":"string"},"token_endpoint":{"type":"string"},"audiences":{"$ref":"#/components/schemas/Audiences"},"reservation":{"$ref":"#/components/schemas/ReservationView"},"roots":{"type":"array","items":{"$ref":"#/components/schemas/AssignedRoot"}},"heartbeat_interval_secs":{"type":"integer","format":"int64","minimum":0},"dead_man_secs":{"type":"integer","format":"int64","minimum":0},"protocol":{"type":"integer","format":"int32","minimum":0}}},"EnrollmentTokenResponse":{"type":"object","description":"The enrollment-token fetch's answer. Units differ on purpose and are fixed\nby D8: hub APIs carry milliseconds (`expires_at_ms`), the outbound webhook\nbody carries Unix seconds (`expires_at`).","required":["token","expires_at_ms","generation"],"properties":{"token":{"type":"string","description":"The single-use enrollment token (`aet_...`)."},"expires_at_ms":{"type":"integer","format":"int64","description":"When the token stops working, in Unix milliseconds: 15 minutes after\nthe fetch, or the reservation's own end, whichever is first."},"generation":{"type":"integer","format":"int32","description":"Rises with every fetch; only the latest generation can enroll.","minimum":0}}},"ErrorBody":{"type":"object","description":"Uniform error body for the workers API.","required":["error"],"properties":{"error":{"type":"string"},"retry_after_secs":{"type":["integer","null"],"format":"int64","minimum":0}}},"ErrorResponse":{"type":"object","description":"Mirrors the `{\"error\": \"...\"}` body `err_json` (reimplemented identically\nin `chat_routes.rs`, `turn_routes.rs`, `jobs_routes.rs`,\n`capabilities_routes.rs`, `index_routes.rs`) builds for every non-2xx\nresponse in this spec, and the `{\"error\": \"...\"}` shape\n`integration_routes.rs::error` builds independently for the same purpose.","required":["error"],"properties":{"error":{"type":"string"}}},"EventAppendRequest":{"type":"object","description":"Events appended to one claimed execution lease.","required":["claim_token","batch_id","events"],"properties":{"claim_token":{"type":"string"},"batch_id":{"type":"string","description":"Stable, opaque identity for this logical append. A transport retry\nreuses this value so the source can acknowledge it without appending\nthe events twice."},"events":{"type":"array","items":{"$ref":"#/components/schemas/TurnEvent"}}}},"Evidence":{"type":"object","required":["id","source_kind","reference","label","note"],"properties":{"id":{"type":"string","description":"Non-blank, at most 16 KiB (bytes), and UNIQUE across this result's\nown `evidence` array — see [`ReviewResult`]'s own doc.","maxLength":16384,"minLength":1},"source_kind":{"type":"string","description":"Exactly one of these three values ([`parse_result`]'s own check).","pattern":"^(brain|project|conversation)$"},"reference":{"type":"string","description":"Non-blank, at most 16 KiB (bytes), and SAFE ([`safe_reference`]: no\nleading `/`, no `\\\\`, no `file://` (case-insensitive), and no\ndrive-letter-absolute path like `C:\\`) — the safety rule has no\nJSON Schema representation, enforced server-side only.","maxLength":16384,"minLength":1},"label":{"type":"string","description":"Non-blank, at most 16 KiB (bytes).","maxLength":16384,"minLength":1},"note":{"type":"string","description":"Non-blank, at most 16 KiB (bytes).","maxLength":16384,"minLength":1}},"additionalProperties":false},"GrantTemplate":{"type":"object","description":"One entry of the grants a worker of this pool receives. `audience` is a\nplaceholder (`$hub_agent`, `$relay`, `$provision`) or an external resource\nthe operator allow-listed in `WORKLOAD_EXTERNAL_AUDIENCES`.","required":["audience","scopes"],"properties":{"audience":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}}},"additionalProperties":false},"GrantWire":{"type":"object","required":["audience","scopes"],"properties":{"audience":{"type":"string","description":"The resource (audience) tokens may be requested for."},"scopes":{"type":"array","items":{"type":"string"}}}},"Idea":{"type":"object","required":["id","title","description"],"properties":{"id":{"type":"string","description":"Non-blank, at most 16 KiB (bytes).","maxLength":16384,"minLength":1},"title":{"type":"string","description":"Non-blank, at most 16 KiB (bytes).","maxLength":16384,"minLength":1},"description":{"type":"string","description":"At most 16 KiB (bytes) — unlike `id`/`title`, blank is allowed.","maxLength":16384}},"additionalProperties":false},"IntegrationFailure":{"type":"object","description":"Mirrors the `\"failure\"` object `integration_routes.rs::get_turn` builds\nfor a `state: \"failed\"` response.","required":["code","message","retryable"],"properties":{"code":{"type":"string","description":"`\"execution_failed\"` (the turn itself failed) or `\"invalid_result\"`\n(the turn succeeded, but its output did not parse as a\n[`crate::integration_routes::ReviewResult`]) — the only two values\n`get_turn` ever sets."},"message":{"type":"string"},"retryable":{"type":"boolean"}}},"IntegrationQueued":{"type":"object","description":"Mirrors `integration_routes.rs::post_turn`'s `202` response body.","required":["request_id","state","replayed"],"properties":{"request_id":{"type":"string"},"state":{"type":"string","description":"Always the literal `\"queued\"` on this response — a distinct field\n(rather than a bare `202` with no body) so a poller's very first read\nalready matches the shape `GET /api/integrations/turns/{request_id}`\nkeeps returning."},"replayed":{"type":"boolean","description":"`true` when this `request_id` had already been queued with the\nidentical input hash and this call is an idempotent replay, not a new\nenqueue."}}},"IntegrationTurnRequest":{"type":"object","description":"The whole request body is additionally capped at [`MAX_BODY_BYTES`]\n(512 KiB) before it's even parsed into this shape — see `post_turn`'s\nown `413` response. `min_length`/`max_length` below are JSON Schema's\nown Unicode-code-point count, not bytes — the real, server-enforced\nper-field limit ([`valid_field`]) is UTF-8 BYTE length, so a\nschema-valid multi-byte-character string can still be rejected\nserver-side for exceeding it.","required":["request_id","purpose","input_version","context"],"properties":{"request_id":{"type":"string","description":"Non-blank after trimming whitespace, at most 16 KiB (bytes — see\nthis type's own doc).","maxLength":16384,"minLength":1},"purpose":{"type":"string","description":"The only value this endpoint accepts is `\"idea_review_v1\"`\n([`validate_request`]).","pattern":"^idea_review_v1$"},"input_version":{"type":"integer","format":"int32","description":"The only value this endpoint accepts is `1` ([`validate_request`]).\nEncoded as `minimum`/`maximum` both `1` — a plain integer field has\nno JSON Schema \"const\"/single-value `enum` shorthand in this\ngenerator, and `minimum == maximum` has the identical effect.","maximum":1,"minimum":1},"context":{"$ref":"#/components/schemas/ReviewContext"}},"additionalProperties":false},"IntegrationTurnStatus":{"type":"object","description":"Mirrors `integration_routes.rs::get_turn`'s response body across all four\nstates it can report. Modeled as ONE schema with state-dependent optional\nfields, rather than a `oneOf` of four distinct schemas, because `state`\nis a plain string discriminator here (not a serde-tagged Rust enum with\nits own type to derive from — see `get_turn`'s own match arms): `result`\nis present only when `state = \"ready\"`, `failure` only when `state =\n\"failed\"`, and neither is present for `\"queued\"`/`\"running\"`. This is\nflagged in the self-verification report as the one response documented\nwith looser typing than a real Rust enum would give; a future revision\ncould tighten it by giving `get_turn` an actual `IntegrationTurnStatus`\nenum to serialize instead of hand-built `json!(...)` per branch.","required":["request_id","state"],"properties":{"request_id":{"type":"string"},"state":{"type":"string","description":"`\"queued\"`, `\"running\"`, `\"ready\"`, or `\"failed\"`."},"result":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ReviewResult"}]},"failure":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/IntegrationFailure"}]}}},"Jwks":{"type":"object","required":["keys"],"properties":{"keys":{"type":"array","items":{"$ref":"#/components/schemas/RsaJwk"}}}},"KeysRotated":{"type":"object","required":["active_kid","retiring_kids"],"properties":{"active_kid":{"type":"string"},"next_kid":{"type":["string","null"]},"retiring_kids":{"type":"array","items":{"type":"string"}}}},"Limits":{"type":"object","properties":{"max_active":{"type":"integer","format":"int32","minimum":0},"per_user":{"type":"integer","format":"int32","minimum":0},"per_team":{"type":"integer","format":"int32","minimum":0},"per_org":{"type":"integer","format":"int32","minimum":0},"max_queue":{"type":"integer","format":"int32","minimum":0},"max_inflight_creates":{"type":"integer","format":"int32","description":"Concurrent `Submitting`/`Provisioning` creates the driver allows.","minimum":0}},"additionalProperties":false},"MachinePut":{"type":"object","required":["credential"],"properties":{"credential":{"type":"string","description":"Replaces the machine's credential; it is stored hashed and shown to nobody."},"labels":{"type":"object","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}},"owner_principal":{"type":["string","null"],"description":"Pin the machine to one principal's reservations."}},"additionalProperties":false},"MachineView":{"type":"object","required":["pool","machine_id","state","labels"],"properties":{"pool":{"type":"string"},"machine_id":{"type":"string"},"state":{"type":"string","description":"`available`, `assigned`, `needs_reset` or `retired`."},"reservation_id":{"type":["string","null"]},"labels":{"type":"object","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}},"owner_principal":{"type":["string","null"]},"last_checkin_ms":{"type":["integer","null"],"format":"int64"},"place":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PlaceRef","description":"The machine's own name (`machine:<hostname>`): its latest reported\nname when that name is bound to it, or its latest admin binding if it\nhas never reported."}]},"aliases":{"type":"array","items":{"type":"string"},"description":"The place's aliases, as typed."}}},"McpServer":{"type":"object","description":"One MCP server a driven session has configured (CONTRACT.md §21), as\nits harness reports it (`caps.mcp.inventory`).","required":["name"],"properties":{"name":{"type":"string"},"command":{"type":["string","null"]},"healthy":{"type":["boolean","null"],"description":"Result of an actual connectivity check (`claude mcp list`). `None`\nwhen this entry came from a source that only knows the server's\nname — e.g. Claude's init-record manifest, which carries no health\nsignal at all (CONTRACT.md §21) — never a guess dressed up as a\nnegative result."},"status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/McpServerStatus","description":"The server's state in the harness's own report, normalized. `None`\nwhen the harness gave no state (an init manifest, an ACP session's\ninjected list), never a guess."}]},"detail":{"type":["string","null"],"description":"What the harness said beyond the state: a connection error, a failed\ntool listing. Its own words, trimmed; `None` when it said nothing."}}},"McpServerStatus":{"type":"string","description":"An MCP server's state as a harness reports it (CONTRACT.md §23e).","enum":["connected","starting","needs_auth","failed","disabled"]},"McpTool":{"type":"object","description":"The MCP server and tool behind a tool call, as the harness reported them\n(Claude's `mcp__<server>__<tool>` name, Codex's `mcpToolCall` item, ...).\nAbsent where the harness does not say which server a call went to; such a\ncall is a plain tool call.","required":["server","tool"],"properties":{"server":{"type":"string"},"tool":{"type":"string"}}},"Message":{"type":"object","required":["id","body","created_at"],"properties":{"id":{"type":"string","description":"Non-blank, at most 16 KiB (bytes).","maxLength":16384,"minLength":1},"body":{"type":"string","description":"Non-blank, at most 16 KiB (bytes).","maxLength":16384,"minLength":1},"created_at":{"type":"string","description":"Non-blank, at most 16 KiB (bytes).","maxLength":16384,"minLength":1}},"additionalProperties":false},"MessageNode":{"type":"object","description":"One node in a session's message tree.","required":["native_id","role","kind","blocks"],"properties":{"native_id":{"type":"string","description":"Provider-native id (Claude record uuid; Hermes message id)."},"parent_native_id":{"type":["string","null"],"description":"None = a root of the tree. Claude rewinds create siblings."},"role":{"$ref":"#/components/schemas/Role"},"kind":{"type":"string","description":"The provider's native record type, preserved verbatim\n(\"user\", \"assistant\", \"system\", \"attachment\", …)."},"timestamp":{"type":["integer","null"],"format":"int64","description":"Unix milliseconds."},"text":{"type":["string","null"],"description":"Plain text extracted for display and FTS. None when non-textual."},"blocks":{"type":"array","items":{"$ref":"#/components/schemas/ContentBlock"}},"is_sidechain":{"type":"boolean","description":"Claude: record belonged to a subagent sidechain."},"extra":{}}},"OAuthError":{"type":"object","description":"RFC 6749 §5.2 error body.","required":["error"],"properties":{"error":{"type":"string","description":"`invalid_request`, `invalid_client`, `invalid_grant`,\n`unauthorized_client`, `unsupported_grant_type`, `invalid_scope`,\n`invalid_target` or `unsupported_response_type`."},"error_description":{"type":["string","null"]}}},"OkResponse":{"type":"object","description":"Mirrors the `{\"ok\": true}` body every `agent_events`/`agent_done`/\n`agent_job_result`/`agent_report_capabilities` success response shares.","required":["ok"],"properties":{"ok":{"type":"boolean"}}},"OkpJwk":{"type":"object","description":"A public OKP/Ed25519 JWK. Unknown members are rejected on parse so a\nworker cannot smuggle a `d` (private) member into the hub's database.","required":["kty","crv","x"],"properties":{"kty":{"type":"string"},"crv":{"type":"string"},"x":{"type":"string"},"kid":{"type":["string","null"]},"use":{"type":["string","null"]},"alg":{"type":["string","null"]}},"additionalProperties":false},"PlaceAliasPut":{"type":"object","required":["place"],"properties":{"place":{"type":"string","description":"The machine: `machine:<name>` or a bare name."}},"additionalProperties":false},"PlaceAliasRemoved":{"type":"object","required":["alias","place"],"properties":{"alias":{"type":"string","description":"The alias as it was typed."},"place":{"$ref":"#/components/schemas/PlaceRef"}}},"PlaceAliasRename":{"type":"object","required":["to"],"properties":{"to":{"type":"string","description":"The new alias."}},"additionalProperties":false},"PlaceBindingPut":{"type":"object","required":["identity"],"properties":{"identity":{"type":"string","description":"The enrolled identity that owns the name from now on:\n`client:<client_id>` (an active `static_worker` client) or\n`static:<pool>/<machine_id>` (a static-pool machine)."}},"additionalProperties":false},"PlaceConflictView":{"type":"object","required":["identity","reason","first_seen_ms","last_seen_ms","count"],"properties":{"identity":{"type":"string","description":"The identity whose report was refused."},"reason":{"type":"string","description":"`identity`: the name is bound to another identity (`bound`).\n`alias`: the name is an alias of another machine (`bound` is it)."},"bound":{"type":["string","null"]},"first_seen_ms":{"type":"integer","format":"int64"},"last_seen_ms":{"type":"integer","format":"int64"},"count":{"type":"integer","format":"int64"}}},"PlaceList":{"type":"object","required":["places","truncated","over_quota","over_quota_truncated"],"properties":{"places":{"type":"array","items":{"$ref":"#/components/schemas/PlaceView"},"description":"Sorted by id, at most 1000."},"truncated":{"type":"boolean"},"over_quota":{"type":"array","items":{"$ref":"#/components/schemas/QuotaFlagView"},"description":"Every identity over its place quota, sorted by identity, at most 1000."},"over_quota_truncated":{"type":"boolean","description":"More identities are over quota than `over_quota` lists."}}},"PlaceRef":{"type":"string","description":"Where an agent runs: `<kind>:<value>` (CONTRACT.md §26). Opaque; never decode the value.","examples":["machine:games"],"pattern":"^(machine|worker|e2b|lp):[A-Za-z0-9._-]{1,128}$"},"PlaceUnbound":{"type":"object","required":["place","identity"],"properties":{"place":{"$ref":"#/components/schemas/PlaceRef"},"identity":{"type":"string","description":"The identity it was bound to."}}},"PlaceView":{"type":"object","description":"One machine name as the hub holds it.","required":["place","current","aliases","conflicts"],"properties":{"place":{"$ref":"#/components/schemas/PlaceRef","description":"The canonical id, `machine:<name>`."},"identity":{"type":["string","null"],"description":"The enrolled identity it is bound to (`client:<client_id>` or\n`static:<pool>/<machine_id>`); absent when unbound."},"source":{"type":["string","null"],"description":"`observed` (first report) or `admin` (an operator's rebind)."},"bound_at_ms":{"type":["integer","null"],"format":"int64"},"last_reported_ms":{"type":["integer","null"],"format":"int64","description":"When the bound identity last reported this name."},"current":{"type":"boolean","description":"Whether this name is the bound identity's current place, including the\nadmin-binding fallback when it has never reported."},"aliases":{"type":"array","items":{"type":"string"},"description":"Display and input names, as typed."},"conflicts":{"type":"array","items":{"$ref":"#/components/schemas/PlaceConflictView"},"description":"Refused reports of this name; never merged."},"over_quota":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/QuotaFlagView","description":"The bound identity is over its place quota."}]}}},"PoolList":{"type":"object","required":["pools"],"properties":{"pools":{"type":"array","items":{"$ref":"#/components/schemas/PoolView"}}}},"PoolSpec":{"type":"object","required":["name","harnesses","backend","attestation"],"properties":{"name":{"type":"string"},"org":{"type":"string"},"access":{"$ref":"#/components/schemas/Access"},"labels":{"type":"object","description":"Labels a worker must carry (`key=value`, all of them).","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}},"min_agent_version":{"type":["string","null"]},"harnesses":{"type":"array","items":{"type":"string"}},"workspace_path":{"type":"string","description":"Where the worker checks out the workspace; `{repo}` is substituted."},"limits":{"$ref":"#/components/schemas/Limits"},"budget":{"$ref":"#/components/schemas/Budget"},"ttl":{"$ref":"#/components/schemas/Ttl"},"source":{"$ref":"#/components/schemas/SourceSchema"},"stream_policy":{"type":["string","null"]},"reuse":{"$ref":"#/components/schemas/Reuse"},"backend":{"$ref":"#/components/schemas/BackendSpec"},"attestation":{"$ref":"#/components/schemas/AttestationSpec"},"grants":{"type":"array","items":{"$ref":"#/components/schemas/GrantTemplate"}},"require_doctor_ok":{"type":"boolean"}},"additionalProperties":false},"PoolView":{"type":"object","description":"A stored pool: the document plus the optimistic-concurrency version.","required":["id","version","spec","status","updated_at_ms"],"properties":{"id":{"type":"string"},"version":{"type":"integer","format":"int64","description":"Increments on every accepted `PUT`; echo it in `If-Match: \"<version>\"`."},"spec":{"$ref":"#/components/schemas/PoolSpec"},"status":{"type":"string","description":"`active`, or `retired` (no new reservations; a `PUT` re-activates it)."},"updated_at_ms":{"type":"integer","format":"int64"}}},"Project":{"type":"object","required":["id","name","description"],"properties":{"id":{"type":"string","description":"Non-blank, at most 16 KiB (bytes), and UNIQUE across this request's\nown `projects` array — the uniqueness rule has no JSON Schema\nrepresentation, enforced server-side only ([`validate_request`]).","maxLength":16384,"minLength":1},"name":{"type":"string","description":"Non-blank, at most 16 KiB (bytes).","maxLength":16384,"minLength":1},"description":{"type":"string","description":"At most 16 KiB (bytes) — unlike `id`/`name`, blank is allowed.","maxLength":16384}},"additionalProperties":false},"Proposal":{"type":"object","required":["id","kind","project_id","rationale","evidence_ids"],"properties":{"id":{"type":"string","description":"Non-blank, at most 16 KiB (bytes), and UNIQUE across this result's\nown `proposals` array — see [`ReviewResult`]'s own doc.","maxLength":16384,"minLength":1},"kind":{"type":"string","description":"Exactly one of these two values ([`parse_result`]'s own check) —\nsee [`ReviewResult`]'s own doc for the `title`/`description` rule\neach value implies.","pattern":"^(project_link|task)$"},"project_id":{"type":"string","description":"Must be the `id` of one of the original request's own\n`context.projects` — a cross-request rule with no JSON Schema\nrepresentation, see [`ReviewResult`]'s own doc.","maxLength":16384,"minLength":1},"rationale":{"type":"string","description":"Non-blank, at most 16 KiB (bytes).","maxLength":16384,"minLength":1},"evidence_ids":{"type":"array","items":{"type":"string"},"description":"Every entry must be the `id` of one of THIS SAME response's own\n`evidence` above, with no duplicates within this one proposal's own\nlist — see [`ReviewResult`]'s own doc."},"title":{"type":["string","null"],"description":"Required (and non-blank, at most 16 KiB) when `kind == \"task\"`;\nmust be omitted or null when `kind == \"project_link\"` — see\n[`ReviewResult`]'s own doc.","maxLength":16384,"minLength":1},"description":{"type":["string","null"],"description":"At most 16 KiB (bytes) when present; must be omitted or null when\n`kind == \"project_link\"` — see [`ReviewResult`]'s own doc.","maxLength":16384}},"additionalProperties":false},"ProvisionStatusRequest":{"type":"object","description":"IT -> hub status callback.","required":["state"],"properties":{"state":{"$ref":"#/components/schemas/CallbackState"},"eta_secs":{"type":["integer","null"],"format":"int64","minimum":0},"position":{"type":["integer","null"],"format":"int32","minimum":0},"instance_id":{"type":["string","null"]},"nonce_sha256":{"type":["string","null"],"description":"SHA-256 (hex) of the nonce IT placed on the instance, for `it_nonce`."},"reason":{"type":["string","null"]}},"additionalProperties":false},"PublishOutcome":{"type":"object","description":"Outcome of one [`IndexWriter::publish_sessions`] batch: `applied` counts\nsessions the store actually rewrote, `skipped_unchanged` counts ones\nwhose fingerprint already matched what the hub had (see that method's own\ndoc for why the skip is load-bearing, not merely an optimization).","required":["applied","skipped_unchanged"],"properties":{"applied":{"type":"integer","format":"int64","minimum":0},"skipped_unchanged":{"type":"integer","format":"int64","minimum":0}}},"PublishSessionsRequest":{"type":"object","required":["root","sessions"],"properties":{"root":{"$ref":"#/components/schemas/Root"},"sessions":{"type":"array","items":{"$ref":"#/components/schemas/SessionUpload"}}}},"PullRequest":{"type":"object","required":["url","number","head_sha","state"],"properties":{"url":{"type":"string","description":"An `https://` URL ([`safe_https_url`]: has a host, and no embedded\ncredentials, query string, or fragment), non-blank, at most 2,000\nbytes. This pattern pins only the `https://` scheme — the rest of\n`safe_https_url`'s rules have no JSON Schema representation and are\nenforced server-side only.","maxLength":2000,"minLength":1,"pattern":"^[Hh][Tt][Tt][Pp][Ss]://.+"},"number":{"type":"integer","format":"int64","description":"A positive PR number (`result.pr.number <= 0` is rejected).","minimum":1},"head_sha":{"type":"string","description":"A full 40-character SHA-1 or 64-character SHA-256 hexadecimal object id — see\n[`parse_result`]'s own `sha_is_valid` check.","pattern":"^[0-9a-fA-F]{40}$|^[0-9a-fA-F]{64}$"},"state":{"type":"string","description":"Non-blank after trimming whitespace, at most 100 bytes.","maxLength":100,"minLength":1}},"additionalProperties":false},"Question":{"type":"object","required":["id","text","reason"],"properties":{"id":{"type":"string","description":"Non-blank, at most 16 KiB (bytes), and UNIQUE across this result's\nown `questions` array — see [`ReviewResult`]'s own doc.","maxLength":16384,"minLength":1},"text":{"type":"string","description":"Non-blank, at most 16 KiB (bytes).","maxLength":16384,"minLength":1},"reason":{"type":"string","description":"Non-blank, at most 16 KiB (bytes).","maxLength":16384,"minLength":1}},"additionalProperties":false},"QueueOwner":{"type":"string","enum":["aya","backend"]},"QuotaFlagView":{"type":"object","description":"An identity over its place quota: its newest names are refused until an\noperator unbinds or rebinds one of its names.","required":["identity","reason","refused_place","first_seen_ms","last_seen_ms","count"],"properties":{"identity":{"type":"string"},"reason":{"type":"string","description":"`names` (too many bound names) or `conflicts` (too many conflicting\nnames recorded)."},"refused_place":{"type":"string","description":"The most recently refused name."},"first_seen_ms":{"type":"integer","format":"int64"},"last_seen_ms":{"type":"integer","format":"int64"},"count":{"type":"integer","format":"int64","description":"Refusals recorded (at most one a write window)."}}},"ReasonBody":{"type":"object","required":["reason"],"properties":{"reason":{"type":"string"}},"additionalProperties":false},"RemoteAuthInfo":{"type":"object","required":["issuer","resource","scopes"],"properties":{"issuer":{"type":"string","description":"The OIDC issuer a client fetches its token from."},"resource":{"type":"string","description":"The `resource` (token audience) to request."},"scopes":{"type":"array","items":{"type":"string"},"description":"Both scopes this API knows: `chat:read` and `chat:drive`."}}},"RemoteChatResponse":{"type":"object","required":["turn_id","replayed"],"properties":{"turn_id":{"type":"integer","format":"int64"},"replayed":{"type":"boolean"},"warning":{"type":["string","null"]}}},"RemoteHarness":{"type":"object","required":["id","label","model_hint"],"properties":{"id":{"type":"string"},"label":{"type":"string"},"model_hint":{"type":"string"}}},"RemoteMachine":{"type":"object","required":["id","name","online","roots"],"properties":{"id":{"type":"string","description":"The presence principal of the helper polling these roots, or\n`\"unknown\"` for roots never seen polling."},"name":{"type":"string","description":"The operator's alias for `id` (`REMOTE_MACHINE_ALIASES_JSON`), else `id`."},"online":{"type":"boolean"},"last_seen_ms":{"type":["integer","null"],"format":"int64"},"roots":{"type":"array","items":{"$ref":"#/components/schemas/RemoteMachineRoot"}},"place":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PlaceRef","description":"Where this helper runs, as an `aya_core::place` id: `machine:<name>`\nfor an enrolled static box (its latest reported name when that name is\nbound to it, or its latest admin binding if it has never reported),\n`worker:w-…` for an enrolled reservation worker. Absent otherwise."}]},"aliases":{"type":"array","items":{"type":"string"},"description":"The machine place's aliases, as typed."}}},"RemoteMachineRoot":{"type":"object","required":["id","provider","label","online"],"properties":{"id":{"type":"string"},"provider":{"type":"string"},"label":{"type":"string"},"account":{"type":["string","null"]},"online":{"type":"boolean","description":"A helper polled for this root within the presence window."}}},"RemoteMachines":{"type":"object","required":["machines","harnesses"],"properties":{"machines":{"type":"array","items":{"$ref":"#/components/schemas/RemoteMachine"}},"harnesses":{"type":"array","items":{"$ref":"#/components/schemas/RemoteHarness"}}}},"RemoteMessagesCursor":{"type":"object","required":["pk"],"properties":{"ts":{"type":["integer","null"],"format":"int64"},"pk":{"type":"integer","format":"int64"}}},"RemoteMessagesPage":{"type":"object","required":["messages"],"properties":{"messages":{"type":"array","items":{"$ref":"#/components/schemas/MessageNode"},"description":"Newest first."},"next_cursor":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/RemoteMessagesCursor","description":"Pass back as `cursor` for the page of older messages; `null` when\nthis page reached the start of the session."}]}}},"RemoteNewChatRequest":{"allOf":[{"type":"object","description":"`POST /chats` body fields.","required":["request_id","provider","text"],"properties":{"request_id":{"type":"string","pattern":"^[A-Za-z0-9._~-]{1,200}$"},"provider":{"type":"string"},"root_id":{"type":["string","null"],"description":"A root from `GET /machines`. Give exactly one of `root_id` and `reservation_id`."},"reservation_id":{"type":["string","null"],"description":"One of your own reservations: the chat runs on its worker, on the\nharness named by `provider`."},"working_directory":{"type":["string","null"],"description":"A project from `GET /roots/{root_id}/projects`."},"text":{"type":"string","minLength":1},"permission_mode":{"type":["string","null"],"pattern":"^(read-only|allow-edits)$"},"model":{"type":["string","null"]},"effort":{"type":["string","null"]}},"additionalProperties":false},{"oneOf":[{"type":"object","required":["root_id"]},{"type":"object","required":["reservation_id"]}]}],"description":"`POST /chats` body. Give exactly one of `root_id` and `reservation_id`."},"RemoteProject":{"type":"object","required":["project","count"],"properties":{"project":{"type":"string"},"count":{"type":"integer","format":"int64"}}},"RemoteProjects":{"type":"object","required":["projects"],"properties":{"projects":{"type":"array","items":{"$ref":"#/components/schemas/RemoteProject"}}}},"RemoteSearchHit":{"type":"object","required":["session","message_native_id","role","snippet"],"properties":{"session":{"$ref":"#/components/schemas/RemoteSession"},"message_native_id":{"type":"string"},"role":{"type":"string"},"timestamp":{"type":["integer","null"],"format":"int64"},"snippet":{"type":"string","description":"Full-text snippet with `[match]…[/match]` markers around hits."}}},"RemoteSendRequest":{"type":"object","description":"`POST /sessions/{pk}/messages` body.","required":["request_id","text"],"properties":{"request_id":{"type":"string","description":"1-200 characters of `[A-Za-z0-9._~-]`, unique per caller per input.","pattern":"^[A-Za-z0-9._~-]{1,200}$"},"text":{"type":"string","description":"At most 64 KiB (bytes) after trimming.","minLength":1},"permission_mode":{"type":["string","null"],"description":"`read-only` (default) or `allow-edits`.","pattern":"^(read-only|allow-edits)$"},"model":{"type":["string","null"]},"effort":{"type":["string","null"]}},"additionalProperties":false},"RemoteSendResponse":{"type":"object","required":["turn_id","session_pk","root_id","replayed"],"properties":{"turn_id":{"type":"integer","format":"int64"},"session_pk":{"type":"integer","format":"int64"},"root_id":{"type":"string"},"permission_mode":{"type":["string","null"]},"replayed":{"type":"boolean","description":"`true` when this `request_id` had already been accepted with the\nidentical input; `turn_id` is then the original turn."},"warning":{"type":["string","null"],"description":"Set when no helper has recently polled for this chat's root, so the\nturn may wait. The turn is queued regardless."}}},"RemoteSession":{"type":"object","description":"The session JSON the hub's own read API serves, except that\n`source_path` is always `null` here: it is a path on the machine that\nowns the transcript, which has no business leaving the hub.","required":["pk","provider","root_id","native_id","message_count","extra","archived","machine_archived","pinned"],"properties":{"pk":{"type":"integer","format":"int64"},"provider":{"type":"string"},"root_id":{"type":"string"},"native_id":{"type":"string"},"account":{"type":["string","null"]},"project":{"type":["string","null"]},"title":{"type":["string","null"]},"created_at":{"type":["integer","null"],"format":"int64"},"updated_at":{"type":["integer","null"],"format":"int64"},"message_count":{"type":"integer","format":"int64"},"parent_session_id":{"type":["string","null"]},"git_branch":{"type":["string","null"]},"entrypoint":{"type":["string","null"]},"source_mtime":{"type":["integer","null"],"format":"int64"},"source_path":{"type":["string","null"]},"extra":{},"archived":{"type":"boolean"},"machine_archived":{"type":"boolean"},"pinned":{"type":"boolean"},"user_title":{"type":["string","null"]},"moved_to":{"oneOf":[{"type":"null"},{"type":"object","description":"Where a moved session now lives.","required":["reservation_id","root_id"],"properties":{"reservation_id":{"type":"string"},"root_id":{"type":"string","description":"`res-<reservation_id>.<harness>`."}}}],"description":"Present while the session is natively moved onto a live reservation:\nits turns run on that reservation's worker, and only the\nreservation's requester may send to it. Absent otherwise."}}},"RemoteTicketRequest":{"type":"object","description":"Body of `POST /api/remote/v1/relay/tickets`.","required":["worker","service","cap"],"properties":{"worker":{"type":"string","description":"Worker label (16 characters of `a-z2-7`)."},"service":{"type":"string","description":"`http:<port>` or `stream:<id>`.","example":"http:5173"},"cap":{"type":"array","items":{"type":"string"},"description":"Capabilities: any of `open`, `view`, `control`.","example":["open"]},"lease":{"type":["string","null"],"description":"A control lease the hub minted earlier for this subject and worker,\nto keep the same control lease across tickets. Omit it and a ticket\nwith `control` on a `stream` service gets a fresh one. A lease the hub\ndid not mint, minted for someone else, or revoked is refused.\nIgnored without `control` on a `stream` service."}}},"RemoteTurnEvents":{"type":"object","required":["events","next_since","claim_generation","done","attempts"],"properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/TurnEvent"}},"next_since":{"type":"integer","format":"int64","description":"Pass back as `since` for the next poll."},"claim_generation":{"type":"integer","format":"int64","description":"A change between polls means a helper re-claimed the turn: discard\nwhat you rendered and re-read from `since=-1`."},"done":{"type":"boolean"},"ok":{"type":["boolean","null"],"description":"`null` until `done`."},"error":{"type":["string","null"],"description":"The failure reason once done, or why the turn is being retried."},"session_pk":{"type":["integer","null"],"format":"int64","description":"`null` until a new chat's session exists."},"attempts":{"type":"integer","format":"int64"}}},"ReportCapabilitiesRequest":{"type":"object","required":["root_id","capabilities"],"properties":{"root_id":{"type":"string"},"capabilities":{"$ref":"#/components/schemas/Capabilities"}}},"ReservationKind":{"type":"string","enum":["job","checkout"]},"ReservationList":{"type":"object","required":["reservations"],"properties":{"reservations":{"type":"array","items":{"$ref":"#/components/schemas/ReservationView"},"description":"Newest first, at most 200."},"truncated":{"type":"boolean","description":"More reservations match than were returned. The state filter runs\nbefore the cap, so a run of newer non-matching reservations never\nhides an older matching one."}}},"ReservationState":{"type":"string","enum":["requested","queued","submitting","backend_queued","provisioning","booted","enrolled","active","suspended","releasing","destroying","destroyed","cancelled","rejected","failed","expired"]},"ReservationView":{"type":"object","required":["reservation_id","pool","kind","state","harnesses","roots","expires_at_ms","created_at_ms","runtime_ms_accrued"],"properties":{"reservation_id":{"type":"string"},"pool":{"type":"string"},"kind":{"$ref":"#/components/schemas/ReservationKind"},"state":{"$ref":"#/components/schemas/ReservationState"},"state_reason":{"type":["string","null"]},"harnesses":{"type":"array","items":{"type":"string"}},"roots":{"type":"array","items":{"type":"string"},"description":"Root ids a requester addresses turns to (`res-<id>.<harness>`)."},"queue_position":{"type":["integer","null"],"format":"int32","minimum":0},"eta_secs":{"type":["integer","null"],"format":"int64","minimum":0},"retry_after_secs":{"type":["integer","null"],"format":"int64","minimum":0},"backend_state":{"type":["string","null"]},"worker_id":{"type":["string","null"]},"expires_at_ms":{"type":"integer","format":"int64"},"created_at_ms":{"type":"integer","format":"int64"},"enrolled_at_ms":{"type":["integer","null"],"format":"int64"},"ended_at_ms":{"type":["integer","null"],"format":"int64"},"runtime_ms_accrued":{"type":"integer","format":"int64"},"place":{"type":["string","null"],"description":"Where the reservation runs, as an `aya_core::place` id: the static\nmachine's `machine:<name>` once that machine has reported a bound\nname, `e2b:<sandbox>` or `lp:<instance>` from the backend's\nreference. Absent when the hub knows no place for it. Informational\nonly: nothing routes or authorizes by it."}}},"Reuse":{"type":"object","properties":{"same_user":{"type":"boolean","description":"A static machine may go straight back to `available` when it is\nowner-pinned to the same requesting principal."}},"additionalProperties":false},"ReviewContext":{"type":"object","required":["idea","messages","omitted_message_count","projects","linked_project_ids"],"properties":{"idea":{"$ref":"#/components/schemas/Idea"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/Message"},"description":"At most [`MAX_MESSAGES`] (100) entries.","maxItems":100},"omitted_message_count":{"type":"integer","format":"int32","minimum":0},"projects":{"type":"array","items":{"$ref":"#/components/schemas/Project"},"description":"At most [`MAX_PROJECTS`] (250) entries, each with a UNIQUE `id` —\nnot expressible as a JSON Schema array constraint, enforced\nserver-side only ([`validate_request`]).","maxItems":250},"linked_project_ids":{"type":"array","items":{"type":"string"},"description":"Every entry must be the `id` of one of THIS SAME request's own\n`projects` above, with no duplicates — a cross-field membership\nrule with no JSON Schema representation, enforced server-side only\n([`validate_request`])."}},"additionalProperties":false},"ReviewResult":{"type":"object","description":"The one JSON object shape a `purpose = \"idea_review_v1\"` turn's model\noutput must parse as (see [`parse_result`]) — reused here, unmodified, as\nthe documented shape of the `result` field in a `ready`\n`GET /api/integrations/turns/{request_id}` response\n([`crate::openapi::IntegrationTurnStatus`]). No unknown fields are\naccepted (`#[serde(deny_unknown_fields)]`), and the model's TOTAL\ncollected output text — before this shape is even parsed out of it —\nmust be at most [`MAX_RESULT_BYTES`] (512 KiB); either violation reports\n`state: \"failed\"`, `failure.code: \"invalid_result\"` on the poll route,\nsame as a structurally-mismatched JSON body. `min_length`/`max_length`\nbelow are JSON Schema's own Unicode-code-point count, not bytes — the\nreal, server-enforced per-field limit ([`valid_field`]) is UTF-8 BYTE\nlength.\n\nCross-field and cross-request rules [`parse_result`] enforces that have\nNO JSON Schema representation at all (documented here, not on any one\nfield): every `questions[].id` must be unique across the array; every\n`evidence[].id` must be unique across the array; every\n`proposals[].id` must be unique across the array;\n`proposals[].project_id` must be the `id` of one of the ORIGINAL\nrequest's own `context.projects` (a project this response never saw\nnamed on the way in is rejected); `proposals[].evidence_ids` must each\nname an `id` present in THIS SAME response's own `evidence` array, with\nno duplicates within one proposal's own list; a `kind: \"task\"` proposal\nrequires a non-blank `title` (`description` stays optional); a\n`kind: \"project_link\"` proposal requires BOTH `title` and `description`\nto be omitted or null; a non-null value for either is rejected.","required":["summary","questions","evidence","proposals"],"properties":{"summary":{"type":"string","description":"Non-blank, at most 16 KiB (bytes).","maxLength":16384,"minLength":1},"questions":{"type":"array","items":{"$ref":"#/components/schemas/Question"},"description":"At most 20 entries.","maxItems":20},"evidence":{"type":"array","items":{"$ref":"#/components/schemas/Evidence"},"description":"At most 20 entries.","maxItems":20},"proposals":{"type":"array","items":{"$ref":"#/components/schemas/Proposal"},"description":"At most 20 entries.","maxItems":20}},"additionalProperties":false},"RevocationEntryBody":{"type":"object","description":"One entry of the revocation log: `RevocationEntry` on the wire.","required":["cursor","kind","id","at"],"properties":{"cursor":{"type":"integer","format":"int64","description":"The entry's position in the log; increases with every entry."},"kind":{"$ref":"#/components/schemas/RevocationKindBody"},"id":{"type":"string","description":"The share id, `sid`, worker label, subject or lease id."},"at":{"type":"integer","format":"int64","description":"Unix seconds when it was revoked."}}},"RevocationKindBody":{"type":"string","description":"What a revocation entry revokes, by its `id`.","enum":["share","session","worker","subject","lease"]},"RevocationsBody":{"type":"object","description":"Body of `GET /api/relay/revocations`: `RevocationsResponse` on the wire.","required":["entries","cursor","more"],"properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/RevocationEntryBody"},"description":"Entries oldest first. A relay skips a `kind` it does not know and\nstill advances its cursor."},"cursor":{"type":"integer","format":"int64","description":"The last entry's cursor, or `since` when nothing is new."},"more":{"type":"boolean","description":"More entries remain past `cursor`: ask again at once."},"head":{"type":["integer","null"],"format":"int64","description":"The newest cursor the hub has handed out. A relay holding a larger\ncursor is reading a replaced log and starts over from 0."},"epoch":{"type":["string","null"],"description":"Names this log. A different value means the cursor restarted."}}},"Role":{"type":"string","enum":["user","assistant","system","meta"]},"Root":{"type":"object","description":"A configured source of sessions: a Claude config root (directory) or a\nHermes endpoint (base URL). `account` is an optional human label for\nattribution (\"personal\", \"work\"); None means attribution is unknown.","required":["id","provider","label","location"],"properties":{"id":{"type":"string"},"provider":{"type":"string"},"label":{"type":"string"},"location":{"type":"string","description":"Directory path (claude) or base URL (hermes)."},"account":{"type":["string","null"]}}},"RootBindingPut":{"type":"object","required":["principal"],"properties":{"principal":{"type":"string","description":"The principal that owns the root from now on."}},"additionalProperties":false},"RootBindingView":{"type":"object","required":["root_id","principal","source"],"properties":{"root_id":{"type":"string"},"principal":{"type":"string"},"source":{"type":"string"}}},"RsaJwk":{"type":"object","description":"One RSA public key of the hub's issuer.","required":["kty","use","alg","kid","n","e"],"properties":{"kty":{"type":"string"},"use":{"type":"string"},"alg":{"type":"string"},"kid":{"type":"string"},"n":{"type":"string"},"e":{"type":"string"}}},"SecretRotated":{"type":"object","required":["client_id","client_secret"],"properties":{"client_id":{"type":"string"},"client_secret":{"type":"string","description":"Shown once; the previous secret stops working immediately."}}},"SeedMode":{"type":"string","enum":["native","summary"]},"SessionLogoutBody":{"type":"object","description":"Body of `POST /api/relay/sessions/logout`: `SessionLogoutReport` on the\nwire.","required":["sid","at"],"properties":{"sid":{"type":"string","description":"The ended session's id (`sid`), at most 128 characters."},"at":{"type":"integer","format":"int64","description":"Unix seconds when the relay ended it."}}},"SessionMeta":{"type":"object","description":"Session metadata as a provider reports it (pre-persistence).","required":["native_id","message_count"],"properties":{"native_id":{"type":"string","description":"Provider-native id (Claude session uuid, Hermes session id)."},"title":{"type":["string","null"]},"project":{"type":["string","null"],"description":"Project facet — Claude cwd. Optional by design; Hermes has none."},"account":{"type":["string","null"]},"created_at":{"type":["integer","null"],"format":"int64","description":"Unix milliseconds."},"updated_at":{"type":["integer","null"],"format":"int64","description":"Unix milliseconds."},"message_count":{"type":"integer","format":"int64"},"parent_session_id":{"type":["string","null"],"description":"Hermes: previous session in a compaction chain."},"git_branch":{"type":["string","null"]},"entrypoint":{"type":["string","null"],"description":"Claude: \"cli\" | \"claude-desktop\" | …; None when the source omits it."},"source_mtime":{"type":["integer","null"],"format":"int64","description":"Source file mtime (unix ms) — powers the advisory \"open elsewhere\"\nhint and incremental ingest. None for remote providers."},"source_path":{"type":["string","null"],"description":"Source file path (local providers) for display/debugging."},"records_skipped":{"type":"integer","format":"int64","description":"Records skipped defensively while assembling this session's metadata\n(malformed lines, unreadable chunks). Rolled up into\n`IngestReport.records_skipped` by `Store::ingest`.","minimum":0},"extra":{"description":"Unmodeled source data (e.g. Claude pr-link records, mode records)."},"provider":{"type":["string","null"],"description":"This session's OWN provider, when it differs from the root it was\nread through. Exists for aggregating sources: a single `aya-helper`\ndaemon can serve sessions from several underlying providers (a Claude\nroot and a Hermes endpoint both live behind one helper) behind one\n`Root`, and our `Root` has exactly one `provider` — so a helper\ningesting as an ordinary root would mislabel every session it\nreports as the root's own provider. `None` means \"inherit the root's\nprovider\", which is every adapter's behavior before this field\nexisted and stays exactly that for all of them (Claude, Hermes, SSH\nmirrors). `Some(p)` overrides it, so a session keeps reporting what\nit actually is — the rail's provider badges/filters (CONTRACT.md\n§13) stay truthful even when the transport in front of them is one\nmore layer removed from the source."}}},"SessionMoveRequest":{"type":"object","required":["reservation_id","seed"],"properties":{"reservation_id":{"type":"string"},"seed":{"$ref":"#/components/schemas/SeedMode"},"harness":{"type":["string","null"],"description":"Which harness root on the reservation; defaults to the session's."},"request_id":{"type":["string","null"],"description":"For `summary`: an idempotency key, `[A-Za-z0-9._:-]{1,128}`. A\nrepeat with the same key replays the first move even if the\ntranscript has grown since. Without one, a repeat replays only while\nthe transcript tail is unchanged; once it changes, a repeat starts\nanother chat. Ignored for `native`, which is idempotent by itself."}},"additionalProperties":false},"SessionMoveResponse":{"type":"object","required":["session_pk","root_id","seed"],"properties":{"session_pk":{"type":"integer","format":"int64"},"root_id":{"type":"string"},"seed":{"$ref":"#/components/schemas/SeedMode"},"new_session_pk":{"type":["integer","null"],"format":"int64","description":"For `summary`: the new chat created on the target root."},"turn_id":{"type":["integer","null"],"format":"int64","description":"For `summary`: the turn that starts the new chat; poll it like any\nother turn to learn the new session."}}},"SessionUpload":{"type":"object","description":"One published session's full payload — mirrors HUB.md §4.1's wire body\n(`{\"meta\": SessionMeta, \"messages\": [MessageNode]}`) field-for-field, so\n`index_routes.rs`'s `Json<PublishSessionsRequest>` extractor can deserialize\nstraight into a `Vec<SessionUpload>` with no translation layer in between.\n`aya-helper`'s `hub_client::SessionUpload` is an independent copy of this\nsame shape on the sending side (same convention this workspace already\nuses for `ClaudeRootConfig`/`HermesEndpointConfig` — see\n`aya-helper/src/config.rs`'s module doc): the two crates never share a\ndependency edge, so the wire shape is duplicated by hand rather than\nimported.","required":["meta","messages"],"properties":{"meta":{"$ref":"#/components/schemas/SessionMeta"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/MessageNode"}}}},"SourceSchema":{"type":"object","properties":{"schema":{"type":"object"},"hash":{"type":["string","null"],"description":"Computed by the hub; a supplied value that disagrees is rejected."}},"additionalProperties":false},"StaticCheckinRequest":{"type":"object","required":["pool","machine_id","credential"],"properties":{"pool":{"type":"string"},"machine_id":{"type":"string"},"credential":{"type":"string","description":"The static machine's pre-shared credential. It authorizes this\noperation and is sent here in the body, not in an `Authorization`\nheader."},"wait_ms":{"type":["integer","null"],"format":"int64","minimum":0},"place":{"type":["string","null"],"description":"The machine's own place, `machine:<hostname>` (`aya_core::place`).\nOptional; an older helper sends none. The hub binds it to this\nmachine's identity (`static:<pool>/<machine_id>`) first come, never\nmerges a conflict, and ignores a value it cannot use. A value that is\nnot a string is read as absent, so it never fails the check-in."}}},"StaticCheckinResponse":{"type":"object","required":["enrollment_token"],"properties":{"enrollment_token":{"type":"string"}}},"TeamPut":{"type":"object","required":["team"],"properties":{"team":{"type":"string","description":"The team the principal's reservations count against."}},"additionalProperties":false},"TeamView":{"type":"object","required":["principal","team","live_reservations"],"properties":{"principal":{"type":"string"},"team":{"type":"string"},"live_reservations":{"type":"integer","format":"int32","description":"The principal's reservations that have not ended. They keep the team\nthey were admitted under (D3): their limits and budget still count\nagainst it until they end; only new reservations use the new team.","minimum":0}}},"TicketResponse":{"type":"object","required":["ticket","url","expires_at"],"properties":{"ticket":{"type":"string","description":"Compact EdDSA JWT."},"url":{"type":"string","description":"Where to present it: the preview origin for an `http` service, the\nstream-channel WebSocket URL for a `stream` service."},"expires_at":{"type":"integer","format":"int64","description":"Unix seconds.","minimum":0},"lease":{"type":["string","null"],"description":"The control lease the ticket carries (minted by the hub), present\nonly for `control` on a `stream` service. Present it on the next\nticket request to keep the same lease."}}},"Tier":{"type":"string","description":"Work tier — the caller's statement of how much model the task deserves\n(design §1 \"Tier table\"). `Display`/`FromStr` use the lowercase\nspellings, matching the `serde(rename_all = \"lowercase\")` wire form so a\ntier round-trips identically through TOML config, JSON requests, and\nplain strings (e.g. a CLI flag or query param) an embedder might parse.","enum":["quick","standard","heavy"]},"TierCandidate":{"type":"object","description":"One (provider, model-or-default) candidate in a tier's ordered list.\n`provider` is always an `aya_core::model` provider constant / harness\nregistry id (`harness.rs`'s `HarnessDescriptor::id`), never a second\nspelling. `model: None` means \"the harness's own default for this\nprovider\" — not \"no preference within the tier\": the tier still picked\nthis provider, it just isn't pinning a model on it.","required":["provider"],"properties":{"provider":{"type":"string"},"model":{"type":["string","null"]}},"additionalProperties":false},"TokenRequest":{"type":"object","description":"The form body of `POST /oauth/token` (RFC 6749 §4.4 with RFC 8707's\n`resource`). Credentials go in the form for `client_secret_post`, in an\n`Authorization: Basic` header for `client_secret_basic`, or as a signed\nassertion for `private_key_jwt`.","required":["grant_type","resource"],"properties":{"grant_type":{"type":"string","description":"Must be `client_credentials`."},"scope":{"type":["string","null"],"description":"Space-separated; intersected with what the client may hold."},"resource":{"type":["string","null"],"description":"The audience (resource indicator) the token is for. Required: a\nrequest without it is `invalid_target`."},"client_id":{"type":["string","null"]},"client_secret":{"type":["string","null"]},"client_assertion_type":{"type":["string","null"],"description":"`urn:ietf:params:oauth:client-assertion-type:jwt-bearer` for `private_key_jwt`."},"client_assertion":{"type":["string","null"]}}},"TokenResponse":{"type":"object","required":["access_token","token_type","expires_in","scope"],"properties":{"access_token":{"type":"string"},"token_type":{"type":"string","description":"Always `Bearer`."},"expires_in":{"type":"integer","format":"int64","minimum":0},"scope":{"type":"string","description":"The scopes actually granted, space-separated."}}},"Ttl":{"type":"object","properties":{"job_max_secs":{"type":"integer","format":"int64","minimum":0},"checkout_max_secs":{"type":"integer","format":"int64","minimum":0},"idle_secs":{"type":"integer","format":"int64","minimum":0},"enroll_timeout_secs":{"type":"integer","format":"int64","minimum":0},"dead_man_secs":{"type":"integer","format":"int64","minimum":0}},"additionalProperties":false},"TurnEvent":{"oneOf":[{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["started"]}}},{"type":"object","required":["delta","type"],"properties":{"delta":{"type":"string"},"type":{"type":"string","enum":["text"]}}},{"type":"object","required":["delta","type"],"properties":{"delta":{"type":"string"},"type":{"type":"string","enum":["thinking"]}}},{"type":"object","required":["call_id","tool_name","input","type"],"properties":{"call_id":{"type":"string"},"tool_name":{"type":"string"},"input":{},"mcp":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/McpTool","description":"The MCP server and tool, when the harness says this call is one\n(CONTRACT.md §23i). Omitted on the wire when unknown."}]},"type":{"type":"string","enum":["tool_call"]}}},{"type":"object","required":["call_id","output","is_error","type"],"properties":{"call_id":{"type":"string"},"output":{},"is_error":{"type":"boolean"},"type":{"type":"string","enum":["tool_result"]}}},{"type":"object","required":["text","type"],"properties":{"text":{"type":"string"},"type":{"type":"string","enum":["notice"]}}},{"type":"object","required":["message","type"],"properties":{"message":{"type":"string"},"type":{"type":"string","enum":["failed"]}}},{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["completed"]}}},{"type":"object","description":"A `rate_limit_event` record (CONTRACT's chat-drive addendum, Fact 3).\nStream-only — never present in the ingested JSONL — so this is the\nonly way the app ever sees it, and only while a turn is running.\nCarries a window TYPE, a STATUS, and a RESET TIME — deliberately NO\npercentage/remaining-count field, because the wire record doesn't\nhave one; inventing one would imply precision Claude itself doesn't\nreport.","required":["rate_limit_type","status","resets_at_ms","type"],"properties":{"rate_limit_type":{"type":"string"},"status":{"type":"string"},"resets_at_ms":{"type":"integer","format":"int64"},"overage_status":{"type":["string","null"]},"is_using_overage":{"type":["boolean","null"]},"type":{"type":"string","enum":["rate_limit"]}}},{"type":"object","description":"Cost/model info off a successful `result` record — separate from\n`Completed` (which stays exactly as it was) so existing consumers of\n`Completed` are unaffected; this fires just before it when the data\nis available.","required":["type"],"properties":{"cost_usd":{"type":["number","null"],"format":"double"},"model":{"type":["string","null"]},"type":{"type":"string","enum":["turn_result"]}}}],"description":"App-wide event payload, emitted as `{ session_pk, event }` under the\n`\"turn-event\"` name (CONTRACT.md, this task) for every turn regardless of\nwhich provider is driving it."},"TurnHistoryResponse":{"type":"object","description":"Mirrors `turn_routes.rs::turn_history`'s response body.","required":["turn_id","transitions"],"properties":{"turn_id":{"type":"string"},"transitions":{"type":"array","items":{"$ref":"#/components/schemas/TurnTransition"}}}},"TurnListResponse":{"type":"object","description":"Mirrors `turn_routes.rs::list_turns`'s response body.","required":["turns","total","limit","offset"],"properties":{"turns":{"type":"array","items":{"$ref":"#/components/schemas/TurnSummary"}},"total":{"type":"integer","format":"int64"},"limit":{"type":"integer","format":"int64"},"offset":{"type":"integer","format":"int64"}}},"TurnSummary":{"type":"object","description":"Mirrors one entry of `turn_routes.rs::list_turns`'s `\"turns\"` array.\n\n`provider` is nullable (model tiers step 3 follow-up, 2026-09-02,\nDECISIONS.md — an INTENTIONAL, accepted break of `GET /api/agent/turns`,\nmajor-bumped per README.md's accepted-break flow): a tiered \"new chat\"\nturn genuinely has no provider until a helper claims and resolves it\n(`turns::TurnSummary::provider`'s own doc — the real field this mirrors\nhas been nullable since model tiers step 3 landed; this documented type\nis now honest about it too, rather than knowingly stale).","required":["turn_id","status","created_at_ms","claim_generation","attempts"],"properties":{"turn_id":{"type":"string"},"status":{"type":"string"},"provider":{"type":["string","null"]},"root_id":{"type":["string","null"]},"created_at_ms":{"type":"integer","format":"int64"},"claimed_at_ms":{"type":["integer","null"],"format":"int64"},"completed_at_ms":{"type":["integer","null"],"format":"int64"},"claim_generation":{"type":"integer","format":"int64"},"attempts":{"type":"integer","format":"int64"},"ok":{"type":["boolean","null"]},"error":{"type":["string","null"]}}},"TurnTransition":{"type":"object","description":"Mirrors one entry of `turn_routes.rs::turn_history`'s `\"transitions\"`\narray.","required":["at_ms","to_status"],"properties":{"at_ms":{"type":"integer","format":"int64"},"from_status":{"type":["string","null"],"description":"`None` only for a turn's very first transition (creation has no\nprior state) — see `turns::TurnStateTransition`'s own doc."},"to_status":{"type":"string"},"detail":{"type":["string","null"]}}},"WakeReason":{"type":"string","enum":["work","reservation_ended"]},"WakeResponse":{"type":"object","required":["cursor","reason"],"properties":{"cursor":{"type":"integer","format":"int64","minimum":0},"reason":{"$ref":"#/components/schemas/WakeReason"}}},"WebhookKey":{"type":"object","description":"One Ed25519 key the hub signs provisioner webhooks with.","required":["kid","public_key"],"properties":{"kid":{"type":"string"},"public_key":{"type":"string","description":"Standard Webhooks signing key in `whpk_<base64>` form."}}},"WebhookKeys":{"type":"object","required":["keys"],"properties":{"keys":{"type":"array","items":{"$ref":"#/components/schemas/WebhookKey"}}}},"WorkCheckout":{"type":"object","description":"How a work run's working copy is prepared, when its work profile asks for\na fresh one (`checkout: \"fresh\"`) instead of the configured working\ndirectory. Data only: the source validated it against the operator's\nprofile; the worker re-validates it before running a single git command.\nCarried on [`ClaimedTurn::work_checkout`]. The shape is closed\n(`deny_unknown_fields`): a claim whose spec a worker cannot fully read\ndoes not parse, rather than running a checkout that ignores part of it.","required":["repo","branch"],"properties":{"repo":{"type":"string","description":"Where the run's copy comes from: a git URL (`https`, `ssh`, `git`,\n`file`), cloned afresh, or an absolute local path to a mirror, from\nwhich a new worktree is added."},"branch":{"type":"string","description":"The branch the run commits to, created from `base_ref` unless the\nrepository already has it."},"base_ref":{"type":["string","null"],"description":"What a new branch starts from. Absent: the repository's own `HEAD`."}},"additionalProperties":false},"WorkResult":{"type":"object","description":"The one JSON object shape a work run's model output must parse as (see\n[`parse_result`]) — reused here, unmodified, as the documented shape of\nthe `result` field in a `completed`\n`GET /api/integrations/work-runs/{request_id}` response\n([`crate::openapi::WorkRunStatus`]). No unknown fields are accepted\n(`#[serde(deny_unknown_fields)]`), and the model's TOTAL collected\noutput text — before this shape is even parsed out of it — must be at\nmost [`MAX_RESULT_BYTES`] (128 KiB); either violation reports\n`state: \"failed\"`, `failure.code: \"invalid_result\"` on the poll route,\nsame as a structurally-mismatched JSON body.","required":["status","summary","pr"],"properties":{"status":{"type":"string","description":"Must be exactly `\"completed\"` — the only value [`parse_result`]\naccepts (a run whose own output reports anything else, or omits\nthis field, fails the same way as malformed JSON).","pattern":"^completed$"},"summary":{"type":"string","description":"Non-blank after trimming whitespace, at most 65,536 bytes.","maxLength":65536,"minLength":1},"pr":{"$ref":"#/components/schemas/PullRequest"}},"additionalProperties":false},"WorkRunFailure":{"type":"object","description":"Mirrors the `\"failure\"` object `work_routes.rs::get_work_run` builds for\na `state: \"failed\"` response. Only ever ONE of two exact, fixed\n`code`/`message` pairs — never a free-form combination — with\n`retryable` always `true` today: `\"execution_failed\"` /\n`\"work execution failed\"` (the run itself failed) or\n`\"invalid_result\"` / `\"work execution produced an invalid result\"` (the\nrun succeeded, but its output did not parse as a\n[`crate::work_routes::WorkResult`] — see that type's own doc for the\nexact rules an output must satisfy).","required":["code","message","retryable"],"properties":{"code":{"type":"string","description":"`\"execution_failed\"` or `\"invalid_result\"` — see this type's own doc\nfor the exact code/message/retryable triples; never any other value.","pattern":"^(execution_failed|invalid_result)$"},"message":{"type":"string","description":"`\"work execution failed\"` (paired with `code: \"execution_failed\"`)\nor `\"work execution produced an invalid result\"` (paired with\n`code: \"invalid_result\"`) — always exactly one of these two\nsentences, never a different message for the same code.","pattern":"^(work execution failed|work execution produced an invalid result)$"},"retryable":{"type":"boolean","description":"Always `true` in the current implementation — there is no failure\nmode `get_work_run` reports as non-retryable today."}}},"WorkRunQueued":{"type":"object","description":"Mirrors `work_routes.rs::post_work_run`'s `202` response body. Field-for-\nfield identical to [`IntegrationQueued`] but kept as its own type — the\nwork-run and idea-review queues are deliberately siblings, not a shared\nimplementation (`work_routes.rs`'s own module doc), and their documented\nschemas stay just as independent so one can evolve without the other.","required":["request_id","state","replayed"],"properties":{"request_id":{"type":"string"},"state":{"type":"string","description":"Always the literal `\"queued\"` on this response — see\n[`IntegrationQueued`]'s own doc for why this is a field of its own\nrather than a bare `202`."},"replayed":{"type":"boolean","description":"`true` when this `request_id` had already been queued with the\nidentical input hash and this call is an idempotent replay, not a new\nenqueue."}}},"WorkRunRequest":{"type":"object","required":["request_id","input_version","process_id","target_ref","title","brief"],"properties":{"request_id":{"type":"string","description":"Non-blank after trimming whitespace, 1-200 BYTES, unreserved\ncharacters only (letters, digits, `.`, `_`, `~`, `-` —\n[`request_id_is_unreserved`]). `min_length`/`max_length` below are\nJSON Schema's own Unicode-code-point count, not bytes — the real,\nserver-enforced limit is UTF-8 byte length (`valid`'s `value.len()`),\nso a value containing multi-byte characters can pass this schema\ncheck yet still be rejected as too long.","maxLength":200,"minLength":1,"pattern":"^[A-Za-z0-9._~-]+$"},"input_version":{"type":"integer","format":"int32","description":"The only value this endpoint accepts is `1` ([`validate_request`]).\nEncoded as `minimum`/`maximum` both `1` — a plain integer field has\nno JSON Schema \"const\"/single-value `enum` shorthand in this\ngenerator, and `minimum == maximum` has the identical effect.","maximum":1,"minimum":1},"process_id":{"type":"string","description":"Non-blank after trimming whitespace, 1-200 bytes (see `request_id`'s\nown note on bytes vs. this schema's code-point count) — your own\nopaque reference; never interpreted by the hub.","maxLength":200,"minLength":1},"target_ref":{"type":"string","description":"Non-blank after trimming whitespace, 1-500 bytes, AND must name a\nwork profile the operator actually configured — that second rule\nhas no JSON Schema representation; see the `400` response's own\ndescription.","maxLength":500,"minLength":1},"title":{"type":"string","description":"Non-blank after trimming whitespace, 1-500 bytes.","maxLength":500,"minLength":1},"brief":{"type":"string","description":"Non-blank after trimming whitespace, 1-65,536 bytes — the\ninstructions the run actually receives.","maxLength":65536,"minLength":1},"branch":{"type":["string","null"],"description":"Optional: the branch the run commits to. 1-200 bytes of ASCII\nletters, digits, `.`, `_`, `-` and `/`, a valid git branch name (no\n`..`, `//`, leading `-`, component starting with `.` or ending in\n`.lock`, trailing `/` or `.`, never `HEAD`), AND starting with one of\nthe target profile's `branch_prefixes` (default `aya-task/`); that\nlast rule has no JSON Schema representation. Absent: the hub derives\n`work/<hash of process_id>`, as it always has.","maxLength":200,"minLength":1,"pattern":"^[A-Za-z0-9._/-]+$"},"base_ref":{"type":["string","null"],"description":"Optional: what the branch starts from. Same name rules as `branch`,\nwithout the prefix rule. Absent: the target profile's own `base_ref`,\nelse the repository's default.","maxLength":200,"minLength":1,"pattern":"^[A-Za-z0-9._/-]+$"}},"additionalProperties":false},"WorkRunStatus":{"type":"object","description":"Mirrors `work_routes.rs::get_work_run`'s response body across every state\nit can report. Same \"one schema, state-dependent optional fields\" shape\nas [`IntegrationTurnStatus`] (see that type's own doc for why), with two\ndifferences from it: `state` here is `\"queued\"`, `\"running\"`,\n`\"completed\"`, or `\"failed\"` (never `\"ready\"`), and `report_state` is\npresent on EVERY response — see [`crate::work_routes::get_work_run`]'s\nown doc for what it tracks.\n\nThe state/field pairing is EXACT, not \"typically\": `\"queued\"` and\n`\"running\"` carry NEITHER `result` NOR `failure` (only `request_id`/\n`state`/`report_state`); `\"completed\"` carries `result` and NEVER\n`failure`; `\"failed\"` carries `failure` and NEVER `result`. A response\nwith both, or with neither for a terminal state, never happens —\n`get_work_run`'s own match arms each set at most one of the two fields.","required":["request_id","state","report_state"],"properties":{"request_id":{"type":"string"},"state":{"type":"string","description":"`\"queued\"`, `\"running\"`, `\"completed\"`, or `\"failed\"`."},"report_state":{"type":"string","description":"`\"pending\"`, `\"delivered\"`, or `\"dead\"` — the result callback's own\nstate, independent of `state` above."},"result":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/WorkResult","description":"Present ONLY when `state == \"completed\"` — see this type's own doc."}]},"failure":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/WorkRunFailure","description":"Present ONLY when `state == \"failed\"` — see this type's own doc."}]}}},"WorkerCommandCode":{"type":"object","description":"Mirrors the `{\"code\": \"...\"}` `200` body of `GET\n/api/agent/workers/self/commands/{id}/code` — a one-time delivery, so this\nis never constructed anywhere but that one response (module doc).","required":["code"],"properties":{"code":{"type":"string"}}},"WorkerDescription":{"type":"object","description":"What the worker says about itself. The hub only ever *narrows* with this:\nevery field is compared against a pool requirement, none of it grants\neligibility.","properties":{"labels":{"type":"object","additionalProperties":{"type":"string"},"propertyNames":{"type":"string"}},"capabilities":{"type":"array","items":{"type":"string"},"description":"e.g. `cross_host_resume`."},"hooks":{"type":"array","items":{"type":"string"}},"source_schema_hash":{"type":["string","null"]},"doctor":{"$ref":"#/components/schemas/DoctorResult"},"agent_version":{"type":["string","null"]}}}},"securitySchemes":{"oidc_agent":{"type":"oauth2","flows":{"clientCredentials":{"tokenUrl":"https://issuer.example.test/token","scopes":{"agent:claim":"Claim exactly one pending turn or job for this worker.","agent:report":"Report progress or completion for previously claimed work: events, done, job results, capability reports, and index publishes.","execution:control":"Claim and drive a worker-bound provider-auth control command (events, lease heartbeat, complete, one-time code). Cannot report worker-registry presence or touch the turn/job queues.","execution:heartbeat":"Report worker-registry presence. Cannot claim, drive, or complete a control command."}}},"description":"A client-credentials access token for the agent audience, from the deployment's identity provider or from this hub's own workload issuer (`/oauth/token`, workers enrolled through `/api/enroll/v1`). A worker enrolled for an ephemeral reservation holds a hub-issued token."},"oidc_integration":{"type":"oauth2","flows":{"clientCredentials":{"tokenUrl":"https://issuer.example.test/token","scopes":{"review:run":"Run one automated read-only idea review.","work:run":"Submit and poll one service work run against a hub-configured work profile."}}}},"oidc_relay":{"type":"oauth2","flows":{"clientCredentials":{"tokenUrl":"https://issuer.example.test/token","scopes":{"relay:audit":"Ship audit events (audience `RELAY_HUB_RESOURCE`; held by the relay).","relay:open":"Mint a ticket to open a worker service (audience `REMOTE_API_RESOURCE`).","relay:sync":"Read the revocation log (audience `RELAY_HUB_RESOURCE`; held by the relay)."}}}},"oidc_remote":{"type":"oauth2","flows":{"clientCredentials":{"tokenUrl":"https://issuer.example.test/token","scopes":{"chat:drive":"Send messages to sessions and start new chats. Does not imply `chat:read`.","chat:read":"Read sessions, messages, search results, machines and turn events.","workers:admin":"Administer pools, teams, issuer clients, static machines, root bindings and issuer keys; deprovision a principal.","workers:reserve":"Request, read and release ephemeral worker reservations, and move a session onto one."}}}},"oidc_workload":{"type":"oauth2","flows":{"clientCredentials":{"tokenUrl":"https://hub.example.test/oauth/token","scopes":{"workers:provision:{pool}":"Fetch enrollment tokens for, and report status of, reservations in the named pool."}}}}}},"tags":[{"name":"agent-turns","description":"The chat-drive turn lease lifecycle a worker polls: claim, append events, complete/requeue, and introspect."},{"name":"agent-jobs","description":"The generic job relay (currently `git_diff`, `terminal_exec`) a worker polls."},{"name":"agent-index","description":"HUB.md §4.1's index-publish surface: fingerprint diffing and session upload."},{"name":"agent-capabilities","description":"CONTRACT.md §21 capability reporting: what a driven session's agent can currently do."},{"name":"agent-workers","description":"The worker registry and provider-auth control-command queue (owner decision 2026-09-02) — the same contract `crates/aya-helper/src/hub_client.rs` speaks against a purpose-routed dispatcher, now also served by this hub."},{"name":"remote","description":"The remote chat API (`/api/remote/v1`): bearer-only, its own token audience and the `chat:read` / `chat:drive` scopes. Lets a cloud agent that cannot join the owner's network read sessions and message `aya-helper` chats through the hub. Absent (404) unless the hub sets `REMOTE_API_RESOURCE`."},{"name":"relay","description":"The preview relay's hub side: a bearer ticket for a cloud agent (`relay:open`), the ticket-verification JWKS, and the relay's own revocation and audit feeds (`relay:sync`, `relay:audit`). The browser routes (`/api/relay/open`, shares) are cookie-authenticated and out of scope here. Absent (404) unless the hub sets `RELAY_TICKET_SIGNING_KEY`."},{"name":"integrations","description":"Service-to-service integration APIs for an integrating product: read-only idea-review turns, and service work runs against a hub-configured work profile."},{"name":"workers-admin","description":"Operator administration of ephemeral workers under `/api/remote/v1` (scope `workers:admin`): pools, teams, issuer clients, static machines, root bindings, issuer key rotation and deprovisioning. Absent (404) unless the workload issuer and the remote API are both enabled."},{"name":"reservations","description":"Ephemeral worker reservations under `/api/remote/v1` (scope `workers:reserve`): request a worker from a pool, poll it, release it, and move a session onto it."},{"name":"issuer","description":"The hub's workload issuer: OIDC discovery, JWKS, provisioner webhook keys and the `client_credentials` token endpoint. Public; absent (404) unless `WORKLOAD_ISSUER` is on."},{"name":"enrollment","description":"Worker enrollment (`/api/enroll/v1`): a freshly provisioned worker proves itself and receives its identity. The credential travels in the request body."},{"name":"provisioning","description":"What a provisioner (for example IT's service) calls back on, authenticated by a hub-issued bearer scoped `workers:provision:<pool>`."}]}