@cat-factory/app 0.46.9 → 0.46.11
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.
- package/app/components/board/AddTaskModal.vue +106 -88
- package/app/components/board/AgentFailureCard.vue +8 -5
- package/app/components/board/AgentStopButton.vue +8 -5
- package/app/components/board/BoardCanvas.vue +13 -9
- package/app/components/board/RecurringPipelineModal.vue +33 -23
- package/app/components/board/nodes/BlockNode.vue +50 -34
- package/app/components/board/nodes/DecisionBadge.vue +6 -3
- package/app/components/board/nodes/DraggableTask.vue +2 -1
- package/app/components/board/nodes/EpicNode.vue +10 -3
- package/app/components/board/nodes/ModuleFrame.vue +6 -5
- package/app/components/board/nodes/TaskCard.vue +40 -22
- package/app/components/board/nodes/TaskPipelineMini.vue +4 -3
- package/app/components/layout/BoardToolbar.vue +26 -19
- package/app/components/observability/StepMetricsBar.vue +31 -11
- package/app/components/observability/StepModelActivity.vue +3 -2
- package/app/components/panels/AgentStepDetail.vue +52 -39
- package/app/components/panels/DecisionModal.vue +12 -5
- package/app/components/panels/GenericStructuredResultView.vue +16 -6
- package/app/components/panels/InspectorPanel.vue +35 -28
- package/app/components/panels/ObservabilityPanel.vue +92 -49
- package/app/components/panels/StepMetadataCard.vue +87 -30
- package/app/components/panels/StepRestartControl.vue +4 -3
- package/app/components/panels/StepRunMeta.vue +23 -10
- package/app/components/panels/StepTestReport.vue +14 -11
- package/app/components/panels/inspector/ContainerSummary.vue +25 -14
- package/app/components/panels/inspector/EpicChildren.vue +7 -4
- package/app/components/panels/inspector/RecurringScheduleSettings.vue +60 -19
- package/app/components/panels/inspector/ServiceFragments.vue +3 -2
- package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +18 -13
- package/app/components/panels/inspector/ServiceTestConfig.vue +50 -39
- package/app/components/panels/inspector/TaskAgentConfig.vue +6 -5
- package/app/components/panels/inspector/TaskDependencies.vue +9 -4
- package/app/components/panels/inspector/TaskEstimateBadge.vue +14 -13
- package/app/components/panels/inspector/TaskExecution.vue +65 -31
- package/app/components/panels/inspector/TaskRunSettings.vue +93 -56
- package/app/components/panels/inspector/TaskStructure.vue +5 -4
- package/i18n/locales/en.json +606 -0
- package/i18n/locales/es.json +596 -2
- package/i18n/locales/fr.json +596 -2
- package/i18n/locales/pl.json +596 -2
- package/i18n/locales/uk.json +596 -2
- package/package.json +1 -1
|
@@ -15,6 +15,7 @@ const ui = useUiStore()
|
|
|
15
15
|
const execution = useExecutionStore()
|
|
16
16
|
const board = useBoardStore()
|
|
17
17
|
const observability = useObservabilityStore()
|
|
18
|
+
const { t, d } = useI18n()
|
|
18
19
|
|
|
19
20
|
const executionId = computed(() => ui.observabilityInstanceId)
|
|
20
21
|
const open = computed(() => !!executionId.value)
|
|
@@ -110,7 +111,7 @@ function agentMeta(kind: string) {
|
|
|
110
111
|
return agentKindMeta(kind)
|
|
111
112
|
}
|
|
112
113
|
function clock(ms: number): string {
|
|
113
|
-
return new Date(ms)
|
|
114
|
+
return d(new Date(ms), 'long')
|
|
114
115
|
}
|
|
115
116
|
/** Pretty-print the prompt JSON; fall back to the raw string if it isn't JSON. */
|
|
116
117
|
function prettyPrompt(raw: string): string {
|
|
@@ -150,7 +151,9 @@ function exportJson() {
|
|
|
150
151
|
<UIcon name="i-lucide-activity" class="h-5 w-5 text-sky-400" />
|
|
151
152
|
</div>
|
|
152
153
|
<div class="min-w-0">
|
|
153
|
-
<h1 class="truncate text-base font-semibold text-white">
|
|
154
|
+
<h1 class="truncate text-base font-semibold text-white">
|
|
155
|
+
{{ t('observability.modelActivity') }}
|
|
156
|
+
</h1>
|
|
154
157
|
<p v-if="block" class="truncate text-xs text-slate-500">
|
|
155
158
|
{{ block.title }} · {{ instance?.pipelineName }}
|
|
156
159
|
</p>
|
|
@@ -166,7 +169,7 @@ function exportJson() {
|
|
|
166
169
|
"
|
|
167
170
|
@click="view = 'calls'"
|
|
168
171
|
>
|
|
169
|
-
|
|
172
|
+
{{ t('observability.modelActivity') }}
|
|
170
173
|
</button>
|
|
171
174
|
<button
|
|
172
175
|
class="rounded-md px-2.5 py-1 transition"
|
|
@@ -177,7 +180,7 @@ function exportJson() {
|
|
|
177
180
|
"
|
|
178
181
|
@click="view = 'context'"
|
|
179
182
|
>
|
|
180
|
-
|
|
183
|
+
{{ t('observability.providedContext') }}
|
|
181
184
|
</button>
|
|
182
185
|
</div>
|
|
183
186
|
<UButton
|
|
@@ -188,17 +191,17 @@ function exportJson() {
|
|
|
188
191
|
size="sm"
|
|
189
192
|
:loading="exporting"
|
|
190
193
|
:disabled="!calls.length"
|
|
191
|
-
title="
|
|
194
|
+
:title="t('observability.exportHint')"
|
|
192
195
|
@click="exportJson"
|
|
193
196
|
>
|
|
194
|
-
|
|
197
|
+
{{ t('observability.exportJson') }}
|
|
195
198
|
</UButton>
|
|
196
199
|
<UButton
|
|
197
200
|
icon="i-lucide-x"
|
|
198
201
|
color="neutral"
|
|
199
202
|
variant="ghost"
|
|
200
203
|
size="sm"
|
|
201
|
-
title="
|
|
204
|
+
:title="t('observability.closeEsc')"
|
|
202
205
|
@click="close"
|
|
203
206
|
/>
|
|
204
207
|
</div>
|
|
@@ -210,12 +213,14 @@ function exportJson() {
|
|
|
210
213
|
<section class="rounded-xl border border-slate-800 bg-slate-900/50 p-4">
|
|
211
214
|
<dl class="grid grid-cols-2 gap-x-6 gap-y-3 text-[13px] sm:grid-cols-4">
|
|
212
215
|
<div>
|
|
213
|
-
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
216
|
+
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
217
|
+
{{ t('observability.summary.calls') }}
|
|
218
|
+
</dt>
|
|
214
219
|
<dd class="mt-0.5 tabular-nums text-slate-200">{{ totals.calls }}</dd>
|
|
215
220
|
</div>
|
|
216
221
|
<div>
|
|
217
222
|
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
218
|
-
|
|
223
|
+
{{ t('observability.summary.tokensInOut') }}
|
|
219
224
|
</dt>
|
|
220
225
|
<dd class="mt-0.5 tabular-nums text-slate-200">
|
|
221
226
|
{{ formatTokens(totals.promptTokens) }} /
|
|
@@ -224,7 +229,7 @@ function exportJson() {
|
|
|
224
229
|
</div>
|
|
225
230
|
<div>
|
|
226
231
|
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
227
|
-
|
|
232
|
+
{{ t('observability.summary.transportOverhead') }}
|
|
228
233
|
</dt>
|
|
229
234
|
<dd class="mt-0.5 tabular-nums text-slate-200">
|
|
230
235
|
<span v-if="totals.transportPct !== null">
|
|
@@ -235,7 +240,7 @@ function exportJson() {
|
|
|
235
240
|
</div>
|
|
236
241
|
<div>
|
|
237
242
|
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
238
|
-
|
|
243
|
+
{{ t('observability.summary.modelExecution') }}
|
|
239
244
|
</dt>
|
|
240
245
|
<dd class="mt-0.5 tabular-nums text-slate-200">
|
|
241
246
|
{{ formatMs(totals.upstreamMs) }}
|
|
@@ -244,13 +249,27 @@ function exportJson() {
|
|
|
244
249
|
</dl>
|
|
245
250
|
<div class="mt-3 flex flex-wrap gap-1.5">
|
|
246
251
|
<UBadge v-if="totals.errors" color="error" variant="subtle" size="sm">
|
|
247
|
-
{{
|
|
252
|
+
{{
|
|
253
|
+
t('observability.metricsBar.errors', { count: totals.errors }, totals.errors)
|
|
254
|
+
}}
|
|
248
255
|
</UBadge>
|
|
249
256
|
<UBadge v-if="totals.warnings" color="warning" variant="subtle" size="sm">
|
|
250
|
-
{{
|
|
257
|
+
{{
|
|
258
|
+
t(
|
|
259
|
+
'observability.metricsBar.warnings',
|
|
260
|
+
{ count: totals.warnings },
|
|
261
|
+
totals.warnings,
|
|
262
|
+
)
|
|
263
|
+
}}
|
|
251
264
|
</UBadge>
|
|
252
265
|
<UBadge v-if="totals.truncated" color="error" variant="subtle" size="sm">
|
|
253
|
-
{{
|
|
266
|
+
{{
|
|
267
|
+
t(
|
|
268
|
+
'observability.summary.truncated',
|
|
269
|
+
{ count: totals.truncated },
|
|
270
|
+
totals.truncated,
|
|
271
|
+
)
|
|
272
|
+
}}
|
|
254
273
|
</UBadge>
|
|
255
274
|
</div>
|
|
256
275
|
</section>
|
|
@@ -260,8 +279,8 @@ function exportJson() {
|
|
|
260
279
|
v-if="loading && !calls.length"
|
|
261
280
|
class="flex items-center gap-2 py-8 text-center text-sm text-slate-500 justify-center"
|
|
262
281
|
>
|
|
263
|
-
<UIcon name="i-lucide-loader-circle" class="h-4 w-4 animate-spin" />
|
|
264
|
-
|
|
282
|
+
<UIcon name="i-lucide-loader-circle" class="h-4 w-4 animate-spin" />
|
|
283
|
+
{{ t('observability.loadingActivity') }}
|
|
265
284
|
</p>
|
|
266
285
|
<p
|
|
267
286
|
v-else-if="error"
|
|
@@ -273,7 +292,7 @@ function exportJson() {
|
|
|
273
292
|
v-else-if="!calls.length"
|
|
274
293
|
class="rounded-lg border border-dashed border-slate-800 py-8 text-center text-sm text-slate-500"
|
|
275
294
|
>
|
|
276
|
-
|
|
295
|
+
{{ t('observability.noCalls') }}
|
|
277
296
|
</p>
|
|
278
297
|
|
|
279
298
|
<!-- per-call list -->
|
|
@@ -309,18 +328,26 @@ function exportJson() {
|
|
|
309
328
|
class="ml-auto flex items-center gap-2.5 text-[11px] tabular-nums text-slate-400"
|
|
310
329
|
>
|
|
311
330
|
<span
|
|
312
|
-
:title="
|
|
331
|
+
:title="
|
|
332
|
+
t('observability.call.tokensTitle', {
|
|
333
|
+
prompt: c.promptTokens,
|
|
334
|
+
completion: c.completionTokens,
|
|
335
|
+
})
|
|
336
|
+
"
|
|
313
337
|
>
|
|
314
338
|
{{ formatTokens(c.promptTokens) }}↑ {{ formatTokens(c.completionTokens) }}↓
|
|
315
339
|
</span>
|
|
316
|
-
<span
|
|
340
|
+
<span
|
|
341
|
+
v-if="headroomOf(c) !== null"
|
|
342
|
+
:title="t('observability.call.outputUsedVsLimit')"
|
|
343
|
+
>
|
|
317
344
|
{{ headroomOf(c) }}%
|
|
318
345
|
</span>
|
|
319
|
-
<span title="
|
|
346
|
+
<span :title="t('observability.call.transportVsExecution')">
|
|
320
347
|
{{ formatMs(c.overheadMs) }} / {{ formatMs(c.upstreamMs) }}
|
|
321
348
|
</span>
|
|
322
349
|
<UBadge v-if="!c.ok" color="error" variant="subtle" size="sm">
|
|
323
|
-
{{ c.httpStatus ?? 'error' }}
|
|
350
|
+
{{ c.httpStatus ?? t('observability.call.error') }}
|
|
324
351
|
</UBadge>
|
|
325
352
|
<UBadge
|
|
326
353
|
v-else-if="isWarning(c.finishReason)"
|
|
@@ -330,7 +357,9 @@ function exportJson() {
|
|
|
330
357
|
>
|
|
331
358
|
{{ c.finishReason }}
|
|
332
359
|
</UBadge>
|
|
333
|
-
<span v-else class="text-slate-600">{{
|
|
360
|
+
<span v-else class="text-slate-600">{{
|
|
361
|
+
c.finishReason ?? t('observability.call.ok')
|
|
362
|
+
}}</span>
|
|
334
363
|
<span class="hidden text-slate-600 md:inline">{{ clock(c.createdAt) }}</span>
|
|
335
364
|
</div>
|
|
336
365
|
</button>
|
|
@@ -340,27 +369,40 @@ function exportJson() {
|
|
|
340
369
|
{{ c.errorMessage }}
|
|
341
370
|
</p>
|
|
342
371
|
<div class="flex flex-wrap gap-x-5 gap-y-1 text-[11px] text-slate-500">
|
|
343
|
-
<span>{{ c.messageCount }}
|
|
344
|
-
<span>{{ c.toolCount }}
|
|
345
|
-
<span>{{
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
372
|
+
<span>{{ t('observability.call.messages', { count: c.messageCount }) }}</span>
|
|
373
|
+
<span>{{ t('observability.call.tools', { count: c.toolCount }) }}</span>
|
|
374
|
+
<span>{{
|
|
375
|
+
c.streaming
|
|
376
|
+
? t('observability.call.streamed')
|
|
377
|
+
: t('observability.call.buffered')
|
|
378
|
+
}}</span>
|
|
379
|
+
<span v-if="c.requestMaxTokens != null">{{
|
|
380
|
+
t('observability.call.maxTokens', { value: c.requestMaxTokens })
|
|
381
|
+
}}</span>
|
|
382
|
+
<span v-if="c.cachedPromptTokens > 0" class="text-emerald-400">{{
|
|
383
|
+
t('observability.call.promptCached', {
|
|
384
|
+
cached: c.cachedPromptTokens,
|
|
385
|
+
prompt: c.promptTokens,
|
|
386
|
+
})
|
|
387
|
+
}}</span>
|
|
388
|
+
<span>{{
|
|
389
|
+
t('observability.call.total', { duration: formatMs(c.totalMs) })
|
|
390
|
+
}}</span>
|
|
353
391
|
</div>
|
|
354
392
|
<div>
|
|
355
393
|
<div
|
|
356
394
|
class="mb-1 flex items-center gap-2 text-[11px] uppercase tracking-wide text-slate-500"
|
|
357
395
|
>
|
|
358
|
-
<span>
|
|
396
|
+
<span>{{ t('observability.call.prompt') }}</span>
|
|
359
397
|
<span
|
|
360
398
|
v-if="c.promptPrefixCount > 0"
|
|
361
399
|
class="normal-case tracking-normal text-slate-600"
|
|
362
400
|
>
|
|
363
|
-
|
|
401
|
+
{{
|
|
402
|
+
t('observability.call.promptPrefixOmitted', {
|
|
403
|
+
count: c.promptPrefixCount,
|
|
404
|
+
})
|
|
405
|
+
}}
|
|
364
406
|
</span>
|
|
365
407
|
</div>
|
|
366
408
|
<pre
|
|
@@ -370,7 +412,7 @@ function exportJson() {
|
|
|
370
412
|
</div>
|
|
371
413
|
<div>
|
|
372
414
|
<div class="mb-1 text-[11px] uppercase tracking-wide text-slate-500">
|
|
373
|
-
|
|
415
|
+
{{ t('observability.call.response') }}
|
|
374
416
|
</div>
|
|
375
417
|
<pre
|
|
376
418
|
class="max-h-72 overflow-auto rounded-lg bg-slate-950/70 p-3 text-[11px] leading-relaxed text-slate-300"
|
|
@@ -379,7 +421,7 @@ function exportJson() {
|
|
|
379
421
|
</div>
|
|
380
422
|
<div v-if="c.reasoningText">
|
|
381
423
|
<div class="mb-1 text-[11px] uppercase tracking-wide text-slate-500">
|
|
382
|
-
|
|
424
|
+
{{ t('observability.call.reasoning') }}
|
|
383
425
|
</div>
|
|
384
426
|
<pre
|
|
385
427
|
class="max-h-72 overflow-auto rounded-lg bg-slate-950/70 p-3 text-[11px] leading-relaxed text-slate-400"
|
|
@@ -397,15 +439,14 @@ function exportJson() {
|
|
|
397
439
|
v-if="contextLoading && !contextSnapshots.length"
|
|
398
440
|
class="flex items-center justify-center gap-2 py-8 text-center text-sm text-slate-500"
|
|
399
441
|
>
|
|
400
|
-
<UIcon name="i-lucide-loader-circle" class="h-4 w-4 animate-spin" />
|
|
401
|
-
|
|
442
|
+
<UIcon name="i-lucide-loader-circle" class="h-4 w-4 animate-spin" />
|
|
443
|
+
{{ t('observability.loadingContext') }}
|
|
402
444
|
</p>
|
|
403
445
|
<p
|
|
404
446
|
v-else-if="!contextSnapshots.length"
|
|
405
447
|
class="rounded-lg border border-dashed border-slate-800 py-8 text-center text-sm text-slate-500"
|
|
406
448
|
>
|
|
407
|
-
|
|
408
|
-
has "Store full agent context" enabled.
|
|
449
|
+
{{ t('observability.noContext') }}
|
|
409
450
|
</p>
|
|
410
451
|
|
|
411
452
|
<ul v-else class="space-y-2">
|
|
@@ -435,10 +476,12 @@ function exportJson() {
|
|
|
435
476
|
<div
|
|
436
477
|
class="ml-auto flex items-center gap-2.5 text-[11px] tabular-nums text-slate-400"
|
|
437
478
|
>
|
|
438
|
-
<span :title="'
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
>
|
|
479
|
+
<span :title="t('observability.context.injectedFiles')">{{
|
|
480
|
+
t('observability.context.filesCount', { count: s.contextFiles.length })
|
|
481
|
+
}}</span>
|
|
482
|
+
<span :title="t('observability.context.bestPracticeFragments')">{{
|
|
483
|
+
t('observability.context.fragmentsCount', { count: s.fragments.length })
|
|
484
|
+
}}</span>
|
|
442
485
|
<span class="hidden text-slate-600 md:inline">{{ clock(s.createdAt) }}</span>
|
|
443
486
|
</div>
|
|
444
487
|
</button>
|
|
@@ -446,7 +489,7 @@ function exportJson() {
|
|
|
446
489
|
<div v-if="expandedCtx[s.id]" class="border-t border-slate-800 px-4 py-3 space-y-3">
|
|
447
490
|
<div>
|
|
448
491
|
<div class="mb-1 text-[11px] uppercase tracking-wide text-slate-500">
|
|
449
|
-
|
|
492
|
+
{{ t('observability.context.systemPrompt') }}
|
|
450
493
|
</div>
|
|
451
494
|
<pre
|
|
452
495
|
class="max-h-72 overflow-auto rounded-lg bg-slate-950/70 p-3 text-[11px] leading-relaxed text-slate-300"
|
|
@@ -455,7 +498,7 @@ function exportJson() {
|
|
|
455
498
|
</div>
|
|
456
499
|
<div>
|
|
457
500
|
<div class="mb-1 text-[11px] uppercase tracking-wide text-slate-500">
|
|
458
|
-
|
|
501
|
+
{{ t('observability.context.userPrompt') }}
|
|
459
502
|
</div>
|
|
460
503
|
<pre
|
|
461
504
|
class="max-h-72 overflow-auto rounded-lg bg-slate-950/70 p-3 text-[11px] leading-relaxed text-slate-300"
|
|
@@ -464,7 +507,7 @@ function exportJson() {
|
|
|
464
507
|
</div>
|
|
465
508
|
<div v-if="s.fragments.length">
|
|
466
509
|
<div class="mb-1 text-[11px] uppercase tracking-wide text-slate-500">
|
|
467
|
-
|
|
510
|
+
{{ t('observability.context.bestPracticeFragments') }}
|
|
468
511
|
</div>
|
|
469
512
|
<div
|
|
470
513
|
v-for="f in s.fragments"
|
|
@@ -480,7 +523,7 @@ function exportJson() {
|
|
|
480
523
|
</div>
|
|
481
524
|
<div v-if="s.contextFiles.length">
|
|
482
525
|
<div class="mb-1 text-[11px] uppercase tracking-wide text-slate-500">
|
|
483
|
-
|
|
526
|
+
{{ t('observability.context.injectedFiles') }}
|
|
484
527
|
</div>
|
|
485
528
|
<div
|
|
486
529
|
v-for="file in s.contextFiles"
|
|
@@ -499,7 +542,7 @@ function exportJson() {
|
|
|
499
542
|
</div>
|
|
500
543
|
<div>
|
|
501
544
|
<div class="mb-1 text-[11px] uppercase tracking-wide text-slate-500">
|
|
502
|
-
|
|
545
|
+
{{ t('observability.context.details') }}
|
|
503
546
|
</div>
|
|
504
547
|
<pre
|
|
505
548
|
class="max-h-48 overflow-auto rounded-lg bg-slate-950/70 p-3 text-[11px] leading-relaxed text-slate-400"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue'
|
|
3
|
-
import type { AgentState, PipelineStep, CompanionVerdict } from '~/types/execution'
|
|
3
|
+
import type { AgentState, PipelineStep, CompanionVerdict, StepApproval } from '~/types/execution'
|
|
4
4
|
import { subtaskIconClass } from '~/utils/pipelineRender'
|
|
5
5
|
import StepModelActivity from '~/components/observability/StepModelActivity.vue'
|
|
6
6
|
|
|
@@ -21,20 +21,28 @@ const props = defineProps<{
|
|
|
21
21
|
}>()
|
|
22
22
|
|
|
23
23
|
const models = useModelsStore()
|
|
24
|
+
const { t, d } = useI18n()
|
|
24
25
|
|
|
25
|
-
const
|
|
26
|
-
pending:
|
|
27
|
-
working:
|
|
28
|
-
waiting_decision:
|
|
29
|
-
done:
|
|
26
|
+
const STATE_LABEL_KEYS: Record<AgentState, string> = {
|
|
27
|
+
pending: 'panels.stepMeta.state.pending',
|
|
28
|
+
working: 'panels.stepMeta.state.working',
|
|
29
|
+
waiting_decision: 'panels.stepMeta.state.waiting_decision',
|
|
30
|
+
done: 'panels.stepMeta.state.done',
|
|
31
|
+
}
|
|
32
|
+
const STATE_COLOR: Record<AgentState, string> = {
|
|
33
|
+
pending: '#64748b',
|
|
34
|
+
working: '#6366f1',
|
|
35
|
+
waiting_decision: '#f59e0b',
|
|
36
|
+
done: '#22c55e',
|
|
30
37
|
}
|
|
31
38
|
|
|
32
39
|
// The state badge: a step left mid-flight on a failed run keeps `state: 'working'`,
|
|
33
40
|
// so report it as "Failed" rather than the misleading "Working".
|
|
34
41
|
const stateMeta = computed(() => {
|
|
35
42
|
const s = props.step
|
|
36
|
-
if (props.runFailed && s.state === 'working')
|
|
37
|
-
|
|
43
|
+
if (props.runFailed && s.state === 'working')
|
|
44
|
+
return { label: t('panels.stepMeta.state.failed'), color: '#ef4444' }
|
|
45
|
+
return { label: t(STATE_LABEL_KEYS[s.state]), color: STATE_COLOR[s.state] }
|
|
38
46
|
})
|
|
39
47
|
|
|
40
48
|
const modelLabel = computed(() => (props.step.model ? models.labelForRef(props.step.model) : null))
|
|
@@ -47,8 +55,18 @@ const ITEM_ICON: Record<string, string> = {
|
|
|
47
55
|
|
|
48
56
|
const pctOf = (n: number) => `${Math.round(n * 100)}%`
|
|
49
57
|
|
|
58
|
+
const APPROVAL_STATUS_KEYS: Record<StepApproval['status'], string> = {
|
|
59
|
+
pending: 'panels.stepMeta.approvalStatus.pending',
|
|
60
|
+
approved: 'panels.stepMeta.approvalStatus.approved',
|
|
61
|
+
changes_requested: 'panels.stepMeta.approvalStatus.changes_requested',
|
|
62
|
+
rejected: 'panels.stepMeta.approvalStatus.rejected',
|
|
63
|
+
}
|
|
64
|
+
const approvalStatusLabel = computed(() =>
|
|
65
|
+
props.step.approval ? t(APPROVAL_STATUS_KEYS[props.step.approval.status]) : '',
|
|
66
|
+
)
|
|
67
|
+
|
|
50
68
|
function formatClock(ms?: number | null): string | null {
|
|
51
|
-
return ms ? new Date(ms)
|
|
69
|
+
return ms ? d(new Date(ms), 'long') : null
|
|
52
70
|
}
|
|
53
71
|
|
|
54
72
|
async function copyRunId() {
|
|
@@ -61,7 +79,9 @@ async function copyRunId() {
|
|
|
61
79
|
<div>
|
|
62
80
|
<dl class="grid grid-cols-2 gap-x-6 gap-y-3 text-[13px] sm:grid-cols-3">
|
|
63
81
|
<div>
|
|
64
|
-
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
82
|
+
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
83
|
+
{{ t('panels.stepMeta.stateLabel') }}
|
|
84
|
+
</dt>
|
|
65
85
|
<dd class="mt-0.5 flex items-center gap-1.5 text-slate-200">
|
|
66
86
|
<UIcon
|
|
67
87
|
v-if="runFailed && step.state === 'working'"
|
|
@@ -74,7 +94,9 @@ async function copyRunId() {
|
|
|
74
94
|
</dd>
|
|
75
95
|
</div>
|
|
76
96
|
<div>
|
|
77
|
-
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
97
|
+
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
98
|
+
{{ t('panels.stepMeta.duration') }}
|
|
99
|
+
</dt>
|
|
78
100
|
<dd class="mt-0.5 flex items-center gap-1.5 tabular-nums text-slate-200">
|
|
79
101
|
<UIcon
|
|
80
102
|
v-if="isRunning"
|
|
@@ -83,33 +105,47 @@ async function copyRunId() {
|
|
|
83
105
|
/>
|
|
84
106
|
<span v-if="durationLabel">{{ durationLabel }}</span>
|
|
85
107
|
<span v-else class="text-slate-500">—</span>
|
|
86
|
-
<span v-if="isRunning" class="text-[11px] text-slate-500">
|
|
108
|
+
<span v-if="isRunning" class="text-[11px] text-slate-500">{{
|
|
109
|
+
t('panels.stepMeta.elapsed')
|
|
110
|
+
}}</span>
|
|
87
111
|
</dd>
|
|
88
112
|
</div>
|
|
89
113
|
<div>
|
|
90
|
-
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
91
|
-
|
|
114
|
+
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
115
|
+
{{ t('panels.stepMeta.step') }}
|
|
116
|
+
</dt>
|
|
117
|
+
<dd class="mt-0.5 text-slate-200">
|
|
118
|
+
{{ t('panels.stepMeta.stepOf', { number: stepNumber, total: totalSteps }) }}
|
|
119
|
+
</dd>
|
|
92
120
|
</div>
|
|
93
121
|
<div>
|
|
94
|
-
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
122
|
+
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
123
|
+
{{ t('panels.stepMeta.started') }}
|
|
124
|
+
</dt>
|
|
95
125
|
<dd class="mt-0.5 text-slate-300">{{ formatClock(step.startedAt) ?? '—' }}</dd>
|
|
96
126
|
</div>
|
|
97
127
|
<div>
|
|
98
|
-
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
128
|
+
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
129
|
+
{{ t('panels.stepMeta.finished') }}
|
|
130
|
+
</dt>
|
|
99
131
|
<dd class="mt-0.5 text-slate-300">{{ formatClock(step.finishedAt) ?? '—' }}</dd>
|
|
100
132
|
</div>
|
|
101
133
|
<div>
|
|
102
|
-
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
134
|
+
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
135
|
+
{{ t('panels.stepMeta.model') }}
|
|
136
|
+
</dt>
|
|
103
137
|
<dd class="mt-0.5 truncate text-slate-300" :title="step.model">
|
|
104
|
-
{{ modelLabel ?? '
|
|
138
|
+
{{ modelLabel ?? t('panels.stepMeta.notRecorded') }}
|
|
105
139
|
</dd>
|
|
106
140
|
</div>
|
|
107
141
|
<!-- The run id this step belongs to, surfaced for debugging (copyable). -->
|
|
108
142
|
<div class="col-span-2 sm:col-span-3">
|
|
109
|
-
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
143
|
+
<dt class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
144
|
+
{{ t('panels.stepMeta.run') }}
|
|
145
|
+
</dt>
|
|
110
146
|
<dd
|
|
111
147
|
class="mt-0.5 cursor-pointer truncate font-mono text-[12px] text-slate-400 hover:text-slate-200"
|
|
112
|
-
:title="
|
|
148
|
+
:title="t('panels.stepMeta.clickToCopy', { id: step.runId ?? instanceId ?? '' })"
|
|
113
149
|
@click="copyRunId"
|
|
114
150
|
>
|
|
115
151
|
{{ step.runId ?? instanceId ?? '—' }}
|
|
@@ -124,13 +160,18 @@ async function copyRunId() {
|
|
|
124
160
|
class="mt-4 flex items-center gap-2 rounded-lg border border-sky-900/50 bg-sky-950/30 px-3 py-2 text-[12px] text-sky-300"
|
|
125
161
|
>
|
|
126
162
|
<UIcon name="i-lucide-loader-circle" class="h-4 w-4 shrink-0 animate-spin" />
|
|
127
|
-
<span>
|
|
163
|
+
<span>{{ t('panels.stepMeta.spinningUpContainer') }}</span>
|
|
128
164
|
</div>
|
|
129
165
|
|
|
130
166
|
<!-- live subtask breakdown -->
|
|
131
167
|
<div v-if="step.subtasks && step.subtasks.total > 0" class="mt-4">
|
|
132
168
|
<div class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
133
|
-
|
|
169
|
+
{{
|
|
170
|
+
t('panels.stepMeta.subtasks', {
|
|
171
|
+
completed: step.subtasks.completed,
|
|
172
|
+
total: step.subtasks.total,
|
|
173
|
+
})
|
|
174
|
+
}}
|
|
134
175
|
</div>
|
|
135
176
|
<div class="mt-1 h-1 overflow-hidden rounded-full bg-slate-700/60">
|
|
136
177
|
<div
|
|
@@ -170,7 +211,9 @@ async function copyRunId() {
|
|
|
170
211
|
|
|
171
212
|
<!-- standards (prompt fragments) folded into this step -->
|
|
172
213
|
<div v-if="step.selectedFragmentIds && step.selectedFragmentIds.length" class="mt-4">
|
|
173
|
-
<div class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
214
|
+
<div class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
215
|
+
{{ t('panels.stepMeta.standardsApplied') }}
|
|
216
|
+
</div>
|
|
174
217
|
<div class="mt-1 flex flex-wrap gap-1">
|
|
175
218
|
<UBadge
|
|
176
219
|
v-for="id in step.selectedFragmentIds"
|
|
@@ -186,7 +229,9 @@ async function copyRunId() {
|
|
|
186
229
|
|
|
187
230
|
<!-- decision raised on this step -->
|
|
188
231
|
<div v-if="step.decision" class="mt-4">
|
|
189
|
-
<div class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
232
|
+
<div class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
233
|
+
{{ t('panels.stepMeta.decision') }}
|
|
234
|
+
</div>
|
|
190
235
|
<p class="mt-0.5 text-[13px] text-slate-200">{{ step.decision.question }}</p>
|
|
191
236
|
<p
|
|
192
237
|
v-if="step.decision.chosen"
|
|
@@ -195,21 +240,27 @@ async function copyRunId() {
|
|
|
195
240
|
<UIcon name="i-lucide-check" class="h-3 w-3 shrink-0" />
|
|
196
241
|
{{ step.decision.chosen }}
|
|
197
242
|
</p>
|
|
198
|
-
<p v-else class="mt-0.5 text-[12px] text-amber-400">
|
|
243
|
+
<p v-else class="mt-0.5 text-[12px] text-amber-400">
|
|
244
|
+
{{ t('panels.stepMeta.awaitingChoice') }}
|
|
245
|
+
</p>
|
|
199
246
|
</div>
|
|
200
247
|
|
|
201
248
|
<!-- approval gate state -->
|
|
202
249
|
<div v-if="step.approval" class="mt-4">
|
|
203
|
-
<div class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
204
|
-
|
|
205
|
-
|
|
250
|
+
<div class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
251
|
+
{{ t('panels.stepMeta.approvalGate') }}
|
|
252
|
+
</div>
|
|
253
|
+
<p class="mt-0.5 text-[13px] text-slate-200">
|
|
254
|
+
{{ approvalStatusLabel }}
|
|
206
255
|
</p>
|
|
207
256
|
</div>
|
|
208
257
|
|
|
209
258
|
<!-- companion verdict + full correction sequence -->
|
|
210
259
|
<div v-if="companionVerdicts.length" class="mt-4">
|
|
211
260
|
<div class="flex items-center justify-between">
|
|
212
|
-
<span class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
261
|
+
<span class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
262
|
+
{{ t('panels.stepMeta.companionReview') }}
|
|
263
|
+
</span>
|
|
213
264
|
<UBadge :color="latestVerdict?.passed ? 'success' : 'warning'" variant="subtle" size="sm">
|
|
214
265
|
{{ pctOf(latestVerdict!.rating) }}
|
|
215
266
|
{{ latestVerdict?.passed ? '≥' : '<' }} {{ pctOf(latestVerdict!.threshold) }}
|
|
@@ -234,7 +285,13 @@ async function copyRunId() {
|
|
|
234
285
|
</li>
|
|
235
286
|
</ol>
|
|
236
287
|
<p v-if="companionVerdicts.length > 1" class="mt-1 text-[11px] text-slate-500">
|
|
237
|
-
{{
|
|
288
|
+
{{
|
|
289
|
+
t(
|
|
290
|
+
'panels.stepMeta.correctionIterations',
|
|
291
|
+
{ count: companionVerdicts.length },
|
|
292
|
+
companionVerdicts.length,
|
|
293
|
+
)
|
|
294
|
+
}}
|
|
238
295
|
</p>
|
|
239
296
|
</div>
|
|
240
297
|
</div>
|
|
@@ -25,6 +25,7 @@ const props = defineProps<{
|
|
|
25
25
|
const emit = defineEmits<{ restarted: [] }>()
|
|
26
26
|
|
|
27
27
|
const execution = useExecutionStore()
|
|
28
|
+
const { t } = useI18n()
|
|
28
29
|
|
|
29
30
|
const instance = computed(() =>
|
|
30
31
|
props.instanceId ? execution.getInstance(props.instanceId) : undefined,
|
|
@@ -62,7 +63,7 @@ async function restart() {
|
|
|
62
63
|
color="neutral"
|
|
63
64
|
variant="ghost"
|
|
64
65
|
size="sm"
|
|
65
|
-
title="
|
|
66
|
+
:title="t('panels.stepRestart.restartFromStep')"
|
|
66
67
|
@click="armed = true"
|
|
67
68
|
/>
|
|
68
69
|
<template v-else>
|
|
@@ -74,7 +75,7 @@ async function restart() {
|
|
|
74
75
|
:loading="restarting"
|
|
75
76
|
@click="restart"
|
|
76
77
|
>
|
|
77
|
-
|
|
78
|
+
{{ t('panels.stepRestart.restartFromHere') }}
|
|
78
79
|
</UButton>
|
|
79
80
|
<UButton
|
|
80
81
|
color="neutral"
|
|
@@ -83,7 +84,7 @@ async function restart() {
|
|
|
83
84
|
:disabled="restarting"
|
|
84
85
|
@click="armed = false"
|
|
85
86
|
>
|
|
86
|
-
|
|
87
|
+
{{ t('common.cancel') }}
|
|
87
88
|
</UButton>
|
|
88
89
|
</template>
|
|
89
90
|
</template>
|