{
  "openapi": "3.1.0",
  "info": {
    "title": "Leviath HTTP API",
    "version": "0.6.4",
    "description": "The REST and WebSocket surface `lev serve` puts in front of the shared-world daemon. Every route requires a bearer token. Paths here are checked against the router in crates/leviath-cli/src/commands/serve/mod.rs by a test in that file, so a route added without a spec entry fails the build. See https://leviath.dev/docs/api.",
    "license": {
      "name": "MIT"
    }
  },
  "servers": [
    {
      "url": "http://127.0.0.1:3000",
      "description": "The default `lev serve` bind."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "runs"
    },
    {
      "name": "blueprints"
    },
    {
      "name": "agents"
    },
    {
      "name": "interactions"
    },
    {
      "name": "mcp"
    },
    {
      "name": "config"
    },
    {
      "name": "diagnostics"
    },
    {
      "name": "events"
    },
    {
      "name": "scripts"
    },
    {
      "name": "providers"
    },
    {
      "name": "yolo",
      "description": "The named profiles behind `lev run --yolo=<name>`."
    },
    {
      "name": "graphql",
      "description": "One endpoint where the request body names the fields it wants."
    }
  ],
  "paths": {
    "/": {
      "get": {
        "tags": [
          "system"
        ],
        "summary": "Liveness page (no token required)",
        "description": "A minimal HTML page saying the server is running. **The only route that does not require the bearer token**, because it exists to be opened in a browser tab and a tab cannot send an `Authorization` header.\n\nIts purpose is TLS, not status: with a self-signed certificate, opening this in a tab is how a user reaches the certificate interstitial and accepts it, after which a browser console's `fetch` to the same origin inherits the exception.\n\nIt deliberately reports nothing else - no version, no run counts, no endpoint list. Anyone who can load it already knows the port is open, and that should remain all they learn.",
        "security": [],
        "responses": {
          "200": {
            "description": "The server is running.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/blueprints": {
      "get": {
        "tags": [
          "blueprints"
        ],
        "summary": "List installed blueprints",
        "description": "**Breaking change in 0.3.0**: returns the same paginated envelope as /api/runs rather than a bare array. Check `blueprints.envelope` in the `capabilities` list on GET /api/config.\n\nNote that pagination saves the server nothing here - discovery parses every manifest on every request regardless of page size, and the catalog is bounded by what a person installs. `q` is the parameter with real value.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            },
            "description": "Page size. Larger values are clamped rather than refused."
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Continuation token from a previous page's next_cursor. Opaque; bound to the query that minted it."
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Case-insensitive substring over name, description and stage names."
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "name",
                "version"
              ],
              "default": "name"
            },
            "description": "Ordering key."
          },
          {
            "name": "order",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "asc"
            },
            "description": "Sort direction. Ascending by name is the order a person reads a catalog in."
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      },
      "post": {
        "tags": [
          "blueprints"
        ],
        "summary": "Install a blueprint from a manifest string",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "manifest"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "manifest": {
                    "type": "string",
                    "description": "The agent.leviath text."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/blueprints/validate": {
      "post": {
        "tags": [
          "blueprints"
        ],
        "summary": "Validate a manifest without installing it",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "manifest"
                ],
                "properties": {
                  "manifest": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The verdict. Note an invalid manifest is still a 200 with `valid: false`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidateResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/blueprints/{name}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Name"
        }
      ],
      "get": {
        "tags": [
          "blueprints"
        ],
        "summary": "Fetch one blueprint",
        "description": "The catalog entry plus the manifest text, the context regions, fan_outs (one entry per fan-out stage with its worker source, merge stage and both caps resolved as the daemon applies them, null meaning unlimited), and stage_routing (one entry per stage that routes the model's produced parts by mime type via output_routing, or empties a region on entry via context.reset).",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      },
      "put": {
        "tags": [
          "blueprints"
        ],
        "summary": "Replace a blueprint's manifest",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      },
      "delete": {
        "tags": [
          "blueprints"
        ],
        "summary": "Uninstall a blueprint",
        "responses": {
          "204": {
            "description": "Done. No body."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/runs": {
      "get": {
        "tags": [
          "runs"
        ],
        "summary": "List runs, paginated and searchable",
        "description": "Supersedes the GET half of /api/agents, which returns every run ever recorded as one unbounded array.\n\nPagination is keyset, not offset: runs are created and deleted while a client walks the list, and an offset into a shifting list silently skips and repeats items. Pass `next_cursor` back verbatim and loop until it is null.\n\n`sort=started_at` is the only immutable sort key, and it is the default. `updated_at` also advances on the daemon's 30-second persistence heartbeat, so every live run moves under a walk, and a run whose sort value changes mid-walk may be skipped or repeated. Use started_at for a stable walk, and `since=` with no cursor to poll for what changed.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            },
            "description": "Page size. Larger values are clamped rather than refused; the live cap is in GET /api/config."
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Continuation token from a previous page's next_cursor. Opaque - do not parse or construct it. Presenting one against a different sort, order or filter set is a 400, not a quietly different result."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated statuses to keep. Matched loosely: waiting_input, waitinginput and Waiting-Input are all accepted."
          },
          {
            "name": "parent",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Which runs to list by their place in the tree. `none` keeps only runs nobody started, which is what a top-level list wants; `sub` keeps only runs somebody started, at any depth; a run id keeps that run's direct children. Omitted lists every run, sub-agents included. A run id that names nothing gives an empty page rather than a 404. Announced as `runs.parent`."
          },
          {
            "name": "descendant_of",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Every run under this one, at any depth, and not the run itself: the flat read of a whole fan-out, where `parent=` is one level of it. Setting both is a 400, because they name two different sets and the one you meant is not recoverable from the pair."
          },
          {
            "name": "blueprint",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Only runs of this blueprint, by the name each run recorded. A name nothing matches is an empty page rather than an error, because a blueprint with no runs yet is an ordinary answer."
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "started_at",
                "updated_at",
                "last_progress_at"
              ],
              "default": "started_at"
            },
            "description": "Ordering key. last_progress_at falls back to started_at where it is absent."
          },
          {
            "name": "order",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "desc",
                "asc"
              ],
              "default": "desc"
            },
            "description": "Sort direction."
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Case-insensitive substring to search for. Not a regex; no boolean operators or phrase quoting, and ASCII case folding only."
          },
          {
            "name": "q_in",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "meta,files"
            },
            "description": "Comma-separated search sources: meta, files, context, logs, journal. The last three read files, so they are opt-in and subject to a scan cap - see scan_truncated in the response. They also match the raw JSON on disk, so a query containing a quote, backslash or newline may not match text that does contain it."
          },
          {
            "name": "fields",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated top-level RunMeta fields to return. run_id is always included. Nested paths are not supported."
          },
          {
            "name": "ids",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated run ids to fetch directly, bypassing the scan. Cannot be combined with cursor, q, status or since. Ids that no longer exist come back in `missing` rather than failing the request."
          },
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Keep only runs whose sort field is at or after this unix-seconds value. Inclusive, so a client polling with the previous response's server_time re-receives same-second items rather than losing them."
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      },
      "delete": {
        "tags": [
          "runs"
        ],
        "summary": "Delete many runs at once",
        "description": "Prunes finished runs. The realistic use is \"clear everything older than a month\", which one request per run over a few hundred runs does badly.\n\nNeeds `before` or `ids`. Neither is a 400 rather than \"every run\": a bulk delete with no predicate is far more likely to be a client that failed to build its query than an operator asking to erase the machine's history.\n\nPartial success is the normal outcome, not a failure. A sweep that meets a live run has still correctly deleted the rest, so this answers 200 with a verdict per run rather than failing the whole request. Read `skipped` to find out why the list did not empty. Use DELETE /api/runs/{id} when you want a status code per outcome.\n\nEvery run named takes its sub-agent tree with it, as on the single-run route, so `deleted` can hold ids you never mentioned. Reporting them is the point: they are the runs that are now gone.\n\nDeletion is real and irreversible: the run directory and its transcript go.",
        "parameters": [
          {
            "name": "before",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "description": "Delete every finished run whose updated_at is strictly before this unix timestamp. Ignored when ids is given."
          },
          {
            "name": "ids",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated run ids to delete. At most max_ids (see GET /api/config) per request."
          }
        ],
        "responses": {
          "200": {
            "description": "The sweep ran. Every named run is in exactly one of the two lists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteRunsResp"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/runs/{id}": {
      "delete": {
        "tags": [
          "runs"
        ],
        "summary": "Delete one run's record",
        "description": "Removes a finished run's directory from disk. Distinct from DELETE /api/agents/{id}, which cancels the run and leaves the record: one stops the work, the other forgets it happened.\n\nDeletion is real and irreversible - the directory and everything in it, including the transcript. A console offering a \"Delete\" that only hid the run locally would tell somebody clearing a sensitive transcript that it was gone when it was not.\n\nThe run's sub-agent runs go with it. A fan-out worker or a sub_agent spawn is drawn nested under the run that started it and nothing else on disk accounts for it, so a delete that left them behind did not merely strand them: a client that nests runs under their parent has nowhere to draw a parentless run but the top level, and the delete read as a promotion. The walk is downwards only - deleting a sub-agent run leaves its parent and its siblings exactly where they were.\n\n409 on a live run: removing a directory out from under a running agent is a different and much larger feature, so cancel it first. A live sub-agent is a 409 too, and the reason names it. 404 on a run that is already gone, so a client that lost the response to its own delete can repeat it rather than treat a missing run as a failure. 409 as well on a run whose record will not parse, which force=true overrides.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The run id."
          },
          {
            "name": "force",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Delete a run whose meta.json will not parse, which is otherwise a 409. A record that cannot be read is not proof the run finished - that is what a live run looks like to a binary whose RunMeta has moved on - so this is something the caller types rather than the default. The bulk route never forces."
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted. No body."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/exports/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The export job's id, as the `startRunExport` mutation returned it."
        }
      ],
      "get": {
        "tags": [
          "runs"
        ],
        "summary": "The file one bulk export wrote",
        "description": "Exports are started over GraphQL, with the `startRunExport` mutation, and this is where the file it writes is fetched. The body is JSONL: one run per line, under the same field names the run listing uses, narrowed to the `fields` the export asked for. One run per line rather than one JSON array so a reader can start on it before the writer has finished, and so neither side ever holds the whole store in memory. A file is kept for one hour and then removed with its job record, so an id that has expired and one that was never started both answer 404. While the export is still queued or running, or after it failed, the answer is 409 and the client polls `runExport(id:)` instead. Every response advertises `Accept-Ranges: bytes`; a `Range` header fetches part of the body. Also takes a short-lived signed link in place of the bearer token (`exp` and `sig`, minted as `downloadUrl` on the job), which is what a browser download link needs: a header cannot be set on one.",
        "parameters": [
          {
            "name": "Range",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "A single byte range, `bytes=start-end` (an open end or a `-suffix` too). A well-formed range is answered 206 with `Content-Range`; one past the end is 416. A malformed or multi-range header is ignored and the whole body served."
          }
        ],
        "responses": {
          "200": {
            "description": "The export, as `application/jsonl`.",
            "content": {
              "application/jsonl": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "206": {
            "description": "The requested byte range, when a `Range` header asked for one. Carries `Content-Range: bytes start-end/total`.",
            "content": {
              "application/jsonl": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "416": {
            "description": "The `Range` header named bytes the body does not have. Carries `Content-Range: bytes */total`."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "409": {
            "description": "The export is not ready, or it failed. The message says which, and `runExport(id:)` carries the reason.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents": {
      "get": {
        "tags": [
          "agents"
        ],
        "summary": "List runs",
        "description": "Deprecated: use GET /api/runs, which paginates and supports search. This route returns every run ever recorded as one array, and is kept unchanged for existing clients.",
        "deprecated": true,
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter to one status."
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      },
      "post": {
        "tags": [
          "agents"
        ],
        "summary": "Spawn a run",
        "description": "Returns as soon as the daemon accepts the run. Poll GET /api/agents/{id} or subscribe to /ws for what happens next.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SpawnAgentReq"
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "request"
                ],
                "properties": {
                  "request": {
                    "type": "string",
                    "description": "The JSON request, as the application/json body would carry it."
                  },
                  "part": {
                    "type": "string",
                    "format": "binary",
                    "description": "A file for the task region. Repeatable. Name the field `part:<region>` for another region; the file's `filename` names the part and its `Content-Type` declares the type."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The new run's identifiers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SpawnAgentResp"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents/tree": {
      "get": {
        "tags": [
          "agents"
        ],
        "summary": "The full sub-agent tree",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "agents"
        ],
        "summary": "One run's metadata, with secrets redacted",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      },
      "delete": {
        "tags": [
          "agents"
        ],
        "summary": "Cancel a run",
        "responses": {
          "204": {
            "description": "Done. No body."
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents/{id}/children": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "agents"
        ],
        "summary": "This run's direct children",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents/{id}/context": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "agents"
        ],
        "summary": "The run's current context window",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents/{id}/context/history": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "agents"
        ],
        "summary": "How the context window changed over the run",
        "description": "**Breaking change in 0.3.0**: paginated, returning the shared envelope rather than every recorded point as one array. Each point carries a full context window with untruncated region text, on a journal that grows for as long as the run does, so the unpaged form was comfortably the largest response in the API.\n\nThe cursor is the point index. The journal is append-only, so an index is stable once written and new points only ever arrive at the end.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Points per page. Capped lower than the run listing because each item carries a whole context window."
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Continuation token from the previous page's next_cursor."
          },
          {
            "name": "order",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "asc"
            },
            "description": "`asc` is chronological, and matches what the unpaged response gave. `desc` starts from the most recent point, which is what a view tailing a live run wants."
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents/{id}/files": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        },
        {
          "name": "offset",
          "in": "query",
          "required": false,
          "schema": {
            "type": "integer",
            "minimum": 0
          },
          "description": "Byte offset to start reading at. An offset landing mid-character is moved forward to the next boundary, and the response's `offset` says where the window actually began, so concatenating pages reproduces the file. Past the end returns 416."
        }
      ],
      "get": {
        "tags": [
          "agents"
        ],
        "summary": "Read a file inside the run's workdir, or list files",
        "description": "With `path`, reads that file: confined to the run's working directory, 1 MiB per request. A larger file is read a window at a time - pass `offset` and continue from the `next_offset` each response carries, which is how an agent's dataset artifact is fetched in full. Without `path`, lists instead, and the response carries `kind: \"listing\"` so the two shapes can be told apart. Reading a file returns exactly the shape it always has.\n\n`source=modified` lists what the run recorded changing - free, but capped when it was recorded, so check `modified_files_truncated`. `source=workdir` reads the filesystem, **one directory level per request**; pass a directory as `path` to descend. Note `modifying_tool_calls` counts modifying tool calls and not distinct files, so subtracting it from the entry count does not give \"how many more files\".",
        "parameters": [
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Workdir-relative path. A file is read; a directory is listed; absent lists the run's files."
          },
          {
            "name": "source",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "modified",
                "workdir"
              ],
              "default": "modified"
            },
            "description": "`modified` lists what the run recorded changing; `workdir` reads one directory level of the filesystem."
          },
          {
            "name": "hidden",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Include dot-prefixed entries when listing a directory."
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "415": {
            "$ref": "#/components/responses/Error"
          },
          "416": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents/{id}/files/raw": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "agents"
        ],
        "summary": "A workdir file's bytes, under its own content type",
        "description": "Where `GET /api/agents/{id}/files?path=` wraps text in JSON, this serves the bytes as they are, typed by the mime registry from the file's bytes and name: an image the run wrote comes back as `image/png`, ready for an `<img>`. Confined to the run's working directory. A path the directory does not hold but the run's result lists as an artifact is served from the run's blob store, so a file a model made and nothing wrote to disk answers here too. `download=1` adds a `Content-Disposition: attachment` naming the file. Every response advertises `Accept-Ranges: bytes`; a `Range` header fetches part of the body. Also takes a short-lived signed link in place of the bearer token (`exp` and `sig`, minted by the GraphQL API), which is what a browser needs for an `<img src>` or a download link: a header cannot be set on either. A signed link opens one path, for five minutes, and nothing else.",
        "parameters": [
          {
            "name": "path",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Workdir-relative path, or an absolute path inside the workdir."
          },
          {
            "name": "download",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Ask the browser to save the file rather than show it."
          },
          {
            "name": "Range",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "A single byte range, `bytes=start-end` (an open end or a `-suffix` too), to fetch part of the body for seeking or resuming. A well-formed range is answered 206 with `Content-Range`; one past the end is 416. A malformed or multi-range header is ignored and the whole body served."
          }
        ],
        "responses": {
          "200": {
            "description": "The file's bytes, under its mime type.",
            "content": {
              "*/*": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "206": {
            "description": "The requested byte range, when a `Range` header asked for one. Carries `Content-Range: bytes start-end/total`.",
            "content": {
              "*/*": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "416": {
            "description": "The `Range` header named bytes the body does not have. Carries `Content-Range: bytes */total`."
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents/{id}/blobs": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "agents"
        ],
        "summary": "The stored parts a run holds",
        "description": "Every stored part the run's context carries, by sha256: its type, name, size, dimensions, the regions carrying it, and whether the bytes are on disk under the run's `blobs/`. A part a user attached, a file `read_file` stored, an image an MCP tool returned, and an artifact the run submitted all appear here.",
        "responses": {
          "200": {
            "description": "The listing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BlobListing"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents/{id}/blobs/{sha256}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        },
        {
          "name": "sha256",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The part's sha256, 64 lowercase hex characters, as the listing and the run's context give it."
        }
      ],
      "get": {
        "tags": [
          "agents"
        ],
        "summary": "The bytes of one stored part",
        "description": "Served under the type the part was stored as, so an image renders in a browser. `download=1` adds a `Content-Disposition: attachment` carrying the part's name. A hash that is not 64 hex characters is refused; one the run does not hold is 404. Every response advertises `Accept-Ranges: bytes`; a `Range` header fetches part of the body. Also takes a short-lived signed link in place of the bearer token (`exp` and `sig`, minted by the GraphQL API), which is what a browser needs for an `<img src>` or a download link: a header cannot be set on either. A signed link opens one path, for five minutes, and nothing else.",
        "parameters": [
          {
            "name": "download",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Ask the browser to save the file rather than show it."
          },
          {
            "name": "Range",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "A single byte range, `bytes=start-end` (an open end or a `-suffix` too), to fetch part of the body for seeking or resuming. A well-formed range is answered 206 with `Content-Range`; one past the end is 416. A malformed or multi-range header is ignored and the whole body served."
          }
        ],
        "responses": {
          "200": {
            "description": "The bytes, under the part's mime type.",
            "content": {
              "*/*": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "206": {
            "description": "The requested byte range, when a `Range` header asked for one. Carries `Content-Range: bytes start-end/total`.",
            "content": {
              "*/*": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "416": {
            "description": "The `Range` header named bytes the body does not have. Carries `Content-Range: bytes */total`."
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents/{id}/artifacts/{name}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        },
        {
          "name": "name",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "The artifact's name, as the result route lists it under `final_output.artifacts`."
        }
      ],
      "get": {
        "tags": [
          "agents"
        ],
        "summary": "The bytes of one file the run handed back",
        "description": "Follows the artifact the way the runtime does: the run's blob store by hash first, so a file a model made and nothing wrote to the working directory is served, then the working directory. Served under the artifact's own mime type; `download=1` adds a `Content-Disposition: attachment` carrying its file name. A run that has not handed back a result, or one with no artifact of that name, is 404. Every response advertises `Accept-Ranges: bytes`; a `Range` header fetches part of the body. Also takes a short-lived signed link in place of the bearer token (`exp` and `sig`, minted by the GraphQL API), which is what a browser needs for an `<img src>` or a download link: a header cannot be set on either. A signed link opens one path, for five minutes, and nothing else.",
        "parameters": [
          {
            "name": "download",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Ask the browser to save the file rather than show it."
          },
          {
            "name": "Range",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "A single byte range, `bytes=start-end` (an open end or a `-suffix` too). A well-formed range is answered 206 with `Content-Range`; one past the end is 416. A malformed or multi-range header is ignored and the whole body served."
          }
        ],
        "responses": {
          "200": {
            "description": "The bytes, under the artifact's mime type.",
            "content": {
              "*/*": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "206": {
            "description": "The requested byte range, when a `Range` header asked for one. Carries `Content-Range: bytes start-end/total`.",
            "content": {
              "*/*": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "416": {
            "description": "The `Range` header named bytes the body does not have. Carries `Content-Range: bytes */total`."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents/{id}/logs": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "agents"
        ],
        "summary": "The run's logs",
        "description": "A run's logs, by stage. Output is recorded per stage under stages/<idx>/, and the two streams are kept separate rather than interleaved because they share no ordering.",
        "parameters": [
          {
            "name": "tail",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 32768
            },
            "description": "Return only the last N bytes (not lines)."
          },
          {
            "name": "stage",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Stage index to read, or `all` to join every stage oldest first. Defaults to the stage the run is on now."
          },
          {
            "name": "stream",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "output",
                "logs"
              ],
              "default": "output"
            },
            "description": "`output` for the assistant's readable output, `logs` for the operational stream: tool calls, token counts and errors."
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents/{id}/result": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "agents"
        ],
        "summary": "The run's output and token usage",
        "responses": {
          "200": {
            "description": "The result so far. Check `status` before trusting `output`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentResultResp"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents/{id}/tree-status": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "agents"
        ],
        "summary": "Token roll-ups across this run and its descendants",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents/{id}/pause": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "post": {
        "tags": [
          "agents"
        ],
        "summary": "Pause a run after its in-flight step",
        "responses": {
          "204": {
            "description": "Done. No body."
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents/{id}/resume": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "post": {
        "tags": [
          "agents"
        ],
        "summary": "Un-pause a run",
        "responses": {
          "204": {
            "description": "Done. No body."
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents/{id}/message": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "post": {
        "tags": [
          "agents"
        ],
        "summary": "Inject a message into a running agent's context",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "message"
                ],
                "properties": {
                  "message": {
                    "type": "string",
                    "description": "The text. A `@path` token attaches that workdir file to the same entry."
                  },
                  "target_region": {
                    "type": "string",
                    "description": "The region to land in; the conversation when absent."
                  },
                  "parts": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/PartRef"
                    },
                    "description": "Files already inside the run's working directory to send with the message."
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "request"
                ],
                "properties": {
                  "request": {
                    "type": "string",
                    "description": "The JSON request, as the application/json body would carry it."
                  },
                  "part": {
                    "type": "string",
                    "format": "binary",
                    "description": "A file to send beside the message. Repeatable; `part:<region>` aims one at another region."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted: the daemon holds it, and the work happens after the response."
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/agents/{id}/interaction": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "interactions"
        ],
        "summary": "The question this run is waiting on, if any",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      },
      "post": {
        "tags": [
          "interactions"
        ],
        "summary": "Answer the pending question",
        "responses": {
          "202": {
            "description": "Accepted: the daemon holds it, and the work happens after the response."
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "413": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        },
        "description": "Body: `request_id` (the open request's id) plus one of `value`, `choice_index`, or `approved` with an optional `scope` (`once`, `stage`, `session`). A deny (`approved: false`) may carry `feedback`, a string the model reads inside the tool result for the refused call: `[denied] User declined tool call '<tool>'. Feedback: <text>`. `feedback` beside `approved: true` is a 400. 202 once the daemon holds the answer, 404 when nothing with that id is open. Announced as `interaction.feedback`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "request_id"
                ],
                "properties": {
                  "request_id": {
                    "type": "string",
                    "description": "The interaction to answer, from GET."
                  },
                  "value": {
                    "type": "string",
                    "description": "A free-text or edited answer. A `@path` token attaches that workdir file."
                  },
                  "choice_index": {
                    "type": "integer",
                    "description": "The chosen option, zero-based, for a multiple-choice or tool-approval prompt."
                  },
                  "approved": {
                    "type": "boolean",
                    "description": "Whether a tool approval or a confirmation is granted."
                  },
                  "scope": {
                    "type": "string",
                    "enum": [
                      "once",
                      "stage",
                      "session"
                    ],
                    "description": "How long a grant holds: this call, this stage, or the whole run (`session`)."
                  },
                  "feedback": {
                    "type": "string",
                    "description": "On a deny, what the model should do instead."
                  },
                  "parts": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/PartRef"
                    },
                    "description": "Files already inside the run's working directory to send with a text answer."
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "request"
                ],
                "properties": {
                  "request": {
                    "type": "string",
                    "description": "The JSON request, as the application/json body would carry it."
                  },
                  "part": {
                    "type": "string",
                    "format": "binary",
                    "description": "A file to send beside a text answer. Repeatable; `part:<region>` aims one at another region."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/providers": {
      "get": {
        "tags": [
          "providers"
        ],
        "summary": "Providers that sign in with a browser, and whether they are signed in",
        "parameters": [
          {
            "name": "quota",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Read each signed-in subscription's usage too, into its `quota` field."
          },
          {
            "name": "refresh",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Read the accounts again and wait for them, rather than answering from the reading the server keeps. For a console's \"check again\"; means nothing without `quota=true`."
          }
        ],
        "responses": {
          "200": {
            "description": "Success. With `quota=true` the reports come from a reading the server keeps: the accounts are asked once, side by side, each given five seconds, and the answer is served for a minute (fifteen seconds when an account could not be read). An account that does not answer carries `{\"error\": ...}` and never holds the response.",
            "headers": {
              "X-Leviath-Quota-Age": {
                "description": "Seconds since the accounts were read. Only with `quota=true`.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Leviath-Quota-Complete": {
                "description": "Whether every account answered when they were read. `false` names no provider; the per-provider `quota.error` does. Only with `quota=true`.",
                "schema": {
                  "type": "boolean"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        },
        "description": "Reports `enabled` (what `config.toml` says) and `signed_in` (whether a grant is stored) separately, because either can be true alone and a provider that is enabled without a sign-in fails every run. While a sign-in is in flight `signin` carries `{\"state\": \"waiting\", \"authorize_url\": ...}`; after one fails it carries `{\"state\": \"failed\", \"message\": ...}` until the next attempt, and it is absent when there is neither. Open to any caller: it discloses the account address and plan tier, the same facts `lev auth status` prints, and no token. With `?quota=true`, each enabled, signed-in provider also carries `quota`: `{\"report\": {\"plan\", \"windows\": [{\"label\", \"used_percent\" or \"used\", \"limit\", \"unit\", \"resets_at\"}], \"balance\", \"limit_reached\"}}`, or `{\"error\": ...}` when the account could not be read. Off by default: it is a reading of the accounts rather than of this machine. The reading is kept, so a providers page can ask every time it opens; `X-Leviath-Quota-Age` and `X-Leviath-Quota-Complete` say how old it is and whether every account answered, and `?refresh=1` reads them again. Announced as the `providers.quota` capability."
      }
    },
    "/api/providers/{name}/login": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Name"
        }
      ],
      "post": {
        "tags": [
          "providers"
        ],
        "summary": "Start the browser sign-in for a provider",
        "responses": {
          "202": {
            "description": "The flow started; poll `GET /api/providers`."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "405": {
            "$ref": "#/components/responses/NotMounted"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "502": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        },
        "description": "Admin only. Mounted only when `lev serve --allow-admin` is passed, because it opens the operator's browser on the host running the server and binds a fixed loopback port there. Returns `202` with `{\"status\": \"waiting\", \"authorize_url\": ...}` as soon as there is a URL, and the flow continues behind the response - poll `GET /api/providers` for the outcome. It does not hold the request open for the whole flow the way the MCP login does: a browser UI needs to render the URL and a progress state, and a request held open for five minutes gives it neither. **The browser has to be on the serving host**, because the redirect goes to `localhost:1455` there and nowhere else - that is what OpenAI registered the public client id against. A console driving a remote daemon can start the flow and show `authorize_url`, but somebody has to open it on that machine. `409` when a sign-in to the same provider is already waiting, with its `authorize_url` in the body so a reconnecting console can pick the flow back up rather than having to cancel it."
      }
    },
    "/api/providers/{name}/logout": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Name"
        }
      ],
      "post": {
        "tags": [
          "providers"
        ],
        "summary": "Forget a provider's stored sign-in",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "405": {
            "$ref": "#/components/responses/NotMounted"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        },
        "description": "Admin only, like the sign-in it undoes: it deletes a credential from the grant store, and from the OS keychain when `[security] credential_store = \"keychain\"`. `config.toml` is deliberately untouched, the same as `lev auth logout` - signing out is not the same as turning the provider off, and doing both would surprise anyone who meant to sign in again. Set `codex_enabled` through `PUT /api/config` to do the other one."
      }
    },
    "/api/providers/{name}/check": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Name"
        }
      ],
      "post": {
        "tags": [
          "providers"
        ],
        "summary": "Prove a provider's stored sign-in still works",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "405": {
            "$ref": "#/components/responses/NotMounted"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "502": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        },
        "description": "Admin only, the same category as `POST /api/models/probe`: it makes this host open an authenticated connection to the provider. The same check `lev setup` runs, through the same code. It asks the account rather than reading a compiled model table, so a `200` means the subscription really did agree - and it refreshes a lapsed access token on the way in, which is also what keeps a rarely-used sign-in alive. Answers `{\"status\": \"ok\", \"models\": [...]}` with the models this plan can reach, and `502` with the provider's own reason when it is refused."
      }
    },
    "/api/mcp/servers": {
      "get": {
        "tags": [
          "mcp"
        ],
        "summary": "Configured MCP servers",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      },
      "post": {
        "tags": [
          "mcp"
        ],
        "summary": "Add an MCP server",
        "description": "Admin only. Mounted only when `lev serve --allow-admin` is passed, because adding a stdio server is arbitrary code execution by construction.",
        "responses": {
          "201": {
            "description": "Created."
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "$ref": "#/components/responses/NotMounted"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/mcp/servers/{name}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Name"
        }
      ],
      "delete": {
        "tags": [
          "mcp"
        ],
        "summary": "Remove an MCP server",
        "description": "Admin only. See POST /api/mcp/servers.",
        "responses": {
          "204": {
            "description": "Done. No body."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/mcp/servers/{name}/status": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Name"
        }
      ],
      "get": {
        "tags": [
          "mcp"
        ],
        "summary": "Whether this server is authenticated",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/mcp/servers/{name}/login": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Name"
        }
      ],
      "post": {
        "tags": [
          "mcp"
        ],
        "summary": "Begin the OAuth flow for this server, or report that it needs no login",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          },
          "502": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        },
        "description": "Admin only. Mounted only when `lev serve --allow-admin` is passed, because it opens the operator's browser on the host running the server and completes an OAuth flow there. Returns `{\"status\": \"authenticated\"}` when the browser flow completed and credentials were stored, or `{\"status\": \"not_required\"}` when the server answered a probe carrying its configured headers, which means there is no OAuth flow to run."
      }
    },
    "/api/mcp/servers/{name}/test": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Name"
        }
      ],
      "post": {
        "tags": [
          "mcp"
        ],
        "summary": "Connect and list this server's tools",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          },
          "502": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        },
        "description": "Admin only. Mounted only when `lev serve --allow-admin` is passed, because it connects to the server and, for a stdio server, spawns its configured command."
      }
    },
    "/api/doctor": {
      "get": {
        "tags": [
          "diagnostics"
        ],
        "summary": "The offline `lev doctor` checks, as data",
        "description": "The same checks as `lev doctor --offline`: config, search and resolve, then stop. Nothing here contacts a provider or the daemon, so pressing the button costs nothing. A failing check is reported inside a 200 with `ok: false`, not as an HTTP error: the request succeeded, the install is what did not.",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/mime": {
      "get": {
        "tags": [
          "config"
        ],
        "summary": "The effective mime registry",
        "description": "Every row of the mime registry this server resolves types with, keys sorted, each with where it came from: `builtin`, `config` (`[mime_types]` in config.toml), or the blueprint or provider that added it. A row names only what it sets, so a `family`, `text` or `extensions` that is absent is inherited from the pattern rows above it.",
        "responses": {
          "200": {
            "description": "The listing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MimeListing"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      },
      "put": {
        "tags": [
          "config"
        ],
        "summary": "Add or update a mime row",
        "description": "Write a row into `mime_types.toml` beside the config, or set the given fields on the row already there. Admin-only (needs `--allow-admin`; otherwise 405). Every field but `mime_type` is optional, and only the fields sent are changed. Validated the way `lev mime add` is: a type that is not `type/subtype`, a token rule that names none or more than one of `per_byte`/`per_pixel`/`per_second`/`fixed`, a `magic` that is not hex, or a `check` script that will not compile is a 400 and nothing is written. Announced as `mime.write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "mime_type"
                ],
                "properties": {
                  "mime_type": {
                    "type": "string",
                    "description": "`type/subtype`, or `type/*` for a whole family."
                  },
                  "family": {
                    "type": "string"
                  },
                  "text": {
                    "type": "boolean"
                  },
                  "tokens": {
                    "type": "object",
                    "description": "One of `{ per_byte }`, `{ per_pixel, max? }`, `{ per_second }`, `{ per_page }` or `{ fixed }`.",
                    "properties": {
                      "per_byte": {
                        "type": "number"
                      },
                      "per_pixel": {
                        "type": "integer"
                      },
                      "per_second": {
                        "type": "integer"
                      },
                      "per_page": {
                        "type": "integer"
                      },
                      "fixed": {
                        "type": "integer"
                      },
                      "max": {
                        "type": "integer"
                      }
                    }
                  },
                  "extensions": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "magic": {
                    "type": "string",
                    "description": "A hex prefix that identifies the bytes."
                  },
                  "stand_in": {
                    "type": "string"
                  },
                  "check": {
                    "type": "string",
                    "description": "A Rhai check script path; an empty string lifts a broader row's check."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The row was written.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "mime_type": {
                      "type": "string"
                    },
                    "created": {
                      "type": "boolean",
                      "description": "True when the row was new, false when it updated one already there."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "$ref": "#/components/responses/NotMounted"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      },
      "delete": {
        "tags": [
          "config"
        ],
        "summary": "Remove a mime row",
        "description": "Take a row out of `mime_types.toml` beside the config. Admin-only (needs `--allow-admin`; otherwise 405). A type with no row there is a 404. Announced as `mime.write`.",
        "parameters": [
          {
            "name": "mime_type",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "`type/subtype` or `type/*`."
          }
        ],
        "responses": {
          "204": {
            "description": "Removed. No body."
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "405": {
            "$ref": "#/components/responses/NotMounted"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/doctor/live": {
      "post": {
        "tags": [
          "diagnostics"
        ],
        "summary": "The whole `lev doctor` chain, billed calls included",
        "description": "Admin only. Mounted only when `lev serve --allow-admin` is passed: past the offline checks it makes one billed provider call, then spawns a throwaway one-stage run through the daemon (a second billed call) and waits for it. One at a time; a second caller while one is going gets 409. A failing check is reported inside a 200 with `ok: false`, not as an HTTP error.",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "409": {
            "description": "Another live doctor run is already in progress"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/yolo": {
      "get": {
        "tags": [
          "yolo"
        ],
        "summary": "The profiles behind `--yolo=<name>`",
        "description": "Lists `yolo.toml` as it stands, the same file a spawn reads: where it is, whether it exists and loads, and each profile's default, its three human knobs (`questions`, `checkpoints`, `gate`) and how many tool and shell rules of each kind it has. A file that does not load comes back with `exists: true`, an `error` naming the line, and no profiles, because that is what a spawn naming one would be refused with.",
        "responses": {
          "200": {
            "description": "The listing.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "path": {
                      "type": "string",
                      "description": "The `yolo.toml` the profiles are read from."
                    },
                    "exists": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string",
                      "description": "Why the file does not load, when it does not; the profiles are then empty. Absent while it loads."
                    },
                    "profiles": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "default": {
                            "type": "string",
                            "enum": [
                              "allow",
                              "ask"
                            ]
                          },
                          "questions": {
                            "type": "string",
                            "enum": [
                              "ask",
                              "auto"
                            ]
                          },
                          "checkpoints": {
                            "type": "string",
                            "enum": [
                              "ask",
                              "auto"
                            ]
                          },
                          "gate": {
                            "type": "string",
                            "enum": [
                              "ask",
                              "auto"
                            ]
                          },
                          "tool_rules": {
                            "type": "array",
                            "items": {
                              "type": "integer"
                            },
                            "description": "`[allow, ask, deny]` entry counts."
                          },
                          "shell_rules": {
                            "type": "array",
                            "items": {
                              "type": "integer"
                            },
                            "description": "`[allow, ask, deny]` rule counts."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      },
      "put": {
        "tags": [
          "yolo"
        ],
        "summary": "Replace `yolo.toml`",
        "description": "Admin only. Mounted only when `lev serve --allow-admin` is passed: a profile is a grant of permissions, so writing the file is the same category of act as writing the config. The text is parsed and compiled first, and a save that would not load answers 400 with the same message a spawn would give, leaving the file on disk as it was. Answers with the listing `GET` returns.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "description": "The whole file, as TOML."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The listing.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "path": {
                      "type": "string",
                      "description": "The `yolo.toml` the profiles are read from."
                    },
                    "exists": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string",
                      "description": "Why the file does not load, when it does not; the profiles are then empty. Absent while it loads."
                    },
                    "profiles": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "default": {
                            "type": "string",
                            "enum": [
                              "allow",
                              "ask"
                            ]
                          },
                          "questions": {
                            "type": "string",
                            "enum": [
                              "ask",
                              "auto"
                            ]
                          },
                          "checkpoints": {
                            "type": "string",
                            "enum": [
                              "ask",
                              "auto"
                            ]
                          },
                          "gate": {
                            "type": "string",
                            "enum": [
                              "ask",
                              "auto"
                            ]
                          },
                          "tool_rules": {
                            "type": "array",
                            "items": {
                              "type": "integer"
                            },
                            "description": "`[allow, ask, deny]` entry counts."
                          },
                          "shell_rules": {
                            "type": "array",
                            "items": {
                              "type": "integer"
                            },
                            "description": "`[allow, ask, deny]` rule counts."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "405": {
            "$ref": "#/components/responses/NotMounted"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/yolo/test": {
      "post": {
        "tags": [
          "yolo"
        ],
        "summary": "What a profile would decide for one call",
        "description": "The same code path a run takes, without running anything, so the answer here is the run's answer. `command` is for the shell; any other tool takes `arguments` as an object. `workdir` is where relative paths resolve, defaulting to the server's own. `configured` (`allow`, `ask`, `deny`) stands in for what the config layers resolve the tool to, otherwise that is read from the config in force; `kind` (`builtin`, `subagent`, `script`, `mcp`) says where the tool comes from for `@group` rules, otherwise it is guessed from the name; `allowed` decides as if `--allow <tool>` had been passed.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "profile",
                  "tool"
                ],
                "properties": {
                  "profile": {
                    "type": "string"
                  },
                  "tool": {
                    "type": "string"
                  },
                  "command": {
                    "type": "string"
                  },
                  "arguments": {
                    "type": "object"
                  },
                  "workdir": {
                    "type": "string"
                  },
                  "configured": {
                    "type": "string",
                    "enum": [
                      "allow",
                      "ask",
                      "deny"
                    ]
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "builtin",
                      "subagent",
                      "script",
                      "mcp"
                    ]
                  },
                  "allowed": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The decision.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "profile": {
                      "type": "string"
                    },
                    "tool": {
                      "type": "string"
                    },
                    "configured": {
                      "type": "string",
                      "enum": [
                        "allow",
                        "ask",
                        "deny"
                      ],
                      "description": "What the config layers resolved the tool to, before the profile."
                    },
                    "policy": {
                      "type": "string",
                      "enum": [
                        "allow",
                        "ask",
                        "deny"
                      ],
                      "description": "What the run would do: run it unprompted, open the ordinary approval prompt, or refuse it."
                    },
                    "reason": {
                      "type": "string",
                      "description": "The rule, the config, or the profile default that decided it."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/yolo/{name}": {
      "get": {
        "tags": [
          "yolo"
        ],
        "summary": "One profile in full",
        "description": "`{\"name\", \"spec\", \"holds\"}`: the profile as parsed (the same keys the file has) and the list of things it still puts to a person, as `lev run` prints before a profiled run starts. 404 for a name the file does not have and for no file at all; 422 when the file does not load.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The profile.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "spec": {
                      "type": "object"
                    },
                    "holds": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/update": {
      "get": {
        "tags": [
          "diagnostics"
        ],
        "summary": "How this copy was installed, and what upgrades it",
        "description": "The same answer `lev update --check --json` prints, from the same planner, so a client never sends a user a command their own terminal disagrees with. `install_method` is one of `homebrew`, `scoop`, `cargo`, `script`, `unknown`. `binary.action` is `run` with a `commands` list of argv lists to run in order, or `advise` with a `message` to show. Read-only and available without `--allow-admin`: it works out what an update would do and does none of it.",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      },
      "post": {
        "tags": [
          "diagnostics"
        ],
        "summary": "Carry out the update the GET half describes",
        "description": "Admin only. Mounted only when `lev serve --allow-admin` is passed, because it runs a package manager, rewrites the agents directory and rewrites the config. Answers 202 with a `job_id` and runs the work behind it: an upgrade is a download and an install, so every step change is sent on `/ws` as `update_progress`, the last as `update_finished`, and `GET /api/update/jobs/{id}` answers the same record for a client that would rather poll. The body names which parts to do - `binary`, `agents`, `migrations`, each defaulting to true, so an empty body is the whole plan. A `binary.action` of `advise` stays advice: the step is recorded as `advised` with the reason and no compile is started. A blueprint you edited locally is never installed. 409 while another update is running.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "binary": {
                    "type": "boolean"
                  },
                  "agents": {
                    "type": "boolean"
                  },
                  "keys": {
              "type": "boolean"
            },
            "migrations": {
                    "type": "boolean"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "202": {
            "$ref": "#/components/responses/Ok"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "$ref": "#/components/responses/NotMounted"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/update/jobs/{id}": {
      "get": {
        "tags": [
          "diagnostics"
        ],
        "summary": "Where one update run got to",
        "description": "Admin only, mounted alongside `POST /api/update`. The same record the `update_finished` frame carries: `status` of `running`, `complete` or `failed`; a `steps` list with one entry per step (`binary`, `agents`, `migrations`), each `pending`, `running`, `done`, `skipped`, `advised` or `failed` with a line of detail; and `restart_required`, set when the binary was replaced - this server and the daemon keep running the old build until they are restarted. The last few runs are kept, so a client that reads back after the fact finds the job.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "An update job id, as returned by POST /api/update."
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/fs/dirs": {
      "get": {
        "tags": [
          "diagnostics"
        ],
        "summary": "One directory level, for a folder picker",
        "parameters": [
          {
            "name": "path",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "hidden",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "Include dot-directories."
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      },
      "post": {
        "tags": [
          "diagnostics"
        ],
        "summary": "Create one directory, for a folder picker's New Folder",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "path",
                  "name"
                ],
                "properties": {
                  "path": {
                    "type": "string",
                    "description": "Absolute parent directory. Must exist, and lie inside --workdir-root when one is set."
                  },
                  "name": {
                    "type": "string",
                    "description": "The new directory's name: one segment, no separators, not . or .."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "$ref": "#/components/responses/Ok"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/config": {
      "get": {
        "tags": [
          "config"
        ],
        "summary": "The active configuration, with credentials redacted",
        "responses": {
          "200": {
            "description": "The configuration, redacted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RedactedConfig"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        },
        "description": "Describes the config in force. When `config.toml` on disk does not load, the server keeps serving the last config that did and reports why here: `config_error` carries `kind` (`parse`, `validation` or `read`), `path`, a one-line `message`, `line` and `column` for a syntax error, `key` for a refused value, `since` in unix seconds, and a `note` saying the running config is the last good one. `config_mtime` is the mtime of that config, so a client can tell whether its own write was picked up. `config_error` is absent while the file loads. Announced as the `config.health` capability."
      },
      "put": {
        "tags": [
          "config"
        ],
        "summary": "Replace the configuration",
        "description": "Admin only. Mounted only when `lev serve --allow-admin` is passed. The body is validated with the same checks the loader makes, before anything is written, so a request that would produce a config this build refuses to read back answers 400 and leaves the file byte for byte as it was.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Every field is optional and an absent one is left untouched, so a console can change one setting without reading the rest back and writing it again.",
                "properties": {
                  "default_provider": {
                    "type": "string"
                  },
                  "override_model": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Three states, unlike most fields here. Absent leaves the setting alone; `null` clears it, so each blueprint picks its own model per stage again; a string pins that model across every stage that allows a user default. An empty string is a 400, not a clear: `\"\"` is not a model id, and a form that posts its empty box should be told rather than silently lose the setting."
                  },
                  "fallback_model": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "The model a stage falls back to when none of the models it names is configured on this machine. Same three states and the same empty-string refusal as `override_model`."
                  },
                  "anthropic_key": {
                    "type": ["string", "null"],
                    "description": "Three states like `override_model`: absent leaves the key alone, null clears it and takes the provider out of this install, a string sets it. An empty string is a 400."
                  },
                  "openai_key": {
                    "type": ["string", "null"],
                    "description": "Same three states as `anthropic_key`."
                  },
                  "google_key": {
                    "type": ["string", "null"],
                    "description": "Same three states as `anthropic_key`."
                  },
                  "xai_key": {
                    "type": ["string", "null"],
                    "description": "An xAI API key, which starts `xai-`. Same three states as `anthropic_key`."
                  },
                  "meta_key": {
                    "type": ["string", "null"],
                    "description": "A Meta Model API key. Same three states as `anthropic_key`."
                  },
                  "openrouter_key": {
                    "type": ["string", "null"],
                    "description": "Same three states as `anthropic_key`."
                  },
                  "grok_enabled": {
                    "type": "boolean",
                    "description": "Bill Grok to a SuperGrok or X Premium+ subscription. Flips the switch only; the sign-in is `POST /api/providers/grok/login`."
                  },
                  "file_uploads": {
                    "type": "boolean",
                    "description": "Upload large parts to a provider's file storage once and name them by id. Zero data retention turns uploads off whatever this says."
                  },
                  "bedrock_key": {
                    "type": ["string", "null"],
                    "description": "An AWS Bedrock API key, sent as a bearer token. Not an AWS access key. Same three states as `anthropic_key`."
                  },
                  "bedrock_region": {
                    "type": "string",
                    "description": "The AWS region Bedrock is called in. An empty string is a 400."
                  },
                  "ollama_base_url": {
                    "type": "string"
                  },
                  "gateways": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "name"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "description": "The `[model_providers]` key. Created when no entry has this name."
                        },
                        "kind": {
                          "type": "string",
                          "enum": [
                            "script",
                            "openai-compatible",
                            "openai"
                          ],
                          "description": "Absent leaves the existing kind; an entry created without one is a script."
                        },
                        "base_url": {
                          "type": "string"
                        },
                        "api_key": {
                          "type": "string"
                        },
                        "script": {
                          "type": "string"
                        },
                        "headers": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "string"
                          },
                          "description": "Extra headers for an endpoint, replacing the existing set."
                        },
                        "models": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "The ids an endpoint falls back to when its server will not list them."
                        }
                      }
                    },
                    "description": "Gateways to add or update, by name. A gateway this list does not mention is left as it was."
                  },
                  "remove_gateways": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Gateways to delete, by name. Applied after the edits above."
                  },
                  "provider_order": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Ordered provider preference for a bare model name, best first (e.g. [\"codex\", \"openrouter\", \"openai\"]). Absent leaves it untouched; a present list replaces it whole; an empty list clears it back to default_provider alone. Naming a subscription transport here opts it into bare-name routing at that priority."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The configuration as written, redacted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RedactedConfig"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "$ref": "#/components/responses/NotMounted"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/config/validate": {
      "post": {
        "tags": [
          "config"
        ],
        "summary": "Validate one configuration key",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidateResponse"
                }
              }
            },
            "description": "Whether the key and value are usable."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/models": {
      "get": {
        "parameters": [
          {
            "name": "provider",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only models this provider serves. `openai` and `codex` serve the same model ids and bill to different places, so `provider` and `id` together are the key, not `id` alone. A provider this machine has not configured lists nothing rather than 404ing."
          },
          {
            "name": "refresh",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Ask the providers again and wait for them, rather than answering from the catalogue the server keeps. For a settings page that has just changed something."
          }
        ],
        "tags": [
          "config"
        ],
        "summary": "Models this install can reach, from the server's catalogue",
        "responses": {
          "200": {
            "description": "Success. The listing comes from a catalogue the server keeps; the providers are asked once per config and then behind the answer. A model's `pricing` may carry `long_context` (`threshold_tokens` and the rates a whole request is billed at once its prompt reaches it) and, for an image, video or speech model, `unit` (`usd` per `image`, `video_second`, `audio_hour`, `million_chars` or `clip`).",
            "headers": {
              "X-Leviath-Catalog-Age": {
                "description": "Seconds since the listing was built.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Leviath-Catalog-Complete": {
                "description": "Whether every provider answered when the listing was built. `false` means one timed out, errored or could not be built, and its models are absent.",
                "schema": {
                  "type": "boolean"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/models/probe": {
      "post": {
        "tags": [
          "config"
        ],
        "summary": "Ask an OpenAI-compatible server what models it serves",
        "description": "Admin only. Mounted only when `lev serve --allow-admin` is passed. Body: `base_url` (required), `api_key`, `headers`. Answers `{\"models\": [ids]}`, or 502 carrying the server's own error text, or 400 for a base URL with no scheme.",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/graphql": {
      "post": {
        "tags": [
          "graphql"
        ],
        "summary": "Run a GraphQL query",
        "description": "The GraphQL surface of this server. It runs over the same core the REST routes call, so it is not a wrapper around them. One request names exactly the fields it wants, at every depth: a view that costs a listing plus one request per run over REST is a single request here, and a field nobody selected is never read from disk. One grammar covers all of it. Every listing takes `filter`, `orderBy`, `first` and `after` and answers with `results`, `cursor` and `total`; every filter mirrors the type it selects, field for field, and composes with `and`, `or`, `not` and `isNull`; every mutation takes one argument named `request` and answers with a result carrying what it changed. A type's suffix says what it is for, so `RunOutput` is read, `RunInput` filters, `SpawnRunRequest` is a mutation argument and `RunSpawnedEvent` is a subscription frame. The schema is published beside this spec as `leviath.graphql`, and introspection answers the same thing live. A failure inside a field answers 200 with an `errors` array, each entry carrying `extensions.code` (`BAD_USER_INPUT`, `FORBIDDEN`, `NOT_FOUND`, `CONFLICT`, `PAYLOAD_TOO_LARGE`, `UNPROCESSABLE`, `UPSTREAM`, `DAEMON_INCOMPATIBLE`, `DAEMON_UNAVAILABLE`, `INTERNAL`) and `extensions.httpStatus`, the status the matching REST route answers with. The statuses below are the transport ones, answered by the layers around the handler before any query runs.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "query"
                ],
                "properties": {
                  "query": {
                    "type": "string",
                    "description": "The GraphQL document to run."
                  },
                  "operationName": {
                    "type": ["string", "null"],
                    "description": "Which operation to run, for a document carrying several."
                  },
                  "variables": {
                    "type": ["object", "null"],
                    "additionalProperties": true,
                    "description": "Values for the document's variables."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/ws": {
      "get": {
        "tags": [
          "events"
        ],
        "summary": "Event stream for every run",
        "description": "A WebSocket upgrade, not a JSON response. Authenticate with `?token=` since a browser cannot set a header on the handshake. Each frame is a ServerEvent. Frames about the machine rather than a run - `daemon_link` and `config_health` - arrive here. `config_health` is sent when `config.toml` stops loading, when it loads again, and when it is still broken for a different reason than the last frame gave, carrying the same error shape `GET /api/config` reports.",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "101": {
            "description": "Switching protocols."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/ws/graphql": {
      "get": {
        "tags": [
          "graphql"
        ],
        "summary": "Subscribe to the live frames over GraphQL",
        "description": "A WebSocket upgrade speaking `graphql-transport-ws` (the older `graphql-ws` protocol is accepted too), not a JSON response. Authenticate with `?token=` since a browser cannot set a header on the handshake. The difference from `/ws`: a subscription names the frame types it wants and the runs it is about, and the filtering happens on the server, before a frame is serialized. `includeDescendants` widens a run-scoped subscription to the sub-agents of those runs as they spawn, so a fan-out needs no re-subscribe. A subscription that falls behind receives an `EventsDroppedEvent` frame saying how many frames it missed, rather than silently skipping them.",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "101": {
            "description": "Switching protocols."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/ws/agents/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "events"
        ],
        "summary": "Event stream for one run",
        "description": "As /ws, filtered to a single run.",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "101": {
            "description": "Switching protocols."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/api/agents/{id}/stages": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Id"
        }
      ],
      "get": {
        "tags": [
          "agents"
        ],
        "summary": "The run's per-stage ledger",
        "description": "What each stage spent, in tokens and in dollars, per visit as well as in total, and which regions it carried. Not derivable from the other routes: a stage that ran and wrote nothing to any region leaves no trace in context/history, so `entered` is the only way to tell it from a stage that was never reached, and pricing is the daemon's job - a console multiplying tokens by a rate card of its own produces a fourth answer that disagrees with the run's, the stage's and the provider's.",
        "responses": {
          "200": {
            "description": "Stage records in blueprint order. Empty for a run that has not reached its first stage boundary.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RunStagesResp"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No run with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/tools": {
      "get": {
        "tags": [
          "scripts"
        ],
        "summary": "Tools an agent on this machine can call",
        "description": "Built-ins, sub-agent tools, the named agent's own `tools/*.rhai` and the global ones, each with a `source`. `groups` lists the tokens `available_tools` accepts in place of names (`@all`, `@builtin`, `@subagent`, `@scripts`, `@mcp`), each with a one-line description, so a picker can offer a whole kind at once. `skipped` carries the `.rhai` files that were found and could not be offered, with the reason. MCP tools are not included; `/api/mcp/servers/{name}` answers for those.",
        "parameters": [
          {
            "name": "agent",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Scope to this agent's own directory."
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/scripts": {
      "get": {
        "tags": [
          "scripts"
        ],
        "summary": "Rhai scripts this scope can see",
        "description": "Script tools from the agent's `tools/` and the global directory, the region hooks, stage hooks, output validators and mime checks the agent's manifest declares, the mime checks the operator's `mime_types.toml` and `[mime_types]` rows name (resolved against the config's directory), and the model providers in `~/.leviath/providers`. Each entry carries its kind, its source (`agent` or `global`), a `declared` flag and whether it compiles right now; an agent-scoped entry also carries `relative_path`, where the file sits relative to the agent's own directory, which is the spelling a manifest wants. A provider also carries a `provider` object with what its `// @` annotations declare. Providers are listed with or without `agent`, since nothing scopes one to an agent.",
        "parameters": [
          {
            "name": "agent",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Scope to this agent's own directory."
          },
          {
            "name": "include",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "candidates"
              ]
            },
            "description": "Comma-separated extras. `candidates` also lists the `.rhai` files under the agent's directory that nothing declares, as `kind: \"unknown\"` with `declared: false` and no `compiles`, so an editor can offer a validator or a hook that no manifest names yet. Only meaningful with `agent`: both global directories are already listed file by file. The scan is bounded (four levels deep, 128 directories, 256 files) and follows no symlink out of the agent's directory. An unrecognized token is a 400."
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/scripts/validate": {
      "post": {
        "tags": [
          "scripts"
        ],
        "summary": "Compile a script without writing it",
        "description": "Takes `kind` and `content`, plus `hooks` for a stage hook, and answers with `valid` and the compiler's complaint. Writes nothing and runs nothing: every compiler here stops at the AST, a provider's `initialize` included. A provider that does not define `initialize(config)` and `inference(state, request)` fails here rather than at the first inference.",
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    },
    "/api/scripts/{kind}/{name}": {
      "get": {
        "tags": [
          "scripts"
        ],
        "summary": "A script's source text",
        "parameters": [
          {
            "name": "kind",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "tool",
                "region_hook",
                "stage_hook",
                "output_validator",
                "mime_check",
                "provider"
              ]
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The file's name without its `.rhai` extension. May be a relative path when the manifest declared one, with the separator percent-encoded so it stays one segment: `validators%2Fa2ui`. Every part may hold only letters, digits, `.`, `_` and `-`, and the result must resolve inside the directory the route is fenced to."
          },
          {
            "name": "agent",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Scope to this agent's own directory. A `provider` is global to the machine and refuses this parameter."
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      },
      "put": {
        "tags": [
          "scripts"
        ],
        "summary": "Write a script",
        "description": "Admin only. Mounted only when `lev serve --allow-admin` is passed, because a `.rhai` file is executable code every agent then runs. A file that does not compile is still written, and the response says so.",
        "parameters": [
          {
            "name": "kind",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "tool",
                "region_hook",
                "stage_hook",
                "output_validator",
                "mime_check",
                "provider"
              ]
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The file's name without its `.rhai` extension. May be a relative path when the manifest declared one, with the separator percent-encoded so it stays one segment: `validators%2Fa2ui`. Every part may hold only letters, digits, `.`, `_` and `-`, and the result must resolve inside the directory the route is fenced to."
          },
          {
            "name": "agent",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Scope to this agent's own directory. A `provider` is global to the machine and refuses this parameter."
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Ok"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "$ref": "#/components/responses/NotMounted"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      },
      "delete": {
        "tags": [
          "scripts"
        ],
        "summary": "Delete a script",
        "description": "Admin only. Mounted only when `lev serve --allow-admin` is passed.",
        "parameters": [
          {
            "name": "kind",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "tool",
                "region_hook",
                "stage_hook",
                "output_validator",
                "mime_check",
                "provider"
              ]
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The file's name without its `.rhai` extension. May be a relative path when the manifest declared one, with the separator percent-encoded so it stays one segment: `validators%2Fa2ui`. Every part may hold only letters, digits, `.`, `_` and `-`, and the result must resolve inside the directory the route is fenced to."
          },
          {
            "name": "agent",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Scope to this agent's own directory. A `provider` is global to the machine and refuses this parameter."
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "$ref": "#/components/responses/NotMounted"
          },
          "408": {
            "$ref": "#/components/responses/Timeout"
          },
          "503": {
            "$ref": "#/components/responses/Overloaded"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "The token from `lev serve --token` or LEVIATH_API_TOKEN. The server refuses to start without one. WebSocket routes take it as `?token=` instead."
      }
    },
    "parameters": {
      "Id": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "A run id, as returned by `lev run --json` or POST /api/agents."
      },
      "Name": {
        "name": "name",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "Ok": {
        "description": "Success.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object"
            }
          }
        }
      },
      "Error": {
        "description": "The request failed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "The bearer token is missing or wrong. Every route, the websocket included.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "NotMounted": {
        "description": "The method is mounted only with `lev serve --allow-admin`; without it the path exists for its other methods and this one is refused.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Timeout": {
        "description": "The request ran past `[serve] request_timeout_secs` and its work was dropped. Not on the websocket routes.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Overloaded": {
        "description": "More requests in flight than `[serve] max_concurrent_requests` allows, or the daemon is not answering. Retry after a moment.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "SpawnAgentReq": {
        "type": "object",
        "required": [
          "blueprint",
          "task"
        ],
        "properties": {
          "blueprint": {
            "type": "string",
            "description": "An installed blueprint name, or a path to one."
          },
          "task": {
            "type": "string"
          },
          "model": {
            "type": "string",
            "description": "\"provider/model\", or a bare model id."
          },
          "max_depth": {
            "type": "integer",
            "minimum": 0
          },
          "yolo": {
            "type": "boolean",
            "default": false,
            "description": "Run unattended. Refused when the server was started with --no-remote-yolo."
          },
          "allow": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "no_seed_commands": {
            "type": "boolean",
            "default": false
          },
          "capture_model_input": {
            "type": "boolean",
            "default": false,
            "description": "Write this run's exact requests into its journal, once per provider attempt, whatever the machine's own [observability] capture_model_input says. A captured request is the whole prompt and there is no size cap, so the journal grows by roughly the context size per attempt. Read it back on InferenceAttemptOutput.modelInput over GraphQL."
          },
          "workdir": {
            "type": "string",
            "description": "Confined to --workdir-root when the server sets one."
          },
          "regions": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Seed content per named context region."
          },
          "output_format": {
            "type": "string",
            "description": "Ask for the final output in this shape, overriding the blueprint's. Any label works - nothing converts between shapes; the label reaches the model, which produces the bytes. Naming a format the blueprint does not declare retires any Rhai validator and JSON schema it declared (a check written for one shape says nothing about another); the response's warnings array names what was retired. Supply output_schema when the new shape should still be checked."
          },
          "output_instructions": {
            "type": "string",
            "description": "Extra guidance about that shape. How an unusual format gets explained."
          },
          "output_schema": {
            "type": "object",
            "description": "A JSON Schema the final output must satisfy. The only thing that inspects the answer's contents, and only because you asked: a failing submission is refused back to the agent to correct."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "callback_url": {
            "type": "string",
            "description": "POSTed on completion or error."
          },
          "callback_secret": {
            "type": "string",
            "description": "Shared secret for the HMAC-SHA256 signature on that callback."
          },
          "parts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PartRef"
            },
            "description": "Files already inside the working directory to attach as typed parts. A `multipart/form-data` body carries files instead: a `request` field holding this JSON, then any number of file fields named `part` (bound for the task region) or `part:<region>`, each with a `filename` and a `Content-Type`. A `@path` token inside `task` or a region's text attaches that file too, resolved inside the working directory, and the text keeps the token."
          }
        }
      },
      "SpawnAgentResp": {
        "type": "object",
        "required": [
          "agent_id",
          "run_id"
        ],
        "properties": {
          "agent_id": {
            "type": "string"
          },
          "run_id": {
            "type": "string"
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "How this run will differ from what its blueprint declares: today, the Rhai validator or JSON schema that a differing output_format retired. Omitted when there is nothing to say."
          }
        }
      },
      "AgentResultResp": {
        "type": "object",
        "required": [
          "run_id",
          "status",
          "output",
          "prompt_tokens",
          "completion_tokens"
        ],
        "properties": {
          "run_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "The run's status, in the same words a run object uses: starting, running, waiting_input, paused, complete, complete_interactive, error, cancelled. A server that does not announce `events.run_status` sends the Display spelling here instead (`WaitingInput`)."
          },
          "output": {
            "type": "string",
            "description": "The tail of the last stage's log: what the run did. Prefer final_output for what it concluded."
          },
          "final_output": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/FinalOutput"
              },
              {
                "type": "null"
              }
            ],
            "description": "The answer the agent submitted, if any."
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "prompt_tokens": {
            "type": "integer",
            "minimum": 0
          },
          "completion_tokens": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "FinalOutput": {
        "type": "object",
        "description": "What an agent handed back. `content` is byte-for-byte what it submitted; nothing re-serializes it. `format` is an opaque label the server never interprets - match on it to decide how to render.",
        "required": [
          "content",
          "stage",
          "submitted_at"
        ],
        "properties": {
          "content": {
            "type": "string"
          },
          "format": {
            "type": [
              "string",
              "null"
            ],
            "description": "Whatever shape was asked for: \"markdown\", \"json\", \"a2ui\", a mime type, anything. Not a closed set."
          },
          "stage": {
            "type": "string",
            "description": "The stage that produced it."
          },
          "submitted_at": {
            "type": "integer",
            "description": "Unix seconds."
          },
          "truncated": {
            "type": "boolean",
            "default": false,
            "description": "The answer hit the 256 KiB cap and was cut short."
          },
          "artifacts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Artifact"
            },
            "description": "Files the run produced, typed and hashed. An answer is one model response, so anything larger is a file named here. Fetch one with GET /api/agents/{id}/files?path=."
          }
        }
      },
      "Artifact": {
        "type": "object",
        "required": [
          "name",
          "path",
          "mime_type",
          "size"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "The name the stage declared for it, or the file name."
          },
          "path": {
            "type": "string",
            "description": "The file, relative to the run's working directory."
          },
          "mime_type": {
            "type": "string",
            "description": "Its mime type, from the registry or as declared."
          },
          "size": {
            "type": "integer",
            "description": "Size in bytes."
          },
          "sha256": {
            "type": "string",
            "description": "The hash the run's blob store holds the bytes under. Absent when the file was too large to store."
          }
        }
      },
      "ValidateResponse": {
        "type": "object",
        "required": [
          "valid"
        ],
        "properties": {
          "valid": {
            "type": "boolean"
          },
          "errors": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "warnings": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "RunStagesResp": {
        "type": "object",
        "required": [
          "run_id",
          "stages"
        ],
        "properties": {
          "run_id": {
            "type": "string"
          },
          "stages": {
            "type": "array",
            "description": "One record per declared stage, in blueprint order.",
            "items": {
              "$ref": "#/components/schemas/StageRecord"
            }
          }
        }
      },
      "StageRecord": {
        "type": "object",
        "required": [
          "name",
          "index",
          "status",
          "entered",
          "prompt_tokens",
          "completion_tokens"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "The stage's key under [stages]."
          },
          "index": {
            "type": "integer",
            "minimum": 0,
            "description": "Position in the blueprint's stage list. Not a progress measure: stages loop and revisit."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "active",
              "waiting_input",
              "complete",
              "error",
              "skipped"
            ],
            "description": "`skipped` means the run finished without ever entering this stage, which is distinct from `pending` (not yet, on a live run)."
          },
          "entered": {
            "type": "boolean",
            "description": "Whether the run has ever been in this stage. Position cannot answer this - a graph reaches its stages in whatever order its edges describe. False for every stage of a run recorded before Leviath tracked this, because the field is not in those files; read it together with `status`."
          },
          "prompt_tokens": {
            "type": "integer",
            "minimum": 0
          },
          "completion_tokens": {
            "type": "integer",
            "minimum": 0
          },
          "cached_tokens": {
            "type": "integer",
            "minimum": 0
          },
          "cache_write_tokens": {
            "type": "integer",
            "minimum": 0,
            "description": "Without this a stage showing no cache reads cannot be told apart from one paying to write a prefix nothing reuses."
          },
          "cost_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "What this stage spent, accumulated across revisits. Null means unknown, never free: some call was served by a model with no reported cost and no known rates, so any total would understate by an unknown amount. Counts every call made in the stage - its own turns, the compaction calls that summarize its context, and the routing call at its boundary. The run's title call belongs to no stage, so the stages can sum to slightly less than the run's own cost_usd. Absent on a daemon without the runs.stages.cost capability, which is not the same as null."
          },
          "unpriced_calls": {
            "type": "integer",
            "minimum": 0,
            "description": "Calls in this stage that could not be priced at all. Non-zero forces cost_usd to null."
          },
          "cost_is_exact": {
            "type": "boolean",
            "description": "Whether every priced call carried the provider's own cost figure. False means the total is a reconstruction from published rates, not the invoice."
          },
          "cost_priced_usd": {
            "type": "number",
            "description": "The priced subtotal, kept even while cost_usd is null so a resumed run does not restart from zero. Not a substitute for cost_usd: showing it while calls went unpriced is the partial total that looks authoritative and is not."
          },
          "models": {
            "type": "array",
            "description": "Every provider and model this stage ran an inference on, in the order it first reached each. A list because a stage that fails over runs on more than one: the first entry is where it started, the last is where it ended up, and one entry means it never moved. Each pair appears once however many calls it served, so this says what ran rather than how often. Absent on a stage that has run no inference - one the run never entered, one whose first call has not come back, one whose only provider could not be reached - and on every stage of a run recorded before Leviath kept it. Absent too on a daemon without the runs.stages.models capability, which is why an empty array is never written here.",
            "items": {
              "$ref": "#/components/schemas/StageModelUse"
            }
          },
          "visit_count": {
            "type": "integer",
            "minimum": 0,
            "description": "How many times the run has entered this stage, counting past the point where `visits` stopped recording them. Greater than visits.length means the per-visit split is partial and the accumulated figures on this record are the complete ones."
          },
          "visits": {
            "type": "array",
            "description": "Each contiguous stay in this stage, oldest first, capped at 128. The record itself accumulates across revisits, which is the right total for the stage and the wrong shape for a graph of the path a run took, where a stage entered twice is two nodes. A stage that loops back to itself starts a new visit; iterations within one stay do not. Empty on a stage the run never entered, and on records written before Leviath split visits out.",
            "items": {
              "$ref": "#/components/schemas/StageVisitRecord"
            }
          },
          "region_tokens": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "minimum": 0
            },
            "description": "The largest each region reached while this stage was active, by region name. The number that decides whether a region is earning its place."
          },
          "first_call_prompt_tokens": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "runaway_warned": {
            "type": "boolean",
            "description": "The stage's per-call prompt passed four times its first call - the shape of a region accumulating without a cap."
          },
          "started_at": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Unix seconds; null until the stage is entered."
          },
          "ended_at": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Unix seconds; null until the stage is left."
          },
          "active": {
            "type": [
              "object",
              "null"
            ],
            "description": "How long this stage actually spent working, as against how long it was the cursor. Null on records written before Leviath kept the clock, which is distinct from a stage that did no work. A stage the run is parked in - paused, or holding a prompt open - is still the cursor stage, so started_at..ended_at counts time nothing spent working.",
            "properties": {
              "banked_secs": {
                "type": "integer",
                "minimum": 0,
                "description": "Seconds from spans that have already ended."
              },
              "since": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Unix seconds when the span in progress began, or null while the clock is stopped. The working total is banked_secs plus now - since when since is set."
              }
            }
          }
        }
      },
      "StageModelUse": {
        "type": "object",
        "description": "One provider and model a stage ran an inference on. Both halves, because neither identifies what ran on its own: one provider serves many models, and one model is spelled differently by each provider that routes to it.",
        "required": [
          "provider",
          "model"
        ],
        "properties": {
          "provider": {
            "type": "string",
            "description": "The registered provider that served the call."
          },
          "model": {
            "type": "string",
            "description": "The model the call named, spelled as that provider spells it. The same model reached through two providers is two different strings here."
          }
        }
      },
      "StageVisitRecord": {
        "type": "object",
        "description": "One contiguous stay in a stage: what it cost, and how long it took. The nodes of the graph a run actually walked, where a stage entered twice is two of them.",
        "required": [
          "entered_at",
          "left_at",
          "prompt_tokens",
          "completion_tokens",
          "cached_tokens",
          "cache_write_tokens",
          "cost_usd",
          "unpriced_calls",
          "cost_is_exact",
          "cost_priced_usd"
        ],
        "properties": {
          "entered_at": {
            "type": "integer",
            "description": "Unix seconds when the run entered the stage on this visit."
          },
          "left_at": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Unix seconds when it left. Null on the visit in progress, which is the last entry of the stage the run is in right now."
          },
          "prompt_tokens": {
            "type": "integer",
            "minimum": 0
          },
          "completion_tokens": {
            "type": "integer",
            "minimum": 0
          },
          "cached_tokens": {
            "type": "integer",
            "minimum": 0
          },
          "cache_write_tokens": {
            "type": "integer",
            "minimum": 0
          },
          "cost_usd": {
            "type": [
              "number",
              "null"
            ],
            "description": "What this visit spent. Null means unknown, never free, on the same rule as the stage and the run."
          },
          "unpriced_calls": {
            "type": "integer",
            "minimum": 0,
            "description": "Calls in this visit that could not be priced at all. Non-zero forces cost_usd to null."
          },
          "cost_is_exact": {
            "type": "boolean",
            "description": "Whether every priced call in this visit carried the provider's own cost figure rather than one computed from published rates."
          },
          "cost_priced_usd": {
            "type": "number",
            "description": "The priced subtotal, kept even while cost_usd is null so a resumed run does not restart this visit's accounting from zero."
          },
          "active": {
            "type": [
              "object",
              "null"
            ],
            "description": "How long this visit actually spent working, as against how long the run was parked in the stage. Same shape and same rule as the stage's own clock.",
            "properties": {
              "banked_secs": {
                "type": "integer",
                "minimum": 0,
                "description": "Seconds from spans that have already ended."
              },
              "since": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Unix seconds when the span in progress began, or null while the clock is stopped."
              }
            }
          }
        }
      },
      "SkippedRun": {
        "type": "object",
        "required": [
          "id",
          "reason"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The run that was not deleted."
          },
          "reason": {
            "type": "string",
            "description": "Why, as a sentence a console can show verbatim."
          }
        }
      },
      "DeleteRunsResp": {
        "type": "object",
        "required": [
          "deleted",
          "skipped"
        ],
        "properties": {
          "deleted": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Ids whose directories are gone."
          },
          "skipped": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SkippedRun"
            },
            "description": "Ids that were left alone, each with a reason. A live run and a run that was already gone are both non-deletions, and a caller needs to tell them apart."
          }
        }
      },
      "ApiLimits": {
        "type": "object",
        "description": "The caps this server enforces, so a client can size its requests instead of discovering a 400.",
        "required": [
          "max_limit",
          "max_ids",
          "max_file_bytes",
          "max_listing_entries",
          "max_search_scan",
          "search_log_tail_bytes",
          "max_history_limit",
          "max_tracked_modified_files",
          "max_concurrent_requests",
          "request_timeout_secs",
          "max_upload_bytes"
        ],
        "properties": {
          "max_limit": {
            "type": "integer",
            "description": "Largest `limit` on a paginated listing."
          },
          "max_ids": {
            "type": "integer",
            "description": "Most ids one bulk request may name."
          },
          "max_file_bytes": {
            "type": "integer",
            "description": "Largest file `GET /api/agents/{id}/files` serves whole."
          },
          "max_listing_entries": {
            "type": "integer",
            "description": "Most entries a directory listing returns."
          },
          "max_search_scan": {
            "type": "integer",
            "description": "Most runs a search reads before it stops."
          },
          "search_log_tail_bytes": {
            "type": "integer",
            "description": "How much of each log a search reads, from the end."
          },
          "max_history_limit": {
            "type": "integer",
            "description": "Largest `limit` on the context history route."
          },
          "max_tracked_modified_files": {
            "type": "integer",
            "description": "Most modified files a run reports."
          },
          "max_concurrent_requests": {
            "type": "integer",
            "description": "Requests in flight before a 503; 0 is no cap."
          },
          "request_timeout_secs": {
            "type": "integer",
            "description": "Seconds before a request is dropped with a 408; 0 is no deadline."
          },
          "max_upload_bytes": {
            "type": "integer",
            "description": "Bytes one request body may carry, which bounds a multipart upload. `[serve] max_upload_bytes`."
          }
        }
      },
      "GatewayInfo": {
        "type": "object",
        "description": "One `[model_providers.<name>]` entry, with its credential reduced to whether one is set.",
        "required": [
          "name",
          "has_api_key",
          "kind"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "base_url": {
            "type": "string",
            "description": "Absent when the entry has none."
          },
          "has_api_key": {
            "type": "boolean"
          },
          "kind": {
            "type": "string",
            "enum": [
              "script",
              "openai-compatible",
              "openai"
            ],
            "description": "How the entry reaches its provider: a Rhai script, an OpenAI-compatible endpoint spoken to directly, or OpenAI's own API at another host."
          },
          "script": {
            "type": "string",
            "description": "The script path, for `kind = \"script\"`."
          },
          "header_names": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The names of the extra headers an endpoint sends, never their values. Absent when there are none."
          },
          "models": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The model ids the entry declares it serves. Absent when it declares none."
          }
        }
      },
      "RedactedConfig": {
        "type": "object",
        "description": "The active configuration with every credential reduced to whether one is set, plus what this server can do and how much it accepts.",
        "required": [
          "default_provider",
          "override_model",
          "fallback_model",
          "provider_order",
          "has_anthropic_key",
          "has_openai_key",
          "has_google_key",
          "has_openrouter_key",
          "has_bedrock_key",
          "bedrock_region",
          "gateways",
          "agent_paths",
          "mcp_server_count",
          "api_version",
          "capabilities",
          "limits"
        ],
        "properties": {
          "default_provider": {
            "type": "string"
          },
          "override_model": {
            "type": [
              "string",
              "null"
            ],
            "description": "The model every stage that allows a user default starts on while it is set, ahead of what its blueprint names, as a bare model id on `default_provider`. `null` when nothing is pinned, which is the usual and generally better state: each blueprint then picks its own model per stage. Always sent, so a missing key means the server predates this field rather than meaning nothing is set."
          },
          "fallback_model": {
            "type": [
              "string",
              "null"
            ],
            "description": "The model a stage falls back to when none of the models it names is configured here, never ahead of them, as a bare model id on `default_provider`. `null` when unset. Always sent."
          },
          "provider_order": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Ordered provider preference for a bare model name, best first. Empty when the user set none, in which case `default_provider` alone decides. Always sent (empty array, not omitted) so a console can tell \"no order set\" from an old daemon that cannot report it."
          },
          "has_anthropic_key": {
            "type": "boolean"
          },
          "has_openai_key": {
            "type": "boolean"
          },
          "has_google_key": {
            "type": "boolean"
          },
          "has_openrouter_key": {
            "type": "boolean"
          },
          "has_xai_key": {
            "type": "boolean"
          },
          "has_meta_key": {
            "type": "boolean"
          },
          "grok_enabled": {
            "type": "boolean",
            "description": "Whether Grok is billed to a subscription. Whether a sign-in is stored is `GET /api/providers`."
          },
          "file_uploads": {
            "type": "boolean",
            "description": "Whether large parts upload to a provider's file storage. Zero data retention turns uploads off whatever this says."
          },
          "has_bedrock_key": {
            "type": "boolean"
          },
          "bedrock_region": {
            "type": [
              "string",
              "null"
            ],
            "description": "The AWS region Bedrock is called in, when the config pins one. `null` when unset, in which case the daemon follows AWS_REGION, then AWS_DEFAULT_REGION, then us-east-1. Always sent."
          },
          "ollama_base_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "gateways": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GatewayInfo"
            }
          },
          "agent_paths": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "mcp_server_count": {
            "type": "integer"
          },
          "api_version": {
            "type": "string",
            "description": "The version of this document the server implements; equal to `info.version` here."
          },
          "capabilities": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Feature names a client may rely on. Each is explained in the API guide."
          },
          "limits": {
            "$ref": "#/components/schemas/ApiLimits"
          }
        }
      },
      "PartRef": {
        "type": "object",
        "required": [
          "path"
        ],
        "description": "A file inside the run's working directory, to attach as a typed part.",
        "properties": {
          "path": {
            "type": "string",
            "description": "The file, relative to the working directory."
          },
          "region": {
            "type": "string",
            "description": "The region to put it in; the task region when absent."
          },
          "name": {
            "type": "string",
            "description": "The name the part carries; the file name when absent."
          },
          "mime_type": {
            "type": "string",
            "description": "Its mime type, when the bytes and name do not say."
          },
          "deliver": {
            "type": "string",
            "enum": [
              "native",
              "text",
              "stand_in"
            ],
            "description": "How it reaches the model: as its own type, as text, or as a one-line stand-in."
          },
          "caption": {
            "type": "string",
            "description": "Text stored beside it."
          }
        }
      },
      "BlobEntry": {
        "type": "object",
        "required": [
          "sha256",
          "mime_type",
          "size",
          "tokens",
          "regions",
          "stored"
        ],
        "properties": {
          "sha256": {
            "type": "string",
            "description": "The store's key: the bytes' sha256."
          },
          "mime_type": {
            "type": "string",
            "description": "The type the bytes were stored as."
          },
          "name": {
            "type": "string",
            "description": "The name the part carries, when the context gave it one."
          },
          "size": {
            "type": "integer",
            "description": "Size in bytes."
          },
          "width": {
            "type": "integer",
            "description": "Pixel width, when known."
          },
          "height": {
            "type": "integer",
            "description": "Pixel height, when known."
          },
          "duration_ms": {
            "type": "integer",
            "description": "Duration in milliseconds, when known."
          },
          "tokens": {
            "type": "integer",
            "description": "The token estimate the region was charged."
          },
          "regions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The regions whose entries carry this part, in layout order."
          },
          "stored": {
            "type": "boolean",
            "description": "Whether the bytes are on disk under the run's blobs directory."
          }
        }
      },
      "BlobListing": {
        "type": "object",
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BlobEntry"
            },
            "description": "Every stored part, first appearance first."
          }
        }
      },
      "MimeTypeEntry": {
        "type": "object",
        "required": [
          "mime_type",
          "source"
        ],
        "properties": {
          "mime_type": {
            "type": "string",
            "description": "The row's key: a type, or a pattern such as image/*."
          },
          "source": {
            "type": "string",
            "description": "Where the row came from: builtin, config, or a blueprint or provider name."
          },
          "family": {
            "type": "string",
            "description": "The family: resolved through broader rows for a type, as written for a pattern."
          },
          "text": {
            "type": "boolean",
            "description": "Whether the bytes are text: resolved for a type, as written for a pattern."
          },
          "tokens": {
            "type": "object",
            "description": "How the bytes are counted in tokens, the same way: one of per_byte, per_pixel, per_second or fixed, with an optional max."
          },
          "extensions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The extensions the type is known by, the same way."
          },
          "stand_in": {
            "type": "string",
            "description": "The stand-in template a row set, when one applies."
          },
          "check": {
            "type": "string",
            "description": "The check script the bytes must pass, when one applies."
          }
        }
      },
      "MimeListing": {
        "type": "object",
        "required": [
          "types"
        ],
        "properties": {
          "types": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MimeTypeEntry"
            },
            "description": "Every row, keys sorted."
          }
        }
      }
    }
  }
}
