Skip to content

Commit 4e9df5e

Browse files
committed
Refresh prompt cost when the live model switches
1 parent 4b343cb commit 4e9df5e

2 files changed

Lines changed: 61 additions & 4 deletions

File tree

src/tui/runner-host.test.ts

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ import type { KeyEvent } from "@opentui/core";
66
import type { CostSummary } from "../cost/cost-summary.js";
77
import type { SubAgentSession } from "../subagent/session-store.js";
88
import { createHarness } from "./harness.js";
9-
import { acceptOverlaySelection, closeInsetOverlay, runOverlayAction } from "./shell.js";
9+
import {
10+
acceptOverlaySelection,
11+
closeInsetOverlay,
12+
moveOverlaySelection,
13+
runOverlayAction,
14+
} from "./shell.js";
1015
import {
1116
mountRunnerHost,
1217
observeSessionFromSubAgents,
@@ -443,6 +448,55 @@ describe("bottom border cost run", () => {
443448
}
444449
});
445450

451+
test("selecting a Codex model hides prompt $ without waiting for inference", async () => {
452+
const harness = await createHarness({ width: 80, height: 24 });
453+
let provider = "xai";
454+
const host = await mountRunnerHost({
455+
title: "test",
456+
eventEmitter: new EventEmitter(),
457+
send: () => {},
458+
interrupt: () => {},
459+
providers: {
460+
xai: { models: ["grok-4"] },
461+
"codex/abk-labs": { models: ["gpt-5.5"] },
462+
},
463+
onModelSelect: (id) => {
464+
const sep = id.indexOf(":");
465+
if (sep <= 0) return;
466+
provider = id.slice(0, sep);
467+
},
468+
commands: [],
469+
onCommand: () => {},
470+
chrome: () => ({ agents: [] }),
471+
subscribeChrome: () => () => {},
472+
subAgentSessions: () => [],
473+
createRenderer: async () => harness.renderer,
474+
readCostSummary: () => ({
475+
...fakeCostSummary(),
476+
costHiddenReason: provider.startsWith("codex/") ? "chatgpt-subscription" : null,
477+
}),
478+
showPromptCost: () => true,
479+
});
480+
try {
481+
expect(ruleOf(host.shell.promptBottomRule)).toContain("$0.42");
482+
483+
expect(host.openSurface("models")).toBe(true);
484+
const items = host.shell.overlayItems;
485+
const codexIndex = items.findIndex((label) => label.includes("codex/abk-labs"));
486+
expect(codexIndex).toBeGreaterThanOrEqual(0);
487+
moveOverlaySelection(host.shell, codexIndex);
488+
acceptOverlaySelection(host.shell);
489+
490+
expect(provider).toBe("codex/abk-labs");
491+
expect(ruleOf(host.shell.promptBottomRule)).not.toContain("$0.42");
492+
expect(host.shell.costContext?.costLabel ?? null).toBeNull();
493+
expect(ruleOf(host.shell.promptBottomRule)).toContain("10%");
494+
} finally {
495+
host.dispose();
496+
harness.destroy();
497+
}
498+
});
499+
446500
test("session.clear paints the context meter unknown immediately", async () => {
447501
const harness = await createHarness({ width: 80, height: 24 });
448502
const emitter = new EventEmitter();

src/tui/runner-host.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,9 @@ export interface RunnerHostDeps {
104104
*/
105105
readonly modelLabel?: () => PromptActionBarModelLabelInput;
106106
/**
107-
* Live cost/context source for the bottom border's meter. Read on mount and
108-
* again after every completed inference turn, so the meter tracks usage
109-
* without a timer of its own.
107+
* Live cost/context source for the bottom border's meter. Read on mount, after
108+
* every completed inference turn, and after a live model pick so hide/show
109+
* follows the new identity without waiting for the next inference.
110110
*/
111111
readonly readCostSummary?: () => CostSummary | undefined;
112112
/**
@@ -248,6 +248,9 @@ export async function mountRunnerHost(deps: RunnerHostDeps): Promise<RunnerHost>
248248
const onModelSelect = (id: string): void => {
249249
deps.onModelSelect(id);
250250
if (readModelLabel) setPromptModelLabel(host.shell, readModelLabel());
251+
// Identity is already applied (deps.onModelSelect). Re-read so Codex
252+
// hides $ (and a metered provider shows it) without waiting for inference.
253+
pushCostContext();
251254
};
252255
const cwd = deps.cwd ?? process.cwd();
253256
const host = await mountProductHost({

0 commit comments

Comments
 (0)