ACP の内部
目次
ACP アダプタは、Hermes の同期で動く AIAgent を、非同期の JSON-RPC stdio サーバーで包んだものです。
実装の中心となるファイルは次のとおりです。
acp_adapter/entry.pyacp_adapter/server.pyacp_adapter/session.pyacp_adapter/events.pyacp_adapter/permissions.pyacp_adapter/tools.pyacp_adapter/auth.py
起動の流れ
hermes acp / hermes-acp / python -m acp_adapter
-> acp_adapter.entry.main()
-> parse --version / --check / --setup before server startup
-> load ~/.hermes/.env
-> configure stderr logging
-> construct HermesACPAgent
-> acp.run_agent(agent, use_unstable_protocol=True)標準出力は ACP の JSON-RPC のやり取り専用です。人が読むログは標準エラー出力へ送られます。
主な構成要素
HermesACPAgent
acp_adapter/server.py が ACP のエージェントプロトコルを実装しています。
役割は次のとおりです。
- 初期化と認証
- セッションの新規作成・読み込み・再開・分岐・一覧・中止の各メソッド
- プロンプトの実行
- セッションのモデル切り替え
- 同期で動く AIAgent のコールバックを、ACP の非同期の通知につなぐこと
SessionManager
acp_adapter/session.py が、動いている ACP のセッションを管理します。
セッションごとに次を持ちます。
session_idagentcwdmodelhistorycancel_event
このマネージャはスレッドセーフで、次の操作に対応します。
- 作成
- 取得
- 削除
- 分岐
- 一覧
- 片づけ
- 作業ディレクトリの更新
イベントの橋渡し
acp_adapter/events.py が、AIAgent のコールバックを ACP の session_update イベントに変換します。
橋渡しされるコールバックは次のとおりです。
tool_progress_callbackthinking_callback(ACP の橋渡しでは今のところNoneに設定されていて、推論の内容はstep_callbackを通して送られます)step_callback
AIAgent はワーカースレッドで動く一方、ACP の入出力はメインのイベントループにあるため、橋渡しには次を使います。
asyncio.run_coroutine_threadsafe(...)承認の橋渡し
acp_adapter/permissions.py が、危険なターミナル操作の承認の問いかけを、ACP の許可の要求に合わせます。
対応は次のとおりです。
allow_once-> Hermes のonceallow_always-> Hermes のalways- 拒否の選択肢 -> Hermes の
deny
待ち時間切れと橋渡しの失敗は、既定で拒否になります。
ツールの表示を助けるしくみ
acp_adapter/tools.py が、Hermes のツールを ACP のツールの種類に対応づけ、エディタに見せる内容を組み立てます。
例を挙げます。
patch/write_file-> ファイルの差分terminal-> シェルのコマンドの文字列read_file/search_files-> テキストの下読み- 大きな結果 -> 画面が壊れないよう切り詰めたテキストの塊
セッションの一生
new_session(cwd)
-> create SessionState
-> create AIAgent(platform="acp", enabled_toolsets=["hermes-acp"])
-> bind task_id/session_id to cwd override
prompt(..., session_id)
-> extract text from ACP content blocks
-> reset cancel event
-> install callbacks + approval bridge
-> run AIAgent in ThreadPoolExecutor
-> update session history
-> emit final agent message chunk中止
cancel(session_id) は次のように動きます。
- セッションの中止イベントを立てる
- 使えるときは
agent.interrupt()を呼ぶ - プロンプトの応答が
stop_reason="cancelled"を返すようにする
分岐
fork_session() は、メッセージの履歴を深くコピーして新しいセッションを作ります。会話の状態はそのまま引き継ぎつつ、分岐した側には独自のセッション ID と作業ディレクトリを与えます。
プロバイダと認証の扱い
ACP は独自の認証情報の保管場所を持ちません。
代わりに、Hermes の実行時の解決のしくみをそのまま使います。
acp_adapter/auth.pyhermes_cli/runtime_provider.py
そのため ACP は、今 Hermes に設定されているプロバイダと認証情報を、そのまま告知して使います。加えて、ターミナルでのセットアップという認証方法(hermes-setup、引数は --setup)を常に告知するので、初めて使う ACP のクライアントは、通常の ACP セッションを始める前に Hermes の対話式のモデル・プロバイダ設定を開けます。
作業ディレクトリの結びつけ
ACP のセッションは、エディタ側の作業ディレクトリを持ちます。
セッションマネージャは、その作業ディレクトリを、タスク単位のターミナル・ファイルの上書き設定を通して ACP のセッション ID に結びつけます。これにより、ファイルとターミナルのツールはエディタの作業場所を基準に動きます。
同じ名前のツールが重なって呼ばれるとき
イベントの橋渡しは、ツール ID をツール名ごとに先入れ先出しで管理します。名前ごとに 1 つだけ持つのではありません。これは次の場合に効いてきます。
- 同じ名前のツールが並行して呼ばれるとき
- 1 つのステップで同じ名前のツールが繰り返し呼ばれるとき
先入れ先出しの待ち行列がないと、完了のイベントが別の呼び出しに結び付いてしまいます。
承認コールバックの復元
ACP は、プロンプトの実行中だけターミナルのツールに承認のコールバックを差し込み、終わったら元のコールバックに戻します。これにより、ACP のセッション固有の承認の処理が、いつまでも全体に居座ることを防ぎます。
今のところの制限
- ACP のセッションは共有の
~/.hermes/state.db(SessionDB)に保存され、プロセスを再起動しても自動で戻ります。session_searchにも出てきます - テキスト以外のプロンプトの塊は、今のところ本文の取り出しでは無視されます
- エディタごとの使い勝手は、ACP クライアントの実装によって変わります
関連するファイル
tests/acp/— ACP のテスト一式toolsets.py—hermes-acpツールセットの定義hermes_cli/main.py—hermes acpのサブコマンドpyproject.toml—[acp]の追加依存とhermes-acpのスクリプト