ACP-threadgebundene Agents
Überblick
Dieser Plan definiert, wie OpenClaw ACP-Coding-Agents in threadfähigen Kanälen (Discord zuerst) mit produktionsreifem Lifecycle und Recovery unterstützen soll.
Verwandtes Dokument:
Angestrebte Benutzererfahrung:
- Ein Benutzer spawnt oder fokussiert eine ACP-Session in einem Thread
- Benutzernachrichten in diesem Thread werden an die gebundene ACP-Session geroutet
- Agent-Output wird in derselben Thread-Persona zurückgestreamt
- Die Session kann persistent oder einmalig sein, mit expliziten Cleanup-Steuerungen
Entscheidungszusammenfassung
Langfristige Empfehlung ist eine hybride Architektur:
- OpenClaw-Kern steuert ACP-Steuerungsebenen-Belange
- Session-Identität und Metadaten
- Thread-Binding und Routing-Entscheidungen
- Zustellungsinvarianten und Duplikat-Unterdrückung
- Lifecycle-Bereinigung und Recovery-Semantik
- ACP-Laufzeit-Backend ist austauschbar
- Erstes Backend ist ein acpx-gestützter Plugin-Service
- Runtime erledigt ACP-Transport, Queuing, Abbruch, Reconnect
OpenClaw sollte ACP-Transport-Interna nicht im Kern nachimplementieren. OpenClaw sollte sich für Routing nicht auf einen reinen Plugin-only-Interception-Pfad verlassen.
Zielarchitektur (Holy Grail)
ACP als erstklassige Steuerungsebene in OpenClaw behandeln, mit austauschbaren Laufzeit-Adaptern.
Unverrückbare Invarianten:
- Jedes ACP-Thread-Binding referenziert einen gültigen ACP-Session-Datensatz
- Jede ACP-Session hat einen expliziten Lifecycle-Status (
creating,idle,running,cancelling,closed,error) - Jeder ACP-Run hat einen expliziten Run-Status (
queued,running,completed,failed,cancelled) - Spawn, Bind und initiales Enqueue sind atomar
- Befehlswiederholungen sind idempotent (keine doppelten Runs oder Discord-Ausgaben)
- Threadgebundene Kanalausgabe ist eine Projektion von ACP-Run-Events, niemals Ad-hoc-Seiteneffekte
Langfristiges Ownership-Modell:
AcpSessionManagerist der einzige ACP-Writer und Orchestrator- Manager lebt zunächst im Gateway-Prozess; kann später hinter derselben Schnittstelle in einen dedizierten Sidecar verschoben werden
- Pro ACP-Session-Key besitzt der Manager einen In-Memory-Actor (serialisierte Befehlsausführung)
- Adapter (
acpx, zukünftige Backends) sind nur Transport-/Runtime-Implementierungen
Langfristiges Persistenzmodell:
- ACP-Steuerungsebenen-Zustand in eine dedizierte SQLite-Datenbank (WAL-Modus) unter dem OpenClaw-State-Verzeichnis verschieben
SessionEntry.acpals Kompatibilitätsprojektion während der Migration beibehalten, nicht als Source of Truth- ACP-Events append-only speichern, um Replay, Crash-Recovery und deterministische Zustellung zu unterstützen
Zustellungsstrategie (Brücke zum Holy Grail)
- Kurzfristige Brücke
- Aktuelle Thread-Binding-Mechaniken und bestehende ACP-Konfigurationsoberfläche beibehalten
- Metadaten-Lücken-Bugs beheben und ACP-Turns durch einen einzigen Core-ACP-Zweig leiten
- Idempotenz-Schlüssel und Fail-Closed-Routing-Prüfungen sofort hinzufügen
- Langfristiger Umstieg
- ACP-Source-of-Truth in Steuerungsebenen-DB + Actors verschieben
- Threadgebundene Zustellung rein auf Event-Projektion basieren lassen
- Legacy-Fallback-Verhalten entfernen, das auf opportunistische Session-Entry-Metadaten angewiesen ist
Warum nicht rein Plugin-only
Aktuelle Plugin-Hooks reichen nicht für durchgehendes ACP-Session-Routing ohne Kernänderungen.
- Eingehendes Routing aus Thread-Binding löst zuerst im Core-Dispatch zu einem Session-Key auf
- Message-Hooks sind fire-and-forget und können den Haupt-Reply-Pfad nicht kurzschließen
- Plugin-Befehle sind gut für Steuerungsoperationen, aber nicht zum Ersetzen des Core-Per-Turn-Dispatch-Flows
Ergebnis:
- ACP-Runtime kann als Plugin implementiert werden
- ACP-Routing-Zweig muss im Kern existieren
Bestehende Grundlagen zur Wiederverwendung
Bereits implementiert und kanonisch:
- Thread-Binding-Ziel unterstützt
subagentundacp - Eingehendes Thread-Routing-Override wird vor normalem Dispatch per Binding aufgelöst
- Ausgehende Thread-Identität über Webhook in der Reply-Zustellung
/focus- und/unfocus-Flow mit ACP-Ziel-Kompatibilität- Persistenter Binding-Store mit Wiederherstellung beim Start
- Unbind-Lifecycle bei Archivierung, Löschung, Unfocus, Reset und Delete
Dieser Plan erweitert diese Grundlage, anstatt sie zu ersetzen.
Architektur
Grenzmodell
Kern (muss im OpenClaw-Kern sein):
- ACP-Session-Modus-Dispatch-Zweig in der Reply-Pipeline
- Zustellungs-Arbitrierung, um Duplikate zwischen Parent und Thread zu vermeiden
- ACP-Steuerungsebenen-Persistenz (mit
SessionEntry.acp-Kompatibilitätsprojektion während der Migration) - Lifecycle-Unbind und Runtime-Detach-Semantik gekoppelt an Session-Reset/-Delete
Plugin-Backend (acpx-Implementierung):
- ACP-Runtime-Worker-Supervision
- acpx-Prozessaufruf und Event-Parsing
- ACP-Befehlshandler (
/acp ...) und Operator-UX - Backend-spezifische Konfigurationsstandards und Diagnose
Runtime-Ownership-Modell
- Ein Gateway-Prozess besitzt den ACP-Orchestrierungszustand
- ACP-Ausführung läuft in überwachten Kindprozessen über das acpx-Backend
- Prozessstrategie ist langlebig pro aktivem ACP-Session-Key, nicht pro Nachricht
Dies vermeidet Startup-Kosten bei jeder Eingabe und hält Cancel- und Reconnect-Semantik zuverlässig.
Core-Runtime-Vertrag
Einen Core-ACP-Runtime-Vertrag hinzufügen, damit Routing-Code nicht von CLI-Details abhängt und Backends ohne Dispatch-Logik-Änderungen wechseln kann:
export type AcpRuntimePromptMode = "prompt" | "steer";
export type AcpRuntimeHandle = {
sessionKey: string;
backend: string;
runtimeSessionName: string;
};
export type AcpRuntimeEvent =
| { type: "text_delta"; stream: "output" | "thought"; text: string }
| { type: "tool_call"; name: string; argumentsText: string }
| { type: "done"; usage?: Record<string, number> }
| { type: "error"; code: string; message: string; retryable?: boolean };
export interface AcpRuntime {
ensureSession(input: {
sessionKey: string;
agent: string;
mode: "persistent" | "oneshot";
cwd?: string;
env?: Record<string, string>;
idempotencyKey: string;
}): Promise<AcpRuntimeHandle>;
submit(input: {
handle: AcpRuntimeHandle;
text: string;
mode: AcpRuntimePromptMode;
idempotencyKey: string;
}): Promise<{ runtimeRunId: string }>;
stream(input: {
handle: AcpRuntimeHandle;
runtimeRunId: string;
onEvent: (event: AcpRuntimeEvent) => Promise<void> | void;
signal?: AbortSignal;
}): Promise<void>;
cancel(input: {
handle: AcpRuntimeHandle;
runtimeRunId?: string;
reason?: string;
idempotencyKey: string;
}): Promise<void>;
close(input: { handle: AcpRuntimeHandle; reason: string; idempotencyKey: string }): Promise<void>;
health?(): Promise<{ ok: boolean; details?: string }>;
}
Implementierungsdetail:
- Erstes Backend:
AcpxRuntime, ausgeliefert als Plugin-Service - Kern löst Runtime über Registry auf und schlägt mit explizitem Operator-Fehler fehl, wenn kein ACP-Runtime-Backend verfügbar ist
Steuerungsebenen-Datenmodell und Persistenz
Langfristige Source of Truth ist eine dedizierte ACP-SQLite-Datenbank (WAL-Modus) für transaktionale Updates und crashsichere Recovery:
acp_sessionssession_key(pk),backend,agent,mode,cwd,state,created_at,updated_at,last_error
acp_runsrun_id(pk),session_key(fk),state,requester_message_id,idempotency_key,started_at,ended_at,error_code,error_message
acp_bindingsbinding_key(pk),thread_id,channel_id,account_id,session_key(fk),expires_at,bound_at
acp_eventsevent_id(pk),run_id(fk),seq,kind,payload_json,created_at
acp_delivery_checkpointrun_id(pk/fk),last_event_seq,last_discord_message_id,updated_at
acp_idempotencyscope,idempotency_key,result_json,created_at, unique(scope, idempotency_key)
export type AcpSessionMeta = {
backend: string;
agent: string;
runtimeSessionName: string;
mode: "persistent" | "oneshot";
cwd?: string;
state: "idle" | "running" | "error";
lastActivityAt: number;
lastError?: string;
};
Speicherregeln:
SessionEntry.acpals Kompatibilitätsprojektion während der Migration beibehalten- Prozess-IDs und Sockets bleiben nur im Speicher
- Dauerhafter Lifecycle- und Run-Status lebt in der ACP-DB, nicht im generischen Session-JSON
- Wenn der Runtime-Owner stirbt, rehydriert das Gateway aus der ACP-DB und setzt ab Checkpoints fort
Routing und Zustellung
Eingehend:
- Aktuelles Thread-Binding-Lookup als ersten Routing-Schritt beibehalten
- Wenn gebundenes Ziel eine ACP-Session ist, zum ACP-Runtime-Zweig routen statt zu
getReplyFromConfig - Expliziter
/acp steer-Befehl nutztmode: "steer"
Ausgehend:
- ACP-Event-Stream wird in OpenClaw-Reply-Chunks normalisiert
- Zustellungsziel wird über den bestehenden gebundenen Zielpfad aufgelöst
- Wenn ein gebundener Thread für diesen Session-Turn aktiv ist, wird die Parent-Kanal-Vervollständigung unterdrückt
Streaming-Policy:
- Teilausgabe mit Koaleszenzfenster streamen
- Konfigurierbares Mindestintervall und maximale Chunk-Bytes, um unter Discord-Ratenlimits zu bleiben
- Abschlussnachricht wird immer bei Vervollständigung oder Fehler gesendet
Zustandsmaschinen und Transaktionsgrenzen
Session-Zustandsmaschine:
creating -> idle -> running -> idlerunning -> cancelling -> idle | erroridle -> closederror -> idle | closed
Run-Zustandsmaschine:
queued -> running -> completedrunning -> failed | cancelledqueued -> cancelled
Erforderliche Transaktionsgrenzen:
- Spawn-Transaktion
- ACP-Session-Zeile erstellen
- ACP-Thread-Binding-Zeile erstellen/aktualisieren
- Initialen Run-Eintrag enqueuen
- Close-Transaktion
- Session als geschlossen markieren
- Binding-Zeilen löschen/ablaufen lassen
- Finales Close-Event schreiben
- Cancel-Transaktion
- Ziel-Run als cancelling/cancelled mit Idempotenz-Schlüssel markieren
Kein partieller Erfolg ist über diese Grenzen hinweg erlaubt.
Per-Session-Actor-Modell
AcpSessionManager betreibt einen Actor pro ACP-Session-Key:
- Actor-Mailbox serialisiert
submit-,cancel-,close- undstream-Seiteneffekte - Actor besitzt Runtime-Handle-Hydratisierung und Runtime-Adapter-Prozess-Lifecycle für diese Session
- Actor schreibt Run-Events in Reihenfolge (
seq) vor jeder Discord-Zustellung - Actor aktualisiert Zustellungs-Checkpoints nach erfolgreichem ausgehendem Senden
Dies beseitigt Turn-übergreifende Races und verhindert doppelte oder ungeordnete Thread-Ausgaben.
Idempotenz und Zustellungsprojektion
Alle externen ACP-Aktionen müssen Idempotenz-Schlüssel tragen:
- Spawn-Idempotenz-Schlüssel
- Prompt/Steer-Idempotenz-Schlüssel
- Cancel-Idempotenz-Schlüssel
- Close-Idempotenz-Schlüssel
Zustellungsregeln:
- Discord-Nachrichten werden aus
acp_eventsplusacp_delivery_checkpointabgeleitet - Wiederholungen setzen ab Checkpoint fort, ohne bereits zugestellte Chunks erneut zu senden
- Finale Reply-Emission ist exactly-once pro Run durch Projektionslogik
Recovery und Selbstheilung
Beim Gateway-Start:
- Nicht-terminale ACP-Sessions laden (
creating,idle,running,cancelling,error) - Actors lazy beim ersten eingehenden Event oder eager unter konfiguriertem Limit neu erstellen
- Alle
running-Runs ohne Heartbeat abgleichen und alsfailedmarkieren oder über Adapter recovern
Bei eingehender Discord-Thread-Nachricht:
- Wenn Binding existiert, aber ACP-Session fehlt: geschlossen fehlschlagen mit expliziter Stale-Binding-Nachricht
- Optional Stale-Binding nach operator-sicherer Validierung automatisch lösen
- Stale ACP-Bindings niemals stillschweigend zum normalen LLM-Pfad routen
Lifecycle und Sicherheit
Unterstützte Operationen:
- Aktuellen Run abbrechen:
/acp cancel - Thread-Binding lösen:
/unfocus - ACP-Session schließen:
/acp close - Idle-Sessions per effektiver TTL automatisch schließen
TTL-Policy:
- Effektive TTL ist das Minimum aus
- globaler/Session-TTL
- Discord-Thread-Binding-TTL
- ACP-Runtime-Owner-TTL
Sicherheitskontrollen:
- ACP-Agents per Name auf Allowlist setzen
- Workspace-Roots für ACP-Sessions einschränken
- Env-Allowlist-Passthrough
- Maximale gleichzeitige ACP-Sessions pro Account und global
- Begrenzter Restart-Backoff bei Runtime-Crashs
Konfigurationsoberfläche
Kern-Schlüssel:
acp.enabledacp.dispatch.enabled(unabhängiger ACP-Routing-Kill-Switch)acp.backend(Standardacpx)acp.defaultAgentacp.allowedAgents[]acp.maxConcurrentSessionsacp.stream.coalesceIdleMsacp.stream.maxChunkCharsacp.runtime.ttlMinutesacp.controlPlane.store(Standardsqlite)acp.controlPlane.storePathacp.controlPlane.recovery.eagerActorsacp.controlPlane.recovery.reconcileRunningAfterMsacp.controlPlane.checkpoint.flushEveryEventsacp.controlPlane.checkpoint.flushEveryMsacp.idempotency.ttlHourschannels.discord.threadBindings.spawnAcpSessions
Plugin-/Backend-Schlüssel (acpx-Plugin-Bereich):
- Backend-Command/Path-Overrides
- Backend-Env-Allowlist
- Backend-per-Agent-Presets
- Backend-Startup/Stop-Timeouts
- Backend max inflight Runs pro Session
Implementierungsspezifikation
Steuerungsebenen-Module (neu)
Dedizierte ACP-Steuerungsebenen-Module im Kern hinzufügen:
src/acp/control-plane/manager.ts- besitzt ACP-Actors, Lifecycle-Übergänge, Befehls-Serialisierung
src/acp/control-plane/store.ts- SQLite-Schemaverwaltung, Transaktionen, Abfrage-Helper
src/acp/control-plane/events.ts- typisierte ACP-Event-Definitionen und Serialisierung
src/acp/control-plane/checkpoint.ts- dauerhafte Zustellungs-Checkpoints und Replay-Cursors
src/acp/control-plane/idempotency.ts- Idempotenz-Schlüssel-Reservierung und Response-Replay
src/acp/control-plane/recovery.ts- Boot-Zeit-Abgleich und Actor-Rehydrate-Plan
Kompatibilitäts-Brücken-Module:
src/acp/runtime/session-meta.ts- bleibt temporär für Projektion in
SessionEntry.acp - darf nach Migrations-Umstellung nicht mehr Source of Truth sein
- bleibt temporär für Projektion in
Erforderliche Invarianten (müssen im Code erzwungen werden)
- ACP-Session-Erstellung und Thread-Bind sind atomar (einzelne Transaktion)
- Pro ACP-Session-Actor ist höchstens ein aktiver Run gleichzeitig
- Event-
seqist streng steigend pro Run - Zustellungs-Checkpoint rückt nie über das letzte committete Event hinaus vor
- Idempotenz-Replay gibt vorherige Erfolgs-Payload für doppelte Befehlsschlüssel zurück
- Fehlende/veraltete ACP-Metadaten können nicht zum normalen Nicht-ACP-Reply-Pfad routen
Core-Berührungspunkte
Zu ändernde Core-Dateien:
src/auto-reply/reply/dispatch-from-config.ts- ACP-Zweig ruft
AcpSessionManager.submitund Event-Projektions-Zustellung auf - Direkten ACP-Fallback entfernen, der Steuerungsebenen-Invarianten umgeht
- ACP-Zweig ruft
src/auto-reply/reply/inbound-context.ts(oder nächste normalisierte Kontextgrenze)- Normalisierte Routing-Schlüssel und Idempotenz-Seeds für die ACP-Steuerungsebene bereitstellen
src/config/sessions/types.tsSessionEntry.acpals Projections-only-Kompatibilitätsfeld beibehalten
src/gateway/server-methods/sessions.ts- Reset/Delete/Archivierung muss ACP-Manager-Close/Unbind-Transaktionspfad aufrufen
src/infra/outbound/bound-delivery-router.ts- Fail-Closed-Zielverhalten für ACP-gebundene Session-Turns erzwingen
src/discord/monitor/thread-bindings.ts- ACP-Stale-Binding-Validierungs-Helper hinzufügen, verbunden mit Steuerungsebenen-Lookups
src/auto-reply/reply/commands-acp.ts- Spawn/Cancel/Close/Steer über ACP-Manager-APIs routen
src/agents/acp-spawn.ts- Ad-hoc-Metadaten-Schreibvorgänge stoppen; ACP-Manager-Spawn-Transaktion aufrufen
src/plugin-sdk/**und Plugin-Runtime-Brücke- ACP-Backend-Registrierung und Health-Semantik sauber bereitstellen
Core-Dateien, die explizit nicht ersetzt werden:
src/discord/monitor/message-handler.preflight.ts- Thread-Binding-Override-Verhalten als kanonischer Session-Key-Resolver beibehalten
ACP-Runtime-Registry-API
Core-Registry-Modul hinzufügen:
src/acp/runtime/registry.ts
Erforderliche API:
export type AcpRuntimeBackend = {
id: string;
runtime: AcpRuntime;
healthy?: () => boolean;
};
export function registerAcpRuntimeBackend(backend: AcpRuntimeBackend): void;
export function unregisterAcpRuntimeBackend(id: string): void;
export function getAcpRuntimeBackend(id?: string): AcpRuntimeBackend | null;
export function requireAcpRuntimeBackend(id?: string): AcpRuntimeBackend;
Verhalten:
requireAcpRuntimeBackendwirft einen typisierten ACP-Backend-Missing-Error, wenn nicht verfügbar- Plugin-Service registriert Backend bei
startund deregistriert beistop - Runtime-Lookups sind read-only und prozesslokal
acpx-Runtime-Plugin-Vertrag (Implementierungsdetail)
Für das erste Produktions-Backend (extensions/acpx) sind OpenClaw und acpx über einen strikten Befehlsvertrag verbunden:
- Backend-ID:
acpx - Plugin-Service-ID:
acpx-runtime - Runtime-Handle-Kodierung:
runtimeSessionName = acpx:v1:<base64url(json)> - Kodierte Payload-Felder:
name(acpx-benannte Session; nutzt OpenClawssessionKey)agent(acpx-Agent-Befehl)cwd(Session-Workspace-Root)mode(persistent | oneshot)
Befehlszuordnung:
- Session sicherstellen:
acpx --format json --json-strict --cwd <cwd> <agent> sessions ensure --name <name>
- Prompt-Turn:
acpx --format json --json-strict --cwd <cwd> <agent> prompt --session <name> --file -
- Abbrechen:
acpx --format json --json-strict --cwd <cwd> <agent> cancel --session <name>
- Schließen:
acpx --format json --json-strict --cwd <cwd> <agent> sessions close <name>
Streaming:
- OpenClaw konsumiert ndjson-Events aus
acpx --format json --json-strict text=>text_delta/outputthought=>text_delta/thoughttool_call=>tool_calldone=>doneerror=>error
Session-Schema-Patch
SessionEntry in src/config/sessions/types.ts patchen:
type SessionAcpMeta = {
backend: string;
agent: string;
runtimeSessionName: string;
mode: "persistent" | "oneshot";
cwd?: string;
state: "idle" | "running" | "error";
lastActivityAt: number;
lastError?: string;
};
Persistiertes Feld:
SessionEntry.acp?: SessionAcpMeta
Migrationsregeln:
- Phase A: Dual-Write (
acp-Projektion + ACP-SQLite-Source-of-Truth) - Phase B: Read-Primary aus ACP-SQLite, Fallback-Read aus Legacy-
SessionEntry.acp - Phase C: Migrationsbefehl füllt fehlende ACP-Zeilen aus gültigen Legacy-Einträgen nach
- Phase D: Fallback-Read entfernen und Projektion optional nur für UX behalten
- Legacy-Felder (
cliSessionIds,claudeCliSessionId) bleiben unverändert
Fehlervertrag
Stabile ACP-Fehlercodes und benutzerseitige Meldungen ergänzen:
ACP_BACKEND_MISSING- Meldung:
ACP runtime backend is not configured. Install and enable the acpx runtime plugin.
- Meldung:
ACP_BACKEND_UNAVAILABLE- Meldung:
ACP runtime backend is currently unavailable. Try again in a moment.
- Meldung:
ACP_SESSION_INIT_FAILED- Meldung:
Could not initialize ACP session runtime.
- Meldung:
ACP_TURN_FAILED- Meldung:
ACP turn failed before completion.
- Meldung:
Regeln:
- Handlungsfähige, benutzersichere Meldung im Thread zurückgeben
- Detaillierte Backend-/Systemfehler nur in Runtime-Logs protokollieren
- Niemals stillschweigend zum normalen LLM-Pfad zurückfallen, wenn ACP-Routing explizit ausgewählt wurde
Duplikat-Zustellungs-Arbitrierung
Einzelne Routing-Regel für ACP-gebundene Turns:
- Wenn ein aktives Thread-Binding für die Ziel-ACP-Session und den Anforderungskontext existiert, nur an diesen gebundenen Thread zustellen
- Nicht zusätzlich für denselben Turn an den Parent-Kanal senden
- Wenn die gebundene Zielauswahl mehrdeutig ist, geschlossen mit explizitem Fehler fehlschlagen (kein impliziter Parent-Fallback)
- Wenn kein aktives Binding existiert, normales Session-Zielverhalten verwenden
Beobachtbarkeit und Betriebsbereitschaft
Erforderliche Metriken:
- ACP-Spawn-Erfolg/Fehler-Anzahl nach Backend und Fehlercode
- ACP-Run-Latenz-Perzentile (Queue-Wartezeit, Runtime-Turn-Zeit, Zustellungs-Projektionszeit)
- ACP-Actor-Restart-Anzahl und Restart-Grund
- Stale-Binding-Erkennungsanzahl
- Idempotenz-Replay-Trefferrate
- Discord-Zustellungs-Retry- und Ratenlimit-Zähler
Erforderliche Logs:
- Strukturierte Logs mit
sessionKey,runId,backend,threadId,idempotencyKey - Explizite Zustandsübergangs-Logs für Session- und Run-Zustandsmaschinen
- Adapter-Befehlslogs mit maskierungssicheren Argumenten und Exit-Zusammenfassung
Erforderliche Diagnose:
/acp sessionsenthält Status, aktiven Run, letzten Fehler und Binding-Status/acp doctor(oder Äquivalent) validiert Backend-Registrierung, Store-Health und Stale-Bindings
Konfigurationsvorrang und effektive Werte
ACP-Aktivierungsvorrang:
- Account-Override:
channels.discord.accounts.<id>.threadBindings.spawnAcpSessions - Kanal-Override:
channels.discord.threadBindings.spawnAcpSessions - Globales ACP-Gate:
acp.enabled - Dispatch-Gate:
acp.dispatch.enabled - Backend-Verfügbarkeit: registriertes Backend für
acp.backend
Auto-Enable-Verhalten:
- Wenn ACP konfiguriert ist (
acp.enabled=true,acp.dispatch.enabled=trueoderacp.backend=acpx), markiert Plugin-Auto-Enableplugins.entries.acpx.enabled=true, sofern nicht auf der Denylist oder explizit deaktiviert
Effektiver TTL-Wert:
min(Session-TTL, Discord-Thread-Binding-TTL, ACP-Runtime-TTL)
Testplan
Unit-Tests:
src/acp/runtime/registry.test.ts(neu)src/auto-reply/reply/dispatch-from-config.acp.test.ts(neu)src/infra/outbound/bound-delivery-router.test.ts(ACP-Fail-Closed-Fälle erweitern)src/config/sessions/types.test.tsoder nächste Session-Store-Tests (ACP-Metadaten-Persistenz)
Integrationstests:
src/discord/monitor/reply-delivery.test.ts(gebundenes ACP-Zustellungsziel-Verhalten)src/discord/monitor/message-handler.preflight*.test.ts(gebundene ACP-Session-Key-Routing-Kontinuität)- acpx-Plugin-Runtime-Tests im Backend-Paket (Service-Register/Start/Stop + Event-Normalisierung)
Gateway-E2E-Tests:
src/gateway/server.sessions.gateway-server-sessions-a.e2e.test.ts(ACP-Reset/Delete-Lifecycle-Abdeckung erweitern)- ACP-Thread-Turn-Roundtrip-E2E für Spawn, Nachricht, Stream, Cancel, Unfocus, Restart-Recovery
Rollout-Schutz
Unabhängigen ACP-Dispatch-Kill-Switch hinzufügen:
acp.dispatch.enabledStandardfalsefür das erste Release- Wenn deaktiviert:
- ACP-Spawn/Focus-Steuerungsbefehle können weiterhin Sessions binden
- ACP-Dispatch-Pfad aktiviert sich nicht
- Benutzer erhält explizite Meldung, dass ACP-Dispatch per Policy deaktiviert ist
- Nach Canary-Validierung kann der Standard in einem späteren Release auf
trueumgestellt werden
Befehls- und UX-Plan
Neue Befehle
/acp spawn <agent-id> [--mode persistent|oneshot] [--thread auto|here|off]/acp cancel [session]/acp steer <instruction>/acp close [session]/acp sessions
Bestehende Befehlskompatibilität
/focus <sessionKey>unterstützt weiterhin ACP-Ziele/unfocusbehält aktuelle Semantik/session idleund/session max-ageersetzen den alten TTL-Override
Schrittweiser Rollout
Phase 0: ADR und Schema-Freeze
- ADR für ACP-Steuerungsebenen-Ownership und Adapter-Grenzen ausliefern
- DB-Schema einfrieren (
acp_sessions,acp_runs,acp_bindings,acp_events,acp_delivery_checkpoint,acp_idempotency) - Stabile ACP-Fehlercodes, Event-Vertrag und Zustandsübergangs-Guards definieren
Phase 1: Steuerungsebenen-Grundlage im Kern
AcpSessionManagerund Per-Session-Actor-Runtime implementieren- ACP-SQLite-Store und Transaktions-Helper implementieren
- Idempotenz-Store und Replay-Helper implementieren
- Event-Append + Zustellungs-Checkpoint-Module implementieren
- Spawn/Cancel/Close-APIs mit transaktionalen Garantien an den Manager anbinden
Phase 2: Core-Routing und Lifecycle-Integration
- Threadgebundene ACP-Turns aus der Dispatch-Pipeline in den ACP-Manager routen
- Fail-Closed-Routing erzwingen, wenn ACP-Binding/Session-Invarianten scheitern
- Reset/Delete/Archiv/Unfocus-Lifecycle mit ACP-Close/Unbind-Transaktionen integrieren
- Stale-Binding-Erkennung und optionale Auto-Unbind-Policy hinzufügen
Phase 3: acpx-Backend-Adapter/Plugin
acpx-Adapter gegen Runtime-Vertrag implementieren (ensureSession,submit,stream,cancel,close)- Backend-Health-Checks und Startup/Teardown-Registrierung hinzufügen
- acpx-ndjson-Events in ACP-Runtime-Events normalisieren
- Backend-Timeouts, Prozess-Supervision und Restart/Backoff-Policy erzwingen
Phase 4: Zustellungsprojektion und Kanal-UX (Discord zuerst)
- Event-gesteuerte Kanalprojektion mit Checkpoint-Resume implementieren (Discord zuerst)
- Streaming-Chunks mit ratenlimitbewusster Flush-Policy zusammenführen
- Exactly-Once finale Abschlussnachricht pro Run garantieren
/acp spawn,/acp cancel,/acp steer,/acp close,/acp sessionsausliefern
Phase 5: Migration und Umstellung
- Dual-Write zu
SessionEntry.acp-Projektion plus ACP-SQLite-Source-of-Truth einführen - Migrationstool für Legacy-ACP-Metadatenzeilen hinzufügen
- Lesepfad auf ACP-SQLite-Primary umstellen
- Legacy-Fallback-Routing entfernen, das von fehlendem
SessionEntry.acpabhängt
Phase 6: Härtung, SLOs und Skalierungslimits
- Gleichzeitigkeitslimits (global/Account/Session), Queue-Policies und Timeout-Budgets erzwingen
- Vollständige Telemetrie, Dashboards und Schwellenwert-Alerts hinzufügen
- Chaos-Test für Crash-Recovery und Duplikat-Zustellungs-Unterdrückung
- Runbook für Backend-Ausfall, DB-Korruption und Stale-Binding-Behebung veröffentlichen
Vollständige Implementierungs-Checkliste
- Core-Steuerungsebenen-Module und Tests
- DB-Migrationen und Rollback-Plan
- ACP-Manager-API-Integration über Dispatch und Befehle
- Adapter-Registrierungsschnittstelle in Plugin-Runtime-Brücke
- acpx-Adapter-Implementierung und Tests
- Threadfähige Kanal-Zustellungs-Projektionslogik mit Checkpoint-Replay (Discord zuerst)
- Lifecycle-Hooks für Reset/Delete/Archiv/Unfocus
- Stale-Binding-Detektor und operator-seitige Diagnose
- Konfigurationsvalidierung und Vorrangtests für alle neuen ACP-Schlüssel
- Betriebsdokumentation und Troubleshooting-Runbook
Testplan
Unit-Tests:
- ACP-DB-Transaktionsgrenzen (Spawn/Bind/Enqueue-Atomarität, Cancel, Close)
- ACP-Zustandsmaschinen-Übergangs-Guards für Sessions und Runs
- Idempotenz-Reservierung/Replay-Semantik über alle ACP-Befehle
- Per-Session-Actor-Serialisierung und Queue-Reihenfolge
- acpx-Event-Parser und Chunk-Koaleszierer
- Runtime-Supervisor-Restart und Backoff-Policy
- Konfigurationsvorrang und effektive TTL-Berechnung
- Core-ACP-Routing-Zweigauswahl und Fail-Closed-Verhalten bei ungültigem Backend/Session
Integrationstests:
- Fake-ACP-Adapter-Prozess für deterministisches Streaming- und Cancel-Verhalten
- ACP-Manager + Dispatch-Integration mit transaktionaler Persistenz
- Threadgebundenes eingehendes Routing zu ACP-Session-Key
- Threadgebundene ausgehende Zustellung unterdrückt Parent-Kanal-Duplikation
- Checkpoint-Replay erholt sich nach Zustellungsfehler und setzt vom letzten Event fort
- Plugin-Service-Registrierung und Teardown des ACP-Runtime-Backends
Gateway-E2E-Tests:
- ACP mit Thread spawnen, Multi-Turn-Prompts austauschen, unfocusen
- Gateway-Neustart mit persistierter ACP-DB und Bindings, dann dieselbe Session fortsetzen
- Gleichzeitige ACP-Sessions in verschiedenen Threads haben kein Crosstalk
- Doppelte Befehlswiederholungen (gleicher Idempotenz-Schlüssel) erzeugen keine doppelten Runs oder Replies
- Stale-Binding-Szenario liefert expliziten Fehler und optionales Auto-Clean-Verhalten
Risiken und Gegenmaßnahmen
- Doppelte Zustellungen während der Umstellung
- Gegenmaßnahme: einzelner Ziel-Resolver und idempotenter Event-Checkpoint
- Runtime-Prozess-Churn unter Last
- Gegenmaßnahme: langlebige Per-Session-Owners + Gleichzeitigkeitslimits + Backoff
- Plugin fehlt oder ist falsch konfiguriert
- Gegenmaßnahme: expliziter Operator-Fehler und Fail-Closed-ACP-Routing (kein impliziter Fallback zum normalen Session-Pfad)
- Konfigurationsverwirrung zwischen Subagent- und ACP-Gates
- Gegenmaßnahme: explizite ACP-Schlüssel und Befehls-Feedback mit effektiver Policy-Quelle
- Steuerungsebenen-Store-Korruption oder Migrationsbugs
- Gegenmaßnahme: WAL-Modus, Backup/Restore-Hooks, Migrations-Smoke-Tests und Read-Only-Fallback-Diagnose
- Actor-Deadlocks oder Mailbox-Aushungerung
- Gegenmaßnahme: Watchdog-Timer, Actor-Health-Probes und begrenzte Mailbox-Tiefe mit Ablehnungs-Telemetrie
Akzeptanz-Checkliste
- ACP-Session-Spawn kann einen Thread in einem unterstützten Kanal-Adapter erstellen oder binden (derzeit Discord)
- Alle Thread-Nachrichten routen nur zur gebundenen ACP-Session
- ACP-Ausgaben erscheinen in derselben Thread-Identität mit Streaming oder Batches
- Keine doppelte Ausgabe im Parent-Kanal für gebundene Turns
- Spawn+Bind+initiales Enqueue sind atomar im persistenten Store
- ACP-Befehlswiederholungen sind idempotent und erzeugen keine doppelten Runs oder Ausgaben
- Cancel, Close, Unfocus, Archiv, Reset und Delete führen deterministisches Cleanup durch
- Crash-Restart bewahrt Zuordnung und setzt Multi-Turn-Kontinuität fort
- Gleichzeitige threadgebundene ACP-Sessions arbeiten unabhängig
- Fehlender ACP-Backend-Zustand erzeugt klaren, handlungsfähigen Fehler
- Stale-Bindings werden erkannt und explizit angezeigt (mit optionalem sicheren Auto-Clean)
- Steuerungsebenen-Metriken und Diagnose stehen Operatoren zur Verfügung
- Neue Unit-, Integrations- und E2E-Abdeckung besteht
Anhang: Gezielte Refactorings für die aktuelle Implementierung (Status)
Dies sind nicht-blockierende Follow-ups, um den ACP-Pfad nach dem Landen des aktuellen Funktionsumfangs wartbar zu halten.
1) ACP-Dispatch-Policy-Auswertung zentralisieren (abgeschlossen)
- Implementiert über gemeinsame ACP-Policy-Helper in
src/acp/policy.ts - Dispatch, ACP-Befehl-Lifecycle-Handler und ACP-Spawn-Pfad nutzen jetzt gemeinsame Policy-Logik
2) ACP-Befehlshandler nach Subbefehlsdomäne aufteilen (abgeschlossen)
src/auto-reply/reply/commands-acp.tsist jetzt ein dünner Router- Subbefehlsverhalten ist aufgeteilt in:
src/auto-reply/reply/commands-acp/lifecycle.tssrc/auto-reply/reply/commands-acp/runtime-options.tssrc/auto-reply/reply/commands-acp/diagnostics.ts- gemeinsame Helper in
src/auto-reply/reply/commands-acp/shared.ts
3) ACP-Session-Manager nach Verantwortung aufteilen (abgeschlossen)
- Manager ist aufgeteilt in:
src/acp/control-plane/manager.ts(öffentliche Fassade + Singleton)src/acp/control-plane/manager.core.ts(Manager-Implementierung)src/acp/control-plane/manager.types.ts(Manager-Typen/Abhängigkeiten)src/acp/control-plane/manager.utils.ts(Normalisierung + Helper-Funktionen)
4) Optionaler acpx-Runtime-Adapter-Cleanup
extensions/acpx/src/runtime.tskann aufgeteilt werden in:- Prozessausführung/-supervision
- ndjson-Event-Parsing/-Normalisierung
- Runtime-API-Oberfläche (
submit,cancel,close, etc.) - Verbessert die Testbarkeit und macht Backend-Verhalten leichter auditierbar