ACP Drive Path
The ClaudusCode terminal is the interactive face of ClaudusBridge. The ACP
drive path is its programmatic counterpart: a native C++ client,
FClaudusAcpClient, that drives the very same embedded agent without a
terminal, PTY, or TUI — straight C++ to agent, over the
Agent Client Protocol.
Use it when you want to script the agent from your own editor tooling: kick off a prompt from a button, react to streamed assistant text and tool calls in C++, gate the agent's actions behind your own permission UI, or run the agent headless as part of an automated workflow.
How it differs from the terminal
| ClaudusCode terminal | ACP drive path | |
|---|---|---|
| Transport | OS pseudo-terminal (ConPTY / forkpty) | node cli-node.js --acp, JSON-RPC over stdio |
| Surface | Rendered TUI in a Slate tab | Delegates + console commands; no UI |
| Input | Keystrokes you type | SendPrompt(...) calls |
| Output | Painted terminal cells | OnAgentText / OnToolCall delegates |
| Use case | A human chatting with the agent | C++ code driving the agent |
Both launch the same bundled agent (see the Embedded Agent Bundle page) — the ACP path simply speaks a structured protocol instead of a terminal.
What FClaudusAcpClient does
FClaudusAcpClient (in the ClaudusCode module) spawns
node "<Resources/Agent/cli-node.js>" --acp and:
- Speaks JSON-RPC 2.0, newline-delimited (ndjson), over the child process's stdio pipes.
- Performs the
initialize+session/newhandshake. - Forwards your prompts as ACP prompt turns.
- Streams the agent's reply back through delegates: assistant text chunks
(
agent_message_chunk) and tool calls (tool_call). - Answers the agent's permission requests according to the configured permission mode (or your own hook).
It is created, owned, and ticked on the game thread via the core ticker, and is not thread-safe — drive it from the game thread.
Configuration
Start() takes an FConfig:
| Field | Meaning | Empty default |
|---|---|---|
NodeExePath | Absolute path to the node executable. | Resolve node from PATH. |
AgentScriptPath | Absolute path to cli-node.js. | The plugin's Resources/Agent/cli-node.js. |
WorkingDir | Session working directory. | The Unreal project directory. |
PermissionMode | ACP permission-mode seed (see below). | The agent's own default. |
Model | Model id (e.g. opus, claude-opus-4-8, gpt-5.5). | The agent's default model. |
Permission modes
The permission mode controls whether — and how — the agent is allowed to take actions (run tools, edit files, call MCP tools on the editor):
default— the safe mode. With no permission UI bound, the client denies tool-permission requests, so the agent can converse and read but cannot execute. Good for read-only exploration.acceptEdits— auto-accept file edits.bypassPermissions— let the agent act autonomously without prompting.- Additional seeds (
dontAsk,plan,auto) are passed through to the agent.
For finer control, bind the PermissionRequest delegate. It receives the
tool title and the offered ACP option ids (e.g. allow_always, allow,
reject) and returns the chosen option id — or an empty string to cancel. If you
do not bind it, the client auto-allows once. This is the hook to wire up your own
in-editor "Allow / Deny" dialog.
Delegates
Subscribe to these multicast delegates on the client to react to the agent:
| Delegate | Fires when |
|---|---|
OnSessionReady | The session is established and prompts run immediately. |
OnAgentText(Text) | A streamed chunk of assistant text arrives. |
OnToolCall(Title, RawInputJson) | The agent invokes a tool (human title + condensed JSON input). |
OnTurnComplete(StopReason) | The current prompt turn finished (end_turn, cancelled, error, …). |
OnStopped(Reason) | The client stopped (process exit, error, or Stop()). |
PermissionRequest(Title, OptionIds) | (return-value) The agent asks permission; you choose the option. |
Prompts sent before OnSessionReady are queued and flushed once the session
is up, so you can call SendPrompt immediately after Start without racing the
handshake. CancelTurn() cancels the in-flight turn via an ACP session/cancel
notification.
Console commands
The subsystem (UClaudusCodeSubsystem) exposes the ACP path through console
commands, handy for trying it out from the editor's command line or for a quick
test harness:
ClaudusCode.Acp.Start [permissionMode]
ClaudusCode.Acp.Prompt <your prompt text…>
ClaudusCode.Acp.Stop
ClaudusCode.Acp.Start— starts the agent. The optional argument is the permission mode; it defaults todefault(deny tools — converse/read only). PassbypassPermissionsoracceptEditsto let it act.ClaudusCode.Acp.Prompt— sends a prompt; remaining args are joined with spaces. Queued until the session is ready.ClaudusCode.Acp.Stop— stops the agent and releases the process.
While running through these commands, streamed text, tool calls, turn completion,
and stop events are surfaced to the Output Log (prefixed [ClaudusAcp]), so
you can watch the agent work without any UI. A richer in-engine UI can subscribe
to the same delegates.
Driving it from C++
UClaudusCodeSubsystem* Sub = GEditor->GetEditorSubsystem<UClaudusCodeSubsystem>();
// Start the agent with autonomous permissions.
Sub->StartAcpAgent(TEXT("bypassPermissions"));
// React to streamed output.
if (FClaudusAcpClient* Acp = Sub->GetAcpClient())
{
Acp->OnAgentText.AddLambda([](const FString& Text)
{
UE_LOG(LogTemp, Display, TEXT("Agent: %s"), *Text);
});
Acp->OnTurnComplete.AddLambda([](const FString& StopReason)
{
UE_LOG(LogTemp, Display, TEXT("Turn done: %s"), *StopReason);
});
}
// Prompt it (queued until the session is ready).
Sub->SendAcpPrompt(TEXT("List the actors in the current level and tidy up their names."));
Stop() (or StopAcpAgent()) cancels any active turn, closes the session,
terminates the process, and releases resources; it is idempotent and safe to call
on shutdown.
Verifying the agent responds to ACP
You can sanity-check the bundle's ACP support directly with your system Node, exactly as the plugin invokes it:
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":1,"clientCapabilities":{}}}' | node cli-node.js --acp
A successful run returns a JSON-RPC initialize result with the agent's
capabilities. See the Embedded Agent Bundle page for where cli-node.js lives
and what it requires.