@koda-sl/baker-cli 0.319.0 → 0.321.0-dev.2d7a8f01b

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.
package/README.md CHANGED
@@ -6240,6 +6240,14 @@ Pushing to `main` with changes in `packages/cli/` triggers the GitHub Actions wo
6240
6240
 
6241
6241
  - **0.291.0**: **the image handover Task no longer depends on having uploaded the images.** It was built only from staged `asset.create` image ops, so a Session that audited a live account — found the brand campaign carrying 22 image extensions and the generic one carrying none, and reached for `assets attach` on assets that were *already in the library* — got Google's refusal and nothing else: no upload to hang a Task on, and a CLI hint telling the agent not to raise one by hand because "Baker stages it itself". The one step only the user can take reached them as a sentence in a chat that scrolls away. An image `assets attach` is now **skipped rather than refused**: it still never reaches Google (re-verified live with `validate_only` on v23 *and* v25 against 3865083115, against a control op that validates clean in the same request — `AD_IMAGE` → `UNSUPPORTED_FIELD_TYPE` at customer and campaign level, `MARKETING_IMAGE` → `FIELD_TYPE_INCOMPATIBLE_WITH_CAMPAIGN_TYPE`, `BUSINESS_LOGO` → `CUSTOMER_NOT_VERIFIED`, which is what proves the field type is the objection; **we are not on Google's allowlist for it**), but the skip carries the image and campaign names, recorded on the draft so the next staged op cannot recompute the Task away, and a batch that also carries sitelinks keeps them instead of failing whole. The schema stopped refusing the field type for the same reason: a boundary refusal has no chat to record a handover against.
6242
6242
 
6243
+ - **0.307.2**: an ad with an avatar and cutaways stopped dubbing over her. Whether the video model voices a beat itself is now decided **per beat** — it was one switch for the whole ad, so a single `cast: false` cutaway sent HER beats down the dubbed path too, and the default video model always renders audio: her clip spoke her line while an ElevenLabs narrator read the same script over the top, lips matching neither and the timing matching nothing. Her beats keep their own audio, only the beats she is not in are read, and the ad declares `mixed` — the engine's own word for a video with on-camera lines and narrated ones, which also stops the mislabelled-blueprint safety net folding the narrator into her and merging the beats between them into one take. Two fixes to the closing card, both found by rendering it: the panel behind the mark **adapts to the mark**, measured off the file rather than guessed from its name, so a white logo no longer renders as an empty white box in the corner and again 300px tall on the card; and the CTA button is no longer painted in the palette's dominant hex, which is the colour the plate it sits on is filled with — brand-on-brand, invisible as a button, its white copy reading as one more line of the captions below it. The captions themselves are now **cleared off the closing card**, where what they captioned was the line spoken over it: the call to action, printed a second time under the button that already says it.
6244
+
6245
+ Review follow-ups. **`pickBrandMark` was choosing the wrong file**, which is what made the white mark reachable at all: `\blogo\b` matches `logo-dark.svg` exactly as well as `logo.svg`, so the two tied and the tie broke alphabetically — and `-` sorts before `.`. The variant list now holds `dark`/`light` and their Spanish equivalents (`blanco`, `negativo`, `oscuro`, …), which no client repo's naming was covered by before, and ties break on the shorter name so an unlisted qualifier still loses. **The button's colour is computed against the plate**, not fixed: a near-black pill is the mirror of the bug it replaced on the black plate an ad with no palette gets (and `#14181f` is a legal brand hex), so the fill is the opposite of whatever the plate's luminance says. And **the narrator is cast off the person the ad cast** when no voice is described — naming a narrator of our own lost the fold that used to hand the closing line the presenter's own persona, and the engine's last-resort brief for a speaker in nobody's `cast` is the literal `"ad_narrator voice"`. With `--avatar` that fallback reads the avatar's own subject, persona and speech, which is the case that matters: casting somebody with the flag leaves `cast.description` empty.
6246
+
6247
+ Found by rendering one, which is the only thing that could have found it: **every ad `scaffold-ad` wrote was refused by `baker canvas run` before a single node executed.** The command pins gpt-image-2.5 and asks for the best quality step that model has (`xhigh`), while the `image_generate` node kept a private copy of the quality ladder that stopped at `high` — the same drift its own model roster had already been burned by and fixed for. The ladder now comes from the contract (`IMAGE_QUALITY_STEPS`), which model may use which step is still decided per model against the registry, and a scaffolded ad is now put through the real validator in the command's own tests.
6248
+
6249
+ And what the first finished ad sounded like closed the loop on the rest: **the 196-word realism block is no longer carried into the clip prompt**, only onto the frames. It is 196 words about what may appear in the picture, and by clip time the picture is decided — the clip is generated FROM the frame, and the prompt's own closing direction already says "describe MOTION ONLY". Carried there it made every ad's clip prompt ~479 words against this model's ~200-word budget, which the engine warns about on every clip, with the spoken line buried in the middle of it; the presenter came back saying "la obra acabó cada día" where the script said "avanzó" — a sentence that means nothing, in her own voice, which is the one thing the native path is for. Now 283 words, with the line near the top.
6250
+
6243
6251
  - **0.306.6**: the render review moved onto OpenRouter, where every other model call on this platform already goes. It was calling Gemini directly on the stated grounds that the watch pass uploads the finished mp4 inline and OpenRouter had no route that takes one — it does: a `video_url` content part accepts a base64 data URL, verified end to end (HTTP 200, video tokens counted, the model describing the clip it was sent). That one wrong premise had cost a hand-maintained price table in the backend, token counts round-tripping through the run record to be priced there, a second credential in every Runtime, and a model id pinned to `gemini-3.7-flash` so the table's key would not drift — which is also why the review had quietly stayed off `gemini-3.8-flash`. Now the reply carries `usage.cost`, the Runtime relays the gateway's figure instead of computing one, and the models float (`~google/gemini-flash-latest`, `~google/gemini-pro-latest`). The key it reads is `OPENROUTER_API_KEY`; `GEMINI_API_KEY` is still shipped for HyperFrames' own `snapshot --describe` pass, which is a third-party tool and reads that name.
6244
6252
  - **0.306.0**: review fixes across the avatar and ad paths. **A silent clip is 5 seconds again** — the 8s speaking floor was applied to every `studio animate` call, not just ones that cast an avatar, so plain b-roll was lengthened by three seconds and billed for them. **`scaffold-ad` validates the mark it will actually draw**: the logo gates read the path the spec named rather than the one `fetchClientLogo` had just put there, so a fetched mark either skipped both checks or was refused as unreadable. **The render review is told where the ad is set, who it is for and what the script says** — read off the blueprint beside the canvas; without them the watch pass judged an accent against nothing and still graded the verdict as blocking. **A beat is "too short" by its LINE, not by the clip floor** — the old test needed a 25-word clause, so it fired on all three beats of this command's own documented example. **A voiceover stays a voiceover** — a phrase nobody is shown speaking was being folded into the presenter's clip and said on camera, words that shot was never generated for; the fold now applies only where there is a mouth for the words to contradict. `--voice-id` / `--voice-description` are gone from `baker avatars create` (0.252.0 said so; the flags were still declared and still sent), `end_card.cta` is documented as having no default, and the dead Veo/Omni content-block fallbacks are removed.
6245
6253
 
@@ -17,9 +17,9 @@ import {
17
17
  shouldEscalate
18
18
  } from "./chunk-UIQJZGBG.js";
19
19
 
20
- // ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js
20
+ // ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/509a695c9237c6e3727f1f1f99169917f329e3ff3f7877be290b684e24bf153d/node_modules/safe-stable-stringify/index.js
21
21
  var require_safe_stable_stringify = __commonJS({
22
- "../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/index.js"(exports, module) {
22
+ "../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/509a695c9237c6e3727f1f1f99169917f329e3ff3f7877be290b684e24bf153d/node_modules/safe-stable-stringify/index.js"(exports, module) {
23
23
  "use strict";
24
24
  var { hasOwnProperty } = Object.prototype;
25
25
  var stringify = configure2();
@@ -1111,7 +1111,7 @@ function resolveAdaptFormats(params) {
1111
1111
  return params.formats ?? [];
1112
1112
  }
1113
1113
 
1114
- // ../../node_modules/.pnpm/safe-stable-stringify@2.5.0/node_modules/safe-stable-stringify/esm/wrapper.js
1114
+ // ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/509a695c9237c6e3727f1f1f99169917f329e3ff3f7877be290b684e24bf153d/node_modules/safe-stable-stringify/esm/wrapper.js
1115
1115
  var import__ = __toESM(require_safe_stable_stringify(), 1);
1116
1116
  var configure = import__.default.configure;
1117
1117
  var wrapper_default = import__.default;
@@ -2259,6 +2259,7 @@ function maxInputSlot(kind, model, inputKind) {
2259
2259
  function maxInputReferences(kind, model) {
2260
2260
  return maxInputSlot(kind, model, "image");
2261
2261
  }
2262
+ var IMAGE_QUALITY_STEPS = ["auto", "low", "medium", "high", "xhigh", "max"];
2262
2263
  function supportedQualities(kind, model) {
2263
2264
  const schema = MODEL_REGISTRY[kind]?.[model]?.params.quality;
2264
2265
  return schema?.kind === "string" ? schema.enum : void 0;
@@ -8668,9 +8669,13 @@ var ImageGenerateParams = z17.object({
8668
8669
  prompt: z17.string().min(1),
8669
8670
  aspect_ratio: z17.enum(["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "4:5", "5:4", "21:9", "1:4", "4:1", "1:8", "8:1"]).optional(),
8670
8671
  image_size: z17.enum(OPENROUTER_IMAGE_SIZES).optional(),
8671
- // Rendering quality. OpenRouter models without a quality knob ignore it; the
8672
- // registry gates which models accept it (gpt-image, Gemini).
8673
- quality: z17.enum(["auto", "low", "medium", "high"]).optional(),
8672
+ // Rendering quality, taken from the registry rather than spelled out again here.
8673
+ // OpenRouter models without a quality knob ignore it, and the registry gates which
8674
+ // models accept which step (gpt-image, Gemini). Spelled out, this list stopped at
8675
+ // `high` while gpt-image-2.5 grew `xhigh` and `max` — and since `scaffold-ad` pins
8676
+ // that model and asks for the best step it has, every ad it wrote failed validation
8677
+ // here before a single node ran.
8678
+ quality: z17.enum(IMAGE_QUALITY_STEPS).optional(),
8674
8679
  // Recraft v4 vector controls, sent as the provider's own `controls` block.
8675
8680
  // Registry rejects them on non-Recraft models.
8676
8681
  strength: z17.number().min(0).max(1).optional(),
@@ -9661,4 +9666,4 @@ export {
9661
9666
  defaultRegistry,
9662
9667
  createEngineFromEnv
9663
9668
  };
9664
- //# sourceMappingURL=chunk-WONWYE7B.js.map
9669
+ //# sourceMappingURL=chunk-OLAB3JMW.js.map