fix(coding-agent): warn on Anthropic subscription auth
This commit is contained in:
@@ -31,6 +31,9 @@ export class EarendilAnnouncementComponent extends Container {
|
||||
this.addChild(new DynamicBorder((text) => theme.fg("accent", text)));
|
||||
this.addChild(new Text(theme.bold(theme.fg("accent", "pi has joined Earendil")), 1, 0));
|
||||
this.addChild(new Spacer(1));
|
||||
this.addChild(new Text(theme.fg("muted", "Read the blog post:"), 1, 0));
|
||||
this.addChild(new Text(theme.fg("mdLink", BLOG_URL), 1, 0));
|
||||
this.addChild(new Spacer(1));
|
||||
|
||||
const imageBase64 = loadImageBase64();
|
||||
if (imageBase64) {
|
||||
@@ -45,8 +48,6 @@ export class EarendilAnnouncementComponent extends Container {
|
||||
this.addChild(new Spacer(1));
|
||||
}
|
||||
|
||||
this.addChild(new Text(theme.fg("muted", "Read the blog post:"), 1, 0));
|
||||
this.addChild(new Text(theme.fg("mdLink", BLOG_URL), 1, 0));
|
||||
this.addChild(new DynamicBorder((text) => theme.fg("accent", text)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +129,13 @@ type CompactionQueuedMessage = {
|
||||
mode: "steer" | "followUp";
|
||||
};
|
||||
|
||||
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.";
|
||||
|
||||
function isAnthropicSubscriptionAuthKey(apiKey: string | undefined): boolean {
|
||||
return typeof apiKey === "string" && apiKey.startsWith("sk-ant-oat");
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for InteractiveMode initialization.
|
||||
*/
|
||||
@@ -175,6 +182,7 @@ export class InteractiveMode {
|
||||
private lastEscapeTime = 0;
|
||||
private changelogMarkdown: string | undefined = undefined;
|
||||
private startupNoticesShown = false;
|
||||
private anthropicSubscriptionWarningShown = false;
|
||||
|
||||
// Status line tracking (for mutating immediately-sequential status updates)
|
||||
private lastStatusSpacer: Spacer | undefined = undefined;
|
||||
@@ -431,7 +439,7 @@ export class InteractiveMode {
|
||||
|
||||
private shouldShowEarendilAnnouncement(): boolean {
|
||||
const now = new Date();
|
||||
return now.getFullYear() === 2026 && now.getMonth() === 3 && now.getDate() === 8;
|
||||
return now.getFullYear() === 2026 && now.getMonth() === 3 && (now.getDate() === 8 || now.getDate() === 9);
|
||||
}
|
||||
|
||||
private showStartupNoticesIfNeeded(): void {
|
||||
@@ -631,6 +639,8 @@ export class InteractiveMode {
|
||||
this.showWarning(modelFallbackMessage);
|
||||
}
|
||||
|
||||
void this.maybeWarnAboutAnthropicSubscriptionAuth();
|
||||
|
||||
// Process initial messages
|
||||
if (initialMessage) {
|
||||
try {
|
||||
@@ -2980,6 +2990,7 @@ export class InteractiveMode {
|
||||
const thinkingStr =
|
||||
result.model.reasoning && result.thinkingLevel !== "off" ? ` (thinking: ${result.thinkingLevel})` : "";
|
||||
this.showStatus(`Switched to ${result.model.name || result.model.id}${thinkingStr}`);
|
||||
void this.maybeWarnAboutAnthropicSubscriptionAuth(result.model);
|
||||
}
|
||||
} catch (error) {
|
||||
this.showError(error instanceof Error ? error.message : String(error));
|
||||
@@ -3476,6 +3487,7 @@ export class InteractiveMode {
|
||||
this.footer.invalidate();
|
||||
this.updateEditorBorderColor();
|
||||
this.showStatus(`Model: ${model.id}`);
|
||||
void this.maybeWarnAboutAnthropicSubscriptionAuth(model);
|
||||
this.checkDaxnutsEasterEgg(model);
|
||||
} catch (error) {
|
||||
this.showError(error instanceof Error ? error.message : String(error));
|
||||
@@ -3511,6 +3523,35 @@ export class InteractiveMode {
|
||||
this.footerDataProvider.setAvailableProviderCount(uniqueProviders.size);
|
||||
}
|
||||
|
||||
private async maybeWarnAboutAnthropicSubscriptionAuth(
|
||||
model: Model<any> | undefined = this.session.model,
|
||||
): Promise<void> {
|
||||
if (this.anthropicSubscriptionWarningShown) {
|
||||
return;
|
||||
}
|
||||
if (!model || model.provider !== "anthropic") {
|
||||
return;
|
||||
}
|
||||
|
||||
const storedCredential = this.session.modelRegistry.authStorage.get("anthropic");
|
||||
if (storedCredential?.type === "oauth") {
|
||||
this.anthropicSubscriptionWarningShown = true;
|
||||
this.showWarning(ANTHROPIC_SUBSCRIPTION_AUTH_WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const apiKey = await this.session.modelRegistry.getApiKeyForProvider(model.provider);
|
||||
if (!isAnthropicSubscriptionAuthKey(apiKey)) {
|
||||
return;
|
||||
}
|
||||
this.anthropicSubscriptionWarningShown = true;
|
||||
this.showWarning(ANTHROPIC_SUBSCRIPTION_AUTH_WARNING);
|
||||
} catch {
|
||||
// Ignore auth lookup failures for warning-only checks.
|
||||
}
|
||||
}
|
||||
|
||||
private showModelSelector(initialSearchInput?: string): void {
|
||||
this.showSelector((done) => {
|
||||
const selector = new ModelSelectorComponent(
|
||||
@@ -3526,6 +3567,7 @@ export class InteractiveMode {
|
||||
this.updateEditorBorderColor();
|
||||
done();
|
||||
this.showStatus(`Model: ${model.id}`);
|
||||
void this.maybeWarnAboutAnthropicSubscriptionAuth(model);
|
||||
this.checkDaxnutsEasterEgg(model);
|
||||
} catch (error) {
|
||||
done();
|
||||
|
||||
Reference in New Issue
Block a user