openapi: 3.0.3
info:
  title: Stacksona Gate Agent API (action-bound approval contract)
  version: 2.0.0-draft
  description: |
    Draft schema for the Gate V1 legacy and V2 action-bound approval paths.
    This document describes the current PR implementation, not a released API.
    Gate authenticates a caller-provided action hash; the runtime computes it
    over the exact immutable action and enforces it during handler execution.
servers:
  - url: https://{installation}
    variables:
      installation:
        default: your-gate.example.com
security:
  - agentBearer: []
paths:
  /api/agent/tasks/{taskID}/requests:
    post:
      summary: Evaluate a proposed tool call
      operationId: requestAgentDecision
      parameters:
        - name: taskID
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DecisionRequest'
      responses:
        '200':
          description: Automatic decision or new review thread
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Decision'
        '400':
          description: Invalid request, malformed action hash or policy input
  /api/agent/decisions:
    get:
      summary: Poll the exact review thread or newest thread in a task group
      operationId: getAgentDecision
      description: |
        Use thread_id for an exact pending action. A task_id can have multiple
        review threads and returns the newest matching thread.
      parameters:
        - name: thread_id
          in: query
          schema: { type: string }
          description: Preferred exact review thread; provide thread_id or task_id.
        - name: task_id
          in: query
          schema: { type: string }
          description: Reusable task/group identity; provide thread_id or task_id.
      responses:
        '200':
          description: Current review state; raw approval_token is delivered once.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Decision'
        '400': { description: Missing decision identifier }
        '404': { description: No decision in authenticated agent context }
  /api/agent/approvals/validate:
    post:
      summary: Validate and consume signed approval proof
      operationId: validateAgentApproval
      description: |
        request_payload is mandatory for V1 and V2. When the original request
        supplied action_hash, both action_hash and exact thread_id are required.
        Failed V2 validation must never downgrade to V1. Most invalid outcomes
        return HTTP 200 with valid:false; inspect the response body.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ApprovalValidationRequest'
      responses:
        '200':
          description: Authenticated one-time validation result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApprovalValidationResult'
  /api/agent/tasks/{taskID}/events:
    post:
      summary: Log evidence or submit an in-thread revision
      operationId: logAgentEvent
      parameters:
        - name: taskID
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgentEvent'
      responses:
        '201': { description: Event accepted }
        '400': { description: Invalid event or malformed revision hash }
        '409': { description: Stale, duplicate or non-pending revision }
components:
  securitySchemes:
    agentBearer:
      type: http
      scheme: bearer
      description: Gate agent API key for the authenticated tenant/agent.
  schemas:
    ActionHash:
      type: string
      minLength: 64
      maxLength: 64
      pattern: '^[0-9a-f]{64}$'
      description: |
        Lowercase hex SHA-256 of the full immutable runtime action envelope,
        with no sha256: prefix. Runtime-defined canonicalization is not part of
        Gate's payload JSON digest; see action-bound-approval-v2.md.
      example: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    DecisionRequest:
      type: object
      required: [tool_name]
      additionalProperties: false
      properties:
        tool_name: { type: string }
        workflow_name: { type: string }
        task_label: { type: string }
        subject: { type: string }
        preview: { type: string }
        risk_level: { type: string, enum: [low, medium, high, critical] }
        summary: { type: array, items: { type: string } }
        payload: { type: object, additionalProperties: true, default: {} }
        action_hash:
          $ref: '#/components/schemas/ActionHash'
        images: { type: array, items: { type: string } }
      description: |
        An absent action_hash uses legacy V1. Supplying it opts into strict
        action-bound V2 for this review thread.
    Decision:
      type: object
      required: [status, task_id]
      properties:
        status:
          type: string
          enum: [allow, reject, pending_review, approved, rejected, changes_requested]
        task_id: { type: string }
        thread_id: { type: string }
        message: { type: string }
        recommended_poll_after_seconds: { type: integer }
        approval_token: { type: string, description: One-time raw token on approved signed decisions. }
        token_expires_at: { type: string, format: date-time }
        action_hash:
          $ref: '#/components/schemas/ActionHash'
      description: action_hash echoes the submitted action on immediate allow, reject or pending_review, and is returned on subsequent V2 thread polls; absent for legacy V1.
    ApprovalValidationRequest:
      type: object
      required: [task_id, signature, request_payload]
      additionalProperties: false
      properties:
        task_id: { type: string }
        signature: { type: string, description: Raw approval token. }
        request_payload: { type: object, additionalProperties: true }
        thread_id: { type: string, description: Required for action-bound V2. }
        action_hash:
          $ref: '#/components/schemas/ActionHash'
      description: |
        V2 requires action_hash and thread_id together. Gate rejects a V2
        approval when either is missing/mismatched, without consuming its token.
    ApprovalValidationResult:
      type: object
      required: [valid]
      properties:
        valid: { type: boolean }
        reason: { type: string, enum: [invalid, expired, used] }
        task_id: { type: string, description: Present for successful V2 validation. }
        thread_id: { type: string, description: Present for successful V2 validation. }
        action_hash:
          $ref: '#/components/schemas/ActionHash'
    AgentEvent:
      type: object
      required: [event_type, event_summary]
      additionalProperties: false
      properties:
        event_type: { type: string, description: revision.{taskID}.{threadID} activates guarded in-thread revision. }
        event_summary: { type: string }
        payload:
          type: object
          additionalProperties: true
          properties:
            revision:
              $ref: '#/components/schemas/Revision'
        images: { type: array, items: { type: string } }
    Revision:
      type: object
      required: [revision_id, tool_name, subject, request_payload]
      additionalProperties: true
      properties:
        revision_id: { type: string }
        workflow_name: { type: string }
        task_label: { type: string }
        tool_name: { type: string }
        subject: { type: string }
        preview: { type: string }
        risk_level: { type: string, enum: [low, medium, high, critical] }
        summary: { type: array, items: { type: string } }
        request_payload: { type: object, additionalProperties: true }
        action_hash:
          $ref: '#/components/schemas/ActionHash'
      description: action_hash is required when revising an existing action-bound V2 review.
