Add Github Copilot Provider - #2185

Closed
zortos293 wants to merge 19 commits into
pingdotgg:mainfrom
zortos293:feat/add-copilot-sdk-support
Closed

Add Github Copilot Provider#2185
zortos293 wants to merge 19 commits into
pingdotgg:mainfrom
zortos293:feat/add-copilot-sdk-support

Conversation

@zortos293

@zortos293zortos293 commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Added GitHub Copilot as a first-class provider using the official Copilot SDK.
  • Wired Copilot through the server runtime, including provider detection, auth/status reporting, model discovery, session startup/resume, turn sending, request handling, and event streaming.
  • Added Copilot-backed git text generation for commit messages, PR content, branch names, and thread titles.
  • Added Copilot to shared contracts/settings so it behaves like the existing providers for model selection, reasoning options, defaults, and persisted settings.
  • Updated the web app so Copilot appears in the provider/model dropdowns, settings panels, custom model management, composer state, and git writing model selection.

Why

T3 Code already supports multiple agent providers, but it did not have GitHub Copilot support. This change adds Copilot end to end in the same shape as the other providers instead of treating it as a one-off integration.

That keeps the UX and server behavior consistent: users can configure Copilot in settings, select Copilot models from the same pickers, use it for git text generation, and rely on the same persistence and runtime flows that already exist for other providers. Using the official SDK also keeps the integration aligned with GitHub’s supported protocol and model discovery/auth flows.

UI Changes

  • Added Copilot to the provider picker.
  • Added a Copilot settings section with binary path, server URL, and custom model configuration.
  • Added Copilot to the git writing model picker and traits handling.
imageimageimage

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
Adds a new first-class provider with complex session/event handling and introduces the @github/copilot-sdk runtime dependency, so failures/auth/model discovery issues could impact provider startup and chat/runtime flows.

Overview
Adds GitHub Copilot as a first-class provider end-to-end. The server now includes a new CopilotDriver registered in BUILT_IN_DRIVERS, a Copilot SDK runtime wrapper (copilotRuntime.ts) for auth/status/model probing, and a full CopilotAdapter that starts/resumes sessions, translates Copilot SDK events into provider runtime events (turn lifecycle, streaming deltas, tool execution, permissions/user-input), and hardens error/cleanup behavior.

Adds Copilot-backed git text generation (CopilotTextGeneration.ts) for commit messages/PR content/branch names/thread titles with pooled SDK clients and idle shutdown. Updates shared contracts/settings to include CopilotSettings, provider defaults/aliases/display names, and extends runtime raw event sources; the web app surfaces Copilot in provider pickers/settings/icons and removes the “coming soon” placeholder when a real Copilot instance exists. Includes extensive new tests covering adapter behavior, provider probe errors, and text-generation client reuse.

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

Note

Add GitHub Copilot as a built-in provider for chat and text generation

  • Adds a full CopilotDriver and CopilotAdapter that integrate with @github/copilot-sdk to support chat sessions, permission handling, and runtime event translation.
  • Adds makeCopilotTextGeneration to generate commit messages, PR content, branch names, and thread titles via Copilot models using structured JSON outputs and a shared client pool.
  • Adds checkCopilotProviderStatus to periodically probe the Copilot SDK for auth state, available models, and version info.
  • Updates the settings UI, model picker, and provider icon mapping to treat Copilot as a first-class provider, removing the "Coming soon" placeholder.
  • Risk: introduces a new runtime dependency @github/copilot-sdk@^0.2.2 and spawns external CLI processes; probe failures are surfaced as provider errors rather than silently ignored.

Macroscope summarized d464878.

@coderabbitai

coderabbitaiBot commented Apr 18, 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: 6417f1bb-b9f1-4115-890c-8459048c5942

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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@zortos293zortos293 mentioned this pull request Apr 18, 2026
4 tasks
@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Apr 18, 2026
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@macroscopeapp

macroscopeappBot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR adds a new GitHub Copilot provider integration with ~4500 lines of new code, external SDK dependencies, and complex session management. New feature additions with this scope require human review. Additionally, an unresolved high-severity comment identifies a potential race condition in session teardown that could cause lost events.

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

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
@zortos293

Copy link
Copy Markdown
ContributorAuthor

This needs to be tested if the desktop app is working fine with Copilot

@brrock

Copy link
Copy Markdown

Because julius plumbed in ACP can't we just use ACP, I guess the copilot sdk has copilot specific stuff

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@zortos293
zortos293force-pushed the feat/add-copilot-sdk-support branch from f02de1a to 290befbCompareApril 20, 2026 08:04
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/git/Layers/CopilotTextGeneration.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/git/Layers/CopilotTextGeneration.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/textGeneration/CopilotTextGeneration.ts
fav-devs added a commit to fav-devs/fdcode that referenced this pull request Apr 21, 2026
Comment threadpackages/contracts/src/settings.ts Outdated
@@ -88,6 +92,13 @@ export const CodexSettings = Schema.Struct({
customModels: Schema.Array(Schema.String).pipe(Schema.withDecodingDefault(Effect.succeed([]))),
});
export type CodexSettings = typeof CodexSettings.Type;
export const CopilotSettings = Schema.Struct({
enabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),

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.

Copilot enabled by default causes unwanted CLI spawns

Medium Severity

CopilotSettings defaults enabled to true, meaning every existing user who upgrades will have Copilot auto-enabled. Combined with the 1-hour COPILOT_REFRESH_INTERVAL, this will repeatedly spawn the bundled Copilot CLI binary, attempt auth checks, and fail for the majority of users who have no GitHub Copilot subscription or auth configured. Unlike Codex/Claude/OpenCode which are the app's core providers, Copilot is a newly added optional integration — defaulting to false (like Cursor does) would avoid unnecessary process spawns and error noise on every refresh cycle.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit c9b851217439d17fbbe96d68b3f89077440f3446. Configure here.

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
@rgembalik

Copy link
Copy Markdown

I am not a maintainer, but I did couple of local tests. There were few cases where session stopped without showing final results message. I don't know if this is upstream bug, or something related to the PR (currently I have only copilot license so it's hard to test against other providers). After asking, I got a response that the results were there, but probably didn't show on my end.

image

@zortos293

Copy link
Copy Markdown
ContributorAuthor

i'l look into this

Comment threadapps/server/src/provider/copilotRuntime.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@brrock

Copy link
Copy Markdown

conflicts btw

@zortos293

Copy link
Copy Markdown
ContributorAuthor

conflicts btw

imma look at it tmr 👍

@zortos293
zortos293force-pushed the feat/add-copilot-sdk-support branch from 01451c8 to 614bc62CompareMay 7, 2026 13:46
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Services/CopilotProvider.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/copilotRuntime.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment on lines +61 to +147
export function checkCopilotProviderStatus(input: {
readonly settings: CopilotSettings;
readonly cwd: string;
readonly environment?: NodeJS.ProcessEnv | undefined;
}): Effect.Effect<ServerProviderDraft> {
if (!input.settings.enabled) {
return Effect.succeed(makePendingCopilotProvider(input.settings));
}

const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const fallback = (cause: unknown, version: string | null = null) => {
const failure = formatCopilotProbeError({
cause,
settings: input.settings,
});
return buildServerProvider({
driver: PROVIDER,
presentation: COPILOT_PRESENTATION,
enabled: true,
checkedAt,
models: modelsFromCopilotSdk({
models: [],
customModels: input.settings.customModels,
}),
probe: {
installed: failure.installed,
version,
status: "error",
auth: { status: "unknown" },
message: failure.message,
},
});
};

return Effect.acquireUseRelease(
Effect.sync(() =>
createCopilotClient({
settings: input.settings,
cwd: input.cwd,
...(input.environment ? { env: input.environment } : {}),
logLevel: "error",
}),
),
(client) =>
Effect.tryPromise({
try: async () => {
await client.start();
const [status, authStatus, models] = await Promise.all([
client.getStatus(),
client.getAuthStatus(),
client.listModels(),
]);
const authSnapshot = authSnapshotFromCopilotSdk(authStatus);
const providerModels = modelsFromCopilotSdk({
models,
customModels: input.settings.customModels,
});
const hasBuiltInModels = models.length > 0;

return buildServerProvider({
driver: PROVIDER,
presentation: COPILOT_PRESENTATION,
enabled: true,
checkedAt,
models: providerModels,
probe: {
installed: true,
version: versionFromCopilotStatus(status),
status:
authSnapshot.status !== "ready"
? authSnapshot.status
: hasBuiltInModels
? "ready"
: "warning",
auth: authSnapshot.auth,
...(authSnapshot.message
? { message: authSnapshot.message }
: hasBuiltInModels
? {}
: { message: "Copilot did not report any available models for this account." }),
},
});
},
catch: toCopilotProbeError,
}).pipe(Effect.catch((cause) => Effect.succeed(fallback(cause)))),
(client) => Effect.promise(() => client.stop()).pipe(Effect.ignore({ log: true })),
);

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.

🟢 LowLayers/CopilotProvider.ts:61

The checkedAt timestamp is captured at function call time on line 70, so every execution of the returned Effect reports the same stale timestamp from when checkCopilotProviderStatus was first invoked. Since makeManagedServerProvider re-executes this Effect on each refresh interval, all subsequent checks incorrectly show the original timestamp rather than when each check actually occurred.

 if (!input.settings.enabled) {
return Effect.succeed(makePendingCopilotProvider(input.settings));
}
- const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const fallback = (cause: unknown, version: string | null = null) => {
+ const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const failure = formatCopilotProbeError({
cause,
settings: input.settings,
@@ -92,6 +92,7 @@
});
};
+ const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
return Effect.acquireUseRelease(
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/CopilotProvider.ts around lines 61-147:
The `checkedAt` timestamp is captured at function call time on line 70, so every execution of the returned Effect reports the same stale timestamp from when `checkCopilotProviderStatus` was first invoked. Since `makeManagedServerProvider` re-executes this Effect on each refresh interval, all subsequent checks incorrectly show the original timestamp rather than when each check actually occurred.
Evidence trail:
apps/server/src/provider/Layers/CopilotProvider.ts lines 61-70 (checkedAt captured eagerly outside Effect), lines 95-149 (Effect.acquireUseRelease returned, closing over checkedAt). apps/server/src/provider/Drivers/CopilotDriver.ts lines 92-95 (checkCopilotProviderStatus called once, Effect passed as checkProvider). apps/server/src/provider/makeManagedServerProvider.ts lines 127-130 (input.checkProvider re-executed in applySnapshotBase), lines 163-168 (forever loop re-running refreshSnapshot on each interval).

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
decision === "accept" || decision === "acceptForSession"
? { kind: "approved" }
: { kind: "denied-interactively-by-user" };
yield* Deferred.succeed(binding.deferred, 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.

🟢 LowLayers/CopilotAdapter.ts:2264

In respondToRequest, after resolving binding.deferred, the requestId is not removed from pendingPermissionBindings. If called twice with the same requestId, the second call silently succeeds instead of returning an error, which can mask client bugs or race conditions. Consider deleting the binding immediately after resolving it.

- yield* Deferred.succeed(binding.deferred, result);+ yield* Deferred.succeed(binding.deferred, result);+ context.pendingPermissionBindings.delete(requestId);
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/CopilotAdapter.ts around line 2264:
In `respondToRequest`, after resolving `binding.deferred`, the `requestId` is not removed from `pendingPermissionBindings`. If called twice with the same `requestId`, the second call silently succeeds instead of returning an error, which can mask client bugs or race conditions. Consider deleting the binding immediately after resolving it.
Evidence trail:
apps/server/src/provider/Layers/CopilotAdapter.ts lines 2248-2265 (respondToRequest function: no delete after Deferred.succeed at line 2264). Compare with lines 574-577 (settlePendingPermissionHandlers: calls .clear() after settling). Effect-TS Deferred.succeed returns Effect<boolean> — false if already done, no error thrown (see Effect-TS source/docs).

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts

@cursorcursorBot 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.

There are 2 total unresolved issues (including 1 from previous review).

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 d464878. Configure here.

},
});
sessions.delete(threadId);
});

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.

Session teardown doesn't await pending event chain

High Severity

stopSessionInternal disconnects the SDK session and stops the client without first awaiting context.eventChain. Events still being processed asynchronously in the chain (including turn.completed and item.completed events) can race against or fail after the SDK resources are torn down. This likely explains the reported bug where sessions stop without surfacing final results. In contrast, startSession correctly awaits the chain at line 2180 with yield* Effect.promise(() => context.eventChain).

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit d464878. Configure here.

@juliusmarmingeChatGPT Codex Connector

Copy link
Copy Markdown
Member

A newer Copilot provider implementation, #3076, is active against the current provider architecture. Keeping this older provider branch would duplicate that effort.

This was closed as part of a large repo-wide maintenance sweep. If you think it's still relevant, please reopen.

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

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@zortos293@brrock@rgembalik@akshaylingamaneni@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Add Github Copilot Provider - #2185

Closed
zortos293 wants to merge 19 commits into
pingdotgg:mainfrom
zortos293:feat/add-copilot-sdk-support
Closed

Add Github Copilot Provider#2185
zortos293 wants to merge 19 commits into
pingdotgg:mainfrom
zortos293:feat/add-copilot-sdk-support

Conversation

@zortos293

@zortos293zortos293 commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Added GitHub Copilot as a first-class provider using the official Copilot SDK.
  • Wired Copilot through the server runtime, including provider detection, auth/status reporting, model discovery, session startup/resume, turn sending, request handling, and event streaming.
  • Added Copilot-backed git text generation for commit messages, PR content, branch names, and thread titles.
  • Added Copilot to shared contracts/settings so it behaves like the existing providers for model selection, reasoning options, defaults, and persisted settings.
  • Updated the web app so Copilot appears in the provider/model dropdowns, settings panels, custom model management, composer state, and git writing model selection.

Why

T3 Code already supports multiple agent providers, but it did not have GitHub Copilot support. This change adds Copilot end to end in the same shape as the other providers instead of treating it as a one-off integration.

That keeps the UX and server behavior consistent: users can configure Copilot in settings, select Copilot models from the same pickers, use it for git text generation, and rely on the same persistence and runtime flows that already exist for other providers. Using the official SDK also keeps the integration aligned with GitHub’s supported protocol and model discovery/auth flows.

UI Changes

  • Added Copilot to the provider picker.
  • Added a Copilot settings section with binary path, server URL, and custom model configuration.
  • Added Copilot to the git writing model picker and traits handling.
imageimageimage

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
Adds a new first-class provider with complex session/event handling and introduces the @github/copilot-sdk runtime dependency, so failures/auth/model discovery issues could impact provider startup and chat/runtime flows.

Overview
Adds GitHub Copilot as a first-class provider end-to-end. The server now includes a new CopilotDriver registered in BUILT_IN_DRIVERS, a Copilot SDK runtime wrapper (copilotRuntime.ts) for auth/status/model probing, and a full CopilotAdapter that starts/resumes sessions, translates Copilot SDK events into provider runtime events (turn lifecycle, streaming deltas, tool execution, permissions/user-input), and hardens error/cleanup behavior.

Adds Copilot-backed git text generation (CopilotTextGeneration.ts) for commit messages/PR content/branch names/thread titles with pooled SDK clients and idle shutdown. Updates shared contracts/settings to include CopilotSettings, provider defaults/aliases/display names, and extends runtime raw event sources; the web app surfaces Copilot in provider pickers/settings/icons and removes the “coming soon” placeholder when a real Copilot instance exists. Includes extensive new tests covering adapter behavior, provider probe errors, and text-generation client reuse.

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

Note

Add GitHub Copilot as a built-in provider for chat and text generation

  • Adds a full CopilotDriver and CopilotAdapter that integrate with @github/copilot-sdk to support chat sessions, permission handling, and runtime event translation.
  • Adds makeCopilotTextGeneration to generate commit messages, PR content, branch names, and thread titles via Copilot models using structured JSON outputs and a shared client pool.
  • Adds checkCopilotProviderStatus to periodically probe the Copilot SDK for auth state, available models, and version info.
  • Updates the settings UI, model picker, and provider icon mapping to treat Copilot as a first-class provider, removing the "Coming soon" placeholder.
  • Risk: introduces a new runtime dependency @github/copilot-sdk@^0.2.2 and spawns external CLI processes; probe failures are surfaced as provider errors rather than silently ignored.

Macroscope summarized d464878.

@coderabbitai

coderabbitaiBot commented Apr 18, 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: 6417f1bb-b9f1-4115-890c-8459048c5942

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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@zortos293zortos293 mentioned this pull request Apr 18, 2026
4 tasks
@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Apr 18, 2026
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@macroscopeapp

macroscopeappBot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR adds a new GitHub Copilot provider integration with ~4500 lines of new code, external SDK dependencies, and complex session management. New feature additions with this scope require human review. Additionally, an unresolved high-severity comment identifies a potential race condition in session teardown that could cause lost events.

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

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
@zortos293

Copy link
Copy Markdown
ContributorAuthor

This needs to be tested if the desktop app is working fine with Copilot

@brrock

Copy link
Copy Markdown

Because julius plumbed in ACP can't we just use ACP, I guess the copilot sdk has copilot specific stuff

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@zortos293
zortos293force-pushed the feat/add-copilot-sdk-support branch from f02de1a to 290befbCompareApril 20, 2026 08:04
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/git/Layers/CopilotTextGeneration.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/git/Layers/CopilotTextGeneration.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/textGeneration/CopilotTextGeneration.ts
fav-devs added a commit to fav-devs/fdcode that referenced this pull request Apr 21, 2026
Comment threadpackages/contracts/src/settings.ts Outdated
@@ -88,6 +92,13 @@ export const CodexSettings = Schema.Struct({
customModels: Schema.Array(Schema.String).pipe(Schema.withDecodingDefault(Effect.succeed([]))),
});
export type CodexSettings = typeof CodexSettings.Type;
export const CopilotSettings = Schema.Struct({
enabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),

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.

Copilot enabled by default causes unwanted CLI spawns

Medium Severity

CopilotSettings defaults enabled to true, meaning every existing user who upgrades will have Copilot auto-enabled. Combined with the 1-hour COPILOT_REFRESH_INTERVAL, this will repeatedly spawn the bundled Copilot CLI binary, attempt auth checks, and fail for the majority of users who have no GitHub Copilot subscription or auth configured. Unlike Codex/Claude/OpenCode which are the app's core providers, Copilot is a newly added optional integration — defaulting to false (like Cursor does) would avoid unnecessary process spawns and error noise on every refresh cycle.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit c9b851217439d17fbbe96d68b3f89077440f3446. Configure here.

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
@rgembalik

Copy link
Copy Markdown

I am not a maintainer, but I did couple of local tests. There were few cases where session stopped without showing final results message. I don't know if this is upstream bug, or something related to the PR (currently I have only copilot license so it's hard to test against other providers). After asking, I got a response that the results were there, but probably didn't show on my end.

image

@zortos293

Copy link
Copy Markdown
ContributorAuthor

i'l look into this

Comment threadapps/server/src/provider/copilotRuntime.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@brrock

Copy link
Copy Markdown

conflicts btw

@zortos293

Copy link
Copy Markdown
ContributorAuthor

conflicts btw

imma look at it tmr 👍

@zortos293
zortos293force-pushed the feat/add-copilot-sdk-support branch from 01451c8 to 614bc62CompareMay 7, 2026 13:46
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Services/CopilotProvider.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/copilotRuntime.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment on lines +61 to +147
export function checkCopilotProviderStatus(input: {
readonly settings: CopilotSettings;
readonly cwd: string;
readonly environment?: NodeJS.ProcessEnv | undefined;
}): Effect.Effect<ServerProviderDraft> {
if (!input.settings.enabled) {
return Effect.succeed(makePendingCopilotProvider(input.settings));
}

const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const fallback = (cause: unknown, version: string | null = null) => {
const failure = formatCopilotProbeError({
cause,
settings: input.settings,
});
return buildServerProvider({
driver: PROVIDER,
presentation: COPILOT_PRESENTATION,
enabled: true,
checkedAt,
models: modelsFromCopilotSdk({
models: [],
customModels: input.settings.customModels,
}),
probe: {
installed: failure.installed,
version,
status: "error",
auth: { status: "unknown" },
message: failure.message,
},
});
};

return Effect.acquireUseRelease(
Effect.sync(() =>
createCopilotClient({
settings: input.settings,
cwd: input.cwd,
...(input.environment ? { env: input.environment } : {}),
logLevel: "error",
}),
),
(client) =>
Effect.tryPromise({
try: async () => {
await client.start();
const [status, authStatus, models] = await Promise.all([
client.getStatus(),
client.getAuthStatus(),
client.listModels(),
]);
const authSnapshot = authSnapshotFromCopilotSdk(authStatus);
const providerModels = modelsFromCopilotSdk({
models,
customModels: input.settings.customModels,
});
const hasBuiltInModels = models.length > 0;

return buildServerProvider({
driver: PROVIDER,
presentation: COPILOT_PRESENTATION,
enabled: true,
checkedAt,
models: providerModels,
probe: {
installed: true,
version: versionFromCopilotStatus(status),
status:
authSnapshot.status !== "ready"
? authSnapshot.status
: hasBuiltInModels
? "ready"
: "warning",
auth: authSnapshot.auth,
...(authSnapshot.message
? { message: authSnapshot.message }
: hasBuiltInModels
? {}
: { message: "Copilot did not report any available models for this account." }),
},
});
},
catch: toCopilotProbeError,
}).pipe(Effect.catch((cause) => Effect.succeed(fallback(cause)))),
(client) => Effect.promise(() => client.stop()).pipe(Effect.ignore({ log: true })),
);

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.

🟢 LowLayers/CopilotProvider.ts:61

The checkedAt timestamp is captured at function call time on line 70, so every execution of the returned Effect reports the same stale timestamp from when checkCopilotProviderStatus was first invoked. Since makeManagedServerProvider re-executes this Effect on each refresh interval, all subsequent checks incorrectly show the original timestamp rather than when each check actually occurred.

 if (!input.settings.enabled) {
return Effect.succeed(makePendingCopilotProvider(input.settings));
}
- const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const fallback = (cause: unknown, version: string | null = null) => {
+ const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const failure = formatCopilotProbeError({
cause,
settings: input.settings,
@@ -92,6 +92,7 @@
});
};
+ const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
return Effect.acquireUseRelease(
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/CopilotProvider.ts around lines 61-147:
The `checkedAt` timestamp is captured at function call time on line 70, so every execution of the returned Effect reports the same stale timestamp from when `checkCopilotProviderStatus` was first invoked. Since `makeManagedServerProvider` re-executes this Effect on each refresh interval, all subsequent checks incorrectly show the original timestamp rather than when each check actually occurred.
Evidence trail:
apps/server/src/provider/Layers/CopilotProvider.ts lines 61-70 (checkedAt captured eagerly outside Effect), lines 95-149 (Effect.acquireUseRelease returned, closing over checkedAt). apps/server/src/provider/Drivers/CopilotDriver.ts lines 92-95 (checkCopilotProviderStatus called once, Effect passed as checkProvider). apps/server/src/provider/makeManagedServerProvider.ts lines 127-130 (input.checkProvider re-executed in applySnapshotBase), lines 163-168 (forever loop re-running refreshSnapshot on each interval).

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
decision === "accept" || decision === "acceptForSession"
? { kind: "approved" }
: { kind: "denied-interactively-by-user" };
yield* Deferred.succeed(binding.deferred, 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.

🟢 LowLayers/CopilotAdapter.ts:2264

In respondToRequest, after resolving binding.deferred, the requestId is not removed from pendingPermissionBindings. If called twice with the same requestId, the second call silently succeeds instead of returning an error, which can mask client bugs or race conditions. Consider deleting the binding immediately after resolving it.

- yield* Deferred.succeed(binding.deferred, result);+ yield* Deferred.succeed(binding.deferred, result);+ context.pendingPermissionBindings.delete(requestId);
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/CopilotAdapter.ts around line 2264:
In `respondToRequest`, after resolving `binding.deferred`, the `requestId` is not removed from `pendingPermissionBindings`. If called twice with the same `requestId`, the second call silently succeeds instead of returning an error, which can mask client bugs or race conditions. Consider deleting the binding immediately after resolving it.
Evidence trail:
apps/server/src/provider/Layers/CopilotAdapter.ts lines 2248-2265 (respondToRequest function: no delete after Deferred.succeed at line 2264). Compare with lines 574-577 (settlePendingPermissionHandlers: calls .clear() after settling). Effect-TS Deferred.succeed returns Effect<boolean> — false if already done, no error thrown (see Effect-TS source/docs).

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts

@cursorcursorBot 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.

There are 2 total unresolved issues (including 1 from previous review).

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 d464878. Configure here.

},
});
sessions.delete(threadId);
});

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.

Session teardown doesn't await pending event chain

High Severity

stopSessionInternal disconnects the SDK session and stops the client without first awaiting context.eventChain. Events still being processed asynchronously in the chain (including turn.completed and item.completed events) can race against or fail after the SDK resources are torn down. This likely explains the reported bug where sessions stop without surfacing final results. In contrast, startSession correctly awaits the chain at line 2180 with yield* Effect.promise(() => context.eventChain).

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit d464878. Configure here.

@juliusmarmingeChatGPT Codex Connector

Copy link
Copy Markdown
Member

A newer Copilot provider implementation, #3076, is active against the current provider architecture. Keeping this older provider branch would duplicate that effort.

This was closed as part of a large repo-wide maintenance sweep. If you think it's still relevant, please reopen.

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

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@zortos293@brrock@rgembalik@akshaylingamaneni@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add Github Copilot Provider - #2185

Closed
zortos293 wants to merge 19 commits into
pingdotgg:mainfrom
zortos293:feat/add-copilot-sdk-support
Closed

Add Github Copilot Provider#2185
zortos293 wants to merge 19 commits into
pingdotgg:mainfrom
zortos293:feat/add-copilot-sdk-support

Conversation

@zortos293

@zortos293zortos293 commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Added GitHub Copilot as a first-class provider using the official Copilot SDK.
  • Wired Copilot through the server runtime, including provider detection, auth/status reporting, model discovery, session startup/resume, turn sending, request handling, and event streaming.
  • Added Copilot-backed git text generation for commit messages, PR content, branch names, and thread titles.
  • Added Copilot to shared contracts/settings so it behaves like the existing providers for model selection, reasoning options, defaults, and persisted settings.
  • Updated the web app so Copilot appears in the provider/model dropdowns, settings panels, custom model management, composer state, and git writing model selection.

Why

T3 Code already supports multiple agent providers, but it did not have GitHub Copilot support. This change adds Copilot end to end in the same shape as the other providers instead of treating it as a one-off integration.

That keeps the UX and server behavior consistent: users can configure Copilot in settings, select Copilot models from the same pickers, use it for git text generation, and rely on the same persistence and runtime flows that already exist for other providers. Using the official SDK also keeps the integration aligned with GitHub’s supported protocol and model discovery/auth flows.

UI Changes

  • Added Copilot to the provider picker.
  • Added a Copilot settings section with binary path, server URL, and custom model configuration.
  • Added Copilot to the git writing model picker and traits handling.
imageimageimage

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
Adds a new first-class provider with complex session/event handling and introduces the @github/copilot-sdk runtime dependency, so failures/auth/model discovery issues could impact provider startup and chat/runtime flows.

Overview
Adds GitHub Copilot as a first-class provider end-to-end. The server now includes a new CopilotDriver registered in BUILT_IN_DRIVERS, a Copilot SDK runtime wrapper (copilotRuntime.ts) for auth/status/model probing, and a full CopilotAdapter that starts/resumes sessions, translates Copilot SDK events into provider runtime events (turn lifecycle, streaming deltas, tool execution, permissions/user-input), and hardens error/cleanup behavior.

Adds Copilot-backed git text generation (CopilotTextGeneration.ts) for commit messages/PR content/branch names/thread titles with pooled SDK clients and idle shutdown. Updates shared contracts/settings to include CopilotSettings, provider defaults/aliases/display names, and extends runtime raw event sources; the web app surfaces Copilot in provider pickers/settings/icons and removes the “coming soon” placeholder when a real Copilot instance exists. Includes extensive new tests covering adapter behavior, provider probe errors, and text-generation client reuse.

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

Note

Add GitHub Copilot as a built-in provider for chat and text generation

  • Adds a full CopilotDriver and CopilotAdapter that integrate with @github/copilot-sdk to support chat sessions, permission handling, and runtime event translation.
  • Adds makeCopilotTextGeneration to generate commit messages, PR content, branch names, and thread titles via Copilot models using structured JSON outputs and a shared client pool.
  • Adds checkCopilotProviderStatus to periodically probe the Copilot SDK for auth state, available models, and version info.
  • Updates the settings UI, model picker, and provider icon mapping to treat Copilot as a first-class provider, removing the "Coming soon" placeholder.
  • Risk: introduces a new runtime dependency @github/copilot-sdk@^0.2.2 and spawns external CLI processes; probe failures are surfaced as provider errors rather than silently ignored.

Macroscope summarized d464878.

@coderabbitai

coderabbitaiBot commented Apr 18, 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: 6417f1bb-b9f1-4115-890c-8459048c5942

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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@zortos293zortos293 mentioned this pull request Apr 18, 2026
4 tasks
@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Apr 18, 2026
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@macroscopeapp

macroscopeappBot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR adds a new GitHub Copilot provider integration with ~4500 lines of new code, external SDK dependencies, and complex session management. New feature additions with this scope require human review. Additionally, an unresolved high-severity comment identifies a potential race condition in session teardown that could cause lost events.

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

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
@zortos293

Copy link
Copy Markdown
ContributorAuthor

This needs to be tested if the desktop app is working fine with Copilot

@brrock

Copy link
Copy Markdown

Because julius plumbed in ACP can't we just use ACP, I guess the copilot sdk has copilot specific stuff

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@zortos293
zortos293force-pushed the feat/add-copilot-sdk-support branch from f02de1a to 290befbCompareApril 20, 2026 08:04
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/git/Layers/CopilotTextGeneration.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/git/Layers/CopilotTextGeneration.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/textGeneration/CopilotTextGeneration.ts
fav-devs added a commit to fav-devs/fdcode that referenced this pull request Apr 21, 2026
Comment threadpackages/contracts/src/settings.ts Outdated
@@ -88,6 +92,13 @@ export const CodexSettings = Schema.Struct({
customModels: Schema.Array(Schema.String).pipe(Schema.withDecodingDefault(Effect.succeed([]))),
});
export type CodexSettings = typeof CodexSettings.Type;
export const CopilotSettings = Schema.Struct({
enabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),

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.

Copilot enabled by default causes unwanted CLI spawns

Medium Severity

CopilotSettings defaults enabled to true, meaning every existing user who upgrades will have Copilot auto-enabled. Combined with the 1-hour COPILOT_REFRESH_INTERVAL, this will repeatedly spawn the bundled Copilot CLI binary, attempt auth checks, and fail for the majority of users who have no GitHub Copilot subscription or auth configured. Unlike Codex/Claude/OpenCode which are the app's core providers, Copilot is a newly added optional integration — defaulting to false (like Cursor does) would avoid unnecessary process spawns and error noise on every refresh cycle.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit c9b851217439d17fbbe96d68b3f89077440f3446. Configure here.

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
@rgembalik

Copy link
Copy Markdown

I am not a maintainer, but I did couple of local tests. There were few cases where session stopped without showing final results message. I don't know if this is upstream bug, or something related to the PR (currently I have only copilot license so it's hard to test against other providers). After asking, I got a response that the results were there, but probably didn't show on my end.

image

@zortos293

Copy link
Copy Markdown
ContributorAuthor

i'l look into this

Comment threadapps/server/src/provider/copilotRuntime.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@brrock

Copy link
Copy Markdown

conflicts btw

@zortos293

Copy link
Copy Markdown
ContributorAuthor

conflicts btw

imma look at it tmr 👍

@zortos293
zortos293force-pushed the feat/add-copilot-sdk-support branch from 01451c8 to 614bc62CompareMay 7, 2026 13:46
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Services/CopilotProvider.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/copilotRuntime.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment on lines +61 to +147
export function checkCopilotProviderStatus(input: {
readonly settings: CopilotSettings;
readonly cwd: string;
readonly environment?: NodeJS.ProcessEnv | undefined;
}): Effect.Effect<ServerProviderDraft> {
if (!input.settings.enabled) {
return Effect.succeed(makePendingCopilotProvider(input.settings));
}

const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const fallback = (cause: unknown, version: string | null = null) => {
const failure = formatCopilotProbeError({
cause,
settings: input.settings,
});
return buildServerProvider({
driver: PROVIDER,
presentation: COPILOT_PRESENTATION,
enabled: true,
checkedAt,
models: modelsFromCopilotSdk({
models: [],
customModels: input.settings.customModels,
}),
probe: {
installed: failure.installed,
version,
status: "error",
auth: { status: "unknown" },
message: failure.message,
},
});
};

return Effect.acquireUseRelease(
Effect.sync(() =>
createCopilotClient({
settings: input.settings,
cwd: input.cwd,
...(input.environment ? { env: input.environment } : {}),
logLevel: "error",
}),
),
(client) =>
Effect.tryPromise({
try: async () => {
await client.start();
const [status, authStatus, models] = await Promise.all([
client.getStatus(),
client.getAuthStatus(),
client.listModels(),
]);
const authSnapshot = authSnapshotFromCopilotSdk(authStatus);
const providerModels = modelsFromCopilotSdk({
models,
customModels: input.settings.customModels,
});
const hasBuiltInModels = models.length > 0;

return buildServerProvider({
driver: PROVIDER,
presentation: COPILOT_PRESENTATION,
enabled: true,
checkedAt,
models: providerModels,
probe: {
installed: true,
version: versionFromCopilotStatus(status),
status:
authSnapshot.status !== "ready"
? authSnapshot.status
: hasBuiltInModels
? "ready"
: "warning",
auth: authSnapshot.auth,
...(authSnapshot.message
? { message: authSnapshot.message }
: hasBuiltInModels
? {}
: { message: "Copilot did not report any available models for this account." }),
},
});
},
catch: toCopilotProbeError,
}).pipe(Effect.catch((cause) => Effect.succeed(fallback(cause)))),
(client) => Effect.promise(() => client.stop()).pipe(Effect.ignore({ log: true })),
);

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.

🟢 LowLayers/CopilotProvider.ts:61

The checkedAt timestamp is captured at function call time on line 70, so every execution of the returned Effect reports the same stale timestamp from when checkCopilotProviderStatus was first invoked. Since makeManagedServerProvider re-executes this Effect on each refresh interval, all subsequent checks incorrectly show the original timestamp rather than when each check actually occurred.

 if (!input.settings.enabled) {
return Effect.succeed(makePendingCopilotProvider(input.settings));
}
- const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const fallback = (cause: unknown, version: string | null = null) => {
+ const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const failure = formatCopilotProbeError({
cause,
settings: input.settings,
@@ -92,6 +92,7 @@
});
};
+ const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
return Effect.acquireUseRelease(
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/CopilotProvider.ts around lines 61-147:
The `checkedAt` timestamp is captured at function call time on line 70, so every execution of the returned Effect reports the same stale timestamp from when `checkCopilotProviderStatus` was first invoked. Since `makeManagedServerProvider` re-executes this Effect on each refresh interval, all subsequent checks incorrectly show the original timestamp rather than when each check actually occurred.
Evidence trail:
apps/server/src/provider/Layers/CopilotProvider.ts lines 61-70 (checkedAt captured eagerly outside Effect), lines 95-149 (Effect.acquireUseRelease returned, closing over checkedAt). apps/server/src/provider/Drivers/CopilotDriver.ts lines 92-95 (checkCopilotProviderStatus called once, Effect passed as checkProvider). apps/server/src/provider/makeManagedServerProvider.ts lines 127-130 (input.checkProvider re-executed in applySnapshotBase), lines 163-168 (forever loop re-running refreshSnapshot on each interval).

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
decision === "accept" || decision === "acceptForSession"
? { kind: "approved" }
: { kind: "denied-interactively-by-user" };
yield* Deferred.succeed(binding.deferred, 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.

🟢 LowLayers/CopilotAdapter.ts:2264

In respondToRequest, after resolving binding.deferred, the requestId is not removed from pendingPermissionBindings. If called twice with the same requestId, the second call silently succeeds instead of returning an error, which can mask client bugs or race conditions. Consider deleting the binding immediately after resolving it.

- yield* Deferred.succeed(binding.deferred, result);+ yield* Deferred.succeed(binding.deferred, result);+ context.pendingPermissionBindings.delete(requestId);
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/CopilotAdapter.ts around line 2264:
In `respondToRequest`, after resolving `binding.deferred`, the `requestId` is not removed from `pendingPermissionBindings`. If called twice with the same `requestId`, the second call silently succeeds instead of returning an error, which can mask client bugs or race conditions. Consider deleting the binding immediately after resolving it.
Evidence trail:
apps/server/src/provider/Layers/CopilotAdapter.ts lines 2248-2265 (respondToRequest function: no delete after Deferred.succeed at line 2264). Compare with lines 574-577 (settlePendingPermissionHandlers: calls .clear() after settling). Effect-TS Deferred.succeed returns Effect<boolean> — false if already done, no error thrown (see Effect-TS source/docs).

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts

@cursorcursorBot 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.

There are 2 total unresolved issues (including 1 from previous review).

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 d464878. Configure here.

},
});
sessions.delete(threadId);
});

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.

Session teardown doesn't await pending event chain

High Severity

stopSessionInternal disconnects the SDK session and stops the client without first awaiting context.eventChain. Events still being processed asynchronously in the chain (including turn.completed and item.completed events) can race against or fail after the SDK resources are torn down. This likely explains the reported bug where sessions stop without surfacing final results. In contrast, startSession correctly awaits the chain at line 2180 with yield* Effect.promise(() => context.eventChain).

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit d464878. Configure here.

@juliusmarmingeChatGPT Codex Connector

Copy link
Copy Markdown
Member

A newer Copilot provider implementation, #3076, is active against the current provider architecture. Keeping this older provider branch would duplicate that effort.

This was closed as part of a large repo-wide maintenance sweep. If you think it's still relevant, please reopen.

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

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@zortos293@brrock@rgembalik@akshaylingamaneni@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add Github Copilot Provider - #2185

Closed
zortos293 wants to merge 19 commits into
pingdotgg:mainfrom
zortos293:feat/add-copilot-sdk-support
Closed

Add Github Copilot Provider#2185
zortos293 wants to merge 19 commits into
pingdotgg:mainfrom
zortos293:feat/add-copilot-sdk-support

Conversation

@zortos293

@zortos293zortos293 commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Added GitHub Copilot as a first-class provider using the official Copilot SDK.
  • Wired Copilot through the server runtime, including provider detection, auth/status reporting, model discovery, session startup/resume, turn sending, request handling, and event streaming.
  • Added Copilot-backed git text generation for commit messages, PR content, branch names, and thread titles.
  • Added Copilot to shared contracts/settings so it behaves like the existing providers for model selection, reasoning options, defaults, and persisted settings.
  • Updated the web app so Copilot appears in the provider/model dropdowns, settings panels, custom model management, composer state, and git writing model selection.

Why

T3 Code already supports multiple agent providers, but it did not have GitHub Copilot support. This change adds Copilot end to end in the same shape as the other providers instead of treating it as a one-off integration.

That keeps the UX and server behavior consistent: users can configure Copilot in settings, select Copilot models from the same pickers, use it for git text generation, and rely on the same persistence and runtime flows that already exist for other providers. Using the official SDK also keeps the integration aligned with GitHub’s supported protocol and model discovery/auth flows.

UI Changes

  • Added Copilot to the provider picker.
  • Added a Copilot settings section with binary path, server URL, and custom model configuration.
  • Added Copilot to the git writing model picker and traits handling.
imageimageimage

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
Adds a new first-class provider with complex session/event handling and introduces the @github/copilot-sdk runtime dependency, so failures/auth/model discovery issues could impact provider startup and chat/runtime flows.

Overview
Adds GitHub Copilot as a first-class provider end-to-end. The server now includes a new CopilotDriver registered in BUILT_IN_DRIVERS, a Copilot SDK runtime wrapper (copilotRuntime.ts) for auth/status/model probing, and a full CopilotAdapter that starts/resumes sessions, translates Copilot SDK events into provider runtime events (turn lifecycle, streaming deltas, tool execution, permissions/user-input), and hardens error/cleanup behavior.

Adds Copilot-backed git text generation (CopilotTextGeneration.ts) for commit messages/PR content/branch names/thread titles with pooled SDK clients and idle shutdown. Updates shared contracts/settings to include CopilotSettings, provider defaults/aliases/display names, and extends runtime raw event sources; the web app surfaces Copilot in provider pickers/settings/icons and removes the “coming soon” placeholder when a real Copilot instance exists. Includes extensive new tests covering adapter behavior, provider probe errors, and text-generation client reuse.

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

Note

Add GitHub Copilot as a built-in provider for chat and text generation

  • Adds a full CopilotDriver and CopilotAdapter that integrate with @github/copilot-sdk to support chat sessions, permission handling, and runtime event translation.
  • Adds makeCopilotTextGeneration to generate commit messages, PR content, branch names, and thread titles via Copilot models using structured JSON outputs and a shared client pool.
  • Adds checkCopilotProviderStatus to periodically probe the Copilot SDK for auth state, available models, and version info.
  • Updates the settings UI, model picker, and provider icon mapping to treat Copilot as a first-class provider, removing the "Coming soon" placeholder.
  • Risk: introduces a new runtime dependency @github/copilot-sdk@^0.2.2 and spawns external CLI processes; probe failures are surfaced as provider errors rather than silently ignored.

Macroscope summarized d464878.

@coderabbitai

coderabbitaiBot commented Apr 18, 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: 6417f1bb-b9f1-4115-890c-8459048c5942

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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@zortos293zortos293 mentioned this pull request Apr 18, 2026
4 tasks
@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Apr 18, 2026
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@macroscopeapp

macroscopeappBot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR adds a new GitHub Copilot provider integration with ~4500 lines of new code, external SDK dependencies, and complex session management. New feature additions with this scope require human review. Additionally, an unresolved high-severity comment identifies a potential race condition in session teardown that could cause lost events.

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

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
@zortos293

Copy link
Copy Markdown
ContributorAuthor

This needs to be tested if the desktop app is working fine with Copilot

@brrock

Copy link
Copy Markdown

Because julius plumbed in ACP can't we just use ACP, I guess the copilot sdk has copilot specific stuff

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@zortos293
zortos293force-pushed the feat/add-copilot-sdk-support branch from f02de1a to 290befbCompareApril 20, 2026 08:04
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/git/Layers/CopilotTextGeneration.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/git/Layers/CopilotTextGeneration.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/textGeneration/CopilotTextGeneration.ts
fav-devs added a commit to fav-devs/fdcode that referenced this pull request Apr 21, 2026
Comment threadpackages/contracts/src/settings.ts Outdated
@@ -88,6 +92,13 @@ export const CodexSettings = Schema.Struct({
customModels: Schema.Array(Schema.String).pipe(Schema.withDecodingDefault(Effect.succeed([]))),
});
export type CodexSettings = typeof CodexSettings.Type;
export const CopilotSettings = Schema.Struct({
enabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),

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.

Copilot enabled by default causes unwanted CLI spawns

Medium Severity

CopilotSettings defaults enabled to true, meaning every existing user who upgrades will have Copilot auto-enabled. Combined with the 1-hour COPILOT_REFRESH_INTERVAL, this will repeatedly spawn the bundled Copilot CLI binary, attempt auth checks, and fail for the majority of users who have no GitHub Copilot subscription or auth configured. Unlike Codex/Claude/OpenCode which are the app's core providers, Copilot is a newly added optional integration — defaulting to false (like Cursor does) would avoid unnecessary process spawns and error noise on every refresh cycle.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit c9b851217439d17fbbe96d68b3f89077440f3446. Configure here.

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
@rgembalik

Copy link
Copy Markdown

I am not a maintainer, but I did couple of local tests. There were few cases where session stopped without showing final results message. I don't know if this is upstream bug, or something related to the PR (currently I have only copilot license so it's hard to test against other providers). After asking, I got a response that the results were there, but probably didn't show on my end.

image

@zortos293

Copy link
Copy Markdown
ContributorAuthor

i'l look into this

Comment threadapps/server/src/provider/copilotRuntime.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@brrock

Copy link
Copy Markdown

conflicts btw

@zortos293

Copy link
Copy Markdown
ContributorAuthor

conflicts btw

imma look at it tmr 👍

@zortos293
zortos293force-pushed the feat/add-copilot-sdk-support branch from 01451c8 to 614bc62CompareMay 7, 2026 13:46
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Services/CopilotProvider.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/copilotRuntime.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment on lines +61 to +147
export function checkCopilotProviderStatus(input: {
readonly settings: CopilotSettings;
readonly cwd: string;
readonly environment?: NodeJS.ProcessEnv | undefined;
}): Effect.Effect<ServerProviderDraft> {
if (!input.settings.enabled) {
return Effect.succeed(makePendingCopilotProvider(input.settings));
}

const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const fallback = (cause: unknown, version: string | null = null) => {
const failure = formatCopilotProbeError({
cause,
settings: input.settings,
});
return buildServerProvider({
driver: PROVIDER,
presentation: COPILOT_PRESENTATION,
enabled: true,
checkedAt,
models: modelsFromCopilotSdk({
models: [],
customModels: input.settings.customModels,
}),
probe: {
installed: failure.installed,
version,
status: "error",
auth: { status: "unknown" },
message: failure.message,
},
});
};

return Effect.acquireUseRelease(
Effect.sync(() =>
createCopilotClient({
settings: input.settings,
cwd: input.cwd,
...(input.environment ? { env: input.environment } : {}),
logLevel: "error",
}),
),
(client) =>
Effect.tryPromise({
try: async () => {
await client.start();
const [status, authStatus, models] = await Promise.all([
client.getStatus(),
client.getAuthStatus(),
client.listModels(),
]);
const authSnapshot = authSnapshotFromCopilotSdk(authStatus);
const providerModels = modelsFromCopilotSdk({
models,
customModels: input.settings.customModels,
});
const hasBuiltInModels = models.length > 0;

return buildServerProvider({
driver: PROVIDER,
presentation: COPILOT_PRESENTATION,
enabled: true,
checkedAt,
models: providerModels,
probe: {
installed: true,
version: versionFromCopilotStatus(status),
status:
authSnapshot.status !== "ready"
? authSnapshot.status
: hasBuiltInModels
? "ready"
: "warning",
auth: authSnapshot.auth,
...(authSnapshot.message
? { message: authSnapshot.message }
: hasBuiltInModels
? {}
: { message: "Copilot did not report any available models for this account." }),
},
});
},
catch: toCopilotProbeError,
}).pipe(Effect.catch((cause) => Effect.succeed(fallback(cause)))),
(client) => Effect.promise(() => client.stop()).pipe(Effect.ignore({ log: true })),
);

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.

🟢 LowLayers/CopilotProvider.ts:61

The checkedAt timestamp is captured at function call time on line 70, so every execution of the returned Effect reports the same stale timestamp from when checkCopilotProviderStatus was first invoked. Since makeManagedServerProvider re-executes this Effect on each refresh interval, all subsequent checks incorrectly show the original timestamp rather than when each check actually occurred.

 if (!input.settings.enabled) {
return Effect.succeed(makePendingCopilotProvider(input.settings));
}
- const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const fallback = (cause: unknown, version: string | null = null) => {
+ const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const failure = formatCopilotProbeError({
cause,
settings: input.settings,
@@ -92,6 +92,7 @@
});
};
+ const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
return Effect.acquireUseRelease(
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/CopilotProvider.ts around lines 61-147:
The `checkedAt` timestamp is captured at function call time on line 70, so every execution of the returned Effect reports the same stale timestamp from when `checkCopilotProviderStatus` was first invoked. Since `makeManagedServerProvider` re-executes this Effect on each refresh interval, all subsequent checks incorrectly show the original timestamp rather than when each check actually occurred.
Evidence trail:
apps/server/src/provider/Layers/CopilotProvider.ts lines 61-70 (checkedAt captured eagerly outside Effect), lines 95-149 (Effect.acquireUseRelease returned, closing over checkedAt). apps/server/src/provider/Drivers/CopilotDriver.ts lines 92-95 (checkCopilotProviderStatus called once, Effect passed as checkProvider). apps/server/src/provider/makeManagedServerProvider.ts lines 127-130 (input.checkProvider re-executed in applySnapshotBase), lines 163-168 (forever loop re-running refreshSnapshot on each interval).

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
decision === "accept" || decision === "acceptForSession"
? { kind: "approved" }
: { kind: "denied-interactively-by-user" };
yield* Deferred.succeed(binding.deferred, 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.

🟢 LowLayers/CopilotAdapter.ts:2264

In respondToRequest, after resolving binding.deferred, the requestId is not removed from pendingPermissionBindings. If called twice with the same requestId, the second call silently succeeds instead of returning an error, which can mask client bugs or race conditions. Consider deleting the binding immediately after resolving it.

- yield* Deferred.succeed(binding.deferred, result);+ yield* Deferred.succeed(binding.deferred, result);+ context.pendingPermissionBindings.delete(requestId);
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/CopilotAdapter.ts around line 2264:
In `respondToRequest`, after resolving `binding.deferred`, the `requestId` is not removed from `pendingPermissionBindings`. If called twice with the same `requestId`, the second call silently succeeds instead of returning an error, which can mask client bugs or race conditions. Consider deleting the binding immediately after resolving it.
Evidence trail:
apps/server/src/provider/Layers/CopilotAdapter.ts lines 2248-2265 (respondToRequest function: no delete after Deferred.succeed at line 2264). Compare with lines 574-577 (settlePendingPermissionHandlers: calls .clear() after settling). Effect-TS Deferred.succeed returns Effect<boolean> — false if already done, no error thrown (see Effect-TS source/docs).

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts

@cursorcursorBot 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.

There are 2 total unresolved issues (including 1 from previous review).

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 d464878. Configure here.

},
});
sessions.delete(threadId);
});

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.

Session teardown doesn't await pending event chain

High Severity

stopSessionInternal disconnects the SDK session and stops the client without first awaiting context.eventChain. Events still being processed asynchronously in the chain (including turn.completed and item.completed events) can race against or fail after the SDK resources are torn down. This likely explains the reported bug where sessions stop without surfacing final results. In contrast, startSession correctly awaits the chain at line 2180 with yield* Effect.promise(() => context.eventChain).

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit d464878. Configure here.

@juliusmarmingeChatGPT Codex Connector

Copy link
Copy Markdown
Member

A newer Copilot provider implementation, #3076, is active against the current provider architecture. Keeping this older provider branch would duplicate that effort.

This was closed as part of a large repo-wide maintenance sweep. If you think it's still relevant, please reopen.

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

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@zortos293@brrock@rgembalik@akshaylingamaneni@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Add Github Copilot Provider - #2185

Closed
zortos293 wants to merge 19 commits into
pingdotgg:mainfrom
zortos293:feat/add-copilot-sdk-support
Closed

Add Github Copilot Provider#2185
zortos293 wants to merge 19 commits into
pingdotgg:mainfrom
zortos293:feat/add-copilot-sdk-support

Conversation

@zortos293

@zortos293zortos293 commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Added GitHub Copilot as a first-class provider using the official Copilot SDK.
  • Wired Copilot through the server runtime, including provider detection, auth/status reporting, model discovery, session startup/resume, turn sending, request handling, and event streaming.
  • Added Copilot-backed git text generation for commit messages, PR content, branch names, and thread titles.
  • Added Copilot to shared contracts/settings so it behaves like the existing providers for model selection, reasoning options, defaults, and persisted settings.
  • Updated the web app so Copilot appears in the provider/model dropdowns, settings panels, custom model management, composer state, and git writing model selection.

Why

T3 Code already supports multiple agent providers, but it did not have GitHub Copilot support. This change adds Copilot end to end in the same shape as the other providers instead of treating it as a one-off integration.

That keeps the UX and server behavior consistent: users can configure Copilot in settings, select Copilot models from the same pickers, use it for git text generation, and rely on the same persistence and runtime flows that already exist for other providers. Using the official SDK also keeps the integration aligned with GitHub’s supported protocol and model discovery/auth flows.

UI Changes

  • Added Copilot to the provider picker.
  • Added a Copilot settings section with binary path, server URL, and custom model configuration.
  • Added Copilot to the git writing model picker and traits handling.
imageimageimage

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
Adds a new first-class provider with complex session/event handling and introduces the @github/copilot-sdk runtime dependency, so failures/auth/model discovery issues could impact provider startup and chat/runtime flows.

Overview
Adds GitHub Copilot as a first-class provider end-to-end. The server now includes a new CopilotDriver registered in BUILT_IN_DRIVERS, a Copilot SDK runtime wrapper (copilotRuntime.ts) for auth/status/model probing, and a full CopilotAdapter that starts/resumes sessions, translates Copilot SDK events into provider runtime events (turn lifecycle, streaming deltas, tool execution, permissions/user-input), and hardens error/cleanup behavior.

Adds Copilot-backed git text generation (CopilotTextGeneration.ts) for commit messages/PR content/branch names/thread titles with pooled SDK clients and idle shutdown. Updates shared contracts/settings to include CopilotSettings, provider defaults/aliases/display names, and extends runtime raw event sources; the web app surfaces Copilot in provider pickers/settings/icons and removes the “coming soon” placeholder when a real Copilot instance exists. Includes extensive new tests covering adapter behavior, provider probe errors, and text-generation client reuse.

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

Note

Add GitHub Copilot as a built-in provider for chat and text generation

  • Adds a full CopilotDriver and CopilotAdapter that integrate with @github/copilot-sdk to support chat sessions, permission handling, and runtime event translation.
  • Adds makeCopilotTextGeneration to generate commit messages, PR content, branch names, and thread titles via Copilot models using structured JSON outputs and a shared client pool.
  • Adds checkCopilotProviderStatus to periodically probe the Copilot SDK for auth state, available models, and version info.
  • Updates the settings UI, model picker, and provider icon mapping to treat Copilot as a first-class provider, removing the "Coming soon" placeholder.
  • Risk: introduces a new runtime dependency @github/copilot-sdk@^0.2.2 and spawns external CLI processes; probe failures are surfaced as provider errors rather than silently ignored.

Macroscope summarized d464878.

@coderabbitai

coderabbitaiBot commented Apr 18, 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: 6417f1bb-b9f1-4115-890c-8459048c5942

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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@zortos293zortos293 mentioned this pull request Apr 18, 2026
4 tasks
@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Apr 18, 2026
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@macroscopeapp

macroscopeappBot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR adds a new GitHub Copilot provider integration with ~4500 lines of new code, external SDK dependencies, and complex session management. New feature additions with this scope require human review. Additionally, an unresolved high-severity comment identifies a potential race condition in session teardown that could cause lost events.

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

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
@zortos293

Copy link
Copy Markdown
ContributorAuthor

This needs to be tested if the desktop app is working fine with Copilot

@brrock

Copy link
Copy Markdown

Because julius plumbed in ACP can't we just use ACP, I guess the copilot sdk has copilot specific stuff

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@zortos293
zortos293force-pushed the feat/add-copilot-sdk-support branch from f02de1a to 290befbCompareApril 20, 2026 08:04
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/git/Layers/CopilotTextGeneration.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/git/Layers/CopilotTextGeneration.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/textGeneration/CopilotTextGeneration.ts
fav-devs added a commit to fav-devs/fdcode that referenced this pull request Apr 21, 2026
Comment threadpackages/contracts/src/settings.ts Outdated
@@ -88,6 +92,13 @@ export const CodexSettings = Schema.Struct({
customModels: Schema.Array(Schema.String).pipe(Schema.withDecodingDefault(Effect.succeed([]))),
});
export type CodexSettings = typeof CodexSettings.Type;
export const CopilotSettings = Schema.Struct({
enabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),

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.

Copilot enabled by default causes unwanted CLI spawns

Medium Severity

CopilotSettings defaults enabled to true, meaning every existing user who upgrades will have Copilot auto-enabled. Combined with the 1-hour COPILOT_REFRESH_INTERVAL, this will repeatedly spawn the bundled Copilot CLI binary, attempt auth checks, and fail for the majority of users who have no GitHub Copilot subscription or auth configured. Unlike Codex/Claude/OpenCode which are the app's core providers, Copilot is a newly added optional integration — defaulting to false (like Cursor does) would avoid unnecessary process spawns and error noise on every refresh cycle.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit c9b851217439d17fbbe96d68b3f89077440f3446. Configure here.

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
@rgembalik

Copy link
Copy Markdown

I am not a maintainer, but I did couple of local tests. There were few cases where session stopped without showing final results message. I don't know if this is upstream bug, or something related to the PR (currently I have only copilot license so it's hard to test against other providers). After asking, I got a response that the results were there, but probably didn't show on my end.

image

@zortos293

Copy link
Copy Markdown
ContributorAuthor

i'l look into this

Comment threadapps/server/src/provider/copilotRuntime.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@brrock

Copy link
Copy Markdown

conflicts btw

@zortos293

Copy link
Copy Markdown
ContributorAuthor

conflicts btw

imma look at it tmr 👍

@zortos293
zortos293force-pushed the feat/add-copilot-sdk-support branch from 01451c8 to 614bc62CompareMay 7, 2026 13:46
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Services/CopilotProvider.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/copilotRuntime.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment on lines +61 to +147
export function checkCopilotProviderStatus(input: {
readonly settings: CopilotSettings;
readonly cwd: string;
readonly environment?: NodeJS.ProcessEnv | undefined;
}): Effect.Effect<ServerProviderDraft> {
if (!input.settings.enabled) {
return Effect.succeed(makePendingCopilotProvider(input.settings));
}

const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const fallback = (cause: unknown, version: string | null = null) => {
const failure = formatCopilotProbeError({
cause,
settings: input.settings,
});
return buildServerProvider({
driver: PROVIDER,
presentation: COPILOT_PRESENTATION,
enabled: true,
checkedAt,
models: modelsFromCopilotSdk({
models: [],
customModels: input.settings.customModels,
}),
probe: {
installed: failure.installed,
version,
status: "error",
auth: { status: "unknown" },
message: failure.message,
},
});
};

return Effect.acquireUseRelease(
Effect.sync(() =>
createCopilotClient({
settings: input.settings,
cwd: input.cwd,
...(input.environment ? { env: input.environment } : {}),
logLevel: "error",
}),
),
(client) =>
Effect.tryPromise({
try: async () => {
await client.start();
const [status, authStatus, models] = await Promise.all([
client.getStatus(),
client.getAuthStatus(),
client.listModels(),
]);
const authSnapshot = authSnapshotFromCopilotSdk(authStatus);
const providerModels = modelsFromCopilotSdk({
models,
customModels: input.settings.customModels,
});
const hasBuiltInModels = models.length > 0;

return buildServerProvider({
driver: PROVIDER,
presentation: COPILOT_PRESENTATION,
enabled: true,
checkedAt,
models: providerModels,
probe: {
installed: true,
version: versionFromCopilotStatus(status),
status:
authSnapshot.status !== "ready"
? authSnapshot.status
: hasBuiltInModels
? "ready"
: "warning",
auth: authSnapshot.auth,
...(authSnapshot.message
? { message: authSnapshot.message }
: hasBuiltInModels
? {}
: { message: "Copilot did not report any available models for this account." }),
},
});
},
catch: toCopilotProbeError,
}).pipe(Effect.catch((cause) => Effect.succeed(fallback(cause)))),
(client) => Effect.promise(() => client.stop()).pipe(Effect.ignore({ log: true })),
);

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.

🟢 LowLayers/CopilotProvider.ts:61

The checkedAt timestamp is captured at function call time on line 70, so every execution of the returned Effect reports the same stale timestamp from when checkCopilotProviderStatus was first invoked. Since makeManagedServerProvider re-executes this Effect on each refresh interval, all subsequent checks incorrectly show the original timestamp rather than when each check actually occurred.

 if (!input.settings.enabled) {
return Effect.succeed(makePendingCopilotProvider(input.settings));
}
- const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const fallback = (cause: unknown, version: string | null = null) => {
+ const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const failure = formatCopilotProbeError({
cause,
settings: input.settings,
@@ -92,6 +92,7 @@
});
};
+ const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
return Effect.acquireUseRelease(
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/CopilotProvider.ts around lines 61-147:
The `checkedAt` timestamp is captured at function call time on line 70, so every execution of the returned Effect reports the same stale timestamp from when `checkCopilotProviderStatus` was first invoked. Since `makeManagedServerProvider` re-executes this Effect on each refresh interval, all subsequent checks incorrectly show the original timestamp rather than when each check actually occurred.
Evidence trail:
apps/server/src/provider/Layers/CopilotProvider.ts lines 61-70 (checkedAt captured eagerly outside Effect), lines 95-149 (Effect.acquireUseRelease returned, closing over checkedAt). apps/server/src/provider/Drivers/CopilotDriver.ts lines 92-95 (checkCopilotProviderStatus called once, Effect passed as checkProvider). apps/server/src/provider/makeManagedServerProvider.ts lines 127-130 (input.checkProvider re-executed in applySnapshotBase), lines 163-168 (forever loop re-running refreshSnapshot on each interval).

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
decision === "accept" || decision === "acceptForSession"
? { kind: "approved" }
: { kind: "denied-interactively-by-user" };
yield* Deferred.succeed(binding.deferred, 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.

🟢 LowLayers/CopilotAdapter.ts:2264

In respondToRequest, after resolving binding.deferred, the requestId is not removed from pendingPermissionBindings. If called twice with the same requestId, the second call silently succeeds instead of returning an error, which can mask client bugs or race conditions. Consider deleting the binding immediately after resolving it.

- yield* Deferred.succeed(binding.deferred, result);+ yield* Deferred.succeed(binding.deferred, result);+ context.pendingPermissionBindings.delete(requestId);
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/CopilotAdapter.ts around line 2264:
In `respondToRequest`, after resolving `binding.deferred`, the `requestId` is not removed from `pendingPermissionBindings`. If called twice with the same `requestId`, the second call silently succeeds instead of returning an error, which can mask client bugs or race conditions. Consider deleting the binding immediately after resolving it.
Evidence trail:
apps/server/src/provider/Layers/CopilotAdapter.ts lines 2248-2265 (respondToRequest function: no delete after Deferred.succeed at line 2264). Compare with lines 574-577 (settlePendingPermissionHandlers: calls .clear() after settling). Effect-TS Deferred.succeed returns Effect<boolean> — false if already done, no error thrown (see Effect-TS source/docs).

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts

@cursorcursorBot 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.

There are 2 total unresolved issues (including 1 from previous review).

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 d464878. Configure here.

},
});
sessions.delete(threadId);
});

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.

Session teardown doesn't await pending event chain

High Severity

stopSessionInternal disconnects the SDK session and stops the client without first awaiting context.eventChain. Events still being processed asynchronously in the chain (including turn.completed and item.completed events) can race against or fail after the SDK resources are torn down. This likely explains the reported bug where sessions stop without surfacing final results. In contrast, startSession correctly awaits the chain at line 2180 with yield* Effect.promise(() => context.eventChain).

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit d464878. Configure here.

@juliusmarmingeChatGPT Codex Connector

Copy link
Copy Markdown
Member

A newer Copilot provider implementation, #3076, is active against the current provider architecture. Keeping this older provider branch would duplicate that effort.

This was closed as part of a large repo-wide maintenance sweep. If you think it's still relevant, please reopen.

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

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@zortos293@brrock@rgembalik@akshaylingamaneni@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add Github Copilot Provider - #2185

Closed
zortos293 wants to merge 19 commits into
pingdotgg:mainfrom
zortos293:feat/add-copilot-sdk-support
Closed

Add Github Copilot Provider#2185
zortos293 wants to merge 19 commits into
pingdotgg:mainfrom
zortos293:feat/add-copilot-sdk-support

Conversation

@zortos293

@zortos293zortos293 commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Added GitHub Copilot as a first-class provider using the official Copilot SDK.
  • Wired Copilot through the server runtime, including provider detection, auth/status reporting, model discovery, session startup/resume, turn sending, request handling, and event streaming.
  • Added Copilot-backed git text generation for commit messages, PR content, branch names, and thread titles.
  • Added Copilot to shared contracts/settings so it behaves like the existing providers for model selection, reasoning options, defaults, and persisted settings.
  • Updated the web app so Copilot appears in the provider/model dropdowns, settings panels, custom model management, composer state, and git writing model selection.

Why

T3 Code already supports multiple agent providers, but it did not have GitHub Copilot support. This change adds Copilot end to end in the same shape as the other providers instead of treating it as a one-off integration.

That keeps the UX and server behavior consistent: users can configure Copilot in settings, select Copilot models from the same pickers, use it for git text generation, and rely on the same persistence and runtime flows that already exist for other providers. Using the official SDK also keeps the integration aligned with GitHub’s supported protocol and model discovery/auth flows.

UI Changes

  • Added Copilot to the provider picker.
  • Added a Copilot settings section with binary path, server URL, and custom model configuration.
  • Added Copilot to the git writing model picker and traits handling.
imageimageimage

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
Adds a new first-class provider with complex session/event handling and introduces the @github/copilot-sdk runtime dependency, so failures/auth/model discovery issues could impact provider startup and chat/runtime flows.

Overview
Adds GitHub Copilot as a first-class provider end-to-end. The server now includes a new CopilotDriver registered in BUILT_IN_DRIVERS, a Copilot SDK runtime wrapper (copilotRuntime.ts) for auth/status/model probing, and a full CopilotAdapter that starts/resumes sessions, translates Copilot SDK events into provider runtime events (turn lifecycle, streaming deltas, tool execution, permissions/user-input), and hardens error/cleanup behavior.

Adds Copilot-backed git text generation (CopilotTextGeneration.ts) for commit messages/PR content/branch names/thread titles with pooled SDK clients and idle shutdown. Updates shared contracts/settings to include CopilotSettings, provider defaults/aliases/display names, and extends runtime raw event sources; the web app surfaces Copilot in provider pickers/settings/icons and removes the “coming soon” placeholder when a real Copilot instance exists. Includes extensive new tests covering adapter behavior, provider probe errors, and text-generation client reuse.

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

Note

Add GitHub Copilot as a built-in provider for chat and text generation

  • Adds a full CopilotDriver and CopilotAdapter that integrate with @github/copilot-sdk to support chat sessions, permission handling, and runtime event translation.
  • Adds makeCopilotTextGeneration to generate commit messages, PR content, branch names, and thread titles via Copilot models using structured JSON outputs and a shared client pool.
  • Adds checkCopilotProviderStatus to periodically probe the Copilot SDK for auth state, available models, and version info.
  • Updates the settings UI, model picker, and provider icon mapping to treat Copilot as a first-class provider, removing the "Coming soon" placeholder.
  • Risk: introduces a new runtime dependency @github/copilot-sdk@^0.2.2 and spawns external CLI processes; probe failures are surfaced as provider errors rather than silently ignored.

Macroscope summarized d464878.

@coderabbitai

coderabbitaiBot commented Apr 18, 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: 6417f1bb-b9f1-4115-890c-8459048c5942

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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@zortos293zortos293 mentioned this pull request Apr 18, 2026
4 tasks
@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Apr 18, 2026
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@macroscopeapp

macroscopeappBot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR adds a new GitHub Copilot provider integration with ~4500 lines of new code, external SDK dependencies, and complex session management. New feature additions with this scope require human review. Additionally, an unresolved high-severity comment identifies a potential race condition in session teardown that could cause lost events.

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

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
@zortos293

Copy link
Copy Markdown
ContributorAuthor

This needs to be tested if the desktop app is working fine with Copilot

@brrock

Copy link
Copy Markdown

Because julius plumbed in ACP can't we just use ACP, I guess the copilot sdk has copilot specific stuff

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@zortos293
zortos293force-pushed the feat/add-copilot-sdk-support branch from f02de1a to 290befbCompareApril 20, 2026 08:04
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/git/Layers/CopilotTextGeneration.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/git/Layers/CopilotTextGeneration.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/textGeneration/CopilotTextGeneration.ts
fav-devs added a commit to fav-devs/fdcode that referenced this pull request Apr 21, 2026
Comment threadpackages/contracts/src/settings.ts Outdated
@@ -88,6 +92,13 @@ export const CodexSettings = Schema.Struct({
customModels: Schema.Array(Schema.String).pipe(Schema.withDecodingDefault(Effect.succeed([]))),
});
export type CodexSettings = typeof CodexSettings.Type;
export const CopilotSettings = Schema.Struct({
enabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),

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.

Copilot enabled by default causes unwanted CLI spawns

Medium Severity

CopilotSettings defaults enabled to true, meaning every existing user who upgrades will have Copilot auto-enabled. Combined with the 1-hour COPILOT_REFRESH_INTERVAL, this will repeatedly spawn the bundled Copilot CLI binary, attempt auth checks, and fail for the majority of users who have no GitHub Copilot subscription or auth configured. Unlike Codex/Claude/OpenCode which are the app's core providers, Copilot is a newly added optional integration — defaulting to false (like Cursor does) would avoid unnecessary process spawns and error noise on every refresh cycle.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit c9b851217439d17fbbe96d68b3f89077440f3446. Configure here.

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
@rgembalik

Copy link
Copy Markdown

I am not a maintainer, but I did couple of local tests. There were few cases where session stopped without showing final results message. I don't know if this is upstream bug, or something related to the PR (currently I have only copilot license so it's hard to test against other providers). After asking, I got a response that the results were there, but probably didn't show on my end.

image

@zortos293

Copy link
Copy Markdown
ContributorAuthor

i'l look into this

Comment threadapps/server/src/provider/copilotRuntime.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@brrock

Copy link
Copy Markdown

conflicts btw

@zortos293

Copy link
Copy Markdown
ContributorAuthor

conflicts btw

imma look at it tmr 👍

@zortos293
zortos293force-pushed the feat/add-copilot-sdk-support branch from 01451c8 to 614bc62CompareMay 7, 2026 13:46
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Services/CopilotProvider.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/copilotRuntime.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment on lines +61 to +147
export function checkCopilotProviderStatus(input: {
readonly settings: CopilotSettings;
readonly cwd: string;
readonly environment?: NodeJS.ProcessEnv | undefined;
}): Effect.Effect<ServerProviderDraft> {
if (!input.settings.enabled) {
return Effect.succeed(makePendingCopilotProvider(input.settings));
}

const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const fallback = (cause: unknown, version: string | null = null) => {
const failure = formatCopilotProbeError({
cause,
settings: input.settings,
});
return buildServerProvider({
driver: PROVIDER,
presentation: COPILOT_PRESENTATION,
enabled: true,
checkedAt,
models: modelsFromCopilotSdk({
models: [],
customModels: input.settings.customModels,
}),
probe: {
installed: failure.installed,
version,
status: "error",
auth: { status: "unknown" },
message: failure.message,
},
});
};

return Effect.acquireUseRelease(
Effect.sync(() =>
createCopilotClient({
settings: input.settings,
cwd: input.cwd,
...(input.environment ? { env: input.environment } : {}),
logLevel: "error",
}),
),
(client) =>
Effect.tryPromise({
try: async () => {
await client.start();
const [status, authStatus, models] = await Promise.all([
client.getStatus(),
client.getAuthStatus(),
client.listModels(),
]);
const authSnapshot = authSnapshotFromCopilotSdk(authStatus);
const providerModels = modelsFromCopilotSdk({
models,
customModels: input.settings.customModels,
});
const hasBuiltInModels = models.length > 0;

return buildServerProvider({
driver: PROVIDER,
presentation: COPILOT_PRESENTATION,
enabled: true,
checkedAt,
models: providerModels,
probe: {
installed: true,
version: versionFromCopilotStatus(status),
status:
authSnapshot.status !== "ready"
? authSnapshot.status
: hasBuiltInModels
? "ready"
: "warning",
auth: authSnapshot.auth,
...(authSnapshot.message
? { message: authSnapshot.message }
: hasBuiltInModels
? {}
: { message: "Copilot did not report any available models for this account." }),
},
});
},
catch: toCopilotProbeError,
}).pipe(Effect.catch((cause) => Effect.succeed(fallback(cause)))),
(client) => Effect.promise(() => client.stop()).pipe(Effect.ignore({ log: true })),
);

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.

🟢 LowLayers/CopilotProvider.ts:61

The checkedAt timestamp is captured at function call time on line 70, so every execution of the returned Effect reports the same stale timestamp from when checkCopilotProviderStatus was first invoked. Since makeManagedServerProvider re-executes this Effect on each refresh interval, all subsequent checks incorrectly show the original timestamp rather than when each check actually occurred.

 if (!input.settings.enabled) {
return Effect.succeed(makePendingCopilotProvider(input.settings));
}
- const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const fallback = (cause: unknown, version: string | null = null) => {
+ const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const failure = formatCopilotProbeError({
cause,
settings: input.settings,
@@ -92,6 +92,7 @@
});
};
+ const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
return Effect.acquireUseRelease(
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/CopilotProvider.ts around lines 61-147:
The `checkedAt` timestamp is captured at function call time on line 70, so every execution of the returned Effect reports the same stale timestamp from when `checkCopilotProviderStatus` was first invoked. Since `makeManagedServerProvider` re-executes this Effect on each refresh interval, all subsequent checks incorrectly show the original timestamp rather than when each check actually occurred.
Evidence trail:
apps/server/src/provider/Layers/CopilotProvider.ts lines 61-70 (checkedAt captured eagerly outside Effect), lines 95-149 (Effect.acquireUseRelease returned, closing over checkedAt). apps/server/src/provider/Drivers/CopilotDriver.ts lines 92-95 (checkCopilotProviderStatus called once, Effect passed as checkProvider). apps/server/src/provider/makeManagedServerProvider.ts lines 127-130 (input.checkProvider re-executed in applySnapshotBase), lines 163-168 (forever loop re-running refreshSnapshot on each interval).

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
decision === "accept" || decision === "acceptForSession"
? { kind: "approved" }
: { kind: "denied-interactively-by-user" };
yield* Deferred.succeed(binding.deferred, 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.

🟢 LowLayers/CopilotAdapter.ts:2264

In respondToRequest, after resolving binding.deferred, the requestId is not removed from pendingPermissionBindings. If called twice with the same requestId, the second call silently succeeds instead of returning an error, which can mask client bugs or race conditions. Consider deleting the binding immediately after resolving it.

- yield* Deferred.succeed(binding.deferred, result);+ yield* Deferred.succeed(binding.deferred, result);+ context.pendingPermissionBindings.delete(requestId);
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/CopilotAdapter.ts around line 2264:
In `respondToRequest`, after resolving `binding.deferred`, the `requestId` is not removed from `pendingPermissionBindings`. If called twice with the same `requestId`, the second call silently succeeds instead of returning an error, which can mask client bugs or race conditions. Consider deleting the binding immediately after resolving it.
Evidence trail:
apps/server/src/provider/Layers/CopilotAdapter.ts lines 2248-2265 (respondToRequest function: no delete after Deferred.succeed at line 2264). Compare with lines 574-577 (settlePendingPermissionHandlers: calls .clear() after settling). Effect-TS Deferred.succeed returns Effect<boolean> — false if already done, no error thrown (see Effect-TS source/docs).

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts

@cursorcursorBot 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.

There are 2 total unresolved issues (including 1 from previous review).

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 d464878. Configure here.

},
});
sessions.delete(threadId);
});

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.

Session teardown doesn't await pending event chain

High Severity

stopSessionInternal disconnects the SDK session and stops the client without first awaiting context.eventChain. Events still being processed asynchronously in the chain (including turn.completed and item.completed events) can race against or fail after the SDK resources are torn down. This likely explains the reported bug where sessions stop without surfacing final results. In contrast, startSession correctly awaits the chain at line 2180 with yield* Effect.promise(() => context.eventChain).

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit d464878. Configure here.

@juliusmarmingeChatGPT Codex Connector

Copy link
Copy Markdown
Member

A newer Copilot provider implementation, #3076, is active against the current provider architecture. Keeping this older provider branch would duplicate that effort.

This was closed as part of a large repo-wide maintenance sweep. If you think it's still relevant, please reopen.

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

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@zortos293@brrock@rgembalik@akshaylingamaneni@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add Github Copilot Provider - #2185

Closed
zortos293 wants to merge 19 commits into
pingdotgg:mainfrom
zortos293:feat/add-copilot-sdk-support
Closed

Add Github Copilot Provider#2185
zortos293 wants to merge 19 commits into
pingdotgg:mainfrom
zortos293:feat/add-copilot-sdk-support

Conversation

@zortos293

@zortos293zortos293 commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Added GitHub Copilot as a first-class provider using the official Copilot SDK.
  • Wired Copilot through the server runtime, including provider detection, auth/status reporting, model discovery, session startup/resume, turn sending, request handling, and event streaming.
  • Added Copilot-backed git text generation for commit messages, PR content, branch names, and thread titles.
  • Added Copilot to shared contracts/settings so it behaves like the existing providers for model selection, reasoning options, defaults, and persisted settings.
  • Updated the web app so Copilot appears in the provider/model dropdowns, settings panels, custom model management, composer state, and git writing model selection.

Why

T3 Code already supports multiple agent providers, but it did not have GitHub Copilot support. This change adds Copilot end to end in the same shape as the other providers instead of treating it as a one-off integration.

That keeps the UX and server behavior consistent: users can configure Copilot in settings, select Copilot models from the same pickers, use it for git text generation, and rely on the same persistence and runtime flows that already exist for other providers. Using the official SDK also keeps the integration aligned with GitHub’s supported protocol and model discovery/auth flows.

UI Changes

  • Added Copilot to the provider picker.
  • Added a Copilot settings section with binary path, server URL, and custom model configuration.
  • Added Copilot to the git writing model picker and traits handling.
imageimageimage

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
Adds a new first-class provider with complex session/event handling and introduces the @github/copilot-sdk runtime dependency, so failures/auth/model discovery issues could impact provider startup and chat/runtime flows.

Overview
Adds GitHub Copilot as a first-class provider end-to-end. The server now includes a new CopilotDriver registered in BUILT_IN_DRIVERS, a Copilot SDK runtime wrapper (copilotRuntime.ts) for auth/status/model probing, and a full CopilotAdapter that starts/resumes sessions, translates Copilot SDK events into provider runtime events (turn lifecycle, streaming deltas, tool execution, permissions/user-input), and hardens error/cleanup behavior.

Adds Copilot-backed git text generation (CopilotTextGeneration.ts) for commit messages/PR content/branch names/thread titles with pooled SDK clients and idle shutdown. Updates shared contracts/settings to include CopilotSettings, provider defaults/aliases/display names, and extends runtime raw event sources; the web app surfaces Copilot in provider pickers/settings/icons and removes the “coming soon” placeholder when a real Copilot instance exists. Includes extensive new tests covering adapter behavior, provider probe errors, and text-generation client reuse.

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

Note

Add GitHub Copilot as a built-in provider for chat and text generation

  • Adds a full CopilotDriver and CopilotAdapter that integrate with @github/copilot-sdk to support chat sessions, permission handling, and runtime event translation.
  • Adds makeCopilotTextGeneration to generate commit messages, PR content, branch names, and thread titles via Copilot models using structured JSON outputs and a shared client pool.
  • Adds checkCopilotProviderStatus to periodically probe the Copilot SDK for auth state, available models, and version info.
  • Updates the settings UI, model picker, and provider icon mapping to treat Copilot as a first-class provider, removing the "Coming soon" placeholder.
  • Risk: introduces a new runtime dependency @github/copilot-sdk@^0.2.2 and spawns external CLI processes; probe failures are surfaced as provider errors rather than silently ignored.

Macroscope summarized d464878.

@coderabbitai

coderabbitaiBot commented Apr 18, 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: 6417f1bb-b9f1-4115-890c-8459048c5942

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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@zortos293zortos293 mentioned this pull request Apr 18, 2026
4 tasks
@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Apr 18, 2026
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@macroscopeapp

macroscopeappBot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR adds a new GitHub Copilot provider integration with ~4500 lines of new code, external SDK dependencies, and complex session management. New feature additions with this scope require human review. Additionally, an unresolved high-severity comment identifies a potential race condition in session teardown that could cause lost events.

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

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
@zortos293

Copy link
Copy Markdown
ContributorAuthor

This needs to be tested if the desktop app is working fine with Copilot

@brrock

Copy link
Copy Markdown

Because julius plumbed in ACP can't we just use ACP, I guess the copilot sdk has copilot specific stuff

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@zortos293
zortos293force-pushed the feat/add-copilot-sdk-support branch from f02de1a to 290befbCompareApril 20, 2026 08:04
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/git/Layers/CopilotTextGeneration.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/git/Layers/CopilotTextGeneration.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/textGeneration/CopilotTextGeneration.ts
fav-devs added a commit to fav-devs/fdcode that referenced this pull request Apr 21, 2026
Comment threadpackages/contracts/src/settings.ts Outdated
@@ -88,6 +92,13 @@ export const CodexSettings = Schema.Struct({
customModels: Schema.Array(Schema.String).pipe(Schema.withDecodingDefault(Effect.succeed([]))),
});
export type CodexSettings = typeof CodexSettings.Type;
export const CopilotSettings = Schema.Struct({
enabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),

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.

Copilot enabled by default causes unwanted CLI spawns

Medium Severity

CopilotSettings defaults enabled to true, meaning every existing user who upgrades will have Copilot auto-enabled. Combined with the 1-hour COPILOT_REFRESH_INTERVAL, this will repeatedly spawn the bundled Copilot CLI binary, attempt auth checks, and fail for the majority of users who have no GitHub Copilot subscription or auth configured. Unlike Codex/Claude/OpenCode which are the app's core providers, Copilot is a newly added optional integration — defaulting to false (like Cursor does) would avoid unnecessary process spawns and error noise on every refresh cycle.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit c9b851217439d17fbbe96d68b3f89077440f3446. Configure here.

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
@rgembalik

Copy link
Copy Markdown

I am not a maintainer, but I did couple of local tests. There were few cases where session stopped without showing final results message. I don't know if this is upstream bug, or something related to the PR (currently I have only copilot license so it's hard to test against other providers). After asking, I got a response that the results were there, but probably didn't show on my end.

image

@zortos293

Copy link
Copy Markdown
ContributorAuthor

i'l look into this

Comment threadapps/server/src/provider/copilotRuntime.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@brrock

Copy link
Copy Markdown

conflicts btw

@zortos293

Copy link
Copy Markdown
ContributorAuthor

conflicts btw

imma look at it tmr 👍

@zortos293
zortos293force-pushed the feat/add-copilot-sdk-support branch from 01451c8 to 614bc62CompareMay 7, 2026 13:46
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Services/CopilotProvider.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/copilotRuntime.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment on lines +61 to +147
export function checkCopilotProviderStatus(input: {
readonly settings: CopilotSettings;
readonly cwd: string;
readonly environment?: NodeJS.ProcessEnv | undefined;
}): Effect.Effect<ServerProviderDraft> {
if (!input.settings.enabled) {
return Effect.succeed(makePendingCopilotProvider(input.settings));
}

const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const fallback = (cause: unknown, version: string | null = null) => {
const failure = formatCopilotProbeError({
cause,
settings: input.settings,
});
return buildServerProvider({
driver: PROVIDER,
presentation: COPILOT_PRESENTATION,
enabled: true,
checkedAt,
models: modelsFromCopilotSdk({
models: [],
customModels: input.settings.customModels,
}),
probe: {
installed: failure.installed,
version,
status: "error",
auth: { status: "unknown" },
message: failure.message,
},
});
};

return Effect.acquireUseRelease(
Effect.sync(() =>
createCopilotClient({
settings: input.settings,
cwd: input.cwd,
...(input.environment ? { env: input.environment } : {}),
logLevel: "error",
}),
),
(client) =>
Effect.tryPromise({
try: async () => {
await client.start();
const [status, authStatus, models] = await Promise.all([
client.getStatus(),
client.getAuthStatus(),
client.listModels(),
]);
const authSnapshot = authSnapshotFromCopilotSdk(authStatus);
const providerModels = modelsFromCopilotSdk({
models,
customModels: input.settings.customModels,
});
const hasBuiltInModels = models.length > 0;

return buildServerProvider({
driver: PROVIDER,
presentation: COPILOT_PRESENTATION,
enabled: true,
checkedAt,
models: providerModels,
probe: {
installed: true,
version: versionFromCopilotStatus(status),
status:
authSnapshot.status !== "ready"
? authSnapshot.status
: hasBuiltInModels
? "ready"
: "warning",
auth: authSnapshot.auth,
...(authSnapshot.message
? { message: authSnapshot.message }
: hasBuiltInModels
? {}
: { message: "Copilot did not report any available models for this account." }),
},
});
},
catch: toCopilotProbeError,
}).pipe(Effect.catch((cause) => Effect.succeed(fallback(cause)))),
(client) => Effect.promise(() => client.stop()).pipe(Effect.ignore({ log: true })),
);

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.

🟢 LowLayers/CopilotProvider.ts:61

The checkedAt timestamp is captured at function call time on line 70, so every execution of the returned Effect reports the same stale timestamp from when checkCopilotProviderStatus was first invoked. Since makeManagedServerProvider re-executes this Effect on each refresh interval, all subsequent checks incorrectly show the original timestamp rather than when each check actually occurred.

 if (!input.settings.enabled) {
return Effect.succeed(makePendingCopilotProvider(input.settings));
}
- const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const fallback = (cause: unknown, version: string | null = null) => {
+ const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const failure = formatCopilotProbeError({
cause,
settings: input.settings,
@@ -92,6 +92,7 @@
});
};
+ const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
return Effect.acquireUseRelease(
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/CopilotProvider.ts around lines 61-147:
The `checkedAt` timestamp is captured at function call time on line 70, so every execution of the returned Effect reports the same stale timestamp from when `checkCopilotProviderStatus` was first invoked. Since `makeManagedServerProvider` re-executes this Effect on each refresh interval, all subsequent checks incorrectly show the original timestamp rather than when each check actually occurred.
Evidence trail:
apps/server/src/provider/Layers/CopilotProvider.ts lines 61-70 (checkedAt captured eagerly outside Effect), lines 95-149 (Effect.acquireUseRelease returned, closing over checkedAt). apps/server/src/provider/Drivers/CopilotDriver.ts lines 92-95 (checkCopilotProviderStatus called once, Effect passed as checkProvider). apps/server/src/provider/makeManagedServerProvider.ts lines 127-130 (input.checkProvider re-executed in applySnapshotBase), lines 163-168 (forever loop re-running refreshSnapshot on each interval).

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
decision === "accept" || decision === "acceptForSession"
? { kind: "approved" }
: { kind: "denied-interactively-by-user" };
yield* Deferred.succeed(binding.deferred, 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.

🟢 LowLayers/CopilotAdapter.ts:2264

In respondToRequest, after resolving binding.deferred, the requestId is not removed from pendingPermissionBindings. If called twice with the same requestId, the second call silently succeeds instead of returning an error, which can mask client bugs or race conditions. Consider deleting the binding immediately after resolving it.

- yield* Deferred.succeed(binding.deferred, result);+ yield* Deferred.succeed(binding.deferred, result);+ context.pendingPermissionBindings.delete(requestId);
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/CopilotAdapter.ts around line 2264:
In `respondToRequest`, after resolving `binding.deferred`, the `requestId` is not removed from `pendingPermissionBindings`. If called twice with the same `requestId`, the second call silently succeeds instead of returning an error, which can mask client bugs or race conditions. Consider deleting the binding immediately after resolving it.
Evidence trail:
apps/server/src/provider/Layers/CopilotAdapter.ts lines 2248-2265 (respondToRequest function: no delete after Deferred.succeed at line 2264). Compare with lines 574-577 (settlePendingPermissionHandlers: calls .clear() after settling). Effect-TS Deferred.succeed returns Effect<boolean> — false if already done, no error thrown (see Effect-TS source/docs).

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts

@cursorcursorBot 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.

There are 2 total unresolved issues (including 1 from previous review).

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 d464878. Configure here.

},
});
sessions.delete(threadId);
});

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.

Session teardown doesn't await pending event chain

High Severity

stopSessionInternal disconnects the SDK session and stops the client without first awaiting context.eventChain. Events still being processed asynchronously in the chain (including turn.completed and item.completed events) can race against or fail after the SDK resources are torn down. This likely explains the reported bug where sessions stop without surfacing final results. In contrast, startSession correctly awaits the chain at line 2180 with yield* Effect.promise(() => context.eventChain).

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit d464878. Configure here.

@juliusmarmingeChatGPT Codex Connector

Copy link
Copy Markdown
Member

A newer Copilot provider implementation, #3076, is active against the current provider architecture. Keeping this older provider branch would duplicate that effort.

This was closed as part of a large repo-wide maintenance sweep. If you think it's still relevant, please reopen.

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

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@zortos293@brrock@rgembalik@akshaylingamaneni@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Add Github Copilot Provider - #2185

Closed
zortos293 wants to merge 19 commits into
pingdotgg:mainfrom
zortos293:feat/add-copilot-sdk-support
Closed

Add Github Copilot Provider#2185
zortos293 wants to merge 19 commits into
pingdotgg:mainfrom
zortos293:feat/add-copilot-sdk-support

Conversation

@zortos293

@zortos293zortos293 commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Added GitHub Copilot as a first-class provider using the official Copilot SDK.
  • Wired Copilot through the server runtime, including provider detection, auth/status reporting, model discovery, session startup/resume, turn sending, request handling, and event streaming.
  • Added Copilot-backed git text generation for commit messages, PR content, branch names, and thread titles.
  • Added Copilot to shared contracts/settings so it behaves like the existing providers for model selection, reasoning options, defaults, and persisted settings.
  • Updated the web app so Copilot appears in the provider/model dropdowns, settings panels, custom model management, composer state, and git writing model selection.

Why

T3 Code already supports multiple agent providers, but it did not have GitHub Copilot support. This change adds Copilot end to end in the same shape as the other providers instead of treating it as a one-off integration.

That keeps the UX and server behavior consistent: users can configure Copilot in settings, select Copilot models from the same pickers, use it for git text generation, and rely on the same persistence and runtime flows that already exist for other providers. Using the official SDK also keeps the integration aligned with GitHub’s supported protocol and model discovery/auth flows.

UI Changes

  • Added Copilot to the provider picker.
  • Added a Copilot settings section with binary path, server URL, and custom model configuration.
  • Added Copilot to the git writing model picker and traits handling.
imageimageimage

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
Adds a new first-class provider with complex session/event handling and introduces the @github/copilot-sdk runtime dependency, so failures/auth/model discovery issues could impact provider startup and chat/runtime flows.

Overview
Adds GitHub Copilot as a first-class provider end-to-end. The server now includes a new CopilotDriver registered in BUILT_IN_DRIVERS, a Copilot SDK runtime wrapper (copilotRuntime.ts) for auth/status/model probing, and a full CopilotAdapter that starts/resumes sessions, translates Copilot SDK events into provider runtime events (turn lifecycle, streaming deltas, tool execution, permissions/user-input), and hardens error/cleanup behavior.

Adds Copilot-backed git text generation (CopilotTextGeneration.ts) for commit messages/PR content/branch names/thread titles with pooled SDK clients and idle shutdown. Updates shared contracts/settings to include CopilotSettings, provider defaults/aliases/display names, and extends runtime raw event sources; the web app surfaces Copilot in provider pickers/settings/icons and removes the “coming soon” placeholder when a real Copilot instance exists. Includes extensive new tests covering adapter behavior, provider probe errors, and text-generation client reuse.

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

Note

Add GitHub Copilot as a built-in provider for chat and text generation

  • Adds a full CopilotDriver and CopilotAdapter that integrate with @github/copilot-sdk to support chat sessions, permission handling, and runtime event translation.
  • Adds makeCopilotTextGeneration to generate commit messages, PR content, branch names, and thread titles via Copilot models using structured JSON outputs and a shared client pool.
  • Adds checkCopilotProviderStatus to periodically probe the Copilot SDK for auth state, available models, and version info.
  • Updates the settings UI, model picker, and provider icon mapping to treat Copilot as a first-class provider, removing the "Coming soon" placeholder.
  • Risk: introduces a new runtime dependency @github/copilot-sdk@^0.2.2 and spawns external CLI processes; probe failures are surfaced as provider errors rather than silently ignored.

Macroscope summarized d464878.

@coderabbitai

coderabbitaiBot commented Apr 18, 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: 6417f1bb-b9f1-4115-890c-8459048c5942

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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@zortos293zortos293 mentioned this pull request Apr 18, 2026
4 tasks
@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Apr 18, 2026
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@macroscopeapp

macroscopeappBot commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR adds a new GitHub Copilot provider integration with ~4500 lines of new code, external SDK dependencies, and complex session management. New feature additions with this scope require human review. Additionally, an unresolved high-severity comment identifies a potential race condition in session teardown that could cause lost events.

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

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
@zortos293

Copy link
Copy Markdown
ContributorAuthor

This needs to be tested if the desktop app is working fine with Copilot

@brrock

Copy link
Copy Markdown

Because julius plumbed in ACP can't we just use ACP, I guess the copilot sdk has copilot specific stuff

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@zortos293
zortos293force-pushed the feat/add-copilot-sdk-support branch from f02de1a to 290befbCompareApril 20, 2026 08:04
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/git/Layers/CopilotTextGeneration.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/git/Layers/CopilotTextGeneration.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/textGeneration/CopilotTextGeneration.ts
fav-devs added a commit to fav-devs/fdcode that referenced this pull request Apr 21, 2026
Comment threadpackages/contracts/src/settings.ts Outdated
@@ -88,6 +92,13 @@ export const CodexSettings = Schema.Struct({
customModels: Schema.Array(Schema.String).pipe(Schema.withDecodingDefault(Effect.succeed([]))),
});
export type CodexSettings = typeof CodexSettings.Type;
export const CopilotSettings = Schema.Struct({
enabled: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),

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.

Copilot enabled by default causes unwanted CLI spawns

Medium Severity

CopilotSettings defaults enabled to true, meaning every existing user who upgrades will have Copilot auto-enabled. Combined with the 1-hour COPILOT_REFRESH_INTERVAL, this will repeatedly spawn the bundled Copilot CLI binary, attempt auth checks, and fail for the majority of users who have no GitHub Copilot subscription or auth configured. Unlike Codex/Claude/OpenCode which are the app's core providers, Copilot is a newly added optional integration — defaulting to false (like Cursor does) would avoid unnecessary process spawns and error noise on every refresh cycle.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit c9b851217439d17fbbe96d68b3f89077440f3446. Configure here.

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
@rgembalik

Copy link
Copy Markdown

I am not a maintainer, but I did couple of local tests. There were few cases where session stopped without showing final results message. I don't know if this is upstream bug, or something related to the PR (currently I have only copilot license so it's hard to test against other providers). After asking, I got a response that the results were there, but probably didn't show on my end.

image

@zortos293

Copy link
Copy Markdown
ContributorAuthor

i'l look into this

Comment threadapps/server/src/provider/copilotRuntime.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
@brrock

Copy link
Copy Markdown

conflicts btw

@zortos293

Copy link
Copy Markdown
ContributorAuthor

conflicts btw

imma look at it tmr 👍

@zortos293
zortos293force-pushed the feat/add-copilot-sdk-support branch from 01451c8 to 614bc62CompareMay 7, 2026 13:46
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Services/CopilotProvider.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/copilotRuntime.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts Outdated
Comment on lines +61 to +147
export function checkCopilotProviderStatus(input: {
readonly settings: CopilotSettings;
readonly cwd: string;
readonly environment?: NodeJS.ProcessEnv | undefined;
}): Effect.Effect<ServerProviderDraft> {
if (!input.settings.enabled) {
return Effect.succeed(makePendingCopilotProvider(input.settings));
}

const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const fallback = (cause: unknown, version: string | null = null) => {
const failure = formatCopilotProbeError({
cause,
settings: input.settings,
});
return buildServerProvider({
driver: PROVIDER,
presentation: COPILOT_PRESENTATION,
enabled: true,
checkedAt,
models: modelsFromCopilotSdk({
models: [],
customModels: input.settings.customModels,
}),
probe: {
installed: failure.installed,
version,
status: "error",
auth: { status: "unknown" },
message: failure.message,
},
});
};

return Effect.acquireUseRelease(
Effect.sync(() =>
createCopilotClient({
settings: input.settings,
cwd: input.cwd,
...(input.environment ? { env: input.environment } : {}),
logLevel: "error",
}),
),
(client) =>
Effect.tryPromise({
try: async () => {
await client.start();
const [status, authStatus, models] = await Promise.all([
client.getStatus(),
client.getAuthStatus(),
client.listModels(),
]);
const authSnapshot = authSnapshotFromCopilotSdk(authStatus);
const providerModels = modelsFromCopilotSdk({
models,
customModels: input.settings.customModels,
});
const hasBuiltInModels = models.length > 0;

return buildServerProvider({
driver: PROVIDER,
presentation: COPILOT_PRESENTATION,
enabled: true,
checkedAt,
models: providerModels,
probe: {
installed: true,
version: versionFromCopilotStatus(status),
status:
authSnapshot.status !== "ready"
? authSnapshot.status
: hasBuiltInModels
? "ready"
: "warning",
auth: authSnapshot.auth,
...(authSnapshot.message
? { message: authSnapshot.message }
: hasBuiltInModels
? {}
: { message: "Copilot did not report any available models for this account." }),
},
});
},
catch: toCopilotProbeError,
}).pipe(Effect.catch((cause) => Effect.succeed(fallback(cause)))),
(client) => Effect.promise(() => client.stop()).pipe(Effect.ignore({ log: true })),
);

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.

🟢 LowLayers/CopilotProvider.ts:61

The checkedAt timestamp is captured at function call time on line 70, so every execution of the returned Effect reports the same stale timestamp from when checkCopilotProviderStatus was first invoked. Since makeManagedServerProvider re-executes this Effect on each refresh interval, all subsequent checks incorrectly show the original timestamp rather than when each check actually occurred.

 if (!input.settings.enabled) {
return Effect.succeed(makePendingCopilotProvider(input.settings));
}
- const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const fallback = (cause: unknown, version: string | null = null) => {
+ const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
const failure = formatCopilotProbeError({
cause,
settings: input.settings,
@@ -92,6 +92,7 @@
});
};
+ const checkedAt = DateTime.formatIso(DateTime.nowUnsafe());
return Effect.acquireUseRelease(
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/CopilotProvider.ts around lines 61-147:
The `checkedAt` timestamp is captured at function call time on line 70, so every execution of the returned Effect reports the same stale timestamp from when `checkCopilotProviderStatus` was first invoked. Since `makeManagedServerProvider` re-executes this Effect on each refresh interval, all subsequent checks incorrectly show the original timestamp rather than when each check actually occurred.
Evidence trail:
apps/server/src/provider/Layers/CopilotProvider.ts lines 61-70 (checkedAt captured eagerly outside Effect), lines 95-149 (Effect.acquireUseRelease returned, closing over checkedAt). apps/server/src/provider/Drivers/CopilotDriver.ts lines 92-95 (checkCopilotProviderStatus called once, Effect passed as checkProvider). apps/server/src/provider/makeManagedServerProvider.ts lines 127-130 (input.checkProvider re-executed in applySnapshotBase), lines 163-168 (forever loop re-running refreshSnapshot on each interval).

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts
decision === "accept" || decision === "acceptForSession"
? { kind: "approved" }
: { kind: "denied-interactively-by-user" };
yield* Deferred.succeed(binding.deferred, 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.

🟢 LowLayers/CopilotAdapter.ts:2264

In respondToRequest, after resolving binding.deferred, the requestId is not removed from pendingPermissionBindings. If called twice with the same requestId, the second call silently succeeds instead of returning an error, which can mask client bugs or race conditions. Consider deleting the binding immediately after resolving it.

- yield* Deferred.succeed(binding.deferred, result);+ yield* Deferred.succeed(binding.deferred, result);+ context.pendingPermissionBindings.delete(requestId);
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/CopilotAdapter.ts around line 2264:
In `respondToRequest`, after resolving `binding.deferred`, the `requestId` is not removed from `pendingPermissionBindings`. If called twice with the same `requestId`, the second call silently succeeds instead of returning an error, which can mask client bugs or race conditions. Consider deleting the binding immediately after resolving it.
Evidence trail:
apps/server/src/provider/Layers/CopilotAdapter.ts lines 2248-2265 (respondToRequest function: no delete after Deferred.succeed at line 2264). Compare with lines 574-577 (settlePendingPermissionHandlers: calls .clear() after settling). Effect-TS Deferred.succeed returns Effect<boolean> — false if already done, no error thrown (see Effect-TS source/docs).

Comment threadapps/server/src/provider/Layers/CopilotAdapter.ts

@cursorcursorBot 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.

There are 2 total unresolved issues (including 1 from previous review).

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 d464878. Configure here.

},
});
sessions.delete(threadId);
});

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.

Session teardown doesn't await pending event chain

High Severity

stopSessionInternal disconnects the SDK session and stops the client without first awaiting context.eventChain. Events still being processed asynchronously in the chain (including turn.completed and item.completed events) can race against or fail after the SDK resources are torn down. This likely explains the reported bug where sessions stop without surfacing final results. In contrast, startSession correctly awaits the chain at line 2180 with yield* Effect.promise(() => context.eventChain).

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit d464878. Configure here.

@juliusmarmingeChatGPT Codex Connector

Copy link
Copy Markdown
Member

A newer Copilot provider implementation, #3076, is active against the current provider architecture. Keeping this older provider branch would duplicate that effort.

This was closed as part of a large repo-wide maintenance sweep. If you think it's still relevant, please reopen.

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

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@zortos293@brrock@rgembalik@akshaylingamaneni@juliusmarminge