feat: GitHub Copilot CLI over ACP + TCP transport [INT-945]#415
Open
AlexanderZ-Band wants to merge 5 commits into
Open
feat: GitHub Copilot CLI over ACP + TCP transport [INT-945]#415AlexanderZ-Band wants to merge 5 commits into
AlexanderZ-Band wants to merge 5 commits into
Conversation
dd08372 to
9801c4b
Compare
e9563a3 to
bd5035f
Compare
9801c4b to
0477e81
Compare
Base automatically changed from
int-921-sdk-copilot_sdk-adapter-python-implement-test
to
dev
July 9, 2026 20:09
bd5035f to
dd1ddeb
Compare
pyrefly runs on Linux CI where msvcrt's locking/LK_NBLCK/LK_UNLCK attributes are absent, failing the lint gate. Suppress inline, matching the existing pyrefly-ignore convention. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jul 10, 2026
* feat(e2e): baseline scorecard — N/A reasons on exclude + CI artifact [INT-820] Excluded adapters vanished from baseline results with no row and the reason buried in a comment. Make the full adapter×test grid observable: - exclude carries a reason: @per_adapter(exclude=...) takes ExcludedAdapter( adapter, reason) records; a non-empty reason is required at decoration time (ExcludedAdapter.__post_init__), mirroring AdapterSpec.e2e_pending. - the PerAdapter marker carries the exclusions so collection can read them back; specs()/adapter_params() stay id-based (clean layering). - scorecard.py: pure functions (na_rows / outcome_row / merge / to_markdown) build the grid from the run reports (exact nodeid keys, no junit scraping) plus the marker exclusions; a settings-driven conftest hook writes per-run JSON, and a merge CLI folds the per-lane files into one artifact. - CI: each e2e lane emits artifacts/scorecard-<lane>-<os>.json; a final scorecard job merges them into artifacts/scorecard.json (+ markdown). It reports the matrix, it does not gate on it. - migrate all 13 exclude call sites to ExcludedAdapter with reasons. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(e2e): scorecard counts only registered-adapter cells [INT-820] outcome_row treated any parametrized nodeid as a matrix cell, so non-adapter parametrizations (e.g. test_send_event[thought]) leaked "error"/"task"/ "thought" into the grid as phantom adapters. Filter cell params to the registered Adapter ids; add a regression test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(e2e): scorecard collector as a registered plugin, no globals [INT-820] The conftest routed session-wide hooks (pytest_runtest_logreport / pytest_sessionfinish) through module globals because logreport gets no config/session. Make ScorecardCollector a plugin object that owns its state and output path; the conftest registers one only when a path is set. Removes the globals and the `global` statement — the idiomatic pytest pattern for stateful hook collection. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(e2e): exclude letta from partial-reboot scenario [INT-985] Two Letta agents in one org can't keep separate identities: Letta stores MCP tools by name at org scope and re-points the shared band_send_message row to the most-recently-registered adapter's server, so the rebooted agent's reply routes through the peer's MCP server and posts under the peer's identity. Confirmed with a live probe. The cascade-delete fix (no deregister on release) removed the 404s that previously masked this, exposing the identity cross-wire. Excluded with a plain-language reason until per-instance tool-name isolation lands (INT-985). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: add ci-required aggregate gate job [INT-975] Branch protection can't reliably require the unit/conformance CI today: the `test`/`test-crewai` jobs are matrices, so GitHub emits per-cell contexts (`test (ubuntu-latest, 3.11)`, …) and the bare `test (3.11)`/`test (3.12)` names in the ruleset never report — they sit "Expected" forever. dev requires no test context at all. Add one aggregate `ci-required` job that `needs` every gating job and fails unless all resolved to success. A matrix dependency is green only when all its cells passed, so a single required `ci-required` context covers the whole OS/Python matrix and survives future matrix changes without another ruleset edit. `if: always()` so a failed dependency still runs the gate (a skipped job reports nothing and would leave the required check stuck). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
GitHub Copilot CLI as an ACP client backend, plus a TCP transport on the generic
ACP client layer. Copilot speaks vanilla ACP, so it rides the existing
ACPClientAdaptervia a thin
CopilotACPAdapter— the only net-new SDK capability is TCP. Verified liveagainst
copilot1.0.69 over both stdio and TCP.Linear: https://linear.app/thenvoi/issue/INT-945/add-github-copilot-cli-integration-via-acp (Implements INT-945)
Changes
tcp_spawn_processconnect-only seam +host/portonACPClientAdapter(keyword-only; exactly-one-ofcommand/host+port). ZeroACPRuntimecore change (uses the injectablespawn_processseam).CopilotACPAdapter(+ config) — thinACPClientAdapter; flexible auth viaenv(env token / stored login /
gh/ BYOK;github_tokenis a convenience). Registered inthe baseline matrix (
backendslane, gated on the CLI only like codex); excluded fromframework-conformance as a bridge.
FakeSpawn) instead of monkeypatching; newacp_toolkit(a fake ACPagent +
acp_adapterdriver +Replyview) drives the adapter over a real in-processsocketpair — real JSON-RPC, LLM faked: text/thought/tool_call+result/plan relay,
permission auto-approve, room isolation.
examples/acp/{servers,clients,copilot_docker,copilot_sandbox}/;two Copilot-in-a-container Docker examples (compose + colocated) over TCP with Band tools via a
band-mcpSSE server (socatfrontscopilot --acpon0.0.0.0, since--portbinds loopbackonly; images build), plus a Docker-sandbox (
sbx) example over stdio — validated live(
sbx exec -i … copilot --acpis byte-clean; host secret-proxy auth, token never enters the VM).Testing
ruff/ruff format/pyreflycleancopilot --acpround-trips over stdio and TCP (copilot 1.0.69, stored login, no token)🤖 Generated with Claude Code