Skip to content

feat: add GitHub Copilot CLI adapter#1

Merged
ScottRBK merged 2 commits into
mainfrom
feature-copilot-adapter
Apr 20, 2026
Merged

feat: add GitHub Copilot CLI adapter#1
ScottRBK merged 2 commits into
mainfrom
feature-copilot-adapter

Conversation

@ScottRBK

Copy link
Copy Markdown
Owner

Summary

  • Add CopilotCLIAdapter implementing the AgentAdapter protocol for GitHub Copilot CLI
  • Maps Copilot CLI NDJSON events (reasoning_delta, message_delta, result, etc.) to shared StreamEvent types
  • Full test coverage: 148 unit+integration tests + 10 E2E tests (174 total, all passing)

Key design decisions

  • Reasoning events require --enable-reasoning-summaries CLI flag; disabled by default when include_thinking=False
  • Cost defaults to 0.0 (Copilot doesn't expose pricing data)
  • Session ID extracted from the result event (not turn_start — a behavioral difference from Claude Code/OpenCode)
  • Auto-approve uses --allow-all-tools by default; individual --allow-tool flags when allowed_tools is specified
  • Duck-typed Protocol satisfaction (no inheritance required)

Files

File Purpose
src/agent_shell/adapters/copilot_cli_adapter.py Adapter implementation (~194 lines)
tests/unit/copilot_fixtures.py NDJSON event fixtures
tests/unit/test_copilot_cli_*.py Unit tests (parse, stream, execute, cancel)
tests/integration/test_copilot_cli_integration.py Integration tests through AgentShell
tests/e2e/test_copilot_cli_e2e.py E2E tests against real CLI
src/agent_shell/shell.py Wired adapter into _resolve_adapter()
README.md Updated with Copilot CLI example

Add CopilotCLIAdapter implementing the AgentAdapter protocol, enabling
agent-shell to execute Copilot CLI as a supported agent type alongside
Claude Code and OpenCode.

Key details:
- Maps Copilot CLI NDJSON events (reasoning_delta, message_delta, result, etc.)
  to shared StreamEvent types
- Requires --enable-reasoning-summaries flag for thinking/reasoning events
- Cost defaults to 0.0 (Copilot doesn't expose pricing)
- Session ID extracted from the result event (not turn_start)
- Uses --allow-all-tools by default for auto-approve, individual --allow-tool
  flags when allowed_tools list is specified

148 unit+integration tests + 10 E2E tests all passing (174 total).

@ScottRBK ScottRBK left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Review

Overall: solid adapter, cleanly mirrors OpenCodeAdapter, good test coverage (unit + integration + E2E). One behavioural item worth addressing before merge, plus minor notes.

assistant.turn_start emits a payload-less system event

copilot_cli_adapter.py:195-196:

if t == "assistant.turn_start":
    events.append(StreamEvent(type="system", content=""))

Copilot's turn_start payload has no sessionId (only result does — see copilot_fixtures.py:893), so this yields StreamEvent(type="system", content="", session_id=None) — zero information.

There's a direct precedent in claude_code_adapter.py:140-143 which guards emission on session_id:

if t == "system":
    if session_id:
        events.append(StreamEvent(type="system", content="", session_id=session_id))

Across the three adapters, type="system" is implicitly a session-id carrier, not a lifecycle marker. An empty one is invisible to execute()'s next((e.session_id for e in chunks if e.session_id), None) harvest and misleading to consumers filtering by type.

Suggestion: drop the turn_start branch entirely. The result branch already emits session_id correctly.

duration isn't surfaced on AgentResponse

README says duration is populated from usage.totalApiDurationMs, but AgentResponse has no duration field — only the StreamEvent carries it, so execute() callers can't see it. Either add it to AgentResponse (cross-adapter change) or tighten the README wording.

Minor

  • test_copilot_cli_cancel.py doesn't assert unregister_process_group was called.

@ScottRBK ScottRBK merged commit 8ea00c1 into main Apr 20, 2026
2 checks passed
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.

1 participant