-
Notifications
You must be signed in to change notification settings - Fork 8.3k
[GitHub] github:copilot is a routing alias — no model control or visibility #897
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
base: main
Are you sure you want to change the base?
Changes from 10 commits
1c38084
de4c1b2
db1f9f3
65c00e1
c451548
144cc6e
b3e1a61
d1c1829
ee6f94c
20599b2
3f8d867
6d02223
9cc8902
b717f8f
a42bf48
29130a8
faaa5e6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| You are a coding agent working on the repository Gitlawb/openclaude. | ||
|
|
||
| ## Context | ||
|
|
||
| This pull request (https://github.com/Gitlawb/openclaude/pull/897) represents multiple failed attempts over several commits to implement a working GitHub Copilot model list. The branch is messy: it contains debug scripts, half-working shims, conflicting env var logic, and leftover test files accumulated across many iterations. None of the current implementations fully work. | ||
|
|
||
| You will operate directly on the branch of PR #897. Your job is NOT to add more code on top of what is there — it is to perform a clean surgical replacement: delete everything that was added across all commits of this PR related to Copilot model fetching, and replace it with a single correct implementation ported faithfully from anomalyco/opencode. | ||
|
|
||
| If the opencode system works correctly end-to-end (which it does), nothing from the PR's previous attempts should survive. | ||
|
|
||
| DO NOT commit, push, or open any pull request. All changes must remain local only. | ||
|
|
||
|
LoackyBit marked this conversation as resolved.
Outdated
|
||
| --- | ||
|
|
||
| ## Objective | ||
|
|
||
| Replace the entire Copilot model-fetching system introduced across all commits of PR #897 with a faithful port of the implementation used by anomalyco/opencode (https://github.com/anomalyco/opencode), which correctly fetches the list of available models based on the user's Copilot plan at runtime. | ||
|
|
||
| --- | ||
|
|
||
| ## Step 0 — Audit the branch before writing any code | ||
|
|
||
| Before touching any file: | ||
|
|
||
| 1. Run `git log main..HEAD --oneline` to get the full list of commits in this PR | ||
| 2. Run `git diff main...HEAD --name-only` to get the full list of files modified or added across the entire PR | ||
| 3. For each modified/added file, read it and determine: is any part of it related to the Copilot model-fetching system? If yes, mark it for cleanup | ||
| 4. Print a full audit report listing: | ||
| - Files to delete entirely (added within the PR, no longer needed) | ||
| - Files to partially revert (existed before the PR, modified by it) | ||
| - Files to leave untouched | ||
|
|
||
| Only after completing and printing the audit should you start making changes. | ||
|
|
||
| --- | ||
|
|
||
| ## Step 1 — Delete all previous attempts | ||
|
|
||
| Remove or fully revert every file and code change introduced across the PR commits that relates to Copilot model fetching, provider shims, or model normalization. This includes but is not limited to: | ||
|
|
||
| - Any propagation of `GITHUB_MODEL` into `OPENAI_MODEL` | ||
| - Any `isGithubNewEndpointModel()` or similar routing functions | ||
| - All `console.log` / `console.error` debug statements added in any file | ||
| - Temporary or test scripts: `list_models.ts`, `m.ts`, `test_api_version.ts`, or any file that exists only for debugging — delete them entirely | ||
| - Any `normalizeGithubCopilotModel`, `normalizeGithubModelsApiModel`, or similar normalization functions that patch model names inconsistently | ||
| - Any static fallback model lists hardcoded in any file | ||
| - Any env var hacks (`OPENAI_BASE_URL`, `OPENAI_MODEL`, `GITHUB_MODEL`) used to drive model selection for Copilot | ||
|
|
||
| If a file was added entirely within this PR and serves no purpose after the cleanup, delete it completely. | ||
| If a file existed before this PR but was modified, revert only the modifications introduced by the PR — leave the original file intact. | ||
|
|
||
| Do not leave dead code, commented-out blocks, or TODO comments from previous attempts. | ||
|
|
||
| --- | ||
|
|
||
| ## Step 2 — Implement the opencode system | ||
|
|
||
| ### Reference implementation | ||
|
|
||
| Read both files in full before writing any code: | ||
| - https://github.com/anomalyco/opencode/blob/main/packages/opencode/src/plugin/github-copilot/models.ts | ||
| - https://github.com/anomalyco/opencode/blob/main/packages/opencode/src/plugin/github-copilot/copilot.ts | ||
|
|
||
| ### 2a. Create `src/github-copilot/models.ts` | ||
|
|
||
| Implement the following exactly as opencode does: | ||
|
|
||
| **Zod schema** validating the response from `GET {baseURL}/models`: | ||
| - `data[].id` (string) | ||
| - `data[].name` (string) | ||
| - `data[].version` (string) | ||
| - `data[].model_picker_enabled` (boolean) | ||
| - `data[].supported_endpoints` (string[], optional) | ||
| - `data[].policy.state` (string, optional) | ||
| - `data[].capabilities.family` (string) | ||
| - `data[].capabilities.limits.max_context_window_tokens` (number) | ||
| - `data[].capabilities.limits.max_output_tokens` (number) | ||
| - `data[].capabilities.limits.max_prompt_tokens` (number) | ||
| - `data[].capabilities.limits.vision` (optional: max_prompt_image_size, max_prompt_images, supported_media_types) | ||
| - `data[].capabilities.supports.tool_calls` (boolean) | ||
| - `data[].capabilities.supports.vision` (boolean, optional) | ||
| - `data[].capabilities.supports.streaming` (boolean) | ||
| - `data[].capabilities.supports.structured_outputs` (boolean, optional) | ||
| - `data[].capabilities.supports.adaptive_thinking` (boolean, optional) | ||
| - `data[].capabilities.supports.reasoning_effort` (string[], optional) | ||
| - `data[].capabilities.supports.max_thinking_budget` (number, optional) | ||
| - `data[].capabilities.supports.min_thinking_budget` (number, optional) | ||
|
|
||
| **`build(key, remote, url, prev?)` function** mapping raw API item to internal `Model`: | ||
| - `providerID` → `"github-copilot"` | ||
| - `api.id` → `remote.id` | ||
| - `api.url` → `${url}/v1` if `remote.supported_endpoints` includes `"/v1/messages"`, else `url` | ||
| - `api.npm` → `"@ai-sdk/anthropic"` if messages endpoint, else `"@ai-sdk/github-copilot"` | ||
| - `reasoning` derived from: `adaptive_thinking`, `reasoning_effort.length > 0`, or `max_thinking_budget`/`min_thinking_budget` defined | ||
| - `image` derived from: `supports.vision === true` OR any item in `limits.vision.supported_media_types` starts with `"image/"` | ||
| - When `prev` is provided: preserve `name`, `family`, `release_date`, `options`, `headers`, `variants` — remote API wins only for limits and capabilities | ||
| - All `cost` fields → `0` (Copilot is subscription-based) | ||
|
|
||
| **`get(baseURL, headers, existing)` async function**: | ||
| - Fetches `${baseURL}/models` with `AbortSignal.timeout(5_000)` | ||
| - Throws descriptive error if `res.ok === false` | ||
| - Parses with Zod schema | ||
| - Filters: keep only `model_picker_enabled === true` AND `policy?.state !== "disabled"` | ||
| - Merges with `existing`: prune removed models, update existing ones, add new ones | ||
| - Returns `Record<string, Model>` | ||
|
|
||
| ### 2b. Wire into the Copilot provider | ||
|
|
||
| In the Copilot provider initialization file (read the codebase to find the correct one): | ||
| - After obtaining the Copilot API token, call `get(baseURL, authHeaders, existingModels)` from `models.ts` | ||
| - Use `https://api.githubcopilot.com` as `baseURL` (the function appends `/models` itself) | ||
| - Pass `Authorization: Bearer <token>` and `Copilot-Integration-Id: vscode-chat` headers (adjust integration ID if openclaude uses a different one) | ||
| - Store the result as the canonical model list — never override it with env vars | ||
|
|
||
| --- | ||
|
|
||
| ## Constraints | ||
|
|
||
| - No `process.env.OPENAI_MODEL` or `process.env.OPENAI_BASE_URL` for Copilot model selection | ||
| - No hardcoded model IDs anywhere — the list is always fetched dynamically | ||
| - No static fallback model list — if the fetch fails, throw and let the caller handle it | ||
| - No `console.log`, no debug output, no `@ts-ignore`, no `any` | ||
| - No new npm dependencies — use only what is already in the project (`zod` must already be present) | ||
| - Do not add tests unless the project already has a test suite for this module | ||
|
|
||
| --- | ||
|
|
||
| ## Deliverable | ||
|
|
||
| When done, print a summary report with: | ||
| 1. List of files deleted | ||
| 2. List of files reverted (with a brief description of what was removed) | ||
| 3. List of files created or modified for the new implementation | ||
| 4. The exact local command the user should run to manually test the Copilot model fetch before proceeding to the next step | ||
|
|
||
| Do NOT run `git add`, `git commit`, `git push`, or open any PR. | ||
| Leave all changes as unstaged working-tree modifications so the user can inspect them freely. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,6 +36,7 @@ import { | |
| readGithubModelsToken, | ||
| readGithubModelsTokenAsync, | ||
| } from '../utils/githubModelsCredentials.js' | ||
| import { refreshGithubModelsCache } from '../utils/model/githubModels.js' | ||
| import { | ||
| probeAtomicChatReadiness, | ||
| probeOllamaGenerationReadiness, | ||
|
|
@@ -138,9 +139,8 @@ const FORM_STEPS: Array<{ | |
| ] | ||
|
|
||
| const GITHUB_PROVIDER_ID = '__github_models__' | ||
| const GITHUB_PROVIDER_LABEL = 'GitHub Models' | ||
| const GITHUB_PROVIDER_DEFAULT_MODEL = 'github:copilot' | ||
| const GITHUB_PROVIDER_DEFAULT_BASE_URL = 'https://models.github.ai/inference' | ||
| const GITHUB_PROVIDER_LABEL = 'GitHub Copilot' | ||
| const GITHUB_PROVIDER_DEFAULT_BASE_URL = 'https://api.githubcopilot.com' | ||
| const CODEX_OAUTH_PROVIDER_NAME = 'Codex OAuth' | ||
| const CODEX_OAUTH_PROVIDER_MODEL = 'codexplan' | ||
|
|
||
|
|
@@ -216,9 +216,9 @@ function getGithubProviderModel( | |
| processEnv: NodeJS.ProcessEnv = process.env, | ||
| ): string { | ||
| if (isEnvTruthy(processEnv.CLAUDE_CODE_USE_GITHUB)) { | ||
| return processEnv.OPENAI_MODEL?.trim() || GITHUB_PROVIDER_DEFAULT_MODEL | ||
| return '' | ||
| } | ||
| return GITHUB_PROVIDER_DEFAULT_MODEL | ||
| return '' | ||
| } | ||
|
Comment on lines
259
to
266
|
||
|
|
||
| function getGithubProviderSummary( | ||
|
|
@@ -233,7 +233,8 @@ function getGithubProviderSummary( | |
| ? 'token via env' | ||
| : 'no token found' | ||
| const activeSuffix = isActive ? ' (active)' : '' | ||
| return `github-models · ${GITHUB_PROVIDER_DEFAULT_BASE_URL} · ${getGithubProviderModel(processEnv)} · ${credentialSummary}${activeSuffix}` | ||
| const modelSummary = getGithubProviderModel(processEnv) | ||
| return `github-copilot · ${GITHUB_PROVIDER_DEFAULT_BASE_URL}${modelSummary ? ` · ${modelSummary}` : ''} · ${credentialSummary}${activeSuffix}` | ||
| } | ||
|
|
||
| function describeAtomicChatSelectionIssue( | ||
|
|
@@ -572,6 +573,18 @@ export function ProviderManager({ mode, onDone }: Props): React.ReactNode { | |
| } | ||
| }, [refreshCodexOAuthCredentialState, refreshGithubProviderState]) | ||
|
|
||
| React.useEffect(() => { | ||
| if (!githubProviderAvailable || githubCredentialSource === 'none') { | ||
| return | ||
| } | ||
|
|
||
| void refreshGithubModelsCache().catch(error => { | ||
| setErrorMessage( | ||
| `Could not load GitHub models: ${error instanceof Error ? error.message : String(error)}`, | ||
| ) | ||
| }) | ||
| }, [githubCredentialSource, githubProviderAvailable]) | ||
|
|
||
| React.useEffect(() => { | ||
| if (screen !== 'select-ollama-model') { | ||
| return | ||
|
|
@@ -755,14 +768,9 @@ export function ProviderManager({ mode, onDone }: Props): React.ReactNode { | |
|
|
||
| setAppState(prev => ({ | ||
| ...prev, | ||
|
LoackyBit marked this conversation as resolved.
|
||
| mainLoopModel: GITHUB_PROVIDER_DEFAULT_MODEL, | ||
| mainLoopModelForSession: null, | ||
| })) | ||
| refreshProfiles() | ||
| setAppState(prev => ({ | ||
| ...prev, | ||
| mainLoopModel: GITHUB_PROVIDER_DEFAULT_MODEL, | ||
| })) | ||
| setStatusMessage(`Active provider: ${GITHUB_PROVIDER_LABEL}`) | ||
| setIsActivating(false) | ||
| returnToMenu() | ||
|
|
@@ -840,7 +848,6 @@ export function ProviderManager({ mode, onDone }: Props): React.ReactNode { | |
| const { error } = updateSettingsForSource('userSettings', { | ||
| env: { | ||
| CLAUDE_CODE_USE_GITHUB: '1', | ||
| OPENAI_MODEL: GITHUB_PROVIDER_DEFAULT_MODEL, | ||
| OPENAI_API_KEY: undefined as any, | ||
| OPENAI_ORG: undefined as any, | ||
| OPENAI_PROJECT: undefined as any, | ||
|
|
@@ -859,7 +866,6 @@ export function ProviderManager({ mode, onDone }: Props): React.ReactNode { | |
| } | ||
|
|
||
| process.env.CLAUDE_CODE_USE_GITHUB = '1' | ||
| process.env.OPENAI_MODEL = GITHUB_PROVIDER_DEFAULT_MODEL | ||
| delete process.env.OPENAI_API_KEY | ||
| delete process.env.OPENAI_ORG | ||
| delete process.env.OPENAI_PROJECT | ||
|
|
@@ -889,7 +895,6 @@ export function ProviderManager({ mode, onDone }: Props): React.ReactNode { | |
| const { error } = updateSettingsForSource('userSettings', { | ||
| env: { | ||
| CLAUDE_CODE_USE_GITHUB: undefined as any, | ||
| OPENAI_MODEL: undefined as any, | ||
| OPENAI_BASE_URL: undefined as any, | ||
| OPENAI_API_BASE: undefined as any, | ||
| }, | ||
|
|
@@ -909,7 +914,6 @@ export function ProviderManager({ mode, onDone }: Props): React.ReactNode { | |
|
|
||
| delete process.env.CLAUDE_CODE_USE_GITHUB | ||
| delete process.env[GITHUB_MODELS_HYDRATED_ENV_MARKER] | ||
| delete process.env.OPENAI_MODEL | ||
| delete process.env.OPENAI_API_KEY | ||
| delete process.env.OPENAI_ORG | ||
| delete process.env.OPENAI_PROJECT | ||
|
|
@@ -1458,7 +1462,7 @@ export function ProviderManager({ mode, onDone }: Props): React.ReactNode { | |
| isGithubCredentialSourceResolved ? ( | ||
| <Text dimColor>No provider profiles configured yet.</Text> | ||
| ) : ( | ||
| <Text dimColor>Checking GitHub Models credentials...</Text> | ||
| <Text dimColor>Checking GitHub Copilot credentials...</Text> | ||
| ) | ||
| ) : ( | ||
| <> | ||
|
|
@@ -1578,7 +1582,7 @@ export function ProviderManager({ mode, onDone }: Props): React.ReactNode { | |
| label: isGithubActive | ||
| ? `${GITHUB_PROVIDER_LABEL} (active)` | ||
| : GITHUB_PROVIDER_LABEL, | ||
| description: `github-models · ${GITHUB_PROVIDER_DEFAULT_BASE_URL} · ${getGithubProviderModel()}`, | ||
| description: `github-copilot · ${GITHUB_PROVIDER_DEFAULT_BASE_URL}`, | ||
| }) | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.