taskMetadata.ts 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import NodeCache from "node-cache"
  2. import getFolderSize from "get-folder-size"
  3. import type { ClineMessage, HistoryItem } from "@roo-code/types"
  4. import { combineApiRequests } from "../../shared/combineApiRequests"
  5. import { combineCommandSequences } from "../../shared/combineCommandSequences"
  6. import { getApiMetrics } from "../../shared/getApiMetrics"
  7. import { findLastIndex } from "../../shared/array"
  8. import { getTaskDirectoryPath } from "../../utils/storage"
  9. import { t } from "../../i18n"
  10. const taskSizeCache = new NodeCache({ stdTTL: 30, checkperiod: 5 * 60 })
  11. export type TaskMetadataOptions = {
  12. messages: ClineMessage[]
  13. taskId: string
  14. taskNumber: number
  15. globalStoragePath: string
  16. workspace: string
  17. }
  18. export async function taskMetadata({
  19. messages,
  20. taskId,
  21. taskNumber,
  22. globalStoragePath,
  23. workspace,
  24. }: TaskMetadataOptions) {
  25. const taskDir = await getTaskDirectoryPath(globalStoragePath, taskId)
  26. // Determine message availability upfront
  27. const hasMessages = messages && messages.length > 0
  28. // Pre-calculate all values based on availability
  29. let timestamp: number
  30. let tokenUsage: ReturnType<typeof getApiMetrics>
  31. let taskDirSize: number
  32. let taskMessage: ClineMessage | undefined
  33. if (!hasMessages) {
  34. // Handle no messages case
  35. timestamp = Date.now()
  36. tokenUsage = {
  37. totalTokensIn: 0,
  38. totalTokensOut: 0,
  39. totalCacheWrites: 0,
  40. totalCacheReads: 0,
  41. totalCost: 0,
  42. contextTokens: 0,
  43. }
  44. taskDirSize = 0
  45. } else {
  46. // Handle messages case
  47. taskMessage = messages[0] // First message is always the task say.
  48. const lastRelevantMessage =
  49. messages[findLastIndex(messages, (m) => !(m.ask === "resume_task" || m.ask === "resume_completed_task"))] ||
  50. taskMessage
  51. timestamp = lastRelevantMessage.ts
  52. tokenUsage = getApiMetrics(combineApiRequests(combineCommandSequences(messages.slice(1))))
  53. // Get task directory size
  54. const cachedSize = taskSizeCache.get<number>(taskDir)
  55. if (cachedSize === undefined) {
  56. try {
  57. taskDirSize = await getFolderSize.loose(taskDir)
  58. taskSizeCache.set<number>(taskDir, taskDirSize)
  59. } catch (error) {
  60. taskDirSize = 0
  61. }
  62. } else {
  63. taskDirSize = cachedSize
  64. }
  65. }
  66. // Create historyItem once with pre-calculated values
  67. const historyItem: HistoryItem = {
  68. id: taskId,
  69. number: taskNumber,
  70. ts: timestamp,
  71. task: hasMessages
  72. ? taskMessage!.text?.trim() || t("common:tasks.incomplete", { taskNumber })
  73. : t("common:tasks.no_messages", { taskNumber }),
  74. tokensIn: tokenUsage.totalTokensIn,
  75. tokensOut: tokenUsage.totalTokensOut,
  76. cacheWrites: tokenUsage.totalCacheWrites,
  77. cacheReads: tokenUsage.totalCacheReads,
  78. totalCost: tokenUsage.totalCost,
  79. size: taskDirSize,
  80. workspace,
  81. }
  82. return { historyItem, tokenUsage }
  83. }