ACP 執行緒綁定代理

概覽

本計畫定義 OpenClaw 如何在支援執行緒的頻道(優先 Discord)中支援 ACP 程式碼代理,具備正式環境等級的生命週期與復原能力。

相關文件:

目標使用者體驗:

  • 使用者建立或聚焦一個 ACP session 到執行緒中
  • 該執行緒中的使用者訊息路由至已綁定的 ACP session
  • 代理輸出以相同的執行緒身分串流回覆
  • session 可設為持久或一次性,並提供明確的清理控制

決策摘要

長期建議採用混合架構:

  • OpenClaw 核心掌管 ACP 控制平面的關注點
    • session 身分與中繼資料
    • 執行緒綁定與路由決策
    • 投遞不變量與重複抑制
    • 生命週期清理與復原語義
  • ACP 執行時後端可抽換
    • 第一個後端為 acpx 支援的外掛服務
    • 執行時負責 ACP 傳輸、排隊、取消、重連

OpenClaw 不應在核心中重新實作 ACP 傳輸內部機制。 OpenClaw 不應僅依賴純外掛攔截路徑進行路由。

終極架構(聖杯)

將 ACP 視為 OpenClaw 的一級控制平面,搭配可抽換的執行時轉接器。

不可妥協的不變量:

  • 每個 ACP 執行緒綁定都參照有效的 ACP session 記錄
  • 每個 ACP session 都有明確的生命週期狀態(creatingidlerunningcancellingclosederror
  • 每次 ACP 執行都有明確的執行狀態(queuedrunningcompletedfailedcancelled
  • spawn、bind 和初始入隊為原子操作
  • 指令重試為冪等的(不產生重複的執行或 Discord 輸出)
  • 綁定執行緒的頻道輸出是 ACP 執行事件的投影,絕非即興的副作用

長期擁有權模型:

  • AcpSessionManager 是唯一的 ACP 寫入者與協調者
  • manager 初期位於 gateway 行程中;之後可移至獨立的 sidecar,使用相同介面
  • 對每個 ACP session key,manager 擁有一個記憶體中的 actor(序列化的指令執行)
  • 轉接器(acpx、未來的後端)僅作為傳輸/執行時實作

長期持久化模型:

  • 將 ACP 控制平面狀態移至 OpenClaw 狀態目錄下的專屬 SQLite 儲存區(WAL 模式)
  • 遷移期間保留 SessionEntry.acp 作為相容投影,非事實來源
  • ACP 事件以僅附加方式儲存,支援重播、崩潰復原與確定性投遞

投遞策略(通往聖杯的橋樑)

  • 短期橋接
    • 保留現有的執行緒綁定機制與既有 ACP 設定介面
    • 修復中繼資料缺口的 bug,透過單一核心 ACP 分支路由 ACP 回合
    • 立即加入冪等鍵與預設拒絕的路由檢查
  • 長期轉換
    • 將 ACP 事實來源移至控制平面 DB + actors
    • 使綁定執行緒的投遞成為純粹的事件投影
    • 移除依賴投機式 session-entry 中繼資料的舊有回退行為

為何不採用純外掛模式

目前的外掛掛鉤不足以在不修改核心的情況下完成端對端 ACP session 路由。

  • 從執行緒綁定的入站路由在核心分派中先解析為 session key
  • 訊息掛鉤是發射後不管的,無法短路主回覆路徑
  • 外掛指令適合控制操作,但無法取代核心的逐回合分派流程

結論:

  • ACP 執行時可以外掛化
  • ACP 路由分支必須存在於核心

可重用的既有基礎

已實作且應維持為正規的部分:

  • 執行緒綁定目標支援 subagentacp
  • 入站執行緒路由覆寫在正常分派前透過綁定解析
  • 透過 webhook 在回覆投遞中實現出站執行緒身分
  • /focus/unfocus 流程支援 ACP 目標
  • 持久綁定儲存區,啟動時可恢復
  • 在歸檔、刪除、unfocus、重置、刪除時的解綁生命週期

本計畫擴展而非取代該基礎。

架構

邊界模型

核心(必須在 OpenClaw 核心中):

  • 回覆管線中的 ACP session 模式分派分支
  • 投遞仲裁以避免父頻道加執行緒的重複
  • ACP 控制平面持久化(遷移期間搭配 SessionEntry.acp 相容投影)
  • 與 session 重置/刪除相關的生命週期解綁和執行時分離語義

外掛後端(acpx 實作):

  • ACP 執行時 worker 監督
  • acpx 行程呼叫與事件解析
  • ACP 指令處理器(/acp ...)與操作者 UX
  • 後端專屬的設定預設值與診斷

執行時擁有權模型

  • 一個 gateway 行程擁有 ACP 協調狀態
  • ACP 執行在 acpx 後端透過被監督的子行程運行
  • 行程策略為每個活躍 ACP session key 維持長期存活,非逐訊息

這避免了每次提示的啟動成本,並使取消和重連語義更可靠。

核心執行時合約

新增核心 ACP 執行時合約,使路由程式碼不依賴 CLI 細節,並可在不改變分派邏輯的情況下切換後端:

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 }>;
}

實作細節:

  • 第一個後端:以外掛服務形式發布的 AcpxRuntime
  • 核心透過登錄檔解析執行時,並在沒有可用 ACP 執行時後端時回報明確的操作者錯誤

控制平面資料模型與持久化

長期事實來源是專屬的 ACP SQLite 資料庫(WAL 模式),用於交易式更新與崩潰安全復原:

  • acp_sessions
    • session_key(pk)、backendagentmodecwdstatecreated_atupdated_atlast_error
  • acp_runs
    • run_id(pk)、session_key(fk)、staterequester_message_ididempotency_keystarted_atended_aterror_codeerror_message
  • acp_bindings
    • binding_key(pk)、thread_idchannel_idaccount_idsession_key(fk)、expires_atbound_at
  • acp_events
    • event_id(pk)、run_id(fk)、seqkindpayload_jsoncreated_at
  • acp_delivery_checkpoint
    • run_id(pk/fk)、last_event_seqlast_discord_message_idupdated_at
  • acp_idempotency
    • scopeidempotency_keyresult_jsoncreated_at,唯一約束 (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;
};

儲存規則:

  • 遷移期間保留 SessionEntry.acp 作為相容投影
  • 行程 ID 和 socket 僅存於記憶體
  • 持久的生命週期和執行狀態存放在 ACP DB 中,非一般 session JSON
  • 若執行時擁有者異常終止,gateway 從 ACP DB 重新載入並從檢查點恢復

路由與投遞

入站:

  • 保持現有的執行緒綁定查詢作為第一個路由步驟
  • 若綁定目標為 ACP session,路由至 ACP 執行時分支而非 getReplyFromConfig
  • 明確的 /acp steer 指令使用 mode: "steer"

出站:

  • ACP 事件串流正規化為 OpenClaw 回覆區塊
  • 投遞目標透過既有的綁定目的地路徑解析
  • 當該 session 回合有活躍的綁定執行緒時,抑制父頻道的完成訊息

串流策略:

  • 以合併視窗串流部分輸出
  • 可設定的最小間隔和最大區塊位元組以符合 Discord 速率限制
  • 完成或失敗時一律送出最終訊息

狀態機與交易邊界

Session 狀態機:

  • creating -> idle -> running -> idle
  • running -> cancelling -> idle | error
  • idle -> closed
  • error -> idle | closed

執行狀態機:

  • queued -> running -> completed
  • running -> failed | cancelled
  • queued -> cancelled

必要的交易邊界:

  • spawn 交易
    • 建立 ACP session 列
    • 建立/更新 ACP 執行緒綁定列
    • 入隊初始執行列
  • close 交易
    • 標記 session 為 closed
    • 刪除/過期綁定列
    • 寫入最終 close 事件
  • cancel 交易
    • 使用冪等鍵標記目標執行為 cancelling/cancelled

不允許在這些邊界中出現部分成功。

逐 session actor 模型

AcpSessionManager 為每個 ACP session key 運行一個 actor:

  • actor 信箱序列化 submitcancelclosestream 副作用
  • actor 擁有該 session 的執行時 handle 載入和執行時轉接器行程生命週期
  • actor 在任何 Discord 投遞前按序(seq)寫入執行事件
  • actor 在成功的出站傳送後更新投遞檢查點

這消除了跨回合的競爭條件,防止重複或亂序的執行緒輸出。

冪等性與投遞投影

所有外部 ACP 操作必須攜帶冪等鍵:

  • spawn 冪等鍵
  • prompt/steer 冪等鍵
  • cancel 冪等鍵
  • close 冪等鍵

投遞規則:

  • Discord 訊息從 acp_events 加上 acp_delivery_checkpoint 衍生
  • 重試從檢查點恢復,不重新傳送已投遞的區塊
  • 最終回覆的送出由投影邏輯保證每次執行恰好一次

復原與自我修復

Gateway 啟動時:

  • 載入非終態的 ACP session(creatingidlerunningcancellingerror
  • 在首次入站事件時惰性重建 actor,或在設定上限內積極重建
  • 調和任何遺漏心跳的 running 執行,標記為 failed 或透過轉接器復原

入站 Discord 執行緒訊息時:

  • 若綁定存在但 ACP session 遺失,以明確的過時綁定訊息預設拒絕
  • 可選的在操作者安全驗證後自動解綁過時綁定
  • 絕不靜默將過時 ACP 綁定路由至一般 LLM 路徑

生命週期與安全

支援的操作:

  • 取消當前執行:/acp cancel
  • 解綁執行緒:/unfocus
  • 關閉 ACP session:/acp close
  • 依有效 TTL 自動關閉閒置 session

TTL 策略:

  • 有效 TTL 取以下的最小值
    • 全域/session TTL
    • Discord 執行緒綁定 TTL
    • ACP 執行時擁有者 TTL

安全控制:

  • 依名稱白名單 ACP 代理
  • 限制 ACP session 的工作區根目錄
  • 環境變數白名單傳遞
  • 每帳號及全域的最大並行 ACP session 數
  • 執行時崩潰的有界重啟退避

設定介面

核心鍵:

  • acp.enabled
  • acp.dispatch.enabled(獨立的 ACP 路由開關)
  • acp.backend(預設 acpx
  • acp.defaultAgent
  • acp.allowedAgents[]
  • acp.maxConcurrentSessions
  • acp.stream.coalesceIdleMs
  • acp.stream.maxChunkChars
  • acp.runtime.ttlMinutes
  • acp.controlPlane.store(預設 sqlite
  • acp.controlPlane.storePath
  • acp.controlPlane.recovery.eagerActors
  • acp.controlPlane.recovery.reconcileRunningAfterMs
  • acp.controlPlane.checkpoint.flushEveryEvents
  • acp.controlPlane.checkpoint.flushEveryMs
  • acp.idempotency.ttlHours
  • channels.discord.threadBindings.spawnAcpSessions

外掛/後端鍵(acpx 外掛區段):

  • 後端指令/路徑覆寫
  • 後端環境變數白名單
  • 後端逐代理預設
  • 後端啟動/停止逾時
  • 後端每 session 最大進行中執行數

實作規格

控制平面模組(新增)

在核心中新增專屬的 ACP 控制平面模組:

  • src/acp/control-plane/manager.ts
    • 擁有 ACP actor、生命週期轉換、指令序列化
  • src/acp/control-plane/store.ts
    • SQLite schema 管理、交易、查詢輔助
  • src/acp/control-plane/events.ts
    • 帶型別的 ACP 事件定義與序列化
  • src/acp/control-plane/checkpoint.ts
    • 持久投遞檢查點與重播游標
  • src/acp/control-plane/idempotency.ts
    • 冪等鍵預留與回應重播
  • src/acp/control-plane/recovery.ts
    • 啟動時的調和與 actor 重載計畫

相容橋接模組:

  • src/acp/runtime/session-meta.ts
    • 暫時保留用於向 SessionEntry.acp 投影
    • 遷移轉換後必須停止作為事實來源

必須強制的不變量(必須在程式碼中強制)

  • ACP session 建立與執行緒綁定為原子操作(單一交易)
  • 每個 ACP session actor 同時最多一個活躍執行
  • 事件 seq 在每次執行中嚴格遞增
  • 投遞檢查點永不超過最後提交的事件
  • 冪等重播對重複的指令鍵回傳先前的成功 payload
  • 過時/遺失的 ACP 中繼資料無法路由至一般非 ACP 回覆路徑

核心接觸點

需修改的核心檔案:

  • src/auto-reply/reply/dispatch-from-config.ts
    • ACP 分支呼叫 AcpSessionManager.submit 和事件投影投遞
    • 移除繞過控制平面不變量的直接 ACP 回退
  • src/auto-reply/reply/inbound-context.ts(或最近的正規化上下文邊界)
    • 為 ACP 控制平面公開正規化的路由鍵和冪等種子
  • src/config/sessions/types.ts
    • 保留 SessionEntry.acp 為僅投影的相容欄位
  • src/gateway/server-methods/sessions.ts
    • 重置/刪除/歸檔必須呼叫 ACP manager 的 close/unbind 交易路徑
  • src/infra/outbound/bound-delivery-router.ts
    • 對 ACP 綁定 session 回合強制預設拒絕的目的地行為
  • src/discord/monitor/thread-bindings.ts
    • 新增連接到控制平面查詢的 ACP 過時綁定驗證輔助函式
  • src/auto-reply/reply/commands-acp.ts
    • 透過 ACP manager API 路由 spawn/cancel/close/steer
  • src/agents/acp-spawn.ts
    • 停止即興的中繼資料寫入;呼叫 ACP manager 的 spawn 交易
  • src/plugin-sdk/** 及外掛執行時橋接
    • 乾淨地公開 ACP 後端登錄與健康語義

明確不替換的核心檔案:

  • src/discord/monitor/message-handler.preflight.ts
    • 保持執行緒綁定覆寫行為作為正規的 session-key 解析器

ACP 執行時登錄檔 API

新增核心登錄檔模組:

  • src/acp/runtime/registry.ts

必要 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;

行為:

  • requireAcpRuntimeBackend 在後端不可用時拋出帶型別的 ACP 後端遺失錯誤
  • 外掛服務在 start 時登錄後端,在 stop 時取消登錄
  • 執行時查詢為唯讀且行程內部的

acpx 執行時外掛合約(實作細節)

對於第一個正式後端(extensions/acpx),OpenClaw 和 acpx 透過嚴格的指令合約連接:

  • 後端 id:acpx
  • 外掛服務 id:acpx-runtime
  • 執行時 handle 編碼:runtimeSessionName = acpx:v1:<base64url(json)>
  • 編碼的 payload 欄位:
    • name(acpx 具名 session;使用 OpenClaw 的 sessionKey
    • agent(acpx 代理指令)
    • cwd(session 工作區根目錄)
    • modepersistent | oneshot

指令對應:

  • 確保 session:
    • acpx --format json --json-strict --cwd <cwd> <agent> sessions ensure --name <name>
  • prompt 回合:
    • acpx --format json --json-strict --cwd <cwd> <agent> prompt --session <name> --file -
  • 取消:
    • acpx --format json --json-strict --cwd <cwd> <agent> cancel --session <name>
  • 關閉:
    • acpx --format json --json-strict --cwd <cwd> <agent> sessions close <name>

串流:

  • OpenClaw 從 acpx --format json --json-strict 消費 ndjson 事件
  • text => text_delta/output
  • thought => text_delta/thought
  • tool_call => tool_call
  • done => done
  • error => error

Session schema 修補

src/config/sessions/types.ts 中修補 SessionEntry

type SessionAcpMeta = {
  backend: string;
  agent: string;
  runtimeSessionName: string;
  mode: "persistent" | "oneshot";
  cwd?: string;
  state: "idle" | "running" | "error";
  lastActivityAt: number;
  lastError?: string;
};

持久化欄位:

  • SessionEntry.acp?: SessionAcpMeta

遷移規則:

  • 階段 A:雙寫(acp 投影 + ACP SQLite 事實來源)
  • 階段 B:主要從 ACP SQLite 讀取,回退讀取舊有 SessionEntry.acp
  • 階段 C:遷移指令從有效的舊有項目回填遺漏的 ACP 列
  • 階段 D:移除回退讀取,僅保留投影作為 UX 用途
  • 舊有欄位(cliSessionIdsclaudeCliSessionId)保持不變

錯誤合約

新增穩定的 ACP 錯誤碼與面向使用者的訊息:

  • ACP_BACKEND_MISSING
    • 訊息:ACP runtime backend is not configured. Install and enable the acpx runtime plugin.
  • ACP_BACKEND_UNAVAILABLE
    • 訊息:ACP runtime backend is currently unavailable. Try again in a moment.
  • ACP_SESSION_INIT_FAILED
    • 訊息:Could not initialize ACP session runtime.
  • ACP_TURN_FAILED
    • 訊息:ACP turn failed before completion.

規則:

  • 在執行緒中回傳可操作的使用者安全訊息
  • 詳細的後端/系統錯誤僅記錄於執行時日誌
  • 當明確選擇了 ACP 路由時,絕不靜默回退至一般 LLM 路徑

重複投遞仲裁

ACP 綁定回合的單一路由規則:

  • 若目標 ACP session 和請求者上下文存在活躍的執行緒綁定,僅投遞至該綁定執行緒
  • 同一回合不同時傳送至父頻道
  • 若綁定目的地選擇模糊,以明確錯誤預設拒絕(不隱式回退至父頻道)
  • 若不存在活躍綁定,使用正常的 session 目的地行為

可觀測性與營運就緒

必要指標:

  • 依後端和錯誤碼分類的 ACP spawn 成功/失敗次數
  • ACP 執行延遲百分位數(排隊等待、執行時回合時間、投遞投影時間)
  • ACP actor 重啟次數與重啟原因
  • 過時綁定偵測次數
  • 冪等重播命中率
  • Discord 投遞重試與速率限制計數器

必要日誌:

  • sessionKeyrunIdbackendthreadIdidempotencyKey 為鍵的結構化日誌
  • session 和執行狀態機的明確狀態轉換日誌
  • 帶脫敏安全參數和退出摘要的轉接器指令日誌

必要診斷:

  • /acp sessions 包含狀態、活躍執行、最後錯誤和綁定狀態
  • /acp doctor(或等效指令)驗證後端登錄、儲存區健康和過時綁定

設定優先順序與有效值

ACP 啟用優先順序:

  • 帳號覆寫:channels.discord.accounts.<id>.threadBindings.spawnAcpSessions
  • 頻道覆寫:channels.discord.threadBindings.spawnAcpSessions
  • 全域 ACP 開關:acp.enabled
  • 分派開關:acp.dispatch.enabled
  • 後端可用性:acp.backend 的已登錄後端

自動啟用行為:

  • 當 ACP 已設定(acp.enabled=trueacp.dispatch.enabled=trueacp.backend=acpx)時,外掛自動啟用將 plugins.entries.acpx.enabled=true 標記為啟用,除非被拒絕清單或明確停用

TTL 有效值:

  • min(session ttl, discord thread binding ttl, acp runtime ttl)

測試對照

單元測試:

  • src/acp/runtime/registry.test.ts(新增)
  • src/auto-reply/reply/dispatch-from-config.acp.test.ts(新增)
  • src/infra/outbound/bound-delivery-router.test.ts(擴展 ACP 預設拒絕案例)
  • src/config/sessions/types.test.ts 或最近的 session-store 測試(ACP 中繼資料持久化)

整合測試:

  • src/discord/monitor/reply-delivery.test.ts(綁定 ACP 投遞目標行為)
  • src/discord/monitor/message-handler.preflight*.test.ts(綁定 ACP session-key 路由延續性)
  • acpx 外掛執行時測試在後端套件中(服務登錄/啟動/停止 + 事件正規化)

Gateway 端對端測試:

  • src/gateway/server.sessions.gateway-server-sessions-a.e2e.test.ts(擴展 ACP 重置/刪除生命週期覆蓋)
  • ACP 執行緒回合來回端對端測試:spawn、訊息、串流、取消、unfocus、重啟復原

上線防護

新增獨立的 ACP 分派開關:

  • acp.dispatch.enabled 初次發布時預設為 false
  • 停用時:
    • ACP spawn/focus 控制指令仍可綁定 session
    • ACP 分派路徑不會啟動
    • 使用者收到明確訊息說明 ACP 分派已被策略停用
  • 經金絲雀驗證後,可在後續版本中將預設翻轉為 true

指令與 UX 計畫

新指令

  • /acp spawn <agent-id> [--mode persistent|oneshot] [--thread auto|here|off]
  • /acp cancel [session]
  • /acp steer <instruction>
  • /acp close [session]
  • /acp sessions

既有指令相容性

  • /focus <sessionKey> 繼續支援 ACP 目標
  • /unfocus 保持現行語義
  • /session idle/session max-age 取代舊有的 TTL 覆寫

分階段上線

第 0 階段:ADR 與 schema 凍結

  • 發布 ACP 控制平面擁有權與轉接器邊界的 ADR
  • 凍結 DB schema(acp_sessionsacp_runsacp_bindingsacp_eventsacp_delivery_checkpointacp_idempotency
  • 定義穩定的 ACP 錯誤碼、事件合約和狀態轉換防護

第 1 階段:核心控制平面基礎

  • 實作 AcpSessionManager 和逐 session 的 actor 執行時
  • 實作 ACP SQLite 儲存區和交易輔助
  • 實作冪等儲存區和重播輔助
  • 實作事件附加 + 投遞檢查點模組
  • 將 spawn/cancel/close API 連接至 manager,提供交易保證

第 2 階段:核心路由與生命週期整合

  • 從分派管線將執行緒綁定的 ACP 回合路由至 ACP manager
  • 當 ACP 綁定/session 不變量失敗時強制預設拒絕路由
  • 將重置/刪除/歸檔/unfocus 生命週期與 ACP close/unbind 交易整合
  • 新增過時綁定偵測和選用的自動解綁策略

第 3 階段:acpx 後端轉接器/外掛

  • 依執行時合約實作 acpx 轉接器(ensureSessionsubmitstreamcancelclose
  • 新增後端健康檢查和啟動/拆卸登錄
  • 將 acpx ndjson 事件正規化為 ACP 執行時事件
  • 強制後端逾時、行程監督和重啟/退避策略

第 4 階段:投遞投影與頻道 UX(優先 Discord)

  • 實作帶有檢查點恢復的事件驅動頻道投影(優先 Discord)
  • 以速率限制感知的刷新策略合併串流區塊
  • 保證每次執行恰好一次的最終完成訊息
  • 發布 /acp spawn/acp cancel/acp steer/acp close/acp sessions

第 5 階段:遷移與轉換

  • 引入向 SessionEntry.acp 投影加 ACP SQLite 事實來源的雙寫
  • 新增舊有 ACP 中繼資料列的遷移工具
  • 將讀取路徑翻轉為 ACP SQLite 優先
  • 移除依賴遺漏 SessionEntry.acp 的舊有回退路由

第 6 階段:穩固化、SLO 與規模限制

  • 強制並行限制(全域/帳號/session)、佇列策略和逾時預算
  • 新增完整的遙測、儀表板和告警閾值
  • 混沌測試崩潰復原和重複投遞抑制
  • 發布後端故障、DB 損壞和過時綁定修復的運維手冊

完整實作檢查清單

  • 核心控制平面模組和測試
  • DB 遷移和回滾計畫
  • ACP manager API 跨分派和指令的整合
  • 外掛執行時橋接中的轉接器登錄介面
  • acpx 轉接器實作和測試
  • 支援執行緒的頻道投遞投影邏輯搭配檢查點重播(優先 Discord)
  • 重置/刪除/歸檔/unfocus 的生命週期掛鉤
  • 過時綁定偵測器和面向操作者的診斷
  • 所有新 ACP 鍵的設定驗證和優先順序測試
  • 營運文件和故障排除手冊

測試計畫

單元測試:

  • ACP DB 交易邊界(spawn/bind/入隊原子性、cancel、close)
  • session 和執行的 ACP 狀態機轉換防護
  • 所有 ACP 指令的冪等預留/重播語義
  • 逐 session actor 的序列化和佇列排序
  • acpx 事件解析器和區塊合併器
  • 執行時監督器重啟和退避策略
  • 設定優先順序和有效 TTL 計算
  • 核心 ACP 路由分支選擇和後端/session 無效時的預設拒絕行為

整合測試:

  • 假 ACP 轉接器行程用於確定性的串流和取消行為
  • ACP manager + 分派整合搭配交易式持久化
  • 執行緒綁定入站路由至 ACP session key
  • 執行緒綁定出站投遞抑制父頻道重複
  • 檢查點重播在投遞失敗後恢復並從最後事件繼續
  • 外掛服務登錄和 ACP 執行時後端的拆卸

Gateway 端對端測試:

  • 建立帶執行緒的 ACP,進行多回合 prompt,unfocus
  • 使用持久化 ACP DB 和綁定的 gateway 重啟,然後繼續同一 session
  • 在多個執行緒中的並行 ACP session 無串擾
  • 重複指令重試(相同冪等鍵)不產生重複的執行或回覆
  • 過時綁定場景產生明確錯誤和選用的自動清理行為

風險與緩解

  • 轉換期間的重複投遞
    • 緩解:單一目的地解析器和冪等事件檢查點
  • 負載下的執行時行程頻繁變動
    • 緩解:每個 session 長期存活的擁有者 + 並行上限 + 退避
  • 外掛缺失或設定錯誤
    • 緩解:明確的操作者錯誤和預設拒絕的 ACP 路由(不隱式回退至一般 session 路徑)
  • subagent 和 ACP 開關之間的設定混淆
    • 緩解:明確的 ACP 鍵和包含有效策略來源的指令回饋
  • 控制平面儲存區損壞或遷移 bug
    • 緩解:WAL 模式、備份/還原掛鉤、遷移冒煙測試和唯讀回退診斷
  • Actor 死鎖或信箱飢餓
    • 緩解:看門狗計時器、actor 健康探測和帶拒絕遙測的有界信箱深度

驗收檢查清單

  • ACP session spawn 可在支援的頻道轉接器中建立或綁定執行緒(目前為 Discord)
  • 所有執行緒訊息僅路由至已綁定的 ACP session
  • ACP 輸出以串流或批次方式出現在相同的執行緒身分中
  • 綁定回合不在父頻道產生重複輸出
  • spawn+bind+初始入隊在持久儲存中為原子操作
  • ACP 指令重試為冪等的,不重複產生執行或輸出
  • cancel、close、unfocus、archive、reset 和 delete 執行確定性的清理
  • 崩潰重啟保留對應關係並恢復多回合的延續性
  • 並行的執行緒綁定 ACP session 獨立運作
  • ACP 後端遺失狀態產生清晰可操作的錯誤
  • 過時綁定被偵測並明確呈現(搭配選用的安全自動清理)
  • 控制平面指標和診斷可供操作者使用
  • 新的單元、整合和端對端覆蓋通過

附錄:當前實作的針對性重構(狀態)

以下是非阻塞的後續工作,用於在目前功能集落地後保持 ACP 路徑的可維護性。

1) 集中 ACP 分派策略評估(已完成)

  • 透過 src/acp/policy.ts 中的共用 ACP 策略輔助實作
  • 分派、ACP 指令生命週期處理器和 ACP spawn 路徑現在使用共用策略邏輯

2) 依子指令領域分割 ACP 指令處理器(已完成)

  • src/auto-reply/reply/commands-acp.ts 現在是精簡的路由器
  • 子指令行為分割為:
    • src/auto-reply/reply/commands-acp/lifecycle.ts
    • src/auto-reply/reply/commands-acp/runtime-options.ts
    • src/auto-reply/reply/commands-acp/diagnostics.ts
    • 共用輔助在 src/auto-reply/reply/commands-acp/shared.ts

3) 依職責分割 ACP session manager(已完成)

  • manager 分割為:
    • src/acp/control-plane/manager.ts(公開外觀 + 單例)
    • src/acp/control-plane/manager.core.ts(manager 實作)
    • src/acp/control-plane/manager.types.ts(manager 型別/依賴)
    • src/acp/control-plane/manager.utils.ts(正規化 + 輔助函式)

4) 選用的 acpx 執行時轉接器清理

  • extensions/acpx/src/runtime.ts 可分割為:
  • 行程執行/監督
  • ndjson 事件解析/正規化
  • 執行時 API 介面(submitcancelclose 等)
  • 提升可測試性並讓後端行為更容易稽核