{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/contraforce/json-schema/contraforce-agent-tool-investigation-submission-request-schema.json",
  "title": "AgentToolInvestigationSubmissionRequest",
  "description": "Request DTO for the agent's submit-investigation tool — the closeout call the agent\nmakes after it has finished investigating an incident.",
  "x-generated": "2026-11-04",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/contraforce-agent-tools-api-openapi.yml#/components/schemas/AgentToolInvestigationSubmissionRequest",
  "type": "object",
  "properties": {
    "comment": {
      "type": [
        "null",
        "string"
      ],
      "description": "Final investigation summary. Posted on the incident as the agent's comment as-is — no server-side wrapping or section headers are applied. Write as a detailed security analyst would: cover initial hypothesis, indicators matched, tools called and why, evidence found, classification rationale, response actions, and recommended next steps. This is what a human analyst will read to learn from the agent's analysis."
    },
    "classification_reason_comment": {
      "type": [
        "null",
        "string"
      ],
      "description": "Short rationale (1–2 sentences) shown on the incident's closure record when the status transition closes the incident. Surfaces in the closure dialog and audit log alongside the classification reason."
    },
    "malicious_score": {
      "pattern": "^-?(?:0|[1-9]\\d*)$",
      "type": [
        "integer",
        "string"
      ],
      "description": "How malicious the agent judged the activity, 0 to 100, where 0 is clearly benign and 100 is\nclearly malicious. This is not confidence in the verdict: a False Positive the agent is\ncertain about scores near zero, not near a hundred.",
      "format": "int32"
    },
    "classification": {
      "oneOf": [
        {
          "type": "null"
        },
        {
          "description": "Final incident classification, identical for every security platform. Allowed values: TruePositive (confirmed malicious), FalsePositive (the detection was wrong; no suspicious activity occurred), BenignPositive (real activity confirmed benign, expected, or authorized), Undetermined (no firm verdict reached). Status transition follows from this via the per-severity agent settings.",
          "$ref": "#/$defs/Classification"
        }
      ]
    },
    "classification_reason": {
      "oneOf": [
        {
          "type": "null"
        },
        {
          "description": "Reason corresponding to the classification; required on every classified closeout and identical for every security platform. TruePositive → MultistagedAttack | Malware | MaliciousUserActivity | UnwantedSoftware | Phishing | CompromisedUser | Apt | SuspiciousActivity | Other. FalsePositive → NotMalicious | NoEnoughDataToValidate | InaccurateData | IncorrectAlertLogic | Other. BenignPositive → SecurityTesting | ConfirmedUserActivity | LineOfBusinessApplication | SecurityPersonnel | SuspiciousButExpected | Other. Undetermined → Unknown | Other. Pick the most specific reason the evidence supports; a pairing outside this list is normalized server-side to Other.",
          "$ref": "#/$defs/ClassificationReason"
        }
      ]
    },
    "classification_sop": {
      "description": "The single classification SOP whose decision tree drove the classification. Null when no SOP applied to this incident type.",
      "$ref": "#/$defs/AgentToolSopReference"
    },
    "response_sops": {
      "type": [
        "null",
        "array"
      ],
      "items": {
        "description": "A reference to a Standard Operating Procedure used by the agent — both the SOP's\ninternal id and its display name are sent so the metadata is self-describing\nwithout a follow-up lookup.",
        "$ref": "#/$defs/AgentToolSopReference"
      },
      "description": "SOPs whose response steps the agent executed during the investigation. Empty when the agent decided no response action was warranted."
    },
    "gamebook_ids": {
      "type": [
        "null",
        "array"
      ],
      "items": {
        "type": "string"
      },
      "description": "IDs of gamebooks the agent queued earlier via submit-gamebook during this investigation. Use the `gamebook_id` returned by each submit-gamebook call. Receive-only here — submit-investigation does NOT queue additional gamebooks."
    },
    "tool_calls": {
      "type": [
        "null",
        "array"
      ],
      "items": {
        "description": "One entry in the agent's tool-call timeline.",
        "$ref": "#/$defs/AgentToolCallRecord"
      },
      "description": "Per-tool-call timeline the agent recorded during the investigation. Used to render the activity header on the comment so an analyst can see exactly which tools the agent invoked, on what target, in what order, and how long each took."
    }
  },
  "$defs": {
    "AgentToolCallKql": {
      "type": "object",
      "properties": {
        "query": {
          "type": [
            "null",
            "string"
          ],
          "description": "Exact KQL query that was executed. Shown verbatim in the comment activity header so an analyst can replay or audit the query."
        },
        "row_count": {
          "pattern": "^-?(?:0|[1-9]\\d*)$",
          "type": [
            "integer",
            "string"
          ],
          "description": "Number of rows the query returned before any client-side truncation. Use the row_count from the execute-kql tool result.",
          "format": "int32"
        }
      },
      "description": "Per-call detail emitted alongside an `execute-kql` tool call so the comment\nheader can render the query and result size without a re-execution."
    },
    "AgentToolCallRecord": {
      "type": "object",
      "properties": {
        "name": {
          "type": [
            "null",
            "string"
          ],
          "description": "Tool name as the agent invoked it (e.g. 'execute-kql', 'get-user-details', 'list-signin-logs'). Use the MCP tool name verbatim so the activity header maps to the right icon/label."
        },
        "time": {
          "type": "string",
          "description": "UTC instant the tool call started. ISO 8601 / RFC 3339 format.",
          "format": "date-time"
        },
        "duration_ms": {
          "pattern": "^-?(?:0|[1-9]\\d*)$",
          "type": [
            "integer",
            "string"
          ],
          "description": "Wall-clock duration of the call in milliseconds, including network time. Used to surface slow tool calls in the activity header.",
          "format": "int64"
        },
        "target_entity": {
          "type": [
            "null",
            "string"
          ],
          "description": "Entity the tool call targeted — UPN for users, device ID for hosts, table name for KQL, etc. Null for tools that do not address a specific entity (e.g. list-kql-tables)."
        },
        "kql": {
          "description": "KQL-specific detail. REQUIRED on every execute-kql tool call and null for every other tool. Carry the exact query text you passed to execute-kql in `query` and the tool result's row_count in `row_count`: the portal shows the query to the analyst on the comment, lets them re-run it in log search, and audits it. An execute-kql entry without `query` cannot be reviewed or replayed.",
          "$ref": "#/$defs/AgentToolCallKql"
        }
      },
      "description": "One entry in the agent's tool-call timeline."
    },
    "AgentToolSopReference": {
      "type": "object",
      "properties": {
        "id": {
          "type": [
            "null",
            "string"
          ],
          "description": "Internal SOP identifier (UUID or slug). Used to link back to the SOP record in the knowledge base."
        },
        "name": {
          "type": [
            "null",
            "string"
          ],
          "description": "Display name of the SOP. Shown in the comment header so the analyst sees which SOP drove the decision without a follow-up lookup."
        }
      },
      "description": "A reference to a Standard Operating Procedure used by the agent — both the SOP's\ninternal id and its display name are sent so the metadata is self-describing\nwithout a follow-up lookup."
    },
    "Classification": {
      "enum": [
        "BenignPositive",
        "FalsePositive",
        "TruePositive",
        "Undetermined",
        null
      ]
    },
    "ClassificationReason": {
      "enum": [
        "InaccurateData",
        "IncorrectAlertLogic",
        "SuspiciousActivity",
        "SuspiciousButExpected",
        "Unknown",
        "Apt",
        "Malware",
        "SecurityPersonnel",
        "SecurityTesting",
        "UnwantedSoftware",
        "Other",
        "MultistagedAttack",
        "CompromisedUser",
        "Phishing",
        "MaliciousUserActivity",
        "NotMalicious",
        "NoEnoughDataToValidate",
        "ConfirmedUserActivity",
        "LineOfBusinessApplication",
        null
      ]
    }
  }
}