diff --git a/docs/cli/gateway.md b/docs/cli/gateway.md
index 4613e5718f2..b224bb769d6 100644
--- a/docs/cli/gateway.md
+++ b/docs/cli/gateway.md
@@ -181,12 +181,20 @@ Fetch usage-cost summaries from session logs.
```bash
openclaw gateway usage-cost
openclaw gateway usage-cost --days 7
+openclaw gateway usage-cost --agent work --json
+openclaw gateway usage-cost --all-agents
openclaw gateway usage-cost --json
```
Number of days to include.
+
+ Scope the cost summary to one configured agent id.
+
+
+ Aggregate the cost summary across all configured agents. Cannot be combined with `--agent`.
+
### `gateway stability`
diff --git a/src/cli/gateway-cli.coverage.test.ts b/src/cli/gateway-cli.coverage.test.ts
index 139d6bd021a..d74b5f42cfb 100644
--- a/src/cli/gateway-cli.coverage.test.ts
+++ b/src/cli/gateway-cli.coverage.test.ts
@@ -213,6 +213,55 @@ describe("gateway-cli coverage", () => {
});
});
+ it("scopes usage-cost to a specific agent via --agent", async () => {
+ callGateway.mockClear();
+
+ await runGatewayCommand(["gateway", "usage-cost", "--agent", "alpha", "--days", "7", "--json"]);
+
+ expect(callGateway).toHaveBeenCalledTimes(1);
+ const costCall = firstMockArg(callGateway) as { method?: string; params?: unknown };
+ expect(costCall?.method).toBe("usage.cost");
+ expect(costCall?.params).toEqual({ days: 7, agentId: "alpha" });
+ });
+
+ it("omits agentId from usage-cost when --agent is absent or blank", async () => {
+ callGateway.mockClear();
+
+ await runGatewayCommand(["gateway", "usage-cost", "--agent", " ", "--days", "7", "--json"]);
+
+ expect(callGateway).toHaveBeenCalledTimes(1);
+ const costCall = firstMockArg(callGateway) as { method?: string; params?: unknown };
+ expect(costCall?.method).toBe("usage.cost");
+ expect(costCall?.params).toEqual({ days: 7 });
+ });
+
+ it("aggregates usage-cost across agents via --all-agents", async () => {
+ callGateway.mockClear();
+
+ await runGatewayCommand(["gateway", "usage-cost", "--all-agents", "--days", "7", "--json"]);
+
+ expect(callGateway).toHaveBeenCalledTimes(1);
+ const costCall = firstMockArg(callGateway) as { method?: string; params?: unknown };
+ expect(costCall?.method).toBe("usage.cost");
+ expect(costCall?.params).toEqual({ days: 7, agentScope: "all" });
+ });
+
+ it("rejects combining --agent with --all-agents for usage-cost", async () => {
+ callGateway.mockClear();
+
+ await expectGatewayExit([
+ "gateway",
+ "usage-cost",
+ "--agent",
+ "alpha",
+ "--all-agents",
+ "--json",
+ ]);
+
+ expect(callGateway).not.toHaveBeenCalled();
+ expect(runtimeErrors.join("\n")).toContain("Use --agent or --all-agents, not both");
+ });
+
it("writes JSON for gateway health transport failures in JSON mode", async () => {
const error = new Error("gateway closed (1006)");
const payload = {
diff --git a/src/cli/gateway-cli/register.ts b/src/cli/gateway-cli/register.ts
index a80504d8cfd..e3d7d0ea679 100644
--- a/src/cli/gateway-cli/register.ts
+++ b/src/cli/gateway-cli/register.ts
@@ -556,12 +556,24 @@ export function registerGatewayCli(program: Command) {
.command("usage-cost")
.description("Fetch usage cost summary from session logs")
.option("--days ", "Number of days to include", "30")
+ .option("--agent ", "Scope the cost summary to a specific agent id")
+ .option("--all-agents", "Aggregate the cost summary across all agents", false)
.action(async (opts, command) => {
await runGatewayCommand(
async () => {
const rpcOpts = resolveGatewayRpcOptions(opts, command);
const days = parseDaysOption(opts.days);
- const result = await callGatewayCli("usage.cost", rpcOpts, { days });
+ const agentId = typeof opts.agent === "string" ? opts.agent.trim() : undefined;
+ // The gateway honors agentScope only when no agentId is set, so reject the
+ // ambiguous combination here instead of silently dropping --all-agents.
+ if (agentId && opts.allAgents) {
+ throw new Error("Use --agent or --all-agents, not both");
+ }
+ const result = await callGatewayCli("usage.cost", rpcOpts, {
+ days,
+ ...(agentId ? { agentId } : {}),
+ ...(opts.allAgents ? { agentScope: "all" } : {}),
+ });
if (rpcOpts.json) {
defaultRuntime.writeJson(result);
return;