@cat-factory/app 0.206.0 → 0.207.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.
@@ -88,6 +88,16 @@ const state = computed(() => {
88
88
  <dt class="text-slate-500">{{ t('binaryOutput.contextServices') }}</dt>
89
89
  <dd class="min-w-0 font-mono text-slate-400">{{ view.contextServices.join(', ') }}</dd>
90
90
  </template>
91
+ <!-- The formats the step REQUIRED, beside where they were meant to go. Rendered whenever
92
+ the step stated any, including on a run that delivered them: the requirement is what
93
+ makes the content types below it readable, and a reader checking whether a mesh will
94
+ load needs to see what was asked for even when nothing went wrong. -->
95
+ <template v-if="view.mediaTypes.length">
96
+ <dt class="text-slate-500">{{ t('binaryOutput.mediaTypes') }}</dt>
97
+ <dd class="min-w-0 font-mono text-slate-400" data-testid="binary-output-media-types">
98
+ {{ view.mediaTypes.join(', ') }}
99
+ </dd>
100
+ </template>
91
101
  </dl>
92
102
 
93
103
  <!-- The artifacts. `location` is the service's OWN addressing — an object key, a path, a
@@ -201,6 +211,22 @@ const state = computed(() => {
201
211
  <li v-if="view.generatorsUnverified" data-testid="binary-output-generators-unverified">
202
212
  {{ t('binaryOutput.warning.generatorsUnverified') }}
203
213
  </li>
214
+ <!-- The one judgement this panel can make that admission could not: admission checked what
215
+ the selected integrations CAN emit, this checks what came back. Derived in code from
216
+ the step's own two records — the requirement and the reported content types — never
217
+ read off the agent's prose. -->
218
+ <li v-if="view.undeliveredMediaTypes.length" data-testid="binary-output-undelivered-formats">
219
+ {{
220
+ t(
221
+ 'binaryOutput.warning.undeliveredMediaTypes',
222
+ {
223
+ formats: view.undeliveredMediaTypes.join(', '),
224
+ count: view.undeliveredMediaTypes.length,
225
+ },
226
+ view.undeliveredMediaTypes.length,
227
+ )
228
+ }}
229
+ </li>
204
230
  <li v-if="view.misdirected" data-testid="binary-output-misdirected-note">
205
231
  {{
206
232
  t(
@@ -0,0 +1,58 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { parseMediaTypeRequirement, sameFormats } from './BinaryOutputStepPicker.logic'
3
+
4
+ describe('parseMediaTypeRequirement', () => {
5
+ it('stores the reduction the backend compares against, not what was typed', () => {
6
+ // The field is forgiving on the way in and exact on the way out. A locally-lowercased copy
7
+ // would store a format that matches nothing and then reads everywhere as one that was simply
8
+ // never emitted — indistinguishable from a real delivery failure.
9
+ expect(parseMediaTypeRequirement(' Model/GLTF-Binary , image/PNG ').usable).toEqual([
10
+ 'model/gltf-binary',
11
+ 'image/png',
12
+ ])
13
+ })
14
+
15
+ it('drops a parameter, because a requirement is a format and not one request encoding', () => {
16
+ expect(parseMediaTypeRequirement('model/gltf-binary; charset=binary').usable).toEqual([
17
+ 'model/gltf-binary',
18
+ ])
19
+ })
20
+
21
+ it('NAMES what it refused rather than quietly shortening the requirement', () => {
22
+ // A requirement someone typed and the step does not carry is the "absent reads as fine"
23
+ // failure the rest of this surface is built to avoid, so the entry survives verbatim for the
24
+ // warning to quote.
25
+ const parsed = parseMediaTypeRequirement('gltf, model/obj, ')
26
+ expect(parsed.usable).toEqual(['model/obj'])
27
+ expect(parsed.unusable).toEqual(['gltf'])
28
+ })
29
+
30
+ it('deduplicates what two spellings reduce to, keeping first-stated order', () => {
31
+ const parsed = parseMediaTypeRequirement('model/obj, MODEL/OBJ, model/gltf-binary')
32
+ expect(parsed.usable).toEqual(['model/obj', 'model/gltf-binary'])
33
+ })
34
+
35
+ it('maps no synonyms, so a near neighbour stays a separate requirement', () => {
36
+ // `model/obj` and `application/x-tgif` are the same file. Collapsing them would make the
37
+ // admission check accept a GLB where an OBJ was required — the failure it exists to prevent.
38
+ const parsed = parseMediaTypeRequirement('model/obj, application/x-tgif')
39
+ expect(parsed.usable).toEqual(['model/obj', 'application/x-tgif'])
40
+ })
41
+
42
+ it('reads an empty requirement as no requirement', () => {
43
+ expect(parseMediaTypeRequirement(' , ')).toEqual({ usable: [], unusable: [] })
44
+ })
45
+ })
46
+
47
+ describe('sameFormats', () => {
48
+ it('treats an absent list and an empty one as the same write', () => {
49
+ // Clearing the field stores `undefined`, so the two spellings of "no requirement" must not
50
+ // read as a change that came from elsewhere.
51
+ expect(sameFormats(undefined, [])).toBe(true)
52
+ })
53
+
54
+ it('is order-sensitive, because the field writes back exactly what it read', () => {
55
+ expect(sameFormats(['a/b', 'c/d'], ['c/d', 'a/b'])).toBe(false)
56
+ expect(sameFormats(['a/b', 'c/d'], ['a/b', 'c/d'])).toBe(true)
57
+ })
58
+ })
@@ -0,0 +1,55 @@
1
+ import { mediaTypeSchema, normalizeMediaType } from '@cat-factory/contracts'
2
+ import * as v from 'valibot'
3
+
4
+ // The pure half of BinaryOutputStepPicker: reading a free-text FORMAT requirement, and telling
5
+ // this field's own write apart from one that landed underneath it. Extracted for the reason every
6
+ // `*.logic.ts` here is — a decision worth a test should not need a mounted component to reach.
7
+
8
+ /** A parsed format requirement: what the step will carry, and what was refused on the way in. */
9
+ export interface ParsedMediaTypeRequirement {
10
+ /** Normalised, deduplicated, order-preserving — exactly what gets stored. */
11
+ usable: string[]
12
+ /** Entries that are not a `type/subtype` at all, kept VERBATIM so the warning can quote them. */
13
+ unusable: string[]
14
+ }
15
+
16
+ /**
17
+ * Read a comma-separated format requirement the way the field accepts it and the way the backend
18
+ * will hold it.
19
+ *
20
+ * Forgiving on the way IN, exact on the way out, and both halves are the backend's own rules
21
+ * imported rather than re-implemented: `normalizeMediaType` is the same reduction the comparison
22
+ * uses at both ends (a divergent local lowercasing would store a format that matches nothing and
23
+ * reads everywhere as one that was simply never emitted), and `mediaTypeSchema` is what the save
24
+ * boundary holds this to — so what is refused here is exactly what would come back as a 422 one
25
+ * round trip later.
26
+ *
27
+ * A refused entry is REPORTED, never quietly dropped: a requirement someone typed and the step
28
+ * does not carry is the "absent reads as fine" failure the rest of this surface exists to avoid.
29
+ */
30
+ export function parseMediaTypeRequirement(text: string): ParsedMediaTypeRequirement {
31
+ const usable: string[] = []
32
+ const unusable: string[] = []
33
+ for (const entry of text
34
+ .split(',')
35
+ .map((part) => part.trim())
36
+ .filter(Boolean)) {
37
+ const normalized = normalizeMediaType(entry)
38
+ if (normalized && v.safeParse(mediaTypeSchema, normalized).success) usable.push(normalized)
39
+ else unusable.push(entry)
40
+ }
41
+ return { usable: [...new Set(usable)], unusable }
42
+ }
43
+
44
+ /**
45
+ * Whether two stored format lists are the same write.
46
+ *
47
+ * Order-sensitive on purpose: the field writes back what it read, so a differing order means the
48
+ * value came from somewhere else, which is precisely what the caller is asking about.
49
+ */
50
+ export function sameFormats(
51
+ a: readonly string[] | undefined,
52
+ b: readonly string[] | undefined,
53
+ ): boolean {
54
+ return (a ?? []).join(',') === (b ?? []).join(',')
55
+ }
@@ -19,14 +19,16 @@
19
19
  // ride the workspace snapshot (`binaryGenerators`) rather than a catalog read. Both halves are
20
20
  // offered here because a step needs both to work, and only this surface can tell a human that the
21
21
  // content types it promises to deliver are not covered by anything it selected.
22
- import { computed } from 'vue'
22
+ import { computed, ref, watch } from 'vue'
23
23
  import {
24
24
  ASSET_STORAGE_CAPABILITY,
25
25
  GENERATION_CONTEXT_CAPABILITY,
26
+ isBinaryModality,
26
27
  type BinaryModality,
27
28
  type BinaryOutputConfig,
28
29
  } from '@cat-factory/contracts'
29
30
  import { binaryOutputPickIssues, type BinaryOutputPickIssue } from '~/utils/binaryOutput'
31
+ import { parseMediaTypeRequirement, sameFormats } from './BinaryOutputStepPicker.logic'
30
32
 
31
33
  const props = defineProps<{ index: number }>()
32
34
 
@@ -44,12 +46,42 @@ const MODALITY_LABELS: Record<BinaryModality, () => string> = {
44
46
  image: () => t('pipeline.builder.binaryOutputModality.image'),
45
47
  audio: () => t('pipeline.builder.binaryOutputModality.audio'),
46
48
  video: () => t('pipeline.builder.binaryOutputModality.video'),
47
- '3d': () => t('pipeline.builder.binaryOutputModality.3d'),
49
+ '3d-model': () => t('pipeline.builder.binaryOutputModality.3d-model'),
50
+ '3d-scene': () => t('pipeline.builder.binaryOutputModality.3d-scene'),
48
51
  document: () => t('pipeline.builder.binaryOutputModality.document'),
49
52
  }
50
- const MODALITY_ORDER: BinaryModality[] = ['image', 'audio', 'video', '3d', 'document']
53
+ const MODALITY_ORDER: BinaryModality[] = [
54
+ 'image',
55
+ 'audio',
56
+ 'video',
57
+ '3d-model',
58
+ '3d-scene',
59
+ 'document',
60
+ ]
61
+ /**
62
+ * A content type in the reader's language, INCLUDING one this build no longer defines.
63
+ *
64
+ * The `Record` above is exhaustive over the union, so the lookup looks total — and is not, because
65
+ * `modalities` is PERSISTED: a step saved under an earlier vocabulary carries a member that has
66
+ * since been retired (`3d` did exactly that when it split into `3d-model` and `3d-scene`). Such a
67
+ * value is by construction uncovered by every registered integration, so it lands in the
68
+ * `modality_uncovered` warning below — the one line whose job is to tell someone what to re-pick —
69
+ * and a bare `MODALITY_LABELS[modality]()` there is a `TypeError` that takes the whole builder
70
+ * down, on exactly the surface the fix has to be made on.
71
+ *
72
+ * The guard is `isBinaryModality` (contracts, derived from the picklist itself) rather than an
73
+ * optional call on the `Record`, so the narrowing says WHY it is needed and a member added to the
74
+ * vocabulary is known here without anyone remembering to widen anything.
75
+ *
76
+ * The retired value is NAMED rather than silently dropped or guessed at a current member: nothing
77
+ * here knows which one was meant, and a modality quietly missing from the list reads as a step
78
+ * that never required it. This is the standing "absent is not zero" rule at the one place the
79
+ * typed-key check cannot reach — the key is static, but the LOOKUP is a runtime value.
80
+ */
51
81
  function modalityLabel(modality: BinaryModality): string {
52
- return MODALITY_LABELS[modality]()
82
+ return isBinaryModality(modality)
83
+ ? MODALITY_LABELS[modality]()
84
+ : t('pipeline.builder.binaryOutputModalityRetired', { modality: String(modality) })
53
85
  }
54
86
 
55
87
  const config = computed(() => pipelines.draftBinaryOutput(props.index))
@@ -143,6 +175,64 @@ function setGenerators(ids: string[]) {
143
175
  function setModalities(modalities: BinaryModality[]) {
144
176
  patch({ modalities })
145
177
  }
178
+
179
+ /**
180
+ * The FORMAT requirement is free text, not a pick from the selection, and that is deliberate: the
181
+ * whole reason a step states a format is that the selected integrations might not cover it, and a
182
+ * picker offering only what they declare could never express the requirement whose violation this
183
+ * feature exists to catch. What the selection declares is offered as a HINT below instead.
184
+ *
185
+ * Held in a local ref rather than bound straight to the config so a half-typed `model/` is not
186
+ * parsed on every keystroke, and so the normalisation the field applies is VISIBLE — the text
187
+ * snaps back to what was stored.
188
+ */
189
+ const mediaTypeText = ref((config.value?.mediaTypes ?? []).join(', '))
190
+
191
+ /**
192
+ * Entries that are not a `type/subtype` at all, named rather than silently dropped — a
193
+ * requirement someone typed and the step does not carry is exactly the "absent reads as fine"
194
+ * failure the rest of this surface is built to avoid.
195
+ */
196
+ const unusableMediaTypes = ref<string[]>([])
197
+
198
+ /**
199
+ * What this field last wrote, so the watch below can tell its OWN patch from a config that
200
+ * changed underneath it.
201
+ */
202
+ let lastWritten: string[] | undefined = config.value?.mediaTypes
203
+
204
+ watch(
205
+ () => config.value?.mediaTypes,
206
+ (mediaTypes) => {
207
+ mediaTypeText.value = (mediaTypes ?? []).join(', ')
208
+ // The rejected entries belong to the TEXT that was typed, so they outlive this field's own
209
+ // patch — clearing them on every config change would erase the warning in the same tick it
210
+ // was raised, since accepting `image/png` out of `foo, image/png` is itself a patch. Any
211
+ // OTHER route to a new value (the picker rebound to another step, the draft reloaded,
212
+ // storage cleared and the bag dropped) is describing text that no longer exists, and a
213
+ // warning about entries nobody can see is the same "absent reads as fine" failure pointed
214
+ // the other way.
215
+ if (!sameFormats(mediaTypes, lastWritten)) unusableMediaTypes.value = []
216
+ lastWritten = mediaTypes
217
+ },
218
+ )
219
+
220
+ function setMediaTypes(text: string) {
221
+ const { usable, unusable } = parseMediaTypeRequirement(text)
222
+ unusableMediaTypes.value = unusable
223
+ mediaTypeText.value = usable.join(', ')
224
+ // Claimed BEFORE the patch, so the watch above reads this write as its own however it is
225
+ // flushed, and the entries just rejected survive to be rendered.
226
+ lastWritten = usable.length ? usable : undefined
227
+ patch({ mediaTypes: lastWritten })
228
+ }
229
+
230
+ /** What the SELECTED integrations say they emit — the discoverable half of the free-text field. */
231
+ const declaredFormats = computed(() => {
232
+ const byId = new Map(agents.binaryGenerators.map((generator) => [generator.id, generator]))
233
+ const selected = (config.value?.generatorIds ?? []).flatMap((id) => byId.get(id) ?? [])
234
+ return [...new Set(selected.flatMap((generator) => generator.mediaTypes ?? []))]
235
+ })
146
236
  </script>
147
237
 
148
238
  <template>
@@ -216,6 +306,33 @@ function setModalities(modalities: BinaryModality[]) {
216
306
  />
217
307
  </div>
218
308
 
309
+ <!-- The FORMAT requirement, one notch finer than the content types above it and shown right
310
+ under them. Both tiers: like the rest of this picker it is not an override of a default —
311
+ a format nobody stated is a format the run does not check. -->
312
+ <div v-if="config?.storageServiceId" class="flex items-center gap-2">
313
+ <span class="text-[10px] text-slate-500">{{
314
+ t('pipeline.builder.binaryOutputMediaTypes')
315
+ }}</span>
316
+ <UInput
317
+ class="w-56"
318
+ :model-value="mediaTypeText"
319
+ size="xs"
320
+ :placeholder="t('pipeline.builder.binaryOutputMediaTypesPlaceholder')"
321
+ data-testid="binary-output-media-type-input"
322
+ @update:model-value="mediaTypeText = String($event)"
323
+ @change="setMediaTypes(mediaTypeText)"
324
+ />
325
+ </div>
326
+ <p
327
+ v-if="config?.storageServiceId && declaredFormats.length"
328
+ class="ms-1 text-[10px] text-slate-500"
329
+ data-testid="binary-output-declared-formats"
330
+ >
331
+ {{
332
+ t('pipeline.builder.binaryOutputDeclaredFormats', { formats: declaredFormats.join(', ') })
333
+ }}
334
+ </p>
335
+
219
336
  <!-- Every refusal this step would hit, named where it is fixable. Each is its own line
220
337
  with its own remedy: an unreachable catalog is not an empty one, a lost service is not
221
338
  an untagged one, and a lost CONTEXT service is not a lost storage target. -->
@@ -280,5 +397,42 @@ function setModalities(modalities: BinaryModality[]) {
280
397
  })
281
398
  }}
282
399
  </p>
400
+ <p
401
+ v-if="has('media_type_uncovered')"
402
+ class="text-[10px] text-amber-400"
403
+ data-testid="binary-output-media-type-uncovered"
404
+ >
405
+ {{
406
+ t('pipeline.builder.binaryOutputMediaTypeUncovered', {
407
+ formats: pick.uncoveredMediaTypes.join(', '),
408
+ })
409
+ }}
410
+ </p>
411
+ <!-- ADVISORY, and styled apart from every line above it: the step starts. The backend admits
412
+ a format requirement it could not judge, because a generator that declares no formats has
413
+ said only that its formats are unknown — and a surface that dressed that up as a refusal
414
+ would send someone editing a selection that is fine. -->
415
+ <p
416
+ v-if="has('media_type_unverifiable')"
417
+ class="text-[10px] text-slate-500"
418
+ data-testid="binary-output-media-type-unverifiable"
419
+ >
420
+ {{
421
+ t('pipeline.builder.binaryOutputMediaTypeUnverifiable', {
422
+ formats: pick.unverifiableMediaTypes.join(', '),
423
+ })
424
+ }}
425
+ </p>
426
+ <p
427
+ v-if="unusableMediaTypes.length"
428
+ class="text-[10px] text-amber-400"
429
+ data-testid="binary-output-media-type-unusable"
430
+ >
431
+ {{
432
+ t('pipeline.builder.binaryOutputMediaTypeUnusable', {
433
+ entries: unusableMediaTypes.join(', '),
434
+ })
435
+ }}
436
+ </p>
283
437
  </div>
284
438
  </template>
@@ -311,6 +311,66 @@ describe('the generative half of the read model', () => {
311
311
  })
312
312
  })
313
313
 
314
+ // The one judgement this surface can make that admission cannot: admission checked what the
315
+ // selected integrations CAN emit, this checks what the run actually came back with.
316
+ describe('the delivered-format check', () => {
317
+ const required = (mediaTypes: string[], stored: { location: string; contentType?: string }[]) =>
318
+ binaryOutputView(
319
+ step({
320
+ stepOptions: { binaryOutput: { storageServiceId: 'files', mediaTypes } },
321
+ binaryOutputs: report({
322
+ stored: stored.map((entry) => ({ service: 'files', ...entry })),
323
+ }),
324
+ }),
325
+ )
326
+
327
+ it('names a required format no declared artifact reports', () => {
328
+ const view = required(
329
+ ['model/gltf-binary', 'model/fbx'],
330
+ [{ location: 'a.glb', contentType: 'model/gltf-binary' }],
331
+ )
332
+ expect(view?.undeliveredMediaTypes).toEqual(['model/fbx'])
333
+ expect(binaryOutputHasWarnings(view!)).toBe(true)
334
+ })
335
+
336
+ it('reduces the agent’s own spelling before comparing, and only then', () => {
337
+ // The requirement came through `mediaTypeSchema`; the artifact's content type is the model's
338
+ // prose. Comparing them raw reports a format as undelivered while the file sits where it was
339
+ // asked for.
340
+ expect(
341
+ required(['model/gltf-binary'], [{ location: 'a.glb', contentType: 'Model/GLTF-Binary' }])
342
+ ?.undeliveredMediaTypes,
343
+ ).toEqual([])
344
+ })
345
+
346
+ it('does not accept a near neighbour of the required format', () => {
347
+ // The entire point of requiring a format rather than a content type: both of these are 3D.
348
+ expect(
349
+ required(['model/gltf-binary'], [{ location: 'a.fbx', contentType: 'model/fbx' }])
350
+ ?.undeliveredMediaTypes,
351
+ ).toEqual(['model/gltf-binary'])
352
+ })
353
+
354
+ it('counts an artifact that reports no content type as covering nothing', () => {
355
+ expect(required(['model/gltf-binary'], [{ location: 'a.glb' }])?.undeliveredMediaTypes).toEqual(
356
+ ['model/gltf-binary'],
357
+ )
358
+ })
359
+
360
+ it('stays silent when there are no artifacts, because the state line already said so', () => {
361
+ // "It did not deliver a GLB" on top of "it declared nothing" is one fact stated twice as if
362
+ // it were two, and the second one adds nothing a reader can act on.
363
+ const view = binaryOutputView(
364
+ step({
365
+ stepOptions: { binaryOutput: { storageServiceId: 'files', mediaTypes: ['model/obj'] } },
366
+ binaryOutputs: report({ undeclared: true }),
367
+ }),
368
+ )
369
+ expect(view?.mediaTypes).toEqual(['model/obj'])
370
+ expect(view?.undeliveredMediaTypes).toEqual([])
371
+ })
372
+ })
373
+
314
374
  describe('binaryOutputPickIssues, generative half', () => {
315
375
  const catalog = [{ id: 'files', capabilities: ['asset-storage'] }]
316
376
  const generators = [
@@ -368,6 +428,51 @@ describe('binaryOutputPickIssues, generative half', () => {
368
428
  expect(pick.issues).toEqual([])
369
429
  })
370
430
 
431
+ // The FORMAT half, mirroring kernel's `binaryFormatCoverage` — and its three outcomes, which
432
+ // are what a second copy of the rule most easily loses.
433
+ const meshy = {
434
+ id: 'meshy',
435
+ modalities: ['3d-model' as const],
436
+ mediaTypes: ['model/gltf-binary'],
437
+ }
438
+
439
+ it('mirrors the refusal for a format no DECLARING integration emits', () => {
440
+ const pick = binaryOutputPickIssues(
441
+ { storageServiceId: 'files', generatorIds: ['meshy'], mediaTypes: ['model/fbx'] },
442
+ catalog,
443
+ true,
444
+ [meshy],
445
+ )
446
+ expect(pick.issues).toContain('media_type_uncovered')
447
+ expect(pick.uncoveredMediaTypes).toEqual(['model/fbx'])
448
+ expect(pick.unverifiableMediaTypes).toEqual([])
449
+ })
450
+
451
+ it('keeps an UNCHECKABLE format apart from a refused one, because the step still starts', () => {
452
+ // `retro` declares no formats — "only my modality is known". Flagging this as a refusal would
453
+ // send someone editing a selection the backend admits; saying nothing would present an
454
+ // unchecked requirement as a checked one.
455
+ const pick = binaryOutputPickIssues(
456
+ { storageServiceId: 'files', generatorIds: ['retro'], mediaTypes: ['image/webp'] },
457
+ catalog,
458
+ true,
459
+ generators,
460
+ )
461
+ expect(pick.issues).toContain('media_type_unverifiable')
462
+ expect(pick.issues).not.toContain('media_type_uncovered')
463
+ expect(pick.unverifiableMediaTypes).toEqual(['image/webp'])
464
+ })
465
+
466
+ it('accepts a format the selection covers, however many other formats it emits', () => {
467
+ const pick = binaryOutputPickIssues(
468
+ { storageServiceId: 'files', generatorIds: ['meshy'], mediaTypes: ['model/gltf-binary'] },
469
+ catalog,
470
+ true,
471
+ [meshy],
472
+ )
473
+ expect(pick.issues).toEqual([])
474
+ })
475
+
371
476
  it('reports an UNREADABLE set as an outage and makes no claim about the selection', () => {
372
477
  // The picker's half of the mothership-mode disposition. A failed read arrives as the same
373
478
  // empty list an unregistering deployment produces, so judging the selection against it would
@@ -1,4 +1,4 @@
1
- import { ASSET_STORAGE_CAPABILITY } from '@cat-factory/contracts'
1
+ import { ASSET_STORAGE_CAPABILITY, normalizeMediaType } from '@cat-factory/contracts'
2
2
  import type { BinaryModality, RegisteredBinaryGenerator } from '@cat-factory/contracts'
3
3
  import type {
4
4
  BinaryOutputArtifact,
@@ -118,6 +118,28 @@ export interface BinaryOutputView {
118
118
  * Empty ⇒ the step imposes no requirement, so nothing is uncovered by construction.
119
119
  */
120
120
  modalities: readonly BinaryModality[]
121
+ /**
122
+ * The concrete FORMATS the step declares it must deliver
123
+ * (`stepOptions.binaryOutput.mediaTypes`), for the deliverables where the container is the
124
+ * requirement rather than a preference — a mesh the engine can import.
125
+ */
126
+ mediaTypes: readonly string[]
127
+ /**
128
+ * Required formats no DECLARED artifact reports a matching `contentType` for.
129
+ *
130
+ * The one judgement this surface can make that admission cannot: admission checked what the
131
+ * selected integrations CAN emit, and this checks what the run actually came back with. It is
132
+ * derived in code from the two records the step already carries — never read off the agent's
133
+ * prose — and it is the question a human opens this panel to answer once a mesh is supposed to
134
+ * load in a build.
135
+ *
136
+ * Computed only when there ARE artifacts to compare against: with none, the state line above
137
+ * already says nothing was recorded, and "it did not deliver a GLB" on top of "it declared
138
+ * nothing" is the same fact stated twice as if it were two. An artifact that reports no
139
+ * `contentType` covers nothing — the platform does not guess a format from a filename — so a
140
+ * report with formats required and none reported says so rather than passing.
141
+ */
142
+ undeliveredMediaTypes: readonly string[]
121
143
  /**
122
144
  * Integration ids the AGENT named that the deployment does not register. The generative twin of
123
145
  * {@link unknownDeclaredServices}, and it needs no exclusion to stay disjoint from anything —
@@ -168,6 +190,7 @@ export function binaryOutputView(step: PipelineStep | null | undefined): BinaryO
168
190
  const contextServices = config?.contextServiceIds ?? []
169
191
  const generators = config?.generatorIds ?? []
170
192
  const modalities = config?.modalities ?? []
193
+ const mediaTypes = config?.mediaTypes ?? []
171
194
  if (!report) {
172
195
  return {
173
196
  // A step still queued has not had the chance to record anything, which is a different
@@ -180,6 +203,8 @@ export function binaryOutputView(step: PipelineStep | null | undefined): BinaryO
180
203
  unknownDeclaredServices: [],
181
204
  generators,
182
205
  modalities,
206
+ mediaTypes,
207
+ undeliveredMediaTypes: [],
183
208
  unknownDeclaredGenerators: [],
184
209
  generatorsUnverified: false,
185
210
  invalidEntries: 0,
@@ -208,6 +233,8 @@ export function binaryOutputView(step: PipelineStep | null | undefined): BinaryO
208
233
  unknownDeclaredServices: report.unknownServices.filter((id) => id !== target),
209
234
  generators,
210
235
  modalities,
236
+ mediaTypes,
237
+ undeliveredMediaTypes: undeliveredMediaTypes(mediaTypes, rows),
211
238
  unknownDeclaredGenerators: report.unknownGenerators,
212
239
  generatorsUnverified: report.generatorsUnverified === true,
213
240
  invalidEntries: report.invalidEntries,
@@ -216,6 +243,30 @@ export function binaryOutputView(step: PipelineStep | null | undefined): BinaryO
216
243
  }
217
244
  }
218
245
 
246
+ /**
247
+ * The required formats {@link BinaryOutputRow.contentType} does not account for.
248
+ *
249
+ * Compared through `normalizeMediaType` on the DECLARED side only: the step's requirement already
250
+ * came through `mediaTypeSchema` at the write boundary, while the artifact's content type is the
251
+ * agent's own prose and matches nothing until it is reduced the same way. Exact match after that,
252
+ * never a modality fallback — an artifact reported as `model/fbx` does not satisfy a requirement
253
+ * for `model/gltf-binary` just because both are 3D, and that is the entire point of requiring a
254
+ * format rather than a content type.
255
+ */
256
+ function undeliveredMediaTypes(
257
+ required: readonly string[],
258
+ rows: readonly BinaryOutputRow[],
259
+ ): string[] {
260
+ if (required.length === 0 || rows.length === 0) return []
261
+ const delivered = new Set(
262
+ rows.flatMap((row) => {
263
+ const normalized = row.contentType ? normalizeMediaType(row.contentType) : null
264
+ return normalized ? [normalized] : []
265
+ }),
266
+ )
267
+ return required.filter((mediaType) => !delivered.has(mediaType))
268
+ }
269
+
219
270
  /**
220
271
  * Which failure the report records, in the order the parser can produce them. `parseFailed`
221
272
  * and `undeclared` are checked BEFORE the (always empty in those cases) `stored` list, so a
@@ -298,6 +349,7 @@ export function binaryOutputHasWarnings(view: BinaryOutputView): boolean {
298
349
  view.unknownDeclaredServices.length > 0 ||
299
350
  view.unknownDeclaredGenerators.length > 0 ||
300
351
  view.generatorsUnverified ||
352
+ view.undeliveredMediaTypes.length > 0 ||
301
353
  view.invalidEntries > 0 ||
302
354
  view.omitted > 0 ||
303
355
  view.misdirected > 0
@@ -345,6 +397,21 @@ export type BinaryOutputPickIssue =
345
397
  | 'unknown_generator'
346
398
  /** A content type the step declares it delivers is produced by NO selected integration. */
347
399
  | 'modality_uncovered'
400
+ /**
401
+ * A concrete FORMAT the step declares it delivers is emitted by no selected integration that
402
+ * declared its formats (kernel's `media_type_uncovered` spelling verbatim). A refusal, like the
403
+ * two above it.
404
+ */
405
+ | 'media_type_uncovered'
406
+ /**
407
+ * A declared format nothing selected claims, where a selected integration declares no formats
408
+ * at all — so it MIGHT be met and nothing may say otherwise. ADVISORY: unlike every other
409
+ * member here it is not a refusal and must not be styled as one, or a step that is going to
410
+ * start perfectly well reads as broken. It is here rather than nowhere because the alternative
411
+ * is silence about a requirement the platform could not check, which is how "nobody looked"
412
+ * comes to look exactly like "this is fine".
413
+ */
414
+ | 'media_type_unverifiable'
348
415
 
349
416
  /** What the builder found wrong with one step's selection, and which ids to name. */
350
417
  export interface BinaryOutputPickState {
@@ -355,6 +422,10 @@ export interface BinaryOutputPickState {
355
422
  unknownGeneratorIds: readonly string[]
356
423
  /** The declared content types nothing selected can produce, for the message that names them. */
357
424
  uncoveredModalities: readonly BinaryModality[]
425
+ /** The declared formats no DECLARING integration emits — the refusal's own list. */
426
+ uncoveredMediaTypes: readonly string[]
427
+ /** The declared formats that could not be judged, kept apart from the refusal above. */
428
+ unverifiableMediaTypes: readonly string[]
358
429
  }
359
430
 
360
431
  /**
@@ -374,24 +445,72 @@ export interface BinaryOutputPickState {
374
445
  */
375
446
  function generatorPickIssues(
376
447
  config: BinaryOutputConfig | undefined,
377
- generators: readonly Pick<RegisteredBinaryGenerator, 'id' | 'modalities'>[],
448
+ generators: readonly Pick<RegisteredBinaryGenerator, 'id' | 'modalities' | 'mediaTypes'>[],
378
449
  unavailable: boolean,
379
- ): { issues: BinaryOutputPickIssue[]; unknownGeneratorIds: string[]; uncovered: BinaryModality[] } {
380
- if (unavailable) {
381
- return { issues: ['generators_unavailable'], unknownGeneratorIds: [], uncovered: [] }
450
+ ): {
451
+ issues: BinaryOutputPickIssue[]
452
+ unknownGeneratorIds: string[]
453
+ uncovered: BinaryModality[]
454
+ uncoveredMediaTypes: string[]
455
+ unverifiableMediaTypes: string[]
456
+ } {
457
+ const none = {
458
+ unknownGeneratorIds: [],
459
+ uncovered: [],
460
+ uncoveredMediaTypes: [],
461
+ unverifiableMediaTypes: [],
382
462
  }
463
+ if (unavailable) return { issues: ['generators_unavailable'], ...none }
383
464
  const byId = new Map(generators.map((g) => [g.id, g]))
384
465
  const selectedIds = config?.generatorIds ?? []
385
466
  const unknownGeneratorIds = selectedIds.filter((id) => !byId.has(id))
386
467
  // Coverage is judged against what RESOLVED, exactly as admission judges it: an unknown id
387
468
  // contributes no content types, so a step whose only audio generator is unregistered is told
388
469
  // BOTH things — the id is gone, and the requirement it was covering is now uncovered.
389
- const covered = new Set(selectedIds.flatMap((id) => byId.get(id)?.modalities ?? []))
470
+ const selected = selectedIds.flatMap((id) => byId.get(id) ?? [])
471
+ const covered = new Set(selected.flatMap((g) => g.modalities))
390
472
  const uncovered = (config?.modalities ?? []).filter((m) => !covered.has(m))
473
+ const format = formatCoverage(config?.mediaTypes ?? [], selected)
391
474
  const issues: BinaryOutputPickIssue[] = []
392
475
  if (unknownGeneratorIds.length) issues.push('unknown_generator')
393
476
  if (uncovered.length) issues.push('modality_uncovered')
394
- return { issues, unknownGeneratorIds, uncovered }
477
+ if (format.uncovered.length) issues.push('media_type_uncovered')
478
+ if (format.unverifiable.length) issues.push('media_type_unverifiable')
479
+ return {
480
+ issues,
481
+ unknownGeneratorIds,
482
+ uncovered,
483
+ uncoveredMediaTypes: format.uncovered,
484
+ unverifiableMediaTypes: format.unverifiable,
485
+ }
486
+ }
487
+
488
+ /**
489
+ * The SPA's copy of kernel's `binaryFormatCoverage`, restated for the reason the two `*_service`
490
+ * members above are: the builder cannot see kernel, and the wire vocabulary that does cross
491
+ * (`@cat-factory/contracts`) carries the schema, not the rule.
492
+ *
493
+ * The THIRD outcome is what must not be lost in the copying. A generator that declares no formats
494
+ * has said "only my modality is known" — a documented state, not an empty answer — so a
495
+ * requirement it cannot be judged against is unverifiable and the step still starts. Collapsing
496
+ * that into `uncovered` would flag steps the backend admits (and send someone editing a selection
497
+ * that is fine); collapsing it into silence would present an unchecked requirement as a checked
498
+ * one.
499
+ */
500
+ function formatCoverage(
501
+ required: readonly string[],
502
+ selected: readonly Pick<RegisteredBinaryGenerator, 'mediaTypes'>[],
503
+ ): { uncovered: string[]; unverifiable: string[] } {
504
+ const emitted = new Set(selected.flatMap((g) => g.mediaTypes ?? []))
505
+ const undeclared = selected.some((g) => (g.mediaTypes ?? []).length === 0)
506
+ const uncovered: string[] = []
507
+ const unverifiable: string[] = []
508
+ for (const mediaType of required) {
509
+ if (emitted.has(mediaType)) continue
510
+ if (undeclared) unverifiable.push(mediaType)
511
+ else uncovered.push(mediaType)
512
+ }
513
+ return { uncovered, unverifiable }
395
514
  }
396
515
 
397
516
  /**
@@ -425,7 +544,7 @@ export function binaryOutputPickIssues(
425
544
  // that registers no integrations cannot satisfy a step that selects one. So a call site that
426
545
  // omits this FLAGS a selection rather than passing it — the loud direction — and the default
427
546
  // stays a legitimate value rather than a hole.
428
- generators: readonly Pick<RegisteredBinaryGenerator, 'id' | 'modalities'>[] = [],
547
+ generators: readonly Pick<RegisteredBinaryGenerator, 'id' | 'modalities' | 'mediaTypes'>[] = [],
429
548
  // Whether the deployment's integrations could not be READ. Defaulted to `false` — the honest
430
549
  // default, since every deployment but a mothership-mode node reads them in-process and cannot
431
550
  // fail — so an omitting call site judges the list it was given rather than claiming an outage.
@@ -451,6 +570,8 @@ export function binaryOutputPickIssues(
451
570
  unknownContextIds: [],
452
571
  unknownGeneratorIds: generative.unknownGeneratorIds,
453
572
  uncoveredModalities: generative.uncovered,
573
+ uncoveredMediaTypes: generative.uncoveredMediaTypes,
574
+ unverifiableMediaTypes: generative.unverifiableMediaTypes,
454
575
  }
455
576
  }
456
577
 
@@ -472,5 +593,7 @@ export function binaryOutputPickIssues(
472
593
  unknownContextIds,
473
594
  unknownGeneratorIds: generative.unknownGeneratorIds,
474
595
  uncoveredModalities: generative.uncovered,
596
+ uncoveredMediaTypes: generative.uncoveredMediaTypes,
597
+ unverifiableMediaTypes: generative.unverifiableMediaTypes,
475
598
  }
476
599
  }
@@ -3748,13 +3748,21 @@
3748
3748
  "binaryOutputGeneratorsPlaceholder": "Keine Integration ausgewählt",
3749
3749
  "binaryOutputModalities": "Muss liefern",
3750
3750
  "binaryOutputModalitiesPlaceholder": "Keine Anforderung",
3751
+ "binaryOutputMediaTypes": "Exakte Formate",
3752
+ "binaryOutputMediaTypesPlaceholder": "Keine Formatanforderung",
3753
+ "binaryOutputDeclaredFormats": "Ausgewählte Integrationen geben an: {formats}",
3754
+ "binaryOutputMediaTypeUncovered": "Keine ausgewählte Integration erzeugt {formats}, was dieser Schritt liefern soll. Wähle eine, die das kann, oder entferne die Anforderung.",
3755
+ "binaryOutputMediaTypeUnverifiable": "Keine ausgewählte Integration gibt {formats} an, aber eine von ihnen gibt überhaupt keine Formate an, daher konnte das nicht geprüft werden. Der Schritt startet trotzdem; prüfe in der API der Integration, ob sie das erzeugen kann.",
3756
+ "binaryOutputMediaTypeUnusable": "Nicht gespeichert, denn dies sind keine Medientypen: {entries}. Schreibe jeden als type/subtype.",
3751
3757
  "binaryOutputGeneratorMissing": "Diese Installation registriert diese generativen Integrationen nicht: {ids}. Sie werden im Code der Installation registriert, nicht in diesem Workspace.",
3752
3758
  "binaryOutputModalityUncovered": "Keine ausgewählte Integration erzeugt {modalities}, was dieser Schritt liefern soll.",
3759
+ "binaryOutputModalityRetired": "{modality} (nicht mehr verfügbar — wähle die Inhaltstypen dieses Schritts neu)",
3753
3760
  "binaryOutputModality": {
3754
3761
  "image": "Bilder",
3755
3762
  "audio": "Audio",
3756
3763
  "video": "Video",
3757
- "3d": "3D-Modelle",
3764
+ "3d-model": "3D-Modelle",
3765
+ "3d-scene": "3D-Szenen",
3758
3766
  "document": "Dokumente"
3759
3767
  }
3760
3768
  },
@@ -4412,6 +4420,7 @@
4412
4420
  "target": "Speicherdienst",
4413
4421
  "targetNone": "Für diesen Schritt nicht erfasst",
4414
4422
  "contextServices": "Kontext der Erzeugung",
4423
+ "mediaTypes": "Geforderte Formate",
4415
4424
  "misdirectedBadge": "Anderer Dienst",
4416
4425
  "unknownBadge": "Nicht im Katalog",
4417
4426
  "storedCount": "{outcome}, 1 Artefakt | {outcome}, {count} Artefakte",
@@ -4443,6 +4452,7 @@
4443
4452
  "warning": {
4444
4453
  "unknownServices": "Nennt einen Dienst, den der Katalog nicht enthält: {ids}. Der Eintrag bleibt wie angegeben erhalten; prüfe die Kennung gegen den Katalog des Boards. | Nennt Dienste, die der Katalog nicht enthält: {ids}. Die Einträge bleiben wie angegeben erhalten; prüfe die Kennungen gegen den Katalog des Boards.",
4445
4454
  "targetUnknown": "Der Katalog enthält den eigenen Speicherdienst dieses Schritts nicht mehr ({id}), deshalb konnte nichts unten dagegen geprüft werden. Registriere ihn erneut, oder verweise den Schritt auf einen anderen Dienst.",
4455
+ "undeliveredMediaTypes": "Dieser Schritt sollte {formats} liefern, und kein Artefakt unten meldet dieses Format. | Dieser Schritt sollte {formats} liefern, und kein Artefakt unten meldet diese Formate.",
4446
4456
  "misdirected": "1 Artefakt ging an einen anderen Dienst als {target}. | {count} Artefakte gingen an einen anderen Dienst als {target}.",
4447
4457
  "invalidEntries": "1 angegebener Eintrag wurde verworfen: er nannte weder Dienst noch Ablageort. | {count} angegebene Einträge wurden verworfen: sie nannten weder Dienst noch Ablageort.",
4448
4458
  "omitted": "1 weiteres Artefakt wurde jenseits der Berichtsgrenze angegeben und ist nicht aufgeführt. | {count} weitere Artefakte wurden jenseits der Berichtsgrenze angegeben und sind nicht aufgeführt.",
@@ -4233,13 +4233,22 @@
4233
4233
  "binaryOutputGeneratorsPlaceholder": "No integration selected",
4234
4234
  "binaryOutputModalities": "Must deliver",
4235
4235
  "binaryOutputModalitiesPlaceholder": "No requirement",
4236
+ "binaryOutputMediaTypes": "Exact formats",
4237
+ "binaryOutputMediaTypesPlaceholder": "No format requirement",
4238
+ "binaryOutputDeclaredFormats": "Selected integrations declare: {formats}",
4239
+ "binaryOutputMediaTypeUncovered": "No selected integration emits {formats}, which this step is set to deliver. Pick one that does, or drop the requirement.",
4240
+ "binaryOutputMediaTypeUnverifiable": "No selected integration declares {formats}, but one of them declares no formats at all, so this could not be checked. The step still starts; confirm from the integration's API that it can emit this.",
4241
+ "binaryOutputMediaTypeUnusable": "Not saved, because these are not media types: {entries}. Write each one as type/subtype.",
4236
4242
  "binaryOutputGeneratorMissing": "This deployment does not register these generative integrations: {ids}. They are registered in the deployment's code, not in this workspace.",
4237
4243
  "binaryOutputModalityUncovered": "No selected integration produces {modalities}, which this step is set to deliver.",
4244
+ "binaryOutputModalityRetired": "{modality} (no longer offered — re-pick this step's content types)",
4245
+ "@binaryOutputModalityRetired": "A content type saved on a step that this build no longer defines. {modality} is a raw machine value (e.g. 3d) and must not be translated. Appears inside the binaryOutputModalityUncovered sentence, so keep it a short noun phrase, not a sentence.",
4238
4246
  "binaryOutputModality": {
4239
4247
  "image": "Images",
4240
4248
  "audio": "Audio",
4241
4249
  "video": "Video",
4242
- "3d": "3D models",
4250
+ "3d-model": "3D models",
4251
+ "3d-scene": "3D scenes",
4243
4252
  "document": "Documents"
4244
4253
  }
4245
4254
  },
@@ -5630,6 +5639,7 @@
5630
5639
  "target": "Storage service",
5631
5640
  "targetNone": "None recorded on this step",
5632
5641
  "contextServices": "Generation context",
5642
+ "mediaTypes": "Required formats",
5633
5643
  "misdirectedBadge": "Other service",
5634
5644
  "unknownBadge": "Not in catalog",
5635
5645
  "storedCount": "{outcome}, 1 artifact | {outcome}, {count} artifacts",
@@ -5661,6 +5671,7 @@
5661
5671
  "warning": {
5662
5672
  "unknownServices": "Named a service the catalog does not contain: {ids}. The entry is kept as claimed; check the id against the workspace catalog. | Named services the catalog does not contain: {ids}. Their entries are kept as claimed; check the ids against the workspace catalog.",
5663
5673
  "targetUnknown": "The catalog no longer contains this step's own storage service ({id}), so nothing below could be checked against it. Register it again, or point the step at another service.",
5674
+ "undeliveredMediaTypes": "This step was set to deliver {formats}, and no artifact below reports that format. | This step was set to deliver {formats}, and no artifact below reports those formats.",
5664
5675
  "misdirected": "1 artifact went to a service other than {target}. | {count} artifacts went to a service other than {target}.",
5665
5676
  "invalidEntries": "1 declared entry was dropped: it named no service and location. | {count} declared entries were dropped: they named no service and location.",
5666
5677
  "omitted": "1 more artifact was declared beyond the report's limit and is not listed. | {count} more artifacts were declared beyond the report's limit and are not listed.",
@@ -4106,13 +4106,21 @@
4106
4106
  "binaryOutputGeneratorsPlaceholder": "Ninguna integración seleccionada",
4107
4107
  "binaryOutputModalities": "Debe entregar",
4108
4108
  "binaryOutputModalitiesPlaceholder": "Sin requisito",
4109
+ "binaryOutputMediaTypes": "Formatos exactos",
4110
+ "binaryOutputMediaTypesPlaceholder": "Sin requisito de formato",
4111
+ "binaryOutputDeclaredFormats": "Las integraciones seleccionadas declaran: {formats}",
4112
+ "binaryOutputMediaTypeUncovered": "Ninguna integración seleccionada emite {formats}, que este paso debe entregar. Elige una que lo haga o quita el requisito.",
4113
+ "binaryOutputMediaTypeUnverifiable": "Ninguna integración seleccionada declara {formats}, pero una de ellas no declara ningún formato, así que no se pudo comprobar. El paso se inicia igualmente; confirma en la API de la integración que puede emitirlo.",
4114
+ "binaryOutputMediaTypeUnusable": "No se guardó, porque esto no son tipos de medio: {entries}. Escribe cada uno como type/subtype.",
4109
4115
  "binaryOutputGeneratorMissing": "Esta instalación no registra estas integraciones generativas: {ids}. Se registran en el código de la instalación, no en este espacio de trabajo.",
4110
4116
  "binaryOutputModalityUncovered": "Ninguna integración seleccionada produce {modalities}, que este paso debe entregar.",
4117
+ "binaryOutputModalityRetired": "{modality} (ya no disponible: vuelve a elegir los tipos de contenido de este paso)",
4111
4118
  "binaryOutputModality": {
4112
4119
  "image": "Imágenes",
4113
4120
  "audio": "Audio",
4114
4121
  "video": "Vídeo",
4115
- "3d": "Modelos 3D",
4122
+ "3d-model": "Modelos 3D",
4123
+ "3d-scene": "Escenas 3D",
4116
4124
  "document": "Documentos"
4117
4125
  }
4118
4126
  },
@@ -5381,6 +5389,7 @@
5381
5389
  "target": "Servicio de almacenamiento",
5382
5390
  "targetNone": "No registrado en este paso",
5383
5391
  "contextServices": "Contexto de generación",
5392
+ "mediaTypes": "Formatos requeridos",
5384
5393
  "misdirectedBadge": "Otro servicio",
5385
5394
  "unknownBadge": "Fuera del catálogo",
5386
5395
  "storedCount": "{outcome}, 1 artefacto | {outcome}, {count} artefactos",
@@ -5412,6 +5421,7 @@
5412
5421
  "warning": {
5413
5422
  "unknownServices": "Nombró un servicio que el catálogo no contiene: {ids}. La entrada se conserva tal como se declaró; comprueba el identificador con el catálogo del tablero. | Nombró servicios que el catálogo no contiene: {ids}. Sus entradas se conservan tal como se declararon; comprueba los identificadores con el catálogo del tablero.",
5414
5423
  "targetUnknown": "El catálogo ya no contiene el servicio de almacenamiento de este paso ({id}), así que nada de lo de abajo pudo comprobarse contra él. Vuelve a registrarlo, o apunta el paso a otro servicio.",
5424
+ "undeliveredMediaTypes": "Este paso debía entregar {formats} y ningún artefacto de abajo informa ese formato. | Este paso debía entregar {formats} y ningún artefacto de abajo informa esos formatos.",
5415
5425
  "misdirected": "1 artefacto fue a un servicio distinto de {target}. | {count} artefactos fueron a un servicio distinto de {target}.",
5416
5426
  "invalidEntries": "Se descartó 1 entrada declarada: no nombraba servicio ni ubicación. | Se descartaron {count} entradas declaradas: no nombraban servicio ni ubicación.",
5417
5427
  "omitted": "Se declaró 1 artefacto más por encima del límite del informe y no aparece en la lista. | Se declararon {count} artefactos más por encima del límite del informe y no aparecen en la lista.",
@@ -4106,13 +4106,21 @@
4106
4106
  "binaryOutputGeneratorsPlaceholder": "Aucune intégration sélectionnée",
4107
4107
  "binaryOutputModalities": "Doit livrer",
4108
4108
  "binaryOutputModalitiesPlaceholder": "Aucune exigence",
4109
+ "binaryOutputMediaTypes": "Formats exacts",
4110
+ "binaryOutputMediaTypesPlaceholder": "Aucune exigence de format",
4111
+ "binaryOutputDeclaredFormats": "Les intégrations sélectionnées déclarent : {formats}",
4112
+ "binaryOutputMediaTypeUncovered": "Aucune intégration sélectionnée n’émet {formats}, que cette étape doit livrer. Choisissez-en une qui le fait, ou retirez l’exigence.",
4113
+ "binaryOutputMediaTypeUnverifiable": "Aucune intégration sélectionnée ne déclare {formats}, mais l’une d’elles ne déclare aucun format, cela n’a donc pas pu être vérifié. L’étape démarre quand même ; confirmez dans l’API de l’intégration qu’elle peut l’émettre.",
4114
+ "binaryOutputMediaTypeUnusable": "Non enregistré, car ce ne sont pas des types de média : {entries}. Écrivez chacun sous la forme type/subtype.",
4109
4115
  "binaryOutputGeneratorMissing": "Ce déploiement n'enregistre pas ces intégrations génératives : {ids}. Elles s'enregistrent dans le code du déploiement, pas dans cet espace de travail.",
4110
4116
  "binaryOutputModalityUncovered": "Aucune intégration sélectionnée ne produit {modalities}, que cette étape doit livrer.",
4117
+ "binaryOutputModalityRetired": "{modality} (n'est plus proposé — resélectionnez les types de contenu de cette étape)",
4111
4118
  "binaryOutputModality": {
4112
4119
  "image": "Images",
4113
4120
  "audio": "Audio",
4114
4121
  "video": "Vidéo",
4115
- "3d": "Modèles 3D",
4122
+ "3d-model": "Modèles 3D",
4123
+ "3d-scene": "Scènes 3D",
4116
4124
  "document": "Documents"
4117
4125
  }
4118
4126
  },
@@ -5381,6 +5389,7 @@
5381
5389
  "target": "Service de stockage",
5382
5390
  "targetNone": "Non enregistré pour cette étape",
5383
5391
  "contextServices": "Contexte de génération",
5392
+ "mediaTypes": "Formats exigés",
5384
5393
  "misdirectedBadge": "Autre service",
5385
5394
  "unknownBadge": "Hors catalogue",
5386
5395
  "storedCount": "{outcome}, 1 artefact | {outcome}, {count} artefacts",
@@ -5412,6 +5421,7 @@
5412
5421
  "warning": {
5413
5422
  "unknownServices": "A nommé un service absent du catalogue : {ids}. L'entrée est conservée telle que déclarée ; vérifiez l'identifiant dans le catalogue du tableau. | A nommé des services absents du catalogue : {ids}. Leurs entrées sont conservées telles que déclarées ; vérifiez les identifiants dans le catalogue du tableau.",
5414
5423
  "targetUnknown": "Le catalogue ne contient plus le service de stockage propre à cette étape ({id}), donc rien ci-dessous n'a pu être vérifié par rapport à lui. Enregistrez-le de nouveau, ou orientez l'étape vers un autre service.",
5424
+ "undeliveredMediaTypes": "Cette étape devait livrer {formats}, et aucun artefact ci-dessous ne signale ce format. | Cette étape devait livrer {formats}, et aucun artefact ci-dessous ne signale ces formats.",
5415
5425
  "misdirected": "1 artefact est allé vers un service autre que {target}. | {count} artefacts sont allés vers un service autre que {target}.",
5416
5426
  "invalidEntries": "1 entrée déclarée a été écartée : elle ne nommait ni service ni emplacement. | {count} entrées déclarées ont été écartées : elles ne nommaient ni service ni emplacement.",
5417
5427
  "omitted": "1 artefact supplémentaire a été déclaré au-delà de la limite du rapport et n'est pas listé. | {count} artefacts supplémentaires ont été déclarés au-delà de la limite du rapport et ne sont pas listés.",
@@ -4117,13 +4117,21 @@
4117
4117
  "binaryOutputGeneratorsPlaceholder": "לא נבחרה אינטגרציה",
4118
4118
  "binaryOutputModalities": "חייב לספק",
4119
4119
  "binaryOutputModalitiesPlaceholder": "ללא דרישה",
4120
+ "binaryOutputMediaTypes": "פורמטים מדויקים",
4121
+ "binaryOutputMediaTypesPlaceholder": "ללא דרישת פורמט",
4122
+ "binaryOutputDeclaredFormats": "האינטגרציות שנבחרו מצהירות על: {formats}",
4123
+ "binaryOutputMediaTypeUncovered": "אף אינטגרציה שנבחרה אינה מפיקה {formats}, שהשלב הזה אמור לספק. בחרו אחת שמפיקה, או הסירו את הדרישה.",
4124
+ "binaryOutputMediaTypeUnverifiable": "אף אינטגרציה שנבחרה אינה מצהירה על {formats}, אבל אחת מהן אינה מצהירה על פורמטים כלל, ולכן לא ניתן היה לבדוק זאת. השלב יתחיל בכל מקרה; ודאו ב-API של האינטגרציה שהיא יכולה להפיק את זה.",
4125
+ "binaryOutputMediaTypeUnusable": "לא נשמר, כי אלה אינם סוגי מדיה: {entries}. כתבו כל אחד בצורה type/subtype.",
4120
4126
  "binaryOutputGeneratorMissing": "ההתקנה הזו אינה רושמת את האינטגרציות הגנרטיביות האלה: {ids}. הן נרשמות בקוד ההתקנה, לא במרחב העבודה הזה.",
4121
4127
  "binaryOutputModalityUncovered": "אף אינטגרציה שנבחרה אינה מייצרת {modalities}, שהשלב הזה אמור לספק.",
4128
+ "binaryOutputModalityRetired": "{modality} (כבר לא נתמך — בחרו מחדש את סוגי התוכן של השלב)",
4122
4129
  "binaryOutputModality": {
4123
4130
  "image": "תמונות",
4124
4131
  "audio": "אודיו",
4125
4132
  "video": "וידאו",
4126
- "3d": "מודלים תלת-ממדיים",
4133
+ "3d-model": "מודלים תלת-ממדיים",
4134
+ "3d-scene": "סצנות תלת-ממדיות",
4127
4135
  "document": "מסמכים"
4128
4136
  }
4129
4137
  },
@@ -5392,6 +5400,7 @@
5392
5400
  "target": "שירות אחסון",
5393
5401
  "targetNone": "לא נרשם בשלב זה",
5394
5402
  "contextServices": "הקשר ליצירה",
5403
+ "mediaTypes": "פורמטים נדרשים",
5395
5404
  "misdirectedBadge": "שירות אחר",
5396
5405
  "unknownBadge": "לא בקטלוג",
5397
5406
  "storedCount": "{outcome}, פריט אחד | {outcome}, {count} פריטים",
@@ -5423,6 +5432,7 @@
5423
5432
  "warning": {
5424
5433
  "unknownServices": "צוין שירות שאינו בקטלוג: {ids}. הרשומה נשמרת כפי שהוצהרה; בדוק את המזהה מול קטלוג הלוח. | צוינו שירותים שאינם בקטלוג: {ids}. הרשומות נשמרות כפי שהוצהרו; בדוק את המזהים מול קטלוג הלוח.",
5425
5434
  "targetUnknown": "הקטלוג כבר אינו מכיל את שירות האחסון של השלב הזה ({id}), ולכן לא ניתן היה להשוות מולו דבר ממה שלהלן. רשום אותו מחדש, או הפנה את השלב לשירות אחר.",
5435
+ "undeliveredMediaTypes": "השלב הזה אמור היה לספק {formats}, ואף פריט למטה אינו מדווח על הפורמט הזה. | השלב הזה אמור היה לספק {formats}, ואף פריט למטה אינו מדווח על הפורמטים האלה.",
5426
5436
  "misdirected": "פריט אחד הגיע לשירות אחר מ- {target}. | {count} פריטים הגיעו לשירות אחר מ- {target}.",
5427
5437
  "invalidEntries": "רשומה מוצהרת אחת נדחתה: לא צוינו בה שירות ומיקום. | {count} רשומות מוצהרות נדחו: לא צוינו בהן שירות ומיקום.",
5428
5438
  "omitted": "פריט נוסף אחד הוצהר מעבר למגבלת הדוח ואינו מופיע ברשימה. | {count} פריטים נוספים הוצהרו מעבר למגבלת הדוח ואינם מופיעים ברשימה.",
@@ -3748,13 +3748,21 @@
3748
3748
  "binaryOutputGeneratorsPlaceholder": "Nessuna integrazione selezionata",
3749
3749
  "binaryOutputModalities": "Deve fornire",
3750
3750
  "binaryOutputModalitiesPlaceholder": "Nessun requisito",
3751
+ "binaryOutputMediaTypes": "Formati esatti",
3752
+ "binaryOutputMediaTypesPlaceholder": "Nessun requisito di formato",
3753
+ "binaryOutputDeclaredFormats": "Le integrazioni selezionate dichiarano: {formats}",
3754
+ "binaryOutputMediaTypeUncovered": "Nessuna integrazione selezionata emette {formats}, che questo passaggio deve fornire. Scegline una che lo faccia oppure rimuovi il requisito.",
3755
+ "binaryOutputMediaTypeUnverifiable": "Nessuna integrazione selezionata dichiara {formats}, ma una di esse non dichiara alcun formato, quindi non è stato possibile verificarlo. Il passaggio parte comunque; verifica nell’API dell’integrazione che possa emetterlo.",
3756
+ "binaryOutputMediaTypeUnusable": "Non salvato, perché questi non sono tipi di media: {entries}. Scrivi ciascuno come type/subtype.",
3751
3757
  "binaryOutputGeneratorMissing": "Questa installazione non registra queste integrazioni generative: {ids}. Si registrano nel codice dell'installazione, non in questo spazio di lavoro.",
3752
3758
  "binaryOutputModalityUncovered": "Nessuna integrazione selezionata produce {modalities}, che questo passaggio deve fornire.",
3759
+ "binaryOutputModalityRetired": "{modality} (non più disponibile: riseleziona i tipi di contenuto di questo passaggio)",
3753
3760
  "binaryOutputModality": {
3754
3761
  "image": "Immagini",
3755
3762
  "audio": "Audio",
3756
3763
  "video": "Video",
3757
- "3d": "Modelli 3D",
3764
+ "3d-model": "Modelli 3D",
3765
+ "3d-scene": "Scene 3D",
3758
3766
  "document": "Documenti"
3759
3767
  }
3760
3768
  },
@@ -4412,6 +4420,7 @@
4412
4420
  "target": "Servizio di archiviazione",
4413
4421
  "targetNone": "Non registrato per questo passo",
4414
4422
  "contextServices": "Contesto della generazione",
4423
+ "mediaTypes": "Formati richiesti",
4415
4424
  "misdirectedBadge": "Altro servizio",
4416
4425
  "unknownBadge": "Fuori catalogo",
4417
4426
  "storedCount": "{outcome}, 1 artefatto | {outcome}, {count} artefatti",
@@ -4443,6 +4452,7 @@
4443
4452
  "warning": {
4444
4453
  "unknownServices": "Ha indicato un servizio che il catalogo non contiene: {ids}. La voce viene conservata come dichiarata; verifica l'identificativo nel catalogo della board. | Ha indicato servizi che il catalogo non contiene: {ids}. Le voci vengono conservate come dichiarate; verifica gli identificativi nel catalogo della board.",
4445
4454
  "targetUnknown": "Il catalogo non contiene più il servizio di archiviazione di questo passo ({id}), quindi nulla di quanto segue ha potuto essere verificato rispetto ad esso. Registralo di nuovo, oppure indirizza il passo a un altro servizio.",
4455
+ "undeliveredMediaTypes": "Questo passaggio doveva fornire {formats} e nessun artefatto qui sotto riporta quel formato. | Questo passaggio doveva fornire {formats} e nessun artefatto qui sotto riporta quei formati.",
4446
4456
  "misdirected": "1 artefatto è finito su un servizio diverso da {target}. | {count} artefatti sono finiti su un servizio diverso da {target}.",
4447
4457
  "invalidEntries": "1 voce dichiarata è stata scartata: non indicava né servizio né posizione. | {count} voci dichiarate sono state scartate: non indicavano né servizio né posizione.",
4448
4458
  "omitted": "È stato dichiarato 1 altro artefatto oltre il limite del rapporto e non compare nell'elenco. | Sono stati dichiarati altri {count} artefatti oltre il limite del rapporto e non compaiono nell'elenco.",
@@ -4118,13 +4118,21 @@
4118
4118
  "binaryOutputGeneratorsPlaceholder": "統合が未選択",
4119
4119
  "binaryOutputModalities": "提供が必要",
4120
4120
  "binaryOutputModalitiesPlaceholder": "要件なし",
4121
+ "binaryOutputMediaTypes": "厳密な形式",
4122
+ "binaryOutputMediaTypesPlaceholder": "形式の要件なし",
4123
+ "binaryOutputDeclaredFormats": "選択した統合が宣言している形式: {formats}",
4124
+ "binaryOutputMediaTypeUncovered": "このステップが提供することになっている {formats} を、選択されたどの統合も出力できません。出力できる統合を選ぶか、要件を外してください。",
4125
+ "binaryOutputMediaTypeUnverifiable": "選択されたどの統合も {formats} を宣言していませんが、うち一つは形式をまったく宣言していないため、確認できませんでした。ステップはそのまま開始されます。その統合の API で出力できるか確認してください。",
4126
+ "binaryOutputMediaTypeUnusable": "メディアタイプではないため保存されませんでした: {entries}。それぞれ type/subtype の形で入力してください。",
4121
4127
  "binaryOutputGeneratorMissing": "このデプロイメントは次の生成統合を登録していません: {ids}。これらはこのワークスペースではなくデプロイメントのコードで登録します。",
4122
4128
  "binaryOutputModalityUncovered": "このステップが提供することになっている {modalities} を、選択されたどの統合も生成できません。",
4129
+ "binaryOutputModalityRetired": "{modality}(現在は提供されていません。このステップのコンテンツ種別を選び直してください)",
4123
4130
  "binaryOutputModality": {
4124
4131
  "image": "画像",
4125
4132
  "audio": "音声",
4126
4133
  "video": "動画",
4127
- "3d": "3D モデル",
4134
+ "3d-model": "3D モデル",
4135
+ "3d-scene": "3D シーン",
4128
4136
  "document": "ドキュメント"
4129
4137
  }
4130
4138
  },
@@ -5393,6 +5401,7 @@
5393
5401
  "target": "保存サービス",
5394
5402
  "targetNone": "このステップには記録なし",
5395
5403
  "contextServices": "生成のコンテキスト",
5404
+ "mediaTypes": "必要な形式",
5396
5405
  "misdirectedBadge": "別のサービス",
5397
5406
  "unknownBadge": "カタログ外",
5398
5407
  "storedCount": "{outcome}、{count} 件",
@@ -5424,6 +5433,7 @@
5424
5433
  "warning": {
5425
5434
  "unknownServices": "カタログにないサービスが指定されています: {ids}。宣言のまま保持されます。ボードのカタログで ID を確認してください。",
5426
5435
  "targetUnknown": "このステップ自身の保存サービス ({id}) がカタログにもうありません。このため下のどれも照合できませんでした。再登録するか、別のサービスを指定してください。",
5436
+ "undeliveredMediaTypes": "このステップは {formats} を提供するはずでしたが、下のどの成果物もそれを報告していません。",
5427
5437
  "misdirected": "{count} 件の成果物が {target} 以外のサービスに保存されました。",
5428
5438
  "invalidEntries": "サービスと場所のどちらも示さない宣言項目 {count} 件を破棄しました。",
5429
5439
  "omitted": "レポートの上限を超えてさらに {count} 件が宣言されており、一覧には含まれていません。",
@@ -4106,13 +4106,21 @@
4106
4106
  "binaryOutputGeneratorsPlaceholder": "Nie wybrano integracji",
4107
4107
  "binaryOutputModalities": "Musi dostarczyć",
4108
4108
  "binaryOutputModalitiesPlaceholder": "Brak wymagania",
4109
+ "binaryOutputMediaTypes": "Dokładne formaty",
4110
+ "binaryOutputMediaTypesPlaceholder": "Brak wymagania formatu",
4111
+ "binaryOutputDeclaredFormats": "Wybrane integracje deklarują: {formats}",
4112
+ "binaryOutputMediaTypeUncovered": "Żadna wybrana integracja nie tworzy {formats}, które ten krok ma dostarczyć. Wybierz taką, która to potrafi, albo usuń wymaganie.",
4113
+ "binaryOutputMediaTypeUnverifiable": "Żadna wybrana integracja nie deklaruje {formats}, ale jedna z nich nie deklaruje żadnych formatów, więc nie dało się tego sprawdzić. Krok i tak wystartuje; potwierdź w API integracji, że potrafi to wytworzyć.",
4114
+ "binaryOutputMediaTypeUnusable": "Nie zapisano, bo to nie są typy mediów: {entries}. Zapisz każdy jako type/subtype.",
4109
4115
  "binaryOutputGeneratorMissing": "Ta instalacja nie rejestruje tych integracji generatywnych: {ids}. Rejestruje się je w kodzie instalacji, a nie w tym obszarze roboczym.",
4110
4116
  "binaryOutputModalityUncovered": "Żadna wybrana integracja nie tworzy {modalities}, które ten krok ma dostarczyć.",
4117
+ "binaryOutputModalityRetired": "{modality} (już niedostępne — wybierz ponownie typy treści tego kroku)",
4111
4118
  "binaryOutputModality": {
4112
4119
  "image": "Obrazy",
4113
4120
  "audio": "Dźwięk",
4114
4121
  "video": "Wideo",
4115
- "3d": "Modele 3D",
4122
+ "3d-model": "Modele 3D",
4123
+ "3d-scene": "Sceny 3D",
4116
4124
  "document": "Dokumenty"
4117
4125
  }
4118
4126
  },
@@ -5381,6 +5389,7 @@
5381
5389
  "target": "Usługa przechowywania",
5382
5390
  "targetNone": "Nie zapisano dla tego kroku",
5383
5391
  "contextServices": "Kontekst generowania",
5392
+ "mediaTypes": "Wymagane formaty",
5384
5393
  "misdirectedBadge": "Inna usługa",
5385
5394
  "unknownBadge": "Spoza katalogu",
5386
5395
  "storedCount": "{outcome}, 1 artefakt | {outcome}, {count} artefakty | {outcome}, {count} artefaktów",
@@ -5412,6 +5421,7 @@
5412
5421
  "warning": {
5413
5422
  "unknownServices": "Wskazał usługę, której nie ma w katalogu: {ids}. Wpis zachowano w zadeklarowanej postaci; sprawdź identyfikator w katalogu tablicy. | Wskazał usługi, których nie ma w katalogu: {ids}. Wpisy zachowano w zadeklarowanej postaci; sprawdź identyfikatory w katalogu tablicy. | Wskazał usługi, których nie ma w katalogu: {ids}. Wpisy zachowano w zadeklarowanej postaci; sprawdź identyfikatory w katalogu tablicy.",
5414
5423
  "targetUnknown": "Katalog nie zawiera już własnej usługi przechowywania tego kroku ({id}), więc niczego poniżej nie dało się z nią porównać. Zarejestruj ją ponownie albo wskaż krokowi inną usługę.",
5424
+ "undeliveredMediaTypes": "Ten krok miał dostarczyć {formats}, a żaden artefakt poniżej nie zgłasza tego formatu. | Ten krok miał dostarczyć {formats}, a żaden artefakt poniżej nie zgłasza tych formatów. | Ten krok miał dostarczyć {formats}, a żaden artefakt poniżej nie zgłasza tych formatów.",
5415
5425
  "misdirected": "1 artefakt trafił do usługi innej niż {target}. | {count} artefakty trafiły do usługi innej niż {target}. | {count} artefaktów trafiło do usługi innej niż {target}.",
5416
5426
  "invalidEntries": "Odrzucono 1 zadeklarowany wpis: nie wskazywał usługi ani lokalizacji. | Odrzucono {count} zadeklarowane wpisy: nie wskazywały usługi ani lokalizacji. | Odrzucono {count} zadeklarowanych wpisów: nie wskazywały usługi ani lokalizacji.",
5417
5427
  "omitted": "Zadeklarowano jeszcze 1 artefakt ponad limit raportu i nie ma go na liście. | Zadeklarowano jeszcze {count} artefakty ponad limit raportu i nie ma ich na liście. | Zadeklarowano jeszcze {count} artefaktów ponad limit raportu i nie ma ich na liście.",
@@ -4118,13 +4118,21 @@
4118
4118
  "binaryOutputGeneratorsPlaceholder": "Entegrasyon seçilmedi",
4119
4119
  "binaryOutputModalities": "Teslim etmeli",
4120
4120
  "binaryOutputModalitiesPlaceholder": "Gereksinim yok",
4121
+ "binaryOutputMediaTypes": "Kesin biçimler",
4122
+ "binaryOutputMediaTypesPlaceholder": "Biçim gereksinimi yok",
4123
+ "binaryOutputDeclaredFormats": "Seçili entegrasyonların bildirdiği biçimler: {formats}",
4124
+ "binaryOutputMediaTypeUncovered": "Seçili entegrasyonların hiçbiri, bu adımın teslim etmesi gereken {formats} biçimini üretmiyor. Üretebilen birini seçin ya da gereksinimi kaldırın.",
4125
+ "binaryOutputMediaTypeUnverifiable": "Seçili entegrasyonların hiçbiri {formats} bildirmiyor, ancak içlerinden biri hiç biçim bildirmiyor; bu yüzden denetlenemedi. Adım yine de başlar; entegrasyonun API’sinden bunu üretebildiğini doğrulayın.",
4126
+ "binaryOutputMediaTypeUnusable": "Kaydedilmedi, çünkü bunlar ortam türü değil: {entries}. Her birini type/subtype olarak yazın.",
4121
4127
  "binaryOutputGeneratorMissing": "Bu kurulum şu üretken entegrasyonları kaydetmiyor: {ids}. Bunlar bu çalışma alanında değil, kurulumun kodunda kaydedilir.",
4122
4128
  "binaryOutputModalityUncovered": "Seçili entegrasyonların hiçbiri, bu adımın teslim etmesi gereken {modalities} içeriğini üretmiyor.",
4129
+ "binaryOutputModalityRetired": "{modality} (artık sunulmuyor — bu adımın içerik türlerini yeniden seçin)",
4123
4130
  "binaryOutputModality": {
4124
4131
  "image": "Görseller",
4125
4132
  "audio": "Ses",
4126
4133
  "video": "Video",
4127
- "3d": "3B modeller",
4134
+ "3d-model": "3B modeller",
4135
+ "3d-scene": "3B sahneler",
4128
4136
  "document": "Belgeler"
4129
4137
  }
4130
4138
  },
@@ -5393,6 +5401,7 @@
5393
5401
  "target": "Depolama hizmeti",
5394
5402
  "targetNone": "Bu adım için kaydedilmedi",
5395
5403
  "contextServices": "Üretim bağlamı",
5404
+ "mediaTypes": "Gerekli biçimler",
5396
5405
  "misdirectedBadge": "Başka hizmet",
5397
5406
  "unknownBadge": "Katalogda yok",
5398
5407
  "storedCount": "{outcome}, 1 ürün | {outcome}, {count} ürün",
@@ -5424,6 +5433,7 @@
5424
5433
  "warning": {
5425
5434
  "unknownServices": "Katalogda bulunmayan bir hizmet adı verdi: {ids}. Kayıt bildirildiği gibi saklanır; kimliği pano kataloguyla karşılaştır. | Katalogda bulunmayan hizmet adları verdi: {ids}. Kayıtları bildirildiği gibi saklanır; kimlikleri pano kataloguyla karşılaştır.",
5426
5435
  "targetUnknown": "Katalog artık bu adımın kendi depolama hizmetini ({id}) içermiyor, bu yüzden aşağıdakilerin hiçbiri onunla karşılaştırılamadı. Hizmeti yeniden kaydet ya da adımı başka bir hizmete yönlendir.",
5436
+ "undeliveredMediaTypes": "Bu adım {formats} teslim etmeliydi ve aşağıdaki hiçbir ürün bu biçimi bildirmiyor. | Bu adım {formats} teslim etmeliydi ve aşağıdaki hiçbir ürün bu biçimleri bildirmiyor.",
5427
5437
  "misdirected": "1 ürün {target} dışında bir hizmete gitti. | {count} ürün {target} dışında bir hizmete gitti.",
5428
5438
  "invalidEntries": "Bildirilen 1 kayıt düşürüldü: ne hizmet ne de konum belirtiyordu. | Bildirilen {count} kayıt düşürüldü: ne hizmet ne de konum belirtiyorlardı.",
5429
5439
  "omitted": "Raporun sınırının ötesinde 1 ürün daha bildirildi ve listede yer almıyor. | Raporun sınırının ötesinde {count} ürün daha bildirildi ve listede yer almıyor.",
@@ -4106,13 +4106,21 @@
4106
4106
  "binaryOutputGeneratorsPlaceholder": "Інтеграцію не вибрано",
4107
4107
  "binaryOutputModalities": "Має надати",
4108
4108
  "binaryOutputModalitiesPlaceholder": "Без вимоги",
4109
+ "binaryOutputMediaTypes": "Точні формати",
4110
+ "binaryOutputMediaTypesPlaceholder": "Без вимоги до формату",
4111
+ "binaryOutputDeclaredFormats": "Вибрані інтеграції заявляють: {formats}",
4112
+ "binaryOutputMediaTypeUncovered": "Жодна вибрана інтеграція не створює {formats}, які має надати цей крок. Виберіть таку, яка це вміє, або зніміть вимогу.",
4113
+ "binaryOutputMediaTypeUnverifiable": "Жодна вибрана інтеграція не заявляє {formats}, але одна з них взагалі не заявляє форматів, тому перевірити це не вдалося. Крок все одно запуститься; перевірте в API інтеграції, чи може вона це створити.",
4114
+ "binaryOutputMediaTypeUnusable": "Не збережено, бо це не типи медіа: {entries}. Запишіть кожен як type/subtype.",
4109
4115
  "binaryOutputGeneratorMissing": "Ця інсталяція не реєструє ці генеративні інтеграції: {ids}. Їх реєструють у коді інсталяції, а не в цьому робочому просторі.",
4110
4116
  "binaryOutputModalityUncovered": "Жодна вибрана інтеграція не створює {modalities}, які має надати цей крок.",
4117
+ "binaryOutputModalityRetired": "{modality} (більше не пропонується — виберіть типи вмісту цього кроку заново)",
4111
4118
  "binaryOutputModality": {
4112
4119
  "image": "Зображення",
4113
4120
  "audio": "Аудіо",
4114
4121
  "video": "Відео",
4115
- "3d": "3D-моделі",
4122
+ "3d-model": "3D-моделі",
4123
+ "3d-scene": "3D-сцени",
4116
4124
  "document": "Документи"
4117
4125
  }
4118
4126
  },
@@ -5381,6 +5389,7 @@
5381
5389
  "target": "Служба зберігання",
5382
5390
  "targetNone": "Для цього кроку не записано",
5383
5391
  "contextServices": "Контекст генерації",
5392
+ "mediaTypes": "Потрібні формати",
5384
5393
  "misdirectedBadge": "Інша служба",
5385
5394
  "unknownBadge": "Поза каталогом",
5386
5395
  "storedCount": "{outcome}, 1 артефакт | {outcome}, {count} артефакти | {outcome}, {count} артефактів",
@@ -5412,6 +5421,7 @@
5412
5421
  "warning": {
5413
5422
  "unknownServices": "Названо службу, якої немає в каталозі: {ids}. Запис збережено так, як його заявлено; перевірте ідентифікатор у каталозі дошки. | Названо служби, яких немає в каталозі: {ids}. Записи збережено так, як їх заявлено; перевірте ідентифікатори у каталозі дошки. | Названо служби, яких немає в каталозі: {ids}. Записи збережено так, як їх заявлено; перевірте ідентифікатори у каталозі дошки.",
5414
5423
  "targetUnknown": "Каталог більше не містить власної служби зберігання цього кроку ({id}), тому нічого нижче не вдалося з нею зіставити. Зареєструйте її знову або вкажіть кроку іншу службу.",
5424
+ "undeliveredMediaTypes": "Цей крок мав надати {formats}, а жоден артефакт нижче не заявляє цього формату. | Цей крок мав надати {formats}, а жоден артефакт нижче не заявляє цих форматів. | Цей крок мав надати {formats}, а жоден артефакт нижче не заявляє цих форматів.",
5415
5425
  "misdirected": "1 артефакт потрапив до служби, відмінної від {target}. | {count} артефакти потрапили до служби, відмінної від {target}. | {count} артефактів потрапило до служби, відмінної від {target}.",
5416
5426
  "invalidEntries": "Відкинуто 1 заявлений запис: у ньому не було ні служби, ні розташування. | Відкинуто {count} заявлені записи: у них не було ні служби, ні розташування. | Відкинуто {count} заявлених записів: у них не було ні служби, ні розташування.",
5417
5427
  "omitted": "Ще 1 артефакт заявлено понад межу звіту, і його немає в списку. | Ще {count} артефакти заявлено понад межу звіту, і їх немає в списку. | Ще {count} артефактів заявлено понад межу звіту, і їх немає в списку.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.206.0",
3
+ "version": "0.207.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",
@@ -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.213.0"
43
+ "@cat-factory/contracts": "0.214.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@toad-contracts/testing": "0.3.2",