Skip to content

ACP プロトコル

源码版本v2026.7.20

職務

ACP(Agent Client Protocol)はエディタ/IDE と agent が通信する標準プロトコル。acp_adapter/ が Hermes を ACP server として露出し、ACP 対応クライアント(Zed、VS Code 拡張など)が直接 Hermes を駆動できる:セッション (session) 管理、リソース読み書き(ファイル/画像)、編集承認、由来(provenance)の追跡、権限でツールを隔離。これにより Hermes はメッセージプラットフォームにとどまらず、開発ワークフローにも組み込める。

設計動機

Hermes は元々メッセージプラットフォーム向けに設計された——ゲートウェイ (gateway) 層は Telegram/Discord のような長会話を受ける。しかし IDE は別物だ:作業ディレクトリはエディタが開いたもので、cwd はエディタに追従する。ファイル変更は agent が変えると言って変えるのでなく、ユーザーに一回見せなければならない。セッション間では「この圧縮 (compression) はどのセッションから来たか」を追跡できる必要がある。ACP 層はこうした IDE 固有の制約を Hermes 既存のプリミティブに翻訳する。コードは agent ループをもう一セット書き直すのではなく、ACP メッセージストリームを run_conversation に接続し、ACP リソースを OpenAI content parts に変換し、ツール呼び出し (tool dispatch) のファイル書き込みを EditProposal に改造して人審査に回す。核心は「適合であって書き直しではない」ことだ。

主要ファイル

データフロー

  1. ACP クライアント(エディタ)が acp_registry/agent.json で Hermes ACP server を発見し起動(acp_adapter/__main__.py)。
  2. クライアントがセッションを開始 → SessionManager:175SessionState を生成、cwd を整列(acp_adapter/session.py:29)、toolsets を展開(acp_adapter/session.py:129)。
  3. クライアントがメッセージ/リソースを送る → リソース変換:201 が ACP resource(ファイル/画像、acp_adapter/server.py:96 のサイズ制限付き)を OpenAI content parts に変換し、run_conversation:588 に渡す。
  4. agent がファイルを変更したいとき、ツール呼び出しは build_edit_proposal:178EditProposal になり、承認リクエスタ(acp_adapter/edit_approval.py:51) でユーザーに確認を求める。
  5. ツール実行は acp_adapter/permissions.py の権限管理に従う。各操作は acp_adapter/provenance.py:22 で provenance を記録する。
  6. ストリーミング出力とイベント(acp_adapter/events.py) をクライアントに返す。

acp_registry/agent.json はクライアントが初めて Hermes を見るときの名刺だ:バージョン、配布方式(uvx)、起動コマンド。この層の契約は実際のバイナリと揃っていなければならず、さもないとクライアントが間違ったバージョンを入れて起動しなくなる。

cwd 翻訳は ACP シナリオで最も罠にはまりやすい場所だ:Windows クライアントが Hermes を WSL で走らせ、ワークスペースは E:\Projects\\wsl.localhost\... の UNC パスだったりする。_translate_acp_cwd がこれを POSIX に統一し、さもないと agent 内のすべての Path 操作が揃わなくなる。

編集提案は frozen dataclass で、鍵は old_text だ——_proposal_for_write_file が既存ファイルの内容を先に読み、diff 表示で「前/後」を見せ、盲変更を避ける。

python
@dataclass(frozen=True)
class EditProposal:
    tool_name: str
    path: str
    old_text: str | None
    new_text: str
    arguments: dict[str, Any]

承認リクエスタは ContextVar で束縛し、グローバル変数ではない。複数の並行 ACP セッションがそれぞれ自身の requester を持ち、混線しない。.env / id_rsa はデフォルトで workspace_session ではなく ask に回し、「ワークスペース全体を信頼する」ポリシーで誤って通るのを避ける。

provenance は IDE シナリオ特有のものだ:メッセージプラットフォームでは会話は一本の線だが、IDE では圧縮、分岐、resume される。build_session_provenanceparent_session_id を上に辿り、end_reason == 'compression' の層数を数え、sessionKindcompressionDepth をマークし、内部 Hermes session id と対外 ACP session id を _meta.hermes.sessionProvenance に一緒に置く。

境界と失敗

  • ハンドシェイクバージョンの不一致:agent.jsonhermes-agent[acp]==0.19.0 に固定している。Hermes が 0.20 に上がった後、クライアントが古いマニフェストをキャッシュしていると、起動した server とクライアントの schema が合わず、InitializeResponse のフィールドが欠けてハンドシェイクが失敗する。
  • WSL cwd 翻訳の抜け:translate_cwd_for_wsl_backend が新しい Windows パス形式をカバーしていないとき、agent は揃わない cwd を受け取る。すべての Path 操作がずれ、すぐにはエラーを出さず、最初のファイル書き込みで「ディレクトリが見つからない」と炸裂する。
  • 承認リクエスタのデッドロック:承認は ContextVar + ユーザーコールバックで走る。クライアントのイベントループが詰まって ACK を返さないと、build_edit_proposal の後の待ちがずっと残る。ACP プロトコル層の cancel がこの待ち点にまで伝播するかは、events.py の中断パスがカバーしているかによる。
  • provenance 鎖の切断:圧縮分岐で parent_session_id を書き間違えると、build_session_provenance は切れ目に当たった時点で root を返す。クライアントは continuation を新規セッションと誤表示し、履歴が「消えた」ように見える。

まとめ

ACP は Hermes をエディタワークフローに組み込む:セッション管理、リソース読み書き、編集承認、権限隔離、由来追跡。コアは server.py(プロトコル適合)+ session.py(セッション)+ edit_approval.py(人による編集承認)。ゲートウェイ層と補完的——ゲートウェイはメッセージプラットフォーム、ACP は IDE を接続する。

非公式コミュニティ学習サイト。MIT ライセンスの NousResearch/hermes-agent ソースに基づく。