Surface task-derived token usage for ephemeral agents in dashboard agent views. - Attribute task token totals across assigned, source, and checkout agent links. - Backfill zero-valued agent list token totals from linked task usage. - Allow Agent Detail token usage windows for ephemeral/task-worker agents and cover the routes with tests. - Document the dashboard behavior and add a patch changeset. Files changed: .changeset/fn-7150-ephemeral-agent-token-counts.md | 7 ++ docs/dashboard-guide.md | 1 + .../core/src/__tests__/agent-token-usage.test.ts | 99 ++++++++++++++++++++-- packages/core/src/__tests__/team-analytics.test.ts | 48 +++++++++++ packages/core/src/agent-token-usage.ts | 83 +++++++++++++++++- packages/core/src/index.ts | 4 +- .../__tests__/AgentTokenStatsPanel.test.tsx | 21 +++++ .../src/__tests__/routes-agent-token-usage.test.ts | 20 ++++- .../dashboard/src/__tests__/routes-agents.test.ts | 87 +++++++++++++++++++ .../src/routes/register-agent-core-routes.ts | 38 ++++++++- .../src/routes/register-agent-runtime-routes.ts | 5 +- 11 files changed, 395 insertions(+), 18 deletions(-) Fusion-Task-Id: FN-7150 Fusion-Task-Lineage: ce2661ce-5ba5-43b0-aa5c-7fddee90a915 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
91 lines
3.2 KiB
TypeScript
91 lines
3.2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { render, screen, within } from "@testing-library/react";
|
|
import { AgentTokenStatsPanel } from "../AgentTokenStatsPanel";
|
|
import type { Agent } from "../../api";
|
|
|
|
function makeAgent(overrides: Partial<Agent>): Agent {
|
|
return {
|
|
id: "agent-default",
|
|
name: "Default Agent",
|
|
role: "executor",
|
|
state: "idle",
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
metadata: {},
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("AgentTokenStatsPanel", () => {
|
|
it("renders aggregate totals from agent cumulative token fields", () => {
|
|
render(
|
|
<AgentTokenStatsPanel
|
|
agents={[
|
|
makeAgent({ id: "a-1", name: "Alpha", totalInputTokens: 100, totalOutputTokens: 40 }),
|
|
makeAgent({ id: "a-2", name: "Beta", totalInputTokens: 10, totalOutputTokens: 5 }),
|
|
]}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByText("Input Tokens")).toBeInTheDocument();
|
|
expect(screen.getByText("110")).toBeInTheDocument();
|
|
expect(screen.getByText("Output Tokens")).toBeInTheDocument();
|
|
expect(screen.getByText("45")).toBeInTheDocument();
|
|
expect(screen.getByText("Combined Tokens")).toBeInTheDocument();
|
|
expect(screen.getByText("155")).toBeInTheDocument();
|
|
});
|
|
|
|
it("sorts per-agent rows by total usage descending", () => {
|
|
render(
|
|
<AgentTokenStatsPanel
|
|
agents={[
|
|
makeAgent({ id: "a-1", name: "Alpha", totalInputTokens: 50, totalOutputTokens: 20 }),
|
|
makeAgent({ id: "a-2", name: "Beta", totalInputTokens: 5, totalOutputTokens: 5 }),
|
|
makeAgent({ id: "a-3", name: "Gamma", totalInputTokens: 20, totalOutputTokens: 40 }),
|
|
]}
|
|
/>,
|
|
);
|
|
|
|
const rows = screen.getAllByRole("row").slice(1);
|
|
expect(within(rows[0]).getByText("Alpha")).toBeInTheDocument();
|
|
expect(within(rows[1]).getByText("Gamma")).toBeInTheDocument();
|
|
expect(within(rows[2]).getByText("Beta")).toBeInTheDocument();
|
|
});
|
|
|
|
it("renders non-zero task-derived totals for an ephemeral agent row", () => {
|
|
render(
|
|
<AgentTokenStatsPanel
|
|
agents={[
|
|
makeAgent({
|
|
id: "agent-ephemeral",
|
|
name: "executor-FN-1234",
|
|
metadata: { agentKind: "task-worker" },
|
|
totalInputTokens: 120,
|
|
totalOutputTokens: 45,
|
|
}),
|
|
]}
|
|
/>,
|
|
);
|
|
|
|
const row = screen.getByRole("row", { name: /executor-FN-1234/i });
|
|
expect(within(row).getByText("120")).toBeInTheDocument();
|
|
expect(within(row).getByText("45")).toBeInTheDocument();
|
|
expect(within(row).getByText("165")).toBeInTheDocument();
|
|
});
|
|
|
|
it("treats missing token fields as zero and shows empty state when there is no usage", () => {
|
|
render(
|
|
<AgentTokenStatsPanel
|
|
agents={[
|
|
makeAgent({ id: "a-1", name: "Zero One", totalInputTokens: undefined, totalOutputTokens: undefined }),
|
|
makeAgent({ id: "a-2", name: "Zero Two" }),
|
|
]}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByText("No token usage recorded yet. Token totals appear here once agents run.")).toBeInTheDocument();
|
|
expect(screen.queryByRole("table")).toBeNull();
|
|
expect(screen.getAllByText("0").length).toBeGreaterThanOrEqual(3);
|
|
});
|
|
});
|