Skip to content

Add GitHub Copilot as a built-in provider#3076

Open
huxcrux wants to merge 72 commits into
pingdotgg:mainfrom
huxcrux:hux-copliot-fix
Open

Add GitHub Copilot as a built-in provider#3076
huxcrux wants to merge 72 commits into
pingdotgg:mainfrom
huxcrux:hux-copliot-fix

Conversation

@huxcrux

@huxcrux huxcrux commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

This PR supersedes #2185. It includes the full Copilot integration from that PR plus the fixes and hardening discovered while testing it against the current codebase.

What Changed

  • Added GitHub Copilot as a first-class provider using the official Copilot SDK.
  • Wired Copilot through the server runtime for auth/status checks, model discovery, session start/resume, turn sending, permission replies, request handling, and event streaming.
  • Added Copilot-backed git text generation for commit messages, PR descriptions, branch names, and thread titles.
  • Added Copilot provider settings, defaults, model traits, reasoning effort, context window handling, and persisted configuration.
  • Updated the web UI so Copilot appears in provider/model pickers, settings, icons, context window selection, task rendering, and git writing model selection.
  • Hardened Copilot runtime behavior for queued turns, duplicate completions, tool-only turns, empty diffs, missing checkpoint refs, stale resume cursors, failed sends, task completions, and rollback-safe checkpointing.
  • Added regression coverage for the Copilot adapter, provider probing, text generation, context window handling, and related projection flows.

Why

T3 Code supports multiple agent providers, but did not have GitHub Copilot support. This adds Copilot end to end in the same shape as the existing providers, so users can configure Copilot, select Copilot models, use Copilot for git text generation, and rely on the same session/runtime flows as other providers.

The extra hardening keeps Copilot predictable under real usage: turns complete once, tool output and reasoning project correctly, diffs are only emitted when meaningful, permission replies resolve cleanly, checkpoint handling is safer, and provider status/model data stays accurate.

UI Changes

  • Added Copilot to the provider/model picker.
  • Added Copilot settings and model configuration surfaces.
  • Added Copilot icons, reasoning/context-window controls, and task output rendering.

Updated model picker:
image

Provider settings:
image

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

High Risk
Large new provider integration with external SDK dependencies plus changes to checkpoint revert ordering and provider rollback recovery, which affect filesystem and conversation state on revert.

Overview
Adds GitHub Copilot as a first-class server provider via @github/copilot / @github/copilot-sdk, including a new CopilotDriver and a large CopilotAdapter test suite covering sessions, permissions, diffs, queued turns, and lifecycle edge cases.

Checkpoint revert is reordered and hardened: validate target refs before restore, restore filesystem first, then call provider rollback only after a successful restore; on rollback failure (e.g. Copilot has no thread rollback), attempt to restore the current checkpoint and invalidate workspace entries when recovery fails. Tests cover failed filesystem restore (no provider rollback) and Copilot-specific failure paths.

Orchestration/runtime improvements include projecting content.delta streams into reasoning and tool output thread activities; not advancing latestTurn / latestTurnId while another turn is still running; first-turn title/branch generation on a drainable worker with retries and truncated-seed title replacement; and clearer checkpoint diff errors when a git ref is missing.

Reviewed by Cursor Bugbot for commit 65e0a24. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add GitHub Copilot as a built-in provider with full adapter, text generation, and settings support

  • Registers a CopilotDriver as a built-in provider, wiring together a Copilot adapter, text generation layer, and periodic health checks with isolated per-instance home directories.
  • Adds CopilotSettings schema (binaryPath, serverUrl, customModels, enabled) to server settings with validation and patch support.
  • Implements makeCopilotTextGeneration for commit messages, PR content, branch names, and thread titles via shared Copilot SDK clients with automatic idle shutdown after 30 seconds.
  • Adds classifyProviderToolItemType and classifyProviderToolRequestType in packages/shared to normalize provider tool invocations into canonical lifecycle and approval types using name/argument heuristics.
  • Fixes promptEffort derivation to only use reasoning-related descriptors (e.g. reasoningEffort) and not unrelated selectors like contextTier; fixes latestTurnId advancing when a different turn is still running.
  • Risk: the Copilot CLI path is auto-resolved from bundled node_modules/.bin/copilot; misconfigured or missing binaries surface as probe errors rather than startup failures.

Macroscope summarized 65e0a24.

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 62b8ef35-0e69-4845-be6e-e41e6e3ac17e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Jun 14, 2026
Comment thread apps/server/src/orchestration/Layers/CheckpointReactor.ts
Comment thread packages/shared/src/providerToolClassification.ts
Comment thread apps/server/src/orchestration/Layers/CheckpointReactor.ts
@macroscopeapp

macroscopeapp Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

2 blocking correctness issues found. Diff is too large for automated approval analysis. A human reviewer should evaluate this PR.

You can customize Macroscope's approvability policy. Learn more.

Comment thread apps/server/src/orchestration/Layers/CheckpointReactor.ts
Comment thread packages/shared/src/providerToolClassification.ts
Comment on lines +1855 to +1858
message: event.data.message.trim(),
detail: event.data,
},
});

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.

🟡 Medium Layers/CopilotAdapter.ts:1855

event.data.message.trim() throws when event.data.message is null or undefined. Other handlers in this file use trimOrUndefined() to safely handle optional SDK messages.

-          payload: {
-            message: event.data.message.trim(),
-            detail: event.data,
-          },
+          payload: {
+            message: trimOrUndefined(event.data.message) ?? "",
+            detail: event.data,
+          },
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Layers/CopilotAdapter.ts around lines 1855-1858:

`event.data.message.trim()` throws when `event.data.message` is `null` or `undefined`. Other handlers in this file use `trimOrUndefined()` to safely handle optional SDK messages.

Evidence trail:
apps/server/src/provider/Layers/CopilotAdapter.ts line 1855 (direct .trim() on event.data.message), line 1761 (same field uses trimOrUndefined for session.error), apps/server/src/provider/copilotRuntime.ts lines 63-65 (trimOrUndefined definition with null-safe ?.trim())

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3bbc092. Configure here.

Comment thread pnpm-workspace.yaml Outdated
huxcrux and others added 24 commits June 15, 2026 03:41
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep sidebarThreadSummaryById owned by shell updates by removing the detail-stream metadata patch and its regression test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ensure Copilot session.error events complete the active turn as failed before clearing active turn state, and cover the lifecycle with a regression test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
toolCallId: input.toolCallId,
...(input.toolMeta?.toolName ? { toolName: input.toolMeta.toolName } : {}),
...(input.toolMeta?.command ? { command: input.toolMeta.command } : {}),
...(input.result ? { result: input.result } : {}),

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.

🟢 Low Layers/CopilotAdapter.ts:812

The truthiness check on input.result silently drops falsy tool results like false, 0, '', or null. Since result is unknown and can legitimately be any value from tool execution, these valid return values are lost in telemetry. Consider using input.result !== undefined to preserve all non-undefined results.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Layers/CopilotAdapter.ts around line 812:

The truthiness check on `input.result` silently drops falsy tool results like `false`, `0`, `''`, or `null`. Since `result` is `unknown` and can legitimately be any value from tool execution, these valid return values are lost in telemetry. Consider using `input.result !== undefined` to preserve all non-undefined results.

Evidence trail:
apps/server/src/provider/Layers/CopilotAdapter.ts lines 799-816 (function toolLifecycleData with truthiness check at line 812), line 803 (result typed as `unknown`), lines 2290-2296 (caller at tool.execution_complete also using truthiness check on event.data.result)

Comment on lines +496 to +503
case "url": {
try {
const domain = new URL(request.url).hostname.trim();
return domain ? { kind: "approve-for-session", domain } : undefined;
} catch {
return undefined;
}
}

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.

🟠 High Layers/CopilotAdapter.ts:496

In the url case, the return statement returns { kind: "approve-for-session", domain } directly instead of using the approve() helper. This produces an object missing the required approval field, so URL permission approvals fail validation as SessionApprovalDecision. All other cases wrap the approval details via approve({ kind: "...", ... }). Change to return domain ? approve({ kind: "url", domain }) : undefined; to match the expected structure.

     case "url": {
       try {
         const domain = new URL(request.url).hostname.trim();
-        return domain ? { kind: "approve-for-session", domain } : undefined;
+        return domain ? approve({ kind: "url", domain }) : undefined;
       } catch {
         return undefined;
       }
     }
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Layers/CopilotAdapter.ts around lines 496-503:

In the `url` case, the return statement returns `{ kind: "approve-for-session", domain }` directly instead of using the `approve()` helper. This produces an object missing the required `approval` field, so URL permission approvals fail validation as `SessionApprovalDecision`. All other cases wrap the approval details via `approve({ kind: "...", ... })`. Change to `return domain ? approve({ kind: "url", domain }) : undefined;` to match the expected structure.

Evidence trail:
apps/server/src/provider/Layers/CopilotAdapter.ts lines 65-66 (type definitions), lines 467-474 (function signature and approve helper), line 499 (url case returning wrong structure), lines 486-495 and 504-519 (all other cases using approve()), lines 2777-2791 (consumer of the function result)

Comment on lines +221 to +224
const env = {
...process.env,
...input.env,
};

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.

🟢 Low provider/copilotRuntime.ts:221

In validateConfiguredCopilotCliPath, process.env is spread before input.env, which re-introduces COPILOT_CLI_PATH_ENV after the caller explicitly deleted it. The function receives a processed environment from buildCopilotClientOptions (which deletes COPILOT_CLI_PATH_ENV and calls normalizeCopilotRuntimeEnvironment), but spreading process.env first loses those changes. Use input.env ?? process.env instead of rebuilding the object.

-    const env = {
-      ...process.env,
-      ...input.env,
-    };
+    const env = input.env ?? process.env;
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/copilotRuntime.ts around lines 221-224:

In `validateConfiguredCopilotCliPath`, `process.env` is spread before `input.env`, which re-introduces `COPILOT_CLI_PATH_ENV` after the caller explicitly deleted it. The function receives a processed environment from `buildCopilotClientOptions` (which deletes `COPILOT_CLI_PATH_ENV` and calls `normalizeCopilotRuntimeEnvironment`), but spreading `process.env` first loses those changes. Use `input.env ?? process.env` instead of rebuilding the object.

Evidence trail:
apps/server/src/provider/copilotRuntime.ts line 50: `const COPILOT_CLI_PATH_ENV = "COPILOT_CLI_PATH";`
apps/server/src/provider/copilotRuntime.ts lines 296-303: `buildCopilotClientOptions` creates env from `process.env`, merges `input.env`, then `delete env[COPILOT_CLI_PATH_ENV]` and normalizes.
apps/server/src/provider/copilotRuntime.ts lines 305-309: passes the processed `env` to `validateConfiguredCopilotCliPath`.
apps/server/src/provider/copilotRuntime.ts lines 221-224: `validateConfiguredCopilotCliPath` rebuilds env with `{ ...process.env, ...input.env }`, re-introducing the deleted key from `process.env`.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants