GooseのHooksが「拒否」を覚えた──ツール実行を止められる仕組みと、暴走したフック自体は止めない設計
オープンソースAIエージェントGoose(Block)は2026年5月、Hooks機能に「PreToolUse denial(ツール実行前の拒否)」を追加した。フックが終了コード2や特定のJSONを返すとツール呼び出しをブロックできる一方、フック自体がタイムアウトやクラッシュを起こした場合は許可扱いになる、という設計思想がPR本文から読み取れる。

目次
AIエージェントに「このコマンドだけは実行させたくない」というルールをどう守らせるか。Claude Codeが同種の仕組みを持っているように、Block社のオープンソースエージェント「Goose」も2026年5月、Hooks機能を導入した。v1.46.0(2026年8月12日公開)のリリースノートには「Hooks feature with PreToolUse denial」という1行があるが、実際には5月時点で基本機能が入り、2週間ほどでツール実行を止める権限(denial)が追加される、という段階を踏んで作られている。
3行まとめ
- Goose(Block社)のHooksは2026年5月11日に土台が追加され、2週間後の5月19日にツール呼び出しを拒否する「PreToolUse denial」が追加された。フックが明示的に「拒否」と言った時だけブロックし、タイムアウトやクラッシュは「許可」扱いになる設計
- 公式ガイド(documentation/docs/guides/context-engineering/hooks.md)を読むと、この「壊れたフックは許可扱い」という挙動は
on_failureという設定項目で変更可能になっている。既定はallowだが、blockを指定すればフックの失敗時にツール呼び出しを拒否する「fail closed」に切り替えられる- フックのブロック対象と紐づく背景Issueには「組織をまたぐエージェント展開でのガバナンス」「Human-in-the-loop」「検知ルールをツール呼び出し境界に置けるか」という3種類の異なる要望があったことが、各Issueのタイトルから確認できる
まずHooks自体が5月に追加された
Hooksの土台を実装したPR #9093(2026年5月11日マージ)の本文はシンプルだ。
"Hooks support https://open-plugins.com/agent-builders/components/hooks"
このタイトルが示す通り、「Hooks」というエージェント設計上の共通コンポーネントに対応した、という趣旨の追加で、この時点ではまだ「拒否する」機能までは持っていなかったとみられる。
2週間後、フックに「ツール呼び出しを拒否する権限」を追加
Hooksが実際にツール呼び出しをブロックできるようになったのは、PR #9304(2026年5月19日マージ)だ。本文はこう説明している。
"Make it so hooks can deny tool calls." "Runs hooks in order and stops at the first explicit deny (exit code 2 or {"decision":"block",...} on stdout). Any other failure — spawn errors, timeouts, other non-zero exits — is logged and treated as Allow, so a misbehaving hook can't block."
(フックがツール呼び出しを拒否できるようにした。フックは順番に実行され、最初の明示的な拒否(終了コード2、または標準出力に{"decision":"block",...})で停止する。それ以外の失敗——プロセス起動エラー、タイムアウト、その他の非ゼロ終了——はログに記録された上でAllow(許可)扱いになる。これにより、誤動作したフックがブロックを引き起こすことはない)
ここには明確な設計判断がある。「フックが明示的に『拒否する』と言った時だけブロックし、フックがただ壊れているだけ(タイムアウトやクラッシュ)の場合はブロックしない」というルールだ。安全側に倒すなら「フックが正常に応答できない=念のためブロック」という設計もあり得るが、Gooseの実装は逆で、「壊れたフックのせいでエージェントが完全に止まってしまう」事態のほうを避けている。フックの信頼性がエージェント全体の可用性に直結しないようにした、という判断だと読める。
拒否の合図は2種類用意されている。
- 終了コード2でプロセスを終える
- 標準出力に
{"decision":"block", ...}というJSONを出す
どちらの方法でも「このツール呼び出しは止める」とGoose本体に伝えられる。
公式ガイド(Hooks公式ドキュメント)をあわせて読むと、PR本文が説明していた「フックの異常はすべて許可扱い」という挙動は、その後on_failureという設定項目でユーザー側が選べるようになっていることが分かる。
| 項目 | 内容 |
|---|---|
on_failureの既定値 |
allow(フックが実行できない・タイムアウト・想定外の出力を返した場合はツール呼び出しを許可) |
on_failure: blockを指定した場合 |
同じ状況でツール呼び出しを拒否する「fail closed」に切り替わる |
| 適用範囲 | 選択されたPreToolUseコマンドアクションにのみ有効。他のイベントでは無視される |
Stopフックの扱い |
on_failure: blockを設定してもStopフックは常にfail open。壊れたフックが、完了したターンの終了を妨げないようにするため |
| 拒否シグナルの判定順 | ①終了コード2+stderrの理由 ②stdout上の{"decision":"block"}(終了コードに関わらず優先) ③終了コード0+空のstdoutは許可 ④終了コード0+{"decision":"allow"}も許可 ⑤それ以外はすべて「判定なし」扱い |
Stopブロックの上限 |
無限ループを防ぐため、連続ブロック数に上限があり、GOOSE_STOP_HOOK_BLOCK_CAP環境変数で変更できる |
(出典:Goose公式Hooksガイド)
つまり、PR #9304がマージされた時点での「壊れたフックは問答無用でAllow」という設計は、その後のドキュメント整備で「既定はAllowだが、fail closedに切り替える設定も用意した」という、より柔軟な形に発展していたことが確認できる。公式ガイドが挙げる実例は「sudoを使うシェルコマンドを拒否する」というもので、まさに記事冒頭で触れた「このコマンドだけは実行させたくない」という要望に対応する具体例だ。
関連するIssue番号から見える背景
PR #9304の本文には関連Issueとしてhttps://github.com/aaif-goose/goose/issues/8742と「Fixes #9068」の記載があり、さらに本文末尾のスクリーンショット説明の後にも「Fixes #9277」という記載が別途ある。
複数のIssue番号が紐づいていることから、この機能はユーザーからの複数の要望・不具合報告を受けて実装された可能性が高い。実際に3件のIssueページをそれぞれ開いてタイトルを確認すると、要望の中身が異なることが分かる。
| Issue | タイトル | 読み取れる要望 |
|---|---|---|
| #8742 | Proposal: pre-tool-call governance hook for cross-org agent deployments | 組織をまたぐエージェント運用で、誰がどんな権限でツール呼び出しを承認したかを暗号学的に記録できる仕組みへの要望。本文は「Gooseは70以上のMCP拡張に対してツール呼び出しを実行するが、組織をまたぐ展開ではその呼び出しを誰がどんな委任のもとで承認したかの暗号学的な記録がない」と説明している |
| #9068 | HITL and lifecycle hooks | Human-in-the-loop(人間の承認を挟む)ためのライフサイクルフックへの要望 |
| #9277 | extensions: would a detection-rule hook at tool-call boundary be in scope? | ツール呼び出しの境界に検知ルール(detection rule)を置けるようにしてほしいという拡張機能側からの要望 |
(出典:各Issueページのタイトル、#8742は本文冒頭も確認)
「組織をまたぐガバナンス」「人間の承認」「検知ルール」という3つの異なる文脈から、同じ「ツール呼び出し前に介入したい」という要望が集まっていたことが、これらのタイトルから見て取れる。
「フックの裏側」にはPreToolUseというタイミング名がある
機能名の「PreToolUse」という語自体が示す通り、このフックはツールが実際に実行される前のタイミングで発火する。これは、Claude Codeが提供しているフックイベント(PreToolUse/PostToolUseなど)と同じ命名の系譜にある考え方で、「ツールを呼ぼうとした瞬間に外部スクリプトで介入し、可否を判定する」という設計自体は、エージェントツール全般で収斂しつつある型の1つと見てよさそうだ。v1.46.0のリリースノートには、このほかにも「Pass working_dir to the Stop hook context」という関連する更新が同じリリースに含まれていることが確認できる。
実際に確認した設計思想
筆者がこのPR本文を読んで印象に残ったのは、「拒否する権限を与えると同時に、拒否の主導権をフック側の異常動作に渡さない」という線引きだ。エージェントに強制力のあるガードレールを持たせようとすると、素朴には「判定できない場合はブロックする(fail closed)」設計にしたくなる。Gooseはあえて逆の「fail open(判定できない場合は許可する)」を選び、その理由を「誤動作したフックがブロックを引き起こさないため」と明言している。これは安全性より可用性を優先した、意図的なトレードオフだ。
block/gooseがaaif-goose/gooseにリダイレクトする理由は追えていない
Hooks機能全体の設定方法については、GitHubリポジトリ内のMarkdownファイルを直接取得する形で、公式ガイド(documentation/docs/guides/context-engineering/hooks.md)の中身を確認できた。これによりon_failure設定やIssue背景など、当初到達できなかった情報の大部分は埋まった。一方、これらのURLを確認する過程で、github.com/block/goose/...のURLが一貫してaaif-goose/gooseというリポジトリへリダイレクトされることに気づいたが、この組織名の変更がいつ・なぜ起きたのか(Block社からのスピンアウトなのか、単なるGitHub Organization名の変更なのか)は、本記事の範囲では確認できていない。またfail open/fail closedの選択について、on_failure: blockをユーザーがどの程度実際に使っているか(既定値のまま運用しているユーザーが大半なのか)といった利用実態のデータも、公式ドキュメントには記載がなかった。
関連記事
出典・参照資料
AIニュースの解説を動画でも
YouTubeでは注目ニュースの背景を解説し、Xでは新着記事をお知らせしています。
コメント
まだコメントはありません。最初のコメントを書いてみませんか?
AIについて聞きたいことはありますか?
質問箱で無料で受け付けています。回答は公開され、他の方の参考にもなります。
質問箱を見る →新しい記事をメールで受け取る
AIの新しい発表を、出典付きで整理して届けます。