-
Notifications
You must be signed in to change notification settings - Fork 495
feat: Add GitHub Copilot SDK provider integration #661
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Adds comprehensive GitHub Copilot SDK provider support including: - CopilotProvider class with CLI detection and OAuth authentication check - Copilot models definition with GPT-4o, Claude, and o1/o3 series models - Settings UI integration with provider tab, model configuration, and navigation - Onboarding flow integration with Copilot setup step - Model selector integration for all phase-specific model dropdowns - Persistence of enabled models and default model settings via API sync - Server route for Copilot CLI status endpoint https://claude.ai/code/session_01D26w7ZyEzP4H6Dor3ttk9d
- Update all references from "GitHub Copilot SDK" to "GitHub Copilot CLI" - Change install command from @github/copilot-sdk to @github/copilot - Update CopilotIcon to use official GitHub Octocat logo - Update error codes and comments throughout codebase Co-Authored-By: Claude <noreply@anthropic.com>
- Update COPILOT_MODEL_MAP with correct models from CLI (claude-sonnet-4.5, claude-haiku-4.5, claude-opus-4.5, claude-sonnet-4, gpt-5.x series, gpt-4.1, gemini-3-pro-preview) - Change default Copilot model to copilot-claude-sonnet-4.5 - Add model caching methods to CopilotProvider (hasCachedModels, clearModelCache, refreshModels) - Add API routes for dynamic model discovery: - GET /api/setup/copilot/models - POST /api/setup/copilot/models/refresh - POST /api/setup/copilot/cache/clear Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Install @github/copilot-sdk package for proper SDK integration - Rewrite CopilotProvider to use SDK's CopilotClient API - Use client.createSession() for session management - Handle SDK events (assistant.message, tool.execution_*, session.idle) - Auto-approve permissions for autonomous agent operation - Remove incorrect CLI flags (--mode, --output-format) - Update default model to claude-sonnet-4.5 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Import isCopilotModel and isGeminiModel from types - Add explicit checks for copilot- and gemini- prefixed models - Pass through Copilot/Gemini models unchanged to their providers - Update resolver documentation to list all supported providers Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create CopilotClient per execution with correct cwd from options.cwd - This ensures the CLI operates in the correct project directory, not the server's current directory - Skip assistant.message_delta events (they create excessive noise) - Only yield the final assistant.message event which has complete content - Clean up client on completion and error paths Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use sendAndWait() instead of manual event polling for more reliable execution - Disable streaming (streaming: false) to simplify response handling - Increase timeout to 10 minutes for agentic operations - Still capture tool execution events for UI display - Add more debug logging for troubleshooting - This should fix the "invalid_request_body" error on subsequent calls Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot's bare model IDs legitimately contain prefixes like claude-, gemini-, gpt- because those are the actual model names from the Copilot CLI (e.g., claude-sonnet-4.5, gemini-3-pro-preview, gpt-5.1). The generic validateBareModelId function was incorrectly rejecting these valid model IDs. Now we only check that the copilot- prefix has been stripped by the ProviderFactory. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Switch back to streaming mode (streaming: true) for real-time events - Use async queue pattern to bridge SDK callbacks to async generator - Events are now yielded as they happen, not batched at the end - Tool calls (Read, Write, Edit, Bash, TodoWrite, etc.) show in real-time - Better progress visibility during agentic operations Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tool name mapping additions: - view → Read (Copilot's file viewing tool) - create_file → Write - replace, patch → Edit - run_shell_command, terminal → Bash - search_file_content → Grep - list_directory → Ls - google_web_search → WebSearch - report_intent → ReportIntent (Copilot-specific planning) - think, plan → Think, Plan Input normalization improvements: - Read/Write/Edit: Map file, filename, filePath → file_path - Bash: Map cmd, script → command - Grep: Map query, search, regex → pattern Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. 📝 WalkthroughWalkthroughAdds GitHub Copilot support across server, types, and UI: new CopilotProvider with SDK/CLI integration, setup routes and connection service, tool normalization helpers, Copilot model types and resolver passthrough, extensive UI/settings/store/query/client wiring, reusable BaseModelConfiguration, and unit tests. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as Client (UI)
participant Server as App Server
participant Provider as CopilotProvider
participant SDK as GitHub Copilot SDK/CLI
UI->>Server: GET /api/setup/copilot-status
Server->>Provider: getCliStatus()
Provider->>SDK: detect CLI, check auth (gh/token)
SDK-->>Provider: status/auth response
Provider-->>Server: aggregated status
Server-->>UI: JSON status
UI->>Server: GET /api/setup/copilot/models (refresh=true)
Server->>Provider: refreshModels()
Provider->>SDK: runtime model discovery
SDK-->>Provider: runtime models
Provider-->>Server: combined model list
Server-->>UI: models response
UI->>Server: start session / tool call
Server->>Provider: startSession(call)
Provider->>SDK: create client, start streaming session
SDK-->>Provider: streaming events (assistant.message, tool.use, tool_result)
Provider-->>Server: translate events -> ProviderMessage stream
Server-->>UI: forward streaming messages
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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. Comment |
Summary of ChangesHello @stefandevo, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a significant new capability by integrating GitHub Copilot as a first-class AI provider. This allows the application to tap into the diverse range of AI models offered through a Copilot subscription, including various Claude, GPT, and Gemini models. The integration focuses on providing a seamless and responsive experience, with features like real-time feedback on AI actions and intelligent handling of underlying tool differences. This expands the application's AI model ecosystem, offering users more choice and flexibility in their AI-powered workflows. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces a comprehensive integration for the GitHub Copilot provider, leveraging the official SDK. The implementation is well-structured, covering everything from provider registration and CLI detection to dynamic model discovery and extensive UI updates. The tool name and input normalization is a thoughtful addition for ensuring a consistent user experience.
However, I've identified a critical security vulnerability in the automatic approval of all tool execution permissions, which could allow for arbitrary code execution. This must be addressed before this feature can be safely merged.
The @electron/node-gyp dependency was resolved with a git+ssh URL which fails in CI environments without SSH keys. Convert to HTTPS. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/server/src/providers/provider-factory.ts (1)
251-252: Missing 'copilot' in provider prefix regex patterns.The model string matching logic for vision support checking doesn't include 'copilot' in the provider prefix patterns. This could prevent proper vision capability detection for Copilot models.
Suggested fix
- model.modelString === modelId.replace(/^(claude|cursor|codex|gemini)-/, '') || - model.modelString === modelId.replace(/-(claude|cursor|codex|gemini)$/, '') + model.modelString === modelId.replace(/^(claude|cursor|codex|gemini|copilot)-/, '') || + model.modelString === modelId.replace(/-(claude|cursor|codex|gemini|copilot)$/, '')apps/ui/src/components/ui/provider-icon.tsx (1)
474-534: Copilot models may render as OpenAI icons for provider/model IDs.
Line 496 returnsopenaibefore Line 561 is reached, so a dynamic ID likegithub-copilot/gpt-4owill show the OpenAI icon instead of Copilot. Consider checking the provider name for “copilot” earlier in the dynamic-provider branch.🔧 Suggested tweak
if (providerName && modelName) { + if (providerName.includes('copilot')) { + return 'copilot'; + } // Check model name for known patterns if (modelName.includes('glm')) { return 'glm'; }Also applies to: 561-564
🤖 Fix all issues with AI agents
In `@apps/server/src/providers/copilot-provider.ts`:
- Around line 819-834: On failure to fetch models in the try/catch around
execSync, clear the cached runtimeModels so stale entries aren't returned:
inside the catch block of the function that calls this.cliPath (the block that
currently logs `Failed to fetch runtime models: ${error}` and returns []),
assign this.runtimeModels = [] (or null if your code expects null) before
returning, and keep the existing log; this ensures the property referenced as
runtimeModels is reset when `models list` fails.
- Around line 296-306: CopilotProvider advertises vision support but
extractPromptText silently drops non-text parts; update CopilotProvider to
either stop claiming vision support or preserve/handle non-text content by
mirroring ClaudeProvider's approach: modify supportsFeature('vision') in
CopilotProvider to return false if images aren't supported, or update
extractPromptText (and the execute flow) to detect non-text prompt elements,
log/throw a clear error or forward image data to the vision handler so callers
aren't silently losing data; reference CopilotProvider, extractPromptText,
supportsFeature('vision'), and use ClaudeProvider handling as the implementation
pattern.
In
`@apps/ui/src/components/views/settings-view/cli-status/copilot-cli-status.tsx`:
- Around line 9-21: Remove the local CopilotAuthMethod and CopilotAuthStatus
type definitions from copilot-cli-status.tsx and import them from the shared
package instead (import { CopilotAuthStatus, CopilotAuthMethod } from
'@automaker/types'). Update any local usages in the file/component to use the
imported types and account for the additional optional authType?: string field
present on the shared CopilotAuthStatus type (e.g., when reading or forwarding
status objects), ensuring no duplicate/local type declarations remain.
In `@apps/ui/src/store/app-store.ts`:
- Around line 1532-1534: Update the misleading comment for copilotDefaultModel
so it matches the actual value of DEFAULT_COPILOT_MODEL; locate the
copilotDefaultModel assignment (and nearby enabledCopilotModels and
disabledProviders) and change the comment from “Default to GPT-4o” to a comment
that reflects the current default (e.g., “Default to copilot-claude-sonnet-4.5”
or a neutral “Default Copilot model”). Ensure the comment references
DEFAULT_COPILOT_MODEL so it won’t diverge from the actual constant.
In `@libs/types/src/copilot-models.ts`:
- Around line 31-133: COPILOT_MODEL_MAP currently contains outdated model
entries and incorrect uniform capability flags; update the COPILOT_MODEL_MAP
object so each entry reflects the current GitHub Copilot SDK model IDs, labels,
descriptions, and accurate per-model flags (supportsVision, supportsTools,
contextWindow) instead of assuming all true. Use the `@github/copilot-sdk` runtime
discovery (listModels()) and the SDK docs to determine which models exist now
and what capabilities they support, remove deprecated IDs (e.g., any o3/gpt-o3
entries), correct names like Claude Sonnet 4.5/GPT-5 variants, and adjust
contextWindow values; update entries inside the COPILOT_MODEL_MAP constant and
ensure the map still satisfies Record<string, CopilotModelConfig> so
copilot-provider.ts prefix-stripping logic continues to work.
♻️ Duplicate comments (1)
apps/server/src/providers/copilot-provider.ts (1)
571-580: Permission requests are auto-approved.
This is a security risk for tool execution policies and has been flagged previously; please add a denylist or interactive gating.Copilot SDK onPermissionRequest recommended handling and allowed return values
🧹 Nitpick comments (7)
apps/ui/src/components/views/settings-view/cli-status/copilot-cli-status.tsx (1)
23-32: Consider handling the'none'case explicitly for clarity.The
'none'value ofCopilotAuthMethodfalls through to the default case, returning the literal string'none'. While the caller at line 171 guards against this, explicit handling would improve readability and ensure exhaustive type coverage.♻️ Proposed refactor
function getAuthMethodLabel(method: CopilotAuthMethod): string { switch (method) { case 'oauth': return 'GitHub OAuth'; case 'cli': return 'Copilot CLI'; + case 'none': + return 'None'; default: - return method || 'Unknown'; + return 'Unknown'; } }apps/server/src/routes/setup/routes/copilot-status.ts (1)
36-49: Disconnected state returnsinstalled: truewithout verification.When the disconnected marker file exists, the handler returns
installed: truewithout actually verifying if Copilot is installed. This could be misleading to the UI if the user has both disconnected and uninstalled Copilot.Consider either:
- Still checking installation status even when disconnected, or
- Returning a more explicit
disconnected: truefield in the responseapps/ui/src/components/views/settings-view/providers/provider-tabs.tsx (1)
25-25: Consider responsive behavior with 6 columns.The grid now has 6 columns to accommodate all providers. On narrower viewports, this may cause the tab labels to truncate or wrap.
Consider verifying the tabs display correctly at various viewport widths, or whether a responsive grid (
grid-cols-3 md:grid-cols-6) might be more appropriate.apps/ui/src/components/views/board-view/shared/model-constants.ts (1)
145-157: Avoid hardcodinghasThinking: falsefor all Copilot models.
Some Copilot-backed models may support reasoning; hardcoding disables UI affordances and can misrepresent capabilities. Consider adding a per-modelsupportsThinkingflag toCOPILOT_MODEL_MAPand mapping it here. Based on learnings, ...apps/ui/src/components/views/settings-view/providers/copilot-settings-tab.tsx (1)
66-79: Unnecessary async error handling pattern for synchronous store action.
setCopilotDefaultModelis a synchronous Zustand action. Wrapping it in try/catch withsetIsSavingstate creates misleading UX - the "saving" state will be set and immediately cleared in the same render cycle since there's no actual async operation. The catch block will never execute for store errors (Zustand doesn't throw onsetState).If this pattern is intentional to match other provider tabs, consider adding an actual async operation (e.g., validating with the server) or simplifying to remove the
isSavingstate.Simplified alternative if no server validation needed
const handleDefaultModelChange = useCallback( (model: CopilotModelId) => { - setIsSaving(true); - try { - setCopilotDefaultModel(model); - toast.success('Default model updated'); - } catch { - toast.error('Failed to update default model'); - } finally { - setIsSaving(false); - } + setCopilotDefaultModel(model); + toast.success('Default model updated'); }, [setCopilotDefaultModel] );apps/server/src/routes/setup/routes/copilot-models.ts (1)
15-15: Unused logger variable.The
loggeris created but never used in this file. OnlylogErrorfromcommon.jsis used for error logging. Either remove the unused logger or use it for debug/info logging in the handlers.Option 1: Remove unused logger
-import { createLogger } from '@automaker/utils'; - -const logger = createLogger('CopilotModelsRoute');Option 2: Use logger for info logging
export function createGetCopilotModelsHandler() { return async (req: Request, res: Response): Promise<void> => { try { const provider = getProvider(); const forceRefresh = req.query.refresh === 'true'; + logger.debug('Getting Copilot models', { forceRefresh }); let models: ModelDefinition[]; // ... rest of handlerlibs/types/src/copilot-models.ts (1)
155-174: Consolidate duplicate type definitions to avoid drift.
CopilotCliStatusandCopilotAuthStatusare also defined in:
apps/ui/src/store/setup-store.ts(lines 82-95)apps/ui/src/components/views/settings-view/cli-status/copilot-cli-status.tsx(lines 13-20)The UI definitions include additional fields (
installCommand,loginCommandinCopilotCliStatus) that this file lacks, andCopilotAuthStatus.methoduses different types ('oauth' | 'cli' | 'none'here vsCopilotAuthMethodin the UI).Per the coding guidelines, always import shared types from
@automaker/*packages. The UI components should import these types from@automaker/typesrather than defining their own. Ensure the canonical definition here includes all required fields.♻️ Suggested consolidated CopilotCliStatus
export interface CopilotCliStatus { installed: boolean; version?: string; path?: string; auth?: CopilotAuthStatus; + installCommand?: string; + loginCommand?: string; error?: string; }
apps/ui/src/components/views/settings-view/cli-status/copilot-cli-status.tsx
Outdated
Show resolved
Hide resolved
| export const COPILOT_MODEL_MAP = { | ||
| // Claude models (Anthropic via GitHub Copilot) | ||
| 'copilot-claude-sonnet-4.5': { | ||
| label: 'Claude Sonnet 4.5', | ||
| description: 'Anthropic Claude Sonnet 4.5 via GitHub Copilot.', | ||
| supportsVision: true, | ||
| supportsTools: true, | ||
| contextWindow: 200000, | ||
| }, | ||
| 'copilot-claude-haiku-4.5': { | ||
| label: 'Claude Haiku 4.5', | ||
| description: 'Fast and efficient Claude Haiku 4.5 via GitHub Copilot.', | ||
| supportsVision: true, | ||
| supportsTools: true, | ||
| contextWindow: 200000, | ||
| }, | ||
| 'copilot-claude-opus-4.5': { | ||
| label: 'Claude Opus 4.5', | ||
| description: 'Most capable Claude Opus 4.5 via GitHub Copilot.', | ||
| supportsVision: true, | ||
| supportsTools: true, | ||
| contextWindow: 200000, | ||
| }, | ||
| 'copilot-claude-sonnet-4': { | ||
| label: 'Claude Sonnet 4', | ||
| description: 'Anthropic Claude Sonnet 4 via GitHub Copilot.', | ||
| supportsVision: true, | ||
| supportsTools: true, | ||
| contextWindow: 200000, | ||
| }, | ||
| // GPT-5 series (OpenAI via GitHub Copilot) | ||
| 'copilot-gpt-5.2-codex': { | ||
| label: 'GPT-5.2 Codex', | ||
| description: 'OpenAI GPT-5.2 Codex for advanced coding tasks.', | ||
| supportsVision: true, | ||
| supportsTools: true, | ||
| contextWindow: 128000, | ||
| }, | ||
| 'copilot-gpt-5.1-codex-max': { | ||
| label: 'GPT-5.1 Codex Max', | ||
| description: 'Maximum capability GPT-5.1 Codex model.', | ||
| supportsVision: true, | ||
| supportsTools: true, | ||
| contextWindow: 128000, | ||
| }, | ||
| 'copilot-gpt-5.1-codex': { | ||
| label: 'GPT-5.1 Codex', | ||
| description: 'OpenAI GPT-5.1 Codex for coding tasks.', | ||
| supportsVision: true, | ||
| supportsTools: true, | ||
| contextWindow: 128000, | ||
| }, | ||
| 'copilot-gpt-5.2': { | ||
| label: 'GPT-5.2', | ||
| description: 'Latest OpenAI GPT-5.2 model.', | ||
| supportsVision: true, | ||
| supportsTools: true, | ||
| contextWindow: 128000, | ||
| }, | ||
| 'copilot-gpt-5.1': { | ||
| label: 'GPT-5.1', | ||
| description: 'OpenAI GPT-5.1 model.', | ||
| supportsVision: true, | ||
| supportsTools: true, | ||
| contextWindow: 128000, | ||
| }, | ||
| 'copilot-gpt-5': { | ||
| label: 'GPT-5', | ||
| description: 'OpenAI GPT-5 base model.', | ||
| supportsVision: true, | ||
| supportsTools: true, | ||
| contextWindow: 128000, | ||
| }, | ||
| 'copilot-gpt-5.1-codex-mini': { | ||
| label: 'GPT-5.1 Codex Mini', | ||
| description: 'Fast and efficient GPT-5.1 Codex Mini.', | ||
| supportsVision: true, | ||
| supportsTools: true, | ||
| contextWindow: 128000, | ||
| }, | ||
| 'copilot-gpt-5-mini': { | ||
| label: 'GPT-5 Mini', | ||
| description: 'Lightweight GPT-5 Mini model.', | ||
| supportsVision: true, | ||
| supportsTools: true, | ||
| contextWindow: 128000, | ||
| }, | ||
| 'copilot-gpt-4.1': { | ||
| label: 'GPT-4.1', | ||
| description: 'OpenAI GPT-4.1 model.', | ||
| supportsVision: true, | ||
| supportsTools: true, | ||
| contextWindow: 128000, | ||
| }, | ||
| // Gemini models (Google via GitHub Copilot) | ||
| 'copilot-gemini-3-pro-preview': { | ||
| label: 'Gemini 3 Pro Preview', | ||
| description: 'Google Gemini 3 Pro Preview via GitHub Copilot.', | ||
| supportsVision: true, | ||
| supportsTools: true, | ||
| contextWindow: 1000000, | ||
| }, | ||
| } as const satisfies Record<string, CopilotModelConfig>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
GitHub Copilot SDK available models @github/copilot-sdk 2025
💡 Result:
@github/copilot-sdk doesn’t have a fixed, hardcoded model list—it supports whatever models are available via the Copilot CLI, and it exposes an API to enumerate the currently-available models at runtime (so the exact set depends on your Copilot plan/policies and the date). [1]
If you specifically mean “which Copilot chat/agent models were available during 2025”, GitHub announced these as generally available on April 4, 2025: Claude 3.7 Sonnet, Claude 3.5 Sonnet, OpenAI o3-mini, and Gemini 2.0 Flash. [2]
Later, GitHub deprecated several models on October 23, 2025 (including Claude Sonnet 3.7, Claude Sonnet 3.7 Thinking, Claude Opus 4, GPT o3, o1 mini, o3 mini, o4 mini, Gemini 2.0 Flash) and provided suggested replacements (for example Claude Sonnet 4.5, Claude Opus 4.1, GPT-5 / GPT-5 mini, Gemini 2.5 Pro). [3]
Sources: [1] [2] [3]
🏁 Script executed:
# First, let's examine the actual file and understand the prefix stripping logic
head -30 libs/types/src/copilot-models.tsRepository: AutoMaker-Org/automaker
Length of output: 876
🏁 Script executed:
# Search for how COPILOT_MODEL_MAP is used in the codebase
rg "COPILOT_MODEL_MAP" -A 3 -B 3Repository: AutoMaker-Org/automaker
Length of output: 7080
🏁 Script executed:
# Check for any model resolution functions or capabilities helpers
rg "copilot-" -t ts -t tsx | head -30Repository: AutoMaker-Org/automaker
Length of output: 94
🏁 Script executed:
# Look for any documentation or type definitions related to CopilotModelConfig
rg "CopilotModelConfig" -A 5 -B 5Repository: AutoMaker-Org/automaker
Length of output: 7383
🏁 Script executed:
# Check lines around 26-27 for prefix stripping logic
sed -n '20,35p' libs/types/src/copilot-models.tsRepository: AutoMaker-Org/automaker
Length of output: 621
🏁 Script executed:
# Search for prefix stripping in copilot-provider
rg "copilot-" apps/server/src/providers/copilot-provider.ts -B 3 -A 3 | head -50Repository: AutoMaker-Org/automaker
Length of output: 2027
🏁 Script executed:
# Check if there's runtime model discovery integration
rg "listModels\|discover.*model" apps/server/src/providers/ -iRepository: AutoMaker-Org/automaker
Length of output: 49
Update model definitions to match current GitHub Copilot SDK availability and add per-model capability flags.
The hardcoded model list is outdated. According to GitHub's latest updates (October 2025), several models were deprecated and replaced—e.g., Claude Sonnet 3.7 was replaced with Claude Sonnet 4.5, and GPT o3 series were replaced with GPT-5 and o3-mini. Additionally, the list includes model IDs like "Claude Haiku 4.5" and "GPT-5.1 Codex" that don't align with currently available Copilot models.
More critically, all models incorrectly assume identical capabilities (supportsVision: true, supportsTools: true uniformly). Model capabilities vary—not all Copilot models support vision or tool use. Define capabilities per-model rather than assuming uniformity across all entries. The prefix stripping logic (lines 26–27 and verified in copilot-provider.ts) works correctly and does not need changes.
Reference the @github/copilot-sdk documentation and runtime discovery via the CLI's listModels() method to determine accurate, current model support and capabilities.
🤖 Prompt for AI Agents
In `@libs/types/src/copilot-models.ts` around lines 31 - 133, COPILOT_MODEL_MAP
currently contains outdated model entries and incorrect uniform capability
flags; update the COPILOT_MODEL_MAP object so each entry reflects the current
GitHub Copilot SDK model IDs, labels, descriptions, and accurate per-model flags
(supportsVision, supportsTools, contextWindow) instead of assuming all true. Use
the `@github/copilot-sdk` runtime discovery (listModels()) and the SDK docs to
determine which models exist now and what capabilities they support, remove
deprecated IDs (e.g., any o3/gpt-o3 entries), correct names like Claude Sonnet
4.5/GPT-5 variants, and adjust contextWindow values; update entries inside the
COPILOT_MODEL_MAP constant and ensure the map still satisfies Record<string,
CopilotModelConfig> so copilot-provider.ts prefix-stripping logic continues to
work.
- Add guard for non-text prompts (vision not yet supported) - Clear runtime model cache on fetch failure - Fix race condition in async queue error handling - Import CopilotAuthStatus from shared types - Fix comment mismatch for default model constant - Add auth-copilot and deauth-copilot routes - Extract shared tool normalization utilities - Create base model configuration UI component - Add comprehensive unit tests for CopilotProvider - Replace magic strings with constants - Add debug logging for cleanup errors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🤖 Fix all issues with AI agents
In `@apps/server/tests/unit/providers/copilot-provider.test.ts`:
- Around line 121-134: The test's execSync mock used by the checkAuth test
doesn't handle the GH CLI version check, causing ensureCliDetected() to fail;
update the vi.mocked(execSync) implementation in
apps/server/tests/unit/providers/copilot-provider.test.ts (the test for
provider.checkAuth) to also return a valid version string when the command
includes '--version' (e.g., 'gh version x.y.z') or fall back to the original
execSync for other commands, so ensureCliDetected() succeeds and the existing
auth assertions remain valid.
- Around line 152-170: The test's execSync mock doesn't handle CLI version
checks invoked by ensureCliDetected(), causing provider.checkAuth() to return
unauthenticated before the GITHUB_TOKEN branch; update the mocked execSync in
the 'should detect GITHUB_TOKEN environment variable' test to return a non-error
version string when cmd includes '--version' (in addition to the existing
branches for 'gh auth status' and 'copilot auth status'), so ensureCliDetected()
succeeds and the checkAuth() path that reads process.env.GITHUB_TOKEN is
exercised.
- Around line 173-190: The failing test is due to an incomplete execSync mock
and the provider being instantiated before the mock is set, so
detectInstallation/isInstalled runs against the real environment; fix by either
(A) move or set up the vi.mocked(execSync) stub to handle all expected commands
(including the initial CLI check used in the provider constructor) before
constructing the provider instance, or (B) create a fresh CopilotProvider (or
the class under test) inside the test after configuring the execSync mock so
detectInstallation and isInstalled run with the mocked responses; reference
detectInstallation, isInstalled, the provider instance and execSync when making
the change.
In
`@apps/ui/src/components/views/settings-view/cli-status/copilot-cli-status.tsx`:
- Around line 103-117: The refresh Button in copilot-cli-status is icon-only and
currently relies on title for accessibility; update the Button (the one with
props onRefresh, disabled={isChecking}, data-testid="refresh-copilot-cli") to
include an explicit aria-label (e.g., aria-label="Refresh Copilot CLI
detection") so screen readers get a reliable accessible name while keeping
existing title and visual behavior intact.
In
`@apps/ui/src/components/views/settings-view/providers/shared/base-model-configuration.tsx`:
- Around line 141-156: The checkbox for each model can be disabled when it is
the default (isDefault) but still render unchecked if defaultModel isn't present
in enabledModels; update the Checkbox so its checked state treats the default as
enabled by using checked={isEnabled || isDefault} (leave disabled as isSaving ||
isDefault) so the default model appears checked and cannot be toggled; ensure
onModelToggle remains unchanged (no-op when disabled) and reference the
models.map loop, isEnabled, isDefault, enabledModels, defaultModel, Checkbox,
and onModelToggle to locate where to change the checked prop.
🧹 Nitpick comments (6)
apps/ui/src/components/views/settings-view/providers/shared/base-model-configuration.tsx (1)
1-10: Prefer components barrel imports for intra‑app UI components.For UI components within apps/ui, consider importing from the components index barrel rather than direct paths to improve refactor safety. Based on learnings, ...
apps/server/src/routes/setup/routes/deauth-copilot.ts (1)
10-10: Consider extracting shared constant to avoid duplication.
DISCONNECTED_MARKER_FILEis duplicated in bothauth-copilot.tsanddeauth-copilot.ts. Consider extracting to a shared location (e.g.,../common.jsor a dedicated constants file) to ensure consistency.♻️ Suggested approach
Create a shared constant in
common.ts:// In apps/server/src/routes/setup/common.ts export const COPILOT_DISCONNECTED_MARKER_FILE = '.copilot-disconnected';Then import in both files:
-const DISCONNECTED_MARKER_FILE = '.copilot-disconnected'; +import { COPILOT_DISCONNECTED_MARKER_FILE } from '../common.js';apps/server/src/providers/copilot-provider.ts (1)
569-574: Add a comment explaining the intentional auto-approval of permissions.The
onPermissionRequesthandler auto-approves all tool requests. This aligns with AutoMaker's intentional architectural decision to bypass permissions for fully autonomous operation, with Docker containerization as the security boundary. However, unlikeclaude-provider.ts(which includes a "AUTONOMOUS MODE" comment),copilot-provider.tslacks this clarification. Add a similar comment here to document that this auto-approval is an intentional design choice.apps/server/src/providers/tool-normalization.ts (3)
20-24: Consider using a union type forstatusto match documented values.The JSDoc at line 34 specifies that
statusshould be'pending'|'in_progress'|'completed', but the interface declares it asstring. Using a union type would provide compile-time safety and prevent invalid status values from propagating.Suggested improvement
+type TodoStatus = 'pending' | 'in_progress' | 'completed'; + interface NormalizedTodo { content: string; - status: string; + status: TodoStatus; activeForm: string; }
36-44: Consider validating unknown status values and guarding against null input.Two edge cases to consider:
If a provider sends an unexpected status (e.g.,
'canceled'with American spelling,'done'), it passes through unchanged rather than being normalized to a valid value.If
todosisnullorundefined, the function will throw. Consider adding a guard.Suggested defensive approach
export function normalizeTodos(todos: ProviderTodo[]): NormalizedTodo[] { + if (!todos) return []; + const validStatuses = new Set(['pending', 'in_progress', 'completed']); return todos.map((todo) => ({ content: todo.content || todo.description || '', - status: todo.status === 'cancelled' ? 'completed' : todo.status || 'pending', + status: todo.status === 'cancelled' + ? 'completed' + : (validStatuses.has(todo.status || '') ? todo.status! : 'pending'), activeForm: todo.content || todo.description || '', })); }
52-92: Optional: The three input normalization functions could share a generic helper.All three functions (
normalizeFilePathInput,normalizeCommandInput,normalizePatternInput) follow the same pattern. A generic helper could reduce duplication, though the current explicit approach is also readable.Example generic approach
function normalizeInputKey( input: Record<string, unknown>, targetKey: string, alternateKeys: string[] ): Record<string, unknown> { const normalized = { ...input }; if (!normalized[targetKey]) { for (const key of alternateKeys) { if (input[key]) { normalized[targetKey] = input[key]; break; } } } return normalized; } // Usage: export const normalizeFilePathInput = (input: Record<string, unknown>) => normalizeInputKey(input, 'file_path', ['path', 'file', 'filename', 'filePath']); export const normalizeCommandInput = (input: Record<string, unknown>) => normalizeInputKey(input, 'command', ['cmd', 'script']); export const normalizePatternInput = (input: Record<string, unknown>) => normalizeInputKey(input, 'pattern', ['query', 'search', 'regex']);
apps/ui/src/components/views/settings-view/cli-status/copilot-cli-status.tsx
Show resolved
Hide resolved
| {models.map((model) => { | ||
| const isEnabled = enabledModels.includes(model.id); | ||
| const isDefault = model.id === defaultModel; | ||
| const badge = getFeatureBadge?.(model); | ||
|
|
||
| return ( | ||
| <div | ||
| key={model.id} | ||
| className="flex items-center justify-between p-3 rounded-xl border border-border/50 bg-card/50 hover:bg-accent/30 transition-colors" | ||
| > | ||
| <div className="flex items-center gap-3"> | ||
| <Checkbox | ||
| checked={isEnabled} | ||
| onCheckedChange={(checked) => onModelToggle(model.id, !!checked)} | ||
| disabled={isSaving || isDefault} | ||
| /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Default model can appear disabled and unchecked.
If defaultModel isn’t in enabledModels, the checkbox is disabled yet unchecked, leaving no way to reconcile the state. Consider always treating the default as enabled in the UI.
🛠️ Suggested adjustment
- const isEnabled = enabledModels.includes(model.id);
+ const isEnabled = isDefault || enabledModels.includes(model.id);
const isDefault = model.id === defaultModel;
const badge = getFeatureBadge?.(model);
return (
<div
key={model.id}
className="flex items-center justify-between p-3 rounded-xl border border-border/50 bg-card/50 hover:bg-accent/30 transition-colors"
>
<div className="flex items-center gap-3">
<Checkbox
- checked={isEnabled}
+ checked={isEnabled}
onCheckedChange={(checked) => onModelToggle(model.id, !!checked)}
disabled={isSaving || isDefault}
/>🤖 Prompt for AI Agents
In
`@apps/ui/src/components/views/settings-view/providers/shared/base-model-configuration.tsx`
around lines 141 - 156, The checkbox for each model can be disabled when it is
the default (isDefault) but still render unchecked if defaultModel isn't present
in enabledModels; update the Checkbox so its checked state treats the default as
enabled by using checked={isEnabled || isDefault} (leave disabled as isSaving ||
isDefault) so the default model appears checked and cannot be toggled; ensure
onModelToggle remains unchanged (no-op when disabled) and reference the
models.map loop, isEnabled, isDefault, enabledModels, defaultModel, Checkbox,
and onModelToggle to locate where to change the checked prop.
- Fix test mocks to include --version check for CLI detection - Add aria-label for accessibility on refresh button - Ensure default model checkbox always appears checked/enabled
- Fix test mocks by creating fresh provider instances after mock setup - Extract COPILOT_DISCONNECTED_MARKER_FILE constant to common.ts - Add AUTONOMOUS MODE comment explaining auto-approval of permissions - Improve tool-normalization with union types and null guards - Handle 'canceled' (American spelling) status in todo normalization
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@apps/server/src/routes/setup/routes/auth-copilot.ts`:
- Around line 14-38: The route handler createAuthCopilotHandler contains
filesystem/state logic (building .automaker path and unlinking
COPILOT_DISCONNECTED_MARKER_FILE); extract that into a new service function
(e.g., copilotService.connectCopilot or copilotService.removeDisconnectedMarker)
in a services module under services/, move the try/catch and fs.unlink logic
there (accepting projectRoot or resolving process.cwd() internally) and export
it; then update createAuthCopilotHandler to simply call the service, handle its
success/error result, and respond with the existing JSON payloads—also add the
appropriate import for the new service function where createAuthCopilotHandler
is defined.
In `@apps/server/tests/unit/providers/copilot-provider.test.ts`:
- Around line 40-58: The beforeEach execSync mock is missing handling for CLI
path detection called during CopilotProvider construction (ensureCliDetected via
the CopilotProvider constructor); update the mockImplementation in beforeEach to
also return a truthy CLI path for the path-detection command(s) used (e.g.,
commands that probe for the GitHub CLI binary), so ensureCliDetected/constructor
can set cliPath and subsequent ops (models list, gh auth status, --version)
succeed; locate the mock in the beforeEach block where execSync is mocked and
add a branch matching the CLI path probe command to return a realistic path
string.
- Around line 18-35: The tests currently mock fs/promises but not the
synchronous fs.existsSync used by findCliInCommonPaths and they mock execSync
without handling which/where used by findCliInPath, causing cliPath to stay null
and checkAuth to return unauthenticated; update the test setup to mock
fs.existsSync (return true for the expected common path(s) or as needed) and
extend the execSync mock to return a valid path string when called with 'which
copilot' or 'where copilot' (and keep other execSync uses stubbed) so
findCliInCommonPaths/findCliInPath can locate the CLI and checkAuth proceeds as
intended.
- Create copilot-connection-service.ts with connect/disconnect logic - Update auth-copilot and deauth-copilot routes to use service - Fix test mocks for CLI detection: - Mock fs.existsSync for CLI path validation - Mock which/where command for CLI path detection
Summary
This PR adds a new provider integration for GitHub Copilot using the official
@github/copilot-sdkpackage. This allows users to leverage their GitHub Copilot subscription to access various AI models (Claude, GPT, Gemini) through a unified interface.Features
New Provider: CopilotProvider
Location:
apps/server/src/providers/copilot-provider.ts@github/copilot-sdkfor programmatic Copilot accesscopilot-claude-sonnet-4.5,copilot-claude-haiku-4.5,copilot-claude-opus-4.5)copilot-gpt-5.2-codex,copilot-gpt-5.1,copilot-gpt-5, etc.)copilot-gemini-3-pro-preview)ghCLITool Name Mapping
Copilot SDK uses different tool names than our standard. The provider normalizes these:
view,read_fileReadwrite_file,create_fileWriteedit_file,patchEditrun_shell,bashBashsearch,grepGrepfind_files,globGlobtodo_write,write_todosTodoWritereport_intentReportIntentDynamic Model Discovery
Routes:
GET /api/setup/copilot/models- Get available models (cached)POST /api/setup/copilot/models/refresh- Force refresh from CLIPOST /api/setup/copilot/cache/clear- Clear model cacheAuthentication
ghCLIgh auth statusfor authentication stateGITHUB_TOKENenvironment variableGET /api/setup/copilot-statusModel Definitions
Location:
libs/types/src/copilot-models.tsDefines available Copilot models with:
Technical Details
Event Streaming
Uses an async queue pattern to bridge SDK callback events to async generators:
tool.execution_start→ Yields tool use events in real-timetool.execution_end→ Yields tool result eventsassistant.message→ Yields assistant messagessession.idle→ Signals completionModel Resolution
Updated
libs/model-resolver/src/resolver.tsto:copilot-prefixed modelsTesting
ghCLI installationDependencies
@github/copilot-sdk- Official GitHub Copilot SDKSummary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.