GitHub Copilot: auto-enable models, fix gpt-5 API, normalize tool call IDs

- Auto-enable all models after /login via POST /models/{model}/policy
- Use openai-responses API for gpt-5/o3/o4 models (not accessible via completions)
- Normalize tool call IDs when switching between github-copilot models with different APIs
  (fixes #198: openai-responses generates 450+ char IDs with special chars that break other models)
- Update README with streamlined GitHub Copilot docs
This commit is contained in:
Mario Zechner 2025-12-15 20:03:51 +01:00
parent 16c8861842
commit c5543f7586
7 changed files with 216 additions and 127 deletions

View file

@ -132,18 +132,11 @@ pi
/login # Select "GitHub Copilot", authorize in browser
```
During login, you'll be prompted for an enterprise domain. Press Enter to use github.com, or enter your GitHub Enterprise Server domain (e.g., `github.mycompany.com`).
During login, you'll be prompted for an enterprise domain. Press Enter to use github.com, or enter your GitHub Enterprise Server domain (e.g., `github.mycompany.com`). All models are automatically enabled after login.
Some models require explicit enablement before use. If you get "The requested model is not supported" error:
If you get "The requested model is not supported" error, enable the model manually in VS Code: open Copilot Chat, click the model selector, select the model (warning icon), and click "Enable".
1. Open VS Code with GitHub Copilot Chat extension
2. Open the Copilot Chat panel and click the model selector
3. Select the model (marked with a warning icon)
4. Click "Enable" to accept the terms
For enterprise users, check with your organization's Copilot administrator for model availability and policies.
Note: Enabling some models (e.g., Grok from xAI) may involve sharing usage data with the provider. Review the terms before enabling.
For enterprise users, check with your organization's Copilot administrator for model availability.
Tokens stored in `~/.pi/agent/oauth.json` (mode 0600). Use `/logout` to clear.

View file

@ -1,3 +1,4 @@
import { getModels } from "@mariozechner/pi-ai";
import type { OAuthCredentials } from "./storage.js";
const CLIENT_ID = "Iv1.b507a08c87ecfe98";
@ -216,9 +217,58 @@ export async function refreshGitHubCopilotToken(
} satisfies OAuthCredentials;
}
/**
* Enable a model for the user's GitHub Copilot account.
* This is required for some models (like Claude, Grok) before they can be used.
*/
export async function enableGitHubCopilotModel(
token: string,
modelId: string,
enterpriseDomain?: string,
): Promise<boolean> {
const baseUrl = getGitHubCopilotBaseUrl(token, enterpriseDomain);
const url = `${baseUrl}/models/${modelId}/policy`;
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
...COPILOT_HEADERS,
"openai-intent": "chat-policy",
"x-interaction-type": "chat-policy",
},
body: JSON.stringify({ state: "enabled" }),
});
return response.ok;
} catch {
return false;
}
}
/**
* Enable all known GitHub Copilot models that may require policy acceptance.
* Called after successful login to ensure all models are available.
*/
export async function enableAllGitHubCopilotModels(
token: string,
enterpriseDomain?: string,
onProgress?: (model: string, success: boolean) => void,
): Promise<void> {
const models = getModels("github-copilot");
await Promise.all(
models.map(async (model) => {
const success = await enableGitHubCopilotModel(token, model.id, enterpriseDomain);
onProgress?.(model.id, success);
}),
);
}
export async function loginGitHubCopilot(options: {
onAuth: (url: string, instructions?: string) => void;
onPrompt: (prompt: { message: string; placeholder?: string; allowEmpty?: boolean }) => Promise<string>;
onProgress?: (message: string) => void;
}): Promise<OAuthCredentials> {
const input = await options.onPrompt({
message: "GitHub Enterprise URL/domain (blank for github.com)",
@ -242,5 +292,11 @@ export async function loginGitHubCopilot(options: {
device.interval,
device.expires_in,
);
return await refreshGitHubCopilotToken(githubAccessToken, enterpriseDomain ?? undefined);
const credentials = await refreshGitHubCopilotToken(githubAccessToken, enterpriseDomain ?? undefined);
// Enable all models after successful login
options.onProgress?.("Enabling models...");
await enableAllGitHubCopilotModels(credentials.access, enterpriseDomain ?? undefined);
return credentials;
}

View file

@ -55,6 +55,7 @@ export async function login(
provider: SupportedOAuthProvider,
onAuth: (info: OAuthAuthInfo) => void,
onPrompt: (prompt: OAuthPrompt) => Promise<string>,
onProgress?: (message: string) => void,
): Promise<void> {
switch (provider) {
case "anthropic":
@ -67,6 +68,7 @@ export async function login(
const creds = await loginGitHubCopilot({
onAuth: (url, instructions) => onAuth({ url, instructions }),
onPrompt,
onProgress,
});
saveOAuthCredentials("github-copilot", creds);
break;

View file

@ -1419,6 +1419,10 @@ export class InteractiveMode {
this.ui.requestRender();
});
},
(message) => {
this.chatContainer.addChild(new Text(theme.fg("dim", message), 1, 0));
this.ui.requestRender();
},
);
invalidateOAuthCache();