feat(coding-agent): add warnings.anthropicExtraUsage opt-out
closes #3808
This commit is contained in:
@@ -48,6 +48,20 @@ Edit directly or use `/settings` for common options.
|
|||||||
| `autocompleteMaxVisible` | number | `5` | Max visible items in autocomplete dropdown (3-20) |
|
| `autocompleteMaxVisible` | number | `5` | Max visible items in autocomplete dropdown (3-20) |
|
||||||
| `showHardwareCursor` | boolean | `false` | Show terminal cursor |
|
| `showHardwareCursor` | boolean | `false` | Show terminal cursor |
|
||||||
|
|
||||||
|
### Warnings
|
||||||
|
|
||||||
|
| Setting | Type | Default | Description |
|
||||||
|
|---------|------|---------|-------------|
|
||||||
|
| `warnings.anthropicExtraUsage` | boolean | `true` | Show a warning when Anthropic subscription auth may use paid extra usage |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"warnings": {
|
||||||
|
"anthropicExtraUsage": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Compaction
|
### Compaction
|
||||||
|
|
||||||
| Setting | Type | Default | Description |
|
| Setting | Type | Default | Description |
|
||||||
@@ -226,6 +240,9 @@ See [packages.md](packages.md) for package management details.
|
|||||||
"maxRetries": 3
|
"maxRetries": 3
|
||||||
},
|
},
|
||||||
"enabledModels": ["claude-*", "gpt-4o"],
|
"enabledModels": ["claude-*", "gpt-4o"],
|
||||||
|
"warnings": {
|
||||||
|
"anthropicExtraUsage": true
|
||||||
|
},
|
||||||
"packages": ["pi-skills"]
|
"packages": ["pi-skills"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ export interface MarkdownSettings {
|
|||||||
codeBlockIndent?: string; // default: " "
|
codeBlockIndent?: string; // default: " "
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WarningSettings {
|
||||||
|
anthropicExtraUsage?: boolean; // default: true
|
||||||
|
}
|
||||||
|
|
||||||
export type TransportSetting = Transport;
|
export type TransportSetting = Transport;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -104,6 +108,7 @@ export interface Settings {
|
|||||||
autocompleteMaxVisible?: number; // Max visible items in autocomplete dropdown (default: 5)
|
autocompleteMaxVisible?: number; // Max visible items in autocomplete dropdown (default: 5)
|
||||||
showHardwareCursor?: boolean; // Show terminal cursor while still positioning it for IME
|
showHardwareCursor?: boolean; // Show terminal cursor while still positioning it for IME
|
||||||
markdown?: MarkdownSettings;
|
markdown?: MarkdownSettings;
|
||||||
|
warnings?: WarningSettings;
|
||||||
sessionDir?: string; // Custom session storage directory (same format as --session-dir CLI flag)
|
sessionDir?: string; // Custom session storage directory (same format as --session-dir CLI flag)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1049,4 +1054,14 @@ export class SettingsManager {
|
|||||||
getCodeBlockIndent(): string {
|
getCodeBlockIndent(): string {
|
||||||
return this.settings.markdown?.codeBlockIndent ?? " ";
|
return this.settings.markdown?.codeBlockIndent ?? " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getWarnings(): WarningSettings {
|
||||||
|
return { ...(this.settings.warnings ?? {}) };
|
||||||
|
}
|
||||||
|
|
||||||
|
setWarnings(warnings: WarningSettings): void {
|
||||||
|
this.globalSettings.warnings = { ...warnings };
|
||||||
|
this.markModified("warnings");
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
Spacer,
|
Spacer,
|
||||||
Text,
|
Text,
|
||||||
} from "@mariozechner/pi-tui";
|
} from "@mariozechner/pi-tui";
|
||||||
|
import type { WarningSettings } from "../../../core/settings-manager.js";
|
||||||
import { getSelectListTheme, getSettingsListTheme, theme } from "../theme/theme.js";
|
import { getSelectListTheme, getSettingsListTheme, theme } from "../theme/theme.js";
|
||||||
import { DynamicBorder } from "./dynamic-border.js";
|
import { DynamicBorder } from "./dynamic-border.js";
|
||||||
|
|
||||||
@@ -53,6 +54,7 @@ export interface SettingsConfig {
|
|||||||
quietStartup: boolean;
|
quietStartup: boolean;
|
||||||
clearOnShrink: boolean;
|
clearOnShrink: boolean;
|
||||||
showTerminalProgress: boolean;
|
showTerminalProgress: boolean;
|
||||||
|
warnings: WarningSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SettingsCallbacks {
|
export interface SettingsCallbacks {
|
||||||
@@ -79,12 +81,55 @@ export interface SettingsCallbacks {
|
|||||||
onQuietStartupChange: (enabled: boolean) => void;
|
onQuietStartupChange: (enabled: boolean) => void;
|
||||||
onClearOnShrinkChange: (enabled: boolean) => void;
|
onClearOnShrinkChange: (enabled: boolean) => void;
|
||||||
onShowTerminalProgressChange: (enabled: boolean) => void;
|
onShowTerminalProgressChange: (enabled: boolean) => void;
|
||||||
|
onWarningsChange: (warnings: WarningSettings) => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A submenu component for selecting from a list of options.
|
* A submenu component for selecting from a list of options.
|
||||||
*/
|
*/
|
||||||
|
class WarningSettingsSubmenu extends Container {
|
||||||
|
private settingsList: SettingsList;
|
||||||
|
private state: WarningSettings;
|
||||||
|
|
||||||
|
constructor(warnings: WarningSettings, onChange: (warnings: WarningSettings) => void, onCancel: () => void) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.state = { ...warnings };
|
||||||
|
|
||||||
|
const items: SettingItem[] = [
|
||||||
|
{
|
||||||
|
id: "anthropic-extra-usage",
|
||||||
|
label: "Anthropic extra usage",
|
||||||
|
description: "Warn when Anthropic subscription auth may use paid extra usage",
|
||||||
|
currentValue: (this.state.anthropicExtraUsage ?? true) ? "true" : "false",
|
||||||
|
values: ["true", "false"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
this.settingsList = new SettingsList(
|
||||||
|
items,
|
||||||
|
Math.min(items.length, 10),
|
||||||
|
getSettingsListTheme(),
|
||||||
|
(id, newValue) => {
|
||||||
|
switch (id) {
|
||||||
|
case "anthropic-extra-usage":
|
||||||
|
this.state = { ...this.state, anthropicExtraUsage: newValue === "true" };
|
||||||
|
onChange({ ...this.state });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCancel,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.addChild(this.settingsList);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInput(data: string): void {
|
||||||
|
this.settingsList.handleInput(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SelectSubmenu extends Container {
|
class SelectSubmenu extends Container {
|
||||||
private selectList: SelectList;
|
private selectList: SelectList;
|
||||||
|
|
||||||
@@ -159,6 +204,7 @@ export class SettingsSelectorComponent extends Container {
|
|||||||
super();
|
super();
|
||||||
|
|
||||||
const supportsImages = getCapabilities().images;
|
const supportsImages = getCapabilities().images;
|
||||||
|
let currentWarnings = { ...config.warnings };
|
||||||
|
|
||||||
const items: SettingItem[] = [
|
const items: SettingItem[] = [
|
||||||
{
|
{
|
||||||
@@ -233,6 +279,21 @@ export class SettingsSelectorComponent extends Container {
|
|||||||
currentValue: config.treeFilterMode,
|
currentValue: config.treeFilterMode,
|
||||||
values: ["default", "no-tools", "user-only", "labeled-only", "all"],
|
values: ["default", "no-tools", "user-only", "labeled-only", "all"],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "warnings",
|
||||||
|
label: "Warnings",
|
||||||
|
description: "Enable or disable individual warnings",
|
||||||
|
currentValue: "configure",
|
||||||
|
submenu: (_currentValue, done) =>
|
||||||
|
new WarningSettingsSubmenu(
|
||||||
|
currentWarnings,
|
||||||
|
(warnings) => {
|
||||||
|
currentWarnings = warnings;
|
||||||
|
callbacks.onWarningsChange(warnings);
|
||||||
|
},
|
||||||
|
() => done(),
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "thinking",
|
id: "thinking",
|
||||||
label: "Thinking level",
|
label: "Thinking level",
|
||||||
|
|||||||
@@ -3788,6 +3788,7 @@ export class InteractiveMode {
|
|||||||
quietStartup: this.settingsManager.getQuietStartup(),
|
quietStartup: this.settingsManager.getQuietStartup(),
|
||||||
clearOnShrink: this.settingsManager.getClearOnShrink(),
|
clearOnShrink: this.settingsManager.getClearOnShrink(),
|
||||||
showTerminalProgress: this.settingsManager.getShowTerminalProgress(),
|
showTerminalProgress: this.settingsManager.getShowTerminalProgress(),
|
||||||
|
warnings: this.settingsManager.getWarnings(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onAutoCompactChange: (enabled) => {
|
onAutoCompactChange: (enabled) => {
|
||||||
@@ -3901,6 +3902,9 @@ export class InteractiveMode {
|
|||||||
onShowTerminalProgressChange: (enabled) => {
|
onShowTerminalProgressChange: (enabled) => {
|
||||||
this.settingsManager.setShowTerminalProgress(enabled);
|
this.settingsManager.setShowTerminalProgress(enabled);
|
||||||
},
|
},
|
||||||
|
onWarningsChange: (warnings) => {
|
||||||
|
this.settingsManager.setWarnings(warnings);
|
||||||
|
},
|
||||||
onCancel: () => {
|
onCancel: () => {
|
||||||
done();
|
done();
|
||||||
this.ui.requestRender();
|
this.ui.requestRender();
|
||||||
@@ -3963,6 +3967,9 @@ export class InteractiveMode {
|
|||||||
private async maybeWarnAboutAnthropicSubscriptionAuth(
|
private async maybeWarnAboutAnthropicSubscriptionAuth(
|
||||||
model: Model<any> | undefined = this.session.model,
|
model: Model<any> | undefined = this.session.model,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
if (this.settingsManager.getWarnings().anthropicExtraUsage === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.anthropicSubscriptionWarningShown) {
|
if (this.anthropicSubscriptionWarningShown) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
import { describe, expect, test, vi } from "vitest";
|
import { describe, expect, test, vi } from "vitest";
|
||||||
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.js";
|
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.js";
|
||||||
|
|
||||||
|
function createSettingsManager(warnings: { anthropicExtraUsage?: boolean } = {}) {
|
||||||
|
return {
|
||||||
|
getWarnings: vi.fn().mockReturnValue(warnings),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe("InteractiveMode.maybeWarnAboutAnthropicSubscriptionAuth", () => {
|
describe("InteractiveMode.maybeWarnAboutAnthropicSubscriptionAuth", () => {
|
||||||
test("warns once when Anthropic subscription auth is detected", async () => {
|
test("warns once when Anthropic subscription auth is detected", async () => {
|
||||||
const fakeThis: any = {
|
const fakeThis: any = {
|
||||||
anthropicSubscriptionWarningShown: false,
|
anthropicSubscriptionWarningShown: false,
|
||||||
|
settingsManager: createSettingsManager(),
|
||||||
session: {
|
session: {
|
||||||
modelRegistry: {
|
modelRegistry: {
|
||||||
authStorage: {
|
authStorage: {
|
||||||
@@ -30,6 +37,7 @@ describe("InteractiveMode.maybeWarnAboutAnthropicSubscriptionAuth", () => {
|
|||||||
test("warns when Anthropic OAuth is stored even if token refresh lookup would fail", async () => {
|
test("warns when Anthropic OAuth is stored even if token refresh lookup would fail", async () => {
|
||||||
const fakeThis: any = {
|
const fakeThis: any = {
|
||||||
anthropicSubscriptionWarningShown: false,
|
anthropicSubscriptionWarningShown: false,
|
||||||
|
settingsManager: createSettingsManager(),
|
||||||
session: {
|
session: {
|
||||||
modelRegistry: {
|
modelRegistry: {
|
||||||
authStorage: {
|
authStorage: {
|
||||||
@@ -52,6 +60,7 @@ describe("InteractiveMode.maybeWarnAboutAnthropicSubscriptionAuth", () => {
|
|||||||
test("does not warn for non-Anthropic models", async () => {
|
test("does not warn for non-Anthropic models", async () => {
|
||||||
const fakeThis: any = {
|
const fakeThis: any = {
|
||||||
anthropicSubscriptionWarningShown: false,
|
anthropicSubscriptionWarningShown: false,
|
||||||
|
settingsManager: createSettingsManager(),
|
||||||
session: {
|
session: {
|
||||||
modelRegistry: {
|
modelRegistry: {
|
||||||
authStorage: {
|
authStorage: {
|
||||||
@@ -70,4 +79,28 @@ describe("InteractiveMode.maybeWarnAboutAnthropicSubscriptionAuth", () => {
|
|||||||
expect(fakeThis.showWarning).not.toHaveBeenCalled();
|
expect(fakeThis.showWarning).not.toHaveBeenCalled();
|
||||||
expect(fakeThis.session.modelRegistry.getApiKeyForProvider).not.toHaveBeenCalled();
|
expect(fakeThis.session.modelRegistry.getApiKeyForProvider).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("does not warn when Anthropic extra usage warning is disabled", async () => {
|
||||||
|
const fakeThis: any = {
|
||||||
|
anthropicSubscriptionWarningShown: false,
|
||||||
|
settingsManager: createSettingsManager({ anthropicExtraUsage: false }),
|
||||||
|
session: {
|
||||||
|
modelRegistry: {
|
||||||
|
authStorage: {
|
||||||
|
get: vi.fn(),
|
||||||
|
},
|
||||||
|
getApiKeyForProvider: vi.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
showWarning: vi.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
await (InteractiveMode as any).prototype.maybeWarnAboutAnthropicSubscriptionAuth.call(fakeThis, {
|
||||||
|
provider: "anthropic",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(fakeThis.showWarning).not.toHaveBeenCalled();
|
||||||
|
expect(fakeThis.session.modelRegistry.authStorage.get).not.toHaveBeenCalled();
|
||||||
|
expect(fakeThis.session.modelRegistry.getApiKeyForProvider).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user