人の承認を挟みながら Claude Code のエージェントを何本もヘッドレスで走らせる技術検証で、PreToolUse フックの defer で止めたセッションを、承認のあとに --resume で再開する作りを試しました。このとき、-p に渡す再開のプロンプトにタスクを書き直せば二重になり、何も渡さなければエージェントが止まらなくなりました。プロセスが強制終了されたあとの再開では、エージェントが自分のした最後の作業を覚えていませんでした。
結論
--resumeは、1つのプロセスで2ターン動きました。1ターン目で保留のツールを実行して元の会話の続きを自動で進め、2ターン目で-pに渡したプロンプトを処理します。プロンプトは自動の続きを置き換えず、そのあとに足されます。- そのため、再開のプロンプトにはタスクを書き直さず、承認の中身(承認・条件・差し戻しの理由)を書きます。result は2回流れるので、最後のものを採ります。
- 区切りの無い再開では、エージェントが止まらずに動き続けました。「保留中の操作を実行し、結果を報告して停止する」のように終わる所をはっきり書き、使わせたくない道具は
--disallowedToolsで外します。 kill -9で落ちたセッションも--resumeで戻せましたが、最後のツール呼び出しとその結果は文脈に入っていませんでした。再開の指示に「実際の状態を確かめてから続ける」を入れます。
試した環境
- Mac、Claude Code 2.1.170
- 起こし方:
claude -p --output-format stream-json --verbose --include-hook-events --settings <フックの設定> --setting-sources '' --session-id <uuid> --allowedTools Bash。再開には--resume <session-id>を付けました。
--resume は1プロセスで2ターン動く
承認のあと --resume で起こすと、stream-json に init から result までが2回流れました。1回目の result から2回目の始まりまでは 7ms でした。
1ターン目は、こちらのプロンプトとは関係なく、保留していたツールを実行して元の会話を続けます。-p に渡したプロンプトが処理されるのは2ターン目です。つまり、再開のプロンプトは「続き」を指示するものではなく、続きのあとに足される1通です。
ここで元のタスクを書き直して渡すと、1ターン目ですでに済んだことを、もう一度頼むことになります。実測では、エージェントは「その操作はすでに実行済みです」と返しました。そこで、再開のプロンプトには承認の中身(承認か、条件付きか、差し戻しの理由は何か)を書き、元のタスクは書き直さないことにしました。
起こす側の読み取りにも気をつけます。1回目の result を受け取った時点では、プロセスはまだ終わっていません。私たちは、result を受けるたびに上書きして、最後のものを採るようにしました。

区切りの無い再開は止まらなかった
再開のときにプロンプトを渡さなかった回では、エージェントが止まらなくなりました。元のタスクは echo を1回打つだけで、それはもう終わっていました。それなのに、再開したエージェントは次のことを始めました。
- メモリのファイル、リポジトリ、
git log、ドキュメントを読む - 承認の関門が見ている状態ファイルを自分で
rejectedに書き換え、deny の経路を試す - 結果の文書を書こうとする(Write を許可していなかったので、そこで止まった)
セッションは3分以上動きました。
そこで再開には、どこで止まるかがはっきりした指示を渡すことにしました。往復を確かめたときに渡したのは、次の1文です。
保留中の操作を実行し、結果を1行で報告して停止してください。
あわせて、再開のときの --allowedTools を最小にしました。ただし --allowedTools は境界ではなく、一覧に無い道具を使われることがあったので、使わせたくない道具は --disallowedTools で名前を挙げて外します。再開がすぐに終わるとは限らないので、同期で待つ前提でも組まないことにしました。
強制終了したセッションの再開では、最後の1手が抜ける
停電で落ちたエージェントの作業を、続きから戻すか捨てるかを選べるようにしたくて、強制終了からの再開も測りました。--session-id を指定して起こし、ツールの途中(sleep 30 の最中)で kill -9 を送ってから、--resume で戻しました。
- 再開はできました。
session_idも同じで、会話と計画は戻っていました。 - ただし、最後のツール呼び出しとその結果は、文脈に入っていませんでした。
tool_resultが書かれる前にプロセスが落ちたためです。 - 最初の手順で書いたファイルは実際に残っていたのに、再開したエージェントは「何も実行していません」と答えました。
再開したエージェントは、実際より1手遅れていると思い込みます。そこで、強制終了からの再開では、指示に「実際の状態を確かめてから続ける」を入れることにしました。
再開のプロンプトに入れたこと
まとめると、再開のプロンプトには次のことだけを書きました。元のタスクは書き直しません。
- 承認の中身(承認・条件・差し戻しの理由)
- どこで止まるか(保留中の操作を実行し、結果を報告して停止する)
- 強制終了からの再開なら、実際の状態を確かめてから続けること
確かめていないこと
- 申請1件あたりの再開の費用(キャッシュが切れたときの入力トークン)。止まらなくなった回が混ざって、測れませんでした。
- 承認待ちが
cleanupPeriodDays(既定 30 日)を超えるとセッションが消えること、再開のときに MCP が繋がっていないとエラーで終わること。どちらも公式に書かれていることで、私たちは測っていません。 - 2.1.170 より後の版でも同じかどうか。
おわりに
--resume に渡すプロンプトは「続きを頼む」ためのものではなく、自動で進む続きのあとに足される1通でした。再開のプロンプトは、承認の中身と止まる所だけを書いた短いものにする、という形に落ち着きました。
※ この記事に書いた Claude Code の振る舞いは、執筆時点で私たちが測ったものです。いまは変わっている可能性があります。


