Skip to content

Conversation

@jeffnash
Copy link
Contributor

Summary

Implements the Copilot request executor with header injection and dynamic model registry.

Changes

  • Copilot executor with VS Code version headers and integration IDs
  • Agent header logic (X-Copilot-Initiator detection)
  • Vision request header for image inputs
  • Dynamic model fetching and aliasing from Copilot API
  • Management API endpoints for auth files
  • Copilot types for API responses
  • Cache eviction on auth removal to prevent memory leaks

Stack

This is PR 3/5 in the Copilot feature stack:

  1. Infrastructure & Config (feat(copilot): add shared infrastructure and config [1/5] #380)
  2. Auth Flow (feat(copilot): implement GitHub Copilot authentication flow [2/5] #381)
  3. [This PR] Executor & Models
  4. Gemini Reasoning
  5. Tests

Depends on: #381 - merge that first

- Add auth directory management helper (GetAuthDir)
- Add random hex string generator for request IDs
- Add helper for generating unique machine identifiers
@chatgpt-codex-connector
Copy link

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@gemini-code-assist
Copy link

Summary of Changes

Hello @jeffnash, 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 significantly expands the system's capabilities by integrating GitHub Copilot support. It introduces a robust framework for authenticating with Copilot, executing requests, and dynamically managing available models. The changes ensure seamless interaction with Copilot's various API endpoints, including proper header handling for different request types and account configurations.

Highlights

  • Copilot Request Executor: Introduces a dedicated executor for GitHub Copilot API requests, handling token management, dynamic model fetching, and specific header injection for VS Code versions and integration IDs.
  • Dynamic Model Registry: Enables dynamic fetching and aliasing of models from the Copilot API, providing up-to-date model information and fallback static models if dynamic fetching fails.
  • Copilot Authentication Flow: Implements the GitHub Copilot OAuth2 device code authentication flow, allowing users to log in via CLI or a new management API endpoint, with support for individual, business, and enterprise account types.
  • Agent Header Logic & Vision Support: Adds sophisticated logic for detecting and injecting the X-Copilot-Initiator header (agent vs. user) based on request payload, and includes support for vision requests with appropriate headers.
  • Management API Endpoints: New management API endpoints are added for CRUD operations on Copilot API key configurations and for initiating the Copilot authentication URL.
  • Cache Eviction: Implements cache eviction mechanisms for Copilot models and Gemini reasoning data upon authentication removal, preventing memory leaks and ensuring data consistency.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a 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 significant new functionality for GitHub Copilot integration. It adds the core authentication flow using device codes, a request executor that handles Copilot-specific headers, and a dynamic model registry. The changes are well-structured, with new packages for Copilot authentication and shared types. I've identified a critical compilation error due to a duplicated struct definition, a potential race condition in a new management handler, and a couple of places where error handling could be improved. Overall, this is a solid implementation of a complex feature.

Comment on lines 219 to 247
// CopilotKey represents the configuration for GitHub Copilot API access.
// Authentication is handled via device code OAuth flow, not API keys.
type CopilotKey struct {
// AccountType is the Copilot subscription type (individual, business, enterprise).
// Defaults to "individual" if not specified.
AccountType string `yaml:"account-type" json:"account-type"`

// ProxyURL overrides the global proxy setting for Copilot requests if provided.
ProxyURL string `yaml:"proxy-url,omitempty" json:"proxy-url,omitempty"`

// AgentInitiatorPersist, when true, forces subsequent Copilot requests sharing the
// same prompt_cache_key to send X-Initiator=agent after the first call. Default false.
AgentInitiatorPersist bool `yaml:"agent-initiator-persist" json:"agent-initiator-persist"`
}

// CopilotKey represents the configuration for GitHub Copilot API access.
// Authentication is handled via device code OAuth flow, not API keys.
type CopilotKey struct {
// AccountType is the Copilot subscription type (individual, business, enterprise).
// Defaults to "individual" if not specified.
AccountType string `yaml:"account-type" json:"account-type"`

// ProxyURL overrides the global proxy setting for Copilot requests if provided.
ProxyURL string `yaml:"proxy-url,omitempty" json:"proxy-url,omitempty"`

// AgentInitiatorPersist, when true, forces subsequent Copilot requests sharing the
// same prompt_cache_key to send X-Initiator=agent after the first call. Default false.
AgentInitiatorPersist bool `yaml:"agent-initiator-persist" json:"agent-initiator-persist"`
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The CopilotKey struct is defined multiple times. This will cause a compilation error due to redeclaration. Please remove the duplicate definitions.

Comment on lines +808 to +836
func (h *Handler) PutCopilotKeys(c *gin.Context) {
data, err := c.GetRawData()
if err != nil {
c.JSON(400, gin.H{"error": "failed to read body"})
return
}
var arr []config.CopilotKey
if err = json.Unmarshal(data, &arr); err != nil {
var obj struct {
Items []config.CopilotKey `json:"items"`
}
if err2 := json.Unmarshal(data, &obj); err2 != nil || len(obj.Items) == 0 {
c.JSON(400, gin.H{"error": "invalid body"})
return
}
arr = obj.Items
}
// Normalize entries
filtered := make([]config.CopilotKey, 0, len(arr))
for i := range arr {
entry := arr[i]
entry.AccountType = strings.TrimSpace(entry.AccountType)
entry.ProxyURL = strings.TrimSpace(entry.ProxyURL)
filtered = append(filtered, entry)
}
h.cfg.CopilotKey = filtered
h.cfg.SanitizeCopilotKeys()
h.persist(c)
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The PutCopilotKeys function modifies h.cfg.CopilotKey, which is a shared configuration object. Other handler functions like PatchCopilotKey and DeleteCopilotKey use a mutex to protect concurrent access. To prevent potential race conditions, this function should also use a mutex lock.

func (h *Handler) PutCopilotKeys(c *gin.Context) {
	data, err := c.GetRawData()
	if err != nil {
		c.JSON(400, gin.H{"error": "failed to read body"})
		return
	}
	var arr []config.CopilotKey
	if err = json.Unmarshal(data, &arr); err != nil {
		var obj struct {
			Items []config.CopilotKey `json:"items"`
		}
		if err2 := json.Unmarshal(data, &obj); err2 != nil || len(obj.Items) == 0 {
			c.JSON(400, gin.H{"error": "invalid body"})
			return
		}
		arr = obj.Items
	}
	// Normalize entries
	filtered := make([]config.CopilotKey, 0, len(arr))
	for i := range arr {
		entry := arr[i]
		entry.AccountType = strings.TrimSpace(entry.AccountType)
		entry.ProxyURL = strings.TrimSpace(entry.ProxyURL)
		filtered = append(filtered, entry)
	}
	h.mu.Lock()
	defer h.mu.Unlock()
	h.cfg.CopilotKey = filtered
	h.cfg.SanitizeCopilotKeys()
	h.persist(c)
}

Comment on lines 110 to 149
# GitHub Copilot account configuration
# Note: Copilot uses OAuth device code authentication, NOT API keys or tokens.
# Do NOT paste your GitHub access token or Copilot bearer token here.
# Tokens are stored only in auth-dir JSON files, never in config.yaml.
#
# To authenticate:
# - CLI: run with -copilot-login flag
# - Web: use the /copilot-auth-url management endpoint
#
# After OAuth login, tokens are managed automatically and stored in auth-dir.
# The entries below only configure account type and optional proxy settings.
#copilot-api-key:
# - account-type: "individual" # Options: individual, business, enterprise
# proxy-url: "socks5://proxy.example.com:1080" # optional: proxy for Copilot requests

# # When set to true, this flag forces subsequent requests in a session (sharing the same prompt_cache_key)
# # to send the header "X-Initiator: agent" instead of "vscode". This mirrors VS Code's behavior for
# # long-running agent interactions and helps prevent hitting standard rate limits.
# agent-initiator-persist: true

# GitHub Copilot account configuration
# Note: Copilot uses OAuth device code authentication, NOT API keys or tokens.
# Do NOT paste your GitHub access token or Copilot bearer token here.
# Tokens are stored only in auth-dir JSON files, never in config.yaml.
#
# To authenticate:
# - CLI: run with -copilot-login flag
# - Web: use the /copilot-auth-url management endpoint
#
# After OAuth login, tokens are managed automatically and stored in auth-dir.
# The entries below only configure account type and optional proxy settings.
#copilot-api-key:
# - account-type: "individual" # Options: individual, business, enterprise
# proxy-url: "socks5://proxy.example.com:1080" # optional: proxy for Copilot requests

# # When set to true, this flag forces subsequent requests in a session (sharing the same prompt_cache_key)
# # to send the header "X-Initiator: agent" instead of "vscode". This mirrors VS Code's behavior for
# # long-running agent interactions and helps prevent hitting standard rate limits.
# agent-initiator-persist: true

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This configuration block for copilot-api-key is duplicated. It seems to be a copy-paste error. Please remove the redundant blocks to avoid confusion for users configuring the service.

"client_id": GitHubClientID,
"scope": GitHubAppScopes,
}
bodyBytes, _ := json.Marshal(reqBody)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error from json.Marshal is being ignored. While it's unlikely to fail when marshalling a map[string]string, it's best practice to handle potential errors to prevent unexpected behavior.

	bodyBytes, err := json.Marshal(reqBody)
	if err != nil {
		return nil, fmt.Errorf("%w: failed to marshal request body: %v", ErrDeviceCodeFailed, err)
	}

"device_code": deviceCode,
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
}
bodyBytes, _ := json.Marshal(reqBody)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error from json.Marshal is being ignored. While it's unlikely to fail when marshalling a map[string]string, it's best practice to handle potential errors to prevent unexpected behavior.

	bodyBytes, err := json.Marshal(reqBody)
	if err != nil {
		return "", fmt.Errorf("failed to marshal access token request: %w", err)
	}

@jeffnash
Copy link
Contributor Author

@luispater just saw your rule on no 3p resellers, I could disable all non-copilot models (i.e. keep raptor-mini + oswe) and commit the gemini PR + dynamic model fetching and other models in the registry to the new repo.

also curious about how this works since iFlow has qwen, kimi, glm, and others

- Add Copilot section with agent-initiator-persist flag
- Add scanner buffer size configuration
- Add account type configuration option
- Add copilot types with account type validation
- Document configuration options in example config
- Add tests for util and copilot types
- Add device-code OAuth flow with GitHub token exchange
- Implement Copilot token acquisition and refresh logic
- Add account type handling (individual/business/enterprise)
- Add token persistence and storage management
- Add CLI login command (cliproxy-api copilot login)
- Register Copilot refresh handler in SDK
- Validate account_type with warning for invalid values
@jeffnash jeffnash force-pushed the feat/copilot-executor branch from 8e9ba84 to 21a24d3 Compare November 30, 2025 21:48
- Implement Copilot executor with header injection
- Add VS Code version headers and integration IDs
- Add agent header logic (X-Initiator detection)
- Add vision request header for image inputs
- Add static model registry with raptor-mini and oswe-vscode-prime
- Add management API endpoints for auth files

Note: Full model list and dynamic fetching added in gemini branch
@jeffnash jeffnash force-pushed the feat/copilot-executor branch from 21a24d3 to 11a07b6 Compare November 30, 2025 23:09
@luispater luispater closed this Dec 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants