diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 85e63a7c..344813b3 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -21,10 +21,12 @@ ### 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. ### 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. - 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)) diff --git a/packages/coding-agent/src/core/model-resolver.ts b/packages/coding-agent/src/core/model-resolver.ts index 8da22f54..e702c355 100644 --- a/packages/coding-agent/src/core/model-resolver.ts +++ b/packages/coding-agent/src/core/model-resolver.ts @@ -13,28 +13,28 @@ import type { ModelRegistry } from "./model-registry.js"; /** Default model IDs for each known provider */ export const defaultModelPerProvider: Record = { "amazon-bedrock": "us.anthropic.claude-opus-4-6-v1", - anthropic: "claude-opus-4-6", + anthropic: "claude-opus-4-7", openai: "gpt-5.4", - "azure-openai-responses": "gpt-5.2", - "openai-codex": "gpt-5.4", - google: "gemini-2.5-pro", - "google-gemini-cli": "gemini-2.5-pro", + "azure-openai-responses": "gpt-5.4", + "openai-codex": "gpt-5.5", + google: "gemini-3.1-pro-preview", + "google-gemini-cli": "gemini-3.1-pro-preview", "google-antigravity": "gemini-3.1-pro-high", - "google-vertex": "gemini-3-pro-preview", - "github-copilot": "gpt-4o", - openrouter: "openai/gpt-5.1-codex", - "vercel-ai-gateway": "anthropic/claude-opus-4-6", - xai: "grok-4-fast-non-reasoning", + "google-vertex": "gemini-3.1-pro-preview", + "github-copilot": "gpt-5.4", + openrouter: "moonshotai/kimi-k2.6", + "vercel-ai-gateway": "zai/glm-5.1", + xai: "grok-4.20-0309-reasoning", groq: "openai/gpt-oss-120b", cerebras: "zai-glm-4.7", - zai: "glm-5", + zai: "glm-5.1", mistral: "devstral-medium-latest", minimax: "MiniMax-M2.7", "minimax-cn": "MiniMax-M2.7", - huggingface: "moonshotai/Kimi-K2.5", + huggingface: "moonshotai/Kimi-K2.6", fireworks: "accounts/fireworks/models/kimi-k2p6", - opencode: "claude-opus-4-6", - "opencode-go": "kimi-k2.5", + opencode: "kimi-k2.6", + "opencode-go": "kimi-k2.6", "kimi-coding": "kimi-for-coding", }; diff --git a/packages/coding-agent/src/modes/interactive/components/login-dialog.ts b/packages/coding-agent/src/modes/interactive/components/login-dialog.ts index 90839b99..98d2096b 100644 --- a/packages/coding-agent/src/modes/interactive/components/login-dialog.ts +++ b/packages/coding-agent/src/modes/interactive/components/login-dialog.ts @@ -31,18 +31,20 @@ export class LoginDialogComponent extends Container implements Focusable { providerId: string, private onComplete: (success: boolean, message?: string) => void, providerNameOverride?: string, + titleOverride?: string, ) { super(); this.tui = tui; const providerInfo = getOAuthProviders().find((p) => p.id === providerId); const providerName = providerNameOverride || providerInfo?.name || providerId; + const title = titleOverride ?? `Login to ${providerName}`; // Top border this.addChild(new DynamicBorder()); // 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 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) */ diff --git a/packages/coding-agent/src/modes/interactive/components/model-selector.ts b/packages/coding-agent/src/modes/interactive/components/model-selector.ts index 3acbfa86..711ac3a9 100644 --- a/packages/coding-agent/src/modes/interactive/components/model-selector.ts +++ b/packages/coding-agent/src/modes/interactive/components/model-selector.ts @@ -93,7 +93,7 @@ export class ModelSelectorComponent extends Container implements Focusable { this.scopeHintText = new Text(this.getScopeHintText(), 0, 0); this.addChild(this.scopeHintText); } 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 Spacer(1)); diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index 3972d542..b29fd90c 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -52,6 +52,7 @@ import { getAgentDir, getAuthPath, getDebugLogPath, + getDocsPath, getShareViewerUrl, getUpdateInstruction, VERSION, @@ -161,7 +162,7 @@ type CompactionQueuedMessage = { }; 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 { 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; } +const BEDROCK_PROVIDER_ID = "amazon-bedrock"; + const API_KEY_LOGIN_PROVIDERS: Record = { anthropic: "Anthropic", + [BEDROCK_PROVIDER_ID]: "Amazon Bedrock", "azure-openai-responses": "Azure OpenAI Responses", cerebras: "Cerebras", fireworks: "Fireworks", @@ -4423,6 +4427,8 @@ export class InteractiveMode { if (providerOption.authType === "oauth") { await this.showLoginDialog(providerOption.id, providerOption.name); + } else if (providerOption.id === BEDROCK_PROVIDER_ID) { + this.showBedrockSetupDialog(providerOption.id, providerOption.name); } else { 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 { const previousModel = this.session.model;