Skip to main content
Version: 1.2.0

Multi-Agent Orchestration

Since 1.2.0, ClaudusBridge treats one editor as a shared workbench for a whole fleet of AI agents. The Orchestration toolset (ClaudusBridgeEditor.ClaudusBridgeOrchestrationToolset) adds the three things a fleet needs that plain MCP calls cannot provide: background jobs, agent identity, and asset locks.

Why it exists

A long tool call — a Niagara compile wait, a big save, a batch migration — executes on the editor's game thread while its HTTP request stays open. Every other agent connected to the same editor stalls behind it, and the client's HTTP timeout (often ~120 s) kills the call even though the editor finishes the work. With two or more agents (verified with 20 concurrent sessions) working on different assets, that serialization is the #1 pain.

The job queue

RunToolAsync(ToolsetName, ToolName, ArgumentsJson, AgentName, Priority) queues any toolset tool as a background job and returns a jobId immediately:

{ "method": "tools/call", "params": { "name": "call_tool", "arguments": {
"toolset_name": "ClaudusBridgeEditor.ClaudusBridgeOrchestrationToolset",
"tool_name": "RunToolAsync",
"arguments": {
"ToolsetName": "ClaudusBridgeEditor.ClaudusBridgeNiagaraSystemToolset",
"ToolName": "WaitNiagaraCompile",
"ArgumentsJson": "{\"SystemPath\":\"/Game/FX/NS_MyEffect\"}",
"AgentName": "claude-vfx"
}
} } }
  • One job runs at a time (engine mutations are game-thread bound either way); at most one job starts per editor tick, so other agents' fast calls interleave between jobs instead of starving behind a drain loop.
  • Queued jobs run priority-first, FIFO within a priority.
  • Poll GetJobStatus(jobId) — it returns the state, elapsed seconds, and your queuePosition (−1 when not queued). Poll at 0.5–1 s intervals, not hot loops; use ListJobs(AgentName) to batch-check several jobs in one call.
  • Fetch GetJobResult(jobId) when the state is Succeeded / Failed. The result field is the raw toolset JSON (typically {"returnValue":"<json string>"}) — unwrap returnValue and parse, exactly like a call_tool response.
  • Finished jobs stay queryable for ~30 minutes (max 200 retained).
  • CancelJob works on queued jobs only; a running engine call is not abortable. A 30-minute watchdog fails a running job whose tool future never completes, so a wedged tool can never block the fleet.
  • Do not queue interactive/human-gated tools (anything that opens a dialog) — they hold the job slot until the watchdog fails them.

Agent sessions

RegisterAgentSession(AgentName, Purpose) once at session start — pick a short stable name ("claude-waves", "codex-ui"). ListAgentSessions shows every registered agent, its purpose, last-seen time, and jobs submitted, so agents (and you) can see who is working on what. EndAgentSession removes the entry.

Sessions are advisory identity only — they gate nothing by themselves.

Advisory asset locks

Before mutating an asset, acquire a lock:

AcquireAssetLock(AssetPath, AgentName, LeaseSeconds, Note)
  • Locks are cooperative (advisory): the protocol is check/acquire before edits. They gate nothing by force.
  • Keys are normalized package paths/Game/X and /Game/X.X are the same lock.
  • Re-acquiring a lock you hold renews the lease. Another agent's unexpired lock fails with {heldBy, note, expiresInSeconds} — wait and retry, or coordinate.
  • Leases auto-expire (default 300 s), so a crashed agent never wedges the fleet. Keep leases short and scoped to the asset actually being mutated.
  • ReleaseAssetLock(AssetPath, AgentName, bForce)bForce=true breaks a stale lock and reports whose lock was broken.
  • ListAssetLocks shows every active lock with seconds until expiry.

The busy-state dashboard

GetEditorBusyState() returns, in one cheap call: last game-thread frame time, shader compiles remaining, job-queue depth, the running job, registered agents, and active locks. Poll it before deciding whether to fire a heavy call directly or queue it with RunToolAsync.

  1. RegisterAgentSession once.
  2. AcquireAssetLock before mutating an asset; release when done.
  3. RunToolAsync for anything that can take more than ~10 seconds (compile waits, saves, batch edits); poll politely.
  4. GetEditorBusyState before heavy direct calls.

The editor also ships this protocol as the ClaudusBridge_MultiAgentOrchestration Agent Skill, so any agent that connects can discover it via ListSkills.

Fleet-scale HTTP

The engine HTTP server defaults accept one connection per editor frame with a backlog of 16 — enough for one occasional client, not for a fleet. Since 1.2.0 the bridge raises both (backlog 128, accepts/frame 32) before its listener starts. User-configured [HTTPServer.Listeners] values in Engine.ini are respected, and the plugin-written values are removed after bind so they never persist into the project's saved config. Measured on a 20-agent burst: connection refusals 4/20 → 0, wall time 77.8 s → 11.5 s, fast-call p50 5.7 s → 334 ms.

If you plan fleets far beyond ~100 concurrent clients, set explicit DefaultConnectionsBacklogSize / DefaultMaxConnectionsAcceptPerFrame overrides under [HTTPServer.Listeners] in Engine.ini.

Schemas mark every string parameter required

UFUNCTION string parameters remain required in the generated tool schemas even when they carry C++ defaults — pass every string argument explicitly (e.g. Note on AcquireAssetLock, AgentName on RunToolAsync).