Skip to content

Register the Copilot event handler via SessionConfig.OnEvent so session-lifecycle events are not dropped - #746

Open
PratikDhanave wants to merge 2 commits into
microsoft:mainfrom
PratikDhanaveFork:register-copilot-onevent-before-session-rpc
Open

Register the Copilot event handler via SessionConfig.OnEvent so session-lifecycle events are not dropped#746
PratikDhanave wants to merge 2 commits into
microsoft:mainfrom
PratikDhanaveFork:register-copilot-onevent-before-session-rpc

Conversation

@PratikDhanave

Copy link
Copy Markdown
Contributor

What

The Copilot provider opened the session (openSessionclient.CreateSession/ResumeSession) and only afterward called copilotSession.On(eventHandler). sessionConfig() and resumeSessionConfig() never set OnEvent.

This change threads the eventHandler created in run() into openSession and sets cfg.OnEvent = eventHandler inside both sessionConfig() and resumeSessionConfig(), then removes the now-redundant post-return .On(eventHandler).

Why

The Copilot SDK registers the session (and any config.OnEvent handler) synchronously before it issues the session.create / session.resume RPC, precisely so that lifecycle events the CLI emits during session creation (e.g. session.start) are not missed. The SDK read loop runs on a separate goroutine, so with the old post-return On there is a window between the RPC returning and the .On call during which such events are dispatched to a session with zero handlers and silently dropped (the event channel's consumer snapshots the handler list at dequeue time, so an event dequeued before .On runs is lost).

Registering through OnEvent closes that window and matches the cross-SDK behaviour: the .NET and Python Copilot integrations attach the provider's event handler at session-construction time (as part of the create/resume options) rather than after the create call returns, so no early events are missed.

This is not a duplicate of #710 (SessionConfig field carry-over) or #595 (Tools clone).

Testing

Added TestRun_SurfacesLifecycleEventEmittedDuringSessionResume in copilot_test.go. The fake runtime now emits a session.start event during session.resume handling, before the RPC response, and the test asserts the provider surfaces it. The test flakily fails against the old code (the event is dropped in the race window — reproduced deterministically with -count) and passes reliably with the fix, including under -race and -count=300.

  • go build ./...
  • go vet ./provider/copilotprovider/...
  • go test ./provider/copilotprovider/... -race

Copilot AI review requested due to automatic review settings July 24, 2026 04:04
@PratikDhanave
PratikDhanave requested a review from a team as a code owner July 24, 2026 04:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Copilot provider’s session creation/resume path so the run-scoped Copilot event handler is registered via SessionConfig.OnEvent at session construction time, preventing session-lifecycle events emitted during session.create/session.resume from being dropped.

Changes:

  • Thread the run’s eventHandler into openSession and set it on SessionConfig.OnEvent / ResumeSessionConfig.OnEvent before the create/resume RPC.
  • Remove the post-return session.On(eventHandler) subscription that introduced an event-loss window.
  • Add a regression test that emits a lifecycle event during session.resume (before the RPC response) and asserts it is surfaced.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
provider/copilotprovider/copilot.go Registers the run event handler through config at session construction time to avoid dropping early lifecycle events.
provider/copilotprovider/copilot_test.go Adds a fake-runtime mechanism to emit pre-response resume events and a test ensuring the provider surfaces them.
Comments suppressed due to low confidence (1)

provider/copilotprovider/copilot.go:252

  • resumeSessionConfig unconditionally sets cfg.OnEvent = eventHandler, which prevents any handler configured on p.cfg.SessionConfig.OnEvent from ever running on resumed sessions (and also makes create/resume behavior inconsistent if callers rely on SessionConfig.OnEvent). Consider seeding cfg.OnEvent from p.cfg.SessionConfig.OnEvent and chaining it with the per-run handler.
func (p *provider) resumeSessionConfig(streaming bool, eventHandler copilot.SessionEventHandler, options []agent.Option) copilot.ResumeSessionConfig {
	cfg := copyResumeSessionConfig(p.cfg.SessionConfig)
	cfg.Streaming = copilot.Bool(streaming)
	cfg.OnEvent = eventHandler
	cfg.SystemMessage = systemMessageWithInstructions(cfg.SystemMessage, slices.Collect(agent.AllOptions(options, agent.WithInstructions)))
	cfg.Tools = append(cfg.Tools, copilotTools(options)...)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread provider/copilotprovider/copilot.go
@github-actions

This comment has been minimized.

@github-actions github-actions Bot added the parity-approved Go API consistency review found no parity issues label Jul 24, 2026
The Copilot provider opened the session and only afterward called
copilotSession.On(eventHandler). Because the SDK runs the session read
loop on a separate goroutine and registers the session before the
session.create/session.resume RPC, any lifecycle events the CLI emits
during session creation (e.g. session.start) were dispatched to a
session with zero handlers and dropped in the window before the
post-return On call.

Thread the handler into openSession and set it on OnEvent in both
sessionConfig and resumeSessionConfig so it is registered before the
RPC, matching the SDK's no-missed-events guarantee and the .NET/Python
behaviour of attaching the provider handler at session-construction
time rather than after the create call returns. The redundant
post-return On call is removed.
@PratikDhanave
PratikDhanave force-pushed the register-copilot-onevent-before-session-rpc branch from c0bbe44 to 2869077 Compare July 24, 2026 09:30
@github-actions

Copy link
Copy Markdown
Contributor

Parity Review ✅

This PR changes only unexported/internal methods in provider/copilotprovider/copilot.go:

  • openSession, sessionConfig, resumeSessionConfig (all unexported)
  • New unexported helper chainSessionEventHandlers
  • Bug fix: moves Copilot event-handler registration from post-RPC session.On() to pre-RPC SessionConfig.OnEvent to avoid dropped lifecycle events

No exported Go API surface was added or changed, so the public-api-change label is not warranted.

Cross-repo parity: This is Copilot-specific session plumbing with no direct equivalent in the upstream .NET or Python microsoft/agent-framework implementations. The change is an internal correctness fix (race-window between RPC and handler registration) that does not affect public contracts or user-visible behavior semantics.

No parity issues found. 🟢

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

Generated by Go API Consistency Review Agent · 18.7 AIC · ⌖ 5.56 AIC · ⊞ 5.9K ·

@gdams
gdams added this pull request to the merge queue Jul 29, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Jul 29, 2026
@gdams
gdams enabled auto-merge July 29, 2026 14:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

parity-approved Go API consistency review found no parity issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants