openapi: 3.1.0
info:
  title: Hengam Server 2 Non-P&ID Document Bridge
  version: 3.3.0
  description: Claim only Hengam AI non-P&ID document knowledge jobs, report progress, and submit grounded results.
servers:
  - url: https://doc.138-124-60-41.sslip.io
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
  schemas:
    OriginalDocumentSource:
      type: object
      additionalProperties: false
      required: [document_no, revision, page]
      properties:
        document_no:
          type: string
          minLength: 5
          maxLength: 180
        revision:
          type: string
          minLength: 1
          maxLength: 80
        page:
          type: integer
          minimum: 1
          description: Page inside the original source PDF, never a Markdown line or merged offset.
security:
  - bearerAuth: []
paths:
  /api/action/jobs/claim:
    post:
      operationId: claimNextFileJob
      summary: Claim the next non-P&ID knowledge job
      description: Always send command=claim_next and job_type=document_knowledge_chat. The type filter prevents this GPT from claiming unrelated queue items.
      x-openai-isConsequential: false
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [command, job_type]
              properties:
                command:
                  type: string
                  enum: [claim_next]
                job_type:
                  type: string
                  enum: [document_knowledge_chat]
      responses:
        '200':
          description: Claimed job or empty typed queue
          content:
            application/json:
              schema:
                type: object
                properties:
                  found: { type: boolean }
                  message: { type: string }
                  job_id: { type: string }
                  job_type: { type: string }
                  source_job_id: { type: string }
                  metadata_json: { type: string }
                  conversation_context_json: { type: string }
                  heartbeat_interval_seconds: { type: integer }
                  reclaimed: { type: boolean }
                  claim_attempt: { type: integer }
  /api/action/jobs/{job_id}/heartbeat:
    post:
      operationId: reportJobProgress
      summary: Report public progress and renew the Job lease
      x-openai-isConsequential: false
      parameters:
        - name: job_id
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [phase, message]
              properties:
                phase:
                  type: string
                  maxLength: 80
                message:
                  type: string
                  maxLength: 1200
                progress_percent:
                  type: number
                  minimum: 0
                  maximum: 99
                current_item:
                  type: string
                  maxLength: 240
      responses:
        '200':
          description: Progress saved
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  job_id: { type: string }
                  status: { type: string }
                  heartbeat_count: { type: integer }
  /api/action/jobs/{job_id}/result:
    post:
      operationId: submitFileJobResult
      summary: Submit and archive the final non-P&ID result
      description: Answered results require original-document citations. no_answer and failed results may use an empty sources array.
      x-openai-isConsequential: false
      parameters:
        - name: job_id
          in: path
          required: true
          schema: { type: string }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [status, outcome, answer, sources]
              properties:
                status:
                  type: string
                  enum: [completed, failed]
                outcome:
                  type: string
                  enum: [answered, no_answer, failed]
                answer:
                  type: string
                  minLength: 1
                  maxLength: 200000
                sources:
                  type: array
                  maxItems: 200
                  items:
                    $ref: '#/components/schemas/OriginalDocumentSource'
                conversation_state:
                  type: object
                  additionalProperties: false
                  properties:
                    active_equipment: { type: string }
                    active_package: { type: string }
                    active_topic: { type: string }
                    active_component: { type: string }
                    active_side: { type: string }
                    last_answer: { type: string }
                    important_documents:
                      type: array
                      maxItems: 100
                      items: { type: string }
                    assumptions:
                      type: array
                      maxItems: 50
                      items: { type: string }
              allOf:
                - if:
                    properties:
                      outcome: { const: answered }
                    required: [outcome]
                  then:
                    properties:
                      sources:
                        minItems: 1
      responses:
        '200':
          description: Result stored
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  job_id: { type: string }
                  status: { type: string }
                  outcome: { type: string }
