@jokerized/decksmith 0.9.6 → 0.9.8
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/dist/cli.js +134 -16
- package/dist/index.js +133 -16
- package/dist/mcp.js +133 -16
- package/dist/types/plan/codex.d.ts +2 -0
- package/dist/types/plan/variety.d.ts +12 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -23530,9 +23530,9 @@ function literalFindings(storyboard, source, opts = {}) {
|
|
|
23530
23530
|
prevKey = { id: beat2.id, key };
|
|
23531
23531
|
});
|
|
23532
23532
|
const tables = beats.filter((b) => b.literal?.kind === "table");
|
|
23533
|
-
|
|
23533
|
+
for (const t2 of tables.slice(1))
|
|
23534
23534
|
out.push(
|
|
23535
|
-
|
|
23535
|
+
`${t2.id} draws a table, and so does ${tables[0]?.id}. A table reads like a report: keep at most one, and draw this one as what it lists \u2014 results as \`place\` (margin or plane), what was and was not evaluated as \`place\` sets, experiments not run as \`place\` combos, earlier approaches as \`prior-work\`, reported costs as \`place\` length \u2014 or leave \`literal\` null where nothing is honest.`
|
|
23536
23536
|
);
|
|
23537
23537
|
const byVectors = /* @__PURE__ */ new Map();
|
|
23538
23538
|
for (const b of beats) {
|
|
@@ -23541,20 +23541,20 @@ function literalFindings(storyboard, source, opts = {}) {
|
|
|
23541
23541
|
const key = JSON.stringify(l.vectors.map((v) => v.values));
|
|
23542
23542
|
byVectors.set(key, [...byVectors.get(key) ?? [], b.id]);
|
|
23543
23543
|
}
|
|
23544
|
-
for (const
|
|
23545
|
-
|
|
23544
|
+
for (const [first, ...rest] of byVectors.values())
|
|
23545
|
+
for (const id2 of rest)
|
|
23546
23546
|
out.push(
|
|
23547
|
-
`${
|
|
23547
|
+
`${id2} runs operate on the same vectors as ${first}, so the deck shows one chart twice. Give its chain to ${first} (one longer chain), or draw ${id2} another way.`
|
|
23548
23548
|
);
|
|
23549
23549
|
const forms = /* @__PURE__ */ new Map();
|
|
23550
23550
|
for (const b of beats) {
|
|
23551
23551
|
const f = b.literal && wordForm(b.literal);
|
|
23552
23552
|
if (f) forms.set(f, [...forms.get(f) ?? [], b.id]);
|
|
23553
23553
|
}
|
|
23554
|
-
for (const [f,
|
|
23555
|
-
|
|
23554
|
+
for (const [f, [first, ...rest]] of forms)
|
|
23555
|
+
for (const id2 of rest)
|
|
23556
23556
|
out.push(
|
|
23557
|
-
|
|
23557
|
+
`${id2} draws \`${f}\`, as ${first} already does. It draws no picture, so a second one is the same slide again: draw ${id2} another way, or with \`literal\` null.`
|
|
23558
23558
|
);
|
|
23559
23559
|
return out;
|
|
23560
23560
|
}
|
|
@@ -23630,15 +23630,26 @@ function isScene(beat2, design2) {
|
|
|
23630
23630
|
return pictured(p.backdrop);
|
|
23631
23631
|
}
|
|
23632
23632
|
}
|
|
23633
|
+
function shapesFor(storyboard, i, offered, images) {
|
|
23634
|
+
const beats = storyboard.beats;
|
|
23635
|
+
const others = beats.filter((_, k) => k !== i);
|
|
23636
|
+
const kinetic2 = others.filter((b) => b.archetype === "kinetic").length;
|
|
23637
|
+
const panels = others.filter((b) => PANEL_ARCHETYPES.has(b.archetype)).length;
|
|
23638
|
+
const panelCap = panelsAllowed(beats.length, images);
|
|
23639
|
+
return offered.filter(
|
|
23640
|
+
(a) => a !== beats[i - 1]?.archetype && a !== beats[i + 1]?.archetype && (a !== "title" || i === 0) && (a !== "kinetic" || kinetic2 < KINETIC_MAX) && (!PANEL_ARCHETYPES.has(a) || panels < panelCap)
|
|
23641
|
+
);
|
|
23642
|
+
}
|
|
23633
23643
|
function varietyFindings(storyboard, images, design2) {
|
|
23634
23644
|
const beats = storyboard.beats;
|
|
23635
23645
|
const out = [];
|
|
23646
|
+
const avoid = (beat2, other) => [beat2.archetype, other?.archetype].filter((x, k, xs) => x !== void 0 && xs.indexOf(x) === k).map((x) => `\`${x}\``).join(" or ");
|
|
23636
23647
|
for (let i = 1; i < beats.length; i++) {
|
|
23637
23648
|
const a = beats[i - 1];
|
|
23638
23649
|
const b = beats[i];
|
|
23639
23650
|
if (a.archetype === b.archetype) {
|
|
23640
23651
|
out.push(
|
|
23641
|
-
`${a.id} and ${b.id} are both \`${a.archetype}\`, next to each other.
|
|
23652
|
+
`${a.id} and ${b.id} are both \`${a.archetype}\`, next to each other. Redraw ${a.id} as anything but ${avoid(a, beats[i - 2])}, or ${b.id} as anything but ${avoid(b, beats[i + 1])}.`
|
|
23642
23653
|
);
|
|
23643
23654
|
}
|
|
23644
23655
|
}
|
|
@@ -24997,13 +25008,39 @@ function operateVariants(v) {
|
|
|
24997
25008
|
return [only(OPERATE_CHAIN, OPERATE_TERMS), only(OPERATE_TERMS)];
|
|
24998
25009
|
}
|
|
24999
25010
|
function schemaFor(prefs, source) {
|
|
25000
|
-
return
|
|
25001
|
-
|
|
25002
|
-
|
|
25003
|
-
|
|
25011
|
+
return shareLiteral(
|
|
25012
|
+
splitOperate(
|
|
25013
|
+
hideFromPlanner(
|
|
25014
|
+
forStructuredOutput(z3.toJSONSchema(storyboardSchema, { io: "input" })),
|
|
25015
|
+
plannerInvisible(prefs, source)
|
|
25016
|
+
)
|
|
25004
25017
|
)
|
|
25005
25018
|
);
|
|
25006
25019
|
}
|
|
25020
|
+
function shareLiteral(schema) {
|
|
25021
|
+
const root = schema;
|
|
25022
|
+
const variants = root.properties.beats.items.anyOf;
|
|
25023
|
+
const literal = variants[0]?.properties.literal;
|
|
25024
|
+
if (!literal) return schema;
|
|
25025
|
+
const one2 = JSON.stringify(literal);
|
|
25026
|
+
if (!variants.every((v) => JSON.stringify(v.properties.literal) === one2))
|
|
25027
|
+
throw new Error("schemaFor: the beat variants carry different `literal` schemas.");
|
|
25028
|
+
const ref = { $ref: "#/$defs/literal" };
|
|
25029
|
+
return {
|
|
25030
|
+
...root,
|
|
25031
|
+
$defs: { literal },
|
|
25032
|
+
properties: {
|
|
25033
|
+
...root.properties,
|
|
25034
|
+
beats: {
|
|
25035
|
+
...root.properties.beats,
|
|
25036
|
+
items: {
|
|
25037
|
+
...root.properties.beats.items,
|
|
25038
|
+
anyOf: variants.map((v) => ({ ...v, properties: { ...v.properties, literal: ref } }))
|
|
25039
|
+
}
|
|
25040
|
+
}
|
|
25041
|
+
}
|
|
25042
|
+
};
|
|
25043
|
+
}
|
|
25007
25044
|
var SCHEMA = schemaFor(prefsSchema.parse({}));
|
|
25008
25045
|
async function codexPlanner(source, opts = {}) {
|
|
25009
25046
|
const prefs = opts.prefs ?? prefsSchema.parse({});
|
|
@@ -25011,7 +25048,8 @@ async function codexPlanner(source, opts = {}) {
|
|
|
25011
25048
|
try {
|
|
25012
25049
|
const schemaPath = join9(dir, "storyboard.schema.json");
|
|
25013
25050
|
const outPath = join9(dir, "storyboard.json");
|
|
25014
|
-
|
|
25051
|
+
const schema = schemaFor(prefs, source);
|
|
25052
|
+
await writeFile7(schemaPath, JSON.stringify(schema));
|
|
25015
25053
|
const ask = async (prompt2) => {
|
|
25016
25054
|
await rm3(outPath, { force: true });
|
|
25017
25055
|
await (opts.run ?? runCodex)({
|
|
@@ -25155,6 +25193,46 @@ ${left.map((m) => ` ${m}`).join("\n")}`
|
|
|
25155
25193
|
);
|
|
25156
25194
|
return plan;
|
|
25157
25195
|
};
|
|
25196
|
+
const beatSchemaPath = join9(dir, "beat.schema.json");
|
|
25197
|
+
const offered = beatVariants(schema).map((v) => v.properties.archetype.const);
|
|
25198
|
+
const reshape2 = async (deck, i) => {
|
|
25199
|
+
const beat2 = deck.beats[i];
|
|
25200
|
+
const shapes = shapesFor(deck, i, offered, prefs.images);
|
|
25201
|
+
if (!shapes.length) return deck;
|
|
25202
|
+
await writeFile7(beatSchemaPath, JSON.stringify(beatSchemaFor(schema, shapes)));
|
|
25203
|
+
let problems = [];
|
|
25204
|
+
for (let t2 = 1; t2 <= SCENE_REPAIRS; t2++) {
|
|
25205
|
+
opts.onReshape?.(beat2.id, shapes);
|
|
25206
|
+
const out = join9(dir, `beat-${beat2.id}-${t2}.json`);
|
|
25207
|
+
try {
|
|
25208
|
+
await (opts.run ?? runCodex)({
|
|
25209
|
+
prompt: beatPrompt(prompt, deck, beat2, shapes, problems),
|
|
25210
|
+
schemaPath: beatSchemaPath,
|
|
25211
|
+
outPath: out,
|
|
25212
|
+
...opts.model === void 0 ? {} : { model: opts.model },
|
|
25213
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
|
25214
|
+
});
|
|
25215
|
+
const got = stripNulls(JSON.parse(await readFile8(out, "utf8")))?.beat;
|
|
25216
|
+
const redrawn = {
|
|
25217
|
+
...got,
|
|
25218
|
+
id: beat2.id,
|
|
25219
|
+
...beat2.literal ? { literal: beat2.literal } : {}
|
|
25220
|
+
};
|
|
25221
|
+
const beats = deck.beats.map((b, k) => k === i ? redrawn : b);
|
|
25222
|
+
return withStockLabels(checkPlan({ ...deck, beats }, source, prefs), source);
|
|
25223
|
+
} catch (err) {
|
|
25224
|
+
problems = err instanceof PlanRejected ? err.problems : [`the ask failed: ${err instanceof Error ? err.message : err}`];
|
|
25225
|
+
}
|
|
25226
|
+
}
|
|
25227
|
+
return deck;
|
|
25228
|
+
};
|
|
25229
|
+
const neighbours = async (planned) => {
|
|
25230
|
+
let plan = planned;
|
|
25231
|
+
for (let i = 1; i < plan.beats.length; i++)
|
|
25232
|
+
if (plan.beats[i]?.archetype === plan.beats[i - 1]?.archetype)
|
|
25233
|
+
plan = await reshape2(plan, i);
|
|
25234
|
+
return plan;
|
|
25235
|
+
};
|
|
25158
25236
|
let answer = await attempt2(prompt);
|
|
25159
25237
|
if (answer.plan && listOf(answer.broken).length === 0) return await scenes(answer.plan);
|
|
25160
25238
|
const firstBeats = beatCount(answer.json);
|
|
@@ -25174,7 +25252,13 @@ ${left.map((m) => ` ${m}`).join("\n")}`
|
|
|
25174
25252
|
advice: []
|
|
25175
25253
|
};
|
|
25176
25254
|
if (next.plan && listOf(still).length === 0) return await scenes(next.plan);
|
|
25177
|
-
if (round8 === REPAIRS)
|
|
25255
|
+
if (round8 === REPAIRS) {
|
|
25256
|
+
if (!next.plan || !listOf(still).every(isNeighbour)) throw unrepaired(next, still, round8);
|
|
25257
|
+
const plan = await neighbours(next.plan);
|
|
25258
|
+
const left = { ...findings(plan), advice: [] };
|
|
25259
|
+
if (listOf(left).length) throw unrepaired(next, left, round8);
|
|
25260
|
+
return await scenes(plan);
|
|
25261
|
+
}
|
|
25178
25262
|
merging ||= still.size.length > 0;
|
|
25179
25263
|
answer = { ...next, broken: still };
|
|
25180
25264
|
}
|
|
@@ -25229,6 +25313,37 @@ ${list2}
|
|
|
25229
25313
|
|
|
25230
25314
|
Return {"literal": ...}: beat ${id2}'s scene, redrawn so none of these hold \u2014 every number, word and figure one the source states, every slot its verb needs filled. Keep its verb unless a finding names another. Return a scene: this ask has no way to leave it out. No other beat is yours to change.
|
|
25231
25315
|
|
|
25316
|
+
THE STORYBOARD:
|
|
25317
|
+
${JSON.stringify(plan, null, 2)}`;
|
|
25318
|
+
}
|
|
25319
|
+
function isNeighbour(finding2) {
|
|
25320
|
+
return /^\S+ and \S+ are both `[^`]+`, next to each other\./.test(finding2);
|
|
25321
|
+
}
|
|
25322
|
+
function beatVariants(schema) {
|
|
25323
|
+
return schema.properties.beats.items.anyOf;
|
|
25324
|
+
}
|
|
25325
|
+
function beatSchemaFor(schema, shapes) {
|
|
25326
|
+
const anyOf = beatVariants(schema).filter((v) => shapes.includes(v.properties.archetype.const)).map((v) => hideFromPlanner(v, /* @__PURE__ */ new Set(["literal"])));
|
|
25327
|
+
return {
|
|
25328
|
+
type: "object",
|
|
25329
|
+
properties: { beat: { anyOf } },
|
|
25330
|
+
required: ["beat"],
|
|
25331
|
+
additionalProperties: false
|
|
25332
|
+
};
|
|
25333
|
+
}
|
|
25334
|
+
function beatPrompt(prompt, plan, beat2, shapes, problems) {
|
|
25335
|
+
const refused = problems.length ? `
|
|
25336
|
+
Your last answer was refused for:
|
|
25337
|
+
${problems.map((m) => ` - ${m}`).join("\n")}
|
|
25338
|
+
` : "";
|
|
25339
|
+
return `${prompt}
|
|
25340
|
+
|
|
25341
|
+
THE DECK IS PLANNED. ONE BEAT'S SHAPE IS LEFT.
|
|
25342
|
+
|
|
25343
|
+
The storyboard below is settled, but beat ${beat2.id} is \`${beat2.archetype}\` beside a beat of the same shape, and no two beats in a row may share an archetype.
|
|
25344
|
+
${refused}
|
|
25345
|
+
Return {"beat": ...}: beat ${beat2.id} redrawn as one of ${shapes.map((s) => `\`${s}\``).join(", ")} \u2014 whichever shows its point best \u2014 with the params that shape needs, every number, word and figure one the source states. Keep its id, its point, its evidence, its narration and its part. No other beat is yours to change.
|
|
25346
|
+
|
|
25232
25347
|
THE STORYBOARD:
|
|
25233
25348
|
${JSON.stringify(plan, null, 2)}`;
|
|
25234
25349
|
}
|
|
@@ -25326,7 +25441,9 @@ async function readPlan(outPath, source, prefs) {
|
|
|
25326
25441
|
${raw2.slice(0, 200)}`
|
|
25327
25442
|
);
|
|
25328
25443
|
}
|
|
25329
|
-
|
|
25444
|
+
return checkPlan(stripNulls(parsed), source, prefs);
|
|
25445
|
+
}
|
|
25446
|
+
function checkPlan(plan, source, prefs) {
|
|
25330
25447
|
const result = storyboardSchema.safeParse(plan);
|
|
25331
25448
|
if (!result.success) {
|
|
25332
25449
|
const issues = result.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`);
|
|
@@ -38201,6 +38318,7 @@ imageFlags(
|
|
|
38201
38318
|
step(`plan: redrawing ${beat2}'s scene on its own \u2014`);
|
|
38202
38319
|
for (const m of broken) step(`plan: ${m}`);
|
|
38203
38320
|
},
|
|
38321
|
+
onReshape: (beat2, shapes) => step(`plan: redrawing ${beat2} on its own, as one of: ${shapes.join(", ")}`),
|
|
38204
38322
|
onDropScenes: (beats, why3) => {
|
|
38205
38323
|
step(
|
|
38206
38324
|
`plan: WARNING: ${beats.join(", ")} drawn by their archetype instead; their scenes stay broken \u2014`
|
package/dist/index.js
CHANGED
|
@@ -25877,9 +25877,9 @@ function literalFindings(storyboard, source, opts = {}) {
|
|
|
25877
25877
|
prevKey = { id: beat2.id, key };
|
|
25878
25878
|
});
|
|
25879
25879
|
const tables = beats.filter((b) => b.literal?.kind === "table");
|
|
25880
|
-
|
|
25880
|
+
for (const t2 of tables.slice(1))
|
|
25881
25881
|
out.push(
|
|
25882
|
-
|
|
25882
|
+
`${t2.id} draws a table, and so does ${tables[0]?.id}. A table reads like a report: keep at most one, and draw this one as what it lists \u2014 results as \`place\` (margin or plane), what was and was not evaluated as \`place\` sets, experiments not run as \`place\` combos, earlier approaches as \`prior-work\`, reported costs as \`place\` length \u2014 or leave \`literal\` null where nothing is honest.`
|
|
25883
25883
|
);
|
|
25884
25884
|
const byVectors = /* @__PURE__ */ new Map();
|
|
25885
25885
|
for (const b of beats) {
|
|
@@ -25888,20 +25888,20 @@ function literalFindings(storyboard, source, opts = {}) {
|
|
|
25888
25888
|
const key = JSON.stringify(l.vectors.map((v) => v.values));
|
|
25889
25889
|
byVectors.set(key, [...byVectors.get(key) ?? [], b.id]);
|
|
25890
25890
|
}
|
|
25891
|
-
for (const
|
|
25892
|
-
|
|
25891
|
+
for (const [first, ...rest] of byVectors.values())
|
|
25892
|
+
for (const id2 of rest)
|
|
25893
25893
|
out.push(
|
|
25894
|
-
`${
|
|
25894
|
+
`${id2} runs operate on the same vectors as ${first}, so the deck shows one chart twice. Give its chain to ${first} (one longer chain), or draw ${id2} another way.`
|
|
25895
25895
|
);
|
|
25896
25896
|
const forms = /* @__PURE__ */ new Map();
|
|
25897
25897
|
for (const b of beats) {
|
|
25898
25898
|
const f = b.literal && wordForm(b.literal);
|
|
25899
25899
|
if (f) forms.set(f, [...forms.get(f) ?? [], b.id]);
|
|
25900
25900
|
}
|
|
25901
|
-
for (const [f,
|
|
25902
|
-
|
|
25901
|
+
for (const [f, [first, ...rest]] of forms)
|
|
25902
|
+
for (const id2 of rest)
|
|
25903
25903
|
out.push(
|
|
25904
|
-
|
|
25904
|
+
`${id2} draws \`${f}\`, as ${first} already does. It draws no picture, so a second one is the same slide again: draw ${id2} another way, or with \`literal\` null.`
|
|
25905
25905
|
);
|
|
25906
25906
|
return out;
|
|
25907
25907
|
}
|
|
@@ -25977,15 +25977,26 @@ function isScene(beat2, design2) {
|
|
|
25977
25977
|
return pictured(p.backdrop);
|
|
25978
25978
|
}
|
|
25979
25979
|
}
|
|
25980
|
+
function shapesFor(storyboard, i, offered, images) {
|
|
25981
|
+
const beats = storyboard.beats;
|
|
25982
|
+
const others = beats.filter((_, k) => k !== i);
|
|
25983
|
+
const kinetic2 = others.filter((b) => b.archetype === "kinetic").length;
|
|
25984
|
+
const panels = others.filter((b) => PANEL_ARCHETYPES.has(b.archetype)).length;
|
|
25985
|
+
const panelCap = panelsAllowed(beats.length, images);
|
|
25986
|
+
return offered.filter(
|
|
25987
|
+
(a) => a !== beats[i - 1]?.archetype && a !== beats[i + 1]?.archetype && (a !== "title" || i === 0) && (a !== "kinetic" || kinetic2 < KINETIC_MAX) && (!PANEL_ARCHETYPES.has(a) || panels < panelCap)
|
|
25988
|
+
);
|
|
25989
|
+
}
|
|
25980
25990
|
function varietyFindings(storyboard, images, design2) {
|
|
25981
25991
|
const beats = storyboard.beats;
|
|
25982
25992
|
const out = [];
|
|
25993
|
+
const avoid = (beat2, other) => [beat2.archetype, other?.archetype].filter((x, k, xs) => x !== void 0 && xs.indexOf(x) === k).map((x) => `\`${x}\``).join(" or ");
|
|
25983
25994
|
for (let i = 1; i < beats.length; i++) {
|
|
25984
25995
|
const a = beats[i - 1];
|
|
25985
25996
|
const b = beats[i];
|
|
25986
25997
|
if (a.archetype === b.archetype) {
|
|
25987
25998
|
out.push(
|
|
25988
|
-
`${a.id} and ${b.id} are both \`${a.archetype}\`, next to each other.
|
|
25999
|
+
`${a.id} and ${b.id} are both \`${a.archetype}\`, next to each other. Redraw ${a.id} as anything but ${avoid(a, beats[i - 2])}, or ${b.id} as anything but ${avoid(b, beats[i + 1])}.`
|
|
25989
26000
|
);
|
|
25990
26001
|
}
|
|
25991
26002
|
}
|
|
@@ -27344,13 +27355,39 @@ function operateVariants(v) {
|
|
|
27344
27355
|
return [only(OPERATE_CHAIN, OPERATE_TERMS), only(OPERATE_TERMS)];
|
|
27345
27356
|
}
|
|
27346
27357
|
function schemaFor(prefs, source) {
|
|
27347
|
-
return
|
|
27348
|
-
|
|
27349
|
-
|
|
27350
|
-
|
|
27358
|
+
return shareLiteral(
|
|
27359
|
+
splitOperate(
|
|
27360
|
+
hideFromPlanner(
|
|
27361
|
+
forStructuredOutput(z3.toJSONSchema(storyboardSchema, { io: "input" })),
|
|
27362
|
+
plannerInvisible(prefs, source)
|
|
27363
|
+
)
|
|
27351
27364
|
)
|
|
27352
27365
|
);
|
|
27353
27366
|
}
|
|
27367
|
+
function shareLiteral(schema) {
|
|
27368
|
+
const root = schema;
|
|
27369
|
+
const variants = root.properties.beats.items.anyOf;
|
|
27370
|
+
const literal = variants[0]?.properties.literal;
|
|
27371
|
+
if (!literal) return schema;
|
|
27372
|
+
const one2 = JSON.stringify(literal);
|
|
27373
|
+
if (!variants.every((v) => JSON.stringify(v.properties.literal) === one2))
|
|
27374
|
+
throw new Error("schemaFor: the beat variants carry different `literal` schemas.");
|
|
27375
|
+
const ref = { $ref: "#/$defs/literal" };
|
|
27376
|
+
return {
|
|
27377
|
+
...root,
|
|
27378
|
+
$defs: { literal },
|
|
27379
|
+
properties: {
|
|
27380
|
+
...root.properties,
|
|
27381
|
+
beats: {
|
|
27382
|
+
...root.properties.beats,
|
|
27383
|
+
items: {
|
|
27384
|
+
...root.properties.beats.items,
|
|
27385
|
+
anyOf: variants.map((v) => ({ ...v, properties: { ...v.properties, literal: ref } }))
|
|
27386
|
+
}
|
|
27387
|
+
}
|
|
27388
|
+
}
|
|
27389
|
+
};
|
|
27390
|
+
}
|
|
27354
27391
|
var SCHEMA = schemaFor(prefsSchema.parse({}));
|
|
27355
27392
|
async function codexPlanner(source, opts = {}) {
|
|
27356
27393
|
const prefs = opts.prefs ?? prefsSchema.parse({});
|
|
@@ -27358,7 +27395,8 @@ async function codexPlanner(source, opts = {}) {
|
|
|
27358
27395
|
try {
|
|
27359
27396
|
const schemaPath = join12(dir, "storyboard.schema.json");
|
|
27360
27397
|
const outPath = join12(dir, "storyboard.json");
|
|
27361
|
-
|
|
27398
|
+
const schema = schemaFor(prefs, source);
|
|
27399
|
+
await writeFile10(schemaPath, JSON.stringify(schema));
|
|
27362
27400
|
const ask = async (prompt2) => {
|
|
27363
27401
|
await rm5(outPath, { force: true });
|
|
27364
27402
|
await (opts.run ?? runCodex)({
|
|
@@ -27502,6 +27540,46 @@ ${left.map((m) => ` ${m}`).join("\n")}`
|
|
|
27502
27540
|
);
|
|
27503
27541
|
return plan;
|
|
27504
27542
|
};
|
|
27543
|
+
const beatSchemaPath = join12(dir, "beat.schema.json");
|
|
27544
|
+
const offered = beatVariants(schema).map((v) => v.properties.archetype.const);
|
|
27545
|
+
const reshape2 = async (deck, i) => {
|
|
27546
|
+
const beat2 = deck.beats[i];
|
|
27547
|
+
const shapes = shapesFor(deck, i, offered, prefs.images);
|
|
27548
|
+
if (!shapes.length) return deck;
|
|
27549
|
+
await writeFile10(beatSchemaPath, JSON.stringify(beatSchemaFor(schema, shapes)));
|
|
27550
|
+
let problems = [];
|
|
27551
|
+
for (let t2 = 1; t2 <= SCENE_REPAIRS; t2++) {
|
|
27552
|
+
opts.onReshape?.(beat2.id, shapes);
|
|
27553
|
+
const out = join12(dir, `beat-${beat2.id}-${t2}.json`);
|
|
27554
|
+
try {
|
|
27555
|
+
await (opts.run ?? runCodex)({
|
|
27556
|
+
prompt: beatPrompt(prompt, deck, beat2, shapes, problems),
|
|
27557
|
+
schemaPath: beatSchemaPath,
|
|
27558
|
+
outPath: out,
|
|
27559
|
+
...opts.model === void 0 ? {} : { model: opts.model },
|
|
27560
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2
|
|
27561
|
+
});
|
|
27562
|
+
const got = stripNulls(JSON.parse(await readFile12(out, "utf8")))?.beat;
|
|
27563
|
+
const redrawn = {
|
|
27564
|
+
...got,
|
|
27565
|
+
id: beat2.id,
|
|
27566
|
+
...beat2.literal ? { literal: beat2.literal } : {}
|
|
27567
|
+
};
|
|
27568
|
+
const beats = deck.beats.map((b, k) => k === i ? redrawn : b);
|
|
27569
|
+
return withStockLabels(checkPlan({ ...deck, beats }, source, prefs), source);
|
|
27570
|
+
} catch (err) {
|
|
27571
|
+
problems = err instanceof PlanRejected ? err.problems : [`the ask failed: ${err instanceof Error ? err.message : err}`];
|
|
27572
|
+
}
|
|
27573
|
+
}
|
|
27574
|
+
return deck;
|
|
27575
|
+
};
|
|
27576
|
+
const neighbours = async (planned) => {
|
|
27577
|
+
let plan = planned;
|
|
27578
|
+
for (let i = 1; i < plan.beats.length; i++)
|
|
27579
|
+
if (plan.beats[i]?.archetype === plan.beats[i - 1]?.archetype)
|
|
27580
|
+
plan = await reshape2(plan, i);
|
|
27581
|
+
return plan;
|
|
27582
|
+
};
|
|
27505
27583
|
let answer = await attempt2(prompt);
|
|
27506
27584
|
if (answer.plan && listOf(answer.broken).length === 0) return await scenes(answer.plan);
|
|
27507
27585
|
const firstBeats = beatCount(answer.json);
|
|
@@ -27521,7 +27599,13 @@ ${left.map((m) => ` ${m}`).join("\n")}`
|
|
|
27521
27599
|
advice: []
|
|
27522
27600
|
};
|
|
27523
27601
|
if (next.plan && listOf(still).length === 0) return await scenes(next.plan);
|
|
27524
|
-
if (round8 === REPAIRS)
|
|
27602
|
+
if (round8 === REPAIRS) {
|
|
27603
|
+
if (!next.plan || !listOf(still).every(isNeighbour)) throw unrepaired(next, still, round8);
|
|
27604
|
+
const plan = await neighbours(next.plan);
|
|
27605
|
+
const left = { ...findings(plan), advice: [] };
|
|
27606
|
+
if (listOf(left).length) throw unrepaired(next, left, round8);
|
|
27607
|
+
return await scenes(plan);
|
|
27608
|
+
}
|
|
27525
27609
|
merging ||= still.size.length > 0;
|
|
27526
27610
|
answer = { ...next, broken: still };
|
|
27527
27611
|
}
|
|
@@ -27576,6 +27660,37 @@ ${list2}
|
|
|
27576
27660
|
|
|
27577
27661
|
Return {"literal": ...}: beat ${id2}'s scene, redrawn so none of these hold \u2014 every number, word and figure one the source states, every slot its verb needs filled. Keep its verb unless a finding names another. Return a scene: this ask has no way to leave it out. No other beat is yours to change.
|
|
27578
27662
|
|
|
27663
|
+
THE STORYBOARD:
|
|
27664
|
+
${JSON.stringify(plan, null, 2)}`;
|
|
27665
|
+
}
|
|
27666
|
+
function isNeighbour(finding2) {
|
|
27667
|
+
return /^\S+ and \S+ are both `[^`]+`, next to each other\./.test(finding2);
|
|
27668
|
+
}
|
|
27669
|
+
function beatVariants(schema) {
|
|
27670
|
+
return schema.properties.beats.items.anyOf;
|
|
27671
|
+
}
|
|
27672
|
+
function beatSchemaFor(schema, shapes) {
|
|
27673
|
+
const anyOf = beatVariants(schema).filter((v) => shapes.includes(v.properties.archetype.const)).map((v) => hideFromPlanner(v, /* @__PURE__ */ new Set(["literal"])));
|
|
27674
|
+
return {
|
|
27675
|
+
type: "object",
|
|
27676
|
+
properties: { beat: { anyOf } },
|
|
27677
|
+
required: ["beat"],
|
|
27678
|
+
additionalProperties: false
|
|
27679
|
+
};
|
|
27680
|
+
}
|
|
27681
|
+
function beatPrompt(prompt, plan, beat2, shapes, problems) {
|
|
27682
|
+
const refused = problems.length ? `
|
|
27683
|
+
Your last answer was refused for:
|
|
27684
|
+
${problems.map((m) => ` - ${m}`).join("\n")}
|
|
27685
|
+
` : "";
|
|
27686
|
+
return `${prompt}
|
|
27687
|
+
|
|
27688
|
+
THE DECK IS PLANNED. ONE BEAT'S SHAPE IS LEFT.
|
|
27689
|
+
|
|
27690
|
+
The storyboard below is settled, but beat ${beat2.id} is \`${beat2.archetype}\` beside a beat of the same shape, and no two beats in a row may share an archetype.
|
|
27691
|
+
${refused}
|
|
27692
|
+
Return {"beat": ...}: beat ${beat2.id} redrawn as one of ${shapes.map((s) => `\`${s}\``).join(", ")} \u2014 whichever shows its point best \u2014 with the params that shape needs, every number, word and figure one the source states. Keep its id, its point, its evidence, its narration and its part. No other beat is yours to change.
|
|
27693
|
+
|
|
27579
27694
|
THE STORYBOARD:
|
|
27580
27695
|
${JSON.stringify(plan, null, 2)}`;
|
|
27581
27696
|
}
|
|
@@ -27673,7 +27788,9 @@ async function readPlan(outPath, source, prefs) {
|
|
|
27673
27788
|
${raw2.slice(0, 200)}`
|
|
27674
27789
|
);
|
|
27675
27790
|
}
|
|
27676
|
-
|
|
27791
|
+
return checkPlan(stripNulls(parsed), source, prefs);
|
|
27792
|
+
}
|
|
27793
|
+
function checkPlan(plan, source, prefs) {
|
|
27677
27794
|
const result = storyboardSchema.safeParse(plan);
|
|
27678
27795
|
if (!result.success) {
|
|
27679
27796
|
const issues = result.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`);
|
package/dist/mcp.js
CHANGED
|
@@ -24389,9 +24389,9 @@ function literalFindings(storyboard, source, opts = {}) {
|
|
|
24389
24389
|
prevKey = { id: beat.id, key };
|
|
24390
24390
|
});
|
|
24391
24391
|
const tables = beats.filter((b) => b.literal?.kind === "table");
|
|
24392
|
-
|
|
24392
|
+
for (const t2 of tables.slice(1))
|
|
24393
24393
|
out.push(
|
|
24394
|
-
|
|
24394
|
+
`${t2.id} draws a table, and so does ${tables[0]?.id}. A table reads like a report: keep at most one, and draw this one as what it lists \u2014 results as \`place\` (margin or plane), what was and was not evaluated as \`place\` sets, experiments not run as \`place\` combos, earlier approaches as \`prior-work\`, reported costs as \`place\` length \u2014 or leave \`literal\` null where nothing is honest.`
|
|
24395
24395
|
);
|
|
24396
24396
|
const byVectors = /* @__PURE__ */ new Map();
|
|
24397
24397
|
for (const b of beats) {
|
|
@@ -24400,20 +24400,20 @@ function literalFindings(storyboard, source, opts = {}) {
|
|
|
24400
24400
|
const key = JSON.stringify(l.vectors.map((v) => v.values));
|
|
24401
24401
|
byVectors.set(key, [...byVectors.get(key) ?? [], b.id]);
|
|
24402
24402
|
}
|
|
24403
|
-
for (const
|
|
24404
|
-
|
|
24403
|
+
for (const [first, ...rest] of byVectors.values())
|
|
24404
|
+
for (const id2 of rest)
|
|
24405
24405
|
out.push(
|
|
24406
|
-
`${
|
|
24406
|
+
`${id2} runs operate on the same vectors as ${first}, so the deck shows one chart twice. Give its chain to ${first} (one longer chain), or draw ${id2} another way.`
|
|
24407
24407
|
);
|
|
24408
24408
|
const forms = /* @__PURE__ */ new Map();
|
|
24409
24409
|
for (const b of beats) {
|
|
24410
24410
|
const f = b.literal && wordForm(b.literal);
|
|
24411
24411
|
if (f) forms.set(f, [...forms.get(f) ?? [], b.id]);
|
|
24412
24412
|
}
|
|
24413
|
-
for (const [f,
|
|
24414
|
-
|
|
24413
|
+
for (const [f, [first, ...rest]] of forms)
|
|
24414
|
+
for (const id2 of rest)
|
|
24415
24415
|
out.push(
|
|
24416
|
-
|
|
24416
|
+
`${id2} draws \`${f}\`, as ${first} already does. It draws no picture, so a second one is the same slide again: draw ${id2} another way, or with \`literal\` null.`
|
|
24417
24417
|
);
|
|
24418
24418
|
return out;
|
|
24419
24419
|
}
|
|
@@ -24489,15 +24489,26 @@ function isScene(beat, design2) {
|
|
|
24489
24489
|
return pictured(p.backdrop);
|
|
24490
24490
|
}
|
|
24491
24491
|
}
|
|
24492
|
+
function shapesFor(storyboard, i, offered, images) {
|
|
24493
|
+
const beats = storyboard.beats;
|
|
24494
|
+
const others = beats.filter((_, k) => k !== i);
|
|
24495
|
+
const kinetic2 = others.filter((b) => b.archetype === "kinetic").length;
|
|
24496
|
+
const panels = others.filter((b) => PANEL_ARCHETYPES.has(b.archetype)).length;
|
|
24497
|
+
const panelCap = panelsAllowed(beats.length, images);
|
|
24498
|
+
return offered.filter(
|
|
24499
|
+
(a) => a !== beats[i - 1]?.archetype && a !== beats[i + 1]?.archetype && (a !== "title" || i === 0) && (a !== "kinetic" || kinetic2 < KINETIC_MAX) && (!PANEL_ARCHETYPES.has(a) || panels < panelCap)
|
|
24500
|
+
);
|
|
24501
|
+
}
|
|
24492
24502
|
function varietyFindings(storyboard, images, design2) {
|
|
24493
24503
|
const beats = storyboard.beats;
|
|
24494
24504
|
const out = [];
|
|
24505
|
+
const avoid = (beat, other) => [beat.archetype, other?.archetype].filter((x, k, xs) => x !== void 0 && xs.indexOf(x) === k).map((x) => `\`${x}\``).join(" or ");
|
|
24495
24506
|
for (let i = 1; i < beats.length; i++) {
|
|
24496
24507
|
const a = beats[i - 1];
|
|
24497
24508
|
const b = beats[i];
|
|
24498
24509
|
if (a.archetype === b.archetype) {
|
|
24499
24510
|
out.push(
|
|
24500
|
-
`${a.id} and ${b.id} are both \`${a.archetype}\`, next to each other.
|
|
24511
|
+
`${a.id} and ${b.id} are both \`${a.archetype}\`, next to each other. Redraw ${a.id} as anything but ${avoid(a, beats[i - 2])}, or ${b.id} as anything but ${avoid(b, beats[i + 1])}.`
|
|
24501
24512
|
);
|
|
24502
24513
|
}
|
|
24503
24514
|
}
|
|
@@ -25814,13 +25825,39 @@ function operateVariants(v) {
|
|
|
25814
25825
|
return [only(OPERATE_CHAIN, OPERATE_TERMS), only(OPERATE_TERMS)];
|
|
25815
25826
|
}
|
|
25816
25827
|
function schemaFor(prefs, source) {
|
|
25817
|
-
return
|
|
25818
|
-
|
|
25819
|
-
|
|
25820
|
-
|
|
25828
|
+
return shareLiteral(
|
|
25829
|
+
splitOperate(
|
|
25830
|
+
hideFromPlanner(
|
|
25831
|
+
forStructuredOutput(z3.toJSONSchema(storyboardSchema, { io: "input" })),
|
|
25832
|
+
plannerInvisible(prefs, source)
|
|
25833
|
+
)
|
|
25821
25834
|
)
|
|
25822
25835
|
);
|
|
25823
25836
|
}
|
|
25837
|
+
function shareLiteral(schema2) {
|
|
25838
|
+
const root2 = schema2;
|
|
25839
|
+
const variants = root2.properties.beats.items.anyOf;
|
|
25840
|
+
const literal = variants[0]?.properties.literal;
|
|
25841
|
+
if (!literal) return schema2;
|
|
25842
|
+
const one2 = JSON.stringify(literal);
|
|
25843
|
+
if (!variants.every((v) => JSON.stringify(v.properties.literal) === one2))
|
|
25844
|
+
throw new Error("schemaFor: the beat variants carry different `literal` schemas.");
|
|
25845
|
+
const ref = { $ref: "#/$defs/literal" };
|
|
25846
|
+
return {
|
|
25847
|
+
...root2,
|
|
25848
|
+
$defs: { literal },
|
|
25849
|
+
properties: {
|
|
25850
|
+
...root2.properties,
|
|
25851
|
+
beats: {
|
|
25852
|
+
...root2.properties.beats,
|
|
25853
|
+
items: {
|
|
25854
|
+
...root2.properties.beats.items,
|
|
25855
|
+
anyOf: variants.map((v) => ({ ...v, properties: { ...v.properties, literal: ref } }))
|
|
25856
|
+
}
|
|
25857
|
+
}
|
|
25858
|
+
}
|
|
25859
|
+
};
|
|
25860
|
+
}
|
|
25824
25861
|
var SCHEMA = schemaFor(prefsSchema.parse({}));
|
|
25825
25862
|
async function codexPlanner(source, opts = {}) {
|
|
25826
25863
|
const prefs = opts.prefs ?? prefsSchema.parse({});
|
|
@@ -25828,7 +25865,8 @@ async function codexPlanner(source, opts = {}) {
|
|
|
25828
25865
|
try {
|
|
25829
25866
|
const schemaPath = join11(dir, "storyboard.schema.json");
|
|
25830
25867
|
const outPath = join11(dir, "storyboard.json");
|
|
25831
|
-
|
|
25868
|
+
const schema2 = schemaFor(prefs, source);
|
|
25869
|
+
await writeFile8(schemaPath, JSON.stringify(schema2));
|
|
25832
25870
|
const ask = async (prompt2) => {
|
|
25833
25871
|
await rm5(outPath, { force: true });
|
|
25834
25872
|
await (opts.run ?? runCodex)({
|
|
@@ -25972,6 +26010,46 @@ ${left.map((m) => ` ${m}`).join("\n")}`
|
|
|
25972
26010
|
);
|
|
25973
26011
|
return plan;
|
|
25974
26012
|
};
|
|
26013
|
+
const beatSchemaPath = join11(dir, "beat.schema.json");
|
|
26014
|
+
const offered = beatVariants(schema2).map((v) => v.properties.archetype.const);
|
|
26015
|
+
const reshape2 = async (deck, i) => {
|
|
26016
|
+
const beat = deck.beats[i];
|
|
26017
|
+
const shapes = shapesFor(deck, i, offered, prefs.images);
|
|
26018
|
+
if (!shapes.length) return deck;
|
|
26019
|
+
await writeFile8(beatSchemaPath, JSON.stringify(beatSchemaFor(schema2, shapes)));
|
|
26020
|
+
let problems = [];
|
|
26021
|
+
for (let t2 = 1; t2 <= SCENE_REPAIRS; t2++) {
|
|
26022
|
+
opts.onReshape?.(beat.id, shapes);
|
|
26023
|
+
const out = join11(dir, `beat-${beat.id}-${t2}.json`);
|
|
26024
|
+
try {
|
|
26025
|
+
await (opts.run ?? runCodex)({
|
|
26026
|
+
prompt: beatPrompt(prompt, deck, beat, shapes, problems),
|
|
26027
|
+
schemaPath: beatSchemaPath,
|
|
26028
|
+
outPath: out,
|
|
26029
|
+
...opts.model === void 0 ? {} : { model: opts.model },
|
|
26030
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2
|
|
26031
|
+
});
|
|
26032
|
+
const got = stripNulls(JSON.parse(await readFile9(out, "utf8")))?.beat;
|
|
26033
|
+
const redrawn = {
|
|
26034
|
+
...got,
|
|
26035
|
+
id: beat.id,
|
|
26036
|
+
...beat.literal ? { literal: beat.literal } : {}
|
|
26037
|
+
};
|
|
26038
|
+
const beats = deck.beats.map((b, k) => k === i ? redrawn : b);
|
|
26039
|
+
return withStockLabels(checkPlan({ ...deck, beats }, source, prefs), source);
|
|
26040
|
+
} catch (err) {
|
|
26041
|
+
problems = err instanceof PlanRejected ? err.problems : [`the ask failed: ${err instanceof Error ? err.message : err}`];
|
|
26042
|
+
}
|
|
26043
|
+
}
|
|
26044
|
+
return deck;
|
|
26045
|
+
};
|
|
26046
|
+
const neighbours = async (planned) => {
|
|
26047
|
+
let plan = planned;
|
|
26048
|
+
for (let i = 1; i < plan.beats.length; i++)
|
|
26049
|
+
if (plan.beats[i]?.archetype === plan.beats[i - 1]?.archetype)
|
|
26050
|
+
plan = await reshape2(plan, i);
|
|
26051
|
+
return plan;
|
|
26052
|
+
};
|
|
25975
26053
|
let answer = await attempt2(prompt);
|
|
25976
26054
|
if (answer.plan && listOf(answer.broken).length === 0) return await scenes(answer.plan);
|
|
25977
26055
|
const firstBeats = beatCount(answer.json);
|
|
@@ -25991,7 +26069,13 @@ ${left.map((m) => ` ${m}`).join("\n")}`
|
|
|
25991
26069
|
advice: []
|
|
25992
26070
|
};
|
|
25993
26071
|
if (next.plan && listOf(still).length === 0) return await scenes(next.plan);
|
|
25994
|
-
if (round6 === REPAIRS)
|
|
26072
|
+
if (round6 === REPAIRS) {
|
|
26073
|
+
if (!next.plan || !listOf(still).every(isNeighbour)) throw unrepaired(next, still, round6);
|
|
26074
|
+
const plan = await neighbours(next.plan);
|
|
26075
|
+
const left = { ...findings(plan), advice: [] };
|
|
26076
|
+
if (listOf(left).length) throw unrepaired(next, left, round6);
|
|
26077
|
+
return await scenes(plan);
|
|
26078
|
+
}
|
|
25995
26079
|
merging ||= still.size.length > 0;
|
|
25996
26080
|
answer = { ...next, broken: still };
|
|
25997
26081
|
}
|
|
@@ -26046,6 +26130,37 @@ ${list2}
|
|
|
26046
26130
|
|
|
26047
26131
|
Return {"literal": ...}: beat ${id2}'s scene, redrawn so none of these hold \u2014 every number, word and figure one the source states, every slot its verb needs filled. Keep its verb unless a finding names another. Return a scene: this ask has no way to leave it out. No other beat is yours to change.
|
|
26048
26132
|
|
|
26133
|
+
THE STORYBOARD:
|
|
26134
|
+
${JSON.stringify(plan, null, 2)}`;
|
|
26135
|
+
}
|
|
26136
|
+
function isNeighbour(finding) {
|
|
26137
|
+
return /^\S+ and \S+ are both `[^`]+`, next to each other\./.test(finding);
|
|
26138
|
+
}
|
|
26139
|
+
function beatVariants(schema2) {
|
|
26140
|
+
return schema2.properties.beats.items.anyOf;
|
|
26141
|
+
}
|
|
26142
|
+
function beatSchemaFor(schema2, shapes) {
|
|
26143
|
+
const anyOf = beatVariants(schema2).filter((v) => shapes.includes(v.properties.archetype.const)).map((v) => hideFromPlanner(v, /* @__PURE__ */ new Set(["literal"])));
|
|
26144
|
+
return {
|
|
26145
|
+
type: "object",
|
|
26146
|
+
properties: { beat: { anyOf } },
|
|
26147
|
+
required: ["beat"],
|
|
26148
|
+
additionalProperties: false
|
|
26149
|
+
};
|
|
26150
|
+
}
|
|
26151
|
+
function beatPrompt(prompt, plan, beat, shapes, problems) {
|
|
26152
|
+
const refused = problems.length ? `
|
|
26153
|
+
Your last answer was refused for:
|
|
26154
|
+
${problems.map((m) => ` - ${m}`).join("\n")}
|
|
26155
|
+
` : "";
|
|
26156
|
+
return `${prompt}
|
|
26157
|
+
|
|
26158
|
+
THE DECK IS PLANNED. ONE BEAT'S SHAPE IS LEFT.
|
|
26159
|
+
|
|
26160
|
+
The storyboard below is settled, but beat ${beat.id} is \`${beat.archetype}\` beside a beat of the same shape, and no two beats in a row may share an archetype.
|
|
26161
|
+
${refused}
|
|
26162
|
+
Return {"beat": ...}: beat ${beat.id} redrawn as one of ${shapes.map((s) => `\`${s}\``).join(", ")} \u2014 whichever shows its point best \u2014 with the params that shape needs, every number, word and figure one the source states. Keep its id, its point, its evidence, its narration and its part. No other beat is yours to change.
|
|
26163
|
+
|
|
26049
26164
|
THE STORYBOARD:
|
|
26050
26165
|
${JSON.stringify(plan, null, 2)}`;
|
|
26051
26166
|
}
|
|
@@ -26143,7 +26258,9 @@ async function readPlan(outPath, source, prefs) {
|
|
|
26143
26258
|
${raw2.slice(0, 200)}`
|
|
26144
26259
|
);
|
|
26145
26260
|
}
|
|
26146
|
-
|
|
26261
|
+
return checkPlan(stripNulls(parsed), source, prefs);
|
|
26262
|
+
}
|
|
26263
|
+
function checkPlan(plan, source, prefs) {
|
|
26147
26264
|
const result = storyboardSchema.safeParse(plan);
|
|
26148
26265
|
if (!result.success) {
|
|
26149
26266
|
const issues = result.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`);
|
|
@@ -31,6 +31,8 @@ export interface CodexOptions {
|
|
|
31
31
|
onScene?: (beat: string, broken: readonly string[]) => void;
|
|
32
32
|
/** Told the beats whose scene was dropped (still broken after its own repairs, or broken only by a symbolic quantity), and why. */
|
|
33
33
|
onDropScenes?: (beats: readonly string[], why: readonly string[]) => void;
|
|
34
|
+
/** Told a beat still beside its own shape after the last repair is redrawn on its own, as one of `shapes`. */
|
|
35
|
+
onReshape?: (beat: string, shapes: readonly string[]) => void;
|
|
34
36
|
/** Each source figure's panel count as the build will cut it, when the caller read them. */
|
|
35
37
|
panels?: Readonly<Record<string, number>>;
|
|
36
38
|
}
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* So the rule is a minimum, not an invitation, and it is CHECKED: the prompt
|
|
13
13
|
* (src/plan/prompt.ts) states these numbers by importing them from here, and
|
|
14
14
|
* `codexPlanner` (src/plan/codex.ts) runs `varietyFindings` on what comes back,
|
|
15
|
-
* asks
|
|
15
|
+
* asks for up to three repairs, redraws a beat still beside its own shape on
|
|
16
|
+
* its own (`shapesFor`), and refuses a plan that still breaks it. One table,
|
|
16
17
|
* read by both, so the sentence the model is given and the check it is held to
|
|
17
18
|
* cannot drift apart.
|
|
18
19
|
*
|
|
@@ -90,6 +91,16 @@ export declare function scenesRequired(beats: number, images: Prefs["images"]):
|
|
|
90
91
|
* the cards-on-paper the share exists to refuse.
|
|
91
92
|
*/
|
|
92
93
|
export declare function isScene(beat: Beat, design?: Design): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* The shapes, of those `offered`, that beat `i` may be redrawn as: not either
|
|
96
|
+
* neighbour's, not a title past the first beat, and not one that tips the
|
|
97
|
+
* kinetic or panel cap. What the last-resort redraw of one beat
|
|
98
|
+
* (src/plan/codex.ts) offers in its schema, so the shape cannot be wrong.
|
|
99
|
+
*
|
|
100
|
+
* ponytail: the A/B/A/B and scene-share rules are not foreseen here; the plan
|
|
101
|
+
* is checked whole after the redraw, and refused if one of them broke.
|
|
102
|
+
*/
|
|
103
|
+
export declare function shapesFor(storyboard: Storyboard, i: number, offered: readonly Archetype[], images: Prefs["images"]): Archetype[];
|
|
93
104
|
/**
|
|
94
105
|
* Every way `storyboard` breaks the variety rule, as sentences the planner can
|
|
95
106
|
* act on. Empty is a pass.
|