@aicopycoders/exodus 2026.10.2400-beta.2 → 2026.10.2400-beta.3

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.
@@ -65,6 +65,11 @@ export interface ClipFinding {
65
65
  export declare function qcWithoutJudgeWording(qc: ClipQc): ClipQc;
66
66
  export declare const NO_TAKE_HISTORY_LINE = "no history recorded for this clip";
67
67
  export declare function renderQcTakeHistory(takes: ClipQcTake[] | undefined): string[];
68
+ export interface ReplannedIdea {
69
+ framePrompt?: string;
70
+ videoPrompt?: string;
71
+ imageUrl: string;
72
+ }
68
73
  export type ArtifactSubset = {
69
74
  type: "storyboard";
70
75
  storyboard?: unknown;
@@ -100,6 +105,7 @@ export type ArtifactSubset = {
100
105
  voiceFirstBlockedBy?: string[];
101
106
  voicePitchHz?: number;
102
107
  voicePitchMethod?: string;
108
+ replannedIdea?: ReplannedIdea;
103
109
  } | {
104
110
  type: "audio";
105
111
  sceneIndex?: number;
@@ -747,6 +747,7 @@ function clipFromArtifact(sceneIndex, artifact) {
747
747
  voiceFirstBlockedBy: artifact.voiceFirstBlockedBy ?? null,
748
748
  voicePitchHz: artifact.voicePitchHz ?? null,
749
749
  voicePitchMethod: artifact.voicePitchMethod ?? null,
750
+ replannedIdea: artifact.replannedIdea ?? null,
750
751
  };
751
752
  }
752
753
  function keyframeDownload(sceneIndex, imageUrl) {
@@ -903,23 +904,37 @@ function isNarrationMaster(artifact) {
903
904
  artifact.sceneIndex === undefined &&
904
905
  Array.isArray(artifact.narration?.cuts));
905
906
  }
907
+ function withoutLookRider(prompt) {
908
+ return prompt.replace(/\n\nCUTAWAY STYLE: [\s\S]*$/, "");
909
+ }
910
+ function withReplannedPrompts(raw, replanned) {
911
+ let board;
912
+ try {
913
+ board = JSON.parse(raw);
914
+ }
915
+ catch {
916
+ return raw;
917
+ }
918
+ if (!Array.isArray(board.scenes))
919
+ return raw;
920
+ const scenes = board.scenes.map((scene) => {
921
+ const idea = typeof scene.sceneIndex === "number" ? replanned.get(scene.sceneIndex) : undefined;
922
+ return idea ? { ...scene, ...idea } : scene;
923
+ });
924
+ return JSON.stringify({ ...board, scenes }, null, 2);
925
+ }
906
926
  export function planPull(run, items, opts) {
907
927
  const texts = [];
908
928
  const storyboardArtifact = run.nodes
909
929
  .flatMap((n) => n.outputs ?? [])
910
930
  .find((a) => a.type === "storyboard");
911
- let storyboard = null;
912
- if (storyboardArtifact) {
913
- const raw = typeof storyboardArtifact.storyboardJson === "string"
931
+ const storyboardRaw = storyboardArtifact
932
+ ? typeof storyboardArtifact.storyboardJson === "string"
914
933
  ? storyboardArtifact.storyboardJson
915
934
  : storyboardArtifact.storyboard !== undefined
916
935
  ? JSON.stringify(storyboardArtifact.storyboard, null, 2)
917
- : undefined;
918
- if (raw !== undefined) {
919
- storyboard = "storyboard.json";
920
- texts.push({ file: storyboard, body: raw.endsWith("\n") ? raw : `${raw}\n` });
921
- }
922
- }
936
+ : undefined
937
+ : undefined;
923
938
  let reference = null;
924
939
  let productSheet = null;
925
940
  let setPlate = null;
@@ -1056,6 +1071,21 @@ export function planPull(run, items, opts) {
1056
1071
  const pendingRedo = scenesBeingRedone(run, items);
1057
1072
  for (const sceneIndex of pendingRedo)
1058
1073
  clipByScene.delete(sceneIndex);
1074
+ const replanned = new Map();
1075
+ for (const [sceneIndex, clip] of clipByScene) {
1076
+ if (!clip.replannedIdea)
1077
+ continue;
1078
+ const { imageUrl, framePrompt, videoPrompt } = clip.replannedIdea;
1079
+ keyframeByScene.set(sceneIndex, keyframeDownload(sceneIndex, imageUrl));
1080
+ if (framePrompt && videoPrompt)
1081
+ replanned.set(sceneIndex, { framePrompt, videoPrompt });
1082
+ }
1083
+ let storyboard = null;
1084
+ if (storyboardRaw !== undefined) {
1085
+ const body = replanned.size > 0 ? withReplannedPrompts(storyboardRaw, replanned) : storyboardRaw;
1086
+ storyboard = "storyboard.json";
1087
+ texts.unshift({ file: storyboard, body: body.endsWith("\n") ? body : `${body}\n` });
1088
+ }
1059
1089
  const sceneIndexes = [
1060
1090
  ...new Set([
1061
1091
  ...clipByScene.keys(),
@@ -2201,6 +2231,10 @@ export async function statusFlow(runId, json, deps) {
2201
2231
  }
2202
2232
  if (row.clip?.error)
2203
2233
  lines.push(` ${row.clip.error}`);
2234
+ const usedIdea = row.clip?.artifact?.type === "video" ? row.clip.artifact.replannedIdea : undefined;
2235
+ if (usedIdea?.framePrompt) {
2236
+ lines.push(` clip: made from a new idea instead of the approved one. It shows: ${withoutLookRider(usedIdea.framePrompt)}`);
2237
+ }
2204
2238
  if (revoicedByScene.has(sceneIndex))
2205
2239
  lines.push(" voice: cast voice applied");
2206
2240
  if (voiceFirstByScene.has(sceneIndex))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aicopycoders/exodus",
3
- "version": "2026.10.2400-beta.2",
3
+ "version": "2026.10.2400-beta.3",
4
4
  "description": "CLI + Claude Code skills for the Copycoders Exodus ad pipeline",
5
5
  "repository": {
6
6
  "type": "git",