> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentopfor.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK

> Run opfor scans programmatically from your own TypeScript or Node code.

`@keyvaluesystems/agent-opfor-sdk` is the programmatic surface — the same engine as the CLI, callable from your own code. Use it to embed red-teaming in custom tooling, gate CI on results, or build your own workflows. It's TypeScript-first.

## Install

```bash theme={null}
npm install @keyvaluesystems/agent-opfor-sdk
```

## Quickstart

Both a class-based and a functional API are available — they're equivalent, so use whichever you prefer.

<Tabs>
  <Tab title="Class">
    ```typescript theme={null}
    import { Opfor } from "@keyvaluesystems/agent-opfor-sdk";

    const opfor = new Opfor({ apiKey: process.env.ANTHROPIC_API_KEY });

    const results = await opfor.run({
      target: { url: "https://api.example.com/chat", apiKey: process.env.TARGET_API_KEY },
      suite: "owasp-llm-top10",
    });

    console.log(results.score, results.findings.length);
    ```
  </Tab>

  <Tab title="Functional">
    ```typescript theme={null}
    import { run, report } from "@keyvaluesystems/agent-opfor-sdk";

    const results = await run({
      target: { url: "https://api.example.com/chat", apiKey: process.env.TARGET_API_KEY },
      suite: "owasp-llm-top10",
      apiKey: process.env.ANTHROPIC_API_KEY,
    });

    await report(results).html("./report.html");
    ```
  </Tab>
</Tabs>

## Run

Run a suite or a list of evaluators against a target.

```typescript theme={null}
const results = await opfor.run({
  target: { url: "https://api.example.com/chat", apiKey: process.env.TARGET_API_KEY, model: "gpt-4o" },
  suite: "owasp-llm-top10",
  strategy: { effort: "adaptive", turnMode: "multi", turns: 3 },
  attackerModel: "claude-sonnet-4",
  judgeModel: "claude-sonnet-4",
});
```

Run specific evaluators instead of a suite with `evaluators: [...]`:

```typescript theme={null}
await opfor.run({
  target: { url: "https://api.example.com/chat" },
  evaluators: ["jailbreaking", "prompt-injection", "system-prompt-leakage"],
});
```

## Targets

<Tabs>
  <Tab title="HTTP endpoint">
    ```typescript theme={null}
    target: {
      url: "https://api.example.com/chat",
      name: "My Chatbot",
      description: "Customer support chatbot with order-database access",
      apiKey: process.env.TARGET_API_KEY,
      model: "gpt-4o",
      headers: { "X-Custom-Header": "value" },
      requestFormat: "openai",     // "auto" | "openai" | "json"
      promptPath: "input.message", // for requestFormat: "json"
      responsePath: "output.text",
    }
    ```
  </Tab>

  <Tab title="Stateful vs stateless">
    ```typescript theme={null}
    // Stateless — opfor sends full chat history each turn (raw LLM APIs)
    target: { url: "https://api.openai.com/v1/chat/completions", stateful: false }

    // Stateful, client-owned — your server keeps history, keyed by a session id opfor sends
    target: { url: "https://api.example.com/chat", stateful: true, sessionField: "session_id" }

    // Stateful, server-owned — the target mints its own id and returns it; opfor
    // sends none on turn 1, then captures and echoes it
    target: {
      url: "https://api.example.com/chat",
      stateful: true,
      session: {
        send: { in: "header", name: "Mcp-Session-Id" },
        receive: { in: "header", name: "Mcp-Session-Id" },
      },
    }
    ```

    See [session handling](/reference/sessions) for the full client- vs server-owned model.
  </Tab>

  <Tab title="Local script">
    ```typescript theme={null}
    target: {
      type: "local-script",
      name: "My Local Agent",
      scriptPath: "./my-agent.js", // or .py
    }
    ```
  </Tab>

  <Tab title="MCP server">
    ```typescript theme={null}
    // stdio transport
    target: {
      kind: "mcp",
      transport: "stdio",
      command: "node",
      args: ["./dist/server.js"],
      env: { DEBUG: "true" },
    }

    // url transport
    target: { kind: "mcp", transport: "url", url: "http://localhost:3000/mcp" }
    ```
  </Tab>
</Tabs>

## Strategy

Control how thorough the attack loop is.

```typescript theme={null}
strategy: {
  effort: "adaptive",   // "adaptive" (one sustained chat) | "comprehensive" (one attack per pattern)
  turnMode: "multi",    // "single" | "multi"
  turns: 3,             // max turns per attack when multi
}
```

## Models

Set attacker and judge LLMs as a shorthand string or a full provider object. The judge defaults to the attacker if unset.

```typescript theme={null}
const opfor = new Opfor({ apiKey: process.env.ANTHROPIC_API_KEY });

await opfor.run({
  target: { url: "https://api.example.com/chat" },
  suite: "owasp-llm-top10",
  attackerModel: "claude-sonnet-4",
  judgeModel: { provider: "anthropic", model: "claude-opus-4", apiKey: process.env.ANTHROPIC_API_KEY },
});
```

Provider objects accept `{ provider, model, apiKey, baseUrl? }` — `apiKey` is the key **value**, not an env var name. See [providers](/reference/providers) for the full provider list.

## Autonomous mode

`hunt()` runs the [autonomous campaign](/cli/autonomous) programmatically — adaptive multi-turn attacks driven by an AI agent, with progress streaming.

```typescript theme={null}
import { hunt } from "@keyvaluesystems/agent-opfor-sdk";

const results = await hunt({
  target: { url: "https://api.example.com/chat", apiKey: process.env.TARGET_API_KEY },
  objective: "Find jailbreaks, data leaks, and authorization flaws",
  limits: { budgetUsd: 5 },
  onProgress: (event) => {
    if (event.type === "finding") console.log(`Found: ${event.vulnClass} (${event.severity})`);
  },
});

console.log(results.outcome, results.findings.length, results.htmlReportPath);
```

<Accordion title="HuntOptions reference">
  ```typescript theme={null}
  interface HuntOptions {
    target: {
      url: string;
      name?: string;
      apiKey?: string;
      headers?: Record<string, string>;
      stateful?: boolean;
      sessionField?: string;
      promptPath?: string;
      responsePath?: string;
      model?: string;
    };
    objective: string;
    models?: { commander?: string; operator?: string; scout?: string; verifier?: string };
    limits?: {
      budgetUsd?: number;       // default 10
      maxOperators?: number;    // default 6
      maxTurns?: number;        // default 120
      maxThreadTurns?: number;  // default 25
      maxTotalThreads?: number; // default 40
      maxDepth?: number;        // default 3
      maxReconProbes?: number;  // default 8
    };
    verify?: boolean;
    sequential?: boolean;
    outputDir?: string;
    onProgress?: (event: HuntProgressEvent) => void;
  }
  ```

  `onProgress` events: `line`, `recon_start`, `recon_done`, `thread_start`, `thread_turn`, `thread_done`, `finding`, `complete`. `HuntResults` includes `outcome`, `summary`, `recon`, `findings`, `recommendations`, `totalCostUsd`, and report paths.
</Accordion>

## Telemetry

Enable [trace-aware testing](/telemetry/overview) so the judge sees the target's internal tool calls and retrievals.

```typescript theme={null}
await opfor.run({
  target: { url: "https://api.example.com/chat" },
  suite: "owasp-llm-top10",
  telemetry: {
    provider: "langfuse",
    langfuse: {
      publicKeyEnv: "LANGFUSE_PUBLIC_KEY",
      secretKeyEnv: "LANGFUSE_SECRET_KEY",
      baseUrl: "https://cloud.langfuse.com",
      traceSelection: { lookbackHours: 24, environment: "production" },
    },
    enrichJudgeFromTrace: true,
    propagation: { headers: { "X-Trace-Id": "{{traceId}}" }, traceIdStrategy: "per-attack" },
  },
});
```

For Netra, swap `provider` and use the `netra` block:

```typescript theme={null}
telemetry: {
  provider: "netra",
  netra: {
    baseUrl: "http://localhost:3000",
    apiKeyEnv: "NETRA_API_KEY",
    traceSelection: { lookbackHours: 24 },
  },
  enrichJudgeFromTrace: true,
  propagation: { traceIdBodyField: "trace_id", traceIdStrategy: "per-attack" },
}
```

See [Trace-aware testing](/telemetry/overview) for the full config reference.

## Results

```typescript theme={null}
interface RunResults {
  id: string;
  timestamp: string;
  targetName: string;
  targetKind: "agent" | "mcp";
  effort: "adaptive" | "comprehensive";
  attackerModel: string;
  judgeModel: string;
  score: number; // 0–100 safety score
  summary: {
    total: number;
    passed: number;
    failed: number;
    errors: number;
    safetyScore: number;
    attackSuccessRate: number;
  };
  findings: Finding[];
  evaluators: EvaluatorResult[];
}
```

A `Finding` carries `severity`, `title`, `description`, `evidence?`, and a `standards` map. Each `EvaluatorResult` holds per-evaluator pass/fail counts and an `attacks` array with full prompt/response transcripts and `verdict`.

## Reports

```typescript theme={null}
const r = opfor.report(results);
await r.json("./report.json");
await r.html("./report.html");

// functional
import { report } from "@keyvaluesystems/agent-opfor-sdk";
await report(results).html("./report.html");
```

## Gate CI on findings

```typescript theme={null}
import { run } from "@keyvaluesystems/agent-opfor-sdk";

const results = await run({
  target: { url: process.env.TARGET_URL!, apiKey: process.env.TARGET_API_KEY },
  suite: "owasp-llm-top10",
  apiKey: process.env.ANTHROPIC_API_KEY,
});

if (results.findings.length > 0) {
  for (const f of results.findings) console.error(`[${f.severity}] ${f.title}`);
  process.exit(1);
}
console.log(`Safety score: ${results.score}%`);
```

## Exports

```typescript theme={null}
import {
  Opfor,          // class API
  run,            // run a suite / evaluators
  hunt,           // autonomous red-team
  report,         // generate reports from results
  listSuites,     // list available suites
  listEvaluators, // list available evaluators (optionally { kind: "mcp" })
} from "@keyvaluesystems/agent-opfor-sdk";
```

Types are exported too: `RunOptions`, `RunResults`, `TargetConfig`, `McpTargetConfig`, `Finding`, `EvaluatorResult`, `AttackResult`, `TelemetryConfig`, and the `Hunt*` variants.
