fix: improve status usage filtering and directives
This commit is contained in:
@@ -58,6 +58,7 @@
|
|||||||
- TUI: refresh status bar after think/verbose/reasoning changes. (#519) — thanks @jdrhyne
|
- TUI: refresh status bar after think/verbose/reasoning changes. (#519) — thanks @jdrhyne
|
||||||
- Status: show Verbose/Elevated only when enabled.
|
- Status: show Verbose/Elevated only when enabled.
|
||||||
- Status: filter usage summary to the active model provider.
|
- Status: filter usage summary to the active model provider.
|
||||||
|
- Status: map model providers to usage sources so unrelated usage doesn’t appear.
|
||||||
- Commands: allow /elevated off in groups without a mention; keep /elevated on mention-gated.
|
- Commands: allow /elevated off in groups without a mention; keep /elevated on mention-gated.
|
||||||
- Commands: keep multi-directive messages from clearing directive handling.
|
- Commands: keep multi-directive messages from clearing directive handling.
|
||||||
- Commands: warn when /elevated runs in direct (unsandboxed) runtime.
|
- Commands: warn when /elevated runs in direct (unsandboxed) runtime.
|
||||||
|
|||||||
@@ -538,6 +538,39 @@ describe("directive behavior", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("handles multiple directives in a single message", async () => {
|
||||||
|
await withTempHome(async (home) => {
|
||||||
|
vi.mocked(runEmbeddedPiAgent).mockReset();
|
||||||
|
|
||||||
|
const res = await getReplyFromConfig(
|
||||||
|
{
|
||||||
|
Body: "/elevated off\n/verbose on",
|
||||||
|
From: "+1222",
|
||||||
|
To: "+1222",
|
||||||
|
Provider: "whatsapp",
|
||||||
|
SenderE164: "+1222",
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
agent: {
|
||||||
|
model: "anthropic/claude-opus-4-5",
|
||||||
|
workspace: path.join(home, "clawd"),
|
||||||
|
elevated: {
|
||||||
|
allowFrom: { whatsapp: ["+1222"] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
whatsapp: { allowFrom: ["+1222"] },
|
||||||
|
session: { store: path.join(home, "sessions.json") },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||||
|
expect(text).toContain("Elevated mode disabled.");
|
||||||
|
expect(text).toContain("Verbose logging enabled.");
|
||||||
|
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("acks queue directive and persists override", async () => {
|
it("acks queue directive and persists override", async () => {
|
||||||
await withTempHome(async (home) => {
|
await withTempHome(async (home) => {
|
||||||
vi.mocked(runEmbeddedPiAgent).mockReset();
|
vi.mocked(runEmbeddedPiAgent).mockReset();
|
||||||
|
|||||||
@@ -332,7 +332,11 @@ export async function getReplyFromConfig(
|
|||||||
let parsedDirectives = parseInlineDirectives(rawBody, {
|
let parsedDirectives = parseInlineDirectives(rawBody, {
|
||||||
modelAliases: configuredAliases,
|
modelAliases: configuredAliases,
|
||||||
});
|
});
|
||||||
if (isGroup && ctx.WasMentioned !== true && parsedDirectives.hasElevatedDirective) {
|
if (
|
||||||
|
isGroup &&
|
||||||
|
ctx.WasMentioned !== true &&
|
||||||
|
parsedDirectives.hasElevatedDirective
|
||||||
|
) {
|
||||||
if (parsedDirectives.elevatedLevel !== "off") {
|
if (parsedDirectives.elevatedLevel !== "off") {
|
||||||
parsedDirectives = {
|
parsedDirectives = {
|
||||||
...parsedDirectives,
|
...parsedDirectives,
|
||||||
@@ -358,7 +362,6 @@ export async function getReplyFromConfig(
|
|||||||
if (noMentions.trim().length > 0) {
|
if (noMentions.trim().length > 0) {
|
||||||
const directiveOnlyCheck = parseInlineDirectives(noMentions, {
|
const directiveOnlyCheck = parseInlineDirectives(noMentions, {
|
||||||
modelAliases: configuredAliases,
|
modelAliases: configuredAliases,
|
||||||
disableElevated: disableElevatedInGroup,
|
|
||||||
});
|
});
|
||||||
if (directiveOnlyCheck.cleaned.trim().length > 0) {
|
if (directiveOnlyCheck.cleaned.trim().length > 0) {
|
||||||
parsedDirectives = clearInlineDirectives(parsedDirectives.cleaned);
|
parsedDirectives = clearInlineDirectives(parsedDirectives.cleaned);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
getCustomProviderApiKey,
|
getCustomProviderApiKey,
|
||||||
resolveEnvApiKey,
|
resolveEnvApiKey,
|
||||||
} from "../../agents/model-auth.js";
|
} from "../../agents/model-auth.js";
|
||||||
|
import { normalizeProviderId } from "../../agents/model-selection.js";
|
||||||
import {
|
import {
|
||||||
abortEmbeddedPiRun,
|
abortEmbeddedPiRun,
|
||||||
compactEmbeddedPiSession,
|
compactEmbeddedPiSession,
|
||||||
@@ -23,6 +24,7 @@ import { logVerbose } from "../../globals.js";
|
|||||||
import {
|
import {
|
||||||
formatUsageSummaryLine,
|
formatUsageSummaryLine,
|
||||||
loadProviderUsageSummary,
|
loadProviderUsageSummary,
|
||||||
|
type UsageProviderId,
|
||||||
} from "../../infra/provider-usage.js";
|
} from "../../infra/provider-usage.js";
|
||||||
import {
|
import {
|
||||||
scheduleGatewaySigusr1Restart,
|
scheduleGatewaySigusr1Restart,
|
||||||
@@ -37,7 +39,6 @@ import {
|
|||||||
normalizeCommandBody,
|
normalizeCommandBody,
|
||||||
shouldHandleTextCommands,
|
shouldHandleTextCommands,
|
||||||
} from "../commands-registry.js";
|
} from "../commands-registry.js";
|
||||||
import { normalizeProviderId } from "../../agents/model-selection.js";
|
|
||||||
import {
|
import {
|
||||||
normalizeGroupActivation,
|
normalizeGroupActivation,
|
||||||
parseActivationCommand,
|
parseActivationCommand,
|
||||||
@@ -63,6 +64,22 @@ 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,
|
||||||
@@ -423,11 +440,14 @@ export async function handleCommands(params: {
|
|||||||
}
|
}
|
||||||
let usageLine: string | null = null;
|
let usageLine: string | null = null;
|
||||||
try {
|
try {
|
||||||
const usageSummary = await loadProviderUsageSummary({
|
const usageProvider = resolveUsageProviderId(provider);
|
||||||
timeoutMs: 3500,
|
if (usageProvider) {
|
||||||
providers: [normalizeProviderId(provider)],
|
const usageSummary = await loadProviderUsageSummary({
|
||||||
});
|
timeoutMs: 3500,
|
||||||
usageLine = formatUsageSummaryLine(usageSummary, { now: Date.now() });
|
providers: [usageProvider],
|
||||||
|
});
|
||||||
|
usageLine = formatUsageSummaryLine(usageSummary, { now: Date.now() });
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
usageLine = null;
|
usageLine = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,24 @@ describe("buildStatusMessage", () => {
|
|||||||
expect(text).toContain("Queue: collect");
|
expect(text).toContain("Queue: collect");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("shows verbose/elevated labels only when enabled", () => {
|
||||||
|
const text = buildStatusMessage({
|
||||||
|
agent: { model: "anthropic/claude-opus-4-5" },
|
||||||
|
sessionEntry: { sessionId: "v1", updatedAt: 0 },
|
||||||
|
sessionKey: "agent:main:main",
|
||||||
|
sessionScope: "per-sender",
|
||||||
|
resolvedThink: "low",
|
||||||
|
resolvedVerbose: "on",
|
||||||
|
resolvedElevated: "on",
|
||||||
|
queue: { mode: "collect", depth: 0 },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(text).toContain("Verbose");
|
||||||
|
expect(text).toContain("Elevated");
|
||||||
|
expect(text).not.toContain("Verbose:");
|
||||||
|
expect(text).not.toContain("Elevated:");
|
||||||
|
});
|
||||||
|
|
||||||
it("prefers model overrides over last-run model", () => {
|
it("prefers model overrides over last-run model", () => {
|
||||||
const text = buildStatusMessage({
|
const text = buildStatusMessage({
|
||||||
agent: {
|
agent: {
|
||||||
|
|||||||
Reference in New Issue
Block a user