@cat-factory/app 0.212.0 → 0.213.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.
@@ -12,6 +12,7 @@
12
12
  // base-model picker plus a filterable per-agent override list.
13
13
  import { computed, ref, watch } from 'vue'
14
14
  import { onKeyStroke } from '@vueuse/core'
15
+ import type { ModelFlavor } from '@cat-factory/contracts'
15
16
  import type { AgentKind } from '~/types/domain'
16
17
  import type { ModelPreset } from '~/types/model-presets'
17
18
  import AgentTierSelect from '~/components/palettes/AgentTierSelect.vue'
@@ -19,9 +20,12 @@ import { filterByAgentTierKeeping } from '~/utils/agentTier'
19
20
  import { MODEL_CONFIGURABLE_SYSTEM_KINDS } from '~/utils/catalog'
20
21
  import { cachingLabel, contextLabel, costLabel, displayFlavor, isSelectable } from '~/stores/models'
21
22
  import ConsensusGroupsSection from '~/components/settings/ConsensusGroupsSection.vue'
23
+ import ProviderPreferenceEditor from '~/components/settings/ProviderPreferenceEditor.vue'
24
+ import { showOverrideField } from '~/utils/uiMode'
22
25
 
23
26
  const { t } = useI18n()
24
27
  const ui = useUiStore()
28
+ const uiMode = useUiModeStore()
25
29
  const models = useModelsStore()
26
30
  const presets = useModelPresetsStore()
27
31
  const agents = useAgentsStore()
@@ -43,6 +47,8 @@ interface EditorState {
43
47
  baseModelId: string
44
48
  overrides: Record<string, string>
45
49
  isDefault: boolean
50
+ /** The preset's route order; undefined ⇒ it inherits the deployment's default order. */
51
+ providerPreference: ModelFlavor[] | undefined
46
52
  }
47
53
  const editor = ref<EditorState | null>(null)
48
54
  const busy = ref(false)
@@ -144,6 +150,7 @@ function startCreate() {
144
150
  baseModelId: selectableModels.value[0]?.id ?? 'kimi-k2.7',
145
151
  overrides: {},
146
152
  isDefault: false,
153
+ providerPreference: undefined,
147
154
  }
148
155
  filter.value = ''
149
156
  }
@@ -154,10 +161,19 @@ function startEdit(p: ModelPreset) {
154
161
  baseModelId: p.baseModelId,
155
162
  overrides: { ...p.overrides },
156
163
  isDefault: p.isDefault,
164
+ providerPreference: p.providerPreference ? [...p.providerPreference] : undefined,
157
165
  }
158
166
  filter.value = ''
159
167
  }
160
168
 
169
+ // The route order is an OVERRIDE of the deployment's default, so basic mode hides it — but only
170
+ // while this preset states none. A preset that already carries one (written by a teammate, by the
171
+ // API, or here at the advanced tier) keeps the control, or a basic-mode user would be looking at a
172
+ // preset whose routes they can neither read nor reset.
173
+ const showRouteOrder = computed(() =>
174
+ showOverrideField(uiMode.isAdvanced, editor.value?.providerPreference),
175
+ )
176
+
161
177
  async function setDefault(p: ModelPreset) {
162
178
  if (p.isDefault) return
163
179
  busy.value = true
@@ -249,6 +265,9 @@ async function save() {
249
265
  baseModelId: e.baseModelId,
250
266
  overrides: e.overrides,
251
267
  isDefault: e.isDefault,
268
+ // Always sent on a patch, `[]` included: an absent field means "leave the stored order
269
+ // alone", so a reset has to arrive as the empty list that clears it.
270
+ providerPreference: e.providerPreference ?? [],
252
271
  })
253
272
  } else {
254
273
  await presets.create({
@@ -256,6 +275,7 @@ async function save() {
256
275
  baseModelId: e.baseModelId,
257
276
  overrides: e.overrides,
258
277
  isDefault: e.isDefault,
278
+ ...(e.providerPreference ? { providerPreference: e.providerPreference } : {}),
259
279
  })
260
280
  }
261
281
  editor.value = null
@@ -422,6 +442,12 @@ function fail(title: string, e: unknown) {
422
442
  )
423
443
  }}
424
444
  </span>
445
+ <!-- A custom route order changes which provider the same model runs on, so the
446
+ list says so rather than leaving it visible only inside the editor (which
447
+ basic mode hides). -->
448
+ <span v-if="p.providerPreference?.length" class="text-slate-300">
449
+ · {{ t('settings.modelConfiguration.list.customRouteOrder') }}
450
+ </span>
425
451
  </div>
426
452
  </div>
427
453
  <p
@@ -483,6 +509,17 @@ function fail(title: string, e: unknown) {
483
509
  </label>
484
510
  </div>
485
511
 
512
+ <!-- Which of a model's ROUTES this preset's runs prefer: a compliance preset can put
513
+ AWS Bedrock ahead of a model's own provider API, an everyday preset a flat-rate
514
+ subscription first. An override of the deployment default, so basic mode hides it
515
+ until the preset actually carries one. -->
516
+ <ProviderPreferenceEditor
517
+ v-if="showRouteOrder"
518
+ v-model="editor.providerPreference"
519
+ :has-subscription="creds.configuredVendors.size > 0"
520
+ :is-default-preset="editor.isDefault"
521
+ />
522
+
486
523
  <div>
487
524
  <div class="mb-1 flex items-start justify-between gap-3">
488
525
  <span class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
@@ -0,0 +1,104 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { DEFAULT_MODEL_FLAVOR_ORDER, type ModelFlavor } from '@cat-factory/contracts'
3
+ import {
4
+ commitFlavorOrder,
5
+ isDefaultFlavorOrder,
6
+ moveFlavor,
7
+ subscriptionOverridesOrder,
8
+ } from '~/components/settings/ProviderPreferenceEditor.logic'
9
+
10
+ const DEFAULT = [...DEFAULT_MODEL_FLAVOR_ORDER]
11
+
12
+ describe('isDefaultFlavorOrder', () => {
13
+ it('is true only for the full shipped order, position for position', () => {
14
+ expect(isDefaultFlavorOrder(DEFAULT)).toBe(true)
15
+ expect(isDefaultFlavorOrder([...DEFAULT].reverse())).toBe(false)
16
+ })
17
+
18
+ it('rejects a PREFIX of the default rather than reading it as "no preference"', () => {
19
+ // `every` is vacuously true for a prefix, so without the length check a partial order that
20
+ // happens to start like the default would be silently cleared — the preset would lose an
21
+ // order the caller meant to store.
22
+ expect(isDefaultFlavorOrder(DEFAULT.slice(0, 2))).toBe(false)
23
+ expect(isDefaultFlavorOrder([])).toBe(false)
24
+ })
25
+ })
26
+
27
+ describe('commitFlavorOrder', () => {
28
+ it('stores an order that equals the default as ABSENT, not as a copy of it', () => {
29
+ // The whole "a preset keeps tracking the shipped order as the product changes it" property
30
+ // rests on this, and the SPA is the only place it happens.
31
+ expect(commitFlavorOrder(DEFAULT)).toBeUndefined()
32
+ })
33
+
34
+ it('stores a genuinely reordered list', () => {
35
+ const reordered: ModelFlavor[] = [
36
+ 'bedrock',
37
+ 'direct',
38
+ 'openrouter',
39
+ 'cloudflare',
40
+ 'subscription',
41
+ ]
42
+ expect(commitFlavorOrder(reordered)).toEqual(reordered)
43
+ })
44
+
45
+ it('copies rather than aliasing the array it was given', () => {
46
+ const next: ModelFlavor[] = ['bedrock', 'direct', 'openrouter', 'cloudflare', 'subscription']
47
+ const stored = commitFlavorOrder(next)
48
+ next[0] = 'cloudflare'
49
+ expect(stored?.[0]).toBe('bedrock')
50
+ })
51
+ })
52
+
53
+ describe('moveFlavor', () => {
54
+ it('swaps with the neighbour in the given direction', () => {
55
+ expect(moveFlavor(DEFAULT, 1, -1)).toEqual([DEFAULT[1], DEFAULT[0], ...DEFAULT.slice(2)])
56
+ })
57
+
58
+ it('is a no-op past either end', () => {
59
+ expect(moveFlavor(DEFAULT, 0, -1)).toBeUndefined()
60
+ expect(moveFlavor(DEFAULT, DEFAULT.length - 1, 1)).toBeUndefined()
61
+ })
62
+
63
+ it('never drops or duplicates a route (a preference reorders, it never filters)', () => {
64
+ const moved = moveFlavor(DEFAULT, 2, 1)!
65
+ expect([...moved].sort()).toEqual([...DEFAULT].sort())
66
+ })
67
+ })
68
+
69
+ describe('subscriptionOverridesOrder', () => {
70
+ const compliance: ModelFlavor[] = ['bedrock', 'direct']
71
+ const subscriptionFirst: ModelFlavor[] = ['subscription', 'direct']
72
+
73
+ it('warns whenever a connected plan can overrule the order (the compliance-preset case)', () => {
74
+ // "Subscriptions always win" is applied by the engine ON TOP of the route this order resolves,
75
+ // so a preset promoting AWS Bedrock is silently overruled for a dual-mode model on a workspace
76
+ // holding a token. The control has to say so rather than promise the route.
77
+ expect(subscriptionOverridesOrder({ preference: compliance, hasSubscription: true })).toBe(true)
78
+ })
79
+
80
+ it('stays quiet when the order already puts the subscription first (override agrees with it)', () => {
81
+ expect(
82
+ subscriptionOverridesOrder({ preference: subscriptionFirst, hasSubscription: true }),
83
+ ).toBe(false)
84
+ })
85
+
86
+ it('stays quiet with no connected subscription — no token, no override', () => {
87
+ expect(subscriptionOverridesOrder({ preference: compliance, hasSubscription: false })).toBe(
88
+ false,
89
+ )
90
+ })
91
+
92
+ it('stays quiet when the preset states no order at all (nothing was promised)', () => {
93
+ expect(subscriptionOverridesOrder({ preference: undefined, hasSubscription: true })).toBe(false)
94
+ expect(subscriptionOverridesOrder({ preference: [], hasSubscription: true })).toBe(false)
95
+ })
96
+
97
+ it('judges the RESOLVED total order, not the caller’s partial list', () => {
98
+ // A one-entry list still resolves to a full order, so "is subscription first" is answerable
99
+ // for it — and `['subscription']` promotes the route even though it names nothing else.
100
+ expect(
101
+ subscriptionOverridesOrder({ preference: ['subscription'], hasSubscription: true }),
102
+ ).toBe(false)
103
+ })
104
+ })
@@ -0,0 +1,81 @@
1
+ // The pure half of `ProviderPreferenceEditor.vue`: what a reordering COMMITS, and what the
2
+ // control has to say about a route order the run path may still override. Extracted so both can
3
+ // be pinned without mounting Vue — the "equals the default is stored as absent" normalisation in
4
+ // particular, which the backend's whole "a preset keeps tracking the shipped order" property rests
5
+ // on and which no backend test can see (the SPA is the only thing that performs it).
6
+
7
+ import {
8
+ DEFAULT_MODEL_FLAVOR_ORDER,
9
+ type ModelFlavor,
10
+ orderedModelFlavorPreference,
11
+ } from '@cat-factory/contracts'
12
+
13
+ /**
14
+ * Whether an order is today's shipped default, position for position.
15
+ *
16
+ * The length check is not redundant: `every` is vacuously true for a PREFIX, so without it a
17
+ * partial order that happens to start like the default would be read as "no preference" and
18
+ * silently cleared. The editor only ever produces full permutations, which is exactly why the
19
+ * guard belongs here rather than in the caller — it is what keeps that a property of this
20
+ * function instead of a property of its one current caller.
21
+ */
22
+ export function isDefaultFlavorOrder(next: readonly ModelFlavor[]): boolean {
23
+ return (
24
+ next.length === DEFAULT_MODEL_FLAVOR_ORDER.length &&
25
+ next.every((flavor, i) => DEFAULT_MODEL_FLAVOR_ORDER[i] === flavor)
26
+ )
27
+ }
28
+
29
+ /**
30
+ * What a reordering stores: the new order, or UNSET when it lands back on the shipped default.
31
+ *
32
+ * "No preference" and "an order that happens to match today's default" are different states, and
33
+ * only the first keeps following the shipped order as the product changes it. Storing a copy would
34
+ * silently pin today's wording of a list that is itself scheduled to be reordered.
35
+ */
36
+ export function commitFlavorOrder(next: readonly ModelFlavor[]): ModelFlavor[] | undefined {
37
+ return isDefaultFlavorOrder(next) ? undefined : [...next]
38
+ }
39
+
40
+ /** Swap the entry at `index` with its neighbour `delta` away; undefined when the move is a no-op. */
41
+ export function moveFlavor(
42
+ order: readonly ModelFlavor[],
43
+ index: number,
44
+ delta: number,
45
+ ): ModelFlavor[] | undefined {
46
+ const next = [...order]
47
+ const target = index + delta
48
+ const moved = next[index]
49
+ const displaced = next[target]
50
+ if (!moved || !displaced) return undefined
51
+ next[index] = displaced
52
+ next[target] = moved
53
+ return next
54
+ }
55
+
56
+ /**
57
+ * Whether to warn that a connected subscription will still win over this order.
58
+ *
59
+ * "Subscriptions always win" is applied by the ENGINE on top of the route this order resolves: a
60
+ * dual-mode model (Kimi/DeepSeek/GLM) switches to its subscription flavour whenever the workspace
61
+ * or the run initiator holds a token, whatever the preset asked for. So the override does not
62
+ * merely re-rank the subscription route within this list — it sits OUTSIDE the list — and the only
63
+ * order it cannot contradict is one that already puts `subscription` first.
64
+ *
65
+ * That is why the test is "is subscription first", not "was subscription demoted": `subscription`
66
+ * is last in today's shipped order, so a demotion test could never fire, and the case that actually
67
+ * bites is the opposite one — a compliance preset promoting `bedrock` and being silently overruled.
68
+ * Until the override moves into this order, the control has to say so, because copy promising a
69
+ * residency-guaranteed route that a connected plan quietly overrules is the one thing a compliance
70
+ * preset must never do.
71
+ *
72
+ * Only when the preset actually states an order (nothing is promised otherwise) AND the workspace
73
+ * has a connected subscription (no token, no override).
74
+ */
75
+ export function subscriptionOverridesOrder(input: {
76
+ preference: readonly ModelFlavor[] | undefined
77
+ hasSubscription: boolean
78
+ }): boolean {
79
+ if (!input.preference?.length || !input.hasSubscription) return false
80
+ return orderedModelFlavorPreference(input.preference)[0] !== 'subscription'
81
+ }
@@ -0,0 +1,180 @@
1
+ <script setup lang="ts">
2
+ // The ROUTE-ORDER editor for one model preset: which of a model's routes (its own provider API,
3
+ // AWS Bedrock, the OpenRouter gateway, Cloudflare Workers AI, a subscription harness) the preset's
4
+ // runs prefer, most preferred first.
5
+ //
6
+ // Two properties of the backend model decide the whole shape of this control:
7
+ //
8
+ // - A preference REORDERS, never filters. So the list always shows EVERY route and the only
9
+ // affordance is moving one. There is deliberately no way to remove a route: a preset that
10
+ // could drop one would make a model whose only route is that one unstartable, which is not
11
+ // what anybody choosing an order means.
12
+ // - "No preference" is a real, distinct state from "an order that happens to match today's
13
+ // default". Only the first keeps tracking the shipped order as the product changes it, so
14
+ // reordering back to the default clears the preference rather than storing a copy of it, and
15
+ // the header says which of the two the preset is in.
16
+ //
17
+ // A third property is the engine's rather than this order's, and it is why the control can warn:
18
+ // "subscriptions always win" is applied on TOP of the resolved route, so on a workspace with a
19
+ // connected plan a dual-mode model ignores an order that ranks `subscription` lower than the
20
+ // shipped default does. The logic module says when; the copy says so plainly.
21
+ import { computed } from 'vue'
22
+ import type { ModelFlavor } from '@cat-factory/contracts'
23
+ import { orderedModelFlavorPreference } from '@cat-factory/contracts'
24
+ import {
25
+ commitFlavorOrder,
26
+ moveFlavor,
27
+ subscriptionOverridesOrder,
28
+ } from '~/components/settings/ProviderPreferenceEditor.logic'
29
+
30
+ const props = defineProps<{
31
+ /** The preset's stored order; empty/absent ⇒ the deployment's default order. */
32
+ modelValue: ModelFlavor[] | undefined
33
+ /**
34
+ * Whether this workspace has ANY subscription vendor connected. Drives the caveat only: the
35
+ * override it warns about is the engine's, so the control cannot prevent it, only name it.
36
+ */
37
+ hasSubscription?: boolean
38
+ /**
39
+ * Whether the preset being edited is the workspace DEFAULT. `GET /models` resolves its flavour
40
+ * badges under the default preset's order, so on any OTHER preset the model list beside this
41
+ * control is showing routes this order does not govern, and the control says so.
42
+ */
43
+ isDefaultPreset?: boolean
44
+ }>()
45
+ const emit = defineEmits<{ 'update:modelValue': [ModelFlavor[] | undefined] }>()
46
+
47
+ const { t } = useI18n()
48
+
49
+ /** Static literal keys, one per route, so the typed-message-key check sees them all. */
50
+ const ROUTE_LABELS: Record<ModelFlavor, string> = {
51
+ direct: 'settings.modelConfiguration.routes.direct',
52
+ bedrock: 'settings.modelConfiguration.routes.bedrock',
53
+ openrouter: 'settings.modelConfiguration.routes.openrouter',
54
+ cloudflare: 'settings.modelConfiguration.routes.cloudflare',
55
+ subscription: 'settings.modelConfiguration.routes.subscription',
56
+ }
57
+ const ROUTE_HINTS: Record<ModelFlavor, string> = {
58
+ direct: 'settings.modelConfiguration.routeHints.direct',
59
+ bedrock: 'settings.modelConfiguration.routeHints.bedrock',
60
+ openrouter: 'settings.modelConfiguration.routeHints.openrouter',
61
+ cloudflare: 'settings.modelConfiguration.routeHints.cloudflare',
62
+ subscription: 'settings.modelConfiguration.routeHints.subscription',
63
+ }
64
+
65
+ /** The order actually in force: the preset's own, else the default. Always all five routes. */
66
+ const order = computed(() => orderedModelFlavorPreference(props.modelValue))
67
+ const isCustom = computed(() => (props.modelValue?.length ?? 0) > 0)
68
+
69
+ /** A connected plan overrules a deprioritised subscription route — see the logic module. */
70
+ const subscriptionWins = computed(() =>
71
+ subscriptionOverridesOrder({
72
+ preference: props.modelValue,
73
+ hasSubscription: props.hasSubscription ?? false,
74
+ }),
75
+ )
76
+
77
+ /** The badges beside this control render under the DEFAULT preset's order, not this one's. */
78
+ const badgesShowAnotherOrder = computed(() => isCustom.value && props.isDefaultPreset === false)
79
+
80
+ function move(index: number, delta: number) {
81
+ const next = moveFlavor(order.value, index, delta)
82
+ if (next) emit('update:modelValue', commitFlavorOrder(next))
83
+ }
84
+
85
+ function reset() {
86
+ emit('update:modelValue', undefined)
87
+ }
88
+ </script>
89
+
90
+ <template>
91
+ <div>
92
+ <div class="mb-1 flex items-start justify-between gap-3">
93
+ <span class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
94
+ {{ t('settings.modelConfiguration.routeOrder.label') }}
95
+ </span>
96
+ <UButton
97
+ v-if="isCustom"
98
+ size="xs"
99
+ variant="ghost"
100
+ color="neutral"
101
+ icon="i-lucide-rotate-ccw"
102
+ data-testid="preset-route-order-reset"
103
+ @click="reset"
104
+ >
105
+ {{ t('settings.modelConfiguration.routeOrder.reset') }}
106
+ </UButton>
107
+ </div>
108
+ <p class="mb-2 text-[11px] leading-relaxed text-slate-500">
109
+ {{
110
+ isCustom
111
+ ? t('settings.modelConfiguration.routeOrder.customHint')
112
+ : t('settings.modelConfiguration.routeOrder.defaultHint')
113
+ }}
114
+ </p>
115
+ <!-- The engine applies "subscriptions always win" ON TOP of this order, so on a workspace with
116
+ a connected plan a dual-mode model ignores a deprioritised subscription route. Said here
117
+ rather than left to be discovered in a run: copy that promises a residency-guaranteed
118
+ route a connected plan quietly overrules is the one thing this control must not do. -->
119
+ <p
120
+ v-if="subscriptionWins"
121
+ class="mb-2 text-[11px] leading-relaxed text-amber-400/90"
122
+ data-testid="preset-route-order-subscription-warning"
123
+ >
124
+ {{ t('settings.modelConfiguration.routeOrder.subscriptionOverrideHint') }}
125
+ </p>
126
+ <!-- The model list beside this control shows each model's route under the WORKSPACE DEFAULT
127
+ preset (that is what `GET /models` resolves), so on any other preset those badges are
128
+ answering a different question than this order asks. -->
129
+ <p
130
+ v-if="badgesShowAnotherOrder"
131
+ class="mb-2 text-[11px] leading-relaxed text-slate-500"
132
+ data-testid="preset-route-order-badge-hint"
133
+ >
134
+ {{ t('settings.modelConfiguration.routeOrder.badgesUseDefaultPresetHint') }}
135
+ </p>
136
+ <ol
137
+ class="divide-y divide-slate-800 rounded-xl border border-slate-800 bg-slate-900/50"
138
+ data-testid="preset-route-order"
139
+ >
140
+ <li
141
+ v-for="(flavor, index) in order"
142
+ :key="flavor"
143
+ class="flex items-center gap-3 px-4 py-2.5"
144
+ :data-testid="`preset-route-${flavor}`"
145
+ >
146
+ <span
147
+ class="flex h-5 w-5 shrink-0 items-center justify-center rounded bg-slate-800 text-[10px] font-semibold text-slate-400"
148
+ >
149
+ {{ index + 1 }}
150
+ </span>
151
+ <div class="min-w-0 flex-1">
152
+ <p class="truncate text-sm text-slate-200">{{ t(ROUTE_LABELS[flavor]) }}</p>
153
+ <p class="truncate text-[11px] text-slate-500">{{ t(ROUTE_HINTS[flavor]) }}</p>
154
+ </div>
155
+ <div class="flex shrink-0 items-center gap-1">
156
+ <UButton
157
+ size="xs"
158
+ variant="ghost"
159
+ color="neutral"
160
+ icon="i-lucide-chevron-up"
161
+ :disabled="index === 0"
162
+ :title="t('settings.modelConfiguration.routeOrder.moveUp')"
163
+ :aria-label="t('settings.modelConfiguration.routeOrder.moveUp')"
164
+ @click="move(index, -1)"
165
+ />
166
+ <UButton
167
+ size="xs"
168
+ variant="ghost"
169
+ color="neutral"
170
+ icon="i-lucide-chevron-down"
171
+ :disabled="index === order.length - 1"
172
+ :title="t('settings.modelConfiguration.routeOrder.moveDown')"
173
+ :aria-label="t('settings.modelConfiguration.routeOrder.moveDown')"
174
+ @click="move(index, 1)"
175
+ />
176
+ </div>
177
+ </li>
178
+ </ol>
179
+ </div>
180
+ </template>
@@ -240,6 +240,10 @@ const CONFLICT_INFO: Record<Exclude<ConflictReason, BespokeConflictReason>, Conf
240
240
  titleKey: 'errors.conflict.title.pipeline_schedule_intake_unconfigured',
241
241
  descriptionKey: 'errors.conflict.description.pipeline_schedule_intake_unconfigured',
242
242
  },
243
+ ticket_already_linked: {
244
+ titleKey: 'errors.conflict.title.ticket_already_linked',
245
+ descriptionKey: 'errors.conflict.description.ticket_already_linked',
246
+ },
243
247
  }
244
248
 
245
249
  /**
@@ -22,7 +22,8 @@
22
22
  "deleteTitle": "Preset löschen",
23
23
  "basePrefix": "Basis:",
24
24
  "overrideCount": "{count} Override | {count} Overrides",
25
- "empty": "Noch keine Presets. Erstelle eines, um Modelle deinen Agenten zuzuordnen."
25
+ "empty": "Noch keine Presets. Erstelle eines, um Modelle deinen Agenten zuzuordnen.",
26
+ "customRouteOrder": "eigene Routen-Reihenfolge"
26
27
  },
27
28
  "editor": {
28
29
  "useBaseModel": "Basismodell verwenden",
@@ -43,6 +44,30 @@
43
44
  "nameRequiredTitle": "Name erforderlich",
44
45
  "nameRequiredBody": "Gib dem Preset einen Namen.",
45
46
  "saveFailed": "Preset konnte nicht gespeichert werden"
47
+ },
48
+ "routes": {
49
+ "direct": "Die eigene Provider-API des Modells",
50
+ "bedrock": "AWS Bedrock",
51
+ "openrouter": "OpenRouter-Gateway",
52
+ "cloudflare": "Cloudflare Workers AI",
53
+ "subscription": "Abo (Claude Code / Codex)"
54
+ },
55
+ "routeHints": {
56
+ "direct": "Benötigt einen API-Schlüssel für diesen Provider.",
57
+ "bedrock": "Läuft in deiner AWS-Region, mit den für dein Konto freigegebenen Modellen.",
58
+ "openrouter": "Ein Schlüssel, viele Anbieter, mit Aufschlag weiterverkauft.",
59
+ "cloudflare": "Immer verfügbar, ohne Prompt-Caching.",
60
+ "subscription": "Pauschales Kontingent eines verbundenen Tarifs."
61
+ },
62
+ "routeOrder": {
63
+ "label": "Routen-Reihenfolge",
64
+ "defaultHint": "Dieses Preset folgt dem Standard des Deployments. Verschiebe eine Route nach oben, um sie zu bevorzugen.",
65
+ "customHint": "Dieses Preset bevorzugt Routen in dieser Reihenfolge. Ein Modell, das nur über eine niedrigere Route erreichbar ist, läuft weiterhin und wird nur später versucht.",
66
+ "reset": "Standardreihenfolge verwenden",
67
+ "moveUp": "Diese Route stärker bevorzugen",
68
+ "moveDown": "Diese Route weniger bevorzugen",
69
+ "subscriptionOverrideHint": "Ein verbundenes Abo gewinnt weiterhin bei Modellen, die eines anbieten: Die Engine leitet diese unabhängig von dieser Reihenfolge auf den Tarif. Routen unterhalb des Abo-Eintrags gelten für alle anderen Modelle.",
70
+ "badgesUseDefaultPresetHint": "Die Route neben jedem Modell oben ist die des Standard-Presets des Workspace, nicht die dieses Presets."
46
71
  }
47
72
  },
48
73
  "account": {
@@ -5096,7 +5121,8 @@
5096
5121
  "foundational_service_exists": "Basisdienst existiert bereits",
5097
5122
  "binary_output_service_invalid": "Dienst für Binärausgaben nicht auflösbar",
5098
5123
  "binary_output_generator_invalid": "Generator für Binärausgaben nicht auflösbar",
5099
- "foundational_service_not_inherited": "Dieses Board hat den Dienst registriert"
5124
+ "foundational_service_not_inherited": "Dieses Board hat den Dienst registriert",
5125
+ "ticket_already_linked": "Dieses Ticket hat bereits eine Aufgabe"
5100
5126
  },
5101
5127
  "description": {
5102
5128
  "dependencies_unmet": "Diese Aufgabe hängt von anderen ab, die noch nicht abgeschlossen sind. Schließe sie ab oder gib sie frei und starte dann erneut.",
@@ -5128,7 +5154,8 @@
5128
5154
  "foundational_service_exists": "Ein Basisdienst mit dieser ID ist in diesem Bereich bereits registriert. Öffnen Sie den vorhandenen Eintrag und bearbeiten Sie ihn — zwei Dienste können sich keine ID teilen, denn die ID ist der Name, den ein Architekt in seinem Entwurf verwendet.",
5129
5155
  "binary_output_service_invalid": "Ein Schritt, der Binärausgaben erzeugt, wählt einen Basisdienst aus, den der Katalog dieses Workspace nicht auflösen kann: Die ID ist unbekannt, oder der gewählte Speicherdienst trägt nicht die Fähigkeit asset-storage. Korrigieren Sie die Auswahl des Schritts oder registrieren Sie den Dienst und starten Sie erneut.",
5130
5156
  "binary_output_generator_invalid": "Ein Schritt, der Binärausgaben erzeugt, wählt eine generative Integration aus, die diese Installation nicht registriert, oder keine der gewählten Integrationen erzeugt einen Inhaltstyp, den der Schritt liefern muss. Generative Integrationen werden im Code der Installation registriert, nicht in diesem Workspace: registrieren Sie sie oder korrigieren Sie die Auswahl des Schritts und starten Sie erneut.",
5131
- "foundational_service_not_inherited": "Abwählen gilt für einen vom Konto geerbten Dienst. Diese ID ist von diesem Board registriert, es gibt also nichts abzuwählen - lösche stattdessen den eigenen Eintrag des Boards."
5157
+ "foundational_service_not_inherited": "Abwählen gilt für einen vom Konto geerbten Dienst. Diese ID ist von diesem Board registriert, es gibt also nichts abzuwählen - lösche stattdessen den eigenen Eintrag des Boards.",
5158
+ "ticket_already_linked": "Ein Ticket kann nur eine Aufgabe stützen. Es erneut zu verknüpfen würde der bestehenden Aufgabe genau den Kontext entziehen, mit dem sie angelegt wurde. Öffne stattdessen diese Aufgabe oder hebe die Verknüpfung des Tickets zuerst auf."
5132
5159
  },
5133
5160
  "action": {
5134
5161
  "connectGitHub": "GitHub verbinden",
@@ -5948,7 +5975,7 @@
5948
5975
  "newDescription": "Neue integrierte Modell-Presets sind verfügbar. Füge sie zur Bibliothek dieses Boards hinzu.",
5949
5976
  "add": "Hinzufügen",
5950
5977
  "updatesHeading": "Updates verfügbar",
5951
- "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).",
5978
+ "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, eine eigene Routen-Reihenfolge auf dem Preset wird jedoch zurückgesetzt.",
5952
5979
  "versionAvailable": "Version {from} → {to} verfügbar.",
5953
5980
  "reseed": "Erneut seeden",
5954
5981
  "reseedAll": "Alle aktualisieren ({count})",
@@ -622,7 +622,8 @@
622
622
  "foundational_service_exists": "Foundational service already exists",
623
623
  "binary_output_service_invalid": "Binary output service can't be resolved",
624
624
  "binary_output_generator_invalid": "Binary output generator can't be resolved",
625
- "foundational_service_not_inherited": "This board registered that service"
625
+ "foundational_service_not_inherited": "This board registered that service",
626
+ "ticket_already_linked": "This issue already has a task"
626
627
  },
627
628
  "description": {
628
629
  "dependencies_unmet": "This task depends on others that aren't finished yet. Complete or unblock them, then start it again.",
@@ -657,7 +658,8 @@
657
658
  "foundational_service_exists": "A foundational service with this id is already registered at this scope. Open the existing entry and edit it — two services cannot share an id, because the id is what an architect names in its design.",
658
659
  "binary_output_service_invalid": "A step that generates binary outputs selects a foundational service this workspace's catalog can't resolve: the id is unknown, or the chosen storage service doesn't carry the asset-storage capability. Fix the step's selection or register the service, then start again.",
659
660
  "binary_output_generator_invalid": "A step that generates binary outputs selects a generative integration this deployment doesn't register, or none of the selected integrations produces a content type the step must deliver. Generative integrations are registered in the deployment's code, not in this workspace: register it or fix the step's selection, then start again.",
660
- "foundational_service_not_inherited": "Opting out applies to a service inherited from the account. This id is registered by this board, so there is nothing to opt out of - delete the board's own entry instead."
661
+ "foundational_service_not_inherited": "Opting out applies to a service inherited from the account. This id is registered by this board, so there is nothing to opt out of - delete the board's own entry instead.",
662
+ "ticket_already_linked": "An issue can back only one task, so linking it again would strip the existing task of the context it was created with. Open that task instead, or unlink the issue first."
661
663
  },
662
664
  "action": {
663
665
  "connectGitHub": "Connect GitHub",
@@ -2416,7 +2418,8 @@
2416
2418
  "deleteTitle": "Delete preset",
2417
2419
  "basePrefix": "Base:",
2418
2420
  "overrideCount": "{count} override | {count} overrides",
2419
- "empty": "No presets yet. Create one to map models to your agents."
2421
+ "empty": "No presets yet. Create one to map models to your agents.",
2422
+ "customRouteOrder": "custom route order"
2420
2423
  },
2421
2424
  "editor": {
2422
2425
  "useBaseModel": "Use base model",
@@ -2437,6 +2440,30 @@
2437
2440
  "nameRequiredTitle": "Name required",
2438
2441
  "nameRequiredBody": "Give the preset a name.",
2439
2442
  "saveFailed": "Could not save the preset"
2443
+ },
2444
+ "routes": {
2445
+ "direct": "The model's own provider API",
2446
+ "bedrock": "AWS Bedrock",
2447
+ "openrouter": "OpenRouter gateway",
2448
+ "cloudflare": "Cloudflare Workers AI",
2449
+ "subscription": "Subscription (Claude Code / Codex)"
2450
+ },
2451
+ "routeHints": {
2452
+ "direct": "Needs an API key for that provider.",
2453
+ "bedrock": "Runs in your AWS Region, on the models your account allows.",
2454
+ "openrouter": "One key, many vendors, resold at a margin.",
2455
+ "cloudflare": "Always available, no prompt caching.",
2456
+ "subscription": "Flat-rate quota on a connected plan."
2457
+ },
2458
+ "routeOrder": {
2459
+ "label": "Route order",
2460
+ "defaultHint": "This preset follows the deployment default. Move a route up to prefer it instead.",
2461
+ "customHint": "This preset prefers routes in this order. A model reachable only on a lower route still runs, it is just tried later.",
2462
+ "reset": "Use default order",
2463
+ "moveUp": "Prefer this route more",
2464
+ "moveDown": "Prefer this route less",
2465
+ "subscriptionOverrideHint": "A connected subscription still wins for models that offer one: the engine routes those to the plan regardless of this order. Routes below the subscription entry apply to every other model.",
2466
+ "badgesUseDefaultPresetHint": "The route shown next to each model above is the one the workspace default preset takes, not this preset's."
2440
2467
  }
2441
2468
  },
2442
2469
  "account": {
@@ -4562,7 +4589,7 @@
4562
4589
  "newDescription": "New built-in model presets have shipped. Add them to this board's library.",
4563
4590
  "add": "Add",
4564
4591
  "updatesHeading": "Updates available",
4565
- "updatesDescription": "A newer version of these built-in model presets has shipped. Reseed to adopt it (the default and ordering are kept).",
4592
+ "updatesDescription": "A newer version of these built-in model presets has shipped. Reseed to adopt it: the default and ordering are kept, but a custom route order on the preset is cleared.",
4566
4593
  "versionAvailable": "Version {from} → {to} available.",
4567
4594
  "reseed": "Reseed",
4568
4595
  "reseedAll": "Update all ({count})",
@@ -565,7 +565,8 @@
565
565
  "foundational_service_exists": "El servicio fundacional ya existe",
566
566
  "binary_output_service_invalid": "No se puede resolver el servicio de salidas binarias",
567
567
  "binary_output_generator_invalid": "No se puede resolver el generador de salidas binarias",
568
- "foundational_service_not_inherited": "Este tablero registró ese servicio"
568
+ "foundational_service_not_inherited": "Este tablero registró ese servicio",
569
+ "ticket_already_linked": "Esta incidencia ya tiene una tarea"
569
570
  },
570
571
  "description": {
571
572
  "dependencies_unmet": "Esta tarea depende de otras que aún no están terminadas. Complétalas o desbloquéalas y vuelve a iniciarla.",
@@ -597,7 +598,8 @@
597
598
  "foundational_service_exists": "Ya hay un servicio fundacional con este identificador registrado en este ámbito. Abre la entrada existente y edítala: dos servicios no pueden compartir un identificador, porque es el nombre que un arquitecto usa en su diseño.",
598
599
  "binary_output_service_invalid": "Un paso que genera salidas binarias selecciona un servicio fundacional que el catálogo de este espacio de trabajo no puede resolver: el identificador es desconocido, o el servicio de almacenamiento elegido no tiene la capacidad asset-storage. Corrige la selección del paso o registra el servicio y vuelve a iniciarlo.",
599
600
  "binary_output_generator_invalid": "Un paso que genera salidas binarias selecciona una integración generativa que esta instalación no registra, o ninguna de las integraciones seleccionadas produce un tipo de contenido que el paso debe entregar. Las integraciones generativas se registran en el código de la instalación, no en este espacio de trabajo: regístrala o corrige la selección del paso y vuelve a iniciar.",
600
- "foundational_service_not_inherited": "La exclusión se aplica a un servicio heredado de la cuenta. Este id está registrado por este tablero, así que no hay nada que excluir: elimina la entrada propia del tablero."
601
+ "foundational_service_not_inherited": "La exclusión se aplica a un servicio heredado de la cuenta. Este id está registrado por este tablero, así que no hay nada que excluir: elimina la entrada propia del tablero.",
602
+ "ticket_already_linked": "Una incidencia solo puede respaldar una tarea, así que volver a vincularla dejaría a la tarea existente sin el contexto con el que se creó. Abre esa tarea o desvincula antes la incidencia."
601
603
  },
602
604
  "action": {
603
605
  "connectGitHub": "Conectar GitHub",
@@ -2330,7 +2332,8 @@
2330
2332
  "deleteTitle": "Eliminar ajuste",
2331
2333
  "basePrefix": "Base:",
2332
2334
  "overrideCount": "{count} anulación | {count} anulaciones",
2333
- "empty": "Aún no hay ajustes. Crea uno para asignar modelos a tus agentes."
2335
+ "empty": "Aún no hay ajustes. Crea uno para asignar modelos a tus agentes.",
2336
+ "customRouteOrder": "orden de rutas personalizado"
2334
2337
  },
2335
2338
  "editor": {
2336
2339
  "useBaseModel": "Usar el modelo base",
@@ -2355,6 +2358,30 @@
2355
2358
  "confirmDelete": {
2356
2359
  "title": "¿Eliminar este preset de modelo?",
2357
2360
  "body": "Se eliminará \"{name}\". Las tareas que lo usan volverán al valor predeterminado del espacio de trabajo."
2361
+ },
2362
+ "routes": {
2363
+ "direct": "La API propia del proveedor del modelo",
2364
+ "bedrock": "AWS Bedrock",
2365
+ "openrouter": "Pasarela OpenRouter",
2366
+ "cloudflare": "Cloudflare Workers AI",
2367
+ "subscription": "Suscripción (Claude Code / Codex)"
2368
+ },
2369
+ "routeHints": {
2370
+ "direct": "Requiere una clave de API para ese proveedor.",
2371
+ "bedrock": "Se ejecuta en tu región de AWS, con los modelos que tu cuenta permite.",
2372
+ "openrouter": "Una clave, muchos proveedores, revendidos con margen.",
2373
+ "cloudflare": "Siempre disponible, sin caché de prompts.",
2374
+ "subscription": "Cuota de tarifa plana en un plan conectado."
2375
+ },
2376
+ "routeOrder": {
2377
+ "label": "Orden de rutas",
2378
+ "defaultHint": "Este preajuste sigue el valor predeterminado del despliegue. Sube una ruta para preferirla.",
2379
+ "customHint": "Este preajuste prefiere las rutas en este orden. Un modelo accesible solo por una ruta inferior sigue funcionando, solo se intenta más tarde.",
2380
+ "reset": "Usar el orden predeterminado",
2381
+ "moveUp": "Preferir más esta ruta",
2382
+ "moveDown": "Preferir menos esta ruta",
2383
+ "subscriptionOverrideHint": "Una suscripción conectada sigue ganando en los modelos que ofrecen una: el motor los enruta al plan sin importar este orden. Las rutas por debajo de la entrada de suscripción se aplican a todos los demás modelos.",
2384
+ "badgesUseDefaultPresetHint": "La ruta que aparece junto a cada modelo arriba es la del preset predeterminado del espacio de trabajo, no la de este preset."
2358
2385
  }
2359
2386
  },
2360
2387
  "account": {
@@ -5802,7 +5829,7 @@
5802
5829
  "newDescription": "Hay nuevos presets de modelo integrados. Añádelos a la biblioteca de este tablero.",
5803
5830
  "add": "Añadir",
5804
5831
  "updatesHeading": "Actualizaciones disponibles",
5805
- "updatesDescription": "Hay una versión más reciente de estos presets de modelo integrados. Regenera para adoptarla (se conservan el predeterminado y el orden).",
5832
+ "updatesDescription": "Hay una versión más reciente de estos presets de modelo integrados. Regenera para adoptarla: se conservan el predeterminado y el orden, pero se borra un orden de rutas personalizado del preset.",
5806
5833
  "versionAvailable": "Versión {from} → {to} disponible.",
5807
5834
  "reseed": "Regenerar",
5808
5835
  "reseedAll": "Actualizar todos ({count})",
@@ -565,7 +565,8 @@
565
565
  "foundational_service_exists": "Ce service fondamental existe déjà",
566
566
  "binary_output_service_invalid": "Service des sorties binaires introuvable",
567
567
  "binary_output_generator_invalid": "Générateur de sorties binaires introuvable",
568
- "foundational_service_not_inherited": "Ce tableau a enregistré ce service"
568
+ "foundational_service_not_inherited": "Ce tableau a enregistré ce service",
569
+ "ticket_already_linked": "Ce ticket a déjà une tâche"
569
570
  },
570
571
  "description": {
571
572
  "dependencies_unmet": "Cette tâche dépend d'autres qui ne sont pas encore terminées. Terminez-les ou débloquez-les, puis relancez-la.",
@@ -597,7 +598,8 @@
597
598
  "foundational_service_exists": "Un service fondamental portant cet identifiant est déjà enregistré dans cette portée. Ouvrez l’entrée existante et modifiez-la : deux services ne peuvent pas partager un identifiant, car c’est le nom qu’un architecte emploie dans sa conception.",
598
599
  "binary_output_service_invalid": "Une étape qui génère des sorties binaires sélectionne un service fondamental que le catalogue de cet espace de travail ne peut pas résoudre : l’identifiant est inconnu, ou le service de stockage choisi ne porte pas la capacité asset-storage. Corrigez la sélection de l’étape ou enregistrez le service, puis relancez.",
599
600
  "binary_output_generator_invalid": "Une étape qui génère des sorties binaires sélectionne une intégration générative que ce déploiement n’enregistre pas, ou aucune des intégrations sélectionnées ne produit un type de contenu que l’étape doit livrer. Les intégrations génératives sont enregistrées dans le code du déploiement, pas dans cet espace de travail : enregistrez-la ou corrigez la sélection de l’étape, puis relancez.",
600
- "foundational_service_not_inherited": "L'écartement s'applique à un service hérité du compte. Cet identifiant est enregistré par ce tableau : il n'y a donc rien à écarter - supprimez plutôt l'entrée propre au tableau."
601
+ "foundational_service_not_inherited": "L'écartement s'applique à un service hérité du compte. Cet identifiant est enregistré par ce tableau : il n'y a donc rien à écarter - supprimez plutôt l'entrée propre au tableau.",
602
+ "ticket_already_linked": "Un ticket ne peut alimenter qu'une seule tâche : le relier à nouveau priverait la tâche existante du contexte avec lequel elle a été créée. Ouvrez plutôt cette tâche, ou dissociez d'abord le ticket."
601
603
  },
602
604
  "action": {
603
605
  "connectGitHub": "Connecter GitHub",
@@ -2330,7 +2332,8 @@
2330
2332
  "deleteTitle": "Supprimer le préréglage",
2331
2333
  "basePrefix": "Base :",
2332
2334
  "overrideCount": "{count} remplacement | {count} remplacements",
2333
- "empty": "Aucun préréglage pour l'instant. Créez-en un pour associer des modèles à vos agents."
2335
+ "empty": "Aucun préréglage pour l'instant. Créez-en un pour associer des modèles à vos agents.",
2336
+ "customRouteOrder": "ordre de routes personnalisé"
2334
2337
  },
2335
2338
  "editor": {
2336
2339
  "useBaseModel": "Utiliser le modèle de base",
@@ -2355,6 +2358,30 @@
2355
2358
  "confirmDelete": {
2356
2359
  "title": "Supprimer ce préréglage de modèle ?",
2357
2360
  "body": "\"{name}\" sera supprimé. Les tâches qui l'utilisent reviendront au préréglage par défaut de l'espace de travail."
2361
+ },
2362
+ "routes": {
2363
+ "direct": "L'API propre du fournisseur du modèle",
2364
+ "bedrock": "AWS Bedrock",
2365
+ "openrouter": "Passerelle OpenRouter",
2366
+ "cloudflare": "Cloudflare Workers AI",
2367
+ "subscription": "Abonnement (Claude Code / Codex)"
2368
+ },
2369
+ "routeHints": {
2370
+ "direct": "Nécessite une clé API pour ce fournisseur.",
2371
+ "bedrock": "Exécuté dans votre région AWS, sur les modèles autorisés par votre compte.",
2372
+ "openrouter": "Une clé, plusieurs fournisseurs, revendus avec une marge.",
2373
+ "cloudflare": "Toujours disponible, sans mise en cache des prompts.",
2374
+ "subscription": "Quota forfaitaire d'un forfait connecté."
2375
+ },
2376
+ "routeOrder": {
2377
+ "label": "Ordre des routes",
2378
+ "defaultHint": "Ce préréglage suit la valeur par défaut du déploiement. Remontez une route pour la préférer.",
2379
+ "customHint": "Ce préréglage préfère les routes dans cet ordre. Un modèle accessible uniquement par une route inférieure fonctionne toujours, il est simplement essayé plus tard.",
2380
+ "reset": "Utiliser l'ordre par défaut",
2381
+ "moveUp": "Préférer davantage cette route",
2382
+ "moveDown": "Préférer moins cette route",
2383
+ "subscriptionOverrideHint": "Un abonnement connecté l'emporte toujours pour les modèles qui en proposent un : le moteur les achemine vers le forfait quel que soit cet ordre. Les routes situées sous l'entrée abonnement s'appliquent à tous les autres modèles.",
2384
+ "badgesUseDefaultPresetHint": "La route affichée à côté de chaque modèle ci-dessus est celle du preset par défaut de l'espace de travail, pas celle de ce preset."
2358
2385
  }
2359
2386
  },
2360
2387
  "account": {
@@ -5802,7 +5829,7 @@
5802
5829
  "newDescription": "De nouveaux presets de modèle intégrés sont arrivés. Ajoutez-les à la bibliothèque de ce tableau.",
5803
5830
  "add": "Ajouter",
5804
5831
  "updatesHeading": "Mises à jour disponibles",
5805
- "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).",
5832
+ "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, mais un ordre de routes personnalisé sur le preset est effacé.",
5806
5833
  "versionAvailable": "Version {from} → {to} disponible.",
5807
5834
  "reseed": "Régénérer",
5808
5835
  "reseedAll": "Tout mettre à jour ({count})",
@@ -565,7 +565,8 @@
565
565
  "foundational_service_exists": "שירות תשתית כזה כבר קיים",
566
566
  "binary_output_service_invalid": "לא ניתן לזהות את השירות לפלט בינארי",
567
567
  "binary_output_generator_invalid": "לא ניתן לזהות את מחולל הפלט הבינארי",
568
- "foundational_service_not_inherited": "הלוח הזה רשם את השירות"
568
+ "foundational_service_not_inherited": "הלוח הזה רשם את השירות",
569
+ "ticket_already_linked": "לכרטיס הזה כבר יש משימה"
569
570
  },
570
571
  "description": {
571
572
  "dependencies_unmet": "משימה זו תלויה במשימות אחרות שטרם הושלמו. השלם או שחרר אותן, ולאחר מכן הפעל אותה שוב.",
@@ -597,7 +598,8 @@
597
598
  "foundational_service_exists": "שירות תשתית עם מזהה זה כבר רשום בהיקף הזה. פתחו את הרשומה הקיימת וערכו אותה — שני שירותים אינם יכולים לחלוק מזהה, מפני שהמזהה הוא השם שארכיטקט מציין בתכנון שלו.",
598
599
  "binary_output_service_invalid": "שלב שמייצר פלט בינארי בוחר שירות תשתית שהקטלוג של סביבת העבודה אינו יכול לזהות: המזהה אינו מוכר, או ששירות האחסון שנבחר אינו נושא את היכולת asset-storage. תקנו את הבחירה בשלב או רשמו את השירות, ואז התחילו מחדש.",
599
600
  "binary_output_generator_invalid": "שלב שמייצר פלט בינארי בוחר אינטגרציה גנרטיבית שהפריסה הזו אינה רושמת, או שאף אחת מהאינטגרציות שנבחרו אינה מייצרת סוג תוכן שהשלב אמור לספק. אינטגרציות גנרטיביות נרשמות בקוד של הפריסה ולא במרחב העבודה הזה: רשמו אותה או תקנו את הבחירה בשלב, ואז התחילו מחדש.",
600
- "foundational_service_not_inherited": "החרגה חלה על שירות שנורש מהחשבון. המזהה הזה רשום על ידי הלוח הזה, ולכן אין מה להחריג - מחקו במקום זאת את הרשומה של הלוח עצמו."
601
+ "foundational_service_not_inherited": "החרגה חלה על שירות שנורש מהחשבון. המזהה הזה רשום על ידי הלוח הזה, ולכן אין מה להחריג - מחקו במקום זאת את הרשומה של הלוח עצמו.",
602
+ "ticket_already_linked": "כרטיס יכול לגבות משימה אחת בלבד, ולכן קישור נוסף שלו ישלול מהמשימה הקיימת את ההקשר שאיתו נוצרה. פתחו את המשימה הזו במקום זאת, או בטלו קודם את קישור הכרטיס."
601
603
  },
602
604
  "action": {
603
605
  "connectGitHub": "חבר את GitHub",
@@ -2330,7 +2332,8 @@
2330
2332
  "deleteTitle": "מחיקת תצורה",
2331
2333
  "basePrefix": "בסיס:",
2332
2334
  "overrideCount": "עקיפה אחת | {count} עקיפות",
2333
- "empty": "אין עדיין תצורות. צור אחת כדי למפות מודלים לסוכנים שלך."
2335
+ "empty": "אין עדיין תצורות. צור אחת כדי למפות מודלים לסוכנים שלך.",
2336
+ "customRouteOrder": "סדר מסלולים מותאם"
2334
2337
  },
2335
2338
  "editor": {
2336
2339
  "useBaseModel": "השתמש במודל הבסיס",
@@ -2355,6 +2358,30 @@
2355
2358
  "confirmDelete": {
2356
2359
  "title": "למחוק את קדם־הגדרת המודל הזו?",
2357
2360
  "body": "\"{name}\" יימחק. משימות המשתמשות בו יחזרו לברירת המחדל של סביבת העבודה."
2361
+ },
2362
+ "routes": {
2363
+ "direct": "ה-API של ספק המודל עצמו",
2364
+ "bedrock": "AWS Bedrock",
2365
+ "openrouter": "שער OpenRouter",
2366
+ "cloudflare": "Cloudflare Workers AI",
2367
+ "subscription": "מנוי (Claude Code / Codex)"
2368
+ },
2369
+ "routeHints": {
2370
+ "direct": "נדרש מפתח API לספק הזה.",
2371
+ "bedrock": "פועל באזור ה-AWS שלך, על המודלים שהחשבון שלך מתיר.",
2372
+ "openrouter": "מפתח אחד, ספקים רבים, נמכרים מחדש בתוספת רווח.",
2373
+ "cloudflare": "זמין תמיד, בלי מטמון הנחיות.",
2374
+ "subscription": "מכסה בתעריף אחיד בתוכנית מחוברת."
2375
+ },
2376
+ "routeOrder": {
2377
+ "label": "סדר המסלולים",
2378
+ "defaultHint": "התצורה הזו פועלת לפי ברירת המחדל של הפריסה. העלה מסלול כדי להעדיף אותו.",
2379
+ "customHint": "התצורה הזו מעדיפה את המסלולים בסדר הזה. מודל שנגיש רק במסלול נמוך יותר עדיין ירוץ, פשוט ינוסה מאוחר יותר.",
2380
+ "reset": "השתמש בסדר ברירת המחדל",
2381
+ "moveUp": "העדף את המסלול הזה יותר",
2382
+ "moveDown": "העדף את המסלול הזה פחות",
2383
+ "subscriptionOverrideHint": "מנוי מחובר עדיין גובר עבור מודלים שמציעים אחד: המנוע מנתב אותם לתוכנית ללא קשר לסדר הזה. המסלולים שמתחת לרשומת המנוי חלים על כל שאר המודלים.",
2384
+ "badgesUseDefaultPresetHint": "המסלול שמוצג ליד כל מודל למעלה הוא זה של תצורת ברירת המחדל של סביבת העבודה, לא של התצורה הזו."
2358
2385
  }
2359
2386
  },
2360
2387
  "account": {
@@ -5813,7 +5840,7 @@
5813
5840
  "newDescription": "הגיעו קביעות מודל מובנות חדשות. הוסף אותן לספריית הלוח הזה.",
5814
5841
  "add": "הוסף",
5815
5842
  "updatesHeading": "עדכונים זמינים",
5816
- "updatesDescription": "גרסה חדשה יותר של קביעות המודל המובנות האלה הגיעה. זרע מחדש כדי לאמץ אותה (ברירת המחדל והסדר נשמרים).",
5843
+ "updatesDescription": "גרסה חדשה יותר של קביעות המודל המובנות האלה הגיעה. זרע מחדש כדי לאמץ אותה: ברירת המחדל והסדר נשמרים, אך סדר מסלולים מותאם שנשמר בתצורה יימחק.",
5817
5844
  "versionAvailable": "גרסה {from} → {to} זמינה.",
5818
5845
  "reseed": "זרע מחדש",
5819
5846
  "reseedAll": "עדכן הכל ({count})",
@@ -22,7 +22,8 @@
22
22
  "deleteTitle": "Elimina preset",
23
23
  "basePrefix": "Base:",
24
24
  "overrideCount": "{count} override | {count} override",
25
- "empty": "Ancora nessun preset. Creane uno per associare i modelli ai tuoi agenti."
25
+ "empty": "Ancora nessun preset. Creane uno per associare i modelli ai tuoi agenti.",
26
+ "customRouteOrder": "ordine rotte personalizzato"
26
27
  },
27
28
  "editor": {
28
29
  "useBaseModel": "Usa il modello di base",
@@ -43,6 +44,30 @@
43
44
  "nameRequiredTitle": "Nome obbligatorio",
44
45
  "nameRequiredBody": "Assegna un nome al preset.",
45
46
  "saveFailed": "Impossibile salvare il preset"
47
+ },
48
+ "routes": {
49
+ "direct": "L'API propria del fornitore del modello",
50
+ "bedrock": "AWS Bedrock",
51
+ "openrouter": "Gateway OpenRouter",
52
+ "cloudflare": "Cloudflare Workers AI",
53
+ "subscription": "Abbonamento (Claude Code / Codex)"
54
+ },
55
+ "routeHints": {
56
+ "direct": "Richiede una chiave API per quel fornitore.",
57
+ "bedrock": "Viene eseguito nella tua region AWS, sui modelli consentiti dal tuo account.",
58
+ "openrouter": "Una chiave, molti fornitori, rivenduti con un margine.",
59
+ "cloudflare": "Sempre disponibile, senza cache dei prompt.",
60
+ "subscription": "Quota forfettaria su un piano collegato."
61
+ },
62
+ "routeOrder": {
63
+ "label": "Ordine delle rotte",
64
+ "defaultHint": "Questo preset segue il valore predefinito del deployment. Sposta una rotta in alto per preferirla.",
65
+ "customHint": "Questo preset preferisce le rotte in questo ordine. Un modello raggiungibile solo su una rotta inferiore funziona comunque, viene solo provato dopo.",
66
+ "reset": "Usa l'ordine predefinito",
67
+ "moveUp": "Preferisci di più questa rotta",
68
+ "moveDown": "Preferisci di meno questa rotta",
69
+ "subscriptionOverrideHint": "Un abbonamento collegato prevale comunque sui modelli che ne offrono uno: il motore li instrada al piano indipendentemente da questo ordine. Le rotte sotto la voce abbonamento si applicano a tutti gli altri modelli.",
70
+ "badgesUseDefaultPresetHint": "La rotta mostrata accanto a ogni modello qui sopra è quella del preset predefinito dello spazio di lavoro, non di questo preset."
46
71
  }
47
72
  },
48
73
  "account": {
@@ -5096,7 +5121,8 @@
5096
5121
  "foundational_service_exists": "Il servizio fondamentale esiste già",
5097
5122
  "binary_output_service_invalid": "Impossibile risolvere il servizio per gli output binari",
5098
5123
  "binary_output_generator_invalid": "Impossibile risolvere il generatore per gli output binari",
5099
- "foundational_service_not_inherited": "Questa bacheca ha registrato quel servizio"
5124
+ "foundational_service_not_inherited": "Questa bacheca ha registrato quel servizio",
5125
+ "ticket_already_linked": "Questo ticket ha già un'attività"
5100
5126
  },
5101
5127
  "description": {
5102
5128
  "dependencies_unmet": "Questa attività dipende da altre non ancora completate. Completale o sbloccale, poi avviala di nuovo.",
@@ -5128,7 +5154,8 @@
5128
5154
  "foundational_service_exists": "Un servizio fondamentale con questo identificatore è già registrato in questo ambito. Apri la voce esistente e modificala: due servizi non possono condividere un identificatore, perché è il nome che un architetto indica nella sua progettazione.",
5129
5155
  "binary_output_service_invalid": "Un passaggio che genera output binari seleziona un servizio fondamentale che il catalogo di questo workspace non riesce a risolvere: l'identificatore è sconosciuto, oppure il servizio di archiviazione scelto non ha la capacità asset-storage. Correggi la selezione del passaggio o registra il servizio, poi riavvia.",
5130
5156
  "binary_output_generator_invalid": "Un passaggio che genera output binari seleziona un'integrazione generativa che questa installazione non registra, oppure nessuna delle integrazioni selezionate produce un tipo di contenuto che il passaggio deve consegnare. Le integrazioni generative si registrano nel codice dell'installazione, non in questo workspace: registrala o correggi la selezione del passaggio, poi riavvia.",
5131
- "foundational_service_not_inherited": "L'esclusione vale per un servizio ereditato dall'account. Questo id è registrato da questa bacheca, quindi non c'è nulla da escludere: elimina invece la voce propria della bacheca."
5157
+ "foundational_service_not_inherited": "L'esclusione vale per un servizio ereditato dall'account. Questo id è registrato da questa bacheca, quindi non c'è nulla da escludere: elimina invece la voce propria della bacheca.",
5158
+ "ticket_already_linked": "Un ticket può sostenere una sola attività, quindi ricollegarlo toglierebbe all'attività esistente il contesto con cui è stata creata. Apri invece quell'attività, oppure scollega prima il ticket."
5132
5159
  },
5133
5160
  "action": {
5134
5161
  "connectGitHub": "Collega GitHub",
@@ -5948,7 +5975,7 @@
5948
5975
  "newDescription": "Sono stati rilasciati nuovi preset di modello integrati. Aggiungili alla libreria di questa board.",
5949
5976
  "add": "Aggiungi",
5950
5977
  "updatesHeading": "Aggiornamenti disponibili",
5951
- "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).",
5978
+ "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, ma un ordine delle rotte personalizzato sul preset viene azzerato.",
5952
5979
  "versionAvailable": "Versione {from} → {to} disponibile.",
5953
5980
  "reseed": "Ripristina",
5954
5981
  "reseedAll": "Aggiorna tutti ({count})",
@@ -565,7 +565,8 @@
565
565
  "foundational_service_exists": "その基盤サービスはすでに存在します",
566
566
  "binary_output_service_invalid": "バイナリ出力用のサービスを解決できません",
567
567
  "binary_output_generator_invalid": "バイナリ出力のジェネレーターを解決できません",
568
- "foundational_service_not_inherited": "このボードが登録したサービスです"
568
+ "foundational_service_not_inherited": "このボードが登録したサービスです",
569
+ "ticket_already_linked": "この課題にはすでにタスクがあります"
569
570
  },
570
571
  "description": {
571
572
  "dependencies_unmet": "このタスクは、まだ完了していない他のタスクに依存しています。それらを完了または解除してから、もう一度開始してください。",
@@ -597,7 +598,8 @@
597
598
  "foundational_service_exists": "この ID の基盤サービスはこのスコープにすでに登録されています。既存のエントリを開いて編集してください。ID は設計でアーキテクトが指定する名前なので、2 つのサービスが同じ ID を共有することはできません。",
598
599
  "binary_output_service_invalid": "バイナリ出力を生成するステップが、このワークスペースのカタログでは解決できない基盤サービスを選択しています。ID が不明か、選択した保存先サービスに asset-storage ケイパビリティがありません。ステップの選択を修正するかサービスを登録して、もう一度開始してください。",
599
600
  "binary_output_generator_invalid": "バイナリ出力を生成するステップが、このデプロイメントに登録されていない生成インテグレーションを選択しているか、選択されたインテグレーションのいずれもステップが提供すべきコンテンツタイプを生成できません。生成インテグレーションはこのワークスペースではなくデプロイメントのコードに登録します。登録するかステップの選択を修正して、もう一度開始してください。",
600
- "foundational_service_not_inherited": "除外はアカウントから継承したサービスに対する操作です。この ID はこのボード自身が登録しているため、除外するものがありません。代わりにボード自身のエントリを削除してください。"
601
+ "foundational_service_not_inherited": "除外はアカウントから継承したサービスに対する操作です。この ID はこのボード自身が登録しているため、除外するものがありません。代わりにボード自身のエントリを削除してください。",
602
+ "ticket_already_linked": "1 つの課題が支えられるタスクは 1 つだけです。もう一度リンクすると、既存のタスクは作成時の文脈を失います。代わりにそのタスクを開くか、先に課題のリンクを解除してください。"
601
603
  },
602
604
  "action": {
603
605
  "connectGitHub": "GitHub に接続",
@@ -2330,7 +2332,8 @@
2330
2332
  "deleteTitle": "プリセットを削除",
2331
2333
  "basePrefix": "ベース:",
2332
2334
  "overrideCount": "{count} 件の上書き | {count} 件の上書き",
2333
- "empty": "プリセットはまだありません。1 つ作成してモデルをエージェントに割り当てましょう。"
2335
+ "empty": "プリセットはまだありません。1 つ作成してモデルをエージェントに割り当てましょう。",
2336
+ "customRouteOrder": "ルート順をカスタム設定"
2334
2337
  },
2335
2338
  "editor": {
2336
2339
  "useBaseModel": "ベースモデルを使用",
@@ -2355,6 +2358,30 @@
2355
2358
  "confirmDelete": {
2356
2359
  "title": "このモデルプリセットを削除しますか?",
2357
2360
  "body": "「{name}」が削除されます。使用中のタスクはワークスペースの既定に戻ります。"
2361
+ },
2362
+ "routes": {
2363
+ "direct": "モデル自身のプロバイダー API",
2364
+ "bedrock": "AWS Bedrock",
2365
+ "openrouter": "OpenRouter ゲートウェイ",
2366
+ "cloudflare": "Cloudflare Workers AI",
2367
+ "subscription": "サブスクリプション(Claude Code / Codex)"
2368
+ },
2369
+ "routeHints": {
2370
+ "direct": "そのプロバイダーの API キーが必要です。",
2371
+ "bedrock": "お使いの AWS リージョンで、アカウントが許可したモデルのみ実行します。",
2372
+ "openrouter": "1 つのキーで多数のベンダーに接続でき、手数料が上乗せされます。",
2373
+ "cloudflare": "常に利用できますが、プロンプトキャッシュはありません。",
2374
+ "subscription": "接続済みプランの定額枠を使用します。"
2375
+ },
2376
+ "routeOrder": {
2377
+ "label": "ルートの優先順",
2378
+ "defaultHint": "このプリセットはデプロイの既定順に従います。優先したいルートを上に移動してください。",
2379
+ "customHint": "このプリセットはこの順でルートを優先します。下位のルートしか使えないモデルも実行されますが、試されるのが後になります。",
2380
+ "reset": "既定の順序を使う",
2381
+ "moveUp": "このルートの優先度を上げる",
2382
+ "moveDown": "このルートの優先度を下げる",
2383
+ "subscriptionOverrideHint": "サブスクリプションを持つモデルでは、接続済みのプランが引き続き優先されます。この順序に関わらず、エンジンはそれらをプランに振り分けます。サブスクリプションより下のルートは、それ以外のすべてのモデルに適用されます。",
2384
+ "badgesUseDefaultPresetHint": "上の各モデルの横に表示されているルートは、ワークスペース既定のプリセットのものであり、このプリセットのものではありません。"
2358
2385
  }
2359
2386
  },
2360
2387
  "account": {
@@ -5814,7 +5841,7 @@
5814
5841
  "newDescription": "新しい組み込みモデルプリセットが追加されました。このボードのライブラリに追加してください。",
5815
5842
  "add": "追加",
5816
5843
  "updatesHeading": "更新あり",
5817
- "updatesDescription": "これらの組み込みモデルプリセットの新しいバージョンがあります。再シードして取り込みます(既定と並び順は保持されます)。",
5844
+ "updatesDescription": "これらの組み込みモデルプリセットの新しいバージョンがあります。再シードして取り込みます。既定と並び順は保持されますが、プリセットに設定したカスタムのルート順はクリアされます。",
5818
5845
  "versionAvailable": "バージョン {from} → {to} が利用可能です。",
5819
5846
  "reseed": "再シード",
5820
5847
  "reseedAll": "すべて更新 ({count})",
@@ -565,7 +565,8 @@
565
565
  "foundational_service_exists": "Usługa fundamentalna już istnieje",
566
566
  "binary_output_service_invalid": "Nie można rozpoznać usługi dla wyników binarnych",
567
567
  "binary_output_generator_invalid": "Nie można rozpoznać generatora danych binarnych",
568
- "foundational_service_not_inherited": "Ta tablica zarejestrowała tę usługę"
568
+ "foundational_service_not_inherited": "Ta tablica zarejestrowała tę usługę",
569
+ "ticket_already_linked": "To zgłoszenie ma już zadanie"
569
570
  },
570
571
  "description": {
571
572
  "dependencies_unmet": "To zadanie zależy od innych, które nie zostały jeszcze ukończone. Ukończ je lub odblokuj, a następnie uruchom je ponownie.",
@@ -597,7 +598,8 @@
597
598
  "foundational_service_exists": "Usługa fundamentalna o tym identyfikatorze jest już zarejestrowana w tym zakresie. Otwórz istniejący wpis i go edytuj — dwie usługi nie mogą współdzielić identyfikatora, ponieważ to jego nazwą architekt posługuje się w projekcie.",
598
599
  "binary_output_service_invalid": "Krok generujący wyniki binarne wybiera usługę fundamentalną, której katalog tego obszaru roboczego nie może rozpoznać: identyfikator jest nieznany albo wybrana usługa przechowywania nie ma zdolności asset-storage. Popraw wybór w kroku lub zarejestruj usługę, a następnie uruchom ponownie.",
599
600
  "binary_output_generator_invalid": "Krok generujący dane binarne wybiera integrację generatywną, której to wdrożenie nie rejestruje, albo żadna z wybranych integracji nie tworzy typu treści, który krok ma dostarczyć. Integracje generatywne rejestruje się w kodzie wdrożenia, a nie w tej przestrzeni roboczej: zarejestruj ją lub popraw wybór w kroku, a następnie uruchom ponownie.",
600
- "foundational_service_not_inherited": "Wyłączenie dotyczy usługi dziedziczonej z konta. Ten identyfikator jest zarejestrowany przez tę tablicę, więc nie ma czego wyłączać - usuń zamiast tego własny wpis tablicy."
601
+ "foundational_service_not_inherited": "Wyłączenie dotyczy usługi dziedziczonej z konta. Ten identyfikator jest zarejestrowany przez tę tablicę, więc nie ma czego wyłączać - usuń zamiast tego własny wpis tablicy.",
602
+ "ticket_already_linked": "Zgłoszenie może stać za tylko jednym zadaniem, więc ponowne powiązanie pozbawiłoby istniejące zadanie kontekstu, z którym powstało. Otwórz to zadanie albo najpierw odłącz zgłoszenie."
601
603
  },
602
604
  "action": {
603
605
  "connectGitHub": "Połącz GitHub",
@@ -2330,7 +2332,8 @@
2330
2332
  "deleteTitle": "Usuń ustawienie",
2331
2333
  "basePrefix": "Bazowy:",
2332
2334
  "overrideCount": "{count} nadpisanie | {count} nadpisania | {count} nadpisań",
2333
- "empty": "Brak ustawień. Utwórz jedno, aby przypisać modele do swoich agentów."
2335
+ "empty": "Brak ustawień. Utwórz jedno, aby przypisać modele do swoich agentów.",
2336
+ "customRouteOrder": "własna kolejność tras"
2334
2337
  },
2335
2338
  "editor": {
2336
2339
  "useBaseModel": "Użyj modelu bazowego",
@@ -2355,6 +2358,30 @@
2355
2358
  "confirmDelete": {
2356
2359
  "title": "Usunąć ten preset modelu?",
2357
2360
  "body": "\"{name}\" zostanie usunięty. Zadania go używające wrócą do domyślnego ustawienia obszaru roboczego."
2361
+ },
2362
+ "routes": {
2363
+ "direct": "Własne API dostawcy modelu",
2364
+ "bedrock": "AWS Bedrock",
2365
+ "openrouter": "Brama OpenRouter",
2366
+ "cloudflare": "Cloudflare Workers AI",
2367
+ "subscription": "Subskrypcja (Claude Code / Codex)"
2368
+ },
2369
+ "routeHints": {
2370
+ "direct": "Wymaga klucza API tego dostawcy.",
2371
+ "bedrock": "Działa w Twoim regionie AWS, na modelach dozwolonych dla Twojego konta.",
2372
+ "openrouter": "Jeden klucz, wielu dostawców, odsprzedawanych z marżą.",
2373
+ "cloudflare": "Zawsze dostępne, bez buforowania promptów.",
2374
+ "subscription": "Zryczałtowany limit w połączonym planie."
2375
+ },
2376
+ "routeOrder": {
2377
+ "label": "Kolejność tras",
2378
+ "defaultHint": "Ten preset korzysta z domyślnej kolejności wdrożenia. Przesuń trasę w górę, aby ją preferować.",
2379
+ "customHint": "Ten preset preferuje trasy w tej kolejności. Model dostępny tylko na niższej trasie nadal działa, jest tylko próbowany później.",
2380
+ "reset": "Użyj domyślnej kolejności",
2381
+ "moveUp": "Preferuj tę trasę bardziej",
2382
+ "moveDown": "Preferuj tę trasę mniej",
2383
+ "subscriptionOverrideHint": "Podłączona subskrypcja nadal wygrywa w przypadku modeli, które ją oferują: silnik kieruje je do planu niezależnie od tej kolejności. Trasy poniżej pozycji subskrypcji dotyczą wszystkich pozostałych modeli.",
2384
+ "badgesUseDefaultPresetHint": "Trasa pokazana obok każdego modelu powyżej to trasa domyślnego presetu przestrzeni roboczej, a nie tego presetu."
2358
2385
  }
2359
2386
  },
2360
2387
  "account": {
@@ -5802,7 +5829,7 @@
5802
5829
  "newDescription": "Pojawiły się nowe wbudowane presety modelu. Dodaj je do biblioteki tej tablicy.",
5803
5830
  "add": "Dodaj",
5804
5831
  "updatesHeading": "Dostępne aktualizacje",
5805
- "updatesDescription": "Dostępna jest nowsza wersja tych wbudowanych presetów modelu. Zregeneruj, aby ją przyjąć (domyślny i kolejność są zachowane).",
5832
+ "updatesDescription": "Dostępna jest nowsza wersja tych wbudowanych presetów modelu. Zregeneruj, aby ją przyjąć: domyślny i kolejność są zachowane, ale własna kolejność tras na presecie zostanie wyczyszczona.",
5806
5833
  "versionAvailable": "Dostępna wersja {from} → {to}.",
5807
5834
  "reseed": "Zregeneruj",
5808
5835
  "reseedAll": "Zaktualizuj wszystkie ({count})",
@@ -565,7 +565,8 @@
565
565
  "foundational_service_exists": "Temel hizmet zaten var",
566
566
  "binary_output_service_invalid": "İkili çıktı hizmeti çözümlenemiyor",
567
567
  "binary_output_generator_invalid": "İkili çıktı üreticisi çözümlenemiyor",
568
- "foundational_service_not_inherited": "Bu hizmeti bu pano kaydetti"
568
+ "foundational_service_not_inherited": "Bu hizmeti bu pano kaydetti",
569
+ "ticket_already_linked": "Bu kayda ait bir görev zaten var"
569
570
  },
570
571
  "description": {
571
572
  "dependencies_unmet": "Bu görev henüz tamamlanmamış başka görevlere bağlı. Onları tamamla veya engelini kaldır, ardından yeniden başlat.",
@@ -597,7 +598,8 @@
597
598
  "foundational_service_exists": "Bu kimliğe sahip bir temel hizmet bu kapsamda zaten kayıtlı. Var olan kaydı açıp düzenleyin — iki hizmet aynı kimliği paylaşamaz, çünkü kimlik bir mimarın tasarımında andığı addır.",
598
599
  "binary_output_service_invalid": "İkili çıktılar üreten bir adım, bu çalışma alanının kataloğunda çözümlenemeyen bir temel hizmet seçiyor: kimlik bilinmiyor ya da seçilen depolama hizmeti asset-storage yeteneğini taşımıyor. Adımın seçimini düzeltin veya hizmeti kaydedin, sonra yeniden başlatın.",
599
600
  "binary_output_generator_invalid": "İkili çıktı üreten bir adım, bu dağıtımın kaydetmediği bir üretken entegrasyon seçiyor ya da seçilen entegrasyonların hiçbiri adımın teslim etmesi gereken içerik türünü üretmiyor. Üretken entegrasyonlar bu çalışma alanında değil, dağıtımın kodunda kaydedilir: entegrasyonu kaydedin veya adımın seçimini düzeltin, sonra yeniden başlatın.",
600
- "foundational_service_not_inherited": "Devre dışı bırakma, hesaptan devralınan bir hizmet için geçerlidir. Bu kimlik bu pano tarafından kaydedilmiş, dolayısıyla devre dışı bırakılacak bir şey yok - bunun yerine panonun kendi kaydını silin."
601
+ "foundational_service_not_inherited": "Devre dışı bırakma, hesaptan devralınan bir hizmet için geçerlidir. Bu kimlik bu pano tarafından kaydedilmiş, dolayısıyla devre dışı bırakılacak bir şey yok - bunun yerine panonun kendi kaydını silin.",
602
+ "ticket_already_linked": "Bir kayıt yalnızca tek bir görevi besleyebilir; yeniden bağlamak mevcut görevi oluşturulduğu bağlamdan yoksun bırakır. Bunun yerine o görevi açın ya da önce kaydın bağlantısını kaldırın."
601
603
  },
602
604
  "action": {
603
605
  "connectGitHub": "GitHub'ı bağla",
@@ -2330,7 +2332,8 @@
2330
2332
  "deleteTitle": "Hazır ayarı sil",
2331
2333
  "basePrefix": "Temel:",
2332
2334
  "overrideCount": "{count} geçersiz kılma | {count} geçersiz kılma",
2333
- "empty": "Henüz hazır ayar yok. Modelleri ajanlarınıza eşlemek için bir tane oluşturun."
2335
+ "empty": "Henüz hazır ayar yok. Modelleri ajanlarınıza eşlemek için bir tane oluşturun.",
2336
+ "customRouteOrder": "özel rota sırası"
2334
2337
  },
2335
2338
  "editor": {
2336
2339
  "useBaseModel": "Temel modeli kullan",
@@ -2355,6 +2358,30 @@
2355
2358
  "confirmDelete": {
2356
2359
  "title": "Bu model ön ayarı silinsin mi?",
2357
2360
  "body": "\"{name}\" kaldırılacak. Onu kullanan görevler çalışma alanı varsayılanına döner."
2361
+ },
2362
+ "routes": {
2363
+ "direct": "Modelin kendi sağlayıcı API'si",
2364
+ "bedrock": "AWS Bedrock",
2365
+ "openrouter": "OpenRouter geçidi",
2366
+ "cloudflare": "Cloudflare Workers AI",
2367
+ "subscription": "Abonelik (Claude Code / Codex)"
2368
+ },
2369
+ "routeHints": {
2370
+ "direct": "Bu sağlayıcı için bir API anahtarı gerekir.",
2371
+ "bedrock": "AWS bölgenizde, hesabınızın izin verdiği modellerle çalışır.",
2372
+ "openrouter": "Tek anahtar, çok sağlayıcı, kâr payıyla yeniden satılır.",
2373
+ "cloudflare": "Her zaman kullanılabilir, istem önbelleği yok.",
2374
+ "subscription": "Bağlı bir planda sabit ücretli kota."
2375
+ },
2376
+ "routeOrder": {
2377
+ "label": "Rota sırası",
2378
+ "defaultHint": "Bu hazır ayar dağıtımın varsayılanını izler. Bir rotayı tercih etmek için yukarı taşıyın.",
2379
+ "customHint": "Bu hazır ayar rotaları bu sırayla tercih eder. Yalnızca daha alt bir rotadan erişilebilen bir model yine çalışır, sadece daha sonra denenir.",
2380
+ "reset": "Varsayılan sırayı kullan",
2381
+ "moveUp": "Bu rotayı daha çok tercih et",
2382
+ "moveDown": "Bu rotayı daha az tercih et",
2383
+ "subscriptionOverrideHint": "Bağlı bir abonelik, abonelik sunan modellerde hâlâ önceliklidir: motor bu sıralamadan bağımsız olarak onları plana yönlendirir. Abonelik girdisinin altındaki rotalar diğer tüm modeller için geçerlidir.",
2384
+ "badgesUseDefaultPresetHint": "Yukarıda her modelin yanında gösterilen rota, bu ön ayarın değil, çalışma alanının varsayılan ön ayarının rotasıdır."
2358
2385
  }
2359
2386
  },
2360
2387
  "account": {
@@ -5814,7 +5841,7 @@
5814
5841
  "newDescription": "Yeni yerleşik model ön ayarları geldi. Bu panonun kütüphanesine ekleyin.",
5815
5842
  "add": "Ekle",
5816
5843
  "updatesHeading": "Güncellemeler mevcut",
5817
- "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).",
5844
+ "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, ancak ön ayardaki özel rota sırası temizlenir.",
5818
5845
  "versionAvailable": "Sürüm {from} → {to} mevcut.",
5819
5846
  "reseed": "Yeniden tohumla",
5820
5847
  "reseedAll": "Tümünü güncelle ({count})",
@@ -565,7 +565,8 @@
565
565
  "foundational_service_exists": "Базовий сервіс уже існує",
566
566
  "binary_output_service_invalid": "Не вдається розпізнати сервіс для бінарних результатів",
567
567
  "binary_output_generator_invalid": "Не вдається розпізнати генератор бінарних результатів",
568
- "foundational_service_not_inherited": "Цей сервіс зареєструвала ця дошка"
568
+ "foundational_service_not_inherited": "Цей сервіс зареєструвала ця дошка",
569
+ "ticket_already_linked": "У цього тікета вже є завдання"
569
570
  },
570
571
  "description": {
571
572
  "dependencies_unmet": "Це завдання залежить від інших, які ще не завершені. Заверши або розблокуй їх, а потім запусти його знову.",
@@ -597,7 +598,8 @@
597
598
  "foundational_service_exists": "Базовий сервіс із цим ідентифікатором уже зареєстровано в цій області. Відкрийте наявний запис і відредагуйте його — два сервіси не можуть мати спільний ідентифікатор, бо саме його архітектор називає у своєму проєкті.",
598
599
  "binary_output_service_invalid": "Крок, що генерує бінарні результати, вибирає базовий сервіс, який каталог цього робочого простору не може розпізнати: ідентифікатор невідомий або вибраний сервіс зберігання не має здатності asset-storage. Виправте вибір у кроці або зареєструйте сервіс і запустіть знову.",
599
600
  "binary_output_generator_invalid": "Крок, що генерує бінарні результати, вибирає генеративну інтеграцію, якої це розгортання не реєструє, або жодна з вибраних інтеграцій не створює тип вмісту, який крок має надати. Генеративні інтеграції реєструються в коді розгортання, а не в цьому робочому просторі: зареєструйте її або виправте вибір у кроці й запустіть знову.",
600
- "foundational_service_not_inherited": "Вимкнення стосується сервісу, успадкованого від облікового запису. Цей ідентифікатор зареєстровано цією дошкою, тож вимикати нічого - натомість видаліть власний запис дошки."
601
+ "foundational_service_not_inherited": "Вимкнення стосується сервісу, успадкованого від облікового запису. Цей ідентифікатор зареєстровано цією дошкою, тож вимикати нічого - натомість видаліть власний запис дошки.",
602
+ "ticket_already_linked": "Тікет може живити лише одне завдання, тож повторне звʼязування позбавить наявне завдання контексту, з яким його створено. Відкрийте це завдання або спершу відʼєднайте тікет."
601
603
  },
602
604
  "action": {
603
605
  "connectGitHub": "Під'єднати GitHub",
@@ -2330,7 +2332,8 @@
2330
2332
  "deleteTitle": "Видалити набір",
2331
2333
  "basePrefix": "Базова:",
2332
2334
  "overrideCount": "{count} перевизначення | {count} перевизначення | {count} перевизначень",
2333
- "empty": "Наборів ще немає. Створіть один, щоб призначити моделі вашим агентам."
2335
+ "empty": "Наборів ще немає. Створіть один, щоб призначити моделі вашим агентам.",
2336
+ "customRouteOrder": "власний порядок маршрутів"
2334
2337
  },
2335
2338
  "editor": {
2336
2339
  "useBaseModel": "Використати базову модель",
@@ -2355,6 +2358,30 @@
2355
2358
  "confirmDelete": {
2356
2359
  "title": "Видалити цей пресет моделі?",
2357
2360
  "body": "\"{name}\" буде видалено. Завдання, що його використовують, повернуться до типового пресету робочого простору."
2361
+ },
2362
+ "routes": {
2363
+ "direct": "Власний API постачальника моделі",
2364
+ "bedrock": "AWS Bedrock",
2365
+ "openrouter": "Шлюз OpenRouter",
2366
+ "cloudflare": "Cloudflare Workers AI",
2367
+ "subscription": "Підписка (Claude Code / Codex)"
2368
+ },
2369
+ "routeHints": {
2370
+ "direct": "Потрібен ключ API для цього постачальника.",
2371
+ "bedrock": "Працює у вашому регіоні AWS, на моделях, дозволених для вашого облікового запису.",
2372
+ "openrouter": "Один ключ, багато постачальників, перепродаж із націнкою.",
2373
+ "cloudflare": "Завжди доступно, без кешування промптів.",
2374
+ "subscription": "Фіксована квота підключеного плану."
2375
+ },
2376
+ "routeOrder": {
2377
+ "label": "Порядок маршрутів",
2378
+ "defaultHint": "Цей пресет використовує типовий порядок розгортання. Підніміть маршрут вище, щоб надати йому перевагу.",
2379
+ "customHint": "Цей пресет надає перевагу маршрутам у такому порядку. Модель, доступна лише на нижчому маршруті, усе одно працює, її просто спробують пізніше.",
2380
+ "reset": "Використати типовий порядок",
2381
+ "moveUp": "Більше надавати перевагу цьому маршруту",
2382
+ "moveDown": "Менше надавати перевагу цьому маршруту",
2383
+ "subscriptionOverrideHint": "Підключена підписка все одно перемагає для моделей, які її пропонують: рушій спрямовує їх до тарифу незалежно від цього порядку. Маршрути нижче запису підписки діють для всіх інших моделей.",
2384
+ "badgesUseDefaultPresetHint": "Маршрут, показаний біля кожної моделі вище, належить стандартному пресету робочого простору, а не цьому пресету."
2358
2385
  }
2359
2386
  },
2360
2387
  "account": {
@@ -5802,7 +5829,7 @@
5802
5829
  "newDescription": "З'явилися нові вбудовані пресети моделі. Додайте їх до бібліотеки цієї дошки.",
5803
5830
  "add": "Додати",
5804
5831
  "updatesHeading": "Доступні оновлення",
5805
- "updatesDescription": "Доступна новіша версія цих вбудованих пресетів моделі. Перегенеруйте, щоб застосувати її (стандартний пресет і порядок збережено).",
5832
+ "updatesDescription": "Доступна новіша версія цих вбудованих пресетів моделі. Перегенеруйте, щоб застосувати її: стандартний пресет і порядок збережено, але власний порядок маршрутів у пресеті буде очищено.",
5806
5833
  "versionAvailable": "Доступна версія {from} → {to}.",
5807
5834
  "reseed": "Перегенерувати",
5808
5835
  "reseedAll": "Оновити всі ({count})",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.212.0",
3
+ "version": "0.213.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",
@@ -40,7 +40,7 @@
40
40
  "valibot": "^1.4.2",
41
41
  "vue": "3.5.40",
42
42
  "wretch": "^3.0.9",
43
- "@cat-factory/contracts": "0.221.0"
43
+ "@cat-factory/contracts": "0.223.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@toad-contracts/testing": "0.3.2",