fix(coding-agent): update provider auth guidance and defaults

This commit is contained in:
Mario Zechner
2026-04-24 00:55:14 +02:00
parent 2926f7e887
commit f2f03616ab
5 changed files with 69 additions and 17 deletions

View File

@@ -21,10 +21,12 @@
### Changed ### Changed
- Updated default model selection across providers to current recommended models.
- Improved stale extension context errors after session replacement or reload to tell extension authors to avoid captured `pi`/command `ctx` and use `withSession` for post-replacement work. - Improved stale extension context errors after session replacement or reload to tell extension authors to avoid captured `pi`/command `ctx` and use `withSession` for post-replacement work.
### Fixed ### Fixed
- Added Amazon Bedrock setup guidance to `/login` and updated `/model` copy to refer to configured providers instead of only API keys.
- Improved no-model and missing-auth warnings to point users to `/login` for OAuth or API key setup. - Improved no-model and missing-auth warnings to point users to `/login` for OAuth or API key setup.
- Fixed `/quit` shutdown ordering to stop the TUI before extension UI teardown can repaint, preserving the final rendered frame while still emitting `session_shutdown` before process exit. - Fixed `/quit` shutdown ordering to stop the TUI before extension UI teardown can repaint, preserving the final rendered frame while still emitting `session_shutdown` before process exit.
- Fixed `SettingsManager.inMemory()` initial settings being lost after reloads triggered by SDK resource loading ([#3616](https://github.com/badlogic/pi-mono/issues/3616)) - Fixed `SettingsManager.inMemory()` initial settings being lost after reloads triggered by SDK resource loading ([#3616](https://github.com/badlogic/pi-mono/issues/3616))

View File

@@ -13,28 +13,28 @@ import type { ModelRegistry } from "./model-registry.js";
/** Default model IDs for each known provider */ /** Default model IDs for each known provider */
export const defaultModelPerProvider: Record<KnownProvider, string> = { export const defaultModelPerProvider: Record<KnownProvider, string> = {
"amazon-bedrock": "us.anthropic.claude-opus-4-6-v1", "amazon-bedrock": "us.anthropic.claude-opus-4-6-v1",
anthropic: "claude-opus-4-6", anthropic: "claude-opus-4-7",
openai: "gpt-5.4", openai: "gpt-5.4",
"azure-openai-responses": "gpt-5.2", "azure-openai-responses": "gpt-5.4",
"openai-codex": "gpt-5.4", "openai-codex": "gpt-5.5",
google: "gemini-2.5-pro", google: "gemini-3.1-pro-preview",
"google-gemini-cli": "gemini-2.5-pro", "google-gemini-cli": "gemini-3.1-pro-preview",
"google-antigravity": "gemini-3.1-pro-high", "google-antigravity": "gemini-3.1-pro-high",
"google-vertex": "gemini-3-pro-preview", "google-vertex": "gemini-3.1-pro-preview",
"github-copilot": "gpt-4o", "github-copilot": "gpt-5.4",
openrouter: "openai/gpt-5.1-codex", openrouter: "moonshotai/kimi-k2.6",
"vercel-ai-gateway": "anthropic/claude-opus-4-6", "vercel-ai-gateway": "zai/glm-5.1",
xai: "grok-4-fast-non-reasoning", xai: "grok-4.20-0309-reasoning",
groq: "openai/gpt-oss-120b", groq: "openai/gpt-oss-120b",
cerebras: "zai-glm-4.7", cerebras: "zai-glm-4.7",
zai: "glm-5", zai: "glm-5.1",
mistral: "devstral-medium-latest", mistral: "devstral-medium-latest",
minimax: "MiniMax-M2.7", minimax: "MiniMax-M2.7",
"minimax-cn": "MiniMax-M2.7", "minimax-cn": "MiniMax-M2.7",
huggingface: "moonshotai/Kimi-K2.5", huggingface: "moonshotai/Kimi-K2.6",
fireworks: "accounts/fireworks/models/kimi-k2p6", fireworks: "accounts/fireworks/models/kimi-k2p6",
opencode: "claude-opus-4-6", opencode: "kimi-k2.6",
"opencode-go": "kimi-k2.5", "opencode-go": "kimi-k2.6",
"kimi-coding": "kimi-for-coding", "kimi-coding": "kimi-for-coding",
}; };

View File

@@ -31,18 +31,20 @@ export class LoginDialogComponent extends Container implements Focusable {
providerId: string, providerId: string,
private onComplete: (success: boolean, message?: string) => void, private onComplete: (success: boolean, message?: string) => void,
providerNameOverride?: string, providerNameOverride?: string,
titleOverride?: string,
) { ) {
super(); super();
this.tui = tui; this.tui = tui;
const providerInfo = getOAuthProviders().find((p) => p.id === providerId); const providerInfo = getOAuthProviders().find((p) => p.id === providerId);
const providerName = providerNameOverride || providerInfo?.name || providerId; const providerName = providerNameOverride || providerInfo?.name || providerId;
const title = titleOverride ?? `Login to ${providerName}`;
// Top border // Top border
this.addChild(new DynamicBorder()); this.addChild(new DynamicBorder());
// Title // Title
this.addChild(new Text(theme.fg("accent", theme.bold(`Login to ${providerName}`)), 1, 0)); this.addChild(new Text(theme.fg("accent", theme.bold(title)), 1, 0));
// Dynamic content area // Dynamic content area
this.contentContainer = new Container(); this.contentContainer = new Container();
@@ -147,6 +149,20 @@ export class LoginDialogComponent extends Container implements Focusable {
}); });
} }
/**
* Show informational text without prompting for input.
*/
showInfo(lines: string[]): void {
this.contentContainer.clear();
this.contentContainer.addChild(new Spacer(1));
for (const line of lines) {
this.contentContainer.addChild(new Text(line, 1, 0));
}
this.contentContainer.addChild(new Spacer(1));
this.contentContainer.addChild(new Text(`(${keyHint("tui.select.cancel", "to close")})`, 1, 0));
this.tui.requestRender();
}
/** /**
* Show waiting message (for polling flows like GitHub Copilot) * Show waiting message (for polling flows like GitHub Copilot)
*/ */

View File

@@ -93,7 +93,7 @@ export class ModelSelectorComponent extends Container implements Focusable {
this.scopeHintText = new Text(this.getScopeHintText(), 0, 0); this.scopeHintText = new Text(this.getScopeHintText(), 0, 0);
this.addChild(this.scopeHintText); this.addChild(this.scopeHintText);
} else { } else {
const hintText = "Only showing models with configured API keys (see README for details)"; const hintText = "Only showing models from configured providers. Use /login to add providers.";
this.addChild(new Text(theme.fg("warning", hintText), 0, 0)); this.addChild(new Text(theme.fg("warning", hintText), 0, 0));
} }
this.addChild(new Spacer(1)); this.addChild(new Spacer(1));

View File

@@ -52,6 +52,7 @@ import {
getAgentDir, getAgentDir,
getAuthPath, getAuthPath,
getDebugLogPath, getDebugLogPath,
getDocsPath,
getShareViewerUrl, getShareViewerUrl,
getUpdateInstruction, getUpdateInstruction,
VERSION, VERSION,
@@ -161,7 +162,7 @@ type CompactionQueuedMessage = {
}; };
const ANTHROPIC_SUBSCRIPTION_AUTH_WARNING = const ANTHROPIC_SUBSCRIPTION_AUTH_WARNING =
"Anthropic subscription auth is active. Third-party usage now draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage."; "Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage.";
function isAnthropicSubscriptionAuthKey(apiKey: string | undefined): boolean { function isAnthropicSubscriptionAuthKey(apiKey: string | undefined): boolean {
return typeof apiKey === "string" && apiKey.startsWith("sk-ant-oat"); return typeof apiKey === "string" && apiKey.startsWith("sk-ant-oat");
@@ -175,8 +176,11 @@ function hasDefaultModelProvider(providerId: string): providerId is keyof typeof
return providerId in defaultModelPerProvider; return providerId in defaultModelPerProvider;
} }
const BEDROCK_PROVIDER_ID = "amazon-bedrock";
const API_KEY_LOGIN_PROVIDERS: Record<string, string> = { const API_KEY_LOGIN_PROVIDERS: Record<string, string> = {
anthropic: "Anthropic", anthropic: "Anthropic",
[BEDROCK_PROVIDER_ID]: "Amazon Bedrock",
"azure-openai-responses": "Azure OpenAI Responses", "azure-openai-responses": "Azure OpenAI Responses",
cerebras: "Cerebras", cerebras: "Cerebras",
fireworks: "Fireworks", fireworks: "Fireworks",
@@ -4423,6 +4427,8 @@ export class InteractiveMode {
if (providerOption.authType === "oauth") { if (providerOption.authType === "oauth") {
await this.showLoginDialog(providerOption.id, providerOption.name); await this.showLoginDialog(providerOption.id, providerOption.name);
} else if (providerOption.id === BEDROCK_PROVIDER_ID) {
this.showBedrockSetupDialog(providerOption.id, providerOption.name);
} else { } else {
await this.showApiKeyLoginDialog(providerOption.id, providerOption.name); await this.showApiKeyLoginDialog(providerOption.id, providerOption.name);
} }
@@ -4536,6 +4542,34 @@ export class InteractiveMode {
} }
} }
private showBedrockSetupDialog(providerId: string, providerName: string): void {
const restoreEditor = () => {
this.editorContainer.clear();
this.editorContainer.addChild(this.editor);
this.ui.setFocus(this.editor);
this.ui.requestRender();
};
const dialog = new LoginDialogComponent(
this.ui,
providerId,
() => restoreEditor(),
providerName,
"Amazon Bedrock setup",
);
dialog.showInfo([
theme.fg("text", "Amazon Bedrock uses AWS credentials instead of a single API key."),
theme.fg("text", "Configure an AWS profile, IAM keys, bearer token, or role-based credentials."),
theme.fg("muted", "See:"),
theme.fg("accent", ` ${path.join(getDocsPath(), "providers.md")}`),
]);
this.editorContainer.clear();
this.editorContainer.addChild(dialog);
this.ui.setFocus(dialog);
this.ui.requestRender();
}
private async showApiKeyLoginDialog(providerId: string, providerName: string): Promise<void> { private async showApiKeyLoginDialog(providerId: string, providerName: string): Promise<void> {
const previousModel = this.session.model; const previousModel = this.session.model;