Files
open-design/apps/daemon/tests/media/diagnostics.test.ts
open-design-crew[bot]GitHubCheemsa1chztClaude Opus 5open-design-crew[bot] <299007234+open-design-crew[bot]@users.noreply.github.com>
cf2cc74818 feat(media): route Open Design Cloud generation through Vela (#6500)
* feat(media): route Open Design Cloud generation through Vela

Add Vela image and video adapters behind od media generate with trusted workspace attribution, multi-image support, video polling, and fail-closed behavior.

Expose AMR-specific media defaults and pin the packaged Vela CLI used by the integration.

* fix(nix): refresh pnpm dependency hashes

Refresh the daemon and web fixed-output hashes after the bundled Vela CLI lockfile update. The values come from the failed Nix CI derivations.

* chore(pack): bundle Vela CLI 0.0.31-test.0

0.0.29-test.0 is the build QA hit the 413 with: an agent that reads a
generated image turns it into base64 for the next model request, and a
1.7 MB PNG became 2.13 MiB of attachment that the ingress in front of Link
rejected. 0.0.31-test.0 configures OpenCode to downscale read images to
512px and cap them at 1 MiB of base64.

Verified against this build: the same 1,678,735-byte PNG now reaches the
model as a 512x288 attachment, 326,762 bytes of base64 -- 6.85x smaller --
and the model still describes the image correctly from it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VDg4zffeaNboRdhAh17evB

* chore(pack): bundle Vela CLI 0.0.31-test.1

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VDg4zffeaNboRdhAh17evB

* feat(media): request published aspect ratios from Vela

The Vela image adapter refused every aspect but 1:1 and then sent no size at
all, so the one shape it did accept was still whatever the provider defaulted
to -- the guard proved nothing about the output. Vela now publishes
(aspect_ratio, resolution) profiles per model and per request kind, and all
five image models carry 1:1, 16:9, 9:16, 4:3, and 3:4.

Resolve the pair from `vela media models --json` and pass --aspect-ratio and
--resolution, which the CLI requires together. When one aspect ratio publishes
several resolutions, prefer the model's default tier: asking for a shape should
not quietly change quality or price. When the requested ratio is not published,
fail with the ratios that are, before spawning a render.

The lookup runs per request rather than from a cache. Vela owns which shapes a
model can deliver, so a catalogue that gains or loses one has to take effect
immediately, and one CLI call is nothing beside the generation behind it.

The prompt contract already advertised these five ratios, so this closes the
gap where the prompt promised what the adapter rejected.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VDg4zffeaNboRdhAh17evB

* fix(media): harden multimodal generation and delivery (#6607)

* fix(media): harden multimodal generation and delivery

Use daemon-authored artifact paths for created and modified outputs.
Preserve media references through CLI staging and make Cloud model routing
deterministic. Recover one transient post-tool continuation, surface useful
generation progress, and pin the verified Vela CLI package.

* fix(web): make produced rows keyboard accessible

Expose the row-level open action to keyboard and assistive technology users
while preserving the nested Open and Download controls. Cover Enter and
Space activation in the component regression test.

* build(nix): refresh pnpm dependency hashes

Update the daemon and web fixed-output hashes after the bundled Vela CLI lockfile change.

---------

Co-authored-by: Cheems <94773058+itscheems@users.noreply.github.com>

* fix(media): keep image errors user-friendly and diagnosable

* feat(media): forward quality and resolution to Vela images

`od media generate` had no way to name an image quality tier or output
resolution, so neither reached the Vela CLI. A user asking for "high
quality, 1K" got whatever the model's default profile produced, with the
request surviving only as prose inside the prompt.

Thread both through the existing layers -- CLI flag whitelist, the
generate route, generateMedia, MediaContext -- and append them to the
`vela image gen|edit` argv.

Unspecified stays unspecified. Tiers are priced more than an order of
magnitude apart, so substituting a tier for a caller who named none would
silently re-price every unqualified request; omitting `--quality` is what
leaves the model's own default in charge. Resolution keeps its existing
default-profile selection, because Vela's CLI refuses a resolution
without an aspect ratio and an aspect ratio is always sent.

Both values are validated against `vela media models` before spawning:
quality is published per model (only gpt-image-2 is tiered today) and
resolution must be published for the requested aspect. Failing here costs
nothing, versus a round trip that ends in a provider rejection.

Bump the bundled Vela CLI to 0.0.32, the first release whose `image
gen`/`image edit` publish `--quality`.

* feat(media): teach the agent to map a named size or tier onto the flags

The forwarding change added `--quality` / `--resolution` and told the agent
when NOT to send them, but never said what counts as the user asking. A
request for "2k" came back as `16:9 2K · model default quality`: the size
survived only as prose inside the prompt, where the provider never sees it.

Name the mapping in both prompt copies, in one sentence each. The golden
system-prompt matrix tracks total prompt length, so its two character
counts move with the added text.

* fix(media): match published Vela resolutions and tiers case-insensitively

A real run asked for `--resolution 2k`. Vela publishes `2K`, the lookup
compared the two exactly, and the request failed before reaching the
provider; the agent read the supported value out of the error and retried
ten seconds later with the shifted letter.

Fold case on both sides when matching, and put the catalogue's own spelling
on the wire rather than the caller's. No published value differs from
another by case alone, so nothing becomes ambiguous, and a request that
names a shape or tier the model genuinely lacks still fails the same way.

---------

Co-authored-by: Cheems <94773058+itscheems@users.noreply.github.com>
Co-authored-by: a1chzt <chizblank@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: open-design-crew[bot] <299007234+open-design-crew[bot]@users.noreply.github.com>
2026-08-10 08:53:25 +00:00

35 lines
1.2 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { formatMediaTaskDiagnostic } from '../../src/media/diagnostics.js';
describe('formatMediaTaskDiagnostic', () => {
it('keeps correlation and routing context while redacting secrets', () => {
const line = formatMediaTaskDiagnostic({
event: 'failed',
taskId: 'media-task-123',
runId: 'run-456',
projectId: 'project-789',
surface: 'image',
model: 'vela/gpt-image-2',
providerId: 'vela',
status: 503,
code: 'provider_error',
elapsedMs: 1_234.4,
referenceImageCount: 2,
hasCompositionDir: false,
error: 'upstream rejected Authorization: Bearer abcdefghijklmnop',
});
expect(line).toContain('"task_id":"media-task-123"');
expect(line).toContain('"run_id":"run-456"');
expect(line).toContain('"model_id":"vela/gpt-image-2"');
expect(line).toContain('"provider_id":"vela"');
expect(line).toContain('"code":"provider_error"');
expect(line).toContain('"elapsed_ms":1234');
expect(line).toContain('"reference_image_count":2');
expect(line).toContain('"has_composition_dir":false');
expect(line).toContain('[REDACTED:');
expect(line).not.toContain('abcdefghijklmnop');
});
});