feat(agent): add GitHub Copilot CLI agent backend#318
Merged
kunchenguid merged 4 commits intoJun 26, 2026
Conversation
Add the GitHub Copilot CLI as a first-class agent at parity with the existing backends. New copilot agent runs `copilot -p` with JSONL output, streams assistant deltas, and inlines the JSON schema into the prompt. Wire it into auto-detection, agent_args_override, reserved flags, doctor, and the default config. Add a copilot intent transcript reader over ~/.copilot/session-state and register it. Update docs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The fix step requests schema-constrained output, but Copilot 1.0.x is
non-deterministic about honoring the JSON-only output contract: it often
emits the schema JSON in an earlier assistant message and then closes with
a prose summary (e.g. "Now I've applied all four fixes..."). The previous
code kept only the last assistant.message, so extraction had nothing to
recover and the whole step failed with an undiagnosable error:
copilot output parse: invalid character 'N' looking for beginning of value
parseCopilotEvents now collects every non-empty assistant.message, and
finalizeCopilotResult tries each newest-first against the schema, returning
the first that parses. finalizeTextResult also embeds a trimmed output
snippet in parse-failure errors so future failures are debuggable.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Please @kunchenguid can you add support for Copilot CLI |
Owner
|
Thanks @kevinamick - merged! 🎉 A clean, full-parity Copilot backend: the arg-array subprocess wiring (through |
|
Excellent! Thank you both @kevinamick and @kunchenguid |
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.
Intent
Add the GitHub Copilot CLI as a first-class no-mistakes agent at full parity with the existing backends (subprocess agent running 'copilot -p' with JSONL output and inlined schema, plus a ~/.copilot/session-state transcript intent reader), wired into auto-detection, config, reserved flags, doctor, and docs. Re-running only push/pr/ci to open the PR and watch CI after switching to a GitHub account permitted to create the PR.
What Changed
copilotagent backend (internal/agent/copilot.go) that runscopilot -pwith JSONL output and an inlined response schema, plus a matching~/.copilot/session-statetranscript intent reader (internal/intent/reader_copilot.go).AgentCopilottype into auto-detection probe order, default binary mapping,agent_args_overridewith reserved flags (-p,--prompt,--output-format,--no-color), thedoctoragent check, and theautoagent factory.Risk Assessment
✅ Low: The change is additive and well-bounded, faithfully mirrors the existing pi/claude agent and reader patterns with comprehensive unit tests and accurate docs, all call sites are wired, and the only prior actionable finding was correctly resolved with no new issues introduced.
Testing
Baseline
go build ./...is clean. Ran the targeted copilot agent and intent-reader suites plus the agent/config/types packages: every copilot-specific test passes, including two that spawn a real fakecopilotprocess and exercise the fullcopilot -pJSONL streaming/parse path and non-zero-exit error surfacing, and reader tests that discover/load ~/.copilot/session-state transcripts with cwd/time filtering and synthetic-message exclusion. For reviewer-visible product evidence I added a portable in-process test that renders the actualno-mistakes doctorCLI report and confirmscopilotis now a first-class Agents entry detected on PATH (captured as a transcript artifact). Two failing tests (internal/cli TestRoot*/telemetry and internal/intent symlink) are pre-existing, reproduced, environment-only issues (git 2.54 bare-repo config behavior; Windows symlink privilege) that the additive Copilot change does not touch. Transient build output (bin/) was removed; the new test file is intentionally kept.Evidence: Rendered `no-mistakes doctor` report — copilot listed and detected as a first-class agent
Agents – claude not found – codex not found – rovodev not found – opencode not found – pi not found ✓ copilot <bin>\copilot.cmd (detected on PATH, same probe as the other agents)Evidence: Copilot agent + transcript-reader test transcript (incl. real-subprocess copilot -p JSONL run)
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 3 issues found → auto-fixed ✅
internal/agent/copilot.go:145- copilotUserSetPermissionMode treats --available-tools/--excluded-tools as permission flags, so supplying only one of them via agent_args_override suppresses the default --allow-all-tools. Those flags restrict the available tool set, not the approval policy; without an approval grant the non-interactive-prun can block or fail on tool-approval prompts, because the always-added --no-ask-user only disables the ask_user tool, it does not auto-approve tool execution. Confirm whether --allow-all-tools should still be appended when the user supplied only tool-set (not approval) flags.internal/agent/copilot.go:248- parseCopilotEvents only accumulates OutputTokens; copilotEventData declares no input/cache token fields and the terminalresultevent's usage object (present in fixtures, e.g. {"premiumRequests":1}) is ignored. Every other agent records InputTokens/cache tokens, so copilot usage/cost reporting will undercount. Likely acceptable given Copilot's premium-request billing model, but it is an intentional-looking asymmetry worth a quick confirm.internal/agent/copilot.go:96- The error message captured fromerror/assistant.abortevents (copilotErr) is only surfaced when waitErr != nil or exitCode != 0. If copilot reports an error/abort but still exits 0 with no non-empty assistant.message content, finalizeTextResult returns a generic "copilot returned no text output" and the real reason is discarded. There is also no delta-accumulation fallback (unlike pi's finalText), so a final answer delivered only through assistant.message_delta would be lost. Consider surfacing copilotErr (and/or accumulated deltas) when lastMessage is empty.🔧 Fix: Restrict copilot allow-all-tools suppression to approval grants
✅ Re-checked - no issues remain.
internal/cli/root_test.go- Pre-existing, environment-only failures unrelated to the Copilot change: internal/cli TestRoot* and TestInitTracksCommandTelemetry fail because git 2.54.0.windows.1 returnsfatal: not in a git directory(exit 128) forgit config receive.advertisePushOptions trueagainst a bare repo (invoked by internal/gate and internal/daemon during init). Reproduced directly:git init --bare <dir>; git -C <dir> config receive.advertisePushOptions true=> exit 128. The Copilot change touches none of gate/daemon/git/init, so these are not regressions; flagged for reviewer awareness so CI noise isn't mistaken for a Copilot defect.internal/intent/disambiguator_test.go:234- Pre-existing, environment-only failure unrelated to the Copilot change: internal/intent TestAgentDisambiguatorPreservesPreexistingIgnoredSymlink fails withA required privilege is not held by the clientbecause creating a symlink on this Windows host requires Developer Mode/elevated privileges. All Copilot reader tests in the same package pass. Flagged for awareness only.internal/cli/doctor_copilot_test.go- new test file written by agent: internal/cli/doctor_copilot_test.gogo build ./...(clean)go test -run 'Copilot' -v ./internal/agent ./internal/intent ./internal/config— all copilot agent + transcript-reader tests pass, including TestCopilotAgent_RunParsesJSONOutput and TestCopilotAgent_RunReportsErrorOnNonZeroExit which spawn a real fakecopilotsubprocess and parse its JSONL end-to-endgo test ./internal/agent ./internal/config ./internal/types(green: agent registration, defaultBinary, auto-detect probe order, agent_args_override/reserved flags, AgentCopilot const)Added and ran new portable testgo test -run 'TestDoctorListsCopilotAgent' -v ./internal/cli— renders the realno-mistakes doctorreport and asserts copilot appears in Agents and is detected at its on-PATH binarygo test -run 'TestDoctor' -v ./internal/cli(doctor telemetry test passes)Reproducedgit init --bare <dir>; git -C <dir> config receive.advertisePushOptions true=>fatal: not in a git directory(exit 128) to confirm the cli TestRoot*/telemetry failures are git 2.54 environment issues, not regressions✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.