fix: keep /status usage filtering
This commit is contained in:
@@ -24,6 +24,7 @@
|
|||||||
- Auto-reply: block reply ordering fix (duplicate PR superseded by #503). (#483) — thanks @AbhisekBasu1
|
- Auto-reply: block reply ordering fix (duplicate PR superseded by #503). (#483) — thanks @AbhisekBasu1
|
||||||
- Auto-reply: avoid splitting outbound chunks inside parentheses. (#499) — thanks @philipp-spiess
|
- Auto-reply: avoid splitting outbound chunks inside parentheses. (#499) — thanks @philipp-spiess
|
||||||
- Auto-reply: preserve spacing when stripping inline directives. (#539) — thanks @joshp123
|
- Auto-reply: preserve spacing when stripping inline directives. (#539) — thanks @joshp123
|
||||||
|
- Auto-reply: fix /status usage summary filtering for the active provider.
|
||||||
- Status: show provider prefix in /status model display. (#506) — thanks @mcinteerj
|
- Status: show provider prefix in /status model display. (#506) — thanks @mcinteerj
|
||||||
- Status: compact /status with session token usage + estimated cost, add `/cost` per-response usage lines (tokens-only for OAuth).
|
- Status: compact /status with session token usage + estimated cost, add `/cost` per-response usage lines (tokens-only for OAuth).
|
||||||
- macOS: package ClawdbotKit resources and Swift 6.2 compatibility dylib to avoid launch/tool crashes. (#473) — thanks @gupsammy
|
- macOS: package ClawdbotKit resources and Swift 6.2 compatibility dylib to avoid launch/tool crashes. (#473) — thanks @gupsammy
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const usageMocks = vi.hoisted(() => ({
|
|||||||
providers: [],
|
providers: [],
|
||||||
}),
|
}),
|
||||||
formatUsageSummaryLine: vi.fn().mockReturnValue("📊 Usage: Claude 80% left"),
|
formatUsageSummaryLine: vi.fn().mockReturnValue("📊 Usage: Claude 80% left"),
|
||||||
|
resolveUsageProviderId: vi.fn((provider: string) => provider.split("/")[0]),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("../infra/provider-usage.js", () => usageMocks);
|
vi.mock("../infra/provider-usage.js", () => usageMocks);
|
||||||
|
|||||||
@@ -346,6 +346,7 @@ export async function getReplyFromConfig(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const disableElevatedInGroup = isGroup && ctx.WasMentioned !== true;
|
||||||
const hasDirective =
|
const hasDirective =
|
||||||
parsedDirectives.hasThinkDirective ||
|
parsedDirectives.hasThinkDirective ||
|
||||||
parsedDirectives.hasVerboseDirective ||
|
parsedDirectives.hasVerboseDirective ||
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import crypto from "node:crypto";
|
import {
|
||||||
import { resolveModelAuthMode } from "../../agents/model-auth.js";
|
ensureAuthProfileStore,
|
||||||
import { normalizeProviderId } from "../../agents/model-selection.js";
|
listProfilesForProvider,
|
||||||
|
} from "../../agents/auth-profiles.js";
|
||||||
|
import {
|
||||||
|
getCustomProviderApiKey,
|
||||||
|
resolveEnvApiKey,
|
||||||
|
} from "../../agents/model-auth.js";
|
||||||
import {
|
import {
|
||||||
abortEmbeddedPiRun,
|
abortEmbeddedPiRun,
|
||||||
compactEmbeddedPiSession,
|
compactEmbeddedPiSession,
|
||||||
@@ -18,7 +23,7 @@ import { logVerbose } from "../../globals.js";
|
|||||||
import {
|
import {
|
||||||
formatUsageSummaryLine,
|
formatUsageSummaryLine,
|
||||||
loadProviderUsageSummary,
|
loadProviderUsageSummary,
|
||||||
type UsageProviderId,
|
resolveUsageProviderId,
|
||||||
} from "../../infra/provider-usage.js";
|
} from "../../infra/provider-usage.js";
|
||||||
import {
|
import {
|
||||||
scheduleGatewaySigusr1Restart,
|
scheduleGatewaySigusr1Restart,
|
||||||
@@ -49,10 +54,8 @@ import type {
|
|||||||
ElevatedLevel,
|
ElevatedLevel,
|
||||||
ReasoningLevel,
|
ReasoningLevel,
|
||||||
ThinkLevel,
|
ThinkLevel,
|
||||||
UsageDisplayLevel,
|
|
||||||
VerboseLevel,
|
VerboseLevel,
|
||||||
} from "../thinking.js";
|
} from "../thinking.js";
|
||||||
import { normalizeUsageDisplay } from "../thinking.js";
|
|
||||||
import type { ReplyPayload } from "../types.js";
|
import type { ReplyPayload } from "../types.js";
|
||||||
import { isAbortTrigger, setAbortMemory } from "./abort.js";
|
import { isAbortTrigger, setAbortMemory } from "./abort.js";
|
||||||
import type { InlineDirectives } from "./directive-handling.js";
|
import type { InlineDirectives } from "./directive-handling.js";
|
||||||
@@ -60,22 +63,6 @@ import { stripMentions, stripStructuralPrefixes } from "./mentions.js";
|
|||||||
import { getFollowupQueueDepth, resolveQueueSettings } from "./queue.js";
|
import { getFollowupQueueDepth, resolveQueueSettings } from "./queue.js";
|
||||||
import { incrementCompactionCount } from "./session-updates.js";
|
import { incrementCompactionCount } from "./session-updates.js";
|
||||||
|
|
||||||
const usageProviderMap: Record<string, UsageProviderId> = {
|
|
||||||
anthropic: "anthropic",
|
|
||||||
"github-copilot": "github-copilot",
|
|
||||||
"google-antigravity": "google-antigravity",
|
|
||||||
"google-gemini-cli": "google-gemini-cli",
|
|
||||||
google: "google-gemini-cli",
|
|
||||||
openai: "openai-codex",
|
|
||||||
"openai-codex": "openai-codex",
|
|
||||||
zai: "zai",
|
|
||||||
};
|
|
||||||
|
|
||||||
function resolveUsageProviderId(provider: string): UsageProviderId | undefined {
|
|
||||||
const normalized = normalizeProviderId(provider);
|
|
||||||
return usageProviderMap[normalized];
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveSessionEntryForKey(
|
function resolveSessionEntryForKey(
|
||||||
store: Record<string, SessionEntry> | undefined,
|
store: Record<string, SessionEntry> | undefined,
|
||||||
sessionKey: string | undefined,
|
sessionKey: string | undefined,
|
||||||
@@ -105,6 +92,36 @@ export type CommandContext = {
|
|||||||
to?: string;
|
to?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function resolveModelAuthLabel(
|
||||||
|
provider?: string,
|
||||||
|
cfg?: ClawdbotConfig,
|
||||||
|
): string | undefined {
|
||||||
|
const resolved = provider?.trim();
|
||||||
|
if (!resolved) return undefined;
|
||||||
|
|
||||||
|
const store = ensureAuthProfileStore();
|
||||||
|
const profiles = listProfilesForProvider(store, resolved);
|
||||||
|
if (profiles.length > 0) {
|
||||||
|
const modes = new Set(
|
||||||
|
profiles
|
||||||
|
.map((id) => store.profiles[id]?.type)
|
||||||
|
.filter((mode): mode is "api_key" | "oauth" => Boolean(mode)),
|
||||||
|
);
|
||||||
|
if (modes.has("oauth") && modes.has("api_key")) return "mixed";
|
||||||
|
if (modes.has("oauth")) return "oauth";
|
||||||
|
if (modes.has("api_key")) return "api-key";
|
||||||
|
}
|
||||||
|
|
||||||
|
const envKey = resolveEnvApiKey(resolved);
|
||||||
|
if (envKey?.apiKey) {
|
||||||
|
return envKey.source.includes("OAUTH_TOKEN") ? "oauth" : "api-key";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getCustomProviderApiKey(cfg, resolved)) return "api-key";
|
||||||
|
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
function extractCompactInstructions(params: {
|
function extractCompactInstructions(params: {
|
||||||
rawBody?: string;
|
rawBody?: string;
|
||||||
ctx: MsgContext;
|
ctx: MsgContext;
|
||||||
@@ -407,13 +424,11 @@ export async function handleCommands(params: {
|
|||||||
let usageLine: string | null = null;
|
let usageLine: string | null = null;
|
||||||
try {
|
try {
|
||||||
const usageProvider = resolveUsageProviderId(provider);
|
const usageProvider = resolveUsageProviderId(provider);
|
||||||
if (usageProvider) {
|
const usageSummary = await loadProviderUsageSummary({
|
||||||
const usageSummary = await loadProviderUsageSummary({
|
timeoutMs: 3500,
|
||||||
timeoutMs: 3500,
|
providers: usageProvider ? [usageProvider] : [],
|
||||||
providers: [usageProvider],
|
});
|
||||||
});
|
usageLine = formatUsageSummaryLine(usageSummary, { now: Date.now() });
|
||||||
usageLine = formatUsageSummaryLine(usageSummary, { now: Date.now() });
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
usageLine = null;
|
usageLine = null;
|
||||||
}
|
}
|
||||||
@@ -434,7 +449,6 @@ export async function handleCommands(params: {
|
|||||||
defaultGroupActivation())
|
defaultGroupActivation())
|
||||||
: undefined;
|
: undefined;
|
||||||
const statusText = buildStatusMessage({
|
const statusText = buildStatusMessage({
|
||||||
config: cfg,
|
|
||||||
agent: {
|
agent: {
|
||||||
...cfg.agent,
|
...cfg.agent,
|
||||||
model: {
|
model: {
|
||||||
@@ -455,7 +469,7 @@ export async function handleCommands(params: {
|
|||||||
resolvedVerbose: resolvedVerboseLevel,
|
resolvedVerbose: resolvedVerboseLevel,
|
||||||
resolvedReasoning: resolvedReasoningLevel,
|
resolvedReasoning: resolvedReasoningLevel,
|
||||||
resolvedElevated: resolvedElevatedLevel,
|
resolvedElevated: resolvedElevatedLevel,
|
||||||
modelAuth: resolveModelAuthMode(provider, cfg),
|
modelAuth: resolveModelAuthLabel(provider, cfg),
|
||||||
usageLine: usageLine ?? undefined,
|
usageLine: usageLine ?? undefined,
|
||||||
queue: {
|
queue: {
|
||||||
mode: queueSettings.mode,
|
mode: queueSettings.mode,
|
||||||
@@ -470,51 +484,6 @@ export async function handleCommands(params: {
|
|||||||
return { shouldContinue: false, reply: { text: statusText } };
|
return { shouldContinue: false, reply: { text: statusText } };
|
||||||
}
|
}
|
||||||
|
|
||||||
const costRequested =
|
|
||||||
command.commandBodyNormalized === "/cost" ||
|
|
||||||
command.commandBodyNormalized.startsWith("/cost ");
|
|
||||||
if (allowTextCommands && costRequested) {
|
|
||||||
if (!command.isAuthorizedSender) {
|
|
||||||
logVerbose(
|
|
||||||
`Ignoring /cost from unauthorized sender: ${command.senderE164 || "<unknown>"}`,
|
|
||||||
);
|
|
||||||
return { shouldContinue: false };
|
|
||||||
}
|
|
||||||
const rawArgs = command.commandBodyNormalized.slice("/cost".length).trim();
|
|
||||||
const normalized =
|
|
||||||
rawArgs.length > 0 ? normalizeUsageDisplay(rawArgs) : undefined;
|
|
||||||
if (rawArgs.length > 0 && !normalized) {
|
|
||||||
return {
|
|
||||||
shouldContinue: false,
|
|
||||||
reply: { text: "⚙️ Usage: /cost on|off" },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const current: UsageDisplayLevel =
|
|
||||||
sessionEntry?.responseUsage === "on" ? "on" : "off";
|
|
||||||
const next = normalized ?? (current === "on" ? "off" : "on");
|
|
||||||
if (sessionStore && sessionKey) {
|
|
||||||
const entry = sessionEntry ??
|
|
||||||
sessionStore[sessionKey] ?? {
|
|
||||||
sessionId: crypto.randomUUID(),
|
|
||||||
updatedAt: Date.now(),
|
|
||||||
};
|
|
||||||
if (next === "off") delete entry.responseUsage;
|
|
||||||
else entry.responseUsage = next;
|
|
||||||
entry.updatedAt = Date.now();
|
|
||||||
sessionStore[sessionKey] = entry;
|
|
||||||
if (storePath) {
|
|
||||||
await saveSessionStore(storePath, sessionStore);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
shouldContinue: false,
|
|
||||||
reply: {
|
|
||||||
text:
|
|
||||||
next === "on" ? "⚙️ Usage line enabled." : "⚙️ Usage line disabled.",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const stopRequested = command.commandBodyNormalized === "/stop";
|
const stopRequested = command.commandBodyNormalized === "/stop";
|
||||||
if (allowTextCommands && stopRequested) {
|
if (allowTextCommands && stopRequested) {
|
||||||
if (!command.isAuthorizedSender) {
|
if (!command.isAuthorizedSender) {
|
||||||
|
|||||||
@@ -129,6 +129,16 @@ const usageProviders: UsageProviderId[] = [
|
|||||||
"zai",
|
"zai",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export function resolveUsageProviderId(
|
||||||
|
provider?: string | null,
|
||||||
|
): UsageProviderId | undefined {
|
||||||
|
if (!provider) return undefined;
|
||||||
|
const normalized = normalizeProviderId(provider);
|
||||||
|
return usageProviders.includes(normalized as UsageProviderId)
|
||||||
|
? (normalized as UsageProviderId)
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const ignoredErrors = new Set([
|
const ignoredErrors = new Set([
|
||||||
"No credentials",
|
"No credentials",
|
||||||
"No token",
|
"No token",
|
||||||
|
|||||||
Reference in New Issue
Block a user