commit f6033271b3a1539c341e2c6052c6ce38227b8a4e Author: <> Date: Tue Jul 14 12:55:51 2026 +0000 Deployed 62871b6 with MkDocs version: 1.6.1 diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/404.html b/404.html new file mode 100644 index 00000000..111f84e9 --- /dev/null +++ b/404.html @@ -0,0 +1,2761 @@ + + + +
+ + + + + + + + + + + + + + +
+
+
+
+ Agents are the core of CAI. An agent uses Large Language Models (LLMs), configured with instructions and tools to perform specialized cybersecurity tasks. Each agent is defined in its own .py file in src/cai/agents and optimized for specific security domains.
CAI provides a comprehensive suite of specialized agents for different cybersecurity scenarios:
+| Agent | +Description | +Primary Use Case | +Key Tools | +
|---|---|---|---|
| redteam_agent | +Offensive security specialist for penetration testing | +Active exploitation, vulnerability discovery | +nmap, metasploit, burp | +
| blueteam_agent | +Defensive security expert for threat mitigation | +Security hardening, incident response | +wireshark, suricata, osquery | +
| bug_bounter_agent | +Bug bounty hunter optimized for vulnerability research | +Web app security, API testing | +ffuf, sqlmap, nuclei | +
| one_tool_agent | +Minimalist agent focused on single-tool execution | +Quick scans, specific tool operations | +Generic Linux commands | +
| dfir_agent | +Digital Forensics and Incident Response expert | +Log analysis, forensic investigation | +volatility, autopsy, log2timeline | +
| reverse_engineering_agent | +Binary analysis and reverse engineering | +Malware analysis, firmware reversing | +ghidra, radare2, ida | +
| memory_analysis_agent | +Memory dump analysis specialist | +RAM forensics, process analysis | +volatility, rekall | +
| network_traffic_analyzer | +Network packet analysis expert | +PCAP analysis, traffic inspection | +wireshark, tcpdump, tshark | +
| android_sast_agent | +Android Static Application Security Testing | +APK analysis, Android vulnerability scanning | +jadx, apktool, mobsf | +
| wifi_security_tester | +Wireless network security assessment | +WiFi penetration testing, WPA cracking | +aircrack-ng, reaver, wifite | +
| replay_attack_agent | +Replay attack execution specialist | +Protocol replay, authentication bypass | +custom scripts, burp | +
| subghz_sdr_agent | +Sub-GHz SDR signal analysis expert | +RF analysis, IoT protocol testing | +hackrf, gqrx, urh | +
# Launch CAI with a specific agent
+CAI_AGENT_TYPE=redteam_agent cai
+
+# Launch with custom model
+CAI_AGENT_TYPE=bug_bounter_agent CAI_MODEL=alias0 cai
+
+# Or switch agents during a session
+CAI>/agent redteam_agent
+
+# List all available agents with descriptions
+CAI>/agent list
+
+# Get detailed info about a specific agent
+CAI>/agent info redteam_agent
+redteam_agentbug_bounter_agentdfir_agent or memory_analysis_agentsubghz_sdr_agent or reverse_engineering_agentnetwork_traffic_analyzer or blueteam_agentandroid_sast_agentwifi_security_tester| Capability | +redteam | +blueteam | +bug_bounty | +dfir | +reverse_eng | +network | +
|---|---|---|---|---|---|---|
| Web App Testing | +⭐⭐⭐ | +⭐ | +⭐⭐⭐⭐⭐ | +⭐⭐ | +⭐ | +⭐⭐ | +
| Network Analysis | +⭐⭐⭐⭐ | +⭐⭐⭐⭐⭐ | +⭐⭐ | +⭐⭐⭐ | +⭐ | +⭐⭐⭐⭐⭐ | +
| Binary Analysis | +⭐⭐ | +⭐ | +⭐ | +⭐⭐ | +⭐⭐⭐⭐⭐ | +⭐ | +
| Forensics | +⭐⭐ | +⭐⭐⭐ | +⭐ | +⭐⭐⭐⭐⭐ | +⭐⭐⭐ | +⭐⭐ | +
| IoT/Embedded | +⭐⭐⭐ | +⭐⭐ | +⭐⭐ | +⭐⭐ | +⭐⭐⭐⭐ | +⭐⭐⭐ | +
| API Testing | +⭐⭐⭐ | +⭐⭐ | +⭐⭐⭐⭐⭐ | +⭐⭐ | +⭐ | +⭐⭐ | +
| Exploit Development | +⭐⭐⭐⭐⭐ | +⭐ | +⭐⭐⭐ | +⭐ | +⭐⭐⭐⭐ | +⭐ | +
Legend: ⭐ Limited | ⭐⭐⭐ Moderate | ⭐⭐⭐⭐⭐ Excellent
+# 1. Start with reconnaissance
+CAI>/agent bug_bounter_agent
+CAI> Scan https://target.com for vulnerabilities
+
+# 2. Switch to exploitation
+CAI>/agent redteam_agent
+CAI> Exploit the SQL injection found at /login
+
+# 3. Post-exploitation analysis
+CAI>/agent dfir_agent
+CAI> Analyze the logs to understand the attack surface
+# 1. RF signal analysis
+CAI>/agent subghz_sdr_agent
+CAI> Analyze the 433MHz signals from the device
+
+# 2. Firmware analysis
+CAI>/agent reverse_engineering_agent
+CAI> Extract and analyze the firmware from dump.bin
+
+# 3. Memory analysis if device captured
+CAI>/agent memory_analysis_agent
+CAI> Analyze the memory dump for secrets
+# 1. Network traffic analysis
+CAI>/agent network_traffic_analyzer
+CAI> Analyze capture.pcap for suspicious activity
+
+# 2. Forensic investigation
+CAI>/agent dfir_agent
+CAI> Investigate the compromised host logs
+
+# 3. Defensive recommendations
+CAI>/agent blueteam_agent
+CAI> Provide mitigation strategies based on findings
+Key agent properties include:
+name: Name of the agent (e.g., the name of one_tool_agent is 'CTF Agent')instructions: The system prompt that defines agent behaviormodel: Which LLM to use, with optional model_settings to configure parameters like temperature, top_p, etc.tools: Tools that the agent can use to achieve its taskshandoffs: Allows an agent to delegate tasks to another agentone_tool_agent.pyfrom cai.sdk.agents import Agent, OpenAIChatCompletionsModel
+from cai.tools.reconnaissance.generic_linux_command import generic_linux_command
+from openai import AsyncOpenAI
+
+one_tool_agent = Agent(
+ name="CTF agent",
+ description="Agent focused on conquering security challenges using generic linux commands",
+ instructions="You are a Cybersecurity expert Leader facing a CTF challenge.",
+ tools=[
+ generic_linux_command,
+ ],
+ model=OpenAIChatCompletionsModel(
+ model="qwen2.5:14b",
+ openai_client=AsyncOpenAI(),
+ )
+)
+There are two main context types. See context for details.
+Agents are generic on their context type. Context is a dependency-injection tool: it's an object you create and pass to Runner.run(), that is passed to every agent, tool, handoff etc, and it serves as a grab bag of dependencies and state for the agent run. You can provide any Python object as the context.
@dataclass
+class SecurityContext:
+ target_system: str
+ is_compromised: bool
+
+ async def get_exploits() -> list[Exploits]:
+ return ...
+
+agent = Agent[SecurityContext](
+ ...,
+)
+By default, agents produce plain text (i.e. str) outputs. If you want the agent to produce a particular type of output, you can use the output_type parameter. A common choice is to use Pydantic objects, but we support any type that can be wrapped in a Pydantic TypeAdapter - dataclasses, lists, TypedDict, etc.
from pydantic import BaseModel
+from cai.sdk.agents import Agent
+
+class SecurityVulnerability(BaseModel):
+ name: str
+ severity: str
+ affected_files: list[str]
+ description: str
+
+agent = Agent(
+ name="Vulnerability scanner",
+ instructions="Analyze system output and identify security vulnerabilities",
+ output_type=SecurityVulnerability,
+)
+Note
+When you pass an output_type, that tells the model to use structured outputs instead of regular plain text responses.
Handoffs are sub-agents that the agent can delegate to. You provide a list of handoffs, and the agent can choose to delegate to them if relevant. This is a powerful pattern that allows orchestrating modular, specialized agents that excel at a single task. Read more in the handoffs documentation.
+from cai.sdk.agents import Agent
+
+crypto_agent = Agent(
+ name="Cryptography agent",
+ description="Agent specialized in solving cryptographic challenges and decoding encrypted messages",
+ instructions="Analyze encrypted data and apply cryptographic techniques to decode it.",
+ tools=[
+ execute_cli_command,
+ ],
+ handoff_description="Specialized agent in Cryptography and code breaking",
+ model=OpenAIChatCompletionsModel(
+ model=os.getenv('CAI_MODEL', "qwen2.5:14b"),
+ openai_client=AsyncOpenAI(),
+ )
+)
+network_agent = Agent(
+ name="Network Agent",
+ description="Agent specialized in network analysis, packet inspection, and network security assessments",
+ instructions="Analyze network traffic, identify suspicious patterns, and help with network-related CTF challenges",
+ handoff_description="Specialized agent in network security, traffic analysis, and protocol understanding",
+ model=OpenAIChatCompletionsModel(
+ model=os.getenv('CAI_MODEL', "qwen2.5:72b"),
+ openai_client=AsyncOpenAI(),
+ )
+)
+lead_agent = Agent(
+ name="Cybersecurity Lead Agent",
+ instructions=(
+ "You are a lead cybersecurity expert coordinating security operations."
+ "If the user needs network analysis or traffic inspection, handoff to the network agent."
+ "If the user needs cryptographic solutions or code breaking, handoff to the crypto agent."
+ ),
+ handoffs=[network_agent, crypto_agent],
+ model="qwen2.5:72b"
+)
+In most cases, you can provide instructions when you create the agent. However, you can also provide dynamic instructions via a function. The function will receive the agent and context, and must return the prompt. Both regular and async functions are accepted.
def dynamic_instructions(
+ context: RunContextWrapper[UserContext], agent: Agent[UserContext]
+) -> str:
+ security_level = "high" if context.context.is_admin else "standard"
+ return f"You are assisting {context.context.name} with cybersecurity operations. Their security clearance level is {security_level}. Tailor your security recommendations appropriately and prioritize addressing their immediate security concerns."
+
+
+agent = Agent[UserContext](
+ name="Cybersecurity Triage Agent",
+ instructions=dynamic_instructions,
+)
+cai
+1. Use streaming for better responsiveness: +
CAI_STREAM=true cai
+CAI_TRACING=true cai
+Don't use a specialized agent for general tasks. Match the agent to your objective:
+# ✅ Good: Using bug bounty agent for web testing
+CAI_AGENT_TYPE=bug_bounter_agent cai
+CAI> Test https://target.com for vulnerabilities
+
+# ❌ Bad: Using reverse engineering agent for web testing
+CAI_AGENT_TYPE=reverse_engineering_agent cai
+CAI> Test https://target.com for vulnerabilities
+Don't hesitate to switch agents mid-session:
+CAI>/agent bug_bounter_agent
+CAI> Find vulnerabilities in the web app
+# ... agent finds SQL injection ...
+
+CAI>/agent redteam_agent
+CAI> Exploit the SQL injection to gain access
+# ... successful exploitation ...
+
+CAI>/agent dfir_agent
+CAI> Analyze what data was exposed during the test
+Keep an eye on costs and performance:
+# During session, check costs
+CAI>/cost
+
+# Set limits before starting
+CAI_PRICE_LIMIT="5.00" CAI_MAX_TURNS=50 cai
+Use /load to reuse successful approaches:
# In future session
+CAI>/load logs/logname.jsonl
+
+
+
+
+ The cai --api mode exposes a stateful HTTP backend built with FastAPI. It uses per-session agents to keep conversation state and REST routes to run REPL commands or send prompts to the model.
cai --api --api-host 0.0.0.0 --api-port 8080
+# If 8080 (or your chosen port) is busy, the server auto-picks
+# the next free port and prints it in the console.
+CLI flags and environment variables:
+| Flag | +Env | +Description | +
|---|---|---|
--api |
+CAI_API_MODE |
+Enable the HTTP backend. | +
--api-host |
+CAI_API_HOST |
+Bind host/interface (default 127.0.0.1). | +
--api-port |
+CAI_API_PORT |
+Bind port (default 8000). | +
--api-reload |
+CAI_API_RELOAD |
+Dev autoreload. | +
--api-workers |
+CAI_API_WORKERS |
+Worker processes (ignored with reload). | +
Interactive docs at /api/docs and OpenAPI spec at /api/openapi.json.
ALIAS_API_KEY as the secret. Set ALIAS_API_KEY and send it in header X-CAI-API-Key (customizable via CAI_API_KEY_HEADER).ALIAS_API_KEY is not set, the API is unprotected (local dev only). For compatibility, CAI_API_KEY is accepted as a fallback.Verbose/auth logging
+- Server logs level: set CAI_API_LOG_LEVEL to debug (or trace) before cai --api.
+- Request logging (method/path/headers/body preview): CAI_API_LOG_REQUESTS=true.
+- Authentication decisions (why 401): CAI_API_LOG_AUTH=true.
+- Dev autoreload: CAI_API_RELOAD=true.
Example: +
ALIAS_API_KEY="your_key" \
+CAI_API_LOG_LEVEL=debug \
+CAI_API_LOG_REQUESTS=true \
+CAI_API_LOG_AUTH=true \
+CAI_API_RELOAD=true \
+cai --api --api-host 0.0.0.0 --api-port 8080
+text/event-stream).Below are the endpoints with request/response examples and headers. For authenticated calls, include:
+X-CAI-API-Key: $ALIAS_API_KEYQuick index +- GET /api/v1/health +- GET /api/v1/commands +- POST /api/v1/commands/{command} +- POST /api/v1/sessions +- GET /api/v1/sessions +- GET /api/v1/sessions/{id} +- DELETE /api/v1/sessions/{id} +- POST /api/v1/sessions/{id}/reset +- POST /api/v1/sessions/{id}/messages +- POST /api/v1/sessions/{id}/messages/stream +- GET /api/v1/sessions/{id}/history +- POST /api/v1/sessions/{id}/interrupt +- POST /api/v1/sessions/{id}/reload +- GET /api/v1/agents +- GET /api/v1/models +- POST /api/v1/sessions/{id}/ux/final_message/stream_tokens +- POST /api/v1/ux/title +- POST /api/v1/ux/summarize
+{"status":"ok","version":"<semver or dev>"}
+X-CAI-API-Key{
+ "commands": [
+ {"name":"/memory","description":"memory ops","aliases":[],"subcommands":["show"]},
+ {"name":"/help","description":"display help","aliases":["/h"],"subcommands":[]}
+ ]
+}
+X-CAI-API-Key, Content-Type: application/json{"args": ["show"], "auto_correct": true}
+{"handled": true, "suggested_command": null, "stdout": "...", "stderr": "", "exit_code": null}
+X-CAI-API-Key, Content-Type: application/json{"agent": "redteam_agent", "model": "alias1", "stateful": true, "metadata": {}}
+X-CAI-API-Key{"sessions": [{"id":"<uuid>","agent":"redteam_agent","model":"alias1","stateful":true,"history_length":0, "created_at":"...","updated_at":"...","metadata":{}}]}
+X-CAI-API-KeyX-CAI-API-KeyX-CAI-API-KeyX-CAI-API-Key{"cancelled": true, "message": "Task in session <id> has been cancelled"}
+or
+{"cancelled": false, "message": "No running task found in session <id>"}
+X-CAI-API-Key, Content-Type: application/json{"input": "List current risks", "context": {"org": "acme"}, "max_turns": 8}
+{
+ "session": {"id": "<uuid>", ...},
+ "result": {
+ "messages": [/* semantic items: messages, tool calls, outputs, ... */],
+ "history": [/* updated message list */],
+ "final_output": {/* typed final output if agent uses an output schema, else string */},
+ "text_output": "<assistant final text, if any>",
+ "input_guardrails": [],
+ "output_guardrails": []
+ }
+}
+X-CAI-API-Key, Content-Type: application/json, Accept: text/event-streamevent: reasoning_step — One event per step with JSON data (examples below).event: final — Final event with { steps, final_message, final_output }.Reasoning step payloads (no token deltas):
+// Message generated by the assistant
+{"type":"message","agent":"Red Team","text":"...full assistant message..."}
+
+// Tool call
+{"type":"tool_call","agent":"Red Team","tool":"nmap_scan","arguments":{"target":"10.0.0.5"}}
+
+// Tool output
+{"type":"tool_output","agent":"Red Team","output":"open ports: 22,80"}
+
+// Agent switch (handoff)
+{"type":"handoff","from_agent":"Coordinator","to_agent":"Exploiter"}
+
+// Explicit agent switch signal
+{"type":"agent_switched","agent":"Exploiter"}
+Final event payload:
+{
+ "steps": [ /* the same reasoning steps emitted during the stream */ ],
+ "final_message": "...last assistant message (if any)...",
+ "final_output": {/* structured output if present, else string/null */}
+}
+Example with curl (SSE):
+curl -N \
+ -H "Accept: text/event-stream" \
+ -H "Content-Type: application/json" \
+ -H "X-CAI-API-Key: $ALIAS_API_KEY" \
+ -d '{"input": "List current risks"}' \
+ http://localhost:8080/api/v1/sessions/<SESSION_ID>/messages/stream
+X-CAI-API-Key, Content-Type: application/json, Accept: text/event-streamevent: token with data { "type": "token_delta", "text": "..." } for each emitted text delta.event: token with data { "type": "message_start" } and { "type": "message_end" } to mark boundaries.event: reasoning_step for high-level steps (same schema as /messages/stream).event: final with the same summary payload as /messages/stream.Notes +- Token streaming can be quite chatty; ensure your client handles backpressure and uses streaming-friendly APIs. +- For iOS, prefer URLSession streaming (see sample below); Safari’s EventSource cannot set custom headers.
+curl example (tokens): +
curl -N \
+ -H "Accept: text/event-stream" \
+ -H "Content-Type: application/json" \
+ -H "X-CAI-API-Key: $ALIAS_API_KEY" \
+ -d '{"input": "Write a haiku about ports"}' \
+ http://localhost:8080/api/v1/sessions/<SESSION_ID>/messages/stream_tokens
+iOS (Swift) streaming example (tokens) +
let sid = "<SESSION_ID>"
+var req = URLRequest(url: URL(string: "http://127.0.0.1:8080/api/v1/sessions/\(sid)/messages/stream_tokens")!)
+req.httpMethod = "POST"
+req.addValue("text/event-stream", forHTTPHeaderField: "Accept")
+req.addValue("application/json", forHTTPHeaderField: "Content-Type")
+req.addValue(ProcessInfo.processInfo.environment["ALIAS_API_KEY"] ?? "", forHTTPHeaderField: "X-CAI-API-Key")
+req.httpBody = try! JSONSerialization.data(withJSONObject: ["input": "Hi"], options: [])
+
+let task = URLSession.shared.streamTask(with: req)
+task.resume()
+task.readData(ofMinLength: 1, maxLength: 8192, timeout: 0) { data, atEOF, error in
+ if let data = data, let s = String(data: data, encoding: .utf8) {
+ // parse SSE lines: event: <name> / data: <json>
+ print(s)
+ }
+}
+Implementation notes (for curious devs) +- API streaming never enables OpenAI chat completions token streaming. Instead: + - We run the agent with non-streaming model calls and emit events via RunHooks (tools start/end, handoffs, agent switches). + - We add one message step after each assistant turn (full text, no token deltas). + - This guarantees that model streaming is always off while still providing live step updates.
+version: string
+CommandMetadata
+subcommands: string[] (e.g., ["show"])
+CommandsResponse
+commands: CommandMetadata[]
+CommandRequest
+auto_correct: boolean (default true)
+CommandResponse
+exit_code: number | null
+CancelTaskResponse
+message: string
+CreateSessionRequest
+metadata: object (optional)
+SessionSummary
+metadata: object
+SessionDetail
+history: ResponseInputItem[] (OpenAI Responses input items list – user/system/assistant/tool items)
+SessionsResponse
+sessions: SessionSummary[]
+InferenceRequest
+max_turns: number (optional)
+RunResultPayload
+output: any (only present for tool_call_output_item; the structured tool return value)
+message_output_item
+text extraction: text_output consolidates last text chunk
+tool_call_item
+typical fields (function call): name, arguments
+tool_call_output_item
+output: any (decoded tool result)
+handoff_output_item
+Final event (event: final) +- steps: the array of emitted reasoning_step payloads +- final_message: string | null +- final_output: any
+X-CAI-API-Key when auth is enabledPython (requests; SSE via iter_lines) +
import json
+import os
+import requests
+
+BASE = "http://127.0.0.1:8080/api/v1"
+HEADERS = {"X-CAI-API-Key": os.environ.get("ALIAS_API_KEY", ""), "Content-Type": "application/json"}
+
+# 1) Create session
+sess = requests.post(f"{BASE}/sessions", headers=HEADERS, json={"agent":"redteam_agent","model":"alias1","stateful":True}).json()
+sid = sess["id"]
+
+# 2) Non-streamed
+res = requests.post(f"{BASE}/sessions/{sid}/messages", headers=HEADERS, json={"input":"List current risks"}).json()
+print(res["result"]["text_output"]) # final message
+
+# 3) Streaming (SSE)
+stream_headers = HEADERS | {"Accept": "text/event-stream"}
+with requests.post(f"{BASE}/sessions/{sid}/messages/stream", headers=stream_headers, json={"input":"List current risks"}, stream=True) as r:
+ for line in r.iter_lines(decode_unicode=True):
+ if not line:
+ continue
+ if line.startswith("event:"):
+ evt = line.split(":", 1)[1].strip()
+ elif line.startswith("data:"):
+ data = json.loads(line.split(":", 1)[1].strip())
+ if evt == "reasoning_step":
+ print("step:", data)
+ elif evt == "final":
+ print("final:", data)
+Node (browser/EventSource) +
const key = process.env.ALIAS_API_KEY;
+const sid = "<SESSION_ID>"; // create via POST /sessions
+const es = new EventSource(`http://localhost:8080/api/v1/sessions/${sid}/messages/stream`, {
+ withCredentials: false
+});
+// Note: To send headers with SSE in the browser, proxy or use fetch+ReadableStream.
+es.addEventListener('reasoning_step', ev => console.log('step', JSON.parse(ev.data)));
+es.addEventListener('final', ev => console.log('final', JSON.parse(ev.data)));
+Node (fetch + ReadableStream; set auth header) +
import fetch from 'node-fetch';
+const key = process.env.ALIAS_API_KEY;
+const sid = process.env.SID;
+const resp = await fetch(`http://localhost:8080/api/v1/sessions/${sid}/messages/stream`, {
+ method: 'POST',
+ headers: { 'Content-Type':'application/json', 'Accept':'text/event-stream', 'X-CAI-API-Key': key },
+ body: JSON.stringify({ input: 'List current risks' })
+});
+for await (const chunk of resp.body) {
+ const s = chunk.toString();
+ // parse SSE lines: event: <name> / data: <json>
+ process.stdout.write(s);
+}
+Best practices
+- Always include Accept: text/event-stream for streaming.
+- Expect multiple reasoning_step events, then exactly one final event.
+- No token deltas are emitted; each message step contains the full assistant message text.
+- Tool calls can be frequent; handle backpressure in your client.
+- Keep your connection timeouts relaxed for long runs.
# Healthcheck
+curl -s http://localhost:8080/api/v1/health
+
+# List agents
+curl -s -H "X-CAI-API-Key: $ALIAS_API_KEY" http://localhost:8080/api/v1/agents | jq .
+
+# List models
+curl -s -H "X-CAI-API-Key: $ALIAS_API_KEY" http://localhost:8080/api/v1/models | jq .
+
+# List commands
+curl -s -H "X-CAI-API-Key: $ALIAS_API_KEY" http://localhost:8080/api/v1/commands
+
+# Run a command
+curl -s -X POST http://localhost:8080/api/v1/commands/memory \
+ -H 'Content-Type: application/json' \
+ -H "X-CAI-API-Key: $ALIAS_API_KEY" \
+ -d '{"args": ["show"]}'
+
+# Create a session
+curl -s -X POST http://localhost:8080/api/v1/sessions \
+ -H 'Content-Type: application/json' \
+ -H "X-CAI-API-Key: $ALIAS_API_KEY" \
+ -d '{"agent": "redteam_agent", "model": "alias1", "stateful": true}'
+
+# Interrupt and reload
+curl -s -X POST -H "X-CAI-API-Key: $ALIAS_API_KEY" \
+ http://localhost:8080/api/v1/sessions/<SESSION_ID>/interrupt
+curl -s -X POST -H "Content-Type: application/json" -H "X-CAI-API-Key: $ALIAS_API_KEY" \
+ -d '{"preserve_history": true}' \
+ http://localhost:8080/api/v1/sessions/<SESSION_ID>/reload
+
+# Send a non-streamed prompt
+curl -s -X POST http://localhost:8080/api/v1/sessions/<SESSION_ID>/messages \
+ -H 'Content-Type: application/json' \
+ -H "X-CAI-API-Key: $ALIAS_API_KEY" \
+ -d '{"input": "List current risks"}'
+
+# Stream reasoning steps (SSE)
+curl -N -X POST http://localhost:8080/api/v1/sessions/<SESSION_ID>/messages/stream \
+ -H 'Content-Type: application/json' \
+ -H 'Accept: text/event-stream' \
+ -H "X-CAI-API-Key: $ALIAS_API_KEY" \
+ -d '{"input": "List current risks"}'
+
+# Reset and delete session
+curl -s -X POST -H "X-CAI-API-Key: $ALIAS_API_KEY" http://localhost:8080/api/v1/sessions/<SESSION_ID>/reset
+curl -s -X DELETE -H "X-CAI-API-Key: $ALIAS_API_KEY" http://localhost:8080/api/v1/sessions/<SESSION_ID>
+examples/cai_api_cli.py — minimal loop: prompts → responses.examples/cai_api_tester.py — interactive menu that covers all endpoints including streaming.cai.agents).X-CAI-API-Key{
+ "agents": [
+ {
+ "name": "redteam_agent",
+ "description": "...",
+ "type": "agent",
+ "pattern_type": null,
+ "tools": [
+ {"name": "nmap_scan", "description": "Scan a host or subnet"},
+ {"name": "http_get", "description": "Fetch a URL"}
+ ]
+ },
+ {
+ "name": "swarm_pattern",
+ "description": "Swarm agentic pattern",
+ "type": "pattern",
+ "pattern_type": "swarm",
+ "tools": []
+ }
+ ]
+}
+pricings/pricing.json if present.X-CAI-API-Key{
+ "models": [
+ {
+ "name": "alias1",
+ "provider": "OpenAI",
+ "category": "Alias",
+ "description": "Best model for Cybersecurity AI tasks",
+ "input_cost": 0.50,
+ "output_cost": 0.50,
+ "pricing": {
+ "input_cost_per_token": 0.000005,
+ "output_cost_per_token": 0.000005,
+ "max_tokens": 128000,
+ "max_input_tokens": 200000,
+ "max_output_tokens": 128000,
+ "supports_function_calling": true,
+ "supports_vision": true,
+ "supports_response_schema": true,
+ "supports_tool_choice": true
+ }
+ }
+ ]
+}
+X-CAI-API-Key{"interrupted": true}
+X-CAI-API-Key, Content-Type: application/json{"preserve_history": true}
+X-CAI-API-Key, Content-Type: application/json, Accept: text/event-stream{
+ "prompt": "Explain to the user what we found and next steps.",
+ "steps": [ /* optional: client-collected steps; otherwise server uses session.last_steps */ ],
+ "include_history": true,
+ "max_turns": 8
+}
+event: token with { "type": "message_start" }event: token with { "type": "token_delta", "text": "..." } repeatedevent: token with { "type": "message_end" }event: reasoning_step may appear if the UX agent emits stepsevent: final with { "steps": [...], "final_message": "...", "final_output": ... }Notes for iOS
+alias1 vía LiteLLM. No usa sesiones.X-CAI-API-Key, Content-Type: application/json{
+ "messages": [
+ {"role": "user", "content": "Analiza CVE-2024-..."}
+ ],
+ "title_hint": "(opcional)"
+}
+{"title": "Analizando CVE-2024-..."}
+alias1 vía LiteLLM. No usa sesiones.X-CAI-API-Key, Content-Type: application/json{
+ "messages": [
+ {"role": "user", "content": "Escanea 10.0.0.5"}
+ ],
+ "steps": [
+ {"type": "tool_call", "agent": "Red Team", "tool": "nmap_scan", "arguments": {"target": "10.0.0.5"}},
+ {"type": "tool_output", "agent": "Red Team"}
+ ],
+ "max_len": 100
+}
+{"summary_text": "Tool output procesado por Red Team"}
+Implementation notes
+- Ambos endpoints fuerzan tool_choice: required con una única función produce_title_and_summary y usan siempre model: alias1 con api_base Alias y ALIAS_API_KEY.
+- El servidor no almacena ni lee estado de sesión.
+- Call this to stream the “final message” of a task. Use a UX prompt tuned to your voice (“Explain briefly in a friendly tone, with next steps”).
+- If you already collected steps client-side, pass them; otherwise the backend uses session.last_steps.
+- Render arriving token_delta chunks into the chat bubble; close on message_end/final.