{
  "openapi": "3.0.3",
  "info": {
    "title": "Assemble VMs API",
    "version": "0.1.0",
    "description": "Development preview of the hosted Assemble VMs API. Access requires an Assemble API key; customer signup and npm publication remain pending. Each session has one retained Linux VM and its own writable filesystem at /workspace. API tools and VM processes share files, with no automatic per-command rollback. Use the API to open sessions, work with files, run commands, issue SSH access, and call the provided harness. The public API has no automatic VM-replacement endpoint."
  },
  "servers": [
    {
      "url": "https://assemble-vms-api.fly.dev",
      "description": "Hosted development preview; operator-issued API key required"
    },
    {
      "url": "http://localhost:8787",
      "description": "Local development service"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/healthz": {
      "get": {
        "summary": "Check process liveness",
        "operationId": "health",
        "security": [],
        "responses": {
          "200": {
            "description": "Process health; this does not verify provider readiness.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "ok"
                      ]
                    }
                  },
                  "required": [
                    "status"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/readyz": {
      "get": {
        "summary": "Check service readiness",
        "operationId": "readiness",
        "description": "Unauthenticated readiness check. A coalesced database probe must succeed within two seconds, and the service must still accept work. This does not check VM or session-storage availability.",
        "security": [],
        "responses": {
          "200": {
            "description": "Database probe succeeded and the service accepts work.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "ready"
                      ]
                    }
                  },
                  "required": [
                    "status"
                  ]
                },
                "example": {
                  "status": "ready"
                }
              }
            }
          },
          "503": {
            "description": "Database probe failed or exceeded two seconds, or the service is draining.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "unavailable"
                      ]
                    }
                  },
                  "required": [
                    "status"
                  ]
                },
                "example": {
                  "status": "unavailable"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sessions": {
      "post": {
        "summary": "Open a session",
        "operationId": "openSession",
        "description": "Create or find an owner's session by name, then prepare its VM and filesystem. Reopening the same name reuses its identity. Selecting a different image for an existing name fails.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OpenSession"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Session ready for work.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "get": {
        "summary": "List sessions",
        "operationId": "listSessions",
        "parameters": [
          {
            "name": "after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One owner-scoped page of sessions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Session"
                      }
                    },
                    "nextCursor": {
                      "type": "string",
                      "format": "uuid",
                      "nullable": true
                    }
                  },
                  "required": [
                    "items",
                    "nextCursor"
                  ]
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/sessions/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Session ID returned by open or list.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Inspect a session",
        "operationId": "getSession",
        "responses": {
          "200": {
            "description": "Current session metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "summary": "Delete a session permanently",
        "operationId": "deleteSession",
        "description": "Permanently removes both the Assemble VM and its session filesystem. No restore endpoint is available. A running managed command or provided-agent request blocks deletion.",
        "responses": {
          "204": {
            "description": "Session and retained resources deleted."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/sessions/{id}/pause": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Session ID returned by open or list.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "summary": "Pause a session",
        "operationId": "pauseSession",
        "description": "Pause the VM while retaining session files and VM disk state. A running managed command or provided-agent request blocks the operation.",
        "responses": {
          "200": {
            "description": "Updated session metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/sessions/{id}/resume": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Session ID returned by open or list.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "summary": "Resume a session",
        "operationId": "resumeSession",
        "description": "Start or resume compute, verify ownership, and check the session mount. A running managed command or provided-agent request blocks the operation.",
        "responses": {
          "200": {
            "description": "Session ready for work.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/sessions/{id}/executions": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Session ID returned by open or list.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "summary": "Start a command",
        "operationId": "startExecution",
        "description": "Start an asynchronous managed command. The session must be running and may have only one running managed execution. An active provided-agent request reserves the session and blocks external commands. Preserve the returned ID for reconnect or cancellation. Commands mutate the shared session directly. Failed or cancelled commands may leave partial file changes; successful process exit does not establish that every write was flushed durably.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Execute"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Execution accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Execution"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/sessions/{id}/executions/{executionId}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Session ID returned by open or list.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        },
        {
          "name": "executionId",
          "in": "path",
          "required": true,
          "description": "Execution ID returned when starting a command.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Get execution status",
        "operationId": "getExecution",
        "responses": {
          "200": {
            "description": "Execution state and exit status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Execution"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "summary": "Request command cancellation",
        "operationId": "cancelExecution",
        "description": "Requests cancellation. The response is not confirmation that the process has stopped; follow events or inspect state until terminal.",
        "responses": {
          "202": {
            "description": "Cancellation requested; current execution state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Execution"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/sessions/{id}/executions/{executionId}/events": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Session ID returned by open or list.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        },
        {
          "name": "executionId",
          "in": "path",
          "required": true,
          "description": "Execution ID returned when starting a command.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Stream command events",
        "operationId": "streamExecution",
        "description": "Stream persisted stdout, stderr, error, and exit events as newline-delimited JSON. Every line has the ExecutionEvent shape. Reconnect using the last processed sequence in after. Disconnecting does not cancel the process.",
        "parameters": [
          {
            "name": "after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 2147483647,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "ExecutionEvent records in increasing sequence order.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string",
                  "description": "One JSON event per line."
                },
                "example": "{\"sequence\":1,\"type\":\"stdout\",\"data\":\"/workspace\\n\"}\n{\"sequence\":2,\"type\":\"exit\",\"state\":\"completed\",\"exitCode\":0}\n"
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/sessions/{id}/files": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Session ID returned by open or list.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        },
        {
          "name": "path",
          "in": "query",
          "required": true,
          "schema": {
            "type": "string",
            "maxLength": 4096
          },
          "description": "A file path relative to /workspace, or an absolute path inside it."
        }
      ],
      "get": {
        "summary": "Read a file",
        "operationId": "readFile",
        "description": "Read a regular session file. File operations reject symlink traversal and files over 10 MiB.",
        "responses": {
          "200": {
            "description": "Raw file bytes.",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "put": {
        "summary": "Write a file",
        "operationId": "writeFile",
        "description": "Write raw bytes, creating missing parent directories inside the running session. The maximum request body is 10 MiB. A running managed command or provided-agent request blocks file writes.",
        "requestBody": {
          "required": true,
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "File written."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/sessions/{id}/files/list": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Session ID returned by open or list.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "List a directory",
        "operationId": "listFiles",
        "parameters": [
          {
            "name": "path",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "default": ".",
              "maxLength": 4096
            }
          }
        ],
        "description": "List up to 1,000 direct entries. A larger directory returns an error; this endpoint does not paginate directory entries.",
        "responses": {
          "200": {
            "description": "Directory entries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/FileEntry"
                      }
                    }
                  },
                  "required": [
                    "items"
                  ]
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/sessions/{id}/ssh": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Session ID returned by open or list.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "summary": "Create temporary SSH access",
        "operationId": "createSshAccess",
        "description": "Request an expiring connection command for the same session VM. SSH processes operate outside managed-command coordination.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "expiresInMinutes": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 60,
                    "default": 15
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Temporary SSH grant; the command contains a credential.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SshAccess"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/sessions/{id}/ssh/{accessId}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Session ID returned by open or list.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        },
        {
          "name": "accessId",
          "in": "path",
          "required": true,
          "description": "SSH grant ID returned when access was created.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "delete": {
        "summary": "Revoke SSH access",
        "operationId": "revokeSshAccess",
        "description": "Request revocation of an issued SSH grant. Stop active guest processes separately.",
        "responses": {
          "204": {
            "description": "Grant revoked."
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/sessions/{id}/agent": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Session ID returned by open or list.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "summary": "Run the provided Pi harness",
        "operationId": "runAgent",
        "description": "Run one fresh Pi conversation against this session. Omitting provider and model uses the configured service default; supply both to override it. The service must have credentials for the selected provider. The request reserves the session until it finishes. Streams AgentEvent records as NDJSON. Agent history and events are not persisted for replay. Disconnect requests cancellation; requests have a 15-minute deadline.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RunAgent"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "AgentEvent records; done or error is terminal.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "type": "string",
                  "description": "One JSON event per line."
                },
                "example": "{\"type\":\"text\",\"delta\":\"I will inspect the session.\"}\n{\"type\":\"tool_start\",\"tool\":\"read\"}\n{\"type\":\"tool_end\",\"tool\":\"read\",\"isError\":false}\n{\"type\":\"done\"}\n"
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/sessions/{id}/computer": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Session ID returned by open or list.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "summary": "Perform a computer action",
        "operationId": "computerAction",
        "description": "Click, type, press keys, scroll, drag, or capture the session desktop. Requires a running session; the desktop starts on first use. Actions run alongside commands and SSH without taking the session lease.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ComputerAction"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Action result. Screenshots return base64 PNG data.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ComputerActionResult"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/sessions/{id}/streams": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Session ID returned by open or list.",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "summary": "Create a live stream ticket",
        "operationId": "createStream",
        "description": "Mint a single-use WebSocket ticket for a live desktop view (VNC-compatible binary stream) or an interactive terminal (JSON text frames). Connect to the returned url with exactly the returned subprotocols before expiresAt. The credential travels only in the subprotocol. Streams end when the session pauses, stops, or is deleted.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateStream"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Single-use connection grant; the subprotocols contain a credential.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StreamAccess"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Assemble product API key",
        "description": "Use the owner-scoped product API key provided by the service operator."
      }
    },
    "schemas": {
      "Session": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "starting",
              "running",
              "paused",
              "stopped",
              "error",
              "deleting"
            ]
          },
          "rootDirectory": {
            "type": "string",
            "example": "/workspace",
            "description": "Persistent project directory used by file operations and the default working directory for managed commands."
          },
          "homeDirectory": {
            "type": "string",
            "description": "VM-local HOME for managed commands. Raw SSH uses the image login home unless the user explicitly exports this value."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "resources": {
            "$ref": "#/components/schemas/SessionResources",
            "description": "Explicit creation allocation. Requires an operator-enabled configuration. Omit on reopen to retain the saved allocation."
          }
        },
        "required": [
          "id",
          "name",
          "state",
          "rootDirectory",
          "homeDirectory",
          "createdAt",
          "updatedAt"
        ],
        "description": "One persistent Assemble VM and its session filesystem. The session remains allocated until explicitly deleted; completing an execution or agent run does not delete it."
      },
      "OpenSession": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "pattern": "^[a-z0-9][a-z0-9_-]*$",
            "example": "my-agent-project"
          },
          "resources": {
            "$ref": "#/components/schemas/SessionResources",
            "description": "Explicit creation allocation. Requires an operator-enabled configuration. Omit on reopen to retain the saved allocation."
          }
        },
        "required": [
          "name"
        ],
        "additionalProperties": false
      },
      "Execution": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "sessionId": {
            "type": "string",
            "format": "uuid"
          },
          "state": {
            "type": "string",
            "enum": [
              "running",
              "completed",
              "failed",
              "cancelled",
              "timed_out",
              "interrupted"
            ]
          },
          "exitCode": {
            "type": "integer",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "finishedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "required": [
          "id",
          "sessionId",
          "state",
          "exitCode",
          "createdAt",
          "finishedAt"
        ]
      },
      "Execute": {
        "type": "object",
        "properties": {
          "command": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64000,
            "example": "pwd"
          },
          "cwd": {
            "type": "string",
            "maxLength": 4096,
            "description": "Working directory within /workspace; defaults to /workspace."
          },
          "timeoutSeconds": {
            "type": "integer",
            "minimum": 1,
            "maximum": 3600,
            "default": 300
          },
          "env": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "maxLength": 32768
            },
            "description": "Per-command guest environment overrides; names must match ^[A-Za-z_][A-Za-z0-9_]*$."
          }
        },
        "required": [
          "command"
        ],
        "additionalProperties": false
      },
      "FileEntry": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "file",
              "directory",
              "symlink",
              "other"
            ]
          },
          "size": {
            "type": "integer",
            "minimum": 0
          }
        },
        "required": [
          "name",
          "type",
          "size"
        ]
      },
      "SshAccess": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "command": {
            "type": "string",
            "description": "Temporary connection command containing access credentials. Keep it out of shared logs."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "command",
          "expiresAt"
        ]
      },
      "RunAgent": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64000
          },
          "provider": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80,
            "example": "openai"
          },
          "model": {
            "type": "string",
            "minLength": 1,
            "maxLength": 160,
            "example": "gpt-4.1"
          }
        },
        "required": [
          "message"
        ],
        "additionalProperties": false,
        "description": "Uses the service default model when provider and model are omitted. Supply both fields together to override the default.",
        "oneOf": [
          {
            "required": [
              "provider",
              "model"
            ]
          },
          {
            "not": {
              "anyOf": [
                {
                  "required": [
                    "provider"
                  ]
                },
                {
                  "required": [
                    "model"
                  ]
                }
              ]
            }
          }
        ],
        "example": {
          "message": "Write a short project plan to plan.md, then read it back."
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "requestId": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message",
              "requestId"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "ExecutionEvent": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "sequence": {
                "type": "integer",
                "minimum": 1
              },
              "type": {
                "type": "string",
                "enum": [
                  "stdout"
                ]
              },
              "data": {
                "type": "string"
              }
            },
            "required": [
              "sequence",
              "type",
              "data"
            ]
          },
          {
            "type": "object",
            "properties": {
              "sequence": {
                "type": "integer",
                "minimum": 1
              },
              "type": {
                "type": "string",
                "enum": [
                  "stderr"
                ]
              },
              "data": {
                "type": "string"
              }
            },
            "required": [
              "sequence",
              "type",
              "data"
            ]
          },
          {
            "type": "object",
            "properties": {
              "sequence": {
                "type": "integer",
                "minimum": 1
              },
              "type": {
                "type": "string",
                "enum": [
                  "exit"
                ]
              },
              "exitCode": {
                "type": "integer",
                "nullable": true
              },
              "state": {
                "type": "string",
                "enum": [
                  "running",
                  "completed",
                  "failed",
                  "cancelled",
                  "timed_out",
                  "interrupted"
                ]
              }
            },
            "required": [
              "sequence",
              "type",
              "exitCode",
              "state"
            ]
          },
          {
            "type": "object",
            "properties": {
              "sequence": {
                "type": "integer",
                "minimum": 1
              },
              "type": {
                "type": "string",
                "enum": [
                  "error"
                ]
              },
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "sequence",
              "type",
              "code",
              "message"
            ]
          }
        ]
      },
      "AgentEvent": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "text"
                ]
              },
              "delta": {
                "type": "string"
              }
            },
            "required": [
              "type",
              "delta"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "tool_start"
                ]
              },
              "tool": {
                "type": "string"
              }
            },
            "required": [
              "type",
              "tool"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "tool_end"
                ]
              },
              "tool": {
                "type": "string"
              },
              "isError": {
                "type": "boolean"
              }
            },
            "required": [
              "type",
              "tool",
              "isError"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "done"
                ]
              }
            },
            "required": [
              "type"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "error"
                ]
              },
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "type",
              "code",
              "message"
            ]
          }
        ]
      },
      "SessionResources": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "cpu",
          "memory",
          "disk"
        ],
        "properties": {
          "cpu": {
            "type": "integer",
            "minimum": 1,
            "maximum": 9007199254740991,
            "description": "vCPU count."
          },
          "memory": {
            "type": "integer",
            "minimum": 1,
            "maximum": 9007199254740991,
            "description": "RAM in GiB."
          },
          "disk": {
            "type": "integer",
            "minimum": 1,
            "maximum": 9007199254740991,
            "description": "Local VM disk in GiB; separate from session-filesystem storage."
          }
        }
      },
      "ComputerAction": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "screenshot"
                ]
              }
            },
            "required": [
              "type"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "click"
                ]
              },
              "x": {
                "type": "integer",
                "minimum": 0,
                "maximum": 20000
              },
              "y": {
                "type": "integer",
                "minimum": 0,
                "maximum": 20000
              },
              "button": {
                "type": "string",
                "enum": [
                  "left",
                  "right",
                  "middle"
                ],
                "default": "left"
              },
              "double": {
                "type": "boolean",
                "default": false
              }
            },
            "required": [
              "type",
              "x",
              "y"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "move"
                ]
              },
              "x": {
                "type": "integer",
                "minimum": 0,
                "maximum": 20000
              },
              "y": {
                "type": "integer",
                "minimum": 0,
                "maximum": 20000
              }
            },
            "required": [
              "type",
              "x",
              "y"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "drag"
                ]
              },
              "startX": {
                "type": "integer",
                "minimum": 0,
                "maximum": 20000
              },
              "startY": {
                "type": "integer",
                "minimum": 0,
                "maximum": 20000
              },
              "endX": {
                "type": "integer",
                "minimum": 0,
                "maximum": 20000
              },
              "endY": {
                "type": "integer",
                "minimum": 0,
                "maximum": 20000
              },
              "button": {
                "type": "string",
                "enum": [
                  "left",
                  "right",
                  "middle"
                ],
                "default": "left"
              }
            },
            "required": [
              "type",
              "startX",
              "startY",
              "endX",
              "endY"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "scroll"
                ]
              },
              "x": {
                "type": "integer",
                "minimum": 0,
                "maximum": 20000
              },
              "y": {
                "type": "integer",
                "minimum": 0,
                "maximum": 20000
              },
              "direction": {
                "type": "string",
                "enum": [
                  "up",
                  "down"
                ]
              },
              "amount": {
                "type": "integer",
                "minimum": 1,
                "maximum": 10,
                "default": 3
              }
            },
            "required": [
              "type",
              "x",
              "y",
              "direction"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "type"
                ]
              },
              "text": {
                "type": "string",
                "minLength": 1,
                "maxLength": 16000
              }
            },
            "required": [
              "type",
              "text"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "key"
                ]
              },
              "key": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64
              },
              "modifiers": {
                "type": "array",
                "maxItems": 4,
                "items": {
                  "type": "string",
                  "enum": [
                    "ctrl",
                    "alt",
                    "shift",
                    "cmd"
                  ]
                },
                "default": []
              }
            },
            "required": [
              "type",
              "key"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "cursor_position"
                ]
              }
            },
            "required": [
              "type"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "display"
                ]
              }
            },
            "required": [
              "type"
            ],
            "additionalProperties": false
          }
        ],
        "description": "One desktop action. Keys use canonical names such as enter, escape, tab, letters, digits, and function keys."
      },
      "ComputerActionResult": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "action": {
                "type": "string",
                "enum": [
                  "screenshot"
                ]
              },
              "format": {
                "type": "string",
                "enum": [
                  "png"
                ]
              },
              "data": {
                "type": "string",
                "description": "Base64-encoded image."
              },
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              }
            },
            "required": [
              "action",
              "format",
              "data"
            ]
          },
          {
            "type": "object",
            "properties": {
              "action": {
                "type": "string",
                "enum": [
                  "cursor_position"
                ]
              },
              "x": {
                "type": "integer"
              },
              "y": {
                "type": "integer"
              }
            },
            "required": [
              "action",
              "x",
              "y"
            ]
          },
          {
            "type": "object",
            "properties": {
              "action": {
                "type": "string",
                "enum": [
                  "display"
                ]
              },
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              }
            },
            "required": [
              "action",
              "width",
              "height"
            ]
          },
          {
            "type": "object",
            "properties": {
              "action": {
                "type": "string",
                "enum": [
                  "click",
                  "move",
                  "drag",
                  "scroll",
                  "type",
                  "key"
                ]
              }
            },
            "required": [
              "action"
            ]
          }
        ]
      },
      "CreateStream": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "desktop",
              "terminal"
            ]
          },
          "cols": {
            "type": "integer",
            "minimum": 2,
            "maximum": 500,
            "description": "Terminal streams only. Defaults to 120."
          },
          "rows": {
            "type": "integer",
            "minimum": 2,
            "maximum": 300,
            "description": "Terminal streams only. Defaults to 30."
          }
        },
        "required": [
          "type"
        ],
        "additionalProperties": false
      },
      "StreamAccess": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Ticket ID; each ticket admits one connection."
          },
          "type": {
            "type": "string",
            "enum": [
              "desktop",
              "terminal"
            ]
          },
          "url": {
            "type": "string",
            "description": "WebSocket endpoint to connect to."
          },
          "subprotocols": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Pass exactly these WebSocket subprotocols; the second carries the single-use credential."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Connect-by deadline for the ticket, not a stream duration."
          }
        },
        "required": [
          "id",
          "type",
          "url",
          "subprotocols",
          "expiresAt"
        ]
      }
    },
    "responses": {
      "Error": {
        "description": "Structured API error. HTTP 400 invalid request, 401 authentication, 404 missing resource, 409 conflict, 413 size limit, 429 quota, or 503 unavailable.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}
