◉ANTIGRAVITY LABEN
記事一覧/Agents & Manager
◈ Agents & Manager/2026-10-30初級

夜間に回す Antigravity CLI の止め方を、CLI の外側に置き直しました

agy -p を夜間に自動で回すとき、時間・出力量・重なりの3つの止め方を、ラッパースクリプトの側に持たせる手順です。スタブで4通りの終わり方を試すと、タイムアウトの終了コードが 124 ではなく 137 になる場合がありました。

antigravity-cli16headless6automation54bash6timeout4

個人開発のアプリの手入れを夜のうちに進めておきたくて、寝る前に agy -p を cron に登録し、朝にログを開く。私自身がそういう運用を考えたとき、いちばん最初に気になったのは「朝になっても終わっていなかったら、どうなるのか」でした。

公式のドキュメントを読むと、headless の実行には --print-timeout という待ち時間の指定があり、既定は 5 分です。認証が切れていれば、ハングせずにエラーで終わるとも書かれています。ありがたい作りです。一方で、ターン数の上限については、ドキュメントに書かれていませんでした。

書かれていないものを、あるものとして運用するのは危ういと感じました。そこで、止め方の一部を CLI の外側、つまり呼び出すラッパーの側に持たせることにしました。

外側に置く3つの止め方

最初にお伝えしたいのは、CLI の設定を信用しないという話ではない、ということです。CLI の版が変われば挙動も変わります。外側の枠は、その変化に左右されずに残ります。

置いたのは次の3つです。

  1. 時間: timeout で必ず打ち切る。CLI 側の待ち時間とは別に持つ
  2. 出力量: 標準出力を一定のバイト数で切る。暴走した出力でディスクを埋めないため
  3. 重なり: 前回の実行が残っていたら、今回は始めない

これに、何が起きたかを 1 行で残すログを足します。朝に読むのは、このログだけで足りるようにしたかったのです。

ラッパーの全体

#!/usr/bin/env bash
# 夜間に agy を headless で回すときの外側の枠。
# 使い方: agy_nightly.sh "プロンプト"
set -u
 
PROMPT="${1:?prompt required}"
LOG_DIR="${LOG_DIR:-$HOME/agy-nightly-logs}"
HARD_LIMIT="${HARD_LIMIT:-20m}"       # 時間: CLI の待ち時間とは別に、外側で必ず打ち切る
MAX_BYTES="${MAX_BYTES:-200000}"      # 出力: これを超えたら以降は捨てる
LOCK="$LOG_DIR/.lock"
 
mkdir -p "$LOG_DIR"
STAMP="$(date +%Y%m%d-%H%M%S)"
OUT="$LOG_DIR/$STAMP.out"
META="$LOG_DIR/$STAMP.meta"
 
# 重なり: 前回が残っていたら今回は始めない
exec 9>"$LOCK"
if ! flock -n 9; then
  echo "$STAMP skipped: previous run still holds the lock" >> "$LOG_DIR/summary.log"
  exit 75
fi
 
START=$(date +%s)
# timeout は TERM を送り、10 秒待っても残れば KILL する
timeout --kill-after=10s "$HARD_LIMIT" \
  ${AGY_BIN:-agy} -p "$PROMPT" --output-format json 2>"$OUT.err" \
  | head -c "$MAX_BYTES" > "$OUT"
CODE=${PIPESTATUS[0]}
END=$(date +%s)
 
BYTES=$(wc -c < "$OUT")
LABEL="ok"
case "$CODE" in
  0)       LABEL="ok" ;;
  124|137) LABEL="timeout" ;;      # 124 = TERM で終了 / 137 = TERM を無視されて KILL
  141)     LABEL="truncated" ;;    # head が先に閉じて SIGPIPE
  *)       LABEL="error" ;;
esac
 
printf '%s exit=%s secs=%s bytes=%s label=%s\n' \
  "$STAMP" "$CODE" "$((END-START))" "$BYTES" "$LABEL" | tee "$META" >> "$LOG_DIR/summary.log"
exit "$CODE"

--output-format json を付けているのは、完了時に status や error を含む 1 つの JSON が返るからです。朝にログを機械で読みやすくなります。AGY_BIN は、本物の agy の代わりに試験用の偽物を差し込むための入口で、普段は何も指定しません。

PIPESTATUS[0] を使うのは、パイプの先頭、つまり timeout 側の終了コードを取るためです。ここを $? にすると head の終了コードが返り、失敗が消えます。この落とし穴は、Antigravity CLI の失敗を握りつぶしていたのは呼び出し側のパイプでした に詳しく書きました。

偽物の agy で、4通りの終わり方を試す

本物の agy で長時間の暴走を再現するのは、時間も利用枠も惜しいところです。そこで、挙動の違う小さなスクリプトを 4 つ用意し、AGY_BIN から差し込みました。

偽物の挙動結果
すぐ JSON を返して終わる`exit=0` `label=ok`
`sleep 300` で居座る(`HARD_LIMIT=2s`)`exit=124` `secs=2` `label=timeout`
TERM を無視して居座る(`HARD_LIMIT=2s`)`exit=137` `secs=12` `label=timeout`
5MB を吐き続ける(`MAX_BYTES=1000`)`exit=141` `bytes=1000` `label=truncated`

3 行目で、手が止まりました。私は最初、時間切れなら必ず 124 が返ると思い込んで、[ "$CODE" -eq 124 ] だけを判定に書いていたのです。TERM を無視する相手には KILL が飛び、そのときの終了コードは 137 になります。124 だけを見ていた版では、この行が error と記録されました。時間切れは 124 と 137 の両方で数えるようにしました。 この一行は、試してみなければ書けなかったのだと思います。

秒数が 2 ではなく 12 になっているのも、見逃せない点です。TERM を送ってから 10 秒待つ設定(--kill-after=10s)が、そのまま上乗せされています。夜間の枠を組むときは、HARD_LIMIT にこの猶予を足した時間で見積もる必要があります。

4 行目の 141 は、head が規定のバイト数を読み終えて先に閉じたため、上流が SIGPIPE で落ちた印です。失敗というより「出力が上限で切られた」合図なので、ラベルを分けました。

重なりの確認は、居座る偽物を先に走らせ、1 秒後に別のものを起動して行いました。後発は exit=75 で、summary.log に skipped と残りました。

動いていないことを疑う側の目

ここまでの確認は、あくまで偽物の agy に対するものです。本物の agy が JSON を返す間の挙動、たとえば途中経過をどれだけ標準出力へ出すかは、この方法では確かめていません。--print-timeout と HARD_LIMIT のどちらが先に効くかも、本物で確かめる必要があります。私は、HARD_LIMIT を 1 分に絞って、軽い依頼を一度だけ流すところから始めました。

もう一つ、--dangerously-skip-permissions は、無人で回すときに便利に見えます。ただ、外側の枠は時間と量と重なりしか守りません。ファイルを消す操作そのものは止められないのです。--sandbox を併用するか、権限の範囲を先に狭めておくか。この判断は、枠の外に残ります。

朝に読むための一行

summary.log は 1 実行 1 行なので、朝の確認は ok 以外を拾うだけで済みます。

# ok 以外の行(timeout / truncated / error / skipped)だけを見る
grep -v 'label=ok' ~/agy-nightly-logs/summary.log | tail -n 5
 
# 10 分(600 秒)を超えた実行だけを見る
awk '{for(i=1;i<=NF;i++) if($i ~ /^secs=/){split($i,a,"="); if(a[2]+0>600) print "SLOW:", $0}}' ~/agy-nightly-logs/summary.log

前者は、先ほどの 4 通りを流したログに対して実際に試し、ok 以外の 3 行だけが出ることを確かめました(skipped の行は label= を持たないため、こちらにも出ます)。後者は、秒数が閾値を超えた行だけを拾います。「毎晩ほぼ同じ秒数で終わる」状態を一度つかんでおくと、急に伸びた夜が目に留まりやすくなるのだと感じています。

次にやること

今夜の登録の前に、HARD_LIMIT=1m で軽い依頼を 1 回だけ流して、summary.log に 1 行増えることを確かめていただければと思います。朝にその 1 行を読める状態を作ってから、時間を延ばしても遅くはありません。

止めるのは CLI の仕事、止まったことを知るのは自分の仕事。 この線引きだけは、設定が変わる版でも崩さないようにしています。

バージョンの変化そのものを抑えたい場合は、自動実行の再現性を守る — Antigravity CLI のバージョンを固定して挙動のブレを抑える が、この枠と組み合わせやすい話です。

シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

✦ Tips & 活用術2026-08-21
Antigravity CLI の失敗を握りつぶしていたのは呼び出し側のパイプでした
CLI 1.1.14 で言語サーバーの失敗が非ゼロ終了として表に出るようになりました。ところが自動化のラッパー側が終了コードを受け取れていないと、この改善は届きません。bash 5.1 で16通りの呼び出し方を実際に試し、失敗が消える書き方と直し方をまとめました。
◈ Agents & Manager2026-10-25
空き容量 11MB で無人ジョブが止まりました。du が数えたのは 59MB だけで、残りの約 9GB は見えない場所にありました
エージェントを無人で走らせる前に、書き込み先の空きを測って止める bash スクリプト 2 本の全文です。du と df の数字が噛み合わない理由と、自分では片付けられない空き不足の見分け方も書きました。
◈ Agents & Manager2026-10-24
pipefail を足したら、通るはずの検査が赤になりました。出力が 8 KB を超えた日から起きる誤検知と、終了コードの確かめ方
pipefail を足すと、通るはずの検査が赤になることがあります。出力が 8 KB を超えると起きる誤検知を 1,122 本のリポジトリで再現し、ファイル経由の実行ラッパーとカナリアまでを残します。