Skip to content

onSessionStart hook: additionalContext return value silently ignored #2142

Closed

Description

@shsolomo

Bug

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

  1. Create an extension with onSessionStart returning { additionalContext: 'test' }
  2. Start a session and ask the agent if it sees 'test' in its context
  3. 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.

Environment

  • CLI: v1.0.8
  • OS: Windows 11

Activity

  1. shsolomo commented on Mar 19, 2026

    @shsolomo
    Author

    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:

    n.updateOptions({ hooks: this.createHooksProxy(I, r) })

    The base updateOptions implementation does:

    this.hooks = e.hooks  // direct assignment, not merge

    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:

    1. 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
    2. 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.

  2. ludwigwinkler commented on Mar 20, 2026

    @ludwigwinkler

    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. MRayermannMSFT commented on Mar 20, 2026

    @MRayermannMSFT

    @ludwigwinkler

    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?

    You can still have a simple hooks.json just like Claude Code and other CLI tools. See: https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/use-hooks

    2: Only a single/last extension fires which makes using multiple extensions kind of pointless.

    This is not intended behavior, so we will fix this/before extensions leave experimental.

    Why are hooks confined to only trigger when they deny a permission?

    Can you elaborate on this? I think I know what you are talking about but I want to make sure...

  4. ludwigwinkler commented on Mar 21, 2026

    @ludwigwinkler

    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

  5. MRayermannMSFT commented on Mar 21, 2026

    @MRayermannMSFT

    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):

    1. The original issue to be solved
    2. The capability to give extra additionalContext as part of preToolUse hooks

    ?

  6. MRayermannMSFT commented on Mar 23, 2026

    @MRayermannMSFT

    Fix gone out in latest CLI pre-release

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions