Hermes Agent Wiki 非公式・日本語wiki

更新とアンインストール

更新する

いま動いているインストールの種類に合わせて、更新の方法を選んでください。

インストールの種類 更新の方法
管理されたソースのチェックアウト hermes update、またはソースからビルドしたデスクトップアプリの更新の受け渡し。
Windows のサイドロード版 MSIX デスクトップアプリの更新ボタンと、Windows の App Installer。
Microsoft Store のパッケージ Microsoft Store の更新。
macOS の同梱アプリ デスクトップアプリの更新ボタン(electron-updater 経由)。
Docker イメージ 選んだイメージを pull し、同じデータのマウントでコンテナを作り直す。
Nix flake やプロファイルを更新して再ビルドする。
Termux の APT pkg update のあとに pkg upgrade hermes-agent。

hermes update は、パッケージが持っているアプリのファイルを書き換えません。PM が扱うのは 依存関係であって、アプリの配布ではありません。hermes pm update はメンテナーが固定バージョンを 更新するためのコマンドで、アプリを更新する別の手段ではありません。

管理されたソースのインストールでは、次のコマンドで更新します。

hermes update

公式リポジトリのソースのチェックアウトは、stable チャンネルを追いかけます。つまり、 GitHub で公開された最新の vX.Y.Z リリース(下書き、プレリリース、canary のタグは対象外です)を、 そのコミットちょうどでチェックアウトします。hermes update もデスクトップアプリの更新ボタンも、 リリースからリリースへ進むだけで、コミット単位では動きません。フォークやミラーは、引き続き自分の main ブランチを追いかけます。更新では PM を通じて依存関係を用意し、設定の変更点とプロセスの 再起動結果を報告します。

hermes update --set-channel main     # follow every commit on main instead
hermes update --set-channel stable   # back to releases only
hermes update --channel main         # one run against main; nothing is saved

この選択は、インストールごとに config.yaml の update.installs に保存されます。チャンネルを一度も選んでいないインストールは 前にしか進みません。最新のリリースより新しいチェックアウト(たとえば、この既定になる前に main を 追いかけていたもの)は、さらに新しいリリースが出るまでその場に留まります。--set-channel stable を 実行すると、たとえ古くなる場合でも、チェックアウトをリリースのコミットちょうどに固定します。この とき Desktop はその移動をリリースへのロールバックと表示し、hermes update --check は新しい更新を 知らせる代わりに、チェックアウトのほうが新しいと伝えます。

インストーラー(install.sh、install.ps1、Hermes-Setup)も、新しいインストールには最新の安定版リリースを 入れます。チャンネルを一度も選んでいないインストールにインストーラーをもう一度実行した場合、そのリリースが チェックアウトの子孫にあたるときだけ、そのリリースへ移します。それ以外のチェックアウト(main を追いかけて いたものや、すでにリリースより先にあるもの)はその場に留まり、移すかどうかは hermes update が決めます。 その際、チェックアウトがすでに使っているセッションの保存形式より古いリリースへの移動を 拒否します。main 以外のチャンネルを選んだインストールでは、もう一度実行してもチェックアウトはその場に 留まります。チェックアウトを動かすのは hermes update だけです。 代わりに main の最新を入れたい場合は、--channel main を渡します(install.ps1 では -Channel main、 irm | iex のワンライナーでは事前に HERMES_INSTALL_CHANNEL=main を設定します)。インストーラーはこの選択を、 hermes update --set-channel main と同じように保存します。明示的な --commit の固定は引き続き効きます。 Windows Store、MSIX、同梱デスクトップアプリのインストールは影響を受けません。これらのチャンネルは パッケージに焼き込まれています(下記を参照してください)。

同梱デスクトップアプリの更新

Windows では、サイドロード版のアプリが登録済みの App Installer の配信元を確認します。適用を選ぶと、 アプリが持っているバックエンドを止める前に .appinstaller の記述ファイルをダウンロードし、 そのローカルファイルを開いてから終了し、パッケージの置き換えを Windows に任せます。 切り離された待機プロセスが、パッケージのバージョンが変わったあとにアプリを自動で起動し直そうとします。 待機プロセスの登録に失敗した場合は、手動で開き直すよう警告が出ます。

macOS では、適用を選ぶと ZIP の更新をダウンロードし、署名済みのアプリを Squirrel.Mac が受け入れるのを 待ってからバックエンドを止めます。インストールと再起動を求めるのは、この適用の流れだけです。 普通に終了しただけでは、ダウンロード済みの更新はインストールされません。 ダウンロードや検証に失敗した場合、バックエンドは動いたままです。

Store のパッケージは、どちらのサイドロード用の配信でもなく Store を使います。更新の確認が壊れていたり 使えなかったりしても、それを「すでに最新」と受け取ってはいけません。 アプリと同梱の基本ランタイムは一緒に更新され、ユーザーのデータはパッケージの外に置かれたままです。 デスクトップアプリのインストール をご覧ください。

stable と canary のデスクトップ版は、別々のアプリです。canary を入れても stable は置き換わりません。 Windows の MSIX でも同じです。それぞれのアプリは自分のデスクトップの状態を持ち、ビルドに焼き込まれた チャンネルの中で更新されます。チャンネルを変えるには、設定を変えるのではなく、もう一方のアプリを入れます。 canary のアイコンは背景が黄色(ダークモードでは濃い黄色)で、CLI のコマンドは hermes-canary です。

特定のコミットから作った単発のビルドは、どちらのリリースチャンネルとも、ほかのコミット単位のビルドとも 別のアプリです。赤いアイコンにはビルドの短い SHA が表示され、同じ SHA が CLI のコマンド hermes-<short-sha> にも使われます。これらは更新を確認もインストールもしません。 hermes update や hermes update --check を使っても同じです。表示される説明は次のとおりです。

> This build doesn't get updates. Ask the developer who gave it to you for a new build.

別々のアプリでも、同じ Hermes ホームの下にある Hermes のプロファイル、設定、セッションは共有します。 1 つのプロファイルに対して違うビルドを動かしても、スキーマが分かれるわけではありません。新しいビルドは、 古いビルドが読めない形に保存データを変えることがあります。試す前に共有データをバックアップしてください。 デスクトップアプリと単体の CLI は、同じプロファイルを別の動作中のインストールが使っていると警告しますが、 これは注意喚起であってロックではありません。更新後のお知らせはアプリごとに分かれているので、 canary を起動しても stable 側の未読のお知らせが消費されることはありません。hermes:// の URL スキームは 共有のままで、最後に登録したアプリがリンクを処理します。

自動のお知らせを止める

バージョンを固定している環境や、対話操作をしないインストールでは、CLI のバージョン確認と起動時バナーの更新チェックを止められます。

hermes config set updates.check false

これで、キャッシュ済みの更新のお知らせと、更新確認のための通信の両方を出さなくなります。既定は true です。自分で実行する hermes update --check と hermes update はそのまま使えます。この設定はデスクトップアプリの更新機能には効きません。

更新中に何が起きるか

更新の対象として受け付けられたソースのチェックアウトでは、hermes update は次の順に処理を進めます。

  1. 更新前のスナップショット — Hermes は、プロファイルごとに選んだ状態ファイルを、そのプロファイルの state-snapshots/ ディレクトリに保存します。対象はペアリング情報、cron ジョブ、config.yaml、.env、auth.json などです。自動の quick スナップショットでは、1 GiB を超えるファイルは個別に除外されます。updates.pre_update_backup で quick、full、off のどれかを選びます。full のアーカイブは バックアップの除外対象 に従います。復旧は スナップショットとロールバック の手順で行います。quick スナップショットで戻せるのは状態ファイルで、アプリのコードではありません。スナップショットはできる範囲での処理です。失敗した場合は ⚠ Pre-update snapshot FAILED という警告を表示したうえで更新を続け、レシートには pre_update_backup が失敗した手順として記録されます(自分で off や --no-backup を指定した場合は、失敗ではなく理由付きで飛ばした処理の側に入ります)。
  2. コードの更新 — 設定されたソースのブランチ、または stable のリリースタグを当て、サブモジュールも更新します。Linux と macOS では、このインストールから動いているゲートウェイをすべて、チェックアウトに最初の変更を加える直前に一時停止します(更新中にゲートウェイが止まる時間 をご覧ください)。当てる変更が何もない更新や、チェックアウトを変える前に失敗した更新では、何も止めません。
  3. 取得後の構文チェックと自動ロールバック — 取得のあと、hermes の起動時に必ず読み込まれる重要な 9 ファイルをコンパイルします。どれか 1 つでも構文解析に失敗した場合(マージ競合マーカーの取り残し、途中で切れたファイルなど)、Hermes はインストールを巻き戻し、シェルが起動できる状態を保ちます。取得の前に動いていたコミットへチェックアウトを戻すか(git reset --hard <pre-pull-sha>)、更新が detached の状態や退避中の機能ブランチから切り替えていた場合は、git checkout --detach <sha> / git checkout <branch> でそこへ戻ります(もう一度チェックアウトできないブランチは、同じコミットの detached の状態で戻します)。上流で修正が入ったら、あらためて hermes update を実行してください。
  4. 依存関係の準備 — PM が必要なツールを用意し、新しいロックファイル、すでに入っている追加機能、有効なプラグインの要件から、完全な Python 環境を準備します。その環境を検証してから、選択として公開します。プラグインが原因で更新が失敗することはありません。新しい本体に合わなくなったプラグインは、それを有効にしているすべてのプロファイルで plugins.disabled に追加されます(メモリプロバイダーの場合は memory.provider が空になります)。対象になるのは、requires-python が Hermes の Python を含まないもの、manifest_version がこの Hermes の対応範囲より新しいもの、そして依存関係が本体や設定順で先にあるプラグインと一緒には解決できないと解決器が示したもの、または自分自身のビルドに失敗したものです。ダウンロードや通信の失敗は 1 回だけ再試行し、再び失敗したら無効にします。更新は ⚠ Disabled plugin '<name>' in <home>: <reason> と表示し、レシートの警告に記録して、処理を続けます。プラグインが対応するリリースを出したら、あるいは通信が戻ったら、hermes plugins enable <name> で有効に戻してください。requires_hermes の範囲に動作中のバージョンが入らない場合は、プラグインを無効にしません。リリースタグのないソースのチェックアウトは、古いリリースに見えることがあるからです。その場合プラグインは今回の更新から外れるだけで(⚠ Left plugin '<name>' … out of this update)、有効なままです。Hermes が受け入れられるバージョンを報告するようになれば、また加わります。設定を読めない 2 番目以降のプロファイルも、設定が直るまで同じように外れます。この手順が失敗するのは、本体だけでもビルドできない場合に限られます。
  5. 設定の移行 — 使用中のバージョン以降に追加された設定項目を検出し、値を設定するよう案内します
  6. デスクトップアプリの再ビルド(ステージしてから入れ替え) — Hermes Desktop アプリがこのチェックアウトからビルドされていた場合、GUI が新しいコードに合うよう再ビルドします。再ビルドはまず apps/desktop/release/ の隣にある一時的なステージング用ディレクトリに書き出し、そこで検証してから、前のビルドに上書きする形で名前を付け替えます(Windows でリアルタイム検査のソフトが release/win-unpacked を一時的に掴んでいる場合は、短い再試行を数回はさんでやり過ごします)。Electron のダウンロード破損、依存関係の不足、ディスク不足など、途中で失敗した場合は前のアプリがそのまま残り、起動できます。このとき更新は ⚠ の行を表示しますが、残りの手順(設定の移行、ゲートウェイの再起動)はそのまま終え、レシートに build の後追い項目を残して終了コード 0 で終わります。次の起動、hermes update、hermes desktop --build-only --force-build のどれかで再ビルドをやり直します。macOS では、再ビルドしたバンドルを ditto で(署名を保ったまま)古くなった /Applications/Hermes.app や ~/Applications/Hermes.app に上書きコピーします。Finder や Dock から起動するアプリが、バックエンドと同じものになるようにするためです。インストール済みのアプリが動作中の場合はそれに手を付けず、いったん終了してから hermes update をやり直すよう案内します。以前の更新で入れたコピーがその後削除されていた場合(ゴミ箱へドラッグした場合を含む)、更新は必要なら再ビルドしてそれを戻し、その旨を伝えます。これで Finder、Dock、Spotlight から再び Hermes が見つかるようになります。アプリを完全に消すには hermes uninstall を使います。Windows では、再ビルドの対象となるデスクトップアプリの中から更新が完了する場合(中断された更新をアプリが起動時に仕上げる場合)、再ビルドは行わず、その旨をお知らせします。Windows は動作中のアプリのファイルをロックするうえ、アプリを止めると更新もいっしょに終わってしまうためです。更新の残りの部分は完了します。再ビルドして開き直すには、Hermes Desktop を終了してターミナルから hermes desktop を実行するか、Settings → About の Update now を使ってください。
  7. ゲートウェイの自動再起動: Linux と macOS では、更新のために一時停止したゲートウェイを、依存関係の準備と起動用スクリプトの更新が済みしだい、製品のビルドより前に起動し直します。Windows ではビルドのあとに再起動します。それ以外の動作中のゲートウェイは、更新の完了後に入れ替えます。サービスとして管理されているゲートウェイ(Linux なら systemd、macOS なら launchd)は、サービスマネージャー経由で再起動します。手動で起動したゲートウェイは、その PID をプロファイルに紐付けられた場合に立ち上げ直します。手動で起動した hermes serve / hermes dashboard のバックエンドは扱いが違います。更新処理はそれらを動かしたままにして、持ち主に再起動をお願いします。手動で起動したバックエンドの再起動のお知らせ をご覧ください。動作中のデスクトップアプリが持っているバックエンドは、引き続きアプリ側の担当です。
  8. 多重化への移行(複数プロファイルのインストール) — 新しいコードでの動作が一通り確認できたあと、プロファイルが 2 つ以上あり、いまも プロファイルごとに 1 つのゲートウェイ を動かしているインストールは、妨げになるものがなければ、多重化した既定のゲートウェイ 1 つにまとめられます(hermes gateway migrate --multiplex --yes と同じです)。妨げになるもの(2 つのプロファイルで同じ Bot トークンを使っている、/p/<profile>/ の受け口を持たない 2 番目以降のプロファイルがポートを使っている、など)がある場合は、その内容と直し方を表示するだけで、何も変更しません。プロファイルが 1 つだけのインストールには一切手を加えません。詳しくは プロファイルごとのゲートウェイからの移行 をご覧ください。

更新中にゲートウェイが止まる時間

Linux と macOS では、hermes update はこのインストールから動いているゲートウェイを、チェックアウトに最初の変更を加える直前にすべて止め、依存関係の準備が済んだところで、止めたものとまったく同じ組を新しいコードで起動し直します(数分かかることもある Web 画面、TUI、デスクトップ版のビルドより前です)。そのあいだ Bot はオフラインになります。目安は、git での更新と依存関係の同期にかかる時間です。以前は、git が足元のファイルを書き換えているあいだもゲートウェイが古いコードのまま動き続け、その間に遅れて読み込んだモジュールだけは新しいコードのものになっていました。

  • 更新処理は、何を一時停止したかを示す行(⏸ Paused for the update: hermes-gateway.service)と、戻ったときの行(▶ Restarted paused gateway(s): …)をそれぞれ1行ずつ表示します。
  • systemd のユニットと launchd のエージェントは systemd/launchd を通して止めたり起動したりするので、更新の途中でサービスマネージャーが古いコードのまま再起動してしまうことはありません。手で起動したゲートウェイ(hermes gateway run)は、処理を出し切ってから、同じコマンドラインと同じ Hermes のホームでバックグラウンドに起動し直します。その出力はターミナルではなく、そのホームの logs/gateway-update-resume.log に書かれます。
  • チャットからの /update も一時停止を乗り切ります。ゲートウェイ自身の systemd ユニットの中で更新が動く場合、更新処理はまず自分を独立したスコープへ移すからです。ゲートウェイが止まっている間に出た確認は、中継できるゲートウェイがないため、すぐに既定の答えで進みます。
  • 一時停止した組は、最初に止める前に Hermes のホームへ記録されます。更新が強制終了された場合は、次の hermes コマンド(または hermes update)が、まさにそのゲートウェイを起動し直します。チェックアウトを一度も変えずに失敗した更新では、古いコードのまま起動し直します。チェックアウトが動いたあとで依存関係の同期に失敗した場合は、起動時に依存関係が同期されたところで起動し直します。
  • 更新処理が監視役を通して止められないゲートウェイ(s6 や独自の systemd ユニット、--external-supervisor、この更新を起動した launchd のジョブ)は動かしたままにし、これまでどおり更新のあとに入れ替えます。
  • --no-gateway-restart を付けると何も止めません。ゲートウェイの管理はご自身で行ってください。
  • 一時停止が更新を妨げることはありません。ゲートウェイの一覧を取る、記録する、止める、のどれかができなかった場合は、止めたものを起動し直してその旨を伝え、一時停止の仕組みができる前と同じ形で更新を続けます。つまり、ゲートウェイは入れ替えの最中も動き続け、終わってから再起動されます。

Windows では以前から、更新の前にゲートウェイを一時停止しています(依存関係の同期で置き換えるファイルをゲートウェイがロックするためです)。そして更新が完了したあと、ビルドのあとに再起動します。

ゲートウェイがセッションを持つ実行方式への移行

このリリースから、ホストごとに 1 つのゲートウェイが、ローカルのセッション(CLI、TUI、Desktop、 API、ACP、cron、Bot Chat)をすべて受け持ちます。既存の環境で最初に更新したときに起きることは次のとおりです。

  • cron.bot_chat_delivery_timeout_seconds は廃止されました。 Bot Chat の配信は、

宛先のプロファイルで動いているゲートウェイに受け付けられ、受領記録で追跡されます。cron の側で 時間切れを待つやり取りはもうありません。設定の移行 v51 がこのキーを削除し、一度だけお知らせを 出します(バージョンの書かれていない config.yaml からも削除します)。ゲートウェイが動いていない場合、その実行は delivery_failed として記録され、自動では配信し直されません。

  • 古い Bot Chat の保留レコード(cron/bot_chat_pending/*.json。廃止された

hermes chat の配信経路が残したもの)は、その後のスケジューラの周期で、元の ID のまま 宛先で動いている持ち主に引き渡されます。claimed のレコードは CLI でのやり取りが成否不明だったもので、 再送されることはありません。レコードは最終的な状態を付けたまま、証跡として残ります。

  • 切り替えのための再起動で中断された作業は、やり直されません。 ゲートウェイを初めて起動したとき、

やり取りの途中(started)だった入力と、動いていた管理下のワーカーは unknown になり、その後ろに並んでいる入力は待たされます。セッションの中身を確かめたうえで、後続の入力が動く前に、 hermes chat で /discard <admission_id> を実行する(Desktop や TUI では同等の操作)か、 スクリプトから hermes sessions discard <session> --yes を実行して、了承済みにしてください。ゲートウェイが引き受けたまま終えていなかった cron の配信も、同じように unknown で止められ、delivery_failed として記録されます。

  • 新しいゲートウェイが state.db に書き込んだあとは、元のリリースへ戻すことはサポートされません。

古いリリースへ戻すを参照してください。

古いリリースへ戻す

ゲートウェイの実行方式が書き込んだ state.db の上では、古いリリースは動きません。そうした操作が 起こりうる箇所では、それぞれ履歴を壊す代わりに実行を拒否します。

  • hermes update は、いずれかのプロファイルの state.db にゲートウェイの実行方式の履歴が

あるあいだ、それより前の更新先(--branch、チャンネルの切り替え、固定したリリース)を 受け付けません。チェックアウトに触れる前に止まり、影響を受ける保存先と、元に戻す方法を 一覧で示します。

  • 更新済みの保存先の上で動かした古いリリース(古いタグを手で git checkout した場合)は、

ゲートウェイの実行方式の履歴があるセッションを削除も整理もできません。hermes sessions delete と prune は Refused to delete a session that has gateway runtime history で止まります。 古いリリースではこのメッセージがトレースバックの中に出るままで、prune はまとめて処理していた分を すべて取り消します。

  • 動いているゲートウェイは、チェックアウトがそうしたリリースへ戻されると、約 30 秒以内に

自分で止まり、古いリリースが自分のゲートウェイを起動できるようにします。

それでも戻したい場合は、更新済みのデータベースの上で古いリリースを動かすのではなく、 更新前の状態を復元してください。

  1. hermes gateway stop
  2. 古いリリースをチェックアウトします。
  3. 更新前の最新のスナップショットを復元する(/snapshot restore <id>)か、

hermes update --backup で取った完全バックアップを復元します(hermes import <backup.zip>)。クイックスナップショットは 1 GiB を超える state.db を含めないので、大きな保存先には完全バックアップが必要です。

更新後に作られたセッションとメッセージは、そのスナップショットには含まれません。

ゲートウェイの再起動に時間がかかることがある理由

再起動は、処理を出し切ってから行います。動いているゲートウェイは新しいターンを受け付けなくなり、進行中の作業(チャットのターン、cron のジョブ、API の実行)が終わるのを待ってから終了します。待つ時間の上限は agent.restart_after_turn_timeout(既定は 30 分)で、長く走るジョブが途中で打ち切られることはありません。待っている間、アップデーターは 30 秒ごとに、ゲートウェイがまだ何を待っているかを表示します。たとえば次のとおりです。

  → hermes-gateway: draining (up to 1875s)...
  ⏳ still draining — 1560s left before the forced restart
     waiting on 1 active work unit(s):
       • cron job 6ba19dab68df (nightly-scout) in external worker pid 573597, running 6m40s
     finish or kill the work above to release the drain now; agent.restart_after_turn_timeout in config.yaml caps this wait

チャットのターンにはセッションのキー、モデル、いま使っているツールが出ます。cron のジョブにはジョブの ID、名前、それを動かしているプロセス(systemd のインストールでは再起動に耐える外部のワーカー、それ以外ではゲートウェイ自身)が出ます。ゲートウェイが処理を出し切っている間は、hermes gateway status でも同じ作業の一覧を見られます。待つのをやめたいときは、一覧に出ている作業を終わらせるか止めるか、config.yaml の agent.restart_after_turn_timeout を小さくしてください(0 にすると、すぐに強制的な出し切りに入ります)。

引っかかったまま進まない作業は、再起動を引き止めません。agent.gateway_timeout を超えて何も起きていないチャットのターンや、スケジューラーが認める進行中の上限(max(2 × the job's interval, cron.inflight_max_minutes)。既定では 30 分)より古い cron の実行は、待つ対象から外され、再起動によって打ち切られます。

ゲートウェイより長く生き延びる作業も、再起動を引き止めません。すでに独自の systemd スコープ(systemd-run --user --scope。systemd でインストールした場合の通常の形です)を持つワーカーに渡された cron の実行は、ゲートウェイが止まっても動き続け、その結果は次に立ち上がったゲートウェイが永続キューから届けます。そのため、再起動は上限いっぱいまで待たずに進みます。独自のスコープを得られなかったワーカー(ユーザーの D-Bus セッションに接続できない場合です。cron.require_restart_safe_scope を参照)は、ゲートウェイの cgroup にとどまり、引き続き待つ対象になります。再起動すると実行の途中で終了させてしまうからです。

Windows で更新用のファイルが見つからないとき

保守されている更新スクリプトが見当たらない場合(たとえばウイルス対策ソフトに隔離されたときです)、旧来の更新の受け渡し処理は、引き渡しに成功したと報告せずに失敗します。もう一度試す前に、インストールを修復し、セキュリティソフトの隔離レポートを確認してください。ウイルス対策そのものを止めるのはやめてください。保守されている更新処理は、成功を報告する前に、CLI の読み込み、Windows の実行ファイルのヘッダー、ASAR のヘッダーと同梱された main のエントリ、ローカルのモジュールエントリを持つ読み取り可能な画面側の HTML、最初のモジュールファイル、現在のビルド刻印を確かめます。これらは最低限の成果物チェックであって、依存関係の全数監査でも、アプリやバックエンドを実際に起動する試験でもありません。Python が見つからないことは、デスクトップアプリの終了を待つ前に報告します。依存関係の修復は、更新の一部としてそのまま実行できます。Electron は、その構成であればバックエンドを止める前に受け渡しの前提条件を確かめます。旧来のフラットな更新構成も引き続き使えるため、更新用のファイルの欠落がすべてバックエンド停止の前に見つかるわけではありません。

Windows では、パッケージ作成の途中で開き直されたデスクトップアプリを、用意したビルドへ切り替える直前にもう一度停止します。この後始末の対象は、そのチェックアウトのデスクトップリリース配下にある実行ファイルだけで、無関係なインストールは止めません。ロックが残っていた場合は、名前の付け替えのエラーを見逃さずに、切り替えそのものを失敗させます。

取得が should_include_obj should only be called on existing objects で失敗するとき

Git 2.53 以降では、部分クローンへの取得(fetch)の最中に、一部のパックファイルに .promisor の目印が無いと Git がクラッシュすることがあります。これは、絞り込み付きの取得によって完全なクローンや浅いクローンが部分クローンに変わったときや、 目印が失われたときに起こります。hermes update はそうしたパックに目印を付けて取得を 1 回だけやり直し、 インストーラーを再実行したときは取得の前に目印を付けます。更新処理そのものがこの修正より古いインストールでは、その修正を取得できません。インストーラーを再実行するか、Hermes を閉じた状態で手作業でパックに目印を付けてから、もう一度更新してください。

チェックアウトは、Hermes のホーム(~/.hermes、HERMES_HOME を設定していればそちら。 Windows では HERMES_HOME を設定していない限り %LOCALAPPDATA%\hermes)の下にある hermes-agent です。

# macOS / Linux
repo="${HERMES_HOME:-$HOME/.hermes}/hermes-agent"
for p in "$repo"/.git/objects/pack/pack-*.pack; do [ -e "${p%.pack}.promisor" ] || : > "${p%.pack}.promisor"; done
# Windows
$repo = Join-Path ($(if ($env:HERMES_HOME) { $env:HERMES_HOME } else { "$env:LOCALAPPDATA\hermes" })) 'hermes-agent'
Get-ChildItem "$repo\.git\objects\pack\pack-*.pack" | ForEach-Object {
  $m = [IO.Path]::ChangeExtension($_.FullName, '.promisor')
  if (-not (Test-Path -LiteralPath $m)) { New-Item -ItemType File -Path $m | Out-Null }
}

チェックアウトは部分クローンのままです。クラッシュを避けるために remote.origin.promisor や partialclonefilter を消さないでください。 リリースのタグや更新時のバックアップからしかたどれないオブジェクトは一度もダウンロードされていないので、部分クローンでなくなったチェックアウトでは git gc が bad tree object で失敗します。すでに消してしまった場合は、 足りないオブジェクトを取得し、残りが無いことを確かめてから、自動の後片付けを元に戻してください。

git -C "$repo" rev-list --objects --missing=print --all | grep '^?' | cut -c2- | git -C "$repo" fetch -q --no-tags --stdin origin
git -C "$repo" rev-list --objects --missing=error --all >/dev/null && echo complete
git -C $repo rev-list --objects --missing=print --all | Where-Object { $_.StartsWith('?') } | ForEach-Object { $_.Substring(1) } | git -C $repo fetch -q --no-tags --stdin origin
git -C $repo rev-list --objects --missing=error --all | Out-Null; $LASTEXITCODE   # 0 = complete

部分クローンで .git が大きくなり続けるとき

インストーラーが作るチェックアウトは blob なしの部分クローンです。コミットとディレクトリの一覧はすべて手元にあり、 git はファイルの中身を必要になった時点でダウンロードし、そのたびに小さなパックを1つずつ書き出します。 2026 年 9 月下旬のインストーラーは、代わりにツリーなし(--filter=tree:0)のクローンを作っていました。git は足りないツリーを要求するとき、 すでに持っているツリーを伝えないため、ツリーなしのチェックアウトではチェックアウトのたびや、パスで絞った履歴をたどるたびに、 ディレクトリ全体のスナップショットをまたダウンロードしていました。hermes update はそうしたチェックアウトを1回だけ変換します。変換は、新しいコードを取得した直後、依存関係の作業より前の最初の手順として行います。git fetch --refetch --filter=blob:none を1回実行してすべてのコミットとツリー(約 120 MB)を取得するので、以後の更新ではそれらを再ダウンロードしなくなります。そうしたチェックアウトの上でインストーラーを再実行しても、 同じように変換されます。この取得が失敗した場合、更新は警告を表示してそのまま続け、次回の更新で 変換をやり直します。

この変換ができる前にビルドされた Hermes Desktop は、パスで絞った履歴のたどりを数分おきに実行します。そのため ツリーなしのチェックアウトでは、パックファイルでディスクを埋めてしまうことがあり(434 GB に達したという報告もあります)、 更新に失敗したあとも止まりませんでした。すでにそうなってしまったときは、次の手順で復旧します。

  1. Hermes Desktop を終了してから、残っている git rev-list、git maintenance、

git commit-graph のプロセスを終了させます。アプリを終了しても、これらのプロセスは止まりません。

  1. ディスクが完全に埋まっている場合は、放置された転送用のファイルを消して空きを作ります。

rm -f "$repo"/.git/objects/pack/tmp_pack_* を実行してください(Windows では .git\objects\pack の中の tmp_pack_* を削除します)。git のプロセスが動いていなければ、更新のたびにこれらは自動でも削除されます。

  1. hermes update を実行します。まずチェックアウトを変換し、そのあと今回と以後の更新で、

下で説明するとおりにたまったパックを片付けていきます。

hermes update そのものが起動できないときは、次のコマンドで手作業で変換します。

git -C "$repo" fetch --refetch --filter=blob:none origin
git -C "$repo" config remote.origin.partialclonefilter blob:none
``` `hermes update` and `hermes update --check`
set `maintenance.commit-graph.enabled`, `gc.writeCommitGraph` and `fetch.writeCommitGraph` to
`false` in that checkout, because a commit-graph write over commits the graph has not seen yet
downloads every one of their trees. Leave those settings alone, and leave `gc.auto` at its
default so git's own automatic gc can still fold packs.

Each `hermes update` also spends at most 60 seconds cleaning those packs up, picking up where the
previous update stopped. It deletes packs whose every object is also stored in another pack, then
merges the smallest remaining packs while there are more than 50. Nothing stored locally is ever
lost, and nothing depends on GitHub still serving it. Packs that a killed `git fetch` left pinned
with a `.keep` file are included; git's own repack never touches those. To fold
everything at once by hand instead (with Hermes closed; on a large checkout this is a full repack
that can run for many minutes):

```bash
git -C "$repo" -c gc.writeCommitGraph=false gc --auto

既定以外のブランチに対して更新する: --branch

--branch NAME は、選んでいるチャンネルの代わりにブランチの先端に対して更新する、 その 1 回だけの上書き指定です。

hermes update --branch release-candidate
hermes update --check --branch experimental   # preview behindness only

手元のチェックアウトが別のブランチにある場合、Hermes はコミットしていない作業を自動で stash し、HEAD を目的のブランチへ切り替えてから取得します。ローカルに存在しないブランチは origin/<name> から自動で追跡します(git checkout -B <name> origin/<name>)。どこにも存在しないブランチを指定した場合はきれいに失敗し、stash した変更は終了前に戻るので、中途半端な状態に取り残されることはありません。main でのみ働くフォークと上流の同期処理は、main 以外のブランチでは自動的に飛ばされます。

機能ブランチに置きっぱなしのチェックアウト

ソースのチェックアウトが機能ブランチに置かれたままになっていた場合(ツールの都合、worktree での試行、手動での切り替えなど)、作業ツリーがきれいであれば hermes update が自動で更新対象のブランチへ戻します。

  • ブランチがすべてマージ済み(すべてのコミットが既に origin/main に含まれていて、git cherry が未マージのものを報告しない場合): 更新はその旨を Checkout was parked on '<branch>' (fully merged) — switched back to main と伝え、そのあとは main に留まります。
  • 未マージのコミットがあるが作業ツリーはきれいな場合: 更新を進めるために、それでも main へ切り替えます。デスクトップの更新ボタン、ゲートウェイの /update、cron といった対話できない呼び出し元は、中断を解決する手段がないためこの動作に頼っています。コミットした内容には手を付けません。git checkout がコミット済みの作業を捨てることはなく、更新はブランチ名とコミット数、そしてあとで作業を再開するための git checkout <branch> コマンドを目立つ形で表示します。

独自のブランチを *意図して* 運用している場合(main の上に自前のパッチを載せて維持しているようなケース)は、config.yaml に updates.parked_branch_strategy: update_in_place を設定してください。更新はブランチから離れる代わりに、origin/main をそのブランチ へ マージします。チェックアウトは動かず、コミットも残り、動作するコードだけが進みます。可能なら fast-forward し、履歴が分かれている場合は pre-update-<stamp> という安全用のタグを付けたうえで通常のマージを行い、競合したときは何も変更せずきれいに停止します。hermes update --switch-branch を使うと、その 1 回だけ切り替え方式に戻せます。更新由来のマージコミットを溜めたくない、深い機能ブランチで作業しているときに便利です。

置きっぱなしのブランチに コミットしていない変更 がある(作業ツリーが汚れている)場合、Hermes はそこに手を付け ません。コードの更新は SKIPPED として記録され、ブランチ名、origin/main からどれだけ遅れているか、解決するための具体的なコマンドを添えた警告が目立つ形で出ます。更新が成功したふりをすることはありません。完了行には常に実際のブランチと HEAD が表示される(✓ Update complete! [main @ 30fcf9580])ので、ずれがひと目で分かります。自動切り替えそのものを止めたい場合は、config.yaml に updates.auto_switch_parked_branch: false を設定してください(切り替えを飛ばした際の警告は引き続き出ます)。

更新先のブランチにあるローカルのコミット

更新先(main)に直接コミットしていると、上流が進んだ時点で fast-forward できなくなります。チェックアウトの側からは、そうしたコミットと上流の force-push を見分けられないため、更新は main を origin/main に合わせてリセットします。リセットの前に、元の HEAD を refs/hermes-update-backups/diverged-main-<stamp>-<sha> として保存し、その ref と、ブランチから外れるコミットの数を表示します。外れたコミットは git log origin/main..<ref> で一覧でき、git branch <name> <ref> か git cherry-pick で取り戻せます。既存のチェックアウトの上からインストーラーを実行し直した場合(install.sh / install.ps1。デスクトップアプリの初期セットアップもこれを行います)も、同じ ref が書き出されます。hermes update はこの ref を書くたびに、種類ごとに新しいものを 10 件残し、30 日より古いものを消します。パッチを更新のたびに持ち越したい場合は、独自のブランチに置いたうえで updates.parked_branch_strategy: update_in_place を使ってください。

対話できない更新でのローカル変更

端末から hermes update を実行した場合、Hermes はソースツリーのコミットしていない変更を stash し、取得したあとに戻すかどうかを 尋ねます。これは以前からの動作そのままで、対話的な更新では何も変わりません。

自動の stash が対象にするのは、*ソースツリー* の変更だけです。git のチェックアウトの最上位が $HERMES_HOME を兼ねている フラットなインストール(たとえば HERMES_INSTALL_DIR=$HERMES_HOME で入れたものや、古いインストーラーで作ったもの)では、プロファイルの実行時の状態(state.db とその WAL/SHM の付随ファイル、state-snapshots/、backups/、sessions/、cron/jobs.json、cron/*.db の保存先、config.yaml、auth.json、memories/、ロックや pid のファイルなど)が、追跡されていないファイルとしてチェックアウトの中に置かれています。これらのパスは git の無視対象なので、自動の stash が触れることはなく、動いているゲートウェイは更新の間もデータベースを保ったままです。フラットなインストールの最上位にほかの追跡されていないファイルを置いている場合は、チェックアウトの外へ移すか、.git/info/exclude に追加してください。追跡されておらず無視対象でもないものは、ソースの編集と同じように自動の stash に取り込まれます。

端末のない状態で更新が走る場合 — デスクトップやチャットアプリの「Update」ボタン、あるいはゲートウェイ経由の更新 — は、答えるべき問いかけが出せません。stash した変更をどう扱うかは、updates.non_interactive_local_changes の設定で決まります。

# ~/.hermes/config.yaml
updates:
  non_interactive_local_changes: stash   # default: keep + auto-restore
  # non_interactive_local_changes: discard  # throw local source edits away
  • stash(既定) — 自動で stash し、取得し、更新後のコードの上に変更を自動で戻します。何も失われません。戻すときに競合した場合は git の stash に残るので、あとから手作業で回収できます。
  • discard — 自動で stash し、取得後にその stash を捨てるので、更新は常にきれいなツリーに着地します。Hermes のソースへのローカルな変更をまったく残すつもりがない端末だけで使ってください。処理は stash の削除であって git reset --hard + git clean -fd ではないため、node_modules、venv、ビルド成果物のような無視対象のパスには一切触れません。

デスクトップアプリでは Settings → Advanced → In-App Update Local Changes に相当します。

デスクトップからの更新では自動復元を行いません。 デスクトップの更新処理は hermes update --keep-stash を呼びます。更新を進めるためにローカルのソース変更は stash されますが、そのあと戻すことは ありません。変更は git stash に置かれたままになり、更新ログには取り戻すための git stash apply <ref> コマンドがそのまま表示されます。こうすることで、ローカルの変更が黙ってデスクトップの更新に紛れ込み、更新したばかりのインストールを壊すのを防げます(破棄を選んでいる場合は non_interactive_local_changes: discard が優先されます)。置いたままの変更を手で戻すには、次のようにします。

cd ~/.hermes/hermes-agent   # or your install root
git stash list --format='%gd %H %s'   # find the hermes-update-autostash entry
git stash apply stash@{0}

端末での hermes update でも --keep-stash を渡せます。対話的な実行でも同じく「戻さない」動作にしたいときに使ってください。

確認だけする: hermes update --check

hermes update --check は、チェックアウトを選んでいるチャンネルの更新先(既定では最新の stable リリース、 main チャンネルでは origin/main)と比べるだけで、 コードの適用、依存関係のインストール、ゲートウェイの再起動は行いません。比べる際に Git のメタデータを取得することがあるので、ファイルシステムへの書き込みがまったくないとは限りません。 パッケージが持っているインストールでは、外部の更新方法を報告します。

まとめて事前確認する: hermes update --plan

複数のプロファイルやサービスが動いている端末を更新する前に、hermes update --plan を実行してください。インストールの種類、プロファイルをまたいで動作中の Hermes サービス、その管理役と動いているコードのバージョン、そしてサービスごとの再起動方法が表示されます。手動で起動した hermes serve / hermes dashboard のバックエンドも、記録されたバインド先とともに出てきますが、再起動は持ち主に委ねられます。更新処理がそれらを停止したり立ち上げ直したりすることはありません。イメージやパッケージで管理されているインストールでは、代わりに外部の更新コマンドが表示されます。この計画表示は読み取りだけなので、稼働中の環境でも安全です。

同じ一覧は、実際の更新ごとに書き出されるレシート(~/.hermes/logs/update_receipts/)にも埋め込まれています。更新後に、更新処理が見ていたものと実際に行ったことを突き合わせられます。

更新レシートとバージョンの照合

hermes update は実行のたびに、機械で読めるレシートを ~/.hermes/logs/update_receipts/ に書き出します(直近 20 件を保持し、latest.json は常に最新を指します)。中身は更新前の計画、実施した各手順、飛ばした処理とその理由、ゲートウェイ再起動の結果、そして最終的なバージョンの一覧です。SQLite の実行環境の修復もこの手順のひとつです(sqlite_runtime_repair)。修復に失敗した場合は実際の理由(たとえば uv sync のエラー)と SQLite のバージョンの組が記録され、先送りされた修復や対象外だった修復は、理由とともに飛ばした処理の側に入ります。再起動の段階を終えると、更新処理は動作中の各ゲートウェイのコードを更新後のチェックアウトと比べ、プロファイルごとの一覧を表示します。更新前のコードのまま動いているゲートウェイがあれば、具体的な再起動コマンドとともに目立つ形で報告し、レシートに gateway_restart の後追い項目として記録します。コードはすでに置き換わっているので更新自体は終了コード 0 で終わりますが、新しいコードで全体が動くようになるまで、以後のコマンドはすべて警告を出します。バージョンの混ざった状態が、黙って正常扱いされることはありません。--plan とバージョン照合はどちらも、可能であれば動作中の各ゲートウェイにローカルの制御ソケット(プロファイルのデータディレクトリにある gateway.sock、Windows では名前付きパイプ)経由で直接問い合わせるため、バージョンと管理役の情報はゲートウェイ本人から得られます。古いバージョンのゲートウェイは、従来どおり状態ファイルから見つけます。

多重化した既定のゲートウェイは、1 つのプロセスが複数のプロファイルを受け持つ形なので、一覧には 1 回だけ現れ、その served_profiles の記録にあるすべてのプロファイルを代表します。この同じ守備範囲によって「A previous hermes update pulled new code but did not restart running gateways」という案内も消えます。そのゲートウェイ(手動で git pull したあとであれば、更新が再起動したすべてのゲートウェイ)が現在のコードで動いていれば、hermes gateway restart だけで十分です。次の hermes update が新しいレシートを書き出すのを待つ必要はありません。どのゲートウェイに再起動の義務があったかを記録する前に力尽きた更新(あるいは、そもそもそれを記録しなかった古いアップデーター)が残した再起動の義務についても同じです。動いているゲートウェイがすべて現在のチェックアウトのコードで走るようになれば、その義務は片付けられ、案内も出なくなります。ゲートウェイを 1 つも動かしていないホスト(デスクトップアプリだけの場合)では、きちんと停止しないまま消えたゲートウェイを持つプロファイルがなく、動いているバックエンドがすべて自分の管理役によって再起動されるか、自前の覚え書きを持っていれば、義務は片付けられます。この義務はホストごとに 1 回だけ、プロファイルをまたいで待ち合わせるためのディレクトリ($HERMES_GATEWAY_LOCK_DIR、なければ $XDG_STATE_HOME/hermes/gateway-locks)に host-update-restart-<install key>.json として記録されます(インストールごとに 1 つなので、同じユーザーの 2 つ目の Hermes のチェックアウトは自分の記録を持ちます。古いリリースが書いたキーなしの host-update-restart.json は、自分の記録を持たないインストールだけが尊重して片付けます。そのため、まだそのリリースのままの別のチェックアウトの義務は残ります)。そのため、どのプロファイルの CLI からも同じ 1 件が見えます。hermes -p coder update と hermes -p writer update を実行しても、共有している多重化ゲートウェイの再起動は両方合わせて 1 回で済み、それぞれ 1 回ずつにはなりません。プロファイルごとに記録していた古いアップデーターが残した義務(あるプロファイルの Hermes ホームにある fleet_restart_pending)も、引き続き読み取られ、片付けられます。更新前の計画の時点でゲートウェイが 1 つも見つからなかった更新には義務がないので、目印も残しません。デスクトップアプリや systemd、launchd に管理されているバックエンドは、その管理役が再起動するので、この決着を妨げることはありません。義務が残り続けるのは、再起動の覚え書きを保存できなかった手動のバックエンドだけです。

手動で起動したバックエンドの再起動のお知らせ

更新のあとは、手動で起動したバックエンドそれぞれの持ち主に、hermes serve や hermes dashboard を立ち上げ直してもらってください。SSH 越しのバックエンドなら、デスクトップアプリをつなぎ直します。ゲートウェイを再起動しただけでは、これらのプロセスは新しくなりません。

動作が確認できているバックエンドについては、更新処理はその再起動を deferred と記録する前にお知らせを保存します。これにより、残りの確認が通れば更新を完了できます。プロセスの身元が分からない場合は、新たに先送りの扱いにはできません。

お知らせは、有効な Hermes のホーム配下にある serve_restart_pending/ に、入れ替わっていく更新レシートとは別に置かれます。お知らせは 1 つずつ、PID と作成時刻でプロセスを特定します。起動時の警告は、そのプロセスが生きている間、あるいは生死が分からない間は保持され、まさにそのプロセスが居なくなったと確認できたときにだけ取り除かれます。あとから更新しても、ゲートウェイが健全でも、取り除かれることはありません。

保存に失敗した場合、Hermes は警告を出し、保存できなかった宿題をあとのレシートへ引き継いで再試行します。保存先の権限と空き容量を確かめ、案内のとおりバックエンドを再起動してください。お知らせとレシートの両方の保存に失敗した場合は、確実に残せる保証はありません。

ゲートウェイの中から自動で更新する: --no-gateway-restart

ゲートウェイ*から*起動された更新(cron ジョブ、デスクトップ版の更新機能など、ゲートウェイの 子プロセスとして動く自動処理)は、自分が引き起こす一斉再起動を生き延びられません。ゲートウェイは SIGUSR1 を受けて処理を片付け、そのあと systemd の KillMode=mixed が cgroup に残ったものを 更新処理ごとすべて終了させるからです。hermes update --no-gateway-restart を使うと、取得、依存関係、 Node のワークスペース、Web 画面、保守作業までの一連の処理をすべて行い、再起動とバージョン照合だけを 飛ばします。Linux と macOS では、チェックアウトを変える前の一時停止も飛ばすので、何も止まりません。再起動待ちの目印は残るので、次に CLI を起動したときに警告が出て、次の通常の hermes update (または hermes gateway restart)で全体が追いつきます。再起動は別の手順として組み合わせてください。 たとえば、更新ジョブの 10〜15 分後に動くタイマーです。レシートには先送りしたことが記録され、 先送りだけが原因で古いままのゲートウェイが残っていても、更新が partial 扱いになることはありません。

ゲートウェイの再起動が途中で終わったとき

前回の更新がコードの取得までは終えたのに再起動をやり切れていない場合、次の hermes update は、チェックアウトがすでに最新でも再起動をやり直します。プロセスが見つからないことは、復旧できた証拠にはなりません。失敗した systemd のユニットや、登録済みの launchd のジョブには、動いている PID が無いこともあるからです。管理役を見つけられない、再起動に失敗した、あるいは対象のサービスが動いていると確認できないときは、印は再起動待ちのまま残ります。新しいコードはすでに置かれているので、更新は終了コード 0 で終わりますが、⚠ の行と、レシートの gateway_restart の後追い項目を残します。CLI は起動のたびに、残っている再起動について警告し、次の hermes update が再起動をやり直します。表示されたコマンドで対象のサービスを復旧させるか、hermes update をもう一度実行してください。

過去のレシートが失敗のまま残っていても、それだけでゲートウェイが古いままだとは言えません。起動時とゲートウェイ状態の警告、そして更新の追いつき処理は、そのレシートに記録されたゲートウェイのプロファイルすべてについて、現在のチェックアウトで動く後継がいるかを確かめます。手動でゲートウェイを起動し直せば、過去の結果を書き換えることなく、レシートだけを根拠にした案内を解消できます。手動で起動したバックエンドの宿題は、先に専用のお知らせへ引き継がれている必要があります。

再起動待ちの印は、更新前の実行時の一覧と目標のコミットを自分で記録しています。復旧の判定はその一覧を見るのであって、古いレシートの一覧を見ることはありません。解消と認めるには、確認できた現在の構成が空でないこと、記録されたゲートウェイのプロファイルすべてに目標のコミットで動く後継がいること、そして見つかった手動バックエンドの宿題が滞りなく処理されていることが必要です。手動で起動したバックエンドがまだ生きている、あるいは生死が分からない場合は、お知らせの保存が要ります。終了したと確認できたプロセスにはお知らせは要りません。ゲートウェイが見当たらない、対応していない実行環境である、お知らせの書き出しに失敗したといった場合は、印は再起動待ちのまま残ります。

一覧を持たない古い印や、壊れている・対応していない一覧は、起動時や追いつき処理の照合では自動的に解消できません。見えているゲートウェイをすべて再起動しても、その更新が何を残したのかを確かめたことにはならないからです。警告は、あとの更新が自分の一覧を持つ新しい印とともに完了し、きちんと解消されるまで残ります。

更新前の完全バックアップ: --backup

大事なプロファイル(本番のゲートウェイ、チームで共有しているインストールなど)では、取得前に HERMES_HOME を丸ごとバックアップする方式を選べます(設定、認証情報、セッション、スキル、ペアリングを含みます)。

hermes update --backup

毎回これを既定にすることもできます。

# ~/.hermes/config.yaml
updates:
  pre_update_backup: full

updates.pre_update_backup には 3 つのモードがあります。

  • quick は、上で説明した選ばれた状態ファイルを保存します。これが既定です。
  • full は、バックアップの除外対象 に従った zip のアーカイブを加えます。データのディレクトリが大きいと数分かかることがあります。
  • off は、更新前のバックアップを取りません。--no-backup を渡すと、その 1 回だけこのモードになります。

従来の真偽値も使えます。true は full、false は off を意味します。

Windows のプロセスの持ち主と依存関係の変更

Windows では、プロセスが動いている間、実行ファイルやネイティブ拡張のファイルが開かれたままになることがあります。 そのため PM は、読み込まれているライブラリをその場で置き換えるのではなく、依存関係の世代を別に用意します。 動いているプロセスは、再起動するまで今読み込んでいるものを使い続けます。

妨げになっているものの診断が出たら、そこに示された特定のプロセスとインストールについて対処してください。 Python や Hermes という名前のプロセスを片っ端から終了させるのはやめてください。ソースの更新と MSIX の パッケージの置き換えでは、持ち主も終了の条件も違います。

コマンドの解析部は、Windows との互換のために --force と --force-venv を残しています。これらは 通常の更新手順ではなく、OS のファイルロックを回避できる保証でもありません。失敗した更新をやり直す前に、 hermes update --plan、更新ログ、hermes pm status を確認してください。

更新後に確認しておきたいこと

ソースを更新したあとは、次の診断を実行してください。同梱アプリの場合は、パッケージの中で Git のコマンドを 使うのではなく、アプリの About ページとバックエンドの健全性を確認します。

  1. git status --short — 思い当たらない変更が出ていたら、先へ進む前に中身を確かめます
  2. hermes doctor — 設定、依存関係、サービスの状態を点検します
  3. hermes --version — 期待どおりバージョンが上がったか確かめます
  4. ゲートウェイを使っている場合: hermes gateway status
  5. hermes pm status — 依存関係の準備の状況と、失敗した手順がないかを確かめます。

更新の途中で端末が切れたら

hermes update は、うっかり端末を失っても大丈夫なように作られています。

  • 更新は SIGHUP を無視するので、SSH の接続や端末のウィンドウを閉じても、インストールの途中で止まることはありません。pip と git の子プロセスもこの保護を引き継ぐため、接続が切れても Python 環境が中途半端なまま残ることはありません。
  • 更新中の出力はすべて ~/.hermes/logs/update.log にも書き出されます。端末が消えてしまったら、接続し直してログを見れば、更新が終わったか、ゲートウェイの再起動が成功したかを確認できます。
tail -f ~/.hermes/logs/update.log
  • Ctrl-C(SIGINT)とシステムのシャットダウン(SIGTERM)は従来どおり効きます。こちらは事故ではなく、意図した中断だからです。新しいコードが置かれたあとに Ctrl-C を押すと、終了コード 130 で終わり、その旨を表示します。残りの手順(依存関係、ビルド、ゲートウェイの再起動)は持ち越しになり、次の起動か hermes update がそれを仕上げます。

端末が切れても大丈夫なように hermes update を screen や tmux で包む必要は、もうありません。

更新や起動が推測で進まずに止まるとき

次のような場面では、処理を続けずにメッセージを出して止まります。続けてしまうと、更新が半分だけ済んだチェックアウトや、古いコードのまま動くゲートウェイが残るおそれがあるからです。

  • 別の起動が、中断された更新を修復している。 git がチェックアウトを動かしている最中に更新が強制終了された場合、次の hermes の起動はまずチェックアウトを元に戻します。同時に始まった起動(再起動中のゲートウェイやデスクトップアプリのバックエンドと、手元の CLI など)は順番に処理されます。ほかの起動の修復を 10 秒より長く待った起動は、hermes: another Hermes launch is finishing an interrupted 'hermes update'; launch again in a moment. と表示して終了ステータス 1 で終わります。ほかの起動が終わってから、もう一度起動してください。そのときに Hermes Desktop が起動したバックエンドは起動失敗として表示されますが、開き直せば大丈夫です。
  • Windows での git の失敗 → ZIP への切り替え。 Windows では、hermes update の途中で git がエラーになったとき、リリースの ZIP をダウンロードする方法に切り替えるのは、git がまだチェックアウトに手を付けていない場合だけです。つまり、更新を始めたときと同じコミットとブランチのままで、stash もしておらず、ファイルの移動も始まっていない場合です。git が stash した、ブランチを切り替えた、ファイルの移動を始めた、のいずれかのあとでは、ZIP を上から重ねるとその状態が埋もれてしまうため、更新は git のエラーを報告して終了ステータス 1 で終わります。git の問題を直してから、hermes update をもう一度実行してください。
  • デスクトップアプリのビルドには書き込める .git が必要。 hermes desktop でチェックアウトから Hermes Desktop をビルドするときは、hermes update と同じチェックアウトのロックを取ります(更新の側もこのロックを必要とします)。ロックはチェックアウトの git ディレクトリに置かれます。そのディレクトリに自分のユーザーで書き込めない場合(たとえば別のアカウントが所有するチェックアウト)、更新と並行してビルドしてしまわないよう、ビルドは .../.git/hermes-update.lock is not writable と表示して止まります。.git の所有者か権限を直してください。macOS や Linux では、sudo hermes update によって読み取り専用で残ったロックファイルも、そのまま使えます。
  • 再起動の記録を書き込めない。 hermes update は、チェックアウトを動かす前に、必要なゲートウェイの再起動をホストの状態ディレクトリ($HERMES_GATEWAY_LOCK_DIR、なければ $XDG_STATE_HOME/hermes/gateway-locks)に記録します。このディレクトリに書き込めない場合は、代わりに現在のプロファイルの Hermes ホームに記録します。その記録を読むのはそのプロファイルだけなので、更新が進むのは、ほかのプロファイルがないインストールに限られます。ほかのプロファイルがある場合や、プロファイルの一覧を読めない場合は、何も動かす前に更新を止めます。状態ディレクトリを書き込めるようにしてから、もう一度実行してください。

今のバージョンを確認する

hermes --version

GitHub のリリースページ にある最新版と見比べてください。

メッセージングアプリから更新する

Telegram、Discord、Slack、WhatsApp、Teams から次のように送っても更新できます。

/update

これで最新のコードを取得し、依存関係を更新し、動作中のゲートウェイを再起動します。再起動の間だけボットは一時的に応答しなくなり(おおむね 5〜15 秒)、そのあと復帰します。

ソースの手動保守と巻き戻し

独立した開発用のチェックアウトについては、ソースからのインストールの手引き を ご覧ください。Git のリビジョンを変える前に、手元の作業をコミットするなどして残しておいてください。 サービスが動かしているコードを置き換える前に、自分が持っているそのサービスを止めてください。

ソースのリビジョンを変えたあとは、そのリビジョンの依存関係と初期セットアップの手順を実行します。 PM で管理されたチェックアウトでは、選んだソースの環境から python -m pm.cli install を使います。 素の pip install は同じではありません。管理されたツールの保管場所も、PM による実行環境の選択も用意しないからです。

コードのチェックアウトを戻しただけでは、データは巻き戻りません。新しいリリースは、古いコードが読めない形で 設定やデータベースを移行することがあります。古いリリースが以前のデータ形式を必要とする場合は、更新前に取った 一貫したバックアップを使ってください。復元を試す前に、現在のデータを残しておいてください。

パッケージが持っているインストールでは、そのパッケージマネージャーの巻き戻しか再インストールの手順を使います。 署名されたアプリや変更できないイメージの中で、Git や pip を実行しないでください。

Python 3.14 とそれより古いインタープリター

Hermes が動くのは Python 3.14 だけです。pyproject.toml はいまも requires-python = ">=3.11,<3.15" と宣言していますが、実行時の依存関係にはすべて python_version >= '3.14' のマーカーが付いています。範囲を広くしてあるのには理由が 1 つだけあります。 venv が PM への移行より前(Python 3.11–3.13)のソースのインストールでも、新しいコードをチェックアウトして もう一度 hermes update を実行できる必要があるからです。その更新は新しい仕上げ用のプロセスに引き継がれ、 PM が pm/lock.json から固定された 3.14 のインタープリターを用意し、その上に依存関係の環境を作り、 ランチャーの向き先を付け替えます。それ以降、古いインタープリターは使われません。

hermes update 以外では、これが次のような意味を持ちます。

  • pip install .、uv pip install .、pip install -e .、あるいは Python 3.11–3.13 でビルドされた

Homebrew や PyPI のパッケージでは、hermes-agent が 依存関係なし でインストールされ、 パッケージを読み込めません。これらのインストール方法は サポート対象外 です。ソースのインストーラーか、 パッケージ版のビルドを使ってください。

  • リポジトリの flake を使わない Nix のビルドも、同じマーカー付きの依存関係を見ることになり、

3.14 のインタープリターが必要です。

  • まだ移行前の venv を指している hermes のランチャーは、古い venv に入れ直すのではなく、

hermes update(またはチェックアウトからの python -m pm.cli install)で直してください。

イメージで管理されたインストール(Docker)と来歴マーカー

公開されている Docker イメージには、そのファイルシステムがイメージで管理されていることを確実に示す小さな読み取り専用のマーカー(/etc/hermes/image-provenance.json)が焼き込まれています。hermes update、hermes update --check、ダッシュボードの Update ボタンは、いずれも何かに触れる前にこれを確認します。イメージ管理のインストールではきれいに実行を拒み(終了コード 2)、本来の更新コマンド(docker pull nousresearch/hermes-agent:latest)を表示し、refused のレシートを書き出すので、運用ツールから試行の跡が見えます。ソースのチェックアウトがコンテナへバインドマウントされていても、マーカーが優先されます。拒否の判断は、見た目ではなく動いているファイルシステムが *何であるか* に基づいているからです。マーカーが壊れている場合も拒否します(安全側に倒す設計です)。Nix や apt で管理されたインストールも、従来の検出を使って同じ関門で拒否されます。

Nix を使っている方へ

Nix は明示的にサポートされるインストール経路ではなくなりました(できる範囲での対応のみです)。詳しくは Nix のセットアップ をご覧ください。Nix の flake でインストールした場合、更新は Nix のパッケージマネージャーが担います。

# Update the flake input
nix flake update hermes-agent

# Or rebuild with the latest
nix profile upgrade hermes-agent

Nix でのインストールは変更不能なので、巻き戻しは Nix の世代管理に任せます。

nix profile rollback

詳しくは Nix のセットアップ をご覧ください。


アンインストールする

hermes uninstall

ソースのインストールでは、削除する前に hermes uninstall --dry-run で内容を確かめてください。 既定の削除では、設定とユーザーのデータを残せます。--full はデータも削除します。 --data は、パッケージが持っているコードは消さずにユーザーのデータを削除します。 どのモードも元に戻せないので、先にバックアップを取ってください。

MSIX や Store 版は、Windows の設定 → アプリ → インストールされているアプリから削除します。macOS では、 Hermes を終了してからアプリのバンドルをゴミ箱へ移します。Docker、Nix、Termux は、インストールしたときと 同じ管理ツールを使います。それらのパッケージのファイルは、ソース用のアンインストーラーでは削除されません。 データの削除は、パッケージの削除とは別の作業です。

手動でアンインストールする

rm -f ~/.local/bin/hermes
rm -rf /path/to/hermes-agent
rm -rf ~/.hermes            # Optional — keep if you plan to reinstall