@cat-factory/app 0.99.0 → 0.100.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.
@@ -1,8 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  // The newest-first list of failed-attempt entries (timestamp + message + hint + collapsible
3
- // detail), shared by the task-inspector's "previous errors" disclosure (AgentFailureHistory)
4
- // and the step-detail overlay's per-step "execution history". Presentational only the caller
5
- // decides which trail to pass (the whole run's, or one step's) and how to reveal it.
3
+ // detail) behind the task-inspector's "previous errors" disclosure (AgentFailureHistory).
4
+ // Presentational only — the caller decides which trail to pass and how to reveal it. (The
5
+ // step-detail overlay's per-step "execution history" uses StepExecutionHistory instead, which
6
+ // merges these failures with the successful outputs a restart superseded.)
6
7
  import type { AgentFailure } from '~/types/domain'
7
8
  import FailureDetail from '~/components/board/FailureDetail.vue'
8
9
 
@@ -0,0 +1,100 @@
1
+ <script setup lang="ts">
2
+ // The step-detail overlay's per-step "execution history": a newest-first, MERGED timeline of
3
+ // this step's SUCCESSFUL prior outputs (discarded by a restart) and its FAILED attempts — so
4
+ // the history surfaces what superseded attempts PRODUCED, not only the errors. Presentational
5
+ // only: the caller passes both trails already narrowed to the step (by `stepIndex`).
6
+ import type { AgentFailure, PriorStepOutput } from '~/types/domain'
7
+ import FailureDetail from '~/components/board/FailureDetail.vue'
8
+ import CopyButton from '~/components/common/CopyButton.vue'
9
+
10
+ const props = defineProps<{ failures: AgentFailure[]; outputs: PriorStepOutput[] }>()
11
+
12
+ const { t, d } = useI18n()
13
+
14
+ type Entry =
15
+ | { kind: 'failure'; key: string; occurredAt: number; failure: AgentFailure }
16
+ | { kind: 'success'; key: string; occurredAt: number; output: PriorStepOutput }
17
+
18
+ // Merge both trails and show newest first — the most recent attempt is the most relevant.
19
+ // Each entry's `key` is its position within its OWN trail (both are append-only, so that
20
+ // position is a stable identity), not the volatile merged-sort index — and it stays unique
21
+ // even when several entries share a timestamp (a restart can discard many steps with the same
22
+ // clock-fallback `occurredAt`).
23
+ const entries = computed<Entry[]>(() =>
24
+ [
25
+ ...props.failures.map(
26
+ (failure, i): Entry => ({
27
+ kind: 'failure',
28
+ key: `failure-${i}`,
29
+ occurredAt: failure.occurredAt,
30
+ failure,
31
+ }),
32
+ ),
33
+ ...props.outputs.map(
34
+ (output, i): Entry => ({
35
+ kind: 'success',
36
+ key: `success-${i}`,
37
+ occurredAt: output.occurredAt,
38
+ output,
39
+ }),
40
+ ),
41
+ ].sort((a, b) => b.occurredAt - a.occurredAt),
42
+ )
43
+ </script>
44
+
45
+ <template>
46
+ <ol class="space-y-2">
47
+ <li
48
+ v-for="entry in entries"
49
+ :key="entry.key"
50
+ class="rounded-md border px-2.5 py-2"
51
+ :class="
52
+ entry.kind === 'success'
53
+ ? 'border-emerald-900/60 bg-emerald-950/20'
54
+ : 'border-slate-800/80 bg-slate-950/50'
55
+ "
56
+ :data-testid="
57
+ entry.kind === 'success' ? 'step-history-success-entry' : 'step-history-failure-entry'
58
+ "
59
+ >
60
+ <!-- a superseded SUCCESSFUL attempt: its output, collapsible + copyable -->
61
+ <template v-if="entry.kind === 'success'">
62
+ <div class="flex items-center gap-1.5 text-[10px] text-slate-500">
63
+ <UIcon name="i-lucide-check-circle-2" class="h-3 w-3 shrink-0 text-emerald-400/70" />
64
+ <time>{{ d(new Date(entry.occurredAt), 'long') }}</time>
65
+ <span class="text-emerald-400/80">{{ t('panels.stepDetail.attemptSucceeded') }}</span>
66
+ </div>
67
+ <div class="relative mt-1">
68
+ <CopyButton :text="entry.output.output" class="absolute end-1 top-1 z-10" />
69
+ <pre
70
+ class="max-h-40 overflow-auto whitespace-pre-wrap rounded bg-slate-950/80 p-1.5 pe-9 text-[10px] leading-snug text-slate-300"
71
+ >{{ entry.output.output }}</pre
72
+ >
73
+ </div>
74
+ <p v-if="entry.output.truncated" class="mt-1 text-[10px] text-slate-500">
75
+ {{ t('panels.stepDetail.outputTruncated') }}
76
+ </p>
77
+ </template>
78
+
79
+ <!-- a FAILED attempt: mirrors FailureHistoryList's entry markup -->
80
+ <template v-else>
81
+ <div class="flex items-center gap-1.5 text-[10px] text-slate-500">
82
+ <UIcon name="i-lucide-alert-triangle" class="h-3 w-3 shrink-0 text-rose-400/70" />
83
+ <time>{{ d(new Date(entry.occurredAt), 'long') }}</time>
84
+ </div>
85
+ <p class="mt-1 text-[11px] leading-snug text-slate-300" :title="entry.failure.message">
86
+ {{ entry.failure.message }}
87
+ </p>
88
+ <p v-if="entry.failure.hint" class="mt-1 text-[10px] leading-snug text-slate-500">
89
+ {{ entry.failure.hint }}
90
+ </p>
91
+ <FailureDetail
92
+ :detail="entry.failure.detail"
93
+ :message="entry.failure.message"
94
+ summary-class="text-[10px] text-slate-500 hover:text-slate-300"
95
+ pre-class="bg-slate-950/80 text-[10px] text-slate-400"
96
+ />
97
+ </template>
98
+ </li>
99
+ </ol>
100
+ </template>
@@ -11,7 +11,7 @@ import FrontendBindingsResolved from '~/components/panels/inspector/FrontendBind
11
11
  import { UI_TESTER_AGENT_KIND } from '@cat-factory/contracts'
12
12
  import ProvisioningLogsDrawer from '~/components/provisioning/ProvisioningLogsDrawer.vue'
13
13
  import IterationCapPrompt from '~/components/pipeline/IterationCapPrompt.vue'
14
- import FailureHistoryList from '~/components/board/FailureHistoryList.vue'
14
+ import StepExecutionHistory from '~/components/board/StepExecutionHistory.vue'
15
15
  import { useStepTimer } from '~/composables/useStepTimer'
16
16
  import { useStepProse } from '~/composables/useStepProse'
17
17
  import { useStepApproval } from '~/composables/useStepApproval'
@@ -96,6 +96,16 @@ const stepFailures = computed(() => {
96
96
  if (instance.value?.failure) trail.push(instance.value.failure)
97
97
  return trail.filter((f) => f.stepIndex === idx)
98
98
  })
99
+ // The positive complement of the failure trail: the SUCCESSFUL outputs a restart discarded
100
+ // for THIS step (each carries the `stepIndex` that produced it), so the history surfaces what
101
+ // superseded attempts produced — not only errors. Merged with `stepFailures` in the timeline.
102
+ const stepOutputs = computed(() => {
103
+ const idx = ctx.value?.stepIndex
104
+ if (idx == null) return []
105
+ return (instance.value?.outputHistory ?? []).filter((o) => o.stepIndex === idx)
106
+ })
107
+ // Whether this step has ANY prior-attempt history (successful outputs and/or failures).
108
+ const hasStepHistory = computed(() => stepFailures.value.length > 0 || stepOutputs.value.length > 0)
99
109
  const showHistory = ref(false)
100
110
 
101
111
  // A failed run is no longer executing: a step left mid-flight (state still
@@ -439,10 +449,10 @@ async function copyOutput() {
439
449
  />
440
450
  </div>
441
451
 
442
- <!-- this step's failure trail (the run-level history narrowed to this step),
443
- behind a toggle — mirrors the "previous errors" history on the task inspector
444
- but scoped to the step the user is looking at -->
445
- <div v-if="stepFailures.length">
452
+ <!-- this step's execution history (the run-level trail narrowed to this step),
453
+ behind a toggle — a merged timeline of the SUCCESSFUL outputs a restart
454
+ superseded and the FAILED attempts, scoped to the step being looked at -->
455
+ <div v-if="hasStepHistory">
446
456
  <UButton
447
457
  :icon="showHistory ? 'i-lucide-chevron-up' : 'i-lucide-history'"
448
458
  variant="ghost"
@@ -460,10 +470,11 @@ async function copyOutput() {
460
470
  : t('panels.stepDetail.executionHistory')
461
471
  }}
462
472
  </UButton>
463
- <FailureHistoryList
473
+ <StepExecutionHistory
464
474
  v-if="showHistory"
465
475
  class="mt-2"
466
476
  :failures="stepFailures"
477
+ :outputs="stepOutputs"
467
478
  data-testid="step-execution-history"
468
479
  />
469
480
  </div>
@@ -18,6 +18,7 @@ export type {
18
18
  AgentRunKind,
19
19
  AgentFailureKind,
20
20
  AgentFailure,
21
+ PriorStepOutput,
21
22
  StepApproval,
22
23
  StepMetrics,
23
24
  LlmCallMetric,
@@ -1027,6 +1027,8 @@
1027
1027
  "hideInfraAttempts": "Hide infrastructure attempts",
1028
1028
  "executionHistory": "Execution history",
1029
1029
  "hideExecutionHistory": "Hide execution history",
1030
+ "attemptSucceeded": "Succeeded",
1031
+ "outputTruncated": "Output clipped to keep the run history compact.",
1030
1032
  "editingConclusions": "Editing the conclusions",
1031
1033
  "editConclusionsPlaceholder": "Edit the agent's conclusions; your edits are saved when you approve…",
1032
1034
  "noProseOutput": "This agent produced no prose output.",
@@ -984,6 +984,8 @@
984
984
  "hideInfraAttempts": "Ocultar intentos de infraestructura",
985
985
  "executionHistory": "Historial de ejecución",
986
986
  "hideExecutionHistory": "Ocultar historial de ejecución",
987
+ "attemptSucceeded": "Correcto",
988
+ "outputTruncated": "Salida recortada para mantener compacto el historial de ejecución.",
987
989
  "editingConclusions": "Editando las conclusiones",
988
990
  "editConclusionsPlaceholder": "Edita las conclusiones del agente; tus cambios se guardan cuando apruebas…",
989
991
  "noProseOutput": "Este agente no produjo salida en prosa.",
@@ -984,6 +984,8 @@
984
984
  "hideInfraAttempts": "Masquer les tentatives d'infrastructure",
985
985
  "executionHistory": "Historique d'exécution",
986
986
  "hideExecutionHistory": "Masquer l'historique d'exécution",
987
+ "attemptSucceeded": "Réussi",
988
+ "outputTruncated": "Sortie tronquée pour garder l'historique d'exécution compact.",
987
989
  "editingConclusions": "Modification des conclusions",
988
990
  "editConclusionsPlaceholder": "Modifiez les conclusions de l'agent ; vos modifications sont enregistrées lorsque vous approuvez…",
989
991
  "noProseOutput": "Cet agent n'a produit aucune sortie en texte libre.",
@@ -984,6 +984,8 @@
984
984
  "hideInfraAttempts": "הסתר ניסיונות תשתית",
985
985
  "executionHistory": "היסטוריית הרצה",
986
986
  "hideExecutionHistory": "הסתר היסטוריית הרצה",
987
+ "attemptSucceeded": "הצליח",
988
+ "outputTruncated": "הפלט נקטע כדי לשמור על היסטוריית ההרצה קומפקטית.",
987
989
  "editingConclusions": "עריכת המסקנות",
988
990
  "editConclusionsPlaceholder": "ערוך את מסקנות הסוכן; העריכות שלך נשמרות כשתאשר…",
989
991
  "noProseOutput": "סוכן זה לא הפיק פלט טקסטואלי.",
@@ -984,6 +984,8 @@
984
984
  "hideInfraAttempts": "インフラの試行を非表示",
985
985
  "executionHistory": "実行履歴",
986
986
  "hideExecutionHistory": "実行履歴を非表示",
987
+ "attemptSucceeded": "成功",
988
+ "outputTruncated": "実行履歴を簡潔に保つため出力を切り詰めました。",
987
989
  "editingConclusions": "結論を編集中",
988
990
  "editConclusionsPlaceholder": "エージェントの結論を編集してください。編集内容は承認時に保存されます…",
989
991
  "noProseOutput": "このエージェントは文章出力を生成しませんでした。",
@@ -984,6 +984,8 @@
984
984
  "hideInfraAttempts": "Ukryj próby infrastrukturalne",
985
985
  "executionHistory": "Historia wykonania",
986
986
  "hideExecutionHistory": "Ukryj historię wykonania",
987
+ "attemptSucceeded": "Powodzenie",
988
+ "outputTruncated": "Wynik przycięty, aby zachować zwięzłość historii wykonania.",
987
989
  "editingConclusions": "Edytowanie wniosków",
988
990
  "editConclusionsPlaceholder": "Edytuj wnioski agenta; Twoje zmiany zostaną zapisane po zatwierdzeniu…",
989
991
  "noProseOutput": "Ten agent nie wytworzył wyniku tekstowego.",
@@ -984,6 +984,8 @@
984
984
  "hideInfraAttempts": "Altyapı denemelerini gizle",
985
985
  "executionHistory": "Yürütme geçmişi",
986
986
  "hideExecutionHistory": "Yürütme geçmişini gizle",
987
+ "attemptSucceeded": "Başarılı",
988
+ "outputTruncated": "Yürütme geçmişini derli toplu tutmak için çıktı kırpıldı.",
987
989
  "editingConclusions": "Sonuçlar düzenleniyor",
988
990
  "editConclusionsPlaceholder": "Aracının sonuçlarını düzenleyin; düzenlemeleriniz onayladığınızda kaydedilir…",
989
991
  "noProseOutput": "Bu aracı herhangi bir metin çıktısı üretmedi.",
@@ -984,6 +984,8 @@
984
984
  "hideInfraAttempts": "Приховати спроби інфраструктури",
985
985
  "executionHistory": "Історія виконання",
986
986
  "hideExecutionHistory": "Приховати історію виконання",
987
+ "attemptSucceeded": "Успішно",
988
+ "outputTruncated": "Вивід обрізано, щоб історія виконання залишалася компактною.",
987
989
  "editingConclusions": "Редагування висновків",
988
990
  "editConclusionsPlaceholder": "Відредагуйте висновки агента; ваші зміни зберігаються після затвердження…",
989
991
  "noProseOutput": "Цей агент не створив текстового виводу.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.99.0",
3
+ "version": "0.100.1",
4
4
  "description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -34,7 +34,7 @@
34
34
  "valibot": "^1.4.2",
35
35
  "vue": "3.5.39",
36
36
  "wretch": "^3.0.9",
37
- "@cat-factory/contracts": "0.109.0"
37
+ "@cat-factory/contracts": "0.110.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@toad-contracts/testing": "0.3.2",