





















@@ -21,6 +21,7 @@ import { createSubsystemLogger } from "../logging/subsystem.js";
2121import { stripEnvelope, stripMessageIdHints } from "../shared/chat-envelope.js";
2222import { asFiniteNumber } from "../shared/number-coercion.js";
2323import { normalizeOptionalString } from "../shared/string-coerce.js";
24+import { runTasksWithConcurrency } from "../utils/run-with-concurrency.js";
2425import { countToolResults, extractToolCallNames } from "../utils/transcript-tools.js";
2526import {
2627estimateUsageCost,
@@ -88,6 +89,7 @@ const emptyTotals = (): CostUsageTotals => ({
8889const USAGE_COST_CACHE_VERSION = 4;
8990const USAGE_COST_CACHE_FILE = ".usage-cost-cache.json";
9091const USAGE_COST_CACHE_LOCK_WRITE_GRACE_MS = 10_000;
92+const USAGE_COST_TRANSCRIPT_STAT_CONCURRENCY = 32;
9193const logger = createSubsystemLogger("usage-cost-cache");
92949395type UsageCostRefreshState = {
@@ -366,24 +368,38 @@ async function writeUsageCostCache(cachePath: string, cache: UsageCostCacheFile)
366368});
367369}
368370369-async function listUsageCountedTranscriptFiles(
371+async function listUsageCountedTranscriptFileStats(
370372agentId?: string,
373+params?: { minMtimeMs?: number },
371374): Promise<UsageCostTranscriptFile[]> {
372375const sessionsDir = resolveSessionTranscriptsDirForAgent(agentId);
373376const entries = await fs.promises.readdir(sessionsDir, { withFileTypes: true }).catch(() => []);
374-const files = await Promise.all(
375-entries
376- .filter((entry) => entry.isFile() && isUsageCountedSessionTranscriptFileName(entry.name))
377-.map(async (entry) => {
377+const tasks = entries
378+.filter((entry) => entry.isFile() && isUsageCountedSessionTranscriptFileName(entry.name))
379+.map(
380+(entry) => async (): Promise<UsageCostTranscriptFile | undefined> => {
378381const filePath = path.join(sessionsDir, entry.name);
379382const stats = await fs.promises.stat(filePath).catch(() => null);
380383if (!stats) {
381384return undefined;
382385}
386+if (params?.minMtimeMs !== undefined && stats.mtimeMs < params.minMtimeMs) {
387+return undefined;
388+}
383389return { filePath, size: stats.size, mtimeMs: stats.mtimeMs };
384-}),
385-);
386-return files.filter((file): file is UsageCostTranscriptFile => Boolean(file));
390+},
391+);
392+const { results } = await runTasksWithConcurrency({
393+ tasks,
394+limit: USAGE_COST_TRANSCRIPT_STAT_CONCURRENCY,
395+});
396+return results.filter((file): file is UsageCostTranscriptFile => Boolean(file));
397+}
398+399+async function listUsageCountedTranscriptFiles(
400+agentId?: string,
401+): Promise<UsageCostTranscriptFile[]> {
402+return await listUsageCountedTranscriptFileStats(agentId);
387403}
388404389405function isUsageCostCacheEntryFresh(params: {
@@ -1283,30 +1299,13 @@ export async function loadCostUsageSummary(params?: {
12831299const totals = emptyTotals();
12841300const resolveCost = createUsageCostResolver(params?.config);
128513011286-const sessionsDir = resolveSessionTranscriptsDirForAgent(params?.agentId);
1287-const entries = await fs.promises.readdir(sessionsDir, { withFileTypes: true }).catch(() => []);
1288-const files = (
1289-await Promise.all(
1290-entries
1291-.filter((entry) => entry.isFile() && isUsageCountedSessionTranscriptFileName(entry.name))
1292-.map(async (entry) => {
1293-const filePath = path.join(sessionsDir, entry.name);
1294-const stats = await fs.promises.stat(filePath).catch(() => null);
1295-if (!stats) {
1296-return null;
1297-}
1298-// Include file if it was modified after our start time
1299-if (stats.mtimeMs < sinceTime) {
1300-return null;
1301-}
1302-return filePath;
1303-}),
1304-)
1305-).filter((filePath): filePath is string => Boolean(filePath));
1302+const files = await listUsageCountedTranscriptFileStats(params?.agentId, {
1303+minMtimeMs: sinceTime,
1304+});
130613051307-for (const filePath of files) {
1306+for (const file of files) {
13081307await scanUsageFile({
1309- filePath,
1308+filePath: file.filePath,
13101309config: params?.config,
13111310 resolveCost,
13121311onEntry: (entry) => {
@@ -1870,33 +1869,22 @@ export async function discoverAllSessions(params?: {
18701869endMs?: number;
18711870includeFirstUserMessage?: boolean;
18721871}): Promise<DiscoveredSession[]> {
1873-const sessionsDir = resolveSessionTranscriptsDirForAgent(params?.agentId);
1874-const entries = await fs.promises.readdir(sessionsDir, { withFileTypes: true }).catch(() => []);
1872+const files = await listUsageCountedTranscriptFileStats(params?.agentId, {
1873+minMtimeMs: params?.startMs,
1874+});
1875187518761876const discovered = new Map<string, DiscoveredSession>();
187718771878-for (const entry of entries) {
1879-if (!entry.isFile() || !isUsageCountedSessionTranscriptFileName(entry.name)) {
1880-continue;
1881-}
1882-1883-const filePath = path.join(sessionsDir, entry.name);
1884-const stats = await fs.promises.stat(filePath).catch(() => null);
1885-if (!stats) {
1886-continue;
1887-}
1888-1889-// Filter by date range if provided
1890-if (params?.startMs && stats.mtimeMs < params.startMs) {
1891-continue;
1892-}
1878+for (const file of files) {
18931879// Do not exclude by endMs: a session can have activity in range even if it continued later.
1880+const filePath = file.filePath;
1881+const fileName = path.basename(filePath);
189418821895-const sessionId = parseUsageCountedSessionIdFromFileName(entry.name);
1883+const sessionId = parseUsageCountedSessionIdFromFileName(fileName);
18961884if (!sessionId) {
18971885continue;
18981886}
1899-const isPrimaryTranscript = isPrimarySessionTranscriptFileName(entry.name);
1887+const isPrimaryTranscript = isPrimarySessionTranscriptFileName(fileName);
1900188819011889// Try to read first user message for label extraction
19021890let firstUserMessage: string | undefined;
@@ -1942,13 +1930,13 @@ export async function discoverAllSessions(params?: {
19421930const shouldReplace =
19431931!existing ||
19441932(isPrimaryTranscript && !existingIsPrimary) ||
1945-(isPrimaryTranscript === existingIsPrimary && stats.mtimeMs >= existing.mtime);
1933+(isPrimaryTranscript === existingIsPrimary && file.mtimeMs >= existing.mtime);
1946193419471935if (shouldReplace) {
19481936discovered.set(sessionId, {
19491937 sessionId,
19501938sessionFile: filePath,
1951-mtime: stats.mtimeMs,
1939+mtime: file.mtimeMs,
19521940firstUserMessage: firstUserMessage ?? existing?.firstUserMessage,
19531941});
19541942continue;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。