taskchef 8.0.0 → 8.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +2 -2
- package/docs/spec.md +9 -6
- package/package.json +1 -1
- package/src/dashboard/app.js +0 -3
- package/src/dashboard/dist/app.js +55 -35
- package/src/dashboard/dist/app.js.map +1 -1
- package/src/dashboard/dist/styles.css +1 -1
- package/src/dashboard/github-links.js +9 -1
- package/src/dashboard/react/components/RelativeTime.tsx +23 -5
- package/src/dashboard/react/components/TaskCard.tsx +4 -21
- package/src/dashboard/react/components/TaskCardStats.test.tsx +122 -0
- package/src/dashboard/react/components/TaskCardStats.tsx +86 -0
- package/src/dashboard/react/presentation.test.tsx +2 -2
- package/src/dashboard/react/presentation.ts +50 -20
- package/src/dashboard/react/styles.css +16 -11
- package/src/usage.js +11 -19
- package/src/dashboard/react/components/TaskUsageSummary.test.tsx +0 -122
- package/src/dashboard/react/components/TaskUsageSummary.tsx +0 -24
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { MantineProvider } from "@mantine/core";
|
|
2
|
-
import { cleanup, render, screen } from "@testing-library/react";
|
|
3
|
-
import { afterEach, describe, expect, test, vi } from "vitest";
|
|
4
|
-
import { fixtureTask } from "../fixtures";
|
|
5
|
-
import { TaskCard } from "./TaskCard";
|
|
6
|
-
import { TaskUsageSummary } from "./TaskUsageSummary";
|
|
7
|
-
|
|
8
|
-
afterEach(cleanup);
|
|
9
|
-
|
|
10
|
-
function renderUsage(task = fixtureTask()) {
|
|
11
|
-
return render(<MantineProvider><TaskUsageSummary task={task} /></MantineProvider>);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
describe("task list usage summary", () => {
|
|
15
|
-
test("uses shared compact token and currency formatting with full-value accessibility", () => {
|
|
16
|
-
renderUsage(fixtureTask({
|
|
17
|
-
status: "completed",
|
|
18
|
-
usage: {
|
|
19
|
-
status: "available",
|
|
20
|
-
updatedAt: "2026-08-30T09:00:00.000Z",
|
|
21
|
-
task: {
|
|
22
|
-
totalTokens: 1_324_567,
|
|
23
|
-
estimatedCostUsd: 12.3449,
|
|
24
|
-
sourceUpdatedAt: "2026-08-30T09:00:00.000Z",
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
}));
|
|
28
|
-
const summary = screen.getByText("1.32M tokens · est. $12.34");
|
|
29
|
-
expect(summary).toHaveAttribute("data-usage-state", "ready");
|
|
30
|
-
expect(summary).toHaveAccessibleName(/1,324,567 tokens; estimated cost \$12\.34/i);
|
|
31
|
-
expect(summary).toHaveAttribute("title", expect.stringContaining("unrounded estimate $12.3449"));
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
test("states cost unavailability without replacing a known token total with zero", () => {
|
|
35
|
-
renderUsage(fixtureTask({
|
|
36
|
-
status: "completed",
|
|
37
|
-
usage: { status: "available", task: { totalTokens: 1_324_567, estimatedCostUsd: null } },
|
|
38
|
-
}));
|
|
39
|
-
expect(screen.getByText("1.32M tokens · cost n/a")).toHaveAccessibleName(
|
|
40
|
-
/1,324,567 tokens; estimated cost unavailable/i,
|
|
41
|
-
);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test("keeps the known task total visible while an active turn updates usage", () => {
|
|
45
|
-
renderUsage(fixtureTask({
|
|
46
|
-
status: "working",
|
|
47
|
-
turnRef: "turn-two",
|
|
48
|
-
usage: {
|
|
49
|
-
generationTurnRef: "turn-one",
|
|
50
|
-
status: "unavailable",
|
|
51
|
-
reason: "Token usage calculation was interrupted.",
|
|
52
|
-
task: { totalTokens: 1_324_567, estimatedCostUsd: 12.3449 },
|
|
53
|
-
},
|
|
54
|
-
}));
|
|
55
|
-
const summary = screen.getByText("1.32M tokens · est. $12.34 · Updating…");
|
|
56
|
-
expect(summary).toHaveAttribute("data-usage-state", "ready");
|
|
57
|
-
expect(summary).toHaveAccessibleName(/1,324,567 tokens; estimated cost \$12\.34; updating/i);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test("keeps pending and calculating wording distinct and uses the restrained shimmer", () => {
|
|
61
|
-
const { rerender } = renderUsage();
|
|
62
|
-
expect(screen.getByText("Token usage pending")).toHaveClass("taskchef-shimmer");
|
|
63
|
-
rerender(
|
|
64
|
-
<MantineProvider>
|
|
65
|
-
<TaskUsageSummary task={fixtureTask({
|
|
66
|
-
status: "completed",
|
|
67
|
-
usage: { generationTurnRef: "turn-one", status: "calculating" },
|
|
68
|
-
})} />
|
|
69
|
-
</MantineProvider>,
|
|
70
|
-
);
|
|
71
|
-
expect(screen.getByText("Calculating token usage")).toHaveClass("taskchef-shimmer");
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test("shows a quiet explicit unavailable state with the cache reason in its tooltip", () => {
|
|
75
|
-
renderUsage(fixtureTask({
|
|
76
|
-
status: "completed",
|
|
77
|
-
usage: { status: "unavailable", reason: "No matching cached boundary." },
|
|
78
|
-
}));
|
|
79
|
-
const summary = screen.getByText("n/a tokens");
|
|
80
|
-
expect(summary).toHaveAttribute("data-usage-state", "unavailable");
|
|
81
|
-
expect(summary).toHaveAttribute("title", "No matching cached boundary.");
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test("places usage directly beneath the primary status badge in a responsive metadata block", () => {
|
|
85
|
-
const { container } = render(
|
|
86
|
-
<MantineProvider>
|
|
87
|
-
<TaskCard
|
|
88
|
-
onOpenCodex={vi.fn()}
|
|
89
|
-
onOpenDetail={vi.fn()}
|
|
90
|
-
task={fixtureTask()}
|
|
91
|
-
/>
|
|
92
|
-
</MantineProvider>,
|
|
93
|
-
);
|
|
94
|
-
const metadata = container.querySelector(".taskchef-card-metadata");
|
|
95
|
-
expect(metadata).not.toBeNull();
|
|
96
|
-
expect(metadata?.children[0]).toHaveClass("taskchef-status-badge");
|
|
97
|
-
expect(metadata?.children[1]).toHaveClass("taskchef-task-usage");
|
|
98
|
-
expect(screen.getByRole("button", { name: /^review checkout reconciliation$/i })).toBeVisible();
|
|
99
|
-
expect(screen.getByRole("button", { name: /open chat/i })).toBeVisible();
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("places reported work beside the updated time with matching typography", () => {
|
|
103
|
-
const { container } = render(
|
|
104
|
-
<MantineProvider>
|
|
105
|
-
<TaskCard
|
|
106
|
-
onOpenCodex={vi.fn()}
|
|
107
|
-
onOpenDetail={vi.fn()}
|
|
108
|
-
task={fixtureTask({
|
|
109
|
-
turns: undefined,
|
|
110
|
-
reportedWork: { terminalTurns: 1, validTurns: 1, totalMilliseconds: 1_112_000 },
|
|
111
|
-
})}
|
|
112
|
-
/>
|
|
113
|
-
</MantineProvider>,
|
|
114
|
-
);
|
|
115
|
-
const timing = container.querySelector(".taskchef-card-timing");
|
|
116
|
-
expect(timing).not.toBeNull();
|
|
117
|
-
expect(timing?.children[0]).toHaveClass("taskchef-time");
|
|
118
|
-
expect(timing?.children[1]).toHaveClass("taskchef-card-duration");
|
|
119
|
-
expect(screen.getByText("Reported work")).not.toHaveClass("taskchef-field-label");
|
|
120
|
-
expect(timing?.children[1]?.lastChild).toHaveTextContent("18m 32s");
|
|
121
|
-
});
|
|
122
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { Text } from "@mantine/core";
|
|
2
|
-
import type { Task } from "../types";
|
|
3
|
-
import { listUsageView } from "../presentation";
|
|
4
|
-
import { ShimmerText } from "./ShimmerText";
|
|
5
|
-
|
|
6
|
-
export function TaskUsageSummary({ task }: { task: Task }) {
|
|
7
|
-
const usage = listUsageView(task);
|
|
8
|
-
const content = usage.kind === "pending" || usage.kind === "calculating"
|
|
9
|
-
? <ShimmerText>{usage.label}</ShimmerText>
|
|
10
|
-
: usage.label;
|
|
11
|
-
return (
|
|
12
|
-
<Text
|
|
13
|
-
aria-label={usage.accessibleLabel}
|
|
14
|
-
aria-live="polite"
|
|
15
|
-
c="dimmed"
|
|
16
|
-
className="taskchef-task-usage"
|
|
17
|
-
data-usage-state={usage.kind}
|
|
18
|
-
size="xs"
|
|
19
|
-
title={usage.title}
|
|
20
|
-
>
|
|
21
|
-
{content}
|
|
22
|
-
</Text>
|
|
23
|
-
);
|
|
24
|
-
}
|