From 011ca272874f9910c1821d610d350d7bbf36224b Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Mon, 26 Jan 2026 00:13:17 -0800 Subject: [PATCH] feat: expand api snapshots and schema tooling --- .github/workflows/release.yaml | 99 +- ARCHITECTURE.md | 174 + CLAUDE.md | 12 +- Cargo.toml | 67 + README.md | 23 + bugs.md | 2 + frontend/packages/inspector/src/App.tsx | 392 +- .../artifacts/json-schema/amp.json | 153 + .../artifacts/json-schema/claude.json | 182 + .../artifacts/json-schema/codex.json | 18260 ++++++++++++++++ .../artifacts/json-schema/opencode.json | 5921 +++++ resources/agent-schemas/deno.lock | 695 + resources/agent-schemas/package.json | 6 +- resources/agent-schemas/src/amp.ts | 2 +- .../src/claude-event-types-cli.ts | 11 + .../src/claude-event-types-docs.ts | 8 + .../src/claude-event-types-sdk.ts | 4 + .../agent-schemas/src/claude-event-types.ts | 338 + resources/agent-schemas/src/claude.ts | 97 +- resources/agent-schemas/src/codex.ts | 113 +- resources/agent-schemas/src/index.ts | 4 +- scripts/release/main.ts | 293 +- .../src/generated => openapi}/openapi.json | 79 + sdks/typescript/package.json | 4 +- sdks/typescript/src/client.ts | 29 +- sdks/typescript/src/index.ts | 7 + sdks/typescript/src/spawn.ts | 16 +- server/AGENTS.md | 1 + server/CLAUDE.md | 68 + server/packages/agent-credentials/Cargo.toml | 8 +- server/packages/agent-management/Cargo.toml | 24 +- .../packages/agent-management/src/testing.rs | 303 +- server/packages/agent-schema/Cargo.toml | 18 - server/packages/agent-schema/src/lib.rs | 76 - server/packages/error/Cargo.toml | 10 +- .../extracted-agent-schemas/Cargo.toml | 19 + .../build.rs | 2 +- .../extracted-agent-schemas/src/lib.rs | 111 + server/packages/openapi-gen/Cargo.toml | 12 +- server/packages/sandbox-agent/Cargo.toml | 50 +- server/packages/sandbox-agent/src/router.rs | 7 +- .../sandbox-agent/tests/http_sse_snapshots.rs | 806 +- ...points_snapshots@agent_install_claude.snap | 6 + ...ndpoints_snapshots@agent_modes_claude.snap | 12 + ...ndpoints_snapshots@agents_list_global.snap | 10 + ...oints_snapshots@create_session_claude.snap | 6 + ...api_endpoints_snapshots@health_global.snap | 6 + ...dpoints_snapshots@send_message_claude.snap | 6 + ...points_snapshots@sessions_list_global.snap | 15 + ...ow_snapshots@permission_events_claude.snap | 21 + ...shots@permission_reply_missing_claude.snap | 11 + ...apshots@question_reject_events_claude.snap | 21 + ...pshots@question_reject_missing_claude.snap | 11 + ...napshots@question_reply_events_claude.snap | 21 + ...apshots@question_reply_missing_claude.snap | 11 + ...h_snapshots@auth_health_public_global.snap | 8 + ...h_snapshots@auth_invalid_token_global.snap | 13 + ...h_snapshots@auth_missing_token_global.snap | 13 + ...uth_snapshots@auth_valid_token_global.snap | 12 + ...ts__cors_snapshots@cors_actual_global.snap | 12 + ..._cors_snapshots@cors_preflight_global.snap | 11 + ...cy_snapshot@concurrency_events_claude.snap | 39 + ...tp_events_snapshot@http_events_claude.snap | 21 + ...sse_events_snapshot@sse_events_claude.snap | 21 + .../universal-agent-schema/Cargo.toml | 12 +- .../src/agents/codex.rs | 1038 +- .../universal-agent-schema/src/lib.rs | 2 +- .../packages/universal-schema-gen/Cargo.toml | 12 + server/packages/universal-schema-gen/build.rs | 26 + .../packages/universal-schema-gen/src/lib.rs | 2 + spec/universal-schema.json | 655 + todo.md | 1 + 72 files changed, 29480 insertions(+), 1081 deletions(-) create mode 100644 ARCHITECTURE.md create mode 100644 bugs.md create mode 100644 resources/agent-schemas/artifacts/json-schema/amp.json create mode 100644 resources/agent-schemas/artifacts/json-schema/claude.json create mode 100644 resources/agent-schemas/artifacts/json-schema/codex.json create mode 100644 resources/agent-schemas/artifacts/json-schema/opencode.json create mode 100644 resources/agent-schemas/deno.lock create mode 100644 resources/agent-schemas/src/claude-event-types-cli.ts create mode 100644 resources/agent-schemas/src/claude-event-types-docs.ts create mode 100644 resources/agent-schemas/src/claude-event-types-sdk.ts create mode 100644 resources/agent-schemas/src/claude-event-types.ts rename sdks/{typescript/src/generated => openapi}/openapi.json (94%) create mode 120000 server/AGENTS.md create mode 100644 server/CLAUDE.md delete mode 100644 server/packages/agent-schema/Cargo.toml delete mode 100644 server/packages/agent-schema/src/lib.rs create mode 100644 server/packages/extracted-agent-schemas/Cargo.toml rename server/packages/{agent-schema => extracted-agent-schemas}/build.rs (98%) create mode 100644 server/packages/extracted-agent-schemas/src/lib.rs create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@agent_install_claude.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@agent_modes_claude.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@agents_list_global.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@create_session_claude.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@health_global.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@send_message_claude.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@sessions_list_global.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@permission_events_claude.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@permission_reply_missing_claude.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reject_events_claude.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reject_missing_claude.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reply_events_claude.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reply_missing_claude.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__auth_snapshots@auth_health_public_global.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__auth_snapshots@auth_invalid_token_global.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__auth_snapshots@auth_missing_token_global.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__auth_snapshots@auth_valid_token_global.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__cors_snapshots@cors_actual_global.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__cors_snapshots@cors_preflight_global.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__run_concurrency_snapshot@concurrency_events_claude.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__run_http_events_snapshot@http_events_claude.snap create mode 100644 server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__run_sse_events_snapshot@sse_events_claude.snap create mode 100644 server/packages/universal-schema-gen/Cargo.toml create mode 100644 server/packages/universal-schema-gen/build.rs create mode 100644 server/packages/universal-schema-gen/src/lib.rs create mode 100644 spec/universal-schema.json diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 54439f9..c66bc66 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,14 +1,17 @@ name: release on: - release: - types: [published] workflow_dispatch: inputs: version: description: "Version (e.g. 0.1.0 or v0.1.0)" required: true type: string + latest: + description: "Latest" + required: true + type: boolean + default: true defaults: run: @@ -18,44 +21,33 @@ env: CARGO_INCREMENTAL: 0 jobs: - checks: - uses: ./.github/workflows/ci.yaml - setup: name: "Setup" - needs: [checks] runs-on: ubuntu-24.04 - outputs: - version: ${{ steps.vars.outputs.version }} - latest: ${{ steps.latest.outputs.latest }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 + - uses: dtolnay/rust-toolchain@stable + + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 with: node-version: 20 + cache: pnpm - name: Install tsx run: npm install -g tsx - - name: Resolve version - id: vars + - name: Run setup phase run: | - if [ "${{ github.event_name }}" = "release" ]; then - VERSION="${{ github.event.release.tag_name }}" - else - VERSION="${{ inputs.version }}" + CMD="./scripts/release/main.ts --version '${{ inputs.version }}' --phase setup-ci" + if [ "${{ inputs.latest }}" != "true" ]; then + CMD="$CMD --no-latest" fi - - VERSION="${VERSION#v}" - echo "version=$VERSION" >> "$GITHUB_OUTPUT" - - - name: Determine latest - id: latest - run: | - ./scripts/release/main.ts --version "${{ steps.vars.outputs.version }}" --print-latest --output "$GITHUB_OUTPUT" + eval "$CMD" binaries: name: "Build Binaries" @@ -99,8 +91,8 @@ jobs: name: binary-${{ matrix.target }} path: dist/sandbox-agent-${{ matrix.target }}${{ matrix.binary_ext }} - publish: - name: "Publish Packages" + complete: + name: "Complete" needs: [setup, binaries] runs-on: ubuntu-24.04 steps: @@ -115,49 +107,12 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 20 - registry-url: 'https://registry.npmjs.org' + registry-url: "https://registry.npmjs.org" cache: pnpm - name: Install tsx run: npm install -g tsx - - name: Download binaries - uses: actions/download-artifact@v4 - with: - path: dist/ - pattern: binary-* - merge-multiple: true - - - name: List downloaded binaries - run: ls -la dist/ - - - name: Publish all - env: - CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - run: | - VERSION="${{ needs.setup.outputs.version }}" - ./scripts/release/main.ts --version "$VERSION" \ - --publish-crates \ - --publish-npm-sdk \ - --publish-npm-cli - - artifacts: - name: "Upload Artifacts" - needs: [setup, binaries] - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: actions/setup-node@v4 - with: - node-version: 20 - - - name: Install tsx - run: npm install -g tsx - - name: Install AWS CLI run: | sudo apt-get update @@ -177,19 +132,15 @@ jobs: - name: List downloaded binaries run: ls -la dist/ - - name: Upload artifacts + - name: Publish & upload artifacts env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} R2_RELEASES_ACCESS_KEY_ID: ${{ secrets.R2_RELEASES_ACCESS_KEY_ID }} R2_RELEASES_SECRET_ACCESS_KEY: ${{ secrets.R2_RELEASES_SECRET_ACCESS_KEY }} run: | - VERSION="${{ needs.setup.outputs.version }}" - if [ "${{ needs.setup.outputs.latest }}" = "true" ]; then - LATEST_FLAG="--latest" - else - LATEST_FLAG="--no-latest" + CMD="./scripts/release/main.ts --version '${{ inputs.version }}' --phase complete-ci --no-validate-git" + if [ "${{ inputs.latest }}" != "true" ]; then + CMD="$CMD --no-latest" fi - - ./scripts/release/main.ts --version "$VERSION" $LATEST_FLAG \ - --upload-typescript \ - --upload-install \ - --upload-binaries + eval "$CMD" diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..ffe035b --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,174 @@ +# Architecture + +This document covers three key architectural areas of the sandbox-daemon system. + +## Agent Schema Pipeline + +The schema pipeline extracts type definitions from AI coding agents and converts them to a universal format. + +### Schema Extraction + +TypeScript extractors in `resources/agent-schemas/src/` pull schemas from each agent: + +| Agent | Source | Extractor | +|-------|--------|-----------| +| Claude | `claude --output-format json --json-schema` | `claude.ts` | +| Codex | `codex app-server generate-json-schema` | `codex.ts` | +| OpenCode | GitHub OpenAPI spec | `opencode.ts` | +| Amp | Scrapes ampcode.com docs | `amp.ts` | + +All extractors include fallback schemas for when CLIs or URLs are unavailable. + +**Output:** JSON schemas written to `resources/agent-schemas/artifacts/json-schema/` + +### Rust Type Generation + +The `server/packages/extracted-agent-schemas/` package generates Rust types at build time: + +- `build.rs` reads JSON schemas and uses the `typify` crate to generate Rust structs +- Generated code is written to `$OUT_DIR/{agent}.rs` +- Types are exposed via `include!()` macros in `src/lib.rs` + +``` +resources/agent-schemas/artifacts/json-schema/*.json + ↓ (build.rs + typify) +$OUT_DIR/{claude,codex,opencode,amp}.rs + ↓ (include!) +extracted_agent_schemas::{claude,codex,opencode,amp}::* +``` + +### Universal Schema + +The `server/packages/universal-agent-schema/` package defines agent-agnostic types: + +**Core types** (`src/lib.rs`): +- `UniversalEvent` - Wrapper with id, timestamp, session_id, agent, data +- `UniversalEventData` - Enum: Message, Started, Error, QuestionAsked, PermissionAsked, Unknown +- `UniversalMessage` - Parsed (role, parts, metadata) or Unparsed (raw JSON) +- `UniversalMessagePart` - Text, ToolCall, ToolResult, FunctionCall, FunctionResult, File, Image, Error, Unknown + +**Converters** (`src/agents/{claude,codex,opencode,amp}.rs`): +- Each agent has a converter module that transforms native events to universal format +- Conversions are best-effort; unparseable data preserved in `Unparsed` or `Unknown` variants + +## Session Management + +Sessions track agent conversations with in-memory state. + +### Storage + +Sessions are stored in an in-memory `HashMap` inside `SessionManager`: + +```rust +struct SessionManager { + sessions: Mutex>, + // ... +} +``` + +There is no disk persistence. Sessions are ephemeral and lost on server restart. + +### SessionState + +Each session tracks: + +| Field | Purpose | +|-------|---------| +| `session_id` | Client-provided identifier | +| `agent` | Agent type (Claude, Codex, OpenCode, Amp) | +| `agent_mode` | Operating mode (build, plan, custom) | +| `permission_mode` | Permission handling (default, plan, bypass) | +| `model` | Optional model override | +| `events: Vec` | Full event history | +| `pending_questions` | Question IDs awaiting reply | +| `pending_permissions` | Permission IDs awaiting reply | +| `broadcaster` | Tokio broadcast channel for SSE streaming | +| `ended` | Whether agent process has terminated | + +### Lifecycle + +``` +POST /v1/sessions/{sessionId} Create session, auto-install agent + ↓ +POST /v1/sessions/{id}/messages Spawn agent subprocess, stream output + ↓ +GET /v1/sessions/{id}/events Poll for new events (offset-based) +GET /v1/sessions/{id}/events/sse Subscribe to SSE stream + ↓ +POST .../questions/{id}/reply Answer agent question +POST .../permissions/{id}/reply Grant/deny permission request + ↓ +(agent process terminates) Session marked as ended +``` + +### Event Flow + +When a message is sent: + +1. `send_message()` spawns the agent CLI as a subprocess +2. `consume_spawn()` reads stdout/stderr line by line +3. Each JSON line is parsed and converted via `parse_agent_line()` +4. Events are recorded via `record_event()` which: + - Assigns incrementing event ID + - Appends to `events` vector + - Broadcasts to SSE subscribers + +## SDK Modes + +The TypeScript SDK supports two connection modes. + +### Embedded Mode + +Defined in `sdks/typescript/src/spawn.ts`: + +1. **Binary resolution**: Checks `SANDBOX_AGENT_BIN` env, then platform-specific npm package, then `PATH` +2. **Port selection**: Uses provided port or finds a free one via `net.createServer()` +3. **Token generation**: Uses provided token or generates random 24-byte hex string +4. **Spawn**: Launches `sandbox-agent --host --port --token ` +5. **Health wait**: Polls `GET /v1/health` until server is ready (up to 15s timeout) +6. **Cleanup**: On dispose, sends SIGTERM then SIGKILL if needed; also registers process exit handlers + +```typescript +const handle = await spawnSandboxDaemon({ log: "inherit" }); +// handle.baseUrl = "http://127.0.0.1:" +// handle.token = "" +// handle.dispose() to cleanup +``` + +### Server Mode + +Defined in `sdks/typescript/src/client.ts`: + +- Direct HTTP client to a remote `sandbox-agent` server +- Uses provided `baseUrl` and optional `token` +- No subprocess management + +```typescript +const client = new SandboxDaemonClient({ + baseUrl: "http://remote-server:8080", + token: "secret", +}); +``` + +### Auto-Detection + +`SandboxDaemonClient.connect()` chooses the mode automatically: + +```typescript +// If baseUrl provided → server mode +const client = await SandboxDaemonClient.connect({ + baseUrl: "http://remote:8080", +}); + +// If no baseUrl → embedded mode (spawns subprocess) +const client = await SandboxDaemonClient.connect({}); + +// Explicit control +const client = await SandboxDaemonClient.connect({ + spawn: { enabled: true, port: 9000 }, +}); +``` + +The `spawn` option can be: +- `true` / `false` - Enable/disable embedded mode +- `SandboxDaemonSpawnOptions` - Fine-grained control over host, port, token, binary path, timeout, logging diff --git a/CLAUDE.md b/CLAUDE.md index 885eef9..172221a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,7 +9,15 @@ There are two ways to work with the SDKs: ## Agent Schemas -Agent schemas (Claude Code, Codex, OpenCode, Amp) are available for reference in `resources/agent-schemas/dist/`. +Agent schemas (Claude Code, Codex, OpenCode, Amp) are available for reference in `resources/agent-schemas/artifacts/json-schema/`. + +Extraction methods: +- **Claude**: Uses `claude --output-format json --json-schema` CLI command +- **Codex**: Uses `codex app-server generate-json-schema` CLI command +- **OpenCode**: Fetches from GitHub OpenAPI spec +- **Amp**: Scrapes from `https://ampcode.com/manual/appendix?preview#message-schema` + +All extractors have fallback schemas for when CLI/URL is unavailable. Research on how different agents operate (CLI flags, streaming formats, HITL patterns, etc.) is in `research/agents/`. When adding or making changes to agent docs, follow the same structure as existing files. @@ -24,6 +32,8 @@ Universal schema guidance: - Update `CLAUDE.md` to keep CLI endpoints in sync with HTTP API changes. - When changing the HTTP API, update the TypeScript SDK and CLI together. - Do not make breaking changes to API endpoints. +- When changing API routes, ensure the HTTP/SSE test suite has full coverage of every route. +- When agent schema changes, ensure API tests cover the new schema and event shapes end-to-end. ### CLI ⇄ HTTP endpoint map (keep in sync) diff --git a/Cargo.toml b/Cargo.toml index f60797c..23000a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,70 @@ authors = ["Sandbox Agent Contributors"] license = "Apache-2.0" repository = "https://github.com/rivet-dev/sandbox-agent" description = "Universal agent API for AI coding assistants" + +[workspace.dependencies] +# Internal crates +sandbox-agent-core = { path = "server/packages/sandbox-agent" } +sandbox-agent-error = { path = "server/packages/error" } +sandbox-agent-agent-management = { path = "server/packages/agent-management" } +sandbox-agent-agent-credentials = { path = "server/packages/agent-credentials" } +sandbox-agent-universal-agent-schema = { path = "server/packages/universal-agent-schema" } +sandbox-agent-extracted-agent-schemas = { path = "server/packages/extracted-agent-schemas" } + +# Serialization +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + +# Error handling +thiserror = "1.0" + +# Schema generation +schemars = "0.8" +utoipa = { version = "4.2", features = ["axum_extras"] } + +# Web framework +axum = "0.7" +tower = { version = "0.5", features = ["util"] } +tower-http = { version = "0.5", features = ["cors", "trace"] } + +# Async runtime +tokio = { version = "1.36", features = ["macros", "rt-multi-thread", "signal", "time"] } +tokio-stream = { version = "0.1", features = ["sync"] } +futures = "0.3" + +# HTTP client +reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls", "stream"] } + +# CLI +clap = { version = "4.5", features = ["derive"] } + +# Logging +tracing = "0.1" +tracing-logfmt = "0.3" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } + +# Time/date +time = { version = "0.3", features = ["parsing", "formatting"] } +chrono = { version = "0.4", features = ["serde"] } + +# Filesystem/paths +dirs = "5.0" +tempfile = "3.10" + +# Archive handling +flate2 = "1.0" +tar = "0.4" +zip = { version = "0.6", default-features = false, features = ["deflate"] } + +# Misc +url = "2.5" +regress = "0.10" + +# Code generation (build deps) +typify = "0.4" +prettyplease = "0.2" +syn = "2.0" + +# Testing +http-body-util = "0.1" +insta = { version = "1.41", features = ["yaml"] } diff --git a/README.md b/README.md index c5b373d..3d2810d 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,29 @@ Universal API for running Claude Code, Codex, OpenCode, and Amp inside sandboxes - **Universal session schema**: Universal schema to store agent transcripts - **Supports your sandbox provider**: Daytona, E2B, Vercel Sandboxes, and more - **Lightweight, portable Rust binary**: Install anywhere with 1 curl command +- **OpenAPI spec**: Versioned API schema tracked in `sdks/openapi/openapi.json` + +## Agent Support + +| Feature | [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview) | [Codex](https://github.com/openai/codex) | [OpenCode](https://github.com/opencode-ai/opencode) | [Amp](https://ampcode.com) | +|---------|:-----------:|:-----:|:--------:|:---:| +| Stability | Stable | Stable | Experimental | Experimental | +| Text Messages | ✓ | ✓ | ✓ | ✓ | +| Tool Calls | ✓ | ✓ | ✓ | ✓ | +| Tool Results | ✓ | ✓ | ✓ | ✓ | +| Questions (HITL) | ✓ | | ✓ | | +| Permissions (HITL) | | | ✓ | | +| Images | | ✓ | ✓ | | +| File Attachments | | ✓ | ✓ | | +| Session Lifecycle | | ✓ | ✓ | | +| Error Events | | ✓ | ✓ | ✓ | +| Reasoning/Thinking | | ✓ | | | +| Command Execution | | ✓ | | | +| File Changes | | ✓ | | | +| MCP Tools | | ✓ | | | +| Streaming Deltas | | ✓ | ✓ | | + +Want support for another agent? [Open an issue](https://github.com/anthropics/sandbox-agent/issues/new) to request it. ## Architecture diff --git a/bugs.md b/bugs.md new file mode 100644 index 0000000..e5e065b --- /dev/null +++ b/bugs.md @@ -0,0 +1,2 @@ +- openai exteacted credentials do not work + diff --git a/frontend/packages/inspector/src/App.tsx b/frontend/packages/inspector/src/App.tsx index 0eaa323..68e9311 100644 --- a/frontend/packages/inspector/src/App.tsx +++ b/frontend/packages/inspector/src/App.tsx @@ -14,106 +14,19 @@ import { Zap } from "lucide-react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; - -const API_PREFIX = "/v1"; - -type AgentInfo = { - id: string; - installed: boolean; - version?: string; - path?: string; -}; - -type SessionInfo = { - sessionId: string; - agent: string; - agentMode: string; - permissionMode: string; - model?: string; - variant?: string; - agentSessionId?: string; - ended: boolean; - eventCount: number; -}; - -type AgentMode = { - id: string; - name: string; - description?: string; -}; - -type UniversalEvent = { - id: number; - timestamp: string; - sessionId: string; - agent: string; - agentSessionId?: string; - data: UniversalEventData; -}; - -type UniversalEventData = - | { message: UniversalMessage } - | { started: StartedInfo } - | { error: CrashInfo } - | { questionAsked: QuestionRequest } - | { permissionAsked: PermissionRequest }; - -type UniversalMessagePart = { - type: string; - text?: string; - name?: string; - input?: unknown; - output?: unknown; -}; - -type UniversalMessage = { - role?: string; - parts?: UniversalMessagePart[]; - raw?: unknown; - error?: string; -}; - -type StartedInfo = { - message?: string; - pid?: number; - [key: string]: unknown; -}; - -type CrashInfo = { - message?: string; - code?: string; - detail?: string; - [key: string]: unknown; -}; - -type QuestionOption = { - label: string; - description?: string; -}; - -type QuestionItem = { - header?: string; - question: string; - options: QuestionOption[]; - multiSelect?: boolean; -}; - -type QuestionRequest = { - id: string; - sessionID?: string; - questions: QuestionItem[]; - tool?: { messageID?: string; callID?: string }; -}; - -type PermissionRequest = { - id: string; - sessionID?: string; - permission: string; - patterns?: string[]; - metadata?: Record; - always?: string[]; - tool?: { messageID?: string; callID?: string }; -}; +import { + SandboxDaemonError, + createSandboxDaemonClient, + type SandboxDaemonClient, + type AgentInfo, + type AgentModeInfo, + type PermissionRequest, + type QuestionRequest, + type SessionInfo, + type UniversalEvent, + type UniversalMessage, + type UniversalMessagePart +} from "sandbox-agent"; type RequestLog = { id: number; @@ -130,29 +43,6 @@ type DebugTab = "log" | "events" | "approvals" | "agents"; const defaultAgents = ["claude", "codex", "opencode", "amp"]; -const buildUrl = (endpoint: string, path: string, query?: Record) => { - const base = endpoint.replace(/\/$/, ""); - const fullPath = path.startsWith("/") ? path : `/${path}`; - const url = new URL(`${base}${fullPath}`); - if (query) { - Object.entries(query).forEach(([key, value]) => { - if (value !== "") { - url.searchParams.set(key, value); - } - }); - } - return url.toString(); -}; - -const safeJson = (text: string) => { - if (!text) return null; - try { - return JSON.parse(text); - } catch { - return text; - } -}; - const formatJson = (value: unknown) => { if (value === null || value === undefined) return ""; if (typeof value === "string") return value; @@ -203,7 +93,7 @@ export default function App() { const [connectError, setConnectError] = useState(null); const [agents, setAgents] = useState([]); - const [modesByAgent, setModesByAgent] = useState>({}); + const [modesByAgent, setModesByAgent] = useState>({}); const [sessions, setSessions] = useState([]); const [agentId, setAgentId] = useState("claude"); @@ -222,7 +112,6 @@ export default function App() { const [polling, setPolling] = useState(false); const pollTimerRef = useRef(null); const [streamMode, setStreamMode] = useState<"poll" | "sse">("poll"); - const eventSourceRef = useRef(null); const [eventError, setEventError] = useState(null); const [questionSelections, setQuestionSelections] = useState>({}); @@ -237,6 +126,9 @@ export default function App() { const messagesEndRef = useRef(null); + const clientRef = useRef(null); + const sseAbortRef = useRef(null); + const logRequest = useCallback((entry: RequestLog) => { setRequestLog((prev) => { const next = [entry, ...prev]; @@ -244,25 +136,16 @@ export default function App() { }); }, []); - const apiFetch = useCallback( - async ( - path: string, - options?: { - method?: string; - body?: unknown; - query?: Record; - } - ) => { - const method = options?.method ?? "GET"; - const url = buildUrl(endpoint, path, options?.query); - const bodyText = options?.body ? JSON.stringify(options.body) : undefined; - const headers: Record = {}; - if (bodyText) { - headers["Content-Type"] = "application/json"; - } - if (token) { - headers.Authorization = `Bearer ${token}`; - } + const createClient = useCallback(() => { + const fetchWithLog: typeof fetch = async (input, init) => { + const method = init?.method ?? "GET"; + const url = + typeof input === "string" + ? input + : input instanceof URL + ? input.toString() + : input.url; + const bodyText = typeof init?.body === "string" ? init.body : undefined; const curl = buildCurl(method, url, bodyText, token); const logId = logIdRef.current++; const entry: RequestLog = { @@ -276,23 +159,10 @@ export default function App() { let logged = false; try { - const response = await fetch(url, { - method, - headers, - body: bodyText - }); - const text = await response.text(); - const data = safeJson(text); + const response = await fetch(input, init); logRequest({ ...entry, status: response.status }); logged = true; - if (!response.ok) { - const errorMessage = - (typeof data === "object" && data && "detail" in data && data.detail) || - (typeof data === "object" && data && "title" in data && data.title) || - (typeof data === "string" ? data : `Request failed with ${response.status}`); - throw new Error(String(errorMessage)); - } - return data; + return response; } catch (error) { const message = error instanceof Error ? error.message : "Request failed"; if (!logged) { @@ -300,22 +170,45 @@ export default function App() { } throw error; } - }, - [endpoint, token, logRequest] - ); + }; + + const client = createSandboxDaemonClient({ + baseUrl: endpoint, + token: token || undefined, + fetch: fetchWithLog + }); + clientRef.current = client; + return client; + }, [endpoint, token, logRequest]); + + const getClient = useCallback((): SandboxDaemonClient => { + if (!clientRef.current) { + throw new Error("Not connected"); + } + return clientRef.current; + }, []); + + const getErrorMessage = (error: unknown, fallback: string) => { + if (error instanceof SandboxDaemonError) { + return error.problem?.detail ?? error.problem?.title ?? error.message; + } + return error instanceof Error ? error.message : fallback; + }; const connect = async () => { setConnecting(true); setConnectError(null); try { - await apiFetch(`${API_PREFIX}/health`); + const client = createClient(); + await client.getHealth(); setConnected(true); await refreshAgents(); await fetchSessions(); } catch (error) { - const message = error instanceof Error ? error.message : "Unable to connect"; + const message = getErrorMessage(error, "Unable to connect"); setConnectError(message); setConnected(false); + clientRef.current = null; } finally { setConnecting(false); } @@ -323,6 +216,7 @@ export default function App() { const disconnect = () => { setConnected(false); + clientRef.current = null; setSessionError(null); setEvents([]); setOffset(0); @@ -334,8 +228,8 @@ export default function App() { const refreshAgents = async () => { try { - const data = await apiFetch(`${API_PREFIX}/agents`); - const agentList = (data as { agents?: AgentInfo[] })?.agents ?? []; + const data = await getClient().listAgents(); + const agentList = data.agents ?? []; setAgents(agentList); // Auto-load modes for installed agents for (const agent of agentList) { @@ -344,14 +238,14 @@ export default function App() { } } } catch (error) { - setConnectError(error instanceof Error ? error.message : "Unable to refresh agents"); + setConnectError(getErrorMessage(error, "Unable to refresh agents")); } }; const fetchSessions = async () => { try { - const data = await apiFetch(`${API_PREFIX}/sessions`); - const sessionList = (data as { sessions?: SessionInfo[] })?.sessions ?? []; + const data = await getClient().listSessions(); + const sessionList = data.sessions ?? []; setSessions(sessionList); } catch { // Silently fail - sessions list is supplementary @@ -360,20 +254,17 @@ export default function App() { const installAgent = async (targetId: string, reinstall: boolean) => { try { - await apiFetch(`${API_PREFIX}/agents/${targetId}/install`, { - method: "POST", - body: { reinstall } - }); + await getClient().installAgent(targetId, { reinstall }); await refreshAgents(); } catch (error) { - setConnectError(error instanceof Error ? error.message : "Install failed"); + setConnectError(getErrorMessage(error, "Install failed")); } }; const loadModes = async (targetId: string) => { try { - const data = await apiFetch(`${API_PREFIX}/agents/${targetId}/modes`); - const modes = (data as { modes?: AgentMode[] })?.modes ?? []; + const data = await getClient().getAgentModes(targetId); + const modes = data.modes ?? []; setModesByAgent((prev) => ({ ...prev, [targetId]: modes })); } catch { // Silently fail - modes are optional @@ -384,37 +275,41 @@ export default function App() { if (!message.trim()) return; setSessionError(null); try { - await apiFetch(`${API_PREFIX}/sessions/${sessionId}/messages`, { - method: "POST", - body: { message } - }); + await getClient().postMessage(sessionId, { message }); setMessage(""); // Auto-start polling if not already - if (!polling && streamMode === "poll") { - startPolling(); + if (!polling) { + if (streamMode === "poll") { + startPolling(); + } else { + startSse(); + } } } catch (error) { - setSessionError(error instanceof Error ? error.message : "Unable to send message"); + setSessionError(getErrorMessage(error, "Unable to send message")); } }; const createSession = async () => { setSessionError(null); try { - const body: Record = { agent: agentId }; + const body: { + agent: string; + agentMode?: string; + permissionMode?: string; + model?: string; + variant?: string; + } = { agent: agentId }; if (agentMode) body.agentMode = agentMode; if (permissionMode) body.permissionMode = permissionMode; if (model) body.model = model; if (variant) body.variant = variant; - await apiFetch(`${API_PREFIX}/sessions/${sessionId}`, { - method: "POST", - body - }); + await getClient().createSession(sessionId, body); await fetchSessions(); } catch (error) { - setSessionError(error instanceof Error ? error.message : "Unable to create session"); + setSessionError(getErrorMessage(error, "Unable to create session")); } }; @@ -446,19 +341,22 @@ export default function App() { // Create the session try { - const body: Record = { agent: agentId }; + const body: { + agent: string; + agentMode?: string; + permissionMode?: string; + model?: string; + variant?: string; + } = { agent: agentId }; if (agentMode) body.agentMode = agentMode; if (permissionMode) body.permissionMode = permissionMode; if (model) body.model = model; if (variant) body.variant = variant; - await apiFetch(`${API_PREFIX}/sessions/${id}`, { - method: "POST", - body - }); + await getClient().createSession(id, body); await fetchSessions(); } catch (error) { - setSessionError(error instanceof Error ? error.message : "Unable to create session"); + setSessionError(getErrorMessage(error, "Unable to create session")); } }; @@ -473,20 +371,17 @@ export default function App() { const fetchEvents = useCallback(async () => { if (!sessionId) return; try { - const data = await apiFetch(`${API_PREFIX}/sessions/${sessionId}/events`, { - query: { - offset: String(offsetRef.current), - limit: "200" - } + const response = await getClient().getEvents(sessionId, { + offset: offsetRef.current, + limit: 200 }); - const response = data as { events?: UniversalEvent[]; hasMore?: boolean }; const newEvents = response.events ?? []; appendEvents(newEvents); setEventError(null); } catch (error) { - setEventError(error instanceof Error ? error.message : "Unable to fetch events"); + setEventError(getErrorMessage(error, "Unable to fetch events")); } - }, [apiFetch, appendEvents, sessionId]); + }, [appendEvents, getClient, sessionId]); const startPolling = () => { stopSse(); @@ -506,39 +401,47 @@ export default function App() { const startSse = () => { stopPolling(); - if (eventSourceRef.current) return; - if (token) { - setEventError("SSE streams cannot send auth headers. Use polling or run daemon with --no-token."); + if (sseAbortRef.current) return; + if (!sessionId) { + setEventError("Select or create a session first."); return; } - const url = buildUrl(endpoint, `${API_PREFIX}/sessions/${sessionId}/events/sse`, { - offset: String(offsetRef.current) - }); - const source = new EventSource(url); - eventSourceRef.current = source; - source.onmessage = (event) => { + setEventError(null); + setPolling(true); + const controller = new AbortController(); + sseAbortRef.current = controller; + const start = async () => { try { - const parsed = safeJson(event.data); - if (Array.isArray(parsed)) { - appendEvents(parsed as UniversalEvent[]); - } else if (parsed && typeof parsed === "object") { - appendEvents([parsed as UniversalEvent]); + for await (const event of getClient().streamEvents( + sessionId, + { offset: offsetRef.current }, + controller.signal + )) { + appendEvents([event]); } } catch (error) { - setEventError(error instanceof Error ? error.message : "SSE parse error"); + if (controller.signal.aborted) { + return; + } + setEventError(getErrorMessage(error, "SSE connection error. Falling back to polling.")); + stopSse(); + startPolling(); + } finally { + if (sseAbortRef.current === controller) { + sseAbortRef.current = null; + setPolling(false); + } } }; - source.onerror = () => { - setEventError("SSE connection error. Falling back to polling."); - stopSse(); - }; + void start(); }; const stopSse = () => { - if (eventSourceRef.current) { - eventSourceRef.current.close(); - eventSourceRef.current = null; + if (sseAbortRef.current) { + sseAbortRef.current.abort(); + sseAbortRef.current = null; } + setPolling(false); }; const resetEvents = () => { @@ -584,37 +487,28 @@ export default function App() { const answerQuestion = async (request: QuestionRequest) => { const answers = questionSelections[request.id] ?? []; try { - await apiFetch(`${API_PREFIX}/sessions/${sessionId}/questions/${request.id}/reply`, { - method: "POST", - body: { answers } - }); + await getClient().replyQuestion(sessionId, request.id, { answers }); setQuestionStatus((prev) => ({ ...prev, [request.id]: "replied" })); } catch (error) { - setEventError(error instanceof Error ? error.message : "Unable to reply"); + setEventError(getErrorMessage(error, "Unable to reply")); } }; const rejectQuestion = async (requestId: string) => { try { - await apiFetch(`${API_PREFIX}/sessions/${sessionId}/questions/${requestId}/reject`, { - method: "POST", - body: {} - }); + await getClient().rejectQuestion(sessionId, requestId); setQuestionStatus((prev) => ({ ...prev, [requestId]: "rejected" })); } catch (error) { - setEventError(error instanceof Error ? error.message : "Unable to reject"); + setEventError(getErrorMessage(error, "Unable to reject")); } }; const replyPermission = async (requestId: string, reply: "once" | "always" | "reject") => { try { - await apiFetch(`${API_PREFIX}/sessions/${sessionId}/permissions/${requestId}/reply`, { - method: "POST", - body: { reply } - }); + await getClient().replyPermission(sessionId, requestId, { reply }); setPermissionStatus((prev) => ({ ...prev, [requestId]: "replied" })); } catch (error) { - setEventError(error instanceof Error ? error.message : "Unable to reply"); + setEventError(getErrorMessage(error, "Unable to reply")); } }; @@ -637,14 +531,14 @@ export default function App() { .filter((event): event is UniversalEvent & { data: { message: UniversalMessage } } => "message" in event.data) .map((event) => { const msg = event.data.message; - // Extract text from parts array - const content = msg?.parts - ?.filter((part) => part.type === "text" && part.text) - .map((part) => part.text) - .join("\n") ?? ""; + const parts = "parts" in msg ? msg.parts : []; + const content = parts + .filter((part: UniversalMessagePart) => part.type === "text" && part.text) + .map((part: UniversalMessagePart) => part.text) + .join("\n"); return { id: event.id, - role: msg?.role ?? "assistant", + role: "role" in msg ? msg.role : "assistant", content, timestamp: event.timestamp }; @@ -697,7 +591,11 @@ export default function App() { const toggleStream = () => { if (polling) { - stopPolling(); + if (streamMode === "poll") { + stopPolling(); + } else { + stopSse(); + } } else if (streamMode === "poll") { startPolling(); } else { diff --git a/resources/agent-schemas/artifacts/json-schema/amp.json b/resources/agent-schemas/artifacts/json-schema/amp.json new file mode 100644 index 0000000..78f0e84 --- /dev/null +++ b/resources/agent-schemas/artifacts/json-schema/amp.json @@ -0,0 +1,153 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://sandbox-agent/schemas/amp.json", + "title": "AMP Code SDK Schema", + "definitions": { + "StreamJSONMessage": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "message", + "tool_call", + "tool_result", + "error", + "done" + ] + }, + "id": { + "type": "string" + }, + "content": { + "type": "string" + }, + "tool_call": { + "$ref": "#/definitions/ToolCall" + }, + "error": { + "type": "string" + } + }, + "required": [ + "type" + ] + }, + "AmpOptions": { + "type": "object", + "properties": { + "model": { + "type": "string" + }, + "apiKey": { + "type": "string" + }, + "baseURL": { + "type": "string" + }, + "maxTokens": { + "type": "number" + }, + "temperature": { + "type": "number" + }, + "systemPrompt": { + "type": "string" + }, + "tools": { + "type": "array", + "items": { + "type": "object" + } + }, + "workingDirectory": { + "type": "string" + }, + "permissionRules": { + "type": "array", + "items": { + "$ref": "#/definitions/PermissionRule" + } + } + } + }, + "PermissionRule": { + "type": "object", + "properties": { + "tool": { + "type": "string" + }, + "action": { + "type": "string", + "enum": [ + "allow", + "deny", + "ask" + ] + }, + "pattern": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "tool", + "action" + ] + }, + "Message": { + "type": "object", + "properties": { + "role": { + "type": "string", + "enum": [ + "user", + "assistant", + "system" + ] + }, + "content": { + "type": "string" + }, + "tool_calls": { + "type": "array", + "items": { + "$ref": "#/definitions/ToolCall" + } + } + }, + "required": [ + "role", + "content" + ] + }, + "ToolCall": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "arguments": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + }, + "required": [ + "id", + "name", + "arguments" + ] + } + } +} \ No newline at end of file diff --git a/resources/agent-schemas/artifacts/json-schema/claude.json b/resources/agent-schemas/artifacts/json-schema/claude.json new file mode 100644 index 0000000..5f79616 --- /dev/null +++ b/resources/agent-schemas/artifacts/json-schema/claude.json @@ -0,0 +1,182 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://sandbox-agent/schemas/claude.json", + "title": "Claude Code SDK Schema", + "definitions": { + "SDKMessage": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "user", + "assistant", + "result" + ] + }, + "content": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "type" + ] + }, + "SDKResultMessage": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "result" + }, + "result": { + "type": "object" + }, + "error": { + "type": "string" + }, + "duration_ms": { + "type": "number" + } + }, + "required": [ + "type" + ] + }, + "Options": { + "type": "object", + "properties": { + "model": { + "type": "string" + }, + "maxTokens": { + "type": "number" + }, + "temperature": { + "type": "number" + }, + "systemPrompt": { + "type": "string" + }, + "tools": { + "type": "array", + "items": { + "type": "string" + } + }, + "allowedTools": { + "type": "array", + "items": { + "type": "string" + } + }, + "workingDirectory": { + "type": "string" + } + } + }, + "BashInput": { + "type": "object", + "properties": { + "command": { + "type": "string" + }, + "timeout": { + "type": "number" + }, + "workingDirectory": { + "type": "string" + } + }, + "required": [ + "command" + ] + }, + "FileEditInput": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "oldText": { + "type": "string" + }, + "newText": { + "type": "string" + } + }, + "required": [ + "path", + "oldText", + "newText" + ] + }, + "FileReadInput": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "startLine": { + "type": "number" + }, + "endLine": { + "type": "number" + } + }, + "required": [ + "path" + ] + }, + "FileWriteInput": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "content": { + "type": "string" + } + }, + "required": [ + "path", + "content" + ] + }, + "GlobInput": { + "type": "object", + "properties": { + "pattern": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "required": [ + "pattern" + ] + }, + "GrepInput": { + "type": "object", + "properties": { + "pattern": { + "type": "string" + }, + "path": { + "type": "string" + }, + "include": { + "type": "string" + } + }, + "required": [ + "pattern" + ] + } + } +} \ No newline at end of file diff --git a/resources/agent-schemas/artifacts/json-schema/codex.json b/resources/agent-schemas/artifacts/json-schema/codex.json new file mode 100644 index 0000000..c1d5cc8 --- /dev/null +++ b/resources/agent-schemas/artifacts/json-schema/codex.json @@ -0,0 +1,18260 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://sandbox-agent/schemas/codex.json", + "title": "Codex SDK Schema", + "definitions": { + "FileChange": { + "oneOf": [ + { + "type": "object", + "required": [ + "content", + "type" + ], + "properties": { + "content": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "add" + ], + "title": "AddFileChangeType" + } + }, + "title": "AddFileChange" + }, + { + "type": "object", + "required": [ + "content", + "type" + ], + "properties": { + "content": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "delete" + ], + "title": "DeleteFileChangeType" + } + }, + "title": "DeleteFileChange" + }, + { + "type": "object", + "required": [ + "type", + "unified_diff" + ], + "properties": { + "move_path": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "update" + ], + "title": "UpdateFileChangeType" + }, + "unified_diff": { + "type": "string" + } + }, + "title": "UpdateFileChange" + } + ] + }, + "ThreadId": { + "type": "string" + }, + "ReviewDecision": { + "description": "User's decision in response to an ExecApprovalRequest.", + "oneOf": [ + { + "description": "User has approved this command and the agent should execute it.", + "type": "string", + "enum": [ + "approved" + ] + }, + { + "description": "User has approved this command and wants to apply the proposed execpolicy amendment so future matching commands are permitted.", + "type": "object", + "required": [ + "approved_execpolicy_amendment" + ], + "properties": { + "approved_execpolicy_amendment": { + "type": "object", + "required": [ + "proposed_execpolicy_amendment" + ], + "properties": { + "proposed_execpolicy_amendment": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "additionalProperties": false, + "title": "ApprovedExecpolicyAmendmentReviewDecision" + }, + { + "description": "User has approved this command and wants to automatically approve any future identical instances (`command` and `cwd` match exactly) for the remainder of the session.", + "type": "string", + "enum": [ + "approved_for_session" + ] + }, + { + "description": "User has denied this command and the agent should not execute it, but it should continue the session and try something else.", + "type": "string", + "enum": [ + "denied" + ] + }, + { + "description": "User has denied this command and the agent should not do anything until the user's next command.", + "type": "string", + "enum": [ + "abort" + ] + } + ] + }, + "ClientNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ClientNotification", + "oneOf": [ + { + "type": "object", + "required": [ + "method" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "initialized" + ], + "title": "InitializedNotificationMethod" + } + }, + "title": "InitializedNotification" + } + ] + }, + "AbsolutePathBuf": { + "description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.", + "type": "string" + }, + "AddConversationListenerParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AddConversationListenerParams", + "type": "object", + "required": [ + "conversationId" + ], + "properties": { + "conversationId": { + "$ref": "#/definitions/ThreadId" + }, + "experimentalRawEvents": { + "default": false, + "type": "boolean" + } + } + }, + "AppsListParams": { + "type": "object", + "properties": { + "cursor": { + "description": "Opaque pagination cursor returned by a previous call.", + "type": [ + "string", + "null" + ] + }, + "limit": { + "description": "Optional page size; defaults to a reasonable server-side value.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0 + } + } + }, + "ArchiveConversationParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ArchiveConversationParams", + "type": "object", + "required": [ + "conversationId", + "rolloutPath" + ], + "properties": { + "conversationId": { + "$ref": "#/definitions/ThreadId" + }, + "rolloutPath": { + "type": "string" + } + } + }, + "AskForApproval": { + "description": "Determines the conditions under which the user is consulted to approve running the command proposed by Codex.", + "oneOf": [ + { + "description": "Under this policy, only \"known safe\" commands—as determined by `is_safe_command()`—that **only read files** are auto‑approved. Everything else will ask the user to approve.", + "type": "string", + "enum": [ + "untrusted" + ] + }, + { + "description": "*All* commands are auto‑approved, but they are expected to run inside a sandbox where network access is disabled and writes are confined to a specific set of paths. If the command fails, it will be escalated to the user to approve execution without a sandbox.", + "type": "string", + "enum": [ + "on-failure" + ] + }, + { + "description": "The model decides when to ask the user for approval.", + "type": "string", + "enum": [ + "on-request" + ] + }, + { + "description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.", + "type": "string", + "enum": [ + "never" + ] + } + ] + }, + "AskForApproval2": { + "description": "Determines the conditions under which the user is consulted to approve running the command proposed by Codex.", + "oneOf": [ + { + "description": "Under this policy, only \"known safe\" commands—as determined by `is_safe_command()`—that **only read files** are auto‑approved. Everything else will ask the user to approve.", + "type": "string", + "enum": [ + "untrusted" + ] + }, + { + "description": "*All* commands are auto‑approved, but they are expected to run inside a sandbox where network access is disabled and writes are confined to a specific set of paths. If the command fails, it will be escalated to the user to approve execution without a sandbox.", + "type": "string", + "enum": [ + "on-failure" + ] + }, + { + "description": "The model decides when to ask the user for approval.", + "type": "string", + "enum": [ + "on-request" + ] + }, + { + "description": "Never ask the user to approve commands. Failures are immediately returned to the model, and never escalated to the user for approval.", + "type": "string", + "enum": [ + "never" + ] + } + ] + }, + "ByteRange": { + "type": "object", + "required": [ + "end", + "start" + ], + "properties": { + "end": { + "description": "End byte offset (exclusive) within the UTF-8 text buffer.", + "type": "integer", + "format": "uint", + "minimum": 0 + }, + "start": { + "description": "Start byte offset (inclusive) within the UTF-8 text buffer.", + "type": "integer", + "format": "uint", + "minimum": 0 + } + } + }, + "CancelLoginAccountParams": { + "type": "object", + "required": [ + "loginId" + ], + "properties": { + "loginId": { + "type": "string" + } + } + }, + "CancelLoginChatGptParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CancelLoginChatGptParams", + "type": "object", + "required": [ + "loginId" + ], + "properties": { + "loginId": { + "type": "string" + } + } + }, + "ClientInfo": { + "type": "object", + "required": [ + "name", + "version" + ], + "properties": { + "name": { + "type": "string" + }, + "title": { + "type": [ + "string", + "null" + ] + }, + "version": { + "type": "string" + } + } + }, + "CollaborationMode": { + "description": "Collaboration mode for a Codex session.", + "type": "object", + "required": [ + "mode", + "settings" + ], + "properties": { + "mode": { + "$ref": "#/definitions/ModeKind" + }, + "settings": { + "$ref": "#/definitions/Settings" + } + } + }, + "CollaborationModeListParams": { + "description": "EXPERIMENTAL - list collaboration mode presets.", + "type": "object" + }, + "CommandExecParams": { + "type": "object", + "required": [ + "command" + ], + "properties": { + "command": { + "type": "array", + "items": { + "type": "string" + } + }, + "cwd": { + "type": [ + "string", + "null" + ] + }, + "sandboxPolicy": { + "anyOf": [ + { + "$ref": "#/definitions/SandboxPolicy" + }, + { + "type": "null" + } + ] + }, + "timeoutMs": { + "type": [ + "integer", + "null" + ], + "format": "int64" + } + } + }, + "ConfigBatchWriteParams": { + "type": "object", + "required": [ + "edits" + ], + "properties": { + "edits": { + "type": "array", + "items": { + "$ref": "#/definitions/ConfigEdit" + } + }, + "expectedVersion": { + "type": [ + "string", + "null" + ] + }, + "filePath": { + "description": "Path to the config file to write; defaults to the user's `config.toml` when omitted.", + "type": [ + "string", + "null" + ] + } + } + }, + "ConfigEdit": { + "type": "object", + "required": [ + "keyPath", + "mergeStrategy", + "value" + ], + "properties": { + "keyPath": { + "type": "string" + }, + "mergeStrategy": { + "$ref": "#/definitions/MergeStrategy" + }, + "value": true + } + }, + "ConfigReadParams": { + "type": "object", + "properties": { + "cwd": { + "description": "Optional working directory to resolve project config layers. If specified, return the effective config as seen from that directory (i.e., including any project layers between `cwd` and the project/repo root).", + "type": [ + "string", + "null" + ] + }, + "includeLayers": { + "default": false, + "type": "boolean" + } + } + }, + "ConfigValueWriteParams": { + "type": "object", + "required": [ + "keyPath", + "mergeStrategy", + "value" + ], + "properties": { + "expectedVersion": { + "type": [ + "string", + "null" + ] + }, + "filePath": { + "description": "Path to the config file to write; defaults to the user's `config.toml` when omitted.", + "type": [ + "string", + "null" + ] + }, + "keyPath": { + "type": "string" + }, + "mergeStrategy": { + "$ref": "#/definitions/MergeStrategy" + }, + "value": true + } + }, + "ContentItem": { + "oneOf": [ + { + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "input_text" + ], + "title": "InputTextContentItemType" + } + }, + "title": "InputTextContentItem" + }, + { + "type": "object", + "required": [ + "image_url", + "type" + ], + "properties": { + "image_url": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "input_image" + ], + "title": "InputImageContentItemType" + } + }, + "title": "InputImageContentItem" + }, + { + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "output_text" + ], + "title": "OutputTextContentItemType" + } + }, + "title": "OutputTextContentItem" + } + ] + }, + "ExecOneOffCommandParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecOneOffCommandParams", + "type": "object", + "required": [ + "command" + ], + "properties": { + "command": { + "type": "array", + "items": { + "type": "string" + } + }, + "cwd": { + "type": [ + "string", + "null" + ] + }, + "sandboxPolicy": { + "anyOf": [ + { + "$ref": "#/definitions/SandboxPolicy" + }, + { + "type": "null" + } + ] + }, + "timeoutMs": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + } + } + }, + "FeedbackUploadParams": { + "type": "object", + "required": [ + "classification", + "includeLogs" + ], + "properties": { + "classification": { + "type": "string" + }, + "includeLogs": { + "type": "boolean" + }, + "reason": { + "type": [ + "string", + "null" + ] + }, + "threadId": { + "type": [ + "string", + "null" + ] + } + } + }, + "ForkConversationParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ForkConversationParams", + "type": "object", + "properties": { + "conversationId": { + "anyOf": [ + { + "$ref": "#/definitions/ThreadId" + }, + { + "type": "null" + } + ] + }, + "overrides": { + "anyOf": [ + { + "$ref": "#/definitions/NewConversationParams" + }, + { + "type": "null" + } + ] + }, + "path": { + "type": [ + "string", + "null" + ] + } + } + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType" + } + }, + "title": "InputTextFunctionCallOutputContentItem" + }, + { + "type": "object", + "required": [ + "image_url", + "type" + ], + "properties": { + "image_url": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType" + } + }, + "title": "InputImageFunctionCallOutputContentItem" + } + ] + }, + "FunctionCallOutputPayload": { + "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`content` preserves the historical plain-string payload so downstream integrations (tests, logging, etc.) can keep treating tool output as `String`. When an MCP server returns richer data we additionally populate `content_items` with the structured form that the Responses/Chat Completions APIs understand.", + "type": "object", + "required": [ + "content" + ], + "properties": { + "content": { + "type": "string" + }, + "content_items": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + } + }, + "success": { + "type": [ + "boolean", + "null" + ] + } + } + }, + "FuzzyFileSearchParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "FuzzyFileSearchParams", + "type": "object", + "required": [ + "query", + "roots" + ], + "properties": { + "cancellationToken": { + "type": [ + "string", + "null" + ] + }, + "query": { + "type": "string" + }, + "roots": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "GetAccountParams": { + "type": "object", + "properties": { + "refreshToken": { + "default": false, + "type": "boolean" + } + } + }, + "GetAuthStatusParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GetAuthStatusParams", + "type": "object", + "properties": { + "includeToken": { + "type": [ + "boolean", + "null" + ] + }, + "refreshToken": { + "type": [ + "boolean", + "null" + ] + } + } + }, + "GetConversationSummaryParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GetConversationSummaryParams", + "anyOf": [ + { + "type": "object", + "required": [ + "rolloutPath" + ], + "properties": { + "rolloutPath": { + "type": "string" + } + }, + "title": "RolloutPathv1::GetConversationSummaryParams" + }, + { + "type": "object", + "required": [ + "conversationId" + ], + "properties": { + "conversationId": { + "$ref": "#/definitions/ThreadId" + } + }, + "title": "ConversationIdv1::GetConversationSummaryParams" + } + ] + }, + "GhostCommit": { + "description": "Details of a ghost commit created from a repository state.", + "type": "object", + "required": [ + "id", + "preexisting_untracked_dirs", + "preexisting_untracked_files" + ], + "properties": { + "id": { + "type": "string" + }, + "parent": { + "type": [ + "string", + "null" + ] + }, + "preexisting_untracked_dirs": { + "type": "array", + "items": { + "type": "string" + } + }, + "preexisting_untracked_files": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "GitDiffToRemoteParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GitDiffToRemoteParams", + "type": "object", + "required": [ + "cwd" + ], + "properties": { + "cwd": { + "type": "string" + } + } + }, + "InitializeParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InitializeParams", + "type": "object", + "required": [ + "clientInfo" + ], + "properties": { + "clientInfo": { + "$ref": "#/definitions/ClientInfo" + } + } + }, + "InputItem": { + "oneOf": [ + { + "type": "object", + "required": [ + "data", + "type" + ], + "properties": { + "data": { + "type": "object", + "required": [ + "text" + ], + "properties": { + "text": { + "type": "string" + }, + "text_elements": { + "description": "UI-defined spans within `text` used to render or persist special elements.", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/V1TextElement" + } + } + } + }, + "type": { + "type": "string", + "enum": [ + "text" + ], + "title": "TextInputItemType" + } + }, + "title": "TextInputItem" + }, + { + "type": "object", + "required": [ + "data", + "type" + ], + "properties": { + "data": { + "type": "object", + "required": [ + "image_url" + ], + "properties": { + "image_url": { + "type": "string" + } + } + }, + "type": { + "type": "string", + "enum": [ + "image" + ], + "title": "ImageInputItemType" + } + }, + "title": "ImageInputItem" + }, + { + "type": "object", + "required": [ + "data", + "type" + ], + "properties": { + "data": { + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string" + } + } + }, + "type": { + "type": "string", + "enum": [ + "localImage" + ], + "title": "LocalImageInputItemType" + } + }, + "title": "LocalImageInputItem" + } + ] + }, + "InterruptConversationParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InterruptConversationParams", + "type": "object", + "required": [ + "conversationId" + ], + "properties": { + "conversationId": { + "$ref": "#/definitions/ThreadId" + } + } + }, + "ListConversationsParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ListConversationsParams", + "type": "object", + "properties": { + "cursor": { + "type": [ + "string", + "null" + ] + }, + "modelProviders": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "pageSize": { + "type": [ + "integer", + "null" + ], + "format": "uint", + "minimum": 0 + } + } + }, + "ListMcpServerStatusParams": { + "type": "object", + "properties": { + "cursor": { + "description": "Opaque pagination cursor returned by a previous call.", + "type": [ + "string", + "null" + ] + }, + "limit": { + "description": "Optional page size; defaults to a server-defined value.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0 + } + } + }, + "LocalShellAction": { + "oneOf": [ + { + "type": "object", + "required": [ + "command", + "type" + ], + "properties": { + "command": { + "type": "array", + "items": { + "type": "string" + } + }, + "env": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "string" + } + }, + "timeout_ms": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "type": { + "type": "string", + "enum": [ + "exec" + ], + "title": "ExecLocalShellActionType" + }, + "user": { + "type": [ + "string", + "null" + ] + }, + "working_directory": { + "type": [ + "string", + "null" + ] + } + }, + "title": "ExecLocalShellAction" + } + ] + }, + "LocalShellStatus": { + "type": "string", + "enum": [ + "completed", + "in_progress", + "incomplete" + ] + }, + "LoginAccountParams": { + "oneOf": [ + { + "type": "object", + "required": [ + "apiKey", + "type" + ], + "properties": { + "apiKey": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "apiKey" + ], + "title": "ApiKeyLoginAccountParamsType" + } + }, + "title": "ApiKeyLoginAccountParams" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "chatgpt" + ], + "title": "ChatgptLoginAccountParamsType" + } + }, + "title": "ChatgptLoginAccountParams" + } + ] + }, + "LoginApiKeyParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "LoginApiKeyParams", + "type": "object", + "required": [ + "apiKey" + ], + "properties": { + "apiKey": { + "type": "string" + } + } + }, + "McpServerOauthLoginParams": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "scopes": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "timeoutSecs": { + "type": [ + "integer", + "null" + ], + "format": "int64" + } + } + }, + "MergeStrategy": { + "type": "string", + "enum": [ + "replace", + "upsert" + ] + }, + "ModeKind": { + "description": "Initial collaboration mode to use when the TUI starts.", + "type": "string", + "enum": [ + "plan", + "code", + "pair_programming", + "execute", + "custom" + ] + }, + "ModelListParams": { + "type": "object", + "properties": { + "cursor": { + "description": "Opaque pagination cursor returned by a previous call.", + "type": [ + "string", + "null" + ] + }, + "limit": { + "description": "Optional page size; defaults to a reasonable server-side value.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0 + } + } + }, + "NetworkAccess": { + "description": "Represents whether outbound network access is available to the agent.", + "type": "string", + "enum": [ + "restricted", + "enabled" + ] + }, + "NetworkAccess2": { + "description": "Represents whether outbound network access is available to the agent.", + "type": "string", + "enum": [ + "restricted", + "enabled" + ] + }, + "NewConversationParams": { + "type": "object", + "properties": { + "approvalPolicy": { + "anyOf": [ + { + "$ref": "#/definitions/AskForApproval" + }, + { + "type": "null" + } + ] + }, + "baseInstructions": { + "type": [ + "string", + "null" + ] + }, + "compactPrompt": { + "type": [ + "string", + "null" + ] + }, + "config": { + "type": [ + "object", + "null" + ], + "additionalProperties": true + }, + "cwd": { + "type": [ + "string", + "null" + ] + }, + "developerInstructions": { + "type": [ + "string", + "null" + ] + }, + "includeApplyPatchTool": { + "type": [ + "boolean", + "null" + ] + }, + "model": { + "type": [ + "string", + "null" + ] + }, + "modelProvider": { + "type": [ + "string", + "null" + ] + }, + "profile": { + "type": [ + "string", + "null" + ] + }, + "sandbox": { + "anyOf": [ + { + "$ref": "#/definitions/SandboxMode" + }, + { + "type": "null" + } + ] + } + } + }, + "Personality": { + "type": "string", + "enum": [ + "friendly", + "pragmatic" + ] + }, + "ReasoningEffort": { + "description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning", + "type": "string", + "enum": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ] + }, + "ReasoningItemContent": { + "oneOf": [ + { + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "reasoning_text" + ], + "title": "ReasoningTextReasoningItemContentType" + } + }, + "title": "ReasoningTextReasoningItemContent" + }, + { + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "text" + ], + "title": "TextReasoningItemContentType" + } + }, + "title": "TextReasoningItemContent" + } + ] + }, + "ReasoningItemReasoningSummary": { + "oneOf": [ + { + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "summary_text" + ], + "title": "SummaryTextReasoningItemReasoningSummaryType" + } + }, + "title": "SummaryTextReasoningItemReasoningSummary" + } + ] + }, + "ReasoningSummary": { + "description": "A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process. See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries", + "oneOf": [ + { + "type": "string", + "enum": [ + "auto", + "concise", + "detailed" + ] + }, + { + "description": "Option to disable reasoning summaries.", + "type": "string", + "enum": [ + "none" + ] + } + ] + }, + "RemoveConversationListenerParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "RemoveConversationListenerParams", + "type": "object", + "required": [ + "subscriptionId" + ], + "properties": { + "subscriptionId": { + "type": "string" + } + } + }, + "RequestId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer", + "format": "int64" + } + ] + }, + "ResponseItem": { + "oneOf": [ + { + "type": "object", + "required": [ + "content", + "role", + "type" + ], + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/definitions/ContentItem" + } + }, + "end_turn": { + "type": [ + "boolean", + "null" + ] + }, + "id": { + "writeOnly": true, + "type": [ + "string", + "null" + ] + }, + "role": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "message" + ], + "title": "MessageResponseItemType" + } + }, + "title": "MessageResponseItem" + }, + { + "type": "object", + "required": [ + "id", + "summary", + "type" + ], + "properties": { + "content": { + "default": null, + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/ReasoningItemContent" + } + }, + "encrypted_content": { + "type": [ + "string", + "null" + ] + }, + "id": { + "writeOnly": true, + "type": "string" + }, + "summary": { + "type": "array", + "items": { + "$ref": "#/definitions/ReasoningItemReasoningSummary" + } + }, + "type": { + "type": "string", + "enum": [ + "reasoning" + ], + "title": "ReasoningResponseItemType" + } + }, + "title": "ReasoningResponseItem" + }, + { + "type": "object", + "required": [ + "action", + "status", + "type" + ], + "properties": { + "action": { + "$ref": "#/definitions/LocalShellAction" + }, + "call_id": { + "description": "Set when using the Responses API.", + "type": [ + "string", + "null" + ] + }, + "id": { + "description": "Set when using the chat completions API.", + "writeOnly": true, + "type": [ + "string", + "null" + ] + }, + "status": { + "$ref": "#/definitions/LocalShellStatus" + }, + "type": { + "type": "string", + "enum": [ + "local_shell_call" + ], + "title": "LocalShellCallResponseItemType" + } + }, + "title": "LocalShellCallResponseItem" + }, + { + "type": "object", + "required": [ + "arguments", + "call_id", + "name", + "type" + ], + "properties": { + "arguments": { + "type": "string" + }, + "call_id": { + "type": "string" + }, + "id": { + "writeOnly": true, + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "function_call" + ], + "title": "FunctionCallResponseItemType" + } + }, + "title": "FunctionCallResponseItem" + }, + { + "type": "object", + "required": [ + "call_id", + "output", + "type" + ], + "properties": { + "call_id": { + "type": "string" + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputPayload" + }, + "type": { + "type": "string", + "enum": [ + "function_call_output" + ], + "title": "FunctionCallOutputResponseItemType" + } + }, + "title": "FunctionCallOutputResponseItem" + }, + { + "type": "object", + "required": [ + "call_id", + "input", + "name", + "type" + ], + "properties": { + "call_id": { + "type": "string" + }, + "id": { + "writeOnly": true, + "type": [ + "string", + "null" + ] + }, + "input": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "custom_tool_call" + ], + "title": "CustomToolCallResponseItemType" + } + }, + "title": "CustomToolCallResponseItem" + }, + { + "type": "object", + "required": [ + "call_id", + "output", + "type" + ], + "properties": { + "call_id": { + "type": "string" + }, + "output": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "custom_tool_call_output" + ], + "title": "CustomToolCallOutputResponseItemType" + } + }, + "title": "CustomToolCallOutputResponseItem" + }, + { + "type": "object", + "required": [ + "action", + "type" + ], + "properties": { + "action": { + "$ref": "#/definitions/WebSearchAction" + }, + "id": { + "writeOnly": true, + "type": [ + "string", + "null" + ] + }, + "status": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "web_search_call" + ], + "title": "WebSearchCallResponseItemType" + } + }, + "title": "WebSearchCallResponseItem" + }, + { + "type": "object", + "required": [ + "ghost_commit", + "type" + ], + "properties": { + "ghost_commit": { + "$ref": "#/definitions/GhostCommit" + }, + "type": { + "type": "string", + "enum": [ + "ghost_snapshot" + ], + "title": "GhostSnapshotResponseItemType" + } + }, + "title": "GhostSnapshotResponseItem" + }, + { + "type": "object", + "required": [ + "encrypted_content", + "type" + ], + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "compaction" + ], + "title": "CompactionResponseItemType" + } + }, + "title": "CompactionResponseItem" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "other" + ], + "title": "OtherResponseItemType" + } + }, + "title": "OtherResponseItem" + } + ] + }, + "ResumeConversationParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ResumeConversationParams", + "type": "object", + "properties": { + "conversationId": { + "anyOf": [ + { + "$ref": "#/definitions/ThreadId" + }, + { + "type": "null" + } + ] + }, + "history": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/ResponseItem" + } + }, + "overrides": { + "anyOf": [ + { + "$ref": "#/definitions/NewConversationParams" + }, + { + "type": "null" + } + ] + }, + "path": { + "type": [ + "string", + "null" + ] + } + } + }, + "ReviewDelivery": { + "type": "string", + "enum": [ + "inline", + "detached" + ] + }, + "ReviewStartParams": { + "type": "object", + "required": [ + "target", + "threadId" + ], + "properties": { + "delivery": { + "description": "Where to run the review: inline (default) on the current thread or detached on a new thread (returned in `reviewThreadId`).", + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/ReviewDelivery" + }, + { + "type": "null" + } + ] + }, + "target": { + "$ref": "#/definitions/ReviewTarget" + }, + "threadId": { + "type": "string" + } + } + }, + "ReviewTarget": { + "oneOf": [ + { + "description": "Review the working tree: staged, unstaged, and untracked files.", + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "uncommittedChanges" + ], + "title": "UncommittedChangesReviewTargetType" + } + }, + "title": "UncommittedChangesReviewTarget" + }, + { + "description": "Review changes between the current branch and the given base branch.", + "type": "object", + "required": [ + "branch", + "type" + ], + "properties": { + "branch": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "baseBranch" + ], + "title": "BaseBranchReviewTargetType" + } + }, + "title": "BaseBranchReviewTarget" + }, + { + "description": "Review the changes introduced by a specific commit.", + "type": "object", + "required": [ + "sha", + "type" + ], + "properties": { + "sha": { + "type": "string" + }, + "title": { + "description": "Optional human-readable label (e.g., commit subject) for UIs.", + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "commit" + ], + "title": "CommitReviewTargetType" + } + }, + "title": "CommitReviewTarget" + }, + { + "description": "Arbitrary instructions provided by the user.", + "type": "object", + "required": [ + "instructions", + "type" + ], + "properties": { + "instructions": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "custom" + ], + "title": "CustomReviewTargetType" + } + }, + "title": "CustomReviewTarget" + } + ] + }, + "SandboxMode": { + "type": "string", + "enum": [ + "read-only", + "workspace-write", + "danger-full-access" + ] + }, + "SandboxMode2": { + "type": "string", + "enum": [ + "read-only", + "workspace-write", + "danger-full-access" + ] + }, + "SandboxPolicy": { + "description": "Determines execution restrictions for model shell commands.", + "oneOf": [ + { + "description": "No restrictions whatsoever. Use with caution.", + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "danger-full-access" + ], + "title": "DangerFullAccessSandboxPolicyType" + } + }, + "title": "DangerFullAccessSandboxPolicy" + }, + { + "description": "Read-only access to the entire file-system.", + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "read-only" + ], + "title": "ReadOnlySandboxPolicyType" + } + }, + "title": "ReadOnlySandboxPolicy" + }, + { + "description": "Indicates the process is already in an external sandbox. Allows full disk access while honoring the provided network setting.", + "type": "object", + "required": [ + "type" + ], + "properties": { + "network_access": { + "description": "Whether the external sandbox permits outbound network traffic.", + "default": "restricted", + "allOf": [ + { + "$ref": "#/definitions/NetworkAccess" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "external-sandbox" + ], + "title": "ExternalSandboxSandboxPolicyType" + } + }, + "title": "ExternalSandboxSandboxPolicy" + }, + { + "description": "Same as `ReadOnly` but additionally grants write access to the current working directory (\"workspace\").", + "type": "object", + "required": [ + "type" + ], + "properties": { + "exclude_slash_tmp": { + "description": "When set to `true`, will NOT include the `/tmp` among the default writable roots on UNIX. Defaults to `false`.", + "default": false, + "type": "boolean" + }, + "exclude_tmpdir_env_var": { + "description": "When set to `true`, will NOT include the per-user `TMPDIR` environment variable among the default writable roots. Defaults to `false`.", + "default": false, + "type": "boolean" + }, + "network_access": { + "description": "When set to `true`, outbound network access is allowed. `false` by default.", + "default": false, + "type": "boolean" + }, + "type": { + "type": "string", + "enum": [ + "workspace-write" + ], + "title": "WorkspaceWriteSandboxPolicyType" + }, + "writable_roots": { + "description": "Additional folders (beyond cwd and possibly TMPDIR) that should be writable from within the sandbox.", + "type": "array", + "items": { + "$ref": "#/definitions/AbsolutePathBuf" + } + } + }, + "title": "WorkspaceWriteSandboxPolicy" + } + ] + }, + "SandboxPolicy2": { + "description": "Determines execution restrictions for model shell commands.", + "oneOf": [ + { + "description": "No restrictions whatsoever. Use with caution.", + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "danger-full-access" + ], + "title": "DangerFullAccessSandboxPolicy2Type" + } + }, + "title": "DangerFullAccessSandboxPolicy2" + }, + { + "description": "Read-only access to the entire file-system.", + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "read-only" + ], + "title": "ReadOnlySandboxPolicy2Type" + } + }, + "title": "ReadOnlySandboxPolicy2" + }, + { + "description": "Indicates the process is already in an external sandbox. Allows full disk access while honoring the provided network setting.", + "type": "object", + "required": [ + "type" + ], + "properties": { + "network_access": { + "description": "Whether the external sandbox permits outbound network traffic.", + "default": "restricted", + "allOf": [ + { + "$ref": "#/definitions/NetworkAccess2" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "external-sandbox" + ], + "title": "ExternalSandboxSandboxPolicy2Type" + } + }, + "title": "ExternalSandboxSandboxPolicy2" + }, + { + "description": "Same as `ReadOnly` but additionally grants write access to the current working directory (\"workspace\").", + "type": "object", + "required": [ + "type" + ], + "properties": { + "exclude_slash_tmp": { + "description": "When set to `true`, will NOT include the `/tmp` among the default writable roots on UNIX. Defaults to `false`.", + "default": false, + "type": "boolean" + }, + "exclude_tmpdir_env_var": { + "description": "When set to `true`, will NOT include the per-user `TMPDIR` environment variable among the default writable roots. Defaults to `false`.", + "default": false, + "type": "boolean" + }, + "network_access": { + "description": "When set to `true`, outbound network access is allowed. `false` by default.", + "default": false, + "type": "boolean" + }, + "type": { + "type": "string", + "enum": [ + "workspace-write" + ], + "title": "WorkspaceWriteSandboxPolicy2Type" + }, + "writable_roots": { + "description": "Additional folders (beyond cwd and possibly TMPDIR) that should be writable from within the sandbox.", + "type": "array", + "items": { + "$ref": "#/definitions/AbsolutePathBuf" + } + } + }, + "title": "WorkspaceWriteSandboxPolicy2" + } + ] + }, + "SendUserMessageParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "SendUserMessageParams", + "type": "object", + "required": [ + "conversationId", + "items" + ], + "properties": { + "conversationId": { + "$ref": "#/definitions/ThreadId" + }, + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/InputItem" + } + } + } + }, + "SendUserTurnParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "SendUserTurnParams", + "type": "object", + "required": [ + "approvalPolicy", + "conversationId", + "cwd", + "items", + "model", + "sandboxPolicy", + "summary" + ], + "properties": { + "approvalPolicy": { + "$ref": "#/definitions/AskForApproval" + }, + "conversationId": { + "$ref": "#/definitions/ThreadId" + }, + "cwd": { + "type": "string" + }, + "effort": { + "anyOf": [ + { + "$ref": "#/definitions/ReasoningEffort" + }, + { + "type": "null" + } + ] + }, + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/InputItem" + } + }, + "model": { + "type": "string" + }, + "outputSchema": { + "description": "Optional JSON Schema used to constrain the final assistant message for this turn." + }, + "sandboxPolicy": { + "$ref": "#/definitions/SandboxPolicy" + }, + "summary": { + "$ref": "#/definitions/ReasoningSummary" + } + } + }, + "SetDefaultModelParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "SetDefaultModelParams", + "type": "object", + "properties": { + "model": { + "type": [ + "string", + "null" + ] + }, + "reasoningEffort": { + "anyOf": [ + { + "$ref": "#/definitions/ReasoningEffort" + }, + { + "type": "null" + } + ] + } + } + }, + "Settings": { + "description": "Settings for a collaboration mode.", + "type": "object", + "required": [ + "model" + ], + "properties": { + "developer_instructions": { + "type": [ + "string", + "null" + ] + }, + "model": { + "type": "string" + }, + "reasoning_effort": { + "anyOf": [ + { + "$ref": "#/definitions/ReasoningEffort" + }, + { + "type": "null" + } + ] + } + } + }, + "SkillsConfigWriteParams": { + "type": "object", + "required": [ + "enabled", + "path" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "path": { + "type": "string" + } + } + }, + "SkillsListParams": { + "type": "object", + "properties": { + "cwds": { + "description": "When empty, defaults to the current session working directory.", + "type": "array", + "items": { + "type": "string" + } + }, + "forceReload": { + "description": "When true, bypass the skills cache and re-scan skills from disk.", + "type": "boolean" + } + } + }, + "TextElement": { + "type": "object", + "required": [ + "byte_range" + ], + "properties": { + "byte_range": { + "description": "Byte range in the parent `text` buffer that this element occupies.", + "allOf": [ + { + "$ref": "#/definitions/ByteRange" + } + ] + }, + "placeholder": { + "description": "Optional human-readable placeholder for the element, displayed in the UI.", + "type": [ + "string", + "null" + ] + } + } + }, + "ThreadArchiveParams": { + "type": "object", + "required": [ + "threadId" + ], + "properties": { + "threadId": { + "type": "string" + } + } + }, + "ThreadForkParams": { + "description": "There are two ways to fork a thread: 1. By thread_id: load the thread from disk by thread_id and fork it into a new thread. 2. By path: load the thread from disk by path and fork it into a new thread.\n\nIf using path, the thread_id param will be ignored.\n\nPrefer using thread_id whenever possible.", + "type": "object", + "required": [ + "threadId" + ], + "properties": { + "approvalPolicy": { + "anyOf": [ + { + "$ref": "#/definitions/AskForApproval" + }, + { + "type": "null" + } + ] + }, + "baseInstructions": { + "type": [ + "string", + "null" + ] + }, + "config": { + "type": [ + "object", + "null" + ], + "additionalProperties": true + }, + "cwd": { + "type": [ + "string", + "null" + ] + }, + "developerInstructions": { + "type": [ + "string", + "null" + ] + }, + "model": { + "description": "Configuration overrides for the forked thread, if any.", + "type": [ + "string", + "null" + ] + }, + "modelProvider": { + "type": [ + "string", + "null" + ] + }, + "path": { + "description": "[UNSTABLE] Specify the rollout path to fork from. If specified, the thread_id param will be ignored.", + "type": [ + "string", + "null" + ] + }, + "sandbox": { + "anyOf": [ + { + "$ref": "#/definitions/SandboxMode" + }, + { + "type": "null" + } + ] + }, + "threadId": { + "type": "string" + } + } + }, + "ThreadListParams": { + "type": "object", + "properties": { + "archived": { + "description": "Optional archived filter; when set to true, only archived threads are returned. If false or null, only non-archived threads are returned.", + "type": [ + "boolean", + "null" + ] + }, + "cursor": { + "description": "Opaque pagination cursor returned by a previous call.", + "type": [ + "string", + "null" + ] + }, + "limit": { + "description": "Optional page size; defaults to a reasonable server-side value.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0 + }, + "modelProviders": { + "description": "Optional provider filter; when set, only sessions recorded under these providers are returned. When present but empty, includes all providers.", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "sortKey": { + "description": "Optional sort key; defaults to created_at.", + "anyOf": [ + { + "$ref": "#/definitions/ThreadSortKey" + }, + { + "type": "null" + } + ] + } + } + }, + "ThreadLoadedListParams": { + "type": "object", + "properties": { + "cursor": { + "description": "Opaque pagination cursor returned by a previous call.", + "type": [ + "string", + "null" + ] + }, + "limit": { + "description": "Optional page size; defaults to no limit.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0 + } + } + }, + "ThreadReadParams": { + "type": "object", + "required": [ + "threadId" + ], + "properties": { + "includeTurns": { + "description": "When true, include turns and their items from rollout history.", + "default": false, + "type": "boolean" + }, + "threadId": { + "type": "string" + } + } + }, + "ThreadResumeParams": { + "description": "There are three ways to resume a thread: 1. By thread_id: load the thread from disk by thread_id and resume it. 2. By history: instantiate the thread from memory and resume it. 3. By path: load the thread from disk by path and resume it.\n\nThe precedence is: history > path > thread_id. If using history or path, the thread_id param will be ignored.\n\nPrefer using thread_id whenever possible.", + "type": "object", + "required": [ + "threadId" + ], + "properties": { + "approvalPolicy": { + "anyOf": [ + { + "$ref": "#/definitions/AskForApproval" + }, + { + "type": "null" + } + ] + }, + "baseInstructions": { + "type": [ + "string", + "null" + ] + }, + "config": { + "type": [ + "object", + "null" + ], + "additionalProperties": true + }, + "cwd": { + "type": [ + "string", + "null" + ] + }, + "developerInstructions": { + "type": [ + "string", + "null" + ] + }, + "history": { + "description": "[UNSTABLE] FOR CODEX CLOUD - DO NOT USE. If specified, the thread will be resumed with the provided history instead of loaded from disk.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/ResponseItem" + } + }, + "model": { + "description": "Configuration overrides for the resumed thread, if any.", + "type": [ + "string", + "null" + ] + }, + "modelProvider": { + "type": [ + "string", + "null" + ] + }, + "path": { + "description": "[UNSTABLE] Specify the rollout path to resume from. If specified, the thread_id param will be ignored.", + "type": [ + "string", + "null" + ] + }, + "personality": { + "anyOf": [ + { + "$ref": "#/definitions/Personality" + }, + { + "type": "null" + } + ] + }, + "sandbox": { + "anyOf": [ + { + "$ref": "#/definitions/SandboxMode" + }, + { + "type": "null" + } + ] + }, + "threadId": { + "type": "string" + } + } + }, + "ThreadRollbackParams": { + "type": "object", + "required": [ + "numTurns", + "threadId" + ], + "properties": { + "numTurns": { + "description": "The number of turns to drop from the end of the thread. Must be >= 1.\n\nThis only modifies the thread's history and does not revert local file changes that have been made by the agent. Clients are responsible for reverting these changes.", + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "threadId": { + "type": "string" + } + } + }, + "ThreadSortKey": { + "type": "string", + "enum": [ + "created_at", + "updated_at" + ] + }, + "ThreadStartParams": { + "type": "object", + "properties": { + "approvalPolicy": { + "anyOf": [ + { + "$ref": "#/definitions/AskForApproval" + }, + { + "type": "null" + } + ] + }, + "baseInstructions": { + "type": [ + "string", + "null" + ] + }, + "config": { + "type": [ + "object", + "null" + ], + "additionalProperties": true + }, + "cwd": { + "type": [ + "string", + "null" + ] + }, + "developerInstructions": { + "type": [ + "string", + "null" + ] + }, + "ephemeral": { + "type": [ + "boolean", + "null" + ] + }, + "experimentalRawEvents": { + "description": "If true, opt into emitting raw response items on the event stream.\n\nThis is for internal use only (e.g. Codex Cloud). (TODO): Figure out a better way to categorize internal / experimental events & protocols.", + "default": false, + "type": "boolean" + }, + "model": { + "type": [ + "string", + "null" + ] + }, + "modelProvider": { + "type": [ + "string", + "null" + ] + }, + "personality": { + "anyOf": [ + { + "$ref": "#/definitions/Personality" + }, + { + "type": "null" + } + ] + }, + "sandbox": { + "anyOf": [ + { + "$ref": "#/definitions/SandboxMode" + }, + { + "type": "null" + } + ] + } + } + }, + "TurnInterruptParams": { + "type": "object", + "required": [ + "threadId", + "turnId" + ], + "properties": { + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "TurnStartParams": { + "type": "object", + "required": [ + "input", + "threadId" + ], + "properties": { + "approvalPolicy": { + "description": "Override the approval policy for this turn and subsequent turns.", + "anyOf": [ + { + "$ref": "#/definitions/AskForApproval" + }, + { + "type": "null" + } + ] + }, + "collaborationMode": { + "description": "EXPERIMENTAL - set a pre-set collaboration mode. Takes precedence over model, reasoning_effort, and developer instructions if set.", + "anyOf": [ + { + "$ref": "#/definitions/CollaborationMode" + }, + { + "type": "null" + } + ] + }, + "cwd": { + "description": "Override the working directory for this turn and subsequent turns.", + "type": [ + "string", + "null" + ] + }, + "effort": { + "description": "Override the reasoning effort for this turn and subsequent turns.", + "anyOf": [ + { + "$ref": "#/definitions/ReasoningEffort" + }, + { + "type": "null" + } + ] + }, + "input": { + "type": "array", + "items": { + "$ref": "#/definitions/UserInput" + } + }, + "model": { + "description": "Override the model for this turn and subsequent turns.", + "type": [ + "string", + "null" + ] + }, + "outputSchema": { + "description": "Optional JSON Schema used to constrain the final assistant message for this turn." + }, + "personality": { + "description": "Override the personality for this turn and subsequent turns.", + "anyOf": [ + { + "$ref": "#/definitions/Personality" + }, + { + "type": "null" + } + ] + }, + "sandboxPolicy": { + "description": "Override the sandbox policy for this turn and subsequent turns.", + "anyOf": [ + { + "$ref": "#/definitions/SandboxPolicy" + }, + { + "type": "null" + } + ] + }, + "summary": { + "description": "Override the reasoning summary for this turn and subsequent turns.", + "anyOf": [ + { + "$ref": "#/definitions/ReasoningSummary" + }, + { + "type": "null" + } + ] + }, + "threadId": { + "type": "string" + } + } + }, + "UserInput": { + "description": "User input", + "oneOf": [ + { + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "text_elements": { + "description": "UI-defined spans within `text` that should be treated as special elements. These are byte ranges into the UTF-8 `text` buffer and are used to render or persist rich input markers (e.g., image placeholders) across history and resume without mutating the literal text.", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/TextElement" + } + }, + "type": { + "type": "string", + "enum": [ + "text" + ], + "title": "TextUserInputType" + } + }, + "title": "TextUserInput" + }, + { + "description": "Pre‑encoded data: URI image.", + "type": "object", + "required": [ + "image_url", + "type" + ], + "properties": { + "image_url": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "image" + ], + "title": "ImageUserInputType" + } + }, + "title": "ImageUserInput" + }, + { + "description": "Local image path provided by the user. This will be converted to an `Image` variant (base64 data URL) during request serialization.", + "type": "object", + "required": [ + "path", + "type" + ], + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "local_image" + ], + "title": "LocalImageUserInputType" + } + }, + "title": "LocalImageUserInput" + }, + { + "description": "Skill selected by the user (name + path to SKILL.md).", + "type": "object", + "required": [ + "name", + "path", + "type" + ], + "properties": { + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "skill" + ], + "title": "SkillUserInputType" + } + }, + "title": "SkillUserInput" + } + ] + }, + "V1ByteRange": { + "type": "object", + "required": [ + "end", + "start" + ], + "properties": { + "end": { + "description": "End byte offset (exclusive) within the UTF-8 text buffer.", + "type": "integer", + "format": "uint", + "minimum": 0 + }, + "start": { + "description": "Start byte offset (inclusive) within the UTF-8 text buffer.", + "type": "integer", + "format": "uint", + "minimum": 0 + } + } + }, + "V1TextElement": { + "type": "object", + "required": [ + "byteRange" + ], + "properties": { + "byteRange": { + "description": "Byte range in the parent `text` buffer that this element occupies.", + "allOf": [ + { + "$ref": "#/definitions/V1ByteRange" + } + ] + }, + "placeholder": { + "description": "Optional human-readable placeholder for the element, displayed in the UI.", + "type": [ + "string", + "null" + ] + } + } + }, + "WebSearchAction": { + "oneOf": [ + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "query": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "search" + ], + "title": "SearchWebSearchActionType" + } + }, + "title": "SearchWebSearchAction" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "open_page" + ], + "title": "OpenPageWebSearchActionType" + }, + "url": { + "type": [ + "string", + "null" + ] + } + }, + "title": "OpenPageWebSearchAction" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "pattern": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "find_in_page" + ], + "title": "FindInPageWebSearchActionType" + }, + "url": { + "type": [ + "string", + "null" + ] + } + }, + "title": "FindInPageWebSearchAction" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "other" + ], + "title": "OtherWebSearchActionType" + } + }, + "title": "OtherWebSearchAction" + } + ] + }, + "CommandAction": { + "oneOf": [ + { + "type": "object", + "required": [ + "command", + "name", + "path", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "read" + ], + "title": "ReadCommandActionType" + } + }, + "title": "ReadCommandAction" + }, + { + "type": "object", + "required": [ + "command", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "path": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "listFiles" + ], + "title": "ListFilesCommandActionType" + } + }, + "title": "ListFilesCommandAction" + }, + { + "type": "object", + "required": [ + "command", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "path": { + "type": [ + "string", + "null" + ] + }, + "query": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "search" + ], + "title": "SearchCommandActionType" + } + }, + "title": "SearchCommandAction" + }, + { + "type": "object", + "required": [ + "command", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "unknown" + ], + "title": "UnknownCommandActionType" + } + }, + "title": "UnknownCommandAction" + } + ] + }, + "CommandExecutionApprovalDecision": { + "oneOf": [ + { + "description": "User approved the command.", + "type": "string", + "enum": [ + "accept" + ] + }, + { + "description": "User approved the command and future identical commands should run without prompting.", + "type": "string", + "enum": [ + "acceptForSession" + ] + }, + { + "description": "User approved the command, and wants to apply the proposed execpolicy amendment so future matching commands can run without prompting.", + "type": "object", + "required": [ + "acceptWithExecpolicyAmendment" + ], + "properties": { + "acceptWithExecpolicyAmendment": { + "type": "object", + "required": [ + "execpolicy_amendment" + ], + "properties": { + "execpolicy_amendment": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "additionalProperties": false, + "title": "AcceptWithExecpolicyAmendmentCommandExecutionApprovalDecision" + }, + { + "description": "User denied the command. The agent will continue the turn.", + "type": "string", + "enum": [ + "decline" + ] + }, + { + "description": "User denied the command. The turn will also be immediately interrupted.", + "type": "string", + "enum": [ + "cancel" + ] + } + ] + }, + "AgentMessageContent": { + "oneOf": [ + { + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "Text" + ], + "title": "TextAgentMessageContentType" + } + }, + "title": "TextAgentMessageContent" + } + ] + }, + "AgentStatus": { + "description": "Agent lifecycle status, derived from emitted events.", + "oneOf": [ + { + "description": "Agent is waiting for initialization.", + "type": "string", + "enum": [ + "pending_init" + ] + }, + { + "description": "Agent is currently running.", + "type": "string", + "enum": [ + "running" + ] + }, + { + "description": "Agent is done. Contains the final assistant message.", + "type": "object", + "required": [ + "completed" + ], + "properties": { + "completed": { + "type": [ + "string", + "null" + ] + } + }, + "additionalProperties": false, + "title": "CompletedAgentStatus" + }, + { + "description": "Agent encountered an error.", + "type": "object", + "required": [ + "errored" + ], + "properties": { + "errored": { + "type": "string" + } + }, + "additionalProperties": false, + "title": "ErroredAgentStatus" + }, + { + "description": "Agent has been shutdown.", + "type": "string", + "enum": [ + "shutdown" + ] + }, + { + "description": "Agent is not found.", + "type": "string", + "enum": [ + "not_found" + ] + } + ] + }, + "Annotations": { + "description": "Optional annotations for the client. The client can use annotations to inform how objects are used or displayed", + "type": "object", + "properties": { + "audience": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Role" + } + }, + "lastModified": { + "type": [ + "string", + "null" + ] + }, + "priority": { + "type": [ + "number", + "null" + ], + "format": "double" + } + } + }, + "AudioContent": { + "description": "Audio provided to or from an LLM.", + "type": "object", + "required": [ + "data", + "mimeType", + "type" + ], + "properties": { + "annotations": { + "anyOf": [ + { + "$ref": "#/definitions/Annotations" + }, + { + "type": "null" + } + ] + }, + "data": { + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "BlobResourceContents": { + "type": "object", + "required": [ + "blob", + "uri" + ], + "properties": { + "blob": { + "type": "string" + }, + "mimeType": { + "type": [ + "string", + "null" + ] + }, + "uri": { + "type": "string" + } + } + }, + "CallToolResult": { + "description": "The server's response to a tool call.", + "type": "object", + "required": [ + "content" + ], + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/definitions/ContentBlock" + } + }, + "isError": { + "type": [ + "boolean", + "null" + ] + }, + "structuredContent": true + } + }, + "CodexErrorInfo": { + "description": "Codex errors that we expose to clients.", + "oneOf": [ + { + "type": "string", + "enum": [ + "context_window_exceeded", + "usage_limit_exceeded", + "internal_server_error", + "unauthorized", + "bad_request", + "sandbox_error", + "thread_rollback_failed", + "other" + ] + }, + { + "type": "object", + "required": [ + "http_connection_failed" + ], + "properties": { + "http_connection_failed": { + "type": "object", + "properties": { + "http_status_code": { + "type": [ + "integer", + "null" + ], + "format": "uint16", + "minimum": 0 + } + } + } + }, + "additionalProperties": false, + "title": "HttpConnectionFailedCodexErrorInfo" + }, + { + "description": "Failed to connect to the response SSE stream.", + "type": "object", + "required": [ + "response_stream_connection_failed" + ], + "properties": { + "response_stream_connection_failed": { + "type": "object", + "properties": { + "http_status_code": { + "type": [ + "integer", + "null" + ], + "format": "uint16", + "minimum": 0 + } + } + } + }, + "additionalProperties": false, + "title": "ResponseStreamConnectionFailedCodexErrorInfo" + }, + { + "description": "The response SSE stream disconnected in the middle of a turnbefore completion.", + "type": "object", + "required": [ + "response_stream_disconnected" + ], + "properties": { + "response_stream_disconnected": { + "type": "object", + "properties": { + "http_status_code": { + "type": [ + "integer", + "null" + ], + "format": "uint16", + "minimum": 0 + } + } + } + }, + "additionalProperties": false, + "title": "ResponseStreamDisconnectedCodexErrorInfo" + }, + { + "description": "Reached the retry limit for responses.", + "type": "object", + "required": [ + "response_too_many_failed_attempts" + ], + "properties": { + "response_too_many_failed_attempts": { + "type": "object", + "properties": { + "http_status_code": { + "type": [ + "integer", + "null" + ], + "format": "uint16", + "minimum": 0 + } + } + } + }, + "additionalProperties": false, + "title": "ResponseTooManyFailedAttemptsCodexErrorInfo" + } + ] + }, + "ContentBlock": { + "anyOf": [ + { + "$ref": "#/definitions/TextContent" + }, + { + "$ref": "#/definitions/ImageContent" + }, + { + "$ref": "#/definitions/AudioContent" + }, + { + "$ref": "#/definitions/ResourceLink" + }, + { + "$ref": "#/definitions/EmbeddedResource" + } + ] + }, + "CreditsSnapshot": { + "type": "object", + "required": [ + "has_credits", + "unlimited" + ], + "properties": { + "balance": { + "type": [ + "string", + "null" + ] + }, + "has_credits": { + "type": "boolean" + }, + "unlimited": { + "type": "boolean" + } + } + }, + "CustomPrompt": { + "type": "object", + "required": [ + "content", + "name", + "path" + ], + "properties": { + "argument_hint": { + "type": [ + "string", + "null" + ] + }, + "content": { + "type": "string" + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "Duration": { + "type": "object", + "required": [ + "nanos", + "secs" + ], + "properties": { + "nanos": { + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "secs": { + "type": "integer", + "format": "uint64", + "minimum": 0 + } + } + }, + "EmbeddedResource": { + "description": "The contents of a resource, embedded into a prompt or tool call result.\n\nIt is up to the client how best to render embedded resources for the benefit of the LLM and/or the user.", + "type": "object", + "required": [ + "resource", + "type" + ], + "properties": { + "annotations": { + "anyOf": [ + { + "$ref": "#/definitions/Annotations" + }, + { + "type": "null" + } + ] + }, + "resource": { + "$ref": "#/definitions/EmbeddedResourceResource" + }, + "type": { + "type": "string" + } + } + }, + "EmbeddedResourceResource": { + "anyOf": [ + { + "$ref": "#/definitions/TextResourceContents" + }, + { + "$ref": "#/definitions/BlobResourceContents" + } + ] + }, + "EventMsg": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "EventMsg", + "description": "Response event from the agent NOTE: Make sure none of these values have optional types, as it will mess up the extension code-gen.", + "oneOf": [ + { + "description": "Error while executing a submission", + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "codex_error_info": { + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/v2/CodexErrorInfo" + }, + { + "type": "null" + } + ] + }, + "message": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "error" + ], + "title": "ErrorEventMsgType" + } + }, + "title": "ErrorEventMsg" + }, + { + "description": "Warning issued while processing a submission. Unlike `Error`, this indicates the turn continued but the user should still be notified.", + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "message": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "warning" + ], + "title": "WarningEventMsgType" + } + }, + "title": "WarningEventMsg" + }, + { + "description": "Conversation history was compacted (either automatically or manually).", + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "context_compacted" + ], + "title": "ContextCompactedEventMsgType" + } + }, + "title": "ContextCompactedEventMsg" + }, + { + "description": "Conversation history was rolled back by dropping the last N user turns.", + "type": "object", + "required": [ + "num_turns", + "type" + ], + "properties": { + "num_turns": { + "description": "Number of user turns that were removed from context.", + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "type": { + "type": "string", + "enum": [ + "thread_rolled_back" + ], + "title": "ThreadRolledBackEventMsgType" + } + }, + "title": "ThreadRolledBackEventMsg" + }, + { + "description": "Agent has started a turn. v1 wire format uses `task_started`; accept `turn_started` for v2 interop.", + "type": "object", + "required": [ + "type" + ], + "properties": { + "model_context_window": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "type": { + "type": "string", + "enum": [ + "task_started" + ], + "title": "TaskStartedEventMsgType" + } + }, + "title": "TaskStartedEventMsg" + }, + { + "description": "Agent has completed all actions. v1 wire format uses `task_complete`; accept `turn_complete` for v2 interop.", + "type": "object", + "required": [ + "type" + ], + "properties": { + "last_agent_message": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "task_complete" + ], + "title": "TaskCompleteEventMsgType" + } + }, + "title": "TaskCompleteEventMsg" + }, + { + "description": "Usage update for the current session, including totals and last turn. Optional means unknown — UIs should not display when `None`.", + "type": "object", + "required": [ + "type" + ], + "properties": { + "info": { + "anyOf": [ + { + "$ref": "#/definitions/TokenUsageInfo" + }, + { + "type": "null" + } + ] + }, + "rate_limits": { + "anyOf": [ + { + "$ref": "#/definitions/v2/RateLimitSnapshot" + }, + { + "type": "null" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "token_count" + ], + "title": "TokenCountEventMsgType" + } + }, + "title": "TokenCountEventMsg" + }, + { + "description": "Agent text output message", + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "message": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "agent_message" + ], + "title": "AgentMessageEventMsgType" + } + }, + "title": "AgentMessageEventMsg" + }, + { + "description": "User/system input message (what was sent to the model)", + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "images": { + "description": "Image URLs sourced from `UserInput::Image`. These are safe to replay in legacy UI history events and correspond to images sent to the model.", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "local_images": { + "description": "Local file paths sourced from `UserInput::LocalImage`. These are kept so the UI can reattach images when editing history, and should not be sent to the model or treated as API-ready URLs.", + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "message": { + "type": "string" + }, + "text_elements": { + "description": "UI-defined spans within `message` used to render or persist special elements.", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/v2/TextElement" + } + }, + "type": { + "type": "string", + "enum": [ + "user_message" + ], + "title": "UserMessageEventMsgType" + } + }, + "title": "UserMessageEventMsg" + }, + { + "description": "Agent text output delta message", + "type": "object", + "required": [ + "delta", + "type" + ], + "properties": { + "delta": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "agent_message_delta" + ], + "title": "AgentMessageDeltaEventMsgType" + } + }, + "title": "AgentMessageDeltaEventMsg" + }, + { + "description": "Reasoning event from agent.", + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "agent_reasoning" + ], + "title": "AgentReasoningEventMsgType" + } + }, + "title": "AgentReasoningEventMsg" + }, + { + "description": "Agent reasoning delta event from agent.", + "type": "object", + "required": [ + "delta", + "type" + ], + "properties": { + "delta": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "agent_reasoning_delta" + ], + "title": "AgentReasoningDeltaEventMsgType" + } + }, + "title": "AgentReasoningDeltaEventMsg" + }, + { + "description": "Raw chain-of-thought from agent.", + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "agent_reasoning_raw_content" + ], + "title": "AgentReasoningRawContentEventMsgType" + } + }, + "title": "AgentReasoningRawContentEventMsg" + }, + { + "description": "Agent reasoning content delta event from agent.", + "type": "object", + "required": [ + "delta", + "type" + ], + "properties": { + "delta": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "agent_reasoning_raw_content_delta" + ], + "title": "AgentReasoningRawContentDeltaEventMsgType" + } + }, + "title": "AgentReasoningRawContentDeltaEventMsg" + }, + { + "description": "Signaled when the model begins a new reasoning summary section (e.g., a new titled block).", + "type": "object", + "required": [ + "type" + ], + "properties": { + "item_id": { + "default": "", + "type": "string" + }, + "summary_index": { + "default": 0, + "type": "integer", + "format": "int64" + }, + "type": { + "type": "string", + "enum": [ + "agent_reasoning_section_break" + ], + "title": "AgentReasoningSectionBreakEventMsgType" + } + }, + "title": "AgentReasoningSectionBreakEventMsg" + }, + { + "description": "Ack the client's configure message.", + "type": "object", + "required": [ + "approval_policy", + "cwd", + "history_entry_count", + "history_log_id", + "model", + "model_provider_id", + "sandbox_policy", + "session_id", + "type" + ], + "properties": { + "approval_policy": { + "description": "When to escalate for approval for execution", + "allOf": [ + { + "$ref": "#/definitions/v2/AskForApproval" + } + ] + }, + "cwd": { + "description": "Working directory that should be treated as the *root* of the session.", + "type": "string" + }, + "forked_from_id": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ThreadId" + }, + { + "type": "null" + } + ] + }, + "history_entry_count": { + "description": "Current number of entries in the history log.", + "type": "integer", + "format": "uint", + "minimum": 0 + }, + "history_log_id": { + "description": "Identifier of the history log file (inode on Unix, 0 otherwise).", + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "initial_messages": { + "description": "Optional initial messages (as events) for resumed sessions. When present, UIs can use these to seed the history.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/EventMsg" + } + }, + "model": { + "description": "Tell the client what model is being queried.", + "type": "string" + }, + "model_provider_id": { + "type": "string" + }, + "reasoning_effort": { + "description": "The effort the model is putting into reasoning about the user's request.", + "anyOf": [ + { + "$ref": "#/definitions/v2/ReasoningEffort" + }, + { + "type": "null" + } + ] + }, + "rollout_path": { + "description": "Path in which the rollout is stored. Can be `None` for ephemeral threads", + "type": [ + "string", + "null" + ] + }, + "sandbox_policy": { + "description": "How to sandbox commands executed in the system", + "allOf": [ + { + "$ref": "#/definitions/v2/SandboxPolicy" + } + ] + }, + "session_id": { + "description": "Name left as session_id instead of thread_id for backwards compatibility.", + "allOf": [ + { + "$ref": "#/definitions/v2/ThreadId" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "session_configured" + ], + "title": "SessionConfiguredEventMsgType" + } + }, + "title": "SessionConfiguredEventMsg" + }, + { + "description": "Incremental MCP startup progress updates.", + "type": "object", + "required": [ + "server", + "status", + "type" + ], + "properties": { + "server": { + "description": "Server name being started.", + "type": "string" + }, + "status": { + "description": "Current startup status.", + "allOf": [ + { + "$ref": "#/definitions/McpStartupStatus" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "mcp_startup_update" + ], + "title": "McpStartupUpdateEventMsgType" + } + }, + "title": "McpStartupUpdateEventMsg" + }, + { + "description": "Aggregate MCP startup completion summary.", + "type": "object", + "required": [ + "cancelled", + "failed", + "ready", + "type" + ], + "properties": { + "cancelled": { + "type": "array", + "items": { + "type": "string" + } + }, + "failed": { + "type": "array", + "items": { + "$ref": "#/definitions/McpStartupFailure" + } + }, + "ready": { + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "type": "string", + "enum": [ + "mcp_startup_complete" + ], + "title": "McpStartupCompleteEventMsgType" + } + }, + "title": "McpStartupCompleteEventMsg" + }, + { + "type": "object", + "required": [ + "call_id", + "invocation", + "type" + ], + "properties": { + "call_id": { + "description": "Identifier so this can be paired with the McpToolCallEnd event.", + "type": "string" + }, + "invocation": { + "$ref": "#/definitions/McpInvocation" + }, + "type": { + "type": "string", + "enum": [ + "mcp_tool_call_begin" + ], + "title": "McpToolCallBeginEventMsgType" + } + }, + "title": "McpToolCallBeginEventMsg" + }, + { + "type": "object", + "required": [ + "call_id", + "duration", + "invocation", + "result", + "type" + ], + "properties": { + "call_id": { + "description": "Identifier for the corresponding McpToolCallBegin that finished.", + "type": "string" + }, + "duration": { + "$ref": "#/definitions/Duration" + }, + "invocation": { + "$ref": "#/definitions/McpInvocation" + }, + "result": { + "description": "Result of the tool call. Note this could be an error.", + "allOf": [ + { + "$ref": "#/definitions/Result_of_CallToolResult_or_String" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "mcp_tool_call_end" + ], + "title": "McpToolCallEndEventMsgType" + } + }, + "title": "McpToolCallEndEventMsg" + }, + { + "type": "object", + "required": [ + "call_id", + "type" + ], + "properties": { + "call_id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "web_search_begin" + ], + "title": "WebSearchBeginEventMsgType" + } + }, + "title": "WebSearchBeginEventMsg" + }, + { + "type": "object", + "required": [ + "call_id", + "query", + "type" + ], + "properties": { + "call_id": { + "type": "string" + }, + "query": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "web_search_end" + ], + "title": "WebSearchEndEventMsgType" + } + }, + "title": "WebSearchEndEventMsg" + }, + { + "description": "Notification that the server is about to execute a command.", + "type": "object", + "required": [ + "call_id", + "command", + "cwd", + "parsed_cmd", + "turn_id", + "type" + ], + "properties": { + "call_id": { + "description": "Identifier so this can be paired with the ExecCommandEnd event.", + "type": "string" + }, + "command": { + "description": "The command to be executed.", + "type": "array", + "items": { + "type": "string" + } + }, + "cwd": { + "description": "The command's working directory if not the default cwd for the agent.", + "type": "string" + }, + "interaction_input": { + "description": "Raw input sent to a unified exec session (if this is an interaction event).", + "type": [ + "string", + "null" + ] + }, + "parsed_cmd": { + "type": "array", + "items": { + "$ref": "#/definitions/ParsedCommand" + } + }, + "process_id": { + "description": "Identifier for the underlying PTY process (when available).", + "type": [ + "string", + "null" + ] + }, + "source": { + "description": "Where the command originated. Defaults to Agent for backward compatibility.", + "default": "agent", + "allOf": [ + { + "$ref": "#/definitions/ExecCommandSource" + } + ] + }, + "turn_id": { + "description": "Turn ID that this command belongs to.", + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "exec_command_begin" + ], + "title": "ExecCommandBeginEventMsgType" + } + }, + "title": "ExecCommandBeginEventMsg" + }, + { + "description": "Incremental chunk of output from a running command.", + "type": "object", + "required": [ + "call_id", + "chunk", + "stream", + "type" + ], + "properties": { + "call_id": { + "description": "Identifier for the ExecCommandBegin that produced this chunk.", + "type": "string" + }, + "chunk": { + "description": "Raw bytes from the stream (may not be valid UTF-8).", + "type": "string" + }, + "stream": { + "description": "Which stream produced this chunk.", + "allOf": [ + { + "$ref": "#/definitions/ExecOutputStream" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "exec_command_output_delta" + ], + "title": "ExecCommandOutputDeltaEventMsgType" + } + }, + "title": "ExecCommandOutputDeltaEventMsg" + }, + { + "description": "Terminal interaction for an in-progress command (stdin sent and stdout observed).", + "type": "object", + "required": [ + "call_id", + "process_id", + "stdin", + "type" + ], + "properties": { + "call_id": { + "description": "Identifier for the ExecCommandBegin that produced this chunk.", + "type": "string" + }, + "process_id": { + "description": "Process id associated with the running command.", + "type": "string" + }, + "stdin": { + "description": "Stdin sent to the running session.", + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "terminal_interaction" + ], + "title": "TerminalInteractionEventMsgType" + } + }, + "title": "TerminalInteractionEventMsg" + }, + { + "type": "object", + "required": [ + "call_id", + "command", + "cwd", + "duration", + "exit_code", + "formatted_output", + "parsed_cmd", + "stderr", + "stdout", + "turn_id", + "type" + ], + "properties": { + "aggregated_output": { + "description": "Captured aggregated output", + "default": "", + "type": "string" + }, + "call_id": { + "description": "Identifier for the ExecCommandBegin that finished.", + "type": "string" + }, + "command": { + "description": "The command that was executed.", + "type": "array", + "items": { + "type": "string" + } + }, + "cwd": { + "description": "The command's working directory if not the default cwd for the agent.", + "type": "string" + }, + "duration": { + "description": "The duration of the command execution.", + "allOf": [ + { + "$ref": "#/definitions/Duration" + } + ] + }, + "exit_code": { + "description": "The command's exit code.", + "type": "integer", + "format": "int32" + }, + "formatted_output": { + "description": "Formatted output from the command, as seen by the model.", + "type": "string" + }, + "interaction_input": { + "description": "Raw input sent to a unified exec session (if this is an interaction event).", + "type": [ + "string", + "null" + ] + }, + "parsed_cmd": { + "type": "array", + "items": { + "$ref": "#/definitions/ParsedCommand" + } + }, + "process_id": { + "description": "Identifier for the underlying PTY process (when available).", + "type": [ + "string", + "null" + ] + }, + "source": { + "description": "Where the command originated. Defaults to Agent for backward compatibility.", + "default": "agent", + "allOf": [ + { + "$ref": "#/definitions/ExecCommandSource" + } + ] + }, + "stderr": { + "description": "Captured stderr", + "type": "string" + }, + "stdout": { + "description": "Captured stdout", + "type": "string" + }, + "turn_id": { + "description": "Turn ID that this command belongs to.", + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "exec_command_end" + ], + "title": "ExecCommandEndEventMsgType" + } + }, + "title": "ExecCommandEndEventMsg" + }, + { + "description": "Notification that the agent attached a local image via the view_image tool.", + "type": "object", + "required": [ + "call_id", + "path", + "type" + ], + "properties": { + "call_id": { + "description": "Identifier for the originating tool call.", + "type": "string" + }, + "path": { + "description": "Local filesystem path provided to the tool.", + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "view_image_tool_call" + ], + "title": "ViewImageToolCallEventMsgType" + } + }, + "title": "ViewImageToolCallEventMsg" + }, + { + "type": "object", + "required": [ + "call_id", + "command", + "cwd", + "parsed_cmd", + "type" + ], + "properties": { + "call_id": { + "description": "Identifier for the associated exec call, if available.", + "type": "string" + }, + "command": { + "description": "The command to be executed.", + "type": "array", + "items": { + "type": "string" + } + }, + "cwd": { + "description": "The command's working directory.", + "type": "string" + }, + "parsed_cmd": { + "type": "array", + "items": { + "$ref": "#/definitions/ParsedCommand" + } + }, + "proposed_execpolicy_amendment": { + "description": "Proposed execpolicy amendment that can be applied to allow future runs.", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "reason": { + "description": "Optional human-readable reason for the approval (e.g. retry without sandbox).", + "type": [ + "string", + "null" + ] + }, + "turn_id": { + "description": "Turn ID that this command belongs to. Uses `#[serde(default)]` for backwards compatibility.", + "default": "", + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "exec_approval_request" + ], + "title": "ExecApprovalRequestEventMsgType" + } + }, + "title": "ExecApprovalRequestEventMsg" + }, + { + "type": "object", + "required": [ + "call_id", + "questions", + "type" + ], + "properties": { + "call_id": { + "description": "Responses API call id for the associated tool call, if available.", + "type": "string" + }, + "questions": { + "type": "array", + "items": { + "$ref": "#/definitions/RequestUserInputQuestion" + } + }, + "turn_id": { + "description": "Turn ID that this request belongs to. Uses `#[serde(default)]` for backwards compatibility.", + "default": "", + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "request_user_input" + ], + "title": "RequestUserInputEventMsgType" + } + }, + "title": "RequestUserInputEventMsg" + }, + { + "type": "object", + "required": [ + "id", + "message", + "server_name", + "type" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "message": { + "type": "string" + }, + "server_name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "elicitation_request" + ], + "title": "ElicitationRequestEventMsgType" + } + }, + "title": "ElicitationRequestEventMsg" + }, + { + "type": "object", + "required": [ + "call_id", + "changes", + "type" + ], + "properties": { + "call_id": { + "description": "Responses API call id for the associated patch apply call, if available.", + "type": "string" + }, + "changes": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/FileChange" + } + }, + "grant_root": { + "description": "When set, the agent is asking the user to allow writes under this root for the remainder of the session.", + "type": [ + "string", + "null" + ] + }, + "reason": { + "description": "Optional explanatory reason (e.g. request for extra write access).", + "type": [ + "string", + "null" + ] + }, + "turn_id": { + "description": "Turn ID that this patch belongs to. Uses `#[serde(default)]` for backwards compatibility with older senders.", + "default": "", + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "apply_patch_approval_request" + ], + "title": "ApplyPatchApprovalRequestEventMsgType" + } + }, + "title": "ApplyPatchApprovalRequestEventMsg" + }, + { + "description": "Notification advising the user that something they are using has been deprecated and should be phased out.", + "type": "object", + "required": [ + "summary", + "type" + ], + "properties": { + "details": { + "description": "Optional extra guidance, such as migration steps or rationale.", + "type": [ + "string", + "null" + ] + }, + "summary": { + "description": "Concise summary of what is deprecated.", + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "deprecation_notice" + ], + "title": "DeprecationNoticeEventMsgType" + } + }, + "title": "DeprecationNoticeEventMsg" + }, + { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "message": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "background_event" + ], + "title": "BackgroundEventEventMsgType" + } + }, + "title": "BackgroundEventEventMsg" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "message": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "undo_started" + ], + "title": "UndoStartedEventMsgType" + } + }, + "title": "UndoStartedEventMsg" + }, + { + "type": "object", + "required": [ + "success", + "type" + ], + "properties": { + "message": { + "type": [ + "string", + "null" + ] + }, + "success": { + "type": "boolean" + }, + "type": { + "type": "string", + "enum": [ + "undo_completed" + ], + "title": "UndoCompletedEventMsgType" + } + }, + "title": "UndoCompletedEventMsg" + }, + { + "description": "Notification that a model stream experienced an error or disconnect and the system is handling it (e.g., retrying with backoff).", + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "additional_details": { + "description": "Optional details about the underlying stream failure (often the same human-readable message that is surfaced as the terminal error if retries are exhausted).", + "default": null, + "type": [ + "string", + "null" + ] + }, + "codex_error_info": { + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/v2/CodexErrorInfo" + }, + { + "type": "null" + } + ] + }, + "message": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "stream_error" + ], + "title": "StreamErrorEventMsgType" + } + }, + "title": "StreamErrorEventMsg" + }, + { + "description": "Notification that the agent is about to apply a code patch. Mirrors `ExecCommandBegin` so front‑ends can show progress indicators.", + "type": "object", + "required": [ + "auto_approved", + "call_id", + "changes", + "type" + ], + "properties": { + "auto_approved": { + "description": "If true, there was no ApplyPatchApprovalRequest for this patch.", + "type": "boolean" + }, + "call_id": { + "description": "Identifier so this can be paired with the PatchApplyEnd event.", + "type": "string" + }, + "changes": { + "description": "The changes to be applied.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/FileChange" + } + }, + "turn_id": { + "description": "Turn ID that this patch belongs to. Uses `#[serde(default)]` for backwards compatibility.", + "default": "", + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "patch_apply_begin" + ], + "title": "PatchApplyBeginEventMsgType" + } + }, + "title": "PatchApplyBeginEventMsg" + }, + { + "description": "Notification that a patch application has finished.", + "type": "object", + "required": [ + "call_id", + "stderr", + "stdout", + "success", + "type" + ], + "properties": { + "call_id": { + "description": "Identifier for the PatchApplyBegin that finished.", + "type": "string" + }, + "changes": { + "description": "The changes that were applied (mirrors PatchApplyBeginEvent::changes).", + "default": {}, + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/FileChange" + } + }, + "stderr": { + "description": "Captured stderr (parser errors, IO failures, etc.).", + "type": "string" + }, + "stdout": { + "description": "Captured stdout (summary printed by apply_patch).", + "type": "string" + }, + "success": { + "description": "Whether the patch was applied successfully.", + "type": "boolean" + }, + "turn_id": { + "description": "Turn ID that this patch belongs to. Uses `#[serde(default)]` for backwards compatibility.", + "default": "", + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "patch_apply_end" + ], + "title": "PatchApplyEndEventMsgType" + } + }, + "title": "PatchApplyEndEventMsg" + }, + { + "type": "object", + "required": [ + "type", + "unified_diff" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "turn_diff" + ], + "title": "TurnDiffEventMsgType" + }, + "unified_diff": { + "type": "string" + } + }, + "title": "TurnDiffEventMsg" + }, + { + "description": "Response to GetHistoryEntryRequest.", + "type": "object", + "required": [ + "log_id", + "offset", + "type" + ], + "properties": { + "entry": { + "description": "The entry at the requested offset, if available and parseable.", + "anyOf": [ + { + "$ref": "#/definitions/HistoryEntry" + }, + { + "type": "null" + } + ] + }, + "log_id": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "offset": { + "type": "integer", + "format": "uint", + "minimum": 0 + }, + "type": { + "type": "string", + "enum": [ + "get_history_entry_response" + ], + "title": "GetHistoryEntryResponseEventMsgType" + } + }, + "title": "GetHistoryEntryResponseEventMsg" + }, + { + "description": "List of MCP tools available to the agent.", + "type": "object", + "required": [ + "auth_statuses", + "resource_templates", + "resources", + "tools", + "type" + ], + "properties": { + "auth_statuses": { + "description": "Authentication status for each configured MCP server.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v2/McpAuthStatus" + } + }, + "resource_templates": { + "description": "Known resource templates grouped by server name.", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/ResourceTemplate" + } + } + }, + "resources": { + "description": "Known resources grouped by server name.", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/Resource" + } + } + }, + "tools": { + "description": "Fully qualified tool name -> tool definition.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v2/Tool" + } + }, + "type": { + "type": "string", + "enum": [ + "mcp_list_tools_response" + ], + "title": "McpListToolsResponseEventMsgType" + } + }, + "title": "McpListToolsResponseEventMsg" + }, + { + "description": "List of custom prompts available to the agent.", + "type": "object", + "required": [ + "custom_prompts", + "type" + ], + "properties": { + "custom_prompts": { + "type": "array", + "items": { + "$ref": "#/definitions/CustomPrompt" + } + }, + "type": { + "type": "string", + "enum": [ + "list_custom_prompts_response" + ], + "title": "ListCustomPromptsResponseEventMsgType" + } + }, + "title": "ListCustomPromptsResponseEventMsg" + }, + { + "description": "List of skills available to the agent.", + "type": "object", + "required": [ + "skills", + "type" + ], + "properties": { + "skills": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/SkillsListEntry" + } + }, + "type": { + "type": "string", + "enum": [ + "list_skills_response" + ], + "title": "ListSkillsResponseEventMsgType" + } + }, + "title": "ListSkillsResponseEventMsg" + }, + { + "description": "Notification that skill data may have been updated and clients may want to reload.", + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "skills_update_available" + ], + "title": "SkillsUpdateAvailableEventMsgType" + } + }, + "title": "SkillsUpdateAvailableEventMsg" + }, + { + "type": "object", + "required": [ + "plan", + "type" + ], + "properties": { + "explanation": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "plan": { + "type": "array", + "items": { + "$ref": "#/definitions/PlanItemArg" + } + }, + "type": { + "type": "string", + "enum": [ + "plan_update" + ], + "title": "PlanUpdateEventMsgType" + } + }, + "title": "PlanUpdateEventMsg" + }, + { + "type": "object", + "required": [ + "reason", + "type" + ], + "properties": { + "reason": { + "$ref": "#/definitions/TurnAbortReason" + }, + "type": { + "type": "string", + "enum": [ + "turn_aborted" + ], + "title": "TurnAbortedEventMsgType" + } + }, + "title": "TurnAbortedEventMsg" + }, + { + "description": "Notification that the agent is shutting down.", + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "shutdown_complete" + ], + "title": "ShutdownCompleteEventMsgType" + } + }, + "title": "ShutdownCompleteEventMsg" + }, + { + "description": "Entered review mode.", + "type": "object", + "required": [ + "target", + "type" + ], + "properties": { + "target": { + "$ref": "#/definitions/v2/ReviewTarget" + }, + "type": { + "type": "string", + "enum": [ + "entered_review_mode" + ], + "title": "EnteredReviewModeEventMsgType" + }, + "user_facing_hint": { + "type": [ + "string", + "null" + ] + } + }, + "title": "EnteredReviewModeEventMsg" + }, + { + "description": "Exited review mode with an optional final result to apply.", + "type": "object", + "required": [ + "type" + ], + "properties": { + "review_output": { + "anyOf": [ + { + "$ref": "#/definitions/ReviewOutputEvent" + }, + { + "type": "null" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "exited_review_mode" + ], + "title": "ExitedReviewModeEventMsgType" + } + }, + "title": "ExitedReviewModeEventMsg" + }, + { + "type": "object", + "required": [ + "item", + "type" + ], + "properties": { + "item": { + "$ref": "#/definitions/v2/ResponseItem" + }, + "type": { + "type": "string", + "enum": [ + "raw_response_item" + ], + "title": "RawResponseItemEventMsgType" + } + }, + "title": "RawResponseItemEventMsg" + }, + { + "type": "object", + "required": [ + "item", + "thread_id", + "turn_id", + "type" + ], + "properties": { + "item": { + "$ref": "#/definitions/TurnItem" + }, + "thread_id": { + "$ref": "#/definitions/v2/ThreadId" + }, + "turn_id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "item_started" + ], + "title": "ItemStartedEventMsgType" + } + }, + "title": "ItemStartedEventMsg" + }, + { + "type": "object", + "required": [ + "item", + "thread_id", + "turn_id", + "type" + ], + "properties": { + "item": { + "$ref": "#/definitions/TurnItem" + }, + "thread_id": { + "$ref": "#/definitions/v2/ThreadId" + }, + "turn_id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "item_completed" + ], + "title": "ItemCompletedEventMsgType" + } + }, + "title": "ItemCompletedEventMsg" + }, + { + "type": "object", + "required": [ + "delta", + "item_id", + "thread_id", + "turn_id", + "type" + ], + "properties": { + "delta": { + "type": "string" + }, + "item_id": { + "type": "string" + }, + "thread_id": { + "type": "string" + }, + "turn_id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "agent_message_content_delta" + ], + "title": "AgentMessageContentDeltaEventMsgType" + } + }, + "title": "AgentMessageContentDeltaEventMsg" + }, + { + "type": "object", + "required": [ + "delta", + "item_id", + "thread_id", + "turn_id", + "type" + ], + "properties": { + "delta": { + "type": "string" + }, + "item_id": { + "type": "string" + }, + "summary_index": { + "default": 0, + "type": "integer", + "format": "int64" + }, + "thread_id": { + "type": "string" + }, + "turn_id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "reasoning_content_delta" + ], + "title": "ReasoningContentDeltaEventMsgType" + } + }, + "title": "ReasoningContentDeltaEventMsg" + }, + { + "type": "object", + "required": [ + "delta", + "item_id", + "thread_id", + "turn_id", + "type" + ], + "properties": { + "content_index": { + "default": 0, + "type": "integer", + "format": "int64" + }, + "delta": { + "type": "string" + }, + "item_id": { + "type": "string" + }, + "thread_id": { + "type": "string" + }, + "turn_id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "reasoning_raw_content_delta" + ], + "title": "ReasoningRawContentDeltaEventMsgType" + } + }, + "title": "ReasoningRawContentDeltaEventMsg" + }, + { + "description": "Collab interaction: agent spawn begin.", + "type": "object", + "required": [ + "call_id", + "prompt", + "sender_thread_id", + "type" + ], + "properties": { + "call_id": { + "description": "Identifier for the collab tool call.", + "type": "string" + }, + "prompt": { + "description": "Initial prompt sent to the agent. Can be empty to prevent CoT leaking at the beginning.", + "type": "string" + }, + "sender_thread_id": { + "description": "Thread ID of the sender.", + "allOf": [ + { + "$ref": "#/definitions/v2/ThreadId" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "collab_agent_spawn_begin" + ], + "title": "CollabAgentSpawnBeginEventMsgType" + } + }, + "title": "CollabAgentSpawnBeginEventMsg" + }, + { + "description": "Collab interaction: agent spawn end.", + "type": "object", + "required": [ + "call_id", + "prompt", + "sender_thread_id", + "status", + "type" + ], + "properties": { + "call_id": { + "description": "Identifier for the collab tool call.", + "type": "string" + }, + "new_thread_id": { + "description": "Thread ID of the newly spawned agent, if it was created.", + "anyOf": [ + { + "$ref": "#/definitions/v2/ThreadId" + }, + { + "type": "null" + } + ] + }, + "prompt": { + "description": "Initial prompt sent to the agent. Can be empty to prevent CoT leaking at the beginning.", + "type": "string" + }, + "sender_thread_id": { + "description": "Thread ID of the sender.", + "allOf": [ + { + "$ref": "#/definitions/v2/ThreadId" + } + ] + }, + "status": { + "description": "Last known status of the new agent reported to the sender agent.", + "allOf": [ + { + "$ref": "#/definitions/AgentStatus" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "collab_agent_spawn_end" + ], + "title": "CollabAgentSpawnEndEventMsgType" + } + }, + "title": "CollabAgentSpawnEndEventMsg" + }, + { + "description": "Collab interaction: agent interaction begin.", + "type": "object", + "required": [ + "call_id", + "prompt", + "receiver_thread_id", + "sender_thread_id", + "type" + ], + "properties": { + "call_id": { + "description": "Identifier for the collab tool call.", + "type": "string" + }, + "prompt": { + "description": "Prompt sent from the sender to the receiver. Can be empty to prevent CoT leaking at the beginning.", + "type": "string" + }, + "receiver_thread_id": { + "description": "Thread ID of the receiver.", + "allOf": [ + { + "$ref": "#/definitions/v2/ThreadId" + } + ] + }, + "sender_thread_id": { + "description": "Thread ID of the sender.", + "allOf": [ + { + "$ref": "#/definitions/v2/ThreadId" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "collab_agent_interaction_begin" + ], + "title": "CollabAgentInteractionBeginEventMsgType" + } + }, + "title": "CollabAgentInteractionBeginEventMsg" + }, + { + "description": "Collab interaction: agent interaction end.", + "type": "object", + "required": [ + "call_id", + "prompt", + "receiver_thread_id", + "sender_thread_id", + "status", + "type" + ], + "properties": { + "call_id": { + "description": "Identifier for the collab tool call.", + "type": "string" + }, + "prompt": { + "description": "Prompt sent from the sender to the receiver. Can be empty to prevent CoT leaking at the beginning.", + "type": "string" + }, + "receiver_thread_id": { + "description": "Thread ID of the receiver.", + "allOf": [ + { + "$ref": "#/definitions/v2/ThreadId" + } + ] + }, + "sender_thread_id": { + "description": "Thread ID of the sender.", + "allOf": [ + { + "$ref": "#/definitions/v2/ThreadId" + } + ] + }, + "status": { + "description": "Last known status of the receiver agent reported to the sender agent.", + "allOf": [ + { + "$ref": "#/definitions/AgentStatus" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "collab_agent_interaction_end" + ], + "title": "CollabAgentInteractionEndEventMsgType" + } + }, + "title": "CollabAgentInteractionEndEventMsg" + }, + { + "description": "Collab interaction: waiting begin.", + "type": "object", + "required": [ + "call_id", + "receiver_thread_ids", + "sender_thread_id", + "type" + ], + "properties": { + "call_id": { + "description": "ID of the waiting call.", + "type": "string" + }, + "receiver_thread_ids": { + "description": "Thread ID of the receivers.", + "type": "array", + "items": { + "$ref": "#/definitions/v2/ThreadId" + } + }, + "sender_thread_id": { + "description": "Thread ID of the sender.", + "allOf": [ + { + "$ref": "#/definitions/v2/ThreadId" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "collab_waiting_begin" + ], + "title": "CollabWaitingBeginEventMsgType" + } + }, + "title": "CollabWaitingBeginEventMsg" + }, + { + "description": "Collab interaction: waiting end.", + "type": "object", + "required": [ + "call_id", + "sender_thread_id", + "statuses", + "type" + ], + "properties": { + "call_id": { + "description": "ID of the waiting call.", + "type": "string" + }, + "sender_thread_id": { + "description": "Thread ID of the sender.", + "allOf": [ + { + "$ref": "#/definitions/v2/ThreadId" + } + ] + }, + "statuses": { + "description": "Last known status of the receiver agents reported to the sender agent.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/AgentStatus" + } + }, + "type": { + "type": "string", + "enum": [ + "collab_waiting_end" + ], + "title": "CollabWaitingEndEventMsgType" + } + }, + "title": "CollabWaitingEndEventMsg" + }, + { + "description": "Collab interaction: close begin.", + "type": "object", + "required": [ + "call_id", + "receiver_thread_id", + "sender_thread_id", + "type" + ], + "properties": { + "call_id": { + "description": "Identifier for the collab tool call.", + "type": "string" + }, + "receiver_thread_id": { + "description": "Thread ID of the receiver.", + "allOf": [ + { + "$ref": "#/definitions/v2/ThreadId" + } + ] + }, + "sender_thread_id": { + "description": "Thread ID of the sender.", + "allOf": [ + { + "$ref": "#/definitions/v2/ThreadId" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "collab_close_begin" + ], + "title": "CollabCloseBeginEventMsgType" + } + }, + "title": "CollabCloseBeginEventMsg" + }, + { + "description": "Collab interaction: close end.", + "type": "object", + "required": [ + "call_id", + "receiver_thread_id", + "sender_thread_id", + "status", + "type" + ], + "properties": { + "call_id": { + "description": "Identifier for the collab tool call.", + "type": "string" + }, + "receiver_thread_id": { + "description": "Thread ID of the receiver.", + "allOf": [ + { + "$ref": "#/definitions/v2/ThreadId" + } + ] + }, + "sender_thread_id": { + "description": "Thread ID of the sender.", + "allOf": [ + { + "$ref": "#/definitions/v2/ThreadId" + } + ] + }, + "status": { + "description": "Last known status of the receiver agent reported to the sender agent before the close.", + "allOf": [ + { + "$ref": "#/definitions/AgentStatus" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "collab_close_end" + ], + "title": "CollabCloseEndEventMsgType" + } + }, + "title": "CollabCloseEndEventMsg" + } + ] + }, + "ExecCommandSource": { + "type": "string", + "enum": [ + "agent", + "user_shell", + "unified_exec_startup", + "unified_exec_interaction" + ] + }, + "ExecOutputStream": { + "type": "string", + "enum": [ + "stdout", + "stderr" + ] + }, + "HistoryEntry": { + "type": "object", + "required": [ + "conversation_id", + "text", + "ts" + ], + "properties": { + "conversation_id": { + "type": "string" + }, + "text": { + "type": "string" + }, + "ts": { + "type": "integer", + "format": "uint64", + "minimum": 0 + } + } + }, + "ImageContent": { + "description": "An image provided to or from an LLM.", + "type": "object", + "required": [ + "data", + "mimeType", + "type" + ], + "properties": { + "annotations": { + "anyOf": [ + { + "$ref": "#/definitions/Annotations" + }, + { + "type": "null" + } + ] + }, + "data": { + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "McpAuthStatus": { + "type": "string", + "enum": [ + "unsupported", + "not_logged_in", + "bearer_token", + "o_auth" + ] + }, + "McpInvocation": { + "type": "object", + "required": [ + "server", + "tool" + ], + "properties": { + "arguments": { + "description": "Arguments to the tool call." + }, + "server": { + "description": "Name of the MCP server as defined in the config.", + "type": "string" + }, + "tool": { + "description": "Name of the tool as given by the MCP server.", + "type": "string" + } + } + }, + "McpStartupFailure": { + "type": "object", + "required": [ + "error", + "server" + ], + "properties": { + "error": { + "type": "string" + }, + "server": { + "type": "string" + } + } + }, + "McpStartupStatus": { + "oneOf": [ + { + "type": "object", + "required": [ + "state" + ], + "properties": { + "state": { + "type": "string", + "enum": [ + "starting" + ] + } + }, + "title": "StateMcpStartupStatus" + }, + { + "type": "object", + "required": [ + "state" + ], + "properties": { + "state": { + "type": "string", + "enum": [ + "ready" + ] + } + }, + "title": "StateMcpStartupStatus2" + }, + { + "type": "object", + "required": [ + "error", + "state" + ], + "properties": { + "error": { + "type": "string" + }, + "state": { + "type": "string", + "enum": [ + "failed" + ] + } + } + }, + { + "type": "object", + "required": [ + "state" + ], + "properties": { + "state": { + "type": "string", + "enum": [ + "cancelled" + ] + } + }, + "title": "StateMcpStartupStatus3" + } + ] + }, + "ParsedCommand": { + "oneOf": [ + { + "type": "object", + "required": [ + "cmd", + "name", + "path", + "type" + ], + "properties": { + "cmd": { + "type": "string" + }, + "name": { + "type": "string" + }, + "path": { + "description": "(Best effort) Path to the file being read by the command. When possible, this is an absolute path, though when relative, it should be resolved against the `cwd`` that will be used to run the command to derive the absolute path.", + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "read" + ], + "title": "ReadParsedCommandType" + } + }, + "title": "ReadParsedCommand" + }, + { + "type": "object", + "required": [ + "cmd", + "type" + ], + "properties": { + "cmd": { + "type": "string" + }, + "path": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "list_files" + ], + "title": "ListFilesParsedCommandType" + } + }, + "title": "ListFilesParsedCommand" + }, + { + "type": "object", + "required": [ + "cmd", + "type" + ], + "properties": { + "cmd": { + "type": "string" + }, + "path": { + "type": [ + "string", + "null" + ] + }, + "query": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "search" + ], + "title": "SearchParsedCommandType" + } + }, + "title": "SearchParsedCommand" + }, + { + "type": "object", + "required": [ + "cmd", + "type" + ], + "properties": { + "cmd": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "unknown" + ], + "title": "UnknownParsedCommandType" + } + }, + "title": "UnknownParsedCommand" + } + ] + }, + "PlanItemArg": { + "type": "object", + "required": [ + "status", + "step" + ], + "properties": { + "status": { + "$ref": "#/definitions/StepStatus" + }, + "step": { + "type": "string" + } + }, + "additionalProperties": false + }, + "PlanType": { + "type": "string", + "enum": [ + "free", + "plus", + "pro", + "team", + "business", + "enterprise", + "edu", + "unknown" + ] + }, + "RateLimitSnapshot": { + "type": "object", + "properties": { + "credits": { + "anyOf": [ + { + "$ref": "#/definitions/CreditsSnapshot" + }, + { + "type": "null" + } + ] + }, + "plan_type": { + "anyOf": [ + { + "$ref": "#/definitions/PlanType" + }, + { + "type": "null" + } + ] + }, + "primary": { + "anyOf": [ + { + "$ref": "#/definitions/RateLimitWindow" + }, + { + "type": "null" + } + ] + }, + "secondary": { + "anyOf": [ + { + "$ref": "#/definitions/RateLimitWindow" + }, + { + "type": "null" + } + ] + } + } + }, + "RateLimitWindow": { + "type": "object", + "required": [ + "used_percent" + ], + "properties": { + "resets_at": { + "description": "Unix timestamp (seconds since epoch) when the window resets.", + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "used_percent": { + "description": "Percentage (0-100) of the window that has been consumed.", + "type": "number", + "format": "double" + }, + "window_minutes": { + "description": "Rolling window duration, in minutes.", + "type": [ + "integer", + "null" + ], + "format": "int64" + } + } + }, + "RequestUserInputQuestion": { + "type": "object", + "required": [ + "header", + "id", + "question" + ], + "properties": { + "header": { + "type": "string" + }, + "id": { + "type": "string" + }, + "options": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/RequestUserInputQuestionOption" + } + }, + "question": { + "type": "string" + } + } + }, + "RequestUserInputQuestionOption": { + "type": "object", + "required": [ + "description", + "label" + ], + "properties": { + "description": { + "type": "string" + }, + "label": { + "type": "string" + } + } + }, + "Resource": { + "description": "A known resource that the server is capable of reading.", + "type": "object", + "required": [ + "name", + "uri" + ], + "properties": { + "annotations": { + "anyOf": [ + { + "$ref": "#/definitions/Annotations" + }, + { + "type": "null" + } + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "mimeType": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "size": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "title": { + "type": [ + "string", + "null" + ] + }, + "uri": { + "type": "string" + } + } + }, + "ResourceLink": { + "description": "A resource that the server is capable of reading, included in a prompt or tool call result.\n\nNote: resource links returned by tools are not guaranteed to appear in the results of `resources/list` requests.", + "type": "object", + "required": [ + "name", + "type", + "uri" + ], + "properties": { + "annotations": { + "anyOf": [ + { + "$ref": "#/definitions/Annotations" + }, + { + "type": "null" + } + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "mimeType": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "size": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "title": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string" + }, + "uri": { + "type": "string" + } + } + }, + "ResourceTemplate": { + "description": "A template description for resources available on the server.", + "type": "object", + "required": [ + "name", + "uriTemplate" + ], + "properties": { + "annotations": { + "anyOf": [ + { + "$ref": "#/definitions/Annotations" + }, + { + "type": "null" + } + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "mimeType": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "title": { + "type": [ + "string", + "null" + ] + }, + "uriTemplate": { + "type": "string" + } + } + }, + "Result_of_CallToolResult_or_String": { + "oneOf": [ + { + "type": "object", + "required": [ + "Ok" + ], + "properties": { + "Ok": { + "$ref": "#/definitions/CallToolResult" + } + }, + "title": "OkResult_of_CallToolResult_or_String" + }, + { + "type": "object", + "required": [ + "Err" + ], + "properties": { + "Err": { + "type": "string" + } + }, + "title": "ErrResult_of_CallToolResult_or_String" + } + ] + }, + "ReviewCodeLocation": { + "description": "Location of the code related to a review finding.", + "type": "object", + "required": [ + "absolute_file_path", + "line_range" + ], + "properties": { + "absolute_file_path": { + "type": "string" + }, + "line_range": { + "$ref": "#/definitions/ReviewLineRange" + } + } + }, + "ReviewFinding": { + "description": "A single review finding describing an observed issue or recommendation.", + "type": "object", + "required": [ + "body", + "code_location", + "confidence_score", + "priority", + "title" + ], + "properties": { + "body": { + "type": "string" + }, + "code_location": { + "$ref": "#/definitions/ReviewCodeLocation" + }, + "confidence_score": { + "type": "number", + "format": "float" + }, + "priority": { + "type": "integer", + "format": "int32" + }, + "title": { + "type": "string" + } + } + }, + "ReviewLineRange": { + "description": "Inclusive line range in a file associated with the finding.", + "type": "object", + "required": [ + "end", + "start" + ], + "properties": { + "end": { + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "start": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + } + }, + "ReviewOutputEvent": { + "description": "Structured review result produced by a child review session.", + "type": "object", + "required": [ + "findings", + "overall_confidence_score", + "overall_correctness", + "overall_explanation" + ], + "properties": { + "findings": { + "type": "array", + "items": { + "$ref": "#/definitions/ReviewFinding" + } + }, + "overall_confidence_score": { + "type": "number", + "format": "float" + }, + "overall_correctness": { + "type": "string" + }, + "overall_explanation": { + "type": "string" + } + } + }, + "Role": { + "description": "The sender or recipient of messages and data in a conversation.", + "type": "string", + "enum": [ + "assistant", + "user" + ] + }, + "SkillErrorInfo": { + "type": "object", + "required": [ + "message", + "path" + ], + "properties": { + "message": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "SkillInterface": { + "type": "object", + "properties": { + "brand_color": { + "type": [ + "string", + "null" + ] + }, + "default_prompt": { + "type": [ + "string", + "null" + ] + }, + "display_name": { + "type": [ + "string", + "null" + ] + }, + "icon_large": { + "type": [ + "string", + "null" + ] + }, + "icon_small": { + "type": [ + "string", + "null" + ] + }, + "short_description": { + "type": [ + "string", + "null" + ] + } + } + }, + "SkillMetadata": { + "type": "object", + "required": [ + "description", + "enabled", + "name", + "path", + "scope" + ], + "properties": { + "description": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "interface": { + "anyOf": [ + { + "$ref": "#/definitions/SkillInterface" + }, + { + "type": "null" + } + ] + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "scope": { + "$ref": "#/definitions/SkillScope" + }, + "short_description": { + "description": "Legacy short_description from SKILL.md. Prefer SKILL.toml interface.short_description.", + "type": [ + "string", + "null" + ] + } + } + }, + "SkillScope": { + "type": "string", + "enum": [ + "user", + "repo", + "system", + "admin" + ] + }, + "SkillsListEntry": { + "type": "object", + "required": [ + "cwd", + "errors", + "skills" + ], + "properties": { + "cwd": { + "type": "string" + }, + "errors": { + "type": "array", + "items": { + "$ref": "#/definitions/SkillErrorInfo" + } + }, + "skills": { + "type": "array", + "items": { + "$ref": "#/definitions/SkillMetadata" + } + } + } + }, + "StepStatus": { + "type": "string", + "enum": [ + "pending", + "in_progress", + "completed" + ] + }, + "TextContent": { + "description": "Text provided to or from an LLM.", + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "annotations": { + "anyOf": [ + { + "$ref": "#/definitions/Annotations" + }, + { + "type": "null" + } + ] + }, + "text": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "TextResourceContents": { + "type": "object", + "required": [ + "text", + "uri" + ], + "properties": { + "mimeType": { + "type": [ + "string", + "null" + ] + }, + "text": { + "type": "string" + }, + "uri": { + "type": "string" + } + } + }, + "TokenUsage": { + "type": "object", + "required": [ + "cached_input_tokens", + "input_tokens", + "output_tokens", + "reasoning_output_tokens", + "total_tokens" + ], + "properties": { + "cached_input_tokens": { + "type": "integer", + "format": "int64" + }, + "input_tokens": { + "type": "integer", + "format": "int64" + }, + "output_tokens": { + "type": "integer", + "format": "int64" + }, + "reasoning_output_tokens": { + "type": "integer", + "format": "int64" + }, + "total_tokens": { + "type": "integer", + "format": "int64" + } + } + }, + "TokenUsageInfo": { + "type": "object", + "required": [ + "last_token_usage", + "total_token_usage" + ], + "properties": { + "last_token_usage": { + "$ref": "#/definitions/TokenUsage" + }, + "model_context_window": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total_token_usage": { + "$ref": "#/definitions/TokenUsage" + } + } + }, + "Tool": { + "description": "Definition for a tool the client can call.", + "type": "object", + "required": [ + "inputSchema", + "name" + ], + "properties": { + "annotations": { + "anyOf": [ + { + "$ref": "#/definitions/ToolAnnotations" + }, + { + "type": "null" + } + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "inputSchema": { + "$ref": "#/definitions/ToolInputSchema" + }, + "name": { + "type": "string" + }, + "outputSchema": { + "anyOf": [ + { + "$ref": "#/definitions/ToolOutputSchema" + }, + { + "type": "null" + } + ] + }, + "title": { + "type": [ + "string", + "null" + ] + } + } + }, + "ToolAnnotations": { + "description": "Additional properties describing a Tool to clients.\n\nNOTE: all properties in ToolAnnotations are **hints**. They are not guaranteed to provide a faithful description of tool behavior (including descriptive properties like `title`).\n\nClients should never make tool use decisions based on ToolAnnotations received from untrusted servers.", + "type": "object", + "properties": { + "destructiveHint": { + "type": [ + "boolean", + "null" + ] + }, + "idempotentHint": { + "type": [ + "boolean", + "null" + ] + }, + "openWorldHint": { + "type": [ + "boolean", + "null" + ] + }, + "readOnlyHint": { + "type": [ + "boolean", + "null" + ] + }, + "title": { + "type": [ + "string", + "null" + ] + } + } + }, + "ToolInputSchema": { + "description": "A JSON Schema object defining the expected parameters for the tool.", + "type": "object", + "properties": { + "properties": true, + "required": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "type": { + "default": "object", + "type": "string" + } + } + }, + "ToolOutputSchema": { + "description": "An optional JSON Schema object defining the structure of the tool's output returned in the structuredContent field of a CallToolResult.", + "type": "object", + "properties": { + "properties": true, + "required": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "type": { + "default": "object", + "type": "string" + } + } + }, + "TurnAbortReason": { + "type": "string", + "enum": [ + "interrupted", + "replaced", + "review_ended" + ] + }, + "TurnItem": { + "oneOf": [ + { + "type": "object", + "required": [ + "content", + "id", + "type" + ], + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/definitions/UserInput" + } + }, + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "UserMessage" + ], + "title": "UserMessageTurnItemType" + } + }, + "title": "UserMessageTurnItem" + }, + { + "type": "object", + "required": [ + "content", + "id", + "type" + ], + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/definitions/AgentMessageContent" + } + }, + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "AgentMessage" + ], + "title": "AgentMessageTurnItemType" + } + }, + "title": "AgentMessageTurnItem" + }, + { + "type": "object", + "required": [ + "id", + "summary_text", + "type" + ], + "properties": { + "id": { + "type": "string" + }, + "raw_content": { + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "summary_text": { + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "type": "string", + "enum": [ + "Reasoning" + ], + "title": "ReasoningTurnItemType" + } + }, + "title": "ReasoningTurnItem" + }, + { + "type": "object", + "required": [ + "id", + "query", + "type" + ], + "properties": { + "id": { + "type": "string" + }, + "query": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "WebSearch" + ], + "title": "WebSearchTurnItemType" + } + }, + "title": "WebSearchTurnItem" + } + ] + }, + "FileChangeRequestApprovalParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "FileChangeRequestApprovalParams", + "type": "object", + "required": [ + "itemId", + "threadId", + "turnId" + ], + "properties": { + "grantRoot": { + "description": "[UNSTABLE] When set, the agent is asking the user to allow writes under this root for the remainder of the session (unclear if this is honored today).", + "type": [ + "string", + "null" + ] + }, + "itemId": { + "type": "string" + }, + "reason": { + "description": "Optional explanatory reason (e.g. request for extra write access).", + "type": [ + "string", + "null" + ] + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "FileChangeApprovalDecision": { + "oneOf": [ + { + "description": "User approved the file changes.", + "type": "string", + "enum": [ + "accept" + ] + }, + { + "description": "User approved the file changes and future changes to the same files should run without prompting.", + "type": "string", + "enum": [ + "acceptForSession" + ] + }, + { + "description": "User denied the file changes. The agent will continue the turn.", + "type": "string", + "enum": [ + "decline" + ] + }, + { + "description": "User denied the file changes. The turn will also be immediately interrupted.", + "type": "string", + "enum": [ + "cancel" + ] + } + ] + }, + "FuzzyFileSearchResult": { + "description": "Superset of [`codex_file_search::FileMatch`]", + "type": "object", + "required": [ + "file_name", + "path", + "root", + "score" + ], + "properties": { + "file_name": { + "type": "string" + }, + "indices": { + "type": [ + "array", + "null" + ], + "items": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + }, + "path": { + "type": "string" + }, + "root": { + "type": "string" + }, + "score": { + "type": "integer", + "format": "uint32", + "minimum": 0 + } + } + }, + "JSONRPCErrorError": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "JSONRPCErrorError", + "type": "object", + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int64" + }, + "data": true, + "message": { + "type": "string" + } + } + }, + "JSONRPCError": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "JSONRPCError", + "description": "A response to a request that indicates an error occurred.", + "type": "object", + "required": [ + "error", + "id" + ], + "properties": { + "error": { + "$ref": "#/definitions/JSONRPCErrorError" + }, + "id": { + "$ref": "#/definitions/RequestId" + } + } + }, + "JSONRPCNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "JSONRPCNotification", + "description": "A notification which does not expect a response.", + "type": "object", + "required": [ + "method" + ], + "properties": { + "method": { + "type": "string" + }, + "params": true + } + }, + "JSONRPCRequest": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "JSONRPCRequest", + "description": "A request that expects a response.", + "type": "object", + "required": [ + "id", + "method" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string" + }, + "params": true + } + }, + "JSONRPCResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "JSONRPCResponse", + "description": "A successful (non-error) response to a request.", + "type": "object", + "required": [ + "id", + "result" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "result": true + } + }, + "AccountLoginCompletedNotification": { + "type": "object", + "required": [ + "success" + ], + "properties": { + "error": { + "type": [ + "string", + "null" + ] + }, + "loginId": { + "type": [ + "string", + "null" + ] + }, + "success": { + "type": "boolean" + } + } + }, + "AccountRateLimitsUpdatedNotification": { + "type": "object", + "required": [ + "rateLimits" + ], + "properties": { + "rateLimits": { + "$ref": "#/definitions/RateLimitSnapshot" + } + } + }, + "AccountUpdatedNotification": { + "type": "object", + "properties": { + "authMode": { + "anyOf": [ + { + "$ref": "#/definitions/AuthMode" + }, + { + "type": "null" + } + ] + } + } + }, + "AgentMessageDeltaNotification": { + "type": "object", + "required": [ + "delta", + "itemId", + "threadId", + "turnId" + ], + "properties": { + "delta": { + "type": "string" + }, + "itemId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "AuthMode": { + "type": "string", + "enum": [ + "apikey", + "chatgpt" + ] + }, + "AuthStatusChangeNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AuthStatusChangeNotification", + "description": "Deprecated notification. Use AccountUpdatedNotification instead.", + "type": "object", + "properties": { + "authMethod": { + "anyOf": [ + { + "$ref": "#/definitions/AuthMode" + }, + { + "type": "null" + } + ] + } + } + }, + "ByteRange2": { + "type": "object", + "required": [ + "end", + "start" + ], + "properties": { + "end": { + "description": "End byte offset (exclusive) within the UTF-8 text buffer.", + "type": "integer", + "format": "uint", + "minimum": 0 + }, + "start": { + "description": "Start byte offset (inclusive) within the UTF-8 text buffer.", + "type": "integer", + "format": "uint", + "minimum": 0 + } + } + }, + "CodexErrorInfo2": { + "description": "Codex errors that we expose to clients.", + "oneOf": [ + { + "type": "string", + "enum": [ + "context_window_exceeded", + "usage_limit_exceeded", + "internal_server_error", + "unauthorized", + "bad_request", + "sandbox_error", + "thread_rollback_failed", + "other" + ] + }, + { + "type": "object", + "required": [ + "http_connection_failed" + ], + "properties": { + "http_connection_failed": { + "type": "object", + "properties": { + "http_status_code": { + "type": [ + "integer", + "null" + ], + "format": "uint16", + "minimum": 0 + } + } + } + }, + "additionalProperties": false, + "title": "HttpConnectionFailedCodexErrorInfo2" + }, + { + "description": "Failed to connect to the response SSE stream.", + "type": "object", + "required": [ + "response_stream_connection_failed" + ], + "properties": { + "response_stream_connection_failed": { + "type": "object", + "properties": { + "http_status_code": { + "type": [ + "integer", + "null" + ], + "format": "uint16", + "minimum": 0 + } + } + } + }, + "additionalProperties": false, + "title": "ResponseStreamConnectionFailedCodexErrorInfo2" + }, + { + "description": "The response SSE stream disconnected in the middle of a turnbefore completion.", + "type": "object", + "required": [ + "response_stream_disconnected" + ], + "properties": { + "response_stream_disconnected": { + "type": "object", + "properties": { + "http_status_code": { + "type": [ + "integer", + "null" + ], + "format": "uint16", + "minimum": 0 + } + } + } + }, + "additionalProperties": false, + "title": "ResponseStreamDisconnectedCodexErrorInfo2" + }, + { + "description": "Reached the retry limit for responses.", + "type": "object", + "required": [ + "response_too_many_failed_attempts" + ], + "properties": { + "response_too_many_failed_attempts": { + "type": "object", + "properties": { + "http_status_code": { + "type": [ + "integer", + "null" + ], + "format": "uint16", + "minimum": 0 + } + } + } + }, + "additionalProperties": false, + "title": "ResponseTooManyFailedAttemptsCodexErrorInfo2" + } + ] + }, + "CollabAgentState": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "message": { + "type": [ + "string", + "null" + ] + }, + "status": { + "$ref": "#/definitions/CollabAgentStatus" + } + } + }, + "CollabAgentStatus": { + "type": "string", + "enum": [ + "pendingInit", + "running", + "completed", + "errored", + "shutdown", + "notFound" + ] + }, + "CollabAgentTool": { + "type": "string", + "enum": [ + "spawnAgent", + "sendInput", + "wait", + "closeAgent" + ] + }, + "CollabAgentToolCallStatus": { + "type": "string", + "enum": [ + "inProgress", + "completed", + "failed" + ] + }, + "CommandExecutionOutputDeltaNotification": { + "type": "object", + "required": [ + "delta", + "itemId", + "threadId", + "turnId" + ], + "properties": { + "delta": { + "type": "string" + }, + "itemId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "CommandExecutionStatus": { + "type": "string", + "enum": [ + "inProgress", + "completed", + "failed", + "declined" + ] + }, + "ConfigWarningNotification": { + "type": "object", + "required": [ + "summary" + ], + "properties": { + "details": { + "description": "Optional extra guidance or error details.", + "type": [ + "string", + "null" + ] + }, + "path": { + "description": "Optional path to the config file that triggered the warning.", + "type": [ + "string", + "null" + ] + }, + "range": { + "description": "Optional range for the error location inside the config file.", + "anyOf": [ + { + "$ref": "#/definitions/TextRange" + }, + { + "type": "null" + } + ] + }, + "summary": { + "description": "Concise summary of the warning.", + "type": "string" + } + } + }, + "ContextCompactedNotification": { + "type": "object", + "required": [ + "threadId", + "turnId" + ], + "properties": { + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "CreditsSnapshot2": { + "type": "object", + "required": [ + "has_credits", + "unlimited" + ], + "properties": { + "balance": { + "type": [ + "string", + "null" + ] + }, + "has_credits": { + "type": "boolean" + }, + "unlimited": { + "type": "boolean" + } + } + }, + "DeprecationNoticeNotification": { + "type": "object", + "required": [ + "summary" + ], + "properties": { + "details": { + "description": "Optional extra guidance, such as migration steps or rationale.", + "type": [ + "string", + "null" + ] + }, + "summary": { + "description": "Concise summary of what is deprecated.", + "type": "string" + } + } + }, + "ErrorNotification": { + "type": "object", + "required": [ + "error", + "threadId", + "turnId", + "willRetry" + ], + "properties": { + "error": { + "$ref": "#/definitions/TurnError" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + }, + "willRetry": { + "type": "boolean" + } + } + }, + "FileChangeOutputDeltaNotification": { + "type": "object", + "required": [ + "delta", + "itemId", + "threadId", + "turnId" + ], + "properties": { + "delta": { + "type": "string" + }, + "itemId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "FileUpdateChange": { + "type": "object", + "required": [ + "diff", + "kind", + "path" + ], + "properties": { + "diff": { + "type": "string" + }, + "kind": { + "$ref": "#/definitions/PatchChangeKind" + }, + "path": { + "type": "string" + } + } + }, + "GitInfo": { + "type": "object", + "properties": { + "branch": { + "type": [ + "string", + "null" + ] + }, + "originUrl": { + "type": [ + "string", + "null" + ] + }, + "sha": { + "type": [ + "string", + "null" + ] + } + } + }, + "ItemCompletedNotification": { + "type": "object", + "required": [ + "item", + "threadId", + "turnId" + ], + "properties": { + "item": { + "$ref": "#/definitions/ThreadItem" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "ItemStartedNotification": { + "type": "object", + "required": [ + "item", + "threadId", + "turnId" + ], + "properties": { + "item": { + "$ref": "#/definitions/ThreadItem" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "LoginChatGptCompleteNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "LoginChatGptCompleteNotification", + "description": "Deprecated in favor of AccountLoginCompletedNotification.", + "type": "object", + "required": [ + "loginId", + "success" + ], + "properties": { + "error": { + "type": [ + "string", + "null" + ] + }, + "loginId": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + }, + "McpServerOauthLoginCompletedNotification": { + "type": "object", + "required": [ + "name", + "success" + ], + "properties": { + "error": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + }, + "McpToolCallError": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "message": { + "type": "string" + } + } + }, + "McpToolCallProgressNotification": { + "type": "object", + "required": [ + "itemId", + "message", + "threadId", + "turnId" + ], + "properties": { + "itemId": { + "type": "string" + }, + "message": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "McpToolCallResult": { + "type": "object", + "required": [ + "content" + ], + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/definitions/ContentBlock" + } + }, + "structuredContent": true + } + }, + "McpToolCallStatus": { + "type": "string", + "enum": [ + "inProgress", + "completed", + "failed" + ] + }, + "PatchApplyStatus": { + "type": "string", + "enum": [ + "inProgress", + "completed", + "failed", + "declined" + ] + }, + "PatchChangeKind": { + "oneOf": [ + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "add" + ], + "title": "AddPatchChangeKindType" + } + }, + "title": "AddPatchChangeKind" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "delete" + ], + "title": "DeletePatchChangeKindType" + } + }, + "title": "DeletePatchChangeKind" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "move_path": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "update" + ], + "title": "UpdatePatchChangeKindType" + } + }, + "title": "UpdatePatchChangeKind" + } + ] + }, + "RateLimitSnapshot2": { + "type": "object", + "properties": { + "credits": { + "anyOf": [ + { + "$ref": "#/definitions/CreditsSnapshot2" + }, + { + "type": "null" + } + ] + }, + "plan_type": { + "anyOf": [ + { + "$ref": "#/definitions/PlanType" + }, + { + "type": "null" + } + ] + }, + "primary": { + "anyOf": [ + { + "$ref": "#/definitions/RateLimitWindow2" + }, + { + "type": "null" + } + ] + }, + "secondary": { + "anyOf": [ + { + "$ref": "#/definitions/RateLimitWindow2" + }, + { + "type": "null" + } + ] + } + } + }, + "RateLimitWindow2": { + "type": "object", + "required": [ + "used_percent" + ], + "properties": { + "resets_at": { + "description": "Unix timestamp (seconds since epoch) when the window resets.", + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "used_percent": { + "description": "Percentage (0-100) of the window that has been consumed.", + "type": "number", + "format": "double" + }, + "window_minutes": { + "description": "Rolling window duration, in minutes.", + "type": [ + "integer", + "null" + ], + "format": "int64" + } + } + }, + "RawResponseItemCompletedNotification": { + "type": "object", + "required": [ + "item", + "threadId", + "turnId" + ], + "properties": { + "item": { + "$ref": "#/definitions/ResponseItem" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "ReasoningSummaryPartAddedNotification": { + "type": "object", + "required": [ + "itemId", + "summaryIndex", + "threadId", + "turnId" + ], + "properties": { + "itemId": { + "type": "string" + }, + "summaryIndex": { + "type": "integer", + "format": "int64" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "ReasoningSummaryTextDeltaNotification": { + "type": "object", + "required": [ + "delta", + "itemId", + "summaryIndex", + "threadId", + "turnId" + ], + "properties": { + "delta": { + "type": "string" + }, + "itemId": { + "type": "string" + }, + "summaryIndex": { + "type": "integer", + "format": "int64" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "ReasoningTextDeltaNotification": { + "type": "object", + "required": [ + "contentIndex", + "delta", + "itemId", + "threadId", + "turnId" + ], + "properties": { + "contentIndex": { + "type": "integer", + "format": "int64" + }, + "delta": { + "type": "string" + }, + "itemId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "SessionConfiguredNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "SessionConfiguredNotification", + "type": "object", + "required": [ + "historyEntryCount", + "historyLogId", + "model", + "rolloutPath", + "sessionId" + ], + "properties": { + "historyEntryCount": { + "type": "integer", + "format": "uint", + "minimum": 0 + }, + "historyLogId": { + "type": "integer", + "format": "uint64", + "minimum": 0 + }, + "initialMessages": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/EventMsg" + } + }, + "model": { + "type": "string" + }, + "reasoningEffort": { + "anyOf": [ + { + "$ref": "#/definitions/ReasoningEffort" + }, + { + "type": "null" + } + ] + }, + "rolloutPath": { + "type": "string" + }, + "sessionId": { + "$ref": "#/definitions/ThreadId" + } + } + }, + "SessionSource": { + "oneOf": [ + { + "type": "string", + "enum": [ + "cli", + "vscode", + "exec", + "mcp", + "unknown" + ] + }, + { + "type": "object", + "required": [ + "subagent" + ], + "properties": { + "subagent": { + "$ref": "#/definitions/SubAgentSource" + } + }, + "additionalProperties": false, + "title": "SubagentSessionSource" + } + ] + }, + "SubAgentSource": { + "oneOf": [ + { + "type": "string", + "enum": [ + "review", + "compact" + ] + }, + { + "type": "object", + "required": [ + "thread_spawn" + ], + "properties": { + "thread_spawn": { + "type": "object", + "required": [ + "depth", + "parent_thread_id" + ], + "properties": { + "depth": { + "type": "integer", + "format": "int32" + }, + "parent_thread_id": { + "$ref": "#/definitions/ThreadId" + } + } + } + }, + "additionalProperties": false, + "title": "ThreadSpawnSubAgentSource" + }, + { + "type": "object", + "required": [ + "other" + ], + "properties": { + "other": { + "type": "string" + } + }, + "additionalProperties": false, + "title": "OtherSubAgentSource" + } + ] + }, + "TerminalInteractionNotification": { + "type": "object", + "required": [ + "itemId", + "processId", + "stdin", + "threadId", + "turnId" + ], + "properties": { + "itemId": { + "type": "string" + }, + "processId": { + "type": "string" + }, + "stdin": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "TextElement2": { + "type": "object", + "required": [ + "byte_range" + ], + "properties": { + "byte_range": { + "description": "Byte range in the parent `text` buffer that this element occupies.", + "allOf": [ + { + "$ref": "#/definitions/ByteRange2" + } + ] + }, + "placeholder": { + "description": "Optional human-readable placeholder for the element, displayed in the UI.", + "type": [ + "string", + "null" + ] + } + } + }, + "TextPosition": { + "type": "object", + "required": [ + "column", + "line" + ], + "properties": { + "column": { + "description": "1-based column number (in Unicode scalar values).", + "type": "integer", + "format": "uint", + "minimum": 0 + }, + "line": { + "description": "1-based line number.", + "type": "integer", + "format": "uint", + "minimum": 0 + } + } + }, + "TextRange": { + "type": "object", + "required": [ + "end", + "start" + ], + "properties": { + "end": { + "$ref": "#/definitions/TextPosition" + }, + "start": { + "$ref": "#/definitions/TextPosition" + } + } + }, + "Thread": { + "type": "object", + "required": [ + "cliVersion", + "createdAt", + "cwd", + "id", + "modelProvider", + "preview", + "source", + "turns", + "updatedAt" + ], + "properties": { + "cliVersion": { + "description": "Version of the CLI that created the thread.", + "type": "string" + }, + "createdAt": { + "description": "Unix timestamp (in seconds) when the thread was created.", + "type": "integer", + "format": "int64" + }, + "cwd": { + "description": "Working directory captured for the thread.", + "type": "string" + }, + "gitInfo": { + "description": "Optional Git metadata captured when the thread was created.", + "anyOf": [ + { + "$ref": "#/definitions/GitInfo" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "modelProvider": { + "description": "Model provider used for this thread (for example, 'openai').", + "type": "string" + }, + "path": { + "description": "[UNSTABLE] Path to the thread on disk.", + "type": [ + "string", + "null" + ] + }, + "preview": { + "description": "Usually the first user message in the thread, if available.", + "type": "string" + }, + "source": { + "description": "Origin of the thread (CLI, VSCode, codex exec, codex app-server, etc.).", + "allOf": [ + { + "$ref": "#/definitions/SessionSource" + } + ] + }, + "turns": { + "description": "Only populated on `thread/resume`, `thread/rollback`, `thread/fork`, and `thread/read` (when `includeTurns` is true) responses. For all other responses and notifications returning a Thread, the turns field will be an empty list.", + "type": "array", + "items": { + "$ref": "#/definitions/Turn" + } + }, + "updatedAt": { + "description": "Unix timestamp (in seconds) when the thread was last updated.", + "type": "integer", + "format": "int64" + } + } + }, + "ThreadItem": { + "oneOf": [ + { + "type": "object", + "required": [ + "content", + "id", + "type" + ], + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/definitions/UserInput" + } + }, + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "userMessage" + ], + "title": "UserMessageThreadItemType" + } + }, + "title": "UserMessageThreadItem" + }, + { + "type": "object", + "required": [ + "id", + "text", + "type" + ], + "properties": { + "id": { + "type": "string" + }, + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "agentMessage" + ], + "title": "AgentMessageThreadItemType" + } + }, + "title": "AgentMessageThreadItem" + }, + { + "type": "object", + "required": [ + "id", + "type" + ], + "properties": { + "content": { + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "string" + }, + "summary": { + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "type": "string", + "enum": [ + "reasoning" + ], + "title": "ReasoningThreadItemType" + } + }, + "title": "ReasoningThreadItem" + }, + { + "type": "object", + "required": [ + "command", + "commandActions", + "cwd", + "id", + "status", + "type" + ], + "properties": { + "aggregatedOutput": { + "description": "The command's output, aggregated from stdout and stderr.", + "type": [ + "string", + "null" + ] + }, + "command": { + "description": "The command to be executed.", + "type": "string" + }, + "commandActions": { + "description": "A best-effort parsing of the command to understand the action(s) it will perform. This returns a list of CommandAction objects because a single shell command may be composed of many commands piped together.", + "type": "array", + "items": { + "$ref": "#/definitions/CommandAction" + } + }, + "cwd": { + "description": "The command's working directory.", + "type": "string" + }, + "durationMs": { + "description": "The duration of the command execution in milliseconds.", + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "exitCode": { + "description": "The command's exit code.", + "type": [ + "integer", + "null" + ], + "format": "int32" + }, + "id": { + "type": "string" + }, + "processId": { + "description": "Identifier for the underlying PTY process (when available).", + "type": [ + "string", + "null" + ] + }, + "status": { + "$ref": "#/definitions/CommandExecutionStatus" + }, + "type": { + "type": "string", + "enum": [ + "commandExecution" + ], + "title": "CommandExecutionThreadItemType" + } + }, + "title": "CommandExecutionThreadItem" + }, + { + "type": "object", + "required": [ + "changes", + "id", + "status", + "type" + ], + "properties": { + "changes": { + "type": "array", + "items": { + "$ref": "#/definitions/FileUpdateChange" + } + }, + "id": { + "type": "string" + }, + "status": { + "$ref": "#/definitions/PatchApplyStatus" + }, + "type": { + "type": "string", + "enum": [ + "fileChange" + ], + "title": "FileChangeThreadItemType" + } + }, + "title": "FileChangeThreadItem" + }, + { + "type": "object", + "required": [ + "arguments", + "id", + "server", + "status", + "tool", + "type" + ], + "properties": { + "arguments": true, + "durationMs": { + "description": "The duration of the MCP tool call in milliseconds.", + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "error": { + "anyOf": [ + { + "$ref": "#/definitions/McpToolCallError" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "result": { + "anyOf": [ + { + "$ref": "#/definitions/McpToolCallResult" + }, + { + "type": "null" + } + ] + }, + "server": { + "type": "string" + }, + "status": { + "$ref": "#/definitions/McpToolCallStatus" + }, + "tool": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "mcpToolCall" + ], + "title": "McpToolCallThreadItemType" + } + }, + "title": "McpToolCallThreadItem" + }, + { + "type": "object", + "required": [ + "agentsStates", + "id", + "receiverThreadIds", + "senderThreadId", + "status", + "tool", + "type" + ], + "properties": { + "agentsStates": { + "description": "Last known status of the target agents, when available.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/CollabAgentState" + } + }, + "id": { + "description": "Unique identifier for this collab tool call.", + "type": "string" + }, + "prompt": { + "description": "Prompt text sent as part of the collab tool call, when available.", + "type": [ + "string", + "null" + ] + }, + "receiverThreadIds": { + "description": "Thread ID of the receiving agent, when applicable. In case of spawn operation, this corresponds to the newly spawned agent.", + "type": "array", + "items": { + "type": "string" + } + }, + "senderThreadId": { + "description": "Thread ID of the agent issuing the collab request.", + "type": "string" + }, + "status": { + "description": "Current status of the collab tool call.", + "allOf": [ + { + "$ref": "#/definitions/CollabAgentToolCallStatus" + } + ] + }, + "tool": { + "description": "Name of the collab tool that was invoked.", + "allOf": [ + { + "$ref": "#/definitions/CollabAgentTool" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "collabAgentToolCall" + ], + "title": "CollabAgentToolCallThreadItemType" + } + }, + "title": "CollabAgentToolCallThreadItem" + }, + { + "type": "object", + "required": [ + "id", + "query", + "type" + ], + "properties": { + "id": { + "type": "string" + }, + "query": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "webSearch" + ], + "title": "WebSearchThreadItemType" + } + }, + "title": "WebSearchThreadItem" + }, + { + "type": "object", + "required": [ + "id", + "path", + "type" + ], + "properties": { + "id": { + "type": "string" + }, + "path": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "imageView" + ], + "title": "ImageViewThreadItemType" + } + }, + "title": "ImageViewThreadItem" + }, + { + "type": "object", + "required": [ + "id", + "review", + "type" + ], + "properties": { + "id": { + "type": "string" + }, + "review": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "enteredReviewMode" + ], + "title": "EnteredReviewModeThreadItemType" + } + }, + "title": "EnteredReviewModeThreadItem" + }, + { + "type": "object", + "required": [ + "id", + "review", + "type" + ], + "properties": { + "id": { + "type": "string" + }, + "review": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "exitedReviewMode" + ], + "title": "ExitedReviewModeThreadItemType" + } + }, + "title": "ExitedReviewModeThreadItem" + } + ] + }, + "ThreadStartedNotification": { + "type": "object", + "required": [ + "thread" + ], + "properties": { + "thread": { + "$ref": "#/definitions/Thread" + } + } + }, + "ThreadTokenUsage": { + "type": "object", + "required": [ + "last", + "total" + ], + "properties": { + "last": { + "$ref": "#/definitions/TokenUsageBreakdown" + }, + "modelContextWindow": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total": { + "$ref": "#/definitions/TokenUsageBreakdown" + } + } + }, + "ThreadTokenUsageUpdatedNotification": { + "type": "object", + "required": [ + "threadId", + "tokenUsage", + "turnId" + ], + "properties": { + "threadId": { + "type": "string" + }, + "tokenUsage": { + "$ref": "#/definitions/ThreadTokenUsage" + }, + "turnId": { + "type": "string" + } + } + }, + "TokenUsageBreakdown": { + "type": "object", + "required": [ + "cachedInputTokens", + "inputTokens", + "outputTokens", + "reasoningOutputTokens", + "totalTokens" + ], + "properties": { + "cachedInputTokens": { + "type": "integer", + "format": "int64" + }, + "inputTokens": { + "type": "integer", + "format": "int64" + }, + "outputTokens": { + "type": "integer", + "format": "int64" + }, + "reasoningOutputTokens": { + "type": "integer", + "format": "int64" + }, + "totalTokens": { + "type": "integer", + "format": "int64" + } + } + }, + "Turn": { + "type": "object", + "required": [ + "id", + "items", + "status" + ], + "properties": { + "error": { + "description": "Only populated when the Turn's status is failed.", + "anyOf": [ + { + "$ref": "#/definitions/TurnError" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "items": { + "description": "Only populated on a `thread/resume` or `thread/fork` response. For all other responses and notifications returning a Turn, the items field will be an empty list.", + "type": "array", + "items": { + "$ref": "#/definitions/ThreadItem" + } + }, + "status": { + "$ref": "#/definitions/TurnStatus" + } + } + }, + "TurnCompletedNotification": { + "type": "object", + "required": [ + "threadId", + "turn" + ], + "properties": { + "threadId": { + "type": "string" + }, + "turn": { + "$ref": "#/definitions/Turn" + } + } + }, + "TurnDiffUpdatedNotification": { + "description": "Notification that the turn-level unified diff has changed. Contains the latest aggregated diff across all file changes in the turn.", + "type": "object", + "required": [ + "diff", + "threadId", + "turnId" + ], + "properties": { + "diff": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "TurnError": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "additionalDetails": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "codexErrorInfo": { + "anyOf": [ + { + "$ref": "#/definitions/CodexErrorInfo" + }, + { + "type": "null" + } + ] + }, + "message": { + "type": "string" + } + } + }, + "TurnPlanStep": { + "type": "object", + "required": [ + "status", + "step" + ], + "properties": { + "status": { + "$ref": "#/definitions/TurnPlanStepStatus" + }, + "step": { + "type": "string" + } + } + }, + "TurnPlanStepStatus": { + "type": "string", + "enum": [ + "pending", + "inProgress", + "completed" + ] + }, + "TurnPlanUpdatedNotification": { + "type": "object", + "required": [ + "plan", + "threadId", + "turnId" + ], + "properties": { + "explanation": { + "type": [ + "string", + "null" + ] + }, + "plan": { + "type": "array", + "items": { + "$ref": "#/definitions/TurnPlanStep" + } + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "TurnStartedNotification": { + "type": "object", + "required": [ + "threadId", + "turn" + ], + "properties": { + "threadId": { + "type": "string" + }, + "turn": { + "$ref": "#/definitions/Turn" + } + } + }, + "TurnStatus": { + "type": "string", + "enum": [ + "completed", + "interrupted", + "failed", + "inProgress" + ] + }, + "UserInput2": { + "description": "User input", + "oneOf": [ + { + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "text_elements": { + "description": "UI-defined spans within `text` that should be treated as special elements. These are byte ranges into the UTF-8 `text` buffer and are used to render or persist rich input markers (e.g., image placeholders) across history and resume without mutating the literal text.", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/TextElement2" + } + }, + "type": { + "type": "string", + "enum": [ + "text" + ], + "title": "TextUserInput2Type" + } + }, + "title": "TextUserInput2" + }, + { + "description": "Pre‑encoded data: URI image.", + "type": "object", + "required": [ + "image_url", + "type" + ], + "properties": { + "image_url": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "image" + ], + "title": "ImageUserInput2Type" + } + }, + "title": "ImageUserInput2" + }, + { + "description": "Local image path provided by the user. This will be converted to an `Image` variant (base64 data URL) during request serialization.", + "type": "object", + "required": [ + "path", + "type" + ], + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "local_image" + ], + "title": "LocalImageUserInput2Type" + } + }, + "title": "LocalImageUserInput2" + }, + { + "description": "Skill selected by the user (name + path to SKILL.md).", + "type": "object", + "required": [ + "name", + "path", + "type" + ], + "properties": { + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "skill" + ], + "title": "SkillUserInput2Type" + } + }, + "title": "SkillUserInput2" + } + ] + }, + "WindowsWorldWritableWarningNotification": { + "type": "object", + "required": [ + "extraCount", + "failedScan", + "samplePaths" + ], + "properties": { + "extraCount": { + "type": "integer", + "format": "uint", + "minimum": 0 + }, + "failedScan": { + "type": "boolean" + }, + "samplePaths": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "ApplyPatchApprovalParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ApplyPatchApprovalParams", + "type": "object", + "required": [ + "callId", + "conversationId", + "fileChanges" + ], + "properties": { + "callId": { + "description": "Use to correlate this with [codex_core::protocol::PatchApplyBeginEvent] and [codex_core::protocol::PatchApplyEndEvent].", + "type": "string" + }, + "conversationId": { + "$ref": "#/definitions/v2/ThreadId" + }, + "fileChanges": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/FileChange" + } + }, + "grantRoot": { + "description": "When set, the agent is asking the user to allow writes under this root for the remainder of the session (unclear if this is honored today).", + "type": [ + "string", + "null" + ] + }, + "reason": { + "description": "Optional explanatory reason (e.g. request for extra write access).", + "type": [ + "string", + "null" + ] + } + } + }, + "CommandExecutionRequestApprovalParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CommandExecutionRequestApprovalParams", + "type": "object", + "required": [ + "itemId", + "threadId", + "turnId" + ], + "properties": { + "command": { + "description": "The command to be executed.", + "type": [ + "string", + "null" + ] + }, + "commandActions": { + "description": "Best-effort parsed command actions for friendly display.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/v2/CommandAction" + } + }, + "cwd": { + "description": "The command's working directory.", + "type": [ + "string", + "null" + ] + }, + "itemId": { + "type": "string" + }, + "proposedExecpolicyAmendment": { + "description": "Optional proposed execpolicy amendment to allow similar commands without prompting.", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "reason": { + "description": "Optional explanatory reason (e.g. request for network access).", + "type": [ + "string", + "null" + ] + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "ExecCommandApprovalParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecCommandApprovalParams", + "type": "object", + "required": [ + "callId", + "command", + "conversationId", + "cwd", + "parsedCmd" + ], + "properties": { + "callId": { + "description": "Use to correlate this with [codex_core::protocol::ExecCommandBeginEvent] and [codex_core::protocol::ExecCommandEndEvent].", + "type": "string" + }, + "command": { + "type": "array", + "items": { + "type": "string" + } + }, + "conversationId": { + "$ref": "#/definitions/v2/ThreadId" + }, + "cwd": { + "type": "string" + }, + "parsedCmd": { + "type": "array", + "items": { + "$ref": "#/definitions/ParsedCommand" + } + }, + "reason": { + "type": [ + "string", + "null" + ] + } + } + }, + "ToolRequestUserInputOption": { + "description": "EXPERIMENTAL. Defines a single selectable option for request_user_input.", + "type": "object", + "required": [ + "description", + "label" + ], + "properties": { + "description": { + "type": "string" + }, + "label": { + "type": "string" + } + } + }, + "ToolRequestUserInputParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ToolRequestUserInputParams", + "description": "EXPERIMENTAL. Params sent with a request_user_input event.", + "type": "object", + "required": [ + "itemId", + "questions", + "threadId", + "turnId" + ], + "properties": { + "itemId": { + "type": "string" + }, + "questions": { + "type": "array", + "items": { + "$ref": "#/definitions/ToolRequestUserInputQuestion" + } + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "ToolRequestUserInputQuestion": { + "description": "EXPERIMENTAL. Represents one request_user_input question and its optional options.", + "type": "object", + "required": [ + "header", + "id", + "question" + ], + "properties": { + "header": { + "type": "string" + }, + "id": { + "type": "string" + }, + "options": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/ToolRequestUserInputOption" + } + }, + "question": { + "type": "string" + } + } + }, + "ToolRequestUserInputAnswer": { + "description": "EXPERIMENTAL. Captures a user's answer to a request_user_input question.", + "type": "object", + "required": [ + "answers" + ], + "properties": { + "answers": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "JSONRPCMessage": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "JSONRPCMessage", + "description": "Refers to any valid JSON-RPC object that can be decoded off the wire, or encoded to be sent.", + "anyOf": [ + { + "$ref": "#/definitions/JSONRPCRequest" + }, + { + "$ref": "#/definitions/JSONRPCNotification" + }, + { + "$ref": "#/definitions/JSONRPCResponse" + }, + { + "$ref": "#/definitions/JSONRPCError" + } + ] + }, + "ClientRequest": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ClientRequest", + "description": "Request from the client to the server.", + "oneOf": [ + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "initialize" + ], + "title": "InitializeRequestMethod" + }, + "params": { + "$ref": "#/definitions/InitializeParams" + } + }, + "title": "InitializeRequest" + }, + { + "description": "NEW APIs", + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "thread/start" + ], + "title": "Thread/startRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/ThreadStartParams" + } + }, + "title": "Thread/startRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "thread/resume" + ], + "title": "Thread/resumeRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/ThreadResumeParams" + } + }, + "title": "Thread/resumeRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "thread/fork" + ], + "title": "Thread/forkRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/ThreadForkParams" + } + }, + "title": "Thread/forkRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "thread/archive" + ], + "title": "Thread/archiveRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/ThreadArchiveParams" + } + }, + "title": "Thread/archiveRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "thread/rollback" + ], + "title": "Thread/rollbackRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/ThreadRollbackParams" + } + }, + "title": "Thread/rollbackRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "thread/list" + ], + "title": "Thread/listRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/ThreadListParams" + } + }, + "title": "Thread/listRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "thread/loaded/list" + ], + "title": "Thread/loaded/listRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/ThreadLoadedListParams" + } + }, + "title": "Thread/loaded/listRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "thread/read" + ], + "title": "Thread/readRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/ThreadReadParams" + } + }, + "title": "Thread/readRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "skills/list" + ], + "title": "Skills/listRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/SkillsListParams" + } + }, + "title": "Skills/listRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "app/list" + ], + "title": "App/listRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/AppsListParams" + } + }, + "title": "App/listRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "skills/config/write" + ], + "title": "Skills/config/writeRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/SkillsConfigWriteParams" + } + }, + "title": "Skills/config/writeRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "turn/start" + ], + "title": "Turn/startRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/TurnStartParams" + } + }, + "title": "Turn/startRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "turn/interrupt" + ], + "title": "Turn/interruptRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/TurnInterruptParams" + } + }, + "title": "Turn/interruptRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "review/start" + ], + "title": "Review/startRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/ReviewStartParams" + } + }, + "title": "Review/startRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "model/list" + ], + "title": "Model/listRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/ModelListParams" + } + }, + "title": "Model/listRequest" + }, + { + "description": "EXPERIMENTAL - list collaboration mode presets.", + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "collaborationMode/list" + ], + "title": "CollaborationMode/listRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/CollaborationModeListParams" + } + }, + "title": "CollaborationMode/listRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "mcpServer/oauth/login" + ], + "title": "McpServer/oauth/loginRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/McpServerOauthLoginParams" + } + }, + "title": "McpServer/oauth/loginRequest" + }, + { + "type": "object", + "required": [ + "id", + "method" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "config/mcpServer/reload" + ], + "title": "Config/mcpServer/reloadRequestMethod" + }, + "params": { + "type": "null" + } + }, + "title": "Config/mcpServer/reloadRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "mcpServerStatus/list" + ], + "title": "McpServerStatus/listRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/ListMcpServerStatusParams" + } + }, + "title": "McpServerStatus/listRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "account/login/start" + ], + "title": "Account/login/startRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/LoginAccountParams" + } + }, + "title": "Account/login/startRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "account/login/cancel" + ], + "title": "Account/login/cancelRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/CancelLoginAccountParams" + } + }, + "title": "Account/login/cancelRequest" + }, + { + "type": "object", + "required": [ + "id", + "method" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "account/logout" + ], + "title": "Account/logoutRequestMethod" + }, + "params": { + "type": "null" + } + }, + "title": "Account/logoutRequest" + }, + { + "type": "object", + "required": [ + "id", + "method" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "account/rateLimits/read" + ], + "title": "Account/rateLimits/readRequestMethod" + }, + "params": { + "type": "null" + } + }, + "title": "Account/rateLimits/readRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "feedback/upload" + ], + "title": "Feedback/uploadRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/FeedbackUploadParams" + } + }, + "title": "Feedback/uploadRequest" + }, + { + "description": "Execute a command (argv vector) under the server's sandbox.", + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "command/exec" + ], + "title": "Command/execRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/CommandExecParams" + } + }, + "title": "Command/execRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "config/read" + ], + "title": "Config/readRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/ConfigReadParams" + } + }, + "title": "Config/readRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "config/value/write" + ], + "title": "Config/value/writeRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/ConfigValueWriteParams" + } + }, + "title": "Config/value/writeRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "config/batchWrite" + ], + "title": "Config/batchWriteRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/ConfigBatchWriteParams" + } + }, + "title": "Config/batchWriteRequest" + }, + { + "type": "object", + "required": [ + "id", + "method" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "configRequirements/read" + ], + "title": "ConfigRequirements/readRequestMethod" + }, + "params": { + "type": "null" + } + }, + "title": "ConfigRequirements/readRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "account/read" + ], + "title": "Account/readRequestMethod" + }, + "params": { + "$ref": "#/definitions/v2/GetAccountParams" + } + }, + "title": "Account/readRequest" + }, + { + "description": "DEPRECATED APIs below", + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "newConversation" + ], + "title": "NewConversationRequestMethod" + }, + "params": { + "$ref": "#/definitions/NewConversationParams" + } + }, + "title": "NewConversationRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "getConversationSummary" + ], + "title": "GetConversationSummaryRequestMethod" + }, + "params": { + "$ref": "#/definitions/GetConversationSummaryParams" + } + }, + "title": "GetConversationSummaryRequest" + }, + { + "description": "List recorded Codex conversations (rollouts) with optional pagination and search.", + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "listConversations" + ], + "title": "ListConversationsRequestMethod" + }, + "params": { + "$ref": "#/definitions/ListConversationsParams" + } + }, + "title": "ListConversationsRequest" + }, + { + "description": "Resume a recorded Codex conversation from a rollout file.", + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "resumeConversation" + ], + "title": "ResumeConversationRequestMethod" + }, + "params": { + "$ref": "#/definitions/ResumeConversationParams" + } + }, + "title": "ResumeConversationRequest" + }, + { + "description": "Fork a recorded Codex conversation into a new session.", + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "forkConversation" + ], + "title": "ForkConversationRequestMethod" + }, + "params": { + "$ref": "#/definitions/ForkConversationParams" + } + }, + "title": "ForkConversationRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "archiveConversation" + ], + "title": "ArchiveConversationRequestMethod" + }, + "params": { + "$ref": "#/definitions/ArchiveConversationParams" + } + }, + "title": "ArchiveConversationRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "sendUserMessage" + ], + "title": "SendUserMessageRequestMethod" + }, + "params": { + "$ref": "#/definitions/SendUserMessageParams" + } + }, + "title": "SendUserMessageRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "sendUserTurn" + ], + "title": "SendUserTurnRequestMethod" + }, + "params": { + "$ref": "#/definitions/SendUserTurnParams" + } + }, + "title": "SendUserTurnRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "interruptConversation" + ], + "title": "InterruptConversationRequestMethod" + }, + "params": { + "$ref": "#/definitions/InterruptConversationParams" + } + }, + "title": "InterruptConversationRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "addConversationListener" + ], + "title": "AddConversationListenerRequestMethod" + }, + "params": { + "$ref": "#/definitions/AddConversationListenerParams" + } + }, + "title": "AddConversationListenerRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "removeConversationListener" + ], + "title": "RemoveConversationListenerRequestMethod" + }, + "params": { + "$ref": "#/definitions/RemoveConversationListenerParams" + } + }, + "title": "RemoveConversationListenerRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "gitDiffToRemote" + ], + "title": "GitDiffToRemoteRequestMethod" + }, + "params": { + "$ref": "#/definitions/GitDiffToRemoteParams" + } + }, + "title": "GitDiffToRemoteRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "loginApiKey" + ], + "title": "LoginApiKeyRequestMethod" + }, + "params": { + "$ref": "#/definitions/LoginApiKeyParams" + } + }, + "title": "LoginApiKeyRequest" + }, + { + "type": "object", + "required": [ + "id", + "method" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "loginChatGpt" + ], + "title": "LoginChatGptRequestMethod" + }, + "params": { + "type": "null" + } + }, + "title": "LoginChatGptRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "cancelLoginChatGpt" + ], + "title": "CancelLoginChatGptRequestMethod" + }, + "params": { + "$ref": "#/definitions/CancelLoginChatGptParams" + } + }, + "title": "CancelLoginChatGptRequest" + }, + { + "type": "object", + "required": [ + "id", + "method" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "logoutChatGpt" + ], + "title": "LogoutChatGptRequestMethod" + }, + "params": { + "type": "null" + } + }, + "title": "LogoutChatGptRequest" + }, + { + "description": "DEPRECATED in favor of GetAccount", + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "getAuthStatus" + ], + "title": "GetAuthStatusRequestMethod" + }, + "params": { + "$ref": "#/definitions/GetAuthStatusParams" + } + }, + "title": "GetAuthStatusRequest" + }, + { + "type": "object", + "required": [ + "id", + "method" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "getUserSavedConfig" + ], + "title": "GetUserSavedConfigRequestMethod" + }, + "params": { + "type": "null" + } + }, + "title": "GetUserSavedConfigRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "setDefaultModel" + ], + "title": "SetDefaultModelRequestMethod" + }, + "params": { + "$ref": "#/definitions/SetDefaultModelParams" + } + }, + "title": "SetDefaultModelRequest" + }, + { + "type": "object", + "required": [ + "id", + "method" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "getUserAgent" + ], + "title": "GetUserAgentRequestMethod" + }, + "params": { + "type": "null" + } + }, + "title": "GetUserAgentRequest" + }, + { + "type": "object", + "required": [ + "id", + "method" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "userInfo" + ], + "title": "UserInfoRequestMethod" + }, + "params": { + "type": "null" + } + }, + "title": "UserInfoRequest" + }, + { + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "fuzzyFileSearch" + ], + "title": "FuzzyFileSearchRequestMethod" + }, + "params": { + "$ref": "#/definitions/FuzzyFileSearchParams" + } + }, + "title": "FuzzyFileSearchRequest" + }, + { + "description": "Execute a command (argv vector) under the server's sandbox.", + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "execOneOffCommand" + ], + "title": "ExecOneOffCommandRequestMethod" + }, + "params": { + "$ref": "#/definitions/ExecOneOffCommandParams" + } + }, + "title": "ExecOneOffCommandRequest" + } + ] + }, + "ServerRequest": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ServerRequest", + "description": "Request initiated from the server and sent to the client.", + "oneOf": [ + { + "description": "NEW APIs Sent when approval is requested for a specific command execution. This request is used for Turns started via turn/start.", + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "item/commandExecution/requestApproval" + ], + "title": "Item/commandExecution/requestApprovalRequestMethod" + }, + "params": { + "$ref": "#/definitions/CommandExecutionRequestApprovalParams" + } + }, + "title": "Item/commandExecution/requestApprovalRequest" + }, + { + "description": "Sent when approval is requested for a specific file change. This request is used for Turns started via turn/start.", + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "item/fileChange/requestApproval" + ], + "title": "Item/fileChange/requestApprovalRequestMethod" + }, + "params": { + "$ref": "#/definitions/FileChangeRequestApprovalParams" + } + }, + "title": "Item/fileChange/requestApprovalRequest" + }, + { + "description": "EXPERIMENTAL - Request input from the user for a tool call.", + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "item/tool/requestUserInput" + ], + "title": "Item/tool/requestUserInputRequestMethod" + }, + "params": { + "$ref": "#/definitions/ToolRequestUserInputParams" + } + }, + "title": "Item/tool/requestUserInputRequest" + }, + { + "description": "DEPRECATED APIs below Request to approve a patch. This request is used for Turns started via the legacy APIs (i.e. SendUserTurn, SendUserMessage).", + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "applyPatchApproval" + ], + "title": "ApplyPatchApprovalRequestMethod" + }, + "params": { + "$ref": "#/definitions/ApplyPatchApprovalParams" + } + }, + "title": "ApplyPatchApprovalRequest" + }, + { + "description": "Request to exec a command. This request is used for Turns started via the legacy APIs (i.e. SendUserTurn, SendUserMessage).", + "type": "object", + "required": [ + "id", + "method", + "params" + ], + "properties": { + "id": { + "$ref": "#/definitions/RequestId" + }, + "method": { + "type": "string", + "enum": [ + "execCommandApproval" + ], + "title": "ExecCommandApprovalRequestMethod" + }, + "params": { + "$ref": "#/definitions/ExecCommandApprovalParams" + } + }, + "title": "ExecCommandApprovalRequest" + } + ] + }, + "ServerNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ServerNotification", + "description": "Notification sent from the server to the client.", + "oneOf": [ + { + "description": "NEW NOTIFICATIONS", + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "error" + ], + "title": "ErrorNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/ErrorNotification" + } + }, + "title": "ErrorNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "thread/started" + ], + "title": "Thread/startedNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/ThreadStartedNotification" + } + }, + "title": "Thread/startedNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "thread/tokenUsage/updated" + ], + "title": "Thread/tokenUsage/updatedNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/ThreadTokenUsageUpdatedNotification" + } + }, + "title": "Thread/tokenUsage/updatedNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "turn/started" + ], + "title": "Turn/startedNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/TurnStartedNotification" + } + }, + "title": "Turn/startedNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "turn/completed" + ], + "title": "Turn/completedNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/TurnCompletedNotification" + } + }, + "title": "Turn/completedNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "turn/diff/updated" + ], + "title": "Turn/diff/updatedNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/TurnDiffUpdatedNotification" + } + }, + "title": "Turn/diff/updatedNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "turn/plan/updated" + ], + "title": "Turn/plan/updatedNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/TurnPlanUpdatedNotification" + } + }, + "title": "Turn/plan/updatedNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "item/started" + ], + "title": "Item/startedNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/ItemStartedNotification" + } + }, + "title": "Item/startedNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "item/completed" + ], + "title": "Item/completedNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/ItemCompletedNotification" + } + }, + "title": "Item/completedNotification" + }, + { + "description": "This event is internal-only. Used by Codex Cloud.", + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "rawResponseItem/completed" + ], + "title": "RawResponseItem/completedNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/RawResponseItemCompletedNotification" + } + }, + "title": "RawResponseItem/completedNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "item/agentMessage/delta" + ], + "title": "Item/agentMessage/deltaNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/AgentMessageDeltaNotification" + } + }, + "title": "Item/agentMessage/deltaNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "item/commandExecution/outputDelta" + ], + "title": "Item/commandExecution/outputDeltaNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/CommandExecutionOutputDeltaNotification" + } + }, + "title": "Item/commandExecution/outputDeltaNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "item/commandExecution/terminalInteraction" + ], + "title": "Item/commandExecution/terminalInteractionNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/TerminalInteractionNotification" + } + }, + "title": "Item/commandExecution/terminalInteractionNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "item/fileChange/outputDelta" + ], + "title": "Item/fileChange/outputDeltaNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/FileChangeOutputDeltaNotification" + } + }, + "title": "Item/fileChange/outputDeltaNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "item/mcpToolCall/progress" + ], + "title": "Item/mcpToolCall/progressNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/McpToolCallProgressNotification" + } + }, + "title": "Item/mcpToolCall/progressNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "mcpServer/oauthLogin/completed" + ], + "title": "McpServer/oauthLogin/completedNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/McpServerOauthLoginCompletedNotification" + } + }, + "title": "McpServer/oauthLogin/completedNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "account/updated" + ], + "title": "Account/updatedNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/AccountUpdatedNotification" + } + }, + "title": "Account/updatedNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "account/rateLimits/updated" + ], + "title": "Account/rateLimits/updatedNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/AccountRateLimitsUpdatedNotification" + } + }, + "title": "Account/rateLimits/updatedNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "item/reasoning/summaryTextDelta" + ], + "title": "Item/reasoning/summaryTextDeltaNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/ReasoningSummaryTextDeltaNotification" + } + }, + "title": "Item/reasoning/summaryTextDeltaNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "item/reasoning/summaryPartAdded" + ], + "title": "Item/reasoning/summaryPartAddedNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/ReasoningSummaryPartAddedNotification" + } + }, + "title": "Item/reasoning/summaryPartAddedNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "item/reasoning/textDelta" + ], + "title": "Item/reasoning/textDeltaNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/ReasoningTextDeltaNotification" + } + }, + "title": "Item/reasoning/textDeltaNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "thread/compacted" + ], + "title": "Thread/compactedNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/ContextCompactedNotification" + } + }, + "title": "Thread/compactedNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "deprecationNotice" + ], + "title": "DeprecationNoticeNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/DeprecationNoticeNotification" + } + }, + "title": "DeprecationNoticeNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "configWarning" + ], + "title": "ConfigWarningNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/ConfigWarningNotification" + } + }, + "title": "ConfigWarningNotification" + }, + { + "description": "Notifies the user of world-writable directories on Windows, which cannot be protected by the sandbox.", + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "windows/worldWritableWarning" + ], + "title": "Windows/worldWritableWarningNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/WindowsWorldWritableWarningNotification" + } + }, + "title": "Windows/worldWritableWarningNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "account/login/completed" + ], + "title": "Account/login/completedNotificationMethod" + }, + "params": { + "$ref": "#/definitions/v2/AccountLoginCompletedNotification" + } + }, + "title": "Account/login/completedNotification" + }, + { + "description": "DEPRECATED NOTIFICATIONS below", + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "authStatusChange" + ], + "title": "AuthStatusChangeNotificationMethod" + }, + "params": { + "$ref": "#/definitions/AuthStatusChangeNotification" + } + }, + "title": "AuthStatusChangeNotification" + }, + { + "description": "Deprecated: use `account/login/completed` instead.", + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "loginChatGptComplete" + ], + "title": "LoginChatGptCompleteNotificationMethod" + }, + "params": { + "$ref": "#/definitions/LoginChatGptCompleteNotification" + } + }, + "title": "LoginChatGptCompleteNotification" + }, + { + "type": "object", + "required": [ + "method", + "params" + ], + "properties": { + "method": { + "type": "string", + "enum": [ + "sessionConfigured" + ], + "title": "SessionConfiguredNotificationMethod" + }, + "params": { + "$ref": "#/definitions/SessionConfiguredNotification" + } + }, + "title": "SessionConfiguredNotification" + } + ] + }, + "v2": { + "AskForApproval": { + "type": "string", + "enum": [ + "untrusted", + "on-failure", + "on-request", + "never" + ] + }, + "Personality": { + "type": "string", + "enum": [ + "friendly", + "pragmatic" + ] + }, + "SandboxMode": { + "type": "string", + "enum": [ + "read-only", + "workspace-write", + "danger-full-access" + ] + }, + "ThreadStartParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadStartParams", + "type": "object", + "properties": { + "approvalPolicy": { + "anyOf": [ + { + "$ref": "#/definitions/v2/AskForApproval" + }, + { + "type": "null" + } + ] + }, + "baseInstructions": { + "type": [ + "string", + "null" + ] + }, + "config": { + "type": [ + "object", + "null" + ], + "additionalProperties": true + }, + "cwd": { + "type": [ + "string", + "null" + ] + }, + "developerInstructions": { + "type": [ + "string", + "null" + ] + }, + "ephemeral": { + "type": [ + "boolean", + "null" + ] + }, + "experimentalRawEvents": { + "description": "If true, opt into emitting raw response items on the event stream.\n\nThis is for internal use only (e.g. Codex Cloud). (TODO): Figure out a better way to categorize internal / experimental events & protocols.", + "default": false, + "type": "boolean" + }, + "model": { + "type": [ + "string", + "null" + ] + }, + "modelProvider": { + "type": [ + "string", + "null" + ] + }, + "personality": { + "anyOf": [ + { + "$ref": "#/definitions/v2/Personality" + }, + { + "type": "null" + } + ] + }, + "sandbox": { + "anyOf": [ + { + "$ref": "#/definitions/v2/SandboxMode" + }, + { + "type": "null" + } + ] + } + } + }, + "ContentItem": { + "oneOf": [ + { + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "input_text" + ], + "title": "InputTextContentItemType" + } + }, + "title": "InputTextContentItem" + }, + { + "type": "object", + "required": [ + "image_url", + "type" + ], + "properties": { + "image_url": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "input_image" + ], + "title": "InputImageContentItemType" + } + }, + "title": "InputImageContentItem" + }, + { + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "output_text" + ], + "title": "OutputTextContentItemType" + } + }, + "title": "OutputTextContentItem" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType" + } + }, + "title": "InputTextFunctionCallOutputContentItem" + }, + { + "type": "object", + "required": [ + "image_url", + "type" + ], + "properties": { + "image_url": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType" + } + }, + "title": "InputImageFunctionCallOutputContentItem" + } + ] + }, + "FunctionCallOutputPayload": { + "description": "The payload we send back to OpenAI when reporting a tool call result.\n\n`content` preserves the historical plain-string payload so downstream integrations (tests, logging, etc.) can keep treating tool output as `String`. When an MCP server returns richer data we additionally populate `content_items` with the structured form that the Responses/Chat Completions APIs understand.", + "type": "object", + "required": [ + "content" + ], + "properties": { + "content": { + "type": "string" + }, + "content_items": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/v2/FunctionCallOutputContentItem" + } + }, + "success": { + "type": [ + "boolean", + "null" + ] + } + } + }, + "GhostCommit": { + "description": "Details of a ghost commit created from a repository state.", + "type": "object", + "required": [ + "id", + "preexisting_untracked_dirs", + "preexisting_untracked_files" + ], + "properties": { + "id": { + "type": "string" + }, + "parent": { + "type": [ + "string", + "null" + ] + }, + "preexisting_untracked_dirs": { + "type": "array", + "items": { + "type": "string" + } + }, + "preexisting_untracked_files": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "LocalShellAction": { + "oneOf": [ + { + "type": "object", + "required": [ + "command", + "type" + ], + "properties": { + "command": { + "type": "array", + "items": { + "type": "string" + } + }, + "env": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "string" + } + }, + "timeout_ms": { + "type": [ + "integer", + "null" + ], + "format": "uint64", + "minimum": 0 + }, + "type": { + "type": "string", + "enum": [ + "exec" + ], + "title": "ExecLocalShellActionType" + }, + "user": { + "type": [ + "string", + "null" + ] + }, + "working_directory": { + "type": [ + "string", + "null" + ] + } + }, + "title": "ExecLocalShellAction" + } + ] + }, + "LocalShellStatus": { + "type": "string", + "enum": [ + "completed", + "in_progress", + "incomplete" + ] + }, + "ReasoningItemContent": { + "oneOf": [ + { + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "reasoning_text" + ], + "title": "ReasoningTextReasoningItemContentType" + } + }, + "title": "ReasoningTextReasoningItemContent" + }, + { + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "text" + ], + "title": "TextReasoningItemContentType" + } + }, + "title": "TextReasoningItemContent" + } + ] + }, + "ReasoningItemReasoningSummary": { + "oneOf": [ + { + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "summary_text" + ], + "title": "SummaryTextReasoningItemReasoningSummaryType" + } + }, + "title": "SummaryTextReasoningItemReasoningSummary" + } + ] + }, + "ResponseItem": { + "oneOf": [ + { + "type": "object", + "required": [ + "content", + "role", + "type" + ], + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/ContentItem" + } + }, + "end_turn": { + "type": [ + "boolean", + "null" + ] + }, + "id": { + "writeOnly": true, + "type": [ + "string", + "null" + ] + }, + "role": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "message" + ], + "title": "MessageResponseItemType" + } + }, + "title": "MessageResponseItem" + }, + { + "type": "object", + "required": [ + "id", + "summary", + "type" + ], + "properties": { + "content": { + "default": null, + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/v2/ReasoningItemContent" + } + }, + "encrypted_content": { + "type": [ + "string", + "null" + ] + }, + "id": { + "writeOnly": true, + "type": "string" + }, + "summary": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/ReasoningItemReasoningSummary" + } + }, + "type": { + "type": "string", + "enum": [ + "reasoning" + ], + "title": "ReasoningResponseItemType" + } + }, + "title": "ReasoningResponseItem" + }, + { + "type": "object", + "required": [ + "action", + "status", + "type" + ], + "properties": { + "action": { + "$ref": "#/definitions/v2/LocalShellAction" + }, + "call_id": { + "description": "Set when using the Responses API.", + "type": [ + "string", + "null" + ] + }, + "id": { + "description": "Set when using the chat completions API.", + "writeOnly": true, + "type": [ + "string", + "null" + ] + }, + "status": { + "$ref": "#/definitions/v2/LocalShellStatus" + }, + "type": { + "type": "string", + "enum": [ + "local_shell_call" + ], + "title": "LocalShellCallResponseItemType" + } + }, + "title": "LocalShellCallResponseItem" + }, + { + "type": "object", + "required": [ + "arguments", + "call_id", + "name", + "type" + ], + "properties": { + "arguments": { + "type": "string" + }, + "call_id": { + "type": "string" + }, + "id": { + "writeOnly": true, + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "function_call" + ], + "title": "FunctionCallResponseItemType" + } + }, + "title": "FunctionCallResponseItem" + }, + { + "type": "object", + "required": [ + "call_id", + "output", + "type" + ], + "properties": { + "call_id": { + "type": "string" + }, + "output": { + "$ref": "#/definitions/v2/FunctionCallOutputPayload" + }, + "type": { + "type": "string", + "enum": [ + "function_call_output" + ], + "title": "FunctionCallOutputResponseItemType" + } + }, + "title": "FunctionCallOutputResponseItem" + }, + { + "type": "object", + "required": [ + "call_id", + "input", + "name", + "type" + ], + "properties": { + "call_id": { + "type": "string" + }, + "id": { + "writeOnly": true, + "type": [ + "string", + "null" + ] + }, + "input": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "custom_tool_call" + ], + "title": "CustomToolCallResponseItemType" + } + }, + "title": "CustomToolCallResponseItem" + }, + { + "type": "object", + "required": [ + "call_id", + "output", + "type" + ], + "properties": { + "call_id": { + "type": "string" + }, + "output": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "custom_tool_call_output" + ], + "title": "CustomToolCallOutputResponseItemType" + } + }, + "title": "CustomToolCallOutputResponseItem" + }, + { + "type": "object", + "required": [ + "action", + "type" + ], + "properties": { + "action": { + "$ref": "#/definitions/v2/WebSearchAction" + }, + "id": { + "writeOnly": true, + "type": [ + "string", + "null" + ] + }, + "status": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "web_search_call" + ], + "title": "WebSearchCallResponseItemType" + } + }, + "title": "WebSearchCallResponseItem" + }, + { + "type": "object", + "required": [ + "ghost_commit", + "type" + ], + "properties": { + "ghost_commit": { + "$ref": "#/definitions/v2/GhostCommit" + }, + "type": { + "type": "string", + "enum": [ + "ghost_snapshot" + ], + "title": "GhostSnapshotResponseItemType" + } + }, + "title": "GhostSnapshotResponseItem" + }, + { + "type": "object", + "required": [ + "encrypted_content", + "type" + ], + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "compaction" + ], + "title": "CompactionResponseItemType" + } + }, + "title": "CompactionResponseItem" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "other" + ], + "title": "OtherResponseItemType" + } + }, + "title": "OtherResponseItem" + } + ] + }, + "WebSearchAction": { + "oneOf": [ + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "query": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "search" + ], + "title": "SearchWebSearchActionType" + } + }, + "title": "SearchWebSearchAction" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "open_page" + ], + "title": "OpenPageWebSearchActionType" + }, + "url": { + "type": [ + "string", + "null" + ] + } + }, + "title": "OpenPageWebSearchAction" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "pattern": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "find_in_page" + ], + "title": "FindInPageWebSearchActionType" + }, + "url": { + "type": [ + "string", + "null" + ] + } + }, + "title": "FindInPageWebSearchAction" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "other" + ], + "title": "OtherWebSearchActionType" + } + }, + "title": "OtherWebSearchAction" + } + ] + }, + "ThreadResumeParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadResumeParams", + "description": "There are three ways to resume a thread: 1. By thread_id: load the thread from disk by thread_id and resume it. 2. By history: instantiate the thread from memory and resume it. 3. By path: load the thread from disk by path and resume it.\n\nThe precedence is: history > path > thread_id. If using history or path, the thread_id param will be ignored.\n\nPrefer using thread_id whenever possible.", + "type": "object", + "required": [ + "threadId" + ], + "properties": { + "approvalPolicy": { + "anyOf": [ + { + "$ref": "#/definitions/v2/AskForApproval" + }, + { + "type": "null" + } + ] + }, + "baseInstructions": { + "type": [ + "string", + "null" + ] + }, + "config": { + "type": [ + "object", + "null" + ], + "additionalProperties": true + }, + "cwd": { + "type": [ + "string", + "null" + ] + }, + "developerInstructions": { + "type": [ + "string", + "null" + ] + }, + "history": { + "description": "[UNSTABLE] FOR CODEX CLOUD - DO NOT USE. If specified, the thread will be resumed with the provided history instead of loaded from disk.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/v2/ResponseItem" + } + }, + "model": { + "description": "Configuration overrides for the resumed thread, if any.", + "type": [ + "string", + "null" + ] + }, + "modelProvider": { + "type": [ + "string", + "null" + ] + }, + "path": { + "description": "[UNSTABLE] Specify the rollout path to resume from. If specified, the thread_id param will be ignored.", + "type": [ + "string", + "null" + ] + }, + "personality": { + "anyOf": [ + { + "$ref": "#/definitions/v2/Personality" + }, + { + "type": "null" + } + ] + }, + "sandbox": { + "anyOf": [ + { + "$ref": "#/definitions/v2/SandboxMode" + }, + { + "type": "null" + } + ] + }, + "threadId": { + "type": "string" + } + } + }, + "ThreadForkParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadForkParams", + "description": "There are two ways to fork a thread: 1. By thread_id: load the thread from disk by thread_id and fork it into a new thread. 2. By path: load the thread from disk by path and fork it into a new thread.\n\nIf using path, the thread_id param will be ignored.\n\nPrefer using thread_id whenever possible.", + "type": "object", + "required": [ + "threadId" + ], + "properties": { + "approvalPolicy": { + "anyOf": [ + { + "$ref": "#/definitions/v2/AskForApproval" + }, + { + "type": "null" + } + ] + }, + "baseInstructions": { + "type": [ + "string", + "null" + ] + }, + "config": { + "type": [ + "object", + "null" + ], + "additionalProperties": true + }, + "cwd": { + "type": [ + "string", + "null" + ] + }, + "developerInstructions": { + "type": [ + "string", + "null" + ] + }, + "model": { + "description": "Configuration overrides for the forked thread, if any.", + "type": [ + "string", + "null" + ] + }, + "modelProvider": { + "type": [ + "string", + "null" + ] + }, + "path": { + "description": "[UNSTABLE] Specify the rollout path to fork from. If specified, the thread_id param will be ignored.", + "type": [ + "string", + "null" + ] + }, + "sandbox": { + "anyOf": [ + { + "$ref": "#/definitions/v2/SandboxMode" + }, + { + "type": "null" + } + ] + }, + "threadId": { + "type": "string" + } + } + }, + "ThreadArchiveParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadArchiveParams", + "type": "object", + "required": [ + "threadId" + ], + "properties": { + "threadId": { + "type": "string" + } + } + }, + "ThreadRollbackParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadRollbackParams", + "type": "object", + "required": [ + "numTurns", + "threadId" + ], + "properties": { + "numTurns": { + "description": "The number of turns to drop from the end of the thread. Must be >= 1.\n\nThis only modifies the thread's history and does not revert local file changes that have been made by the agent. Clients are responsible for reverting these changes.", + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "threadId": { + "type": "string" + } + } + }, + "ThreadSortKey": { + "type": "string", + "enum": [ + "created_at", + "updated_at" + ] + }, + "ThreadListParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadListParams", + "type": "object", + "properties": { + "archived": { + "description": "Optional archived filter; when set to true, only archived threads are returned. If false or null, only non-archived threads are returned.", + "type": [ + "boolean", + "null" + ] + }, + "cursor": { + "description": "Opaque pagination cursor returned by a previous call.", + "type": [ + "string", + "null" + ] + }, + "limit": { + "description": "Optional page size; defaults to a reasonable server-side value.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0 + }, + "modelProviders": { + "description": "Optional provider filter; when set, only sessions recorded under these providers are returned. When present but empty, includes all providers.", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "sortKey": { + "description": "Optional sort key; defaults to created_at.", + "anyOf": [ + { + "$ref": "#/definitions/v2/ThreadSortKey" + }, + { + "type": "null" + } + ] + } + } + }, + "ThreadLoadedListParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadLoadedListParams", + "type": "object", + "properties": { + "cursor": { + "description": "Opaque pagination cursor returned by a previous call.", + "type": [ + "string", + "null" + ] + }, + "limit": { + "description": "Optional page size; defaults to no limit.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0 + } + } + }, + "ThreadReadParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadReadParams", + "type": "object", + "required": [ + "threadId" + ], + "properties": { + "includeTurns": { + "description": "When true, include turns and their items from rollout history.", + "default": false, + "type": "boolean" + }, + "threadId": { + "type": "string" + } + } + }, + "SkillsListParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "SkillsListParams", + "type": "object", + "properties": { + "cwds": { + "description": "When empty, defaults to the current session working directory.", + "type": "array", + "items": { + "type": "string" + } + }, + "forceReload": { + "description": "When true, bypass the skills cache and re-scan skills from disk.", + "type": "boolean" + } + } + }, + "AppsListParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AppsListParams", + "type": "object", + "properties": { + "cursor": { + "description": "Opaque pagination cursor returned by a previous call.", + "type": [ + "string", + "null" + ] + }, + "limit": { + "description": "Optional page size; defaults to a reasonable server-side value.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0 + } + } + }, + "SkillsConfigWriteParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "SkillsConfigWriteParams", + "type": "object", + "required": [ + "enabled", + "path" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "path": { + "type": "string" + } + } + }, + "AbsolutePathBuf": { + "description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.", + "type": "string" + }, + "ByteRange": { + "type": "object", + "required": [ + "end", + "start" + ], + "properties": { + "end": { + "type": "integer", + "format": "uint", + "minimum": 0 + }, + "start": { + "type": "integer", + "format": "uint", + "minimum": 0 + } + } + }, + "CollaborationMode": { + "description": "Collaboration mode for a Codex session.", + "type": "object", + "required": [ + "mode", + "settings" + ], + "properties": { + "mode": { + "$ref": "#/definitions/v2/ModeKind" + }, + "settings": { + "$ref": "#/definitions/v2/Settings" + } + } + }, + "ModeKind": { + "description": "Initial collaboration mode to use when the TUI starts.", + "type": "string", + "enum": [ + "plan", + "code", + "pair_programming", + "execute", + "custom" + ] + }, + "NetworkAccess": { + "type": "string", + "enum": [ + "restricted", + "enabled" + ] + }, + "ReasoningEffort": { + "description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning", + "type": "string", + "enum": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ] + }, + "ReasoningSummary": { + "description": "A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process. See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries", + "oneOf": [ + { + "type": "string", + "enum": [ + "auto", + "concise", + "detailed" + ] + }, + { + "description": "Option to disable reasoning summaries.", + "type": "string", + "enum": [ + "none" + ] + } + ] + }, + "SandboxPolicy": { + "oneOf": [ + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "dangerFullAccess" + ], + "title": "DangerFullAccessSandboxPolicyType" + } + }, + "title": "DangerFullAccessSandboxPolicy" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "readOnly" + ], + "title": "ReadOnlySandboxPolicyType" + } + }, + "title": "ReadOnlySandboxPolicy" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "networkAccess": { + "default": "restricted", + "allOf": [ + { + "$ref": "#/definitions/v2/NetworkAccess" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "externalSandbox" + ], + "title": "ExternalSandboxSandboxPolicyType" + } + }, + "title": "ExternalSandboxSandboxPolicy" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "excludeSlashTmp": { + "default": false, + "type": "boolean" + }, + "excludeTmpdirEnvVar": { + "default": false, + "type": "boolean" + }, + "networkAccess": { + "default": false, + "type": "boolean" + }, + "type": { + "type": "string", + "enum": [ + "workspaceWrite" + ], + "title": "WorkspaceWriteSandboxPolicyType" + }, + "writableRoots": { + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/v2/AbsolutePathBuf" + } + } + }, + "title": "WorkspaceWriteSandboxPolicy" + } + ] + }, + "Settings": { + "description": "Settings for a collaboration mode.", + "type": "object", + "required": [ + "model" + ], + "properties": { + "developer_instructions": { + "type": [ + "string", + "null" + ] + }, + "model": { + "type": "string" + }, + "reasoning_effort": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ReasoningEffort" + }, + { + "type": "null" + } + ] + } + } + }, + "TextElement": { + "type": "object", + "required": [ + "byteRange" + ], + "properties": { + "byteRange": { + "description": "Byte range in the parent `text` buffer that this element occupies.", + "allOf": [ + { + "$ref": "#/definitions/v2/ByteRange" + } + ] + }, + "placeholder": { + "description": "Optional human-readable placeholder for the element, displayed in the UI.", + "type": [ + "string", + "null" + ] + } + } + }, + "UserInput": { + "oneOf": [ + { + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "text_elements": { + "description": "UI-defined spans within `text` used to render or persist special elements.", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/v2/TextElement" + } + }, + "type": { + "type": "string", + "enum": [ + "text" + ], + "title": "TextUserInputType" + } + }, + "title": "TextUserInput" + }, + { + "type": "object", + "required": [ + "type", + "url" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "image" + ], + "title": "ImageUserInputType" + }, + "url": { + "type": "string" + } + }, + "title": "ImageUserInput" + }, + { + "type": "object", + "required": [ + "path", + "type" + ], + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "localImage" + ], + "title": "LocalImageUserInputType" + } + }, + "title": "LocalImageUserInput" + }, + { + "type": "object", + "required": [ + "name", + "path", + "type" + ], + "properties": { + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "skill" + ], + "title": "SkillUserInputType" + } + }, + "title": "SkillUserInput" + } + ] + }, + "TurnStartParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TurnStartParams", + "type": "object", + "required": [ + "input", + "threadId" + ], + "properties": { + "approvalPolicy": { + "description": "Override the approval policy for this turn and subsequent turns.", + "anyOf": [ + { + "$ref": "#/definitions/v2/AskForApproval" + }, + { + "type": "null" + } + ] + }, + "collaborationMode": { + "description": "EXPERIMENTAL - set a pre-set collaboration mode. Takes precedence over model, reasoning_effort, and developer instructions if set.", + "anyOf": [ + { + "$ref": "#/definitions/v2/CollaborationMode" + }, + { + "type": "null" + } + ] + }, + "cwd": { + "description": "Override the working directory for this turn and subsequent turns.", + "type": [ + "string", + "null" + ] + }, + "effort": { + "description": "Override the reasoning effort for this turn and subsequent turns.", + "anyOf": [ + { + "$ref": "#/definitions/v2/ReasoningEffort" + }, + { + "type": "null" + } + ] + }, + "input": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/UserInput" + } + }, + "model": { + "description": "Override the model for this turn and subsequent turns.", + "type": [ + "string", + "null" + ] + }, + "outputSchema": { + "description": "Optional JSON Schema used to constrain the final assistant message for this turn." + }, + "personality": { + "description": "Override the personality for this turn and subsequent turns.", + "anyOf": [ + { + "$ref": "#/definitions/v2/Personality" + }, + { + "type": "null" + } + ] + }, + "sandboxPolicy": { + "description": "Override the sandbox policy for this turn and subsequent turns.", + "anyOf": [ + { + "$ref": "#/definitions/v2/SandboxPolicy" + }, + { + "type": "null" + } + ] + }, + "summary": { + "description": "Override the reasoning summary for this turn and subsequent turns.", + "anyOf": [ + { + "$ref": "#/definitions/v2/ReasoningSummary" + }, + { + "type": "null" + } + ] + }, + "threadId": { + "type": "string" + } + } + }, + "TurnInterruptParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TurnInterruptParams", + "type": "object", + "required": [ + "threadId", + "turnId" + ], + "properties": { + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "ReviewDelivery": { + "type": "string", + "enum": [ + "inline", + "detached" + ] + }, + "ReviewTarget": { + "oneOf": [ + { + "description": "Review the working tree: staged, unstaged, and untracked files.", + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "uncommittedChanges" + ], + "title": "UncommittedChangesReviewTargetType" + } + }, + "title": "UncommittedChangesReviewTarget" + }, + { + "description": "Review changes between the current branch and the given base branch.", + "type": "object", + "required": [ + "branch", + "type" + ], + "properties": { + "branch": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "baseBranch" + ], + "title": "BaseBranchReviewTargetType" + } + }, + "title": "BaseBranchReviewTarget" + }, + { + "description": "Review the changes introduced by a specific commit.", + "type": "object", + "required": [ + "sha", + "type" + ], + "properties": { + "sha": { + "type": "string" + }, + "title": { + "description": "Optional human-readable label (e.g., commit subject) for UIs.", + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "commit" + ], + "title": "CommitReviewTargetType" + } + }, + "title": "CommitReviewTarget" + }, + { + "description": "Arbitrary instructions, equivalent to the old free-form prompt.", + "type": "object", + "required": [ + "instructions", + "type" + ], + "properties": { + "instructions": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "custom" + ], + "title": "CustomReviewTargetType" + } + }, + "title": "CustomReviewTarget" + } + ] + }, + "ReviewStartParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ReviewStartParams", + "type": "object", + "required": [ + "target", + "threadId" + ], + "properties": { + "delivery": { + "description": "Where to run the review: inline (default) on the current thread or detached on a new thread (returned in `reviewThreadId`).", + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/v2/ReviewDelivery" + }, + { + "type": "null" + } + ] + }, + "target": { + "$ref": "#/definitions/v2/ReviewTarget" + }, + "threadId": { + "type": "string" + } + } + }, + "ModelListParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ModelListParams", + "type": "object", + "properties": { + "cursor": { + "description": "Opaque pagination cursor returned by a previous call.", + "type": [ + "string", + "null" + ] + }, + "limit": { + "description": "Optional page size; defaults to a reasonable server-side value.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0 + } + } + }, + "CollaborationModeListParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CollaborationModeListParams", + "description": "EXPERIMENTAL - list collaboration mode presets.", + "type": "object" + }, + "McpServerOauthLoginParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "McpServerOauthLoginParams", + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "scopes": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "timeoutSecs": { + "type": [ + "integer", + "null" + ], + "format": "int64" + } + } + }, + "ListMcpServerStatusParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ListMcpServerStatusParams", + "type": "object", + "properties": { + "cursor": { + "description": "Opaque pagination cursor returned by a previous call.", + "type": [ + "string", + "null" + ] + }, + "limit": { + "description": "Optional page size; defaults to a server-defined value.", + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0 + } + } + }, + "LoginAccountParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "LoginAccountParams", + "oneOf": [ + { + "type": "object", + "required": [ + "apiKey", + "type" + ], + "properties": { + "apiKey": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "apiKey" + ], + "title": "ApiKeyv2::LoginAccountParamsType" + } + }, + "title": "ApiKeyv2::LoginAccountParams" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "chatgpt" + ], + "title": "Chatgptv2::LoginAccountParamsType" + } + }, + "title": "Chatgptv2::LoginAccountParams" + } + ] + }, + "CancelLoginAccountParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CancelLoginAccountParams", + "type": "object", + "required": [ + "loginId" + ], + "properties": { + "loginId": { + "type": "string" + } + } + }, + "FeedbackUploadParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "FeedbackUploadParams", + "type": "object", + "required": [ + "classification", + "includeLogs" + ], + "properties": { + "classification": { + "type": "string" + }, + "includeLogs": { + "type": "boolean" + }, + "reason": { + "type": [ + "string", + "null" + ] + }, + "threadId": { + "type": [ + "string", + "null" + ] + } + } + }, + "CommandExecParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CommandExecParams", + "type": "object", + "required": [ + "command" + ], + "properties": { + "command": { + "type": "array", + "items": { + "type": "string" + } + }, + "cwd": { + "type": [ + "string", + "null" + ] + }, + "sandboxPolicy": { + "anyOf": [ + { + "$ref": "#/definitions/v2/SandboxPolicy" + }, + { + "type": "null" + } + ] + }, + "timeoutMs": { + "type": [ + "integer", + "null" + ], + "format": "int64" + } + } + }, + "ConfigReadParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigReadParams", + "type": "object", + "properties": { + "cwd": { + "description": "Optional working directory to resolve project config layers. If specified, return the effective config as seen from that directory (i.e., including any project layers between `cwd` and the project/repo root).", + "type": [ + "string", + "null" + ] + }, + "includeLayers": { + "default": false, + "type": "boolean" + } + } + }, + "MergeStrategy": { + "type": "string", + "enum": [ + "replace", + "upsert" + ] + }, + "ConfigValueWriteParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigValueWriteParams", + "type": "object", + "required": [ + "keyPath", + "mergeStrategy", + "value" + ], + "properties": { + "expectedVersion": { + "type": [ + "string", + "null" + ] + }, + "filePath": { + "description": "Path to the config file to write; defaults to the user's `config.toml` when omitted.", + "type": [ + "string", + "null" + ] + }, + "keyPath": { + "type": "string" + }, + "mergeStrategy": { + "$ref": "#/definitions/v2/MergeStrategy" + }, + "value": true + } + }, + "ConfigEdit": { + "type": "object", + "required": [ + "keyPath", + "mergeStrategy", + "value" + ], + "properties": { + "keyPath": { + "type": "string" + }, + "mergeStrategy": { + "$ref": "#/definitions/v2/MergeStrategy" + }, + "value": true + } + }, + "ConfigBatchWriteParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigBatchWriteParams", + "type": "object", + "required": [ + "edits" + ], + "properties": { + "edits": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/ConfigEdit" + } + }, + "expectedVersion": { + "type": [ + "string", + "null" + ] + }, + "filePath": { + "description": "Path to the config file to write; defaults to the user's `config.toml` when omitted.", + "type": [ + "string", + "null" + ] + } + } + }, + "GetAccountParams": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GetAccountParams", + "type": "object", + "properties": { + "refreshToken": { + "default": false, + "type": "boolean" + } + } + }, + "Annotations": { + "description": "Optional annotations for the client. The client can use annotations to inform how objects are used or displayed", + "type": "object", + "properties": { + "audience": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/v2/Role" + } + }, + "lastModified": { + "type": [ + "string", + "null" + ] + }, + "priority": { + "type": [ + "number", + "null" + ], + "format": "double" + } + } + }, + "AudioContent": { + "description": "Audio provided to or from an LLM.", + "type": "object", + "required": [ + "data", + "mimeType", + "type" + ], + "properties": { + "annotations": { + "anyOf": [ + { + "$ref": "#/definitions/v2/Annotations" + }, + { + "type": "null" + } + ] + }, + "data": { + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "BlobResourceContents": { + "type": "object", + "required": [ + "blob", + "uri" + ], + "properties": { + "blob": { + "type": "string" + }, + "mimeType": { + "type": [ + "string", + "null" + ] + }, + "uri": { + "type": "string" + } + } + }, + "CodexErrorInfo": { + "description": "This translation layer make sure that we expose codex error code in camel case.\n\nWhen an upstream HTTP status is available (for example, from the Responses API or a provider), it is forwarded in `httpStatusCode` on the relevant `codexErrorInfo` variant.", + "oneOf": [ + { + "type": "string", + "enum": [ + "contextWindowExceeded", + "usageLimitExceeded", + "internalServerError", + "unauthorized", + "badRequest", + "threadRollbackFailed", + "sandboxError", + "other" + ] + }, + { + "type": "object", + "required": [ + "httpConnectionFailed" + ], + "properties": { + "httpConnectionFailed": { + "type": "object", + "properties": { + "httpStatusCode": { + "type": [ + "integer", + "null" + ], + "format": "uint16", + "minimum": 0 + } + } + } + }, + "additionalProperties": false, + "title": "HttpConnectionFailedCodexErrorInfo" + }, + { + "description": "Failed to connect to the response SSE stream.", + "type": "object", + "required": [ + "responseStreamConnectionFailed" + ], + "properties": { + "responseStreamConnectionFailed": { + "type": "object", + "properties": { + "httpStatusCode": { + "type": [ + "integer", + "null" + ], + "format": "uint16", + "minimum": 0 + } + } + } + }, + "additionalProperties": false, + "title": "ResponseStreamConnectionFailedCodexErrorInfo" + }, + { + "description": "The response SSE stream disconnected in the middle of a turn before completion.", + "type": "object", + "required": [ + "responseStreamDisconnected" + ], + "properties": { + "responseStreamDisconnected": { + "type": "object", + "properties": { + "httpStatusCode": { + "type": [ + "integer", + "null" + ], + "format": "uint16", + "minimum": 0 + } + } + } + }, + "additionalProperties": false, + "title": "ResponseStreamDisconnectedCodexErrorInfo" + }, + { + "description": "Reached the retry limit for responses.", + "type": "object", + "required": [ + "responseTooManyFailedAttempts" + ], + "properties": { + "responseTooManyFailedAttempts": { + "type": "object", + "properties": { + "httpStatusCode": { + "type": [ + "integer", + "null" + ], + "format": "uint16", + "minimum": 0 + } + } + } + }, + "additionalProperties": false, + "title": "ResponseTooManyFailedAttemptsCodexErrorInfo" + } + ] + }, + "CollabAgentState": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "message": { + "type": [ + "string", + "null" + ] + }, + "status": { + "$ref": "#/definitions/v2/CollabAgentStatus" + } + } + }, + "CollabAgentStatus": { + "type": "string", + "enum": [ + "pendingInit", + "running", + "completed", + "errored", + "shutdown", + "notFound" + ] + }, + "CollabAgentTool": { + "type": "string", + "enum": [ + "spawnAgent", + "sendInput", + "wait", + "closeAgent" + ] + }, + "CollabAgentToolCallStatus": { + "type": "string", + "enum": [ + "inProgress", + "completed", + "failed" + ] + }, + "CommandAction": { + "oneOf": [ + { + "type": "object", + "required": [ + "command", + "name", + "path", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "read" + ], + "title": "ReadCommandActionType" + } + }, + "title": "ReadCommandAction" + }, + { + "type": "object", + "required": [ + "command", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "path": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "listFiles" + ], + "title": "ListFilesCommandActionType" + } + }, + "title": "ListFilesCommandAction" + }, + { + "type": "object", + "required": [ + "command", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "path": { + "type": [ + "string", + "null" + ] + }, + "query": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "search" + ], + "title": "SearchCommandActionType" + } + }, + "title": "SearchCommandAction" + }, + { + "type": "object", + "required": [ + "command", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "unknown" + ], + "title": "UnknownCommandActionType" + } + }, + "title": "UnknownCommandAction" + } + ] + }, + "CommandExecutionStatus": { + "type": "string", + "enum": [ + "inProgress", + "completed", + "failed", + "declined" + ] + }, + "ContentBlock": { + "anyOf": [ + { + "$ref": "#/definitions/v2/TextContent" + }, + { + "$ref": "#/definitions/v2/ImageContent" + }, + { + "$ref": "#/definitions/v2/AudioContent" + }, + { + "$ref": "#/definitions/v2/ResourceLink" + }, + { + "$ref": "#/definitions/v2/EmbeddedResource" + } + ] + }, + "EmbeddedResource": { + "description": "The contents of a resource, embedded into a prompt or tool call result.\n\nIt is up to the client how best to render embedded resources for the benefit of the LLM and/or the user.", + "type": "object", + "required": [ + "resource", + "type" + ], + "properties": { + "annotations": { + "anyOf": [ + { + "$ref": "#/definitions/v2/Annotations" + }, + { + "type": "null" + } + ] + }, + "resource": { + "$ref": "#/definitions/v2/EmbeddedResourceResource" + }, + "type": { + "type": "string" + } + } + }, + "EmbeddedResourceResource": { + "anyOf": [ + { + "$ref": "#/definitions/v2/TextResourceContents" + }, + { + "$ref": "#/definitions/v2/BlobResourceContents" + } + ] + }, + "FileUpdateChange": { + "type": "object", + "required": [ + "diff", + "kind", + "path" + ], + "properties": { + "diff": { + "type": "string" + }, + "kind": { + "$ref": "#/definitions/v2/PatchChangeKind" + }, + "path": { + "type": "string" + } + } + }, + "GitInfo": { + "type": "object", + "properties": { + "branch": { + "type": [ + "string", + "null" + ] + }, + "originUrl": { + "type": [ + "string", + "null" + ] + }, + "sha": { + "type": [ + "string", + "null" + ] + } + } + }, + "ImageContent": { + "description": "An image provided to or from an LLM.", + "type": "object", + "required": [ + "data", + "mimeType", + "type" + ], + "properties": { + "annotations": { + "anyOf": [ + { + "$ref": "#/definitions/v2/Annotations" + }, + { + "type": "null" + } + ] + }, + "data": { + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "McpToolCallError": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "message": { + "type": "string" + } + } + }, + "McpToolCallResult": { + "type": "object", + "required": [ + "content" + ], + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/ContentBlock" + } + }, + "structuredContent": true + } + }, + "McpToolCallStatus": { + "type": "string", + "enum": [ + "inProgress", + "completed", + "failed" + ] + }, + "PatchApplyStatus": { + "type": "string", + "enum": [ + "inProgress", + "completed", + "failed", + "declined" + ] + }, + "PatchChangeKind": { + "oneOf": [ + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "add" + ], + "title": "AddPatchChangeKindType" + } + }, + "title": "AddPatchChangeKind" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "delete" + ], + "title": "DeletePatchChangeKindType" + } + }, + "title": "DeletePatchChangeKind" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "move_path": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "update" + ], + "title": "UpdatePatchChangeKindType" + } + }, + "title": "UpdatePatchChangeKind" + } + ] + }, + "ResourceLink": { + "description": "A resource that the server is capable of reading, included in a prompt or tool call result.\n\nNote: resource links returned by tools are not guaranteed to appear in the results of `resources/list` requests.", + "type": "object", + "required": [ + "name", + "type", + "uri" + ], + "properties": { + "annotations": { + "anyOf": [ + { + "$ref": "#/definitions/v2/Annotations" + }, + { + "type": "null" + } + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "mimeType": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "size": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "title": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string" + }, + "uri": { + "type": "string" + } + } + }, + "Role": { + "description": "The sender or recipient of messages and data in a conversation.", + "type": "string", + "enum": [ + "assistant", + "user" + ] + }, + "SessionSource": { + "oneOf": [ + { + "type": "string", + "enum": [ + "cli", + "vscode", + "exec", + "appServer", + "unknown" + ] + }, + { + "type": "object", + "required": [ + "subAgent" + ], + "properties": { + "subAgent": { + "$ref": "#/definitions/v2/SubAgentSource" + } + }, + "additionalProperties": false, + "title": "SubAgentSessionSource" + } + ] + }, + "SubAgentSource": { + "oneOf": [ + { + "type": "string", + "enum": [ + "review", + "compact" + ] + }, + { + "type": "object", + "required": [ + "thread_spawn" + ], + "properties": { + "thread_spawn": { + "type": "object", + "required": [ + "depth", + "parent_thread_id" + ], + "properties": { + "depth": { + "type": "integer", + "format": "int32" + }, + "parent_thread_id": { + "$ref": "#/definitions/v2/ThreadId" + } + } + } + }, + "additionalProperties": false, + "title": "ThreadSpawnSubAgentSource" + }, + { + "type": "object", + "required": [ + "other" + ], + "properties": { + "other": { + "type": "string" + } + }, + "additionalProperties": false, + "title": "OtherSubAgentSource" + } + ] + }, + "TextContent": { + "description": "Text provided to or from an LLM.", + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "annotations": { + "anyOf": [ + { + "$ref": "#/definitions/v2/Annotations" + }, + { + "type": "null" + } + ] + }, + "text": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "TextResourceContents": { + "type": "object", + "required": [ + "text", + "uri" + ], + "properties": { + "mimeType": { + "type": [ + "string", + "null" + ] + }, + "text": { + "type": "string" + }, + "uri": { + "type": "string" + } + } + }, + "Thread": { + "type": "object", + "required": [ + "cliVersion", + "createdAt", + "cwd", + "id", + "modelProvider", + "preview", + "source", + "turns", + "updatedAt" + ], + "properties": { + "cliVersion": { + "description": "Version of the CLI that created the thread.", + "type": "string" + }, + "createdAt": { + "description": "Unix timestamp (in seconds) when the thread was created.", + "type": "integer", + "format": "int64" + }, + "cwd": { + "description": "Working directory captured for the thread.", + "type": "string" + }, + "gitInfo": { + "description": "Optional Git metadata captured when the thread was created.", + "anyOf": [ + { + "$ref": "#/definitions/v2/GitInfo" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "modelProvider": { + "description": "Model provider used for this thread (for example, 'openai').", + "type": "string" + }, + "path": { + "description": "[UNSTABLE] Path to the thread on disk.", + "type": [ + "string", + "null" + ] + }, + "preview": { + "description": "Usually the first user message in the thread, if available.", + "type": "string" + }, + "source": { + "description": "Origin of the thread (CLI, VSCode, codex exec, codex app-server, etc.).", + "allOf": [ + { + "$ref": "#/definitions/v2/SessionSource" + } + ] + }, + "turns": { + "description": "Only populated on `thread/resume`, `thread/rollback`, `thread/fork`, and `thread/read` (when `includeTurns` is true) responses. For all other responses and notifications returning a Thread, the turns field will be an empty list.", + "type": "array", + "items": { + "$ref": "#/definitions/v2/Turn" + } + }, + "updatedAt": { + "description": "Unix timestamp (in seconds) when the thread was last updated.", + "type": "integer", + "format": "int64" + } + } + }, + "ThreadId": { + "type": "string" + }, + "ThreadItem": { + "oneOf": [ + { + "type": "object", + "required": [ + "content", + "id", + "type" + ], + "properties": { + "content": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/UserInput" + } + }, + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "userMessage" + ], + "title": "UserMessageThreadItemType" + } + }, + "title": "UserMessageThreadItem" + }, + { + "type": "object", + "required": [ + "id", + "text", + "type" + ], + "properties": { + "id": { + "type": "string" + }, + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "agentMessage" + ], + "title": "AgentMessageThreadItemType" + } + }, + "title": "AgentMessageThreadItem" + }, + { + "type": "object", + "required": [ + "id", + "type" + ], + "properties": { + "content": { + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "string" + }, + "summary": { + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "type": "string", + "enum": [ + "reasoning" + ], + "title": "ReasoningThreadItemType" + } + }, + "title": "ReasoningThreadItem" + }, + { + "type": "object", + "required": [ + "command", + "commandActions", + "cwd", + "id", + "status", + "type" + ], + "properties": { + "aggregatedOutput": { + "description": "The command's output, aggregated from stdout and stderr.", + "type": [ + "string", + "null" + ] + }, + "command": { + "description": "The command to be executed.", + "type": "string" + }, + "commandActions": { + "description": "A best-effort parsing of the command to understand the action(s) it will perform. This returns a list of CommandAction objects because a single shell command may be composed of many commands piped together.", + "type": "array", + "items": { + "$ref": "#/definitions/v2/CommandAction" + } + }, + "cwd": { + "description": "The command's working directory.", + "type": "string" + }, + "durationMs": { + "description": "The duration of the command execution in milliseconds.", + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "exitCode": { + "description": "The command's exit code.", + "type": [ + "integer", + "null" + ], + "format": "int32" + }, + "id": { + "type": "string" + }, + "processId": { + "description": "Identifier for the underlying PTY process (when available).", + "type": [ + "string", + "null" + ] + }, + "status": { + "$ref": "#/definitions/v2/CommandExecutionStatus" + }, + "type": { + "type": "string", + "enum": [ + "commandExecution" + ], + "title": "CommandExecutionThreadItemType" + } + }, + "title": "CommandExecutionThreadItem" + }, + { + "type": "object", + "required": [ + "changes", + "id", + "status", + "type" + ], + "properties": { + "changes": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/FileUpdateChange" + } + }, + "id": { + "type": "string" + }, + "status": { + "$ref": "#/definitions/v2/PatchApplyStatus" + }, + "type": { + "type": "string", + "enum": [ + "fileChange" + ], + "title": "FileChangeThreadItemType" + } + }, + "title": "FileChangeThreadItem" + }, + { + "type": "object", + "required": [ + "arguments", + "id", + "server", + "status", + "tool", + "type" + ], + "properties": { + "arguments": true, + "durationMs": { + "description": "The duration of the MCP tool call in milliseconds.", + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "error": { + "anyOf": [ + { + "$ref": "#/definitions/v2/McpToolCallError" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "result": { + "anyOf": [ + { + "$ref": "#/definitions/v2/McpToolCallResult" + }, + { + "type": "null" + } + ] + }, + "server": { + "type": "string" + }, + "status": { + "$ref": "#/definitions/v2/McpToolCallStatus" + }, + "tool": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "mcpToolCall" + ], + "title": "McpToolCallThreadItemType" + } + }, + "title": "McpToolCallThreadItem" + }, + { + "type": "object", + "required": [ + "agentsStates", + "id", + "receiverThreadIds", + "senderThreadId", + "status", + "tool", + "type" + ], + "properties": { + "agentsStates": { + "description": "Last known status of the target agents, when available.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v2/CollabAgentState" + } + }, + "id": { + "description": "Unique identifier for this collab tool call.", + "type": "string" + }, + "prompt": { + "description": "Prompt text sent as part of the collab tool call, when available.", + "type": [ + "string", + "null" + ] + }, + "receiverThreadIds": { + "description": "Thread ID of the receiving agent, when applicable. In case of spawn operation, this corresponds to the newly spawned agent.", + "type": "array", + "items": { + "type": "string" + } + }, + "senderThreadId": { + "description": "Thread ID of the agent issuing the collab request.", + "type": "string" + }, + "status": { + "description": "Current status of the collab tool call.", + "allOf": [ + { + "$ref": "#/definitions/v2/CollabAgentToolCallStatus" + } + ] + }, + "tool": { + "description": "Name of the collab tool that was invoked.", + "allOf": [ + { + "$ref": "#/definitions/v2/CollabAgentTool" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "collabAgentToolCall" + ], + "title": "CollabAgentToolCallThreadItemType" + } + }, + "title": "CollabAgentToolCallThreadItem" + }, + { + "type": "object", + "required": [ + "id", + "query", + "type" + ], + "properties": { + "id": { + "type": "string" + }, + "query": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "webSearch" + ], + "title": "WebSearchThreadItemType" + } + }, + "title": "WebSearchThreadItem" + }, + { + "type": "object", + "required": [ + "id", + "path", + "type" + ], + "properties": { + "id": { + "type": "string" + }, + "path": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "imageView" + ], + "title": "ImageViewThreadItemType" + } + }, + "title": "ImageViewThreadItem" + }, + { + "type": "object", + "required": [ + "id", + "review", + "type" + ], + "properties": { + "id": { + "type": "string" + }, + "review": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "enteredReviewMode" + ], + "title": "EnteredReviewModeThreadItemType" + } + }, + "title": "EnteredReviewModeThreadItem" + }, + { + "type": "object", + "required": [ + "id", + "review", + "type" + ], + "properties": { + "id": { + "type": "string" + }, + "review": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "exitedReviewMode" + ], + "title": "ExitedReviewModeThreadItemType" + } + }, + "title": "ExitedReviewModeThreadItem" + } + ] + }, + "Turn": { + "type": "object", + "required": [ + "id", + "items", + "status" + ], + "properties": { + "error": { + "description": "Only populated when the Turn's status is failed.", + "anyOf": [ + { + "$ref": "#/definitions/v2/TurnError" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "items": { + "description": "Only populated on a `thread/resume` or `thread/fork` response. For all other responses and notifications returning a Turn, the items field will be an empty list.", + "type": "array", + "items": { + "$ref": "#/definitions/v2/ThreadItem" + } + }, + "status": { + "$ref": "#/definitions/v2/TurnStatus" + } + } + }, + "TurnError": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "additionalDetails": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "codexErrorInfo": { + "anyOf": [ + { + "$ref": "#/definitions/v2/CodexErrorInfo" + }, + { + "type": "null" + } + ] + }, + "message": { + "type": "string" + } + } + }, + "TurnStatus": { + "type": "string", + "enum": [ + "completed", + "interrupted", + "failed", + "inProgress" + ] + }, + "ThreadStartResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadStartResponse", + "type": "object", + "required": [ + "approvalPolicy", + "cwd", + "model", + "modelProvider", + "sandbox", + "thread" + ], + "properties": { + "approvalPolicy": { + "$ref": "#/definitions/v2/AskForApproval" + }, + "cwd": { + "type": "string" + }, + "model": { + "type": "string" + }, + "modelProvider": { + "type": "string" + }, + "reasoningEffort": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ReasoningEffort" + }, + { + "type": "null" + } + ] + }, + "sandbox": { + "$ref": "#/definitions/v2/SandboxPolicy" + }, + "thread": { + "$ref": "#/definitions/v2/Thread" + } + } + }, + "ThreadResumeResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadResumeResponse", + "type": "object", + "required": [ + "approvalPolicy", + "cwd", + "model", + "modelProvider", + "sandbox", + "thread" + ], + "properties": { + "approvalPolicy": { + "$ref": "#/definitions/v2/AskForApproval" + }, + "cwd": { + "type": "string" + }, + "model": { + "type": "string" + }, + "modelProvider": { + "type": "string" + }, + "reasoningEffort": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ReasoningEffort" + }, + { + "type": "null" + } + ] + }, + "sandbox": { + "$ref": "#/definitions/v2/SandboxPolicy" + }, + "thread": { + "$ref": "#/definitions/v2/Thread" + } + } + }, + "ThreadForkResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadForkResponse", + "type": "object", + "required": [ + "approvalPolicy", + "cwd", + "model", + "modelProvider", + "sandbox", + "thread" + ], + "properties": { + "approvalPolicy": { + "$ref": "#/definitions/v2/AskForApproval" + }, + "cwd": { + "type": "string" + }, + "model": { + "type": "string" + }, + "modelProvider": { + "type": "string" + }, + "reasoningEffort": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ReasoningEffort" + }, + { + "type": "null" + } + ] + }, + "sandbox": { + "$ref": "#/definitions/v2/SandboxPolicy" + }, + "thread": { + "$ref": "#/definitions/v2/Thread" + } + } + }, + "ThreadArchiveResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadArchiveResponse", + "type": "object" + }, + "ThreadRollbackResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadRollbackResponse", + "type": "object", + "required": [ + "thread" + ], + "properties": { + "thread": { + "description": "The updated thread after applying the rollback, with `turns` populated.\n\nThe ThreadItems stored in each Turn are lossy since we explicitly do not persist all agent interactions, such as command executions. This is the same behavior as `thread/resume`.", + "allOf": [ + { + "$ref": "#/definitions/v2/Thread" + } + ] + } + } + }, + "ThreadListResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadListResponse", + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/Thread" + } + }, + "nextCursor": { + "description": "Opaque cursor to pass to the next call to continue after the last item. if None, there are no more items to return.", + "type": [ + "string", + "null" + ] + } + } + }, + "ThreadLoadedListResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadLoadedListResponse", + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "description": "Thread ids for sessions currently loaded in memory.", + "type": "array", + "items": { + "type": "string" + } + }, + "nextCursor": { + "description": "Opaque cursor to pass to the next call to continue after the last item. if None, there are no more items to return.", + "type": [ + "string", + "null" + ] + } + } + }, + "ThreadReadResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadReadResponse", + "type": "object", + "required": [ + "thread" + ], + "properties": { + "thread": { + "$ref": "#/definitions/v2/Thread" + } + } + }, + "SkillErrorInfo": { + "type": "object", + "required": [ + "message", + "path" + ], + "properties": { + "message": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "SkillInterface": { + "type": "object", + "properties": { + "brandColor": { + "type": [ + "string", + "null" + ] + }, + "defaultPrompt": { + "type": [ + "string", + "null" + ] + }, + "displayName": { + "type": [ + "string", + "null" + ] + }, + "iconLarge": { + "type": [ + "string", + "null" + ] + }, + "iconSmall": { + "type": [ + "string", + "null" + ] + }, + "shortDescription": { + "type": [ + "string", + "null" + ] + } + } + }, + "SkillMetadata": { + "type": "object", + "required": [ + "description", + "enabled", + "name", + "path", + "scope" + ], + "properties": { + "description": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "interface": { + "anyOf": [ + { + "$ref": "#/definitions/v2/SkillInterface" + }, + { + "type": "null" + } + ] + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "scope": { + "$ref": "#/definitions/v2/SkillScope" + }, + "shortDescription": { + "description": "Legacy short_description from SKILL.md. Prefer SKILL.toml interface.short_description.", + "type": [ + "string", + "null" + ] + } + } + }, + "SkillScope": { + "type": "string", + "enum": [ + "user", + "repo", + "system", + "admin" + ] + }, + "SkillsListEntry": { + "type": "object", + "required": [ + "cwd", + "errors", + "skills" + ], + "properties": { + "cwd": { + "type": "string" + }, + "errors": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/SkillErrorInfo" + } + }, + "skills": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/SkillMetadata" + } + } + } + }, + "SkillsListResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "SkillsListResponse", + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/SkillsListEntry" + } + } + } + }, + "AppInfo": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "description": { + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "installUrl": { + "type": [ + "string", + "null" + ] + }, + "isAccessible": { + "default": false, + "type": "boolean" + }, + "logoUrl": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + } + } + }, + "AppsListResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AppsListResponse", + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/AppInfo" + } + }, + "nextCursor": { + "description": "Opaque cursor to pass to the next call to continue after the last item. If None, there are no more items to return.", + "type": [ + "string", + "null" + ] + } + } + }, + "SkillsConfigWriteResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "SkillsConfigWriteResponse", + "type": "object", + "required": [ + "effectiveEnabled" + ], + "properties": { + "effectiveEnabled": { + "type": "boolean" + } + } + }, + "TurnStartResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TurnStartResponse", + "type": "object", + "required": [ + "turn" + ], + "properties": { + "turn": { + "$ref": "#/definitions/v2/Turn" + } + } + }, + "TurnInterruptResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TurnInterruptResponse", + "type": "object" + }, + "ReviewStartResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ReviewStartResponse", + "type": "object", + "required": [ + "reviewThreadId", + "turn" + ], + "properties": { + "reviewThreadId": { + "description": "Identifies the thread where the review runs.\n\nFor inline reviews, this is the original thread id. For detached reviews, this is the id of the new review thread.", + "type": "string" + }, + "turn": { + "$ref": "#/definitions/v2/Turn" + } + } + }, + "Model": { + "type": "object", + "required": [ + "defaultReasoningEffort", + "description", + "displayName", + "id", + "isDefault", + "model", + "supportedReasoningEfforts" + ], + "properties": { + "defaultReasoningEffort": { + "$ref": "#/definitions/v2/ReasoningEffort" + }, + "description": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "isDefault": { + "type": "boolean" + }, + "model": { + "type": "string" + }, + "supportedReasoningEfforts": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/ReasoningEffortOption" + } + } + } + }, + "ReasoningEffortOption": { + "type": "object", + "required": [ + "description", + "reasoningEffort" + ], + "properties": { + "description": { + "type": "string" + }, + "reasoningEffort": { + "$ref": "#/definitions/v2/ReasoningEffort" + } + } + }, + "ModelListResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ModelListResponse", + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/Model" + } + }, + "nextCursor": { + "description": "Opaque cursor to pass to the next call to continue after the last item. If None, there are no more items to return.", + "type": [ + "string", + "null" + ] + } + } + }, + "CollaborationModeMask": { + "description": "A mask for collaboration mode settings, allowing partial updates. All fields except `name` are optional, enabling selective updates.", + "type": "object", + "required": [ + "name" + ], + "properties": { + "developer_instructions": { + "type": [ + "string", + "null" + ] + }, + "mode": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ModeKind" + }, + { + "type": "null" + } + ] + }, + "model": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "reasoning_effort": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/definitions/v2/ReasoningEffort" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + } + } + }, + "CollaborationModeListResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CollaborationModeListResponse", + "description": "EXPERIMENTAL - collaboration mode presets response.", + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/CollaborationModeMask" + } + } + } + }, + "McpServerOauthLoginResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "McpServerOauthLoginResponse", + "type": "object", + "required": [ + "authorizationUrl" + ], + "properties": { + "authorizationUrl": { + "type": "string" + } + } + }, + "McpServerRefreshResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "McpServerRefreshResponse", + "type": "object" + }, + "McpAuthStatus": { + "type": "string", + "enum": [ + "unsupported", + "notLoggedIn", + "bearerToken", + "oAuth" + ] + }, + "McpServerStatus": { + "type": "object", + "required": [ + "authStatus", + "name", + "resourceTemplates", + "resources", + "tools" + ], + "properties": { + "authStatus": { + "$ref": "#/definitions/v2/McpAuthStatus" + }, + "name": { + "type": "string" + }, + "resourceTemplates": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/ResourceTemplate" + } + }, + "resources": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/Resource" + } + }, + "tools": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v2/Tool" + } + } + } + }, + "Resource": { + "description": "A known resource that the server is capable of reading.", + "type": "object", + "required": [ + "name", + "uri" + ], + "properties": { + "annotations": { + "anyOf": [ + { + "$ref": "#/definitions/v2/Annotations" + }, + { + "type": "null" + } + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "mimeType": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "size": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "title": { + "type": [ + "string", + "null" + ] + }, + "uri": { + "type": "string" + } + } + }, + "ResourceTemplate": { + "description": "A template description for resources available on the server.", + "type": "object", + "required": [ + "name", + "uriTemplate" + ], + "properties": { + "annotations": { + "anyOf": [ + { + "$ref": "#/definitions/v2/Annotations" + }, + { + "type": "null" + } + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "mimeType": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "title": { + "type": [ + "string", + "null" + ] + }, + "uriTemplate": { + "type": "string" + } + } + }, + "Tool": { + "description": "Definition for a tool the client can call.", + "type": "object", + "required": [ + "inputSchema", + "name" + ], + "properties": { + "annotations": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ToolAnnotations" + }, + { + "type": "null" + } + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "inputSchema": { + "$ref": "#/definitions/v2/ToolInputSchema" + }, + "name": { + "type": "string" + }, + "outputSchema": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ToolOutputSchema" + }, + { + "type": "null" + } + ] + }, + "title": { + "type": [ + "string", + "null" + ] + } + } + }, + "ToolAnnotations": { + "description": "Additional properties describing a Tool to clients.\n\nNOTE: all properties in ToolAnnotations are **hints**. They are not guaranteed to provide a faithful description of tool behavior (including descriptive properties like `title`).\n\nClients should never make tool use decisions based on ToolAnnotations received from untrusted servers.", + "type": "object", + "properties": { + "destructiveHint": { + "type": [ + "boolean", + "null" + ] + }, + "idempotentHint": { + "type": [ + "boolean", + "null" + ] + }, + "openWorldHint": { + "type": [ + "boolean", + "null" + ] + }, + "readOnlyHint": { + "type": [ + "boolean", + "null" + ] + }, + "title": { + "type": [ + "string", + "null" + ] + } + } + }, + "ToolInputSchema": { + "description": "A JSON Schema object defining the expected parameters for the tool.", + "type": "object", + "properties": { + "properties": true, + "required": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "type": { + "default": "object", + "type": "string" + } + } + }, + "ToolOutputSchema": { + "description": "An optional JSON Schema object defining the structure of the tool's output returned in the structuredContent field of a CallToolResult.", + "type": "object", + "properties": { + "properties": true, + "required": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "type": { + "default": "object", + "type": "string" + } + } + }, + "ListMcpServerStatusResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ListMcpServerStatusResponse", + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/McpServerStatus" + } + }, + "nextCursor": { + "description": "Opaque cursor to pass to the next call to continue after the last item. If None, there are no more items to return.", + "type": [ + "string", + "null" + ] + } + } + }, + "LoginAccountResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "LoginAccountResponse", + "oneOf": [ + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "apiKey" + ], + "title": "ApiKeyv2::LoginAccountResponseType" + } + }, + "title": "ApiKeyv2::LoginAccountResponse" + }, + { + "type": "object", + "required": [ + "authUrl", + "loginId", + "type" + ], + "properties": { + "authUrl": { + "description": "URL the client should open in a browser to initiate the OAuth flow.", + "type": "string" + }, + "loginId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "chatgpt" + ], + "title": "Chatgptv2::LoginAccountResponseType" + } + }, + "title": "Chatgptv2::LoginAccountResponse" + } + ] + }, + "CancelLoginAccountStatus": { + "type": "string", + "enum": [ + "canceled", + "notFound" + ] + }, + "CancelLoginAccountResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CancelLoginAccountResponse", + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "$ref": "#/definitions/v2/CancelLoginAccountStatus" + } + } + }, + "LogoutAccountResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "LogoutAccountResponse", + "type": "object" + }, + "CreditsSnapshot": { + "type": "object", + "required": [ + "hasCredits", + "unlimited" + ], + "properties": { + "balance": { + "type": [ + "string", + "null" + ] + }, + "hasCredits": { + "type": "boolean" + }, + "unlimited": { + "type": "boolean" + } + } + }, + "PlanType": { + "type": "string", + "enum": [ + "free", + "plus", + "pro", + "team", + "business", + "enterprise", + "edu", + "unknown" + ] + }, + "RateLimitSnapshot": { + "type": "object", + "properties": { + "credits": { + "anyOf": [ + { + "$ref": "#/definitions/v2/CreditsSnapshot" + }, + { + "type": "null" + } + ] + }, + "planType": { + "anyOf": [ + { + "$ref": "#/definitions/v2/PlanType" + }, + { + "type": "null" + } + ] + }, + "primary": { + "anyOf": [ + { + "$ref": "#/definitions/v2/RateLimitWindow" + }, + { + "type": "null" + } + ] + }, + "secondary": { + "anyOf": [ + { + "$ref": "#/definitions/v2/RateLimitWindow" + }, + { + "type": "null" + } + ] + } + } + }, + "RateLimitWindow": { + "type": "object", + "required": [ + "usedPercent" + ], + "properties": { + "resetsAt": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "usedPercent": { + "type": "integer", + "format": "int32" + }, + "windowDurationMins": { + "type": [ + "integer", + "null" + ], + "format": "int64" + } + } + }, + "GetAccountRateLimitsResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GetAccountRateLimitsResponse", + "type": "object", + "required": [ + "rateLimits" + ], + "properties": { + "rateLimits": { + "$ref": "#/definitions/v2/RateLimitSnapshot" + } + } + }, + "FeedbackUploadResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "FeedbackUploadResponse", + "type": "object", + "required": [ + "threadId" + ], + "properties": { + "threadId": { + "type": "string" + } + } + }, + "CommandExecResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CommandExecResponse", + "type": "object", + "required": [ + "exitCode", + "stderr", + "stdout" + ], + "properties": { + "exitCode": { + "type": "integer", + "format": "int32" + }, + "stderr": { + "type": "string" + }, + "stdout": { + "type": "string" + } + } + }, + "AnalyticsConfig": { + "type": "object", + "properties": { + "enabled": { + "type": [ + "boolean", + "null" + ] + } + }, + "additionalProperties": true + }, + "Config": { + "type": "object", + "properties": { + "analytics": { + "anyOf": [ + { + "$ref": "#/definitions/v2/AnalyticsConfig" + }, + { + "type": "null" + } + ] + }, + "approval_policy": { + "anyOf": [ + { + "$ref": "#/definitions/v2/AskForApproval" + }, + { + "type": "null" + } + ] + }, + "compact_prompt": { + "type": [ + "string", + "null" + ] + }, + "developer_instructions": { + "type": [ + "string", + "null" + ] + }, + "forced_chatgpt_workspace_id": { + "type": [ + "string", + "null" + ] + }, + "forced_login_method": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ForcedLoginMethod" + }, + { + "type": "null" + } + ] + }, + "instructions": { + "type": [ + "string", + "null" + ] + }, + "model": { + "type": [ + "string", + "null" + ] + }, + "model_auto_compact_token_limit": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "model_context_window": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "model_provider": { + "type": [ + "string", + "null" + ] + }, + "model_reasoning_effort": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ReasoningEffort" + }, + { + "type": "null" + } + ] + }, + "model_reasoning_summary": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ReasoningSummary" + }, + { + "type": "null" + } + ] + }, + "model_verbosity": { + "anyOf": [ + { + "$ref": "#/definitions/v2/Verbosity" + }, + { + "type": "null" + } + ] + }, + "profile": { + "type": [ + "string", + "null" + ] + }, + "profiles": { + "default": {}, + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v2/ProfileV2" + } + }, + "review_model": { + "type": [ + "string", + "null" + ] + }, + "sandbox_mode": { + "anyOf": [ + { + "$ref": "#/definitions/v2/SandboxMode" + }, + { + "type": "null" + } + ] + }, + "sandbox_workspace_write": { + "anyOf": [ + { + "$ref": "#/definitions/v2/SandboxWorkspaceWrite" + }, + { + "type": "null" + } + ] + }, + "tools": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ToolsV2" + }, + { + "type": "null" + } + ] + }, + "web_search": { + "anyOf": [ + { + "$ref": "#/definitions/v2/WebSearchMode" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + "ConfigLayer": { + "type": "object", + "required": [ + "config", + "name", + "version" + ], + "properties": { + "config": true, + "disabledReason": { + "type": [ + "string", + "null" + ] + }, + "name": { + "$ref": "#/definitions/v2/ConfigLayerSource" + }, + "version": { + "type": "string" + } + } + }, + "ConfigLayerMetadata": { + "type": "object", + "required": [ + "name", + "version" + ], + "properties": { + "name": { + "$ref": "#/definitions/v2/ConfigLayerSource" + }, + "version": { + "type": "string" + } + } + }, + "ConfigLayerSource": { + "oneOf": [ + { + "description": "Managed preferences layer delivered by MDM (macOS only).", + "type": "object", + "required": [ + "domain", + "key", + "type" + ], + "properties": { + "domain": { + "type": "string" + }, + "key": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "mdm" + ], + "title": "MdmConfigLayerSourceType" + } + }, + "title": "MdmConfigLayerSource" + }, + { + "description": "Managed config layer from a file (usually `managed_config.toml`).", + "type": "object", + "required": [ + "file", + "type" + ], + "properties": { + "file": { + "description": "This is the path to the system config.toml file, though it is not guaranteed to exist.", + "allOf": [ + { + "$ref": "#/definitions/v2/AbsolutePathBuf" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "system" + ], + "title": "SystemConfigLayerSourceType" + } + }, + "title": "SystemConfigLayerSource" + }, + { + "description": "User config layer from $CODEX_HOME/config.toml. This layer is special in that it is expected to be: - writable by the user - generally outside the workspace directory", + "type": "object", + "required": [ + "file", + "type" + ], + "properties": { + "file": { + "description": "This is the path to the user's config.toml file, though it is not guaranteed to exist.", + "allOf": [ + { + "$ref": "#/definitions/v2/AbsolutePathBuf" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "user" + ], + "title": "UserConfigLayerSourceType" + } + }, + "title": "UserConfigLayerSource" + }, + { + "description": "Path to a .codex/ folder within a project. There could be multiple of these between `cwd` and the project/repo root.", + "type": "object", + "required": [ + "dotCodexFolder", + "type" + ], + "properties": { + "dotCodexFolder": { + "$ref": "#/definitions/v2/AbsolutePathBuf" + }, + "type": { + "type": "string", + "enum": [ + "project" + ], + "title": "ProjectConfigLayerSourceType" + } + }, + "title": "ProjectConfigLayerSource" + }, + { + "description": "Session-layer overrides supplied via `-c`/`--config`.", + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "sessionFlags" + ], + "title": "SessionFlagsConfigLayerSourceType" + } + }, + "title": "SessionFlagsConfigLayerSource" + }, + { + "description": "`managed_config.toml` was designed to be a config that was loaded as the last layer on top of everything else. This scheme did not quite work out as intended, but we keep this variant as a \"best effort\" while we phase out `managed_config.toml` in favor of `requirements.toml`.", + "type": "object", + "required": [ + "file", + "type" + ], + "properties": { + "file": { + "$ref": "#/definitions/v2/AbsolutePathBuf" + }, + "type": { + "type": "string", + "enum": [ + "legacyManagedConfigTomlFromFile" + ], + "title": "LegacyManagedConfigTomlFromFileConfigLayerSourceType" + } + }, + "title": "LegacyManagedConfigTomlFromFileConfigLayerSource" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "legacyManagedConfigTomlFromMdm" + ], + "title": "LegacyManagedConfigTomlFromMdmConfigLayerSourceType" + } + }, + "title": "LegacyManagedConfigTomlFromMdmConfigLayerSource" + } + ] + }, + "ForcedLoginMethod": { + "type": "string", + "enum": [ + "chatgpt", + "api" + ] + }, + "ProfileV2": { + "type": "object", + "properties": { + "approval_policy": { + "anyOf": [ + { + "$ref": "#/definitions/v2/AskForApproval" + }, + { + "type": "null" + } + ] + }, + "chatgpt_base_url": { + "type": [ + "string", + "null" + ] + }, + "model": { + "type": [ + "string", + "null" + ] + }, + "model_provider": { + "type": [ + "string", + "null" + ] + }, + "model_reasoning_effort": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ReasoningEffort" + }, + { + "type": "null" + } + ] + }, + "model_reasoning_summary": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ReasoningSummary" + }, + { + "type": "null" + } + ] + }, + "model_verbosity": { + "anyOf": [ + { + "$ref": "#/definitions/v2/Verbosity" + }, + { + "type": "null" + } + ] + }, + "web_search": { + "anyOf": [ + { + "$ref": "#/definitions/v2/WebSearchMode" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true + }, + "SandboxWorkspaceWrite": { + "type": "object", + "properties": { + "exclude_slash_tmp": { + "default": false, + "type": "boolean" + }, + "exclude_tmpdir_env_var": { + "default": false, + "type": "boolean" + }, + "network_access": { + "default": false, + "type": "boolean" + }, + "writable_roots": { + "default": [], + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "ToolsV2": { + "type": "object", + "properties": { + "view_image": { + "type": [ + "boolean", + "null" + ] + }, + "web_search": { + "type": [ + "boolean", + "null" + ] + } + } + }, + "Verbosity": { + "description": "Controls output length/detail on GPT-5 models via the Responses API. Serialized with lowercase values to match the OpenAI API.", + "type": "string", + "enum": [ + "low", + "medium", + "high" + ] + }, + "WebSearchMode": { + "type": "string", + "enum": [ + "disabled", + "cached", + "live" + ] + }, + "ConfigReadResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigReadResponse", + "type": "object", + "required": [ + "config", + "origins" + ], + "properties": { + "config": { + "$ref": "#/definitions/v2/Config" + }, + "layers": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/v2/ConfigLayer" + } + }, + "origins": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/v2/ConfigLayerMetadata" + } + } + } + }, + "OverriddenMetadata": { + "type": "object", + "required": [ + "effectiveValue", + "message", + "overridingLayer" + ], + "properties": { + "effectiveValue": true, + "message": { + "type": "string" + }, + "overridingLayer": { + "$ref": "#/definitions/v2/ConfigLayerMetadata" + } + } + }, + "WriteStatus": { + "type": "string", + "enum": [ + "ok", + "okOverridden" + ] + }, + "ConfigWriteResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigWriteResponse", + "type": "object", + "required": [ + "filePath", + "status", + "version" + ], + "properties": { + "filePath": { + "description": "Canonical path to the config file that was written.", + "allOf": [ + { + "$ref": "#/definitions/v2/AbsolutePathBuf" + } + ] + }, + "overriddenMetadata": { + "anyOf": [ + { + "$ref": "#/definitions/v2/OverriddenMetadata" + }, + { + "type": "null" + } + ] + }, + "status": { + "$ref": "#/definitions/v2/WriteStatus" + }, + "version": { + "type": "string" + } + } + }, + "ConfigRequirements": { + "type": "object", + "properties": { + "allowedApprovalPolicies": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/v2/AskForApproval" + } + }, + "allowedSandboxModes": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/v2/SandboxMode" + } + } + } + }, + "ConfigRequirementsReadResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigRequirementsReadResponse", + "type": "object", + "properties": { + "requirements": { + "description": "Null if no requirements are configured (e.g. no requirements.toml/MDM entries).", + "anyOf": [ + { + "$ref": "#/definitions/v2/ConfigRequirements" + }, + { + "type": "null" + } + ] + } + } + }, + "Account": { + "oneOf": [ + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "apiKey" + ], + "title": "ApiKeyAccountType" + } + }, + "title": "ApiKeyAccount" + }, + { + "type": "object", + "required": [ + "email", + "planType", + "type" + ], + "properties": { + "email": { + "type": "string" + }, + "planType": { + "$ref": "#/definitions/v2/PlanType" + }, + "type": { + "type": "string", + "enum": [ + "chatgpt" + ], + "title": "ChatgptAccountType" + } + }, + "title": "ChatgptAccount" + } + ] + }, + "GetAccountResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GetAccountResponse", + "type": "object", + "required": [ + "requiresOpenaiAuth" + ], + "properties": { + "account": { + "anyOf": [ + { + "$ref": "#/definitions/v2/Account" + }, + { + "type": "null" + } + ] + }, + "requiresOpenaiAuth": { + "type": "boolean" + } + } + }, + "ErrorNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ErrorNotification", + "type": "object", + "required": [ + "error", + "threadId", + "turnId", + "willRetry" + ], + "properties": { + "error": { + "$ref": "#/definitions/v2/TurnError" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + }, + "willRetry": { + "type": "boolean" + } + } + }, + "ThreadStartedNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadStartedNotification", + "type": "object", + "required": [ + "thread" + ], + "properties": { + "thread": { + "$ref": "#/definitions/v2/Thread" + } + } + }, + "ThreadTokenUsage": { + "type": "object", + "required": [ + "last", + "total" + ], + "properties": { + "last": { + "$ref": "#/definitions/v2/TokenUsageBreakdown" + }, + "modelContextWindow": { + "type": [ + "integer", + "null" + ], + "format": "int64" + }, + "total": { + "$ref": "#/definitions/v2/TokenUsageBreakdown" + } + } + }, + "TokenUsageBreakdown": { + "type": "object", + "required": [ + "cachedInputTokens", + "inputTokens", + "outputTokens", + "reasoningOutputTokens", + "totalTokens" + ], + "properties": { + "cachedInputTokens": { + "type": "integer", + "format": "int64" + }, + "inputTokens": { + "type": "integer", + "format": "int64" + }, + "outputTokens": { + "type": "integer", + "format": "int64" + }, + "reasoningOutputTokens": { + "type": "integer", + "format": "int64" + }, + "totalTokens": { + "type": "integer", + "format": "int64" + } + } + }, + "ThreadTokenUsageUpdatedNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ThreadTokenUsageUpdatedNotification", + "type": "object", + "required": [ + "threadId", + "tokenUsage", + "turnId" + ], + "properties": { + "threadId": { + "type": "string" + }, + "tokenUsage": { + "$ref": "#/definitions/v2/ThreadTokenUsage" + }, + "turnId": { + "type": "string" + } + } + }, + "TurnStartedNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TurnStartedNotification", + "type": "object", + "required": [ + "threadId", + "turn" + ], + "properties": { + "threadId": { + "type": "string" + }, + "turn": { + "$ref": "#/definitions/v2/Turn" + } + } + }, + "TurnCompletedNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TurnCompletedNotification", + "type": "object", + "required": [ + "threadId", + "turn" + ], + "properties": { + "threadId": { + "type": "string" + }, + "turn": { + "$ref": "#/definitions/v2/Turn" + } + } + }, + "TurnDiffUpdatedNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TurnDiffUpdatedNotification", + "description": "Notification that the turn-level unified diff has changed. Contains the latest aggregated diff across all file changes in the turn.", + "type": "object", + "required": [ + "diff", + "threadId", + "turnId" + ], + "properties": { + "diff": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "TurnPlanStep": { + "type": "object", + "required": [ + "status", + "step" + ], + "properties": { + "status": { + "$ref": "#/definitions/v2/TurnPlanStepStatus" + }, + "step": { + "type": "string" + } + } + }, + "TurnPlanStepStatus": { + "type": "string", + "enum": [ + "pending", + "inProgress", + "completed" + ] + }, + "TurnPlanUpdatedNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TurnPlanUpdatedNotification", + "type": "object", + "required": [ + "plan", + "threadId", + "turnId" + ], + "properties": { + "explanation": { + "type": [ + "string", + "null" + ] + }, + "plan": { + "type": "array", + "items": { + "$ref": "#/definitions/v2/TurnPlanStep" + } + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "ItemStartedNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ItemStartedNotification", + "type": "object", + "required": [ + "item", + "threadId", + "turnId" + ], + "properties": { + "item": { + "$ref": "#/definitions/v2/ThreadItem" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "ItemCompletedNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ItemCompletedNotification", + "type": "object", + "required": [ + "item", + "threadId", + "turnId" + ], + "properties": { + "item": { + "$ref": "#/definitions/v2/ThreadItem" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "RawResponseItemCompletedNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "RawResponseItemCompletedNotification", + "type": "object", + "required": [ + "item", + "threadId", + "turnId" + ], + "properties": { + "item": { + "$ref": "#/definitions/v2/ResponseItem" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "AgentMessageDeltaNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AgentMessageDeltaNotification", + "type": "object", + "required": [ + "delta", + "itemId", + "threadId", + "turnId" + ], + "properties": { + "delta": { + "type": "string" + }, + "itemId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "CommandExecutionOutputDeltaNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CommandExecutionOutputDeltaNotification", + "type": "object", + "required": [ + "delta", + "itemId", + "threadId", + "turnId" + ], + "properties": { + "delta": { + "type": "string" + }, + "itemId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "TerminalInteractionNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "TerminalInteractionNotification", + "type": "object", + "required": [ + "itemId", + "processId", + "stdin", + "threadId", + "turnId" + ], + "properties": { + "itemId": { + "type": "string" + }, + "processId": { + "type": "string" + }, + "stdin": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "FileChangeOutputDeltaNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "FileChangeOutputDeltaNotification", + "type": "object", + "required": [ + "delta", + "itemId", + "threadId", + "turnId" + ], + "properties": { + "delta": { + "type": "string" + }, + "itemId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "McpToolCallProgressNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "McpToolCallProgressNotification", + "type": "object", + "required": [ + "itemId", + "message", + "threadId", + "turnId" + ], + "properties": { + "itemId": { + "type": "string" + }, + "message": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "McpServerOauthLoginCompletedNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "McpServerOauthLoginCompletedNotification", + "type": "object", + "required": [ + "name", + "success" + ], + "properties": { + "error": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "success": { + "type": "boolean" + } + } + }, + "AuthMode": { + "type": "string", + "enum": [ + "apikey", + "chatgpt" + ] + }, + "AccountUpdatedNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AccountUpdatedNotification", + "type": "object", + "properties": { + "authMode": { + "anyOf": [ + { + "$ref": "#/definitions/v2/AuthMode" + }, + { + "type": "null" + } + ] + } + } + }, + "AccountRateLimitsUpdatedNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AccountRateLimitsUpdatedNotification", + "type": "object", + "required": [ + "rateLimits" + ], + "properties": { + "rateLimits": { + "$ref": "#/definitions/v2/RateLimitSnapshot" + } + } + }, + "ReasoningSummaryTextDeltaNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ReasoningSummaryTextDeltaNotification", + "type": "object", + "required": [ + "delta", + "itemId", + "summaryIndex", + "threadId", + "turnId" + ], + "properties": { + "delta": { + "type": "string" + }, + "itemId": { + "type": "string" + }, + "summaryIndex": { + "type": "integer", + "format": "int64" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "ReasoningSummaryPartAddedNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ReasoningSummaryPartAddedNotification", + "type": "object", + "required": [ + "itemId", + "summaryIndex", + "threadId", + "turnId" + ], + "properties": { + "itemId": { + "type": "string" + }, + "summaryIndex": { + "type": "integer", + "format": "int64" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "ReasoningTextDeltaNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ReasoningTextDeltaNotification", + "type": "object", + "required": [ + "contentIndex", + "delta", + "itemId", + "threadId", + "turnId" + ], + "properties": { + "contentIndex": { + "type": "integer", + "format": "int64" + }, + "delta": { + "type": "string" + }, + "itemId": { + "type": "string" + }, + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "ContextCompactedNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ContextCompactedNotification", + "type": "object", + "required": [ + "threadId", + "turnId" + ], + "properties": { + "threadId": { + "type": "string" + }, + "turnId": { + "type": "string" + } + } + }, + "DeprecationNoticeNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "DeprecationNoticeNotification", + "type": "object", + "required": [ + "summary" + ], + "properties": { + "details": { + "description": "Optional extra guidance, such as migration steps or rationale.", + "type": [ + "string", + "null" + ] + }, + "summary": { + "description": "Concise summary of what is deprecated.", + "type": "string" + } + } + }, + "TextPosition": { + "type": "object", + "required": [ + "column", + "line" + ], + "properties": { + "column": { + "description": "1-based column number (in Unicode scalar values).", + "type": "integer", + "format": "uint", + "minimum": 0 + }, + "line": { + "description": "1-based line number.", + "type": "integer", + "format": "uint", + "minimum": 0 + } + } + }, + "TextRange": { + "type": "object", + "required": [ + "end", + "start" + ], + "properties": { + "end": { + "$ref": "#/definitions/v2/TextPosition" + }, + "start": { + "$ref": "#/definitions/v2/TextPosition" + } + } + }, + "ConfigWarningNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ConfigWarningNotification", + "type": "object", + "required": [ + "summary" + ], + "properties": { + "details": { + "description": "Optional extra guidance or error details.", + "type": [ + "string", + "null" + ] + }, + "path": { + "description": "Optional path to the config file that triggered the warning.", + "type": [ + "string", + "null" + ] + }, + "range": { + "description": "Optional range for the error location inside the config file.", + "anyOf": [ + { + "$ref": "#/definitions/v2/TextRange" + }, + { + "type": "null" + } + ] + }, + "summary": { + "description": "Concise summary of the warning.", + "type": "string" + } + } + }, + "WindowsWorldWritableWarningNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "WindowsWorldWritableWarningNotification", + "type": "object", + "required": [ + "extraCount", + "failedScan", + "samplePaths" + ], + "properties": { + "extraCount": { + "type": "integer", + "format": "uint", + "minimum": 0 + }, + "failedScan": { + "type": "boolean" + }, + "samplePaths": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "AccountLoginCompletedNotification": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AccountLoginCompletedNotification", + "type": "object", + "required": [ + "success" + ], + "properties": { + "error": { + "type": [ + "string", + "null" + ] + }, + "loginId": { + "type": [ + "string", + "null" + ] + }, + "success": { + "type": "boolean" + } + } + } + }, + "InitializeResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InitializeResponse", + "type": "object", + "required": [ + "userAgent" + ], + "properties": { + "userAgent": { + "type": "string" + } + } + }, + "NewConversationResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "NewConversationResponse", + "type": "object", + "required": [ + "conversationId", + "model", + "rolloutPath" + ], + "properties": { + "conversationId": { + "$ref": "#/definitions/ThreadId" + }, + "model": { + "type": "string" + }, + "reasoningEffort": { + "anyOf": [ + { + "$ref": "#/definitions/ReasoningEffort" + }, + { + "type": "null" + } + ] + }, + "rolloutPath": { + "type": "string" + } + } + }, + "ConversationGitInfo": { + "type": "object", + "properties": { + "branch": { + "type": [ + "string", + "null" + ] + }, + "origin_url": { + "type": [ + "string", + "null" + ] + }, + "sha": { + "type": [ + "string", + "null" + ] + } + } + }, + "ConversationSummary": { + "type": "object", + "required": [ + "cliVersion", + "conversationId", + "cwd", + "modelProvider", + "path", + "preview", + "source" + ], + "properties": { + "cliVersion": { + "type": "string" + }, + "conversationId": { + "$ref": "#/definitions/ThreadId" + }, + "cwd": { + "type": "string" + }, + "gitInfo": { + "anyOf": [ + { + "$ref": "#/definitions/ConversationGitInfo" + }, + { + "type": "null" + } + ] + }, + "modelProvider": { + "type": "string" + }, + "path": { + "type": "string" + }, + "preview": { + "type": "string" + }, + "source": { + "$ref": "#/definitions/SessionSource" + }, + "timestamp": { + "type": [ + "string", + "null" + ] + }, + "updatedAt": { + "type": [ + "string", + "null" + ] + } + } + }, + "GetConversationSummaryResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GetConversationSummaryResponse", + "type": "object", + "required": [ + "summary" + ], + "properties": { + "summary": { + "$ref": "#/definitions/ConversationSummary" + } + } + }, + "ListConversationsResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ListConversationsResponse", + "type": "object", + "required": [ + "items" + ], + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/ConversationSummary" + } + }, + "nextCursor": { + "type": [ + "string", + "null" + ] + } + } + }, + "ResumeConversationResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ResumeConversationResponse", + "type": "object", + "required": [ + "conversationId", + "model", + "rolloutPath" + ], + "properties": { + "conversationId": { + "$ref": "#/definitions/ThreadId" + }, + "initialMessages": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/EventMsg" + } + }, + "model": { + "type": "string" + }, + "rolloutPath": { + "type": "string" + } + } + }, + "ForkConversationResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ForkConversationResponse", + "type": "object", + "required": [ + "conversationId", + "model", + "rolloutPath" + ], + "properties": { + "conversationId": { + "$ref": "#/definitions/ThreadId" + }, + "initialMessages": { + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/EventMsg" + } + }, + "model": { + "type": "string" + }, + "rolloutPath": { + "type": "string" + } + } + }, + "ArchiveConversationResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ArchiveConversationResponse", + "type": "object" + }, + "SendUserMessageResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "SendUserMessageResponse", + "type": "object" + }, + "SendUserTurnResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "SendUserTurnResponse", + "type": "object" + }, + "InterruptConversationResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "InterruptConversationResponse", + "type": "object", + "required": [ + "abortReason" + ], + "properties": { + "abortReason": { + "$ref": "#/definitions/TurnAbortReason" + } + } + }, + "AddConversationSubscriptionResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AddConversationSubscriptionResponse", + "type": "object", + "required": [ + "subscriptionId" + ], + "properties": { + "subscriptionId": { + "type": "string" + } + } + }, + "RemoveConversationSubscriptionResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "RemoveConversationSubscriptionResponse", + "type": "object" + }, + "GitSha": { + "type": "string" + }, + "GitDiffToRemoteResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GitDiffToRemoteResponse", + "type": "object", + "required": [ + "diff", + "sha" + ], + "properties": { + "diff": { + "type": "string" + }, + "sha": { + "$ref": "#/definitions/GitSha" + } + } + }, + "LoginApiKeyResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "LoginApiKeyResponse", + "type": "object" + }, + "LoginChatGptResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "LoginChatGptResponse", + "type": "object", + "required": [ + "authUrl", + "loginId" + ], + "properties": { + "authUrl": { + "type": "string" + }, + "loginId": { + "type": "string" + } + } + }, + "CancelLoginChatGptResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CancelLoginChatGptResponse", + "type": "object" + }, + "LogoutChatGptResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "LogoutChatGptResponse", + "type": "object" + }, + "GetAuthStatusResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GetAuthStatusResponse", + "type": "object", + "properties": { + "authMethod": { + "anyOf": [ + { + "$ref": "#/definitions/AuthMode" + }, + { + "type": "null" + } + ] + }, + "authToken": { + "type": [ + "string", + "null" + ] + }, + "requiresOpenaiAuth": { + "type": [ + "boolean", + "null" + ] + } + } + }, + "ForcedLoginMethod": { + "type": "string", + "enum": [ + "chatgpt", + "api" + ] + }, + "Profile": { + "type": "object", + "properties": { + "approvalPolicy": { + "anyOf": [ + { + "$ref": "#/definitions/AskForApproval" + }, + { + "type": "null" + } + ] + }, + "chatgptBaseUrl": { + "type": [ + "string", + "null" + ] + }, + "model": { + "type": [ + "string", + "null" + ] + }, + "modelProvider": { + "type": [ + "string", + "null" + ] + }, + "modelReasoningEffort": { + "anyOf": [ + { + "$ref": "#/definitions/ReasoningEffort" + }, + { + "type": "null" + } + ] + }, + "modelReasoningSummary": { + "anyOf": [ + { + "$ref": "#/definitions/ReasoningSummary" + }, + { + "type": "null" + } + ] + }, + "modelVerbosity": { + "anyOf": [ + { + "$ref": "#/definitions/Verbosity" + }, + { + "type": "null" + } + ] + } + } + }, + "SandboxSettings": { + "type": "object", + "properties": { + "excludeSlashTmp": { + "type": [ + "boolean", + "null" + ] + }, + "excludeTmpdirEnvVar": { + "type": [ + "boolean", + "null" + ] + }, + "networkAccess": { + "type": [ + "boolean", + "null" + ] + }, + "writableRoots": { + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/AbsolutePathBuf" + } + } + } + }, + "Tools": { + "type": "object", + "properties": { + "viewImage": { + "type": [ + "boolean", + "null" + ] + }, + "webSearch": { + "type": [ + "boolean", + "null" + ] + } + } + }, + "UserSavedConfig": { + "type": "object", + "required": [ + "profiles" + ], + "properties": { + "approvalPolicy": { + "anyOf": [ + { + "$ref": "#/definitions/AskForApproval" + }, + { + "type": "null" + } + ] + }, + "forcedChatgptWorkspaceId": { + "type": [ + "string", + "null" + ] + }, + "forcedLoginMethod": { + "anyOf": [ + { + "$ref": "#/definitions/ForcedLoginMethod" + }, + { + "type": "null" + } + ] + }, + "model": { + "type": [ + "string", + "null" + ] + }, + "modelReasoningEffort": { + "anyOf": [ + { + "$ref": "#/definitions/ReasoningEffort" + }, + { + "type": "null" + } + ] + }, + "modelReasoningSummary": { + "anyOf": [ + { + "$ref": "#/definitions/ReasoningSummary" + }, + { + "type": "null" + } + ] + }, + "modelVerbosity": { + "anyOf": [ + { + "$ref": "#/definitions/Verbosity" + }, + { + "type": "null" + } + ] + }, + "profile": { + "type": [ + "string", + "null" + ] + }, + "profiles": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Profile" + } + }, + "sandboxMode": { + "anyOf": [ + { + "$ref": "#/definitions/SandboxMode" + }, + { + "type": "null" + } + ] + }, + "sandboxSettings": { + "anyOf": [ + { + "$ref": "#/definitions/SandboxSettings" + }, + { + "type": "null" + } + ] + }, + "tools": { + "anyOf": [ + { + "$ref": "#/definitions/Tools" + }, + { + "type": "null" + } + ] + } + } + }, + "Verbosity": { + "description": "Controls output length/detail on GPT-5 models via the Responses API. Serialized with lowercase values to match the OpenAI API.", + "type": "string", + "enum": [ + "low", + "medium", + "high" + ] + }, + "GetUserSavedConfigResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GetUserSavedConfigResponse", + "type": "object", + "required": [ + "config" + ], + "properties": { + "config": { + "$ref": "#/definitions/UserSavedConfig" + } + } + }, + "SetDefaultModelResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "SetDefaultModelResponse", + "type": "object" + }, + "GetUserAgentResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "GetUserAgentResponse", + "type": "object", + "required": [ + "userAgent" + ], + "properties": { + "userAgent": { + "type": "string" + } + } + }, + "UserInfoResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "UserInfoResponse", + "type": "object", + "properties": { + "allegedUserEmail": { + "type": [ + "string", + "null" + ] + } + } + }, + "FuzzyFileSearchResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "FuzzyFileSearchResponse", + "type": "object", + "required": [ + "files" + ], + "properties": { + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/FuzzyFileSearchResult" + } + } + } + }, + "ExecOneOffCommandResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecOneOffCommandResponse", + "type": "object", + "required": [ + "exitCode", + "stderr", + "stdout" + ], + "properties": { + "exitCode": { + "type": "integer", + "format": "int32" + }, + "stderr": { + "type": "string" + }, + "stdout": { + "type": "string" + } + } + }, + "CommandExecutionRequestApprovalResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "CommandExecutionRequestApprovalResponse", + "type": "object", + "required": [ + "decision" + ], + "properties": { + "decision": { + "$ref": "#/definitions/CommandExecutionApprovalDecision" + } + } + }, + "FileChangeRequestApprovalResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "FileChangeRequestApprovalResponse", + "type": "object", + "required": [ + "decision" + ], + "properties": { + "decision": { + "$ref": "#/definitions/FileChangeApprovalDecision" + } + } + }, + "ToolRequestUserInputResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ToolRequestUserInputResponse", + "description": "EXPERIMENTAL. Response payload mapping question ids to answers.", + "type": "object", + "required": [ + "answers" + ], + "properties": { + "answers": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/ToolRequestUserInputAnswer" + } + } + } + }, + "ApplyPatchApprovalResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ApplyPatchApprovalResponse", + "type": "object", + "required": [ + "decision" + ], + "properties": { + "decision": { + "$ref": "#/definitions/ReviewDecision" + } + } + }, + "ExecCommandApprovalResponse": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ExecCommandApprovalResponse", + "type": "object", + "required": [ + "decision" + ], + "properties": { + "decision": { + "$ref": "#/definitions/ReviewDecision" + } + } + } + } +} \ No newline at end of file diff --git a/resources/agent-schemas/artifacts/json-schema/opencode.json b/resources/agent-schemas/artifacts/json-schema/opencode.json new file mode 100644 index 0000000..4bd62a2 --- /dev/null +++ b/resources/agent-schemas/artifacts/json-schema/opencode.json @@ -0,0 +1,5921 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://sandbox-agent/schemas/opencode.json", + "title": "OpenCode SDK Schema", + "definitions": { + "Event.installation.updated": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "installation.updated" + }, + "properties": { + "type": "object", + "properties": { + "version": { + "type": "string" + } + }, + "required": [ + "version" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.installation.update-available": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "installation.update-available" + }, + "properties": { + "type": "object", + "properties": { + "version": { + "type": "string" + } + }, + "required": [ + "version" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Project": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "worktree": { + "type": "string" + }, + "vcs": { + "type": "string", + "const": "git" + }, + "name": { + "type": "string" + }, + "icon": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "override": { + "type": "string" + }, + "color": { + "type": "string" + } + } + }, + "commands": { + "type": "object", + "properties": { + "start": { + "description": "Startup script to run when creating a new workspace (worktree)", + "type": "string" + } + } + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + }, + "updated": { + "type": "number" + }, + "initialized": { + "type": "number" + } + }, + "required": [ + "created", + "updated" + ] + }, + "sandboxes": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "id", + "worktree", + "time", + "sandboxes" + ] + }, + "Event.project.updated": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "project.updated" + }, + "properties": { + "$ref": "#/definitions/Project" + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.server.instance.disposed": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "server.instance.disposed" + }, + "properties": { + "type": "object", + "properties": { + "directory": { + "type": "string" + } + }, + "required": [ + "directory" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.server.connected": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "server.connected" + }, + "properties": { + "type": "object", + "properties": {} + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.global.disposed": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "global.disposed" + }, + "properties": { + "type": "object", + "properties": {} + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.lsp.client.diagnostics": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "lsp.client.diagnostics" + }, + "properties": { + "type": "object", + "properties": { + "serverID": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "required": [ + "serverID", + "path" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.lsp.updated": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "lsp.updated" + }, + "properties": { + "type": "object", + "properties": {} + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.file.edited": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "file.edited" + }, + "properties": { + "type": "object", + "properties": { + "file": { + "type": "string" + } + }, + "required": [ + "file" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "FileDiff": { + "type": "object", + "properties": { + "file": { + "type": "string" + }, + "before": { + "type": "string" + }, + "after": { + "type": "string" + }, + "additions": { + "type": "number" + }, + "deletions": { + "type": "number" + } + }, + "required": [ + "file", + "before", + "after", + "additions", + "deletions" + ] + }, + "UserMessage": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sessionID": { + "type": "string" + }, + "role": { + "type": "string", + "const": "user" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + } + }, + "required": [ + "created" + ] + }, + "summary": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "body": { + "type": "string" + }, + "diffs": { + "type": "array", + "items": { + "$ref": "#/definitions/FileDiff" + } + } + }, + "required": [ + "diffs" + ] + }, + "agent": { + "type": "string" + }, + "model": { + "type": "object", + "properties": { + "providerID": { + "type": "string" + }, + "modelID": { + "type": "string" + } + }, + "required": [ + "providerID", + "modelID" + ] + }, + "system": { + "type": "string" + }, + "tools": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "boolean" + } + }, + "variant": { + "type": "string" + } + }, + "required": [ + "id", + "sessionID", + "role", + "time", + "agent", + "model" + ] + }, + "ProviderAuthError": { + "type": "object", + "properties": { + "name": { + "type": "string", + "const": "ProviderAuthError" + }, + "data": { + "type": "object", + "properties": { + "providerID": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "providerID", + "message" + ] + } + }, + "required": [ + "name", + "data" + ] + }, + "UnknownError": { + "type": "object", + "properties": { + "name": { + "type": "string", + "const": "UnknownError" + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ] + } + }, + "required": [ + "name", + "data" + ] + }, + "MessageOutputLengthError": { + "type": "object", + "properties": { + "name": { + "type": "string", + "const": "MessageOutputLengthError" + }, + "data": { + "type": "object", + "properties": {} + } + }, + "required": [ + "name", + "data" + ] + }, + "MessageAbortedError": { + "type": "object", + "properties": { + "name": { + "type": "string", + "const": "MessageAbortedError" + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ] + } + }, + "required": [ + "name", + "data" + ] + }, + "APIError": { + "type": "object", + "properties": { + "name": { + "type": "string", + "const": "APIError" + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "statusCode": { + "type": "number" + }, + "isRetryable": { + "type": "boolean" + }, + "responseHeaders": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "string" + } + }, + "responseBody": { + "type": "string" + }, + "metadata": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "message", + "isRetryable" + ] + } + }, + "required": [ + "name", + "data" + ] + }, + "AssistantMessage": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sessionID": { + "type": "string" + }, + "role": { + "type": "string", + "const": "assistant" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + }, + "completed": { + "type": "number" + } + }, + "required": [ + "created" + ] + }, + "error": { + "anyOf": [ + { + "$ref": "#/definitions/ProviderAuthError" + }, + { + "$ref": "#/definitions/UnknownError" + }, + { + "$ref": "#/definitions/MessageOutputLengthError" + }, + { + "$ref": "#/definitions/MessageAbortedError" + }, + { + "$ref": "#/definitions/APIError" + } + ] + }, + "parentID": { + "type": "string" + }, + "modelID": { + "type": "string" + }, + "providerID": { + "type": "string" + }, + "mode": { + "type": "string" + }, + "agent": { + "type": "string" + }, + "path": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "root": { + "type": "string" + } + }, + "required": [ + "cwd", + "root" + ] + }, + "summary": { + "type": "boolean" + }, + "cost": { + "type": "number" + }, + "tokens": { + "type": "object", + "properties": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "reasoning": { + "type": "number" + }, + "cache": { + "type": "object", + "properties": { + "read": { + "type": "number" + }, + "write": { + "type": "number" + } + }, + "required": [ + "read", + "write" + ] + } + }, + "required": [ + "input", + "output", + "reasoning", + "cache" + ] + }, + "finish": { + "type": "string" + } + }, + "required": [ + "id", + "sessionID", + "role", + "time", + "parentID", + "modelID", + "providerID", + "mode", + "agent", + "path", + "cost", + "tokens" + ] + }, + "Message": { + "anyOf": [ + { + "$ref": "#/definitions/UserMessage" + }, + { + "$ref": "#/definitions/AssistantMessage" + } + ] + }, + "Event.message.updated": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "message.updated" + }, + "properties": { + "type": "object", + "properties": { + "info": { + "$ref": "#/definitions/Message" + } + }, + "required": [ + "info" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.message.removed": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "message.removed" + }, + "properties": { + "type": "object", + "properties": { + "sessionID": { + "type": "string" + }, + "messageID": { + "type": "string" + } + }, + "required": [ + "sessionID", + "messageID" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "TextPart": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sessionID": { + "type": "string" + }, + "messageID": { + "type": "string" + }, + "type": { + "type": "string", + "const": "text" + }, + "text": { + "type": "string" + }, + "synthetic": { + "type": "boolean" + }, + "ignored": { + "type": "boolean" + }, + "time": { + "type": "object", + "properties": { + "start": { + "type": "number" + }, + "end": { + "type": "number" + } + }, + "required": [ + "start" + ] + }, + "metadata": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "text" + ] + }, + "ReasoningPart": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sessionID": { + "type": "string" + }, + "messageID": { + "type": "string" + }, + "type": { + "type": "string", + "const": "reasoning" + }, + "text": { + "type": "string" + }, + "metadata": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "time": { + "type": "object", + "properties": { + "start": { + "type": "number" + }, + "end": { + "type": "number" + } + }, + "required": [ + "start" + ] + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "text", + "time" + ] + }, + "FilePartSourceText": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "start": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "end": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + } + }, + "required": [ + "value", + "start", + "end" + ] + }, + "FileSource": { + "type": "object", + "properties": { + "text": { + "$ref": "#/definitions/FilePartSourceText" + }, + "type": { + "type": "string", + "const": "file" + }, + "path": { + "type": "string" + } + }, + "required": [ + "text", + "type", + "path" + ] + }, + "Range": { + "type": "object", + "properties": { + "start": { + "type": "object", + "properties": { + "line": { + "type": "number" + }, + "character": { + "type": "number" + } + }, + "required": [ + "line", + "character" + ] + }, + "end": { + "type": "object", + "properties": { + "line": { + "type": "number" + }, + "character": { + "type": "number" + } + }, + "required": [ + "line", + "character" + ] + } + }, + "required": [ + "start", + "end" + ] + }, + "SymbolSource": { + "type": "object", + "properties": { + "text": { + "$ref": "#/definitions/FilePartSourceText" + }, + "type": { + "type": "string", + "const": "symbol" + }, + "path": { + "type": "string" + }, + "range": { + "$ref": "#/definitions/Range" + }, + "name": { + "type": "string" + }, + "kind": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + } + }, + "required": [ + "text", + "type", + "path", + "range", + "name", + "kind" + ] + }, + "ResourceSource": { + "type": "object", + "properties": { + "text": { + "$ref": "#/definitions/FilePartSourceText" + }, + "type": { + "type": "string", + "const": "resource" + }, + "clientName": { + "type": "string" + }, + "uri": { + "type": "string" + } + }, + "required": [ + "text", + "type", + "clientName", + "uri" + ] + }, + "FilePartSource": { + "anyOf": [ + { + "$ref": "#/definitions/FileSource" + }, + { + "$ref": "#/definitions/SymbolSource" + }, + { + "$ref": "#/definitions/ResourceSource" + } + ] + }, + "FilePart": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sessionID": { + "type": "string" + }, + "messageID": { + "type": "string" + }, + "type": { + "type": "string", + "const": "file" + }, + "mime": { + "type": "string" + }, + "filename": { + "type": "string" + }, + "url": { + "type": "string" + }, + "source": { + "$ref": "#/definitions/FilePartSource" + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "mime", + "url" + ] + }, + "ToolStatePending": { + "type": "object", + "properties": { + "status": { + "type": "string", + "const": "pending" + }, + "input": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "raw": { + "type": "string" + } + }, + "required": [ + "status", + "input", + "raw" + ] + }, + "ToolStateRunning": { + "type": "object", + "properties": { + "status": { + "type": "string", + "const": "running" + }, + "input": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "title": { + "type": "string" + }, + "metadata": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "time": { + "type": "object", + "properties": { + "start": { + "type": "number" + } + }, + "required": [ + "start" + ] + } + }, + "required": [ + "status", + "input", + "time" + ] + }, + "ToolStateCompleted": { + "type": "object", + "properties": { + "status": { + "type": "string", + "const": "completed" + }, + "input": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "output": { + "type": "string" + }, + "title": { + "type": "string" + }, + "metadata": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "time": { + "type": "object", + "properties": { + "start": { + "type": "number" + }, + "end": { + "type": "number" + }, + "compacted": { + "type": "number" + } + }, + "required": [ + "start", + "end" + ] + }, + "attachments": { + "type": "array", + "items": { + "$ref": "#/definitions/FilePart" + } + } + }, + "required": [ + "status", + "input", + "output", + "title", + "metadata", + "time" + ] + }, + "ToolStateError": { + "type": "object", + "properties": { + "status": { + "type": "string", + "const": "error" + }, + "input": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "error": { + "type": "string" + }, + "metadata": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "time": { + "type": "object", + "properties": { + "start": { + "type": "number" + }, + "end": { + "type": "number" + } + }, + "required": [ + "start", + "end" + ] + } + }, + "required": [ + "status", + "input", + "error", + "time" + ] + }, + "ToolState": { + "anyOf": [ + { + "$ref": "#/definitions/ToolStatePending" + }, + { + "$ref": "#/definitions/ToolStateRunning" + }, + { + "$ref": "#/definitions/ToolStateCompleted" + }, + { + "$ref": "#/definitions/ToolStateError" + } + ] + }, + "ToolPart": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sessionID": { + "type": "string" + }, + "messageID": { + "type": "string" + }, + "type": { + "type": "string", + "const": "tool" + }, + "callID": { + "type": "string" + }, + "tool": { + "type": "string" + }, + "state": { + "$ref": "#/definitions/ToolState" + }, + "metadata": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "callID", + "tool", + "state" + ] + }, + "StepStartPart": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sessionID": { + "type": "string" + }, + "messageID": { + "type": "string" + }, + "type": { + "type": "string", + "const": "step-start" + }, + "snapshot": { + "type": "string" + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type" + ] + }, + "StepFinishPart": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sessionID": { + "type": "string" + }, + "messageID": { + "type": "string" + }, + "type": { + "type": "string", + "const": "step-finish" + }, + "reason": { + "type": "string" + }, + "snapshot": { + "type": "string" + }, + "cost": { + "type": "number" + }, + "tokens": { + "type": "object", + "properties": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "reasoning": { + "type": "number" + }, + "cache": { + "type": "object", + "properties": { + "read": { + "type": "number" + }, + "write": { + "type": "number" + } + }, + "required": [ + "read", + "write" + ] + } + }, + "required": [ + "input", + "output", + "reasoning", + "cache" + ] + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "reason", + "cost", + "tokens" + ] + }, + "SnapshotPart": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sessionID": { + "type": "string" + }, + "messageID": { + "type": "string" + }, + "type": { + "type": "string", + "const": "snapshot" + }, + "snapshot": { + "type": "string" + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "snapshot" + ] + }, + "PatchPart": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sessionID": { + "type": "string" + }, + "messageID": { + "type": "string" + }, + "type": { + "type": "string", + "const": "patch" + }, + "hash": { + "type": "string" + }, + "files": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "hash", + "files" + ] + }, + "AgentPart": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sessionID": { + "type": "string" + }, + "messageID": { + "type": "string" + }, + "type": { + "type": "string", + "const": "agent" + }, + "name": { + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "start": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "end": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + } + }, + "required": [ + "value", + "start", + "end" + ] + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "name" + ] + }, + "RetryPart": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sessionID": { + "type": "string" + }, + "messageID": { + "type": "string" + }, + "type": { + "type": "string", + "const": "retry" + }, + "attempt": { + "type": "number" + }, + "error": { + "$ref": "#/definitions/APIError" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + } + }, + "required": [ + "created" + ] + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "attempt", + "error", + "time" + ] + }, + "CompactionPart": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sessionID": { + "type": "string" + }, + "messageID": { + "type": "string" + }, + "type": { + "type": "string", + "const": "compaction" + }, + "auto": { + "type": "boolean" + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "auto" + ] + }, + "Part": { + "anyOf": [ + { + "$ref": "#/definitions/TextPart" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "sessionID": { + "type": "string" + }, + "messageID": { + "type": "string" + }, + "type": { + "type": "string", + "const": "subtask" + }, + "prompt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "agent": { + "type": "string" + }, + "model": { + "type": "object", + "properties": { + "providerID": { + "type": "string" + }, + "modelID": { + "type": "string" + } + }, + "required": [ + "providerID", + "modelID" + ] + }, + "command": { + "type": "string" + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "prompt", + "description", + "agent" + ] + }, + { + "$ref": "#/definitions/ReasoningPart" + }, + { + "$ref": "#/definitions/FilePart" + }, + { + "$ref": "#/definitions/ToolPart" + }, + { + "$ref": "#/definitions/StepStartPart" + }, + { + "$ref": "#/definitions/StepFinishPart" + }, + { + "$ref": "#/definitions/SnapshotPart" + }, + { + "$ref": "#/definitions/PatchPart" + }, + { + "$ref": "#/definitions/AgentPart" + }, + { + "$ref": "#/definitions/RetryPart" + }, + { + "$ref": "#/definitions/CompactionPart" + } + ] + }, + "Event.message.part.updated": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "message.part.updated" + }, + "properties": { + "type": "object", + "properties": { + "part": { + "$ref": "#/definitions/Part" + }, + "delta": { + "type": "string" + } + }, + "required": [ + "part" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.message.part.removed": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "message.part.removed" + }, + "properties": { + "type": "object", + "properties": { + "sessionID": { + "type": "string" + }, + "messageID": { + "type": "string" + }, + "partID": { + "type": "string" + } + }, + "required": [ + "sessionID", + "messageID", + "partID" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "PermissionRequest": { + "type": "object", + "properties": { + "id": { + "type": "string", + "pattern": "^per.*" + }, + "sessionID": { + "type": "string", + "pattern": "^ses.*" + }, + "permission": { + "type": "string" + }, + "patterns": { + "type": "array", + "items": { + "type": "string" + } + }, + "metadata": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "always": { + "type": "array", + "items": { + "type": "string" + } + }, + "tool": { + "type": "object", + "properties": { + "messageID": { + "type": "string" + }, + "callID": { + "type": "string" + } + }, + "required": [ + "messageID", + "callID" + ] + } + }, + "required": [ + "id", + "sessionID", + "permission", + "patterns", + "metadata", + "always" + ] + }, + "Event.permission.asked": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "permission.asked" + }, + "properties": { + "$ref": "#/definitions/PermissionRequest" + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.permission.replied": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "permission.replied" + }, + "properties": { + "type": "object", + "properties": { + "sessionID": { + "type": "string" + }, + "requestID": { + "type": "string" + }, + "reply": { + "type": "string", + "enum": [ + "once", + "always", + "reject" + ] + } + }, + "required": [ + "sessionID", + "requestID", + "reply" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "SessionStatus": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "idle" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "retry" + }, + "attempt": { + "type": "number" + }, + "message": { + "type": "string" + }, + "next": { + "type": "number" + } + }, + "required": [ + "type", + "attempt", + "message", + "next" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "busy" + } + }, + "required": [ + "type" + ] + } + ] + }, + "Event.session.status": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "session.status" + }, + "properties": { + "type": "object", + "properties": { + "sessionID": { + "type": "string" + }, + "status": { + "$ref": "#/definitions/SessionStatus" + } + }, + "required": [ + "sessionID", + "status" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.session.idle": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "session.idle" + }, + "properties": { + "type": "object", + "properties": { + "sessionID": { + "type": "string" + } + }, + "required": [ + "sessionID" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "QuestionOption": { + "type": "object", + "properties": { + "label": { + "description": "Display text (1-5 words, concise)", + "type": "string" + }, + "description": { + "description": "Explanation of choice", + "type": "string" + } + }, + "required": [ + "label", + "description" + ] + }, + "QuestionInfo": { + "type": "object", + "properties": { + "question": { + "description": "Complete question", + "type": "string" + }, + "header": { + "description": "Very short label (max 30 chars)", + "type": "string" + }, + "options": { + "description": "Available choices", + "type": "array", + "items": { + "$ref": "#/definitions/QuestionOption" + } + }, + "multiple": { + "description": "Allow selecting multiple choices", + "type": "boolean" + }, + "custom": { + "description": "Allow typing a custom answer (default: true)", + "type": "boolean" + } + }, + "required": [ + "question", + "header", + "options" + ] + }, + "QuestionRequest": { + "type": "object", + "properties": { + "id": { + "type": "string", + "pattern": "^que.*" + }, + "sessionID": { + "type": "string", + "pattern": "^ses.*" + }, + "questions": { + "description": "Questions to ask", + "type": "array", + "items": { + "$ref": "#/definitions/QuestionInfo" + } + }, + "tool": { + "type": "object", + "properties": { + "messageID": { + "type": "string" + }, + "callID": { + "type": "string" + } + }, + "required": [ + "messageID", + "callID" + ] + } + }, + "required": [ + "id", + "sessionID", + "questions" + ] + }, + "Event.question.asked": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "question.asked" + }, + "properties": { + "$ref": "#/definitions/QuestionRequest" + } + }, + "required": [ + "type", + "properties" + ] + }, + "QuestionAnswer": { + "type": "array", + "items": { + "type": "string" + } + }, + "Event.question.replied": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "question.replied" + }, + "properties": { + "type": "object", + "properties": { + "sessionID": { + "type": "string" + }, + "requestID": { + "type": "string" + }, + "answers": { + "type": "array", + "items": { + "$ref": "#/definitions/QuestionAnswer" + } + } + }, + "required": [ + "sessionID", + "requestID", + "answers" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.question.rejected": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "question.rejected" + }, + "properties": { + "type": "object", + "properties": { + "sessionID": { + "type": "string" + }, + "requestID": { + "type": "string" + } + }, + "required": [ + "sessionID", + "requestID" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.session.compacted": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "session.compacted" + }, + "properties": { + "type": "object", + "properties": { + "sessionID": { + "type": "string" + } + }, + "required": [ + "sessionID" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Todo": { + "type": "object", + "properties": { + "content": { + "description": "Brief description of the task", + "type": "string" + }, + "status": { + "description": "Current status of the task: pending, in_progress, completed, cancelled", + "type": "string" + }, + "priority": { + "description": "Priority level of the task: high, medium, low", + "type": "string" + }, + "id": { + "description": "Unique identifier for the todo item", + "type": "string" + } + }, + "required": [ + "content", + "status", + "priority", + "id" + ] + }, + "Event.todo.updated": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "todo.updated" + }, + "properties": { + "type": "object", + "properties": { + "sessionID": { + "type": "string" + }, + "todos": { + "type": "array", + "items": { + "$ref": "#/definitions/Todo" + } + } + }, + "required": [ + "sessionID", + "todos" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.file.watcher.updated": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "file.watcher.updated" + }, + "properties": { + "type": "object", + "properties": { + "file": { + "type": "string" + }, + "event": { + "anyOf": [ + { + "type": "string", + "const": "add" + }, + { + "type": "string", + "const": "change" + }, + { + "type": "string", + "const": "unlink" + } + ] + } + }, + "required": [ + "file", + "event" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.tui.prompt.append": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "tui.prompt.append" + }, + "properties": { + "type": "object", + "properties": { + "text": { + "type": "string" + } + }, + "required": [ + "text" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.tui.command.execute": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "tui.command.execute" + }, + "properties": { + "type": "object", + "properties": { + "command": { + "anyOf": [ + { + "type": "string", + "enum": [ + "session.list", + "session.new", + "session.share", + "session.interrupt", + "session.compact", + "session.page.up", + "session.page.down", + "session.line.up", + "session.line.down", + "session.half.page.up", + "session.half.page.down", + "session.first", + "session.last", + "prompt.clear", + "prompt.submit", + "agent.cycle" + ] + }, + { + "type": "string" + } + ] + } + }, + "required": [ + "command" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.tui.toast.show": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "tui.toast.show" + }, + "properties": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "message": { + "type": "string" + }, + "variant": { + "type": "string", + "enum": [ + "info", + "success", + "warning", + "error" + ] + }, + "duration": { + "description": "Duration in milliseconds", + "default": 5000, + "type": "number" + } + }, + "required": [ + "message", + "variant" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.tui.session.select": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "tui.session.select" + }, + "properties": { + "type": "object", + "properties": { + "sessionID": { + "description": "Session ID to navigate to", + "type": "string", + "pattern": "^ses" + } + }, + "required": [ + "sessionID" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.mcp.tools.changed": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "mcp.tools.changed" + }, + "properties": { + "type": "object", + "properties": { + "server": { + "type": "string" + } + }, + "required": [ + "server" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.mcp.browser.open.failed": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "mcp.browser.open.failed" + }, + "properties": { + "type": "object", + "properties": { + "mcpName": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "mcpName", + "url" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.command.executed": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "command.executed" + }, + "properties": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "sessionID": { + "type": "string", + "pattern": "^ses.*" + }, + "arguments": { + "type": "string" + }, + "messageID": { + "type": "string", + "pattern": "^msg.*" + } + }, + "required": [ + "name", + "sessionID", + "arguments", + "messageID" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "PermissionAction": { + "type": "string", + "enum": [ + "allow", + "deny", + "ask" + ] + }, + "PermissionRule": { + "type": "object", + "properties": { + "permission": { + "type": "string" + }, + "pattern": { + "type": "string" + }, + "action": { + "$ref": "#/definitions/PermissionAction" + } + }, + "required": [ + "permission", + "pattern", + "action" + ] + }, + "PermissionRuleset": { + "type": "array", + "items": { + "$ref": "#/definitions/PermissionRule" + } + }, + "Session": { + "type": "object", + "properties": { + "id": { + "type": "string", + "pattern": "^ses.*" + }, + "slug": { + "type": "string" + }, + "projectID": { + "type": "string" + }, + "directory": { + "type": "string" + }, + "parentID": { + "type": "string", + "pattern": "^ses.*" + }, + "summary": { + "type": "object", + "properties": { + "additions": { + "type": "number" + }, + "deletions": { + "type": "number" + }, + "files": { + "type": "number" + }, + "diffs": { + "type": "array", + "items": { + "$ref": "#/definitions/FileDiff" + } + } + }, + "required": [ + "additions", + "deletions", + "files" + ] + }, + "share": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": [ + "url" + ] + }, + "title": { + "type": "string" + }, + "version": { + "type": "string" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + }, + "updated": { + "type": "number" + }, + "compacting": { + "type": "number" + }, + "archived": { + "type": "number" + } + }, + "required": [ + "created", + "updated" + ] + }, + "permission": { + "$ref": "#/definitions/PermissionRuleset" + }, + "revert": { + "type": "object", + "properties": { + "messageID": { + "type": "string" + }, + "partID": { + "type": "string" + }, + "snapshot": { + "type": "string" + }, + "diff": { + "type": "string" + } + }, + "required": [ + "messageID" + ] + } + }, + "required": [ + "id", + "slug", + "projectID", + "directory", + "title", + "version", + "time" + ] + }, + "Event.session.created": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "session.created" + }, + "properties": { + "type": "object", + "properties": { + "info": { + "$ref": "#/definitions/Session" + } + }, + "required": [ + "info" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.session.updated": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "session.updated" + }, + "properties": { + "type": "object", + "properties": { + "info": { + "$ref": "#/definitions/Session" + } + }, + "required": [ + "info" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.session.deleted": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "session.deleted" + }, + "properties": { + "type": "object", + "properties": { + "info": { + "$ref": "#/definitions/Session" + } + }, + "required": [ + "info" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.session.diff": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "session.diff" + }, + "properties": { + "type": "object", + "properties": { + "sessionID": { + "type": "string" + }, + "diff": { + "type": "array", + "items": { + "$ref": "#/definitions/FileDiff" + } + } + }, + "required": [ + "sessionID", + "diff" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.session.error": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "session.error" + }, + "properties": { + "type": "object", + "properties": { + "sessionID": { + "type": "string" + }, + "error": { + "anyOf": [ + { + "$ref": "#/definitions/ProviderAuthError" + }, + { + "$ref": "#/definitions/UnknownError" + }, + { + "$ref": "#/definitions/MessageOutputLengthError" + }, + { + "$ref": "#/definitions/MessageAbortedError" + }, + { + "$ref": "#/definitions/APIError" + } + ] + } + } + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.vcs.branch.updated": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "vcs.branch.updated" + }, + "properties": { + "type": "object", + "properties": { + "branch": { + "type": "string" + } + } + } + }, + "required": [ + "type", + "properties" + ] + }, + "Pty": { + "type": "object", + "properties": { + "id": { + "type": "string", + "pattern": "^pty.*" + }, + "title": { + "type": "string" + }, + "command": { + "type": "string" + }, + "args": { + "type": "array", + "items": { + "type": "string" + } + }, + "cwd": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "running", + "exited" + ] + }, + "pid": { + "type": "number" + } + }, + "required": [ + "id", + "title", + "command", + "args", + "cwd", + "status", + "pid" + ] + }, + "Event.pty.created": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "pty.created" + }, + "properties": { + "type": "object", + "properties": { + "info": { + "$ref": "#/definitions/Pty" + } + }, + "required": [ + "info" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.pty.updated": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "pty.updated" + }, + "properties": { + "type": "object", + "properties": { + "info": { + "$ref": "#/definitions/Pty" + } + }, + "required": [ + "info" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.pty.exited": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "pty.exited" + }, + "properties": { + "type": "object", + "properties": { + "id": { + "type": "string", + "pattern": "^pty.*" + }, + "exitCode": { + "type": "number" + } + }, + "required": [ + "id", + "exitCode" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.pty.deleted": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "pty.deleted" + }, + "properties": { + "type": "object", + "properties": { + "id": { + "type": "string", + "pattern": "^pty.*" + } + }, + "required": [ + "id" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.worktree.ready": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "worktree.ready" + }, + "properties": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "branch": { + "type": "string" + } + }, + "required": [ + "name", + "branch" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event.worktree.failed": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "worktree.failed" + }, + "properties": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ] + } + }, + "required": [ + "type", + "properties" + ] + }, + "Event": { + "anyOf": [ + { + "$ref": "#/definitions/Event.installation.updated" + }, + { + "$ref": "#/definitions/Event.installation.update-available" + }, + { + "$ref": "#/definitions/Event.project.updated" + }, + { + "$ref": "#/definitions/Event.server.instance.disposed" + }, + { + "$ref": "#/definitions/Event.server.connected" + }, + { + "$ref": "#/definitions/Event.global.disposed" + }, + { + "$ref": "#/definitions/Event.lsp.client.diagnostics" + }, + { + "$ref": "#/definitions/Event.lsp.updated" + }, + { + "$ref": "#/definitions/Event.file.edited" + }, + { + "$ref": "#/definitions/Event.message.updated" + }, + { + "$ref": "#/definitions/Event.message.removed" + }, + { + "$ref": "#/definitions/Event.message.part.updated" + }, + { + "$ref": "#/definitions/Event.message.part.removed" + }, + { + "$ref": "#/definitions/Event.permission.asked" + }, + { + "$ref": "#/definitions/Event.permission.replied" + }, + { + "$ref": "#/definitions/Event.session.status" + }, + { + "$ref": "#/definitions/Event.session.idle" + }, + { + "$ref": "#/definitions/Event.question.asked" + }, + { + "$ref": "#/definitions/Event.question.replied" + }, + { + "$ref": "#/definitions/Event.question.rejected" + }, + { + "$ref": "#/definitions/Event.session.compacted" + }, + { + "$ref": "#/definitions/Event.todo.updated" + }, + { + "$ref": "#/definitions/Event.file.watcher.updated" + }, + { + "$ref": "#/definitions/Event.tui.prompt.append" + }, + { + "$ref": "#/definitions/Event.tui.command.execute" + }, + { + "$ref": "#/definitions/Event.tui.toast.show" + }, + { + "$ref": "#/definitions/Event.tui.session.select" + }, + { + "$ref": "#/definitions/Event.mcp.tools.changed" + }, + { + "$ref": "#/definitions/Event.mcp.browser.open.failed" + }, + { + "$ref": "#/definitions/Event.command.executed" + }, + { + "$ref": "#/definitions/Event.session.created" + }, + { + "$ref": "#/definitions/Event.session.updated" + }, + { + "$ref": "#/definitions/Event.session.deleted" + }, + { + "$ref": "#/definitions/Event.session.diff" + }, + { + "$ref": "#/definitions/Event.session.error" + }, + { + "$ref": "#/definitions/Event.vcs.branch.updated" + }, + { + "$ref": "#/definitions/Event.pty.created" + }, + { + "$ref": "#/definitions/Event.pty.updated" + }, + { + "$ref": "#/definitions/Event.pty.exited" + }, + { + "$ref": "#/definitions/Event.pty.deleted" + }, + { + "$ref": "#/definitions/Event.worktree.ready" + }, + { + "$ref": "#/definitions/Event.worktree.failed" + } + ] + }, + "GlobalEvent": { + "type": "object", + "properties": { + "directory": { + "type": "string" + }, + "payload": { + "$ref": "#/definitions/Event" + } + }, + "required": [ + "directory", + "payload" + ] + }, + "BadRequestError": { + "type": "object", + "properties": { + "data": {}, + "errors": { + "type": "array", + "items": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + } + }, + "success": { + "type": "boolean", + "const": false + } + }, + "required": [ + "data", + "errors", + "success" + ] + }, + "NotFoundError": { + "type": "object", + "properties": { + "name": { + "type": "string", + "const": "NotFoundError" + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ] + } + }, + "required": [ + "name", + "data" + ] + }, + "KeybindsConfig": { + "description": "Custom keybind configurations", + "type": "object", + "properties": { + "leader": { + "description": "Leader key for keybind combinations", + "default": "ctrl+x", + "type": "string" + }, + "app_exit": { + "description": "Exit the application", + "default": "ctrl+c,ctrl+d,q", + "type": "string" + }, + "editor_open": { + "description": "Open external editor", + "default": "e", + "type": "string" + }, + "theme_list": { + "description": "List available themes", + "default": "t", + "type": "string" + }, + "sidebar_toggle": { + "description": "Toggle sidebar", + "default": "b", + "type": "string" + }, + "scrollbar_toggle": { + "description": "Toggle session scrollbar", + "default": "none", + "type": "string" + }, + "username_toggle": { + "description": "Toggle username visibility", + "default": "none", + "type": "string" + }, + "status_view": { + "description": "View status", + "default": "s", + "type": "string" + }, + "session_export": { + "description": "Export session to editor", + "default": "x", + "type": "string" + }, + "session_new": { + "description": "Create a new session", + "default": "n", + "type": "string" + }, + "session_list": { + "description": "List all sessions", + "default": "l", + "type": "string" + }, + "session_timeline": { + "description": "Show session timeline", + "default": "g", + "type": "string" + }, + "session_fork": { + "description": "Fork session from message", + "default": "none", + "type": "string" + }, + "session_rename": { + "description": "Rename session", + "default": "ctrl+r", + "type": "string" + }, + "session_delete": { + "description": "Delete session", + "default": "ctrl+d", + "type": "string" + }, + "stash_delete": { + "description": "Delete stash entry", + "default": "ctrl+d", + "type": "string" + }, + "model_provider_list": { + "description": "Open provider list from model dialog", + "default": "ctrl+a", + "type": "string" + }, + "model_favorite_toggle": { + "description": "Toggle model favorite status", + "default": "ctrl+f", + "type": "string" + }, + "session_share": { + "description": "Share current session", + "default": "none", + "type": "string" + }, + "session_unshare": { + "description": "Unshare current session", + "default": "none", + "type": "string" + }, + "session_interrupt": { + "description": "Interrupt current session", + "default": "escape", + "type": "string" + }, + "session_compact": { + "description": "Compact the session", + "default": "c", + "type": "string" + }, + "messages_page_up": { + "description": "Scroll messages up by one page", + "default": "pageup,ctrl+alt+b", + "type": "string" + }, + "messages_page_down": { + "description": "Scroll messages down by one page", + "default": "pagedown,ctrl+alt+f", + "type": "string" + }, + "messages_line_up": { + "description": "Scroll messages up by one line", + "default": "ctrl+alt+y", + "type": "string" + }, + "messages_line_down": { + "description": "Scroll messages down by one line", + "default": "ctrl+alt+e", + "type": "string" + }, + "messages_half_page_up": { + "description": "Scroll messages up by half page", + "default": "ctrl+alt+u", + "type": "string" + }, + "messages_half_page_down": { + "description": "Scroll messages down by half page", + "default": "ctrl+alt+d", + "type": "string" + }, + "messages_first": { + "description": "Navigate to first message", + "default": "ctrl+g,home", + "type": "string" + }, + "messages_last": { + "description": "Navigate to last message", + "default": "ctrl+alt+g,end", + "type": "string" + }, + "messages_next": { + "description": "Navigate to next message", + "default": "none", + "type": "string" + }, + "messages_previous": { + "description": "Navigate to previous message", + "default": "none", + "type": "string" + }, + "messages_last_user": { + "description": "Navigate to last user message", + "default": "none", + "type": "string" + }, + "messages_copy": { + "description": "Copy message", + "default": "y", + "type": "string" + }, + "messages_undo": { + "description": "Undo message", + "default": "u", + "type": "string" + }, + "messages_redo": { + "description": "Redo message", + "default": "r", + "type": "string" + }, + "messages_toggle_conceal": { + "description": "Toggle code block concealment in messages", + "default": "h", + "type": "string" + }, + "tool_details": { + "description": "Toggle tool details visibility", + "default": "none", + "type": "string" + }, + "model_list": { + "description": "List available models", + "default": "m", + "type": "string" + }, + "model_cycle_recent": { + "description": "Next recently used model", + "default": "f2", + "type": "string" + }, + "model_cycle_recent_reverse": { + "description": "Previous recently used model", + "default": "shift+f2", + "type": "string" + }, + "model_cycle_favorite": { + "description": "Next favorite model", + "default": "none", + "type": "string" + }, + "model_cycle_favorite_reverse": { + "description": "Previous favorite model", + "default": "none", + "type": "string" + }, + "command_list": { + "description": "List available commands", + "default": "ctrl+p", + "type": "string" + }, + "agent_list": { + "description": "List agents", + "default": "a", + "type": "string" + }, + "agent_cycle": { + "description": "Next agent", + "default": "tab", + "type": "string" + }, + "agent_cycle_reverse": { + "description": "Previous agent", + "default": "shift+tab", + "type": "string" + }, + "variant_cycle": { + "description": "Cycle model variants", + "default": "ctrl+t", + "type": "string" + }, + "input_clear": { + "description": "Clear input field", + "default": "ctrl+c", + "type": "string" + }, + "input_paste": { + "description": "Paste from clipboard", + "default": "ctrl+v", + "type": "string" + }, + "input_submit": { + "description": "Submit input", + "default": "return", + "type": "string" + }, + "input_newline": { + "description": "Insert newline in input", + "default": "shift+return,ctrl+return,alt+return,ctrl+j", + "type": "string" + }, + "input_move_left": { + "description": "Move cursor left in input", + "default": "left,ctrl+b", + "type": "string" + }, + "input_move_right": { + "description": "Move cursor right in input", + "default": "right,ctrl+f", + "type": "string" + }, + "input_move_up": { + "description": "Move cursor up in input", + "default": "up", + "type": "string" + }, + "input_move_down": { + "description": "Move cursor down in input", + "default": "down", + "type": "string" + }, + "input_select_left": { + "description": "Select left in input", + "default": "shift+left", + "type": "string" + }, + "input_select_right": { + "description": "Select right in input", + "default": "shift+right", + "type": "string" + }, + "input_select_up": { + "description": "Select up in input", + "default": "shift+up", + "type": "string" + }, + "input_select_down": { + "description": "Select down in input", + "default": "shift+down", + "type": "string" + }, + "input_line_home": { + "description": "Move to start of line in input", + "default": "ctrl+a", + "type": "string" + }, + "input_line_end": { + "description": "Move to end of line in input", + "default": "ctrl+e", + "type": "string" + }, + "input_select_line_home": { + "description": "Select to start of line in input", + "default": "ctrl+shift+a", + "type": "string" + }, + "input_select_line_end": { + "description": "Select to end of line in input", + "default": "ctrl+shift+e", + "type": "string" + }, + "input_visual_line_home": { + "description": "Move to start of visual line in input", + "default": "alt+a", + "type": "string" + }, + "input_visual_line_end": { + "description": "Move to end of visual line in input", + "default": "alt+e", + "type": "string" + }, + "input_select_visual_line_home": { + "description": "Select to start of visual line in input", + "default": "alt+shift+a", + "type": "string" + }, + "input_select_visual_line_end": { + "description": "Select to end of visual line in input", + "default": "alt+shift+e", + "type": "string" + }, + "input_buffer_home": { + "description": "Move to start of buffer in input", + "default": "home", + "type": "string" + }, + "input_buffer_end": { + "description": "Move to end of buffer in input", + "default": "end", + "type": "string" + }, + "input_select_buffer_home": { + "description": "Select to start of buffer in input", + "default": "shift+home", + "type": "string" + }, + "input_select_buffer_end": { + "description": "Select to end of buffer in input", + "default": "shift+end", + "type": "string" + }, + "input_delete_line": { + "description": "Delete line in input", + "default": "ctrl+shift+d", + "type": "string" + }, + "input_delete_to_line_end": { + "description": "Delete to end of line in input", + "default": "ctrl+k", + "type": "string" + }, + "input_delete_to_line_start": { + "description": "Delete to start of line in input", + "default": "ctrl+u", + "type": "string" + }, + "input_backspace": { + "description": "Backspace in input", + "default": "backspace,shift+backspace", + "type": "string" + }, + "input_delete": { + "description": "Delete character in input", + "default": "ctrl+d,delete,shift+delete", + "type": "string" + }, + "input_undo": { + "description": "Undo in input", + "default": "ctrl+-,super+z", + "type": "string" + }, + "input_redo": { + "description": "Redo in input", + "default": "ctrl+.,super+shift+z", + "type": "string" + }, + "input_word_forward": { + "description": "Move word forward in input", + "default": "alt+f,alt+right,ctrl+right", + "type": "string" + }, + "input_word_backward": { + "description": "Move word backward in input", + "default": "alt+b,alt+left,ctrl+left", + "type": "string" + }, + "input_select_word_forward": { + "description": "Select word forward in input", + "default": "alt+shift+f,alt+shift+right", + "type": "string" + }, + "input_select_word_backward": { + "description": "Select word backward in input", + "default": "alt+shift+b,alt+shift+left", + "type": "string" + }, + "input_delete_word_forward": { + "description": "Delete word forward in input", + "default": "alt+d,alt+delete,ctrl+delete", + "type": "string" + }, + "input_delete_word_backward": { + "description": "Delete word backward in input", + "default": "ctrl+w,ctrl+backspace,alt+backspace", + "type": "string" + }, + "history_previous": { + "description": "Previous history item", + "default": "up", + "type": "string" + }, + "history_next": { + "description": "Next history item", + "default": "down", + "type": "string" + }, + "session_child_cycle": { + "description": "Next child session", + "default": "right", + "type": "string" + }, + "session_child_cycle_reverse": { + "description": "Previous child session", + "default": "left", + "type": "string" + }, + "session_parent": { + "description": "Go to parent session", + "default": "up", + "type": "string" + }, + "terminal_suspend": { + "description": "Suspend terminal", + "default": "ctrl+z", + "type": "string" + }, + "terminal_title_toggle": { + "description": "Toggle terminal title", + "default": "none", + "type": "string" + }, + "tips_toggle": { + "description": "Toggle tips on home screen", + "default": "h", + "type": "string" + } + }, + "additionalProperties": false + }, + "LogLevel": { + "description": "Log level", + "type": "string", + "enum": [ + "DEBUG", + "INFO", + "WARN", + "ERROR" + ] + }, + "ServerConfig": { + "description": "Server configuration for opencode serve and web commands", + "type": "object", + "properties": { + "port": { + "description": "Port to listen on", + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 9007199254740991 + }, + "hostname": { + "description": "Hostname to listen on", + "type": "string" + }, + "mdns": { + "description": "Enable mDNS service discovery", + "type": "boolean" + }, + "cors": { + "description": "Additional domains to allow for CORS", + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "PermissionActionConfig": { + "type": "string", + "enum": [ + "ask", + "allow", + "deny" + ] + }, + "PermissionObjectConfig": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "$ref": "#/definitions/PermissionActionConfig" + } + }, + "PermissionRuleConfig": { + "anyOf": [ + { + "$ref": "#/definitions/PermissionActionConfig" + }, + { + "$ref": "#/definitions/PermissionObjectConfig" + } + ] + }, + "PermissionConfig": { + "anyOf": [ + { + "type": "object", + "properties": { + "__originalKeys": { + "type": "array", + "items": { + "type": "string" + } + }, + "read": { + "$ref": "#/definitions/PermissionRuleConfig" + }, + "edit": { + "$ref": "#/definitions/PermissionRuleConfig" + }, + "glob": { + "$ref": "#/definitions/PermissionRuleConfig" + }, + "grep": { + "$ref": "#/definitions/PermissionRuleConfig" + }, + "list": { + "$ref": "#/definitions/PermissionRuleConfig" + }, + "bash": { + "$ref": "#/definitions/PermissionRuleConfig" + }, + "task": { + "$ref": "#/definitions/PermissionRuleConfig" + }, + "external_directory": { + "$ref": "#/definitions/PermissionRuleConfig" + }, + "todowrite": { + "$ref": "#/definitions/PermissionActionConfig" + }, + "todoread": { + "$ref": "#/definitions/PermissionActionConfig" + }, + "question": { + "$ref": "#/definitions/PermissionActionConfig" + }, + "webfetch": { + "$ref": "#/definitions/PermissionActionConfig" + }, + "websearch": { + "$ref": "#/definitions/PermissionActionConfig" + }, + "codesearch": { + "$ref": "#/definitions/PermissionActionConfig" + }, + "lsp": { + "$ref": "#/definitions/PermissionRuleConfig" + }, + "doom_loop": { + "$ref": "#/definitions/PermissionActionConfig" + } + }, + "additionalProperties": { + "$ref": "#/definitions/PermissionRuleConfig" + } + }, + { + "$ref": "#/definitions/PermissionActionConfig" + } + ] + }, + "AgentConfig": { + "type": "object", + "properties": { + "model": { + "type": "string" + }, + "temperature": { + "type": "number" + }, + "top_p": { + "type": "number" + }, + "prompt": { + "type": "string" + }, + "tools": { + "description": "@deprecated Use 'permission' field instead", + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "boolean" + } + }, + "disable": { + "type": "boolean" + }, + "description": { + "description": "Description of when to use the agent", + "type": "string" + }, + "mode": { + "type": "string", + "enum": [ + "subagent", + "primary", + "all" + ] + }, + "hidden": { + "description": "Hide this subagent from the @ autocomplete menu (default: false, only applies to mode: subagent)", + "type": "boolean" + }, + "options": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "color": { + "description": "Hex color code for the agent (e.g., #FF5733)", + "type": "string", + "pattern": "^#[0-9a-fA-F]{6}$" + }, + "steps": { + "description": "Maximum number of agentic iterations before forcing text-only response", + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 9007199254740991 + }, + "maxSteps": { + "description": "@deprecated Use 'steps' field instead.", + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 9007199254740991 + }, + "permission": { + "$ref": "#/definitions/PermissionConfig" + } + }, + "additionalProperties": {} + }, + "ProviderConfig": { + "type": "object", + "properties": { + "api": { + "type": "string" + }, + "name": { + "type": "string" + }, + "env": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "string" + }, + "npm": { + "type": "string" + }, + "models": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "family": { + "type": "string" + }, + "release_date": { + "type": "string" + }, + "attachment": { + "type": "boolean" + }, + "reasoning": { + "type": "boolean" + }, + "temperature": { + "type": "boolean" + }, + "tool_call": { + "type": "boolean" + }, + "interleaved": { + "anyOf": [ + { + "type": "boolean", + "const": true + }, + { + "type": "object", + "properties": { + "field": { + "type": "string", + "enum": [ + "reasoning_content", + "reasoning_details" + ] + } + }, + "required": [ + "field" + ], + "additionalProperties": false + } + ] + }, + "cost": { + "type": "object", + "properties": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "cache_read": { + "type": "number" + }, + "cache_write": { + "type": "number" + }, + "context_over_200k": { + "type": "object", + "properties": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "cache_read": { + "type": "number" + }, + "cache_write": { + "type": "number" + } + }, + "required": [ + "input", + "output" + ] + } + }, + "required": [ + "input", + "output" + ] + }, + "limit": { + "type": "object", + "properties": { + "context": { + "type": "number" + }, + "input": { + "type": "number" + }, + "output": { + "type": "number" + } + }, + "required": [ + "context", + "output" + ] + }, + "modalities": { + "type": "object", + "properties": { + "input": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "text", + "audio", + "image", + "video", + "pdf" + ] + } + }, + "output": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "text", + "audio", + "image", + "video", + "pdf" + ] + } + } + }, + "required": [ + "input", + "output" + ] + }, + "experimental": { + "type": "boolean" + }, + "status": { + "type": "string", + "enum": [ + "alpha", + "beta", + "deprecated" + ] + }, + "options": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "headers": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "string" + } + }, + "provider": { + "type": "object", + "properties": { + "npm": { + "type": "string" + } + }, + "required": [ + "npm" + ] + }, + "variants": { + "description": "Variant-specific configuration", + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "object", + "properties": { + "disabled": { + "description": "Disable this variant for the model", + "type": "boolean" + } + }, + "additionalProperties": {} + } + } + } + } + }, + "whitelist": { + "type": "array", + "items": { + "type": "string" + } + }, + "blacklist": { + "type": "array", + "items": { + "type": "string" + } + }, + "options": { + "type": "object", + "properties": { + "apiKey": { + "type": "string" + }, + "baseURL": { + "type": "string" + }, + "enterpriseUrl": { + "description": "GitHub Enterprise URL for copilot authentication", + "type": "string" + }, + "setCacheKey": { + "description": "Enable promptCacheKey for this provider (default false)", + "type": "boolean" + }, + "timeout": { + "description": "Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.", + "anyOf": [ + { + "description": "Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.", + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 9007199254740991 + }, + { + "description": "Disable timeout for this provider entirely.", + "type": "boolean", + "const": false + } + ] + } + }, + "additionalProperties": {} + } + }, + "additionalProperties": false + }, + "McpLocalConfig": { + "type": "object", + "properties": { + "type": { + "description": "Type of MCP server connection", + "type": "string", + "const": "local" + }, + "command": { + "description": "Command and arguments to run the MCP server", + "type": "array", + "items": { + "type": "string" + } + }, + "environment": { + "description": "Environment variables to set when running the MCP server", + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "string" + } + }, + "enabled": { + "description": "Enable or disable the MCP server on startup", + "type": "boolean" + }, + "timeout": { + "description": "Timeout in ms for MCP server requests. Defaults to 5000 (5 seconds) if not specified.", + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "type", + "command" + ], + "additionalProperties": false + }, + "McpOAuthConfig": { + "type": "object", + "properties": { + "clientId": { + "description": "OAuth client ID. If not provided, dynamic client registration (RFC 7591) will be attempted.", + "type": "string" + }, + "clientSecret": { + "description": "OAuth client secret (if required by the authorization server)", + "type": "string" + }, + "scope": { + "description": "OAuth scopes to request during authorization", + "type": "string" + } + }, + "additionalProperties": false + }, + "McpRemoteConfig": { + "type": "object", + "properties": { + "type": { + "description": "Type of MCP server connection", + "type": "string", + "const": "remote" + }, + "url": { + "description": "URL of the remote MCP server", + "type": "string" + }, + "enabled": { + "description": "Enable or disable the MCP server on startup", + "type": "boolean" + }, + "headers": { + "description": "Headers to send with the request", + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "string" + } + }, + "oauth": { + "description": "OAuth authentication configuration for the MCP server. Set to false to disable OAuth auto-detection.", + "anyOf": [ + { + "$ref": "#/definitions/McpOAuthConfig" + }, + { + "type": "boolean", + "const": false + } + ] + }, + "timeout": { + "description": "Timeout in ms for MCP server requests. Defaults to 5000 (5 seconds) if not specified.", + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "type", + "url" + ], + "additionalProperties": false + }, + "LayoutConfig": { + "description": "@deprecated Always uses stretch layout.", + "type": "string", + "enum": [ + "auto", + "stretch" + ] + }, + "Config": { + "type": "object", + "properties": { + "$schema": { + "description": "JSON schema reference for configuration validation", + "type": "string" + }, + "theme": { + "description": "Theme name to use for the interface", + "type": "string" + }, + "keybinds": { + "$ref": "#/definitions/KeybindsConfig" + }, + "logLevel": { + "$ref": "#/definitions/LogLevel" + }, + "tui": { + "description": "TUI specific settings", + "type": "object", + "properties": { + "scroll_speed": { + "description": "TUI scroll speed", + "type": "number", + "minimum": 0.001 + }, + "scroll_acceleration": { + "description": "Scroll acceleration settings", + "type": "object", + "properties": { + "enabled": { + "description": "Enable scroll acceleration", + "type": "boolean" + } + }, + "required": [ + "enabled" + ] + }, + "diff_style": { + "description": "Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column", + "type": "string", + "enum": [ + "auto", + "stacked" + ] + } + } + }, + "server": { + "$ref": "#/definitions/ServerConfig" + }, + "command": { + "description": "Command configuration, see https://opencode.ai/docs/commands", + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "object", + "properties": { + "template": { + "type": "string" + }, + "description": { + "type": "string" + }, + "agent": { + "type": "string" + }, + "model": { + "type": "string" + }, + "subtask": { + "type": "boolean" + } + }, + "required": [ + "template" + ] + } + }, + "watcher": { + "type": "object", + "properties": { + "ignore": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "plugin": { + "type": "array", + "items": { + "type": "string" + } + }, + "snapshot": { + "type": "boolean" + }, + "share": { + "description": "Control sharing behavior:'manual' allows manual sharing via commands, 'auto' enables automatic sharing, 'disabled' disables all sharing", + "type": "string", + "enum": [ + "manual", + "auto", + "disabled" + ] + }, + "autoshare": { + "description": "@deprecated Use 'share' field instead. Share newly created sessions automatically", + "type": "boolean" + }, + "autoupdate": { + "description": "Automatically update to the latest version. Set to true to auto-update, false to disable, or 'notify' to show update notifications", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "string", + "const": "notify" + } + ] + }, + "disabled_providers": { + "description": "Disable providers that are loaded automatically", + "type": "array", + "items": { + "type": "string" + } + }, + "enabled_providers": { + "description": "When set, ONLY these providers will be enabled. All other providers will be ignored", + "type": "array", + "items": { + "type": "string" + } + }, + "model": { + "description": "Model to use in the format of provider/model, eg anthropic/claude-2", + "type": "string" + }, + "small_model": { + "description": "Small model to use for tasks like title generation in the format of provider/model", + "type": "string" + }, + "default_agent": { + "description": "Default agent to use when none is specified. Must be a primary agent. Falls back to 'build' if not set or if the specified agent is invalid.", + "type": "string" + }, + "username": { + "description": "Custom username to display in conversations instead of system username", + "type": "string" + }, + "mode": { + "description": "@deprecated Use `agent` field instead.", + "type": "object", + "properties": { + "build": { + "$ref": "#/definitions/AgentConfig" + }, + "plan": { + "$ref": "#/definitions/AgentConfig" + } + }, + "additionalProperties": { + "$ref": "#/definitions/AgentConfig" + } + }, + "agent": { + "description": "Agent configuration, see https://opencode.ai/docs/agents", + "type": "object", + "properties": { + "plan": { + "$ref": "#/definitions/AgentConfig" + }, + "build": { + "$ref": "#/definitions/AgentConfig" + }, + "general": { + "$ref": "#/definitions/AgentConfig" + }, + "explore": { + "$ref": "#/definitions/AgentConfig" + }, + "title": { + "$ref": "#/definitions/AgentConfig" + }, + "summary": { + "$ref": "#/definitions/AgentConfig" + }, + "compaction": { + "$ref": "#/definitions/AgentConfig" + } + }, + "additionalProperties": { + "$ref": "#/definitions/AgentConfig" + } + }, + "provider": { + "description": "Custom provider configurations and model overrides", + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "$ref": "#/definitions/ProviderConfig" + } + }, + "mcp": { + "description": "MCP (Model Context Protocol) server configurations", + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/definitions/McpLocalConfig" + }, + { + "$ref": "#/definitions/McpRemoteConfig" + } + ] + }, + { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": [ + "enabled" + ], + "additionalProperties": false + } + ] + } + }, + "formatter": { + "anyOf": [ + { + "type": "boolean", + "const": false + }, + { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "object", + "properties": { + "disabled": { + "type": "boolean" + }, + "command": { + "type": "array", + "items": { + "type": "string" + } + }, + "environment": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "string" + } + }, + "extensions": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + ] + }, + "lsp": { + "anyOf": [ + { + "type": "boolean", + "const": false + }, + { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "anyOf": [ + { + "type": "object", + "properties": { + "disabled": { + "type": "boolean", + "const": true + } + }, + "required": [ + "disabled" + ] + }, + { + "type": "object", + "properties": { + "command": { + "type": "array", + "items": { + "type": "string" + } + }, + "extensions": { + "type": "array", + "items": { + "type": "string" + } + }, + "disabled": { + "type": "boolean" + }, + "env": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "string" + } + }, + "initialization": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + } + }, + "required": [ + "command" + ] + } + ] + } + } + ] + }, + "instructions": { + "description": "Additional instruction files or patterns to include", + "type": "array", + "items": { + "type": "string" + } + }, + "layout": { + "$ref": "#/definitions/LayoutConfig" + }, + "permission": { + "$ref": "#/definitions/PermissionConfig" + }, + "tools": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "boolean" + } + }, + "enterprise": { + "type": "object", + "properties": { + "url": { + "description": "Enterprise URL", + "type": "string" + } + } + }, + "compaction": { + "type": "object", + "properties": { + "auto": { + "description": "Enable automatic compaction when context is full (default: true)", + "type": "boolean" + }, + "prune": { + "description": "Enable pruning of old tool outputs (default: true)", + "type": "boolean" + } + } + }, + "experimental": { + "type": "object", + "properties": { + "hook": { + "type": "object", + "properties": { + "file_edited": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "array", + "items": { + "type": "object", + "properties": { + "command": { + "type": "array", + "items": { + "type": "string" + } + }, + "environment": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "command" + ] + } + } + }, + "session_completed": { + "type": "array", + "items": { + "type": "object", + "properties": { + "command": { + "type": "array", + "items": { + "type": "string" + } + }, + "environment": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "command" + ] + } + } + } + }, + "chatMaxRetries": { + "description": "Number of retries for chat completions on failure", + "type": "number" + }, + "disable_paste_summary": { + "type": "boolean" + }, + "batch_tool": { + "description": "Enable the batch tool", + "type": "boolean" + }, + "openTelemetry": { + "description": "Enable OpenTelemetry spans for AI SDK calls (using the 'experimental_telemetry' flag)", + "type": "boolean" + }, + "primary_tools": { + "description": "Tools that should only be available to primary agents.", + "type": "array", + "items": { + "type": "string" + } + }, + "continue_loop_on_deny": { + "description": "Continue the agent loop when a tool call is denied", + "type": "boolean" + }, + "mcp_timeout": { + "description": "Timeout in milliseconds for model context protocol (MCP) requests", + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 9007199254740991 + } + } + } + }, + "additionalProperties": false + }, + "Model": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "providerID": { + "type": "string" + }, + "api": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "url": { + "type": "string" + }, + "npm": { + "type": "string" + } + }, + "required": [ + "id", + "url", + "npm" + ] + }, + "name": { + "type": "string" + }, + "family": { + "type": "string" + }, + "capabilities": { + "type": "object", + "properties": { + "temperature": { + "type": "boolean" + }, + "reasoning": { + "type": "boolean" + }, + "attachment": { + "type": "boolean" + }, + "toolcall": { + "type": "boolean" + }, + "input": { + "type": "object", + "properties": { + "text": { + "type": "boolean" + }, + "audio": { + "type": "boolean" + }, + "image": { + "type": "boolean" + }, + "video": { + "type": "boolean" + }, + "pdf": { + "type": "boolean" + } + }, + "required": [ + "text", + "audio", + "image", + "video", + "pdf" + ] + }, + "output": { + "type": "object", + "properties": { + "text": { + "type": "boolean" + }, + "audio": { + "type": "boolean" + }, + "image": { + "type": "boolean" + }, + "video": { + "type": "boolean" + }, + "pdf": { + "type": "boolean" + } + }, + "required": [ + "text", + "audio", + "image", + "video", + "pdf" + ] + }, + "interleaved": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "properties": { + "field": { + "type": "string", + "enum": [ + "reasoning_content", + "reasoning_details" + ] + } + }, + "required": [ + "field" + ] + } + ] + } + }, + "required": [ + "temperature", + "reasoning", + "attachment", + "toolcall", + "input", + "output", + "interleaved" + ] + }, + "cost": { + "type": "object", + "properties": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "cache": { + "type": "object", + "properties": { + "read": { + "type": "number" + }, + "write": { + "type": "number" + } + }, + "required": [ + "read", + "write" + ] + }, + "experimentalOver200K": { + "type": "object", + "properties": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "cache": { + "type": "object", + "properties": { + "read": { + "type": "number" + }, + "write": { + "type": "number" + } + }, + "required": [ + "read", + "write" + ] + } + }, + "required": [ + "input", + "output", + "cache" + ] + } + }, + "required": [ + "input", + "output", + "cache" + ] + }, + "limit": { + "type": "object", + "properties": { + "context": { + "type": "number" + }, + "input": { + "type": "number" + }, + "output": { + "type": "number" + } + }, + "required": [ + "context", + "output" + ] + }, + "status": { + "type": "string", + "enum": [ + "alpha", + "beta", + "deprecated", + "active" + ] + }, + "options": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "headers": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "string" + } + }, + "release_date": { + "type": "string" + }, + "variants": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + } + } + }, + "required": [ + "id", + "providerID", + "api", + "name", + "capabilities", + "cost", + "limit", + "status", + "options", + "headers", + "release_date" + ] + }, + "Provider": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "source": { + "type": "string", + "enum": [ + "env", + "config", + "custom", + "api" + ] + }, + "env": { + "type": "array", + "items": { + "type": "string" + } + }, + "key": { + "type": "string" + }, + "options": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "models": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "$ref": "#/definitions/Model" + } + } + }, + "required": [ + "id", + "name", + "source", + "env", + "options", + "models" + ] + }, + "ToolIDs": { + "type": "array", + "items": { + "type": "string" + } + }, + "ToolListItem": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "parameters": {} + }, + "required": [ + "id", + "description", + "parameters" + ] + }, + "ToolList": { + "type": "array", + "items": { + "$ref": "#/definitions/ToolListItem" + } + }, + "Worktree": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "branch": { + "type": "string" + }, + "directory": { + "type": "string" + } + }, + "required": [ + "name", + "branch", + "directory" + ] + }, + "WorktreeCreateInput": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "startCommand": { + "description": "Additional startup script to run after the project's start command", + "type": "string" + } + } + }, + "WorktreeRemoveInput": { + "type": "object", + "properties": { + "directory": { + "type": "string" + } + }, + "required": [ + "directory" + ] + }, + "WorktreeResetInput": { + "type": "object", + "properties": { + "directory": { + "type": "string" + } + }, + "required": [ + "directory" + ] + }, + "McpResource": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "uri": { + "type": "string" + }, + "description": { + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "client": { + "type": "string" + } + }, + "required": [ + "name", + "uri", + "client" + ] + }, + "TextPartInput": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "const": "text" + }, + "text": { + "type": "string" + }, + "synthetic": { + "type": "boolean" + }, + "ignored": { + "type": "boolean" + }, + "time": { + "type": "object", + "properties": { + "start": { + "type": "number" + }, + "end": { + "type": "number" + } + }, + "required": [ + "start" + ] + }, + "metadata": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + } + }, + "required": [ + "type", + "text" + ] + }, + "FilePartInput": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "const": "file" + }, + "mime": { + "type": "string" + }, + "filename": { + "type": "string" + }, + "url": { + "type": "string" + }, + "source": { + "$ref": "#/definitions/FilePartSource" + } + }, + "required": [ + "type", + "mime", + "url" + ] + }, + "AgentPartInput": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "const": "agent" + }, + "name": { + "type": "string" + }, + "source": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "start": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "end": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + } + }, + "required": [ + "value", + "start", + "end" + ] + } + }, + "required": [ + "type", + "name" + ] + }, + "SubtaskPartInput": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "const": "subtask" + }, + "prompt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "agent": { + "type": "string" + }, + "model": { + "type": "object", + "properties": { + "providerID": { + "type": "string" + }, + "modelID": { + "type": "string" + } + }, + "required": [ + "providerID", + "modelID" + ] + }, + "command": { + "type": "string" + } + }, + "required": [ + "type", + "prompt", + "description", + "agent" + ] + }, + "ProviderAuthMethod": { + "type": "object", + "properties": { + "type": { + "anyOf": [ + { + "type": "string", + "const": "oauth" + }, + { + "type": "string", + "const": "api" + } + ] + }, + "label": { + "type": "string" + } + }, + "required": [ + "type", + "label" + ] + }, + "ProviderAuthAuthorization": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "method": { + "anyOf": [ + { + "type": "string", + "const": "auto" + }, + { + "type": "string", + "const": "code" + } + ] + }, + "instructions": { + "type": "string" + } + }, + "required": [ + "url", + "method", + "instructions" + ] + }, + "Symbol": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "kind": { + "type": "number" + }, + "location": { + "type": "object", + "properties": { + "uri": { + "type": "string" + }, + "range": { + "$ref": "#/definitions/Range" + } + }, + "required": [ + "uri", + "range" + ] + } + }, + "required": [ + "name", + "kind", + "location" + ] + }, + "FileNode": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "absolute": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "file", + "directory" + ] + }, + "ignored": { + "type": "boolean" + } + }, + "required": [ + "name", + "path", + "absolute", + "type", + "ignored" + ] + }, + "FileContent": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "text" + }, + "content": { + "type": "string" + }, + "diff": { + "type": "string" + }, + "patch": { + "type": "object", + "properties": { + "oldFileName": { + "type": "string" + }, + "newFileName": { + "type": "string" + }, + "oldHeader": { + "type": "string" + }, + "newHeader": { + "type": "string" + }, + "hunks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "oldStart": { + "type": "number" + }, + "oldLines": { + "type": "number" + }, + "newStart": { + "type": "number" + }, + "newLines": { + "type": "number" + }, + "lines": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "oldStart", + "oldLines", + "newStart", + "newLines", + "lines" + ] + } + }, + "index": { + "type": "string" + } + }, + "required": [ + "oldFileName", + "newFileName", + "hunks" + ] + }, + "encoding": { + "type": "string", + "const": "base64" + }, + "mimeType": { + "type": "string" + } + }, + "required": [ + "type", + "content" + ] + }, + "File": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "added": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "removed": { + "type": "integer", + "minimum": -9007199254740991, + "maximum": 9007199254740991 + }, + "status": { + "type": "string", + "enum": [ + "added", + "deleted", + "modified" + ] + } + }, + "required": [ + "path", + "added", + "removed", + "status" + ] + }, + "MCPStatusConnected": { + "type": "object", + "properties": { + "status": { + "type": "string", + "const": "connected" + } + }, + "required": [ + "status" + ] + }, + "MCPStatusDisabled": { + "type": "object", + "properties": { + "status": { + "type": "string", + "const": "disabled" + } + }, + "required": [ + "status" + ] + }, + "MCPStatusFailed": { + "type": "object", + "properties": { + "status": { + "type": "string", + "const": "failed" + }, + "error": { + "type": "string" + } + }, + "required": [ + "status", + "error" + ] + }, + "MCPStatusNeedsAuth": { + "type": "object", + "properties": { + "status": { + "type": "string", + "const": "needs_auth" + } + }, + "required": [ + "status" + ] + }, + "MCPStatusNeedsClientRegistration": { + "type": "object", + "properties": { + "status": { + "type": "string", + "const": "needs_client_registration" + }, + "error": { + "type": "string" + } + }, + "required": [ + "status", + "error" + ] + }, + "MCPStatus": { + "anyOf": [ + { + "$ref": "#/definitions/MCPStatusConnected" + }, + { + "$ref": "#/definitions/MCPStatusDisabled" + }, + { + "$ref": "#/definitions/MCPStatusFailed" + }, + { + "$ref": "#/definitions/MCPStatusNeedsAuth" + }, + { + "$ref": "#/definitions/MCPStatusNeedsClientRegistration" + } + ] + }, + "Path": { + "type": "object", + "properties": { + "home": { + "type": "string" + }, + "state": { + "type": "string" + }, + "config": { + "type": "string" + }, + "worktree": { + "type": "string" + }, + "directory": { + "type": "string" + } + }, + "required": [ + "home", + "state", + "config", + "worktree", + "directory" + ] + }, + "VcsInfo": { + "type": "object", + "properties": { + "branch": { + "type": "string" + } + }, + "required": [ + "branch" + ] + }, + "Command": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "agent": { + "type": "string" + }, + "model": { + "type": "string" + }, + "mcp": { + "type": "boolean" + }, + "template": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string" + } + ] + }, + "subtask": { + "type": "boolean" + }, + "hints": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "name", + "template", + "hints" + ] + }, + "Agent": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "mode": { + "type": "string", + "enum": [ + "subagent", + "primary", + "all" + ] + }, + "native": { + "type": "boolean" + }, + "hidden": { + "type": "boolean" + }, + "topP": { + "type": "number" + }, + "temperature": { + "type": "number" + }, + "color": { + "type": "string" + }, + "permission": { + "$ref": "#/definitions/PermissionRuleset" + }, + "model": { + "type": "object", + "properties": { + "modelID": { + "type": "string" + }, + "providerID": { + "type": "string" + } + }, + "required": [ + "modelID", + "providerID" + ] + }, + "prompt": { + "type": "string" + }, + "options": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, + "steps": { + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 9007199254740991 + } + }, + "required": [ + "name", + "mode", + "permission", + "options" + ] + }, + "LSPStatus": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "root": { + "type": "string" + }, + "status": { + "anyOf": [ + { + "type": "string", + "const": "connected" + }, + { + "type": "string", + "const": "error" + } + ] + } + }, + "required": [ + "id", + "name", + "root", + "status" + ] + }, + "FormatterStatus": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "extensions": { + "type": "array", + "items": { + "type": "string" + } + }, + "enabled": { + "type": "boolean" + } + }, + "required": [ + "name", + "extensions", + "enabled" + ] + }, + "OAuth": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "oauth" + }, + "refresh": { + "type": "string" + }, + "access": { + "type": "string" + }, + "expires": { + "type": "number" + }, + "accountId": { + "type": "string" + }, + "enterpriseUrl": { + "type": "string" + } + }, + "required": [ + "type", + "refresh", + "access", + "expires" + ] + }, + "ApiAuth": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "api" + }, + "key": { + "type": "string" + } + }, + "required": [ + "type", + "key" + ] + }, + "WellKnownAuth": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "wellknown" + }, + "key": { + "type": "string" + }, + "token": { + "type": "string" + } + }, + "required": [ + "type", + "key", + "token" + ] + }, + "Auth": { + "anyOf": [ + { + "$ref": "#/definitions/OAuth" + }, + { + "$ref": "#/definitions/ApiAuth" + }, + { + "$ref": "#/definitions/WellKnownAuth" + } + ] + } + } +} \ No newline at end of file diff --git a/resources/agent-schemas/deno.lock b/resources/agent-schemas/deno.lock new file mode 100644 index 0000000..ccc48bd --- /dev/null +++ b/resources/agent-schemas/deno.lock @@ -0,0 +1,695 @@ +{ + "version": "5", + "specifiers": { + "npm:@anthropic-ai/claude-code@latest": "2.1.19", + "npm:@openai/codex@latest": "0.91.0", + "npm:@types/node@22": "22.19.7", + "npm:cheerio@1": "1.2.0", + "npm:ts-json-schema-generator@^2.4.0": "2.4.0", + "npm:tsx@^4.19.0": "4.21.0", + "npm:typescript@^5.7.0": "5.9.3" + }, + "npm": { + "@anthropic-ai/claude-code@2.1.19": { + "integrity": "sha512-/bUlQuX/6nKr1Zqfi/9Q6xf7WonUBk72ZfKKENU4WVrIFWqTv/0JJsoW/dHol9QBNHvyfKIeBbYu4avHNRAnuQ==", + "optionalDependencies": [ + "@img/sharp-darwin-arm64", + "@img/sharp-darwin-x64", + "@img/sharp-linux-arm", + "@img/sharp-linux-arm64", + "@img/sharp-linux-x64", + "@img/sharp-linuxmusl-arm64", + "@img/sharp-linuxmusl-x64", + "@img/sharp-win32-x64" + ], + "bin": true + }, + "@esbuild/aix-ppc64@0.27.2": { + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "os": ["aix"], + "cpu": ["ppc64"] + }, + "@esbuild/android-arm64@0.27.2": { + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "os": ["android"], + "cpu": ["arm64"] + }, + "@esbuild/android-arm@0.27.2": { + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "os": ["android"], + "cpu": ["arm"] + }, + "@esbuild/android-x64@0.27.2": { + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "os": ["android"], + "cpu": ["x64"] + }, + "@esbuild/darwin-arm64@0.27.2": { + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@esbuild/darwin-x64@0.27.2": { + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@esbuild/freebsd-arm64@0.27.2": { + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "os": ["freebsd"], + "cpu": ["arm64"] + }, + "@esbuild/freebsd-x64@0.27.2": { + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "os": ["freebsd"], + "cpu": ["x64"] + }, + "@esbuild/linux-arm64@0.27.2": { + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@esbuild/linux-arm@0.27.2": { + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@esbuild/linux-ia32@0.27.2": { + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "os": ["linux"], + "cpu": ["ia32"] + }, + "@esbuild/linux-loong64@0.27.2": { + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "os": ["linux"], + "cpu": ["loong64"] + }, + "@esbuild/linux-mips64el@0.27.2": { + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "os": ["linux"], + "cpu": ["mips64el"] + }, + "@esbuild/linux-ppc64@0.27.2": { + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "os": ["linux"], + "cpu": ["ppc64"] + }, + "@esbuild/linux-riscv64@0.27.2": { + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "os": ["linux"], + "cpu": ["riscv64"] + }, + "@esbuild/linux-s390x@0.27.2": { + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "os": ["linux"], + "cpu": ["s390x"] + }, + "@esbuild/linux-x64@0.27.2": { + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@esbuild/netbsd-arm64@0.27.2": { + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "os": ["netbsd"], + "cpu": ["arm64"] + }, + "@esbuild/netbsd-x64@0.27.2": { + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "os": ["netbsd"], + "cpu": ["x64"] + }, + "@esbuild/openbsd-arm64@0.27.2": { + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "os": ["openbsd"], + "cpu": ["arm64"] + }, + "@esbuild/openbsd-x64@0.27.2": { + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "os": ["openbsd"], + "cpu": ["x64"] + }, + "@esbuild/openharmony-arm64@0.27.2": { + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "os": ["openharmony"], + "cpu": ["arm64"] + }, + "@esbuild/sunos-x64@0.27.2": { + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "os": ["sunos"], + "cpu": ["x64"] + }, + "@esbuild/win32-arm64@0.27.2": { + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "os": ["win32"], + "cpu": ["arm64"] + }, + "@esbuild/win32-ia32@0.27.2": { + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "os": ["win32"], + "cpu": ["ia32"] + }, + "@esbuild/win32-x64@0.27.2": { + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@img/sharp-darwin-arm64@0.33.5": { + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "optionalDependencies": [ + "@img/sharp-libvips-darwin-arm64" + ], + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@img/sharp-darwin-x64@0.33.5": { + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "optionalDependencies": [ + "@img/sharp-libvips-darwin-x64" + ], + "os": ["darwin"], + "cpu": ["x64"] + }, + "@img/sharp-libvips-darwin-arm64@1.0.4": { + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "os": ["darwin"], + "cpu": ["arm64"] + }, + "@img/sharp-libvips-darwin-x64@1.0.4": { + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "os": ["darwin"], + "cpu": ["x64"] + }, + "@img/sharp-libvips-linux-arm64@1.0.4": { + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@img/sharp-libvips-linux-arm@1.0.5": { + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "os": ["linux"], + "cpu": ["arm"] + }, + "@img/sharp-libvips-linux-x64@1.0.4": { + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@img/sharp-libvips-linuxmusl-arm64@1.0.4": { + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "os": ["linux"], + "cpu": ["arm64"] + }, + "@img/sharp-libvips-linuxmusl-x64@1.0.4": { + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "os": ["linux"], + "cpu": ["x64"] + }, + "@img/sharp-linux-arm64@0.33.5": { + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "optionalDependencies": [ + "@img/sharp-libvips-linux-arm64" + ], + "os": ["linux"], + "cpu": ["arm64"] + }, + "@img/sharp-linux-arm@0.33.5": { + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "optionalDependencies": [ + "@img/sharp-libvips-linux-arm" + ], + "os": ["linux"], + "cpu": ["arm"] + }, + "@img/sharp-linux-x64@0.33.5": { + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "optionalDependencies": [ + "@img/sharp-libvips-linux-x64" + ], + "os": ["linux"], + "cpu": ["x64"] + }, + "@img/sharp-linuxmusl-arm64@0.33.5": { + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "optionalDependencies": [ + "@img/sharp-libvips-linuxmusl-arm64" + ], + "os": ["linux"], + "cpu": ["arm64"] + }, + "@img/sharp-linuxmusl-x64@0.33.5": { + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "optionalDependencies": [ + "@img/sharp-libvips-linuxmusl-x64" + ], + "os": ["linux"], + "cpu": ["x64"] + }, + "@img/sharp-win32-x64@0.33.5": { + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "os": ["win32"], + "cpu": ["x64"] + }, + "@isaacs/balanced-match@4.0.1": { + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==" + }, + "@isaacs/brace-expansion@5.0.0": { + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dependencies": [ + "@isaacs/balanced-match" + ] + }, + "@isaacs/cliui@8.0.2": { + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dependencies": [ + "string-width@5.1.2", + "string-width-cjs@npm:string-width@4.2.3", + "strip-ansi@7.1.2", + "strip-ansi-cjs@npm:strip-ansi@6.0.1", + "wrap-ansi@8.1.0", + "wrap-ansi-cjs@npm:wrap-ansi@7.0.0" + ] + }, + "@openai/codex@0.91.0": { + "integrity": "sha512-eRLRg0+uM0g0iW+Ca5VedBk+laslLcq93Hf6rbFtv+gLb4+aMib2UPdvlDlvvCVkBMbvE8ckY/cju+iOOuKCNA==", + "bin": true + }, + "@types/json-schema@7.0.15": { + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, + "@types/node@22.19.7": { + "integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==", + "dependencies": [ + "undici-types" + ] + }, + "ansi-regex@5.0.1": { + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-regex@6.2.2": { + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==" + }, + "ansi-styles@4.3.0": { + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": [ + "color-convert" + ] + }, + "ansi-styles@6.2.3": { + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==" + }, + "boolbase@1.0.0": { + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "cheerio-select@2.1.0": { + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dependencies": [ + "boolbase", + "css-select", + "css-what", + "domelementtype", + "domhandler", + "domutils" + ] + }, + "cheerio@1.2.0": { + "integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==", + "dependencies": [ + "cheerio-select", + "dom-serializer", + "domhandler", + "domutils", + "encoding-sniffer", + "htmlparser2", + "parse5", + "parse5-htmlparser2-tree-adapter", + "parse5-parser-stream", + "undici", + "whatwg-mimetype" + ] + }, + "color-convert@2.0.1": { + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": [ + "color-name" + ] + }, + "color-name@1.1.4": { + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "commander@13.1.0": { + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==" + }, + "cross-spawn@7.0.6": { + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dependencies": [ + "path-key", + "shebang-command", + "which" + ] + }, + "css-select@5.2.2": { + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dependencies": [ + "boolbase", + "css-what", + "domhandler", + "domutils", + "nth-check" + ] + }, + "css-what@6.2.2": { + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==" + }, + "dom-serializer@2.0.0": { + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": [ + "domelementtype", + "domhandler", + "entities@4.5.0" + ] + }, + "domelementtype@2.3.0": { + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domhandler@5.0.3": { + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": [ + "domelementtype" + ] + }, + "domutils@3.2.2": { + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dependencies": [ + "dom-serializer", + "domelementtype", + "domhandler" + ] + }, + "eastasianwidth@0.2.0": { + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "emoji-regex@8.0.0": { + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "emoji-regex@9.2.2": { + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "encoding-sniffer@0.2.1": { + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "dependencies": [ + "iconv-lite", + "whatwg-encoding" + ] + }, + "entities@4.5.0": { + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" + }, + "entities@6.0.1": { + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==" + }, + "entities@7.0.1": { + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==" + }, + "esbuild@0.27.2": { + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "optionalDependencies": [ + "@esbuild/aix-ppc64", + "@esbuild/android-arm", + "@esbuild/android-arm64", + "@esbuild/android-x64", + "@esbuild/darwin-arm64", + "@esbuild/darwin-x64", + "@esbuild/freebsd-arm64", + "@esbuild/freebsd-x64", + "@esbuild/linux-arm", + "@esbuild/linux-arm64", + "@esbuild/linux-ia32", + "@esbuild/linux-loong64", + "@esbuild/linux-mips64el", + "@esbuild/linux-ppc64", + "@esbuild/linux-riscv64", + "@esbuild/linux-s390x", + "@esbuild/linux-x64", + "@esbuild/netbsd-arm64", + "@esbuild/netbsd-x64", + "@esbuild/openbsd-arm64", + "@esbuild/openbsd-x64", + "@esbuild/openharmony-arm64", + "@esbuild/sunos-x64", + "@esbuild/win32-arm64", + "@esbuild/win32-ia32", + "@esbuild/win32-x64" + ], + "scripts": true, + "bin": true + }, + "foreground-child@3.3.1": { + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dependencies": [ + "cross-spawn", + "signal-exit" + ] + }, + "fsevents@2.3.3": { + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "os": ["darwin"], + "scripts": true + }, + "get-tsconfig@4.13.0": { + "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", + "dependencies": [ + "resolve-pkg-maps" + ] + }, + "glob@11.1.0": { + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "dependencies": [ + "foreground-child", + "jackspeak", + "minimatch", + "minipass", + "package-json-from-dist", + "path-scurry" + ], + "bin": true + }, + "htmlparser2@10.1.0": { + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "dependencies": [ + "domelementtype", + "domhandler", + "domutils", + "entities@7.0.1" + ] + }, + "iconv-lite@0.6.3": { + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": [ + "safer-buffer" + ] + }, + "is-fullwidth-code-point@3.0.0": { + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "isexe@2.0.0": { + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "jackspeak@4.1.1": { + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dependencies": [ + "@isaacs/cliui" + ] + }, + "json5@2.2.3": { + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": true + }, + "lru-cache@11.2.5": { + "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==" + }, + "minimatch@10.1.1": { + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "dependencies": [ + "@isaacs/brace-expansion" + ] + }, + "minipass@7.1.2": { + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" + }, + "normalize-path@3.0.0": { + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "nth-check@2.1.1": { + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": [ + "boolbase" + ] + }, + "package-json-from-dist@1.0.1": { + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" + }, + "parse5-htmlparser2-tree-adapter@7.1.0": { + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "dependencies": [ + "domhandler", + "parse5" + ] + }, + "parse5-parser-stream@7.1.2": { + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dependencies": [ + "parse5" + ] + }, + "parse5@7.3.0": { + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dependencies": [ + "entities@6.0.1" + ] + }, + "path-key@3.1.1": { + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "path-scurry@2.0.1": { + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "dependencies": [ + "lru-cache", + "minipass" + ] + }, + "resolve-pkg-maps@1.0.0": { + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==" + }, + "safe-stable-stringify@2.5.0": { + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==" + }, + "safer-buffer@2.1.2": { + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "shebang-command@2.0.0": { + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": [ + "shebang-regex" + ] + }, + "shebang-regex@3.0.0": { + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "signal-exit@4.1.0": { + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" + }, + "string-width@4.2.3": { + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": [ + "emoji-regex@8.0.0", + "is-fullwidth-code-point", + "strip-ansi@6.0.1" + ] + }, + "string-width@5.1.2": { + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": [ + "eastasianwidth", + "emoji-regex@9.2.2", + "strip-ansi@7.1.2" + ] + }, + "strip-ansi@6.0.1": { + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": [ + "ansi-regex@5.0.1" + ] + }, + "strip-ansi@7.1.2": { + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dependencies": [ + "ansi-regex@6.2.2" + ] + }, + "ts-json-schema-generator@2.4.0": { + "integrity": "sha512-HbmNsgs58CfdJq0gpteRTxPXG26zumezOs+SB9tgky6MpqiFgQwieCn2MW70+sxpHouZ/w9LW0V6L4ZQO4y1Ug==", + "dependencies": [ + "@types/json-schema", + "commander", + "glob", + "json5", + "normalize-path", + "safe-stable-stringify", + "tslib", + "typescript" + ], + "bin": true + }, + "tslib@2.8.1": { + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, + "tsx@4.21.0": { + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dependencies": [ + "esbuild", + "get-tsconfig" + ], + "optionalDependencies": [ + "fsevents" + ], + "bin": true + }, + "typescript@5.9.3": { + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "bin": true + }, + "undici-types@6.21.0": { + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" + }, + "undici@7.19.1": { + "integrity": "sha512-Gpq0iNm5M6cQWlyHQv9MV+uOj1jWk7LpkoE5vSp/7zjb4zMdAcUD+VL5y0nH4p9EbUklq00eVIIX/XcDHzu5xg==" + }, + "whatwg-encoding@3.1.1": { + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dependencies": [ + "iconv-lite" + ], + "deprecated": true + }, + "whatwg-mimetype@4.0.0": { + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==" + }, + "which@2.0.2": { + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": [ + "isexe" + ], + "bin": true + }, + "wrap-ansi@7.0.0": { + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": [ + "ansi-styles@4.3.0", + "string-width@4.2.3", + "strip-ansi@6.0.1" + ] + }, + "wrap-ansi@8.1.0": { + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": [ + "ansi-styles@6.2.3", + "string-width@5.1.2", + "strip-ansi@7.1.2" + ] + } + }, + "workspace": { + "packageJson": { + "dependencies": [ + "npm:@anthropic-ai/claude-code@latest", + "npm:@openai/codex@latest", + "npm:@types/node@22", + "npm:cheerio@1", + "npm:ts-json-schema-generator@^2.4.0", + "npm:tsx@^4.19.0", + "npm:typescript@^5.7.0" + ] + } + } +} diff --git a/resources/agent-schemas/package.json b/resources/agent-schemas/package.json index 3707816..4942068 100644 --- a/resources/agent-schemas/package.json +++ b/resources/agent-schemas/package.json @@ -8,7 +8,11 @@ "extract:opencode": "tsx src/index.ts --agent=opencode", "extract:claude": "tsx src/index.ts --agent=claude", "extract:codex": "tsx src/index.ts --agent=codex", - "extract:amp": "tsx src/index.ts --agent=amp" + "extract:amp": "tsx src/index.ts --agent=amp", + "extract:claude-events": "tsx src/claude-event-types.ts", + "extract:claude-events:sdk": "tsx src/claude-event-types-sdk.ts", + "extract:claude-events:cli": "tsx src/claude-event-types-cli.ts", + "extract:claude-events:docs": "tsx src/claude-event-types-docs.ts" }, "dependencies": { "ts-json-schema-generator": "^2.4.0", diff --git a/resources/agent-schemas/src/amp.ts b/resources/agent-schemas/src/amp.ts index 3390c93..cee5e78 100644 --- a/resources/agent-schemas/src/amp.ts +++ b/resources/agent-schemas/src/amp.ts @@ -3,7 +3,7 @@ import { fetchWithCache } from "./cache.js"; import { createNormalizedSchema, type NormalizedSchema } from "./normalize.js"; import type { JSONSchema7 } from "json-schema"; -const AMP_DOCS_URL = "https://ampcode.com/manual/appendix"; +const AMP_DOCS_URL = "https://ampcode.com/manual/appendix?preview#message-schema"; // Key types we want to extract const TARGET_TYPES = ["StreamJSONMessage", "AmpOptions", "PermissionRule", "Message", "ToolCall"]; diff --git a/resources/agent-schemas/src/claude-event-types-cli.ts b/resources/agent-schemas/src/claude-event-types-cli.ts new file mode 100644 index 0000000..2348156 --- /dev/null +++ b/resources/agent-schemas/src/claude-event-types-cli.ts @@ -0,0 +1,11 @@ +import { collectFromCli } from "./claude-event-types.js"; + +const promptArg = process.argv.slice(2).find((arg) => arg.startsWith("--prompt=")); +const timeoutArg = process.argv.slice(2).find((arg) => arg.startsWith("--timeoutMs=")); + +const prompt = promptArg?.split("=")[1] ?? "Reply with exactly OK."; +const timeoutMs = timeoutArg ? Number(timeoutArg.split("=")[1]) : 20000; + +collectFromCli(prompt, timeoutMs).then((result) => { + console.log(JSON.stringify(result, null, 2)); +}); diff --git a/resources/agent-schemas/src/claude-event-types-docs.ts b/resources/agent-schemas/src/claude-event-types-docs.ts new file mode 100644 index 0000000..0af2ff2 --- /dev/null +++ b/resources/agent-schemas/src/claude-event-types-docs.ts @@ -0,0 +1,8 @@ +import { collectFromDocs } from "./claude-event-types.js"; + +const urlsArg = process.argv.slice(2).find((arg) => arg.startsWith("--urls=")); +const urls = urlsArg ? urlsArg.split("=")[1]!.split(",") : undefined; + +collectFromDocs(urls ?? []).then((result) => { + console.log(JSON.stringify(result, null, 2)); +}); diff --git a/resources/agent-schemas/src/claude-event-types-sdk.ts b/resources/agent-schemas/src/claude-event-types-sdk.ts new file mode 100644 index 0000000..a7e43cc --- /dev/null +++ b/resources/agent-schemas/src/claude-event-types-sdk.ts @@ -0,0 +1,4 @@ +import { collectFromSdkTypes } from "./claude-event-types.js"; + +const result = collectFromSdkTypes(); +console.log(JSON.stringify(result, null, 2)); diff --git a/resources/agent-schemas/src/claude-event-types.ts b/resources/agent-schemas/src/claude-event-types.ts new file mode 100644 index 0000000..a1edb3b --- /dev/null +++ b/resources/agent-schemas/src/claude-event-types.ts @@ -0,0 +1,338 @@ +import { readFileSync, existsSync } from "node:fs"; +import { join, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; +import { spawn } from "node:child_process"; +import ts from "typescript"; +import { load } from "cheerio"; + +type SourceResult = { + source: string; + types: string[]; + details?: Record; + error?: string; +}; + +const SDK_POSSIBLE_PATHS = [ + "node_modules/@anthropic-ai/claude-code/sdk-tools.d.ts", + "node_modules/@anthropic-ai/claude-code/dist/index.d.ts", + "node_modules/@anthropic-ai/claude-code/dist/types.d.ts", + "node_modules/@anthropic-ai/claude-code/index.d.ts", +]; + +const DEFAULT_DOC_URLS = [ + "https://platform.claude.com/docs/en/messages-streaming", + "https://platform.claude.com/docs/en/api/messages-streaming", + "https://docs.anthropic.com/claude/reference/messages-streaming", + "https://docs.anthropic.com/claude/reference/messages-streaming#events", + "https://docs.anthropic.com/claude/docs/messages-streaming", +]; + +function moduleDir(): string { + const metaDir = (import.meta as { dirname?: string }).dirname; + if (typeof metaDir === "string") { + return metaDir; + } + return dirname(fileURLToPath(import.meta.url)); +} + +function findSdkTypesPath(): string | null { + const resourceDir = join(moduleDir(), ".."); + const repoRoot = join(moduleDir(), "..", "..", ".."); + const searchRoots = [resourceDir, repoRoot]; + + for (const root of searchRoots) { + for (const relativePath of SDK_POSSIBLE_PATHS) { + const fullPath = join(root, relativePath); + if (existsSync(fullPath)) { + return fullPath; + } + } + } + return null; +} + +function extractStringLiterals(node: ts.TypeNode): string[] { + if (ts.isLiteralTypeNode(node) && ts.isStringLiteral(node.literal)) { + return [node.literal.text]; + } + if (ts.isUnionTypeNode(node)) { + return node.types.flatMap((typeNode) => extractStringLiterals(typeNode)); + } + return []; +} + +function containerName(node: ts.Node): string | null { + let current: ts.Node | undefined = node; + while (current) { + if (ts.isInterfaceDeclaration(current) && current.name) { + return current.name.text; + } + if (ts.isTypeAliasDeclaration(current) && current.name) { + return current.name.text; + } + current = current.parent; + } + return null; +} + +function collectFromSdkTypes(): SourceResult { + const path = findSdkTypesPath(); + if (!path) { + return { source: "sdk", types: [], error: "Claude SDK types not found" }; + } + const content = readFileSync(path, "utf8"); + const sourceFile = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true); + const types = new Set(); + const details: Record = {}; + + function visit(node: ts.Node): void { + if (ts.isPropertySignature(node)) { + const name = node.name && ts.isIdentifier(node.name) ? node.name.text : null; + if (name === "type" && node.type) { + const literals = extractStringLiterals(node.type); + if (literals.length > 0) { + const parentName = containerName(node) ?? "anonymous"; + if (/Event|Stream|Message/i.test(parentName)) { + literals.forEach((value) => types.add(value)); + details[parentName] = (details[parentName] ?? []).concat(literals); + } + } + } + } + ts.forEachChild(node, visit); + } + + visit(sourceFile); + return { source: "sdk", types: Array.from(types).sort(), details }; +} + +function collectFromCli(prompt: string, timeoutMs: number): Promise { + return new Promise((resolve) => { + const result: SourceResult = { source: "cli", types: [] }; + const types = new Set(); + const denoGlobal = (globalThis as { + Deno?: { + which?: (cmd: string) => string | null; + Command?: new ( + cmd: string, + options: { args: string[]; stdout: "piped"; stderr: "piped" }, + ) => { output: () => Promise<{ stdout: Uint8Array; stderr: Uint8Array; code: number }> }; + }; + }).Deno; + + if (denoGlobal?.which && !denoGlobal.which("claude")) { + result.error = "claude binary not found in PATH"; + resolve(result); + return; + } + + if (denoGlobal?.Command) { + const command = new denoGlobal.Command("claude", { + args: ["--print", "--output-format", "stream-json", "--verbose", prompt], + stdout: "piped", + stderr: "piped", + }); + try { + command + .output() + .then(({ stdout, stderr, code }) => { + const text = new TextDecoder().decode(stdout); + for (const line of text.split("\n")) { + const trimmed = line.trim(); + if (!trimmed) continue; + try { + const value = JSON.parse(trimmed); + if (value && typeof value.type === "string") { + types.add(value.type); + } + } catch { + // ignore non-json + } + } + result.types = Array.from(types).sort(); + if (code !== 0) { + result.error = + new TextDecoder().decode(stderr).trim() || + `claude exited with code ${code}`; + } + resolve(result); + }) + .catch((error) => { + result.error = error instanceof Error ? error.message : String(error); + resolve(result); + }); + } catch (error) { + result.error = error instanceof Error ? error.message : String(error); + resolve(result); + } + return; + } + let child; + try { + child = spawn( + "claude", + ["--print", "--output-format", "stream-json", "--verbose", prompt], + { stdio: ["ignore", "pipe", "pipe"] }, + ); + } catch (error) { + result.error = error instanceof Error ? error.message : String(error); + resolve(result); + return; + } + + if (!child.stdout || !child.stderr) { + result.error = "claude stdout/stderr not available"; + resolve(result); + return; + } + + let stderr = ""; + const timer = setTimeout(() => { + child.kill("SIGKILL"); + }, timeoutMs); + + child.stdout.on("data", (chunk) => { + const text = chunk.toString("utf8"); + for (const line of text.split("\n")) { + const trimmed = line.trim(); + if (!trimmed) continue; + try { + const value = JSON.parse(trimmed); + if (value && typeof value.type === "string") { + types.add(value.type); + } + } catch { + // ignore non-json + } + } + }); + + child.stderr.on("data", (chunk) => { + stderr += chunk.toString("utf8"); + }); + + child.on("close", (code) => { + clearTimeout(timer); + result.types = Array.from(types).sort(); + if (code !== 0) { + result.error = stderr.trim() || `claude exited with code ${code}`; + } + resolve(result); + }); + }); +} + +async function collectFromDocs(urls: string[]): Promise { + if (typeof fetch !== "function") { + return { source: "docs", types: [], error: "fetch is not available in this runtime" }; + } + const effectiveUrls = urls.length > 0 ? urls : DEFAULT_DOC_URLS; + const types = new Set(); + const extractFromText = (text: string) => { + const typeMatches = text.match(/\"type\"\\s*:\\s*\"([^\"]+)\"/g) ?? []; + for (const match of typeMatches) { + const value = match.split(":")[1]?.trim().replace(/^\"|\"$/g, ""); + if (value) types.add(value); + } + const eventMatches = text.match(/event\\s*:\\s*([a-z_]+)/gi) ?? []; + for (const match of eventMatches) { + const value = match.split(":")[1]?.trim(); + if (value) types.add(value); + } + }; + for (const url of effectiveUrls) { + try { + const res = await fetch(url); + if (!res.ok) { + continue; + } + const html = await res.text(); + const $ = load(html); + const blocks = $("pre, code") + .map((_, el) => $(el).text()) + .get(); + for (const block of blocks) { + extractFromText(block); + } + const nextData = $("#__NEXT_DATA__").text(); + if (nextData) { + extractFromText(nextData); + } + extractFromText(html); + } catch { + // ignore per-url errors + } + } + return { source: "docs", types: Array.from(types).sort() }; +} + +type Args = { + source: "all" | "sdk" | "cli" | "docs"; + prompt: string; + timeoutMs: number; + urls: string[]; + json: boolean; +}; + +function parseArgs(): Args { + const args = process.argv.slice(2); + const sourceArg = args.find((arg) => arg.startsWith("--source=")); + const promptArg = args.find((arg) => arg.startsWith("--prompt=")); + const timeoutArg = args.find((arg) => arg.startsWith("--timeoutMs=")); + const urlsArg = args.find((arg) => arg.startsWith("--urls=")); + const json = args.includes("--json"); + + return { + source: (sourceArg?.split("=")[1] as Args["source"]) ?? "all", + prompt: promptArg?.split("=")[1] ?? "Reply with exactly OK.", + timeoutMs: timeoutArg ? Number(timeoutArg.split("=")[1]) : 20000, + urls: urlsArg ? urlsArg.split("=")[1]!.split(",") : DEFAULT_DOC_URLS, + json, + }; +} + +function summarize(results: SourceResult[]): void { + const counts = results.map((r) => ({ source: r.source, count: r.types.length })); + const max = Math.max(...counts.map((c) => c.count), 0); + const best = counts.filter((c) => c.count === max).map((c) => c.source); + const union = Array.from( + new Set(results.flatMap((r) => r.types)) + ).sort(); + + console.log("Claude event type extraction"); + console.log("============================"); + for (const result of results) { + console.log(`- ${result.source}: ${result.types.length} types${result.error ? " (error)" : ""}`); + } + console.log(`\nMost comprehensive: ${best.join(", ") || "none"}`); + console.log(`Union (${union.length}): ${union.join(", ")}`); +} + +async function main(): Promise { + const args = parseArgs(); + const results: SourceResult[] = []; + + if (args.source === "all" || args.source === "sdk") { + results.push(collectFromSdkTypes()); + } + if (args.source === "all" || args.source === "cli") { + results.push(await collectFromCli(args.prompt, args.timeoutMs)); + } + if (args.source === "all" || args.source === "docs") { + results.push(await collectFromDocs(args.urls)); + } + + if (args.json) { + console.log(JSON.stringify({ results }, null, 2)); + return; + } + + summarize(results); +} + +main().catch((error) => { + console.error("Fatal error:", error); + process.exit(1); +}); + +export { collectFromCli, collectFromDocs, collectFromSdkTypes }; diff --git a/resources/agent-schemas/src/claude.ts b/resources/agent-schemas/src/claude.ts index 58ae0e1..c09bd74 100644 --- a/resources/agent-schemas/src/claude.ts +++ b/resources/agent-schemas/src/claude.ts @@ -1,92 +1,43 @@ -import { createGenerator, type Config } from "ts-json-schema-generator"; -import { existsSync, readFileSync } from "fs"; -import { join, dirname } from "path"; +import { execSync } from "child_process"; import { createNormalizedSchema, type NormalizedSchema } from "./normalize.js"; import type { JSONSchema7 } from "json-schema"; -// Try multiple possible paths for the SDK types -const POSSIBLE_PATHS = [ - "node_modules/@anthropic-ai/claude-code/sdk-tools.d.ts", - "node_modules/@anthropic-ai/claude-code/dist/index.d.ts", - "node_modules/@anthropic-ai/claude-code/dist/types.d.ts", - "node_modules/@anthropic-ai/claude-code/index.d.ts", -]; - -// Key types we want to extract -const TARGET_TYPES = [ - "ToolInputSchemas", - "AgentInput", - "BashInput", - "FileEditInput", - "FileReadInput", - "FileWriteInput", - "GlobInput", - "GrepInput", - "WebFetchInput", - "WebSearchInput", - "AskUserQuestionInput", -]; - -function findTypesPath(): string | null { - const baseDir = join(import.meta.dirname, "..", "..", "resources", "agent-schemas"); - - for (const relativePath of POSSIBLE_PATHS) { - const fullPath = join(baseDir, relativePath); - if (existsSync(fullPath)) { - return fullPath; - } - } - - return null; -} - export async function extractClaudeSchema(): Promise { - console.log("Extracting Claude Code SDK schema..."); - - const typesPath = findTypesPath(); - - if (!typesPath) { - console.log(" [warn] Claude Code SDK types not found, using fallback schema"); - return createFallbackSchema(); - } - - console.log(` [found] ${typesPath}`); - - const config: Config = { - path: typesPath, - tsconfig: join(import.meta.dirname, "..", "..", "resources", "agent-schemas", "tsconfig.json"), - type: "*", - skipTypeCheck: true, - topRef: false, - expose: "export", - jsDoc: "extended", - }; + console.log("Extracting Claude Code schema via CLI..."); try { - const generator = createGenerator(config); - const schema = generator.createSchema(config.type); + // Run claude CLI with --json-schema flag to get the schema + const output = execSync("claude --output-format json --json-schema", { + encoding: "utf-8", + timeout: 30000, + stdio: ["pipe", "pipe", "pipe"], + }); + // Parse the JSON output + const parsed = JSON.parse(output); + + // Extract definitions from the schema const definitions: Record = {}; - if (schema.definitions) { - for (const [name, def] of Object.entries(schema.definitions)) { + if (parsed.definitions) { + for (const [name, def] of Object.entries(parsed.definitions)) { definitions[name] = def as JSONSchema7; } + } else if (parsed.$defs) { + for (const [name, def] of Object.entries(parsed.$defs)) { + definitions[name] = def as JSONSchema7; + } + } else { + // The output might be a single schema, use it as the root + definitions["Schema"] = parsed as JSONSchema7; } - // Verify target types exist - const found = TARGET_TYPES.filter((name) => definitions[name]); - const missing = TARGET_TYPES.filter((name) => !definitions[name]); - - if (missing.length > 0) { - console.log(` [warn] Missing expected types: ${missing.join(", ")}`); - } - - console.log(` [ok] Extracted ${Object.keys(definitions).length} types (${found.length} target types)`); + console.log(` [ok] Extracted ${Object.keys(definitions).length} types from CLI`); return createNormalizedSchema("claude", "Claude Code SDK Schema", definitions); } catch (error) { - console.log(` [error] Schema generation failed: ${error}`); + const errorMessage = error instanceof Error ? error.message : String(error); + console.log(` [warn] CLI extraction failed: ${errorMessage}`); console.log(" [fallback] Using embedded schema definitions"); return createFallbackSchema(); } diff --git a/resources/agent-schemas/src/codex.ts b/resources/agent-schemas/src/codex.ts index bee5d86..706212e 100644 --- a/resources/agent-schemas/src/codex.ts +++ b/resources/agent-schemas/src/codex.ts @@ -1,88 +1,69 @@ -import { createGenerator, type Config } from "ts-json-schema-generator"; -import { existsSync } from "fs"; +import { execSync } from "child_process"; +import { existsSync, readFileSync, rmSync, readdirSync } from "fs"; import { join } from "path"; import { createNormalizedSchema, type NormalizedSchema } from "./normalize.js"; import type { JSONSchema7 } from "json-schema"; -// Try multiple possible paths for the SDK types -const POSSIBLE_PATHS = [ - "node_modules/@openai/codex/dist/index.d.ts", - "node_modules/@openai/codex/dist/types.d.ts", - "node_modules/@openai/codex/index.d.ts", -]; - -// Key types we want to extract -const TARGET_TYPES = [ - "ThreadEvent", - "ThreadItem", - "CodexOptions", - "ThreadOptions", - "Input", - "ResponseItem", - "FunctionCall", - "Message", -]; - -function findTypesPath(): string | null { - const baseDir = join(import.meta.dirname, "..", "..", "resources", "agent-schemas"); - - for (const relativePath of POSSIBLE_PATHS) { - const fullPath = join(baseDir, relativePath); - if (existsSync(fullPath)) { - return fullPath; - } - } - - return null; -} - export async function extractCodexSchema(): Promise { - console.log("Extracting Codex SDK schema..."); + console.log("Extracting Codex schema via CLI..."); - const typesPath = findTypesPath(); - - if (!typesPath) { - console.log(" [warn] Codex SDK types not found, using fallback schema"); - return createFallbackSchema(); - } - - console.log(` [found] ${typesPath}`); - - const config: Config = { - path: typesPath, - tsconfig: join(import.meta.dirname, "..", "..", "resources", "agent-schemas", "tsconfig.json"), - type: "*", - skipTypeCheck: true, - topRef: false, - expose: "export", - jsDoc: "extended", - }; + const tempDir = join(import.meta.dirname, "..", ".temp-codex-schemas"); try { - const generator = createGenerator(config); - const schema = generator.createSchema(config.type); + // Run codex CLI to generate JSON schema + execSync(`codex app-server generate-json-schema --out "${tempDir}"`, { + encoding: "utf-8", + timeout: 30000, + stdio: ["pipe", "pipe", "pipe"], + }); + // Read generated schema files from temp directory const definitions: Record = {}; - if (schema.definitions) { - for (const [name, def] of Object.entries(schema.definitions)) { - definitions[name] = def as JSONSchema7; + if (existsSync(tempDir)) { + const files = readdirSync(tempDir).filter((f) => f.endsWith(".json")); + + for (const file of files) { + const filePath = join(tempDir, file); + const content = readFileSync(filePath, "utf-8"); + const schema = JSON.parse(content); + + // Extract the name from the file (e.g., "ThreadEvent.json" -> "ThreadEvent") + const name = file.replace(".json", ""); + + if (schema.definitions) { + for (const [defName, def] of Object.entries(schema.definitions)) { + definitions[defName] = def as JSONSchema7; + } + } else if (schema.$defs) { + for (const [defName, def] of Object.entries(schema.$defs)) { + definitions[defName] = def as JSONSchema7; + } + } else { + definitions[name] = schema as JSONSchema7; + } } + + // Clean up temp directory + rmSync(tempDir, { recursive: true, force: true }); } - // Verify target types exist - const found = TARGET_TYPES.filter((name) => definitions[name]); - const missing = TARGET_TYPES.filter((name) => !definitions[name]); - - if (missing.length > 0) { - console.log(` [warn] Missing expected types: ${missing.join(", ")}`); + if (Object.keys(definitions).length === 0) { + console.log(" [warn] No schemas extracted from CLI, using fallback"); + return createFallbackSchema(); } - console.log(` [ok] Extracted ${Object.keys(definitions).length} types (${found.length} target types)`); + console.log(` [ok] Extracted ${Object.keys(definitions).length} types from CLI`); return createNormalizedSchema("codex", "Codex SDK Schema", definitions); } catch (error) { - console.log(` [error] Schema generation failed: ${error}`); + // Clean up temp directory on error + if (existsSync(tempDir)) { + rmSync(tempDir, { recursive: true, force: true }); + } + + const errorMessage = error instanceof Error ? error.message : String(error); + console.log(` [warn] CLI extraction failed: ${errorMessage}`); console.log(" [fallback] Using embedded schema definitions"); return createFallbackSchema(); } diff --git a/resources/agent-schemas/src/index.ts b/resources/agent-schemas/src/index.ts index c2f73bc..877e695 100644 --- a/resources/agent-schemas/src/index.ts +++ b/resources/agent-schemas/src/index.ts @@ -6,8 +6,8 @@ import { extractCodexSchema } from "./codex.js"; import { extractAmpSchema } from "./amp.js"; import { validateSchema, type NormalizedSchema } from "./normalize.js"; -const RESOURCE_DIR = join(import.meta.dirname, "..", "..", "resources", "agent-schemas"); -const DIST_DIR = join(RESOURCE_DIR, "dist"); +const RESOURCE_DIR = join(import.meta.dirname, ".."); +const DIST_DIR = join(RESOURCE_DIR, "artifacts", "json-schema"); type AgentName = "opencode" | "claude" | "codex" | "amp"; diff --git a/scripts/release/main.ts b/scripts/release/main.ts index 559dfb3..9d5fa3d 100755 --- a/scripts/release/main.ts +++ b/scripts/release/main.ts @@ -2,7 +2,8 @@ import fs from "node:fs"; import path from "node:path"; -import { spawnSync, execFileSync } from "node:child_process"; +import { execFileSync, spawnSync } from "node:child_process"; +import readline from "node:readline"; const ENDPOINT_URL = "https://2a94c6a0ced8d35ea63cddc86c2681e7.r2.cloudflarestorage.com"; @@ -32,6 +33,47 @@ const PLATFORM_MAP: Record = { + "setup-local": [ + "confirm-release", + "update-version", + "generate-artifacts", + "git-commit", + "git-push", + "trigger-workflow", + ], + "setup-ci": ["run-checks"], + "complete-ci": [ + "publish-crates", + "publish-npm-sdk", + "publish-npm-cli", + "upload-typescript", + "upload-install", + "upload-binaries", + ], +}; + function parseArgs(argv: string[]) { const args = new Map(); const flags = new Set(); @@ -61,11 +103,7 @@ function run(cmd: string, cmdArgs: string[], options: Record = {}) } } -function runCapture( - cmd: string, - cmdArgs: string[], - options: Record = {}, -) { +function runCapture(cmd: string, cmdArgs: string[], options: Record = {}) { const result = spawnSync(cmd, cmdArgs, { stdio: ["ignore", "pipe", "pipe"], encoding: "utf8", @@ -234,16 +272,55 @@ function uploadContent(content: string, remotePath: string) { } } +function updatePackageJson(filePath: string, version: string, updateOptionalDeps = false) { + const pkg = JSON.parse(fs.readFileSync(filePath, "utf8")); + pkg.version = version; + if (updateOptionalDeps && pkg.optionalDependencies) { + for (const dep of Object.keys(pkg.optionalDependencies)) { + pkg.optionalDependencies[dep] = version; + } + } + fs.writeFileSync(filePath, JSON.stringify(pkg, null, 2) + "\n"); +} + +function updateVersion(rootDir: string, version: string) { + const cargoPath = path.join(rootDir, "Cargo.toml"); + let cargoContent = fs.readFileSync(cargoPath, "utf8"); + cargoContent = cargoContent.replace(/^version = ".*"/m, `version = "${version}"`); + fs.writeFileSync(cargoPath, cargoContent); + + updatePackageJson(path.join(rootDir, "sdks", "typescript", "package.json"), version, true); + updatePackageJson(path.join(rootDir, "sdks", "cli", "package.json"), version, true); + + const platformsDir = path.join(rootDir, "sdks", "cli", "platforms"); + for (const entry of fs.readdirSync(platformsDir, { withFileTypes: true })) { + if (!entry.isDirectory()) continue; + const pkgPath = path.join(platformsDir, entry.name, "package.json"); + if (fs.existsSync(pkgPath)) { + updatePackageJson(pkgPath, version, false); + } + } +} + function buildTypescript(rootDir: string) { const sdkDir = path.join(rootDir, "sdks", "typescript"); if (!fs.existsSync(sdkDir)) { throw new Error(`TypeScript SDK not found at ${sdkDir}`); } - run("npm", ["install"], { cwd: sdkDir }); - run("npm", ["run", "build"], { cwd: sdkDir }); + run("pnpm", ["install"], { cwd: sdkDir }); + run("pnpm", ["run", "build"], { cwd: sdkDir }); return path.join(sdkDir, "dist"); } +function generateArtifacts(rootDir: string) { + const sdkDir = path.join(rootDir, "sdks", "typescript"); + run("pnpm", ["run", "generate"], { cwd: sdkDir }); + run("cargo", ["check", "-p", "sandbox-agent-universal-schema-gen"], { cwd: rootDir }); + run("cargo", ["run", "-p", "sandbox-agent-openapi-gen", "--", "--out", "sdks/openapi/openapi.json"], { + cwd: rootDir, + }); +} + function uploadTypescriptArtifacts(rootDir: string, version: string, latest: boolean) { console.log("==> Building TypeScript SDK"); const distPath = buildTypescript(rootDir); @@ -256,13 +333,7 @@ function uploadTypescriptArtifacts(rootDir: string, version: string, latest: boo } function uploadInstallScript(rootDir: string, version: string, latest: boolean) { - const installPath = path.join( - rootDir, - "scripts", - "release", - "static", - "install.sh", - ); + const installPath = path.join(rootDir, "scripts", "release", "static", "install.sh"); let installContent = fs.readFileSync(installPath, "utf8"); const uploadForVersion = (versionValue: string, remoteVersion: string) => { @@ -295,7 +366,6 @@ function uploadBinaries(rootDir: string, version: string, latest: boolean) { } } -// Pre-release checks function runChecks(rootDir: string) { console.log("==> Running Rust checks"); run("cargo", ["fmt", "--all", "--", "--check"], { cwd: rootDir }); @@ -307,58 +377,46 @@ function runChecks(rootDir: string) { run("pnpm", ["run", "build"], { cwd: rootDir }); } -// Crates.io publishing function publishCrates(rootDir: string, version: string) { - // Update workspace version - const cargoPath = path.join(rootDir, "Cargo.toml"); - let cargoContent = fs.readFileSync(cargoPath, "utf8"); - cargoContent = cargoContent.replace(/^version = ".*"/m, `version = "${version}"`); - fs.writeFileSync(cargoPath, cargoContent); + updateVersion(rootDir, version); for (const crate of CRATE_ORDER) { console.log(`==> Publishing sandbox-agent-${crate}`); const crateDir = path.join(rootDir, "server", "packages", crate); run("cargo", ["publish", "--allow-dirty"], { cwd: crateDir }); - // Wait for crates.io index propagation console.log("Waiting 30s for index..."); Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 30000); } } -// npm SDK publishing function publishNpmSdk(rootDir: string, version: string) { const sdkDir = path.join(rootDir, "sdks", "typescript"); console.log("==> Publishing TypeScript SDK to npm"); - run("npm", ["version", version, "--no-git-tag-version"], { cwd: sdkDir }); + run("npm", ["version", version, "--no-git-tag-version", "--allow-same-version"], { cwd: sdkDir }); run("pnpm", ["install"], { cwd: sdkDir }); run("pnpm", ["run", "build"], { cwd: sdkDir }); run("npm", ["publish", "--access", "public"], { cwd: sdkDir }); } -// npm CLI publishing function publishNpmCli(rootDir: string, version: string) { const cliDir = path.join(rootDir, "sdks", "cli"); const distDir = path.join(rootDir, "dist"); - // Publish platform packages first for (const [target, info] of Object.entries(PLATFORM_MAP)) { const platformDir = path.join(cliDir, "platforms", info.pkg); const binDir = path.join(platformDir, "bin"); fs.mkdirSync(binDir, { recursive: true }); - // Copy binary const srcBinary = path.join(distDir, `sandbox-agent-${target}${info.ext}`); const dstBinary = path.join(binDir, `sandbox-agent${info.ext}`); fs.copyFileSync(srcBinary, dstBinary); if (info.ext !== ".exe") fs.chmodSync(dstBinary, 0o755); - // Update version and publish console.log(`==> Publishing @sandbox-agent/cli-${info.pkg}`); - run("npm", ["version", version, "--no-git-tag-version"], { cwd: platformDir }); + run("npm", ["version", version, "--no-git-tag-version", "--allow-same-version"], { cwd: platformDir }); run("npm", ["publish", "--access", "public"], { cwd: platformDir }); } - // Publish main package (update optionalDeps versions) console.log("==> Publishing @sandbox-agent/cli"); const pkgPath = path.join(cliDir, "package.json"); const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")); @@ -370,7 +428,26 @@ function publishNpmCli(rootDir: string, version: string) { run("npm", ["publish", "--access", "public"], { cwd: cliDir }); } -function main() { +function validateGit(rootDir: string) { + const status = runCapture("git", ["status", "--porcelain"], { cwd: rootDir }); + if (status.trim()) { + throw new Error("Working tree is dirty; commit or stash changes before release."); + } +} + +async function confirmRelease(version: string, latest: boolean) { + const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + const answer = await new Promise((resolve) => { + rl.question(`Release ${version} (latest=${latest})? (yes/no): `, resolve); + }); + rl.close(); + if (answer.toLowerCase() !== "yes" && answer.toLowerCase() !== "y") { + console.log("Release cancelled"); + process.exit(0); + } +} + +async function main() { const { args, flags } = parseArgs(process.argv.slice(2)); const versionArg = args.get("--version"); if (!versionArg) { @@ -399,33 +476,155 @@ function main() { } } - if (flags.has("--check")) { - runChecks(process.cwd()); + const phaseArg = args.get("--phase"); + const stepsArg = args.get("--only-steps"); + const requestedSteps = new Set(); + + if (phaseArg || stepsArg) { + if (phaseArg && stepsArg) { + throw new Error("Cannot use both --phase and --only-steps"); + } + + if (phaseArg) { + const phases = phaseArg.split(",").map((value) => value.trim()); + for (const phase of phases) { + if (!PHASES.includes(phase as Phase)) { + throw new Error(`Invalid phase: ${phase}`); + } + for (const step of PHASE_MAP[phase as Phase]) { + requestedSteps.add(step); + } + } + } + + if (stepsArg) { + const steps = stepsArg.split(",").map((value) => value.trim()); + for (const step of steps) { + if (!STEPS.includes(step as Step)) { + throw new Error(`Invalid step: ${step}`); + } + requestedSteps.add(step as Step); + } + } } - if (flags.has("--publish-crates")) { - publishCrates(process.cwd(), version); + const rootDir = process.cwd(); + const shouldRun = (step: Step) => requestedSteps.has(step); + const hasPhases = requestedSteps.size > 0; + + if (!hasPhases) { + if (flags.has("--check")) { + runChecks(rootDir); + } + if (flags.has("--publish-crates")) { + publishCrates(rootDir, version); + } + if (flags.has("--publish-npm-sdk")) { + publishNpmSdk(rootDir, version); + } + if (flags.has("--publish-npm-cli")) { + publishNpmCli(rootDir, version); + } + if (flags.has("--upload-typescript")) { + uploadTypescriptArtifacts(rootDir, version, latest); + } + if (flags.has("--upload-install")) { + uploadInstallScript(rootDir, version, latest); + } + if (flags.has("--upload-binaries")) { + uploadBinaries(rootDir, version, latest); + } + return; } - if (flags.has("--publish-npm-sdk")) { - publishNpmSdk(process.cwd(), version); + if (shouldRun("confirm-release") && !flags.has("--no-confirm")) { + await confirmRelease(version, latest); } - if (flags.has("--publish-npm-cli")) { - publishNpmCli(process.cwd(), version); + const validateGitEnabled = !flags.has("--no-validate-git"); + if ((shouldRun("git-commit") || shouldRun("git-push")) && validateGitEnabled) { + validateGit(rootDir); } - if (flags.has("--upload-typescript")) { - uploadTypescriptArtifacts(process.cwd(), version, latest); + if (shouldRun("update-version")) { + console.log("==> Updating versions"); + updateVersion(rootDir, version); } - if (flags.has("--upload-install")) { - uploadInstallScript(process.cwd(), version, latest); + if (shouldRun("generate-artifacts")) { + console.log("==> Generating OpenAPI and universal schemas"); + generateArtifacts(rootDir); } - if (flags.has("--upload-binaries")) { - uploadBinaries(process.cwd(), version, latest); + if (shouldRun("git-commit")) { + console.log("==> Committing changes"); + run("git", ["add", "."], { cwd: rootDir }); + run("git", ["commit", "--allow-empty", "-m", `chore(release): update version to ${version}`], { + cwd: rootDir, + }); + } + + if (shouldRun("git-push")) { + console.log("==> Pushing changes"); + const branch = runCapture("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: rootDir }); + if (branch === "main") { + run("git", ["push"], { cwd: rootDir }); + } else { + run("git", ["push", "-u", "origin", "HEAD"], { cwd: rootDir }); + } + } + + if (shouldRun("trigger-workflow")) { + console.log("==> Triggering release workflow"); + const branch = runCapture("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: rootDir }); + const latestFlag = latest ? "true" : "false"; + run( + "gh", + [ + "workflow", + "run", + ".github/workflows/release.yaml", + "-f", + `version=${version}`, + "-f", + `latest=${latestFlag}`, + "--ref", + branch, + ], + { cwd: rootDir }, + ); + } + + if (shouldRun("run-checks")) { + runChecks(rootDir); + } + + if (shouldRun("publish-crates")) { + publishCrates(rootDir, version); + } + + if (shouldRun("publish-npm-sdk")) { + publishNpmSdk(rootDir, version); + } + + if (shouldRun("publish-npm-cli")) { + publishNpmCli(rootDir, version); + } + + if (shouldRun("upload-typescript")) { + uploadTypescriptArtifacts(rootDir, version, latest); + } + + if (shouldRun("upload-install")) { + uploadInstallScript(rootDir, version, latest); + } + + if (shouldRun("upload-binaries")) { + uploadBinaries(rootDir, version, latest); } } -main(); +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/sdks/typescript/src/generated/openapi.json b/sdks/openapi/openapi.json similarity index 94% rename from sdks/typescript/src/generated/openapi.json rename to sdks/openapi/openapi.json index a92b639..10f921b 100644 --- a/sdks/typescript/src/generated/openapi.json +++ b/sdks/openapi/openapi.json @@ -157,6 +157,26 @@ } } }, + "/v1/sessions": { + "get": { + "tags": [ + "sessions" + ], + "operationId": "list_sessions", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionListResponse" + } + } + } + } + } + } + }, "/v1/sessions/{session_id}": { "post": { "tags": [ @@ -1047,6 +1067,65 @@ } } }, + "SessionInfo": { + "type": "object", + "required": [ + "sessionId", + "agent", + "agentMode", + "permissionMode", + "ended", + "eventCount" + ], + "properties": { + "agent": { + "type": "string" + }, + "agentMode": { + "type": "string" + }, + "agentSessionId": { + "type": "string", + "nullable": true + }, + "ended": { + "type": "boolean" + }, + "eventCount": { + "type": "integer", + "format": "int64", + "minimum": 0 + }, + "model": { + "type": "string", + "nullable": true + }, + "permissionMode": { + "type": "string" + }, + "sessionId": { + "type": "string" + }, + "variant": { + "type": "string", + "nullable": true + } + } + }, + "SessionListResponse": { + "type": "object", + "required": [ + "sessions" + ], + "properties": { + "sessions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SessionInfo" + } + } + } + }, "Started": { "type": "object", "properties": { diff --git a/sdks/typescript/package.json b/sdks/typescript/package.json index b74ae2b..99ed3f2 100644 --- a/sdks/typescript/package.json +++ b/sdks/typescript/package.json @@ -20,8 +20,8 @@ "dist" ], "scripts": { - "generate:openapi": "cargo check -p sandbox-agent-openapi-gen && cargo run -p sandbox-agent-openapi-gen -- --out src/generated/openapi.json", - "generate:types": "openapi-typescript src/generated/openapi.json -o src/generated/openapi.ts", + "generate:openapi": "cargo check -p sandbox-agent-openapi-gen && cargo run -p sandbox-agent-openapi-gen -- --out ../openapi/openapi.json", + "generate:types": "openapi-typescript ../openapi/openapi.json -o src/generated/openapi.ts", "generate": "pnpm run generate:openapi && pnpm run generate:types", "build": "pnpm run generate && tsc -p tsconfig.json" }, diff --git a/sdks/typescript/src/client.ts b/sdks/typescript/src/client.ts index e3a03f2..bae0ac5 100644 --- a/sdks/typescript/src/client.ts +++ b/sdks/typescript/src/client.ts @@ -11,14 +11,21 @@ export type AgentInfo = components["schemas"]["AgentInfo"]; export type AgentListResponse = components["schemas"]["AgentListResponse"]; export type CreateSessionRequest = components["schemas"]["CreateSessionRequest"]; export type CreateSessionResponse = components["schemas"]["CreateSessionResponse"]; +export type HealthResponse = components["schemas"]["HealthResponse"]; export type MessageRequest = components["schemas"]["MessageRequest"]; export type EventsQuery = components["schemas"]["EventsQuery"]; export type EventsResponse = components["schemas"]["EventsResponse"]; +export type PermissionRequest = components["schemas"]["PermissionRequest"]; export type QuestionReplyRequest = components["schemas"]["QuestionReplyRequest"]; +export type QuestionRequest = components["schemas"]["QuestionRequest"]; export type PermissionReplyRequest = components["schemas"]["PermissionReplyRequest"]; export type PermissionReply = components["schemas"]["PermissionReply"]; export type ProblemDetails = components["schemas"]["ProblemDetails"]; +export type SessionInfo = components["schemas"]["SessionInfo"]; +export type SessionListResponse = components["schemas"]["SessionListResponse"]; export type UniversalEvent = components["schemas"]["UniversalEvent"]; +export type UniversalMessage = components["schemas"]["UniversalMessage"]; +export type UniversalMessagePart = components["schemas"]["UniversalMessagePart"]; const API_PREFIX = "/v1"; @@ -58,6 +65,7 @@ type RequestOptions = { body?: unknown; headers?: HeadersInit; accept?: string; + signal?: AbortSignal; }; export class SandboxDaemonClient { @@ -108,6 +116,10 @@ export class SandboxDaemonClient { return this.requestJson("GET", `${API_PREFIX}/agents`); } + async getHealth(): Promise { + return this.requestJson("GET", `${API_PREFIX}/health`); + } + async installAgent(agent: string, request: AgentInstallRequest = {}): Promise { await this.requestJson("POST", `${API_PREFIX}/agents/${encodeURIComponent(agent)}/install`, { body: request, @@ -124,6 +136,10 @@ export class SandboxDaemonClient { }); } + async listSessions(): Promise { + return this.requestJson("GET", `${API_PREFIX}/sessions`); + } + async postMessage(sessionId: string, request: MessageRequest): Promise { await this.requestJson("POST", `${API_PREFIX}/sessions/${encodeURIComponent(sessionId)}/messages`, { body: request, @@ -136,15 +152,20 @@ export class SandboxDaemonClient { }); } - async getEventsSse(sessionId: string, query?: EventsQuery): Promise { + async getEventsSse(sessionId: string, query?: EventsQuery, signal?: AbortSignal): Promise { return this.requestRaw("GET", `${API_PREFIX}/sessions/${encodeURIComponent(sessionId)}/events/sse`, { query, accept: "text/event-stream", + signal, }); } - async *streamEvents(sessionId: string, query?: EventsQuery): AsyncGenerator { - const response = await this.getEventsSse(sessionId, query); + async *streamEvents( + sessionId: string, + query?: EventsQuery, + signal?: AbortSignal, + ): AsyncGenerator { + const response = await this.getEventsSse(sessionId, query, signal); if (!response.body) { throw new Error("SSE stream is not readable in this environment."); } @@ -249,7 +270,7 @@ export class SandboxDaemonClient { headers.set("Accept", options.accept); } - const init: RequestInit = { method, headers }; + const init: RequestInit = { method, headers, signal: options.signal }; if (options.body !== undefined) { headers.set("Content-Type", "application/json"); init.body = JSON.stringify(options.body); diff --git a/sdks/typescript/src/index.ts b/sdks/typescript/src/index.ts index cd0df9d..91e9e86 100644 --- a/sdks/typescript/src/index.ts +++ b/sdks/typescript/src/index.ts @@ -14,12 +14,19 @@ export type { CreateSessionResponse, EventsQuery, EventsResponse, + HealthResponse, MessageRequest, + PermissionRequest, PermissionReply, PermissionReplyRequest, ProblemDetails, + QuestionRequest, QuestionReplyRequest, + SessionInfo, + SessionListResponse, UniversalEvent, + UniversalMessage, + UniversalMessagePart, SandboxDaemonClientOptions, SandboxDaemonConnectOptions, } from "./client.js"; diff --git a/sdks/typescript/src/spawn.ts b/sdks/typescript/src/spawn.ts index 8977be1..0cd19a4 100644 --- a/sdks/typescript/src/spawn.ts +++ b/sdks/typescript/src/spawn.ts @@ -50,8 +50,9 @@ export async function spawnSandboxDaemon( const net = await import("node:net"); const { createRequire } = await import("node:module"); - const host = options.host ?? "127.0.0.1"; - const port = options.port ?? (await getFreePort(net, host)); + const bindHost = options.host ?? "127.0.0.1"; + const port = options.port ?? (await getFreePort(net, bindHost)); + const connectHost = bindHost === "0.0.0.0" || bindHost === "::" ? "127.0.0.1" : bindHost; const token = options.token ?? crypto.randomBytes(24).toString("hex"); const timeoutMs = options.timeoutMs ?? 15_000; const logMode: SandboxDaemonSpawnLogMode = options.log ?? "inherit"; @@ -67,7 +68,7 @@ export async function spawnSandboxDaemon( } const stdio = logMode === "inherit" ? "inherit" : logMode === "silent" ? "ignore" : "pipe"; - const args = ["--host", host, "--port", String(port), "--token", token]; + const args = ["--host", bindHost, "--port", String(port), "--token", token]; const child = spawn(binaryPath, args, { stdio, env: { @@ -77,8 +78,8 @@ export async function spawnSandboxDaemon( }); const cleanup = registerProcessCleanup(child); - const baseUrl = `http://${host}:${port}`; - const ready = waitForHealth(baseUrl, fetcher ?? globalThis.fetch, timeoutMs, child); + const baseUrl = `http://${connectHost}:${port}`; + const ready = waitForHealth(baseUrl, fetcher ?? globalThis.fetch, timeoutMs, child, token); await ready; @@ -161,6 +162,7 @@ async function waitForHealth( fetcher: typeof fetch | undefined, timeoutMs: number, child: ChildProcess, + token: string, ): Promise { if (!fetcher) { throw new Error("Fetch API is not available; provide a fetch implementation."); @@ -173,7 +175,9 @@ async function waitForHealth( throw new Error("sandbox-agent exited before becoming healthy."); } try { - const response = await fetcher(`${baseUrl}/v1/health`); + const response = await fetcher(`${baseUrl}/v1/health`, { + headers: { Authorization: `Bearer ${token}` }, + }); if (response.ok) { return; } diff --git a/server/AGENTS.md b/server/AGENTS.md new file mode 120000 index 0000000..681311e --- /dev/null +++ b/server/AGENTS.md @@ -0,0 +1 @@ +CLAUDE.md \ No newline at end of file diff --git a/server/CLAUDE.md b/server/CLAUDE.md new file mode 100644 index 0000000..6e091dc --- /dev/null +++ b/server/CLAUDE.md @@ -0,0 +1,68 @@ +# Server Testing + +## Snapshot tests + +The HTTP/SSE snapshot suite lives in: +- `server/packages/sandbox-agent/tests/http_sse_snapshots.rs` + +Snapshots are written to: +- `server/packages/sandbox-agent/tests/snapshots/` + +## Agent selection + +`SANDBOX_TEST_AGENTS` controls which agents run. It accepts a comma-separated list or `all`. +If it is **not set**, tests will auto-detect installed agents by checking: +- binaries on `PATH`, and +- the default install dir (`$XDG_DATA_HOME/sandbox-agent/bin` or `./.sandbox-agent/bin`) + +If no agents are found, tests fail with a clear error. + +## Credential handling + +Credentials are pulled from the host by default via `extract_all_credentials`: +- environment variables (e.g. `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`) +- local CLI configs (Claude/Codex/Amp/OpenCode) + +You can override host credentials for tests with: +- `SANDBOX_TEST_ANTHROPIC_API_KEY` +- `SANDBOX_TEST_OPENAI_API_KEY` + +If `SANDBOX_TEST_AGENTS` includes an agent that requires a provider credential and it is missing, +tests fail before starting. + +## Credential health checks + +Before running agent tests, credentials are validated with minimal API calls: +- Anthropic: `GET https://api.anthropic.com/v1/models` + - `x-api-key` for API keys + - `Authorization: Bearer` for OAuth tokens + - `anthropic-version: 2023-06-01` +- OpenAI: `GET https://api.openai.com/v1/models` with `Authorization: Bearer` + +401/403 yields a hard failure (`invalid credentials`). Other non-2xx responses or network +errors fail with a health-check error. + +Health checks run in a blocking thread to avoid Tokio runtime drop errors inside async tests. + +## Snapshot stability + +To keep snapshots deterministic: +- Event streams are truncated after the first assistant or error event. +- Permission flow snapshots are truncated after the permission request (or first assistant) event. +- Unknown events are preserved as `kind: unknown` (raw payload in universal schema). + +## Typical commands + +Run only Claude snapshots: +``` +SANDBOX_TEST_AGENTS=claude cargo test -p sandbox-agent-core --test http_sse_snapshots +``` + +Run all detected agents: +``` +cargo test -p sandbox-agent-core --test http_sse_snapshots +``` + +## Universal Schema + +When modifying agent conversion code in `server/packages/universal-agent-schema/src/agents/` or adding/changing properties on the universal schema, update the feature matrix in `README.md` to reflect which agents support which features. diff --git a/server/packages/agent-credentials/Cargo.toml b/server/packages/agent-credentials/Cargo.toml index 7429988..ef4e566 100644 --- a/server/packages/agent-credentials/Cargo.toml +++ b/server/packages/agent-credentials/Cargo.toml @@ -6,7 +6,7 @@ authors.workspace = true license.workspace = true [dependencies] -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -dirs = "5.0" -time = { version = "0.3", features = ["parsing", "formatting"] } +serde.workspace = true +serde_json.workspace = true +dirs.workspace = true +time.workspace = true diff --git a/server/packages/agent-management/Cargo.toml b/server/packages/agent-management/Cargo.toml index 73022b1..4b3bff1 100644 --- a/server/packages/agent-management/Cargo.toml +++ b/server/packages/agent-management/Cargo.toml @@ -6,15 +6,15 @@ authors.workspace = true license.workspace = true [dependencies] -thiserror = "1.0" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -sandbox-agent-agent-credentials = { path = "../agent-credentials" } -reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"] } -flate2 = "1.0" -tar = "0.4" -zip = { version = "0.6", default-features = false, features = ["deflate"] } -url = "2.5" -dirs = "5.0" -tempfile = "3.10" -time = { version = "0.3", features = ["parsing", "formatting"] } +sandbox-agent-agent-credentials.workspace = true +thiserror.workspace = true +serde.workspace = true +serde_json.workspace = true +reqwest.workspace = true +flate2.workspace = true +tar.workspace = true +zip.workspace = true +url.workspace = true +dirs.workspace = true +tempfile.workspace = true +time.workspace = true diff --git a/server/packages/agent-management/src/testing.rs b/server/packages/agent-management/src/testing.rs index f334a34..a28dbdb 100644 --- a/server/packages/agent-management/src/testing.rs +++ b/server/packages/agent-management/src/testing.rs @@ -1,9 +1,17 @@ use std::env; +use std::path::PathBuf; +use std::time::Duration; +use reqwest::blocking::Client; +use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION, CONTENT_TYPE}; +use reqwest::StatusCode; use thiserror::Error; use crate::agents::AgentId; -use crate::credentials::{AuthType, ExtractedCredentials, ProviderCredentials}; +use crate::credentials::{ + extract_all_credentials, AuthType, CredentialExtractionOptions, ExtractedCredentials, + ProviderCredentials, +}; #[derive(Debug, Clone)] pub struct TestAgentConfig { @@ -13,76 +21,121 @@ pub struct TestAgentConfig { #[derive(Debug, Error)] pub enum TestAgentConfigError { - #[error("no test agents configured (set SANDBOX_TEST_AGENTS)")] + #[error("no test agents detected (install agents or set SANDBOX_TEST_AGENTS)")] NoAgentsConfigured, #[error("unknown agent name: {0}")] UnknownAgent(String), #[error("missing credentials for {agent}: {missing}")] MissingCredentials { agent: AgentId, missing: String }, + #[error("invalid credentials for {provider} (status {status})")] + InvalidCredentials { provider: String, status: u16 }, + #[error("credential health check failed for {provider}: {message}")] + HealthCheckFailed { provider: String, message: String }, } const AGENTS_ENV: &str = "SANDBOX_TEST_AGENTS"; const ANTHROPIC_ENV: &str = "SANDBOX_TEST_ANTHROPIC_API_KEY"; const OPENAI_ENV: &str = "SANDBOX_TEST_OPENAI_API_KEY"; +const ANTHROPIC_MODELS_URL: &str = "https://api.anthropic.com/v1/models"; +const OPENAI_MODELS_URL: &str = "https://api.openai.com/v1/models"; +const ANTHROPIC_VERSION: &str = "2023-06-01"; + +#[derive(Default)] +struct HealthCheckCache { + anthropic_ok: bool, + openai_ok: bool, +} pub fn test_agents_from_env() -> Result, TestAgentConfigError> { let raw_agents = env::var(AGENTS_ENV).unwrap_or_default(); - let mut agents = Vec::new(); - for entry in raw_agents.split(',') { - let trimmed = entry.trim(); - if trimmed.is_empty() { - continue; + let mut agents = if raw_agents.trim().is_empty() { + detect_system_agents() + } else { + let mut agents = Vec::new(); + for entry in raw_agents.split(',') { + let trimmed = entry.trim(); + if trimmed.is_empty() { + continue; + } + if trimmed == "all" { + agents.extend([ + AgentId::Claude, + AgentId::Codex, + AgentId::Opencode, + AgentId::Amp, + ]); + continue; + } + let agent = AgentId::parse(trimmed) + .ok_or_else(|| TestAgentConfigError::UnknownAgent(trimmed.to_string()))?; + agents.push(agent); } - if trimmed == "all" { - agents.extend([ - AgentId::Claude, - AgentId::Codex, - AgentId::Opencode, - AgentId::Amp, - ]); - continue; - } - let agent = AgentId::parse(trimmed) - .ok_or_else(|| TestAgentConfigError::UnknownAgent(trimmed.to_string()))?; - agents.push(agent); - } + agents + }; + + agents.sort_by(|a, b| a.as_str().cmp(b.as_str())); + agents.dedup(); if agents.is_empty() { return Err(TestAgentConfigError::NoAgentsConfigured); } - let anthropic_key = read_env_key(ANTHROPIC_ENV); - let openai_key = read_env_key(OPENAI_ENV); + let extracted = extract_all_credentials(&CredentialExtractionOptions::new()); + let anthropic_cred = read_env_key(ANTHROPIC_ENV) + .map(|key| ProviderCredentials { + api_key: key, + source: "sandbox-test-env".to_string(), + auth_type: AuthType::ApiKey, + provider: "anthropic".to_string(), + }) + .or_else(|| extracted.anthropic.clone()); + let openai_cred = read_env_key(OPENAI_ENV) + .map(|key| ProviderCredentials { + api_key: key, + source: "sandbox-test-env".to_string(), + auth_type: AuthType::ApiKey, + provider: "openai".to_string(), + }) + .or_else(|| extracted.openai.clone()); + let mut health_cache = HealthCheckCache::default(); let mut configs = Vec::new(); for agent in agents { let credentials = match agent { AgentId::Claude | AgentId::Amp => { - let anthropic_key = anthropic_key.clone().ok_or_else(|| { + let anthropic_cred = anthropic_cred.clone().ok_or_else(|| { TestAgentConfigError::MissingCredentials { agent, missing: ANTHROPIC_ENV.to_string(), } })?; - credentials_with(anthropic_key, None) + ensure_anthropic_ok(&mut health_cache, &anthropic_cred)?; + credentials_with(Some(anthropic_cred), None) } AgentId::Codex => { - let openai_key = openai_key.clone().ok_or_else(|| { + let openai_cred = openai_cred.clone().ok_or_else(|| { TestAgentConfigError::MissingCredentials { agent, missing: OPENAI_ENV.to_string(), } })?; - credentials_with(None, Some(openai_key)) + ensure_openai_ok(&mut health_cache, &openai_cred)?; + credentials_with(None, Some(openai_cred)) } AgentId::Opencode => { - if anthropic_key.is_none() && openai_key.is_none() { + if anthropic_cred.is_none() && openai_cred.is_none() { return Err(TestAgentConfigError::MissingCredentials { agent, missing: format!("{ANTHROPIC_ENV} or {OPENAI_ENV}"), }); } - credentials_with(anthropic_key.clone(), openai_key.clone()) + if let Some(cred) = anthropic_cred.as_ref() { + ensure_anthropic_ok(&mut health_cache, cred)?; + } + if let Some(cred) = openai_cred.as_ref() { + ensure_openai_ok(&mut health_cache, cred)?; + } + credentials_with(anthropic_cred.clone(), openai_cred.clone()) } }; configs.push(TestAgentConfig { agent, credentials }); @@ -91,6 +144,178 @@ pub fn test_agents_from_env() -> Result, TestAgentConfigErr Ok(configs) } +fn ensure_anthropic_ok( + cache: &mut HealthCheckCache, + credentials: &ProviderCredentials, +) -> Result<(), TestAgentConfigError> { + if cache.anthropic_ok { + return Ok(()); + } + health_check_anthropic(credentials)?; + cache.anthropic_ok = true; + Ok(()) +} + +fn ensure_openai_ok( + cache: &mut HealthCheckCache, + credentials: &ProviderCredentials, +) -> Result<(), TestAgentConfigError> { + if cache.openai_ok { + return Ok(()); + } + health_check_openai(credentials)?; + cache.openai_ok = true; + Ok(()) +} + +fn health_check_anthropic(credentials: &ProviderCredentials) -> Result<(), TestAgentConfigError> { + let credentials = credentials.clone(); + run_blocking_check("anthropic", move || { + let client = Client::builder() + .timeout(Duration::from_secs(10)) + .build() + .map_err(|err| TestAgentConfigError::HealthCheckFailed { + provider: "anthropic".to_string(), + message: err.to_string(), + })?; + let mut headers = HeaderMap::new(); + match credentials.auth_type { + AuthType::ApiKey => { + headers.insert( + "x-api-key", + HeaderValue::from_str(&credentials.api_key).map_err(|_| { + TestAgentConfigError::HealthCheckFailed { + provider: "anthropic".to_string(), + message: "invalid anthropic api key header value".to_string(), + } + })?, + ); + } + AuthType::Oauth => { + let value = format!("Bearer {}", credentials.api_key); + headers.insert( + AUTHORIZATION, + HeaderValue::from_str(&value).map_err(|_| { + TestAgentConfigError::HealthCheckFailed { + provider: "anthropic".to_string(), + message: "invalid anthropic oauth header value".to_string(), + } + })?, + ); + } + } + headers.insert( + "anthropic-version", + HeaderValue::from_static(ANTHROPIC_VERSION), + ); + headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); + + let response = client + .get(ANTHROPIC_MODELS_URL) + .headers(headers) + .send() + .map_err(|err| TestAgentConfigError::HealthCheckFailed { + provider: "anthropic".to_string(), + message: err.to_string(), + })?; + handle_health_response("anthropic", response) + }) +} + +fn health_check_openai(credentials: &ProviderCredentials) -> Result<(), TestAgentConfigError> { + let credentials = credentials.clone(); + run_blocking_check("openai", move || { + let client = Client::builder() + .timeout(Duration::from_secs(10)) + .build() + .map_err(|err| TestAgentConfigError::HealthCheckFailed { + provider: "openai".to_string(), + message: err.to_string(), + })?; + let response = client + .get(OPENAI_MODELS_URL) + .bearer_auth(&credentials.api_key) + .send() + .map_err(|err| TestAgentConfigError::HealthCheckFailed { + provider: "openai".to_string(), + message: err.to_string(), + })?; + handle_health_response("openai", response) + }) +} + +fn handle_health_response( + provider: &str, + response: reqwest::blocking::Response, +) -> Result<(), TestAgentConfigError> { + let status = response.status(); + if status.is_success() { + return Ok(()); + } + if status == StatusCode::UNAUTHORIZED || status == StatusCode::FORBIDDEN { + return Err(TestAgentConfigError::InvalidCredentials { + provider: provider.to_string(), + status: status.as_u16(), + }); + } + let body = response.text().unwrap_or_default(); + let mut summary = body.trim().to_string(); + if summary.len() > 200 { + summary.truncate(200); + } + Err(TestAgentConfigError::HealthCheckFailed { + provider: provider.to_string(), + message: format!("status {}: {}", status.as_u16(), summary), + }) +} + +fn run_blocking_check( + provider: &str, + check: F, +) -> Result<(), TestAgentConfigError> +where + F: FnOnce() -> Result<(), TestAgentConfigError> + Send + 'static, +{ + std::thread::spawn(check).join().unwrap_or_else(|_| { + Err(TestAgentConfigError::HealthCheckFailed { + provider: provider.to_string(), + message: "health check panicked".to_string(), + }) + }) +} + +fn detect_system_agents() -> Vec { + let candidates = [AgentId::Claude, AgentId::Codex, AgentId::Opencode, AgentId::Amp]; + let install_dir = default_install_dir(); + candidates + .into_iter() + .filter(|agent| { + let binary = agent.binary_name(); + find_in_path(binary) || install_dir.join(binary).exists() + }) + .collect() +} + +fn default_install_dir() -> PathBuf { + dirs::data_dir() + .map(|dir| dir.join("sandbox-agent").join("bin")) + .unwrap_or_else(|| PathBuf::from(".").join(".sandbox-agent").join("bin")) +} + +fn find_in_path(binary_name: &str) -> bool { + let path_var = match env::var_os("PATH") { + Some(path) => path, + None => return false, + }; + for path in env::split_paths(&path_var) { + let candidate = path.join(binary_name); + if candidate.exists() { + return true; + } + } + false +} + fn read_env_key(name: &str) -> Option { env::var(name).ok().and_then(|value| { let trimmed = value.trim().to_string(); @@ -103,25 +328,11 @@ fn read_env_key(name: &str) -> Option { } fn credentials_with( - anthropic_key: Option, - openai_key: Option, + anthropic_cred: Option, + openai_cred: Option, ) -> ExtractedCredentials { let mut credentials = ExtractedCredentials::default(); - if let Some(key) = anthropic_key { - credentials.anthropic = Some(ProviderCredentials { - api_key: key, - source: "sandbox-test-env".to_string(), - auth_type: AuthType::ApiKey, - provider: "anthropic".to_string(), - }); - } - if let Some(key) = openai_key { - credentials.openai = Some(ProviderCredentials { - api_key: key, - source: "sandbox-test-env".to_string(), - auth_type: AuthType::ApiKey, - provider: "openai".to_string(), - }); - } + credentials.anthropic = anthropic_cred; + credentials.openai = openai_cred; credentials } diff --git a/server/packages/agent-schema/Cargo.toml b/server/packages/agent-schema/Cargo.toml deleted file mode 100644 index f8350d7..0000000 --- a/server/packages/agent-schema/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "sandbox-agent-agent-schema" -version.workspace = true -edition.workspace = true -authors.workspace = true -license.workspace = true - -[dependencies] -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -regress = "0.10" - -[build-dependencies] -typify = "0.4" -serde_json = "1.0" -schemars = "0.8" -prettyplease = "0.2" -syn = "2.0" diff --git a/server/packages/agent-schema/src/lib.rs b/server/packages/agent-schema/src/lib.rs deleted file mode 100644 index 558932a..0000000 --- a/server/packages/agent-schema/src/lib.rs +++ /dev/null @@ -1,76 +0,0 @@ -//! Generated types from AI coding agent JSON schemas. -//! -//! This crate provides Rust types for: -//! - OpenCode SDK -//! - Claude Code SDK -//! - Codex SDK -//! - AMP Code SDK - -pub mod opencode { - //! OpenCode SDK types extracted from OpenAPI 3.1.1 spec. - include!(concat!(env!("OUT_DIR"), "/opencode.rs")); -} - -pub mod claude { - //! Claude Code SDK types extracted from TypeScript definitions. - include!(concat!(env!("OUT_DIR"), "/claude.rs")); -} - -pub mod codex { - //! Codex SDK types. - include!(concat!(env!("OUT_DIR"), "/codex.rs")); -} - -pub mod amp { - //! AMP Code SDK types. - include!(concat!(env!("OUT_DIR"), "/amp.rs")); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_claude_bash_input() { - let input = claude::BashInput { - command: "ls -la".to_string(), - timeout: Some(5000.0), - description: Some("List files".to_string()), - run_in_background: None, - simulated_sed_edit: None, - dangerously_disable_sandbox: None, - }; - - let json = serde_json::to_string(&input).unwrap(); - assert!(json.contains("ls -la")); - - let parsed: claude::BashInput = serde_json::from_str(&json).unwrap(); - assert_eq!(parsed.command, "ls -la"); - } - - #[test] - fn test_codex_thread_event() { - let event = codex::ThreadEvent { - type_: codex::ThreadEventType::ThreadCreated, - thread_id: Some("thread-123".to_string()), - item: None, - error: serde_json::Map::new(), - }; - - let json = serde_json::to_string(&event).unwrap(); - assert!(json.contains("thread.created")); - } - - #[test] - fn test_amp_message() { - let msg = amp::Message { - role: amp::MessageRole::User, - content: "Hello".to_string(), - tool_calls: vec![], - }; - - let json = serde_json::to_string(&msg).unwrap(); - assert!(json.contains("user")); - assert!(json.contains("Hello")); - } -} diff --git a/server/packages/error/Cargo.toml b/server/packages/error/Cargo.toml index 7845173..77a8265 100644 --- a/server/packages/error/Cargo.toml +++ b/server/packages/error/Cargo.toml @@ -6,8 +6,8 @@ authors.workspace = true license.workspace = true [dependencies] -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -thiserror = "1.0" -schemars = "0.8" -utoipa = "4.2" +serde.workspace = true +serde_json.workspace = true +thiserror.workspace = true +schemars.workspace = true +utoipa.workspace = true diff --git a/server/packages/extracted-agent-schemas/Cargo.toml b/server/packages/extracted-agent-schemas/Cargo.toml new file mode 100644 index 0000000..8534627 --- /dev/null +++ b/server/packages/extracted-agent-schemas/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "sandbox-agent-extracted-agent-schemas" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true + +[dependencies] +serde.workspace = true +serde_json.workspace = true +regress.workspace = true +chrono.workspace = true + +[build-dependencies] +typify.workspace = true +serde_json.workspace = true +schemars.workspace = true +prettyplease.workspace = true +syn.workspace = true diff --git a/server/packages/agent-schema/build.rs b/server/packages/extracted-agent-schemas/build.rs similarity index 98% rename from server/packages/agent-schema/build.rs rename to server/packages/extracted-agent-schemas/build.rs index 7fc5c21..6ab8f0c 100644 --- a/server/packages/agent-schema/build.rs +++ b/server/packages/extracted-agent-schemas/build.rs @@ -4,7 +4,7 @@ use std::path::Path; fn main() { let out_dir = std::env::var("OUT_DIR").unwrap(); - let schema_dir = Path::new("../../../resources/agent-schemas/dist"); + let schema_dir = Path::new("../../../resources/agent-schemas/artifacts/json-schema"); let schemas = [ ("opencode", "opencode.json"), diff --git a/server/packages/extracted-agent-schemas/src/lib.rs b/server/packages/extracted-agent-schemas/src/lib.rs new file mode 100644 index 0000000..d4f6069 --- /dev/null +++ b/server/packages/extracted-agent-schemas/src/lib.rs @@ -0,0 +1,111 @@ +//! Generated types from AI coding agent JSON schemas. +//! +//! This crate provides Rust types for: +//! - OpenCode SDK +//! - Claude Code SDK +//! - Codex SDK +//! - AMP Code SDK + +pub mod opencode { + //! OpenCode SDK types extracted from OpenAPI 3.1.1 spec. + include!(concat!(env!("OUT_DIR"), "/opencode.rs")); +} + +pub mod claude { + //! Claude Code SDK types extracted from TypeScript definitions. + include!(concat!(env!("OUT_DIR"), "/claude.rs")); +} + +pub mod codex { + //! Codex SDK types. + include!(concat!(env!("OUT_DIR"), "/codex.rs")); +} + +pub mod amp { + //! AMP Code SDK types. + include!(concat!(env!("OUT_DIR"), "/amp.rs")); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_claude_bash_input() { + let input = claude::BashInput { + command: "ls -la".to_string(), + timeout: Some(5000.0), + working_directory: None, + }; + + let json = serde_json::to_string(&input).unwrap(); + assert!(json.contains("ls -la")); + + let parsed: claude::BashInput = serde_json::from_str(&json).unwrap(); + assert_eq!(parsed.command, "ls -la"); + } + + #[test] + fn test_codex_server_notification() { + // Test ItemCompletedNotification with AgentMessage + let notification = codex::ServerNotification::ItemCompleted( + codex::ItemCompletedNotification { + item: codex::ThreadItem::AgentMessage { + id: "msg-123".to_string(), + text: "Hello from Codex".to_string(), + }, + thread_id: "thread-123".to_string(), + turn_id: "turn-456".to_string(), + } + ); + + let json = serde_json::to_string(¬ification).unwrap(); + assert!(json.contains("item/completed")); + assert!(json.contains("Hello from Codex")); + assert!(json.contains("agentMessage")); + } + + #[test] + fn test_codex_thread_item_variants() { + // Test UserMessage variant + let user_msg = codex::ThreadItem::UserMessage { + content: vec![codex::UserInput::Text { + text: "Hello".to_string(), + text_elements: vec![], + }], + id: "user-1".to_string(), + }; + let json = serde_json::to_string(&user_msg).unwrap(); + assert!(json.contains("userMessage")); + assert!(json.contains("Hello")); + + // Test CommandExecution variant + let cmd = codex::ThreadItem::CommandExecution { + aggregated_output: Some("output".to_string()), + command: "ls -la".to_string(), + command_actions: vec![], + cwd: "/tmp".to_string(), + duration_ms: Some(100), + exit_code: Some(0), + id: "cmd-1".to_string(), + process_id: None, + status: codex::CommandExecutionStatus::Completed, + }; + let json = serde_json::to_string(&cmd).unwrap(); + assert!(json.contains("commandExecution")); + assert!(json.contains("ls -la")); + } + + #[test] + fn test_amp_message() { + let msg = amp::Message { + role: amp::MessageRole::User, + content: "Hello".to_string(), + tool_calls: vec![], + }; + + let json = serde_json::to_string(&msg).unwrap(); + assert!(json.contains("user")); + assert!(json.contains("Hello")); + } +} diff --git a/server/packages/openapi-gen/Cargo.toml b/server/packages/openapi-gen/Cargo.toml index 92ccdb2..839bd0d 100644 --- a/server/packages/openapi-gen/Cargo.toml +++ b/server/packages/openapi-gen/Cargo.toml @@ -7,11 +7,11 @@ license.workspace = true build = "build.rs" [dependencies] -tracing = "0.1" -tracing-logfmt = "0.3" -tracing-subscriber = { version = "0.3", features = ["env-filter"] } +tracing.workspace = true +tracing-logfmt.workspace = true +tracing-subscriber.workspace = true [build-dependencies] -sandbox-agent-core = { path = "../sandbox-agent" } -serde_json = "1.0" -utoipa = "4.2" +sandbox-agent-core.workspace = true +serde_json.workspace = true +utoipa.workspace = true diff --git a/server/packages/sandbox-agent/Cargo.toml b/server/packages/sandbox-agent/Cargo.toml index 39e1126..c35e0a4 100644 --- a/server/packages/sandbox-agent/Cargo.toml +++ b/server/packages/sandbox-agent/Cargo.toml @@ -10,30 +10,30 @@ name = "sandbox-agent" path = "src/main.rs" [dependencies] -thiserror = "1.0" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -axum = "0.7" -clap = { version = "4.5", features = ["derive"] } -futures = "0.3" -sandbox-agent-error = { path = "../error" } -sandbox-agent-agent-management = { path = "../agent-management" } -sandbox-agent-agent-credentials = { path = "../agent-credentials" } -sandbox-agent-universal-agent-schema = { path = "../universal-agent-schema" } -reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls", "stream"] } -dirs = "5.0" -time = { version = "0.3", features = ["parsing", "formatting"] } -tokio = { version = "1.36", features = ["macros", "rt-multi-thread", "signal", "time"] } -tokio-stream = { version = "0.1", features = ["sync"] } -tower-http = { version = "0.5", features = ["cors", "trace"] } -utoipa = { version = "4.2", features = ["axum_extras"] } -schemars = "0.8" -tracing = "0.1" -tracing-logfmt = "0.3" -tracing-subscriber = { version = "0.3", features = ["env-filter"] } +sandbox-agent-error.workspace = true +sandbox-agent-agent-management.workspace = true +sandbox-agent-agent-credentials.workspace = true +sandbox-agent-universal-agent-schema.workspace = true +thiserror.workspace = true +serde.workspace = true +serde_json.workspace = true +axum.workspace = true +clap.workspace = true +futures.workspace = true +reqwest.workspace = true +dirs.workspace = true +time.workspace = true +tokio.workspace = true +tokio-stream.workspace = true +tower-http.workspace = true +utoipa.workspace = true +schemars.workspace = true +tracing.workspace = true +tracing-logfmt.workspace = true +tracing-subscriber.workspace = true [dev-dependencies] -http-body-util = "0.1" -insta = "1.41" -tempfile = "3.10" -tower = "0.4" +http-body-util.workspace = true +insta.workspace = true +tempfile.workspace = true +tower.workspace = true diff --git a/server/packages/sandbox-agent/src/router.rs b/server/packages/sandbox-agent/src/router.rs index e39aad1..8900ef8 100644 --- a/server/packages/sandbox-agent/src/router.rs +++ b/server/packages/sandbox-agent/src/router.rs @@ -1202,6 +1202,11 @@ async fn require_token( req: Request, next: Next, ) -> Result { + let path = req.uri().path(); + if path == "/v1/health" || path == "/health" { + return Ok(next.run(req).await); + } + let expected = match &state.auth.token { Some(token) => token.as_str(), None => return Ok(next.run(req).await), @@ -1946,7 +1951,7 @@ fn parse_agent_line(agent: AgentId, line: &str, session_id: &str) -> Option match serde_json::from_value(value.clone()) { - Ok(event) => convert_codex::event_to_universal(&event), + Ok(notification) => convert_codex::notification_to_universal(¬ification), Err(err) => EventConversion::new(unparsed_message( &value.to_string(), &err.to_string(), diff --git a/server/packages/sandbox-agent/tests/http_sse_snapshots.rs b/server/packages/sandbox-agent/tests/http_sse_snapshots.rs index 7198d90..59c45ef 100644 --- a/server/packages/sandbox-agent/tests/http_sse_snapshots.rs +++ b/server/packages/sandbox-agent/tests/http_sse_snapshots.rs @@ -1,8 +1,8 @@ use std::collections::BTreeMap; use std::time::{Duration, Instant}; -use axum::body::Body; -use axum::http::{Method, Request, StatusCode}; +use axum::body::{Body, Bytes}; +use axum::http::{header, HeaderMap, HeaderValue, Method, Request, StatusCode}; use axum::Router; use futures::StreamExt; use http_body_util::BodyExt; @@ -13,9 +13,13 @@ use sandbox_agent_agent_management::agents::{AgentId, AgentManager}; use sandbox_agent_agent_management::testing::{test_agents_from_env, TestAgentConfig}; use sandbox_agent_agent_credentials::ExtractedCredentials; use sandbox_agent_core::router::{build_router, AppState, AuthConfig}; -use tower::ServiceExt; +use tower::util::ServiceExt; +use tower_http::cors::CorsLayer; const PROMPT: &str = "Reply with exactly the single word OK."; +const PERMISSION_PROMPT: &str = "List files in the current directory using available tools."; +const QUESTION_PROMPT: &str = + "Ask the user a multiple-choice question with options yes/no using any built-in AskUserQuestion tool, then wait."; struct TestApp { app: Router, @@ -24,11 +28,22 @@ struct TestApp { impl TestApp { fn new() -> Self { + Self::new_with_auth(AuthConfig::disabled()) + } + + fn new_with_auth(auth: AuthConfig) -> Self { + Self::new_with_auth_and_cors(auth, None) + } + + fn new_with_auth_and_cors(auth: AuthConfig, cors: Option) -> Self { let install_dir = tempfile::tempdir().expect("create temp install dir"); let manager = AgentManager::new(install_dir.path()) .expect("create agent manager"); - let state = AppState::new(AuthConfig::disabled(), manager); - let app = build_router(state); + let state = AppState::new(auth, manager); + let mut app = build_router(state); + if let Some(cors) = cors { + app = app.layer(cors); + } Self { app, _install_dir: install_dir, @@ -112,6 +127,37 @@ async fn send_json(app: &Router, method: Method, path: &str, body: Option (status, value) } +async fn send_request(app: &Router, request: Request) -> (StatusCode, HeaderMap, Bytes) { + let response = app + .clone() + .oneshot(request) + .await + .expect("request handled"); + let status = response.status(); + let headers = response.headers().clone(); + let bytes = response + .into_body() + .collect() + .await + .expect("read body") + .to_bytes(); + (status, headers, bytes) +} + +async fn send_json_request( + app: &Router, + request: Request, +) -> (StatusCode, HeaderMap, Value) { + let (status, headers, bytes) = send_request(app, request).await; + let value = if bytes.is_empty() { + Value::Null + } else { + serde_json::from_slice(&bytes) + .unwrap_or(Value::String(String::from_utf8_lossy(&bytes).to_string())) + }; + (status, headers, value) +} + async fn send_status(app: &Router, method: Method, path: &str, body: Option) -> StatusCode { let (status, _) = send_json(app, method, path, body).await; status @@ -128,14 +174,14 @@ async fn install_agent(app: &Router, agent: AgentId) { assert_eq!(status, StatusCode::NO_CONTENT, "install {agent}"); } -async fn create_session(app: &Router, agent: AgentId, session_id: &str) { +async fn create_session(app: &Router, agent: AgentId, session_id: &str, permission_mode: &str) { let status = send_status( app, Method::POST, &format!("/v1/sessions/{session_id}"), Some(json!({ "agent": agent.as_str(), - "permissionMode": "bypass" + "permissionMode": permission_mode })), ) .await; @@ -211,7 +257,7 @@ async fn read_sse_events( _ => break, }; let next = tokio::time::timeout(remaining, stream.next()).await; - let chunk = match next { + let chunk: Bytes = match next { Ok(Some(Ok(chunk))) => chunk, Ok(Some(Err(_))) => break, Ok(None) => break, @@ -267,6 +313,23 @@ fn is_error_event(event: &Value) -> bool { .is_some() } +fn is_permission_event(event: &Value) -> bool { + event + .get("data") + .and_then(|data| data.get("permissionAsked")) + .is_some() +} + +fn truncate_permission_events(events: &[Value]) -> Vec { + if let Some(idx) = events.iter().position(is_permission_event) { + return events[..=idx].to_vec(); + } + if let Some(idx) = events.iter().position(is_assistant_message) { + return events[..=idx].to_vec(); + } + events.to_vec() +} + fn normalize_events(events: &[Value]) -> Value { let normalized = events .iter() @@ -276,6 +339,16 @@ fn normalize_events(events: &[Value]) -> Value { Value::Array(normalized) } +fn truncate_after_first_stop(events: &[Value]) -> Vec { + if let Some(idx) = events + .iter() + .position(|event| is_assistant_message(event) || is_error_event(event)) + { + return events[..=idx].to_vec(); + } + events.to_vec() +} + fn normalize_event(event: &Value, seq: usize) -> Value { let mut map = Map::new(); map.insert("seq".to_string(), Value::Number(seq.into())); @@ -379,8 +452,239 @@ fn normalize_permission(permission: &Value) -> Value { Value::Object(map) } -fn snapshot_name(prefix: &str, agent: AgentId) -> String { - format!("{prefix}_{}", agent.as_str()) +fn normalize_agent_list(value: &Value) -> Value { + let agents = value + .get("agents") + .and_then(Value::as_array) + .cloned() + .unwrap_or_default(); + let mut normalized = Vec::new(); + for agent in agents { + let mut map = Map::new(); + if let Some(id) = agent.get("id").and_then(Value::as_str) { + map.insert("id".to_string(), Value::String(id.to_string())); + } + // Skip installed/version/path fields - they depend on local environment + // and make snapshots non-deterministic + normalized.push(Value::Object(map)); + } + normalized.sort_by(|a, b| { + a.get("id") + .and_then(Value::as_str) + .cmp(&b.get("id").and_then(Value::as_str)) + }); + json!({ "agents": normalized }) +} + +fn normalize_agent_modes(value: &Value) -> Value { + let modes = value + .get("modes") + .and_then(Value::as_array) + .cloned() + .unwrap_or_default(); + let mut normalized = Vec::new(); + for mode in modes { + let mut map = Map::new(); + if let Some(id) = mode.get("id").and_then(Value::as_str) { + map.insert("id".to_string(), Value::String(id.to_string())); + } + if let Some(name) = mode.get("name").and_then(Value::as_str) { + map.insert("name".to_string(), Value::String(name.to_string())); + } + if mode.get("description").is_some() { + map.insert("description".to_string(), Value::Bool(true)); + } + normalized.push(Value::Object(map)); + } + normalized.sort_by(|a, b| { + a.get("id") + .and_then(Value::as_str) + .cmp(&b.get("id").and_then(Value::as_str)) + }); + json!({ "modes": normalized }) +} + +fn normalize_sessions(value: &Value) -> Value { + let sessions = value + .get("sessions") + .and_then(Value::as_array) + .cloned() + .unwrap_or_default(); + let mut normalized = Vec::new(); + for session in sessions { + let mut map = Map::new(); + if let Some(session_id) = session.get("sessionId").and_then(Value::as_str) { + map.insert("sessionId".to_string(), Value::String(session_id.to_string())); + } + if let Some(agent) = session.get("agent").and_then(Value::as_str) { + map.insert("agent".to_string(), Value::String(agent.to_string())); + } + if let Some(agent_mode) = session.get("agentMode").and_then(Value::as_str) { + map.insert("agentMode".to_string(), Value::String(agent_mode.to_string())); + } + if let Some(permission_mode) = session.get("permissionMode").and_then(Value::as_str) { + map.insert("permissionMode".to_string(), Value::String(permission_mode.to_string())); + } + if session.get("model").is_some() { + map.insert("model".to_string(), Value::String("".to_string())); + } + if session.get("variant").is_some() { + map.insert("variant".to_string(), Value::String("".to_string())); + } + if session.get("agentSessionId").is_some() { + map.insert("agentSessionId".to_string(), Value::String("".to_string())); + } + if let Some(ended) = session.get("ended").and_then(Value::as_bool) { + map.insert("ended".to_string(), Value::Bool(ended)); + } + if session.get("eventCount").is_some() { + map.insert("eventCount".to_string(), Value::String("".to_string())); + } + normalized.push(Value::Object(map)); + } + normalized.sort_by(|a, b| { + a.get("sessionId") + .and_then(Value::as_str) + .cmp(&b.get("sessionId").and_then(Value::as_str)) + }); + json!({ "sessions": normalized }) +} + +fn normalize_create_session(value: &Value) -> Value { + let mut map = Map::new(); + if let Some(healthy) = value.get("healthy").and_then(Value::as_bool) { + map.insert("healthy".to_string(), Value::Bool(healthy)); + } + if value.get("agentSessionId").is_some() { + map.insert("agentSessionId".to_string(), Value::String("".to_string())); + } + if let Some(error) = value.get("error") { + map.insert("error".to_string(), error.clone()); + } + Value::Object(map) +} + +fn normalize_health(value: &Value) -> Value { + let mut map = Map::new(); + if let Some(status) = value.get("status").and_then(Value::as_str) { + map.insert("status".to_string(), Value::String(status.to_string())); + } + Value::Object(map) +} + +fn snapshot_status(status: StatusCode) -> Value { + json!({ "status": status.as_u16() }) +} + +fn snapshot_cors(status: StatusCode, headers: &HeaderMap) -> Value { + let mut map = Map::new(); + map.insert("status".to_string(), Value::Number(status.as_u16().into())); + for name in [ + header::ACCESS_CONTROL_ALLOW_ORIGIN, + header::ACCESS_CONTROL_ALLOW_METHODS, + header::ACCESS_CONTROL_ALLOW_HEADERS, + header::ACCESS_CONTROL_ALLOW_CREDENTIALS, + header::VARY, + ] { + if let Some(value) = headers.get(&name) { + map.insert( + name.as_str().to_string(), + Value::String(value.to_str().unwrap_or("").to_string()), + ); + } + } + Value::Object(map) +} + +fn snapshot_name(prefix: &str, agent: Option) -> String { + match agent { + Some(agent) => format!("{prefix}_{}", agent.as_str()), + None => format!("{prefix}_global"), + } +} + + +async fn poll_events_until_match( + app: &Router, + session_id: &str, + timeout: Duration, + stop: F, +) -> Vec +where + F: Fn(&[Value]) -> bool, +{ + let start = Instant::now(); + let mut offset = 0u64; + let mut events = Vec::new(); + while start.elapsed() < timeout { + let path = format!("/v1/sessions/{session_id}/events?offset={offset}&limit=200"); + let (status, payload) = send_json(app, Method::GET, &path, None).await; + assert_eq!(status, StatusCode::OK, "poll events"); + let new_events = payload + .get("events") + .and_then(Value::as_array) + .cloned() + .unwrap_or_default(); + if !new_events.is_empty() { + if let Some(last) = new_events + .last() + .and_then(|event| event.get("id")) + .and_then(Value::as_u64) + { + offset = last; + } + events.extend(new_events); + if stop(&events) { + break; + } + } + tokio::time::sleep(Duration::from_millis(800)).await; + } + events +} + +fn find_permission_id(events: &[Value]) -> Option { + events + .iter() + .find_map(|event| { + event + .get("data") + .and_then(|data| data.get("permissionAsked")) + .and_then(|permission| permission.get("id")) + .and_then(Value::as_str) + .map(|id| id.to_string()) + }) +} + +fn find_question_id_and_answers(events: &[Value]) -> Option<(String, Vec>)> { + let question = events.iter().find_map(|event| { + event + .get("data") + .and_then(|data| data.get("questionAsked")) + .cloned() + })?; + let id = question.get("id").and_then(Value::as_str)?.to_string(); + let questions = question + .get("questions") + .and_then(Value::as_array) + .cloned() + .unwrap_or_default(); + let mut answers = Vec::new(); + for question in questions { + let option = question + .get("options") + .and_then(Value::as_array) + .and_then(|options| options.first()) + .and_then(|option| option.get("label")) + .and_then(Value::as_str) + .map(|label| label.to_string()); + if let Some(label) = option { + answers.push(vec![label]); + } else { + answers.push(Vec::new()); + } + } + Some((id, answers)) } async fn run_http_events_snapshot(app: &Router, config: &TestAgentConfig) { @@ -388,10 +692,11 @@ async fn run_http_events_snapshot(app: &Router, config: &TestAgentConfig) { install_agent(app, config.agent).await; let session_id = format!("session-{}", config.agent.as_str()); - create_session(app, config.agent, &session_id).await; + create_session(app, config.agent, &session_id, "bypass").await; send_message(app, &session_id).await; let events = poll_events_until(app, &session_id, Duration::from_secs(120)).await; + let events = truncate_after_first_stop(&events); assert!( !events.is_empty(), "no events collected for {}", @@ -404,7 +709,7 @@ async fn run_http_events_snapshot(app: &Router, config: &TestAgentConfig) { ); let normalized = normalize_events(&events); insta::with_settings!({ - snapshot_suffix => snapshot_name("http_events", config.agent), + snapshot_suffix => snapshot_name("http_events", Some(config.agent)), }, { insta::assert_yaml_snapshot!(normalized); }); @@ -415,7 +720,7 @@ async fn run_sse_events_snapshot(app: &Router, config: &TestAgentConfig) { install_agent(app, config.agent).await; let session_id = format!("sse-{}", config.agent.as_str()); - create_session(app, config.agent, &session_id).await; + create_session(app, config.agent, &session_id, "bypass").await; let sse_task = { let app = app.clone(); @@ -428,6 +733,7 @@ async fn run_sse_events_snapshot(app: &Router, config: &TestAgentConfig) { send_message(app, &session_id).await; let events = sse_task.await.expect("sse task"); + let events = truncate_after_first_stop(&events); assert!( !events.is_empty(), "no sse events collected for {}", @@ -440,26 +746,494 @@ async fn run_sse_events_snapshot(app: &Router, config: &TestAgentConfig) { ); let normalized = normalize_events(&events); insta::with_settings!({ - snapshot_suffix => snapshot_name("sse_events", config.agent), + snapshot_suffix => snapshot_name("sse_events", Some(config.agent)), }, { insta::assert_yaml_snapshot!(normalized); }); } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn auth_snapshots() { + let token = "test-token"; + let app = TestApp::new_with_auth(AuthConfig::with_token(token.to_string())); + + let (status, payload) = send_json(&app.app, Method::GET, "/v1/health", None).await; + assert_eq!(status, StatusCode::OK, "health should be public"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("auth_health_public", None), + }, { + insta::assert_yaml_snapshot!(json!({ + "status": status.as_u16(), + "payload": normalize_health(&payload), + })); + }); + + let (status, payload) = send_json(&app.app, Method::GET, "/v1/agents", None).await; + assert_eq!(status, StatusCode::UNAUTHORIZED, "missing token should 401"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("auth_missing_token", None), + }, { + insta::assert_yaml_snapshot!(json!({ + "status": status.as_u16(), + "payload": payload, + })); + }); + + let request = Request::builder() + .method(Method::GET) + .uri("/v1/agents") + .header(header::AUTHORIZATION, "Bearer wrong-token") + .body(Body::empty()) + .expect("auth invalid request"); + let (status, _headers, payload) = send_json_request(&app.app, request).await; + assert_eq!(status, StatusCode::UNAUTHORIZED, "invalid token should 401"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("auth_invalid_token", None), + }, { + insta::assert_yaml_snapshot!(json!({ + "status": status.as_u16(), + "payload": payload, + })); + }); + + let request = Request::builder() + .method(Method::GET) + .uri("/v1/agents") + .header(header::AUTHORIZATION, format!("Bearer {token}")) + .body(Body::empty()) + .expect("auth valid request"); + let (status, _headers, payload) = send_json_request(&app.app, request).await; + assert_eq!(status, StatusCode::OK, "valid token should allow request"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("auth_valid_token", None), + }, { + insta::assert_yaml_snapshot!(json!({ + "status": status.as_u16(), + "payload": normalize_agent_list(&payload), + })); + }); +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn cors_snapshots() { + let cors = CorsLayer::new() + .allow_origin(vec![HeaderValue::from_static("http://example.com")]) + .allow_methods([Method::GET, Method::POST]) + .allow_headers([header::CONTENT_TYPE, header::AUTHORIZATION]) + .allow_credentials(true); + let app = TestApp::new_with_auth_and_cors(AuthConfig::disabled(), Some(cors)); + + let preflight = Request::builder() + .method(Method::OPTIONS) + .uri("/v1/health") + .header(header::ORIGIN, "http://example.com") + .header(header::ACCESS_CONTROL_REQUEST_METHOD, "GET") + .header( + header::ACCESS_CONTROL_REQUEST_HEADERS, + "authorization,content-type", + ) + .body(Body::empty()) + .expect("cors preflight request"); + let (status, headers, _payload) = send_request(&app.app, preflight).await; + insta::with_settings!({ + snapshot_suffix => snapshot_name("cors_preflight", None), + }, { + insta::assert_yaml_snapshot!(snapshot_cors(status, &headers)); + }); + + let actual = Request::builder() + .method(Method::GET) + .uri("/v1/health") + .header(header::ORIGIN, "http://example.com") + .body(Body::empty()) + .expect("cors actual request"); + let (status, headers, payload) = send_json_request(&app.app, actual).await; + assert_eq!(status, StatusCode::OK, "cors actual request should succeed"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("cors_actual", None), + }, { + insta::assert_yaml_snapshot!(json!({ + "cors": snapshot_cors(status, &headers), + "payload": normalize_health(&payload), + })); + }); +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn api_endpoints_snapshots() { + let configs = test_agents_from_env().expect("configure SANDBOX_TEST_AGENTS or install agents"); + let app = TestApp::new(); + + let (status, health) = send_json(&app.app, Method::GET, "/v1/health", None).await; + assert_eq!(status, StatusCode::OK, "health status"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("health", None), + }, { + insta::assert_yaml_snapshot!(normalize_health(&health)); + }); + + // List agents (just verify the API returns correct agent IDs, not install state) + let (status, agents) = send_json(&app.app, Method::GET, "/v1/agents", None).await; + assert_eq!(status, StatusCode::OK, "agents list"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("agents_list", None), + }, { + insta::assert_yaml_snapshot!(normalize_agent_list(&agents)); + }); + + // Install agents (ensure they're available for subsequent tests) + for config in &configs { + let _guard = apply_credentials(&config.credentials); + let status = send_status( + &app.app, + Method::POST, + &format!("/v1/agents/{}/install", config.agent.as_str()), + Some(json!({})), + ) + .await; + assert_eq!(status, StatusCode::NO_CONTENT, "install agent"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("agent_install", Some(config.agent)), + }, { + insta::assert_yaml_snapshot!(snapshot_status(status)); + }); + } + + let mut session_ids = Vec::new(); + for config in &configs { + let _guard = apply_credentials(&config.credentials); + let (status, modes) = send_json( + &app.app, + Method::GET, + &format!("/v1/agents/{}/modes", config.agent.as_str()), + None, + ) + .await; + assert_eq!(status, StatusCode::OK, "agent modes"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("agent_modes", Some(config.agent)), + }, { + insta::assert_yaml_snapshot!(normalize_agent_modes(&modes)); + }); + + let session_id = format!("snapshot-{}", config.agent.as_str()); + let (status, created) = send_json( + &app.app, + Method::POST, + &format!("/v1/sessions/{session_id}"), + Some(json!({ + "agent": config.agent.as_str(), + "permissionMode": "bypass" + })), + ) + .await; + assert_eq!(status, StatusCode::OK, "create session"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("create_session", Some(config.agent)), + }, { + insta::assert_yaml_snapshot!(normalize_create_session(&created)); + }); + session_ids.push((config.agent, session_id)); + } + + let (status, sessions) = send_json(&app.app, Method::GET, "/v1/sessions", None).await; + assert_eq!(status, StatusCode::OK, "list sessions"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("sessions_list", None), + }, { + insta::assert_yaml_snapshot!(normalize_sessions(&sessions)); + }); + + for (agent, session_id) in &session_ids { + let status = send_status( + &app.app, + Method::POST, + &format!("/v1/sessions/{session_id}/messages"), + Some(json!({ "message": PROMPT })), + ) + .await; + assert_eq!(status, StatusCode::NO_CONTENT, "send message"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("send_message", Some(*agent)), + }, { + insta::assert_yaml_snapshot!(snapshot_status(status)); + }); + } +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn approval_flow_snapshots() { + let configs = test_agents_from_env().expect("configure SANDBOX_TEST_AGENTS or install agents"); + let app = TestApp::new(); + + for config in &configs { + let _guard = apply_credentials(&config.credentials); + install_agent(&app.app, config.agent).await; + + let permission_session = format!("perm-{}", config.agent.as_str()); + create_session(&app.app, config.agent, &permission_session, "plan").await; + let status = send_status( + &app.app, + Method::POST, + &format!("/v1/sessions/{permission_session}/messages"), + Some(json!({ "message": PERMISSION_PROMPT })), + ) + .await; + assert_eq!(status, StatusCode::NO_CONTENT, "send permission prompt"); + + let permission_events = poll_events_until_match( + &app.app, + &permission_session, + Duration::from_secs(120), + |events| find_permission_id(events).is_some() || should_stop(events), + ) + .await; + let permission_events = truncate_permission_events(&permission_events); + insta::with_settings!({ + snapshot_suffix => snapshot_name("permission_events", Some(config.agent)), + }, { + insta::assert_yaml_snapshot!(normalize_events(&permission_events)); + }); + + if let Some(permission_id) = find_permission_id(&permission_events) { + let status = send_status( + &app.app, + Method::POST, + &format!( + "/v1/sessions/{permission_session}/permissions/{permission_id}/reply" + ), + Some(json!({ "reply": "once" })), + ) + .await; + assert_eq!(status, StatusCode::NO_CONTENT, "reply permission"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("permission_reply", Some(config.agent)), + }, { + insta::assert_yaml_snapshot!(snapshot_status(status)); + }); + } else { + let (status, payload) = send_json( + &app.app, + Method::POST, + &format!( + "/v1/sessions/{permission_session}/permissions/missing-permission/reply" + ), + Some(json!({ "reply": "once" })), + ) + .await; + assert!(!status.is_success(), "missing permission id should error"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("permission_reply_missing", Some(config.agent)), + }, { + insta::assert_yaml_snapshot!(json!({ + "status": status.as_u16(), + "payload": payload, + })); + }); + } + + let question_reply_session = format!("question-reply-{}", config.agent.as_str()); + create_session(&app.app, config.agent, &question_reply_session, "bypass").await; + let status = send_status( + &app.app, + Method::POST, + &format!("/v1/sessions/{question_reply_session}/messages"), + Some(json!({ "message": QUESTION_PROMPT })), + ) + .await; + assert_eq!(status, StatusCode::NO_CONTENT, "send question prompt"); + + let question_events = poll_events_until_match( + &app.app, + &question_reply_session, + Duration::from_secs(120), + |events| find_question_id_and_answers(events).is_some() || should_stop(events), + ) + .await; + insta::with_settings!({ + snapshot_suffix => snapshot_name("question_reply_events", Some(config.agent)), + }, { + insta::assert_yaml_snapshot!(normalize_events(&question_events)); + }); + + if let Some((question_id, answers)) = find_question_id_and_answers(&question_events) { + let status = send_status( + &app.app, + Method::POST, + &format!( + "/v1/sessions/{question_reply_session}/questions/{question_id}/reply" + ), + Some(json!({ "answers": answers })), + ) + .await; + assert_eq!(status, StatusCode::NO_CONTENT, "reply question"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("question_reply", Some(config.agent)), + }, { + insta::assert_yaml_snapshot!(snapshot_status(status)); + }); + } else { + let (status, payload) = send_json( + &app.app, + Method::POST, + &format!( + "/v1/sessions/{question_reply_session}/questions/missing-question/reply" + ), + Some(json!({ "answers": [] })), + ) + .await; + assert!(!status.is_success(), "missing question id should error"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("question_reply_missing", Some(config.agent)), + }, { + insta::assert_yaml_snapshot!(json!({ + "status": status.as_u16(), + "payload": payload, + })); + }); + } + + let question_reject_session = format!("question-reject-{}", config.agent.as_str()); + create_session(&app.app, config.agent, &question_reject_session, "bypass").await; + let status = send_status( + &app.app, + Method::POST, + &format!("/v1/sessions/{question_reject_session}/messages"), + Some(json!({ "message": QUESTION_PROMPT })), + ) + .await; + assert_eq!(status, StatusCode::NO_CONTENT, "send question prompt reject"); + + let reject_events = poll_events_until_match( + &app.app, + &question_reject_session, + Duration::from_secs(120), + |events| find_question_id_and_answers(events).is_some() || should_stop(events), + ) + .await; + insta::with_settings!({ + snapshot_suffix => snapshot_name("question_reject_events", Some(config.agent)), + }, { + insta::assert_yaml_snapshot!(normalize_events(&reject_events)); + }); + + if let Some((question_id, _)) = find_question_id_and_answers(&reject_events) { + let status = send_status( + &app.app, + Method::POST, + &format!( + "/v1/sessions/{question_reject_session}/questions/{question_id}/reject" + ), + None, + ) + .await; + assert_eq!(status, StatusCode::NO_CONTENT, "reject question"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("question_reject", Some(config.agent)), + }, { + insta::assert_yaml_snapshot!(snapshot_status(status)); + }); + } else { + let (status, payload) = send_json( + &app.app, + Method::POST, + &format!( + "/v1/sessions/{question_reject_session}/questions/missing-question/reject" + ), + None, + ) + .await; + assert!(!status.is_success(), "missing question id reject should error"); + insta::with_settings!({ + snapshot_suffix => snapshot_name("question_reject_missing", Some(config.agent)), + }, { + insta::assert_yaml_snapshot!(json!({ + "status": status.as_u16(), + "payload": payload, + })); + }); + } + } +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn http_events_snapshots() { - let configs = test_agents_from_env().expect("configure SANDBOX_TEST_AGENTS"); + let configs = test_agents_from_env().expect("configure SANDBOX_TEST_AGENTS or install agents"); let app = TestApp::new(); for config in &configs { run_http_events_snapshot(&app.app, config).await; } } +async fn run_concurrency_snapshot(app: &Router, config: &TestAgentConfig) { + let _guard = apply_credentials(&config.credentials); + install_agent(app, config.agent).await; + + let session_a = format!("concurrent-a-{}", config.agent.as_str()); + let session_b = format!("concurrent-b-{}", config.agent.as_str()); + create_session(app, config.agent, &session_a, "bypass").await; + create_session(app, config.agent, &session_b, "bypass").await; + + let app_a = app.clone(); + let app_b = app.clone(); + let send_a = send_message(&app_a, &session_a); + let send_b = send_message(&app_b, &session_b); + tokio::join!(send_a, send_b); + + let app_a = app.clone(); + let app_b = app.clone(); + let poll_a = poll_events_until(&app_a, &session_a, Duration::from_secs(120)); + let poll_b = poll_events_until(&app_b, &session_b, Duration::from_secs(120)); + let (events_a, events_b) = tokio::join!(poll_a, poll_b); + let events_a = truncate_after_first_stop(&events_a); + let events_b = truncate_after_first_stop(&events_b); + + assert!( + !events_a.is_empty(), + "no events collected for concurrent session a {}", + config.agent + ); + assert!( + !events_b.is_empty(), + "no events collected for concurrent session b {}", + config.agent + ); + assert!( + should_stop(&events_a), + "timed out waiting for assistant/error event for concurrent session a {}", + config.agent + ); + assert!( + should_stop(&events_b), + "timed out waiting for assistant/error event for concurrent session b {}", + config.agent + ); + + let snapshot = json!({ + "session_a": normalize_events(&events_a), + "session_b": normalize_events(&events_b), + }); + insta::with_settings!({ + snapshot_suffix => snapshot_name("concurrency_events", Some(config.agent)), + }, { + insta::assert_yaml_snapshot!(snapshot); + }); +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn sse_events_snapshots() { - let configs = test_agents_from_env().expect("configure SANDBOX_TEST_AGENTS"); + let configs = test_agents_from_env().expect("configure SANDBOX_TEST_AGENTS or install agents"); let app = TestApp::new(); for config in &configs { run_sse_events_snapshot(&app.app, config).await; } } + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn concurrency_snapshots() { + let configs = test_agents_from_env().expect("configure SANDBOX_TEST_AGENTS or install agents"); + let app = TestApp::new(); + for config in &configs { + run_concurrency_snapshot(&app.app, config).await; + } +} diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@agent_install_claude.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@agent_install_claude.snap new file mode 100644 index 0000000..ad778d4 --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@agent_install_claude.snap @@ -0,0 +1,6 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 874 +expression: snapshot_status(status) +--- +status: 204 diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@agent_modes_claude.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@agent_modes_claude.snap new file mode 100644 index 0000000..b56773c --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@agent_modes_claude.snap @@ -0,0 +1,12 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 900 +expression: normalize_agent_modes(&modes) +--- +modes: + - description: true + id: build + name: Build + - description: true + id: plan + name: Plan diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@agents_list_global.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@agents_list_global.snap new file mode 100644 index 0000000..d805aca --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@agents_list_global.snap @@ -0,0 +1,10 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 881 +expression: normalize_agent_list(&agents) +--- +agents: + - id: amp + - id: claude + - id: codex + - id: opencode diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@create_session_claude.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@create_session_claude.snap new file mode 100644 index 0000000..c9e259a --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@create_session_claude.snap @@ -0,0 +1,6 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 918 +expression: normalize_create_session(&created) +--- +healthy: true diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@health_global.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@health_global.snap new file mode 100644 index 0000000..c23eec3 --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@health_global.snap @@ -0,0 +1,6 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 850 +expression: normalize_health(&health) +--- +status: ok diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@send_message_claude.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@send_message_claude.snap new file mode 100644 index 0000000..636137e --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@send_message_claude.snap @@ -0,0 +1,6 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 943 +expression: snapshot_status(status) +--- +status: 204 diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@sessions_list_global.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@sessions_list_global.snap new file mode 100644 index 0000000..b122534 --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__api_endpoints_snapshots@sessions_list_global.snap @@ -0,0 +1,15 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 928 +expression: normalize_sessions(&sessions) +--- +sessions: + - agent: claude + agentMode: build + agentSessionId: "" + ended: false + eventCount: "" + model: "" + permissionMode: bypass + sessionId: snapshot-claude + variant: "" diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@permission_events_claude.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@permission_events_claude.snap new file mode 100644 index 0000000..e6e25c0 --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@permission_events_claude.snap @@ -0,0 +1,21 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 978 +expression: normalize_events(&permission_events) +--- +- agent: claude + kind: started + seq: 1 + started: + message: session.created +- agent: claude + kind: unknown + seq: 2 +- agent: claude + kind: message + message: + parts: + - text: "" + type: text + role: assistant + seq: 3 diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@permission_reply_missing_claude.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@permission_reply_missing_claude.snap new file mode 100644 index 0000000..006333a --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@permission_reply_missing_claude.snap @@ -0,0 +1,11 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 1011 +expression: "json!({ \"status\": status.as_u16(), \"payload\": payload, })" +--- +payload: + detail: "invalid request: unknown permission id: missing-permission" + status: 400 + title: Invalid Request + type: "urn:sandbox-agent:error:invalid_request" +status: 400 diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reject_events_claude.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reject_events_claude.snap new file mode 100644 index 0000000..159ca8d --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reject_events_claude.snap @@ -0,0 +1,21 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 1100 +expression: normalize_events(&reject_events) +--- +- agent: claude + kind: started + seq: 1 + started: + message: session.created +- agent: claude + kind: unknown + seq: 2 +- agent: claude + kind: message + message: + parts: + - text: "" + type: text + role: assistant + seq: 3 diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reject_missing_claude.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reject_missing_claude.snap new file mode 100644 index 0000000..df61c32 --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reject_missing_claude.snap @@ -0,0 +1,11 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 1151 +expression: "json!({ \"status\": status.as_u16(), \"payload\": payload, })" +--- +payload: + detail: "invalid request: unknown question id: missing-question" + status: 400 + title: Invalid Request + type: "urn:sandbox-agent:error:invalid_request" +status: 400 diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reply_events_claude.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reply_events_claude.snap new file mode 100644 index 0000000..1f30020 --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reply_events_claude.snap @@ -0,0 +1,21 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 1039 +expression: normalize_events(&question_events) +--- +- agent: claude + kind: started + seq: 1 + started: + message: session.created +- agent: claude + kind: unknown + seq: 2 +- agent: claude + kind: message + message: + parts: + - text: "" + type: text + role: assistant + seq: 3 diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reply_missing_claude.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reply_missing_claude.snap new file mode 100644 index 0000000..c3dac9d --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__approval_flow_snapshots@question_reply_missing_claude.snap @@ -0,0 +1,11 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 1072 +expression: "json!({ \"status\": status.as_u16(), \"payload\": payload, })" +--- +payload: + detail: "invalid request: unknown question id: missing-question" + status: 400 + title: Invalid Request + type: "urn:sandbox-agent:error:invalid_request" +status: 400 diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__auth_snapshots@auth_health_public_global.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__auth_snapshots@auth_health_public_global.snap new file mode 100644 index 0000000..7cbd441 --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__auth_snapshots@auth_health_public_global.snap @@ -0,0 +1,8 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 765 +expression: "json!({ \"status\": status.as_u16(), \"payload\": normalize_health(&payload), })" +--- +payload: + status: ok +status: 200 diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__auth_snapshots@auth_invalid_token_global.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__auth_snapshots@auth_invalid_token_global.snap new file mode 100644 index 0000000..7e17f98 --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__auth_snapshots@auth_invalid_token_global.snap @@ -0,0 +1,13 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 793 +expression: "json!({ \"status\": status.as_u16(), \"payload\": payload, })" +--- +payload: + detail: token invalid + details: + message: missing or invalid token + status: 401 + title: Token Invalid + type: "urn:sandbox-agent:error:token_invalid" +status: 401 diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__auth_snapshots@auth_missing_token_global.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__auth_snapshots@auth_missing_token_global.snap new file mode 100644 index 0000000..822695f --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__auth_snapshots@auth_missing_token_global.snap @@ -0,0 +1,13 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 776 +expression: "json!({ \"status\": status.as_u16(), \"payload\": payload, })" +--- +payload: + detail: token invalid + details: + message: missing or invalid token + status: 401 + title: Token Invalid + type: "urn:sandbox-agent:error:token_invalid" +status: 401 diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__auth_snapshots@auth_valid_token_global.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__auth_snapshots@auth_valid_token_global.snap new file mode 100644 index 0000000..62b3f52 --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__auth_snapshots@auth_valid_token_global.snap @@ -0,0 +1,12 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 810 +expression: "json!({\n \"status\": status.as_u16(), \"payload\": normalize_agent_list(&payload),\n})" +--- +payload: + agents: + - id: amp + - id: claude + - id: codex + - id: opencode +status: 200 diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__cors_snapshots@cors_actual_global.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__cors_snapshots@cors_actual_global.snap new file mode 100644 index 0000000..6cb4b87 --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__cors_snapshots@cors_actual_global.snap @@ -0,0 +1,12 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 842 +expression: "json!({\n \"cors\": snapshot_cors(status, &headers), \"payload\":\n normalize_health(&payload),\n})" +--- +cors: + access-control-allow-credentials: "true" + access-control-allow-origin: "http://example.com" + status: 200 + vary: "origin, access-control-request-method, access-control-request-headers" +payload: + status: ok diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__cors_snapshots@cors_preflight_global.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__cors_snapshots@cors_preflight_global.snap new file mode 100644 index 0000000..e2d9f8c --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__cors_snapshots@cors_preflight_global.snap @@ -0,0 +1,11 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 818 +expression: "snapshot_cors(status, &headers)" +--- +access-control-allow-credentials: "true" +access-control-allow-headers: "content-type,authorization" +access-control-allow-methods: "GET,POST" +access-control-allow-origin: "http://example.com" +status: 200 +vary: "origin, access-control-request-method, access-control-request-headers" diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__run_concurrency_snapshot@concurrency_events_claude.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__run_concurrency_snapshot@concurrency_events_claude.snap new file mode 100644 index 0000000..012d604 --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__run_concurrency_snapshot@concurrency_events_claude.snap @@ -0,0 +1,39 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 1232 +expression: snapshot +--- +session_a: + - agent: claude + kind: started + seq: 1 + started: + message: session.created + - agent: claude + kind: unknown + seq: 2 + - agent: claude + kind: message + message: + parts: + - text: "" + type: text + role: assistant + seq: 3 +session_b: + - agent: claude + kind: started + seq: 1 + started: + message: session.created + - agent: claude + kind: unknown + seq: 2 + - agent: claude + kind: message + message: + parts: + - text: "" + type: text + role: assistant + seq: 3 diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__run_http_events_snapshot@http_events_claude.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__run_http_events_snapshot@http_events_claude.snap new file mode 100644 index 0000000..ff7d53d --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__run_http_events_snapshot@http_events_claude.snap @@ -0,0 +1,21 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 721 +expression: normalized +--- +- agent: claude + kind: started + seq: 1 + started: + message: session.created +- agent: claude + kind: unknown + seq: 2 +- agent: claude + kind: message + message: + parts: + - text: "" + type: text + role: assistant + seq: 3 diff --git a/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__run_sse_events_snapshot@sse_events_claude.snap b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__run_sse_events_snapshot@sse_events_claude.snap new file mode 100644 index 0000000..e8d3a35 --- /dev/null +++ b/server/packages/sandbox-agent/tests/snapshots/http_sse_snapshots__run_sse_events_snapshot@sse_events_claude.snap @@ -0,0 +1,21 @@ +--- +source: server/packages/sandbox-agent/tests/http_sse_snapshots.rs +assertion_line: 729 +expression: normalized +--- +- agent: claude + kind: started + seq: 1 + started: + message: session.created +- agent: claude + kind: unknown + seq: 2 +- agent: claude + kind: message + message: + parts: + - text: "" + type: text + role: assistant + seq: 3 diff --git a/server/packages/universal-agent-schema/Cargo.toml b/server/packages/universal-agent-schema/Cargo.toml index dc0bbd3..1f9e365 100644 --- a/server/packages/universal-agent-schema/Cargo.toml +++ b/server/packages/universal-agent-schema/Cargo.toml @@ -6,9 +6,9 @@ authors.workspace = true license.workspace = true [dependencies] -sandbox-agent-agent-schema = { path = "../agent-schema" } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -schemars = "0.8" -thiserror = "1.0" -utoipa = { version = "4.2", features = ["axum_extras"] } +sandbox-agent-extracted-agent-schemas.workspace = true +serde.workspace = true +serde_json.workspace = true +schemars.workspace = true +thiserror.workspace = true +utoipa.workspace = true diff --git a/server/packages/universal-agent-schema/src/agents/codex.rs b/server/packages/universal-agent-schema/src/agents/codex.rs index dca7a3a..fda62ab 100644 --- a/server/packages/universal-agent-schema/src/agents/codex.rs +++ b/server/packages/universal-agent-schema/src/agents/codex.rs @@ -1,6 +1,4 @@ use crate::{ - extract_message_from_value, - text_only_from_parts, AttachmentSource, ConversionError, CrashInfo, @@ -14,48 +12,663 @@ use crate::{ use crate::codex as schema; use serde_json::{Map, Value}; -pub fn event_to_universal(event: &schema::ThreadEvent) -> EventConversion { - let schema::ThreadEvent { - error, - item, - thread_id, - type_, - } = event; - match type_ { - schema::ThreadEventType::ThreadCreated | schema::ThreadEventType::ThreadUpdated => { - let started = Started { - message: Some(type_.to_string()), - details: serde_json::to_value(event).ok(), - }; - EventConversion::new(UniversalEventData::Started { started }) - .with_session(thread_id.clone()) +/// Convert a Codex ServerNotification to a universal event. +/// This is the main entry point for handling Codex events. +pub fn notification_to_universal(notification: &schema::ServerNotification) -> EventConversion { + match notification { + // Thread lifecycle + schema::ServerNotification::ThreadStarted(params) => { + thread_started_to_universal(params) } - schema::ThreadEventType::ItemCreated | schema::ThreadEventType::ItemUpdated => { - if let Some(item) = item.as_ref() { - let message = thread_item_to_message(item); - EventConversion::new(UniversalEventData::Message { message }) - .with_session(thread_id.clone()) - } else { - EventConversion::new(UniversalEventData::Unknown { - raw: serde_json::to_value(event).unwrap_or(Value::Null), - }) - } + schema::ServerNotification::TurnStarted(params) => { + turn_started_to_universal(params) } - schema::ThreadEventType::Error => { - let message = extract_message_from_value(&Value::Object(error.clone())) - .unwrap_or_else(|| "codex error".to_string()); - let crash = CrashInfo { - message, - kind: Some("error".to_string()), - details: Some(Value::Object(error.clone())), - }; - EventConversion::new(UniversalEventData::Error { error: crash }) - .with_session(thread_id.clone()) + schema::ServerNotification::TurnCompleted(params) => { + turn_completed_to_universal(params) + } + + // Item lifecycle + schema::ServerNotification::ItemStarted(params) => { + item_started_to_universal(params) + } + schema::ServerNotification::ItemCompleted(params) => { + item_completed_to_universal(params) + } + + // Streaming deltas + schema::ServerNotification::ItemAgentMessageDelta(params) => { + agent_message_delta_to_universal(params) + } + schema::ServerNotification::ItemReasoningTextDelta(params) => { + reasoning_text_delta_to_universal(params) + } + schema::ServerNotification::ItemReasoningSummaryTextDelta(params) => { + reasoning_summary_delta_to_universal(params) + } + schema::ServerNotification::ItemCommandExecutionOutputDelta(params) => { + command_output_delta_to_universal(params) + } + schema::ServerNotification::ItemFileChangeOutputDelta(params) => { + file_change_delta_to_universal(params) + } + + // Errors + schema::ServerNotification::Error(params) => { + error_notification_to_universal(params) + } + + // Token usage updates + schema::ServerNotification::ThreadTokenUsageUpdated(params) => { + token_usage_to_universal(params) + } + + // Turn diff updates + schema::ServerNotification::TurnDiffUpdated(params) => { + turn_diff_to_universal(params) + } + + // Plan updates + schema::ServerNotification::TurnPlanUpdated(params) => { + turn_plan_to_universal(params) + } + + // Terminal interaction + schema::ServerNotification::ItemCommandExecutionTerminalInteraction(params) => { + terminal_interaction_to_universal(params) + } + + // MCP tool call progress + schema::ServerNotification::ItemMcpToolCallProgress(params) => { + mcp_progress_to_universal(params) + } + + // Reasoning summary part added + schema::ServerNotification::ItemReasoningSummaryPartAdded(params) => { + reasoning_summary_part_to_universal(params) + } + + // Context compacted + schema::ServerNotification::ThreadCompacted(params) => { + context_compacted_to_universal(params) + } + + // Account/auth notifications (less relevant for message conversion) + schema::ServerNotification::AccountUpdated(_) + | schema::ServerNotification::AccountRateLimitsUpdated(_) + | schema::ServerNotification::AccountLoginCompleted(_) + | schema::ServerNotification::McpServerOauthLoginCompleted(_) + | schema::ServerNotification::AuthStatusChange(_) + | schema::ServerNotification::LoginChatGptComplete(_) + | schema::ServerNotification::SessionConfigured(_) + | schema::ServerNotification::DeprecationNotice(_) + | schema::ServerNotification::ConfigWarning(_) + | schema::ServerNotification::WindowsWorldWritableWarning(_) + | schema::ServerNotification::RawResponseItemCompleted(_) => { + EventConversion::new(UniversalEventData::Unknown { + raw: serde_json::to_value(notification).unwrap_or(Value::Null), + }) } } } -pub fn universal_event_to_codex(event: &UniversalEventData) -> Result { +fn thread_started_to_universal(params: &schema::ThreadStartedNotification) -> EventConversion { + let started = Started { + message: Some("thread/started".to_string()), + details: serde_json::to_value(¶ms.thread).ok(), + }; + EventConversion::new(UniversalEventData::Started { started }) + .with_session(Some(params.thread.id.clone())) +} + +fn turn_started_to_universal(params: &schema::TurnStartedNotification) -> EventConversion { + let started = Started { + message: Some("turn/started".to_string()), + details: serde_json::to_value(¶ms.turn).ok(), + }; + EventConversion::new(UniversalEventData::Started { started }) + .with_session(Some(params.thread_id.clone())) +} + +fn turn_completed_to_universal(params: &schema::TurnCompletedNotification) -> EventConversion { + // Convert all items in the turn to messages + let items = ¶ms.turn.items; + if items.is_empty() { + // If no items, just emit as unknown with the turn data + return EventConversion::new(UniversalEventData::Unknown { + raw: serde_json::to_value(params).unwrap_or(Value::Null), + }) + .with_session(Some(params.thread_id.clone())); + } + + // Return the last item as a message (most relevant for completion) + if let Some(last_item) = items.last() { + let message = thread_item_to_message(last_item); + EventConversion::new(UniversalEventData::Message { message }) + .with_session(Some(params.thread_id.clone())) + } else { + EventConversion::new(UniversalEventData::Unknown { + raw: serde_json::to_value(params).unwrap_or(Value::Null), + }) + .with_session(Some(params.thread_id.clone())) + } +} + +fn item_started_to_universal(params: &schema::ItemStartedNotification) -> EventConversion { + let message = thread_item_to_message(¶ms.item); + EventConversion::new(UniversalEventData::Message { message }) + .with_session(Some(params.thread_id.clone())) +} + +fn item_completed_to_universal(params: &schema::ItemCompletedNotification) -> EventConversion { + let message = thread_item_to_message(¶ms.item); + EventConversion::new(UniversalEventData::Message { message }) + .with_session(Some(params.thread_id.clone())) +} + +fn agent_message_delta_to_universal( + params: &schema::AgentMessageDeltaNotification, +) -> EventConversion { + let message = UniversalMessage::Parsed(UniversalMessageParsed { + role: "assistant".to_string(), + id: Some(params.item_id.clone()), + metadata: Map::from_iter([ + ("delta".to_string(), Value::Bool(true)), + ("turnId".to_string(), Value::String(params.turn_id.clone())), + ]), + parts: vec![UniversalMessagePart::Text { + text: params.delta.clone(), + }], + }); + EventConversion::new(UniversalEventData::Message { message }) + .with_session(Some(params.thread_id.clone())) +} + +fn reasoning_text_delta_to_universal( + params: &schema::ReasoningTextDeltaNotification, +) -> EventConversion { + let message = UniversalMessage::Parsed(UniversalMessageParsed { + role: "assistant".to_string(), + id: Some(params.item_id.clone()), + metadata: Map::from_iter([ + ("delta".to_string(), Value::Bool(true)), + ("itemType".to_string(), Value::String("reasoning".to_string())), + ("turnId".to_string(), Value::String(params.turn_id.clone())), + ]), + parts: vec![UniversalMessagePart::Text { + text: params.delta.clone(), + }], + }); + EventConversion::new(UniversalEventData::Message { message }) + .with_session(Some(params.thread_id.clone())) +} + +fn reasoning_summary_delta_to_universal( + params: &schema::ReasoningSummaryTextDeltaNotification, +) -> EventConversion { + let message = UniversalMessage::Parsed(UniversalMessageParsed { + role: "assistant".to_string(), + id: Some(params.item_id.clone()), + metadata: Map::from_iter([ + ("delta".to_string(), Value::Bool(true)), + ("itemType".to_string(), Value::String("reasoning_summary".to_string())), + ("turnId".to_string(), Value::String(params.turn_id.clone())), + ]), + parts: vec![UniversalMessagePart::Text { + text: params.delta.clone(), + }], + }); + EventConversion::new(UniversalEventData::Message { message }) + .with_session(Some(params.thread_id.clone())) +} + +fn command_output_delta_to_universal( + params: &schema::CommandExecutionOutputDeltaNotification, +) -> EventConversion { + let message = UniversalMessage::Parsed(UniversalMessageParsed { + role: "assistant".to_string(), + id: Some(params.item_id.clone()), + metadata: Map::from_iter([ + ("delta".to_string(), Value::Bool(true)), + ("itemType".to_string(), Value::String("commandExecution".to_string())), + ("turnId".to_string(), Value::String(params.turn_id.clone())), + ]), + parts: vec![UniversalMessagePart::Text { + text: params.delta.clone(), + }], + }); + EventConversion::new(UniversalEventData::Message { message }) + .with_session(Some(params.thread_id.clone())) +} + +fn file_change_delta_to_universal( + params: &schema::FileChangeOutputDeltaNotification, +) -> EventConversion { + let message = UniversalMessage::Parsed(UniversalMessageParsed { + role: "assistant".to_string(), + id: Some(params.item_id.clone()), + metadata: Map::from_iter([ + ("delta".to_string(), Value::Bool(true)), + ("itemType".to_string(), Value::String("fileChange".to_string())), + ("turnId".to_string(), Value::String(params.turn_id.clone())), + ]), + parts: vec![UniversalMessagePart::Text { + text: params.delta.clone(), + }], + }); + EventConversion::new(UniversalEventData::Message { message }) + .with_session(Some(params.thread_id.clone())) +} + +fn error_notification_to_universal(params: &schema::ErrorNotification) -> EventConversion { + let crash = CrashInfo { + message: params.error.message.clone(), + kind: Some("error".to_string()), + details: serde_json::to_value(params).ok(), + }; + EventConversion::new(UniversalEventData::Error { error: crash }) + .with_session(Some(params.thread_id.clone())) +} + +fn token_usage_to_universal( + params: &schema::ThreadTokenUsageUpdatedNotification, +) -> EventConversion { + EventConversion::new(UniversalEventData::Unknown { + raw: serde_json::to_value(params).unwrap_or(Value::Null), + }) + .with_session(Some(params.thread_id.clone())) +} + +fn turn_diff_to_universal(params: &schema::TurnDiffUpdatedNotification) -> EventConversion { + EventConversion::new(UniversalEventData::Unknown { + raw: serde_json::to_value(params).unwrap_or(Value::Null), + }) + .with_session(Some(params.thread_id.clone())) +} + +fn turn_plan_to_universal(params: &schema::TurnPlanUpdatedNotification) -> EventConversion { + EventConversion::new(UniversalEventData::Unknown { + raw: serde_json::to_value(params).unwrap_or(Value::Null), + }) + .with_session(Some(params.thread_id.clone())) +} + +fn terminal_interaction_to_universal( + params: &schema::TerminalInteractionNotification, +) -> EventConversion { + EventConversion::new(UniversalEventData::Unknown { + raw: serde_json::to_value(params).unwrap_or(Value::Null), + }) + .with_session(Some(params.thread_id.clone())) +} + +fn mcp_progress_to_universal(params: &schema::McpToolCallProgressNotification) -> EventConversion { + EventConversion::new(UniversalEventData::Unknown { + raw: serde_json::to_value(params).unwrap_or(Value::Null), + }) + .with_session(Some(params.thread_id.clone())) +} + +fn reasoning_summary_part_to_universal( + params: &schema::ReasoningSummaryPartAddedNotification, +) -> EventConversion { + // This notification signals a new summary part was added, but doesn't contain the text itself + // Return as Unknown with all metadata + EventConversion::new(UniversalEventData::Unknown { + raw: serde_json::to_value(params).unwrap_or(Value::Null), + }) + .with_session(Some(params.thread_id.clone())) +} + +fn context_compacted_to_universal( + params: &schema::ContextCompactedNotification, +) -> EventConversion { + EventConversion::new(UniversalEventData::Unknown { + raw: serde_json::to_value(params).unwrap_or(Value::Null), + }) + .with_session(Some(params.thread_id.clone())) +} + +/// Convert a ThreadItem to a UniversalMessage +pub fn thread_item_to_message(item: &schema::ThreadItem) -> UniversalMessage { + match item { + schema::ThreadItem::UserMessage { content, id } => { + user_message_to_universal(content, id) + } + schema::ThreadItem::AgentMessage { id, text } => { + agent_message_to_universal(id, text) + } + schema::ThreadItem::Reasoning { content, id, summary } => { + reasoning_to_universal(id, content, summary) + } + schema::ThreadItem::CommandExecution { + aggregated_output, + command, + command_actions: _, + cwd, + duration_ms, + exit_code, + id, + process_id: _, + status, + } => { + command_execution_to_universal( + id, + command, + cwd, + aggregated_output.as_deref(), + exit_code.as_ref(), + duration_ms.as_ref(), + status, + ) + } + schema::ThreadItem::FileChange { changes, id, status } => { + file_change_to_universal(id, changes, status) + } + schema::ThreadItem::McpToolCall { + arguments, + duration_ms: _, + error, + id, + result, + server, + status, + tool, + } => { + mcp_tool_call_to_universal(id, server, tool, arguments, result.as_ref(), error.as_ref(), status) + } + schema::ThreadItem::CollabAgentToolCall { + agents_states: _, + id, + prompt, + receiver_thread_ids: _, + sender_thread_id, + status, + tool, + } => { + collab_tool_call_to_universal(id, tool, prompt.as_deref(), sender_thread_id, status) + } + schema::ThreadItem::WebSearch { id, query } => { + web_search_to_universal(id, query) + } + schema::ThreadItem::ImageView { id, path } => { + image_view_to_universal(id, path) + } + schema::ThreadItem::EnteredReviewMode { id, review } => { + review_mode_to_universal(id, review, true) + } + schema::ThreadItem::ExitedReviewMode { id, review } => { + review_mode_to_universal(id, review, false) + } + } +} + +fn user_message_to_universal(content: &[schema::UserInput], id: &str) -> UniversalMessage { + let parts: Vec = content.iter().map(user_input_to_part).collect(); + UniversalMessage::Parsed(UniversalMessageParsed { + role: "user".to_string(), + id: Some(id.to_string()), + metadata: Map::new(), + parts, + }) +} + +fn user_input_to_part(input: &schema::UserInput) -> UniversalMessagePart { + match input { + schema::UserInput::Text { text, text_elements: _ } => { + UniversalMessagePart::Text { text: text.clone() } + } + schema::UserInput::Image { image_url } => { + UniversalMessagePart::Image { + source: AttachmentSource::Url { url: image_url.clone() }, + mime_type: None, + alt: None, + raw: None, + } + } + schema::UserInput::LocalImage { path } => { + UniversalMessagePart::Image { + source: AttachmentSource::Path { path: path.clone() }, + mime_type: None, + alt: None, + raw: None, + } + } + schema::UserInput::Skill { name, path } => { + UniversalMessagePart::Unknown { + raw: serde_json::json!({ + "type": "skill", + "name": name, + "path": path, + }), + } + } + } +} + +fn agent_message_to_universal(id: &str, text: &str) -> UniversalMessage { + UniversalMessage::Parsed(UniversalMessageParsed { + role: "assistant".to_string(), + id: Some(id.to_string()), + metadata: Map::from_iter([ + ("itemType".to_string(), Value::String("agentMessage".to_string())), + ]), + parts: vec![UniversalMessagePart::Text { + text: text.to_string(), + }], + }) +} + +fn reasoning_to_universal( + id: &str, + content: &[String], + summary: &[String], +) -> UniversalMessage { + let mut metadata = Map::new(); + metadata.insert("itemType".to_string(), Value::String("reasoning".to_string())); + if !summary.is_empty() { + metadata.insert( + "summary".to_string(), + Value::Array(summary.iter().map(|s| Value::String(s.clone())).collect()), + ); + } + + let parts: Vec = content + .iter() + .map(|text| UniversalMessagePart::Text { text: text.clone() }) + .collect(); + + UniversalMessage::Parsed(UniversalMessageParsed { + role: "assistant".to_string(), + id: Some(id.to_string()), + metadata, + parts, + }) +} + +fn command_execution_to_universal( + id: &str, + command: &str, + cwd: &str, + aggregated_output: Option<&str>, + exit_code: Option<&i32>, + duration_ms: Option<&i64>, + status: &schema::CommandExecutionStatus, +) -> UniversalMessage { + let mut metadata = Map::new(); + metadata.insert("itemType".to_string(), Value::String("commandExecution".to_string())); + metadata.insert("command".to_string(), Value::String(command.to_string())); + metadata.insert("cwd".to_string(), Value::String(cwd.to_string())); + metadata.insert("status".to_string(), Value::String(format!("{:?}", status))); + if let Some(code) = exit_code { + metadata.insert("exitCode".to_string(), Value::Number((*code).into())); + } + if let Some(ms) = duration_ms { + metadata.insert("durationMs".to_string(), Value::Number((*ms).into())); + } + + let parts = if let Some(output) = aggregated_output { + vec![UniversalMessagePart::Text { + text: output.to_string(), + }] + } else { + vec![] + }; + + UniversalMessage::Parsed(UniversalMessageParsed { + role: "assistant".to_string(), + id: Some(id.to_string()), + metadata, + parts, + }) +} + +fn file_change_to_universal( + id: &str, + changes: &[schema::FileUpdateChange], + status: &schema::PatchApplyStatus, +) -> UniversalMessage { + let mut metadata = Map::new(); + metadata.insert("itemType".to_string(), Value::String("fileChange".to_string())); + metadata.insert("status".to_string(), Value::String(format!("{:?}", status))); + + let parts: Vec = changes + .iter() + .map(|change| { + let raw = serde_json::to_value(change).unwrap_or(Value::Null); + UniversalMessagePart::Unknown { raw } + }) + .collect(); + + UniversalMessage::Parsed(UniversalMessageParsed { + role: "assistant".to_string(), + id: Some(id.to_string()), + metadata, + parts, + }) +} + +fn mcp_tool_call_to_universal( + id: &str, + server: &str, + tool: &str, + arguments: &Value, + result: Option<&schema::McpToolCallResult>, + error: Option<&schema::McpToolCallError>, + status: &schema::McpToolCallStatus, +) -> UniversalMessage { + let mut metadata = Map::new(); + metadata.insert("itemType".to_string(), Value::String("mcpToolCall".to_string())); + metadata.insert("server".to_string(), Value::String(server.to_string())); + metadata.insert("status".to_string(), Value::String(format!("{:?}", status))); + + let is_error = error.is_some(); + let result_value = if let Some(res) = result { + serde_json::to_value(res).unwrap_or(Value::Null) + } else if let Some(err) = error { + serde_json::to_value(err).unwrap_or(Value::Null) + } else { + Value::Null + }; + + let parts = vec![ + UniversalMessagePart::ToolCall { + id: Some(id.to_string()), + name: tool.to_string(), + input: arguments.clone(), + }, + UniversalMessagePart::ToolResult { + id: Some(id.to_string()), + name: Some(tool.to_string()), + output: result_value, + is_error: Some(is_error), + }, + ]; + + UniversalMessage::Parsed(UniversalMessageParsed { + role: "assistant".to_string(), + id: Some(id.to_string()), + metadata, + parts, + }) +} + +fn collab_tool_call_to_universal( + id: &str, + tool: &schema::CollabAgentTool, + prompt: Option<&str>, + sender_thread_id: &str, + status: &schema::CollabAgentToolCallStatus, +) -> UniversalMessage { + let mut metadata = Map::new(); + metadata.insert("itemType".to_string(), Value::String("collabAgentToolCall".to_string())); + metadata.insert("tool".to_string(), Value::String(format!("{:?}", tool))); + metadata.insert("senderThreadId".to_string(), Value::String(sender_thread_id.to_string())); + metadata.insert("status".to_string(), Value::String(format!("{:?}", status))); + + let parts = if let Some(p) = prompt { + vec![UniversalMessagePart::Text { text: p.to_string() }] + } else { + vec![] + }; + + UniversalMessage::Parsed(UniversalMessageParsed { + role: "assistant".to_string(), + id: Some(id.to_string()), + metadata, + parts, + }) +} + +fn web_search_to_universal(id: &str, query: &str) -> UniversalMessage { + let mut metadata = Map::new(); + metadata.insert("itemType".to_string(), Value::String("webSearch".to_string())); + + UniversalMessage::Parsed(UniversalMessageParsed { + role: "assistant".to_string(), + id: Some(id.to_string()), + metadata, + parts: vec![UniversalMessagePart::Text { + text: query.to_string(), + }], + }) +} + +fn image_view_to_universal(id: &str, path: &str) -> UniversalMessage { + let mut metadata = Map::new(); + metadata.insert("itemType".to_string(), Value::String("imageView".to_string())); + + UniversalMessage::Parsed(UniversalMessageParsed { + role: "assistant".to_string(), + id: Some(id.to_string()), + metadata, + parts: vec![UniversalMessagePart::Image { + source: AttachmentSource::Path { path: path.to_string() }, + mime_type: None, + alt: None, + raw: None, + }], + }) +} + +fn review_mode_to_universal(id: &str, review: &str, entered: bool) -> UniversalMessage { + let item_type = if entered { "enteredReviewMode" } else { "exitedReviewMode" }; + let mut metadata = Map::new(); + metadata.insert("itemType".to_string(), Value::String(item_type.to_string())); + metadata.insert("review".to_string(), Value::String(review.to_string())); + + UniversalMessage::Parsed(UniversalMessageParsed { + role: "assistant".to_string(), + id: Some(id.to_string()), + metadata, + parts: vec![], + }) +} + +/// Convert a universal event back to a Codex ServerNotification. +/// Note: This is a best-effort conversion and may not preserve all information. +pub fn universal_event_to_codex( + event: &UniversalEventData, +) -> Result { match event { UniversalEventData::Message { message } => { let parsed = match message { @@ -64,312 +677,57 @@ pub fn universal_event_to_codex(event: &UniversalEventData) -> Result Some(schema::ThreadItemRole::User), - "assistant" => Some(schema::ThreadItemRole::Assistant), - "system" => Some(schema::ThreadItemRole::System), - _ => None, - }; - let item = schema::ThreadItem { - content: Some(schema::ThreadItemContent::Variant0(content)), + + // Extract text content + let text = parsed + .parts + .iter() + .filter_map(|part| { + if let UniversalMessagePart::Text { text } = part { + Some(text.as_str()) + } else { + None + } + }) + .collect::>() + .join("\n"); + + let id = parsed.id.clone().unwrap_or_else(|| "msg".to_string()); + let thread_id = "unknown".to_string(); + let turn_id = "unknown".to_string(); + + // Create an ItemCompletedNotification with an AgentMessage item + let item = schema::ThreadItem::AgentMessage { id, - role, - status: None, - type_: schema::ThreadItemType::Message, + text, }; - Ok(schema::ThreadEvent { - error: Map::new(), - item: Some(item), - thread_id: None, - type_: schema::ThreadEventType::ItemCreated, - }) + + Ok(schema::ServerNotification::ItemCompleted( + schema::ItemCompletedNotification { + item, + thread_id, + turn_id, + }, + )) } - _ => Err(ConversionError::Unsupported("codex event")), - } -} - -pub fn message_to_universal(message: &schema::Message) -> UniversalMessage { - let schema::Message { role, content } = message; - UniversalMessage::Parsed(UniversalMessageParsed { - role: role.to_string(), - id: None, - metadata: Map::new(), - parts: vec![UniversalMessagePart::Text { - text: content.clone(), - }], - }) -} - -pub fn universal_message_to_message( - message: &UniversalMessage, -) -> Result { - let parsed = match message { - UniversalMessage::Parsed(parsed) => parsed, - UniversalMessage::Unparsed { .. } => { - return Err(ConversionError::Unsupported("unparsed message")) - } - }; - let content = text_only_from_parts(&parsed.parts)?; - Ok(schema::Message { - role: match parsed.role.as_str() { - "user" => schema::MessageRole::User, - "assistant" => schema::MessageRole::Assistant, - "system" => schema::MessageRole::System, - _ => schema::MessageRole::User, - }, - content, - }) -} - -pub fn inputs_to_universal_message(inputs: &[schema::Input], role: &str) -> UniversalMessage { - let parts = inputs.iter().map(input_to_universal_part).collect(); - UniversalMessage::Parsed(UniversalMessageParsed { - role: role.to_string(), - id: None, - metadata: Map::new(), - parts, - }) -} - -pub fn input_to_universal_part(input: &schema::Input) -> UniversalMessagePart { - let schema::Input { - content, - mime_type, - path, - type_, - } = input; - let raw = serde_json::to_value(input).unwrap_or(Value::Null); - match type_ { - schema::InputType::Text => match content { - Some(content) => UniversalMessagePart::Text { - text: content.clone(), - }, - None => UniversalMessagePart::Unknown { raw }, - }, - schema::InputType::File => { - let source = if let Some(path) = path { - AttachmentSource::Path { path: path.clone() } - } else if let Some(content) = content { - AttachmentSource::Data { - data: content.clone(), - encoding: None, - } - } else { - return UniversalMessagePart::Unknown { raw }; + UniversalEventData::Error { error } => { + let turn_error = schema::TurnError { + message: error.message.clone(), + additional_details: error.details.as_ref().and_then(|d| { + d.get("additionalDetails") + .and_then(Value::as_str) + .map(|s| s.to_string()) + }), + codex_error_info: None, }; - UniversalMessagePart::File { - source, - mime_type: mime_type.clone(), - filename: None, - raw: Some(raw), - } - } - schema::InputType::Image => { - let source = if let Some(path) = path { - AttachmentSource::Path { path: path.clone() } - } else if let Some(content) = content { - AttachmentSource::Data { - data: content.clone(), - encoding: None, - } - } else { - return UniversalMessagePart::Unknown { raw }; - }; - UniversalMessagePart::Image { - source, - mime_type: mime_type.clone(), - alt: None, - raw: Some(raw), - } + + Ok(schema::ServerNotification::Error(schema::ErrorNotification { + error: turn_error, + thread_id: "unknown".to_string(), + turn_id: "unknown".to_string(), + will_retry: false, + })) } - } -} - -pub fn universal_message_to_inputs( - message: &UniversalMessage, -) -> Result, ConversionError> { - let parsed = match message { - UniversalMessage::Parsed(parsed) => parsed, - UniversalMessage::Unparsed { .. } => { - return Err(ConversionError::Unsupported("unparsed message")) - } - }; - universal_parts_to_inputs(&parsed.parts) -} - -pub fn universal_parts_to_inputs( - parts: &[UniversalMessagePart], -) -> Result, ConversionError> { - let mut inputs = Vec::new(); - for part in parts { - match part { - UniversalMessagePart::Text { text } => inputs.push(schema::Input { - content: Some(text.clone()), - mime_type: None, - path: None, - type_: schema::InputType::Text, - }), - UniversalMessagePart::File { - source, - mime_type, - .. - } => inputs.push(input_from_attachment(source, mime_type.as_ref(), schema::InputType::File)?), - UniversalMessagePart::Image { - source, mime_type, .. - } => inputs.push(input_from_attachment( - source, - mime_type.as_ref(), - schema::InputType::Image, - )?), - UniversalMessagePart::ToolCall { .. } - | UniversalMessagePart::ToolResult { .. } - | UniversalMessagePart::FunctionCall { .. } - | UniversalMessagePart::FunctionResult { .. } - | UniversalMessagePart::Error { .. } - | UniversalMessagePart::Unknown { .. } => { - return Err(ConversionError::Unsupported("unsupported part")) - } - } - } - if inputs.is_empty() { - return Err(ConversionError::MissingField("parts")); - } - Ok(inputs) -} - -fn input_from_attachment( - source: &AttachmentSource, - mime_type: Option<&String>, - input_type: schema::InputType, -) -> Result { - match source { - AttachmentSource::Path { path } => Ok(schema::Input { - content: None, - mime_type: mime_type.cloned(), - path: Some(path.clone()), - type_: input_type, - }), - AttachmentSource::Data { data, encoding } => { - if let Some(encoding) = encoding.as_deref() { - if encoding != "base64" { - return Err(ConversionError::Unsupported("codex data encoding")); - } - } - Ok(schema::Input { - content: Some(data.clone()), - mime_type: mime_type.cloned(), - path: None, - type_: input_type, - }) - } - AttachmentSource::Url { .. } => Err(ConversionError::Unsupported("codex input url")), - } -} - -fn thread_item_to_message(item: &schema::ThreadItem) -> UniversalMessage { - let schema::ThreadItem { - content, - id, - role, - status, - type_, - } = item; - let mut metadata = Map::new(); - metadata.insert("itemType".to_string(), Value::String(type_.to_string())); - if let Some(status) = status { - metadata.insert("status".to_string(), Value::String(status.to_string())); - } - let role = role - .as_ref() - .map(|role| role.to_string()) - .unwrap_or_else(|| "assistant".to_string()); - let parts = match type_ { - schema::ThreadItemType::Message => message_parts_from_codex_content(content), - schema::ThreadItemType::FunctionCall => vec![function_call_part_from_codex(id, content)], - schema::ThreadItemType::FunctionResult => vec![function_result_part_from_codex(id, content)], - }; - UniversalMessage::Parsed(UniversalMessageParsed { - role, - id: Some(id.clone()), - metadata, - parts, - }) -} - -fn message_parts_from_codex_content( - content: &Option, -) -> Vec { - match content { - Some(schema::ThreadItemContent::Variant0(text)) => { - vec![UniversalMessagePart::Text { text: text.clone() }] - } - Some(schema::ThreadItemContent::Variant1(raw)) => { - vec![UniversalMessagePart::Unknown { - raw: serde_json::to_value(raw).unwrap_or(Value::Null), - }] - } - None => Vec::new(), - } -} - -fn function_call_part_from_codex( - item_id: &str, - content: &Option, -) -> UniversalMessagePart { - let raw = thread_item_content_to_value(content); - let name = extract_object_field(&raw, "name"); - let arguments = extract_object_value(&raw, "arguments").unwrap_or_else(|| raw.clone()); - UniversalMessagePart::FunctionCall { - id: Some(item_id.to_string()), - name, - arguments, - raw: Some(raw), - } -} - -fn function_result_part_from_codex( - item_id: &str, - content: &Option, -) -> UniversalMessagePart { - let raw = thread_item_content_to_value(content); - let name = extract_object_field(&raw, "name"); - let result = extract_object_value(&raw, "result") - .or_else(|| extract_object_value(&raw, "output")) - .or_else(|| extract_object_value(&raw, "content")) - .unwrap_or_else(|| raw.clone()); - UniversalMessagePart::FunctionResult { - id: Some(item_id.to_string()), - name, - result, - is_error: None, - raw: Some(raw), - } -} - -fn thread_item_content_to_value(content: &Option) -> Value { - match content { - Some(schema::ThreadItemContent::Variant0(text)) => Value::String(text.clone()), - Some(schema::ThreadItemContent::Variant1(raw)) => { - Value::Array(raw.iter().cloned().map(Value::Object).collect()) - } - None => Value::Null, - } -} - -fn extract_object_field(raw: &Value, field: &str) -> Option { - extract_object_value(raw, field) - .and_then(|value| value.as_str().map(|s| s.to_string())) -} - -fn extract_object_value(raw: &Value, field: &str) -> Option { - match raw { - Value::Object(map) => map.get(field).cloned(), - Value::Array(values) => values - .first() - .and_then(|value| value.as_object()) - .and_then(|map| map.get(field).cloned()), - _ => None, + _ => Err(ConversionError::Unsupported("codex event type")), } } diff --git a/server/packages/universal-agent-schema/src/lib.rs b/server/packages/universal-agent-schema/src/lib.rs index 91ec826..fcecee2 100644 --- a/server/packages/universal-agent-schema/src/lib.rs +++ b/server/packages/universal-agent-schema/src/lib.rs @@ -4,7 +4,7 @@ use schemars::JsonSchema; use thiserror::Error; use utoipa::ToSchema; -pub use sandbox_agent_agent_schema::{amp, claude, codex, opencode}; +pub use sandbox_agent_extracted_agent_schemas::{amp, claude, codex, opencode}; pub mod agents; diff --git a/server/packages/universal-schema-gen/Cargo.toml b/server/packages/universal-schema-gen/Cargo.toml new file mode 100644 index 0000000..44ae121 --- /dev/null +++ b/server/packages/universal-schema-gen/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "sandbox-agent-universal-schema-gen" +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +build = "build.rs" + +[build-dependencies] +sandbox-agent-universal-agent-schema.workspace = true +schemars.workspace = true +serde_json.workspace = true diff --git a/server/packages/universal-schema-gen/build.rs b/server/packages/universal-schema-gen/build.rs new file mode 100644 index 0000000..0db84f8 --- /dev/null +++ b/server/packages/universal-schema-gen/build.rs @@ -0,0 +1,26 @@ +use std::{fs, path::Path}; + +fn main() { + println!("cargo:rerun-if-changed=../universal-agent-schema/src/lib.rs"); + + let schema = schemars::schema_for!(sandbox_agent_universal_agent_schema::UniversalEvent); + + let workspace_root = std::env::var("CARGO_MANIFEST_DIR") + .map(|dir| { + Path::new(&dir) + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap() + .to_path_buf() + }) + .unwrap(); + let out_dir = workspace_root.join("spec"); + fs::create_dir_all(&out_dir).unwrap(); + + let json = serde_json::to_string_pretty(&schema).expect("Failed to serialize JSON schema"); + fs::write(out_dir.join("universal-schema.json"), json) + .expect("Failed to write universal-schema.json"); +} diff --git a/server/packages/universal-schema-gen/src/lib.rs b/server/packages/universal-schema-gen/src/lib.rs new file mode 100644 index 0000000..fcb2dff --- /dev/null +++ b/server/packages/universal-schema-gen/src/lib.rs @@ -0,0 +1,2 @@ +// This crate exists only to trigger the build.rs script +// which generates the universal JSON schema at build time. diff --git a/spec/universal-schema.json b/spec/universal-schema.json new file mode 100644 index 0000000..25d144d --- /dev/null +++ b/spec/universal-schema.json @@ -0,0 +1,655 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "UniversalEvent", + "type": "object", + "required": [ + "agent", + "data", + "id", + "sessionId", + "timestamp" + ], + "properties": { + "agent": { + "type": "string" + }, + "agentSessionId": { + "type": [ + "string", + "null" + ] + }, + "data": { + "$ref": "#/definitions/UniversalEventData" + }, + "id": { + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, + "sessionId": { + "type": "string" + }, + "timestamp": { + "type": "string" + } + }, + "definitions": { + "AttachmentSource": { + "oneOf": [ + { + "type": "object", + "required": [ + "path", + "type" + ], + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "path" + ] + } + } + }, + { + "type": "object", + "required": [ + "type", + "url" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "url" + ] + }, + "url": { + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "data", + "type" + ], + "properties": { + "data": { + "type": "string" + }, + "encoding": { + "type": [ + "string", + "null" + ] + }, + "type": { + "type": "string", + "enum": [ + "data" + ] + } + } + } + ] + }, + "CrashInfo": { + "type": "object", + "required": [ + "message" + ], + "properties": { + "details": true, + "kind": { + "type": [ + "string", + "null" + ] + }, + "message": { + "type": "string" + } + } + }, + "PermissionRequest": { + "type": "object", + "required": [ + "always", + "id", + "patterns", + "permission", + "sessionId" + ], + "properties": { + "always": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "string" + }, + "metadata": { + "type": "object", + "additionalProperties": true + }, + "patterns": { + "type": "array", + "items": { + "type": "string" + } + }, + "permission": { + "type": "string" + }, + "sessionId": { + "type": "string" + }, + "tool": { + "anyOf": [ + { + "$ref": "#/definitions/PermissionToolRef" + }, + { + "type": "null" + } + ] + } + } + }, + "PermissionToolRef": { + "type": "object", + "required": [ + "callId", + "messageId" + ], + "properties": { + "callId": { + "type": "string" + }, + "messageId": { + "type": "string" + } + } + }, + "QuestionInfo": { + "type": "object", + "required": [ + "options", + "question" + ], + "properties": { + "custom": { + "type": [ + "boolean", + "null" + ] + }, + "header": { + "type": [ + "string", + "null" + ] + }, + "multiSelect": { + "type": [ + "boolean", + "null" + ] + }, + "options": { + "type": "array", + "items": { + "$ref": "#/definitions/QuestionOption" + } + }, + "question": { + "type": "string" + } + } + }, + "QuestionOption": { + "type": "object", + "required": [ + "label" + ], + "properties": { + "description": { + "type": [ + "string", + "null" + ] + }, + "label": { + "type": "string" + } + } + }, + "QuestionRequest": { + "type": "object", + "required": [ + "id", + "questions", + "sessionId" + ], + "properties": { + "id": { + "type": "string" + }, + "questions": { + "type": "array", + "items": { + "$ref": "#/definitions/QuestionInfo" + } + }, + "sessionId": { + "type": "string" + }, + "tool": { + "anyOf": [ + { + "$ref": "#/definitions/QuestionToolRef" + }, + { + "type": "null" + } + ] + } + } + }, + "QuestionToolRef": { + "type": "object", + "required": [ + "callId", + "messageId" + ], + "properties": { + "callId": { + "type": "string" + }, + "messageId": { + "type": "string" + } + } + }, + "Started": { + "type": "object", + "properties": { + "details": true, + "message": { + "type": [ + "string", + "null" + ] + } + } + }, + "UniversalEventData": { + "anyOf": [ + { + "type": "object", + "required": [ + "message" + ], + "properties": { + "message": { + "$ref": "#/definitions/UniversalMessage" + } + } + }, + { + "type": "object", + "required": [ + "started" + ], + "properties": { + "started": { + "$ref": "#/definitions/Started" + } + } + }, + { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "$ref": "#/definitions/CrashInfo" + } + } + }, + { + "type": "object", + "required": [ + "questionAsked" + ], + "properties": { + "questionAsked": { + "$ref": "#/definitions/QuestionRequest" + } + } + }, + { + "type": "object", + "required": [ + "permissionAsked" + ], + "properties": { + "permissionAsked": { + "$ref": "#/definitions/PermissionRequest" + } + } + }, + { + "type": "object", + "required": [ + "raw" + ], + "properties": { + "raw": true + } + } + ] + }, + "UniversalMessage": { + "anyOf": [ + { + "$ref": "#/definitions/UniversalMessageParsed" + }, + { + "type": "object", + "required": [ + "raw" + ], + "properties": { + "error": { + "type": [ + "string", + "null" + ] + }, + "raw": true + } + } + ] + }, + "UniversalMessageParsed": { + "type": "object", + "required": [ + "parts", + "role" + ], + "properties": { + "id": { + "type": [ + "string", + "null" + ] + }, + "metadata": { + "type": "object", + "additionalProperties": true + }, + "parts": { + "type": "array", + "items": { + "$ref": "#/definitions/UniversalMessagePart" + } + }, + "role": { + "type": "string" + } + } + }, + "UniversalMessagePart": { + "oneOf": [ + { + "type": "object", + "required": [ + "text", + "type" + ], + "properties": { + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "text" + ] + } + } + }, + { + "type": "object", + "required": [ + "input", + "name", + "type" + ], + "properties": { + "id": { + "type": [ + "string", + "null" + ] + }, + "input": true, + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "tool_call" + ] + } + } + }, + { + "type": "object", + "required": [ + "output", + "type" + ], + "properties": { + "id": { + "type": [ + "string", + "null" + ] + }, + "is_error": { + "type": [ + "boolean", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "output": true, + "type": { + "type": "string", + "enum": [ + "tool_result" + ] + } + } + }, + { + "type": "object", + "required": [ + "arguments", + "type" + ], + "properties": { + "arguments": true, + "id": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "raw": true, + "type": { + "type": "string", + "enum": [ + "function_call" + ] + } + } + }, + { + "type": "object", + "required": [ + "result", + "type" + ], + "properties": { + "id": { + "type": [ + "string", + "null" + ] + }, + "is_error": { + "type": [ + "boolean", + "null" + ] + }, + "name": { + "type": [ + "string", + "null" + ] + }, + "raw": true, + "result": true, + "type": { + "type": "string", + "enum": [ + "function_result" + ] + } + } + }, + { + "type": "object", + "required": [ + "source", + "type" + ], + "properties": { + "filename": { + "type": [ + "string", + "null" + ] + }, + "mime_type": { + "type": [ + "string", + "null" + ] + }, + "raw": true, + "source": { + "$ref": "#/definitions/AttachmentSource" + }, + "type": { + "type": "string", + "enum": [ + "file" + ] + } + } + }, + { + "type": "object", + "required": [ + "source", + "type" + ], + "properties": { + "alt": { + "type": [ + "string", + "null" + ] + }, + "mime_type": { + "type": [ + "string", + "null" + ] + }, + "raw": true, + "source": { + "$ref": "#/definitions/AttachmentSource" + }, + "type": { + "type": "string", + "enum": [ + "image" + ] + } + } + }, + { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "message": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "error" + ] + } + } + }, + { + "type": "object", + "required": [ + "raw", + "type" + ], + "properties": { + "raw": true, + "type": { + "type": "string", + "enum": [ + "unknown" + ] + } + } + } + ] + } + } +} \ No newline at end of file diff --git a/todo.md b/todo.md index 1d1c880..2202d51 100644 --- a/todo.md +++ b/todo.md @@ -71,6 +71,7 @@ - [x] Add OpenCode server-mode tests (session create, prompt, SSE) - [ ] Add tests for question/permission flows using deterministic prompts - [x] Add HTTP/SSE snapshot tests for real agents (env-configured) +- [x] Add snapshot coverage for auth, CORS, and concurrent sessions ## Frontend (frontend/packages/inspector) - [x] Build Vite + React app with connect screen (endpoint + optional token)