From 652cb2f695db2ce8e9cee9d67e5fd5cd407369a9 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Thu, 12 Nov 2026 16:04:19 -0800 Subject: [PATCH] fix(automations): follow-ups to the automation detail redesign (#41052) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(automations): follow-ups to the automation detail redesign - Show the latest occurrence as a folded skipped run's last-run time. The detail health band, list rows with loaded history, and the projected list summary all read the retained run's first-occurrence timestamps, so a host that kept refusing runs looked like it last ran days ago. - Translate the new detail strings (Repeats, Missed-run grace, Execution, Not run yet, the usage detail, and the prompt copy labels) in every supported locale, reusing existing localized wording, and give the usage detail CLDR plural forms again. - Assert the prompt preview's eight-line boundary by behavior in the e2e spec instead of the line-clamp CSS value. Co-Authored-By: Claude * fix(automations): zh copy-prompt wording Use 提示词 in the new zh copy-prompt label so it matches the panel heading. Co-Authored-By: Claude --------- Co-authored-by: Claude --- .../details/AutomationDetail.fixture.tsx | 21 ++++++++++ .../details/AutomationDetailHealth.tsx | 5 ++- .../listing/automation-list-last-run.test.ts | 41 +++++++++++++++++++ .../listing/automation-list-last-run.ts | 11 ++--- .../src/i18n/en-runtime-required.json | 4 +- src/renderer/src/i18n/locales/de.json | 13 +++++- src/renderer/src/i18n/locales/en.json | 3 +- src/renderer/src/i18n/locales/es.json | 14 ++++++- src/renderer/src/i18n/locales/fr.json | 14 ++++++- src/renderer/src/i18n/locales/ja.json | 12 +++++- src/renderer/src/i18n/locales/ko.json | 12 +++++- src/renderer/src/i18n/locales/pt.json | 14 ++++++- src/renderer/src/i18n/locales/ru.json | 15 ++++++- src/renderer/src/i18n/locales/tr.json | 13 +++++- src/renderer/src/i18n/locales/zh-Hant.json | 12 +++++- src/renderer/src/i18n/locales/zh.json | 12 +++++- .../automations/history/automation-run.ts | 13 ++++++ .../history/automation-usage-summary.ts | 10 +++-- 18 files changed, 204 insertions(+), 35 deletions(-) create mode 100644 src/renderer/src/components/automations/listing/automation-list-last-run.test.ts diff --git a/src/renderer/src/components/automations/details/AutomationDetail.fixture.tsx b/src/renderer/src/components/automations/details/AutomationDetail.fixture.tsx index 82234c99197..e6195c622c7 100644 --- a/src/renderer/src/components/automations/details/AutomationDetail.fixture.tsx +++ b/src/renderer/src/components/automations/details/AutomationDetail.fixture.tsx @@ -90,6 +90,27 @@ export const failed = defineFixture({ }) }) +export const foldedRun = defineFixture({ + width: 1000, + render: () => + renderDetail({ + automation: { ...automation, lastRunAt: NOW - 6 * HOUR }, + runs: [ + makeRun({ + id: 'run-4', + status: 'skipped_unavailable', + error: 'The execution host is offline.', + scheduledFor: NOW - 8 * 24 * HOUR - 6 * HOUR, + createdAt: NOW - 8 * 24 * HOUR - 6 * HOUR, + dispatchedAt: null, + startedAt: null, + occurrenceCount: 9, + lastOccurrenceAt: NOW - 6 * HOUR + }) + ] + }) +}) + export const paused = defineFixture({ width: 720, render: () => diff --git a/src/renderer/src/components/automations/details/AutomationDetailHealth.tsx b/src/renderer/src/components/automations/details/AutomationDetailHealth.tsx index 585d10403df..1229de1b5f1 100644 --- a/src/renderer/src/components/automations/details/AutomationDetailHealth.tsx +++ b/src/renderer/src/components/automations/details/AutomationDetailHealth.tsx @@ -107,11 +107,12 @@ export function AutomationDetailHealth({ const usageDetail = hasUsage ? t( 'auto.components.automations.AutomationDetail.usageDetail', - '{{tokens}} tokens · {{known}}/{{total}} runs reported', + '{{tokens}} tokens · {{known}}/{{count}} runs reported', { tokens: formatAutomationTokens(usage.totalTokens, intlLocale), known: usage.knownRuns, - total: runs.length + count: runs.length, + defaultValue_one: '{{tokens}} tokens · {{known}}/{{count}} run reported' } ) : null diff --git a/src/renderer/src/components/automations/listing/automation-list-last-run.test.ts b/src/renderer/src/components/automations/listing/automation-list-last-run.test.ts new file mode 100644 index 00000000000..70cee785d21 --- /dev/null +++ b/src/renderer/src/components/automations/listing/automation-list-last-run.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from 'vitest' +import { summarizeAutomationRunUsage } from '../../../../../shared/automations/history/automation-usage-summary' +import { makeAutomation, makeAutomationListRow, makeRun } from '../automations-page-fixtures' +import { + getAutomationRowLastRunSnapshot, + getLocalAutomationLastRunSnapshot +} from './automation-list-last-run' + +const FIRST_OCCURRENCE = Date.UTC(2026, 10, 1, 1, 0) +const LATEST_OCCURRENCE = Date.UTC(2026, 10, 9, 1, 0) + +const automation = makeAutomation({ lastRunAt: LATEST_OCCURRENCE }) +const foldedRun = makeRun({ + status: 'skipped_unavailable', + error: 'Execution host is offline.', + scheduledFor: FIRST_OCCURRENCE, + createdAt: FIRST_OCCURRENCE, + startedAt: null, + dispatchedAt: null, + occurrenceCount: 9, + lastOccurrenceAt: LATEST_OCCURRENCE +}) + +describe('last run time of a folded skipped run', () => { + it('uses the latest occurrence when the run history is loaded', () => { + expect(getLocalAutomationLastRunSnapshot(automation, foldedRun).at).toBe(LATEST_OCCURRENCE) + }) + + it('uses the latest occurrence from the projected list summary', () => { + const row = makeAutomationListRow({ + automation, + usageSummary: summarizeAutomationRunUsage([foldedRun]) + }) + expect(getAutomationRowLastRunSnapshot(row).at).toBe(LATEST_OCCURRENCE) + }) + + it('keeps the dispatch time of a run that never folded', () => { + const run = makeRun({ createdAt: FIRST_OCCURRENCE, dispatchedAt: FIRST_OCCURRENCE + 5_000 }) + expect(getLocalAutomationLastRunSnapshot(automation, run).at).toBe(FIRST_OCCURRENCE + 5_000) + }) +}) diff --git a/src/renderer/src/components/automations/listing/automation-list-last-run.ts b/src/renderer/src/components/automations/listing/automation-list-last-run.ts index 717a1765cf0..0f8094ec285 100644 --- a/src/renderer/src/components/automations/listing/automation-list-last-run.ts +++ b/src/renderer/src/components/automations/listing/automation-list-last-run.ts @@ -1,9 +1,10 @@ import { translate } from '@/i18n/i18n' import type { Translate } from '@/i18n/use-translate' import type { Automation } from '../../../../../shared/automations/automation-definition' -import type { - AutomationRun, - AutomationRunStatus +import { + getAutomationRunLastRunAt, + type AutomationRun, + type AutomationRunStatus } from '../../../../../shared/automations/history/automation-run' import type { ExternalAutomationJob } from '../../../../../shared/automations/external/external-automation' import { @@ -49,10 +50,6 @@ export function indexLatestAutomationRuns( return latest } -export function getAutomationRunLastRunAt(run: AutomationRun): number { - return run.dispatchedAt ?? run.startedAt ?? run.createdAt -} - export function getToneForAutomationRunStatus(status: AutomationRunStatus): AutomationLastRunTone { if (status === 'dispatch_failed') { return 'failed' diff --git a/src/renderer/src/i18n/en-runtime-required.json b/src/renderer/src/i18n/en-runtime-required.json index 2232e4611f7..1ddc84b29ec 100644 --- a/src/renderer/src/i18n/en-runtime-required.json +++ b/src/renderer/src/i18n/en-runtime-required.json @@ -150,7 +150,9 @@ "graceHours_one": "{{count}} hour", "graceHours_other": "{{count}} hours", "graceMinutes_one": "{{count}} minute", - "graceMinutes_other": "{{count}} minutes" + "graceMinutes_other": "{{count}} minutes", + "usageDetail_one": "{{tokens}} tokens · {{known}}/{{count}} run reported", + "usageDetail_other": "{{tokens}} tokens · {{known}}/{{count}} runs reported" }, "AutomationRunsDashboard": { "historyUnavailable_one": "Run history is unavailable for {{count}} automation. Counts include available history only.", diff --git a/src/renderer/src/i18n/locales/de.json b/src/renderer/src/i18n/locales/de.json index 9e7c8ce85c3..24704f206af 100644 --- a/src/renderer/src/i18n/locales/de.json +++ b/src/renderer/src/i18n/locales/de.json @@ -17575,7 +17575,10 @@ }, "AutomationPromptDisclosure": { "showLess": "Weniger anzeigen", - "showMore": "Mehr anzeigen" + "showMore": "Mehr anzeigen", + "copied": "Kopiert", + "copyFailed": "Kopieren fehlgeschlagen", + "copyPrompt": "Prompt kopieren" }, "AutomationDetail": { "host": "Host", @@ -17612,7 +17615,13 @@ "precheckNone": "Keine", "migrate": "Migrieren", "migrateNotice": "{{host}} wurde auf einen neuen Remote-Host umgestellt. Migriere diese Automatisierung, damit sie weiterläuft.", - "needsMigration": "Migration erforderlich" + "needsMigration": "Migration erforderlich", + "repeats": "Wiederholung", + "missedRunGrace": "Nachholfrist", + "execution": "Ausführung", + "notRunYet": "Noch nicht ausgeführt", + "usageDetail_one": "{{tokens}} Token · {{known}}/{{count}} Ausführung gemeldet", + "usageDetail_other": "{{tokens}} Token · {{known}}/{{count}} Ausführungen gemeldet" }, "AutomationEditorDialog": { "fb1896a5e7": "Abbrechen", diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json index 24feb204ba0..74152f8751c 100644 --- a/src/renderer/src/i18n/locales/en.json +++ b/src/renderer/src/i18n/locales/en.json @@ -18556,7 +18556,8 @@ "missedRunGrace": "Missed-run grace", "execution": "Execution", "notRunYet": "Not run yet", - "usageDetail": "{{tokens}} tokens · {{known}}/{{total}} runs reported", + "usageDetail_one": "{{tokens}} tokens · {{known}}/{{count}} run reported", + "usageDetail_other": "{{tokens}} tokens · {{known}}/{{count}} runs reported", "createdBy": "Created by", "createdByPluginTitle": "Plugin {{plugin}}", "createdByEnded": "Plugin removed or disabled; it must ask before changing this automation" diff --git a/src/renderer/src/i18n/locales/es.json b/src/renderer/src/i18n/locales/es.json index 3e9cce1e71b..d28a1760d84 100644 --- a/src/renderer/src/i18n/locales/es.json +++ b/src/renderer/src/i18n/locales/es.json @@ -17308,7 +17308,10 @@ }, "AutomationPromptDisclosure": { "showLess": "Mostrar menos", - "showMore": "Mostrar más" + "showMore": "Mostrar más", + "copied": "Copiado", + "copyFailed": "No se pudo copiar", + "copyPrompt": "Copiar prompt" }, "AutomationDetail": { "host": "Host", @@ -17347,7 +17350,14 @@ "precheckNone": "Ninguna", "migrate": "Migrar", "migrateNotice": "{{host}} se actualizó a un nuevo host remoto. Migra esta automatización para que siga ejecutándose.", - "needsMigration": "Requiere migración" + "needsMigration": "Requiere migración", + "repeats": "Se repite", + "missedRunGrace": "Período de gracia", + "execution": "Ejecución", + "notRunYet": "Aún no se ha ejecutado", + "usageDetail_one": "{{tokens}} tokens · {{known}}/{{count}} ejecución informada", + "usageDetail_many": "{{tokens}} tokens · {{known}}/{{count}} ejecuciones informadas", + "usageDetail_other": "{{tokens}} tokens · {{known}}/{{count}} ejecuciones informadas" }, "AutomationEditorDialog": { "fb1896a5e7": "Cancelar", diff --git a/src/renderer/src/i18n/locales/fr.json b/src/renderer/src/i18n/locales/fr.json index 0b53f73d3ec..849f3ba5e61 100644 --- a/src/renderer/src/i18n/locales/fr.json +++ b/src/renderer/src/i18n/locales/fr.json @@ -17549,7 +17549,10 @@ }, "AutomationPromptDisclosure": { "showLess": "Afficher moins", - "showMore": "Afficher plus" + "showMore": "Afficher plus", + "copied": "Copié", + "copyFailed": "Échec de la copie", + "copyPrompt": "Copier le prompt" }, "AutomationDetail": { "401f40ae79": "Dépense est.", @@ -17588,7 +17591,14 @@ "precheckNone": "Aucune", "migrate": "Migrer", "migrateNotice": "{{host}} a été mis à niveau vers un nouvel hôte distant. Migrez cette automatisation pour qu'elle continue de s'exécuter.", - "needsMigration": "Migration requise" + "needsMigration": "Migration requise", + "repeats": "Répétition", + "missedRunGrace": "Marge de rattrapage", + "execution": "Exécution", + "notRunYet": "Pas encore exécutée", + "usageDetail_one": "{{tokens}} tokens · {{known}}/{{count}} exécution signalée", + "usageDetail_many": "{{tokens}} tokens · {{known}}/{{count}} exécutions signalées", + "usageDetail_other": "{{tokens}} tokens · {{known}}/{{count}} exécutions signalées" }, "AutomationEditorDialog": { "fb1896a5e7": "Annuler", diff --git a/src/renderer/src/i18n/locales/ja.json b/src/renderer/src/i18n/locales/ja.json index de065cc070b..fecaa192c6e 100644 --- a/src/renderer/src/i18n/locales/ja.json +++ b/src/renderer/src/i18n/locales/ja.json @@ -16901,7 +16901,10 @@ }, "AutomationPromptDisclosure": { "showLess": "折りたたむ", - "showMore": "さらに表示" + "showMore": "さらに表示", + "copied": "コピーしました", + "copyFailed": "コピーに失敗しました", + "copyPrompt": "プロンプトをコピー" }, "AutomationDetail": { "host": "ホスト", @@ -16936,7 +16939,12 @@ "precheckNone": "なし", "migrate": "移行", "migrateNotice": "{{host}} は新しいリモートホストにアップグレードされました。実行を続けるには、この自動化を移行してください。", - "needsMigration": "移行が必要" + "needsMigration": "移行が必要", + "repeats": "繰り返し", + "missedRunGrace": "実行漏れの猶予時間", + "execution": "実行", + "notRunYet": "未実行", + "usageDetail_other": "{{tokens}} トークン · {{known}}/{{count}} 回の実行を報告済み" }, "AutomationEditorDialog": { "fb1896a5e7": "キャンセル", diff --git a/src/renderer/src/i18n/locales/ko.json b/src/renderer/src/i18n/locales/ko.json index b9f8f8cbdc2..07456d96b4d 100644 --- a/src/renderer/src/i18n/locales/ko.json +++ b/src/renderer/src/i18n/locales/ko.json @@ -16898,7 +16898,10 @@ }, "AutomationPromptDisclosure": { "showLess": "접기", - "showMore": "더 보기" + "showMore": "더 보기", + "copied": "복사됨", + "copyFailed": "복사하지 못함", + "copyPrompt": "프롬프트 복사" }, "AutomationDetail": { "host": "호스트", @@ -16933,7 +16936,12 @@ "precheckNone": "없음", "migrate": "마이그레이션", "migrateNotice": "{{host}}이(가) 새 원격 호스트로 업그레이드되었습니다. 계속 실행하려면 이 자동화를 마이그레이션하세요.", - "needsMigration": "마이그레이션 필요" + "needsMigration": "마이그레이션 필요", + "repeats": "반복", + "missedRunGrace": "누락된 실행 유예 시간", + "execution": "실행", + "notRunYet": "아직 실행되지 않음", + "usageDetail_other": "토큰 {{tokens}} · 실행 {{known}}/{{count}}회 보고됨" }, "AutomationEditorDialog": { "fb1896a5e7": "취소", diff --git a/src/renderer/src/i18n/locales/pt.json b/src/renderer/src/i18n/locales/pt.json index 4b64607e12e..0e0ed11ce24 100644 --- a/src/renderer/src/i18n/locales/pt.json +++ b/src/renderer/src/i18n/locales/pt.json @@ -14793,7 +14793,14 @@ "precheckNone": "Nenhuma", "migrate": "Migrar", "migrateNotice": "{{host}} foi atualizado para um novo host remoto. Migre esta automação para que ela continue em execução.", - "needsMigration": "Requer migração" + "needsMigration": "Requer migração", + "repeats": "Repetição", + "missedRunGrace": "Tolerância de execução perdida", + "execution": "Execução", + "notRunYet": "Ainda não executada", + "usageDetail_one": "{{tokens}} tokens · {{known}}/{{count}} execução relatada", + "usageDetail_many": "{{tokens}} tokens · {{known}}/{{count}} execuções relatadas", + "usageDetail_other": "{{tokens}} tokens · {{known}}/{{count}} execuções relatadas" }, "ExternalAutomationManagers": { "e02f970595": "Nenhum gerenciador de automação externa encontrado.", @@ -15022,7 +15029,10 @@ }, "AutomationPromptDisclosure": { "showLess": "Mostrar menos", - "showMore": "Mostrar mais" + "showMore": "Mostrar mais", + "copied": "Copiado", + "copyFailed": "Falha ao copiar", + "copyPrompt": "Copiar prompt" }, "AutomationRunPageFrame": { "40a511bed4": "Contexto da execução", diff --git a/src/renderer/src/i18n/locales/ru.json b/src/renderer/src/i18n/locales/ru.json index 594fc386d1d..23612aead57 100644 --- a/src/renderer/src/i18n/locales/ru.json +++ b/src/renderer/src/i18n/locales/ru.json @@ -18070,7 +18070,10 @@ }, "AutomationPromptDisclosure": { "showLess": "Свернуть", - "showMore": "Показать ещё" + "showMore": "Показать ещё", + "copied": "Скопировано", + "copyFailed": "Не удалось скопировать", + "copyPrompt": "Копировать запрос" }, "AutomationDetail": { "host": "Сервер", @@ -18111,7 +18114,15 @@ "precheckNone": "Нет", "migrate": "Перенести", "migrateNotice": "{{host}} обновлён до нового удалённого хоста. Перенесите эту автоматизацию, чтобы она продолжала работать.", - "needsMigration": "Требуется перенос" + "needsMigration": "Требуется перенос", + "repeats": "Повтор", + "missedRunGrace": "Допустимая задержка запуска", + "execution": "Выполнение", + "notRunYet": "Ещё не запускалась", + "usageDetail_one": "Токены: {{tokens}} · {{known}}/{{count}} запуск с данными", + "usageDetail_few": "Токены: {{tokens}} · {{known}}/{{count}} запуска с данными", + "usageDetail_many": "Токены: {{tokens}} · {{known}}/{{count}} запусков с данными", + "usageDetail_other": "Токены: {{tokens}} · {{known}}/{{count}} запуска с данными" }, "AutomationEditorDialog": { "fb1896a5e7": "Отмена", diff --git a/src/renderer/src/i18n/locales/tr.json b/src/renderer/src/i18n/locales/tr.json index dc818d8b1c2..9b7911ed8f4 100644 --- a/src/renderer/src/i18n/locales/tr.json +++ b/src/renderer/src/i18n/locales/tr.json @@ -17552,7 +17552,10 @@ }, "AutomationPromptDisclosure": { "showLess": "Daha az göster", - "showMore": "Daha fazla göster" + "showMore": "Daha fazla göster", + "copied": "Kopyalandı", + "copyFailed": "Kopyalanamadı", + "copyPrompt": "İstemi kopyala" }, "AutomationDetail": { "host": "Sunucu", @@ -17589,7 +17592,13 @@ "precheckNone": "Yok", "migrate": "Taşı", "migrateNotice": "{{host}} yeni bir uzak ana bilgisayara yükseltildi. Çalışmaya devam etmesi için bu otomasyonu taşıyın.", - "needsMigration": "Taşınması gerekiyor" + "needsMigration": "Taşınması gerekiyor", + "repeats": "Tekrar", + "missedRunGrace": "Kaçırılan çalıştırma toleransı", + "execution": "Çalıştırma", + "notRunYet": "Henüz çalıştırılmadı", + "usageDetail_one": "{{tokens}} token · {{known}}/{{count}} çalıştırma raporlandı", + "usageDetail_other": "{{tokens}} token · {{known}}/{{count}} çalıştırma raporlandı" }, "AutomationEditorDialog": { "fb1896a5e7": "İptal", diff --git a/src/renderer/src/i18n/locales/zh-Hant.json b/src/renderer/src/i18n/locales/zh-Hant.json index ed1663e76b0..57905b20501 100644 --- a/src/renderer/src/i18n/locales/zh-Hant.json +++ b/src/renderer/src/i18n/locales/zh-Hant.json @@ -17345,7 +17345,10 @@ }, "AutomationPromptDisclosure": { "showLess": "顯示較少", - "showMore": "顯示更多" + "showMore": "顯示更多", + "copied": "已複製", + "copyFailed": "複製失敗", + "copyPrompt": "複製提示詞" }, "AutomationDetail": { "host": "主機", @@ -17380,7 +17383,12 @@ "precheckNone": "無", "migrate": "遷移", "migrateNotice": "{{host}} 已升級為新的遠端主機。請遷移此自動化以繼續執行。", - "needsMigration": "需要遷移" + "needsMigration": "需要遷移", + "repeats": "重複", + "missedRunGrace": "錯過執行寬限期", + "execution": "執行", + "notRunYet": "尚未執行", + "usageDetail_other": "{{tokens}} token · 已回報 {{known}}/{{count}} 次執行" }, "AutomationEditorDialog": { "fb1896a5e7": "取消", diff --git a/src/renderer/src/i18n/locales/zh.json b/src/renderer/src/i18n/locales/zh.json index 4bd481e1515..d764f29b029 100644 --- a/src/renderer/src/i18n/locales/zh.json +++ b/src/renderer/src/i18n/locales/zh.json @@ -17189,7 +17189,10 @@ }, "AutomationPromptDisclosure": { "showLess": "收起", - "showMore": "显示更多" + "showMore": "显示更多", + "copied": "已复制", + "copyFailed": "复制失败", + "copyPrompt": "复制提示词" }, "AutomationDetail": { "host": "主机", @@ -17224,7 +17227,12 @@ "precheckNone": "无", "migrate": "迁移", "migrateNotice": "{{host}} 已升级为新的远程主机。请迁移此自动化以继续运行。", - "needsMigration": "需要迁移" + "needsMigration": "需要迁移", + "repeats": "重复", + "missedRunGrace": "错过运行宽限期", + "execution": "执行", + "notRunYet": "尚未运行", + "usageDetail_other": "{{tokens}} Token · 已报告 {{known}}/{{count}} 次运行" }, "AutomationEditorDialog": { "fb1896a5e7": "取消", diff --git a/src/shared/automations/history/automation-run.ts b/src/shared/automations/history/automation-run.ts index ef44d208a2f..49b41d19f38 100644 --- a/src/shared/automations/history/automation-run.ts +++ b/src/shared/automations/history/automation-run.ts @@ -26,6 +26,19 @@ export function isFinalAutomationRunStatus(status: AutomationRunStatus): boolean ) } +/** Folded runs keep their first occurrence's timestamps, so the latest occurrence is when it last ran. */ +export function getAutomationRunLastRunAt( + run: Pick< + AutomationRun, + 'dispatchedAt' | 'startedAt' | 'createdAt' | 'occurrenceCount' | 'lastOccurrenceAt' + > +): number { + const at = run.dispatchedAt ?? run.startedAt ?? run.createdAt + return (run.occurrenceCount ?? 1) > 1 && run.lastOccurrenceAt && run.lastOccurrenceAt > at + ? run.lastOccurrenceAt + : at +} + export type AutomationRunUsageProvider = 'claude' | 'codex' export type AutomationRunUsageStatus = 'known' | 'unavailable' diff --git a/src/shared/automations/history/automation-usage-summary.ts b/src/shared/automations/history/automation-usage-summary.ts index 1c29ddbd544..f9ba5e7b070 100644 --- a/src/shared/automations/history/automation-usage-summary.ts +++ b/src/shared/automations/history/automation-usage-summary.ts @@ -1,4 +1,8 @@ -import type { AutomationRun, AutomationRunStatus } from './automation-run' +import { + getAutomationRunLastRunAt, + type AutomationRun, + type AutomationRunStatus +} from './automation-run' import type { SetupTrustMismatch } from '../../worktree/create-types' export type AutomationUsageSummary = { @@ -65,9 +69,7 @@ export function summarizeAutomationRunUsage( totalTokens, estimatedCostUsd: hasKnownCost ? estimatedCostUsd : null, lastRunStatus: latest?.status ?? null, - lastRunAt: latest - ? (latest.dispatchedAt ?? latest.startedAt ?? latest.createdAt ?? null) - : null, + lastRunAt: latest ? getAutomationRunLastRunAt(latest) : null, ...(latest?.setupSkippedReason === 'setup-changed' ? { lastRunSetupSkipped: true,