@cat-factory/app 0.107.6 → 0.108.0

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.
@@ -201,6 +201,7 @@ function selectTask() {
201
201
  v-if="task && statusMeta"
202
202
  :data-block-id="task.id"
203
203
  :data-status="task.status"
204
+ :data-task-type="task.taskType"
204
205
  data-testid="task-card"
205
206
  class="nodrag w-full cursor-pointer rounded-lg border bg-slate-950/70 p-2 text-start transition"
206
207
  :class="[
@@ -0,0 +1,180 @@
1
+ <script setup lang="ts">
2
+ // Startup advisory for built-in model presets that drifted from the catalog. Opened once per
3
+ // session from the board page when `useModelPresetHealth` reports any issue. Lists:
4
+ // • new built-in presets the workspace doesn't have yet (ADD them);
5
+ // • built-ins with a newer catalog version available (RESEED to adopt it).
6
+ // Both fixes are the same reseed call (it creates or updates by catalog id). Detection is
7
+ // client-side (see useModelPresetHealth); the actions hit the modelPresets store.
8
+ const { t } = useI18n()
9
+ const ui = useUiStore()
10
+ const presets = useModelPresetsStore()
11
+ const { newPresets, outdated, hasIssues } = useModelPresetHealth()
12
+ const toast = useToast()
13
+
14
+ const open = computed({
15
+ get: () => ui.modelPresetHealthOpen,
16
+ set: (v: boolean) => {
17
+ if (!v) ui.closeModelPresetHealth()
18
+ },
19
+ })
20
+
21
+ // Per-preset in-flight ids, so each row's button shows its own spinner.
22
+ const busy = ref<Set<string>>(new Set())
23
+ const isBusy = (id: string) => busy.value.has(id)
24
+ const anyBusy = computed(() => busy.value.size > 0)
25
+
26
+ async function reseed(id: string) {
27
+ busy.value = new Set(busy.value).add(id)
28
+ try {
29
+ await presets.reseed(id)
30
+ } catch (e) {
31
+ toast.add({
32
+ title: t('modelPreset.health.toast.reseedFailed'),
33
+ description: e instanceof Error ? e.message : String(e),
34
+ icon: 'i-lucide-triangle-alert',
35
+ color: 'error',
36
+ })
37
+ } finally {
38
+ const next = new Set(busy.value)
39
+ next.delete(id)
40
+ busy.value = next
41
+ }
42
+ }
43
+
44
+ /** Reseed every advised preset (new + outdated built-ins) in one go, refreshing the board once. */
45
+ async function reseedAll() {
46
+ const ids = [...new Set([...newPresets.value, ...outdated.value].map((i) => i.id))]
47
+ busy.value = new Set([...busy.value, ...ids])
48
+ try {
49
+ await presets.reseedMany(ids)
50
+ } catch (e) {
51
+ toast.add({
52
+ title: t('modelPreset.health.toast.reseedFailed'),
53
+ description: e instanceof Error ? e.message : String(e),
54
+ icon: 'i-lucide-triangle-alert',
55
+ color: 'error',
56
+ })
57
+ } finally {
58
+ const next = new Set(busy.value)
59
+ for (const id of ids) next.delete(id)
60
+ busy.value = next
61
+ }
62
+ }
63
+
64
+ const reseedableCount = computed(
65
+ () => new Set([...newPresets.value, ...outdated.value].map((i) => i.id)).size,
66
+ )
67
+ </script>
68
+
69
+ <template>
70
+ <UModal v-model:open="open" :title="t('modelPreset.health.title')" :ui="{ content: 'max-w-2xl' }">
71
+ <template #body>
72
+ <div v-if="!hasIssues" class="py-6 text-center text-sm text-slate-400">
73
+ <UIcon name="i-lucide-check-circle-2" class="mx-auto mb-2 h-8 w-8 text-emerald-400" />
74
+ {{ t('modelPreset.health.allValid') }}
75
+ </div>
76
+
77
+ <div v-else class="space-y-5">
78
+ <!-- New built-in presets the workspace can add. -->
79
+ <section v-if="newPresets.length" class="space-y-2">
80
+ <div class="flex items-center gap-2">
81
+ <UIcon name="i-lucide-sparkles" class="h-4 w-4 text-emerald-400" />
82
+ <h3 class="text-sm font-semibold text-slate-200">
83
+ {{ t('modelPreset.health.newHeading') }}
84
+ </h3>
85
+ </div>
86
+ <p class="text-[11px] text-slate-500">{{ t('modelPreset.health.newDescription') }}</p>
87
+ <ul class="space-y-2">
88
+ <li
89
+ v-for="i in newPresets"
90
+ :key="i.id"
91
+ class="flex items-center justify-between gap-3 rounded-lg border border-slate-800 bg-slate-900/40 p-3"
92
+ >
93
+ <div class="min-w-0">
94
+ <span class="truncate text-sm font-medium text-slate-100 capitalize">{{
95
+ i.name
96
+ }}</span>
97
+ </div>
98
+ <UButton
99
+ size="xs"
100
+ color="primary"
101
+ variant="subtle"
102
+ icon="i-lucide-plus"
103
+ :loading="isBusy(i.id)"
104
+ :disabled="anyBusy"
105
+ @click="reseed(i.id)"
106
+ >
107
+ {{ t('modelPreset.health.add') }}
108
+ </UButton>
109
+ </li>
110
+ </ul>
111
+ </section>
112
+
113
+ <!-- Outdated built-ins: a newer catalog version is available. -->
114
+ <section v-if="outdated.length" class="space-y-2">
115
+ <div class="flex items-center gap-2">
116
+ <UIcon name="i-lucide-arrow-up-circle" class="h-4 w-4 text-amber-400" />
117
+ <h3 class="text-sm font-semibold text-slate-200">
118
+ {{ t('modelPreset.health.updatesHeading') }}
119
+ </h3>
120
+ </div>
121
+ <p class="text-[11px] text-slate-500">{{ t('modelPreset.health.updatesDescription') }}</p>
122
+ <ul class="space-y-2">
123
+ <li
124
+ v-for="i in outdated"
125
+ :key="i.id"
126
+ class="flex items-center justify-between gap-3 rounded-lg border border-slate-800 bg-slate-900/40 p-3"
127
+ >
128
+ <div class="min-w-0">
129
+ <span class="truncate text-sm font-medium text-slate-100">{{ i.name }}</span>
130
+ <p class="text-[11px] text-amber-400/80">
131
+ {{
132
+ t('modelPreset.health.versionAvailable', {
133
+ from: i.fromVersion ?? 0,
134
+ to: i.toVersion ?? 0,
135
+ })
136
+ }}
137
+ </p>
138
+ </div>
139
+ <UButton
140
+ size="xs"
141
+ color="primary"
142
+ variant="subtle"
143
+ icon="i-lucide-rotate-ccw"
144
+ :loading="isBusy(i.id)"
145
+ :disabled="anyBusy"
146
+ @click="reseed(i.id)"
147
+ >
148
+ {{ t('modelPreset.health.reseed') }}
149
+ </UButton>
150
+ </li>
151
+ </ul>
152
+ </section>
153
+ </div>
154
+ </template>
155
+
156
+ <template #footer>
157
+ <div class="flex w-full items-center justify-between gap-2">
158
+ <UButton
159
+ v-if="reseedableCount > 1"
160
+ color="primary"
161
+ variant="ghost"
162
+ icon="i-lucide-rotate-ccw"
163
+ :loading="anyBusy"
164
+ @click="reseedAll"
165
+ >
166
+ {{ t('modelPreset.health.reseedAll', { count: reseedableCount }) }}
167
+ </UButton>
168
+ <span v-else />
169
+ <UButton
170
+ color="neutral"
171
+ variant="ghost"
172
+ :disabled="anyBusy"
173
+ @click="ui.closeModelPresetHealth()"
174
+ >
175
+ {{ hasIssues ? t('modelPreset.health.dismiss') : t('modelPreset.health.done') }}
176
+ </UButton>
177
+ </div>
178
+ </template>
179
+ </UModal>
180
+ </template>
@@ -6,6 +6,7 @@ import {
6
6
  listMergePresetsContract,
7
7
  listModelPresetsContract,
8
8
  reseedMergePresetContract,
9
+ reseedModelPresetContract,
9
10
  updateMergePresetContract,
10
11
  updateModelPresetContract,
11
12
  } from '@cat-factory/contracts'
@@ -60,5 +61,10 @@ export function presetsApi({ send, ws }: ApiContext) {
60
61
 
61
62
  deleteModelPreset: (workspaceId: string, presetId: string) =>
62
63
  send(deleteModelPresetContract, { pathPrefix: ws(workspaceId), pathParams: { presetId } }),
64
+
65
+ // Restore a built-in model preset to its current catalog definition (adopt an update, repair
66
+ // a drifted one, or materialise a new built-in that appeared). Custom presets reject this.
67
+ reseedModelPreset: (workspaceId: string, presetId: string) =>
68
+ send(reseedModelPresetContract, { pathPrefix: ws(workspaceId), pathParams: { presetId } }),
63
69
  }
64
70
  }
@@ -0,0 +1,65 @@
1
+ import { computed } from 'vue'
2
+ import type { ModelPreset } from '~/types/model-presets'
3
+ import { useModelPresetsStore } from '~/stores/modelPresets'
4
+
5
+ export type ModelPresetIssueType = 'outdated' | 'new'
6
+
7
+ /** A built-in model preset that the workspace should reseed (an update, or a new one to add). */
8
+ export interface ModelPresetIssue {
9
+ type: ModelPresetIssueType
10
+ /** The catalog (built-in) id — what the reseed endpoint is keyed by. */
11
+ id: string
12
+ /** The preset name (the stored copy's for `outdated`, the built-in id for a `new` one). */
13
+ name: string
14
+ /** For an `outdated` issue: the persisted copy's version (the display copy renders it via i18n). */
15
+ fromVersion?: number
16
+ /** For an `outdated` issue: the newer catalog version available. */
17
+ toVersion?: number
18
+ }
19
+
20
+ /** A built-in's display name for an issue message (humanise its catalog id as a fallback). */
21
+ function builtinName(id: string, stored: ModelPreset | undefined): string {
22
+ if (stored) return stored.name
23
+ // `mdp_claude` -> "claude" — only used until the row is reseeded into existence.
24
+ return id.replace(/^mdp_/, '').replace(/_/g, ' ')
25
+ }
26
+
27
+ /**
28
+ * Detect built-in model presets the workspace should reseed for the startup advisory: a stored
29
+ * built-in whose catalog definition moved ahead (offer to adopt it) and a brand-new built-in
30
+ * that appeared in the catalog but isn't in the workspace yet (offer to add it). The catalog
31
+ * versions the snapshot ships ARE the set of built-in ids, so detection is entirely client-side:
32
+ * a stored preset is a built-in iff its id is a catalog key, and a catalog key with no stored
33
+ * preset is a new built-in.
34
+ */
35
+ export function useModelPresetHealth() {
36
+ const store = useModelPresetsStore()
37
+
38
+ const issues = computed<ModelPresetIssue[]>(() => {
39
+ const out: ModelPresetIssue[] = []
40
+ const byId = new Map(store.presets.map((p) => [p.id, p]))
41
+ for (const [id, catalogVersion] of Object.entries(store.catalogVersions)) {
42
+ const stored = byId.get(id)
43
+ if (!stored) {
44
+ out.push({ type: 'new', id, name: builtinName(id, undefined) })
45
+ continue
46
+ }
47
+ if (catalogVersion > (stored.version ?? 0)) {
48
+ out.push({
49
+ type: 'outdated',
50
+ id,
51
+ name: stored.name,
52
+ fromVersion: stored.version ?? 0,
53
+ toVersion: catalogVersion,
54
+ })
55
+ }
56
+ }
57
+ return out
58
+ })
59
+
60
+ const hasIssues = computed(() => issues.value.length > 0)
61
+ const newPresets = computed(() => issues.value.filter((i) => i.type === 'new'))
62
+ const outdated = computed(() => issues.value.filter((i) => i.type === 'outdated'))
63
+
64
+ return { issues, hasIssues, newPresets, outdated }
65
+ }
@@ -74,6 +74,10 @@ const PipelineHealthModal = defineAsyncComponent(
74
74
  const MergePresetHealthModal = defineAsyncComponent(
75
75
  () => import('~/components/settings/MergePresetHealthModal.vue'),
76
76
  )
77
+ // Startup advisory for new / outdated built-in model presets — same once-per-session pattern.
78
+ const ModelPresetHealthModal = defineAsyncComponent(
79
+ () => import('~/components/settings/ModelPresetHealthModal.vue'),
80
+ )
77
81
  const IntegrationsHub = defineAsyncComponent(
78
82
  () => import('~/components/layout/IntegrationsHub.vue'),
79
83
  )
@@ -192,6 +196,24 @@ watch(
192
196
  { immediate: true },
193
197
  )
194
198
 
199
+ // Same advisory for built-in model presets: surface new / outdated ones once per session. Defers
200
+ // to the pipeline + merge-preset advisories when they fire, so at most one modal auto-opens.
201
+ const { hasIssues: modelPresetIssues } = useModelPresetHealth()
202
+ watch(
203
+ () => [workspace.ready, modelPresetIssues.value, ui.pipelineHealthOpen, ui.mergePresetHealthOpen],
204
+ () => {
205
+ if (
206
+ workspace.ready &&
207
+ modelPresetIssues.value &&
208
+ !ui.pipelineHealthOpen &&
209
+ !ui.mergePresetHealthOpen
210
+ ) {
211
+ ui.maybeOpenModelPresetHealth()
212
+ }
213
+ },
214
+ { immediate: true },
215
+ )
216
+
195
217
  // Auto-open the right AI-onboarding dialog once per session: the no-source prompt takes
196
218
  // precedence over the preset-mismatch prompt. Honour the per-session dismissed flags so a
197
219
  // user who closed the banner isn't re-interrupted, and only auto-open once each (later opens
@@ -369,6 +391,7 @@ watch(
369
391
  <FragmentLibraryPanel v-if="ui.fragmentLibraryOpen" />
370
392
  <PipelineHealthModal v-if="ui.pipelineHealthOpen" />
371
393
  <MergePresetHealthModal v-if="ui.mergePresetHealthOpen" />
394
+ <ModelPresetHealthModal v-if="ui.modelPresetHealthOpen" />
372
395
  <IntegrationsHub v-if="ui.integrationsOpen" />
373
396
  <PersonalSetupModal v-if="ui.personalSetupOpen" />
374
397
  <WorkspaceSettingsPanel v-if="ui.workspaceSettingsOpen" />
@@ -18,9 +18,18 @@ export const useModelPresetsStore = defineStore('modelPresets', () => {
18
18
  const api = useApi()
19
19
 
20
20
  const presets = ref<ModelPreset[]>([])
21
+ /**
22
+ * Current built-in catalog versions (`seedModelPresets()`), keyed by preset id, from the
23
+ * workspace snapshot. The keys ARE the set of built-in ids: a stored preset whose id is a
24
+ * key here is a built-in (and is outdated when its `version` is below the catalog value),
25
+ * and a key with no matching stored preset is a NEW built-in the workspace can add. Drives
26
+ * `useModelPresetHealth`.
27
+ */
28
+ const catalogVersions = ref<Record<string, number>>({})
21
29
 
22
- function hydrate(list: ModelPreset[]) {
30
+ function hydrate(list: ModelPreset[], versions?: Record<string, number>) {
23
31
  presets.value = [...list].sort((a, b) => a.createdAt - b.createdAt)
32
+ if (versions) catalogVersions.value = versions
24
33
  }
25
34
 
26
35
  /** The workspace default (fallback for a task that picks none). */
@@ -61,5 +70,43 @@ export const useModelPresetsStore = defineStore('modelPresets', () => {
61
70
  await ws.refresh()
62
71
  }
63
72
 
64
- return { presets, defaultPreset, resolve, modelForKind, hydrate, create, update, remove }
73
+ /**
74
+ * Reseed a built-in preset from the backend's current catalog: adopt an updated definition,
75
+ * repair a drifted one, or materialise a NEW built-in that appeared after the workspace was
76
+ * created. The `presetId` is the catalog id (e.g. `mdp_kimi`). Refreshes the snapshot.
77
+ */
78
+ async function reseed(presetId: string) {
79
+ const ws = useWorkspaceStore()
80
+ const updated = await api.reseedModelPreset(ws.requireId(), presetId)
81
+ await ws.refresh()
82
+ return updated
83
+ }
84
+
85
+ /**
86
+ * Reseed several built-ins in one go, refreshing the snapshot ONCE at the end rather than
87
+ * after every id (each `reseed` refetches the whole board, so a per-id refresh in a loop is
88
+ * wasteful). The POSTs run sequentially so the backend's single-default invariant settles
89
+ * deterministically.
90
+ */
91
+ async function reseedMany(presetIds: string[]) {
92
+ const ws = useWorkspaceStore()
93
+ for (const presetId of presetIds) {
94
+ await api.reseedModelPreset(ws.requireId(), presetId)
95
+ }
96
+ await ws.refresh()
97
+ }
98
+
99
+ return {
100
+ presets,
101
+ catalogVersions,
102
+ defaultPreset,
103
+ resolve,
104
+ modelForKind,
105
+ hydrate,
106
+ create,
107
+ update,
108
+ remove,
109
+ reseed,
110
+ reseedMany,
111
+ }
65
112
  })
package/app/stores/ui.ts CHANGED
@@ -44,6 +44,11 @@ export const useUiStore = defineStore('ui', () => {
44
44
  // once per session so it does not re-pop on every snapshot re-hydration (mirrors pipelines).
45
45
  const mergePresetHealthOpen = ref(false)
46
46
  const mergePresetHealthSeen = ref(false)
47
+ // Model-preset health startup advisory: lists built-ins with a newer catalog version (reseed)
48
+ // and new built-in presets the workspace can add. `modelPresetHealthSeen` gates auto-open to
49
+ // once per session so it does not re-pop on every snapshot re-hydration (mirrors pipelines).
50
+ const modelPresetHealthOpen = ref(false)
51
+ const modelPresetHealthSeen = ref(false)
47
52
  const decisionContext = ref<{ instanceId: string; decisionId: string } | null>(null)
48
53
 
49
54
  // Document-source integration modals, keyed by source. `documentImport` and
@@ -323,6 +328,22 @@ export const useUiStore = defineStore('ui', () => {
323
328
  mergePresetHealthOpen.value = false
324
329
  }
325
330
 
331
+ /** Auto-open the model-preset health advisory once per session (no-op after it's been shown). */
332
+ function maybeOpenModelPresetHealth() {
333
+ if (modelPresetHealthSeen.value) return
334
+ modelPresetHealthSeen.value = true
335
+ modelPresetHealthOpen.value = true
336
+ }
337
+
338
+ function openModelPresetHealth() {
339
+ modelPresetHealthSeen.value = true
340
+ modelPresetHealthOpen.value = true
341
+ }
342
+
343
+ function closeModelPresetHealth() {
344
+ modelPresetHealthOpen.value = false
345
+ }
346
+
326
347
  function openDecision(instanceId: string, decisionId: string) {
327
348
  decisionContext.value = { instanceId, decisionId }
328
349
  }
@@ -820,6 +841,8 @@ export const useUiStore = defineStore('ui', () => {
820
841
  pipelineHealthSeen,
821
842
  mergePresetHealthOpen,
822
843
  mergePresetHealthSeen,
844
+ modelPresetHealthOpen,
845
+ modelPresetHealthSeen,
823
846
  decisionContext,
824
847
  documentConnect,
825
848
  documentImport,
@@ -887,6 +910,9 @@ export const useUiStore = defineStore('ui', () => {
887
910
  maybeOpenMergePresetHealth,
888
911
  openMergePresetHealth,
889
912
  closeMergePresetHealth,
913
+ maybeOpenModelPresetHealth,
914
+ openModelPresetHealth,
915
+ closeModelPresetHealth,
890
916
  openDecision,
891
917
  closeDecision,
892
918
  openApprovalDetail,
@@ -127,7 +127,10 @@ export const useWorkspaceStore = defineStore(
127
127
  useSharedStacksStore().hydrate(snapshot.sharedStacks ?? [])
128
128
  useWorkspaceSettingsStore().hydrate(snapshot.settings)
129
129
  useAgentConfigStore().hydrate(snapshot.agentConfigCatalog ?? [])
130
- useModelPresetsStore().hydrate(snapshot.modelPresets ?? [])
130
+ useModelPresetsStore().hydrate(
131
+ snapshot.modelPresets ?? [],
132
+ snapshot.modelPresetCatalogVersions,
133
+ )
131
134
  useServiceFragmentDefaultsStore().hydrate(snapshot.serviceFragmentDefaults?.fragmentIds)
132
135
  useRecurringPipelinesStore().hydrate(snapshot.recurringPipelines ?? [])
133
136
  useInitiativesStore().hydrate(snapshot.initiatives)
@@ -4374,6 +4374,25 @@
4374
4374
  }
4375
4375
  }
4376
4376
  },
4377
+ "modelPreset": {
4378
+ "health": {
4379
+ "title": "Modell-Preset-Updates",
4380
+ "allValid": "Alle integrierten Modell-Presets sind auf dem neuesten Stand.",
4381
+ "newHeading": "Neue Presets verfügbar",
4382
+ "newDescription": "Neue integrierte Modell-Presets sind verfügbar. Füge sie zur Bibliothek dieses Boards hinzu.",
4383
+ "add": "Hinzufügen",
4384
+ "updatesHeading": "Updates verfügbar",
4385
+ "updatesDescription": "Eine neuere Version dieser integrierten Modell-Presets ist verfügbar. Führe ein erneutes Seeding durch, um sie zu übernehmen (Standard und Reihenfolge bleiben erhalten).",
4386
+ "versionAvailable": "Version {from} → {to} verfügbar.",
4387
+ "reseed": "Erneut seeden",
4388
+ "reseedAll": "Alle aktualisieren ({count})",
4389
+ "dismiss": "Schließen",
4390
+ "done": "Fertig",
4391
+ "toast": {
4392
+ "reseedFailed": "Modell-Preset konnte nicht erneut geseedet werden"
4393
+ }
4394
+ }
4395
+ },
4377
4396
  "environmentWizard": {
4378
4397
  "title": "Eine Testumgebung einrichten",
4379
4398
  "subtitle": "Erkenne, prüfe und speichere ein Docker-Compose-Rezept, damit der Deployer es bereitstellt.",
@@ -3269,6 +3269,25 @@
3269
3269
  }
3270
3270
  }
3271
3271
  },
3272
+ "modelPreset": {
3273
+ "health": {
3274
+ "title": "Model preset updates",
3275
+ "allValid": "All built-in model presets are up to date.",
3276
+ "newHeading": "New presets available",
3277
+ "newDescription": "New built-in model presets have shipped. Add them to this board's library.",
3278
+ "add": "Add",
3279
+ "updatesHeading": "Updates available",
3280
+ "updatesDescription": "A newer version of these built-in model presets has shipped. Reseed to adopt it (the default and ordering are kept).",
3281
+ "versionAvailable": "Version {from} → {to} available.",
3282
+ "reseed": "Reseed",
3283
+ "reseedAll": "Update all ({count})",
3284
+ "dismiss": "Dismiss",
3285
+ "done": "Done",
3286
+ "toast": {
3287
+ "reseedFailed": "Could not reseed model preset"
3288
+ }
3289
+ }
3290
+ },
3272
3291
  "palette": {
3273
3292
  "hint": "Click an agent to append it to the pipeline.",
3274
3293
  "customAgents": "Custom agents"
@@ -4264,6 +4264,25 @@
4264
4264
  }
4265
4265
  }
4266
4266
  },
4267
+ "modelPreset": {
4268
+ "health": {
4269
+ "title": "Actualizaciones de presets de modelo",
4270
+ "allValid": "Todos los presets de modelo integrados están actualizados.",
4271
+ "newHeading": "Nuevos presets disponibles",
4272
+ "newDescription": "Hay nuevos presets de modelo integrados. Añádelos a la biblioteca de este tablero.",
4273
+ "add": "Añadir",
4274
+ "updatesHeading": "Actualizaciones disponibles",
4275
+ "updatesDescription": "Hay una versión más reciente de estos presets de modelo integrados. Regenera para adoptarla (se conservan el predeterminado y el orden).",
4276
+ "versionAvailable": "Versión {from} → {to} disponible.",
4277
+ "reseed": "Regenerar",
4278
+ "reseedAll": "Actualizar todos ({count})",
4279
+ "dismiss": "Descartar",
4280
+ "done": "Hecho",
4281
+ "toast": {
4282
+ "reseedFailed": "No se pudo regenerar el preset de modelo"
4283
+ }
4284
+ }
4285
+ },
4267
4286
  "initiative": {
4268
4287
  "create": {
4269
4288
  "title": "Crear iniciativa",
@@ -4264,6 +4264,25 @@
4264
4264
  }
4265
4265
  }
4266
4266
  },
4267
+ "modelPreset": {
4268
+ "health": {
4269
+ "title": "Mises à jour des presets de modèle",
4270
+ "allValid": "Tous les presets de modèle intégrés sont à jour.",
4271
+ "newHeading": "Nouveaux presets disponibles",
4272
+ "newDescription": "De nouveaux presets de modèle intégrés sont arrivés. Ajoutez-les à la bibliothèque de ce tableau.",
4273
+ "add": "Ajouter",
4274
+ "updatesHeading": "Mises à jour disponibles",
4275
+ "updatesDescription": "Une version plus récente de ces presets de modèle intégrés est arrivée. Régénérez pour l'adopter (le preset par défaut et l'ordre sont conservés).",
4276
+ "versionAvailable": "Version {from} → {to} disponible.",
4277
+ "reseed": "Régénérer",
4278
+ "reseedAll": "Tout mettre à jour ({count})",
4279
+ "dismiss": "Ignorer",
4280
+ "done": "Terminé",
4281
+ "toast": {
4282
+ "reseedFailed": "Impossible de régénérer le preset de modèle"
4283
+ }
4284
+ }
4285
+ },
4267
4286
  "initiative": {
4268
4287
  "create": {
4269
4288
  "title": "Creer une initiative",
@@ -4275,6 +4275,25 @@
4275
4275
  }
4276
4276
  }
4277
4277
  },
4278
+ "modelPreset": {
4279
+ "health": {
4280
+ "title": "עדכוני קביעות מודל",
4281
+ "allValid": "כל קביעות המודל המובנות מעודכנות.",
4282
+ "newHeading": "קביעות חדשות זמינות",
4283
+ "newDescription": "הגיעו קביעות מודל מובנות חדשות. הוסף אותן לספריית הלוח הזה.",
4284
+ "add": "הוסף",
4285
+ "updatesHeading": "עדכונים זמינים",
4286
+ "updatesDescription": "גרסה חדשה יותר של קביעות המודל המובנות האלה הגיעה. זרע מחדש כדי לאמץ אותה (ברירת המחדל והסדר נשמרים).",
4287
+ "versionAvailable": "גרסה {from} → {to} זמינה.",
4288
+ "reseed": "זרע מחדש",
4289
+ "reseedAll": "עדכן הכל ({count})",
4290
+ "dismiss": "התעלם",
4291
+ "done": "בוצע",
4292
+ "toast": {
4293
+ "reseedFailed": "לא ניתן לזרוע מחדש את קביעת המודל"
4294
+ }
4295
+ }
4296
+ },
4278
4297
  "initiative": {
4279
4298
  "create": {
4280
4299
  "title": "יצירת יוזמה",
@@ -4374,6 +4374,25 @@
4374
4374
  }
4375
4375
  }
4376
4376
  },
4377
+ "modelPreset": {
4378
+ "health": {
4379
+ "title": "Aggiornamenti dei preset di modello",
4380
+ "allValid": "Tutti i preset di modello integrati sono aggiornati.",
4381
+ "newHeading": "Nuovi preset disponibili",
4382
+ "newDescription": "Sono stati rilasciati nuovi preset di modello integrati. Aggiungili alla libreria di questa board.",
4383
+ "add": "Aggiungi",
4384
+ "updatesHeading": "Aggiornamenti disponibili",
4385
+ "updatesDescription": "È stata rilasciata una versione più recente di questi preset di modello integrati. Ripristina i valori iniziali per adottarla (il predefinito e l'ordinamento vengono mantenuti).",
4386
+ "versionAvailable": "Versione {from} → {to} disponibile.",
4387
+ "reseed": "Ripristina",
4388
+ "reseedAll": "Aggiorna tutti ({count})",
4389
+ "dismiss": "Ignora",
4390
+ "done": "Fatto",
4391
+ "toast": {
4392
+ "reseedFailed": "Impossibile ripristinare il preset di modello"
4393
+ }
4394
+ }
4395
+ },
4377
4396
  "environmentWizard": {
4378
4397
  "title": "Configura un ambiente di test",
4379
4398
  "subtitle": "Rileva, rivedi e salva una ricetta Docker Compose in modo che il Deployer la esegua.",
@@ -4276,6 +4276,25 @@
4276
4276
  }
4277
4277
  }
4278
4278
  },
4279
+ "modelPreset": {
4280
+ "health": {
4281
+ "title": "モデルプリセットの更新",
4282
+ "allValid": "組み込みのモデルプリセットはすべて最新です。",
4283
+ "newHeading": "新しいプリセットがあります",
4284
+ "newDescription": "新しい組み込みモデルプリセットが追加されました。このボードのライブラリに追加してください。",
4285
+ "add": "追加",
4286
+ "updatesHeading": "更新あり",
4287
+ "updatesDescription": "これらの組み込みモデルプリセットの新しいバージョンがあります。再シードして取り込みます(既定と並び順は保持されます)。",
4288
+ "versionAvailable": "バージョン {from} → {to} が利用可能です。",
4289
+ "reseed": "再シード",
4290
+ "reseedAll": "すべて更新 ({count})",
4291
+ "dismiss": "閉じる",
4292
+ "done": "完了",
4293
+ "toast": {
4294
+ "reseedFailed": "モデルプリセットを再シードできませんでした"
4295
+ }
4296
+ }
4297
+ },
4279
4298
  "initiative": {
4280
4299
  "create": {
4281
4300
  "title": "イニシアチブを作成",
@@ -4264,6 +4264,25 @@
4264
4264
  }
4265
4265
  }
4266
4266
  },
4267
+ "modelPreset": {
4268
+ "health": {
4269
+ "title": "Aktualizacje presetów modelu",
4270
+ "allValid": "Wszystkie wbudowane presety modelu są aktualne.",
4271
+ "newHeading": "Dostępne nowe presety",
4272
+ "newDescription": "Pojawiły się nowe wbudowane presety modelu. Dodaj je do biblioteki tej tablicy.",
4273
+ "add": "Dodaj",
4274
+ "updatesHeading": "Dostępne aktualizacje",
4275
+ "updatesDescription": "Dostępna jest nowsza wersja tych wbudowanych presetów modelu. Zregeneruj, aby ją przyjąć (domyślny i kolejność są zachowane).",
4276
+ "versionAvailable": "Dostępna wersja {from} → {to}.",
4277
+ "reseed": "Zregeneruj",
4278
+ "reseedAll": "Zaktualizuj wszystkie ({count})",
4279
+ "dismiss": "Odrzuć",
4280
+ "done": "Gotowe",
4281
+ "toast": {
4282
+ "reseedFailed": "Nie udało się zregenerować presetu modelu"
4283
+ }
4284
+ }
4285
+ },
4267
4286
  "initiative": {
4268
4287
  "create": {
4269
4288
  "title": "Utworz inicjatywe",
@@ -4276,6 +4276,25 @@
4276
4276
  }
4277
4277
  }
4278
4278
  },
4279
+ "modelPreset": {
4280
+ "health": {
4281
+ "title": "Model ön ayarı güncellemeleri",
4282
+ "allValid": "Tüm yerleşik model ön ayarları güncel.",
4283
+ "newHeading": "Yeni ön ayarlar mevcut",
4284
+ "newDescription": "Yeni yerleşik model ön ayarları geldi. Bu panonun kütüphanesine ekleyin.",
4285
+ "add": "Ekle",
4286
+ "updatesHeading": "Güncellemeler mevcut",
4287
+ "updatesDescription": "Bu yerleşik model ön ayarlarının daha yeni bir sürümü geldi. Benimsemek için yeniden tohumlayın (varsayılan ve sıralama korunur).",
4288
+ "versionAvailable": "Sürüm {from} → {to} mevcut.",
4289
+ "reseed": "Yeniden tohumla",
4290
+ "reseedAll": "Tümünü güncelle ({count})",
4291
+ "dismiss": "Yoksay",
4292
+ "done": "Tamam",
4293
+ "toast": {
4294
+ "reseedFailed": "Model ön ayarı yeniden tohumlanamadı"
4295
+ }
4296
+ }
4297
+ },
4279
4298
  "initiative": {
4280
4299
  "create": {
4281
4300
  "title": "Girisim olustur",
@@ -4264,6 +4264,25 @@
4264
4264
  }
4265
4265
  }
4266
4266
  },
4267
+ "modelPreset": {
4268
+ "health": {
4269
+ "title": "Оновлення пресетів моделі",
4270
+ "allValid": "Усі вбудовані пресети моделі актуальні.",
4271
+ "newHeading": "Доступні нові пресети",
4272
+ "newDescription": "З'явилися нові вбудовані пресети моделі. Додайте їх до бібліотеки цієї дошки.",
4273
+ "add": "Додати",
4274
+ "updatesHeading": "Доступні оновлення",
4275
+ "updatesDescription": "Доступна новіша версія цих вбудованих пресетів моделі. Перегенеруйте, щоб застосувати її (стандартний пресет і порядок збережено).",
4276
+ "versionAvailable": "Доступна версія {from} → {to}.",
4277
+ "reseed": "Перегенерувати",
4278
+ "reseedAll": "Оновити всі ({count})",
4279
+ "dismiss": "Відхилити",
4280
+ "done": "Готово",
4281
+ "toast": {
4282
+ "reseedFailed": "Не вдалося перегенерувати пресет моделі"
4283
+ }
4284
+ }
4285
+ },
4267
4286
  "initiative": {
4268
4287
  "create": {
4269
4288
  "title": "Створити ініціативу",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.107.6",
3
+ "version": "0.108.0",
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.118.0"
37
+ "@cat-factory/contracts": "0.119.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@toad-contracts/testing": "0.3.2",