| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- import NodeCache from "node-cache"
- import getFolderSize from "get-folder-size"
- import type { ClineMessage, HistoryItem } from "@roo-code/types"
- import { combineApiRequests } from "../../shared/combineApiRequests"
- import { combineCommandSequences } from "../../shared/combineCommandSequences"
- import { getApiMetrics } from "../../shared/getApiMetrics"
- import { findLastIndex } from "../../shared/array"
- import { getTaskDirectoryPath } from "../../utils/storage"
- import { t } from "../../i18n"
- const taskSizeCache = new NodeCache({ stdTTL: 30, checkperiod: 5 * 60 })
- export type TaskMetadataOptions = {
- taskId: string
- rootTaskId?: string
- parentTaskId?: string
- taskNumber: number
- messages: ClineMessage[]
- globalStoragePath: string
- workspace: string
- mode?: string
- /** Initial status for the task (e.g., "active" for child tasks) */
- initialStatus?: "active" | "delegated" | "completed"
- }
- export async function taskMetadata({
- taskId: id,
- rootTaskId,
- parentTaskId,
- taskNumber,
- messages,
- globalStoragePath,
- workspace,
- mode,
- initialStatus,
- }: TaskMetadataOptions) {
- const taskDir = await getTaskDirectoryPath(globalStoragePath, id)
- // Determine message availability upfront
- const hasMessages = messages && messages.length > 0
- // Pre-calculate all values based on availability
- let timestamp: number
- let tokenUsage: ReturnType<typeof getApiMetrics>
- let taskDirSize: number
- let taskMessage: ClineMessage | undefined
- if (!hasMessages) {
- // Handle no messages case
- timestamp = Date.now()
- tokenUsage = {
- totalTokensIn: 0,
- totalTokensOut: 0,
- totalCacheWrites: 0,
- totalCacheReads: 0,
- totalCost: 0,
- contextTokens: 0,
- }
- taskDirSize = 0
- } else {
- // Handle messages case
- taskMessage = messages[0] // First message is always the task say.
- const lastRelevantMessage =
- messages[findLastIndex(messages, (m) => !(m.ask === "resume_task" || m.ask === "resume_completed_task"))] ||
- taskMessage
- timestamp = lastRelevantMessage.ts
- tokenUsage = getApiMetrics(combineApiRequests(combineCommandSequences(messages.slice(1))))
- // Get task directory size
- const cachedSize = taskSizeCache.get<number>(taskDir)
- if (cachedSize === undefined) {
- try {
- taskDirSize = await getFolderSize.loose(taskDir)
- taskSizeCache.set<number>(taskDir, taskDirSize)
- } catch (error) {
- taskDirSize = 0
- }
- } else {
- taskDirSize = cachedSize
- }
- }
- // Create historyItem once with pre-calculated values.
- // initialStatus is included when provided (e.g., "active" for child tasks)
- // to ensure the status is set from the very first save, avoiding race conditions
- // where attempt_completion might run before a separate status update.
- const historyItem: HistoryItem = {
- id,
- rootTaskId,
- parentTaskId,
- number: taskNumber,
- ts: timestamp,
- task: hasMessages
- ? taskMessage!.text?.trim() || t("common:tasks.incomplete", { taskNumber })
- : t("common:tasks.no_messages", { taskNumber }),
- tokensIn: tokenUsage.totalTokensIn,
- tokensOut: tokenUsage.totalTokensOut,
- cacheWrites: tokenUsage.totalCacheWrites,
- cacheReads: tokenUsage.totalCacheReads,
- totalCost: tokenUsage.totalCost,
- size: taskDirSize,
- workspace,
- mode,
- ...(initialStatus && { status: initialStatus }),
- }
- return { historyItem, tokenUsage }
- }
|