@koda-sl/baker-cli 0.102.0 → 0.103.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.
package/README.md CHANGED
@@ -4279,6 +4279,10 @@ This CLI is designed for AI agent consumption. Key patterns:
4279
4279
  10. **Parse the JSON envelope** — check `ok` field before accessing `data`
4280
4280
  11. **Always check `query_context`** in keyword/research responses — it shows the actual location and language used. If `defaults_warning` is present, the query used US/English defaults. Always pass `--location` and `--language` explicitly when targeting a specific market.
4281
4281
 
4282
+ ## Internal Notes
4283
+
4284
+ - **0.103.0**: `baker actions ...` commands now type their `/api/actions/...` request/response payloads from the shared `@baker/api` contract package (also consumed by the backend) instead of hand-written local interfaces. No command, flag, or output-shape changes.
4285
+
4282
4286
  ## Publishing
4283
4287
 
4284
4288
  ### Auto-publish (CI)
package/dist/cli.js CHANGED
@@ -632,11 +632,11 @@ registerSchema({
632
632
  async function recommendSkills(actionId) {
633
633
  const skills = readSkillCatalog(process.cwd());
634
634
  if (skills.length === 0) return [];
635
- const response = await apiPost(
636
- "/api/actions/recommend-skills",
637
- { actionId, skills }
638
- );
639
- return response.data?.recommendations ?? [];
635
+ const response = await apiPost("/api/actions/recommend-skills", {
636
+ actionId,
637
+ skills
638
+ });
639
+ return response.data.recommendations;
640
640
  }
641
641
  function buildHints(recommendations) {
642
642
  if (recommendations.length === 0) {
@@ -684,9 +684,251 @@ var claimCommand = defineCommand({
684
684
  }
685
685
  });
686
686
 
687
+ // ../api/src/actions.ts
688
+ import { z as z2 } from "zod";
689
+ var actionStatusSchema = z2.enum(["pending", "in_progress", "completed", "discarded"]);
690
+ var actionPrioritySchema = z2.enum(["urgent", "high", "medium", "low"]);
691
+ var actionRefResolutionStatusSchema = z2.union([actionStatusSchema, z2.enum(["draft", "not_found"])]);
692
+ var actionDocSchema = z2.object({
693
+ _id: z2.string(),
694
+ _creationTime: z2.number(),
695
+ companyId: z2.string(),
696
+ name: z2.string(),
697
+ description: z2.string(),
698
+ status: actionStatusSchema,
699
+ priority: actionPrioritySchema.optional(),
700
+ requiresHuman: z2.boolean().optional(),
701
+ tags: z2.array(z2.string()).optional(),
702
+ createdByType: z2.enum(["user", "chat", "schedule"]),
703
+ createdByUserId: z2.string().optional(),
704
+ createdByChatId: z2.string().optional(),
705
+ createdByScheduleId: z2.string().optional(),
706
+ createdFromTempId: z2.string().optional(),
707
+ scheduledActionTrigger: z2.enum(["scheduled", "manual"]).optional(),
708
+ scheduledActionTriggerChatId: z2.string().optional(),
709
+ scheduledActionTriggerActorType: z2.enum(["user", "api", "system"]).optional(),
710
+ scheduledActionTriggerActorUserId: z2.string().optional(),
711
+ assigneeUserId: z2.string().optional(),
712
+ activeChatId: z2.string().optional(),
713
+ activeClaimedAt: z2.number().optional(),
714
+ completedAt: z2.number().optional(),
715
+ completedByType: z2.enum(["user", "chat"]).optional(),
716
+ completedByChatId: z2.string().optional(),
717
+ completedNote: z2.string().optional(),
718
+ discardedAt: z2.number().optional(),
719
+ discardedReason: z2.string().optional(),
720
+ createdAt: z2.number(),
721
+ updatedAt: z2.number(),
722
+ searchText: z2.string().optional()
723
+ });
724
+ var actionAssigneeSchema = z2.object({
725
+ userId: z2.string(),
726
+ name: z2.string(),
727
+ image: z2.string().optional()
728
+ });
729
+ var actionDepRefSchema = z2.object({
730
+ id: z2.string(),
731
+ name: z2.string(),
732
+ status: actionStatusSchema
733
+ });
734
+ var actionWithMetaSchema = z2.object({
735
+ action: actionDocSchema,
736
+ blockerCount: z2.number(),
737
+ openBlockerCount: z2.number(),
738
+ isBlocked: z2.boolean(),
739
+ openBlockingCount: z2.number(),
740
+ blockers: z2.array(actionDepRefSchema),
741
+ blocking: z2.array(actionDepRefSchema),
742
+ assignee: actionAssigneeSchema.nullable()
743
+ });
744
+ var actionBucketEntrySchema = z2.object({
745
+ id: z2.string(),
746
+ name: z2.string(),
747
+ description: z2.string(),
748
+ status: actionStatusSchema,
749
+ hint: z2.string(),
750
+ blockedBy: z2.array(z2.object({ id: z2.string(), name: z2.string() })).optional(),
751
+ claimedByChat: z2.object({ id: z2.string(), title: z2.string() }).optional(),
752
+ draftStatus: z2.enum(["in-progress", "completing", "discarding", "updating"]).optional()
753
+ });
754
+ var draftActionEntrySchema = z2.object({
755
+ tempId: z2.string(),
756
+ name: z2.string(),
757
+ description: z2.string(),
758
+ tags: z2.array(z2.string()),
759
+ hint: z2.string(),
760
+ draftStatus: z2.literal("creating")
761
+ });
762
+ var actionBucketsSchema = z2.object({
763
+ claimable: z2.array(actionBucketEntrySchema),
764
+ myClaims: z2.array(actionBucketEntrySchema),
765
+ blocked: z2.array(actionBucketEntrySchema),
766
+ claimedByOthers: z2.array(actionBucketEntrySchema),
767
+ completed: z2.array(actionBucketEntrySchema),
768
+ discarded: z2.array(actionBucketEntrySchema),
769
+ draftCreates: z2.array(draftActionEntrySchema)
770
+ });
771
+ var actionRefStatusResultSchema = z2.object({
772
+ ref: z2.string(),
773
+ status: actionRefResolutionStatusSchema,
774
+ actionId: z2.string().optional(),
775
+ name: z2.string().optional(),
776
+ hint: z2.string().optional()
777
+ });
778
+ var actionDetailSchema = z2.object({
779
+ action: actionDocSchema,
780
+ blockers: z2.array(actionDocSchema),
781
+ blocked: z2.array(actionDocSchema),
782
+ activeChat: z2.object({ _id: z2.string(), title: z2.string() }).nullable()
783
+ });
784
+ var actionDraftOpKindSchema = z2.enum(["create", "update", "complete", "discard", "link", "unlink"]);
785
+ var chatActionDraftOpViewSchema = z2.object({
786
+ kind: actionDraftOpKindSchema,
787
+ targetName: z2.string(),
788
+ targetActionId: z2.string().optional(),
789
+ tempId: z2.string().optional(),
790
+ tags: z2.array(z2.string()).optional(),
791
+ reason: z2.string().optional(),
792
+ note: z2.string().optional(),
793
+ name: z2.string().optional(),
794
+ description: z2.string().optional(),
795
+ blockerName: z2.string().optional(),
796
+ blockerActionId: z2.string().optional(),
797
+ blockerTempId: z2.string().optional()
798
+ });
799
+ var chatDraftViewSchema = z2.object({
800
+ status: z2.enum(["active", "publishing", "discarded", "applied", "none"]),
801
+ count: z2.number(),
802
+ ops: z2.array(chatActionDraftOpViewSchema),
803
+ warnings: z2.array(z2.object({ tempId: z2.string(), name: z2.string() }))
804
+ });
805
+ var actionsClaimDataSchema = z2.object({
806
+ id: z2.string(),
807
+ name: z2.string(),
808
+ description: z2.string(),
809
+ tags: z2.array(z2.string())
810
+ });
811
+ var skillRecommendationSchema = z2.object({
812
+ name: z2.string(),
813
+ reason: z2.string()
814
+ });
815
+ var removeDraftOpTargetSchema = z2.discriminatedUnion("kind", [
816
+ z2.object({ kind: z2.literal("tempId"), tempId: z2.string() }),
817
+ z2.object({
818
+ kind: z2.literal("actionId"),
819
+ actionId: z2.string(),
820
+ opKind: z2.enum(["update", "complete", "discard"])
821
+ })
822
+ ]);
823
+ function okResponse(data) {
824
+ return z2.object({ ok: z2.literal(true), data });
825
+ }
826
+ function okOnlySchema() {
827
+ return z2.object({ ok: z2.literal(true) });
828
+ }
829
+ var actionsCreateRequestSchema = z2.object({
830
+ chatId: z2.string(),
831
+ tempId: z2.string(),
832
+ name: z2.string().min(1),
833
+ description: z2.string(),
834
+ tags: z2.array(z2.string()).optional(),
835
+ priority: actionPrioritySchema.optional()
836
+ });
837
+ var actionsCreateResponseSchema = okResponse(z2.object({ tempId: z2.string() }));
838
+ var actionsGetRequestSchema = z2.object({ actionId: z2.string().min(1) });
839
+ var actionsGetResponseSchema = okResponse(actionDetailSchema.nullable());
840
+ var actionsListRequestSchema = z2.object({
841
+ chatId: z2.string().optional(),
842
+ status: actionStatusSchema.optional(),
843
+ bucketed: z2.boolean().optional(),
844
+ q: z2.string().optional(),
845
+ sort: z2.enum(["recent", "priority"]).optional()
846
+ });
847
+ var actionsListResponseSchema = okResponse(z2.union([actionBucketsSchema, z2.array(actionWithMetaSchema)]));
848
+ var actionsStatusRequestSchema = z2.object({
849
+ refs: z2.array(z2.string().min(1)).max(50),
850
+ chatId: z2.string().min(1).optional()
851
+ });
852
+ var actionsStatusResponseSchema = okResponse(z2.array(actionRefStatusResultSchema));
853
+ var actionsUpdateRequestSchema = z2.object({
854
+ chatId: z2.string(),
855
+ actionId: z2.string(),
856
+ name: z2.string().optional(),
857
+ description: z2.string().optional(),
858
+ tags: z2.array(z2.string()).optional(),
859
+ priority: actionPrioritySchema.nullable().optional()
860
+ });
861
+ var actionsUpdateResponseSchema = okOnlySchema();
862
+ var ACTION_COMPLETE_NOTE_MIN = 20;
863
+ var actionsCompleteNoteMessage = `A completion note (\u2265${ACTION_COMPLETE_NOTE_MIN} chars) describing what was done is required: what changed, where, and the outcome.`;
864
+ var actionsCompleteRequestSchema = z2.object({
865
+ chatId: z2.string(),
866
+ actionRef: z2.string().min(1, "actionRef is required"),
867
+ note: z2.string().optional()
868
+ }).transform((body) => ({ ...body, note: (body.note ?? "").trim() })).refine((body) => body.note.length >= ACTION_COMPLETE_NOTE_MIN, {
869
+ message: actionsCompleteNoteMessage,
870
+ path: ["note"]
871
+ });
872
+ var actionsCompleteResponseSchema = okOnlySchema();
873
+ var actionsDiscardRequestSchema = z2.object({
874
+ chatId: z2.string(),
875
+ actionId: z2.string(),
876
+ reason: z2.string().optional()
877
+ });
878
+ var actionsDiscardResponseSchema = okOnlySchema();
879
+ var actionsLinkRequestSchema = z2.object({
880
+ chatId: z2.string(),
881
+ blockerRef: z2.string(),
882
+ blockedRef: z2.string()
883
+ });
884
+ var actionsLinkResponseSchema = okOnlySchema();
885
+ var actionsUnlinkRequestSchema = z2.object({
886
+ chatId: z2.string(),
887
+ blockerId: z2.string(),
888
+ blockedId: z2.string()
889
+ });
890
+ var actionsUnlinkResponseSchema = okOnlySchema();
891
+ var actionsClaimRequestSchema = z2.object({
892
+ actionId: z2.string(),
893
+ chatId: z2.string()
894
+ });
895
+ var actionsClaimResponseSchema = okResponse(actionsClaimDataSchema.nullable());
896
+ var actionsReleaseRequestSchema = z2.object({
897
+ actionId: z2.string(),
898
+ chatId: z2.string()
899
+ });
900
+ var actionsReleaseResponseSchema = okOnlySchema();
901
+ var actionsRecommendSkillsRequestSchema = z2.object({
902
+ actionId: z2.string().min(1),
903
+ // Bounds track what the CLI actually sends (descriptions truncated to ~600 chars); they
904
+ // cap the prompt size a direct API-key caller can push into the billed LLM call.
905
+ skills: z2.array(
906
+ z2.object({
907
+ name: z2.string().min(1).max(120),
908
+ description: z2.string().max(800)
909
+ })
910
+ ).max(80)
911
+ });
912
+ var actionsRecommendSkillsResponseSchema = okResponse(
913
+ z2.object({ recommendations: z2.array(skillRecommendationSchema) })
914
+ );
915
+ var actionsLogRequestSchema = z2.object({
916
+ fromMs: z2.number(),
917
+ toMs: z2.number()
918
+ });
919
+ var actionsLogResponseSchema = okResponse(z2.array(actionWithMetaSchema));
920
+ var actionsDraftRequestSchema = z2.object({ chatId: z2.string() });
921
+ var actionsDraftResponseSchema = okResponse(chatDraftViewSchema);
922
+ var actionsDraftClearRequestSchema = z2.object({ chatId: z2.string() });
923
+ var actionsDraftClearResponseSchema = okOnlySchema();
924
+ var actionsRemoveDraftOpRequestSchema = z2.object({
925
+ chatId: z2.string(),
926
+ target: removeDraftOpTargetSchema
927
+ });
928
+ var actionsRemoveDraftOpResponseSchema = okOnlySchema();
929
+
687
930
  // src/commands/actions/complete.ts
688
931
  import { defineCommand as defineCommand2 } from "citty";
689
- var COMPLETE_NOTE_MIN = 20;
690
932
  registerSchema({
691
933
  command: "actions.complete",
692
934
  description: "Stage completion of an action (it was DONE). Accepts a real action ID (must be claimed) or a temp ID from the same draft. The action becomes completed when the chat is published. --note is REQUIRED and must describe what you actually did to resolve it (what changed, where, and the outcome) \u2014 it is the client-facing record surfaced by `baker actions log`. Do NOT use this to drop an action you no longer want \u2014 to remove a staged op use `actions draft remove`, to close an unwanted published action use `actions discard`.",
@@ -723,9 +965,9 @@ var completeCommand = defineCommand2({
723
965
  validateConvexId(id);
724
966
  }
725
967
  const note = args.note?.trim() ?? "";
726
- if (note.length < COMPLETE_NOTE_MIN) {
968
+ if (note.length < ACTION_COMPLETE_NOTE_MIN) {
727
969
  failValidation(
728
- `--note is required and must describe what you did (\u2265${COMPLETE_NOTE_MIN} chars): what changed, where, and the outcome. This becomes the client-facing record surfaced by \`baker actions log\`.`
970
+ `--note is required and must describe what you did (\u2265${ACTION_COMPLETE_NOTE_MIN} chars): what changed, where, and the outcome. This becomes the client-facing record surfaced by \`baker actions log\`.`
729
971
  );
730
972
  }
731
973
  const chatId = requireChatId();
@@ -8559,27 +8801,27 @@ import path5 from "path";
8559
8801
  import { defineCommand as defineCommand82 } from "citty";
8560
8802
 
8561
8803
  // src/engine/scaffold/staticAd.ts
8562
- import { z as z2 } from "zod";
8804
+ import { z as z3 } from "zod";
8563
8805
  var GEN_ASPECT_RATIOS = /* @__PURE__ */ new Set(["1:1", "4:5", "9:16", "16:9", "4:3", "3:4", "2:3", "3:2", "21:9"]);
8564
8806
  var DEFAULT_ASPECT_RATIO = "9:16";
8565
- var Blueprint = z2.object({
8566
- meta: z2.object({ estimated_aspect_ratio: z2.string().optional() }).loose().optional(),
8567
- text_content: z2.array(z2.object({ text: z2.string().optional() }).loose()).optional()
8807
+ var Blueprint = z3.object({
8808
+ meta: z3.object({ estimated_aspect_ratio: z3.string().optional() }).loose().optional(),
8809
+ text_content: z3.array(z3.object({ text: z3.string().optional() }).loose()).optional()
8568
8810
  }).loose();
8569
- var ElementLocator = z2.object({
8570
- collection: z2.enum(["subjects", "people", "brands_logos"]),
8571
- index: z2.number().int().nonnegative()
8811
+ var ElementLocator = z3.object({
8812
+ collection: z3.enum(["subjects", "people", "brands_logos"]),
8813
+ index: z3.number().int().nonnegative()
8572
8814
  }).loose();
8573
- var MainElement = z2.object({
8815
+ var MainElement = z3.object({
8574
8816
  // logo | product | person | animal | badge | other
8575
- type: z2.string(),
8576
- label: z2.string().optional(),
8577
- description: z2.string().optional(),
8578
- expression: z2.string().nullable().optional(),
8579
- reason: z2.string().optional(),
8817
+ type: z3.string(),
8818
+ label: z3.string().optional(),
8819
+ description: z3.string().optional(),
8820
+ expression: z3.string().nullable().optional(),
8821
+ reason: z3.string().optional(),
8580
8822
  locator: ElementLocator.optional()
8581
8823
  }).loose();
8582
- var MainElements = z2.array(MainElement);
8824
+ var MainElements = z3.array(MainElement);
8583
8825
  function sanitizeId(raw, fallback) {
8584
8826
  const id = raw.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
8585
8827
  return /^[a-z]/.test(id) ? id : `${fallback}_${id}`.replace(/_+$/g, "") || fallback;
@@ -9074,7 +9316,7 @@ import { toCardinal as nwKo } from "n2words/ko-KR";
9074
9316
  import { toCardinal as nwNl } from "n2words/nl-NL";
9075
9317
  import { toCardinal as nwPl } from "n2words/pl-PL";
9076
9318
  import { toCardinal as nwPt } from "n2words/pt-PT";
9077
- import { z as z3 } from "zod";
9319
+ import { z as z4 } from "zod";
9078
9320
 
9079
9321
  // src/engine/scaffold/lib/shoot-modes.ts
9080
9322
  var SHOOT_MODES = [
@@ -9331,49 +9573,49 @@ function trimArgs(durationS, offsetS = 0) {
9331
9573
  "{{out.video}}"
9332
9574
  ];
9333
9575
  }
9334
- var FrameAsset = z3.object({ url: z3.string().optional() }).loose().optional();
9335
- var DialogueLine = z3.object({
9336
- speaker: z3.string().optional(),
9337
- line: z3.string().optional(),
9576
+ var FrameAsset = z4.object({ url: z4.string().optional() }).loose().optional();
9577
+ var DialogueLine = z4.object({
9578
+ speaker: z4.string().optional(),
9579
+ line: z4.string().optional(),
9338
9580
  // Absolute seconds on the source timeline (the deconstruct emits both).
9339
- start_s: z3.number().optional(),
9340
- end_s: z3.number().optional(),
9341
- delivery: z3.string().optional(),
9342
- voice_description: z3.string().optional()
9581
+ start_s: z4.number().optional(),
9582
+ end_s: z4.number().optional(),
9583
+ delivery: z4.string().optional(),
9584
+ voice_description: z4.string().optional()
9343
9585
  }).loose();
9344
- var Sfx = z3.object({
9345
- at_s: z3.number().optional(),
9346
- duration_s: z3.number().optional(),
9347
- sound_effect_prompt: z3.string().optional(),
9348
- description: z3.string().optional()
9586
+ var Sfx = z4.object({
9587
+ at_s: z4.number().optional(),
9588
+ duration_s: z4.number().optional(),
9589
+ sound_effect_prompt: z4.string().optional(),
9590
+ description: z4.string().optional()
9349
9591
  }).loose();
9350
- var CompositionRegion = z3.object({
9592
+ var CompositionRegion = z4.object({
9351
9593
  // full | top | bottom | left | right | inset
9352
- panel: z3.string().optional(),
9594
+ panel: z4.string().optional(),
9353
9595
  // 9-grid anchor for an `inset` presenter box.
9354
- position: z3.string().optional(),
9355
- is_presenter: z3.boolean().optional(),
9596
+ position: z4.string().optional(),
9597
+ is_presenter: z4.boolean().optional(),
9356
9598
  // The cast id shown/speaking in this region (routes lip-sync + element refs).
9357
- cast_ref: z3.string().optional(),
9358
- summary: z3.string().optional(),
9359
- frame_prompt: z3.string().optional(),
9360
- motion_prompt: z3.string().optional()
9599
+ cast_ref: z4.string().optional(),
9600
+ summary: z4.string().optional(),
9601
+ frame_prompt: z4.string().optional(),
9602
+ motion_prompt: z4.string().optional()
9361
9603
  }).loose();
9362
- var SceneComposition = z3.object({
9604
+ var SceneComposition = z4.object({
9363
9605
  // full_frame (default) | split_screen | pip | keyed_overlay
9364
- layout: z3.string().optional(),
9606
+ layout: z4.string().optional(),
9365
9607
  // split_screen only: vertical (top/bottom) | horizontal (left/right).
9366
- split_axis: z3.string().optional(),
9367
- regions: z3.array(CompositionRegion).optional()
9608
+ split_axis: z4.string().optional(),
9609
+ regions: z4.array(CompositionRegion).optional()
9368
9610
  }).loose();
9369
- var CameraMotion = z3.object({ movement: z3.string().optional(), detail: z3.string().optional() }).loose();
9370
- var TranscriptWord = z3.object({ text: z3.string().optional() }).loose();
9371
- var Scene = z3.object({
9372
- start_s: z3.number().optional(),
9373
- end_s: z3.number().optional(),
9374
- duration_s: z3.number().optional(),
9375
- summary: z3.string().optional(),
9376
- action_detail: z3.string().optional(),
9611
+ var CameraMotion = z4.object({ movement: z4.string().optional(), detail: z4.string().optional() }).loose();
9612
+ var TranscriptWord = z4.object({ text: z4.string().optional() }).loose();
9613
+ var Scene = z4.object({
9614
+ start_s: z4.number().optional(),
9615
+ end_s: z4.number().optional(),
9616
+ duration_s: z4.number().optional(),
9617
+ summary: z4.string().optional(),
9618
+ action_detail: z4.string().optional(),
9377
9619
  // The scene's spatial layout. Absent/full_frame ⇒ one uncut shot (default path).
9378
9620
  // A layered layout (split_screen/pip/keyed_overlay) with regions ⇒ the scaffold
9379
9621
  // builds one clip per region and stacks/overlays them into the scene picture.
@@ -9381,77 +9623,77 @@ var Scene = z3.object({
9381
9623
  // The capture "look" for this scene — selected from the ad-native shoot-mode
9382
9624
  // grammar (see lib/shoot-modes.ts). When absent the scaffold auto-derives a
9383
9625
  // UGC/product mode; a human can override per scene by setting this.
9384
- shoot_mode: z3.string().optional(),
9626
+ shoot_mode: z4.string().optional(),
9385
9627
  // Diegetic ambient the clip's native audio should carry (no music). When
9386
9628
  // absent the scene falls back to its shoot mode's default ambience.
9387
- ambient: z3.string().optional(),
9629
+ ambient: z4.string().optional(),
9388
9630
  camera_motion: CameraMotion.optional(),
9389
- start_frame_prompt: z3.string().optional(),
9390
- end_frame_prompt: z3.string().optional(),
9391
- motion_prompt: z3.string().optional(),
9631
+ start_frame_prompt: z4.string().optional(),
9632
+ end_frame_prompt: z4.string().optional(),
9633
+ motion_prompt: z4.string().optional(),
9392
9634
  // The scene's role in the ad's persuasion arc (DECON-supplied); drives the
9393
9635
  // script re-craft checklist. Inferred from position when absent.
9394
- narrative_role: z3.string().optional(),
9636
+ narrative_role: z4.string().optional(),
9395
9637
  // DECON-supplied on the HOOK scene: the engineered physical/emotional state that
9396
9638
  // makes the first frame stop the scroll (sweaty/breathless/urgent …). Injected
9397
9639
  // into the hook's start-frame description so the generator renders that state,
9398
9640
  // not a calm influencer (CCA-11).
9399
- hook_mechanic: z3.object({ mechanic: z3.string().optional(), why_it_stops_scroll: z3.string().optional() }).loose().optional(),
9641
+ hook_mechanic: z4.object({ mechanic: z4.string().optional(), why_it_stops_scroll: z4.string().optional() }).loose().optional(),
9400
9642
  // DECON-supplied per-scene location (so a gym hook isn't flattened to "home").
9401
- scene_setting: z3.string().optional(),
9643
+ scene_setting: z4.string().optional(),
9402
9644
  // How this scene cuts to the next (DECON-supplied). A recognized non-cut type
9403
9645
  // (fade/whip/zoom/dissolve/swipe) is reproduced as an ffmpeg xfade at the
9404
9646
  // boundary; cut/match_cut/none/other stay hard cuts. The last scene's value is
9405
9647
  // ignored (nothing follows it).
9406
- transition_out: z3.object({ type: z3.string().optional(), description: z3.string().optional() }).loose().optional(),
9407
- dialogue: z3.array(DialogueLine).optional(),
9408
- sfx: z3.array(Sfx).optional(),
9409
- overlays: z3.array(z3.unknown()).optional(),
9410
- floating_elements: z3.array(z3.unknown()).optional(),
9411
- transcript_slice: z3.array(TranscriptWord).optional(),
9648
+ transition_out: z4.object({ type: z4.string().optional(), description: z4.string().optional() }).loose().optional(),
9649
+ dialogue: z4.array(DialogueLine).optional(),
9650
+ sfx: z4.array(Sfx).optional(),
9651
+ overlays: z4.array(z4.unknown()).optional(),
9652
+ floating_elements: z4.array(z4.unknown()).optional(),
9653
+ transcript_slice: z4.array(TranscriptWord).optional(),
9412
9654
  start_frame_asset: FrameAsset,
9413
9655
  end_frame_asset: FrameAsset,
9414
9656
  // DECON-supplied: true when this scene is a length-split CONTINUATION of the
9415
9657
  // previous one (the SAME physical shot, broken up only because it exceeded the
9416
9658
  // clip ceiling). The scaffold then shares the splice keyframe — this scene's
9417
9659
  // start frame IS the previous scene's end frame — so the join is seamless.
9418
- continues_previous: z3.boolean().optional()
9660
+ continues_previous: z4.boolean().optional()
9419
9661
  }).loose();
9420
- var VideoBlueprint = z3.object({
9421
- source: z3.object({ aspect_ratio: z3.string().optional(), duration_s: z3.number().optional() }).loose().optional(),
9422
- global: z3.object({
9423
- music: z3.object({
9424
- present: z3.boolean().optional(),
9425
- music_prompt: z3.string().optional(),
9662
+ var VideoBlueprint = z4.object({
9663
+ source: z4.object({ aspect_ratio: z4.string().optional(), duration_s: z4.number().optional() }).loose().optional(),
9664
+ global: z4.object({
9665
+ music: z4.object({
9666
+ present: z4.boolean().optional(),
9667
+ music_prompt: z4.string().optional(),
9426
9668
  // Absolute second the music enters in the reference (the bed often
9427
9669
  // kicks in mid-ad, after the hook). We start the regenerated track here
9428
9670
  // instead of at 0 so the timing matches.
9429
- starts_at_s: z3.number().optional(),
9671
+ starts_at_s: z4.number().optional(),
9430
9672
  // Populated by the deconstruct when AudD (Shazam-style) recognizes the
9431
9673
  // reference track. We never reuse it — only style the regenerated bed.
9432
- identified_track: z3.object({ title: z3.string().optional(), artist: z3.string().optional() }).loose().nullish()
9674
+ identified_track: z4.object({ title: z4.string().optional(), artist: z4.string().optional() }).loose().nullish()
9433
9675
  }).loose().optional(),
9434
- cast: z3.array(
9435
- z3.object({
9436
- id: z3.string().optional(),
9437
- description: z3.string().optional(),
9676
+ cast: z4.array(
9677
+ z4.object({
9678
+ id: z4.string().optional(),
9679
+ description: z4.string().optional(),
9438
9680
  // The deconstruct's note on the target-market localization (e.g. "native
9439
9681
  // French speaker") — read to derive the spoken-track language code.
9440
- market_localization_note: z3.string().optional()
9682
+ market_localization_note: z4.string().optional()
9441
9683
  }).loose()
9442
9684
  ).optional(),
9443
- voiceover: z3.object({
9685
+ voiceover: z4.object({
9444
9686
  // on_camera | mixed → mouths are on screen (lip-sync candidates);
9445
9687
  // voiceover | none → narration over the picture (no lip-sync).
9446
- mode: z3.string().optional(),
9447
- voice_description: z3.string().optional(),
9448
- persona: z3.string().optional()
9688
+ mode: z4.string().optional(),
9689
+ voice_description: z4.string().optional(),
9690
+ persona: z4.string().optional()
9449
9691
  }).loose().optional(),
9450
9692
  // Visual palette — read only to colour a clean brand-card/CTA plate (the
9451
9693
  // first hex is the dominant brand colour); never to drive frame generation.
9452
- style: z3.object({ palette: z3.array(z3.object({ hex: z3.string().optional() }).loose()).optional() }).loose().optional()
9694
+ style: z4.object({ palette: z4.array(z4.object({ hex: z4.string().optional() }).loose()).optional() }).loose().optional()
9453
9695
  }).loose().optional(),
9454
- scenes: z3.array(Scene).min(1)
9696
+ scenes: z4.array(Scene).min(1)
9455
9697
  }).loose();
9456
9698
  function injectHookPhysicality(blueprint) {
9457
9699
  for (const scene of blueprint.scenes) {
@@ -9461,26 +9703,26 @@ function injectHookPhysicality(blueprint) {
9461
9703
  scene.start_frame_prompt = `${prompt} The subject's physical state IS the scroll-stopper \u2014 render it explicitly, not a calm pose: ${why}.`;
9462
9704
  }
9463
9705
  }
9464
- var AppearsItem = z3.union([z3.number(), z3.object({ scene: z3.number(), edge: z3.string().optional() }).loose()]);
9465
- var RecurringElement = z3.object({
9706
+ var AppearsItem = z4.union([z4.number(), z4.object({ scene: z4.number(), edge: z4.string().optional() }).loose()]);
9707
+ var RecurringElement = z4.object({
9466
9708
  // person | animal | product | logo | badge | other
9467
- type: z3.string(),
9468
- label: z3.string().optional(),
9469
- description: z3.string().optional(),
9470
- expression: z3.string().nullable().optional(),
9709
+ type: z4.string(),
9710
+ label: z4.string().optional(),
9711
+ description: z4.string().optional(),
9712
+ expression: z4.string().nullable().optional(),
9471
9713
  // When the element maps to a global cast entry, its stable id (for annotation).
9472
- cast_id: z3.string().nullable().optional(),
9714
+ cast_id: z4.string().nullable().optional(),
9473
9715
  // The label of another element that is the SAME individual as this one, shown
9474
9716
  // in a DIFFERENT wardrobe/persona/state (e.g. one creator playing skeptic in a
9475
9717
  // pink shirt and believer in a white shirt). Each look gets its own reference
9476
9718
  // slot, but the face/identity must stay identical across them.
9477
- same_as: z3.string().nullable().optional(),
9719
+ same_as: z4.string().nullable().optional(),
9478
9720
  // Scenes the element appears in. Either a bare list of scene indices (both
9479
9721
  // edges) or per-{scene,edge} entries. Both forms are accepted and merged.
9480
- scenes: z3.array(z3.number()).optional(),
9481
- appears_in: z3.array(AppearsItem).optional()
9722
+ scenes: z4.array(z4.number()).optional(),
9723
+ appears_in: z4.array(AppearsItem).optional()
9482
9724
  }).loose();
9483
- var RecurringElements = z3.array(RecurringElement);
9725
+ var RecurringElements = z4.array(RecurringElement);
9484
9726
  function sanitizeId2(raw, fallback) {
9485
9727
  const id = raw.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
9486
9728
  return /^[a-z]/.test(id) ? id : `${fallback}_${id}`.replace(/_+$/g, "") || fallback;
@@ -10943,25 +11185,25 @@ function buildSfxMusic(blueprint, nodes) {
10943
11185
  }
10944
11186
  return tracks;
10945
11187
  }
10946
- var OverlayStyle = z3.object({ color_hex: z3.string().optional(), background: z3.string().optional(), size: z3.string().optional() }).loose();
10947
- var Overlay = z3.object({
10948
- text: z3.string().optional(),
10949
- appears_at_s: z3.number().optional(),
10950
- duration_s: z3.number().optional(),
10951
- position: z3.string().optional(),
10952
- role: z3.string().optional(),
10953
- animation: z3.string().optional(),
10954
- animation_detail: z3.string().optional(),
11188
+ var OverlayStyle = z4.object({ color_hex: z4.string().optional(), background: z4.string().optional(), size: z4.string().optional() }).loose();
11189
+ var Overlay = z4.object({
11190
+ text: z4.string().optional(),
11191
+ appears_at_s: z4.number().optional(),
11192
+ duration_s: z4.number().optional(),
11193
+ position: z4.string().optional(),
11194
+ role: z4.string().optional(),
11195
+ animation: z4.string().optional(),
11196
+ animation_detail: z4.string().optional(),
10955
11197
  style: OverlayStyle.optional()
10956
11198
  }).loose();
10957
- var FloatingElement = z3.object({
10958
- kind: z3.string().optional(),
10959
- description: z3.string().optional(),
10960
- brand_name: z3.string().nullish(),
10961
- what_it_represents: z3.string().optional(),
10962
- appears_at_s: z3.number().optional(),
10963
- duration_s: z3.number().optional(),
10964
- position: z3.string().optional()
11199
+ var FloatingElement = z4.object({
11200
+ kind: z4.string().optional(),
11201
+ description: z4.string().optional(),
11202
+ brand_name: z4.string().nullish(),
11203
+ what_it_represents: z4.string().optional(),
11204
+ appears_at_s: z4.number().optional(),
11205
+ duration_s: z4.number().optional(),
11206
+ position: z4.string().optional()
10965
11207
  }).loose();
10966
11208
  function escapeHtml(s) {
10967
11209
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
@@ -10993,7 +11235,7 @@ function positionClass(position) {
10993
11235
  function collectCaptions(blueprint) {
10994
11236
  return blueprint.scenes.flatMap((scene) => {
10995
11237
  const sceneStart = scene.start_s ?? 0;
10996
- const overlays = z3.array(Overlay).safeParse(scene.overlays ?? []);
11238
+ const overlays = z4.array(Overlay).safeParse(scene.overlays ?? []);
10997
11239
  return overlays.success ? overlays.data.filter((ov) => Boolean(ov.text?.trim())).map((ov) => {
10998
11240
  const at = ov.appears_at_s ?? sceneStart;
10999
11241
  return { text: ov.text.trim(), at, end: at + (ov.duration_s ?? 2.5), ov };
@@ -11102,7 +11344,7 @@ function buildOverlayHtml(input) {
11102
11344
  if (ovParts.length > 0) blocks.push(ovParts.join("\n"));
11103
11345
  for (const scene of blueprint.scenes) {
11104
11346
  const sceneStart = scene.start_s ?? 0;
11105
- const floats = z3.array(FloatingElement).safeParse(scene.floating_elements ?? []);
11347
+ const floats = z4.array(FloatingElement).safeParse(scene.floating_elements ?? []);
11106
11348
  const parts = (floats.success ? floats.data.map((fe) => floatingStub(fe, sceneStart)) : []).filter(Boolean);
11107
11349
  const pip = uiPipStub(scene);
11108
11350
  if (pip) parts.push(pip);
@@ -11359,8 +11601,8 @@ function buildMotionBoard(blueprint) {
11359
11601
  const end_s = scene.end_s ?? start_s + sceneDurationS(scene);
11360
11602
  cursor = end_s;
11361
11603
  const spoken = sceneSpokenText(scene);
11362
- const overlays = z3.array(Overlay).safeParse(scene.overlays ?? []);
11363
- const floats = z3.array(FloatingElement).safeParse(scene.floating_elements ?? []);
11604
+ const overlays = z4.array(Overlay).safeParse(scene.overlays ?? []);
11605
+ const floats = z4.array(FloatingElement).safeParse(scene.floating_elements ?? []);
11364
11606
  const graphics = [
11365
11607
  ...(overlays.success ? overlays.data : []).filter((ov) => ov.text?.trim()).map((ov) => ({
11366
11608
  kind: "text",