Claude Media
Claude Codeのprompt.attachment hookが動かない原因と対処

Claude Codeのprompt.attachment hookが動かない原因と対処

2.1.299以降、自分で入れたmodのprompt.attachment hookが動かない。原因は組み込みガードのcc-plugin-sec-defaultで、ユーザー側では止められません。見分け方と管理者側の対処をまとめます。

自分で入れたmodのprompt.attachment hookが、2.1.299以降は一度も呼ばれなくなった。こうなる原因は、Claude Codeが先頭に置く組み込みガードcc-plugin-sec-defaultが、そのイベントをユーザー層より先で処理しているためです。ユーザー側の設定では止められません。直せるのは管理設定を持つ管理者だけです。

症状はデバッグログの1行で確かめられる

prompt.attachmentは、Claude Codeが自分でClaudeに渡すメッセージ(リマインダーなど)を書き換えるためのmod用イベントです。e.typeで種類を判別でき、{ text }を返せば差し替え、{ text: null }を返せば省略になります。

GitHubのissue #102653(open)の報告では、deferred toolの一覧(deferred_tools_delta)とエージェント一覧(agent_listing_delta)を書き換えるmodのhookが、2.1.296では動き、2.1.301では動かなくなりました。同じmodのtool.describe、command.run、ui.renderは動き続けています。

hookが呼ばれていないかどうかは、デバッグログで確かめます。

claude -p 'Reply with: pong' --debug-file run.log
grep -n "cc-plugin-sec-default" run.log

該当する場合、ログには次のような行が並びます(報告されたログの抜粋です)。

cc-plugin-sec-default@builtin seated outermost: this machine has managed settings
plugin.register: context-lean (user, context-lean@local-mods), judged by cc-plugin-sec-default: admitted
context-lean: prompt.attachment bypassed by cc-plugin-sec-default (tier user); beneath runs

見るべきは3点です。

  • 1行目: ガードが最外周に置かれた理由。this machine has managed settingsなどと出る
  • 2行目: modそのものはadmitted。つまり拒否されたのではなく、読み込みは通っている
  • 3行目: prompt.attachment bypassed ... (tier user)。このイベントがユーザー層を飛ばされた記録

報告者の環境では、この3行目が1ターンで14回出ていました。modは読み込まれて見えるのに、hookだけが走らない。この状態が、エラーも出ずに続くのが厄介な点です。

なぜ動かないのか

公式のmod管理ガイドは、ガードが読み込まれる環境では、ユーザーのmodが登録した次のイベントのhookは「never runs」だと書いています。prompt.attachmentはその表に載っています。

イベント何を扱うか
classic.<Event>(classic.Stopなど)何を扱うかsettings hookのイベント
prompt.compose / prompt.section何を扱うかシステムプロンプトの節の一覧、個々の節
prompt.context何を扱うか会話の最初のメッセージに付く文脈
prompt.attachment何を扱うかClaude Codeが自分で足すメッセージ
skill.prompt / attribution.text何を扱うかスキルの展開文、コミットやPRの帰属文
session.compact何を扱うか会話の圧縮
settings.read何を扱うか設定の読み取り
session.append(doorがattachment・hook-context・compaction)何を扱うか保存される行

ガードの狙いは、組織が管理するもの(管理設定のhook、管理CLAUDE.md、設定、MCPの許可リストなど)を、個人が入れたmodから触れなくすることです。prompt.attachmentは、管理CLAUDE.mdやルール、エンジンが注入するリマインダーを書き換えられる位置にあるので、一括して対象になっています。

ガードが読み込まれる条件は2つです。

  • マシンに管理設定がある
  • セッションの資格情報が、TeamまたはEnterpriseプランの/loginである

APIキーやBedrockなどで認証している場合は、管理設定のあるマシンでだけガードが入ります。CLAUDE_CODE_OAUTH_TOKENによるセッションは、保存済みのTeam/Enterpriseログインがあっても、このログインには数えられません。

2.1.299のリリースでは、「組織が管理する環境でスキップされるmodのhookを/pluginなどに表示する」変更が入っています。この通知は2.1.299以降が前提です。なおclaude plugin validate側の注記は、2.1.304で削除されています。

公式の記述と報告者の期待がずれている点

issueの報告者は「2.1.296では動いていたので回帰だ」と見ています。一方で、mod管理ガイドの表にはprompt.attachmentが載っています。記述の上では、意図された動作です。

報告者が求めているのは、動作の撤回よりも次の2点です。

  • 2.1.299のchangelogとドキュメントに、この挙動を書くこと
  • ユーザーまたは管理者が許可できる設定を用意すること

issueのコメントでは、同じ仕組みが別のイベントでも報告されています。classic.SessionStart、classic.FileChanged、session.compact、tool.checkなどです。prompt.attachmentだけの話ではありません。

管理設定がなくても起きるケース

issueの2件目のコメントによれば、管理設定ファイルもMDMもない環境で、Claudeデスクトップアプリ経由のセッションでも起きています。報告者の説明では、アプリがCLIを--managed-settings付きで起動します。端末から再現するコマンドとして、次の形が挙げられています。

claude -p 'Reply with: ok' --debug-file run.log \
  --managed-settings '{"availableModels":["<model-id>"],"enforceAvailableModels":false,"modelPricing":{"multiplier":1,"overrides":{}}}'

同じコマンドから--managed-settingsを外すと、ログはcc-plugin-sec-default@builtin not seated: no managed settings and not a Team or Enterprise organizationとなり、hookは動いたとのことです。また'{}'やenvだけを渡した場合はガードが入らず、上のキーを含めたときに入ったとも書かれています。

これはissue上の報告であり、公式ドキュメントには載っていません。デスクトップアプリで使うmodが急に動かなくなった場合の切り分けとして、デバッグログでseated outermostの行を探す価値があります。

別のコメントでは、管理設定ファイルの中身がチャンネル許可(channelsEnabledとallowedChannelPlugins)だけでもガードが入ったと報告されています。この報告どおりなら、管理設定の中身が小さくてもガードは入ることになります。公式の条件も「マシンに管理設定がある」であり、キーの種類は挙げられていません。

対処はユーザー側ではなく管理設定側にある

先に、効かない手を挙げておきます。

  • ユーザー設定のenabledPluginsに"cc-plugin-sec-default@builtin": falseを書く: 無視される(報告者が試して確認)
  • ユーザー設定にprependPluginsを書く: 管理設定のあるマシンやTeam/Enterpriseのセッションでは、ユーザー設定のprependPluginsは読まれない
  • --plugin-dirでmodを読み込み直す: 読み込みは通るが、ガードの対象イベントは同じく飛ばされる

公式が示す解決策は、どちらも管理設定を変える方法です。

手順

管理者が選べる2つの道

  1. 1

    modを組織のmodとして入れる

    mod管理ガイドでは、modを組織のものとして扱わせる方法が1つ目に挙がっています。このmodの対象イベントのhookは動き、ガードは他のmodに対して残ります。

  2. 2

    ガードを外す

    prependPluginsにsec-default@builtinを入れずにおきます。空のリスト"prependPlugins": []で足ります。ガード全体を失うので、prompt.attachment以外の保護も消えます。

1つ目の条件は厳密です。Claude Codeが「組織のmod」と見なすのは、次の3つが揃うときだけです。

  1. 管理設定のenabledPluginsで、そのプラグインがtrue
  2. 管理設定が、プラグインのマーケットプレイスを利用者のマシン上のディレクトリ(絶対パス)として指定している
  3. そのマーケットプレイスが相対パスでプラグインを列挙し、その場で読み込まれる

つまり、MDMなどでマーケットプレイスのディレクトリを各マシンの同じパスに配布する運用が前提です。claude.aiの管理コンソールから配る管理設定は、キーは運べてもディレクトリは置けません。

prependPluginsで順序を決める書き方は、prependPluginsとappendPluginsの記事にまとめています。ガードのオプションはallowManagedModsOnlyの記事が詳しいです。

管理設定を持つ組織の一員として、この問題に当たった場合は、管理者に伝える内容は次の3点で足ります。

  • modの名前と、飛ばされているイベント(prompt.attachment)
  • デバッグログのbypassed by cc-plugin-sec-default (tier user)の行
  • Claude Codeのバージョン(/pluginの通知は2.1.299以降)

管理設定を持たない個人利用で起きたときは、デスクトップアプリ経由かどうかと、--managed-settingsが渡されていないかを先に疑います。

書き換えができなくなったとき、何を失うか

報告者のmodは、deferred toolの名前を隠すことで、あるセッションで1リクエストあたり約17,000トークンを節約していました。2.1.301では、一覧の全文が毎回モデルに送られます。modの目的が「コンテキストを減らす」だったので、hookが走らないと、効果がそのまま消えます。

見落としやすいのは、セキュリティ用途のmodです。コメントには、秘密情報の伏せ字化をprompt.contextとprompt.attachmentで行うmodが、同じ理由で効かなくなったという報告があります。ユーザーに見えるのは、薄い「hook was skipped」の表示だけだったとのことです。mod作者は、自分のmodが管理環境で動かない可能性を前提に設計するほうが安全です。

原因の切り分けには、表の対象外のイベントに軽いhookを仕込む方法があります。たとえばtool.describeのhookが同じ環境で動いたという報告が、issueに上がっています。modの作り方はtool.callの記事に載せています。

ガードが入っているかを、先にmod側で見分ける

hookを書く側は、自分のmodが動く環境にガードが入っているかを、事前に知っておけます。/pluginでmodの詳細を開くと、ガードが読み込まれている環境では、飛ばされるイベントを名指しした注記が出ます。注記は「Its hooks on ... do not run here」の形で、modが登録したとおりのイベント名を並べます。claude plugin validateの注記は2.1.304で削除されたため、現行では/pluginで確かめます。

注意が1点あります。注記が出るのは公式の表にあるイベントだけです。表にないイベントでhookが動かないときは、注記が出ません。注記がなくてhookが動かないときは、デバッグログのbypassedの行を直接探すのが確実です。

切り分けのチェックリスト

modが何もしないときは、次の順で見ると早く絞れます。

  1. claude --debug-file run.logで起動し、seated outermostの行があるかを見る
  2. なければ、ガードは無関係。modが動かないときの切り分けに進む
  3. あれば、bypassed by cc-plugin-sec-defaultの行がmod名と一緒に出ているかを見る
  4. 出ていれば、そのイベントは公式の表の対象。管理者にmodの組織配布かガードの扱いを相談する
  5. 出ていなければ、hook skippedの行(例外・時間切れ)を探す

まとめ

prompt.attachmentが動かない原因はmodの不具合ではなく、管理環境でユーザー層を飛ばす組み込みガードです。issue #102653はopenのままで、2.1.299で入った変更がchangelogに書かれていなかった点と、許可の設定がない点が論点になっています。個人でできるのは、デバッグログで原因を確定し、管理者に渡す材料を揃えるところまでです。

2.1.299の変更はv2.1.299のリリースノートにまとめています。

この記事を共有:XはてブLinkedIn