Configuration Reference
This page documents the SwarmConfig interface used when running swarmtest programmatically, as well as the types that compose it.
SwarmConfig
The top-level configuration object passed to swarm.run().
interface SwarmConfig {
serverUrl: string;
agentCount: number;
adapter: GameAdapter;
tickIntervalMs?: number;
connectStaggerMs?: number;
durationMs?: number;
detectors?: Detector[];
reporters?: Reporter[];
behaviorMix?: {
regression: number;
llmGenerated: number;
handwritten: number;
};
llm?: {
model?: string;
regenerateIntervalMs?: number;
apiKey?: string;
};
treeDir?: string;
trimAfterRun?: boolean;
handwrittenTrees?: TreeNode[];
}Fields
| Field | Type | Default | Description |
|---|---|---|---|
serverUrl | string | (required) | WebSocket URL of the game server |
agentCount | number | (required) | Number of agents to spawn |
adapter | GameAdapter | (required) | Game adapter instance |
tickIntervalMs | number | 100 | Milliseconds between behavior tree ticks |
connectStaggerMs | number | 200 | Milliseconds between agent connections |
durationMs | number | 60000 | Total test duration in milliseconds |
detectors | Detector[] | [] | Array of detector instances |
reporters | Reporter[] | [] | Array of reporter instances |
behaviorMix | object | { regression: 40, llmGenerated: 40, handwritten: 20 } | Proportional weights for behavior assignment |
llm | object | undefined | LLM configuration (omit to disable) |
llm.model | string | claude-haiku-4-5-20251001 | Anthropic model ID |
llm.regenerateIntervalMs | number | 30000 | How often to regenerate trees during a run |
llm.apiKey | string | ANTHROPIC_API_KEY env var | API key override |
treeDir | string | ./trees/<adapter.name>/ | Directory for the tree library |
trimAfterRun | boolean | true | Trim similar trees after the run |
handwrittenTrees | TreeNode[] | [randomWalkTree()] | Custom handwritten behavior trees |
TreeNode
The JSON schema for behavior trees. A discriminated union on the type field.
type TreeNode =
| { type: 'sequence'; children: TreeNode[] }
| { type: 'selector'; children: TreeNode[] }
| { type: 'repeat'; count: number | 'forever'; child: TreeNode }
| { type: 'random_selector'; children: TreeNode[]; weights?: number[] }
| { type: 'action'; name: string; params?: Record<string, unknown> }
| { type: 'wait'; ticks: number }
| { type: 'condition'; predicate: string; then: TreeNode; else?: TreeNode }
| { type: 'probability'; chance: number; child: TreeNode };SavedTree
A persisted behavior tree with metadata, stored as JSON in the tree library.
interface SavedTree {
id: string; // Unique ID (e.g., "tree-1771647225060-tgbfcs")
game: string; // Game adapter name
tree: TreeNode; // The behavior tree definition
source: 'llm' | 'handwritten' | 'recorded';
createdAt: string; // ISO 8601 timestamp
findings: string[]; // IDs of findings this tree triggered
actionSequenceHash: string; // Hash for deduplication
tags: string[]; // Categories from associated findings
}GameAdapter
The interface that game-specific adapters must implement.
interface GameAdapter<TClientMsg = unknown, TServerMsg = unknown, TAgentState = unknown> {
readonly name: string;
connect(url: string, agentId: string): Promise<AgentConnection>;
createConnectMessage(agentId: string, config: AgentConfig): TClientMsg;
createDisconnectMessage?(): TClientMsg | null;
createPingMessage(timestamp: number): TClientMsg;
parseServerMessage(raw: string): TServerMsg;
serializeClientMessage(msg: TClientMsg): string;
isConnectAccepted(msg: TServerMsg): boolean;
isError(msg: TServerMsg): boolean;
getErrorInfo(msg: TServerMsg): { code: string; message: string } | null;
initAgentState(connectResponse: TServerMsg, agentId: string): TAgentState;
updateAgentState(state: TAgentState, msg: TServerMsg): TAgentState;
getAvailableActions(state: TAgentState): AvailableAction[];
buildActionMessage(state: TAgentState, action: string, params?: Record<string, unknown>): TClientMsg | null;
checkInvariants(state: TAgentState): InvariantViolation[];
describeGameContext(): string;
describeAvailableActions(): ActionDescription[];
}AgentConnection
Returned by adapter.connect(). Wraps a WebSocket connection.
interface AgentConnection {
send(data: string): void;
onMessage(handler: (data: string) => void): void;
onClose(handler: (code: number, reason: string) => void): void;
onError(handler: (error: Error) => void): void;
close(): void;
isConnected(): boolean;
}AgentConfig
Configuration for a single agent, passed to the adapter’s createConnectMessage.
interface AgentConfig {
id: string; // e.g., "bot-000"
name: string; // e.g., "S1a2b00"
tickIntervalMs: number;
connectTimeoutMs: number; // Default: 5000
gameConfig?: Record<string, unknown>; // Game-specific config (e.g., starter selection)
}AvailableAction
Describes an action available to an agent in the current game state.
interface AvailableAction {
name: string;
description: string;
params?: Record<string, ParamSpec>;
weight?: number;
}
interface ParamSpec {
type: 'string' | 'number' | 'boolean' | 'enum';
values?: (string | number)[];
min?: number;
max?: number;
}Finding
An issue detected during a test run.
type FindingSeverity = 'crash' | 'bug' | 'jank' | 'warning' | 'info';
interface Finding {
id: string;
severity: FindingSeverity;
category: string;
title: string;
description: string;
agentId: string;
timestamp: number;
context?: { timestamp: number; direction: string; data: string }[];
metadata?: Record<string, unknown>;
}Detector
The interface for detection modules.
interface Detector {
readonly name: string;
onSwarmStart?(): void;
onAgentTick?(agent: AgentHandle): void;
onMessage?(agent: AgentHandle, raw: string, direction: 'sent' | 'received', parsed?: unknown): void;
onAgentConnect?(agent: AgentHandle): void;
onAgentDisconnect?(agent: AgentHandle, code: number, reason: string): void;
onAgentError?(agent: AgentHandle, error: Error): void;
onCrossAgentCheck?(agents: AgentHandle[]): void;
getFindings(): Finding[];
}Reporter
The interface for output reporters.
interface Reporter {
onFinding(finding: Finding): void;
onComplete(summary: SwarmSummary): void;
}SwarmSummary
The complete result of a test run.
interface SwarmSummary {
durationMs: number;
agentCount: number;
connectedCount: number;
failedCount: number;
totalMessagesSent: number;
totalMessagesReceived: number;
totalErrors: number;
findings: Finding[];
findingsBySeverity: Record<string, number>;
agentSummaries: AgentSummary[];
}
interface AgentSummary {
agentId: string;
phase: string;
connected: boolean;
connectFailReason: string | null;
messagesSent: number;
messagesReceived: number;
latencyMs: number;
findings: number;
}