openapi: 3.1.0
info:
  title: Warmplane HTTP Facade API
  version: "v1"
  description: |
    Warmplane exposes a compact, deterministic HTTP facade over multiple upstream MCP servers.

    This API is index-first: list compact capability/resource/prompt entries, then request detail
    or execute operations on-demand.
servers:
  - url: http://127.0.0.1:9090
    description: local default
paths:
  /v1/capabilities:
    get:
      summary: List capabilities
      operationId: listCapabilities
      parameters:
        - in: query
          name: profile
          required: false
          schema:
            type: string
          description: Named server constellation profile ID
        - in: header
          name: X-Warmplane-Profile
          required: false
          schema:
            type: string
          description: Named server constellation profile ID
      responses:
        "200":
          description: Capability index with cache hints
          headers:
            ETag:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapabilitiesListResponse"
        "304":
          description: Not Modified (catalog unchanged)
        "404":
          description: Profile not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/capabilities/search:
    post:
      summary: Search capabilities
      operationId: searchCapabilities
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SearchCapabilitiesRequest"
      responses:
        "200":
          description: Ranked search results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SearchCapabilitiesResponse"
  /v1/capabilities/{id}:
    get:
      summary: Describe capability
      operationId: describeCapability
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Capability detail
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CapabilityDescribeResponse"
        "404":
          description: Capability not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/tools/call:
    post:
      summary: Execute capability
      operationId: callCapability
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CallCapabilityRequest"
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "400":
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: Policy blocked
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Capability not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "502":
          description: Upstream error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "504":
          description: Upstream timeout
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/tools/batch_call:
    post:
      summary: Execute chained multi-step batch capabilities
      operationId: batchCallCapabilities
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchCallRequest"
      responses:
        "200":
          description: Batch execution summary response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BatchCallResponse"
  /v1/idempotency/records:
    get:
      summary: List cached idempotency records
      operationId: listIdempotencyRecords
      parameters:
        - name: limit
          in: query
          description: Max records to return (default 50)
          schema:
            type: integer
        - name: offset
          in: query
          description: Record offset (default 0)
          schema:
            type: integer
      responses:
        "200":
          description: List of cached idempotency records
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  records:
                    type: array
                    items:
                      $ref: "#/components/schemas/IdempotencyRecord"
                  limit:
                    type: integer
                  offset:
                    type: integer
  /v1/idempotency/records/{key}:
    get:
      summary: Get details for a specific idempotency record
      operationId: getIdempotencyRecord
      parameters:
        - name: key
          in: path
          required: true
          description: Idempotency key
          schema:
            type: string
      responses:
        "200":
          description: Single cached idempotency record
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  record:
                    $ref: "#/components/schemas/IdempotencyRecord"
        "404":
          description: Record not found or expired
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/resources:
    get:
      summary: List resources
      operationId: listResources
      responses:
        "200":
          description: Resource index with cache hints
          headers:
            ETag:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ResourcesListResponse"
        "304":
          description: Not Modified (catalog unchanged)
  /v1/resources/read:
    post:
      summary: Read resource
      operationId: readResource
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReadResourceRequest"
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "400":
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: Policy blocked
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Resource not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "502":
          description: Upstream error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "504":
          description: Upstream timeout
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/resources/updates:
    get:
      summary: Real-time resource update SSE feed
      operationId: resourceUpdates
      responses:
        "200":
          description: SSE event stream
          content:
            text/event-stream:
              schema:
                type: string
  /v1/prompts:
    get:
      summary: List prompts
      operationId: listPrompts
      responses:
        "200":
          description: Prompt index with cache hints
          headers:
            ETag:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PromptsListResponse"
        "304":
          description: Not Modified (catalog unchanged)
  /v1/prompts/get:
    post:
      summary: Get prompt output
      operationId: getPrompt
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GetPromptRequest"
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SuccessEnvelope"
        "400":
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "403":
          description: Policy blocked
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "404":
          description: Prompt not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "502":
          description: Upstream error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
        "504":
          description: Upstream timeout
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorEnvelope"
  /v1/catalog/events:
    get:
      summary: Catalog mutation events change feed
      operationId: catalogEvents
      parameters:
        - in: query
          name: after
          required: false
          schema:
            type: string
      responses:
        "200":
          description: Event log feed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CatalogEventsResponse"
  /v1/completion/complete:
    post:
      summary: Argument completion for prompt or resource
      operationId: completeArgument
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CompletionRequest"
      responses:
        "200":
          description: Completion candidate results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CompletionResponse"
  /v1/sampling/create_message:
    post:
      summary: Sampling completion delegation
      operationId: samplingCreateMessage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SamplingRequest"
      responses:
        "200":
          description: Sampling response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SamplingResponse"
        "202":
          description: Sampling request accepted asynchronously
  /v1/sampling/requests:
    get:
      summary: List pending or completed sampling delegation tickets
      operationId: listSamplingRequests
      responses:
        "200":
          description: List of sampling tickets
  /v1/sampling/requests/{id}:
    get:
      summary: Get detailed status of a sampling delegation ticket
      operationId: getSamplingRequest
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Sampling ticket detail
        "404":
          description: Ticket not found
  /v1/sampling/requests/{id}/respond:
    post:
      summary: Provide client LLM completion to resolve a pending sampling ticket
      operationId: respondSamplingRequest
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Completion accepted
        "404":
          description: Ticket not found or not pending
  /v1/config:
    get:
      summary: Get active configuration and metrics
      operationId: getConfig
      responses:
        "200":
          description: Active configuration and runtime metrics
  /v1/config/servers:
    post:
      summary: Upsert upstream server with hot-mounting
      operationId: upsertServer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpsertServerRequest"
      responses:
        "200":
          description: Server upserted and mounted
  /v1/config/servers/{id}:
    delete:
      summary: Delete upstream server and unmount dynamically
      operationId: deleteServer
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Server deleted and unmounted
  /v1/config/ecosystem:
    get:
      summary: Discover external MCP server configurations from Claude Desktop, Cursor, or Zed
      operationId: getEcosystemSources
      responses:
        "200":
          description: Discovered ecosystem configuration files and servers
  /v1/config/import:
    post:
      summary: Import MCP servers from external client configurations
      operationId: importConfig
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ImportConfigRequest"
      responses:
        "200":
          description: Summary of imported servers
  /v1/config/alias:
    post:
      summary: Add or remove capability, resource, or prompt aliases
      operationId: updateAlias
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateAliasRequest"
      responses:
        "200":
          description: Alias updated
  /v1/config/policy:
    post:
      summary: Update security policy rules, approval requirements, and redaction keys
      operationId: updatePolicy
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PolicyConfig"
      responses:
        "200":
          description: Policy updated
  /v1/config/reload:
    post:
      summary: Hot-reload daemon configuration and upstream servers from disk
      operationId: reloadConfig
      responses:
        "200":
          description: Summary of mounted, unmounted, and updated servers
  /v1/approvals:
    get:
      summary: List pending and resolved human approval tickets
      operationId: listApprovals
      responses:
        "200":
          description: List of approval tickets
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ListApprovalsResponse"
  /v1/approvals/{id}:
    get:
      summary: Get details and sanitized parameters for an approval ticket
      operationId: getApproval
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Approval ticket details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GetApprovalResponse"
        "404":
          description: Approval ticket not found
  /v1/approvals/{id}/approve:
    post:
      summary: Approve a suspended capability execution
      operationId: approveTicket
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ApproveTicketRequest"
      responses:
        "200":
          description: Ticket approved and execution resumed
        "409":
          description: Ticket is not pending or already resolved
  /v1/approvals/{id}/reject:
    post:
      summary: Reject a suspended capability execution
      operationId: rejectTicket
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RejectTicketRequest"
      responses:
        "200":
          description: Ticket rejected
        "409":
          description: Ticket is not pending or already resolved
  /v1/audit/events:
    get:
      summary: Query paginated WORM audit events with filters
      operationId: listAuditEvents
      parameters:
        - in: query
          name: actor_id
          schema:
            type: string
        - in: query
          name: capability_id
          schema:
            type: string
        - in: query
          name: event_type
          schema:
            type: string
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: List of audit events
  /v1/audit/events/{id}:
    get:
      summary: Get details of a single audit event record
      operationId: getAuditEvent
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Audit event record
        "404":
          description: Audit event not found
  /v1/audit/verify:
    get:
      summary: Verify complete cryptographic SHA-256 hash chain integrity
      operationId: verifyAuditChain
      responses:
        "200":
          description: Hash chain verification report
  /v1/audit/stats:
    get:
      summary: Retrieve summary metrics and event breakdown of the audit log
      operationId: getAuditStats
      responses:
        "200":
          description: Aggregate audit statistics
  /v1/audit/export:
    get:
      summary: Export raw audit log as JSONL or CSV file attachment
      operationId: exportAuditLog
      parameters:
        - in: query
          name: format
          schema:
            type: string
            enum: [jsonl, csv]
            default: jsonl
      responses:
        "200":
          description: Streamed audit log file
  /v1/operations/{id}/cancel:
    post:
      summary: Cancel in-flight operation
      operationId: cancelOperation
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Operation cancelled
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  request_id:
                    type: string
                  cancelled:
                    type: boolean
components:
  schemas:
    CapabilityListItem:
      type: object
      required: [id, summary, server, tool, tags]
      properties:
        id:
          type: string
        summary:
          type: string
        server:
          type: string
        tool:
          type: string
        tags:
          type: array
          items:
            type: string
    ResourceListItem:
      type: object
      required: [id, server, uri, name, tags]
      properties:
        id:
          type: string
        server:
          type: string
        uri:
          type: string
        name:
          type: string
        description:
          type: [string, "null"]
        mime_type:
          type: [string, "null"]
        tags:
          type: array
          items:
            type: string
    PromptListItem:
      type: object
      required: [id, server, name, arguments, tags]
      properties:
        id:
          type: string
        server:
          type: string
        name:
          type: string
        title:
          type: [string, "null"]
        description:
          type: [string, "null"]
        arguments:
          type: array
          items:
            type: object
        tags:
          type: array
          items:
            type: string
    CapabilitiesListResponse:
      type: object
      required: [version, catalog_version, ttl_ms, cache_scope, capabilities]
      properties:
        version:
          type: string
          const: v1
        catalog_version:
          type: string
        ttl_ms:
          type: integer
          example: 300000
        cache_scope:
          type: string
          example: "public"
        capabilities:
          type: array
          items:
            $ref: "#/components/schemas/CapabilityListItem"
    CapabilityDescribeResponse:
      type: object
      required: [version, capability]
      properties:
        version:
          type: string
          const: v1
        catalog_version:
          type: string
        capability:
          type: object
          required: [id, server, tool, description, input_schema, examples]
          properties:
            id:
              type: string
            server:
              type: string
            tool:
              type: string
            description:
              type: string
            input_schema:
              type: object
            examples:
              type: array
              items: {}
    ResourcesListResponse:
      type: object
      required: [version, catalog_version, ttl_ms, cache_scope, resources]
      properties:
        version:
          type: string
          const: v1
        catalog_version:
          type: string
        ttl_ms:
          type: integer
          example: 300000
        cache_scope:
          type: string
          example: "public"
        resources:
          type: array
          items:
            $ref: "#/components/schemas/ResourceListItem"
    PromptsListResponse:
      type: object
      required: [version, catalog_version, ttl_ms, cache_scope, prompts]
      properties:
        version:
          type: string
          const: v1
        catalog_version:
          type: string
        ttl_ms:
          type: integer
          example: 300000
        cache_scope:
          type: string
          example: "public"
        prompts:
          type: array
          items:
            $ref: "#/components/schemas/PromptListItem"
    SearchCapabilitiesRequest:
      type: object
      properties:
        query:
          type: [string, "null"]
        limit:
          type: integer
          default: 8
        server_ids:
          type: array
          items:
            type: string
        tags:
          type: array
          items:
            type: string
        modes:
          type: array
          items:
            type: string
    SearchCapabilitiesResponse:
      type: object
      required: [version, catalog_version, capabilities]
      properties:
        version:
          type: string
        catalog_version:
          type: string
        capabilities:
          type: array
          items:
            type: object
    CallCapabilityRequest:
      type: object
      required: [capability_id, args]
      properties:
        capability_id:
          type: string
        args:
          type: object
        request_id:
          type: [string, "null"]
        context:
          $ref: "#/components/schemas/RequestContext"
        idempotency_key:
          type: [string, "null"]
        input_responses:
          type: [object, "null"]
          description: MRTR client responses for interactive tool turns
        request_state:
          type: [string, "null"]
          description: MRTR opaque state string
    ReadResourceRequest:
      type: object
      required: [resource_id]
      properties:
        resource_id:
          type: string
        request_id:
          type: [string, "null"]
        context:
          $ref: "#/components/schemas/RequestContext"
        idempotency_key:
          type: [string, "null"]
        input_responses:
          type: [object, "null"]
        request_state:
          type: [string, "null"]
    GetPromptRequest:
      type: object
      required: [prompt_id]
      properties:
        prompt_id:
          type: string
        arguments:
          type: [object, "null"]
        request_id:
          type: [string, "null"]
        context:
          $ref: "#/components/schemas/RequestContext"
        idempotency_key:
          type: [string, "null"]
        input_responses:
          type: [object, "null"]
        request_state:
          type: [string, "null"]
    CompletionRequest:
      type: object
      required: [ref_type, ref_name, argument_name]
      properties:
        ref_type:
          type: string
          enum: [prompt, resource]
        ref_name:
          type: string
        argument_name:
          type: string
        argument_value:
          type: string
    CompletionResponse:
      type: object
      properties:
        ok:
          type: boolean
        trace_id:
          type: string
        data:
          type: object
    SamplingRequest:
      type: object
      required: [server_id, messages]
      properties:
        server_id:
          type: string
        messages:
          type: array
          items:
            type: object
        max_tokens:
          type: [integer, "null"]
    SamplingResponse:
      type: object
      properties:
        ok:
          type: boolean
        trace_id:
          type: string
        data:
          type: object
    CatalogEventsResponse:
      type: object
      required: [catalog_version, cursor, events]
      properties:
        catalog_version:
          type: string
        cursor:
          type: string
        events:
          type: array
          items:
            type: object
    RequestContext:
      type: object
      properties:
        operation_id:
          type: [string, "null"]
        work_item_id:
          type: [string, "null"]
        actor_id:
          type: [string, "null"]
        grant_id:
          type: [string, "null"]
    RetryMetadata:
      type: object
      required: [classification, upstream_execution_state]
      properties:
        classification:
          type: string
          enum: [safe, unsafe, idempotent]
        upstream_execution_state:
          type: string
          enum: [not_started, completed, unknown]
    ServerConfig:
      type: object
      properties:
        command:
          type: [string, "null"]
        args:
          type: array
          items:
            type: string
        env:
          type: object
          additionalProperties:
            type: string
        url:
          type: [string, "null"]
        protocolVersion:
          type: [string, "null"]
          example: "2026-07-28"
        allowStateless:
          type: [boolean, "null"]
        headers:
          type: object
          additionalProperties:
            type: string
        auth:
          type: object
        resilience:
          $ref: "#/components/schemas/ResilienceConfig"
    UpsertServerRequest:
      type: object
      required: [name, server]
      properties:
        name:
          type: string
        server:
          $ref: "#/components/schemas/ServerConfig"
    ImportConfigRequest:
      type: object
      properties:
        source_path:
          type: [string, "null"]
        overwrite:
          type: [boolean, "null"]
    UpdateAliasRequest:
      type: object
      required: [kind, alias]
      properties:
        kind:
          type: string
          enum: [tool, resource, prompt]
        alias:
          type: string
        target:
          type: [string, "null"]
    WebhookConfig:
      type: object
      required: [url]
      properties:
        url:
          type: string
        secret:
          type: [string, "null"]
        secret_env:
          type: [string, "null"]
        auth_header:
          type: [string, "null"]
        headers:
          type: object
          additionalProperties:
            type: string
    PolicyConfig:
      type: object
      properties:
        allow:
          type: array
          items:
            type: string
        deny:
          type: array
          items:
            type: string
        require_approval:
          type: array
          items:
            type: string
        approval_timeout_secs:
          type: integer
        redact_keys:
          type: array
          items:
            type: string
        webhook:
          $ref: "#/components/schemas/WebhookConfig"
    ApprovalStatus:
      type: object
      required: [status]
      properties:
        status:
          type: string
          enum: [pending, approved, rejected, expired]
        operator:
          type: string
        timestamp:
          type: integer
        reason:
          type: string
        modified_args:
          type: object
    ApprovalTicket:
      type: object
      required: [id, capability_id, server_id, args, sanitized_args, status, created_at, expires_at]
      properties:
        id:
          type: string
        capability_id:
          type: string
        server_id:
          type: string
        args:
          type: object
        sanitized_args:
          type: object
        request_id:
          type: [string, "null"]
        status:
          $ref: "#/components/schemas/ApprovalStatus"
        created_at:
          type: integer
        expires_at:
          type: integer
    ListApprovalsResponse:
      type: object
      required: [ok, approvals, total]
      properties:
        ok:
          type: boolean
        approvals:
          type: array
          items:
            $ref: "#/components/schemas/ApprovalTicket"
        total:
          type: integer
    GetApprovalResponse:
      type: object
      required: [ok, approval]
      properties:
        ok:
          type: boolean
        approval:
          $ref: "#/components/schemas/ApprovalTicket"
    ApproveTicketRequest:
      type: object
      required: [operator]
      properties:
        operator:
          type: string
        modified_args:
          type: object
    RejectTicketRequest:
      type: object
      required: [operator]
      properties:
        operator:
          type: string
        reason:
          type: string
    SuccessEnvelope:
      type: object
      required: [ok, request_id, trace_id, data, error, retry]
      properties:
        ok:
          type: boolean
          const: true
        request_id:
          type: [string, "null"]
        trace_id:
          type: string
        context:
          $ref: "#/components/schemas/RequestContext"
        retry:
          $ref: "#/components/schemas/RetryMetadata"
        data: {}
        error:
          type: "null"
    ErrorEnvelope:
      type: object
      required: [ok, request_id, trace_id, data, error, retry]
      properties:
        ok:
          type: boolean
          const: false
        request_id:
          type: [string, "null"]
        trace_id:
          type: string
        context:
          $ref: "#/components/schemas/RequestContext"
        retry:
          $ref: "#/components/schemas/RetryMetadata"
        data:
          type: "null"
        error:
          type: object
          required: [code, message, retryable]
          properties:
            code:
              type: string
              enum:
                - TOOL_NOT_FOUND
                - RESOURCE_NOT_FOUND
                - PROMPT_NOT_FOUND
                - SERVER_UNREACHABLE
                - INVALID_ARGS
                - UPSTREAM_TIMEOUT
                - UPSTREAM_ERROR
                - POLICY_DENIED
                - APPROVAL_PENDING
                - APPROVAL_TIMEOUT
                - APPROVAL_REJECTED
                - CIRCUIT_OPEN
                - OPERATION_CANCELLED
                - INTERNAL_ERROR
            message:
              type: string
            retryable:
              type: boolean
    ResilienceConfig:
      type: object
      properties:
        failureThreshold:
          type: integer
          default: 3
          description: Consecutive failure threshold before tripping circuit to Open
        cooldownMs:
          type: integer
          default: 30000
          description: Cooldown duration in milliseconds before testing recovery in HalfOpen
        consecutiveSuccesses:
          type: integer
          default: 2
          description: Consecutive successful probes in HalfOpen to reset circuit to Closed
        autoRestart:
          type: boolean
          default: true
          description: Automatically restart crashed stdio child processes
        maxRestarts:
          type: integer
          default: 5
          description: Maximum restart attempts before giving up
        healthCheckIntervalSecs:
          type: [integer, "null"]
    BatchStep:
      type: object
      required: [id, capability_id, args]
      properties:
        id:
          type: string
          description: Unique step identifier within the batch (e.g. step1)
        capability_id:
          type: string
          description: Capability ID to invoke
        args:
          type: object
          description: Arguments object, supporting $step_id.field reference interpolation
        continue_on_error:
          type: boolean
          default: false
          description: Whether to continue executing subsequent steps if this step fails
    BatchCallRequest:
      type: object
      required: [steps]
      properties:
        steps:
          type: array
          items:
            $ref: "#/components/schemas/BatchStep"
        request_id:
          type: [string, "null"]
        context:
          $ref: "#/components/schemas/RequestContext"
    BatchStepResult:
      type: object
      required: [id, capability_id, ok, duration_us]
      properties:
        id:
          type: string
        capability_id:
          type: string
        ok:
          type: boolean
        data: {}
        error:
          type: [string, "null"]
        duration_us:
          type: integer
    BatchCallResponse:
      type: object
      required: [ok, trace_id, results, total_duration_us]
      properties:
        ok:
          type: boolean
        request_id:
          type: [string, "null"]
        trace_id:
          type: string
        results:
          type: array
          items:
            $ref: "#/components/schemas/BatchStepResult"
        total_duration_us:
          type: integer
    IdempotencyRecord:
      type: object
      required: [key, response, created_at_epoch_secs, replay_count]
      properties:
        key:
          type: string
          description: Idempotency key
        capability_id:
          type: [string, "null"]
          description: Target capability ID
        response:
          type: object
          description: Cached execution response payload
        created_at_epoch_secs:
          type: integer
          description: Unix timestamp when key was initialized
        completed_at_epoch_secs:
          type: [integer, "null"]
          description: Unix timestamp when execution completed
        replay_count:
          type: integer
          description: Number of times this cached response was replayed
        first_trace_id:
          type: [string, "null"]
          description: Trace ID of the initial execution attempt

