@cat-factory/app 0.47.6 → 0.47.8

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.
@@ -9,12 +9,13 @@ import type { ConsensusStrategy } from '~/types/consensus'
9
9
  type DraftUnit = { index: number; kind: AgentKind; companionIndex: number | null }
10
10
 
11
11
  const pipelines = usePipelinesStore()
12
+ const { t } = useI18n()
12
13
 
13
- const CONSENSUS_STRATEGIES: { value: ConsensusStrategy; label: string }[] = [
14
- { value: 'specialist-panel', label: 'Specialist panel' },
15
- { value: 'debate', label: 'Debate' },
16
- { value: 'ranked-voting', label: 'Ranked voting' },
17
- ]
14
+ const CONSENSUS_STRATEGIES = computed<{ value: ConsensusStrategy; label: string }[]>(() => [
15
+ { value: 'specialist-panel', label: t('pipeline.builder.strategyOption.specialist-panel') },
16
+ { value: 'debate', label: t('pipeline.builder.strategyOption.debate') },
17
+ { value: 'ranked-voting', label: t('pipeline.builder.strategyOption.ranked-voting') },
18
+ ])
18
19
 
19
20
  /** Add a blank participant to the draft step's consensus config. */
20
21
  function addParticipant(i: number) {
@@ -82,12 +83,20 @@ function createAgent() {
82
83
  label: newAgentName.value,
83
84
  description: newAgentDesc.value,
84
85
  })
85
- toast.add({ title: `Added agent “${agent.label}”`, color: 'success', icon: 'i-lucide-check' })
86
+ toast.add({
87
+ title: t('pipeline.builder.toast.added', { name: agent.label }),
88
+ color: 'success',
89
+ icon: 'i-lucide-check',
90
+ })
86
91
  addAgentOpen.value = false
87
92
  }
88
93
 
89
94
  function placeholder(what: string) {
90
- toast.add({ title: 'Placeholder', description: what, icon: 'i-lucide-construction' })
95
+ toast.add({
96
+ title: t('pipeline.builder.toast.placeholderTitle'),
97
+ description: what,
98
+ icon: 'i-lucide-construction',
99
+ })
91
100
  }
92
101
 
93
102
  async function save() {
@@ -96,19 +105,21 @@ async function save() {
96
105
  const saved = await pipelines.saveDraft()
97
106
  if (saved) {
98
107
  toast.add({
99
- title: wasEditing ? `Updated “${saved.name}”` : `Saved “${saved.name}”`,
108
+ title: wasEditing
109
+ ? t('pipeline.builder.toast.updated', { name: saved.name })
110
+ : t('pipeline.builder.toast.saved', { name: saved.name }),
100
111
  color: 'success',
101
112
  icon: 'i-lucide-check',
102
113
  })
103
114
  ui.builderOpen = false
104
115
  } else {
105
- toast.add({ title: 'Add at least one agent first', color: 'warning' })
116
+ toast.add({ title: t('pipeline.builder.toast.addOneFirst'), color: 'warning' })
106
117
  }
107
118
  } catch (e) {
108
119
  // Surface the backend reason (e.g. post-release-health rejected without an
109
120
  // observability integration) rather than a generic failure.
110
121
  toast.add({
111
- title: 'Could not save pipeline',
122
+ title: t('pipeline.builder.toast.saveFailed'),
112
123
  description: e instanceof Error ? e.message : undefined,
113
124
  color: 'error',
114
125
  })
@@ -178,7 +189,7 @@ async function toggleArchive(p: Pipeline) {
178
189
  if (p.archived) await pipelines.unarchive(p.id)
179
190
  else await pipelines.archive(p.id)
180
191
  } catch {
181
- toast.add({ title: 'Could not update pipeline', color: 'error' })
192
+ toast.add({ title: t('pipeline.builder.toast.updateFailed'), color: 'error' })
182
193
  }
183
194
  }
184
195
 
@@ -192,12 +203,12 @@ async function clone(p: Pipeline) {
192
203
  try {
193
204
  const copy = await pipelines.clonePipeline(p.id)
194
205
  toast.add({
195
- title: `Cloned “${p.name}” now editing “${copy.name}”`,
206
+ title: t('pipeline.builder.toast.cloned', { name: p.name, copy: copy.name }),
196
207
  color: 'success',
197
208
  icon: 'i-lucide-copy',
198
209
  })
199
210
  } catch {
200
- toast.add({ title: 'Could not clone pipeline', color: 'error' })
211
+ toast.add({ title: t('pipeline.builder.toast.cloneFailed'), color: 'error' })
201
212
  }
202
213
  }
203
214
  </script>
@@ -205,7 +216,7 @@ async function clone(p: Pipeline) {
205
216
  <template>
206
217
  <USlideover
207
218
  v-model:open="open"
208
- title="Pipeline builder"
219
+ :title="t('pipeline.builder.title')"
209
220
  side="left"
210
221
  :ui="{ content: 'max-w-[90vw] sm:max-w-2xl lg:max-w-5xl xl:max-w-6xl' }"
211
222
  >
@@ -218,7 +229,7 @@ async function clone(p: Pipeline) {
218
229
  <div class="flex flex-col lg:min-h-0 lg:overflow-hidden">
219
230
  <div class="mb-2 flex shrink-0 items-center justify-between gap-2">
220
231
  <h3 class="text-xs font-semibold uppercase tracking-wide text-slate-400">
221
- Agent palette
232
+ {{ t('pipeline.builder.agentPalette') }}
222
233
  </h3>
223
234
  <UButton
224
235
  color="primary"
@@ -227,7 +238,7 @@ async function clone(p: Pipeline) {
227
238
  icon="i-lucide-plus"
228
239
  @click="openAddAgent"
229
240
  >
230
- Add agent
241
+ {{ t('pipeline.builder.addAgent') }}
231
242
  </UButton>
232
243
  </div>
233
244
  <div class="flex-1 pr-1 lg:min-h-0 lg:overflow-y-auto">
@@ -238,21 +249,23 @@ async function clone(p: Pipeline) {
238
249
  <!-- draft chain -->
239
250
  <div class="flex flex-col lg:min-h-0 lg:overflow-hidden">
240
251
  <div class="mb-2 flex items-center justify-between gap-2">
241
- <h3 class="text-xs font-semibold uppercase tracking-wide text-slate-400">Pipeline</h3>
252
+ <h3 class="text-xs font-semibold uppercase tracking-wide text-slate-400">
253
+ {{ t('pipeline.builder.pipeline') }}
254
+ </h3>
242
255
  <UButton
243
256
  color="neutral"
244
257
  variant="soft"
245
258
  size="xs"
246
259
  icon="i-lucide-cpu"
247
- title="Manage model presets (which model each agent runs on)"
260
+ :title="t('pipeline.builder.configureModelsTooltip')"
248
261
  @click="ui.openModelConfig()"
249
262
  >
250
- Configure models
263
+ {{ t('pipeline.builder.configureModels') }}
251
264
  </UButton>
252
265
  </div>
253
266
  <UInput
254
267
  v-model="pipelines.draftName"
255
- placeholder="Pipeline name"
268
+ :placeholder="t('pipeline.builder.namePlaceholder')"
256
269
  size="sm"
257
270
  class="mb-2"
258
271
  />
@@ -274,7 +287,7 @@ async function clone(p: Pipeline) {
274
287
  </UBadge>
275
288
  <input
276
289
  v-model="newLabel"
277
- placeholder="+ label"
290
+ :placeholder="t('pipeline.builder.labelPlaceholder')"
278
291
  class="w-20 rounded border border-slate-700 bg-slate-900 px-1.5 py-0.5 text-[11px] text-slate-200 focus:w-28"
279
292
  @keydown.enter.prevent="addLabel"
280
293
  @blur="addLabel"
@@ -286,14 +299,14 @@ async function clone(p: Pipeline) {
286
299
  class="mb-2 flex items-center gap-1.5 rounded-md border border-amber-800/50 bg-amber-950/30 px-2 py-1 text-[11px] text-amber-300"
287
300
  >
288
301
  <UIcon name="i-lucide-alert-triangle" class="h-3.5 w-3.5 shrink-0" />
289
- A gated step needs a Task Estimator before it — add one or the pipeline won't save.
302
+ {{ t('pipeline.builder.gatingNeedsEstimator') }}
290
303
  </p>
291
304
 
292
305
  <div
293
306
  v-if="pipelines.draft.length === 0"
294
307
  class="flex flex-1 items-center justify-center rounded-lg border border-dashed border-slate-700 p-4 text-center text-xs text-slate-500"
295
308
  >
296
- Click agents on the left to assemble a linear pipeline.
309
+ {{ t('pipeline.builder.emptyDraft') }}
297
310
  </div>
298
311
 
299
312
  <ol v-else class="flex-1 space-y-2 pr-1 lg:min-h-0 lg:overflow-y-auto">
@@ -327,8 +340,12 @@ async function clone(p: Pipeline) {
327
340
  size="xs"
328
341
  :title="
329
342
  unit.companionIndex !== null
330
- ? `Remove the ${companionLabel(unit.kind)} for this step`
331
- : `Add the ${companionLabel(unit.kind)} (reviews this step, loops it back below threshold)`
343
+ ? t('pipeline.builder.companionRemove', {
344
+ companion: companionLabel(unit.kind),
345
+ })
346
+ : t('pipeline.builder.companionAdd', {
347
+ companion: companionLabel(unit.kind),
348
+ })
332
349
  "
333
350
  @click="pipelines.toggleCompanion(unit.index)"
334
351
  />
@@ -344,8 +361,8 @@ async function clone(p: Pipeline) {
344
361
  size="xs"
345
362
  :title="
346
363
  pipelines.draftEnabled[unit.index] === false
347
- ? 'Step disabled (skipped at run) — click to enable'
348
- : 'Disable this step (kept in the pipeline but skipped at run)'
364
+ ? t('pipeline.builder.enableTooltip')
365
+ : t('pipeline.builder.disableTooltip')
349
366
  "
350
367
  @click="toggleEnabled(unit)"
351
368
  />
@@ -360,8 +377,8 @@ async function clone(p: Pipeline) {
360
377
  size="xs"
361
378
  :title="
362
379
  pipelines.draftGates[unit.index]
363
- ? 'Approval required after this step — click to remove the gate'
364
- : 'Require human approval after this step'
380
+ ? t('pipeline.builder.approvalRemoveTooltip')
381
+ : t('pipeline.builder.approvalAddTooltip')
365
382
  "
366
383
  @click="pipelines.toggleDraftGate(unit.index)"
367
384
  />
@@ -379,8 +396,8 @@ async function clone(p: Pipeline) {
379
396
  size="xs"
380
397
  :title="
381
398
  pipelines.draftConsensus[unit.index]?.enabled
382
- ? 'Consensus enabled — click to revert to a single agent'
383
- : 'Enable consensus (multi-model panel/debate/voting) for this step'
399
+ ? t('pipeline.builder.consensusRevertTooltip')
400
+ : t('pipeline.builder.consensusEnableTooltip')
384
401
  "
385
402
  @click="pipelines.toggleDraftConsensus(unit.index)"
386
403
  />
@@ -400,8 +417,8 @@ async function clone(p: Pipeline) {
400
417
  size="xs"
401
418
  :title="
402
419
  pipelines.draftFollowUps[unit.index] === false
403
- ? 'Follow-up companion disabled — click to enable (Coder surfaces loose ends / questions)'
404
- : 'Follow-up companion enabled — Coder surfaces loose ends / side-tasks / questions; click to disable'
420
+ ? t('pipeline.builder.followUpEnableTooltip')
421
+ : t('pipeline.builder.followUpDisableTooltip')
405
422
  "
406
423
  @click="pipelines.toggleDraftFollowUps(unit.index)"
407
424
  />
@@ -410,6 +427,7 @@ async function clone(p: Pipeline) {
410
427
  color="neutral"
411
428
  variant="ghost"
412
429
  size="xs"
430
+ :title="t('pipeline.builder.moveUp')"
413
431
  :disabled="vi === 0"
414
432
  @click="pipelines.moveUnit(vi, vi - 1)"
415
433
  />
@@ -418,6 +436,7 @@ async function clone(p: Pipeline) {
418
436
  color="neutral"
419
437
  variant="ghost"
420
438
  size="xs"
439
+ :title="t('pipeline.builder.moveDown')"
421
440
  :disabled="vi === pipelines.units.length - 1"
422
441
  @click="pipelines.moveUnit(vi, vi + 1)"
423
442
  />
@@ -426,7 +445,7 @@ async function clone(p: Pipeline) {
426
445
  color="error"
427
446
  variant="ghost"
428
447
  size="xs"
429
- title="Remove this step from the pipeline"
448
+ :title="t('pipeline.builder.removeStep')"
430
449
  @click="removeUnit(unit)"
431
450
  />
432
451
  </div>
@@ -458,8 +477,8 @@ async function clone(p: Pipeline) {
458
477
  "
459
478
  variant="ghost"
460
479
  size="xs"
461
- label="Gate on estimate"
462
- title="Only run this companion when the task estimate clears a threshold (needs a Task Estimator earlier)"
480
+ :label="t('pipeline.builder.gateOnEstimate')"
481
+ :title="t('pipeline.builder.companionGateTooltip')"
463
482
  @click="pipelines.toggleDraftGating(unit.companionIndex)"
464
483
  />
465
484
  </div>
@@ -467,8 +486,12 @@ async function clone(p: Pipeline) {
467
486
  v-if="pipelines.draftGating[unit.companionIndex]?.enabled"
468
487
  class="flex flex-wrap items-center gap-2 border-t border-slate-800 pt-2"
469
488
  >
470
- <span class="text-[10px] text-slate-500">run when (any):</span>
471
- <label class="text-slate-400">complexity ≥</label>
489
+ <span class="text-[10px] text-slate-500">{{
490
+ t('pipeline.builder.runWhenAny')
491
+ }}</span>
492
+ <label class="text-slate-400">{{
493
+ t('pipeline.builder.complexityThreshold')
494
+ }}</label>
472
495
  <input
473
496
  v-model.number="pipelines.draftGating[unit.companionIndex]!.minComplexity"
474
497
  type="number"
@@ -477,7 +500,7 @@ async function clone(p: Pipeline) {
477
500
  step="0.1"
478
501
  class="w-14 rounded border border-slate-700 bg-slate-900 px-1.5 py-0.5 text-slate-100"
479
502
  />
480
- <label class="text-slate-400">risk ≥</label>
503
+ <label class="text-slate-400">{{ t('pipeline.builder.riskThreshold') }}</label>
481
504
  <input
482
505
  v-model.number="pipelines.draftGating[unit.companionIndex]!.minRisk"
483
506
  type="number"
@@ -486,7 +509,7 @@ async function clone(p: Pipeline) {
486
509
  step="0.1"
487
510
  class="w-14 rounded border border-slate-700 bg-slate-900 px-1.5 py-0.5 text-slate-100"
488
511
  />
489
- <label class="text-slate-400">impact ≥</label>
512
+ <label class="text-slate-400">{{ t('pipeline.builder.impactThreshold') }}</label>
490
513
  <input
491
514
  v-model.number="pipelines.draftGating[unit.companionIndex]!.minImpact"
492
515
  type="number"
@@ -504,7 +527,7 @@ async function clone(p: Pipeline) {
504
527
  class="ml-6 space-y-2 rounded-md border border-emerald-800/40 bg-emerald-950/20 p-2 text-xs"
505
528
  >
506
529
  <div class="flex items-center gap-2">
507
- <label class="text-slate-400">Strategy</label>
530
+ <label class="text-slate-400">{{ t('pipeline.builder.strategy') }}</label>
508
531
  <select
509
532
  v-model="pipelines.draftConsensus[unit.index]!.strategy"
510
533
  class="rounded border border-slate-700 bg-slate-900 px-1.5 py-0.5 text-slate-100"
@@ -516,7 +539,7 @@ async function clone(p: Pipeline) {
516
539
  <label
517
540
  v-if="pipelines.draftConsensus[unit.index]!.strategy === 'debate'"
518
541
  class="ml-2 text-slate-400"
519
- >Rounds</label
542
+ >{{ t('pipeline.builder.rounds') }}</label
520
543
  >
521
544
  <input
522
545
  v-if="pipelines.draftConsensus[unit.index]!.strategy === 'debate'"
@@ -538,12 +561,12 @@ async function clone(p: Pipeline) {
538
561
  >
539
562
  <input
540
563
  v-model="p.role"
541
- placeholder="Role"
564
+ :placeholder="t('pipeline.builder.rolePlaceholder')"
542
565
  class="w-28 rounded border border-slate-700 bg-slate-900 px-1.5 py-0.5 text-slate-100"
543
566
  />
544
567
  <input
545
568
  v-model="p.modelId"
546
- placeholder="Model id (optional)"
569
+ :placeholder="t('pipeline.builder.modelIdPlaceholder')"
547
570
  class="flex-1 rounded border border-slate-700 bg-slate-900 px-1.5 py-0.5 text-slate-300"
548
571
  />
549
572
  <UButton
@@ -552,7 +575,7 @@ async function clone(p: Pipeline) {
552
575
  variant="ghost"
553
576
  size="xs"
554
577
  :disabled="pipelines.draftConsensus[unit.index]!.participants.length <= 2"
555
- title="Remove participant (min 2)"
578
+ :title="t('pipeline.builder.removeParticipant')"
556
579
  @click="removeParticipant(unit.index, pIdx)"
557
580
  />
558
581
  </div>
@@ -561,7 +584,7 @@ async function clone(p: Pipeline) {
561
584
  color="neutral"
562
585
  variant="ghost"
563
586
  size="xs"
564
- label="Add participant"
587
+ :label="t('pipeline.builder.addParticipant')"
565
588
  @click="addParticipant(unit.index)"
566
589
  />
567
590
  </div>
@@ -579,12 +602,12 @@ async function clone(p: Pipeline) {
579
602
  "
580
603
  variant="ghost"
581
604
  size="xs"
582
- label="Gate on estimate"
583
- title="Only run consensus when the task estimate clears a threshold (else the standard agent runs)"
605
+ :label="t('pipeline.builder.gateOnEstimate')"
606
+ :title="t('pipeline.builder.consensusGateTooltip')"
584
607
  @click="toggleGating(unit.index)"
585
608
  />
586
609
  <template v-if="pipelines.draftConsensus[unit.index]!.gating?.enabled">
587
- <label class="text-slate-400">risk ≥</label>
610
+ <label class="text-slate-400">{{ t('pipeline.builder.riskThreshold') }}</label>
588
611
  <input
589
612
  v-model.number="pipelines.draftConsensus[unit.index]!.gating!.minRisk"
590
613
  type="number"
@@ -593,7 +616,9 @@ async function clone(p: Pipeline) {
593
616
  step="0.1"
594
617
  class="w-14 rounded border border-slate-700 bg-slate-900 px-1.5 py-0.5 text-slate-100"
595
618
  />
596
- <label class="text-slate-400">impact ≥</label>
619
+ <label class="text-slate-400">{{
620
+ t('pipeline.builder.impactThreshold')
621
+ }}</label>
597
622
  <input
598
623
  v-model.number="pipelines.draftConsensus[unit.index]!.gating!.minImpact"
599
624
  type="number"
@@ -614,7 +639,7 @@ async function clone(p: Pipeline) {
614
639
  <div v-if="pipelines.pipelines.length" class="flex flex-col lg:min-h-0 lg:overflow-hidden">
615
640
  <div class="mb-2 flex shrink-0 items-center justify-between gap-2">
616
641
  <h3 class="text-xs font-semibold uppercase tracking-wide text-slate-400">
617
- Saved pipelines
642
+ {{ t('pipeline.builder.savedPipelines') }}
618
643
  </h3>
619
644
  <UButton
620
645
  v-if="archivedCount"
@@ -624,7 +649,11 @@ async function clone(p: Pipeline) {
624
649
  size="xs"
625
650
  @click="showArchived = !showArchived"
626
651
  >
627
- {{ showArchived ? 'Hide archived' : `Archived (${archivedCount})` }}
652
+ {{
653
+ showArchived
654
+ ? t('pipeline.builder.hideArchived')
655
+ : t('pipeline.builder.archivedCount', { count: archivedCount })
656
+ }}
628
657
  </UButton>
629
658
  </div>
630
659
 
@@ -637,7 +666,7 @@ async function clone(p: Pipeline) {
637
666
  class="cursor-pointer"
638
667
  @click="labelFilter = null"
639
668
  >
640
- All
669
+ {{ t('pipeline.builder.allLabels') }}
641
670
  </UBadge>
642
671
  <UBadge
643
672
  v-for="l in allLabels"
@@ -689,10 +718,16 @@ async function clone(p: Pipeline) {
689
718
  size="xs"
690
719
  class="shrink-0"
691
720
  >
692
- default
721
+ {{ t('pipeline.builder.defaultBadge') }}
693
722
  </UBadge>
694
723
  <span class="shrink-0 text-[10px] text-slate-500">
695
- {{ p.agentKinds.length }} {{ p.agentKinds.length === 1 ? 'step' : 'steps' }}
724
+ {{
725
+ t(
726
+ 'pipeline.builder.stepCount',
727
+ { count: p.agentKinds.length },
728
+ p.agentKinds.length,
729
+ )
730
+ }}
696
731
  </span>
697
732
  </button>
698
733
  <div
@@ -705,7 +740,9 @@ async function clone(p: Pipeline) {
705
740
  color="neutral"
706
741
  variant="ghost"
707
742
  size="xs"
708
- :title="p.archived ? 'Unarchive' : 'Archive (hide from the default view)'"
743
+ :title="
744
+ p.archived ? t('pipeline.builder.unarchive') : t('pipeline.builder.archive')
745
+ "
709
746
  @click="toggleArchive(p)"
710
747
  />
711
748
  <!-- Clone is available on every pipeline — it's how a read-only
@@ -715,7 +752,9 @@ async function clone(p: Pipeline) {
715
752
  color="neutral"
716
753
  variant="ghost"
717
754
  size="xs"
718
- :title="p.builtin ? 'Clone this default into an editable copy' : 'Clone'"
755
+ :title="
756
+ p.builtin ? t('pipeline.builder.cloneDefault') : t('pipeline.builder.clone')
757
+ "
719
758
  @click="clone(p)"
720
759
  />
721
760
  <!-- Built-in templates are read-only; only custom pipelines edit in place. -->
@@ -725,7 +764,7 @@ async function clone(p: Pipeline) {
725
764
  color="neutral"
726
765
  variant="ghost"
727
766
  size="xs"
728
- title="Edit this pipeline"
767
+ :title="t('pipeline.builder.edit')"
729
768
  @click="edit(p)"
730
769
  />
731
770
  <!-- Built-in templates are read-only — they can be cloned but not
@@ -736,6 +775,7 @@ async function clone(p: Pipeline) {
736
775
  color="neutral"
737
776
  variant="ghost"
738
777
  size="xs"
778
+ :title="t('pipeline.builder.delete')"
739
779
  @click="pipelines.removePipeline(p.id)"
740
780
  />
741
781
  </div>
@@ -751,7 +791,9 @@ async function clone(p: Pipeline) {
751
791
  :key="i"
752
792
  class="flex items-center gap-2"
753
793
  :class="{ 'opacity-50 line-through': p.enabled?.[i] === false }"
754
- :title="p.enabled?.[i] === false ? 'Disabled — skipped at run' : undefined"
794
+ :title="
795
+ p.enabled?.[i] === false ? t('pipeline.builder.disabledStepTooltip') : undefined
796
+ "
755
797
  >
756
798
  <span class="w-4 shrink-0 text-center text-[10px] text-slate-500">{{
757
799
  i + 1
@@ -768,7 +810,7 @@ async function clone(p: Pipeline) {
768
810
  <template #footer>
769
811
  <div class="flex w-full items-center justify-between">
770
812
  <UButton color="neutral" variant="ghost" size="sm" @click="pipelines.clearDraft()">
771
- {{ pipelines.editingId ? 'Cancel edit' : 'Clear' }}
813
+ {{ pipelines.editingId ? t('pipeline.builder.cancelEdit') : t('pipeline.builder.clear') }}
772
814
  </UButton>
773
815
  <UButton
774
816
  color="primary"
@@ -777,25 +819,25 @@ async function clone(p: Pipeline) {
777
819
  :disabled="pipelines.draft.length === 0"
778
820
  @click="save"
779
821
  >
780
- {{ pipelines.editingId ? 'Update pipeline' : 'Save pipeline' }}
822
+ {{ pipelines.editingId ? t('pipeline.builder.update') : t('pipeline.builder.save') }}
781
823
  </UButton>
782
824
  </div>
783
825
  </template>
784
826
  </USlideover>
785
827
 
786
828
  <!-- Add-agent form -->
787
- <UModal v-model:open="addAgentOpen" title="Add agent">
829
+ <UModal v-model:open="addAgentOpen" :title="t('pipeline.builder.addAgentModal.title')">
788
830
  <template #body>
789
831
  <div class="space-y-3">
790
832
  <div>
791
833
  <label
792
834
  class="mb-1 block text-[11px] font-semibold uppercase tracking-wide text-slate-400"
793
835
  >
794
- Name
836
+ {{ t('pipeline.builder.addAgentModal.name') }}
795
837
  </label>
796
838
  <UInput
797
839
  v-model="newAgentName"
798
- placeholder="e.g. Security Auditor"
840
+ :placeholder="t('pipeline.builder.addAgentModal.namePlaceholder')"
799
841
  size="sm"
800
842
  class="w-full"
801
843
  />
@@ -804,7 +846,7 @@ async function clone(p: Pipeline) {
804
846
  <label
805
847
  class="mb-1 block text-[11px] font-semibold uppercase tracking-wide text-slate-400"
806
848
  >
807
- Description
849
+ {{ t('pipeline.builder.addAgentModal.description') }}
808
850
  </label>
809
851
  <UTextarea
810
852
  v-model="newAgentDesc"
@@ -812,7 +854,7 @@ async function clone(p: Pipeline) {
812
854
  autoresize
813
855
  size="sm"
814
856
  class="w-full"
815
- placeholder="What does this agent do?"
857
+ :placeholder="t('pipeline.builder.addAgentModal.descriptionPlaceholder')"
816
858
  />
817
859
  </div>
818
860
  <UButton
@@ -821,9 +863,9 @@ async function clone(p: Pipeline) {
821
863
  size="xs"
822
864
  icon="i-lucide-file-text"
823
865
  block
824
- @click="placeholder('Link context document')"
866
+ @click="placeholder(t('pipeline.builder.addAgentModal.linkDoc'))"
825
867
  >
826
- Link context document
868
+ {{ t('pipeline.builder.addAgentModal.linkDoc') }}
827
869
  </UButton>
828
870
  </div>
829
871
  </template>
@@ -831,7 +873,7 @@ async function clone(p: Pipeline) {
831
873
  <template #footer>
832
874
  <div class="flex w-full items-center justify-end gap-2">
833
875
  <UButton color="neutral" variant="ghost" size="sm" @click="addAgentOpen = false">
834
- Cancel
876
+ {{ t('common.cancel') }}
835
877
  </UButton>
836
878
  <UButton
837
879
  color="primary"
@@ -840,7 +882,7 @@ async function clone(p: Pipeline) {
840
882
  :disabled="!newAgentName.trim()"
841
883
  @click="createAgent"
842
884
  >
843
- Create agent
885
+ {{ t('pipeline.builder.addAgentModal.create') }}
844
886
  </UButton>
845
887
  </div>
846
888
  </template>