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

FieldTypeDefaultDescription
serverUrlstring(required)WebSocket URL of the game server
agentCountnumber(required)Number of agents to spawn
adapterGameAdapter(required)Game adapter instance
tickIntervalMsnumber100Milliseconds between behavior tree ticks
connectStaggerMsnumber200Milliseconds between agent connections
durationMsnumber60000Total test duration in milliseconds
detectorsDetector[][]Array of detector instances
reportersReporter[][]Array of reporter instances
behaviorMixobject{ regression: 40, llmGenerated: 40, handwritten: 20 }Proportional weights for behavior assignment
llmobjectundefinedLLM configuration (omit to disable)
llm.modelstringclaude-haiku-4-5-20251001Anthropic model ID
llm.regenerateIntervalMsnumber30000How often to regenerate trees during a run
llm.apiKeystringANTHROPIC_API_KEY env varAPI key override
treeDirstring./trees/<adapter.name>/Directory for the tree library
trimAfterRunbooleantrueTrim similar trees after the run
handwrittenTreesTreeNode[][randomWalkTree()]Custom handwritten behavior trees

TreeNode

The JSON schema for behavior trees. A discriminated union on the type field.

type TreeNode =
  | &#123; type: 'sequence'; children: TreeNode[] &#125;
  | &#123; type: 'selector'; children: TreeNode[] &#125;
  | &#123; type: 'repeat'; count: number | 'forever'; child: TreeNode &#125;
  | &#123; type: 'random_selector'; children: TreeNode[]; weights?: number[] &#125;
  | &#123; type: 'action'; name: string; params?: Record&lt;string, unknown&gt; &#125;
  | &#123; type: 'wait'; ticks: number &#125;
  | &#123; type: 'condition'; predicate: string; then: TreeNode; else?: TreeNode &#125;
  | &#123; type: 'probability'; chance: number; child: TreeNode &#125;;

SavedTree

A persisted behavior tree with metadata, stored as JSON in the tree library.

interface SavedTree &#123;
  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
&#125;

GameAdapter

The interface that game-specific adapters must implement.

interface GameAdapter&lt;TClientMsg = unknown, TServerMsg = unknown, TAgentState = unknown&gt; &#123;
  readonly name: string;

  connect(url: string, agentId: string): Promise&lt;AgentConnection&gt;;
  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): &#123; code: string; message: string &#125; | null;

  initAgentState(connectResponse: TServerMsg, agentId: string): TAgentState;
  updateAgentState(state: TAgentState, msg: TServerMsg): TAgentState;
  getAvailableActions(state: TAgentState): AvailableAction[];
  buildActionMessage(state: TAgentState, action: string, params?: Record&lt;string, unknown&gt;): TClientMsg | null;

  checkInvariants(state: TAgentState): InvariantViolation[];
  describeGameContext(): string;
  describeAvailableActions(): ActionDescription[];
&#125;

AgentConnection

Returned by adapter.connect(). Wraps a WebSocket connection.

interface AgentConnection &#123;
  send(data: string): void;
  onMessage(handler: (data: string) =&gt; void): void;
  onClose(handler: (code: number, reason: string) =&gt; void): void;
  onError(handler: (error: Error) =&gt; void): void;
  close(): void;
  isConnected(): boolean;
&#125;

AgentConfig

Configuration for a single agent, passed to the adapter’s createConnectMessage.

interface AgentConfig &#123;
  id: string;                              // e.g., "bot-000"
  name: string;                            // e.g., "S1a2b00"
  tickIntervalMs: number;
  connectTimeoutMs: number;                // Default: 5000
  gameConfig?: Record&lt;string, unknown&gt;;    // Game-specific config (e.g., starter selection)
&#125;

AvailableAction

Describes an action available to an agent in the current game state.

interface AvailableAction &#123;
  name: string;
  description: string;
  params?: Record&lt;string, ParamSpec&gt;;
  weight?: number;
&#125;

interface ParamSpec &#123;
  type: 'string' | 'number' | 'boolean' | 'enum';
  values?: (string | number)[];
  min?: number;
  max?: number;
&#125;

Finding

An issue detected during a test run.

type FindingSeverity = 'crash' | 'bug' | 'jank' | 'warning' | 'info';

interface Finding &#123;
  id: string;
  severity: FindingSeverity;
  category: string;
  title: string;
  description: string;
  agentId: string;
  timestamp: number;
  context?: &#123; timestamp: number; direction: string; data: string &#125;[];
  metadata?: Record&lt;string, unknown&gt;;
&#125;

Detector

The interface for detection modules.

interface Detector &#123;
  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[];
&#125;

Reporter

The interface for output reporters.

interface Reporter &#123;
  onFinding(finding: Finding): void;
  onComplete(summary: SwarmSummary): void;
&#125;

SwarmSummary

The complete result of a test run.

interface SwarmSummary &#123;
  durationMs: number;
  agentCount: number;
  connectedCount: number;
  failedCount: number;
  totalMessagesSent: number;
  totalMessagesReceived: number;
  totalErrors: number;
  findings: Finding[];
  findingsBySeverity: Record&lt;string, number&gt;;
  agentSummaries: AgentSummary[];
&#125;

interface AgentSummary &#123;
  agentId: string;
  phase: string;
  connected: boolean;
  connectFailReason: string | null;
  messagesSent: number;
  messagesReceived: number;
  latencyMs: number;
  findings: number;
&#125;