GitHub Raw JSON API

omnigent-ai / omnigent

8,436 Python

Omnigent is an open-source AI agent framework and meta-harness: orchestrate Claude Code, Codex, Cursor, Pi, and custom agents — swap harnesses without rewriting, enforce policies and sandboxing, and collaborate in real time from any device.

Omnigent Server Specification

Located in openapi.json on branch HEAD

3.2.0 JSON 71 Endpoints 469.9 KB

Operations & Route Endpoints (99)

GET /.well-known/omnigent.json
Well Known Manifest
GET /api/version
Version
GET /health
Health
GET /v1/agents
List Builtin Agents
GET /v1/harnesses
List Harnesses
GET /v1/hosts
List Hosts
GET /v1/hosts/{host_id}
Get Host
GET /v1/hosts/{host_id}/credentials/detected
Detect Host Credentials
POST /v1/hosts/{host_id}/directories
Create Host Directory
GET /v1/hosts/{host_id}/filesystem
List Host Filesystem Root
GET /v1/hosts/{host_id}/filesystem/{path}
List Host Filesystem
POST /v1/hosts/{host_id}/harnesses/{harness}/credential
Store Host Harness Credential
POST /v1/hosts/{host_id}/harnesses/{harness}/install
Install Host Harness
GET /v1/hosts/{host_id}/harnesses/{harness}/model-options
Get Host Model Options
POST /v1/hosts/{host_id}/runners
Launch Runner
GET /v1/hosts/{host_id}/worktrees
List Host Worktrees
POST /v1/imports
Import Session
GET /v1/info
Info
GET /v1/me
Me
GET /v1/policies
List Policies
POST /v1/policies
Create Policy
DELETE /v1/policies/{policy_id}
Delete Policy
GET /v1/policies/{policy_id}
Get Policy
PATCH /v1/policies/{policy_id}
Update Policy
GET /v1/policy-registry
List Registry
GET /v1/projects
List Projects
POST /v1/projects
Create Project
DELETE /v1/projects/{project_id}
Delete Project
GET /v1/projects/{project_id}
Get Project
PATCH /v1/projects/{project_id}
Update Project
GET /v1/runners
List Runners
GET /v1/runners/{runner_id}/status
Runner Status
POST /v1/runners/{runner_id}/token
Mint Runner Owner Token
GET /v1/sessions
List Sessions
POST /v1/sessions
Create Session
GET /v1/sessions/projects
List Session Projects
DELETE /v1/sessions/{session_id}
Delete Session
GET /v1/sessions/{session_id}
Get Session
PATCH /v1/sessions/{session_id}
Update Session
GET /v1/sessions/{session_id}/agent
Get Session Agent
PUT /v1/sessions/{session_id}/agent
Update Session Agent
GET /v1/sessions/{session_id}/agent/contents
Get Session Agent Contents
GET /v1/sessions/{session_id}/agent/mcp-servers
List Mcp Servers
POST /v1/sessions/{session_id}/agent/mcp-servers
Create Mcp Server
DELETE /v1/sessions/{session_id}/agent/mcp-servers/{server_name}
Delete Mcp Server
PUT /v1/sessions/{session_id}/agent/mcp-servers/{server_name}
Update Mcp Server
POST /v1/sessions/{session_id}/auto-title
Automatically Rename Session
GET /v1/sessions/{session_id}/child_sessions
List Child Sessions
DELETE /v1/sessions/{session_id}/codex_goal
Clear Codex Goal
GET /v1/sessions/{session_id}/codex_goal
Get Codex Goal
PUT /v1/sessions/{session_id}/codex_goal
Set Codex Goal
PATCH /v1/sessions/{session_id}/codex_goal/status
Update Codex Goal Status
GET /v1/sessions/{session_id}/comments
List Comments
POST /v1/sessions/{session_id}/comments
Add Comment
POST /v1/sessions/{session_id}/comments/send
Send To Agent
DELETE /v1/sessions/{session_id}/comments/{comment_id}
Delete Comment
PATCH /v1/sessions/{session_id}/comments/{comment_id}
Update Comment
GET /v1/sessions/{session_id}/items
List Session Items
GET /v1/sessions/{session_id}/labels
Get Session Labels
GET /v1/sessions/{session_id}/owner
Get Session Owner
GET /v1/sessions/{session_id}/permissions
List Permissions
PUT /v1/sessions/{session_id}/permissions
Grant Permission
DELETE /v1/sessions/{session_id}/permissions/{target_user_id}
Revoke Permission
GET /v1/sessions/{session_id}/policies
List Policies
POST /v1/sessions/{session_id}/policies
Create Policy
POST /v1/sessions/{session_id}/policies/evaluate
Evaluate Policy
DELETE /v1/sessions/{session_id}/policies/{policy_id}
Delete Policy
GET /v1/sessions/{session_id}/policies/{policy_id}
Get Policy
PATCH /v1/sessions/{session_id}/policies/{policy_id}
Update Policy
PUT /v1/sessions/{session_id}/read-state
Put Read State
GET /v1/sessions/{session_id}/resources
List Session Resources
GET /v1/sessions/{session_id}/resources/environments
List Session Environments
GET /v1/sessions/{session_id}/resources/environments/{environment_id}
Get Session Environment
GET /v1/sessions/{session_id}/resources/environments/{environment_id}/changes
List Environment Filesystem Changes
GET /v1/sessions/{session_id}/resources/environments/{environment_id}/filesystem
List Environment Root
DELETE /v1/sessions/{session_id}/resources/environments/{environment_id}/filesystem/{relative_path}
Delete Environment Path
GET /v1/sessions/{session_id}/resources/environments/{environment_id}/filesystem/{relative_path}
Read Or List Environment Path
PATCH /v1/sessions/{session_id}/resources/environments/{environment_id}/filesystem/{relative_path}
Edit Environment File
PUT /v1/sessions/{session_id}/resources/environments/{environment_id}/filesystem/{relative_path}
Write Environment File
GET /v1/sessions/{session_id}/resources/environments/{environment_id}/search
Search Environment Files
GET /v1/sessions/{session_id}/resources/environments/{environment_id}/search/{path}
Search Environment Files Under
POST /v1/sessions/{session_id}/resources/environments/{environment_id}/shell
Run Environment Shell
GET /v1/sessions/{session_id}/resources/files
List Session Files
POST /v1/sessions/{session_id}/resources/files
Upload Session File
DELETE /v1/sessions/{session_id}/resources/files/{file_id}
Delete Session File
GET /v1/sessions/{session_id}/resources/files/{file_id}
Get Session File
GET /v1/sessions/{session_id}/resources/files/{file_id}/content
Get Session File Content
POST /v1/sessions/{session_id}/resources/files:copy
Copy Session Files
GET /v1/sessions/{session_id}/resources/terminals
List Session Terminals
POST /v1/sessions/{session_id}/resources/terminals
Create Session Terminal
DELETE /v1/sessions/{session_id}/resources/terminals/{terminal_id}
Delete Session Terminal
GET /v1/sessions/{session_id}/resources/terminals/{terminal_id}
Get Session Terminal
GET /v1/sessions/{session_id}/resources/{resource_id}
Get Session Resource
GET /v1/sessions/{session_id}/stream
Stream Session
POST /v1/sessions/{session_id}/switch-agent
Switch Session Agent
POST /v1/sessions/{source_id}/fork
Fork Session
GET /v1/sharing
Get Sharing
PUT /v1/sharing
Set Sharing
GET /v1/usage
Get Usage
Raw JSON Specification
{
  "components": {
    "schemas": {
      "AddCommentRequest": {
        "description": "Request body for `POST /sessions/{id}/comments`.",
        "properties": {
          "anchor_content": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Plain-text snapshot of the selected range, used to re-anchor the comment after file edits. `None` if not provided.",
            "title": "Anchor Content"
          },
          "body": {
            "description": "The comment text.",
            "title": "Body",
            "type": "string"
          },
          "end_index": {
            "description": "0-based absolute character offset (exclusive) within the file where the anchor range ends.",
            "title": "End Index",
            "type": "integer"
          },
          "path": {
            "description": "File path relative to workspace root, e.g. `\"src/App.tsx\"`.",
            "title": "Path",
            "type": "string"
          },
          "start_index": {
            "description": "0-based absolute character offset (inclusive) within the file where the anchor range begins.",
            "title": "Start Index",
            "type": "integer"
          }
        },
        "required": [
          "path",
          "body",
          "start_index",
          "end_index"
        ],
        "title": "AddCommentRequest",
        "type": "object"
      },
      "AgentObject": {
        "description": "API representation of a registered agent.",
        "properties": {
          "builtin": {
            "default": false,
            "description": "Whether this is a server-*seeded* built-in agent (deterministic, name-derived id) as opposed to an operator/user-registered template (random id, e.g. via `omnigent server --agent`) or a session-scoped upload. The Web UI's new-session picker uses this to decide whether a same-named `omnigent run` upload may shadow the catalog entry: seeded built-ins are protected, while a user-registered template is superseded by a newer same-named upload. Always `False` for session-scoped agents.",
            "title": "Builtin",
            "type": "boolean"
          },
          "created_at": {
            "description": "Unix epoch timestamp of creation.",
            "title": "Created At",
            "type": "integer"
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional free-text description of the agent's purpose.",
            "title": "Description"
          },
          "harness": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The agent's harness/kind, e.g. `\"codex\"`, `\"codex-native\"`, or `\"claude-native\"` for `executor.type: omnigent` agents, otherwise the executor type (`\"claude_sdk\"`, `\"agents_sdk\"`). `None` when the bundle cannot be loaded. Lets the Web UI Add Agent picker recognise an agent's kind (Codex vs Claude) without hardcoding by name slug.",
            "title": "Harness"
          },
          "id": {
            "description": "Unique agent identifier, e.g. `\"ag_abc123\"`.",
            "title": "Id",
            "type": "string"
          },
          "mcp_servers": {
            "description": "MCP servers the agent is connected to (secret fields omitted). Empty list when the spec declares no MCP servers or when the bundle cannot be loaded.",
            "items": {
              "$ref": "#/components/schemas/MCPServerSummary"
            },
            "title": "Mcp Servers",
            "type": "array"
          },
          "mcp_servers_editable": {
            "default": false,
            "description": "Whether the MCP list can be edited through the session UI. Built-in template agents are read-only; session-scoped uploaded agents are editable.",
            "title": "Mcp Servers Editable",
            "type": "boolean"
          },
          "name": {
            "description": "Human-readable agent name, e.g. `\"research-agent\"`.",
            "title": "Name",
            "type": "string"
          },
          "object": {
            "default": "agent",
            "description": "Fixed resource type, always `\"agent\"`.",
            "title": "Object",
            "type": "string"
          },
          "policies": {
            "description": "Guardrails policies declared on the agent. Each entry summarises the policy name, type, and phases. Empty list when the spec declares no policies or when the bundle cannot be loaded.",
            "items": {
              "$ref": "#/components/schemas/PolicySummary"
            },
            "title": "Policies",
            "type": "array"
          },
          "skills": {
            "description": "Skills bundled in the agent spec (`skills/<dir>/SKILL.md`). Lets the Web UI's new-session composer offer a slash-command menu before a session (and its runner) exists. Host-discovered skills are runner-owned, so they are NOT listed here — the session snapshot's `skills` field carries the merged set once a runner is bound. Empty list when the spec bundles no skills or when the bundle cannot be loaded.",
            "items": {
              "$ref": "#/components/schemas/SkillSummary"
            },
            "title": "Skills",
            "type": "array"
          },
          "terminals": {
            "description": "Terminal names declared in the spec's `terminals:` block, in declaration order, e.g. `[\"shell\"]`. The Web UI gates its \"new terminal\" affordance on this list (creation is only offered for agents with terminal access) and offers these names as the launchable choices. Empty list when the spec declares no terminals or when the bundle cannot be loaded.",
            "items": {
              "type": "string"
            },
            "title": "Terminals",
            "type": "array"
          },
          "updated_at": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Unix epoch timestamp of the last update, or `None` if never updated.",
            "title": "Updated At"
          },
          "version": {
            "default": 1,
            "description": "Monotonic version counter. Starts at 1, incremented on each update.",
            "title": "Version",
            "type": "integer"
          }
        },
        "required": [
          "id",
          "name",
          "created_at"
        ],
        "title": "AgentObject",
        "type": "object"
      },
      "AutomaticSessionRenameRequest": {
        "additionalProperties": false,
        "description": "Request body for the current-agent automatic rename endpoint.",
        "properties": {
          "title": {
            "maxLength": 60,
            "minLength": 2,
            "title": "Title",
            "type": "string"
          }
        },
        "required": [
          "title"
        ],
        "title": "AutomaticSessionRenameRequest",
        "type": "object"
      },
      "AutomaticSessionRenameResponse": {
        "description": "Result of a conditional automatic session rename.",
        "properties": {
          "reason": {
            "anyOf": [
              {
                "enum": [
                  "not_top_level",
                  "no_seed",
                  "title_changed"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Reason"
          },
          "renamed": {
            "title": "Renamed",
            "type": "boolean"
          },
          "title": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Title"
          }
        },
        "required": [
          "renamed"
        ],
        "title": "AutomaticSessionRenameResponse",
        "type": "object"
      },
      "Body_update_session_agent_v1_sessions__session_id__agent_put": {
        "properties": {
          "bundle": {
            "contentMediaType": "application/octet-stream",
            "title": "Bundle",
            "type": "string"
          }
        },
        "required": [
          "bundle"
        ],
        "title": "Body_update_session_agent_v1_sessions__session_id__agent_put",
        "type": "object"
      },
      "Body_upload_session_file_v1_sessions__session_id__resources_files_post": {
        "properties": {
          "file": {
            "contentMediaType": "application/octet-stream",
            "title": "File",
            "type": "string"
          }
        },
        "required": [
          "file"
        ],
        "title": "Body_upload_session_file_v1_sessions__session_id__resources_files_post",
        "type": "object"
      },
      "BrowserActionRequestEvent": {
        "description": "Request that the desktop renderer perform one browser action.\n\nEmitted by the server `POST /v1/sessions/{id}/browser/action_request`\nroute when a runner-side `browser_*` tool dispatch needs the\nOmnigent desktop app's embedded browser to act. The event fans out\non the session stream to every subscribed renderer; each renderer\nfirst POSTs `/browser/action_claim/{action_id}` and only the\nwinning claimant executes the action and POSTs the result back to\n`/browser/action_result/{action_id}`. The claim lease prevents\ndouble execution when more than one renderer is subscribed.",
        "properties": {
          "action": {
            "description": "The browser action to perform — the `browser_` tool name with the prefix stripped, e.g. `\"navigate\"`, `\"snapshot\"`, `\"click\"`, `\"type\"`, `\"screenshot\"`.",
            "title": "Action",
            "type": "string"
          },
          "action_id": {
            "description": "Unique correlation id for this request, e.g. `\"baction_abc123\"`. Echoed on the claim and result routes.",
            "title": "Action Id",
            "type": "string"
          },
          "args": {
            "additionalProperties": true,
            "description": "Action arguments forwarded from the tool call, e.g. `{\"url\": \"https://example.com\"}`.",
            "title": "Args",
            "type": "object"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "browser.action_request",
            "description": "Always `\"browser.action_request\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "action_id",
          "action",
          "args"
        ],
        "title": "BrowserActionRequestEvent",
        "type": "object"
      },
      "CancelledEvent": {
        "description": "Terminal event for a turn cancelled before completion.",
        "properties": {
          "response": {
            "$ref": "#/components/schemas/ResponseObject",
            "description": "The final response object with `status=\"cancelled\"`."
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.cancelled",
            "description": "Always `\"response.cancelled\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "response"
        ],
        "title": "CancelledEvent",
        "type": "object"
      },
      "ChildSessionList": {
        "description": "Paginated list of child sessions; `data` is a page of `ChildSessionSummary`.",
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/ChildSessionSummary"
            },
            "title": "Data",
            "type": "array"
          },
          "first_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "First Id"
          },
          "has_more": {
            "default": false,
            "title": "Has More",
            "type": "boolean"
          },
          "last_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Id"
          },
          "object": {
            "const": "list",
            "default": "list",
            "title": "Object",
            "type": "string"
          }
        },
        "title": "ChildSessionList",
        "type": "object"
      },
      "ChildSessionSummary": {
        "description": "Summary of a sub-agent (child) session under a parent session.\n\nPowers `GET /v1/sessions/{id}/child_sessions`. Lets the web /\nREPL debug surface enumerate sub-agent calls spawned from a\nparent session without parsing parent `function_call_output`\nJSON handles (the legacy TUI Ctrl+O path). The endpoint is the\ncanonical \"historical truth\" source; the existing transient\n`session.created` SSE event handles live incremental updates.\n\nFields are derived from the child `Conversation` plus its\nlatest `Task` (newest by `created_at`).",
        "properties": {
          "agent_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Agent id recorded on the latest task, e.g. `\"ag_abc123\"`. `None` if the child has no tasks yet (rare — `_spawn_one` creates a task atomically with the conversation).",
            "title": "Agent Id"
          },
          "agent_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Agent type recorded on the latest task, e.g. `\"researcher\"`. Mirrors the `tool` prefix in `title` and is provided alongside it because the title is a denormalized string while `agent_name` is the durable per-task value.",
            "title": "Agent Name"
          },
          "busy": {
            "default": false,
            "description": "`True` when the child's session loop is live. Mirrors the algorithm used by `GET /v1/sessions/{id}` to compute `status`: read the live in-memory cache first (`\"running\"`/`\"waiting\"` → busy), and fall back to the latest task's status on cache miss (`\"queued\"` / `\"in_progress\"` → busy). For NO_DBOS sessions the tasks table is not populated during active runs, so the cache consult is what keeps the rail's \"Working\" badge correct.",
            "title": "Busy",
            "type": "boolean"
          },
          "created_at": {
            "description": "Unix epoch timestamp of child creation.",
            "title": "Created At",
            "type": "integer"
          },
          "current_task_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Latest task id for the child (newest by `created_at`), e.g. `\"task_abc123\"`. `None` if no tasks exist.",
            "title": "Current Task Id"
          },
          "current_task_status": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Status of the latest task, e.g. `\"completed\"`, `\"in_progress\"`, `\"failed\"`. `None` if no tasks exist.",
            "title": "Current Task Status"
          },
          "id": {
            "description": "Child conversation/session identifier, e.g. `\"conv_child123\"`.",
            "title": "Id",
            "type": "string"
          },
          "kind": {
            "default": "sub_agent",
            "description": "Conversation kind discriminator, always `\"sub_agent\"` for rows surfaced by this endpoint.",
            "title": "Kind",
            "type": "string"
          },
          "labels": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Session-scoped guardrails labels on the child conversation (mirrors `ConversationObject.labels`).",
            "title": "Labels",
            "type": "object"
          },
          "last_message_preview": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Single-line preview of the most recent message item in the child's conversation, truncated to ~150 chars with a trailing ellipsis when longer. `None` when the child has no message items yet (rare — the spawn tool immediately commits a user message). Lets the UI render a real-time \"what's the sub-agent saying right now\" line without fetching the child's full item history.",
            "title": "Last Message Preview"
          },
          "last_task_error": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "string"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Error details from the child's most recent failed run, e.g. `{\"code\": \"required_terminal_exited\", \"message\": \"...\"}`. `None` when the child has no durable failure detail. This is the typed projection of runner-owned failure labels; clients should not parse those labels directly.",
            "title": "Last Task Error"
          },
          "object": {
            "default": "child_session",
            "description": "Fixed resource type, always `\"child_session\"`.",
            "title": "Object",
            "type": "string"
          },
          "parent_session_id": {
            "description": "Parent conversation id (echo of the route's `session_id` path parameter), e.g. `\"conv_parent987\"`. Stable join key for clients that cache child rows across multiple parents.",
            "title": "Parent Session Id",
            "type": "string"
          },
          "pending_elicitations_count": {
            "default": 0,
            "description": "Number of approval / input prompts the child is currently blocked on, read from the server's `omnigent.runtime.pending_elicitations` index. `> 0` means the sub-agent is parked awaiting user input — the Agents rail renders an \"awaiting input\" badge so a fanned-out sub-agent that needs attention is visible without opening its chat. Mirrors `SessionListItem.pending_elicitations_count`.",
            "title": "Pending Elicitations Count",
            "type": "integer"
          },
          "routed_model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Model this sub-agent runs on when one was pinned for it, e.g. `\"databricks-claude-opus-4-8\"`. Read from the child's `model_override` — the field intelligent routing writes when it picks a model for a spawned child. `None` when the child inherits the parent/spec model.",
            "title": "Routed Model"
          },
          "routing_decision_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Identifier of the routing decision that produced `routed_model`, mirroring `RoutingDecisionData.decision_id`. Read from the child's `omnigent.routing.decision_id` label, stamped when routing pins the model. `None` when the child was not routed.",
            "title": "Routing Decision Id"
          },
          "session_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Sub-agent instance name, the suffix of `title` after the first `\":\"`, e.g. `\"auth\"`. `None` if `title` is `None` or missing a colon.",
            "title": "Session Name"
          },
          "task_summary": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Task Summary"
          },
          "title": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Sub-agent title, `\"{agent_type}:{session_name}\"` as written by `omnigent.tools.builtins.spawn._spawn_one`, e.g. `\"researcher:auth\"`. `None` only for legacy / malformed rows; the spawn path always sets it.",
            "title": "Title"
          },
          "tool": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "UI-facing sub-agent label. For Omnigent-spawned children this is derived from the prefix of `title` before the first `\":\"`, e.g. `\"researcher\"`. For Codex-native children this is the Codex-assigned `agent_nickname` when available, then `agent_role`, then `\"Codex\"`. Falls back to the raw title for legacy / malformed rows; `None` only when `title` itself is `None` or empty.",
            "title": "Tool"
          },
          "updated_at": {
            "description": "Unix epoch timestamp of the child's most recent update.",
            "title": "Updated At",
            "type": "integer"
          }
        },
        "required": [
          "id",
          "parent_session_id",
          "created_at",
          "updated_at"
        ],
        "title": "ChildSessionSummary",
        "type": "object"
      },
      "ClearCodexGoalResponse": {
        "description": "Response body for `DELETE /v1/sessions/{id}/codex_goal`.",
        "properties": {
          "cleared": {
            "description": "`True` when Codex removed an existing goal; `False` when no goal was present.",
            "title": "Cleared",
            "type": "boolean"
          }
        },
        "required": [
          "cleared"
        ],
        "title": "ClearCodexGoalResponse",
        "type": "object"
      },
      "ClientTaskCancelEvent": {
        "description": "Server-side request that the client cancel a tunneled tool call.\n\nEmitted by `omnigent/runtime/workflow.py` when a parent\ncancellation needs to propagate to a long-running async client\ntool. Wire shape matches `workflow.py:4258-4266`.",
        "properties": {
          "call_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Synthetic `call_id` the SDK uses to reconcile the local task; `None` when no pending tool call row exists for the task.",
            "title": "Call Id"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "task_id": {
            "description": "Identifier of the client-side task being cancelled, e.g. `\"resp_async_abc\"`.",
            "title": "Task Id",
            "type": "string"
          },
          "type": {
            "const": "response.client_task.cancel",
            "description": "Always `\"response.client_task.cancel\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "task_id"
        ],
        "title": "ClientTaskCancelEvent",
        "type": "object"
      },
      "CodexGoalObject": {
        "description": "Current Codex goal state for a Codex-native session.\n\nMirrors Codex app-server's `ThreadGoal` shape using Omnigent's\nsnake-case API convention. `created_at` and `updated_at` are optional\nbecause older app-server documentation examples omit them even though the\ncurrent protocol includes them.",
        "properties": {
          "created_at": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Unix timestamp when the goal was created, e.g. `1776272400`. `None` when not provided by Codex.",
            "title": "Created At"
          },
          "objective": {
            "description": "Goal objective text, e.g. `\"Finish the migration and keep tests green\"`.",
            "title": "Objective",
            "type": "string"
          },
          "status": {
            "description": "Raw Codex goal lifecycle status, e.g. `\"active\"`.",
            "title": "Status",
            "type": "string"
          },
          "thread_id": {
            "description": "Codex app-server thread id, e.g. `\"thr_123\"`.",
            "title": "Thread Id",
            "type": "string"
          },
          "time_used_seconds": {
            "description": "Wall-clock seconds spent on this goal, e.g. `60`.",
            "minimum": 0,
            "title": "Time Used Seconds",
            "type": "integer"
          },
          "token_budget": {
            "anyOf": [
              {
                "exclusiveMinimum": 0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional token budget, e.g. `40000`. `None` means no explicit budget is set.",
            "title": "Token Budget"
          },
          "tokens_used": {
            "description": "Tokens spent while pursuing this goal, e.g. `1024`.",
            "minimum": 0,
            "title": "Tokens Used",
            "type": "integer"
          },
          "updated_at": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Unix timestamp when the goal was last updated, e.g. `1776272460`. `None` when not provided by Codex.",
            "title": "Updated At"
          }
        },
        "required": [
          "thread_id",
          "objective",
          "status",
          "tokens_used",
          "time_used_seconds"
        ],
        "title": "CodexGoalObject",
        "type": "object"
      },
      "CodexGoalResponse": {
        "description": "Response body for reading or setting a Codex-native session goal.",
        "properties": {
          "goal": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CodexGoalObject"
              },
              {
                "type": "null"
              }
            ],
            "description": "Current goal state, or `None` when the session has no persisted Codex goal."
          }
        },
        "required": [
          "goal"
        ],
        "title": "CodexGoalResponse",
        "type": "object"
      },
      "CompactionCompletedEvent": {
        "description": "Conversation history compaction has finished.\n\nEmitted after compaction completes — either by the server after\n`compact_conversation_now()` (explicit `/compact`), or by a\nharness that compacted its own internal context. Clients that\nrendered a \"Compacting…\" spinner on\n`CompactionInProgressEvent` should upgrade it to the\npermanent \"Conversation compacted\" marker on this event.\n\nWhen emitted by a harness, `summary` and `summary_model`\nare populated so the runner can persist a compaction item for\nsession resume. When emitted by the server's explicit\n`/compact` path, those fields are `None`.",
        "properties": {
          "compacted_messages": {
            "anyOf": [
              {
                "items": {
                  "additionalProperties": true,
                  "type": "object"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Compacted Messages"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "summary": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Text summary of the compacted conversation, or `None` for server-side compaction (already persisted).",
            "title": "Summary"
          },
          "summary_model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Model used for summarization, or `None` if truncation-based or server-side.",
            "title": "Summary Model"
          },
          "total_tokens": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Tiktoken estimate of the post-compaction message context size, e.g. `8421`. Used by clients to update the context-ring immediately without waiting for the next `response.completed` usage report. `None` when token counting is unavailable.",
            "title": "Total Tokens"
          },
          "type": {
            "const": "response.compaction.completed",
            "description": "Always `\"response.compaction.completed\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type"
        ],
        "title": "CompactionCompletedEvent",
        "type": "object"
      },
      "CompactionData": {
        "description": "Data payload for a compaction summary item.\n\nStored as a conversation item of `type=\"compaction\"`.\nThe summary covers all items from the start of the\nconversation (or the previous compaction item) through\nthe item identified by `last_item_id`.",
        "properties": {
          "compacted_messages": {
            "anyOf": [
              {
                "items": {
                  "additionalProperties": true,
                  "type": "object"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Compacted Messages"
          },
          "last_item_id": {
            "description": "The item ID (inclusive) of the last conversation item covered by this summary, e.g. `\"msg_abc123\"`. Items at positions <= this item are summarized and do not need to be loaded for prompt construction.",
            "title": "Last Item Id",
            "type": "string"
          },
          "model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The model used to generate the summary, e.g. `\"openai/gpt-4o\"`.",
            "title": "Model"
          },
          "summary": {
            "description": "The LLM-generated summary text covering all conversation items up through `last_item_id`, e.g. `\"User asked to analyze a dataset. Agent loaded data.csv and computed statistics.\"`.",
            "title": "Summary",
            "type": "string"
          },
          "token_count": {
            "description": "Approximate token count of the summary text, for budget tracking, e.g. `342`.",
            "title": "Token Count",
            "type": "integer"
          },
          "window_id": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Window Id"
          }
        },
        "required": [
          "summary",
          "last_item_id",
          "token_count"
        ],
        "title": "CompactionData",
        "type": "object"
      },
      "CompactionFailedEvent": {
        "description": "Conversation history compaction failed.\n\nEmitted by `omnigent/server/routes/sessions.py` when\n`compact_conversation_now()` raises. Clients that rendered a\n\"Compacting…\" spinner on `CompactionInProgressEvent`\nshould dismiss it without leaving a permanent marker, since the\nconversation history was not modified.",
        "properties": {
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.compaction.failed",
            "description": "Always `\"response.compaction.failed\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type"
        ],
        "title": "CompactionFailedEvent",
        "type": "object"
      },
      "CompactionInProgressEvent": {
        "description": "Conversation history is being compacted.\n\nEmitted by `omnigent/runtime/compaction.py` while a\ncompaction step runs so clients can render a \"summarizing\nhistory…\" indicator. Wire shape matches `compaction.py:765`.",
        "properties": {
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.compaction.in_progress",
            "description": "Always `\"response.compaction.in_progress\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type"
        ],
        "title": "CompactionInProgressEvent",
        "type": "object"
      },
      "CompletedEvent": {
        "description": "Terminal event for a successfully completed turn.\n\nCarries the final\n`omnigent.server.schemas.ResponseObject`.",
        "properties": {
          "response": {
            "$ref": "#/components/schemas/ResponseObject",
            "description": "The final response object with `status=\"completed\"`."
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.completed",
            "description": "Always `\"response.completed\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "response"
        ],
        "title": "CompletedEvent",
        "type": "object"
      },
      "ConversationDeleted": {
        "description": "Confirmation payload returned after deleting a conversation.",
        "properties": {
          "deleted": {
            "default": true,
            "description": "Always `True`.",
            "title": "Deleted",
            "type": "boolean"
          },
          "id": {
            "description": "ID of the deleted conversation, e.g. `\"conv_abc123\"`.",
            "title": "Id",
            "type": "string"
          },
          "object": {
            "default": "conversation.deleted",
            "description": "Fixed resource type, always `\"conversation.deleted\"`.",
            "title": "Object",
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "ConversationDeleted",
        "type": "object"
      },
      "ConversationItem": {
        "description": "A persisted item with a store-assigned ID.",
        "properties": {
          "created_at": {
            "description": "Unix epoch timestamp of creation.",
            "title": "Created At",
            "type": "integer"
          },
          "created_by": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Identity of the human actor who authored this item, or `None` for agent/tool/system items and single-user mode. Lets owner and collaborator messages be distinguished.",
            "title": "Created By"
          },
          "data": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/MessageData"
              },
              {
                "$ref": "#/components/schemas/FunctionCallData"
              },
              {
                "$ref": "#/components/schemas/FunctionCallOutputData"
              },
              {
                "$ref": "#/components/schemas/ErrorData"
              },
              {
                "$ref": "#/components/schemas/ReasoningData"
              },
              {
                "$ref": "#/components/schemas/CompactionData"
              },
              {
                "$ref": "#/components/schemas/NativeToolData"
              },
              {
                "$ref": "#/components/schemas/ResourceEventData"
              },
              {
                "$ref": "#/components/schemas/RoutingDecisionData"
              },
              {
                "$ref": "#/components/schemas/SlashCommandData"
              },
              {
                "$ref": "#/components/schemas/TerminalCommandData"
              }
            ],
            "description": "The typed data payload (MessageData, etc.).",
            "title": "Data"
          },
          "id": {
            "description": "Store-assigned item ID, e.g. `\"msg_abc123\"`.",
            "title": "Id",
            "type": "string"
          },
          "response_id": {
            "description": "The task/response ID this item belongs to.",
            "title": "Response Id",
            "type": "string"
          },
          "status": {
            "description": "Item status, e.g. `\"completed\"`.",
            "title": "Status",
            "type": "string"
          },
          "type": {
            "description": "Item type, e.g. `\"message\"`, `\"function_call\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "id",
          "type",
          "status",
          "response_id",
          "created_at",
          "data"
        ],
        "title": "ConversationItem",
        "type": "object"
      },
      "ConversationRef": {
        "description": "Lightweight reference to a conversation, used in request and\nresponse bodies where only the conversation ID is needed.",
        "properties": {
          "id": {
            "description": "Conversation identifier, e.g. `\"conv_abc123\"`.",
            "title": "Id",
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "ConversationRef",
        "type": "object"
      },
      "CopyFilesRequest": {
        "description": "Request to copy files from a lineage ancestor into a session.\n\nThe destination session is the path parameter; `source_session_id`\nmust be a STRICT ancestor of the destination up its\n`parent_conversation_id` chain (spawn lineage) — the destination may\nnot name itself as the source. The copy creates new child-scoped rows —\nit does not grant cross-session read access.",
        "properties": {
          "file_ids": {
            "description": "Non-empty, unique ids of the source-owned files to copy, e.g. `[\"file_abc123\"]`.",
            "items": {
              "minLength": 1,
              "type": "string"
            },
            "minItems": 1,
            "title": "File Ids",
            "type": "array",
            "uniqueItems": true
          },
          "source_session_id": {
            "description": "Session that owns the source files, e.g. `\"conv_parent\"`. Must be a strict ancestor of the destination.",
            "title": "Source Session Id",
            "type": "string"
          }
        },
        "required": [
          "source_session_id",
          "file_ids"
        ],
        "title": "CopyFilesRequest",
        "type": "object"
      },
      "CreateDefaultPolicyRequest": {
        "description": "Request body for `POST /v1/policies`.",
        "properties": {
          "factory_params": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional dict of kwargs passed to the handler when it is a factory function. Only valid for `type=\"python\"`, e.g. `{\"limit\": 10}`.",
            "title": "Factory Params"
          },
          "handler": {
            "description": "Dotted import path (python) or HTTPS URL (url), e.g. `\"github_mcp_policy.block_non_misc_push\"` or `\"https://example.com/policies/eval\"`.",
            "title": "Handler",
            "type": "string"
          },
          "name": {
            "description": "Human-readable policy name. Must be globally unique, e.g. `\"block_non_feature_branch_push\"`.",
            "title": "Name",
            "type": "string"
          },
          "type": {
            "description": "Handler discriminator: `\"python\"`, `\"url\"`,",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "name",
          "type",
          "handler"
        ],
        "title": "CreateDefaultPolicyRequest",
        "type": "object"
      },
      "CreateDirectoryRequest": {
        "description": "Request body for `POST /v1/hosts/{host_id}/directories`.",
        "properties": {
          "path": {
            "description": "Absolute path of the directory to create on the host machine, e.g. `\"/Users/corey/projects/new-app\"`, or a tilde-prefixed path (`\"~/scratch\"`) the host expands against its own process owner. Missing parents are created.",
            "title": "Path",
            "type": "string"
          }
        },
        "required": [
          "path"
        ],
        "title": "CreateDirectoryRequest",
        "type": "object"
      },
      "CreateProjectRequest": {
        "additionalProperties": false,
        "description": "Request body for `POST /v1/projects`.",
        "properties": {
          "config": {
            "additionalProperties": true,
            "description": "Optional default session settings (opaque JSON object). Omitted / empty stores no defaults.",
            "title": "Config",
            "type": "object"
          },
          "name": {
            "description": "Human-readable project name. Trimmed; must be non-empty and at most 100 characters; unique among the caller's projects.",
            "title": "Name",
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "title": "CreateProjectRequest",
        "type": "object"
      },
      "CreateSessionPolicyRequest": {
        "description": "Request body for `POST /v1/sessions/{session_id}/policies`.",
        "properties": {
          "factory_params": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional dict of kwargs passed to the handler when it is a factory function. Only valid for `type=\"python\"`, e.g. `{\"limit\": 10}`.",
            "title": "Factory Params"
          },
          "handler": {
            "description": "Dotted import path (python) or HTTPS URL (url), e.g. `\"github_mcp_policy.block_non_misc_push\"` or `\"https://example.com/policies/eval\"`.",
            "title": "Handler",
            "type": "string"
          },
          "name": {
            "description": "Human-readable policy name. Must be unique within the session, e.g. `\"block_non_feature_branch_push\"`.",
            "title": "Name",
            "type": "string"
          },
          "type": {
            "description": "Handler discriminator: `\"python\"` or `\"url\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "name",
          "type",
          "handler"
        ],
        "title": "CreateSessionPolicyRequest",
        "type": "object"
      },
      "CreatedEvent": {
        "description": "Initial event emitted at the start of every streaming response.\n\nCarries the freshly-allocated\n`omnigent.server.schemas.ResponseObject` (status will\nbe `\"queued\"` or `\"in_progress\"` depending on whether the\ntask started immediately).",
        "properties": {
          "response": {
            "$ref": "#/components/schemas/ResponseObject",
            "description": "The newly-allocated response object."
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.created",
            "description": "Always `\"response.created\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "response"
        ],
        "title": "CreatedEvent",
        "type": "object"
      },
      "DailyCost": {
        "description": "One day's LLM spend for the daily timeline chart.",
        "properties": {
          "cost_usd": {
            "default": 0,
            "title": "Cost Usd",
            "type": "number"
          },
          "day": {
            "title": "Day",
            "type": "string"
          }
        },
        "required": [
          "day"
        ],
        "title": "DailyCost",
        "type": "object"
      },
      "ElicitationRequestEvent": {
        "description": "Synchronous request for a decision from upstream.\n\nEmitted by Omnigent (or, under the new contract, by a harness)\nwhen the LLM / a tool / a policy needs a verdict before\nproceeding. The consumer replies via\n`POST /v1/sessions/{session_id}/events` with\n`type == \"approval\"` and\n`omnigent.server.schemas.ElicitationResult` fields in\n`data`. This preserves MCP request/reply correlation by id\nwithout threading elicitations through PATCH.\n\nWire shape matches the existing emit at\n`omnigent/runtime/policies/approval.py:175`.",
        "properties": {
          "elicitation_id": {
            "description": "Unique correlation id for this request — appears in the consumer's approval event payload, e.g. `\"elicit_abc123\"`.",
            "title": "Elicitation Id",
            "type": "string"
          },
          "method": {
            "const": "elicitation/create",
            "default": "elicitation/create",
            "description": "MCP method literal — always `\"elicitation/create\"` (the value of `_MCP_ELICITATION_METHOD` in `omnigent/runtime/policies/approval.py`).",
            "title": "Method",
            "type": "string"
          },
          "params": {
            "$ref": "#/components/schemas/ElicitationRequestParams",
            "description": "The MCP-shaped params block carrying the prompt and (form-mode only) the requested schema."
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.elicitation_request",
            "description": "Always `\"response.elicitation_request\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "elicitation_id",
          "params"
        ],
        "title": "ElicitationRequestEvent",
        "type": "object"
      },
      "ElicitationRequestParams": {
        "additionalProperties": true,
        "description": "Inner `params` block of a `ElicitationRequestEvent`.\n\nThe standard fields (`mode`, `message`, `requestedSchema`,\n`url`) mirror MCP's `ElicitRequestFormParams` /\n`ElicitRequestUrlParams` byte-for-byte (Principle 8 — adopt\nMCP's wire shape verbatim where it overlaps). The\nAP-specific extensions (`phase`, `policy_name`,\n`content_preview`, `target_session_id`) carry policy-engine\ncontext and mirrored-child routing for the consumer's renderer;\nMCP's `extra=\"allow\"` config permits them under the same params\nblock. Wire shape matches\n`omnigent/runtime/policies/approval.py:175`.",
        "properties": {
          "content_preview": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Truncated preview of the underlying request payload (≤1024 chars in current AP), for the consumer's renderer.",
            "title": "Content Preview"
          },
          "message": {
            "description": "Human-readable prompt the consumer renders, e.g. `\"Approve running 'rm -rf /tmp/cache'?\"`.",
            "title": "Message",
            "type": "string"
          },
          "mode": {
            "default": "form",
            "description": "MCP-standard discriminator. `\"form\"` collects structured input via `requestedSchema`; `\"url\"` directs upstream to an external URL for OAuth / out-of-band interaction.",
            "enum": [
              "form",
              "url"
            ],
            "title": "Mode",
            "type": "string"
          },
          "phase": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Omnigent policy-engine phase the elicitation belongs to, e.g. `\"pre_tool_use\"`.",
            "title": "Phase"
          },
          "policy_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Omnigent policy that triggered the elicitation, e.g. `\"approve_shell_commands\"`.",
            "title": "Policy Name"
          },
          "requestedSchema": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "JSON-Schema dict for form mode (or `None` for url mode). camelCase preserved per MCP spec, e.g. `{\"type\": \"object\", \"properties\": {\"approve\": {\"type\": \"boolean\"}}}`.",
            "title": "Requestedschema"
          },
          "target_session_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "AP session whose resolve endpoint owns this elicitation, e.g. `\"conv_child123\"`. Present when a child/sub-agent prompt is mirrored into an ancestor stream; `None` means resolve against the current session.",
            "title": "Target Session Id"
          },
          "url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "External URL for url mode (or `None` for form mode), e.g. `\"https://oauth.example.com/authorize?...\"`.",
            "title": "Url"
          }
        },
        "required": [
          "message"
        ],
        "title": "ElicitationRequestParams",
        "type": "object"
      },
      "ElicitationResolvedEvent": {
        "description": "Signal that a previously-published elicitation is no longer\noutstanding, even though no UI `approval` verdict was\ndelivered through `POST /v1/sessions/{id}/events`.\n\nEmitted by the runner when its own `_pending_approvals`\nFuture is popped without a verdict (the runner's wait timed\nout, the turn was cancelled, the harness exited) so the AP\nserver's `omnigent.runtime.pending_elicitations`\nindex can decrement the sidebar badge in lockstep with the\nunderlying awaiter's lifecycle. Without this signal, the AP\nserver has no way to learn that the prompt is dead and the\nbadge stays stuck.\n\nIdempotent on the consumer side: the Omnigent server's index\ndecrement is a no-op when the id isn't tracked, so the\nrunner can fire-and-forget on every Future cleanup.",
        "properties": {
          "elicitation_id": {
            "description": "Correlation id of the elicitation being cleared, e.g. `\"elicit_abc123\"`. Must match the id of a prior `ElicitationRequestEvent`.",
            "title": "Elicitation Id",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.elicitation_resolved",
            "description": "Always `\"response.elicitation_resolved\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "elicitation_id"
        ],
        "title": "ElicitationResolvedEvent",
        "type": "object"
      },
      "ErrorData": {
        "description": "Data for a persisted error banner item.\n\nThese items mirror `response.error` events so clients can render\nthe same error banner after reconnect / refresh. They are listed in\n`NON_CONTENT_ITEM_TYPES` because they are operator-visible\ntranscript metadata, not content the next agent turn should receive.",
        "properties": {
          "code": {
            "description": "Stable error classifier, e.g. `\"native_terminal_start_failed\"`.",
            "title": "Code",
            "type": "string"
          },
          "message": {
            "description": "Human-readable error message, e.g. `\"Native Codex requires the 'codex' CLI on PATH.\"`.",
            "title": "Message",
            "type": "string"
          },
          "source": {
            "description": "Error source, e.g. `\"execution\"`.",
            "enum": [
              "llm",
              "execution",
              "tool"
            ],
            "title": "Source",
            "type": "string"
          }
        },
        "required": [
          "source",
          "code",
          "message"
        ],
        "title": "ErrorData",
        "type": "object"
      },
      "ErrorDetail": {
        "description": "Machine-readable error information attached to a failed response.",
        "properties": {
          "cause": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Optional one/two-sentence explanation of why it failed. Paired with `title`.",
            "title": "Cause"
          },
          "code": {
            "description": "Error code string, e.g. `\"server_error\"`, `\"invalid_input\"`.",
            "title": "Code",
            "type": "string"
          },
          "message": {
            "description": "Human-readable error description. Always populated; older clients render this verbatim.",
            "title": "Message",
            "type": "string"
          },
          "remediation": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Optional concrete next step to fix it, e.g. a command to run. `None` when there is no single clear fix.",
            "title": "Remediation"
          },
          "title": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Optional short headline naming what went wrong, e.g. `\"Claude Code can't run as root\"`. Present when the runner recognized the failure (see `omnigent.runner.launch_failure`); lets the UI show a clear card title instead of the raw `code`.",
            "title": "Title"
          }
        },
        "required": [
          "code",
          "message"
        ],
        "title": "ErrorDetail",
        "type": "object"
      },
      "ErrorEvent": {
        "description": "Non-recoverable error reported during the turn.\n\nEmitted from multiple sites in\n`omnigent/runtime/workflow.py` — terminal LLM failures\n(`_emit_llm_error_event`), execution timeouts\n(`_handle_execution_timeout`), and the agent-loop catch-all\n(`except Exception`). Wire shape matches those emits.",
        "properties": {
          "error": {
            "$ref": "#/components/schemas/RetryErrorDetail",
            "description": "Classified error description."
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "source": {
            "description": "Origin of the error — `\"llm\"` for LLM-call failures, `\"execution\"` for timeouts, `\"tool\"` for tool failures (currently emitted by retry exhaustion paths).",
            "enum": [
              "llm",
              "execution",
              "tool"
            ],
            "title": "Source",
            "type": "string"
          },
          "tool_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Tool identifier when `source == \"tool\"`; `None` for the other sources.",
            "title": "Tool Name"
          },
          "type": {
            "const": "response.error",
            "description": "Always `\"response.error\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "source",
          "error"
        ],
        "title": "ErrorEvent",
        "type": "object"
      },
      "FailedEvent": {
        "description": "Terminal event for a turn that ended with an error.\n\nCarries the final\n`omnigent.server.schemas.ResponseObject` whose\n`error` field describes the failure.",
        "properties": {
          "response": {
            "$ref": "#/components/schemas/ResponseObject",
            "description": "The final response object with `status=\"failed\"` and `error` populated."
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.failed",
            "description": "Always `\"response.failed\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "response"
        ],
        "title": "FailedEvent",
        "type": "object"
      },
      "FunctionCallData": {
        "description": "Data for a function_call item.\n\n**Parameters**\n\n- `agent` — Agent name. Serialized as `\"model\"` in JSON.",
        "properties": {
          "arguments": {
            "description": "JSON-encoded arguments string.",
            "title": "Arguments",
            "type": "string"
          },
          "call_id": {
            "description": "Unique call identifier from the LLM, e.g. `\"call_abc123\"`.",
            "title": "Call Id",
            "type": "string"
          },
          "model": {
            "title": "Model",
            "type": "string"
          },
          "name": {
            "description": "Tool function name, e.g. `\"search.web\"`.",
            "title": "Name",
            "type": "string"
          }
        },
        "required": [
          "model",
          "name",
          "arguments",
          "call_id"
        ],
        "title": "FunctionCallData",
        "type": "object"
      },
      "FunctionCallOutputData": {
        "description": "Data for a function_call_output item.",
        "properties": {
          "call_id": {
            "description": "The call_id this output corresponds to, e.g. `\"call_abc123\"`.",
            "title": "Call Id",
            "type": "string"
          },
          "output": {
            "description": "The tool's string result.",
            "title": "Output",
            "type": "string"
          }
        },
        "required": [
          "call_id",
          "output"
        ],
        "title": "FunctionCallOutputData",
        "type": "object"
      },
      "GrantPermissionRequest": {
        "description": "Request body for `PUT /v1/sessions/{id}/permissions`.",
        "properties": {
          "level": {
            "description": "Numeric permission level: `1` = read, `2` = edit, `3` = manage.",
            "maximum": 3,
            "minimum": 1,
            "title": "Level",
            "type": "integer"
          },
          "user_id": {
            "description": "The user to grant access to, e.g. `\"[email protected]\"` or `\"__public__\"` for public read access.",
            "title": "User Id",
            "type": "string"
          }
        },
        "required": [
          "user_id",
          "level"
        ],
        "title": "GrantPermissionRequest",
        "type": "object"
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "title": "Detail",
            "type": "array"
          }
        },
        "title": "HTTPValidationError",
        "type": "object"
      },
      "HeartbeatEvent": {
        "description": "Keepalive event emitted on a fixed cadence during streaming.\n\nLets consumers detect stalled producers via missed-interval\ntiming. Cadence is set by `_HEARTBEAT_INTERVAL_S` in\n`omnigent/runtime/workflow.py` (15 seconds at the time of\nwriting). Wire shape matches the existing emit at\n`omnigent/runtime/workflow.py:4636-4639`.\n\nPer `designs/SERVER_HARNESS_CONTRACT.md` §Heartbeats, the\nevent MAY carry timing metadata so consumers can do richer\ndead-detection than \"did anything arrive\":\n\n- `server_time` is the producer's wall-clock at emission,\n  letting consumers detect clock drift between producer and\n  consumer.\n- `last_event_seq` is the `sequence_number` of the most\n  recent NON-heartbeat event (or `None` when this is the\n  first heartbeat before any user-visible event), letting\n  consumers detect dropped events on reconnect.\n\nBoth fields are optional on the wire (`None` round-trips as\nomitted) so older AP→harness pairs that pre-date the field\naddition still parse cleanly.",
        "properties": {
          "last_event_seq": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Sequence number of the last non- heartbeat event seen on the same stream, e.g. `42`. `None` before any user-visible event has fired (first heartbeat of the turn, before deltas land), or when the producer chose not to populate it.",
            "title": "Last Event Seq"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "server_time": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "ISO 8601 UTC timestamp at emission, e.g. `\"2026-04-27T15:30:00Z\"`. `None` when the producer chose not to populate it (legacy emitters).",
            "title": "Server Time"
          },
          "type": {
            "const": "response.heartbeat",
            "description": "Always `\"response.heartbeat\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type"
        ],
        "title": "HeartbeatEvent",
        "type": "object"
      },
      "ImportItemInput": {
        "description": "One normalized existing Omnigent item received from the CLI.",
        "properties": {
          "data": {
            "additionalProperties": true,
            "title": "Data",
            "type": "object"
          },
          "response_id": {
            "maxLength": 64,
            "minLength": 1,
            "title": "Response Id",
            "type": "string"
          },
          "type": {
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "response_id",
          "data"
        ],
        "title": "ImportItemInput",
        "type": "object"
      },
      "ImportSessionRequest": {
        "description": "Request body for importing one local harness session.",
        "properties": {
          "external_session_id": {
            "maxLength": 128,
            "minLength": 1,
            "title": "External Session Id",
            "type": "string"
          },
          "force": {
            "default": false,
            "title": "Force",
            "type": "boolean"
          },
          "items": {
            "items": {
              "$ref": "#/components/schemas/ImportItemInput"
            },
            "maxItems": 100000,
            "minItems": 1,
            "title": "Items",
            "type": "array"
          },
          "source": {
            "enum": [
              "claude",
              "codex",
              "kimi",
              "kiro",
              "opencode",
              "pi",
              "qwen"
            ],
            "title": "Source",
            "type": "string"
          },
          "workspace": {
            "anyOf": [
              {
                "maxLength": 2048,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Workspace"
          }
        },
        "required": [
          "source",
          "external_session_id",
          "items"
        ],
        "title": "ImportSessionRequest",
        "type": "object"
      },
      "ImportSessionResponse": {
        "description": "Result of importing or locating one source session.",
        "properties": {
          "item_count": {
            "title": "Item Count",
            "type": "integer"
          },
          "session_id": {
            "title": "Session Id",
            "type": "string"
          },
          "status": {
            "const": "imported",
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "session_id",
          "status",
          "item_count"
        ],
        "title": "ImportSessionResponse",
        "type": "object"
      },
      "InProgressEvent": {
        "description": "Event emitted once the task transitions to in-progress.\n\nAlways follows `response.created` (and `response.queued`\nfor background tasks).",
        "properties": {
          "response": {
            "$ref": "#/components/schemas/ResponseObject",
            "description": "The response object with `status=\"in_progress\"`."
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.in_progress",
            "description": "Always `\"response.in_progress\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "response"
        ],
        "title": "InProgressEvent",
        "type": "object"
      },
      "IncompleteDetails": {
        "description": "Details explaining why a response is incomplete.",
        "properties": {
          "reason": {
            "description": "Reason the response stopped early, e.g. `\"max_output_tokens\"`, `\"max_tool_calls\"`.",
            "title": "Reason",
            "type": "string"
          }
        },
        "required": [
          "reason"
        ],
        "title": "IncompleteDetails",
        "type": "object"
      },
      "IncompleteEvent": {
        "description": "Terminal event for a turn that ended without completing\n(e.g. hit the iteration cap or token budget).",
        "properties": {
          "response": {
            "$ref": "#/components/schemas/ResponseObject",
            "description": "The final response object with `status=\"incomplete\"` and `incomplete_details` populated describing the reason."
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.incomplete",
            "description": "Always `\"response.incomplete\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "response"
        ],
        "title": "IncompleteEvent",
        "type": "object"
      },
      "LaunchRunnerRequest": {
        "description": "Request body for `POST /v1/hosts/{host_id}/runners`.",
        "properties": {
          "git": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/SessionGitOptions"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional git worktree options. In create mode the server creates a worktree for a new branch off `workspace` on the host and binds the runner to it (the fork-resume path; mirrors `POST /v1/sessions`). In bind mode (`existing_worktree=True`) `workspace` already IS a worktree — no worktree is created; `branch_name` is recorded as the session's `git_branch` for display and opt-in cleanup. `None` binds `workspace` directly. `host_id` is always present (it is in the path), so no host check is needed here."
          },
          "session_id": {
            "description": "Session to bind the new runner to, e.g. `\"conv_abc123\"`.",
            "title": "Session Id",
            "type": "string"
          },
          "workspace": {
            "description": "Absolute path on the host machine to use as the runner's working directory, e.g. `\"/Users/corey/projects/frontend\"`. When `git` is set, this is interpreted as the source repository directory and the runner starts in the created worktree instead.",
            "title": "Workspace",
            "type": "string"
          }
        },
        "required": [
          "session_id",
          "workspace"
        ],
        "title": "LaunchRunnerRequest",
        "type": "object"
      },
      "MCPServerSummary": {
        "description": "Safe subset of an MCP server's configuration for API exposure.\n\nHeader values are redacted (`\"[REDACTED]\"`) so callers can see\nwhich headers are configured without leaking the actual secrets.\n`env` is still fully excluded.",
        "properties": {
          "args": {
            "description": "Command-line arguments for `transport=\"stdio\"` servers, e.g. `[\"mcp-server-github\"]`. Empty list when unset.",
            "items": {
              "type": "string"
            },
            "title": "Args",
            "type": "array"
          },
          "command": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Executable path for `transport=\"stdio\"` servers, e.g. `\"uvx\"`. `None` for http servers.",
            "title": "Command"
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional free-text description from the spec, e.g. `\"GitHub MCP server\"`. `None` when unset.",
            "title": "Description"
          },
          "headers": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "HTTP headers for `transport=\"http\"` servers. Values are always `\"[REDACTED]\"`; only the key names are exposed.",
            "title": "Headers",
            "type": "object"
          },
          "name": {
            "description": "Server name as declared in the agent spec, e.g. `\"github\"`.",
            "title": "Name",
            "type": "string"
          },
          "transport": {
            "description": "Transport type — `\"stdio\"` or `\"http\"`.",
            "title": "Transport",
            "type": "string"
          },
          "url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "HTTP(S) endpoint URL for `transport=\"http\"` servers, e.g. `\"https://mcp.example.com/sse\"`. `None` for stdio servers.",
            "title": "Url"
          }
        },
        "required": [
          "name",
          "transport"
        ],
        "title": "MCPServerSummary",
        "type": "object"
      },
      "McpServerStartup": {
        "description": "One MCP server's startup state within a `session.mcp_startup` event.",
        "properties": {
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Failure detail when `status == \"failed\"`, e.g. `\"handshaking with MCP server failed\"`. `None` otherwise.",
            "title": "Error"
          },
          "status": {
            "description": "Latest startup state reported by the harness, mirroring Codex's `McpServerStartupState` enum.",
            "enum": [
              "starting",
              "ready",
              "failed",
              "cancelled"
            ],
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "status"
        ],
        "title": "McpServerStartup",
        "type": "object"
      },
      "MessageData": {
        "description": "Data for a message item (user or assistant).\n\n**Parameters**\n\n- `agent` — Agent name (required for assistant messages, absent for user). Serialized as `\"model\"` in JSON.",
        "properties": {
          "content": {
            "description": "Heterogeneous content blocks, e.g. `[{\"type\": \"input_text\", \"text\": \"Hello\"}]`.",
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Content",
            "type": "array"
          },
          "interrupted": {
            "default": false,
            "description": "`True` when an assistant message is a durable partial response from an interrupted external-native turn, e.g. Codex `turn/completed` with status `\"interrupted\"`. Defaults to `False` and is omitted from serialized payloads in that case.",
            "title": "Interrupted",
            "type": "boolean"
          },
          "is_meta": {
            "default": false,
            "description": "`True` for durable context that must be replayed to agents but hidden from user-facing transcripts, e.g. injected skill instructions. Defaults to `False` and is omitted from serialized payloads in that case.",
            "title": "Is Meta",
            "type": "boolean"
          },
          "model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Model"
          },
          "role": {
            "description": "`\"user\"` or `\"assistant\"`.",
            "enum": [
              "user",
              "assistant"
            ],
            "title": "Role",
            "type": "string"
          }
        },
        "required": [
          "role",
          "content"
        ],
        "title": "MessageData",
        "type": "object"
      },
      "ModelUsage": {
        "description": "Cumulative token/cost usage attributed to a single LLM model.\n\nOne value in the `usage_by_model` map on `SessionResponse` /\n`SessionUsageEvent`, keyed by the raw harness-reported model id\n(e.g. `\"claude-sonnet-4-6\"`, `\"databricks-gpt-5-5\"`). Counts are\nsummed over the session's subtree (itself + sub-agent descendants), so a\nparent folds in sub-agents that ran a different model. Token buckets\nmirror the flat per-session breakdown.",
        "properties": {
          "cache_creation_input_tokens": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Cumulative tokens written to the prompt cache, e.g. `2000`. `None` when not recorded.",
            "title": "Cache Creation Input Tokens"
          },
          "cache_read_input_tokens": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Cumulative tokens read from the prompt cache, e.g. `8000`. `None` when not recorded.",
            "title": "Cache Read Input Tokens"
          },
          "input_tokens": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Cumulative non-cached input (prompt) tokens for this model over the subtree, e.g. `12000`. `None` when not recorded.",
            "title": "Input Tokens"
          },
          "output_tokens": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Cumulative output (completion) tokens, e.g. `3400`. `None` when not recorded.",
            "title": "Output Tokens"
          },
          "total_cost_usd": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Cumulative USD spend attributed to this model, e.g. `0.42`. Present **only when this model's turns were priced** (same \"priced ⟺ key present\" contract as the session total); `None` when the model is unpriced, so the sum of priced per-model costs equals the session `total_cost_usd`.",
            "title": "Total Cost Usd"
          },
          "total_tokens": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Cumulative total tokens (counts cache buckets too, as the harness reports), e.g. `15400`. `None` when not recorded.",
            "title": "Total Tokens"
          }
        },
        "title": "ModelUsage",
        "type": "object"
      },
      "NativeModelOption": {
        "additionalProperties": true,
        "description": "One runner-owned native model-picker row.",
        "properties": {
          "defaultReasoningEffort": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Defaultreasoningeffort"
          },
          "displayName": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Displayname"
          },
          "id": {
            "title": "Id",
            "type": "string"
          },
          "isDefault": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Isdefault"
          },
          "model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Model"
          },
          "supportedReasoningEfforts": {
            "items": {
              "$ref": "#/components/schemas/NativeReasoningEffortOption"
            },
            "title": "Supportedreasoningefforts",
            "type": "array"
          }
        },
        "required": [
          "id"
        ],
        "title": "NativeModelOption",
        "type": "object"
      },
      "NativeReasoningEffortOption": {
        "additionalProperties": true,
        "description": "Reasoning-effort metadata advertised by a native model catalog.",
        "properties": {
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Description"
          },
          "reasoningEffort": {
            "title": "Reasoningeffort",
            "type": "string"
          }
        },
        "required": [
          "reasoningEffort"
        ],
        "title": "NativeReasoningEffortOption",
        "type": "object"
      },
      "NativeToolData": {
        "description": "A provider-native tool output item (e.g. `web_search_call`).\n\nThese are executed server-side by the LLM provider and returned\nas opaque dicts. Agent-plane persists and replays them so the\nLLM sees its own tool results on subsequent iterations.",
        "properties": {
          "item": {
            "additionalProperties": true,
            "description": "The raw dict from the Responses API output, e.g. `{\"type\": \"web_search_call\", \"id\": \"ws_abc\", \"status\": \"completed\", \"action\": {...}}`.",
            "title": "Item",
            "type": "object"
          }
        },
        "required": [
          "item"
        ],
        "title": "NativeToolData",
        "type": "object"
      },
      "OutputFileDoneEvent": {
        "description": "A streamed file output completed materializing.\n\nEmitted by `_emit_file_annotation_events` in\n`omnigent/runtime/workflow.py` once per file annotation in\nthe assistant's output. `filename` and `content_type` are\nonly populated when the originating annotation carried them.",
        "properties": {
          "content_type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "MIME content type if the annotation supplied one, e.g. `\"application/pdf\"`. `None` otherwise.",
            "title": "Content Type"
          },
          "file_id": {
            "description": "Identifier of the materialized file, e.g. `\"file_abc123\"`.",
            "title": "File Id",
            "type": "string"
          },
          "filename": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Original filename if the annotation supplied one, e.g. `\"report.pdf\"`. `None` otherwise.",
            "title": "Filename"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.output_file.done",
            "description": "Always `\"response.output_file.done\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "file_id"
        ],
        "title": "OutputFileDoneEvent",
        "type": "object"
      },
      "OutputItemDoneEvent": {
        "description": "A conversation output item completed during the turn.\n\nCarries any item type the conversation persists (message,\nfunction_call, function_call_output, reasoning, compaction,\nnative_tool, …). The `item` payload's wire shape merges\ncommon fields (`id`, `type`, `status`) with the\ntype-specific data fields — it is NOT nested as\n`{type, data}`.",
        "properties": {
          "item": {
            "additionalProperties": true,
            "description": "The completed item dict. Heterogeneous and item-type-specific; see `omnigent/entities/conversation.py` for the per-type `*Data` shapes that drive serialization. Example for a function_call item: `{\"id\": \"fc_abc123\", \"type\": \"function_call\", \"status\": \"action_required\", \"name\": \"search.web\", \"arguments\": \"{\\\"q\\\": \\\"foo\\\"}\", \"call_id\": \"call_xyz\"}`.",
            "title": "Item",
            "type": "object"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.output_item.done",
            "description": "Always `\"response.output_item.done\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "item"
        ],
        "title": "OutputItemDoneEvent",
        "type": "object"
      },
      "OutputTextDeltaEvent": {
        "description": "Incremental assistant-text token emitted during streaming.\n\nWire shape matches the existing raw-dict emit at\n`omnigent/runtime/workflow.py:1352-1356`.",
        "properties": {
          "delta": {
            "description": "The text fragment for this chunk, e.g. `\"Hello\"`.",
            "title": "Delta",
            "type": "string"
          },
          "final": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Optional provider completion marker for the message.",
            "title": "Final"
          },
          "index": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "0-based chunk order within the message, e.g. `3`. Used to suppress repeated chunks; `None` for in-process streaming.",
            "title": "Index"
          },
          "message_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "For native terminal streaming, the provider's stable per-message id, e.g. `\"2ca51d97-2f0f-493a-aed7-85a5b56c5747\"`. `None` for ordinary in-process task streaming, where deltas group by the active response.",
            "title": "Message Id"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.output_text.delta",
            "description": "Always `\"response.output_text.delta\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "delta"
        ],
        "title": "OutputTextDeltaEvent",
        "type": "object"
      },
      "PaginatedList": {
        "description": "A paginated list response following cursor-based pagination.",
        "properties": {
          "data": {
            "description": "Page of results. Items are heterogeneous (`ResponseObject`, `ConversationObject`, `FileObject`, or dicts) and list is invariant, so no single concrete type satisfies all callers.",
            "items": {},
            "title": "Data",
            "type": "array"
          },
          "first_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "ID of the first item in the page, or `None` if the page is empty, e.g. `\"resp_abc123\"`.",
            "title": "First Id"
          },
          "has_more": {
            "default": false,
            "description": "Whether more items exist beyond this page.",
            "title": "Has More",
            "type": "boolean"
          },
          "last_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "ID of the last item in the page, or `None` if the page is empty, e.g. `\"resp_xyz789\"`.",
            "title": "Last Id"
          },
          "object": {
            "default": "list",
            "description": "Fixed resource type, always `\"list\"`.",
            "title": "Object",
            "type": "string"
          }
        },
        "title": "PaginatedList",
        "type": "object"
      },
      "PermissionObject": {
        "description": "API representation of a session permission grant.",
        "properties": {
          "conversation_id": {
            "description": "The session, e.g. `\"conv_abc123\"`.",
            "title": "Conversation Id",
            "type": "string"
          },
          "level": {
            "description": "Numeric permission level (1=read, 2=edit, 3=manage).",
            "title": "Level",
            "type": "integer"
          },
          "user_id": {
            "description": "The grantee, e.g. `\"[email protected]\"`.",
            "title": "User Id",
            "type": "string"
          }
        },
        "required": [
          "user_id",
          "conversation_id",
          "level"
        ],
        "title": "PermissionObject",
        "type": "object"
      },
      "PolicyDeniedEvent": {
        "description": "Signal that a policy DENY was enforced on a native harness turn.\n\nA native harness (Claude Code, Codex, ...) routes each tool call and\nprompt through Omnigent's policy engine via the vendor command-hook\n(`POST /v1/sessions/{id}/policies/evaluate`). The DENY verdict is\nreturned synchronously to that hook, so unlike the SDK/wrap path there is\nno stream-visible signal that a native action was blocked — only the\n*effect* (the blocked tool never runs). This event surfaces the decision\nitself on the session stream so observers (the web UI, the capability\nbench) can see a native DENY as a positive signal rather than infer it\nfrom an absence.\n\nFire-and-forget and observational: it does not gate the turn (the hook\nresponse already did that) and carries no correlation id.",
        "properties": {
          "conversation_id": {
            "description": "Session/conversation id the DENY applies to, e.g. `\"conv_abc123\"`.",
            "title": "Conversation Id",
            "type": "string"
          },
          "phase": {
            "default": "",
            "description": "The policy phase the DENY landed on, e.g. `\"tool_call\"`.",
            "title": "Phase",
            "type": "string"
          },
          "reason": {
            "default": "",
            "description": "Human-readable deny reason from the deciding policy, e.g. `\"Blocked by policy.\"`.",
            "title": "Reason",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.policy_denied",
            "description": "Always `\"response.policy_denied\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "conversation_id"
        ],
        "title": "PolicyDeniedEvent",
        "type": "object"
      },
      "PolicySummary": {
        "description": "Safe subset of a policy's spec for API exposure.\n\nExposes the policy name, type, and phases so the UI can\ndisplay which guardrails are active on an agent. The full\npolicy body (prompt text, callable path, label conditions)\nis intentionally excluded — this is a summary for display,\nnot a full spec.",
        "properties": {
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Short detail string about the policy implementation. For function policies: the callable dotted path. For prompt policies: the first line of the prompt. `None` when not available.",
            "title": "Description"
          },
          "name": {
            "description": "Policy name as declared in the agent spec, e.g. `\"block_long_sleep\"`.",
            "title": "Name",
            "type": "string"
          },
          "on": {
            "description": "List of phase selectors the policy fires on, e.g. `[\"tool_call\"]` or `[\"request\", \"response\"]`.",
            "items": {
              "type": "string"
            },
            "title": "On",
            "type": "array"
          },
          "type": {
            "description": "Policy type discriminator — `\"function\"` or `\"prompt\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "name",
          "type",
          "on"
        ],
        "title": "PolicySummary",
        "type": "object"
      },
      "PresenceViewer": {
        "description": "One user currently viewing a session (holding its SSE stream open).",
        "properties": {
          "idle": {
            "default": false,
            "description": "Whether every stream the user holds reports an idle (backgrounded) tab. The web greys idle viewers' avatars.",
            "title": "Idle",
            "type": "boolean"
          },
          "joined_at": {
            "description": "ISO 8601 UTC timestamp of when the user joined, e.g. `\"2026-06-10T17:00:00Z\"`. Stable across reconnects within the server's leave-grace window.",
            "title": "Joined At",
            "type": "string"
          },
          "user_id": {
            "description": "The viewer's authenticated identity, e.g. `\"[email protected]\"`. Never the reserved single-user `\"local\"` sentinel — presence only tracks distinct human actors (see `attribution_user`).",
            "title": "User Id",
            "type": "string"
          }
        },
        "required": [
          "user_id",
          "joined_at"
        ],
        "title": "PresenceViewer",
        "type": "object"
      },
      "QueuedEvent": {
        "description": "Optional event emitted between `created` and `in_progress`\nfor background tasks that are queued before they start.\n\nForeground streaming responses skip this event.",
        "properties": {
          "response": {
            "$ref": "#/components/schemas/ResponseObject",
            "description": "The response object with `status=\"queued\"`."
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.queued",
            "description": "Always `\"response.queued\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "response"
        ],
        "title": "QueuedEvent",
        "type": "object"
      },
      "ReadStatePutRequest": {
        "additionalProperties": false,
        "description": "Request body for `PUT /v1/sessions/{session_id}/read-state`.\n\nSets the *calling user's* read tracking for one session. Mirrors the\ntwo values the web client keeps per session: a \"last seen\" wall-clock\nbaseline (seconds since epoch) and an explicit \"marked unread\"\noverride. The unread dot shows when `updated_at > last_seen` and the\nsession is finished; `unread` separately pins the override so the\nthread the user is *viewing* (or a running one) still surfaces the dot\nwhere the automatic \"seen\" logic would otherwise suppress it.",
        "properties": {
          "last_seen": {
            "description": "Wall-clock baseline in seconds, e.g. `1717000000`. Marking seen sets this to \"now\"; marking unread pins it to `updated_at - 1` so the row reads unseen.",
            "title": "Last Seen",
            "type": "integer"
          },
          "unread": {
            "description": "Whether this session is explicitly flagged unread for the caller.",
            "title": "Unread",
            "type": "boolean"
          }
        },
        "required": [
          "last_seen",
          "unread"
        ],
        "title": "ReadStatePutRequest",
        "type": "object"
      },
      "ReasoningData": {
        "description": "Data for a reasoning item.\n\n**Parameters**\n\n- `agent` — Agent name. Serialized as `\"model\"` in JSON.",
        "properties": {
          "content": {
            "anyOf": [
              {
                "items": {
                  "additionalProperties": {
                    "type": "string"
                  },
                  "type": "object"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "Raw reasoning content blocks, or `None` if redacted.",
            "title": "Content"
          },
          "encrypted_content": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Encrypted reasoning content, or `None`.",
            "title": "Encrypted Content"
          },
          "model": {
            "title": "Model",
            "type": "string"
          },
          "summary": {
            "description": "Summary text blocks, e.g. `[{\"type\": \"summary_text\", \"text\": \"...\"}]`.",
            "items": {
              "additionalProperties": {
                "type": "string"
              },
              "type": "object"
            },
            "title": "Summary",
            "type": "array"
          }
        },
        "required": [
          "model",
          "summary"
        ],
        "title": "ReasoningData",
        "type": "object"
      },
      "ReasoningStartedEvent": {
        "description": "Marker emitted once when a reasoning block begins.\n\nFired even when the reasoning content itself is encrypted /\nredacted (so no delta events follow), letting clients render\na \"thinking…\" indicator regardless of provider verification\nstatus. Wire shape matches `omnigent/runtime/workflow.py:1350`.",
        "properties": {
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.reasoning.started",
            "description": "Always `\"response.reasoning.started\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type"
        ],
        "title": "ReasoningStartedEvent",
        "type": "object"
      },
      "ReasoningSummaryTextDeltaEvent": {
        "description": "Incremental reasoning-summary token.\n\nEmitted when `reasoning.summary` is configured on the\nrequest. Wire shape matches\n`omnigent/runtime/workflow.py:1370-1373`.",
        "properties": {
          "delta": {
            "description": "The summary text fragment, e.g. `\"Will use the search tool to gather context.\"`.",
            "title": "Delta",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.reasoning_summary_text.delta",
            "description": "Always `\"response.reasoning_summary_text.delta\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "delta"
        ],
        "title": "ReasoningSummaryTextDeltaEvent",
        "type": "object"
      },
      "ReasoningTextDeltaEvent": {
        "description": "Incremental reasoning-text token (full chain-of-thought).\n\nOnly emitted by providers that surface reasoning content\n(e.g. OpenAI o-series with appropriate verification). Wire\nshape matches `omnigent/runtime/workflow.py:1358-1364`.",
        "properties": {
          "delta": {
            "description": "The reasoning text fragment, e.g. `\"Considering the user's intent...\"`.",
            "title": "Delta",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.reasoning_text.delta",
            "description": "Always `\"response.reasoning_text.delta\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "delta"
        ],
        "title": "ReasoningTextDeltaEvent",
        "type": "object"
      },
      "ResourceEventData": {
        "description": "Data payload for a persisted resource lifecycle event.\n\nThese items are written to the conversation store when a\nsession resource is created or deleted, so reconnecting\nclients can discover resource history without replaying the\nlive SSE stream.  The agent loop filters them out of the\nLLM's message context (they are metadata, not conversation\ncontent).",
        "properties": {
          "event_type": {
            "description": "The SSE event type literal, e.g. `\"session.resource.created\"` or `\"session.resource.deleted\"`.",
            "title": "Event Type",
            "type": "string"
          },
          "resource": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Full resource object dict for `created` events. `None` for `deleted` events.",
            "title": "Resource"
          },
          "resource_id": {
            "description": "Opaque id of the affected resource, e.g. `\"terminal_bash_s1\"` or `\"file_abc123\"`.",
            "title": "Resource Id",
            "type": "string"
          },
          "resource_type": {
            "description": "Kind of resource, e.g. `\"terminal\"`, `\"file\"`, `\"environment\"`.",
            "title": "Resource Type",
            "type": "string"
          }
        },
        "required": [
          "event_type",
          "resource_id",
          "resource_type"
        ],
        "title": "ResourceEventData",
        "type": "object"
      },
      "ResponseObject": {
        "description": "API representation of a response (task execution result).",
        "properties": {
          "background": {
            "default": false,
            "description": "Whether this response was created as a background task.",
            "title": "Background",
            "type": "boolean"
          },
          "completed_at": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Unix epoch timestamp of completion, or `None` if not yet complete.",
            "title": "Completed At"
          },
          "conversation": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ConversationRef"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Reference to the owning conversation."
          },
          "created_at": {
            "description": "Unix epoch timestamp of creation.",
            "title": "Created At",
            "type": "integer"
          },
          "error": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ErrorDetail"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Error details if the response failed."
          },
          "id": {
            "description": "Unique response identifier, e.g. `\"resp_abc123\"`.",
            "title": "Id",
            "type": "string"
          },
          "incomplete_details": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/IncompleteDetails"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Details if the response is incomplete (e.g. hit token limit)."
          },
          "instructions": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Per-request system instructions override, or `None`.",
            "title": "Instructions"
          },
          "model": {
            "description": "Agent name that produced this response, e.g. `\"research-agent\"`.",
            "title": "Model",
            "type": "string"
          },
          "object": {
            "default": "response",
            "description": "Fixed resource type, always `\"response\"`.",
            "title": "Object",
            "type": "string"
          },
          "output": {
            "description": "Heterogeneous output items (messages, reasoning, function_calls) serialized as dicts; shape varies by item type. Empty for non-completed responses.",
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Output",
            "type": "array"
          },
          "previous_response_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "ID of the prior response in the conversation thread, or `None` for the first turn.",
            "title": "Previous Response Id"
          },
          "reasoning": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "string"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Reasoning configuration, e.g. `{\"effort\": \"medium\"}`.",
            "title": "Reasoning"
          },
          "status": {
            "description": "Lifecycle status, one of `\"queued\"`, `\"in_progress\"`, `\"completed\"`, `\"failed\"`, `\"incomplete\"`, `\"cancelled\"`.",
            "title": "Status",
            "type": "string"
          },
          "store": {
            "default": true,
            "description": "Whether this response is persisted. Always `True`.",
            "title": "Store",
            "type": "boolean"
          },
          "usage": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Usage"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Token usage statistics, or `None` if not yet available."
          }
        },
        "required": [
          "id",
          "status",
          "model",
          "created_at"
        ],
        "title": "ResponseObject",
        "type": "object"
      },
      "RetryErrorDetail": {
        "description": "Error block carried by `RetryEvent` and `ErrorEvent`.\n\nMirrors the shape that `llm_retry.py` and `tool_retry.py`\nemit today — flat `code` / `message` plus an optional\n`detail` for provider-specific structured fields.",
        "properties": {
          "code": {
            "description": "Stable error classifier, e.g. `\"timeout\"`, `\"rate_limit\"`.",
            "title": "Code",
            "type": "string"
          },
          "detail": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Optional provider-specific structured fields (e.g. `{\"status_code\": 429, \"retry_after\": 5}`); `None` when the classifier had no extra context.",
            "title": "Detail"
          },
          "message": {
            "description": "Human-readable summary, e.g. `\"Connection timed out after 30s\"`.",
            "title": "Message",
            "type": "string"
          }
        },
        "required": [
          "code",
          "message"
        ],
        "title": "RetryErrorDetail",
        "type": "object"
      },
      "RetryEvent": {
        "description": "A retryable failure was caught and a retry is scheduled.\n\nEmitted by `omnigent/runtime/llm_retry.py` (LLM calls)\nand `omnigent/runtime/tool_retry.py` (tool calls) before\nsleeping for the backoff delay. Wire shape matches\n`llm_retry.py:329-340` and `tool_retry.py:168-180`.",
        "properties": {
          "attempt": {
            "description": "1-based count of the upcoming attempt (i.e. attempt that will run AFTER this delay), e.g. `2` for the first retry.",
            "title": "Attempt",
            "type": "integer"
          },
          "delay_seconds": {
            "description": "Seconds the producer will sleep before retrying, rounded to two decimals, e.g. `1.5`.",
            "title": "Delay Seconds",
            "type": "number"
          },
          "error": {
            "$ref": "#/components/schemas/RetryErrorDetail",
            "description": "Classified error description for the failure being retried."
          },
          "max_attempts": {
            "description": "Total tries allowed by the retry policy, e.g. `3`. Lets clients render \"attempt 2 of 3\".",
            "title": "Max Attempts",
            "type": "integer"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "source": {
            "description": "Origin of the retried failure — `\"llm\"` for LLM-call retries, `\"tool\"` for tool-call retries.",
            "enum": [
              "llm",
              "tool"
            ],
            "title": "Source",
            "type": "string"
          },
          "tool_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Tool identifier when `source == \"tool\"`, e.g. `\"search.web\"`. `None` for LLM retries.",
            "title": "Tool Name"
          },
          "type": {
            "const": "response.retry",
            "description": "Always `\"response.retry\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "source",
          "attempt",
          "max_attempts",
          "delay_seconds",
          "error"
        ],
        "title": "RetryEvent",
        "type": "object"
      },
      "RoutingDecisionData": {
        "description": "Data payload for an intelligent model-router decision item.\n\nEmitted by the server-side smart routing path at the START of an\nadvised turn and persisted\nas a display-only transcript item so the model the router chose shows\nin the conversation flow the moment the turn begins. Listed in\n`NON_CONTENT_ITEM_TYPES` so the agent loop's history filter\nskips it — the brain never sees (or answers) its own router note. The\nrunner's harness-input builder also drops every non\nmessage/function_call type, a second guarantee it stays out of the\nmodel's context.",
        "properties": {
          "agent": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Agent"
          },
          "applied": {
            "description": "`True` when the brain actually ran on `model` this turn (optimize mode, no user pin); `False` when the router only WOULD have picked it (advise/shadow mode, or a user model pin won) — the UI renders \"would have picked\".",
            "title": "Applied",
            "type": "boolean"
          },
          "attempted_override": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Model the spawning agent asked for and the router overrode, e.g. `\"databricks-gpt-5-5\"` — an LLM-supplied `args.model` on a child session, or a native spawn's own `requested_model`. `None` when nothing was asked for, or when the router's pick names the same arm as the ask.",
            "title": "Attempted Override"
          },
          "decision_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Router decision identifier, e.g. `\"3f1c…\"`. Correlates the transcript item with the routing telemetry event and the child-sessions API row. `None` for decisions made before decision ids existed.",
            "title": "Decision Id"
          },
          "harness": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Harness the decision applies to, e.g. `\"claude-native\"` or `\"codex\"`. `None` when the decision picked a model only (no harness dimension).",
            "title": "Harness"
          },
          "model": {
            "description": "The concrete brain model the router chose, e.g. `\"databricks-claude-opus-4-8\"`.",
            "title": "Model",
            "type": "string"
          },
          "rationale": {
            "description": "The router's one-line explanation, shown as muted secondary text, e.g. `\"Multi-file refactor needs deep reasoning.\"`.",
            "title": "Rationale",
            "type": "string"
          },
          "raw_model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The router-vocabulary pick before resolution to a servable catalog id, e.g. `\"gpt-5-6-sol\"`. `None` when the pick needed no resolution.",
            "title": "Raw Model"
          },
          "router_source": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Which router produced the decision — `\"databricks-aigw\"` for the external AI-Gateway `task_v1` service, `\"oss-llm\"` for the built-in judge. Deliberately a plain `str` rather than a `Literal`: a source added later must still round-trip through stored rows and the wire instead of failing validation. `None` on rows written before the field existed.",
            "title": "Router Source"
          },
          "scope": {
            "default": "turn",
            "description": "What the decision governs — `\"session\"` (auto-harness session routing), `\"turn\"` (per-turn routing), `\"child_session\"` (an Omnigent-spawned sub-agent) or `\"native_subagent\"` (a Task / `spawn_agent` spawn routed inside the harness). Defaults to `\"turn\"` so rows persisted before this field deserialize.",
            "enum": [
              "session",
              "turn",
              "child_session",
              "native_subagent"
            ],
            "title": "Scope",
            "type": "string"
          }
        },
        "required": [
          "model",
          "applied",
          "rationale"
        ],
        "title": "RoutingDecisionData",
        "type": "object"
      },
      "SandboxStatus": {
        "description": "Managed-sandbox launch progress for a `host_type=\"managed\"` session.\n\nCarried on the session snapshot only while the session's\nbackground sandbox launch is in flight or has failed; `None`\nfor sessions without a managed launch and once the launch\nsucceeds (the session then looks like any host-bound session).",
        "properties": {
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Failure detail when `stage == \"failed\"`, e.g. `\"managed sandbox launch failed: spend limit reached\"`. `None` otherwise.",
            "title": "Error"
          },
          "stage": {
            "description": "Current launch stage, e.g. `\"provisioning\"` — one of `SandboxLaunchStage`, in pipeline order: `provisioning` (creating the sandbox) → `cloning` (cloning the repository workspace; skipped when the session has none) → `starting` (starting the in-sandbox host) → `connecting` (launching the agent runner) → `ready` / `failed`.",
            "enum": [
              "provisioning",
              "cloning",
              "starting",
              "connecting",
              "ready",
              "failed"
            ],
            "title": "Stage",
            "type": "string"
          }
        },
        "required": [
          "stage"
        ],
        "title": "SandboxStatus",
        "type": "object"
      },
      "SendCommentsRequest": {
        "description": "Request body for `POST .../comments/send`.",
        "properties": {
          "comment_ids": {
            "description": "IDs of comments to send.",
            "items": {
              "type": "string"
            },
            "title": "Comment Ids",
            "type": "array"
          },
          "instruction": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional custom instruction prefix; defaults to the standard \"Please address the following file review comments.\" header.",
            "title": "Instruction"
          }
        },
        "required": [
          "comment_ids"
        ],
        "title": "SendCommentsRequest",
        "type": "object"
      },
      "ServerStreamEvent": {
        "discriminator": {
          "mapping": {
            "browser.action_request": "#/components/schemas/BrowserActionRequestEvent",
            "response.cancelled": "#/components/schemas/CancelledEvent",
            "response.client_task.cancel": "#/components/schemas/ClientTaskCancelEvent",
            "response.compaction.completed": "#/components/schemas/CompactionCompletedEvent",
            "response.compaction.failed": "#/components/schemas/CompactionFailedEvent",
            "response.compaction.in_progress": "#/components/schemas/CompactionInProgressEvent",
            "response.completed": "#/components/schemas/CompletedEvent",
            "response.created": "#/components/schemas/CreatedEvent",
            "response.elicitation_request": "#/components/schemas/ElicitationRequestEvent",
            "response.elicitation_resolved": "#/components/schemas/ElicitationResolvedEvent",
            "response.error": "#/components/schemas/ErrorEvent",
            "response.failed": "#/components/schemas/FailedEvent",
            "response.function_call_output.delta": "#/components/schemas/ToolOutputDeltaEvent",
            "response.heartbeat": "#/components/schemas/HeartbeatEvent",
            "response.in_progress": "#/components/schemas/InProgressEvent",
            "response.incomplete": "#/components/schemas/IncompleteEvent",
            "response.output_file.done": "#/components/schemas/OutputFileDoneEvent",
            "response.output_item.done": "#/components/schemas/OutputItemDoneEvent",
            "response.output_text.delta": "#/components/schemas/OutputTextDeltaEvent",
            "response.policy_denied": "#/components/schemas/PolicyDeniedEvent",
            "response.queued": "#/components/schemas/QueuedEvent",
            "response.reasoning.started": "#/components/schemas/ReasoningStartedEvent",
            "response.reasoning_summary_text.delta": "#/components/schemas/ReasoningSummaryTextDeltaEvent",
            "response.reasoning_text.delta": "#/components/schemas/ReasoningTextDeltaEvent",
            "response.retry": "#/components/schemas/RetryEvent",
            "session.agent_changed": "#/components/schemas/SessionAgentChangedEvent",
            "session.changed_files.invalidated": "#/components/schemas/SessionChangedFilesInvalidatedEvent",
            "session.child_session.updated": "#/components/schemas/SessionChildSessionUpdatedEvent",
            "session.collaboration_mode": "#/components/schemas/SessionCollaborationModeEvent",
            "session.created": "#/components/schemas/SessionCreatedEvent",
            "session.heartbeat": "#/components/schemas/SessionHeartbeatEvent",
            "session.input.consumed": "#/components/schemas/SessionInputConsumedEvent",
            "session.interrupted": "#/components/schemas/SessionInterruptedEvent",
            "session.mcp_startup": "#/components/schemas/SessionMcpStartupEvent",
            "session.model": "#/components/schemas/SessionModelEvent",
            "session.model_options": "#/components/schemas/SessionModelOptionsEvent",
            "session.presence": "#/components/schemas/SessionPresenceEvent",
            "session.reasoning_effort": "#/components/schemas/SessionReasoningEffortEvent",
            "session.resource.created": "#/components/schemas/SessionResourceCreatedEvent",
            "session.resource.deleted": "#/components/schemas/SessionResourceDeletedEvent",
            "session.sandbox_status": "#/components/schemas/SessionSandboxStatusEvent",
            "session.skills": "#/components/schemas/SessionSkillsEvent",
            "session.status": "#/components/schemas/SessionStatusEvent",
            "session.superseded": "#/components/schemas/SessionSupersededEvent",
            "session.terminal.activity": "#/components/schemas/SessionTerminalActivityEvent",
            "session.terminal_pending": "#/components/schemas/SessionTerminalPendingEvent",
            "session.todos": "#/components/schemas/SessionTodosEvent",
            "session.usage": "#/components/schemas/SessionUsageEvent",
            "turn.cancelled": "#/components/schemas/TurnCancelledEvent",
            "turn.completed": "#/components/schemas/TurnCompletedEvent",
            "turn.failed": "#/components/schemas/TurnFailedEvent",
            "turn.started": "#/components/schemas/TurnStartedEvent"
          },
          "propertyName": "type"
        },
        "oneOf": [
          {
            "$ref": "#/components/schemas/SessionStatusEvent"
          },
          {
            "$ref": "#/components/schemas/SessionUsageEvent"
          },
          {
            "$ref": "#/components/schemas/SessionModelEvent"
          },
          {
            "$ref": "#/components/schemas/SessionReasoningEffortEvent"
          },
          {
            "$ref": "#/components/schemas/SessionCollaborationModeEvent"
          },
          {
            "$ref": "#/components/schemas/SessionAgentChangedEvent"
          },
          {
            "$ref": "#/components/schemas/SessionTodosEvent"
          },
          {
            "$ref": "#/components/schemas/SessionTerminalPendingEvent"
          },
          {
            "$ref": "#/components/schemas/SessionSandboxStatusEvent"
          },
          {
            "$ref": "#/components/schemas/SessionMcpStartupEvent"
          },
          {
            "$ref": "#/components/schemas/SessionSkillsEvent"
          },
          {
            "$ref": "#/components/schemas/SessionModelOptionsEvent"
          },
          {
            "$ref": "#/components/schemas/SessionInputConsumedEvent"
          },
          {
            "$ref": "#/components/schemas/SessionInterruptedEvent"
          },
          {
            "$ref": "#/components/schemas/SessionCreatedEvent"
          },
          {
            "$ref": "#/components/schemas/SessionSupersededEvent"
          },
          {
            "$ref": "#/components/schemas/SessionPresenceEvent"
          },
          {
            "$ref": "#/components/schemas/SessionResourceCreatedEvent"
          },
          {
            "$ref": "#/components/schemas/SessionResourceDeletedEvent"
          },
          {
            "$ref": "#/components/schemas/SessionChildSessionUpdatedEvent"
          },
          {
            "$ref": "#/components/schemas/SessionChangedFilesInvalidatedEvent"
          },
          {
            "$ref": "#/components/schemas/SessionTerminalActivityEvent"
          },
          {
            "$ref": "#/components/schemas/OutputTextDeltaEvent"
          },
          {
            "$ref": "#/components/schemas/ToolOutputDeltaEvent"
          },
          {
            "$ref": "#/components/schemas/ReasoningStartedEvent"
          },
          {
            "$ref": "#/components/schemas/ReasoningTextDeltaEvent"
          },
          {
            "$ref": "#/components/schemas/ReasoningSummaryTextDeltaEvent"
          },
          {
            "$ref": "#/components/schemas/OutputItemDoneEvent"
          },
          {
            "$ref": "#/components/schemas/OutputFileDoneEvent"
          },
          {
            "$ref": "#/components/schemas/HeartbeatEvent"
          },
          {
            "$ref": "#/components/schemas/SessionHeartbeatEvent"
          },
          {
            "$ref": "#/components/schemas/ElicitationRequestEvent"
          },
          {
            "$ref": "#/components/schemas/ElicitationResolvedEvent"
          },
          {
            "$ref": "#/components/schemas/BrowserActionRequestEvent"
          },
          {
            "$ref": "#/components/schemas/PolicyDeniedEvent"
          },
          {
            "$ref": "#/components/schemas/CreatedEvent"
          },
          {
            "$ref": "#/components/schemas/QueuedEvent"
          },
          {
            "$ref": "#/components/schemas/InProgressEvent"
          },
          {
            "$ref": "#/components/schemas/CompletedEvent"
          },
          {
            "$ref": "#/components/schemas/FailedEvent"
          },
          {
            "$ref": "#/components/schemas/CancelledEvent"
          },
          {
            "$ref": "#/components/schemas/IncompleteEvent"
          },
          {
            "$ref": "#/components/schemas/RetryEvent"
          },
          {
            "$ref": "#/components/schemas/ErrorEvent"
          },
          {
            "$ref": "#/components/schemas/CompactionInProgressEvent"
          },
          {
            "$ref": "#/components/schemas/CompactionCompletedEvent"
          },
          {
            "$ref": "#/components/schemas/CompactionFailedEvent"
          },
          {
            "$ref": "#/components/schemas/ClientTaskCancelEvent"
          },
          {
            "$ref": "#/components/schemas/TurnStartedEvent"
          },
          {
            "$ref": "#/components/schemas/TurnCompletedEvent"
          },
          {
            "$ref": "#/components/schemas/TurnFailedEvent"
          },
          {
            "$ref": "#/components/schemas/TurnCancelledEvent"
          }
        ]
      },
      "SessionAgentChangedEvent": {
        "description": "Bound-agent change on a live session.\n\nEmitted by the switch-agent route after the session's agent binding\nis rewritten in place. Connected clients re-derive their cached\nsession state (harness presentation labels, bound agent id/name)\nfrom a fresh snapshot — the chat UI's native-vs-SDK message\nlifecycle depends on those labels, so a stale cache drops the first\npost-switch message (it reappears only when the transcript\nround-trip lands).",
        "properties": {
          "agent_id": {
            "description": "The session-scoped clone now bound to the session, e.g. `\"ag_abc123\"`.",
            "title": "Agent Id",
            "type": "string"
          },
          "agent_name": {
            "description": "Display name of the agent the session now runs, e.g. `\"claude-native-ui\"`. Deliberately the clean target-agent name — not the clone row's `\"… (switch ag_…)\"` disambiguation name — because clients render it verbatim. Category: **transient** (SSE-only). The switch is persisted on the conversation row, so on reconnect clients read the new binding from the session snapshot rather than from a replayed event.",
            "title": "Agent Name",
            "type": "string"
          },
          "conversation_id": {
            "description": "Session identifier, e.g. `\"conv_abc123\"`.",
            "title": "Conversation Id",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "session.agent_changed",
            "description": "Always `\"session.agent_changed\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "conversation_id",
          "agent_id",
          "agent_name"
        ],
        "title": "SessionAgentChangedEvent",
        "type": "object"
      },
      "SessionChangedFilesInvalidatedEvent": {
        "description": "The session's changed-files list may have changed — refetch it.\n\nA coarse \"something changed\" signal (per-file events aren't available\nfor git-mode workspaces) emitted by the runner after a file-mutating\ntool. The web treats it as a refetch trigger for the changed-files\npanel; transient (not persisted — the REST list is source of truth).",
        "properties": {
          "environment_id": {
            "default": "default",
            "description": "Environment whose changes were invalidated, e.g. `\"default\"`.",
            "title": "Environment Id",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "session_id": {
            "description": "Owning session/conversation id.",
            "title": "Session Id",
            "type": "string"
          },
          "type": {
            "const": "session.changed_files.invalidated",
            "description": "Always `\"session.changed_files.invalidated\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "session_id"
        ],
        "title": "SessionChangedFilesInvalidatedEvent",
        "type": "object"
      },
      "SessionChildSessionUpdatedEvent": {
        "description": "A child (sub-agent) session's status changed — pushed to the PARENT.\n\nLets the parent's resource rail update a child's status without\npolling `GET …/child_sessions`. Carries the full\n`ChildSessionSummary` so the web patches its cache directly.",
        "properties": {
          "child": {
            "additionalProperties": true,
            "description": "A PARTIAL `ChildSessionSummary` — the snapshot-on-connect sends the full summary, while live runner deltas carry only the fields that changed (a status delta omits `last_message_preview`; a preview delta carries only it). The web merges present fields over the cached row, so the payload is an open dict rather than the strict model.",
            "title": "Child",
            "type": "object"
          },
          "child_session_id": {
            "description": "The child session id, e.g. `\"conv_child_abc123\"`.",
            "title": "Child Session Id",
            "type": "string"
          },
          "conversation_id": {
            "description": "The PARENT (carrier) session id.",
            "title": "Conversation Id",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "session.child_session.updated",
            "description": "Always `\"session.child_session.updated\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "conversation_id",
          "child_session_id",
          "child"
        ],
        "title": "SessionChildSessionUpdatedEvent",
        "type": "object"
      },
      "SessionCollaborationModeEvent": {
        "description": "Active collaboration-mode update from a Codex-native session.\n\nEmitted after the web UI toggles Codex collaboration mode, and after the\nCodex forwarder observes a `thread/settings/updated` notification from\nthe native Codex TUI. Lets connected clients show a clear Plan-mode\nindicator without a reload.",
        "properties": {
          "conversation_id": {
            "description": "Session identifier, e.g. `\"conv_abc123\"`.",
            "title": "Conversation Id",
            "type": "string"
          },
          "mode": {
            "description": "The active collaboration mode string, e.g. `\"plan\"` or `\"default\"`. Category: **transient** (SSE-only). The server also writes `omnigent.codex_native.collaboration_mode` on the conversation labels, so reconnect clients restore the same state from the session snapshot.",
            "title": "Mode",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "session.collaboration_mode",
            "description": "Always `\"session.collaboration_mode\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "conversation_id",
          "mode"
        ],
        "title": "SessionCollaborationModeEvent",
        "type": "object"
      },
      "SessionCreatedEvent": {
        "description": "A child (sub-agent) session was spawned from this session.\n\nEmitted by `omnigent/tools/builtins/spawn.py:_spawn_one`\nonto the **parent** session's conversation stream after the\nchild conversation row is created and the child task has been\nstarted. Per the session-rearchitecture spec §3 (\"Event types\nand direction\") and §7 (\"Flow: client interacts with\nsub-agent\"), this lets clients watching the parent session's\nSSE subscribe directly to the child's stream without polling\nhistory for the tunneled `function_call` item.\n\nThe wire shape is FLAT (not enveloped):\n`{\"type\": \"session.created\", \"conversation_id\": <parent>, \"child_session_id\": <child>, \"agent_id\": <agent or None>, \"parent_session_id\": <parent>, \"sequence_number\": null}`.\n\nThe existing tunneled `function_call` ConversationItem\n(carried inside `OutputItemDoneEvent`) is retained\nfor compatibility — clients that don't yet implement the\n\"subscribe to child stream\" pattern can keep rendering sub-\nagent calls from the parent's persistent history.",
        "properties": {
          "agent_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Registered agent id the child runs as, e.g. `\"agent_xyz\"`. `None` is permitted only for legacy spawn paths that did not record an agent id; new code MUST set it.",
            "title": "Agent Id"
          },
          "child_session_id": {
            "description": "The newly-created child session id, e.g. `\"conv_child456\"`. Same as `conversation_id` on the child's own stream when consumers pivot to it.",
            "title": "Child Session Id",
            "type": "string"
          },
          "conversation_id": {
            "description": "The PARENT session/conversation id — this event rides the parent's stream, e.g. `\"conv_parent123\"`.",
            "title": "Conversation Id",
            "type": "string"
          },
          "parent_session_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Echo of `conversation_id` for consumers that key on a dedicated \"parent\" field rather than the carrier `conversation_id`. Always equal to `conversation_id`; included for forward-compat with clients that may relay these events across stream boundaries. Category: **transient** (SSE-only). The corresponding durable record of \"a child session exists\" lives in the conversation store as the child conversation row itself (`parent_conversation_id` foreign key) and the parent's tunneled `function_call` item — reconnecting clients discover children by walking the parent's persisted history, not by replaying this event.",
            "title": "Parent Session Id"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "session.created",
            "description": "Always `\"session.created\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "conversation_id",
          "child_session_id"
        ],
        "title": "SessionCreatedEvent",
        "type": "object"
      },
      "SessionForkRequest": {
        "additionalProperties": false,
        "description": "Request body for `POST /v1/sessions/{source_id}/fork`.\n\nCreates a deep copy of an existing session's items into a new\nsession. All fields are optional.",
        "properties": {
          "agent_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Built-in agent to bind the fork to, switching it away from the source's agent/harness (e.g. fork a Claude session into a Codex one, or a Claude-SDK session into Claude Code). When `None`, the fork keeps the source's agent. Must be a built-in agent (one listed by `GET /v1/agents`).",
            "title": "Agent Id"
          },
          "title": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Title for the forked session. When `None`, the server derives `\"Fork of <source_title>\"`.",
            "title": "Title"
          },
          "up_to_response_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Truncation point for the copied history, e.g. `\"resp_abc123\"`. When set, only items up to and including the last item of that response are copied — items after it are dropped from the fork. When `None` (default), the full history is copied.",
            "title": "Up To Response Id"
          }
        },
        "title": "SessionForkRequest",
        "type": "object"
      },
      "SessionGitOptions": {
        "description": "Git worktree options for `POST /v1/sessions`.\n\nRequires `host_id` to be set (and therefore `workspace`, which\nis interpreted as the source repository directory). Two modes,\nselected by `existing_worktree`:\n\n- **create** (default): the server creates a git worktree on the\n  host for a new branch and starts the runner in that worktree\n  instead of the picked directory.\n- **bind** (`existing_worktree=True`): `workspace` already IS a\n  pre-existing worktree; no worktree is created. `branch_name` is\n  recorded as the session's `git_branch` for display and opt-in\n  cleanup, and `base_branch` must not be set.\n\nSee designs/SESSION_GIT_WORKTREE.md.",
        "properties": {
          "base_branch": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional base ref to branch from, e.g. `\"main\"` or `\"origin/main\"`. `None` branches from the source repository's current `HEAD`. Create mode only — invalid with `existing_worktree`.",
            "title": "Base Branch"
          },
          "branch_name": {
            "description": "In create mode, the new branch to create and check out, e.g. `\"feature/login\"`. In bind mode, the branch already checked out in the existing worktree. Validated against git ref-format rules; invalid names fail with `invalid_input`.",
            "title": "Branch Name",
            "type": "string"
          },
          "existing_worktree": {
            "default": false,
            "description": "When `True`, bind to the pre-existing worktree at `workspace` instead of creating one (see above).",
            "title": "Existing Worktree",
            "type": "boolean"
          }
        },
        "required": [
          "branch_name"
        ],
        "title": "SessionGitOptions",
        "type": "object"
      },
      "SessionHeartbeatEvent": {
        "description": "Idle-stream keepalive on `GET /v1/sessions/{id}/stream`.\n\nEmitted by the session-stream route on a fixed cadence whenever\nthe underlying publish queue has been quiet (no turn in flight,\nno resource events). Distinct from `HeartbeatEvent`\n(`response.heartbeat`), which is per-turn and is driven by\nthe runtime workflow while a response is producing output.\n\nWhy this exists: the session stream stays open across many turns\nand through idle periods (waiting for the user to type). Without\na periodic emit, intermediate proxies, OS-level sockets, and the\nclient's SSE read-timeout can leave a half-open stream\nundetected for minutes after a network event (laptop sleep,\nWi-Fi handoff). The heartbeat puts a regular byte on the wire\nso the client's read-timeout and the server's\n`request.is_disconnected()` check both fire promptly.\n\nConsumers MAY ignore the payload entirely (the bytes crossing\nthe wire are sufficient). The optional `server_time` mirrors\n`HeartbeatEvent` for symmetry and debugging.",
        "properties": {
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "server_time": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "ISO 8601 UTC timestamp at emission, e.g. `\"2026-05-25T10:30:00Z\"`. `None` when the producer chose not to populate it.",
            "title": "Server Time"
          },
          "type": {
            "const": "session.heartbeat",
            "description": "Always `\"session.heartbeat\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type"
        ],
        "title": "SessionHeartbeatEvent",
        "type": "object"
      },
      "SessionInputConsumedEvent": {
        "description": "A queued input item was materialized into conversation history.\n\nEmitted by `POST /v1/sessions/{id}/events` once per accepted\ninput item at the moment it is persisted into conversation\nhistory (either onto a steered active turn or as the seed item\nof a freshly-started one). Wire shape uses the NESTED envelope:\n`{\"type\": \"session.input.consumed\", \"data\": <SessionInputConsumedPayload>, \"sequence_number\": null}`.\n\nThe event name is **provisional** — it may be renamed in a\nfuture revision. Consumers should reference\n`SessionInputConsumedEvent` (or its `type` literal)\nrather than hardcoding the wire string.",
        "properties": {
          "data": {
            "$ref": "#/components/schemas/SessionInputConsumedPayload",
            "description": "The decoded queued-item payload — see `SessionInputConsumedPayload`."
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "session.input.consumed",
            "description": "Always `\"session.input.consumed\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "data"
        ],
        "title": "SessionInputConsumedEvent",
        "type": "object"
      },
      "SessionInputConsumedPayload": {
        "description": "Inner payload of a `SessionInputConsumedEvent`.\n\nEmitted by the sessions route handler at the moment a client\ninput is persisted into `conversation_items`. Carries the\npersisted-item shape so clients can render the input (e.g.\nthe user's message bubble) at the moment of acceptance.",
        "properties": {
          "cleared_pending_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "When this consumed message drains a `omnigent.runtime.pending_inputs` entry (a native- terminal web message round-tripping back from the transcript), the drained entry's id, e.g. `\"pending_a1b2c3\"`. Lets a client drop the matching optimistic bubble by id instead of by position. `None` for non-native messages and for messages that matched no pending entry (e.g. typed directly in the TUI).",
            "title": "Cleared Pending Id"
          },
          "created_by": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Email of the human actor who posted the item, e.g. `\"[email protected]\"`. `None` for agent/tool/system items and single-user mode. Mirrors `ConversationItem.to_api_dict` for live attribution.",
            "title": "Created By"
          },
          "data": {
            "additionalProperties": true,
            "description": "Decoded item payload, e.g. `{\"role\": \"user\", \"content\": [{\"type\": \"input_text\", \"text\": \"Hello\"}]}`. Heterogeneous and `type`-specific.",
            "title": "Data",
            "type": "object"
          },
          "item_id": {
            "description": "Stable identifier of the conversation item just persisted, e.g. `\"item_abc123\"`.",
            "title": "Item Id",
            "type": "string"
          },
          "type": {
            "description": "The item type discriminator — `\"message\"` for user messages, `\"function_call_output\"` for tool results, etc. Mirrors `omnigent.server.schemas.SessionEventInput`'s `type` field.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "item_id",
          "type",
          "data"
        ],
        "title": "SessionInputConsumedPayload",
        "type": "object"
      },
      "SessionInterruptedEvent": {
        "description": "User-triggered cancel reached the loop.\n\nEmitted by `_publish_interrupted` in\n`omnigent/server/routes/sessions.py` when a client posts\na `{\"type\": \"interrupt\"}` to `POST /v1/sessions/{id}/events`. Co-emitted with\n`IncompleteEvent` (with the underlying response carrying\n`incomplete_details.reason == \"user_interrupt\"`) so off-the-\nshelf Responses parsers still close cleanly. Wire shape uses\nthe NESTED envelope verbatim from the existing emit site.",
        "properties": {
          "data": {
            "$ref": "#/components/schemas/SessionInterruptedPayload",
            "description": "The interrupt metadata — see `SessionInterruptedPayload`."
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "session.interrupted",
            "description": "Always `\"session.interrupted\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "data"
        ],
        "title": "SessionInterruptedEvent",
        "type": "object"
      },
      "SessionInterruptedPayload": {
        "description": "Inner payload of a `SessionInterruptedEvent`.\n\nBuilt by `_publish_interrupted` in\n`omnigent/server/routes/sessions.py`.",
        "properties": {
          "requested_at": {
            "description": "Unix epoch seconds when the interrupt request reached the server, e.g. `1704067200`.",
            "title": "Requested At",
            "type": "integer"
          },
          "response_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Optional active response id for terminal-backed integrations, e.g. `\"codex_turn_abc123\"`.",
            "title": "Response Id"
          }
        },
        "required": [
          "requested_at"
        ],
        "title": "SessionInterruptedPayload",
        "type": "object"
      },
      "SessionLabelsResponse": {
        "description": "Lightweight response body for `GET /v1/sessions/{id}/labels`.",
        "properties": {
          "id": {
            "description": "Session identifier, e.g. `\"conv_abc123\"`.",
            "title": "Id",
            "type": "string"
          },
          "labels": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Session-scoped guardrails labels. Empty dict when no labels have been written.",
            "title": "Labels",
            "type": "object"
          }
        },
        "required": [
          "id"
        ],
        "title": "SessionLabelsResponse",
        "type": "object"
      },
      "SessionList": {
        "description": "Paginated list of sessions; `data` is a page of `SessionListItem`.",
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/SessionListItem"
            },
            "title": "Data",
            "type": "array"
          },
          "first_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "First Id"
          },
          "has_more": {
            "default": false,
            "title": "Has More",
            "type": "boolean"
          },
          "last_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Id"
          },
          "object": {
            "const": "list",
            "default": "list",
            "title": "Object",
            "type": "string"
          }
        },
        "title": "SessionList",
        "type": "object"
      },
      "SessionListItem": {
        "description": "Lightweight session summary for `GET /v1/sessions` list responses.\n\nSame shape as `SessionResponse` minus `items`.",
        "properties": {
          "agent_id": {
            "description": "Durable identifier of the bound agent.",
            "title": "Agent Id",
            "type": "string"
          },
          "agent_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Human-readable name of the bound agent, e.g. `\"research-agent\"`. `None` when the agent row cannot be found.",
            "title": "Agent Name"
          },
          "archived": {
            "default": false,
            "description": "Whether the session is archived. Archived sessions are returned by `GET /v1/sessions` only when the request passes `include_archived=true`; the sidebar groups them into a dedicated \"Archived\" section. `False` for normal sessions.",
            "title": "Archived",
            "type": "boolean"
          },
          "comments_count": {
            "default": 0,
            "description": "Total number of review comments (any status) on this session. Together with `comments_updated_at` it forms a change fingerprint: an add or edit bumps the timestamp, a delete changes the count, so the web client can invalidate its cached comment list when either field changes in a `WS /v1/sessions/updates` frame. `0` when the session has no comments or the server has no comment store wired.",
            "title": "Comments Count",
            "type": "integer"
          },
          "comments_updated_at": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Unix epoch **microseconds** of the most recently mutated comment on this session (max `updated_at` across its comments). Microsecond precision keeps back-to-back mutations within one second distinguishable while staying an exact integer in JavaScript; clients only compare it for change. `None` when the session has no comments or the server has no comment store wired.",
            "title": "Comments Updated At"
          },
          "created_at": {
            "description": "Unix epoch seconds of creation.",
            "title": "Created At",
            "type": "integer"
          },
          "external_session_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Runtime-native session id this conversation wraps, e.g. a Claude Code session uuid for `omnigent claude` sessions. `None` for regular AP-only conversations. Lets the sidebar / picker render a runtime badge without a follow-up GET.",
            "title": "External Session Id"
          },
          "git_branch": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Git branch checked out in the session's worktree, e.g. `\"feature/login\"`. Set only when the session was created with a server-created git worktree; `None` otherwise. The Web UI uses a non-`None` value to offer the \"delete local branch\" cleanup checkbox on session delete. See designs/SESSION_GIT_WORKTREE.md.",
            "title": "Git Branch"
          },
          "host_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Host that launched the runner for this session.",
            "title": "Host Id"
          },
          "host_online": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "Whether the session's host tunnel is live (status online and fresh within the host liveness TTL). `None` when the session has no `host_id` (CLI/local). Distinguishes \"runner down but host can relaunch\" from \"host offline\" for the open-session view; not used by the sidebar.",
            "title": "Host Online"
          },
          "id": {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "title": "Id",
            "type": "string"
          },
          "labels": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Session-scoped guardrails labels.",
            "title": "Labels",
            "type": "object"
          },
          "owner": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The user_id of the session owner, or `None` when permissions are disabled. Included so the sidebar can display the owner without a separate API call.",
            "title": "Owner"
          },
          "parent_session_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Parent Session Id"
          },
          "pending_elicitations_count": {
            "default": 0,
            "description": "Number of approval prompts currently waiting on this session. Powers the sidebar's \"needs attention\" badge so a user with several sessions running can tell which ones are blocked on them without opening each chat. Sourced from the Omnigent server's in-memory `omnigent.runtime.pending_elicitations` index, which mirrors every `response.elicitation_request` event passing through `session_stream` and decrements when a verdict is dispatched. `0` when the session has no outstanding elicitations.",
            "title": "Pending Elicitations Count",
            "type": "integer"
          },
          "permission_level": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "The requesting user's numeric permission level on this session: `1` = read, `2` = edit, `3` = manage. `None` when permissions are disabled.",
            "title": "Permission Level"
          },
          "project_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Project Id"
          },
          "reasoning_effort": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Per-session reasoning-effort hint.",
            "title": "Reasoning Effort"
          },
          "runner_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Runner currently bound to the session.",
            "title": "Runner Id"
          },
          "runner_online": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "Strict runner liveness — `True` iff a runner tunnel is currently registered for this session. Matches `GET /health`'s `runner_online` value. Strict: a dead runner on a live host reads `False` here (no host-relaunch optimism folded in), unlike the legacy conflated value. `None` when the server has no runner liveness lookup wired.",
            "title": "Runner Online"
          },
          "search_snippet": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Excerpt of the chat content that matched the request's `search_query`, centered on the match with `…` marking elided ends, so the search UI can show *where* a session matched in its body. Present whenever the query hit an item body (even if the title also matched); `None` on non-search reads and when only the title matched.",
            "title": "Search Snippet"
          },
          "status": {
            "description": "Derived session lifecycle status.",
            "enum": [
              "idle",
              "running",
              "waiting",
              "failed"
            ],
            "title": "Status",
            "type": "string"
          },
          "title": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional human-readable title.",
            "title": "Title"
          },
          "updated_at": {
            "description": "Unix epoch seconds of last update.",
            "title": "Updated At",
            "type": "integer"
          },
          "viewer_last_seen": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "The *requesting user's* \"last seen\" wall-clock baseline in seconds for this session, or `None` when they have never seen it. Per-viewer (built from the server's in-memory per-user read-state, written by `PUT /v1/sessions/{id}/read-state`); the unread dot shows when `updated_at > viewer_last_seen` and the session is finished. In-memory only — resets on a server restart.",
            "title": "Viewer Last Seen"
          },
          "viewer_unread": {
            "default": false,
            "description": "Whether the *requesting user* explicitly marked this session unread. Per-viewer; lifts the active-row dot suppression on the client. `False` by default.",
            "title": "Viewer Unread",
            "type": "boolean"
          },
          "workspace": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Absolute path on disk where the runner cd's, e.g. `\"/Users/corey/universe/src/foo\"`. `None` for sessions that haven't been bound to a host workspace.",
            "title": "Workspace"
          }
        },
        "required": [
          "id",
          "agent_id",
          "status",
          "created_at",
          "updated_at"
        ],
        "title": "SessionListItem",
        "type": "object"
      },
      "SessionMcpStartupEvent": {
        "description": "Per-MCP-server startup progress for a native harness session.\n\nA codex-native session brings up its configured MCP servers when its\nCodex thread starts; slow or failing servers previously left the web\nsession looking hung with no signal. The native forwarder mirrors\nCodex's `mcpServer/startupStatus/updated` notifications as\n`external_mcp_startup` posts, republished here so the web UI can\nshow which servers are still starting and which failed or were\ncancelled.",
        "properties": {
          "conversation_id": {
            "description": "Session identifier, e.g. `\"conv_abc123\"`.",
            "title": "Conversation Id",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "servers": {
            "additionalProperties": {
              "$ref": "#/components/schemas/McpServerStartup"
            },
            "description": "Latest per-server startup map, e.g. `{\"safe\": {\"status\": \"starting\", \"error\": None}}`. Category: **transient** (SSE + snapshot cache). Not persisted; a client connecting mid-startup seeds from the session snapshot's `mcp_startup` field and updates live off this event.",
            "title": "Servers",
            "type": "object"
          },
          "type": {
            "const": "session.mcp_startup",
            "description": "Always `\"session.mcp_startup\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "conversation_id",
          "servers"
        ],
        "title": "SessionMcpStartupEvent",
        "type": "object"
      },
      "SessionModelEvent": {
        "description": "Active-model update from a terminal-backed integration.\n\nEmitted after an `external_model_change` POST from the\n`omnigent claude` transcript forwarder when the model is\nswitched inside the Claude Code terminal (a `/model` command or\nthe in-TUI picker). Lets the web model picker reflect a TUI-side\nswitch without a reload.",
        "properties": {
          "conversation_id": {
            "description": "Session identifier, e.g. `\"conv_abc123\"`.",
            "title": "Conversation Id",
            "type": "string"
          },
          "model": {
            "description": "Tier alias the session is now on, e.g. `\"opus\"` — Claude Code's version-agnostic alias, matching the picker's vocabulary (not a pinned `\"claude-opus-4-8\"` id). Category: **transient** (SSE-only). The server also writes `model_override` on the conversation, so on reconnect clients restore the selection from the snapshot's `model_override` rather than from a replayed event.",
            "title": "Model",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "session.model",
            "description": "Always `\"session.model\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "conversation_id",
          "model"
        ],
        "title": "SessionModelEvent",
        "type": "object"
      },
      "SessionModelOptionsEvent": {
        "description": "Signal that a native session's model catalog has resolved.\n\nModel options are fetched from the bound runner and cached on the session\nsnapshot. The initial snapshot can return an empty list while this\nbackground fetch is in flight; this event tells connected clients to\nre-read the snapshot and apply its now-populated `model_options`.\n\nCarries no payload beyond the conversation id. The snapshot's\n`model_options` field remains the source of truth.",
        "properties": {
          "conversation_id": {
            "description": "Session identifier, e.g. `\"conv_abc123\"`. Category: **transient** (SSE-only). On reconnect, clients seed Native model / effort controls from the session snapshot.",
            "title": "Conversation Id",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "session.model_options",
            "description": "Always `\"session.model_options\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "conversation_id"
        ],
        "title": "SessionModelOptionsEvent",
        "type": "object"
      },
      "SessionPresenceEvent": {
        "description": "The session's viewer list changed — full state, not a delta.\n\nEmitted on `GET /v1/sessions/{id}/stream` whenever a user\njoins, leaves (after the server-side grace window absorbs\nreconnect churn), or flips their idle aggregate, and once to\neach newly-connected stream as a snapshot-on-connect. Every\nevent carries the COMPLETE viewer list so clients replace their\nstate wholesale — missed events self-heal on the next event or\nreconnect. Viewers are scoped to the session *tree* (the root\nconversation and every sub-agent conversation under it), so a\nuser on a sub-agent page and a user on the root page appear in\neach other's lists. See `omnigent/server/presence.py` and\n`designs/UI/PRESENCE.md`.",
        "properties": {
          "conversation_id": {
            "description": "The conversation whose stream delivered this event — the root or a sub-agent conversation, e.g. `\"conv_abc123\"`. Matches the streamed conversation (not necessarily the tree's root) so clients can guard events by the conversation they are viewing.",
            "title": "Conversation Id",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "session.presence",
            "description": "Always `\"session.presence\"`.",
            "title": "Type",
            "type": "string"
          },
          "viewers": {
            "description": "All users currently viewing any conversation in the session tree (including the receiving user — the web filters self out for display), ordered by join time.",
            "items": {
              "$ref": "#/components/schemas/PresenceViewer"
            },
            "title": "Viewers",
            "type": "array"
          }
        },
        "required": [
          "type",
          "conversation_id",
          "viewers"
        ],
        "title": "SessionPresenceEvent",
        "type": "object"
      },
      "SessionProjectSummary": {
        "description": "One entry of `GET /v1/sessions/projects` — a sidebar project folder.\n\nDual-read union of first-class projects and legacy `omni_project`\nlabel-projects, keyed by name.",
        "properties": {
          "id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "First-class project id when one exists, or `None` for a label-only project not yet promoted to the `projects` table.",
            "title": "Id"
          },
          "name": {
            "description": "Project name (the folder's display name and union key).",
            "title": "Name",
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "title": "SessionProjectSummary",
        "type": "object"
      },
      "SessionReasoningEffortEvent": {
        "description": "Active reasoning-effort update from a terminal-backed integration.\n\nEmitted after an `external_reasoning_effort_change` POST from a native\nterminal forwarder when the user changes the thinking level inside the\nterminal UI. Lets the web effort picker reflect a TUI-side switch without\na reload.",
        "properties": {
          "conversation_id": {
            "description": "Session identifier, e.g. `\"conv_abc123\"`.",
            "title": "Conversation Id",
            "type": "string"
          },
          "reasoning_effort": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Reasoning effort now active for the session, e.g. `\"medium\"`, or `None` when Codex cleared to its default. Category: **transient** (SSE-only). The server also writes `reasoning_effort` on the conversation, so on reconnect clients restore the selection from the session snapshot rather than from a replayed event.",
            "title": "Reasoning Effort"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "session.reasoning_effort",
            "description": "Always `\"session.reasoning_effort\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "conversation_id"
        ],
        "title": "SessionReasoningEffortEvent",
        "type": "object"
      },
      "SessionResourceCreatedEvent": {
        "description": "A session resource was created.\n\nEmitted when a terminal is launched, a file is uploaded, or\nany other resource is materialized under a session. Wire shape\nis FLAT: `{\"type\": \"session.resource.created\", \"resource\": <SessionResourceObject-like dict>}`.",
        "properties": {
          "resource": {
            "additionalProperties": true,
            "description": "The newly created resource object.",
            "title": "Resource",
            "type": "object"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "session.resource.created",
            "description": "Always `\"session.resource.created\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "resource"
        ],
        "title": "SessionResourceCreatedEvent",
        "type": "object"
      },
      "SessionResourceDeletedEvent": {
        "description": "A session resource was deleted.\n\nEmitted when a terminal is closed, a file is deleted, or\nany other resource is removed from a session.",
        "properties": {
          "resource_id": {
            "description": "Opaque id of the deleted resource.",
            "title": "Resource Id",
            "type": "string"
          },
          "resource_type": {
            "description": "Type of the deleted resource, e.g. `\"terminal\"`, `\"file\"`.",
            "title": "Resource Type",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "session_id": {
            "description": "Owning session/conversation id.",
            "title": "Session Id",
            "type": "string"
          },
          "type": {
            "const": "session.resource.deleted",
            "description": "Always `\"session.resource.deleted\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "resource_id",
          "resource_type",
          "session_id"
        ],
        "title": "SessionResourceDeletedEvent",
        "type": "object"
      },
      "SessionResourceObject": {
        "additionalProperties": false,
        "description": "API representation of a session-scoped resource handle.",
        "properties": {
          "environment": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "For terminal resources, the environment id the terminal actually runs in. Omitted for non-terminal resources.",
            "title": "Environment"
          },
          "id": {
            "description": "Opaque resource identifier, e.g. `\"default\"` or `\"terminal_bash_s1\"`.",
            "title": "Id",
            "type": "string"
          },
          "metadata": {
            "additionalProperties": true,
            "description": "Resource-type-specific metadata.",
            "title": "Metadata",
            "type": "object"
          },
          "name": {
            "description": "Human-readable display name. Not required to be globally unique.",
            "title": "Name",
            "type": "string"
          },
          "object": {
            "const": "session.resource",
            "description": "Fixed resource type, always `\"session.resource\"`.",
            "title": "Object",
            "type": "string"
          },
          "session_id": {
            "description": "Owning session/conversation id.",
            "title": "Session Id",
            "type": "string"
          },
          "type": {
            "description": "Resource kind, initially `\"environment\"`, `\"terminal\"`, or `\"file\"`.",
            "enum": [
              "environment",
              "terminal",
              "file"
            ],
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "id",
          "object",
          "type",
          "session_id",
          "name"
        ],
        "title": "SessionResourceObject",
        "type": "object"
      },
      "SessionResourcePaginatedList": {
        "description": "Public paginated list of session resources.",
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/SessionResourceObject"
            },
            "title": "Data",
            "type": "array"
          },
          "first_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "First Id"
          },
          "has_more": {
            "default": false,
            "title": "Has More",
            "type": "boolean"
          },
          "last_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Id"
          },
          "object": {
            "const": "list",
            "default": "list",
            "title": "Object",
            "type": "string"
          }
        },
        "title": "SessionResourcePaginatedList",
        "type": "object"
      },
      "SessionResponse": {
        "description": "API representation of a session.\n\nReturned by `POST /v1/sessions`, `GET /v1/sessions/{id}`,\nand `PATCH /v1/sessions/{id}`.",
        "properties": {
          "active_response_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Response id of the turn currently in flight, or `None` when the session is idle. Sourced from the server's `_session_active_response_cache` at snapshot build time so a client connecting mid-turn can reopen a streaming `activeResponse` — the SSE stream is snapshot + live tail with no replay, so the turn-start `running` edge that carried this id is not re-sent on reconnect. Today only native-terminal forwarders (claude-native) stamp a turn id on their status edges; other harnesses leave this `None`.",
            "title": "Active Response Id"
          },
          "agent_id": {
            "description": "Durable identifier of the bound agent, e.g. `\"ag_abc123\"`. Stable across renames of the agent.",
            "title": "Agent Id",
            "type": "string"
          },
          "agent_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Human-readable name of the bound agent, e.g. `\"research-agent\"`. Loaded from the agent row at snapshot-build time. `None` when the agent row cannot be found (deleted or orphaned session).",
            "title": "Agent Name"
          },
          "archived": {
            "default": false,
            "description": "Whether the session is archived. Archived sessions are hidden from the default sidebar listing and surface only behind the \"Show archived\" toggle. `False` for normal sessions. Toggled via `PATCH /v1/sessions/{id}`.",
            "title": "Archived",
            "type": "boolean"
          },
          "background_task_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Background shells (claude-native) still running as of the last status edge, so a reload re-shows \"N shells still running\" even though the session has settled to `\"idle\"`. `None` (the default / omitted) when no shells are tracked.",
            "title": "Background Task Count"
          },
          "context_window": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "The model's context window size in tokens as looked up server-side from litellm's registry (or from the `AP_CONTEXT_WINDOW_OVERRIDE` env var), e.g. `200_000`. `None` when the model is not in litellm's registry and no override is set.",
            "title": "Context Window"
          },
          "cost_control_mode_override": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Per-session cost-control switch: `\"on\"` activates the spec's configured cost-control mode, `\"off\"` disables cost control for this session. `None` means no override is active (the spec default applies). Set at create time or via `PATCH /v1/sessions/{id}` (the web \"Cost Optimized\" toggle); read by the cost-control advisor pipeline.",
            "title": "Cost Control Mode Override"
          },
          "created_at": {
            "description": "Unix epoch seconds of creation.",
            "title": "Created At",
            "type": "integer"
          },
          "external_session_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Runtime-native session id this conversation wraps, e.g. a Claude Code session uuid for `omnigent claude` sessions. `None` for regular AP-only conversations. Populated by the wrapper bridge.",
            "title": "External Session Id"
          },
          "git_branch": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Git branch checked out in the session's worktree, e.g. `\"feature/login\"`. Set only when the session was created with a server-created git worktree; `None` otherwise. The Web UI uses a non-`None` value to offer the \"delete local branch\" cleanup checkbox on session delete. See designs/SESSION_GIT_WORKTREE.md.",
            "title": "Git Branch"
          },
          "harness": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The bound agent's canonical harness, e.g. `\"claude-sdk\"` or `\"openai-agents\"`. Lets the client render the active credential for the correct provider family instead of inferring it from the model string (which is wrong when the agent declares no model). `None` when the agent cannot be looked up.",
            "title": "Harness"
          },
          "host_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Host that launched (or should launch) the runner for this session, e.g. `\"host_a1b2c3d4...\"`. `None` for CLI-initiated sessions.",
            "title": "Host Id"
          },
          "host_online": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "Whether the session's host tunnel is live (status online and fresh within the host liveness TTL). `None` when the session has no `host_id` (CLI/local). Used only to choose what the open view shows when `runner_online` is `False` — host alive ⇒ \"send a message to wake the runner\"; host dead ⇒ \"reconnect / fork\". Never participates in the reachability decision.",
            "title": "Host Online"
          },
          "host_resumable": {
            "default": false,
            "description": "Whether this session is bound to a dormant managed host the server can wake in place (its provider sets `SandboxLauncher.can_resume`). The open view reads it only when `host_online` is `False`, to split a confirmed host-down into a recoverable \"asleep\" state (send a message — the relaunch path resumes the sandbox) versus the terminal `host_offline` dead-end (reconnect from your machine / fork). `False` for non-managed or non-resumable hosts.",
            "title": "Host Resumable",
            "type": "boolean"
          },
          "id": {
            "description": "Unique session identifier (also the underlying conversation ID), e.g. `\"conv_abc123\"`.",
            "title": "Id",
            "type": "string"
          },
          "items": {
            "description": "Committed conversation items in chronological order. Empty for a freshly created session.",
            "items": {
              "$ref": "#/components/schemas/ConversationItem"
            },
            "title": "Items",
            "type": "array"
          },
          "kind": {
            "default": "default",
            "title": "Kind",
            "type": "string"
          },
          "labels": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Session-scoped guardrails labels. Empty dict when no labels have been written.",
            "title": "Labels",
            "type": "object"
          },
          "last_task_error": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "string"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Error details from the most recently failed task. Only present when `status == \"failed\"` and the task stored an error. Lets clients display the failure reason on historical load without relying on the transient `response.error` SSE event (which may have been emitted before the web client subscribed). Format mirrors the `RetryErrorDetail` SSE shape: `{\"code\": \"executor_error\", \"message\": \"...\"}`. `None` in all other cases.",
            "title": "Last Task Error"
          },
          "last_total_tokens": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Total token count (input + output) from the most recently completed task's `usage`, e.g. `45231`. `None` when no task has completed yet. Lets clients seed their context-ring on conversation resume without waiting for the next `response.completed` SSE event.",
            "title": "Last Total Tokens"
          },
          "llm_model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The LLM model identifier from the bound agent's spec, e.g. `\"anthropic/claude-sonnet-4-6\"`. `None` when the agent has no explicit `llm:` block or the agent cannot be looked up.",
            "title": "Llm Model"
          },
          "mcp_startup": {
            "anyOf": [
              {
                "additionalProperties": {
                  "$ref": "#/components/schemas/McpServerStartup"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Mcp Startup"
          },
          "model_options": {
            "description": "Runner-owned model-picker options for native sessions. Claude supplies launch-time gateway aliases; Codex includes each model's supported reasoning efforts. Empty while unavailable.",
            "items": {
              "$ref": "#/components/schemas/NativeModelOption"
            },
            "title": "Model Options",
            "type": "array"
          },
          "model_override": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Per-session LLM model override, e.g. `\"claude-opus-4-7\"`. `None` means no override is active (the agent's `llm_model` applies). Set via `PATCH /v1/sessions/{id}` or the REPL's `/model` command; both write the same column so the web UI and the TUI stay in sync.",
            "title": "Model Override"
          },
          "parent_session_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "For sub-agent sessions, the parent conversation's id, e.g. `\"conv_parent987\"`. `None` for top-level sessions. Lets clients identify a session as a child and link back to its parent without an extra round-trip — the same conversation row exposes this via `parent_conversation_id` internally.",
            "title": "Parent Session Id"
          },
          "pending_elicitations": {
            "description": "Outstanding approval prompts on this session at the moment the snapshot was built — the original `response.elicitation_request` event dicts. Lets the UI render the ApprovalCard on cold load, since the live SSE stream has no replay and a prompt emitted before the user opened the chat would otherwise vanish. Empty list when no prompts are outstanding. Sourced from the Omnigent server's in-memory `omnigent.runtime.pending_elicitations` index.",
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Pending Elicitations",
            "type": "array"
          },
          "pending_inputs": {
            "description": "Un-consumed web-composer user messages on native-terminal (claude-native / codex-native) sessions at snapshot time, each `{\"pending_id\", \"content\"}`. Native sessions don't persist a web message at POST time (the transcript forwarder is the single writer), so a client that posted then navigated away / rebound would lose its optimistic bubble; replaying these re-hydrates it. Empty list otherwise. Sourced from the in-memory `omnigent.runtime.pending_inputs` index.",
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Pending Inputs",
            "type": "array"
          },
          "permission_level": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "The requesting user's numeric permission level on this session: `1` = read, `2` = edit, `3` = manage. `None` when permissions are disabled (single-user mode without a permission store).",
            "title": "Permission Level"
          },
          "project_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Project Id"
          },
          "reasoning_effort": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Per-session reasoning-effort hint. Accepted metadata values are `\"none\"`, `\"minimal\"`, `\"low\"`, `\"medium\"`, `\"high\"`, `\"xhigh\"`, and `\"max\"`. Provider-specific support is validated when a turn executes. `None` means use the agent default.",
            "title": "Reasoning Effort"
          },
          "root_conversation_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The id of this session's spawn-tree root, e.g. `\"conv_root1\"`. Equals `id` for top-level sessions; for sub-agents it points at the top-level ancestor. Lets orchestration tools (e.g. `sys_session_close`) confirm a target shares the caller's spawn tree over the REST path. `None` only when the underlying row predates the `root_conversation_id` column (not expected post-migration).",
            "title": "Root Conversation Id"
          },
          "runner_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Runner currently bound to this session, e.g. `\"runner_abc123\"`. `None` until a client binds one via `PATCH /v1/sessions/{id}`.",
            "title": "Runner Id"
          },
          "runner_online": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "Strict runner liveness — `True` iff a runner tunnel is currently registered for this session. This is the sole reachability signal: `True` means the client can chat normally. It does **not** fold in host-relaunch optimism (a dead runner on a live host reads `False` here, not `True`) — the open-session view pairs it with `host_online` to decide what to show. `None` when the server has no runner liveness lookup wired.",
            "title": "Runner Online"
          },
          "sandbox_status": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/SandboxStatus"
              },
              {
                "type": "null"
              }
            ],
            "description": "Managed-sandbox launch progress while the session's background sandbox launch is in flight or has failed — see `SandboxStatus`. `None` for sessions without a managed launch and once the launch succeeds. Sourced from the Omnigent server's in-memory `_session_sandbox_status_cache` at snapshot build time, so a client opening the session mid-launch sees the current stage."
          },
          "skills": {
            "description": "Skills the bound agent has access to — the merged result of the agent spec's bundled `skills` and the host-scope skills discovered along the agent workdir / `~/.claude/skills/` (subject to the spec's `skills_filter`). Mirrors what the TUI passes to the runner at startup. Empty list when the agent spec cannot be loaded, or when bundled + host discovery yields nothing.",
            "items": {
              "$ref": "#/components/schemas/SkillSummary"
            },
            "title": "Skills",
            "type": "array"
          },
          "status": {
            "description": "Session lifecycle status. One of `\"idle\"` (no loop running), `\"running\"` (loop executing), `\"waiting\"` (loop parked on background work / sub-agents), or `\"failed\"` (terminal failure). Current read paths collapse `\"waiting\"` -> `\"running\"` before building this snapshot; the literal stays a superset of what the runtime can produce so a server that forwards the raw status never 500s on serialization.",
            "enum": [
              "idle",
              "running",
              "waiting",
              "failed"
            ],
            "title": "Status",
            "type": "string"
          },
          "sub_agent_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "For sub-agent sessions, the sub-agent type name within the parent's spec tree, e.g. `\"summarizer\"`. `None` for top-level sessions.",
            "title": "Sub Agent Name"
          },
          "subagent_routing_override": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Per-session subagent-routing switch, two-state: `\"on\"` routes subagent spawns, and `\"off\"` or `None` (unset) both leave them unrouted — the in-session \"Subagent routing\" row renders either as \"Default\". `None` on a row created before this became explicit inherits nothing. Stamped `\"on\"` at create for Smart Routing sessions; also set via `PATCH /v1/sessions/{id}`.",
            "title": "Subagent Routing Override"
          },
          "terminal_launch_args": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "Pass-through CLI args the native terminal wrapper (claude / codex) was launched with, e.g. `[\"--dangerously-skip-permissions\"]`. `None` for non-native sessions or a native session launched with none. Lets the launcher reproduce the command on resume.",
            "title": "Terminal Launch Args"
          },
          "terminal_pending": {
            "default": false,
            "description": "`True` while the runner is auto-creating a terminal-first session's terminal (claude-native / codex-native), so the Web UI shows a spinner on the Terminal pill instead of a silent greyed-out button. Cleared to `False` once the terminal lands or auto-create fails; from then on the client relies purely on whether a terminal resource exists. Sourced from the Omnigent server's in-memory `_session_terminal_pending_cache` at snapshot build time, so a client connecting mid-spin-up still sees the spinner.",
            "title": "Terminal Pending",
            "type": "boolean"
          },
          "title": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional human-readable title, e.g. `\"debugging auth flow\"`. `None` when unset.",
            "title": "Title"
          },
          "todos": {
            "description": "Current Claude Code todo list items for `omnigent claude` sessions, as raw dicts from Claude's todo JSON file. Each dict has `content`, `status`, and `activeForm` keys. Empty list for non-claude-native sessions or when no todos have been reported yet. Sourced from the Omnigent server's in-memory `_session_todos_cache`.",
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Todos",
            "type": "array"
          },
          "total_cost_usd": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Cumulative LLM spend for this session in USD, e.g. `0.42`. `None` when the session is **unpriced** — no turn has been priced yet (the model is absent from the pricing catalog, or no usage has been recorded) — so clients render \"—\" rather than a misleading `$0.00`. Server-computed (cache-aware for relay/codex, exact billing for claude-native), the same total the cost-budget policy gates on. Lets clients seed their cost indicator on resume without waiting for the next `session.usage` SSE event.",
            "title": "Total Cost Usd"
          },
          "updated_at": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Unix epoch timestamp of the last persisted session activity. Advances when conversation items are appended and on session metadata edits (rename, agent switch, archive); a mid-stall rename therefore resets the clock, so an orchestrator treating this as a pure item-append heartbeat should account for that. Can be compared across snapshots independently of lifecycle status.",
            "title": "Updated At"
          },
          "usage_by_model": {
            "anyOf": [
              {
                "additionalProperties": {
                  "$ref": "#/components/schemas/ModelUsage"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Per-model breakdown of the same subtree usage, keyed by the raw harness model id, e.g. `{\"claude-sonnet-4-6\": ModelUsage(input_tokens=12000, ...)}`. `None` when no per-model usage has been recorded (older sessions recorded before this field existed, or before the first turn). Lets the UI show which models a session spent its tokens / budget on.",
            "title": "Usage By Model"
          },
          "workspace": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Absolute path on disk where the runner cd's, e.g. `\"/Users/corey/universe/src/foo\"`. Set when the session was bound to a host workspace at create-time, or when the CLI captured `os.getcwd()` at session-create. Always `None` when not yet validated against a host. When a git worktree was created for the session, this is the worktree directory path.",
            "title": "Workspace"
          }
        },
        "required": [
          "id",
          "agent_id",
          "status",
          "created_at"
        ],
        "title": "SessionResponse",
        "type": "object"
      },
      "SessionSandboxStatusEvent": {
        "description": "Managed-sandbox launch progress for a `host_type=\"managed\"` session.\n\nA managed create returns before its sandbox exists; the Omnigent\nserver emits this event as the background launch pipeline advances\nso the Web UI can show live provisioning progress on the session\npage instead of a silent dead chat: sandbox provision → repository\nclone → host startup → runner connect → ready, or a terminal\nfailure with the reason.",
        "properties": {
          "conversation_id": {
            "description": "Session identifier, e.g. `\"conv_abc123\"`.",
            "title": "Conversation Id",
            "type": "string"
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Failure detail when `stage == \"failed\"`, e.g. `\"managed sandbox launch failed: spend limit reached\"`. `None` otherwise. Category: **transient** (SSE-only). On reconnect, clients seed the progress indicator from the session snapshot's `sandbox_status` field, which is populated by `_session_sandbox_status_cache` at snapshot build time.",
            "title": "Error"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "stage": {
            "description": "The launch stage just entered, e.g. `\"provisioning\"` — see `SandboxStatus` for the full pipeline order.",
            "enum": [
              "provisioning",
              "cloning",
              "starting",
              "connecting",
              "ready",
              "failed"
            ],
            "title": "Stage",
            "type": "string"
          },
          "type": {
            "const": "session.sandbox_status",
            "description": "Always `\"session.sandbox_status\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "conversation_id",
          "stage"
        ],
        "title": "SessionSandboxStatusEvent",
        "type": "object"
      },
      "SessionSkillsEvent": {
        "description": "Signal that a session's runner-owned skills have resolved.\n\nSkills are discovered against the bound runner's filesystem and\nfetched off the session-snapshot hot path: the snapshot kicks a\nsingle background fetch (`_load_runner_skills` in\n`omnigent/server/routes/sessions.py`) and serves `[]` until\nit lands. This event fires the moment that background fetch\npopulates the per-session skills cache, so a connected web client\ncan re-read the snapshot and fill its slash-command menu instead\nof waiting for the next bind.\n\nCarries no payload beyond the conversation id — it is a \"skills\nare ready, re-read the snapshot\" nudge, mirroring the\ninvalidate-then-refetch shape used by\n`SessionChangedFilesInvalidatedEvent`. The snapshot's\n`skills` field (now cache-backed) stays the source of truth.",
        "properties": {
          "conversation_id": {
            "description": "Session identifier, e.g. `\"conv_abc123\"`. Category: **transient** (SSE-only). On reconnect, clients seed the menu from the session snapshot's `skills` field, which is populated by the runner-skills cache at snapshot build time.",
            "title": "Conversation Id",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "session.skills",
            "description": "Always `\"session.skills\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "conversation_id"
        ],
        "title": "SessionSkillsEvent",
        "type": "object"
      },
      "SessionStatusEvent": {
        "description": "Session lifecycle status transition.\n\nEmitted by the runtime / session route handler at every\ntransition between `launching` / `running` / `waiting` /\n`idle` / `failed`. Wire shape is\nFLAT (not enveloped): `{\"type\": \"session.status\", \"conversation_id\": \"...\", \"status\": \"...\", \"sequence_number\": null}`.\n\nThe `waiting` value is emitted by the runtime's parent agent\nloop when it parks on the `async_work_complete` drain\n(`_drain_async_completions(block_for_one=True)` in\n`omnigent/runtime/workflow.py`) — i.e. while the parent\nturn is suspended waiting for background tools or sub-agents\nto complete. Per the session-rearchitecture spec §3\n(\"Event types and direction\"), `waiting` is the\nsession-status companion of the spec's `turn.waiting`\ntransient — clients should render the session as actively\nblocked-on-async-work, distinct from `running`. When the\ndrain wakes (a child completed), the runtime emits a follow-up\n`running` to resume.",
        "properties": {
          "background_task_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Background Task Count"
          },
          "blocked_on": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Short human phrase naming what a still-`running` session is parked on, e.g. `\"permission prompt\"` or `\"dialog open\"`. Set by terminal-backed integrations whose agent can block on a dialog the web UI does not mirror, so the client can say *why* nothing is moving instead of showing a bare spinner. `None` whenever the session is not parked. Unrelated to the `waiting` status above, which means the turn has ended and only background work remains. Category: **transient** (SSE-only). Status is rederived on reconnect from the cached last-relayed turn lifecycle event or by re-querying the runner; not persisted by the runtime.",
            "title": "Blocked On"
          },
          "conversation_id": {
            "description": "The conversation/session identifier whose status changed, e.g. `\"conv_abc123\"`.",
            "title": "Conversation Id",
            "type": "string"
          },
          "error": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ErrorDetail"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Machine-readable failure detail, present only when `status == \"failed\"`. Carries the message the runner attached when a turn died — most importantly a SETUP-phase failure (spec resolution, spawn-env build) that ends the turn before any `response.failed` event is emitted. `None` for every non-failed transition. Clients render `error.message` as the terminal error line; without it a setup failure shows as a silent end."
          },
          "response_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Optional active response id for terminal-backed integrations, e.g. `\"codex_turn_abc123\"`. Clients use it to associate coarse session status edges with the assistant bubble they describe. `None` for ordinary in-process runtime edges.",
            "title": "Response Id"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "status": {
            "description": "New session status. `\"launching\"` (session or child task created, but no concrete harness start observed), `\"idle\"` (no loop running), `\"running\"` (loop executing), `\"waiting\"` (parent turn parked on the async-work drain), or `\"failed\"` (terminal failure).",
            "enum": [
              "idle",
              "launching",
              "running",
              "waiting",
              "failed"
            ],
            "title": "Status",
            "type": "string"
          },
          "type": {
            "const": "session.status",
            "description": "Always `\"session.status\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "conversation_id",
          "status"
        ],
        "title": "SessionStatusEvent",
        "type": "object"
      },
      "SessionSupersededEvent": {
        "description": "This conversation was superseded by another and clients should\nfollow to it.\n\nEmitted by `_publish_session_superseded` in\n`omnigent/server/routes/sessions.py` when the claude-native\nforwarder rotates a session away on a Claude `/clear` (the old\nconversation keeps its history but the live terminal moves to a\nfresh conversation — see `_post_clear_supersession` in\n`omnigent/claude_native_forwarder.py`). A client actively viewing\nthe superseded conversation auto-redirects to `target_conversation_id`.\n\nCategory: **transient** (SSE-only), live-only by design. There is no\nSSE replay: a client that connects after the rotation does not get\nthis event. The durable counterpart is the persisted notice message\nappended to the old conversation (a `message` item linking to the\nnew conversation), which a reloading client renders instead of being\nforce-redirected.\n\nThe wire shape is FLAT (not enveloped):\n`{\"type\": \"session.superseded\", \"conversation_id\": <old>, \"target_conversation_id\": <new>, \"reason\": \"clear\"}`.",
        "properties": {
          "conversation_id": {
            "description": "The superseded (old) conversation id this event rides the stream of, e.g. `\"conv_old\"`.",
            "title": "Conversation Id",
            "type": "string"
          },
          "reason": {
            "const": "clear",
            "default": "clear",
            "description": "Why the session was superseded. Currently always `\"clear\"` (a Claude Code `/clear`); kept as a field so the client can branch on future supersession causes.",
            "title": "Reason",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "target_conversation_id": {
            "description": "The conversation to follow to, e.g. `\"conv_new\"`.",
            "title": "Target Conversation Id",
            "type": "string"
          },
          "type": {
            "const": "session.superseded",
            "description": "Always `\"session.superseded\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "conversation_id",
          "target_conversation_id"
        ],
        "title": "SessionSupersededEvent",
        "type": "object"
      },
      "SessionSwitchAgentRequest": {
        "additionalProperties": false,
        "description": "Request body for `POST /v1/sessions/{id}/switch-agent`.\n\nRebinds an existing session in place to a different agent/harness,\nkeeping the same session (transcript, comments, files, workspace).\nUnlike fork, no new session is created.",
        "properties": {
          "agent_id": {
            "description": "Built-in agent to switch the session to, e.g. `\"ag_builtin_codex\"`. Must be a built-in agent (one listed by `GET /v1/agents`) and different from the session's current agent.",
            "title": "Agent Id",
            "type": "string"
          }
        },
        "required": [
          "agent_id"
        ],
        "title": "SessionSwitchAgentRequest",
        "type": "object"
      },
      "SessionTerminalActivityEvent": {
        "description": "A terminal's pane produced output (runner-determined activity pulse).\n\nPowers the web \"active\" badge for any terminal without a client PTY\nattach — the runner's per-terminal pane watcher emits this when the\npane content changes. Transient (a live pulse; not persisted, not in\nthe connect snapshot).",
        "properties": {
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "session_id": {
            "description": "Owning session/conversation id.",
            "title": "Session Id",
            "type": "string"
          },
          "terminal_id": {
            "description": "Opaque terminal resource id, e.g. `\"terminal_zsh_s1\"`.",
            "title": "Terminal Id",
            "type": "string"
          },
          "type": {
            "const": "session.terminal.activity",
            "description": "Always `\"session.terminal.activity\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "session_id",
          "terminal_id"
        ],
        "title": "SessionTerminalActivityEvent",
        "type": "object"
      },
      "SessionTerminalPendingEvent": {
        "description": "Terminal spin-up status for a terminal-first session.\n\nTwo sources emit this event:\n\n1. The Omnigent server at `POST /v1/sessions` for host-launched\n   terminal-first sessions — the earliest possible point, before\n   the runner even starts, so the spinner appears immediately on\n   session create rather than after the runner boots.\n2. The Omnigent relay when the runner's `session.terminal_pending` frame\n   arrives — covers non-host-launched sessions (e.g. server-dispatched\n   sub-agents) and carries the authoritative `pending=False` clear\n   emitted by the runner's `finally` block.\n\nTogether they allow web to show a spinner on the Terminal pill\nwhile the backend boots the terminal instead of a silent greyed-out\nbutton, and to distinguish \"still starting up\" from \"no terminal\"\n(killed or never created).",
        "properties": {
          "conversation_id": {
            "description": "Session identifier, e.g. `\"conv_abc123\"`.",
            "title": "Conversation Id",
            "type": "string"
          },
          "pending": {
            "description": "`True` while the terminal is being created; `False` once it lands or auto-create fails. Category: **transient** (SSE-only). On reconnect, clients seed the spinner from the session snapshot's `terminal_pending` field, which is populated by `_session_terminal_pending_cache` at snapshot build time.",
            "title": "Pending",
            "type": "boolean"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "session.terminal_pending",
            "description": "Always `\"session.terminal_pending\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "conversation_id",
          "pending"
        ],
        "title": "SessionTerminalPendingEvent",
        "type": "object"
      },
      "SessionTodosEvent": {
        "description": "Todo-list update from a Claude Code terminal-backed session.\n\nEmitted after an `external_session_todos` POST from the\n`omnigent claude` transcript forwarder, which captures todo\nupdates via `PostToolUse`/`TodoWrite` hook events from Claude\nCode and forwards them to the Omnigent server. Lets web render a\nlive todo panel in the right column without polling.",
        "properties": {
          "conversation_id": {
            "description": "Session identifier, e.g. `\"conv_abc123\"`.",
            "title": "Conversation Id",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "todos": {
            "description": "Current todo items read from Claude's todo file. Each entry is a raw dict with `content` (str), `status` (`\"pending\"` | `\"in_progress\"` | `\"completed\"`), and `activeForm` (str, the gerund form) keys, e.g. `[{\"content\": \"Fix the bug\", \"status\": \"in_progress\", \"activeForm\": \"Fixing the bug\"}]`. Category: **transient** (SSE-only). On reconnect, clients seed the panel from the session snapshot's `todos` field, which is populated by `_session_todos_cache` at snapshot build time.",
            "items": {
              "additionalProperties": true,
              "type": "object"
            },
            "title": "Todos",
            "type": "array"
          },
          "type": {
            "const": "session.todos",
            "description": "Always `\"session.todos\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "conversation_id",
          "todos"
        ],
        "title": "SessionTodosEvent",
        "type": "object"
      },
      "SessionUsage": {
        "description": "One session's rolled-up LLM spend for the `GET /v1/usage` report.\n\n`cost_usd` is the subtree total — the session plus every sub-agent it\nspawned — read from `session_usage` via\n`omnigent.runtime.policies.builder.load_session_usage`. It is the\nauthoritative session figure (the same value the web session sidebar\nshows as \"Session cost\" and the daily rollup records).\n\n`models` is the per-model cost breakdown, mirroring the web session\nsidebar's per-model list. Deliberately **not guaranteed to sum to\n`cost_usd`**: native harnesses report a single cumulative session\ntotal and the server attributes it to the currently-active model, so on\na session that switched models mid-run each model's bucket is a snapshot\nof the running total rather than that model's own spend. The header\n`cost_usd` stays authoritative; the per-model values are shown\nfaithfully as recorded (same convention as the web UI).",
        "properties": {
          "agent_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Agent Name"
          },
          "cost_usd": {
            "default": 0,
            "description": "Authoritative cumulative USD spend for this session's subtree.",
            "title": "Cost Usd",
            "type": "number"
          },
          "created_at": {
            "description": "Unix epoch seconds of creation.",
            "title": "Created At",
            "type": "integer"
          },
          "harness": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Harness"
          },
          "id": {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "title": "Id",
            "type": "string"
          },
          "llm_model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Llm Model"
          },
          "models": {
            "additionalProperties": {
              "type": "number"
            },
            "description": "Per-model recorded cost, keyed by the raw harness model id (e.g. `{\"claude-opus-4-8\": 14.03}`). Empty when no per-model cost was recorded. May not sum to `cost_usd` (see above).",
            "title": "Models",
            "type": "object"
          },
          "title": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional human-readable title.",
            "title": "Title"
          },
          "updated_at": {
            "description": "Unix epoch seconds of last activity.",
            "title": "Updated At",
            "type": "integer"
          }
        },
        "required": [
          "id",
          "created_at",
          "updated_at"
        ],
        "title": "SessionUsage",
        "type": "object"
      },
      "SessionUsageEvent": {
        "description": "Token-usage update from a terminal-backed integration.\n\nEmitted after an `external_session_usage` POST from an\nout-of-AP runtime (e.g. the `omnigent claude` transcript\nforwarder). Either field may be absent; clients should leave\ncached values untouched for missing fields.",
        "properties": {
          "context_tokens": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "`input + cache_creation + cache_read` from the latest assistant `message.usage`. `None` on a window-only broadcast.",
            "title": "Context Tokens"
          },
          "context_window": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Resolved window in tokens (e.g. 200_000 normally, 1_000_000 with `opus[1m]` / `sonnet[1m]`). `None` on a tokens-only broadcast.",
            "title": "Context Window"
          },
          "conversation_id": {
            "description": "Session identifier.",
            "title": "Conversation Id",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "total_cost_usd": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Cumulative session spend in USD after this update, e.g. `0.42` — the server-computed total the cost-budget policy gates on. Present **only when the session is priced**; omitted (`None`, stripped by `exclude_none`) when unpriced or on a broadcast that carries no cost change, so the client keeps its prior value (the snapshot seeds the initial \"—\" for an unpriced session). Once a session is priced the total only grows, so it never reverts to unpriced.",
            "title": "Total Cost Usd"
          },
          "type": {
            "const": "session.usage",
            "description": "Always `\"session.usage\"`.",
            "title": "Type",
            "type": "string"
          },
          "usage_by_model": {
            "anyOf": [
              {
                "additionalProperties": {
                  "$ref": "#/components/schemas/ModelUsage"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Per-model breakdown of the same subtree usage after this update, keyed by raw harness model id, e.g. `{\"claude-sonnet-4-6\": ModelUsage(input_tokens=12000, ...)}`. `None` (stripped by `exclude_none`) on a broadcast that carries no per-model change, so the client keeps its cached map. Category: **transient** (SSE-only). On reconnect, clients seed the ring from the session snapshot's `last_total_tokens` and `context_window`, the cost indicator from `total_cost_usd`, and the per-model token breakdown from `usage_by_model`.",
            "title": "Usage By Model"
          }
        },
        "required": [
          "type",
          "conversation_id"
        ],
        "title": "SessionUsageEvent",
        "type": "object"
      },
      "SetCodexGoalRequest": {
        "additionalProperties": false,
        "description": "Request body for `PUT /v1/sessions/{id}/codex_goal`.",
        "properties": {
          "objective": {
            "description": "Goal objective text, e.g. `\"Finish the migration and keep tests green\"`. Must be non-empty after trimming and no longer than 4000 characters, matching Codex app-server's goal contract.",
            "maxLength": 4000,
            "minLength": 1,
            "title": "Objective",
            "type": "string"
          },
          "status": {
            "anyOf": [
              {
                "enum": [
                  "active",
                  "paused"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional user-selected goal status. `\"active\"` starts or resumes the goal, and `\"paused\"` stores it paused. Omit this field to preserve Codex's current lifecycle state.",
            "title": "Status"
          },
          "token_budget": {
            "anyOf": [
              {
                "exclusiveMinimum": 0,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional positive token budget, e.g. `40000`. Explicit JSON `null` clears the Codex goal budget; omitting the field leaves it absent from the forwarded request.",
            "title": "Token Budget"
          }
        },
        "required": [
          "objective"
        ],
        "title": "SetCodexGoalRequest",
        "type": "object"
      },
      "SetSharingRequest": {
        "description": "Body for `PUT /v1/sharing`.\n\nBoth fields are optional so an admin can update either setting\nindependently; at least one must be present.",
        "properties": {
          "public_sharing": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "title": "Public Sharing"
          },
          "sharing_mode": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Sharing Mode"
          }
        },
        "title": "SetSharingRequest",
        "type": "object"
      },
      "SkillSummary": {
        "description": "Safe subset of a discovered skill for API exposure.\n\nSurfaces the skill name and one-line description so clients\n(e.g. the web composer's slash-command menu) can list which\nskills the session has access to. The full skill `content`\nis intentionally omitted — it's only loaded server-side when\nthe harness invokes the skill, and it can be large.",
        "properties": {
          "description": {
            "description": "One-line summary from the SKILL.md frontmatter, e.g. `\"Triage open GitHub issues in the repo.\"`.",
            "title": "Description",
            "type": "string"
          },
          "name": {
            "description": "Skill identifier as parsed from the SKILL.md frontmatter, e.g. `\"triage-issues\"`. Lowercase kebab-case.",
            "title": "Name",
            "type": "string"
          }
        },
        "required": [
          "name",
          "description"
        ],
        "title": "SkillSummary",
        "type": "object"
      },
      "SlashCommandData": {
        "description": "Data payload for a slash-command invocation observed in a\nharness transcript (today: Claude Code's embedded TUI).\n\nListed in `NON_CONTENT_ITEM_TYPES` so the agent loop's\nhistory filter skips it — a downstream LLM never sees this as a\nphantom tool call. Field names mirror `function_call` so the\nweb renderer can reuse the tool-card layout.\n\n**Parameters**\n\n- `agent` — Harness/agent name, e.g. `\"claude-native-ui\"`. Serialized as `\"model\"` for parity with other items.",
        "properties": {
          "arguments": {
            "description": "Raw `<command-args>` text. Empty when none.",
            "title": "Arguments",
            "type": "string"
          },
          "kind": {
            "default": "skill",
            "description": "`\"skill\"` for plugin/Skill invocations, `\"command\"` for surfaced CLI built-ins (`/effort`, `/clear`, `/compact`, `/model`, `/ultrareview`). The web renderer uses this to pick the prefix label and icon. Defaults to `\"skill\"` so persisted items predating this field deserialize without backfill.",
            "enum": [
              "skill",
              "command"
            ],
            "title": "Kind",
            "type": "string"
          },
          "model": {
            "title": "Model",
            "type": "string"
          },
          "name": {
            "description": "Command name with leading `/` stripped, e.g. `\"dev-productivity:simplify\"`.",
            "title": "Name",
            "type": "string"
          },
          "output": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "`<local-command-stdout>` text when present, else `None` (the common case — Skills act via the next assistant turn, not stdout).",
            "title": "Output"
          }
        },
        "required": [
          "model",
          "name",
          "arguments"
        ],
        "title": "SlashCommandData",
        "type": "object"
      },
      "StoreHarnessCredentialRequest": {
        "description": "Request body for `POST /v1/hosts/{id}/harnesses/{harness}/credential`.\n\nCarries the credential in the body (never the URL). The secret field is\noptional so the `adopt` kind — which references an existing host env var\nby name rather than sending a value — can omit it.",
        "properties": {
          "base_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The gateway base URL, required for `kind=\"gateway\"`.",
            "title": "Base Url"
          },
          "default_model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional family default model id to pin.",
            "title": "Default Model"
          },
          "env_var": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "For `kind=\"adopt\"`, the host env var to reference.",
            "title": "Env Var"
          },
          "kind": {
            "description": "`\"key\"` (a vendor API key), `\"gateway\"` (a compatible proxy at `base_url`), or `\"adopt\"` (reference host env `env_var`).",
            "title": "Kind",
            "type": "string"
          },
          "secret": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The API key / gateway token for `key` / `gateway`; `None` for `adopt`.",
            "title": "Secret"
          },
          "wire_api": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Optional OpenAI wire protocol (`\"chat\"` / `\"responses\"`).",
            "title": "Wire Api"
          }
        },
        "required": [
          "kind"
        ],
        "title": "StoreHarnessCredentialRequest",
        "type": "object"
      },
      "TerminalCommandData": {
        "description": "Data payload for a runner-side terminal command (`!cmd`) observed\nin a harness transcript (today: Claude Code's embedded TUI).\n\nListed in `NON_CONTENT_ITEM_TYPES` so the agent loop never\ninjects this as phantom content into the LLM's message history.\n\nClaude Code writes two sibling transcript records per `!cmd`\ninvocation: one `<bash-input>` record and one combined\n`<bash-stdout>`/`<bash-stderr>` record. Each maps to one\n`terminal_command` item with `kind=\"input\"` or\n`kind=\"output\"` respectively.",
        "properties": {
          "input": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The raw command string, e.g. `\"pwd\"`. Present when `kind=\"input\"`, `None` otherwise.",
            "title": "Input"
          },
          "kind": {
            "description": "`\"input\"` for the command text, `\"output\"` for the combined stdout/stderr result.",
            "enum": [
              "input",
              "output"
            ],
            "title": "Kind",
            "type": "string"
          },
          "stderr": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Captured stderr text. Present when `kind=\"output\"`, `None` otherwise.",
            "title": "Stderr"
          },
          "stdout": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Captured stdout text. Present when `kind=\"output\"`, `None` otherwise.",
            "title": "Stdout"
          }
        },
        "required": [
          "kind"
        ],
        "title": "TerminalCommandData",
        "type": "object"
      },
      "ToolOutputDeltaEvent": {
        "description": "Incremental output from an in-progress function call.",
        "properties": {
          "call_id": {
            "description": "Function-call correlation id.",
            "title": "Call Id",
            "type": "string"
          },
          "delta": {
            "description": "Command stdout/stderr fragment.",
            "title": "Delta",
            "type": "string"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "type": {
            "const": "response.function_call_output.delta",
            "description": "Always `\"response.function_call_output.delta\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "call_id",
          "delta"
        ],
        "title": "ToolOutputDeltaEvent",
        "type": "object"
      },
      "TurnCancelledEvent": {
        "description": "Emitted when a turn is interrupted by the user or system.",
        "properties": {
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "session_id": {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "title": "Session Id",
            "type": "string"
          },
          "type": {
            "const": "turn.cancelled",
            "description": "Fixed literal `\"turn.cancelled\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "session_id"
        ],
        "title": "TurnCancelledEvent",
        "type": "object"
      },
      "TurnCompletedEvent": {
        "description": "Emitted when a turn finishes successfully with no pending work.",
        "properties": {
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "session_id": {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "title": "Session Id",
            "type": "string"
          },
          "type": {
            "const": "turn.completed",
            "description": "Fixed literal `\"turn.completed\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "session_id"
        ],
        "title": "TurnCompletedEvent",
        "type": "object"
      },
      "TurnFailedEvent": {
        "description": "Emitted when a turn fails due to an LLM error, timeout, or crash.",
        "properties": {
          "error": {
            "additionalProperties": true,
            "description": "Error details, e.g. `{\"message\": \"LLM timeout\", \"type\": \"TimeoutError\"}`.",
            "title": "Error",
            "type": "object"
          },
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "session_id": {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "title": "Session Id",
            "type": "string"
          },
          "type": {
            "const": "turn.failed",
            "description": "Fixed literal `\"turn.failed\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "session_id"
        ],
        "title": "TurnFailedEvent",
        "type": "object"
      },
      "TurnStartedEvent": {
        "description": "Emitted when the runner starts a new turn for a session.",
        "properties": {
          "sequence_number": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "title": "Sequence Number"
          },
          "session_id": {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "title": "Session Id",
            "type": "string"
          },
          "type": {
            "const": "turn.started",
            "description": "Fixed literal `\"turn.started\"`.",
            "title": "Type",
            "type": "string"
          }
        },
        "required": [
          "type",
          "session_id"
        ],
        "title": "TurnStartedEvent",
        "type": "object"
      },
      "UpdateCodexGoalStatusRequest": {
        "additionalProperties": false,
        "description": "Request body for `PATCH /v1/sessions/{id}/codex_goal/status`.\n\nCodex app-server represents pause/resume as `thread/goal/set` status\nupdates. Omnigent exposes the two user-driven transitions explicitly:\n`\"paused\"` pauses an active goal, and `\"active\"` resumes a paused,\nblocked, or usage-limited goal.",
        "properties": {
          "status": {
            "description": "Target Codex goal status, either `\"paused\"` or `\"active\"`.",
            "enum": [
              "active",
              "paused"
            ],
            "title": "Status",
            "type": "string"
          }
        },
        "required": [
          "status"
        ],
        "title": "UpdateCodexGoalStatusRequest",
        "type": "object"
      },
      "UpdateCommentRequest": {
        "description": "Request body for `PATCH /sessions/{id}/comments/{comment_id}`.",
        "properties": {
          "body": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "New comment body. `None` leaves it unchanged.",
            "title": "Body"
          },
          "status": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "New status, e.g. `\"addressed\"`. `None` leaves it unchanged.",
            "title": "Status"
          }
        },
        "title": "UpdateCommentRequest",
        "type": "object"
      },
      "UpdateDefaultPolicyRequest": {
        "additionalProperties": false,
        "description": "Request body for `PATCH /v1/policies/{policy_id}`.\n\nAll fields are optional; `None` fields are left unchanged.\nUnknown fields (including `type`, which is immutable) are\nrejected with `422`.",
        "properties": {
          "enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "New enabled flag. `None` leaves it unchanged.",
            "title": "Enabled"
          },
          "handler": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "New handler path or URL. `None` leaves it unchanged.",
            "title": "Handler"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "New policy name. `None` leaves it unchanged.",
            "title": "Name"
          }
        },
        "title": "UpdateDefaultPolicyRequest",
        "type": "object"
      },
      "UpdateProjectRequest": {
        "additionalProperties": false,
        "description": "Request body for `PATCH /v1/projects/{project_id}`.\n\nAll fields optional; `None` leaves a field unchanged.",
        "properties": {
          "config": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "New config object to replace the stored one. `None` leaves it unchanged; an empty object `{}` clears the stored defaults.",
            "title": "Config"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "New project name. `None` leaves it unchanged; otherwise trimmed, non-empty, at most 100 characters.",
            "title": "Name"
          }
        },
        "title": "UpdateProjectRequest",
        "type": "object"
      },
      "UpdateSessionPolicyRequest": {
        "additionalProperties": false,
        "description": "Request body for `PATCH /v1/sessions/{session_id}/policies/{policy_id}`.\n\nAll fields are optional; `None` fields are left unchanged.\nUnknown fields (including `type`, which is immutable) are\nrejected with `422`.",
        "properties": {
          "enabled": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "New enabled flag. `None` leaves it unchanged.",
            "title": "Enabled"
          },
          "handler": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "New handler path or URL. `None` leaves it unchanged.",
            "title": "Handler"
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "New policy name. `None` leaves it unchanged.",
            "title": "Name"
          }
        },
        "title": "UpdateSessionPolicyRequest",
        "type": "object"
      },
      "UpdateSessionRequest": {
        "additionalProperties": false,
        "description": "Request body for `PATCH /v1/sessions/{id}`.\n\nThe Alpha runner-state pivot makes this endpoint the mutable\nsession affinity primitive when `runner_id` is provided. The\nserver validates that the runner is online, then replaces\n`conversations.runner_id`. Existing session metadata updates\nremain supported for clients that update title, labels, or\nreasoning effort through the sessions API.",
        "properties": {
          "archived": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "New archived state. `True` archives (hides the session from the default sidebar listing), `False` unarchives, `None` leaves unchanged. Owner-only (unlike `title`, which needs only edit access).",
            "title": "Archived"
          },
          "collaboration_mode": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Codex-native collaboration-mode string. `\"plan\"` enters Plan mode and `\"default\"` returns to Default mode for subsequent Codex turns. Only valid for sessions stamped with the codex-native wrapper label. Omitted leaves unchanged.",
            "title": "Collaboration Mode"
          },
          "cost_control_mode_override": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Per-session cost-control switch: `\"on\"` activates the spec's configured cost-control mode, `\"off\"` disables cost control for this session. Explicit JSON `null` clears the override back to the spec default; omitting the field leaves it unchanged (`\"off\"` is a real value here, so the field's *presence* — not a clear alias — is the clear signal, unlike `model_override`).",
            "title": "Cost Control Mode Override"
          },
          "external_session_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Runtime-native session id captured by a wrapper bridge (e.g. Claude Code's session uuid for `omnigent claude` sessions). Idempotent on same-value writes; the server rejects attempts to overwrite an already-set different value with `invalid_input` to surface programmer errors. `None` leaves unchanged.",
            "title": "External Session Id"
          },
          "labels": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "string"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Guardrails labels to upsert. Merges with existing labels; keys not present are left untouched.",
            "title": "Labels"
          },
          "model_override": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Per-session LLM model override, e.g. `\"claude-opus-4-7\"`. The value is forwarded as-is to the executor at turn start; the server does not enumerate valid models. Clear aliases such as `\"default\"`, `\"off\"`, or `\"reset\"` remove the override (matching the REPL's `/model` semantics). `None` leaves unchanged.",
            "title": "Model Override"
          },
          "project_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "File this session into a first-class project (see `designs/PROJECTS_PRD.md`). A non-empty id moves the session into that project; the empty string `\"\"` unfiles it. **Omitting** the field leaves membership unchanged; an explicit `null` is rejected (400) so it can't silently unfile. Owner-only: because projects are owner-private, only the session owner may file it, and only into a project they own — the server verifies both. Independent of the legacy `omni_project` label, which is set via `labels`.",
            "title": "Project Id"
          },
          "reasoning_effort": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Per-session reasoning-effort hint. Accepted metadata values are `\"none\"`, `\"minimal\"`, `\"low\"`, `\"medium\"`, `\"high\"`, `\"xhigh\"`, and `\"max\"`. Provider-specific support is validated when a turn executes. Clear aliases such as `\"default\"` remove the session override. `None` leaves unchanged.",
            "title": "Reasoning Effort"
          },
          "runner_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Identifier of a registered runner, e.g. `\"runner_abc123\"`. `None` leaves runner binding unchanged.",
            "title": "Runner Id"
          },
          "silent": {
            "default": false,
            "description": "When `True`, persist metadata changes but skip the runner-side side effects — specifically the native `/effort` / `/model` / Codex collaboration-mode forwards into the live runtime. Used by automatic bind-time handoffs (web's sticky-pref apply on session switch, the REPL's pre-create `/model` snapshot) where injecting a visible slash command into a freshly-spawned pane would render as an unexpected \"Command model X\" item before the user has sent anything. Default `False` preserves the user-driven picker / `/model` behaviour where the live forward IS the desired feedback.",
            "title": "Silent",
            "type": "boolean"
          },
          "subagent_routing_override": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Per-session subagent-routing switch: `\"on\"` routes subagent spawns, `\"off\"` leaves them unrouted. Explicit JSON `null` clears the override, which lands the session on Default (the same behavior as `\"off\"` — nothing is inherited); omitting the field leaves it unchanged (same presence-is-the-clear-signal rule as `cost_control_mode_override`). Effective on the next spawn, so it can be changed at any point in a session.",
            "title": "Subagent Routing Override"
          },
          "terminal_launch_args": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "Per-session native-terminal pass-through args, e.g. `[\"--dangerously-skip-permissions\"]`. A list (including `[]`) replaces the stored value wholesale — resume is last-write-wins, never an append. Bounds (count / length) are validated server-side. `None` leaves unchanged.",
            "title": "Terminal Launch Args"
          },
          "title": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "New title, e.g. `\"debugging auth flow\"`. `None` leaves unchanged.",
            "title": "Title"
          }
        },
        "title": "UpdateSessionRequest",
        "type": "object"
      },
      "UpsertMCPServerRequest": {
        "description": "Request body for creating or updating a session agent MCP server.\n\n`env` is still excluded. `headers` is accepted for HTTP servers;\nwhen omitted, existing headers in the bundle are preserved unchanged.",
        "properties": {
          "args": {
            "items": {
              "type": "string"
            },
            "maxItems": 64,
            "title": "Args",
            "type": "array"
          },
          "command": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Command"
          },
          "description": {
            "anyOf": [
              {
                "maxLength": 512,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Description"
          },
          "headers": {
            "anyOf": [
              {
                "additionalProperties": {
                  "type": "string"
                },
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "title": "Headers"
          },
          "name": {
            "maxLength": 128,
            "minLength": 1,
            "pattern": "^[A-Za-z0-9_-][A-Za-z0-9_.-]{0,127}$",
            "title": "Name",
            "type": "string"
          },
          "transport": {
            "enum": [
              "http",
              "stdio"
            ],
            "title": "Transport",
            "type": "string"
          },
          "url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Url"
          }
        },
        "required": [
          "name",
          "transport"
        ],
        "title": "UpsertMCPServerRequest",
        "type": "object"
      },
      "Usage": {
        "description": "Token usage statistics for a response.",
        "properties": {
          "cache_creation_input_tokens": {
            "default": 0,
            "description": "Prompt tokens written to the provider prompt cache (cache creation), billed at a premium rate. Like `cache_read_input_tokens`, this is separate from `input_tokens`; `0` when not reported.",
            "title": "Cache Creation Input Tokens",
            "type": "integer"
          },
          "cache_read_input_tokens": {
            "default": 0,
            "description": "Prompt tokens served from a provider prompt cache (cache hit), billed at a reduced rate. Reported by Anthropic-style providers as a count *separate* from `input_tokens` (which carries only the non-cached portion); `0` when the provider does not break out cache usage. Consumed by the cache-aware server-side cost path.",
            "title": "Cache Read Input Tokens",
            "type": "integer"
          },
          "context_tokens": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Context-fill estimate for the next turn — set only by executors that make multiple LLM sub-calls per turn (e.g. `openai-agents`). For single-call executors this is absent and `total_tokens` serves the same purpose. The toolbar context ring and `/context` command use this field when present, falling back to `total_tokens`.",
            "title": "Context Tokens"
          },
          "cost_usd": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "Authoritative per-turn cost in USD reported directly by the harness/provider (e.g. GitHub Copilot's AI-credit total). When present, the server-side cost path uses it in preference to the catalog token-price estimate; `None` when the harness doesn't report a cost (the common case, where cost is computed from token counts x catalog pricing).",
            "title": "Cost Usd"
          },
          "input_tokens": {
            "default": 0,
            "description": "Number of input (prompt) tokens consumed.",
            "title": "Input Tokens",
            "type": "integer"
          },
          "model": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "default": null,
            "description": "The LLM model the harness actually used for this turn, e.g. `\"claude-opus-4-8\"` or `\"databricks-gpt-5-5\"`. Reported by relay executors so the server-side cost path can price the turn even when the agent spec pins no `llm.model` (e.g. supervisors that delegate / use the harness default). `None` when the executor doesn't report it; the cost path then falls back to the session override / spec model.",
            "title": "Model"
          },
          "output_tokens": {
            "default": 0,
            "description": "Number of output (completion) tokens generated.",
            "title": "Output Tokens",
            "type": "integer"
          },
          "output_tokens_details": {
            "$ref": "#/components/schemas/UsageDetails",
            "description": "Breakdown of output token usage (e.g. reasoning tokens)."
          },
          "total_tokens": {
            "default": 0,
            "description": "Sum of input and output tokens across all LLM sub-calls for this turn (billing total).",
            "title": "Total Tokens",
            "type": "integer"
          }
        },
        "title": "Usage",
        "type": "object"
      },
      "UsageDetails": {
        "description": "Breakdown of output token usage.",
        "properties": {
          "reasoning_tokens": {
            "default": 0,
            "description": "Number of tokens consumed by chain-of-thought reasoning.",
            "title": "Reasoning Tokens",
            "type": "integer"
          }
        },
        "title": "UsageDetails",
        "type": "object"
      },
      "UsageReport": {
        "description": "Aggregated LLM usage for the calling user, powering `omni usage`.\n\nThe cost summary is sourced from the per-user daily-cost rollup\n(`user_daily_cost`), which attributes spend to the UTC calendar day it\noccurred on. Windows are therefore calendar-day buckets summed back from\ntoday — `cost_today` / `cost_last_7d` / `cost_last_30d` — not\nrolling wall-clock hours, so a weeks-old session touched today is not\ncounted wholly in \"today\".\n\nThe `sessions` list is a separate detail view built from each session's\ncumulative `session_usage` (newest activity first), so the summary and\nthe per-session list come from different sources and are not guaranteed\nto tie out to the cent (the summary counts every priced turn ever\nrecorded for the user; the list only covers the user's currently-listed\ntop-level sessions).",
        "properties": {
          "cost_last_30d": {
            "default": 0,
            "description": "Total spend over the last 30 UTC days (incl. today).",
            "title": "Cost Last 30D",
            "type": "number"
          },
          "cost_last_7d": {
            "default": 0,
            "description": "Total spend over the last 7 UTC days (incl. today).",
            "title": "Cost Last 7D",
            "type": "number"
          },
          "cost_today": {
            "default": 0,
            "description": "Total spend on the current UTC day.",
            "title": "Cost Today",
            "type": "number"
          },
          "daily_costs": {
            "items": {
              "$ref": "#/components/schemas/DailyCost"
            },
            "title": "Daily Costs",
            "type": "array"
          },
          "object": {
            "const": "usage_report",
            "default": "usage_report",
            "title": "Object",
            "type": "string"
          },
          "sessions": {
            "description": "Per-session detail, newest activity first.",
            "items": {
              "$ref": "#/components/schemas/SessionUsage"
            },
            "title": "Sessions",
            "type": "array"
          },
          "total_cost_usd": {
            "default": 0,
            "description": "All-time total spend from the daily rollup.",
            "title": "Total Cost Usd",
            "type": "number"
          }
        },
        "title": "UsageReport",
        "type": "object"
      },
      "ValidationError": {
        "properties": {
          "ctx": {
            "title": "Context",
            "type": "object"
          },
          "input": {
            "title": "Input"
          },
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "title": "Location",
            "type": "array"
          },
          "msg": {
            "title": "Message",
            "type": "string"
          },
          "type": {
            "title": "Error Type",
            "type": "string"
          }
        },
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError",
        "type": "object"
      }
    },
    "securitySchemes": {
      "proxyHeaderAuth": {
        "description": "Trusted-proxy identity header (header-auth mode, the default). The header name is configurable via `OMNIGENT_AUTH_HEADER`.",
        "in": "header",
        "name": "X-Forwarded-Email",
        "type": "apiKey"
      },
      "sessionCookieAuth": {
        "description": "Signed session cookie minted after an interactive OIDC or accounts login (oidc / accounts auth modes). Named `ap_session` over HTTP (the advertised local default); under HTTPS the secure `__Host-ap_session` prefixed form is used instead.",
        "in": "cookie",
        "name": "ap_session",
        "type": "apiKey"
      }
    }
  },
  "info": {
    "description": "Omnigent is an open-source meta-harness for building and running AI agents. This is the REST API exposed by the Omnigent server: use it to create and drive **sessions**, manage **agents**, **hosts**, and **runners**, attach **contextual policies**, post **comments**, and work with session **resources** — files, terminals, and sandboxed environments.\n\n## Base URL\n\nOmnigent is self-hosted. The server binds `http://127.0.0.1:6767` by default (`omnigent server`); point the base URL at your own deployment.\n\n## Authentication\n\nThere is no API-key or bearer-token scheme. Identity is supplied by the deployment's configured auth provider (`OMNIGENT_AUTH_PROVIDER`):\n\n- **Trusted proxy header** (default) — an upstream proxy injects an identity header (`X-Forwarded-Email`, configurable). Single-user local runtimes fall back to a reserved `local` user.\n- **Session cookie** — a signed session cookie minted after an interactive OIDC or accounts login. It is named `ap_session` over HTTP (the advertised local default) and `__Host-ap_session` under HTTPS, where the `__Host-` prefix guards against subdomain cookie-tossing.\n\nAuth is configured server-side; clients send the cookie or proxy header according to your deployment.\n\n## Streaming\n\n`GET /v1/sessions/{session_id}/stream` streams Server-Sent Events (`text/event-stream`). Each event conforms to the `ServerStreamEvent` schema documented below.\n",
    "title": "Omnigent Server",
    "version": "0.1.0"
  },
  "openapi": "3.2.0",
  "paths": {
    "/.well-known/omnigent.json": {
      "get": {
        "description": "Version manifest for NON-BROWSER clients — chiefly the desktop shell.\n\nThe desktop shell ships and updates on its own cadence, so any\ninstalled build can meet any server version. The shell is the side\nthat must adapt (the user may not update it for months), and to adapt\nit first has to know what it is talking to. This endpoint is that\nanswer, fetched BEFORE the SPA loads — unlike `/v1/info`, which the\nSPA reads after boot and which is therefore useless to the shell when\ndeciding how to open a window in the first place.\n\nContract, in the order a client should apply it:\n\n1. `manifest_version` (int) versions this ENVELOPE. Clients gate on\n   `>=`, never `==`, so a newer server keeps working with an older\n   shell. See `WELL_KNOWN_MANIFEST_VERSION` for when it bumps —\n   adding a field never does.\n2. `server_version` (str) is the installed omnigent package version,\n   the same value as `/api/version` and `/v1/info.server_version`.\n   Informational: for display and bug reports, NOT for gating. Gate on\n   the fields below, which state capability directly rather than making\n   every client hardcode \"which release added X\".\n3. `min_desktop_version` (str | null) is the oldest desktop build this\n   server still supports. Null means no floor — the overwhelmingly\n   common case, and what every shipped shell must treat as \"fine\".\n   A server sets it only to signal a genuinely breaking change.\n4. Everything else is additive detail an older client may ignore\n   wholesale. `ui` describes where server-driven chrome lives, so a\n   shell can place its own window furniture without guessing from the\n   version number: `server_picker` is `\"sidebar\"` on builds that\n   dock the picker at the sidebar's bottom (it was `\"titlebar\"`,\n   centered in the macOS title-bar strip, before this).\n\nUnknown fields MUST be ignored, and a missing manifest (404 — every\nserver older than this route) MUST be treated as the pre-manifest\nbaseline, not an error: the shell falls back to its current behavior.\nThat is what makes an old shell + new server and a new shell + old\nserver both work.\n\nAuthentication: intentionally UNAUTHED, like `/v1/info`. A client\nmust be able to read this before it holds a session cookie — the whole\npoint is to consult it before loading the app. It exposes only the\nversion already public via `/api/version` plus coarse UI-shape\nstrings, so there is nothing here to leak.\n\nServed under `/.well-known/` (RFC 8615) so it sits at a fixed,\nguessable path that never collides with an SPA client route.\n\n**Returns:** The manifest described above.",
        "operationId": "well_known_manifest__well_known_omnigent_json_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Well Known Manifest  Well Known Omnigent Json Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Well Known Manifest"
      }
    },
    "/api/version": {
      "get": {
        "description": "Return the installed omnigent package version.\n\nUsed by the web UI to include version info in bug reports.\n\n**Returns:** `{\"version\": \"<semver string>\"}`, e.g. `{\"version\": \"0.1.0\"}`.",
        "operationId": "version_api_version_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "type": "string"
                  },
                  "title": "Response Version Api Version Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Version",
        "tags": [
          "system"
        ]
      }
    },
    "/health": {
      "get": {
        "description": "Liveness check with optional session-scoped runner status.\n\nWithout session params, returns `{\"status\": \"ok\"}` (bare\nliveness). With `session_id`, adds a single `session`\nobject. With `session_ids` (comma-separated), adds a\n`sessions` dict keyed by id — used by the sidebar to\nbatch-check all visible sessions in one request. The batch\npath runs a single SQL `IN` query, not N per-id round-trips.\n\nEach per-session object carries both `runner_online` (strict\nrunner reachability) and `host_online` (host tunnel live, or\n`None` when the session has no host binding) — see\n`~omnigent.server.routes.sessions.SessionLiveness`.\n\n**Returns:** `{\"status\": \"ok\"}` with optional `session` and/or `sessions` fields. Each session object has shape `{\"runner_online\": bool, \"host_online\": bool | None, \"host_version\": str | None}` (the single `session` object also includes its `id`). `host_version` is the bound host's reported version, or `None` when there's no host binding / the version isn't resolvable on this replica.",
        "operationId": "health_health_get",
        "parameters": [
          {
            "description": "Optional single session id, e.g. `\"conv_abc123\"`.",
            "in": "query",
            "name": "session_id",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Session Id"
            }
          },
          {
            "description": "Optional comma-separated session ids for batch lookup, e.g. `\"conv_abc,conv_def,conv_ghi\"`.",
            "in": "query",
            "name": "session_ids",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Session Ids"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Health Health Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Health",
        "tags": [
          "system"
        ]
      }
    },
    "/v1/agents": {
      "get": {
        "description": "List built-in agents with cursor-based pagination.\n\nReturns only built-in agents — `agent_store.list()` filters\n`session_id IS NULL` — so session-scoped agents never appear.\n\n**Returns:** A `PaginatedList` of built-in agents.",
        "operationId": "list_builtin_agents_v1_agents_get",
        "parameters": [
          {
            "description": "Maximum number of agents to return (1-1000).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "maximum": 1000,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Cursor — return agents after this id.",
            "in": "query",
            "name": "after",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "After"
            }
          },
          {
            "description": "Cursor — return agents before this id.",
            "in": "query",
            "name": "before",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Before"
            }
          },
          {
            "description": "Sort order, `\"asc\"` or `\"desc\"`.",
            "in": "query",
            "name": "order",
            "required": false,
            "schema": {
              "default": "desc",
              "pattern": "^(asc|desc)$",
              "title": "Order",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedList"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List Builtin Agents",
        "tags": [
          "agents"
        ]
      }
    },
    "/v1/harnesses": {
      "get": {
        "operationId": "list_harnesses_v1_harnesses_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response List Harnesses V1 Harnesses Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List Harnesses",
        "tags": [
          "harnesses"
        ]
      }
    },
    "/v1/hosts": {
      "get": {
        "description": "List all hosts owned by the authenticated user.\n\nReturns both online and offline hosts, with live runner\ninformation for online hosts.\n\n**Returns:** `{\"hosts\": [...]}` with host details — `host_id`, `name`, `owner`, `status`, `sandbox_provider`, `configured_harnesses`, and `gateway_inference` (`None` when no connected host has reported it to this replica).",
        "operationId": "list_hosts_v1_hosts_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "items": {
                      "additionalProperties": true,
                      "type": "object"
                    },
                    "type": "array"
                  },
                  "title": "Response List Hosts V1 Hosts Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List Hosts",
        "tags": [
          "hosts"
        ]
      }
    },
    "/v1/hosts/{host_id}": {
      "get": {
        "description": "Get details for a single host.\n\n**Returns:** Host details dict — the `list_hosts` fields (including `gateway_inference`, `None` when unreported) plus `runners`.\n\n**Raises**\n\n- `HTTPException` — 404 if the host does not exist.",
        "operationId": "get_host_v1_hosts__host_id__get",
        "parameters": [
          {
            "description": "Host identifier, e.g. `\"host_a1b2c3d4...\"`.",
            "in": "path",
            "name": "host_id",
            "required": true,
            "schema": {
              "title": "Host Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Get Host V1 Hosts  Host Id  Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get Host",
        "tags": [
          "hosts"
        ]
      }
    },
    "/v1/hosts/{host_id}/credentials/detected": {
      "get": {
        "description": "List adoptable credentials already present on a connected host.\n\nBacks the setup dialog's \"adopt an existing credential\" affordance: the\nhost reports which UI-auth-family credentials it already has as\nNON-secret descriptors (family + source label + env var name), so the UI\ncan offer a one-click \"Use it\". Owner-scoped and flag-gated like the\ncredential-write route (404 when disabled). Never returns a secret value.\n\n**Returns:** `{\"object\": \"detected_credentials\", \"credentials\": [...]}`.\n\n**Raises**\n\n- `HTTPException` — 404 when disabled or host unknown, 403 when not the owner, 409 when offline, 502/504 on host failure/timeout.",
        "operationId": "detect_host_credentials_v1_hosts__host_id__credentials_detected_get",
        "parameters": [
          {
            "description": "Host identifier.",
            "in": "path",
            "name": "host_id",
            "required": true,
            "schema": {
              "title": "Host Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Detect Host Credentials V1 Hosts  Host Id  Credentials Detected Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Detect Host Credentials",
        "tags": [
          "hosts"
        ]
      }
    },
    "/v1/hosts/{host_id}/directories": {
      "post": {
        "description": "Create a new directory on a host.\n\nBacks the Web UI workspace picker's \"New folder\" action so a\nuser can make a fresh directory to start a session in without\ndropping to a terminal. Owner-scoped exactly like the\nfilesystem browse endpoints (`GET /v1/hosts/{id}/filesystem`):\nonly the host owner can create directories, and — like browse —\nthis is NOT scoped to a session. The workspace-boundary check\nstill runs at session-create time, so creating a directory here\ndoes not by itself grant an agent access to it.\n\n**Parameters**\n\n- `body` — Request body carrying the absolute (or tilde-prefixed) `path` to create.\n\n**Returns:** `{\"object\": \"directory\", \"path\": \"<created abs path>\"}`.\n\n**Raises**\n\n- `HTTPException` — 404 if host not found, 403 if not owned by caller, 409 if host is offline or the directory could not be created (already exists / permission denied), 400 on path validation, 504 on host timeout, 502 on host I/O failure.",
        "operationId": "create_host_directory_v1_hosts__host_id__directories_post",
        "parameters": [
          {
            "description": "Host identifier, e.g. `\"host_a1b2c3d4...\"`.",
            "in": "path",
            "name": "host_id",
            "required": true,
            "schema": {
              "title": "Host Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDirectoryRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Create Host Directory V1 Hosts  Host Id  Directories Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Create Host Directory",
        "tags": [
          "hosts"
        ]
      }
    },
    "/v1/hosts/{host_id}/filesystem": {
      "get": {
        "description": "List the contents of the host daemon's home directory.\n\nEmpty trailing path → forward `~` to the host (the host\nexpands against its own process owner). Used by the\nWeb UI's directory picker to show the \"root\" view.\n\n**Returns:** `{\"object\": \"list\", \"data\": [...], \"has_more\": bool}` mirroring the existing session-scoped filesystem endpoint shape.\n\n**Raises**\n\n- `HTTPException` — 404 if host not found, 403 if not owned by caller, 409 if host is offline, 504 on host timeout, 502 on host I/O failure.",
        "operationId": "list_host_filesystem_root_v1_hosts__host_id__filesystem_get",
        "parameters": [
          {
            "description": "Host identifier, e.g. `\"host_a1b2c3d4...\"`.",
            "in": "path",
            "name": "host_id",
            "required": true,
            "schema": {
              "title": "Host Id",
              "type": "string"
            }
          },
          {
            "description": "Max entries per page.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "maximum": 1000,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Optional forward pagination cursor (entry path), e.g. `\"/Users/corey/projects/m\"`.",
            "in": "query",
            "name": "after",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "After"
            }
          },
          {
            "description": "Optional backward pagination cursor.",
            "in": "query",
            "name": "before",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Before"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response List Host Filesystem Root V1 Hosts  Host Id  Filesystem Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List Host Filesystem Root",
        "tags": [
          "hosts"
        ]
      }
    },
    "/v1/hosts/{host_id}/filesystem/{path}": {
      "get": {
        "description": "List the contents of a directory on a host.\n\nUsed by the Web UI's directory picker (and stat-style\nexistence checks) to render the host's filesystem before\nany runner exists. Owner-scoped: only the host owner can\nbrowse. NOT scoped to a session — this endpoint exposes\nthe entire host filesystem to the authenticated host owner\nper `designs/SESSION_WORKSPACE_SELECTION.md` \"Security\nsurface\".\n\n**Returns:** `{\"object\": \"list\", \"data\": [...], \"has_more\": bool}`.\n\n**Raises**\n\n- `HTTPException` — 404 (host or path missing), 403 (not owner), 409 (offline), 400 (path validation), 504 (timeout), 502 (host I/O).",
        "operationId": "list_host_filesystem_v1_hosts__host_id__filesystem__path__get",
        "parameters": [
          {
            "description": "Host identifier.",
            "in": "path",
            "name": "host_id",
            "required": true,
            "schema": {
              "title": "Host Id",
              "type": "string"
            }
          },
          {
            "description": "Absolute path on the host (e.g. `\"/Users/corey/universe\"`) OR a tilde-prefixed path (`\"~/foo\"`). The host expands `~` itself. FastAPI's `:path` converter strips the leading `/` from the URL, so we re-add it for absolute paths.",
            "in": "path",
            "name": "path",
            "required": true,
            "schema": {
              "title": "Path",
              "type": "string"
            }
          },
          {
            "description": "Max entries per page.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "maximum": 1000,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Optional forward pagination cursor.",
            "in": "query",
            "name": "after",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "After"
            }
          },
          {
            "description": "Optional backward pagination cursor.",
            "in": "query",
            "name": "before",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Before"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response List Host Filesystem V1 Hosts  Host Id  Filesystem  Path  Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List Host Filesystem",
        "tags": [
          "hosts"
        ]
      }
    },
    "/v1/hosts/{host_id}/harnesses/{harness}/credential": {
      "post": {
        "description": "Write a harness provider credential onto a connected host.\n\nBacks the Web UI setup dialog's \"Add a credential\" action so a user can\nconfigure a Claude / Codex / Pi credential on a connected host without a\nterminal. Owner-scoped, allowlisted, and gated behind\n`harness_install` release feature exactly like the install route\n(404 when disabled). The host daemon does the write with the same\nnon-interactive core the `omnigent setup` wizard uses.\n\nSecurity: the server is an authz'd pass-through — it validates\nownership + the allowlist and forwards the secret over the (TLS) tunnel;\nit never persists the secret or logs it. The secret rides in the request\nbody (not the URL), and the frame field is redaction-named so it never\nlands on a telemetry span.\n\n**Parameters**\n\n- `body` — The credential payload (kind + secret / gateway / adopt).\n\n**Returns:** `{\"object\": \"harness_credential\", \"harness\": ..., \"configured_harnesses\": {...}, \"gateway_inference\": {...} | None}` — refreshed readiness so the UI can flip the badge without a reconnect, plus the refreshed gateway-inference map (`None` when the host didn't report one).\n\n**Raises**\n\n- `HTTPException` — 404 when disabled or host unknown, 400 when the harness isn't UI-configurable or the body is invalid, 403 when not the owner, 409 when offline, 502 on host-side failure, 504 on timeout.",
        "operationId": "store_host_harness_credential_v1_hosts__host_id__harnesses__harness__credential_post",
        "parameters": [
          {
            "description": "Host identifier.",
            "in": "path",
            "name": "host_id",
            "required": true,
            "schema": {
              "title": "Host Id",
              "type": "string"
            }
          },
          {
            "description": "Harness being configured, e.g. `\"claude\"`.",
            "in": "path",
            "name": "harness",
            "required": true,
            "schema": {
              "title": "Harness",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StoreHarnessCredentialRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Store Host Harness Credential V1 Hosts  Host Id  Harnesses  Harness  Credential Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Store Host Harness Credential",
        "tags": [
          "hosts"
        ]
      }
    },
    "/v1/hosts/{host_id}/harnesses/{harness}/install": {
      "post": {
        "description": "Install a missing, npm-installable harness CLI onto a host.\n\nBacks the Web UI's New Chat dialog \"Install\" action so a user can\ninstall a harness the connected host is missing without dropping to a\nterminal. Owner-scoped like the other host actions: only the host owner\nmay install onto it. Scoped to the UI-installable allowlist (claude,\ncodex, pi, opencode, qwen) — curl/brew and interactive-auth harnesses\nare refused. The whole route is gated behind\n`harness_install` in `OMNIGENT_FEATURES` (default off): when\ndisabled it returns 404 so the feature is invisible until opted in.\n\nConcurrent requests for the same (host, harness) coalesce onto one\nin-flight install so a double-click can't fire two global npm installs.\n\n**Returns:** `{\"object\": \"harness_install\", \"harness\": ..., \"configured_harnesses\": {...}, \"gateway_inference\": {...} | None}` — the host's refreshed readiness map so the UI can flip the badge without a reconnect, plus its refreshed gateway-inference map (`None` when the host didn't report one).\n\n**Raises**\n\n- `HTTPException` — 404 when the feature is disabled or the host is unknown, 400 when the harness is not UI-installable, 403 when the caller is not the host owner, 409 when the host is offline, 502 on a host-side install failure, 504 on host timeout.",
        "operationId": "install_host_harness_v1_hosts__host_id__harnesses__harness__install_post",
        "parameters": [
          {
            "description": "Host identifier, e.g. `\"host_a1b2c3d4...\"`.",
            "in": "path",
            "name": "host_id",
            "required": true,
            "schema": {
              "title": "Host Id",
              "type": "string"
            }
          },
          {
            "description": "Harness identifier to install, e.g. `\"claude\"`.",
            "in": "path",
            "name": "harness",
            "required": true,
            "schema": {
              "title": "Harness",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Install Host Harness V1 Hosts  Host Id  Harnesses  Harness  Install Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Install Host Harness",
        "tags": [
          "hosts"
        ]
      }
    },
    "/v1/hosts/{host_id}/harnesses/{harness}/model-options": {
      "get": {
        "description": "Return pre-launch model choices resolved by the selected host.\n\nA preview of the host's ambient default catalog, not a binding\nsnapshot: launch re-resolves with the session's agent spec, and the\nin-session picker reflects that launch snapshot once the runner is up.",
        "operationId": "get_host_model_options_v1_hosts__host_id__harnesses__harness__model_options_get",
        "parameters": [
          {
            "in": "path",
            "name": "host_id",
            "required": true,
            "schema": {
              "title": "Host Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "harness",
            "required": true,
            "schema": {
              "title": "Harness",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "items": {
                      "additionalProperties": true,
                      "type": "object"
                    },
                    "type": "array"
                  },
                  "title": "Response Get Host Model Options V1 Hosts  Host Id  Harnesses  Harness  Model Options Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get Host Model Options",
        "tags": [
          "hosts"
        ]
      }
    },
    "/v1/hosts/{host_id}/runners": {
      "post": {
        "description": "Launch a runner on a host for a session.\n\nGenerates a binding token, writes the expected runner_id\nto the session row, sends the launch command to the host,\nand waits for the host's acknowledgement.\n\n**Parameters**\n\n- `body` — Launch request with `session_id` and `workspace`.\n\n**Returns:** `{\"runner_id\": ..., \"status\": \"launching\"}`.\n\n**Raises**\n\n- `HTTPException` — 404 if host not found, 409 if host offline, 403 if caller doesn't own the host, 400 if session already has a runner.",
        "operationId": "launch_runner_v1_hosts__host_id__runners_post",
        "parameters": [
          {
            "description": "Target host, e.g. `\"host_a1b2c3d4...\"`.",
            "in": "path",
            "name": "host_id",
            "required": true,
            "schema": {
              "title": "Host Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LaunchRunnerRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Launch Runner V1 Hosts  Host Id  Runners Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Launch Runner",
        "tags": [
          "hosts"
        ]
      }
    },
    "/v1/hosts/{host_id}/worktrees": {
      "get": {
        "description": "List the git worktrees of a repository on a host.\n\nUsed by the Web UI's new-session worktree picker to show the\nworktrees a session can start in directly. Owner-scoped exactly\nlike the filesystem browse endpoints; NOT scoped to a session.\nA path that is not a git repository is reported as 400 so the\npicker can quietly fall back to \"no worktrees\".\n\n**Returns:** `{\"object\": \"list\", \"data\": [{path, branch, is_main, detached}, ...]}` (main first).\n\n**Raises**\n\n- `HTTPException` — 404 if host not found, 403 if not owned by caller, 409 if host is offline/unresponsive, 400 on path validation or a non-git path.",
        "operationId": "list_host_worktrees_v1_hosts__host_id__worktrees_get",
        "parameters": [
          {
            "description": "Host identifier, e.g. `\"host_a1b2c3d4...\"`.",
            "in": "path",
            "name": "host_id",
            "required": true,
            "schema": {
              "title": "Host Id",
              "type": "string"
            }
          },
          {
            "description": "Absolute path inside the repo on the host to list worktrees for, e.g. `\"/Users/alice/myrepo\"`.",
            "in": "query",
            "name": "path",
            "required": true,
            "schema": {
              "title": "Path",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response List Host Worktrees V1 Hosts  Host Id  Worktrees Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List Host Worktrees",
        "tags": [
          "hosts"
        ]
      }
    },
    "/v1/imports": {
      "post": {
        "description": "Import one normalized transcript, optionally replacing its prior import.",
        "operationId": "import_session_v1_imports_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ImportSessionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportSessionResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Import Session",
        "tags": [
          "imports"
        ]
      }
    },
    "/v1/info": {
      "get": {
        "description": "Runtime capabilities probe for the SPA + CLI.\n\nReturned at app boot by the frontend (and by `omnigent login` when it needs to choose between flows). Drives\nconditional route registration and chrome on the SPA side\n— when `accounts_enabled` is false, the SPA never\nregisters `/login`, `/register`, `/members` and\nnever renders the AccountMenu, so the bundle behaves\nidentically to a pre-PR-2008 build for header / OIDC\ndeploys (in particular, the internal hosted product that\nsyncs from this repo).\n\nAuthentication: this endpoint is intentionally UNAUTHED\nso the SPA can probe it before holding a session cookie.\nIt exposes no sensitive state — only the active auth\nsource, the login URL, whether first-run admin setup is\nstill pending (`needs_setup`), coarse capability\nbooleans (`databricks_features`,\n`managed_sandboxes_enabled`, `dictation_available`,\n`single_user`), the short sandbox provider name\n(`sandbox_provider`) the web UI labels the new-session\nsandbox option with, and the installed\n`server_version` (already public via `/api/version`).",
        "operationId": "info_v1_info_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "anyOf": [
                      {
                        "type": "boolean"
                      },
                      {
                        "type": "string"
                      },
                      {
                        "items": {
                          "type": "string"
                        },
                        "type": "array"
                      },
                      {
                        "additionalProperties": {
                          "type": "boolean"
                        },
                        "type": "object"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "title": "Response Info V1 Info Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Info",
        "tags": [
          "system"
        ]
      }
    },
    "/v1/me": {
      "get": {
        "description": "Return the current user's identity.\n\nReads the user from the auth provider (same logic that\nsession routes use). The frontend calls this on load to\ndiscover who it is.\n\nAlso returns `is_admin` — the mode-agnostic admin signal\n(the shared `users.is_admin` column, set by the admin-list\npromotion at login). The SPA gates admin chrome on it in\nEVERY mode, including OIDC/SSO where the accounts-only\n`/auth/me` endpoint does not exist.\n\nWhen OIDC is active and the user is unauthenticated,\nreturns 401 with a `login_url` so the frontend knows\nwhere to redirect.\n\n**Returns:** `{\"user_id\": \"[email protected]\", \"is_admin\": true}`, `{\"user_id\": null, \"is_admin\": false}` if unauthenticated in header mode, or 401 with `login_url` in OIDC mode.",
        "operationId": "me_v1_me_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Me",
        "tags": [
          "system"
        ]
      }
    },
    "/v1/policies": {
      "get": {
        "description": "List all default policies.\n\nRequires authentication in multi-user mode.\n\n**Returns:** `{\"object\": \"list\", \"data\": [...]}`.\n\n**Raises**\n\n- `OmnigentError` — 401 if unauthenticated in multi-user mode.",
        "operationId": "list_policies_v1_policies_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response List Policies V1 Policies Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List Policies",
        "tags": [
          "default_policies"
        ]
      },
      "post": {
        "description": "Create a new default policy.\n\nRequires admin privileges in multi-user mode.\n\n**Parameters**\n\n- `body` — Policy payload including name, type, and handler.\n\n**Returns:** The created policy as a serialized dict.\n\n**Raises**\n\n- `OmnigentError` — 401/403 if the user lacks admin privileges, or 409 if a policy with the same name already exists.",
        "operationId": "create_policy_v1_policies_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDefaultPolicyRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Create Policy V1 Policies Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Create Policy",
        "tags": [
          "default_policies"
        ]
      }
    },
    "/v1/policies/{policy_id}": {
      "delete": {
        "description": "Delete a default policy.\n\nIdempotent — deleting a missing policy returns 204.\nRequires admin privileges.\n\n**Returns:** `{\"deleted\": true}`.\n\n**Raises**\n\n- `OmnigentError` — 401/403 if the user lacks admin privileges.",
        "operationId": "delete_policy_v1_policies__policy_id__delete",
        "parameters": [
          {
            "description": "The policy to delete, e.g. `\"pol_abc123\"`.",
            "in": "path",
            "name": "policy_id",
            "required": true,
            "schema": {
              "title": "Policy Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Delete Policy V1 Policies  Policy Id  Delete",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Delete Policy",
        "tags": [
          "default_policies"
        ]
      },
      "get": {
        "description": "Get a single default policy.\n\nRequires authentication in multi-user mode.\n\n**Returns:** The policy as a serialized dict.\n\n**Raises**\n\n- `OmnigentError` — 401 if unauthenticated, or 404 if the policy is not found.",
        "operationId": "get_policy_v1_policies__policy_id__get",
        "parameters": [
          {
            "description": "The policy to retrieve, e.g. `\"pol_abc123\"`.",
            "in": "path",
            "name": "policy_id",
            "required": true,
            "schema": {
              "title": "Policy Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Get Policy V1 Policies  Policy Id  Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get Policy",
        "tags": [
          "default_policies"
        ]
      },
      "patch": {
        "description": "Update a default policy's mutable fields.\n\n`type` is immutable — the caller must delete and\nre-create to change it. Requires admin privileges.\n\n**Parameters**\n\n- `body` — Fields to update; `None` fields are left unchanged.\n\n**Returns:** The updated policy as a serialized dict.\n\n**Raises**\n\n- `OmnigentError` — 401/403 if the user lacks admin privileges, or 404 if the policy is not found.",
        "operationId": "update_policy_v1_policies__policy_id__patch",
        "parameters": [
          {
            "description": "The policy to update, e.g. `\"pol_abc123\"`.",
            "in": "path",
            "name": "policy_id",
            "required": true,
            "schema": {
              "title": "Policy Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateDefaultPolicyRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Update Policy V1 Policies  Policy Id  Patch",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Update Policy",
        "tags": [
          "default_policies"
        ]
      }
    },
    "/v1/policy-registry": {
      "get": {
        "description": "List all registered policy functions.\n\nReturns the full catalog of built-in policy callables\nwith their handler paths, descriptions, and parameter\nschemas.\n\n**Returns:** `{\"object\": \"list\", \"data\": [...]}`.",
        "operationId": "list_registry_v1_policy_registry_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response List Registry V1 Policy Registry Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List Registry",
        "tags": [
          "policy_registry"
        ]
      }
    },
    "/v1/projects": {
      "get": {
        "description": "List the caller's projects.\n\n**Returns:** `{\"object\": \"list\", \"data\": [...]}`.\n\n**Raises**\n\n- `OmnigentError` — 401 if unauthenticated in multi-user mode.",
        "operationId": "list_projects_v1_projects_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response List Projects V1 Projects Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List Projects",
        "tags": [
          "projects"
        ]
      },
      "post": {
        "description": "Create a new, empty project owned by the caller.\n\n**Parameters**\n\n- `body` — Project payload (name).\n\n**Returns:** The created project as a serialized dict.\n\n**Raises**\n\n- `OmnigentError` — 401 if unauthenticated in multi-user mode, 409 if the caller already has a project with this name.",
        "operationId": "create_project_v1_projects_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateProjectRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Create Project V1 Projects Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Create Project",
        "tags": [
          "projects"
        ]
      }
    },
    "/v1/projects/{project_id}": {
      "delete": {
        "description": "Delete one of the caller's projects.\n\nMember sessions are not deleted; they are left for the caller to\nunfile (clearing their `project_id`).\n\n**Returns:** `{\"id\": ..., \"object\": \"project.deleted\", \"deleted\": True}`.\n\n**Raises**\n\n- `OmnigentError` — 401 if unauthenticated, 404 if not found / not owned by the caller.",
        "operationId": "delete_project_v1_projects__project_id__delete",
        "parameters": [
          {
            "description": "The project to delete.",
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Delete Project V1 Projects  Project Id  Delete",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Delete Project",
        "tags": [
          "projects"
        ]
      },
      "get": {
        "description": "Return one of the caller's projects.\n\n**Returns:** The project as a serialized dict.\n\n**Raises**\n\n- `OmnigentError` — 401 if unauthenticated, 404 if not found / not owned by the caller.",
        "operationId": "get_project_v1_projects__project_id__get",
        "parameters": [
          {
            "description": "The project to fetch.",
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Get Project V1 Projects  Project Id  Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get Project",
        "tags": [
          "projects"
        ]
      },
      "patch": {
        "description": "Update one of the caller's projects (e.g. rename).\n\n**Parameters**\n\n- `body` — Fields to change; `None` fields are left unchanged.\n\n**Returns:** The updated project as a serialized dict.\n\n**Raises**\n\n- `OmnigentError` — 401 if unauthenticated, 404 if not found / not owned, 409 if the new name collides with another of the caller's projects.",
        "operationId": "update_project_v1_projects__project_id__patch",
        "parameters": [
          {
            "description": "The project to update.",
            "in": "path",
            "name": "project_id",
            "required": true,
            "schema": {
              "title": "Project Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateProjectRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Update Project V1 Projects  Project Id  Patch",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Update Project",
        "tags": [
          "projects"
        ]
      }
    },
    "/v1/runners": {
      "get": {
        "description": "Return currently online runners owned by the requesting user.\n\nWhen auth is active, only runners whose tunnel was\nestablished by the same user are returned. Without auth,\nall online runners are listed (single-user / dev mode).\n\n**Returns:** A `{\"data\": [...]}` list with runner ids and advertised harnesses.",
        "operationId": "list_runners_v1_runners_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "items": {
                      "additionalProperties": true,
                      "type": "object"
                    },
                    "type": "array"
                  },
                  "title": "Response List Runners V1 Runners Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List Runners",
        "tags": [
          "runners"
        ]
      }
    },
    "/v1/runners/{runner_id}/status": {
      "get": {
        "description": "Return whether a runner currently has an open tunnel.\n\nWhen auth is active, a runner owned by a different user\nappears as offline to prevent enumeration.\n\n**Returns:** A JSON object with `runner_id` and `online`.",
        "operationId": "runner_status_v1_runners__runner_id__status_get",
        "parameters": [
          {
            "description": "Stable runner id, e.g. `\"runner_0123456789abcdef\"`.",
            "in": "path",
            "name": "runner_id",
            "required": true,
            "schema": {
              "title": "Runner Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "boolean"
                      }
                    ]
                  },
                  "title": "Response Runner Status V1 Runners  Runner Id  Status Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Runner Status",
        "tags": [
          "runners"
        ]
      }
    },
    "/v1/runners/{runner_id}/token": {
      "post": {
        "description": "Mint a short-lived owner bearer for a delegated runner.\n\nA host-launched or managed-sandbox runner does not inherit the host\nuser's credential; it presents its server-minted tunnel binding token\n(`X-Omnigent-Runner-Tunnel-Token`) and the server returns a\nshort-lived owner JWT the runner then uses on its HTTP callbacks\n(which gate on `require_user`). This is the HTTP analog of the\nrunner tunnel's binding-token handshake: the same SHA-256 gate\n(`token_bound_runner_id(token) == runner_id`) and the same\nowner resolution (`resolve_managed_runner_owner`), minting a\nbearer instead of registering a tunnel.\n\nThe binding-token match is required unconditionally — the\nallow-list shortcut honored on some other runner-token checks is\ndeliberately NOT accepted here, because this endpoint issues a\nfull owner credential and delegated runners are token-bound.\n\n**Returns:** `{\"token\": <jwt>, \"expires_at\": <epoch seconds>}`.\n\n**Raises**\n\n- `OmnigentError` — 401 when the binding token is absent, doesn't match `runner_id`, or resolves to no managed-launch owner; 400 when the active auth mode can't mint server-side (header/proxy, or no auth provider).",
        "operationId": "mint_runner_owner_token_v1_runners__runner_id__token_post",
        "parameters": [
          {
            "description": "Token-bound runner id from the path.",
            "in": "path",
            "name": "runner_id",
            "required": true,
            "schema": {
              "title": "Runner Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "integer"
                      }
                    ]
                  },
                  "title": "Response Mint Runner Owner Token V1 Runners  Runner Id  Token Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Mint Runner Owner Token",
        "tags": [
          "runners"
        ]
      }
    },
    "/v1/sessions": {
      "get": {
        "description": "List sessions with cursor-based pagination.\n\nSessions are conversations with a non-`None` `agent_id`\n— i.e. those created via `POST /v1/sessions`.\nConversations without an agent binding are excluded.\n\n**Returns:** A `PaginatedList` of `SessionListItem`.",
        "operationId": "list_sessions_v1_sessions_get",
        "parameters": [
          {
            "description": "Maximum number of sessions to return (1-1000, default 20).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "maximum": 1000,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Cursor — return sessions after this session ID in sort order, e.g. `\"conv_abc123\"`.",
            "in": "query",
            "name": "after",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "After"
            }
          },
          {
            "description": "Cursor — return sessions before this session ID.",
            "in": "query",
            "name": "before",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Before"
            }
          },
          {
            "description": "When set, only return sessions bound to this agent, e.g. `\"ag_abc123\"`. `None` returns sessions across all agents.",
            "in": "query",
            "name": "agent_id",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Agent Id"
            }
          },
          {
            "description": "When set, only return sessions whose bound agent row has this name. This intentionally includes session-scoped agents that share a name but have distinct bundles. `None` disables the filter.",
            "in": "query",
            "name": "agent_name",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Agent Name"
            }
          },
          {
            "description": "Sort direction, `\"desc\"` (newest-first) or `\"asc\"` (oldest-first).",
            "in": "query",
            "name": "order",
            "required": false,
            "schema": {
              "default": "desc",
              "pattern": "^(asc|desc)$",
              "title": "Order",
              "type": "string"
            }
          },
          {
            "description": "Column to sort on, `\"created_at\"` or `\"updated_at\"`.",
            "in": "query",
            "name": "sort_by",
            "required": false,
            "schema": {
              "default": "created_at",
              "pattern": "^(created_at|updated_at)$",
              "title": "Sort By",
              "type": "string"
            }
          },
          {
            "description": "Case-insensitive substring filter on the session title or conversation content. `None` or empty string disables the filter. A session matches if its title contains the query or any of its conversation items' text does. Powers the sidebar's session search.",
            "in": "query",
            "name": "search_query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Search Query"
            }
          },
          {
            "description": "When `False` (default), archived sessions are omitted. When `True`, archived sessions are returned alongside active ones (the sidebar groups them into an \"Archived\" section). Powers the sidebar's \"Show archived\" toggle.",
            "in": "query",
            "name": "include_archived",
            "required": false,
            "schema": {
              "default": false,
              "title": "Include Archived",
              "type": "boolean"
            }
          },
          {
            "description": "Conversation kind to return. `\"default\"` (the default) returns only top-level user-initiated sessions — the sidebar's view. `\"sub_agent\"` returns only sub-agent child sessions. `\"any\"` returns both; this lets the new-session agent picker discover agents that are only bound to sub-agent sessions (e.g. ones uploaded via `sys_session_create`).",
            "in": "query",
            "name": "kind",
            "required": false,
            "schema": {
              "default": "default",
              "pattern": "^(default|sub_agent|any)$",
              "title": "Kind",
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "project",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Project"
            }
          },
          {
            "description": "When `True`, return only sessions the user has pinned (the `omnigent.pinned` label). Lets the sidebar enumerate pinned sessions that fall outside the loaded pagination window. `False` (default) disables it.",
            "in": "query",
            "name": "pinned",
            "required": false,
            "schema": {
              "default": false,
              "title": "Pinned",
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionList"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List Sessions",
        "tags": [
          "sessions"
        ]
      },
      "post": {
        "description": "Create a session.\n\n`application/json` preserves the existing contract: bind to\nan already-registered agent by `agent_id` and return the full\nsession snapshot. `multipart/form-data` is the Alpha\nrunner-state create path: the request carries a JSON\n`metadata` part and a `bundle` file part, then the server\nstores the bundle and creates the conversation row plus\nsession-scoped agent row in one database transaction.\n\n**Returns:** `SessionResponse` for JSON create, or `CreatedSessionResponse` for bundled create.\n\n**Raises**\n\n- `OmnigentError` — If metadata, bundle, or agent lookup validation fails, artifact storage is unavailable, or database creation fails.",
        "operationId": "create_session_v1_sessions_post",
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Create Session",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/projects": {
      "get": {
        "description": "Return the caller's projects as `{\"id\", \"name\"}` pairs, ordered\nalphabetically by name.\n\nDual-reads both project representations and unions them by name:\n- **First-class projects** (`project_store`) — carry an `id` and\n  appear even when empty (the whole point of the first-class entity).\n- **Legacy label-projects** (implicit `omni_project` label) — exist\n  while at least one owned session carries the label; `id` is\n  `None` until such a project is promoted to first-class.\n\nA name present in both sources collapses to one entry that keeps the\nfirst-class `id`. Filing is owner-only, so both halves are scoped to\nthe caller (label-projects to their owned sessions, first-class to\ntheir owned rows) — a project shared to them but owned by another user\ndoes not surface as one of their own folders.\n\n**Returns:** List of `SessionProjectSummary` ordered by name.",
        "operationId": "list_session_projects_v1_sessions_projects_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/SessionProjectSummary"
                  },
                  "title": "Response List Session Projects V1 Sessions Projects Get",
                  "type": "array"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "List Session Projects",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/{session_id}": {
      "delete": {
        "description": "Delete a session and all associated resources.\n\nRequires owner-level access. Tears down tasks, runner-side\nresources (environments, terminals), session files, and the\nconversation row.\n\n**Returns:** A `ConversationDeleted` confirmation.\n\n**Raises**\n\n- `OmnigentError` — 404 if no session or no access, 403 if insufficient permissions.",
        "operationId": "delete_session_v1_sessions__session_id__delete",
        "parameters": [
          {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Opt-in git cleanup, as a query param (`?delete_branch=true`). When `True` and the session has a server-created worktree (`git_branch` set), the host removes the worktree directory and deletes its branch (`git worktree remove --force` then `git branch -D`). Ignored for sessions with no worktree. Best-effort: a cleanup failure does not block the delete. Defaults to `False` (worktree and branch left untouched). See designs/SESSION_GIT_WORKTREE.md.",
            "in": "query",
            "name": "delete_branch",
            "required": false,
            "schema": {
              "default": false,
              "title": "Delete Branch",
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConversationDeleted"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Delete Session",
        "tags": [
          "sessions"
        ]
      },
      "get": {
        "description": "Return a session snapshot: identity, status, and committed\nitems.\n\n**Returns:** The matching `SessionResponse`.\n\n**Raises**\n\n- `OmnigentError` — 404 if no session exists.",
        "operationId": "get_session_v1_sessions__session_id__get",
        "parameters": [
          {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "When `False`, skip the committed-items read and return `items=[]`. The web chat surface passes `False` because it hydrates the transcript via the paginated `GET /sessions/{id}/items` endpoint in parallel and never reads the snapshot's copy; the items read is the single most expensive step of the snapshot build.",
            "in": "query",
            "name": "include_items",
            "required": false,
            "schema": {
              "default": true,
              "title": "Include Items",
              "type": "boolean"
            }
          },
          {
            "description": "When `False`, skip the runner/host liveness lookup and return `runner_online`/`host_online` as `None`. The web chat surface passes `False` because it sources liveness from the `/health` poll and the WS stream, not the snapshot.",
            "in": "query",
            "name": "include_liveness",
            "required": false,
            "schema": {
              "default": true,
              "title": "Include Liveness",
              "type": "boolean"
            }
          },
          {
            "description": "When `True`, refresh runner-derived snapshot overlays from the live session instead of serving stale AP-process caches. Browser reload/bind requests use this to recover from fixed bugs without restarting the AP server.",
            "in": "query",
            "name": "refresh_state",
            "required": false,
            "schema": {
              "default": false,
              "title": "Refresh State",
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get Session",
        "tags": [
          "sessions"
        ]
      },
      "patch": {
        "description": "Update a session's mutable fields. When `runner_id` is\nprovided, this is the mutable affinity primitive for the Alpha\nrunner-state pivot: create-bind, resume-bind, and recover-bind\nall send the currently registered runner id, and the server\natomically replaces `conversations.runner_id` with that\nvalue using last-write-wins semantics. Title, labels, and\nreasoning-effort updates remain supported for existing\nsessions clients.\n\n**Parameters**\n\n- `body` — The validated `UpdateSessionRequest`.\n\n**Returns:** The updated `SessionResponse` snapshot, with `items` always empty — PATCH callers use only scalar fields, and transcripts are served by `GET /sessions/{id}/items`.\n\n**Raises**\n\n- `OmnigentError` — 400 if the runner is not registered; 404 if no session exists.",
        "operationId": "update_session_v1_sessions__session_id__patch",
        "parameters": [
          {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateSessionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Update Session",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/{session_id}/agent": {
      "get": {
        "description": "Return the `AgentObject` for the session's bound agent.\n\nReplaces the standalone `GET /api/agents/{id}` endpoint by\nresolving the agent through the session's `agent_id` foreign\nkey. The caller only needs to know the session id.\n\n**Returns:** The bound agent's `AgentObject`.\n\n**Raises**\n\n- `OmnigentError` — If the session or agent is not found.",
        "operationId": "get_session_agent_v1_sessions__session_id__agent_get",
        "parameters": [
          {
            "description": "Session identifier, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentObject"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get Session Agent",
        "tags": [
          "sessions"
        ]
      },
      "put": {
        "description": "Replace the session's agent bundle with a new upload.\n\nValidates the new bundle, checks that the spec name matches\nthe existing agent, stores the bundle under a\ncontent-addressed key, updates the agent row, and warm-swaps\nthe cache. Idempotent when the bundle content is unchanged.\n\n**Parameters**\n\n- `bundle` — Uploaded `.tar.gz` agent bundle file.\n\n**Returns:** The updated `AgentObject`.\n\n**Raises**\n\n- `OmnigentError` — If the session or agent is not found, the bundle is invalid, or the spec name doesn't match.",
        "operationId": "update_session_agent_v1_sessions__session_id__agent_put",
        "parameters": [
          {
            "description": "Session identifier, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/Body_update_session_agent_v1_sessions__session_id__agent_put"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentObject"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Update Session Agent",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/{session_id}/agent/contents": {
      "get": {
        "description": "Download the raw `.tar.gz` agent bundle for the session's\nbound agent.\n\nReplaces `GET /api/agents/{id}/contents`. Runners call this\non cache miss to fetch the spec + bundled files.\n\n**Returns:** Raw bundle bytes as `application/gzip`.\n\n**Raises**\n\n- `OmnigentError` — If the session, agent, or bundle is not found.",
        "operationId": "get_session_agent_contents_v1_sessions__session_id__agent_contents_get",
        "parameters": [
          {
            "description": "Session identifier, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/gzip": {}
            },
            "description": "Successful Response"
          },
          "404": {
            "description": "Session or agent not found"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get Session Agent Contents",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/{session_id}/agent/mcp-servers": {
      "get": {
        "description": "List safe MCP server summaries for a session's bound agent.",
        "operationId": "list_mcp_servers_v1_sessions__session_id__agent_mcp_servers_get",
        "parameters": [
          {
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response List Mcp Servers V1 Sessions  Session Id  Agent Mcp Servers Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List Mcp Servers",
        "tags": [
          "session_mcp_servers"
        ]
      },
      "post": {
        "description": "Create one MCP server declaration on a session-scoped agent.",
        "operationId": "create_mcp_server_v1_sessions__session_id__agent_mcp_servers_post",
        "parameters": [
          {
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpsertMCPServerRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MCPServerSummary"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Create Mcp Server",
        "tags": [
          "session_mcp_servers"
        ]
      }
    },
    "/v1/sessions/{session_id}/agent/mcp-servers/{server_name}": {
      "delete": {
        "description": "Delete one MCP server declaration from a session-scoped agent.",
        "operationId": "delete_mcp_server_v1_sessions__session_id__agent_mcp_servers__server_name__delete",
        "parameters": [
          {
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "server_name",
            "required": true,
            "schema": {
              "title": "Server Name",
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Delete Mcp Server",
        "tags": [
          "session_mcp_servers"
        ]
      },
      "put": {
        "description": "Replace one MCP server declaration on a session-scoped agent.",
        "operationId": "update_mcp_server_v1_sessions__session_id__agent_mcp_servers__server_name__put",
        "parameters": [
          {
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "in": "path",
            "name": "server_name",
            "required": true,
            "schema": {
              "title": "Server Name",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpsertMCPServerRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MCPServerSummary"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Update Mcp Server",
        "tags": [
          "session_mcp_servers"
        ]
      }
    },
    "/v1/sessions/{session_id}/auto-title": {
      "post": {
        "description": "Replace the deterministic first-message title when still current.",
        "operationId": "automatically_rename_session_v1_sessions__session_id__auto_title_post",
        "parameters": [
          {
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AutomaticSessionRenameRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AutomaticSessionRenameResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Automatically Rename Session",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/{session_id}/child_sessions": {
      "get": {
        "description": "List sub-agent (child) sessions under a parent session.\n\nReturns a page of `ChildSessionSummary` objects\nderived from child conversations (`kind=\"sub_agent\"`,\n`parent_conversation_id=session_id`) plus each child's\nlatest task. Powers the web / REPL debug surfaces' \"child\nsessions\" panel without parsing parent\n`function_call_output` JSON handles. Pagination contract\nmatches `list_session_items` so existing client code\ncan reuse the same cursor logic.\n\n**Returns:** A `PaginatedList` of `ChildSessionSummary` objects.\n\n**Raises**\n\n- `OmnigentError` — 403 if the caller lacks READ on `session_id`; 404 if no session exists there.",
        "operationId": "list_child_sessions_v1_sessions__session_id__child_sessions_get",
        "parameters": [
          {
            "description": "Parent session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Maximum number of children to return (1-1000, default 20 — sub-agent fan-out is typically sparse compared to conversation items).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "maximum": 1000,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Cursor — return children whose id appears after this one in sort order, e.g. `\"conv_child123\"`.",
            "in": "query",
            "name": "after",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "After"
            }
          },
          {
            "description": "Cursor — return children before this one.",
            "in": "query",
            "name": "before",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Before"
            }
          },
          {
            "description": "Sort direction, `\"desc\"` (newest-first, default) or `\"asc\"`. Sort column is `created_at`.",
            "in": "query",
            "name": "order",
            "required": false,
            "schema": {
              "default": "desc",
              "pattern": "^(asc|desc)$",
              "title": "Order",
              "type": "string"
            }
          },
          {
            "description": "When set, only return children whose title starts with this agent type (the segment before the `\":\"`). Combined with `session_name` to form the exact title `\"{tool}:{session_name}\"` for server-side filtering.",
            "in": "query",
            "name": "tool",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Tool"
            }
          },
          {
            "description": "When set alongside `tool`, only return children whose title matches `\"{tool}:{session_name}\"` exactly.",
            "in": "query",
            "name": "session_name",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Session Name"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChildSessionList"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List Child Sessions",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/{session_id}/codex_goal": {
      "delete": {
        "description": "Clear the current Codex app-server goal.\n\n**Returns:** Whether Codex removed an existing goal.\n\n**Raises**\n\n- `OmnigentError` — 400 for non-Codex sessions, 404 for missing sessions, or 503 when no live Codex runner can clear the goal.",
        "operationId": "clear_codex_goal_v1_sessions__session_id__codex_goal_delete",
        "parameters": [
          {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClearCodexGoalResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Clear Codex Goal",
        "tags": [
          "sessions"
        ]
      },
      "get": {
        "description": "Read the current Codex app-server goal for a Codex-native session.\n\n**Returns:** Current Codex goal state, or `goal=None` when no goal is set.\n\n**Raises**\n\n- `OmnigentError` — 400 for non-Codex sessions, 404 for missing sessions, or 503 when no live Codex runner can read the goal.",
        "operationId": "get_codex_goal_v1_sessions__session_id__codex_goal_get",
        "parameters": [
          {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CodexGoalResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get Codex Goal",
        "tags": [
          "sessions"
        ]
      },
      "put": {
        "description": "Set or replace the current Codex app-server goal.\n\n**Parameters**\n\n- `body` — Goal objective, optional token budget, and optional user-selected status.\n\n**Returns:** Current Codex goal state after the update.\n\n**Raises**\n\n- `OmnigentError` — 400 for non-Codex sessions or blank objectives, 404 for missing sessions, or 503 when no live Codex runner can update the goal.",
        "operationId": "set_codex_goal_v1_sessions__session_id__codex_goal_put",
        "parameters": [
          {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetCodexGoalRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CodexGoalResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Set Codex Goal",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/{session_id}/codex_goal/status": {
      "patch": {
        "description": "Pause or resume the current Codex app-server goal.\n\nCodex exposes this through `thread/goal/set` with only a status\nfield. The Omnigent API keeps that distinct from objective/budget\nedits so callers can implement Pause/Resume controls without\nresending the goal text.\n\n**Parameters**\n\n- `body` — Target status. `\"paused\"` pauses the goal and `\"active\"` resumes it.\n\n**Returns:** Current Codex goal state after the status update.\n\n**Raises**\n\n- `OmnigentError` — 400 for non-Codex sessions, 404 for missing sessions, or 503 when no live Codex runner can update the goal.",
        "operationId": "update_codex_goal_status_v1_sessions__session_id__codex_goal_status_patch",
        "parameters": [
          {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCodexGoalStatusRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CodexGoalResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Update Codex Goal Status",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/{session_id}/comments": {
      "get": {
        "description": "List comments for a session, optionally filtered by file.\n\nRequires `LEVEL_READ` on the session in multi-user mode.\n\n**Returns:** List of serialized comment dicts.\n\n**Raises**\n\n- `OmnigentError` — 401/403/404 if the user lacks read permission.",
        "operationId": "list_comments_v1_sessions__session_id__comments_get",
        "parameters": [
          {
            "description": "The session to query, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "When provided, only return comments for this file, e.g. `\"src/App.tsx\"`.",
            "in": "query",
            "name": "path",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Path"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "additionalProperties": true,
                    "type": "object"
                  },
                  "title": "Response List Comments V1 Sessions  Session Id  Comments Get",
                  "type": "array"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List Comments",
        "tags": [
          "comments"
        ]
      },
      "post": {
        "description": "Create a new review comment.\n\nRequires `LEVEL_EDIT` on the session in multi-user mode.\n\n**Parameters**\n\n- `body` — Comment payload including path, body text, and the two range fields (start_index, end_index).\n\n**Returns:** The created comment as a serialized dict.\n\n**Raises**\n\n- `OmnigentError` — 401/403/404 if the user lacks edit permission.",
        "operationId": "add_comment_v1_sessions__session_id__comments_post",
        "parameters": [
          {
            "description": "The owning session, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddCommentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Add Comment V1 Sessions  Session Id  Comments Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Add Comment",
        "tags": [
          "comments"
        ]
      }
    },
    "/v1/sessions/{session_id}/comments/send": {
      "post": {
        "description": "Mark comments as addressed and format them into an agent message.\n\nFetches each requested comment, marks it `addressed`,\nand formats the full set into a grouped, sorted message string\nsuitable for pasting into the chat composer.\n\nRequires `LEVEL_EDIT` on the session in multi-user mode because\nit transitions comment status from `draft` to `addressed`.\n\n**Parameters**\n\n- `body` — List of comment IDs to send, with an optional custom instruction prefix.\n\n**Returns:** `{\"formatted_message\": str, \"sent_comment_ids\": list[str]}`.\n\n**Raises**\n\n- `OmnigentError` — 401/403/404 if the user lacks edit permission, or 404 if any requested comment is not found or does not belong to this session.",
        "operationId": "send_to_agent_v1_sessions__session_id__comments_send_post",
        "parameters": [
          {
            "description": "The owning session, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendCommentsRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Send To Agent V1 Sessions  Session Id  Comments Send Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Send To Agent",
        "tags": [
          "comments"
        ]
      }
    },
    "/v1/sessions/{session_id}/comments/{comment_id}": {
      "delete": {
        "description": "Delete a comment.\n\nRequires `LEVEL_EDIT` on the session in multi-user mode, and\nadditionally that the caller is the comment's author — one\ncollaborator may not delete another user's comment.\n\n**Returns:** `{\"deleted\": true}`.\n\n**Raises**\n\n- `OmnigentError` — 401/403/404 if the user lacks edit permission, 403 if the caller is not the comment's author, or 404 if the comment is not found or does not belong to this session.",
        "operationId": "delete_comment_v1_sessions__session_id__comments__comment_id__delete",
        "parameters": [
          {
            "description": "The owning session, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "The comment to delete, e.g. `\"a1b2c3d4-...\"`.",
            "in": "path",
            "name": "comment_id",
            "required": true,
            "schema": {
              "title": "Comment Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Delete Comment V1 Sessions  Session Id  Comments  Comment Id  Delete",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Delete Comment",
        "tags": [
          "comments"
        ]
      },
      "patch": {
        "description": "Update a comment's status and/or body text.\n\nRequires `LEVEL_EDIT` on the session in multi-user mode.\nEditing the `body` additionally requires the caller to be the\ncomment's author: rewriting another user's comment is forbidden,\nwhile changing only the `status` (e.g. marking it `\"addressed\"`)\nstays open to any editor as a shared review-workflow action.\n\n**Parameters**\n\n- `body` — Fields to update; `None` fields are left unchanged.\n\n**Returns:** The updated serialized comment.\n\n**Raises**\n\n- `OmnigentError` — 401/403/404 if the user lacks edit permission, 403 if a body edit is attempted on another user's comment, or 404 if the comment is not found.",
        "operationId": "update_comment_v1_sessions__session_id__comments__comment_id__patch",
        "parameters": [
          {
            "description": "The owning session, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "The comment to update, e.g. `\"a1b2c3d4-...\"`.",
            "in": "path",
            "name": "comment_id",
            "required": true,
            "schema": {
              "title": "Comment Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCommentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Update Comment V1 Sessions  Session Id  Comments  Comment Id  Patch",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Update Comment",
        "tags": [
          "comments"
        ]
      }
    },
    "/v1/sessions/{session_id}/items": {
      "get": {
        "description": "List items in a session with cursor-based pagination.\n\nDelegates to the conversation items store — session_id is\nthe conversation_id. Same pagination contract as\n`GET /v1/conversations/{id}/items`.\n\n**Returns:** A `PaginatedList` of conversation items.\n\n**Raises**\n\n- `OmnigentError` — 404 if no session exists.",
        "operationId": "list_session_items_v1_sessions__session_id__items_get",
        "parameters": [
          {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Maximum number of items to return (1-1000, default 100).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 100,
              "maximum": 1000,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Cursor — return items after this item ID, e.g. `\"msg_abc123\"`.",
            "in": "query",
            "name": "after",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "After"
            }
          },
          {
            "description": "Cursor — return items before this item ID.",
            "in": "query",
            "name": "before",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Before"
            }
          },
          {
            "description": "Sort order, `\"asc\"` (chronological, default) or `\"desc\"`.",
            "in": "query",
            "name": "order",
            "required": false,
            "schema": {
              "default": "asc",
              "pattern": "^(asc|desc)$",
              "title": "Order",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaginatedList"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List Session Items",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/{session_id}/labels": {
      "get": {
        "description": "Return only the labels for a session.\n\nNative runner bridge setup needs labels during harness spawn,\nbut the full session snapshot also loads history, skills,\nrunner status, and agent metadata. This endpoint keeps that\nspawn-time dependency to one authorized conversation read.\n\n**Returns:** The session id and labels.\n\n**Raises**\n\n- `OmnigentError` — 404 if no session exists.",
        "operationId": "get_session_labels_v1_sessions__session_id__labels_get",
        "parameters": [
          {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionLabelsResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get Session Labels",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/{session_id}/owner": {
      "get": {
        "description": "Return the owner of a session.\n\nRequires read-level access.\n\n**Returns:** `{\"owner\": \"<user_id>\"}` or `{\"owner\": null}`.",
        "operationId": "get_session_owner_v1_sessions__session_id__owner_get",
        "parameters": [
          {
            "description": "Session to look up, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get Session Owner",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/{session_id}/permissions": {
      "get": {
        "description": "List permission grants on a session with cursor pagination.\n\nRequires manage-level access.\n\n**Returns:** `{\"permissions\": [...], \"next_cursor\": str|null}`.\n\n**Raises**\n\n- `OmnigentError` — 404 if no session or no access.",
        "operationId": "list_permissions_v1_sessions__session_id__permissions_get",
        "parameters": [
          {
            "description": "Session to list grants for, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Max grants to return (1–1000, default 100).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 100,
              "maximum": 1000,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Cursor: user_id to start after",
            "in": "query",
            "name": "after",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Cursor: user_id to start after",
              "title": "After"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List Permissions",
        "tags": [
          "sessions"
        ]
      },
      "put": {
        "description": "Grant or update a permission on a session.\n\nRequires manage-level access. Upserts the grant — can\nupgrade or downgrade an existing level. Auto-creates the\ngrantee user if they don't exist yet.\n\n**Parameters**\n\n- `body` — The grant request with `user_id` and `level`.\n\n**Returns:** The resulting `PermissionObject`.\n\n**Raises**\n\n- `OmnigentError` — 404 if no session or no access, 401 if unauthenticated.",
        "operationId": "grant_permission_v1_sessions__session_id__permissions_put",
        "parameters": [
          {
            "description": "Session to grant access to, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GrantPermissionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PermissionObject"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Grant Permission",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/{session_id}/permissions/{target_user_id}": {
      "delete": {
        "description": "Revoke a permission on a session — someone else's, or your own.\n\nTwo shapes, one endpoint:\n\n* *Revoke* — removing **another** user's grant is a management\n  action and needs manage access.\n* *Leave* — removing **your own** grant (\"unshare myself\", so a\n  shared session stops cluttering your sidebar) needs only read\n  access, since giving up access requires no privilege.\n\nEither way the owner grant is protected: it is what keeps the\nsession reachable, so revoking it would orphan the session. That\nguard covers the self case too — an owner cannot leave, and\narchives or deletes instead. A manage-level *grantee* is still a\nguest and may leave. Leaving records no lasting refusal, so the\nowner re-granting brings the session back.\n\nIdempotent: 204 whether or not a grant existed.\n\n**Returns:** 204 No Content.\n\n**Raises**\n\n- `OmnigentError` — 404 if no session or no access, 403 when the target holds the owner grant, 400 when leaving a sub-agent session, 401 if unauthenticated.",
        "operationId": "revoke_permission_v1_sessions__session_id__permissions__target_user_id__delete",
        "parameters": [
          {
            "description": "Session to revoke access from, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "User whose grant to revoke, or the caller's own id to leave, e.g. `\"[email protected]\"`.",
            "in": "path",
            "name": "target_user_id",
            "required": true,
            "schema": {
              "title": "Target User Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Revoke Permission",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/{session_id}/policies": {
      "get": {
        "description": "List all policies for a session.\n\nReturns both store-persisted (`source=\"session\"`) and\nspec-declared (`source=\"spec\"`) policies. Spec policies\nhave `id=None` and cannot be patched or deleted.\n\nRequires `LEVEL_READ` on the session in multi-user mode.\n\n**Returns:** `{\"object\": \"list\", \"data\": [...]}`.\n\n**Raises**\n\n- `OmnigentError` — 401/403 if the user lacks read permission, 404 if the session is not found.",
        "operationId": "list_policies_v1_sessions__session_id__policies_get",
        "parameters": [
          {
            "description": "The session to query, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response List Policies V1 Sessions  Session Id  Policies Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List Policies",
        "tags": [
          "session_policies"
        ]
      },
      "post": {
        "description": "Create a new session policy.\n\nRequires `LEVEL_EDIT` on the session in multi-user mode.\n\n**Parameters**\n\n- `body` — Policy payload including name, type, and handler.\n\n**Returns:** The created policy as a serialized dict.\n\n**Raises**\n\n- `OmnigentError` — 401/403 if the user lacks edit permission, 404 if the session is not found, or 409 if a policy with the same name already exists.",
        "operationId": "create_policy_v1_sessions__session_id__policies_post",
        "parameters": [
          {
            "description": "The owning session, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSessionPolicyRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Create Policy V1 Sessions  Session Id  Policies Post",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Create Policy",
        "tags": [
          "session_policies"
        ]
      }
    },
    "/v1/sessions/{session_id}/policies/evaluate": {
      "post": {
        "description": "Generic policy evaluation endpoint (proto-compatible).\n\nAccepts an `EvaluationRequest` JSON body whose `event`\nfield carries the phase (`PHASE_TOOL_CALL`,\n`PHASE_TOOL_RESULT`, `PHASE_LLM_REQUEST`,\n`PHASE_LLM_RESPONSE`), the event data, and optional\ncontext. Returns an `EvaluationResponse` with the policy\nverdict (`result`), an optional `reason`, and optional\n`data` for content-rewriting policies.\n\nUsed by Claude Code's `PreToolUse` and `PostToolUse`\ncommand hooks (via `omnigent.claude_native_hook`) to\nevaluate admin policies on native tool calls. Also usable\nby any client that speaks the proto-compatible JSON schema.\n\n**Returns:** `EvaluationResponse` JSON with `result`, `reason`, and optional `data`.\n\n**Raises**\n\n- `OmnigentError` — 404 if the session doesn't exist, 400 if the body is malformed.",
        "operationId": "evaluate_policy_v1_sessions__session_id__policies_evaluate_post",
        "parameters": [
          {
            "description": "Omnigent conversation id from the URL path.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Evaluate Policy",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/{session_id}/policies/{policy_id}": {
      "delete": {
        "description": "Delete a session policy.\n\nIdempotent — deleting a missing policy returns 204.\nRequires `LEVEL_EDIT`.\n\n**Returns:** `{\"deleted\": true}`.\n\n**Raises**\n\n- `OmnigentError` — 401/403 if the user lacks edit permission.",
        "operationId": "delete_policy_v1_sessions__session_id__policies__policy_id__delete",
        "parameters": [
          {
            "description": "The owning session, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "The policy to delete, e.g. `\"pol_abc123\"`.",
            "in": "path",
            "name": "policy_id",
            "required": true,
            "schema": {
              "title": "Policy Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Delete Policy V1 Sessions  Session Id  Policies  Policy Id  Delete",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Delete Policy",
        "tags": [
          "session_policies"
        ]
      },
      "get": {
        "description": "Get a single session policy.\n\nRequires `LEVEL_READ` on the session in multi-user mode.\n\n**Returns:** The policy as a serialized dict.\n\n**Raises**\n\n- `OmnigentError` — 401/403 if the user lacks read permission, or 404 if the policy is not found.",
        "operationId": "get_policy_v1_sessions__session_id__policies__policy_id__get",
        "parameters": [
          {
            "description": "The owning session, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "The policy to retrieve, e.g. `\"pol_abc123\"`.",
            "in": "path",
            "name": "policy_id",
            "required": true,
            "schema": {
              "title": "Policy Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Get Policy V1 Sessions  Session Id  Policies  Policy Id  Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get Policy",
        "tags": [
          "session_policies"
        ]
      },
      "patch": {
        "description": "Update a session policy's mutable fields.\n\n`type` is immutable — the caller must delete and\nre-create to change it. Requires `LEVEL_EDIT`.\n\n**Parameters**\n\n- `body` — Fields to update; `None` fields are left unchanged.\n\n**Returns:** The updated policy as a serialized dict.\n\n**Raises**\n\n- `OmnigentError` — 401/403 if the user lacks edit permission, 404 if the policy is not found, or 409 if renaming would collide with another policy in this session.",
        "operationId": "update_policy_v1_sessions__session_id__policies__policy_id__patch",
        "parameters": [
          {
            "description": "The owning session, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "The policy to update, e.g. `\"pol_abc123\"`.",
            "in": "path",
            "name": "policy_id",
            "required": true,
            "schema": {
              "title": "Policy Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateSessionPolicyRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Update Policy V1 Sessions  Session Id  Policies  Policy Id  Patch",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Update Policy",
        "tags": [
          "session_policies"
        ]
      }
    },
    "/v1/sessions/{session_id}/read-state": {
      "put": {
        "description": "Set the calling user's read-state for one session.\n\nRequires `LEVEL_READ` on the session in multi-user mode — you can\nonly track read-state for sessions you can see. Stores the values\nverbatim (the client enforces the baseline's monotonicity and the\nunread semantics); the server does not interpret them against\nsession status. Returns `204` — the client already has the\noptimistic state and re-reads the authoritative value on the next\n`GET /v1/sessions` poll.\n\n**Parameters**\n\n- `body` — The validated `ReadStatePutRequest`.\n\n**Returns:** An empty `204 No Content` response.\n\n**Raises**\n\n- `OmnigentError` — 403 if the caller lacks read access.",
        "operationId": "put_read_state_v1_sessions__session_id__read_state_put",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReadStatePutRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Put Read State",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/{session_id}/resources": {
      "get": {
        "description": "Return the runner-authoritative resource inventory for a session.\n\nRequires the session to be bound to a runner via\n`PATCH /v1/sessions/{id}`; raises `conflict` otherwise.\nThe server validates the session exists, then proxies to the\nrunner's `GET /v1/sessions/{id}/resources` endpoint. In\nunit-test / in-process setups with no runner router/client, the\nroute falls back to adapting the local terminal registry.",
        "operationId": "list_session_resources_v1_sessions__session_id__resources_get",
        "parameters": [
          {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Optional resource-type filter, e.g. `\"environment\"` / `\"terminal\"` / `\"file\"`. Forwarded to the runner (its registry applies it) and honored by the local-registry fallback and the file-store merge below.",
            "in": "query",
            "name": "type",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Type"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionResourcePaginatedList"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List Session Resources",
        "tags": [
          "session_resources"
        ]
      }
    },
    "/v1/sessions/{session_id}/resources/environments": {
      "get": {
        "description": "Return only environment resources for a session.\n\n**Returns:** `PaginatedList` of environment resources.",
        "operationId": "list_session_environments_v1_sessions__session_id__resources_environments_get",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List Session Environments",
        "tags": [
          "session_resources"
        ]
      }
    },
    "/v1/sessions/{session_id}/resources/environments/{environment_id}": {
      "get": {
        "description": "Return a single environment resource by id.\n\n**Returns:** The environment resource object.",
        "operationId": "get_session_environment_v1_sessions__session_id__resources_environments__environment_id__get",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Opaque environment resource id, e.g. `\"default\"`.",
            "in": "path",
            "name": "environment_id",
            "required": true,
            "schema": {
              "title": "Environment Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get Session Environment",
        "tags": [
          "session_resources"
        ]
      }
    },
    "/v1/sessions/{session_id}/resources/environments/{environment_id}/changes": {
      "get": {
        "description": "List all files changed since session start (flat, registry-backed).\n\nReturns the watchdog change set for the session — every file\ncreated, modified, or deleted since the session began, regardless\nof directory depth.  Use for the flat \"changed files\" view.\n\n**Returns:** Flat list of changed filesystem entries with `status`.",
        "operationId": "list_environment_filesystem_changes_v1_sessions__session_id__resources_environments__environment_id__changes_get",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Environment resource id.",
            "in": "path",
            "name": "environment_id",
            "required": true,
            "schema": {
              "title": "Environment Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List Environment Filesystem Changes",
        "tags": [
          "session_resources"
        ]
      }
    },
    "/v1/sessions/{session_id}/resources/environments/{environment_id}/filesystem": {
      "get": {
        "description": "List root directory of an environment.\n\n**Returns:** PaginatedList of filesystem entries.",
        "operationId": "list_environment_root_v1_sessions__session_id__resources_environments__environment_id__filesystem_get",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Environment resource id.",
            "in": "path",
            "name": "environment_id",
            "required": true,
            "schema": {
              "title": "Environment Id",
              "type": "string"
            }
          },
          {
            "description": "Maximum number of entries to return (1-1000, default 20).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "maximum": 1000,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Cursor entry id for forward pagination.",
            "in": "query",
            "name": "after",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "After"
            }
          },
          {
            "description": "Cursor entry id for backward pagination.",
            "in": "query",
            "name": "before",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Before"
            }
          },
          {
            "description": "Sort order, `\"asc\"` or `\"desc\"`.",
            "in": "query",
            "name": "order",
            "required": false,
            "schema": {
              "default": "desc",
              "pattern": "^(asc|desc)$",
              "title": "Order",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List Environment Root",
        "tags": [
          "session_resources"
        ]
      }
    },
    "/v1/sessions/{session_id}/resources/environments/{environment_id}/filesystem/{relative_path}": {
      "delete": {
        "description": "Delete a file or directory in an environment.\n\n**Returns:** Delete result.",
        "operationId": "delete_environment_path_v1_sessions__session_id__resources_environments__environment_id__filesystem__relative_path__delete",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Environment resource id.",
            "in": "path",
            "name": "environment_id",
            "required": true,
            "schema": {
              "title": "Environment Id",
              "type": "string"
            }
          },
          {
            "description": "Path relative to environment root.",
            "in": "path",
            "name": "relative_path",
            "required": true,
            "schema": {
              "title": "Relative Path",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Delete Environment Path",
        "tags": [
          "session_resources"
        ]
      },
      "get": {
        "description": "Read a file or list a directory in an environment.\n\n**Returns:** File content or directory listing.",
        "operationId": "read_or_list_environment_path_v1_sessions__session_id__resources_environments__environment_id__filesystem__relative_path__get",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Environment resource id.",
            "in": "path",
            "name": "environment_id",
            "required": true,
            "schema": {
              "title": "Environment Id",
              "type": "string"
            }
          },
          {
            "description": "Path relative to environment root.",
            "in": "path",
            "name": "relative_path",
            "required": true,
            "schema": {
              "title": "Relative Path",
              "type": "string"
            }
          },
          {
            "description": "Maximum number of entries to return for directory listings (1-1000, default 20). Ignored for file reads.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "maximum": 1000,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Cursor entry id for forward pagination.",
            "in": "query",
            "name": "after",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "After"
            }
          },
          {
            "description": "Cursor entry id for backward pagination.",
            "in": "query",
            "name": "before",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Before"
            }
          },
          {
            "description": "Sort order, `\"asc\"` or `\"desc\"`.",
            "in": "query",
            "name": "order",
            "required": false,
            "schema": {
              "default": "desc",
              "pattern": "^(asc|desc)$",
              "title": "Order",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Read Or List Environment Path",
        "tags": [
          "session_resources"
        ]
      },
      "patch": {
        "description": "Edit a file in an environment via text replacement.\n\n**Returns:** Edit result.",
        "operationId": "edit_environment_file_v1_sessions__session_id__resources_environments__environment_id__filesystem__relative_path__patch",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Environment resource id.",
            "in": "path",
            "name": "environment_id",
            "required": true,
            "schema": {
              "title": "Environment Id",
              "type": "string"
            }
          },
          {
            "description": "Path relative to environment root.",
            "in": "path",
            "name": "relative_path",
            "required": true,
            "schema": {
              "title": "Relative Path",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Edit Environment File",
        "tags": [
          "session_resources"
        ]
      },
      "put": {
        "description": "Write/replace a file in an environment.\n\n**Returns:** Write result.",
        "operationId": "write_environment_file_v1_sessions__session_id__resources_environments__environment_id__filesystem__relative_path__put",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Environment resource id.",
            "in": "path",
            "name": "environment_id",
            "required": true,
            "schema": {
              "title": "Environment Id",
              "type": "string"
            }
          },
          {
            "description": "Path relative to environment root.",
            "in": "path",
            "name": "relative_path",
            "required": true,
            "schema": {
              "title": "Relative Path",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Write Environment File",
        "tags": [
          "session_resources"
        ]
      }
    },
    "/v1/sessions/{session_id}/resources/environments/{environment_id}/search": {
      "get": {
        "description": "Search for files recursively by name/path substring and glob filters.\n\nProxies to the runner's search endpoint.  Returns a flat list of\nmatching file entries (not directories) whose name or relative path\ncontains `q` (case-insensitive), optionally scoped by `include` /\n`exclude` globs.  Requires at least one non-whitespace character in\n`q` to prevent accidental full-tree scans.\n\n**Returns:** JSON list response with matching filesystem entries.",
        "operationId": "search_environment_files_v1_sessions__session_id__resources_environments__environment_id__search_get",
        "parameters": [
          {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Environment resource id, e.g. `\"default\"`.",
            "in": "path",
            "name": "environment_id",
            "required": true,
            "schema": {
              "title": "Environment Id",
              "type": "string"
            }
          },
          {
            "description": "Case-insensitive search substring, e.g. `\"test.md\"`. Must contain at least one non-whitespace character.",
            "in": "query",
            "name": "q",
            "required": true,
            "schema": {
              "minLength": 1,
              "pattern": ".*\\S.*",
              "title": "Q",
              "type": "string"
            }
          },
          {
            "description": "Comma-separated glob patterns scoping which files are returned, e.g. `\"*.ts,src/**\"`.",
            "in": "query",
            "name": "include",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Include"
            }
          },
          {
            "description": "Comma-separated glob patterns for files to drop, e.g. `\"**/node_modules,*.test.ts\"`.",
            "in": "query",
            "name": "exclude",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Exclude"
            }
          },
          {
            "description": "Maximum number of results (1-500, default 500).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 500,
              "maximum": 500,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Search Environment Files",
        "tags": [
          "session_resources"
        ]
      }
    },
    "/v1/sessions/{session_id}/resources/environments/{environment_id}/search/{path}": {
      "get": {
        "description": "Search under a directory rather than the whole workspace.\n\nTakes the same path shape as the filesystem routes: relative to the\nworkspace, or absolute with a leading slash. Keeps search covering\nexactly what the file tree is showing.\n\n**Returns:** JSON list response with matching filesystem entries.",
        "operationId": "search_environment_files_under_v1_sessions__session_id__resources_environments__environment_id__search__path__get",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Environment resource id.",
            "in": "path",
            "name": "environment_id",
            "required": true,
            "schema": {
              "title": "Environment Id",
              "type": "string"
            }
          },
          {
            "description": "Directory to search under.",
            "in": "path",
            "name": "path",
            "required": true,
            "schema": {
              "title": "Path",
              "type": "string"
            }
          },
          {
            "description": "Case-insensitive search substring.",
            "in": "query",
            "name": "q",
            "required": true,
            "schema": {
              "minLength": 1,
              "pattern": ".*\\S.*",
              "title": "Q",
              "type": "string"
            }
          },
          {
            "description": "Comma-separated include globs.",
            "in": "query",
            "name": "include",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Include"
            }
          },
          {
            "description": "Comma-separated exclude globs.",
            "in": "query",
            "name": "exclude",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Exclude"
            }
          },
          {
            "description": "Maximum number of results (1-500, default 500).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 500,
              "maximum": 500,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Search Environment Files Under",
        "tags": [
          "session_resources"
        ]
      }
    },
    "/v1/sessions/{session_id}/resources/environments/{environment_id}/shell": {
      "post": {
        "description": "Execute a shell command in an environment.\n\n**Returns:** Shell result.",
        "operationId": "run_environment_shell_v1_sessions__session_id__resources_environments__environment_id__shell_post",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Environment resource id.",
            "in": "path",
            "name": "environment_id",
            "required": true,
            "schema": {
              "title": "Environment Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Run Environment Shell",
        "tags": [
          "session_resources"
        ]
      }
    },
    "/v1/sessions/{session_id}/resources/files": {
      "get": {
        "description": "List files owned by a session.\n\n**Returns:** `PaginatedList` of session file resources.",
        "operationId": "list_session_files_v1_sessions__session_id__resources_files_get",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Maximum number of files to return.",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 20,
              "maximum": 1000,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            }
          },
          {
            "description": "Cursor file ID for forward pagination.",
            "in": "query",
            "name": "after",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "After"
            }
          },
          {
            "description": "Cursor file ID for backward pagination.",
            "in": "query",
            "name": "before",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Before"
            }
          },
          {
            "description": "Sort direction, `\"desc\"` or `\"asc\"`.",
            "in": "query",
            "name": "order",
            "required": false,
            "schema": {
              "default": "desc",
              "pattern": "^(asc|desc)$",
              "title": "Order",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List Session Files",
        "tags": [
          "session_resources"
        ]
      },
      "post": {
        "description": "Upload a file into the session file namespace.\n\nAccepts the multipart upload shape used by session file resources.\n\n**Parameters**\n\n- `file` — The uploaded file (multipart form data).\n\n**Returns:** The session file resource object.",
        "operationId": "upload_session_file_v1_sessions__session_id__resources_files_post",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/Body_upload_session_file_v1_sessions__session_id__resources_files_post"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Upload Session File",
        "tags": [
          "session_resources"
        ]
      }
    },
    "/v1/sessions/{session_id}/resources/files/{file_id}": {
      "delete": {
        "description": "Delete a session file resource and its artifact bytes.\n\n**Returns:** Deletion confirmation object.",
        "operationId": "delete_session_file_v1_sessions__session_id__resources_files__file_id__delete",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Unique file identifier.",
            "in": "path",
            "name": "file_id",
            "required": true,
            "schema": {
              "title": "File Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Delete Session File",
        "tags": [
          "session_resources"
        ]
      },
      "get": {
        "description": "Retrieve metadata for a session file resource.\n\nVerifies that `file_id` belongs to `session_id`.\n\n**Returns:** The session file resource object.",
        "operationId": "get_session_file_v1_sessions__session_id__resources_files__file_id__get",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Unique file identifier.",
            "in": "path",
            "name": "file_id",
            "required": true,
            "schema": {
              "title": "File Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get Session File",
        "tags": [
          "session_resources"
        ]
      }
    },
    "/v1/sessions/{session_id}/resources/files/{file_id}/content": {
      "get": {
        "description": "Download raw content of a session file resource.\n\n**Returns:** Response with file bytes and Content-Type.",
        "operationId": "get_session_file_content_v1_sessions__session_id__resources_files__file_id__content_get",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Unique file identifier.",
            "in": "path",
            "name": "file_id",
            "required": true,
            "schema": {
              "title": "File Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get Session File Content",
        "tags": [
          "session_resources"
        ]
      }
    },
    "/v1/sessions/{session_id}/resources/files:copy": {
      "post": {
        "description": "Copy lineage-owned files into this (destination) session.\n\nAuthorizes by spawn lineage: `body.source_session_id` must be a\nSTRICT ancestor of this session up the `parent_conversation_id`\nchain — the session may not name itself as the source. Each source\nfile is read and re-stored as a new child-scoped row owned by\n`session_id` — this preserves the session-scoping invariant (the\nchild reads its OWN copy; no cross-session read grant is created).\nValidation is all-or-nothing: an unauthorized source, a missing\nfile, or a request past the copy limits copies nothing.\n\nThe request is bounded before any blob is read: the file count and\nthe summed `StoredFile.bytes` are checked against the copy limits\nduring metadata validation, so an over-limit request is rejected\nwithout buffering a single blob. Within the limits, files are copied\none at a time (read → create → put) so peak memory is a single blob,\nnot the whole batch.\n\n**Parameters**\n\n- `body` — Source session id plus the file ids to copy.\n\n**Returns:** A `session.files.copied` object carrying the `{source_file_id: new_file_id}` mapping.",
        "operationId": "copy_session_files_v1_sessions__session_id__resources_files_copy_post",
        "parameters": [
          {
            "description": "Destination (child) session/conversation id.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CopyFilesRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Copy Session Files",
        "tags": [
          "session_resources"
        ]
      }
    },
    "/v1/sessions/{session_id}/resources/terminals": {
      "get": {
        "description": "Return only terminal resources for a session.\n\nThe runner endpoint's pagination params (`limit` / `after` /\n`before` / `order`) are forwarded from the incoming query\nstring — without this, a client-requested `order=asc` (the web\nterminal tabs rely on creation order to keep the session's own\nterminal first) would be silently dropped and the runner's\n`desc` default would apply.\n\n**Returns:** `PaginatedList` of terminal resources.",
        "operationId": "list_session_terminals_v1_sessions__session_id__resources_terminals_get",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "List Session Terminals",
        "tags": [
          "session_resources"
        ]
      },
      "post": {
        "description": "Launch or return an existing terminal resource.\n\nPreserves `sys_terminal_launch` idempotency: an\nalready-running `(terminal, session_key)` returns the\nexisting resource.\n\nUser-initiated creates are gated on the agent's terminal\naccess: the requested `terminal` must be one of the names\ndeclared in the agent spec's `terminals:` block. Native\nharness bootstrap requests (marked `ensure_native_terminal`\nor `bridge_inject_dir` — the `omnigent claude` / `codex`\nwrappers launching the session's own CLI terminal) are exempt:\nthey launch undeclared names via the runner's\nsynthesize-from-body path and predate the gate. The markers\nare client-controlled, so the exemption is narrowed to the\nexact shape those wrappers send — a registered native terminal\nname with `session_key` `\"main\"` — anything else carrying a\nmarker still goes through the declared-name gate (it would\notherwise be an arbitrary-terminal bypass).\n\n**Returns:** The terminal resource object.\n\n**Raises**\n\n- `OmnigentError` — 400 when the requested terminal is not declared by the agent spec (or the agent has no `terminals:` block at all).",
        "operationId": "create_session_terminal_v1_sessions__session_id__resources_terminals_post",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Create Session Terminal",
        "tags": [
          "session_resources"
        ]
      }
    },
    "/v1/sessions/{session_id}/resources/terminals/{terminal_id}": {
      "delete": {
        "description": "Close a terminal resource.\n\nDelegates to `TerminalRegistry.close()` on the runner.\nReturns 404 for unknown terminals.\n\n**Returns:** Deletion confirmation object.",
        "operationId": "delete_session_terminal_v1_sessions__session_id__resources_terminals__terminal_id__delete",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Opaque terminal resource id.",
            "in": "path",
            "name": "terminal_id",
            "required": true,
            "schema": {
              "title": "Terminal Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Delete Session Terminal",
        "tags": [
          "session_resources"
        ]
      },
      "get": {
        "description": "Return a single terminal resource by id.\n\n**Returns:** The terminal resource object.",
        "operationId": "get_session_terminal_v1_sessions__session_id__resources_terminals__terminal_id__get",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Opaque terminal resource id.",
            "in": "path",
            "name": "terminal_id",
            "required": true,
            "schema": {
              "title": "Terminal Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get Session Terminal",
        "tags": [
          "session_resources"
        ]
      }
    },
    "/v1/sessions/{session_id}/resources/{resource_id}": {
      "get": {
        "description": "Return a single resource by id from the unified inventory.\n\n**Returns:** The resource object regardless of type.",
        "operationId": "get_session_resource_v1_sessions__session_id__resources__resource_id__get",
        "parameters": [
          {
            "description": "Session/conversation identifier.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Opaque resource id.",
            "in": "path",
            "name": "resource_id",
            "required": true,
            "schema": {
              "title": "Resource Id",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Get Session Resource",
        "tags": [
          "session_resources"
        ]
      }
    },
    "/v1/sessions/{session_id}/stream": {
      "get": {
        "description": "Subscribe to the session's live SSE event stream.\n\nDoes NOT replay history; clients reconcile via the snapshot\nendpoint. The generator emits `[DONE]` on normal completion\nand uses `finally` only for presence cleanup — see\n`_stream_live_events`.\n\nHolding this stream open registers the caller as a session\n*viewer* (presence): co-viewers' streams receive\n`session.presence` events on join/leave/idle edges, and\nthis stream's snapshot-on-connect includes the current\nviewer list. Presence is scoped to the session tree's root\nconversation, so viewers of different agents/sub-agents in\none session see each other. See\n`omnigent/server/presence.py`.\n\n**Returns:** An SSE `StreamingResponse`.\n\n**Raises**\n\n- `OmnigentError` — 404 if no session exists.",
        "operationId": "stream_session_v1_sessions__session_id__stream_get",
        "parameters": [
          {
            "description": "Session/conversation identifier, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          },
          {
            "description": "Presence idle flag computed by the web client at connect time (tab backgrounded ≥ its debounce). An idle *flip* mid-view arrives as a reconnect carrying the new value — there is no separate update endpoint.",
            "in": "query",
            "name": "idle",
            "required": false,
            "schema": {
              "default": false,
              "title": "Idle",
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {}
              },
              "text/event-stream": {
                "itemSchema": {
                  "$ref": "#/components/schemas/ServerStreamEvent"
                }
              }
            },
            "description": "SSE stream of `ServerStreamEvent` frames for the session."
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Stream Session",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/{session_id}/switch-agent": {
      "post": {
        "description": "Switch an existing session in place to a different agent/harness.\n\nUnlike fork, this keeps the SAME session — transcript, comments,\nfiles, host, and workspace are untouched; only the agent/harness\nchanges. The current session-scoped agent is replaced by a clone\nof the target built-in, model settings carry over only within the\nsame provider family (a model id is provider-bound), the native\nruntime session id is cleared, and the harness-presentation labels\nare recomputed for the target. The next turn cold-starts the new\nharness (rebuilding the native transcript from this session's own\nitems for a same-family native target). Only built-in agents are\nbindable, and only while the session is idle.\n\n**Parameters**\n\n- `body` — The validated `SessionSwitchAgentRequest`.\n\n**Returns:** A `SessionResponse` describing the session after the switch (status `\"idle\"`).\n\n**Raises**\n\n- `OmnigentError` — 404 if the session or target agent does not exist or the target is not a bindable built-in; 403 if the caller lacks edit access; 400 if the session is a sub-agent, has no agent binding, or the target bundle can't be loaded; 409 if a turn is currently running.",
        "operationId": "switch_session_agent_v1_sessions__session_id__switch_agent_post",
        "parameters": [
          {
            "description": "Session/conversation identifier to switch, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "session_id",
            "required": true,
            "schema": {
              "title": "Session Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SessionSwitchAgentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Switch Session Agent",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sessions/{source_id}/fork": {
      "post": {
        "description": "Fork an existing session into a new session.\n\nDeep-copies the source session's conversation items and\nclones the agent into a new session. When `body.agent_id`\nis set, the fork binds that built-in agent instead of the\nsource's — switching harness (e.g. Claude-SDK → Claude Code,\nor Claude → Codex). The source's model settings carry over\nonly within the same provider family; a same-family native\ntarget also carries conversation history (the runner rebuilds\nits transcript). The REPL/CLI binds the fork to its runner via\n`PATCH /v1/sessions/{id}` after creation.\n\nWhen `body.up_to_response_id` is set, only history up to and\nincluding that response is copied into the fork (a \"fork from\nthis response\"); a native target then rebuilds its transcript\nfrom the truncated items instead of resuming the source's full\nnative transcript.\n\nA sub-agent source is allowed, which is how a sub-agent is\npromoted to a session of its own: the fork is always a fresh\ntop-level conversation (no parent, its own spawn-tree root, its\nown owner grant), so it appears in the sidebar and outlives the\nparent. The source keeps running under its parent untouched,\nand the fork does not adopt the source's own children.\n\n**Parameters**\n\n- `body` — The validated `SessionForkRequest`.\n\n**Returns:** A `SessionResponse` describing the newly created fork (status `\"idle\"`).\n\n**Raises**\n\n- `OmnigentError` — 404 if *source_id* does not exist or `body.agent_id` is not a bindable built-in agent; 403 if the caller lacks read access; 400 if the source has no agent binding, or `body.up_to_response_id` names no response in the source session.",
        "operationId": "fork_session_v1_sessions__source_id__fork_post",
        "parameters": [
          {
            "description": "Session/conversation identifier of the source session to fork, e.g. `\"conv_abc123\"`.",
            "in": "path",
            "name": "source_id",
            "required": true,
            "schema": {
              "title": "Source Id",
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SessionForkRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionResponse"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Fork Session",
        "tags": [
          "sessions"
        ]
      }
    },
    "/v1/sharing": {
      "get": {
        "description": "Report both settings, whether each is editable here, and the tiers.",
        "operationId": "get_sharing_v1_sharing_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Get Sharing V1 Sharing Get",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Get Sharing",
        "tags": [
          "sharing"
        ]
      },
      "put": {
        "description": "Set the sharing mode and/or public-access setting (admin only).\n\nUpdates only the fields present in the body; requires at least one.\nRejects an unknown mode value with 400 (no fail-open coercion — an admin\nsetting a value should learn about a typo). Rejects a write to a setting\nthe deployment manages itself (not file-backed) with 403.",
        "operationId": "set_sharing_v1_sharing_put",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetSharingRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": true,
                  "title": "Response Set Sharing V1 Sharing Put",
                  "type": "object"
                }
              }
            },
            "description": "Successful Response"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            },
            "description": "Validation Error"
          }
        },
        "summary": "Set Sharing",
        "tags": [
          "sharing"
        ]
      }
    },
    "/v1/usage": {
      "get": {
        "description": "Aggregate the calling user's LLM spend across their sessions.\n\nrequire_user, not get_user_id: the aggregation scopes to the caller,\nso a request slipping through as `None` in multi-user mode would\nread another scope. Fail closed with 401 instead (`user_id` is\n`None` only when auth is disabled — the single-user / local case).",
        "operationId": "get_usage_v1_usage_get",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageReport"
                }
              }
            },
            "description": "Successful Response"
          }
        },
        "summary": "Get Usage",
        "tags": [
          "usage"
        ]
      }
    }
  },
  "servers": [
    {
      "description": "Self-hosted Omnigent server (default local port).",
      "url": "http://127.0.0.1:6767"
    }
  ],
  "tags": [
    {
      "description": "Create, inspect, fork, and drive agent sessions — the core unit of work. Covers session items and events, agent binding, permissions, labels, and child sessions. The files, terminals, and sandboxed environments attached to a session live under Session Resources.",
      "name": "sessions",
      "x-displayName": "Sessions"
    },
    {
      "description": "Files, terminals, and sandboxed environments attached to a session: upload and read files, create and manage terminals, and read, write, edit, and search the environment filesystem.",
      "name": "session_resources",
      "x-displayName": "Session Resources"
    },
    {
      "description": "Manage the MCP server declarations on a session's bound agent: list the configured servers and create, update, and remove them on session-scoped agents.",
      "name": "session_mcp_servers",
      "x-displayName": "Session MCP Servers"
    },
    {
      "description": "Discover the built-in agents available to bind to a session.",
      "name": "agents",
      "x-displayName": "Agents"
    },
    {
      "description": "Hosts that can launch runners. Browse the host filesystem and create directories.",
      "name": "hosts",
      "x-displayName": "Hosts"
    },
    {
      "description": "Launch runners on a host and check their status.",
      "name": "runners",
      "x-displayName": "Runners"
    },
    {
      "description": "Contextual policies scoped to a single session — list, create, update, and remove.",
      "name": "session_policies",
      "x-displayName": "Session Policies"
    },
    {
      "description": "Server-level default policies applied to new sessions.",
      "name": "default_policies",
      "x-displayName": "Default Policies"
    },
    {
      "description": "The catalog of policy types available to instantiate.",
      "name": "policy_registry",
      "x-displayName": "Policy Registry"
    },
    {
      "description": "Threaded comments on a session, including sending a comment to the agent.",
      "name": "comments",
      "x-displayName": "Comments"
    },
    {
      "description": "First-class, owner-private containers that group sessions — create, list, rename, and delete.",
      "name": "projects",
      "x-displayName": "Projects"
    },
    {
      "description": "Health, version, and identity endpoints for the running server.",
      "name": "system",
      "x-displayName": "System"
    }
  ]
}