fix(runner): account for complete Grok prompt receipts

Bind terminal Grok usage to its prompt response, preserve per-turn cost across the sidecar contract, and identify direct xAI billing without changing managed credentials.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-11-03 23:51:21 -06:00
1 parent 8b5a0ebe1f
commit c469b9c218
14 files changed
+300 -15

No files matched your search

@@ -659,6 +659,19 @@ billable usage. These receipt corrections also apply to non-eval ACPX sessions.
The durable redactor preserves these known numeric token-counter fields;
string/object values under the same names remain redacted as sensitive data.
Grok 1.0.13 reports whole-prompt usage in `session/prompt` response
`_meta.usage`. The sibling `_meta` counters describe only the last model call.
Runner binds the nested receipt to the outbound JSON-RPC request and session,
then emits it before the terminal event. Replayed, conflicting, malformed, or
incomplete receipts cannot supply complete accounting. ACP input includes cache
reads and cache creation; Runner separates those buckets and does not add the
reasoning subset to output again. A complete `costUsdTicks` uses 10 billion ticks
per USD. Missing or partial cost remains unknown. The sidecar's `cost.basis`
distinguishes a `turn` total from the default session-cumulative cost, so later
turns do not inherit or subtract an earlier turn's bill.
These semantics follow the upstream [prompt usage contract](https://github.com/xai-org/grok-build/blob/2bdd1d6a6369de0e8c68132ea4539e9abd9e14a8/crates/codegen/xai-grok-shell/src/extensions/notification.rs)
and [prompt response tests](https://github.com/xai-org/grok-build/blob/2bdd1d6a6369de0e8c68132ea4539e9abd9e14a8/crates/codegen/xai-grok-shell/src/agent/mvp_agent/prompt_response_meta_tests.rs).
`all` means the maintained enabled campaign, not every matching file in the
roster directory. A missing campaign file fails closed. Within an enabled
lane, a missing remote profile or unavailable provider is retained as an
@@ -225,6 +225,14 @@
},
"additionalProperties": false,
"allOf": [
{
"if": { "properties": { "eventType": { "const": "runtime.event" }, "payload": {
"type": "object",
"properties": { "type": { "const": "status" }, "tag": { "const": "usage_update" } },
"required": ["type", "tag"]
} } },
"then": { "properties": { "payload": { "type": "object", "properties": { "cost": { "$ref": "#/$defs/usageCost" } } } } }
},
{
"if": { "properties": { "eventType": { "const": "runtime.rich_event" } } },
"then": {
@@ -237,6 +245,16 @@
}
]
},
"usageCost": {
"description": "Provider cost is session-cumulative unless basis explicitly identifies a complete turn total. A turn total is never a session counter.",
"type": ["object", "null"],
"properties": {
"amount": { "type": ["number", "null"], "minimum": 0 },
"currency": { "type": ["string", "null"], "maxLength": 16 },
"basis": { "enum": ["turn", "session_cumulative"] }
},
"additionalProperties": false
},
"richEvent": {
"type": "object",
"required": [
@@ -1208,9 +1208,11 @@ fn normalize_acpx_status(
Some(Value::String(currency)) => currency.eq_ignore_ascii_case("USD"),
Some(_) => false,
};
// ACPX 0.13.1 documents breakdown as per-turn usage while cost is
// session-cumulative. Keep those authorities separate so consumers do
// not add the same tokens twice or treat cumulative cost as a delta.
let turn_cost = payload.pointer("/cost/basis").and_then(Value::as_str) == Some("turn");
let provider_cost = if cost_is_usd {
payload.pointer("/cost/amount").and_then(Value::as_f64)
.filter(|value| value.is_finite() && *value >= 0.0).unwrap_or(0.0)
} else { 0.0 };
let cumulative = json!({
"inputTokens": 0,
"outputTokens": 0,
@@ -1218,15 +1220,7 @@ fn normalize_acpx_status(
"cacheWriteTokens": 0,
"activeSeconds": 0.0,
"requests": provider_requests,
"providerCostUsd": if cost_is_usd {
payload
.pointer("/cost/amount")
.and_then(Value::as_f64)
.filter(|value| value.is_finite() && *value >= 0.0)
.unwrap_or(0.0)
} else {
0.0
},
"providerCostUsd": if turn_cost { 0.0 } else { provider_cost },
});
let run_delta = json!({
"inputTokens": nonnegative_u64(breakdown.get("inputTokens")),
@@ -1243,7 +1237,7 @@ fn normalize_acpx_status(
),
"activeSeconds": 0.0,
"requests": 1,
"providerCostUsd": 0.0,
"providerCostUsd": if turn_cost { provider_cost } else { 0.0 },
});
return vec![NormalizedProviderEvent {
event_type: "usage.reported".to_owned(),
@@ -1499,6 +1493,28 @@ fn has_rfc_uri_scheme_prefix(value: &str) -> bool {
mod tests {
use super::*;
#[test]
fn acpx_turn_cost_is_not_a_cumulative_session_price() {
let base = json!({"tag": "usage_update", "breakdown": {
"inputTokens": 30787, "outputTokens": 5763, "cachedReadTokens": 475008,
"cachedWriteTokens": 0, "thoughtTokens": 0
}, "cost": {"amount": 0.336842, "currency": "USD", "basis": "turn"}});
let first = normalize_acpx_status(&base, "item", "turn-1", 1);
assert_eq!(first[0].payload["runDeltaAvailable"], true);
assert_eq!(first[0].payload["runDelta"]["providerCostUsd"], 0.336842);
assert_eq!(first[0].payload["cumulative"]["providerCostUsd"], 0.0);
assert_eq!(first[0].payload["runDelta"]["inputTokens"], 30787);
assert_eq!(first[0].payload["runDelta"]["outputTokens"], 5763);
let mut second = base.clone();
second["cost"]["amount"] = json!(0.001);
let next = normalize_acpx_status(&second, "next", "turn-2", 2);
assert_eq!(next[0].payload["runDelta"]["providerCostUsd"], 0.001);
second["cost"].as_object_mut().unwrap().remove("basis");
let cumulative = normalize_acpx_status(&second, "legacy", "turn-3", 3);
assert_eq!(cumulative[0].payload["cumulative"]["providerCostUsd"], 0.001);
assert_eq!(cumulative[0].payload["runDelta"]["providerCostUsd"], 0.0);
}
#[test]
fn performance_diagnostics_keep_only_bounded_content_free_measurements() {
let mut spans = vec![json!({"name":"private prompt","startedAtMs":1,"endedAtMs":2})];
@@ -8,7 +8,7 @@ import { appendSemanticToolReceipt, readNativeSemanticReceipt, semanticInputSha2
import { startRunnerToolBridge, type RunnerToolCall } from "../drivers/runner-tool-bridge.js";
import { validatePrpStructuredRunResult } from "../protocol/replay-contract.js";
import { resolveCompletionContinuation } from "../drivers/completion-continuation.js";
import { acpxUsageEstimateNotice, persistedAcpxTurnUsage, persistedCursorUsageNotice } from "../drivers/acpx/usage-accounting.js";
import { acpxUsageEstimateNotice, persistedAcpxTurnUsage, persistedCursorUsageNotice, qualifiedAcpxUsageBreakdown } from "../drivers/acpx/usage-accounting.js";
import { stripTypeScriptTypes } from "node:module";
import { cursorPlanToolIdentity, cursorToolIdentity } from "../drivers/acpx/cursor-plan-tool-identity.js";
import { createHash, randomUUID } from "node:crypto";
@@ -21,6 +21,22 @@ import { createKimiMessageProjection } from "../drivers/acpx/kimi-message-bounda
import { afterEach, describe, expect, it, vi } from "vitest";
describe("live sidecar pending request snapshots", () => {
it("preserves explicit turn cost through the sidecar's closed usage projection", () => {
const source = readFileSync(new URL("./acpx-runtime-sidecar.ts", import.meta.url), "utf8");
const start = source.indexOf("function safeUsage(");
const end = source.indexOf("function safeOutput(", start);
const project = new Function("record", "qualifiedAcpxUsageBreakdown", "initializedAgent", "safeText", "text",
`${stripTypeScriptTypes(source.slice(start, end))}; return safeUsage;`)(
(value: unknown) => value && typeof value === "object" ? value : {}, qualifiedAcpxUsageBreakdown, "grok",
(value: string) => value, (value: unknown) => typeof value === "string" ? value : "",
);
expect(project({ amount: 0.336842, currency: "USD", basis: "turn", rawSecret: "excluded" }, null)).toEqual({
cost: { amount: 0.336842, currency: "USD", basis: "turn" }, breakdown: null,
});
expect(project({ amount: 0.7, currency: "USD" }, null)).toEqual({ cost: { amount: 0.7, currency: "USD" }, breakdown: null });
expect(project(undefined, null)).toEqual({ cost: null, breakdown: null });
});
it("attests current callback identities after the live status barrier without exposing forms", async () => {
const source = readFileSync(new URL("./acpx-runtime-sidecar.ts", import.meta.url), "utf8");
const start = source.indexOf(' if (request.command === "session.snapshot") {');
@@ -1199,6 +1199,7 @@ function safeUsage(cost: unknown, breakdown: unknown): Record<string, unknown> {
? null
: {
amount: safeNonNegativeNumber(nativeCost.amount),
...(nativeCost.basis === "turn" ? { basis: "turn" } : {}),
currency: safeText(text(nativeCost.currency)).slice(0, 16) || null,
},
breakdown:
@@ -19,6 +19,7 @@ import { describe, expect, it, vi } from "vitest";
import type { VerifiedAcpxCommandLease } from "./installation-integrity.js";
import { openCodexAcpxRuntime } from "./codex-runtime-adapter.js";
import { cursorInstructionBinding } from "./cursor-instructions.js";
import { persistedAcpxTurnUsage } from "./usage-accounting.js";
import { createAcpxCommandLeaseOwner } from "./command-lease-owner.js";
import { resolveQualifiedAcpxProfile } from "./qualified-profiles.js";
import type { AcpxRuntimePortOpenOptions } from "./runtime-host.js";
@@ -913,6 +914,44 @@ describe("Codex ACPX runtime adapter", () => {
expect(assertWorkspaceHeld).toHaveBeenCalledOnce();
expect(command.spawn).not.toHaveBeenCalled();
});
it("exposes only the active Grok prompt response as terminal usage across consecutive turns", async () => {
const runtime = fakeRuntime();
const durableRecord = { acpxRecordId: "record-1", acpSessionId: "backend-1", agentSessionId: "agent-1", lastRequestId: "previous" };
const durableStore: AcpSessionStore = { load: vi.fn(async () => structuredClone(durableRecord) as never), save: vi.fn() };
let created!: AcpRuntimeOptions;
const options = openOptions(fakeCommand());
options.profile = { ...options.profile, agent: "grok" };
const port = await openCodexAcpxRuntime(options, { createRegistry: () => registry(), createStore: () => durableStore,
createRuntime: value => { created = value; return runtime; } });
let sequence = 0;
vi.mocked(runtime.startTurn).mockImplementation(input => {
const wireId = ++sequence;
durableRecord.lastRequestId = input.requestId;
created.onAcpMessage?.("outbound", { id: wireId, method: "session/prompt", params: { sessionId: "backend-1" } });
created.onAcpMessage?.("inbound", { id: wireId, result: { _meta: {
sessionId: "backend-1", promptId: `prompt-${wireId}`, requestId: `prompt-${wireId}`,
usage: { inputTokens: 50, outputTokens: 5, totalTokens: 55, cachedReadTokens: 20, cacheCreationTokens: 0,
reasoningTokens: 2, modelCalls: 1, costUsdTicks: wireId === 1 ? 10000000 : 5000000 },
} } });
return { requestId: input.requestId, promptStarted: Promise.resolve(), events: (async function* () {})(),
result: Promise.resolve({ status: "completed" as const }), cancel: async () => undefined, closeStream: async () => undefined };
});
try {
for (const [requestId, amount] of [["runner-1", 0.001], ["runner-2", 0.0005]] as const) {
const before = await port.getStatus();
const turn = port.startTurn({ requestId, text: "fixture" });
await turn.result;
const after = await port.getStatus();
expect(persistedAcpxTurnUsage(before, after, requestId)).toMatchObject({
cost: { amount, currency: "USD", basis: "turn" },
breakdown: { inputTokens: 30, outputTokens: 5, cachedReadTokens: 20, thoughtTokens: 0 },
});
created.onAcpMessage?.("inbound", { id: sequence, error: { message: "late conflicting response" } });
expect(await port.getStatus()).toEqual(after);
}
} finally { await port.close({ reason: "fixture done" }); }
});
it("reads verified status from durable state without draining live updates", async () => {
const runtime = fakeRuntime();
vi.mocked(runtime.getStatus!).mockReturnValue(new Promise(() => {}));
@@ -1,6 +1,7 @@
import { admitAcpxSessionContext } from "./session-context-admission.js";
import { createKimiInstructionAdmission } from "./kimi-instructions.js";
import { kimiReadOnlyPermissionAllowed } from "./kimi-config.js";
import { createGrokPromptUsageCollector } from "./grok-usage.js";
import { resolveGrokDisableWebSearch } from "./grok-web-search.js";
import { resolveGrokEffort, createGrokEffortAdmission } from "./grok-effort.js";
import { resolveClaudeEffort, createClaudeEffortAdmission } from "./claude-effort.js";
@@ -295,6 +296,7 @@ export async function openQualifiedAcpxRuntime(
const extensionProfile = ACPX_CAPABILITY_PROFILES[options.profile.agent];
const extensionRequests = new Set(extensionProfile.extensionRequests);
const extensionNotifications = new Set(extensionProfile.extensionNotifications);
const promptUsage = options.profile.agent === "grok" ? createGrokPromptUsageCollector() : undefined;
const extensionBoundary: AcpxRuntimeExtensionBoundary = {
active: null, sessionIds: new Set(), steering: null,
};
@@ -466,6 +468,10 @@ export async function openQualifiedAcpxRuntime(
: null;
},
onSessionNotification: acceptGoalNotification,
onAcpMessage: (direction, message) => {
const active = extensionBoundary.active;
if (active && !active.signal.aborted) promptUsage?.observe(direction, message);
},
spawnEnvironment: () => ({
...definedEnvironment(options.launchEnvironment),
...(options.profile.agent === "claude"
@@ -685,6 +691,7 @@ export async function openQualifiedAcpxRuntime(
extensionBoundary,
options.profile.agent === "copilot",
options.profile.agent === "copilot" ? assertCopilotPromptPolicy : undefined,
promptUsage,
);
} catch (error) {
const cleanupReason = "ACPX runtime identity validation failed";
@@ -1117,6 +1124,7 @@ function runtimePort(
extensionBoundary: AcpxRuntimeExtensionBoundary,
monitorProviderDeath: boolean,
assertPromptPolicy?: (text: string) => void,
promptUsage?: ReturnType<typeof createGrokPromptUsageCollector>,
): AcpxRuntimePort {
extensionBoundary.sessionIds = new Set([identity.backendSessionId]);
let extensionControls: Promise<void> = Promise.resolve();
@@ -1375,7 +1383,8 @@ function runtimePort(
return structuredClone(identity);
},
async getStatus() {
return await persistedRuntimeStatus(sessionStore, handle, identity);
const status = await persistedRuntimeStatus(sessionStore, handle, identity);
return promptUsage ? { ...status, terminalPromptUsage: promptUsage.snapshot() } : status;
},
steeringCapability() {
return extensionBoundary.steering === null ? null : structuredClone(extensionBoundary.steering);
@@ -1488,6 +1497,7 @@ function runtimePort(
onRequest: input.onExtensionRequest, onNotification: input.onExtensionNotification,
};
extensionBoundary.active = extensionTurn;
promptUsage?.begin(input.requestId, identity.backendSessionId);
// A guardian can outlive the provider while ACP awaits a callback.
// Native exit or a typed ACP agent disconnect retires this turn; losing
// the controller connection must preserve deliverable input instead.
@@ -0,0 +1,77 @@
import { describe, expect, it } from "vitest";
import { createGrokPromptUsageCollector, grokPromptUsage } from "./grok-usage.js";
import { persistedAcpxTurnUsage } from "./usage-accounting.js";
const usage = { inputTokens: 505795, outputTokens: 5763, totalTokens: 511558, cachedReadTokens: 475008,
cacheCreationTokens: 0, reasoningTokens: 531, modelCalls: 20, costUsdTicks: 3368420000 };
const meta = { sessionId: "session-1", promptId: "prompt-1", requestId: "prompt-1", usage,
inputTokens: 999, outputTokens: 999, totalTokens: 999 };
const request = { jsonrpc: "2.0", id: 7, method: "session/prompt", params: { sessionId: "session-1" } };
const response = { jsonrpc: "2.0", id: 7, result: { stopReason: "end_turn", _meta: meta } };
describe("Grok terminal prompt usage", () => {
it("uses the complete prompt ledger with disjoint cache buckets and exact USD tick conversion", () => {
expect(grokPromptUsage({ ...meta, usage: { ...usage, cacheCreationTokens: 100 } }, "session-1")).toEqual({
promptId: "prompt-1", event: { type: "status", tag: "usage_update", text: "terminal prompt usage",
cost: { amount: 0.336842, currency: "USD", basis: "turn" },
breakdown: { inputTokens: 30687, outputTokens: 5763, totalTokens: 511558, cachedReadTokens: 475008,
cachedWriteTokens: 100, thoughtTokens: 0 } },
});
});
it.each([
{ usageIsIncomplete: true }, { usageIsIncomplete: "false" }, { cachedReadTokens: 505796 },
{ inputTokens: -1 }, { outputTokens: 1.5 }, { reasoningTokens: 5764 }, { totalTokens: 1 },
{ modelCalls: undefined }, { inputTokens: Number.MAX_SAFE_INTEGER + 1 },
])("refuses incomplete or invalid token authority: %j", patch => {
expect(grokPromptUsage({ ...meta, usage: { ...usage, ...patch } }, "session-1")).toBeNull();
});
it.each([{ costUsdTicks: undefined }, { costUsdTicks: -1 }, { costIsPartial: true }, { costIsPartial: "false" }])(
"preserves complete tokens without inventing cost: %j", patch => {
const result = grokPromptUsage({ ...meta, usage: { ...usage, ...patch } }, "session-1");
expect(result?.event.breakdown).toMatchObject({ inputTokens: 30787, outputTokens: 5763 });
expect(result?.event).not.toHaveProperty("cost");
});
it("binds only the matching prompt response, rejects replay and leaves the next turn's price independent", () => {
const collector = createGrokPromptUsageCollector();
collector.begin("runner-1", "session-1");
collector.observe("inbound", response);
expect(collector.snapshot()).toBeNull();
collector.observe("outbound", request);
collector.observe("inbound", { ...response, id: 6 });
collector.observe("inbound", { ...response, method: "_x.ai/session/update" });
expect(collector.snapshot()).toBeNull();
collector.observe("inbound", response);
const first = collector.snapshot();
collector.observe("inbound", response);
expect(collector.snapshot()).toEqual(first);
const before = { lastRequestId: "runner-1", terminalPromptUsage: first };
expect(persistedAcpxTurnUsage({}, before, "runner-1")?.cost).toEqual({ amount: 0.336842, currency: "USD", basis: "turn" });
expect(persistedAcpxTurnUsage(before, before, "runner-1")).toBeNull();
expect(persistedAcpxTurnUsage({}, before, "foreign")).toBeNull();
collector.begin("runner-2", "session-1");
collector.observe("inbound", response);
expect(collector.snapshot()).toBeNull();
collector.observe("outbound", { ...request, id: 8 });
collector.observe("inbound", { ...response, id: 8, result: { _meta: { ...meta, promptId: "prompt-2", requestId: "prompt-2",
usage: { ...usage, costUsdTicks: 10000000 } } } });
const after = { lastRequestId: "runner-2", terminalPromptUsage: collector.snapshot() };
expect(persistedAcpxTurnUsage(before, after, "runner-2")?.cost).toEqual({ amount: 0.001, currency: "USD", basis: "turn" });
});
it.each(["foreign-session", "conflicting-receipt", "second-prompt", "rpc-error", "reused-prompt"])("rejects %s", kind => {
const collector = createGrokPromptUsageCollector();
collector.begin("runner-1", "session-1");
collector.observe("outbound", request);
collector.observe("inbound", response);
if (kind === "reused-prompt") { collector.begin("runner-2", "session-1"); collector.observe("outbound", request); }
if (kind === "second-prompt") collector.observe("outbound", request);
else collector.observe("inbound", kind === "rpc-error" ? { ...response, error: {} } : { ...response, result: { _meta: {
...meta, ...(kind === "foreign-session" ? { sessionId: "session-2" } : {}),
...(kind === "conflicting-receipt" ? { usage: { ...usage, costUsdTicks: 1 } } : {}),
} } });
expect(collector.snapshot()).toBeNull();
});
});
@@ -0,0 +1,63 @@
type RecordValue = Record<string, unknown>;
const record = (value: unknown): RecordValue => value !== null && typeof value === "object" && !Array.isArray(value) ? value as RecordValue : {};
const integer = (value: unknown): value is number => Number.isSafeInteger(value) && Number(value) >= 0;
const id = (value: unknown): value is string => typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,239}$/.test(value);
export interface TerminalPromptUsage {
requestId: string;
promptId: string;
event: RecordValue;
}
export function grokPromptUsage(metaValue: unknown, sessionId: string): Omit<TerminalPromptUsage, "requestId"> | null {
const meta = record(metaValue), usage = record(meta.usage);
if (meta.sessionId !== sessionId || !id(meta.promptId) || meta.requestId !== meta.promptId
|| (usage.usageIsIncomplete !== undefined && usage.usageIsIncomplete !== false)) return null;
const { inputTokens, outputTokens, totalTokens, cachedReadTokens, cacheCreationTokens, reasoningTokens, modelCalls } = usage;
if (![inputTokens, outputTokens, totalTokens, cachedReadTokens, cacheCreationTokens, reasoningTokens, modelCalls].every(integer)
|| Number(inputTokens) + Number(outputTokens) !== totalTokens
|| Number(cachedReadTokens) + Number(cacheCreationTokens) > Number(inputTokens)
|| Number(reasoningTokens) > Number(outputTokens)) return null;
const costKnown = integer(usage.costUsdTicks) && (usage.costIsPartial === undefined || usage.costIsPartial === false);
return {
promptId: meta.promptId,
event: {
type: "status", tag: "usage_update", text: "terminal prompt usage",
...(costKnown ? { cost: { amount: Number(usage.costUsdTicks) / 10_000_000_000, currency: "USD", basis: "turn" } } : {}),
breakdown: {
inputTokens: Number(inputTokens) - Number(cachedReadTokens) - Number(cacheCreationTokens),
outputTokens, totalTokens, cachedReadTokens, cachedWriteTokens: cacheCreationTokens, thoughtTokens: 0,
},
},
};
}
export function createGrokPromptUsageCollector() {
let active: { requestId: string; sessionId: string; wireId?: string | number; invalid: boolean; receipt: TerminalPromptUsage | null } | null = null;
let previousPromptId: string | undefined;
return {
begin(requestId: string, sessionId: string) {
previousPromptId = active?.receipt?.promptId ?? previousPromptId;
active = { requestId, sessionId, invalid: false, receipt: null };
},
observe(direction: "inbound" | "outbound", value: unknown) {
if (!active || active.invalid) return;
const message = record(value);
if (direction === "outbound") {
if (message.method !== "session/prompt" || record(message.params).sessionId !== active.sessionId) return;
if (active.wireId !== undefined || !(id(message.id) || integer(message.id))) { active.invalid = true; return; }
active.wireId = message.id;
return;
}
if (active.wireId === undefined || message.id !== active.wireId || message.method !== undefined) return;
const receipt = message.error === undefined ? grokPromptUsage(record(message.result)._meta, active.sessionId) : null;
if (!receipt || receipt.promptId === previousPromptId) { active.invalid = true; return; }
const bound = { ...receipt, requestId: active.requestId };
if (active.receipt && JSON.stringify(active.receipt) !== JSON.stringify(bound)) { active.invalid = true; return; }
active.receipt = bound;
},
snapshot(): TerminalPromptUsage | null {
return active && !active.invalid ? structuredClone(active.receipt) : null;
},
};
}
@@ -43,6 +43,10 @@ export function persistedAcpxTurnUsage(
): Record<string, unknown> | null {
const current = record(after);
if (current.lastRequestId !== requestId) return null;
const terminal = record(current.terminalPromptUsage);
const priorTerminal = record(record(before).terminalPromptUsage);
if (terminal.requestId === requestId && terminal.promptId !== priorTerminal.promptId
&& priorTerminal.requestId !== requestId) return record(terminal.event);
const previousReceipts = record(record(before).requestTokenUsage);
const receipts = record(current.requestTokenUsage);
const added = Object.keys(receipts).filter(
@@ -53,6 +53,18 @@ test("the ACPX sidecar schema accepts each versioned message family", () => {
}
});
test("usage prices declare turn or session scope without changing legacy cumulative prices", () => {
const event = cost => ({ protocolVersion, sequence: 1, eventType: "runtime.event", runId: "run-1", turnId: "turn-1",
payload: { type: "status", tag: "usage_update", cost } });
for (const cost of [null, { amount: 0.1, currency: "USD" }, { amount: 0.1, currency: "USD", basis: "turn" },
{ amount: 0.1, currency: "USD", basis: "session_cumulative" }]) {
assert.equal(validate(event(cost)), true, JSON.stringify(validate.errors));
}
for (const cost of [{ amount: -1 }, { amount: 0.1, basis: "estimated" }, { amount: 0.1, rawProviderReceipt: {} }]) {
assert.equal(validate(event(cost)), false);
}
});
test("failed responses accept the bounded provider cause and reject extra diagnostic fields", () => {
const failure = { schema: "paperclip.provider-failure.v1", provider: "anthropic", phase: "startup",
category: "rate_limit", message: "Account rate limit reached.", httpStatus: 429, retryability: "unknown" };
@@ -1632,6 +1632,18 @@ describe("native provider usage normalization", () => {
it("does not treat a protocol-default price as free usage", () => {
expect(nativeUsageCostUsd({ runDelta: { inputTokens: 20, outputTokens: 10, providerCostUsd: 0 } })).toBeUndefined();
});
it("attributes Grok API usage while preserving managed billing identity", () => {
const provider = { kind: "acpx", agent: "grok", model: "grok-4.7" } as NativeExecutionInput["provider"];
expect(nativeUsageBiller(provider)).toBe("xai");
expect(resolveNativeBilling(provider)).toEqual({ provider: "xai", biller: "xai", billingType: "unknown" });
expect(resolveNativeBilling(provider, { XAI_API_KEY: "fixture" }))
.toEqual({ provider: "xai", biller: "xai", billingType: "metered_api" });
const subscription = { provider: "xai", biller: "xai", billingType: "subscription_included" as const };
expect(resolveNativeBilling(provider, { XAI_API_KEY: "fixture" }, subscription)).toEqual(subscription);
expect(nativeUsageCostUsd({ runDelta: { inputTokens: 30787, outputTokens: 5763, cacheReadTokens: 475008,
cacheWriteTokens: 0, providerCostUsd: 0.336842 } }, provider)).toBe(0.336842);
});
it("reports the actual runtime billing identity", () => {
expect(resolveNativeBilling({ kind: "opencode", model: "openrouter/deepseek/test", permissionMode: "allow" }))
.toEqual({ provider: "deepseek", biller: "openrouter", billingType: "unknown" });
@@ -11011,6 +11011,9 @@ export function resolveNativeBilling(provider: NativeExecutionInput["provider"],
const biller = nativeUsageBiller(provider);
return { provider: biller, biller, billingType: "unknown" };
}
if (provider.kind === "acpx" && provider.agent === "grok") {
return { provider: "xai", biller: "xai", billingType: env.XAI_API_KEY ? "metered_api" : "unknown" };
}
if (provider.kind === "acpx" && provider.agent === "claude") {
if (env.CLAUDE_CODE_USE_BEDROCK === "1") return { provider: "anthropic", biller: "aws_bedrock", billingType: "metered_api" };
return { provider: "anthropic", biller: "anthropic", billingType: env.ANTHROPIC_API_KEY ? "metered_api" : env.CLAUDE_CODE_OAUTH_TOKEN ? "subscription_included" : "unknown" };
@@ -51,6 +51,7 @@ export function nativeUsageBiller(provider: NativeExecutionInput["provider"]): s
if (provider.agent === "cursor") return "cursor";
if (provider.agent === "copilot") return "github";
if (provider.agent === "pi") return "openrouter";
if (provider.agent === "grok") return "xai";
}
return "openai";
}