Skip to content

refactor: decouple ExecutionResponse from Copilot SDK events (Phase 1, #10)#396

Merged
spboyer merged 2 commits into
mainfrom
spboyer-decouple-execution-response-phase1
Jun 30, 2026
Merged

refactor: decouple ExecutionResponse from Copilot SDK events (Phase 1, #10)#396
spboyer merged 2 commits into
mainfrom
spboyer-decouple-execution-response-phase1

Conversation

@spboyer

@spboyer spboyer commented Jun 30, 2026

Copy link
Copy Markdown
Member

Summary

Phase 1 of #10 — introduces an engine-neutral event abstraction so additional agent engines (Claude Code, Codex, generic CLI) can plug in for Phase 2 without coupling ExecutionResponse to the Copilot SDK.

This is a pure refactor with no behavioral change. All existing tests pass unchanged.

Changes

New abstraction layer

  • internal/agentevent — engine-neutral Event carrying a Kind enum (KindAssistantMessage, KindToolStart, …) and an opaque Raw any payload. Accessors Kind() / Raw().
  • internal/copilotevents/bridge.go — bridge between SDK copilot.SessionEvent and the generic agentevent.Event:
    • FromSDK([]copilot.SessionEvent) []agentevent.Event — wrap at engine boundary
    • ToSDK([]agentevent.Event) []copilot.SessionEvent — unwrap for legacy consumers
    • AsSDKEvent(agentevent.Event) (copilot.SessionEvent, bool) — single-item unwrap
    • WrapSDKEvent(copilot.SessionEvent) agentevent.Event

ExecutionResponse

  • Events []copilot.SessionEventEvents []agentevent.Event.
  • ExtractMessages rewritten against the generic Kind, unwrapping via copilotevents.AsSDKEvent to read AssistantMessageData.

Engines

  • internal/execution/copilot.go wraps SDK events with copilotevents.FromSDK(...) at the boundary when building the response.
  • internal/execution/mock.go emits []agentevent.Event{} (dropped the copilot import).

Consumers (minimal-churn migration)

Call sites that still need SDK events use copilotevents.ToSDK(resp.Events):

  • internal/orchestration/runner.gobuildToolEvents, transcript construction, buildTranscript
  • internal/trigger/runner.gotranscript.BuildFromSessionEvents
  • cmd/waza/cmd_run_suggest.gobuildNoSuggestionsError, writeSuggestionTranscript

Tests

Fixture event slices now wrap with copilotevents.FromSDK(...):

  • cmd/waza/cmd_run_suggest_test.go
  • internal/execution/engine_response_test.go
  • internal/orchestration/runner_orchestration_test.go

Out of scope (Phase 1)

  • ExecutionRequest.Tools, .MCPServers, .PermissionHandler still use SDK types. These are engine-input types, not response types, and are outside Phase 1's acceptance criteria. Phase 2 will revisit them.
  • models.TranscriptEvent (on-disk transcript format) still embeds copilot.SessionEvent — persistence format, not an engine-neutral type.

Validation

  • go build ./... — clean
  • go test ./... — all packages pass
  • golangci-lint run — 0 issues

Phase 2 hand-off

Phase 2 (multi-agent engine support, owned by @richardpark-msft) will extend the agentevent.Event model with more kinds and add per-engine adapters parallel to copilotevents. Consumers can then migrate from copilotevents.ToSDK(...) to direct generic accessors.

Partial progress on #10 — issue stays open for Phase 2.

Copilot AI review requested due to automatic review settings June 30, 2026 13:36

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 implements Phase 1 of issue #10 by introducing an engine-neutral event abstraction so ExecutionResponse no longer exposes Copilot SDK event types directly, enabling additional agent engines to integrate in a follow-up phase.

Changes:

  • Added internal/agentevent as an engine-neutral event wrapper with a Kind enum and opaque Raw payload.
  • Added internal/copilotevents bridging helpers to wrap/unwrap Copilot SDK SessionEvent values at engine/consumer boundaries.
  • Updated ExecutionResponse.Events and migrated key call sites + fixtures/tests to the new abstraction.
Show a summary per file
File Description
internal/agentevent/agentevent.go Introduces engine-neutral Event + Kind model.
internal/copilotevents/bridge.go Adds wrap/unwrap bridge between SDK events and agentevent.Event.
internal/execution/engine.go Switches ExecutionResponse.Events to []agentevent.Event and updates ExtractMessages.
internal/execution/copilot.go Wraps SDK events at the Copilot engine boundary via FromSDK.
internal/execution/mock.go Removes Copilot import and emits empty []agentevent.Event.
internal/orchestration/runner.go Unwraps to SDK events at legacy consumer boundaries.
internal/trigger/runner.go Unwraps to SDK events when building transcripts.
cmd/waza/cmd_run_suggest.go Converts to SDK events for transcript/error tracing paths.
cmd/waza/cmd_run_suggest_test.go Updates fixtures to wrap SDK events via FromSDK.
internal/execution/engine_response_test.go Updates fixtures to wrap SDK events via FromSDK.
internal/orchestration/runner_orchestration_test.go Updates fixtures to wrap SDK events via FromSDK.

Review details

  • Files reviewed: 11/11 changed files
  • Comments generated: 3
  • Review effort level: Low

Comment thread internal/orchestration/runner.go Outdated
Comment thread internal/orchestration/runner.go Outdated
Comment thread internal/execution/engine.go Outdated
Copilot AI added 2 commits June 30, 2026 10:19
Phase 1 of issue #10: introduce an engine-neutral event abstraction so
additional engines (Claude Code, Codex, generic CLI) can plug in in
Phase 2 without coupling ExecutionResponse to the Copilot SDK.

- Add internal/agentevent: engine-neutral Event with Kind + opaque Raw
  payload (KindAssistantMessage, KindToolStart, etc.).
- Add internal/copilotevents.{FromSDK,ToSDK,AsSDKEvent,WrapSDKEvent}:
  bridge between SDK SessionEvent and the generic Event.
- Change ExecutionResponse.Events from []copilot.SessionEvent to
  []agentevent.Event; rewrite ExtractMessages against the generic Kind
  and unwrap via copilotevents.AsSDKEvent.
- Copilot engine wraps SDK events at the boundary with
  copilotevents.FromSDK; MockEngine emits []agentevent.Event{}.
- Consumers (orchestration/runner, trigger/runner, cmd_run_suggest)
  call copilotevents.ToSDK at call sites that still consume SDK
  events. Tests updated to wrap fixtures with copilotevents.FromSDK.

Pure refactor; no behavioral change. All existing tests pass.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Three fixes from the PR #396 review:

1. internal/orchestration/runner.go: convert resp.Events to SDK form once
   per executeRun result-building section and reuse the slice across
   buildGraderContext, BuildFromSessionEvents (transcript), and
   buildToolEvents. Previously executeRun walked the event slice three
   times after the responder/follow-up loop. checkpointRunner still
   converts on its own because it runs at each turn boundary.

2. internal/orchestration/runner.go: buildGraderContext now delegates to
   transcript.BuildFromSessionEvents instead of appending to a nil slice,
   so the result slice is preallocated to len(events) in one place.

3. internal/execution/engine.go: ExtractMessages no longer silently drops
   assistant messages from non-Copilot engines. agentevent gains an
   optional TextProvider interface; ExtractMessages falls back to
   Event.Text() when AsSDKEvent fails so future engines (Claude Code,
   Codex, generic CLI) can surface assistant content with no further
   plumbing in this method.

Adds unit tests for the new TextProvider abstraction and the
ExtractMessages fallback (covers Copilot-only, non-Copilot-only, mixed,
and opaque-payload cases).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@spboyer spboyer force-pushed the spboyer-decouple-execution-response-phase1 branch from fee2020 to 3d57f4a Compare June 30, 2026 14:19
@spboyer spboyer merged commit 2e7e871 into main Jun 30, 2026
10 checks passed
@spboyer spboyer deleted the spboyer-decouple-execution-response-phase1 branch June 30, 2026 14:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants