Zum Inhalt springen
OmniRoute source

ACP (Agent Client Protocol) (Deutsch)

ACP (Agent Client Protocol) ist ein „CLI-as-Backend“-Transport für OmniRoute. Anstatt HTTP-API-Aufrufe an KI-Anbieter abzufangen, startet ACP CLI-Agenten als untergeordnete Prozesse und übermittelt Prompts über deren native Schnittstelle.

Vorteil Beschreibung
Keine API-Schlüssel benötigt Verwendet Ihre bestehende CLI-Authentifizierung
Natives Protokoll Verwendet das native Ein-/Ausgabeformat der jeweiligen CLI
Automatische Erkennung Erkennt auf Ihrem System installierte CLIs
15 integrierte Agenten Für beliebte CLI-Tools vorkonfiguriert
Benutzerdefinierte Agenten Fügen Sie über die Einstellungen eigene CLI-Tools hinzu
Prozessverwaltung Verwaltet den Lebenszyklus (Starten, Senden, Beenden)

ACP unterstützt standardmäßig 15 integrierte CLI-Agenten:

Agenten-ID Anzeigename Binärdatei Protokoll
codex OpenAI Codex CLI codex stdio
claude Claude Code CLI claude stdio
goose Goose CLI goose stdio
openclaw OpenClaw openclaw stdio
aider Aider aider stdio
opencode OpenCode opencode stdio
cline Cline cline stdio
qwen Qwen Code qwen --acp stdio
forge ForgeCode forge stdio
amazon-q Amazon Q Developer q stdio
interpreter Open Interpreter interpreter stdio
cursor-cli Cursor CLI cursor stdio
warp Warp AI warp stdio
gemini Gemini CLI gemini stdio
zcode ZCode zcode stdio

Sie können über die Einstellungen eigene CLI-Agenten hinzufügen. Benutzerdefinierte Agenten unterstützen dieselben Funktionen wie integrierte Agenten.


Terminal-Fenster
# Beispiel: Claude Code CLI installieren
npm install -g @anthropic-ai/claude-code
# Installation überprüfen
claude --version

ACP erkennt auf Ihrem System installierte CLI-Agenten automatisch. Es ist keine Konfiguration erforderlich!

Nach der Erkennung kann ACP als Transport für jeden unterstützten Anbieter verwendet werden. OmniRoute verwendet ACP automatisch, wenn die CLI verfügbar ist.


┌─────────────────┐
│ OmniRoute │
│ (HTTP-Proxy) │
└────────┬────────┘
│
│ spawn()
▼
┌─────────────────┐
│ Untergeordneter │
│ Prozess │
│ (CLI-Agent) │
│ │
│ stdin ◄──────┤ Prompt senden
│ stdout ──────►│ Antwort empfangen
│ stderr ──────►│ Fehler empfangen
└─────────────────┘
  1. Starten — ACP erstellt einen untergeordneten Prozess für den CLI-Agenten
  2. Senden — ACP schreibt Prompts in stdin des Prozesses
  3. Empfangen — ACP liest Antworten aus stdout/stderr
  4. Inaktivitätserkennung — ACP wartet 2 Sekunden ohne Aktivität, bevor die Antwort als vollständig betrachtet wird
  5. Beenden — ACP beendet den Prozess (SIGTERM, anschließend SIGKILL nach 5 Sekunden)

ACP verwendet stdio (Standardeingabe/-ausgabe) für die Kommunikation mit CLI-Agenten. Das Protokoll funktioniert folgendermaßen:

  1. Prompt senden — Mit einem Zeilenumbruch in stdin schreiben
  2. Auf Antwort warten — Aus stdout lesen, bis Inaktivität eintritt (2 Sekunden ohne Ausgabe)
  3. Zeitüberschreitung — Standardmäßig 120 Sekunden (konfigurierbar)

Erkennt alle auf dem System installierten CLI-Agenten. Die Ergebnisse werden 60 Sekunden lang zwischengespeichert.

import { detectInstalledAgents } from "@/lib/acp";
const agents = detectInstalledAgents();
// Gibt zurück: CliAgentInfo[]
interface CliAgentInfo {
id: string; // z. B. "codex", "claude"
name: string; // Anzeigename
binary: string; // Name der auszuführenden Binärdatei
versionCommand: string; // Befehl zur Versionserkennung
version: string | null; // Erkannte Version (null, falls nicht installiert)
installed: boolean; // Gibt an, ob der Agent installiert ist
providerAlias: string; // Anbieter-ID in OmniRoute
spawnArgs: string[]; // Beim Start zu übergebende Argumente
protocol: "stdio" | "http"; // Kommunikationsprotokoll
isCustom?: boolean; // Gibt an, ob dies ein benutzerdefinierter Agent ist
}

Ruft nur die Agenten ab, die installiert und für ACP verfügbar sind.

import { getAvailableAgents } from "@/lib/acp";
const available = getAvailableAgents();
// Gibt zurück: CliAgentInfo[] (nur installierte Agenten)

Ruft einen bestimmten Agenten anhand seiner ID ab.

import { getAgentById } from "@/lib/acp";
const agent = getAgentById("claude");
// Gibt zurück: CliAgentInfo | undefined

Legt benutzerdefinierte Agentendefinitionen aus den Einstellungen fest.

import { setCustomAgents } from "@/lib/acp";
setCustomAgents([
{
id: "my-custom-cli",
name: "My Custom CLI",
binary: "mycli",
versionCommand: "mycli --version",
providerAlias: "my-provider",
spawnArgs: [],
protocol: "stdio",
},
]);

Startet einen neuen CLI-Agentenprozess.

import { acpManager } from "@/lib/acp";
const session = acpManager.spawn("claude", "claude", ["--print", "--output-format", "json"], {
/* benutzerdefinierte Umgebungsvariablen */
});
// Gibt zurück: AcpSession

Zulässige Agenten-IDs: ["claude", "codex", "gemini", "qwen"]

acpManager.sendPrompt(sessionId, prompt, timeoutMs)

Abschnitt betitelt „acpManager.sendPrompt(sessionId, prompt, timeoutMs)“

Sendet eine Eingabeaufforderung an einen CLI-Agenten und erfasst die Antwort.

import { acpManager } from "@/lib/acp";
const response = await acpManager.sendPrompt(
"acp-claude-1234567890-abc123",
"What is 2+2?",
120000 // Zeitüberschreitung nach 2 Minuten
);
// Gibt zurück: Promise<string>

Beendet eine Sitzung und führt die Bereinigung durch.

import { acpManager } from "@/lib/acp";
const killed = acpManager.kill("acp-claude-1234567890-abc123");
// Gibt zurück: boolean

Ruft alle aktiven Sitzungen ab.

import { acpManager } from "@/lib/acp";
const sessions = acpManager.getActiveSessions();
// Gibt zurück: AcpSession[]

Beendet alle Sitzungen.

import { acpManager } from "@/lib/acp";
acpManager.killAll();
interface AcpSession {
id: string; // Eindeutige Sitzungs-ID
agentId: string; // Agenten-ID (z. B. "claude")
process: ChildProcess; // Handle des untergeordneten Prozesses
alive: boolean; // Gibt an, ob der Prozess aktiv ist
stdoutBuffer: string; // Akkumulierter stdout-Puffer
stderrBuffer: string; // Akkumulierter stderr-Puffer
createdAt: Date; // Erstellungszeitstempel
}

Der AcpManager erweitert EventEmitter und löst die folgenden Ereignisse aus:

Wird ausgelöst, wenn der CLI-Agent in stdout schreibt.

acpManager.on("stdout", ({ sessionId, data }) => {
console.log(`[${sessionId}] stdout: ${data}`);
});

Wird ausgelöst, wenn der CLI-Agent in stderr schreibt.

acpManager.on("stderr", ({ sessionId, data }) => {
console.error(`[${sessionId}] stderr: ${data}`);
});

Wird ausgelöst, wenn der CLI-Agentenprozess beendet wird.

acpManager.on("exit", ({ sessionId, code, signal }) => {
console.log(`[${sessionId}] exited with code ${code}, signal ${signal}`);
});

Wird ausgelöst, wenn beim CLI-Agentenprozess ein Fehler auftritt.

acpManager.on("error", ({ sessionId, error }) => {
console.error(`[${sessionId}] error: ${error}`);
});

ACP übernimmt alle Umgebungsvariablen des übergeordneten Prozesses und kann um benutzerdefinierte Umgebungsvariablen erweitert werden:

acpManager.spawn("claude", "claude", [], {
ANTHROPIC_API_KEY: "sk-...",
DEBUG: "true",
});

Jeder Agent verfügt über standardmäßige Startargumente, die in der Registry definiert sind. Sie können diese überschreiben:

acpManager.spawn("claude", "claude", ["--print", "--verbose"], {});

Die standardmäßige Zeitüberschreitung für Prompts beträgt 120 Sekunden (2 Minuten). Sie können sie überschreiben:

await acpManager.sendPrompt(sessionId, prompt, 300000); // 5 Minuten

Die Agentenerkennung wird für 60 Sekunden zwischengespeichert, um aufwendige Dateisystem-Scans zu vermeiden. So erzwingen Sie eine Aktualisierung:

import { refreshAgentCache } from "@/lib/acp";
refreshAgentCache();

ACP validiert Versionsbefehle, um Angriffe durch Befehlsinjektion zu verhindern:

const DISALLOWED_VERSION_COMMAND_CHARS = /[;&|<>`$\r\n]/;

Versionsbefehle, die diese Zeichen enthalten, werden abgelehnt:

  • ; — Befehlstrennzeichen
  • & — Hintergrundprozess
  • | — Pipe
  • <, > — Umleitung
  • ` — Befehlsersetzung
  • $ — Variablenexpansion
  • \r, \n — Zeilenumbrüche

ACP validiert, dass die Binärdatei des Versionsbefehls dem erwarteten Binärdateinamen entspricht (außer bei benutzerdefinierten Agenten).

Jede ACP-Sitzung wird in einem eigenen untergeordneten Prozess ausgeführt. Der Prozess wird beendet, wenn die Sitzung endet oder das Zeitlimit überschritten wird.


  • Erster Aufruf: ~50-200ms (führt für jeden Agenten den Befehl version aus)
  • Zwischengespeicherte Aufrufe: <1ms (Rückgabe aus dem Cache)
  • Cache-TTL: 60 Sekunden
  • Starten: ~50-100ms
  • Prompt senden: ~10-50ms
  • Auf Antwort warten: Hängt vom CLI-Agenten ab (typischerweise 1-30 Sekunden)
  • Beenden: ~5 Sekunden (SIGTERM) + sofort (SIGKILL)
  • Arbeitsspeicher pro Sitzung: ~10-50MB (hängt vom CLI-Agenten ab)
  • CPU: Minimal (E/A-gebunden)
  • Festplatte: Keine

Problem: acpManager.spawn() löst Unknown agent: &lt;id&gt; aus

Lösung: In spawn() sind nur diese Agenten zulässig:

  • claude
  • codex
  • gemini
  • qwen

Andere Agenten müssen manuell oder über benutzerdefinierte Agentendefinitionen gestartet werden.

Problem: acpManager.sendPrompt() löst Session ${sessionId} is not alive aus

Lösung: Die Sitzung wurde möglicherweise beendet oder abgebrochen. Überprüfen Sie den Sitzungsstatus:

const session = acpManager.getSession(sessionId);
if (!session?.alive) {
// Sitzung erneut starten
acpManager.spawn("claude", "claude", [], {});
}

Problem: acpManager.sendPrompt() löst ACP timeout after 120000ms aus

Lösung: Erhöhen Sie die Zeitüberschreitung:

await acpManager.sendPrompt(sessionId, prompt, 300000); // 5 Minuten

Problem: detectInstalledAgents() findet Ihre CLI nicht

Lösungen:

  1. PATH überprüfen: Stellen Sie sicher, dass sich die CLI im PATH Ihres Systems befindet
  2. Versionsbefehl überprüfen: Führen Sie claude --version manuell aus
  3. Berechtigungen überprüfen: Stellen Sie sicher, dass die CLI ausführbar ist
  4. Benutzerdefinierter Agent: Fügen Sie eine benutzerdefinierte Agentendefinition für nicht standardmäßige CLIs hinzu

Problem: ACP kann die CLI nicht ausführen

Lösungen:

  1. Dateiberechtigungen überprüfen: chmod +x /usr/local/bin/claude
  2. Eigentümer überprüfen: Stellen Sie sicher, dass OmniRoute über Lese- und Ausführungsberechtigungen verfügt
  3. SELinux/AppArmor überprüfen: Kann das Starten von Prozessen blockieren

import { acpManager, detectInstalledAgents } from "@/lib/acp";
// Installierte Agenten erkennen
const agents = detectInstalledAgents();
const claude = agents.find((a) => a.id === "claude");
if (claude?.installed) {
// Eine neue Sitzung starten
const session = acpManager.spawn("claude", claude.binary, ["--print", "--output-format", "json"]);
// Einen Prompt senden
const response = await acpManager.sendPrompt(
session.id,
"Explain quantum computing in 100 words"
);
console.log("Claude's response:", response);
// Ressourcen bereinigen
acpManager.kill(session.id);
}

Beispiel 2: Automatische Erkennung mit Ausweichlösung

Abschnitt betitelt „Beispiel 2: Automatische Erkennung mit Ausweichlösung“
import { acpManager, getAvailableAgents } from "@/lib/acp";
const available = getAvailableAgents();
// Zuerst Claude versuchen, andernfalls auf Codex ausweichen
let agentId = "claude";
if (!available.find((a) => a.id === "claude")) {
if (available.find((a) => a.id === "codex")) {
agentId = "codex";
} else {
throw new Error("No ACP-compatible CLI agent found");
}
}
const agent = available.find((a) => a.id === agentId)!;
const session = acpManager.spawn(agentId, agent.binary, agent.spawnArgs);
const response = await acpManager.sendPrompt(session.id, "Hello!");
acpManager.kill(session.id);
import { setCustomAgents, detectInstalledAgents } from "@/lib/acp";
// Einen benutzerdefinierten CLI-Agenten registrieren
setCustomAgents([
{
id: "my-llm-cli",
name: "My LLM CLI",
binary: "myllm",
versionCommand: "myllm --version",
providerAlias: "my-llm-provider",
spawnArgs: ["--format", "json"],
protocol: "stdio",
},
]);
// detectInstalledAgents() enthält nun auch "my-llm-cli"
const agents = detectInstalledAgents();


  • AionUi-Projekt — Inspiration für die automatische ACP-Erkennung
  • ACP-Quellcode — Implementierungsdetails
    • manager.ts — Verwaltung des Prozesslebenszyklus
    • registry.ts — Erkennung und Registrierung von Agenten
    • index.ts — Exporte der öffentlichen API

OmniRoute-Quellcode (a58000c7685f)

HagiCode

HagiCode ist ein agentischer Coding-Arbeitsplatz mit strukturierten Workflows, Multi-Agent-Ausführung und Hero-Dungeon-Ansichten.

Mit einem intelligenteren, schnelleren und unterhaltsameren agentischen Workflow wird aus Ideen nutzbare Software.

HagiCode-Hauptoberfläche im hellen Design
  • SmartStrukturierte Workflows machen aus Absichten einen umsetzbaren Weg von der Idee bis zur Auslieferung.
  • EfficientMulti-Agent-Workflows führen Recherche, Umsetzung und Prüfung parallel aus.
  • FunHero Dungeon macht lange Coding-Sitzungen anschaulich und gemeinschaftlich.
HagiCode besuchen