You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
onSessionStart hook: additionalContext return value silently ignored #2142
The Copilot SDK documents additionalContext as a valid return field from onSessionStart hooks (docs). However, the Copilot CLI v1.0.8 ignores the return value entirely.
Evidence
In the CLI's bundled source, the sessionStart hook invocation is fire-and-forget — no variable assignment, so the result (including additionalContext) is discarded. Compare with userPromptSubmitted which captures modifiedPrompt, and postToolUse which captures modifiedResult.
Repro
Create an extension with onSessionStart returning { additionalContext: 'test' }
Start a session and ask the agent if it sees 'test' in its context
Agent says NO
Impact
Extensions wanting to inject boot-time context (knowledge graphs, project metadata, etc.) cannot use the documented additionalContext mechanism. Workaround: cache context in onSessionStart, inject via onUserPromptSubmitted modifiedPrompt.
The hook dispatch function px() iterates this.hooks.sessionStart as an array, but by then only the last extension's proxy remains. A merge function Uxt() exists in the codebase but is only used for config-file hooks (hooks.json), not extension runtime hooks.
Impact
This means:
Even if the fire-and-forget issue from my original report is fixed, multi-extension setups will still lose hooks from all but the last loaded extension
createHooksProxy returns per-connection arrays like sessionStart: [o => r("sessionStart", o)] where r sends a hooks.invoke RPC to that specific extension. When overwritten, those RPCs never fire.
Workaround
session.on("user.message") + session.send() event listeners are registered per-connection and are not subject to the overwrite. This is what my extension now uses.
Related
This is the same root cause as #2076 (extension hooks overriding hooks.json hooks). The fix should apply Uxt() (or equivalent merge logic) in updateOptions for extension hooks, not just config-file hooks.
Yeah, I arrived at the exact same conclusion.
1: The extension framework currently is very hairy to set up and to use. Claude Code has simply hooks in a json that just works. Why are hooks confined to only trigger when they deny a permission?
2: Only a single/last extension fires which makes using multiple extensions kind of pointless.
3: Why is the whole detour through a javascript necessary at all? Why not just implement a hook system like Claude Code for which simple shell scripts suffice?
3: Why is the whole detour through a javascript necessary at all? Why not just implement a hook system like Claude Code for which simple shell scripts suffice?
Regarding the hooks,
I wanted to use it to insert extra GPU information into the prompt but according to your documentation and what I've experienced while trying to configure Copilot CLI, only deny permissions on preToolUse are actually invoked.
I wanted to use it to insert extra GPU information into the prompt but according to your documentation and what I've experienced while trying to configure Copilot CLI, only deny permissions on preToolUse are actually invoked.
The original issue was regarding that additionalContext is being ignored during onSessionStart hooks. I have a PR out for that, should be merged and shipped very early next wekk.
So to clarify are you looking for (and/or):
The original issue to be solved
The capability to give extra additionalContext as part of preToolUse hooks
Activity
Refactor boot context: extract buildBootContext, add self-healing and…
shsolomo commentedon Mar 19, 2026
Additional Root Cause: Multi-Extension Hook Overwrite
After further investigation, I found a deeper issue that compounds this bug. In multi-extension environments, only the last extension's hooks survive.
Root Cause
In
initializeSession()(app.js ~line 4170), each extension's hooks are registered via:The base
updateOptionsimplementation does:The hook dispatch function
px()iteratesthis.hooks.sessionStartas an array, but by then only the last extension's proxy remains. A merge functionUxt()exists in the codebase but is only used for config-file hooks (hooks.json), not extension runtime hooks.Impact
This means:
createHooksProxyreturns per-connection arrays likesessionStart: [o => r("sessionStart", o)]wherersends ahooks.invokeRPC to that specific extension. When overwritten, those RPCs never fire.Workaround
session.on("user.message")+session.send()event listeners are registered per-connection and are not subject to the overwrite. This is what my extension now uses.Related
This is the same root cause as #2076 (extension hooks overriding hooks.json hooks). The fix should apply
Uxt()(or equivalent merge logic) inupdateOptionsfor extension hooks, not just config-file hooks.ludwigwinkler commentedon Mar 20, 2026
Yeah, I arrived at the exact same conclusion.
1: The extension framework currently is very hairy to set up and to use. Claude Code has simply hooks in a json that just works. Why are hooks confined to only trigger when they deny a permission?
2: Only a single/last extension fires which makes using multiple extensions kind of pointless.
3: Why is the whole detour through a javascript necessary at all? Why not just implement a hook system like Claude Code for which simple shell scripts suffice?
MRayermannMSFT commentedon Mar 20, 2026
@ludwigwinkler
You can still have a simple
hooks.jsonjust like Claude Code and other CLI tools. See: https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/use-hooksThis is not intended behavior, so we will fix this/before extensions leave experimental.
Can you elaborate on this? I think I know what you are talking about but I want to make sure...
ludwigwinkler commentedon Mar 21, 2026
Regarding the hooks,
I wanted to use it to insert extra GPU information into the prompt but according to your documentation and what I've experienced while trying to configure Copilot CLI, only deny permissions on preToolUse are actually invoked.
Here's the doc: https://docs.github.com/en/copilot/reference/hooks-configuration#pre-tool-use-hook
MRayermannMSFT commentedon Mar 21, 2026
The original issue was regarding that
additionalContextis being ignored duringonSessionStarthooks. I have a PR out for that, should be merged and shipped very early next wekk.So to clarify are you looking for (and/or):
additionalContextas part ofpreToolUsehooks?
MRayermannMSFT commentedon Mar 23, 2026
Fix gone out in latest CLI pre-release
refactor: use onSessionStart additionalContext for boot injection
feat(pinned-floor): wire UserPromptSubmit hook (Claude Code + Copilot)
fix(copilot): wire sessionStart so Copilot CLI is told token-goat exists