Skip to content

feat(planner): add the Copilot SDK client - #185

Merged
Bakhomious merged 4 commits into
mainfrom
step/copilot-client
Oct 30, 2026
Merged

Bakhomious merged 4 commits into
mainfrom
step/copilot-client

Conversation

@Bakhomious

Copy link
Copy Markdown
Contributor

Brief

Goal: the planner's client of the Copilot SDK (copilot-client, plan §0 and §8.2, decisions 403 and 404). It starts the Copilot CLI and works out a plan in plan mode.

Scope

  • services/planner/internal/copilot: New, Start, Stop and Plan. Plan opens a session on claude-opus-5.5 with §3's seven tools, plan mode, the empty working folder, no custom instructions and a permission handler that rejects every request. It keeps the plan that exit_plan_mode brings, and answers exit_only, which ends the turn.
  • The CLI's environment is the service's own without any COPILOT_* or GH_* variable or GITHUB_TOKEN, plus COPILOT_GH_HOST. UseLoggedInUser is off, and the token goes as GitHubToken.
  • The Copilot SDK for Go v1.0.28 in go.mod. mise's copilot is pinned at 1.0.105, the version that SDK names, and is lazy. mise run copilot:check, part of check, fails when the two differ.
  • Renovate: no updates for mise's copilot. The SDK's PRs get a note to update the pin.
  • Docs: decision 404; §3, decisions 73 and 74, §6.1, §6.2, §10 and §0.

Out of scope

  • The service, its settings and the Keychain (planner-service).
  • Streaming, the agent's questions and resuming (later steps).
  • A live run with the real CLI, which waits for the PAT.

Acceptance criteria

  1. A plan's session asks for claude-opus-5.5, the seven tools, the working folder, no custom instructions, permission requests and exit_plan_mode requests. Its prompt goes in plan mode.
  2. exit_plan_mode is answered approved, with exit_only, and Content() returns its plan.
  3. Every permission request (write, shell, URL) is rejected.
  4. The CLI gets no COPILOT_*, GH_* or GITHUB_TOKEN variable from the service's environment, and gets COPILOT_GH_HOST. The caller's slice stays as it was.
  5. Failures:
    • a session that fails to start returns an error and leaves no session behind;
    • a prompt that fails to send returns an error, and the client leaves the session;
    • a missing CLI gives an fs.ErrNotExist error.
  6. mise run copilot:check passes with the pins as they are, and fails when they differ.
  7. CI doesn't install the CLI, and mise run check passes.

QA

  • BAKHYARD_E2E_PORT=8480 mise run check passed on the merged head. It ran:
    • fmt:check; lint (0 issues); lint:actions; lint:compose; gen:check; copilot:check;
    • cover: 96.5% in total, 96.9% for the package;
    • vuln: the golang.org/x/crypto openpgp module note was already on main, and nothing calls it;
    • //apps/web:check;
    • the end-to-end tests: 386 passed, 8 skipped, as on main.
  • go test -race -count=3 on the package passes.
  • copilot:check passes. It fails with:
    • copilot pinned at 1.0.104;
    • an SDK version that doesn't exist;
    • a 404;
    • a package.json without copilotCliVersion;
    • the pin written as a plain string.
  • go list -m works with an empty module cache, as on a fresh runner.
  • mise install --dry-run skips copilot. mise install copilot installs 1.0.105, which runs.
  • renovate.json passes renovate-config-validator --strict (Renovate 44.198.0). Renovate's mise manager maps copilot to github/copilot-cli releases, so the rule applies.

Review

No blocking findings. Non-blocking:

  • AGENTS.md's Toolchain says mise tools use latest and CI installs them all. Decision 74's exception, an exact copilot that is now lazy, is recorded only in the plan, because AGENTS.md changes only with the user's OK.
  • An empty host would still set COPILOT_GH_HOST=. planner-service must require the host and the token.

Nothing calls the client yet. No credits spent: the tests use a stand-in CLI.

The planner's client of the Copilot SDK starts the pinned CLI and works
out a plan in plan mode on claude-opus-5.5, with the plan's seven tools,
no custom instructions and every permission rejected; it keeps the plan
that exit_plan_mode brings and ends the turn with exit_only.

The CLI's environment drops every COPILOT_* and GH_* variable and
GITHUB_TOKEN, since the CLI reads more than tokens from them, such as
COPILOT_ALLOW_ALL. mise pins copilot at 1.0.105, the version that SDK
v1.0.28 names, lazily so CI doesn't download it, and copilot:check fails
when the two differ. Renovate leaves the CLI to the SDK's PRs.
@Bakhomious
Bakhomious merged commit e4bcc25 into main Oct 30, 2026
2 checks passed
@Bakhomious
Bakhomious deleted the step/copilot-client branch October 30, 2026 03:20
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