taskchef 8.1.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/usage.js +11 -19
package/README.md
CHANGED
|
@@ -368,8 +368,8 @@ figures are labeled API-equivalent estimates; provenance identifies the online
|
|
|
368
368
|
pricing request or offline fallback mode, and zero-priced unknown models show
|
|
369
369
|
cost unavailable rather than a misleading `$0.00`. Per-turn cost is also
|
|
370
370
|
unavailable when its cumulative boundaries used different ccusage versions or
|
|
371
|
-
pricing modes.
|
|
372
|
-
|
|
371
|
+
pricing modes. TaskChef displays the cumulative estimate supplied by ccusage
|
|
372
|
+
without applying a separate model-family or cache-write coverage policy.
|
|
373
373
|
The same detail view reports lifecycle wall-clock elapsed time independently of
|
|
374
374
|
ccusage. Each terminal turn shows **Elapsed** from `startedAt` to
|
|
375
375
|
`result.updatedAt`; **Total reported work** sums only valid terminal-turn
|
package/docs/spec.md
CHANGED
|
@@ -172,10 +172,12 @@ store a cumulative usage boundary, and MUST NOT receive a token or cost delta.
|
|
|
172
172
|
A later executor turn MUST use the nearest preceding reliable executor
|
|
173
173
|
boundary, skipping any intervening dashboard-manual turns.
|
|
174
174
|
|
|
175
|
-
The mode-0600 `.taskchef-usage.json` cache stores only normalized cumulative
|
|
175
|
+
The mode-0600 `.taskchef-usage.json` schema 2 cache stores only normalized cumulative
|
|
176
176
|
token boundaries, per-turn deltas, model names, estimated cost, source version,
|
|
177
177
|
and freshness. It is independent of task-log schema versions so legacy logs remain
|
|
178
|
-
readable.
|
|
178
|
+
readable. Schema 1 usage caches MUST migrate in memory by discarding their
|
|
179
|
+
TaskChef-specific cost-coverage classification while preserving valid ccusage
|
|
180
|
+
cumulative estimates. Writes MUST use the workspace lock and atomic replacement. Symlinked
|
|
179
181
|
or unsupported cache files MUST be rejected. Writes MUST compact the derived
|
|
180
182
|
cache to recent task projections, recent per-turn results, and the latest
|
|
181
183
|
cumulative boundary. An oversized legacy cache MUST be treated as rebuildable
|
|
@@ -214,10 +216,11 @@ The online mode records TaskChef's request, not a claim about ccusage's internal
|
|
|
214
216
|
cache. Dollar values MUST be labeled API-equivalent estimates. Positive token
|
|
215
217
|
usage with a zero or missing analyzer cost MUST display cost unavailable, not
|
|
216
218
|
`$0.00`. A per-turn dollar delta MUST be unavailable when its adjacent
|
|
217
|
-
cumulative boundaries use different analyzer versions
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
219
|
+
cumulative boundaries use different analyzer versions or requested pricing
|
|
220
|
+
modes. When ccusage supplies a valid cumulative estimate, TaskChef MUST display
|
|
221
|
+
it without applying its own model-family or cache-write coverage policy. A model
|
|
222
|
+
family change between otherwise compatible boundaries MUST NOT prevent TaskChef
|
|
223
|
+
from deriving the per-turn estimate.
|
|
221
224
|
The dashboard Settings response MUST report the provider version returned by
|
|
222
225
|
the executable resolved for runtime use, not merely copy the dependency pin.
|
|
223
226
|
|
package/package.json
CHANGED
package/src/dashboard/app.js
CHANGED
|
@@ -482,9 +482,6 @@ export function usagePresentation(usage, { wholeTask = false } = {}) {
|
|
|
482
482
|
: "";
|
|
483
483
|
const freshness = usage.sourceUpdatedAt ?? usage.sampledAt;
|
|
484
484
|
provenance.textContent = `Source: ccusage${version}${pricingMode}${freshness ? ` · updated ${formatRelativeTime(freshness)}` : ""}. Dollar cost is an API-equivalent estimate${wholeTask ? " for the task" : " for this turn"}.`;
|
|
485
|
-
if (usage.provenance?.costCoverage === "cache_writes_unverified") {
|
|
486
|
-
provenance.textContent += " ccusage may omit GPT-5.6 cache-write charges.";
|
|
487
|
-
}
|
|
488
485
|
container.append(provenance);
|
|
489
486
|
return container;
|
|
490
487
|
}
|
package/src/usage.js
CHANGED
|
@@ -19,6 +19,7 @@ const PINNED_CCUSAGE_VERSION = taskchefPackage.optionalDependencies?.ccusage;
|
|
|
19
19
|
const CCUSAGE_RESOLVER = fileURLToPath(new URL("./resolve-ccusage.js", import.meta.url));
|
|
20
20
|
let npxResolvedCcusageInvocation = null;
|
|
21
21
|
const USAGE_FILE_NAME = ".taskchef-usage.json";
|
|
22
|
+
const USAGE_SCHEMA_VERSION = 2;
|
|
22
23
|
const UUID_PATTERN = /[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/gi;
|
|
23
24
|
const TOKEN_FIELDS = [
|
|
24
25
|
"inputTokens",
|
|
@@ -92,10 +93,6 @@ function normalizeProvenance(value, name, { includeSessionCount = false } = {})
|
|
|
92
93
|
pricingMode: value.pricingMode === "online" || value.pricingMode === "offline"
|
|
93
94
|
? value.pricingMode
|
|
94
95
|
: null,
|
|
95
|
-
costCoverage: value.costCoverage === "cache_writes_unverified"
|
|
96
|
-
|| value.costCoverage === "ccusage_reported"
|
|
97
|
-
? value.costCoverage
|
|
98
|
-
: null,
|
|
99
96
|
...(includeSessionCount ? {
|
|
100
97
|
sessionCount: nonNegativeNumber(value.sessionCount, `${name}.provenance.sessionCount`),
|
|
101
98
|
} : {}),
|
|
@@ -127,10 +124,6 @@ function normalizeStoredSnapshot(value, name) {
|
|
|
127
124
|
}
|
|
128
125
|
snapshot.models = models;
|
|
129
126
|
snapshot.provenance = normalizeProvenance(value.provenance, name, { includeSessionCount: true });
|
|
130
|
-
if (snapshot.provenance.pricingMode === null || snapshot.provenance.costCoverage === null) {
|
|
131
|
-
snapshot.estimatedCostUsd = null;
|
|
132
|
-
snapshot.costStatus = "unavailable";
|
|
133
|
-
}
|
|
134
127
|
snapshot.sampledAt = timestampOrNull(value.sampledAt, `${name}.sampledAt`);
|
|
135
128
|
snapshot.sourceUpdatedAt = timestampOrNull(value.sourceUpdatedAt, `${name}.sourceUpdatedAt`);
|
|
136
129
|
return snapshot;
|
|
@@ -150,7 +143,7 @@ function normalizeStoredTurn(value, name) {
|
|
|
150
143
|
? null
|
|
151
144
|
: nonNegativeNumber(value.estimatedCostUsd, `${name}.estimatedCostUsd`);
|
|
152
145
|
const provenance = normalizeProvenance(value.provenance, name);
|
|
153
|
-
if (provenance.pricingMode === null
|
|
146
|
+
if (provenance.pricingMode === null) {
|
|
154
147
|
normalized.estimatedCostUsd = null;
|
|
155
148
|
}
|
|
156
149
|
return {
|
|
@@ -271,9 +264,6 @@ export function aggregateCcusageSessions(payload, threadId, {
|
|
|
271
264
|
provider: "ccusage",
|
|
272
265
|
version,
|
|
273
266
|
pricingMode,
|
|
274
|
-
costCoverage: Object.keys(models).some((model) => /^gpt-5\.6(?:-|$)/i.test(model))
|
|
275
|
-
? "cache_writes_unverified"
|
|
276
|
-
: "ccusage_reported",
|
|
277
267
|
sessionCount: sessions.length,
|
|
278
268
|
},
|
|
279
269
|
sampledAt,
|
|
@@ -627,21 +617,23 @@ export async function readUsageStore(workspace) {
|
|
|
627
617
|
if (error.code === "ENOENT") return null;
|
|
628
618
|
throw error;
|
|
629
619
|
});
|
|
630
|
-
if (details === null) return { schemaVersion:
|
|
620
|
+
if (details === null) return { schemaVersion: USAGE_SCHEMA_VERSION, tasks: {} };
|
|
631
621
|
if (details.isSymbolicLink() || !details.isFile()) {
|
|
632
622
|
throw new Error("TaskChef usage cache must be a regular file");
|
|
633
623
|
}
|
|
634
624
|
if (details.size > MAX_USAGE_FILE_BYTES) {
|
|
635
|
-
return { schemaVersion:
|
|
625
|
+
return { schemaVersion: USAGE_SCHEMA_VERSION, tasks: {} };
|
|
636
626
|
}
|
|
637
627
|
const value = JSON.parse(await readFile(filePath, "utf8"));
|
|
638
|
-
if (
|
|
628
|
+
if (![1, USAGE_SCHEMA_VERSION].includes(value?.schemaVersion)
|
|
629
|
+
|| !value.tasks
|
|
630
|
+
|| typeof value.tasks !== "object") {
|
|
639
631
|
throw new Error("TaskChef usage cache has an unsupported schema");
|
|
640
632
|
}
|
|
641
633
|
const tasks = Object.entries(value.tasks);
|
|
642
|
-
if (tasks.length > 2_000) return { schemaVersion:
|
|
634
|
+
if (tasks.length > 2_000) return { schemaVersion: USAGE_SCHEMA_VERSION, tasks: {} };
|
|
643
635
|
return {
|
|
644
|
-
schemaVersion:
|
|
636
|
+
schemaVersion: USAGE_SCHEMA_VERSION,
|
|
645
637
|
tasks: Object.fromEntries(tasks.map(([taskId, record]) => [
|
|
646
638
|
taskId,
|
|
647
639
|
normalizeStoredRecord(record, `usage task ${taskId}`),
|
|
@@ -672,7 +664,7 @@ export function compactUsageStore(store) {
|
|
|
672
664
|
tasks[taskId] = compacted;
|
|
673
665
|
approximateBytes += entryBytes;
|
|
674
666
|
}
|
|
675
|
-
return { schemaVersion:
|
|
667
|
+
return { schemaVersion: USAGE_SCHEMA_VERSION, tasks };
|
|
676
668
|
}
|
|
677
669
|
|
|
678
670
|
export async function writeUsageStore(workspace, store) {
|
|
@@ -704,7 +696,7 @@ export function usageDelta(current, previous = null) {
|
|
|
704
696
|
if (!Number.isFinite(value) || value < 0) return null;
|
|
705
697
|
delta[field] = value;
|
|
706
698
|
}
|
|
707
|
-
const samePricingSource = previous === null || ["version", "pricingMode"
|
|
699
|
+
const samePricingSource = previous === null || ["version", "pricingMode"]
|
|
708
700
|
.every((field) => current.provenance?.[field] === previous.provenance?.[field]);
|
|
709
701
|
let estimatedCostUsd = !samePricingSource
|
|
710
702
|
|| current.estimatedCostUsd === null
|