Skip to content

Commit 9229111

Browse files
committed
fix(status): use selected model for usage
1 parent 794bd89 commit 9229111

2 files changed

Lines changed: 90 additions & 4 deletions

File tree

src/auto-reply/reply/commands-status.test.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,6 +1192,81 @@ describe("buildStatusReply subagent summary", () => {
11921192
expect(providerUsageCall[0]?.providers).toEqual(["deepseek"]);
11931193
});
11941194

1195+
it("uses session-selected usage when a stale runtime snapshot differs", async () => {
1196+
registerStatusCodexHarness();
1197+
const usageResetBase = Math.floor(Date.now() / 1000);
1198+
providerUsageMock.loadProviderUsageSummary.mockImplementation(async (params) => ({
1199+
updatedAt: Date.now(),
1200+
providers: params.providers?.includes("deepseek")
1201+
? [
1202+
{
1203+
provider: "deepseek",
1204+
displayName: "DeepSeek",
1205+
windows: [],
1206+
summary: "Balance ¥22.75",
1207+
},
1208+
]
1209+
: [
1210+
{
1211+
provider: "openai",
1212+
displayName: "OpenAI",
1213+
windows: [
1214+
{
1215+
label: "5h",
1216+
usedPercent: 9,
1217+
resetAt: (usageResetBase + 60 * 60) * 1000,
1218+
},
1219+
],
1220+
},
1221+
],
1222+
}));
1223+
1224+
const text = await buildStatusText({
1225+
cfg: {
1226+
...baseCfg,
1227+
agents: {
1228+
defaults: {
1229+
agentRuntime: { id: "codex" },
1230+
},
1231+
},
1232+
},
1233+
sessionEntry: {
1234+
sessionId: "sess-status-selected-openai-stale-deepseek",
1235+
updatedAt: 0,
1236+
providerOverride: "openai",
1237+
modelOverride: "gpt-5.5",
1238+
modelOverrideSource: "user",
1239+
modelProvider: "deepseek",
1240+
model: "deepseek-v4-flash",
1241+
},
1242+
sessionKey: "agent:main:main",
1243+
parentSessionKey: "agent:main:main",
1244+
sessionScope: "per-sender",
1245+
statusChannel: "mobilechat",
1246+
provider: "openai",
1247+
model: "gpt-5.5",
1248+
contextTokens: 1_000_000,
1249+
resolvedFastMode: false,
1250+
resolvedVerboseLevel: "off",
1251+
resolvedReasoningLevel: "off",
1252+
resolveDefaultThinkingLevel: async () => undefined,
1253+
isGroup: false,
1254+
defaultGroupActivation: () => "mention",
1255+
modelAuthOverride: "oauth",
1256+
activeModelAuthOverride: "api-key",
1257+
});
1258+
1259+
const normalized = normalizeTestText(text);
1260+
expect(normalized).toContain("Model: openai/gpt-5.5");
1261+
expect(normalized).toContain("Usage: 5h 91% left");
1262+
expect(normalized).not.toContain("Balance ¥22.75");
1263+
const providerUsageCalls = providerUsageMock.loadProviderUsageSummary.mock.calls.map(
1264+
([params]) => params?.providers,
1265+
);
1266+
expect(providerUsageCalls).toContainEqual(["openai"]);
1267+
expect(providerUsageCalls).not.toContainEqual(["deepseek"]);
1268+
});
1269+
11951270
it("uses Codex OAuth auth labels for explicit OpenAI OpenClaw auth order", async () => {
11961271
await withTempHome(
11971272
async (dir) => {

src/status/status-text.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -373,11 +373,22 @@ export async function buildStatusText(params: BuildStatusTextParams): Promise<st
373373
// labels differ; prefer the active auth label so status matches execution.
374374
selectedModelAuth = activeModelAuth;
375375
}
376-
const usageAuthLabel = modelRefs.activeDiffers ? activeModelAuth : selectedModelAuth;
376+
const hasSessionModelOverride = Boolean(
377+
sessionEntry?.providerOverride?.trim() || sessionEntry?.modelOverride?.trim(),
378+
);
379+
const useSelectedModelUsage =
380+
hasSessionModelOverride &&
381+
(sessionEntry?.modelOverrideSource === "user" ||
382+
(sessionEntry?.modelOverrideSource !== "auto" &&
383+
!hasSessionAutoModelFallbackProvenance(sessionEntry)));
384+
const usageStatusProvider = useSelectedModelUsage ? selectedStatusProvider : activeStatusProvider;
385+
const usageProvider = useSelectedModelUsage ? provider : activeProvider;
386+
const usageAuthLabel =
387+
modelRefs.activeDiffers && !useSelectedModelUsage ? activeModelAuth : selectedModelAuth;
377388
const selectedUsageCredentialType = resolveUsageCredentialType(usageAuthLabel);
378389
const useCodexSyntheticUsage =
379390
shouldUseCodexSyntheticUsage({
380-
provider: activeStatusProvider,
391+
provider: usageStatusProvider,
381392
effectiveHarness,
382393
}) &&
383394
(selectedUsageCredentialType === "oauth" || selectedUsageCredentialType === "token");
@@ -390,8 +401,8 @@ export async function buildStatusText(params: BuildStatusTextParams): Promise<st
390401
: undefined;
391402
const usageCredentialType = useCodexSyntheticUsage ? "token" : selectedUsageCredentialType;
392403
const currentUsageProvider =
393-
resolveUsageProviderId(activeStatusProvider, { credentialType: usageCredentialType }) ??
394-
resolveUsageProviderId(activeProvider, { credentialType: usageCredentialType });
404+
resolveUsageProviderId(usageStatusProvider, { credentialType: usageCredentialType }) ??
405+
resolveUsageProviderId(usageProvider, { credentialType: usageCredentialType });
395406
let usageLine: string | null = null;
396407
if (
397408
currentUsageProvider &&

0 commit comments

Comments
 (0)