github / github/copilot-cli

Plugin hooks are not loaded when a session is resumed via `--resume`

オープン
#4,629 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

area:plugins area:sessions
主要言語
Shell
スター
11.2k
フォーク
1.9k
平均マージ
14時間 16分
マージ済み PR(30日)
6

説明

Summary

Plugin-provided hooks are not loaded when a session is started with --resume. The same plugin fires all of its hooks on a fresh session and none of them on a resumed one, in the same working directory, for the same session id, on the same machine.

loadDeferredRepoHooks() does run on the resume path — the fix for #1503 is in place — but it only restores repo/policy hooks. Hooks contributed by installed plugins are dropped, so any plugin that relies on SessionStart, UserPromptSubmit, Stop, or SessionEnd silently stops working the moment a user resumes.

Version: 1.0.81-14
OS: Windows 11 (pwsh)

Reproduction

A minimal plugin whose hooks only echo into a log file.

repro/.github/plugin/marketplace.json:

{
  "name": "repro-local",
  "description": "Minimal local marketplace for reproducing a hook loading bug",
  "owner": { "name": "repro" },
  "plugins": [
    {
      "name": "hook-probe",
      "description": "Appends one line to a log file for every hook event",
      "version": "0.1.0",
      "source": "./hook-probe"
    }
  ]
}

repro/hook-probe/plugin.json:

{
  "name": "hook-probe",
  "description": "Appends one line to a log file for every hook event",
  "version": "0.1.0",
  "hooks": "hooks/hooks.json",
  "author": { "name": "repro" },
  "license": "MIT"
}

repro/hook-probe/hooks/hooks.json:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": ".*",
        "hooks": [{ "type": "command", "bash": "echo SessionStart >> /tmp/hook-probe.log", "powershell": "echo SessionStart >> $env:TEMP\\hook-probe.log" }]
      }
    ],
    "UserPromptSubmit": [
      {
        "matcher": ".*",
        "hooks": [{ "type": "command", "bash": "echo UserPromptSubmit >> /tmp/hook-probe.log", "powershell": "echo UserPromptSubmit >> $env:TEMP\\hook-probe.log" }]
      }
    ],
    "Stop": [
      {
        "matcher": ".*",
        "hooks": [{ "type": "command", "bash": "echo Stop >> /tmp/hook-probe.log", "powershell": "echo Stop >> $env:TEMP\\hook-probe.log" }]
      }
    ],
    "SessionEnd": [
      {
        "matcher": ".*",
        "hooks": [{ "type": "command", "bash": "echo SessionEnd >> /tmp/hook-probe.log", "powershell": "echo SessionEnd >> $env:TEMP\\hook-probe.log" }]
      }
    ]
  }
}

Install it and run one fresh session and one resumed session:

$ copilot plugin marketplace add ./repro
$ copilot plugin install hook-probe@repro-local

$ rm $TEMP/hook-probe.log
$ copilot -p "reply with exactly: A" --allow-all-tools
...
Resume     copilot --resume=8561317a-5af2-4550-a42a-1f9b1f6058c3

$ cat $TEMP/hook-probe.log
UserPromptSubmit
SessionStart
Stop
SessionEnd

$ rm $TEMP/hook-probe.log
$ copilot --resume=8561317a-5af2-4550-a42a-1f9b1f6058c3 -p "reply with exactly: B" --allow-all-tools
...
$ cat $TEMP/hook-probe.log
cat: no such file — the hooks never ran

The resumed run completes normally (the model answers, tokens are billed), so the session itself is fine — only the plugin hooks are missing.

Expected vs actual

Run Hooks fired
copilot -p … UserPromptSubmit, SessionStart, Stop, SessionEnd
copilot --resume=<id> -p … none

Both runs use the same session id and the same trusted working directory. --resume is the only variable.

Debug log evidence

With "logLevel": "all", ~/.copilot/logs/process-*.log reports the hook count at load time. Two plugins were installed for this run (hook-probe contributing 4 hooks and an unrelated one contributing 5), on top of 5 policy hooks:

Fresh session:

[WARNING] Loading repo hooks in prompt mode (folder is trusted or opt-in set)
[DEBUG] loadDeferredRepoHooks(8561317a-5af2-4550-a42a-1f9b1f6058c3): invoked
[DEBUG] loadDeferredRepoHooks(8561317a-5af2-4550-a42a-1f9b1f6058c3): loaded repo hooks (hookCount=14)

Resumed session:

[DEBUG] resume-auto-cd: resolved persisted cwd -> <same directory>
[WARNING] Loading repo hooks in prompt mode (folder is trusted or opt-in set)
[DEBUG] loadDeferredRepoHooks(8561317a-5af2-4550-a42a-1f9b1f6058c3): invoked
[DEBUG] loadDeferredRepoHooks(8561317a-5af2-4550-a42a-1f9b1f6058c3): loaded repo hooks (hookCount=5)

14 → 5 is exactly the two plugins' 9 hooks going missing; the 5 that survive are the policy hooks loaded from HKLM\Software\Policies\GitHub\Copilot\Defender. So the resume path reloads repo/policy hooks and never re-attaches plugin hooks.

The dispatch points still fire — only the plugin handlers are missing

This is not a case of the resumed session skipping its hook points. ~/.copilot/session-state/<id>/events.jsonl records a hook.start / hook.end pair for each one, and the resumed run has them:

// resumed run
{"type":"hook.start","data":{"hookType":"userPromptSubmitted","input":{"prompt":"reply with exactly: B", ...}}}
{"type":"hook.end","data":{"hookType":"userPromptSubmitted","success":true}}
{"type":"hook.start","data":{"hookType":"sessionStart","input":{"source":"resume","initialPrompt":"reply with exactly: B", ...}}}
{"type":"hook.end","data":{"hookType":"sessionStart","success":true}}
{"type":"hook.start","data":{"hookType":"agentStop","input":{"stopReason":"end_turn", ...}}}
{"type":"hook.end","data":{"hookType":"agentStop","success":true}}

sessionStart is even dispatched with "source":"resume", so the resume path is aware it should run session-start hooks. Every hook.end reports success: true — yet hook-probe.log was never created, because no plugin-contributed handler was attached to any of those dispatch points.

So the bug is narrowly in plugin hook registration on the resume path, not in hook dispatch. success: true on an invocation that executed nothing also makes this invisible to anyone reading the transcript.

Ruled out

  • Folder trust. A fresh session in an untrusted directory (C:\Windows\system32) fires all four hooks. Both runs above log folder is trusted or opt-in set.
  • Plugin enablement. copilot plugin list shows the plugin (enabled) before and after the resumed run.
  • Live vs copied install. Reproduced with a directory-source (live) plugin; the plugin is listed and enabled either way.
  • A broken hook command. The identical hooks.json fires on the fresh run seconds earlier.

Impact

A resumed session is indistinguishable from a fresh one to the user, but every plugin hook is silently inactive. Plugins that use hooks for observability, session tracking, or policy enforcement fail open with no warning and no log line saying plugin hooks were skipped.

Related

#1503 covered the same symptom for repo hooks and was fixed by deferring and reloading them on resume. That reload does not include plugin-contributed hooks, which is what this issue is about.

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

まず再開パスとloadDeferredRepoHooks()を追跡し、新規セッションと再開されたセッションについて、提供されたプロセスログ内のフック数を比較します。提供されたプラグインの再現手順とsession-state//events.jsonlを使用して、プラグインが提供するフックが--resume後に登録されて実行される一方で、policyフックは引き続き読み込まれることを確認します。再開された実行でプラグインのSessionStart、UserPromptSubmit、Stop、SessionEndの出力が記録されれば完了です。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
shell
領域
cli, tooling
issue の種類
バグ
難易度
3/5
見積もり時間
1〜2日
活発さ
活発
明瞭さ
明確に書かれている
初心者へのやさしさ
74/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。