@bitmagic/asset-core 0.2.7-dev.0 → 0.2.7-dev.10
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/generators/background.d.ts +37 -0
- package/dist/generators/background.d.ts.map +1 -0
- package/dist/generators/background.js +80 -0
- package/dist/generators/background.js.map +1 -0
- package/dist/generators/block-type.d.ts +34 -0
- package/dist/generators/block-type.d.ts.map +1 -0
- package/dist/generators/block-type.js +164 -0
- package/dist/generators/block-type.js.map +1 -0
- package/dist/generators/character.d.ts +2 -2
- package/dist/generators/image.d.ts +2 -2
- package/dist/generators/prop-voxel.d.ts +60 -0
- package/dist/generators/prop-voxel.d.ts.map +1 -0
- package/dist/generators/prop-voxel.js +97 -0
- package/dist/generators/prop-voxel.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/judge-prompt.d.ts +53 -0
- package/dist/judge-prompt.d.ts.map +1 -0
- package/dist/judge-prompt.js +97 -0
- package/dist/judge-prompt.js.map +1 -0
- package/dist/judge-scorecard.d.ts +69 -0
- package/dist/judge-scorecard.d.ts.map +1 -0
- package/dist/judge-scorecard.js +39 -0
- package/dist/judge-scorecard.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { AssetResultBase, GenerateDeps } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* A 2D backdrop for side-scrolling and top-down games.
|
|
5
|
+
*
|
|
6
|
+
* ── Why this is a sibling of `skybox` and not a flag on it ───────────────────────────────────
|
|
7
|
+
*
|
|
8
|
+
* Both call the same image endpoint at the same 2048x1024 and write the same field
|
|
9
|
+
* (`worldProfileData.skyboxUrl`), so the machinery is identical. What differs is the PROMPT, and
|
|
10
|
+
* it differs completely: a skybox must wrap seamlessly around a sphere with matching left and
|
|
11
|
+
* right edges and its landmarks pushed above the horizon, while a backdrop wants layered depth and
|
|
12
|
+
* a clear ground line and is only ever seen head-on. Asking one prompt to serve both produces an
|
|
13
|
+
* image that is bad at each — a 2D scene with a visible seam, or a panorama with everything
|
|
14
|
+
* bunched at the bottom.
|
|
15
|
+
*
|
|
16
|
+
* The 2048x1024 is not incidental either: the engine maps `skyboxUrl` onto a sphere in every genre
|
|
17
|
+
* (`SkyboxMaterialHelper` builds a SphereGeometry), so a backdrop authored at any other aspect
|
|
18
|
+
* would arrive stretched. 2:1 is the equirectangular ratio, which is what makes one field able to
|
|
19
|
+
* carry both kinds of image.
|
|
20
|
+
*/
|
|
21
|
+
export declare const backgroundParamsSchema: z.ZodObject<{
|
|
22
|
+
description: z.ZodString;
|
|
23
|
+
referenceImageUrl: z.ZodOptional<z.ZodString>;
|
|
24
|
+
}, "strict", z.ZodTypeAny, {
|
|
25
|
+
description: string;
|
|
26
|
+
referenceImageUrl?: string | undefined;
|
|
27
|
+
}, {
|
|
28
|
+
description: string;
|
|
29
|
+
referenceImageUrl?: string | undefined;
|
|
30
|
+
}>;
|
|
31
|
+
export type BackgroundParams = z.input<typeof backgroundParamsSchema>;
|
|
32
|
+
export type BackgroundResult = AssetResultBase & {
|
|
33
|
+
url?: string;
|
|
34
|
+
billable?: boolean;
|
|
35
|
+
};
|
|
36
|
+
export declare function generateBackground(params: BackgroundParams, deps: GenerateDeps): Promise<BackgroundResult>;
|
|
37
|
+
//# sourceMappingURL=background.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"background.d.ts","sourceRoot":"","sources":["../../src/generators/background.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIjE;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;EAIxB,CAAC;AAEZ,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,MAAM,MAAM,gBAAgB,GAAG,eAAe,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAsBtF,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,gBAAgB,CAAC,CAmD3B"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { fail, ok } from '../result.js';
|
|
3
|
+
import { formatZodIssues } from '../validation.js';
|
|
4
|
+
/**
|
|
5
|
+
* A 2D backdrop for side-scrolling and top-down games.
|
|
6
|
+
*
|
|
7
|
+
* ── Why this is a sibling of `skybox` and not a flag on it ───────────────────────────────────
|
|
8
|
+
*
|
|
9
|
+
* Both call the same image endpoint at the same 2048x1024 and write the same field
|
|
10
|
+
* (`worldProfileData.skyboxUrl`), so the machinery is identical. What differs is the PROMPT, and
|
|
11
|
+
* it differs completely: a skybox must wrap seamlessly around a sphere with matching left and
|
|
12
|
+
* right edges and its landmarks pushed above the horizon, while a backdrop wants layered depth and
|
|
13
|
+
* a clear ground line and is only ever seen head-on. Asking one prompt to serve both produces an
|
|
14
|
+
* image that is bad at each — a 2D scene with a visible seam, or a panorama with everything
|
|
15
|
+
* bunched at the bottom.
|
|
16
|
+
*
|
|
17
|
+
* The 2048x1024 is not incidental either: the engine maps `skyboxUrl` onto a sphere in every genre
|
|
18
|
+
* (`SkyboxMaterialHelper` builds a SphereGeometry), so a backdrop authored at any other aspect
|
|
19
|
+
* would arrive stretched. 2:1 is the equirectangular ratio, which is what makes one field able to
|
|
20
|
+
* carry both kinds of image.
|
|
21
|
+
*/
|
|
22
|
+
export const backgroundParamsSchema = z.object({
|
|
23
|
+
description: z.string().min(1).describe('Description of the background scene to generate'),
|
|
24
|
+
referenceImageUrl: z.string().optional()
|
|
25
|
+
.describe('Style/composition reference forwarded to the image model'),
|
|
26
|
+
}).strict();
|
|
27
|
+
/** Same as the skybox: the engine sphere-maps this field, so 2:1 is the only ratio that fits. */
|
|
28
|
+
const BACKGROUND_WIDTH = 2048;
|
|
29
|
+
const BACKGROUND_HEIGHT = 1024;
|
|
30
|
+
const ENDPOINT = '/ai/v1/generate-image-as-webp-url';
|
|
31
|
+
/** Mirrors the agent lane's `background-image-generator`, so both lanes ask for the same picture. */
|
|
32
|
+
function buildPrompt(sceneDescription) {
|
|
33
|
+
return `Create a flat 2D game background illustration for a side-scrolling or top-down 2D game.
|
|
34
|
+
The scene description is: ${sceneDescription}.
|
|
35
|
+
Style: stylized game art with clear shapes, vibrant colors, and good contrast between foreground and background layers.
|
|
36
|
+
The image should work as a static backdrop behind 2D game objects and characters.
|
|
37
|
+
Include depth through layered elements (distant mountains/buildings, mid-ground features, ground/floor).
|
|
38
|
+
Ensure the image has a clear horizon or ground line.
|
|
39
|
+
Do NOT include any game characters, UI elements, or text.`;
|
|
40
|
+
}
|
|
41
|
+
export async function generateBackground(params, deps) {
|
|
42
|
+
const { forger, logger, onProgress } = deps;
|
|
43
|
+
const parsed = backgroundParamsSchema.safeParse(params);
|
|
44
|
+
if (!parsed.success) {
|
|
45
|
+
logger.warn({ err: parsed.error }, '[background] Invalid parameters');
|
|
46
|
+
return fail(formatZodIssues(parsed.error));
|
|
47
|
+
}
|
|
48
|
+
const input = parsed.data;
|
|
49
|
+
if (!forger.hasCredentials()) {
|
|
50
|
+
logger.error('[background] Missing Asset Forger credentials');
|
|
51
|
+
return fail(forger.getMissingCredentialsMessage());
|
|
52
|
+
}
|
|
53
|
+
onProgress(`Generating background: ${input.description}`);
|
|
54
|
+
logger.info(`[background] Starting generation for: "${input.description}"${input.referenceImageUrl ? ' (with reference image)' : ''}`);
|
|
55
|
+
try {
|
|
56
|
+
const response = await forger.post(ENDPOINT, {
|
|
57
|
+
prompt: buildPrompt(input.description),
|
|
58
|
+
height: BACKGROUND_HEIGHT,
|
|
59
|
+
width: BACKGROUND_WIDTH,
|
|
60
|
+
numImages: 1,
|
|
61
|
+
guidanceScale: 1.0,
|
|
62
|
+
...(input.referenceImageUrl ? { referenceImageUrl: input.referenceImageUrl } : {}),
|
|
63
|
+
}, 'background');
|
|
64
|
+
const url = response.imageUrl;
|
|
65
|
+
if (!url) {
|
|
66
|
+
// Billable: the Forger accepted and ran the job, and was paid for it, whatever it answered.
|
|
67
|
+
return { ...fail('Asset Forger returned no image URL'), billable: true };
|
|
68
|
+
}
|
|
69
|
+
logger.info(`[background] Successfully generated background image URL: ${url}`);
|
|
70
|
+
return ok(`Background image generated: ${url}`,
|
|
71
|
+
// The same field a skybox writes — see the file header for why one field carries both.
|
|
72
|
+
[{ type: 'set', path: ['skyboxUrl'], value: url }], { url, billable: true });
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
const message = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
76
|
+
logger.error({ err: error }, '[background] Error during background generation');
|
|
77
|
+
return fail(message);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=background.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"background.js","sourceRoot":"","sources":["../../src/generators/background.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { AssetResultBase, GenerateDeps } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* A custom voxel block type: one tileable texture, registered under a name the rest of the game
|
|
5
|
+
* refers to (`create-voxel-asset`'s `blockType`, `groundConfig.surfaceBlockType`, building code).
|
|
6
|
+
*/
|
|
7
|
+
export declare const blockTypeParamsSchema: z.ZodObject<{
|
|
8
|
+
name: z.ZodString;
|
|
9
|
+
displayName: z.ZodOptional<z.ZodString>;
|
|
10
|
+
description: z.ZodString;
|
|
11
|
+
sideDescription: z.ZodOptional<z.ZodString>;
|
|
12
|
+
referenceImageUrl: z.ZodOptional<z.ZodString>;
|
|
13
|
+
}, "strict", z.ZodTypeAny, {
|
|
14
|
+
description: string;
|
|
15
|
+
name: string;
|
|
16
|
+
referenceImageUrl?: string | undefined;
|
|
17
|
+
displayName?: string | undefined;
|
|
18
|
+
sideDescription?: string | undefined;
|
|
19
|
+
}, {
|
|
20
|
+
description: string;
|
|
21
|
+
name: string;
|
|
22
|
+
referenceImageUrl?: string | undefined;
|
|
23
|
+
displayName?: string | undefined;
|
|
24
|
+
sideDescription?: string | undefined;
|
|
25
|
+
}>;
|
|
26
|
+
export type BlockTypeParams = z.input<typeof blockTypeParamsSchema>;
|
|
27
|
+
export type BlockTypeResult = AssetResultBase & {
|
|
28
|
+
blockTypeName?: string;
|
|
29
|
+
textureUrl?: string;
|
|
30
|
+
sideTextureUrl?: string;
|
|
31
|
+
billable?: boolean;
|
|
32
|
+
};
|
|
33
|
+
export declare function generateBlockType(params: BlockTypeParams, deps: GenerateDeps): Promise<BlockTypeResult>;
|
|
34
|
+
//# sourceMappingURL=block-type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-type.d.ts","sourceRoot":"","sources":["../../src/generators/block-type.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIjE;;;GAGG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;EAWvB,CAAC;AAEZ,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAmDF,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,eAAe,CAAC,CA4H1B"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { fail, ok } from '../result.js';
|
|
3
|
+
import { formatZodIssues } from '../validation.js';
|
|
4
|
+
/**
|
|
5
|
+
* A custom voxel block type: one tileable texture, registered under a name the rest of the game
|
|
6
|
+
* refers to (`create-voxel-asset`'s `blockType`, `groundConfig.surfaceBlockType`, building code).
|
|
7
|
+
*/
|
|
8
|
+
export const blockTypeParamsSchema = z.object({
|
|
9
|
+
name: z.string().min(1)
|
|
10
|
+
.regex(/^[a-z][a-z0-9_]*$/, 'Name must be lowercase snake_case, starting with a letter')
|
|
11
|
+
.describe('Stable block-type id, lowercase snake_case (e.g. mossy_brick)'),
|
|
12
|
+
displayName: z.string().min(1).optional()
|
|
13
|
+
.describe('Human-readable label; derived from name when omitted'),
|
|
14
|
+
description: z.string().min(5).describe('What the block texture depicts'),
|
|
15
|
+
sideDescription: z.string().min(5).optional()
|
|
16
|
+
.describe('When given, the side faces get their own texture generated from this description'),
|
|
17
|
+
referenceImageUrl: z.string().optional()
|
|
18
|
+
.describe('Style/composition reference forwarded to the image model'),
|
|
19
|
+
}).strict();
|
|
20
|
+
const TEXTURE_PIXELS = 256;
|
|
21
|
+
const ENDPOINT = '/ai/v1/generate-image-as-webp-url';
|
|
22
|
+
/**
|
|
23
|
+
* The resample target the engine stores per block, NOT the size asked of the model.
|
|
24
|
+
*
|
|
25
|
+
* `BlockRegistry` reads `spec.textureSize || 64`, so omitting this or writing `0` silently
|
|
26
|
+
* quadruples the resolution of a texture whose prompt explicitly says "displayed at 16x16".
|
|
27
|
+
* Written as a literal, matching the agent lane.
|
|
28
|
+
*/
|
|
29
|
+
const TEXTURE_SIZE = 16;
|
|
30
|
+
/**
|
|
31
|
+
* Built-in Voxel block names and their aliases, from game-play-agent's `block-types.ts`.
|
|
32
|
+
*
|
|
33
|
+
* Duplicated rather than imported: asset-core is a leaf package that cannot depend on the agent,
|
|
34
|
+
* and this list is frozen in practice — the engine pins the built-in ids these names resolve to
|
|
35
|
+
* (`customBlockIdRange.test.ts`), so a name leaving the list would be an engine change.
|
|
36
|
+
* A custom block may not shadow one: `BlockRegistry` matches names case-insensitively and keeps
|
|
37
|
+
* the FIRST registration, so the built-in would win and the paid-for texture would vanish.
|
|
38
|
+
*/
|
|
39
|
+
const BUILTIN_BLOCK_NAMES = new Set([
|
|
40
|
+
'asphalt', 'dirt', 'foliage', 'grass', 'ice', 'lava', 'leaves',
|
|
41
|
+
'marble', 'road', 'rock', 'sand', 'stone', 'trunk', 'water', 'wood',
|
|
42
|
+
]);
|
|
43
|
+
/** Lifted from the agent lane's `block-texture-generator`, so both lanes ask for the same texture. */
|
|
44
|
+
function buildPrompt(description) {
|
|
45
|
+
return `Create a seamless tileable pixel art texture for a Minecraft-style voxel game block.
|
|
46
|
+
The texture should depict: ${description}.
|
|
47
|
+
CRITICAL REQUIREMENTS:
|
|
48
|
+
- Seamless tiling in all directions (edges must match perfectly)
|
|
49
|
+
- Pixel art style with large chunky pixels, NOT photorealistic
|
|
50
|
+
- Use only a few flat colors with minimal gradients
|
|
51
|
+
- Simple pattern with low detail — this will be displayed at 16x16 pixels
|
|
52
|
+
- Fill the entire image with the texture, no borders or margins
|
|
53
|
+
- No text, labels, or watermarks
|
|
54
|
+
- Flat lighting, minimal shading`;
|
|
55
|
+
}
|
|
56
|
+
/** `mossy_brick` -> `Mossy Brick`, matching the agent lane's derivation exactly. */
|
|
57
|
+
function deriveDisplayName(name) {
|
|
58
|
+
return name.replace(/_/g, ' ').replace(/\b\w/g, (character) => character.toUpperCase());
|
|
59
|
+
}
|
|
60
|
+
export async function generateBlockType(params, deps) {
|
|
61
|
+
const { forger, logger, onProgress } = deps;
|
|
62
|
+
const parsed = blockTypeParamsSchema.safeParse(params);
|
|
63
|
+
if (!parsed.success) {
|
|
64
|
+
logger.warn({ err: parsed.error }, '[block-type] Invalid parameters');
|
|
65
|
+
return fail(formatZodIssues(parsed.error));
|
|
66
|
+
}
|
|
67
|
+
const input = parsed.data;
|
|
68
|
+
// Before the credentials check, and well before any spend: a shadowed name cannot be rescued
|
|
69
|
+
// later, because the engine would keep the built-in and drop this block on load.
|
|
70
|
+
if (BUILTIN_BLOCK_NAMES.has(input.name.toLowerCase())) {
|
|
71
|
+
logger.warn(`[block-type] Refusing built-in name "${input.name}"`);
|
|
72
|
+
return fail(`Block type "${input.name}" is a built-in type and cannot be redefined. Pick a unique name.`);
|
|
73
|
+
}
|
|
74
|
+
if (!forger.hasCredentials()) {
|
|
75
|
+
logger.error('[block-type] Missing Asset Forger credentials');
|
|
76
|
+
return fail(forger.getMissingCredentialsMessage());
|
|
77
|
+
}
|
|
78
|
+
const displayName = input.displayName ?? deriveDisplayName(input.name);
|
|
79
|
+
onProgress(`Generating block texture: ${input.description}`);
|
|
80
|
+
logger.info(`[block-type] Starting generation for "${input.name}": "${input.description}"${input.referenceImageUrl ? ' (with reference image)' : ''}`);
|
|
81
|
+
let textureUrl;
|
|
82
|
+
try {
|
|
83
|
+
const response = await forger.post(ENDPOINT, {
|
|
84
|
+
prompt: buildPrompt(input.description),
|
|
85
|
+
height: TEXTURE_PIXELS,
|
|
86
|
+
width: TEXTURE_PIXELS,
|
|
87
|
+
numImages: 1,
|
|
88
|
+
guidanceScale: 1.0,
|
|
89
|
+
...(input.referenceImageUrl ? { referenceImageUrl: input.referenceImageUrl } : {}),
|
|
90
|
+
}, 'block-type');
|
|
91
|
+
if (!response.imageUrl) {
|
|
92
|
+
// Billable: the Forger ran the job and was paid for it, whatever it answered.
|
|
93
|
+
return { ...fail('Asset Forger returned no block texture URL'), billable: true };
|
|
94
|
+
}
|
|
95
|
+
textureUrl = response.imageUrl;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
const message = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
99
|
+
logger.error({ err: error }, '[block-type] Error during block texture generation');
|
|
100
|
+
return fail(message);
|
|
101
|
+
}
|
|
102
|
+
// The side texture is a bonus, not a precondition: a failure here leaves a perfectly usable
|
|
103
|
+
// block whose top texture covers every face, which is what the agent lane does too. Turning it
|
|
104
|
+
// into a hard failure would throw away a texture the creator has already paid for.
|
|
105
|
+
// No reference image on this call — the side faces follow `sideDescription` alone.
|
|
106
|
+
let sideTextureUrl = null;
|
|
107
|
+
if (input.sideDescription !== undefined) {
|
|
108
|
+
onProgress(`Generating side texture: ${input.sideDescription}`);
|
|
109
|
+
try {
|
|
110
|
+
const sideResponse = await forger.post(ENDPOINT, {
|
|
111
|
+
prompt: buildPrompt(input.sideDescription),
|
|
112
|
+
height: TEXTURE_PIXELS,
|
|
113
|
+
width: TEXTURE_PIXELS,
|
|
114
|
+
numImages: 1,
|
|
115
|
+
guidanceScale: 1.0,
|
|
116
|
+
}, 'block-type');
|
|
117
|
+
if (sideResponse.imageUrl) {
|
|
118
|
+
sideTextureUrl = sideResponse.imageUrl;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
logger.warn(`[block-type] Side texture came back empty for "${input.name}"; using the top texture on all faces`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
logger.warn({ err: error }, `[block-type] Side texture generation failed for "${input.name}"; using the top texture on all faces`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
logger.info(`[block-type] Registered "${input.name}" with texture ${textureUrl}`);
|
|
129
|
+
return ok(`Block type "${input.name}" generated: ${textureUrl}`, [{
|
|
130
|
+
// `upsertRoot` with a two-segment path, NOT the `push` the agent lane uses.
|
|
131
|
+
//
|
|
132
|
+
// The agent tool can afford `push` because it reads the game's world.json first and returns
|
|
133
|
+
// early when the name is already there. This generator runs on the SERVER and cannot see the
|
|
134
|
+
// creator's local file, so a `push` would append a second entry with the same name — and
|
|
135
|
+
// `BlockRegistry` keeps the FIRST case-insensitive match and discards the rest, silently
|
|
136
|
+
// throwing away the texture just paid for. Upserting on `name` makes a re-run REPLACE, which
|
|
137
|
+
// is also what makes "regenerate this block until I like it" work.
|
|
138
|
+
//
|
|
139
|
+
// Two segments rather than a `worldProfileData`-scoped `upsert` kind: `WorldPatch` is
|
|
140
|
+
// published and already installed out in the world, and an older CLI receiving an unknown
|
|
141
|
+
// patch type falls through to writing `value` at the document ROOT — clobbering the array
|
|
142
|
+
// with a single object, without an error. `upsertRoot` is understood by every applier that
|
|
143
|
+
// exists, and both walk the two segments correctly.
|
|
144
|
+
type: 'upsertRoot',
|
|
145
|
+
path: ['worldProfileData', 'customBlockTypes'],
|
|
146
|
+
matchKeys: ['name'],
|
|
147
|
+
value: {
|
|
148
|
+
name: input.name,
|
|
149
|
+
displayName,
|
|
150
|
+
description: input.description,
|
|
151
|
+
textureUrl,
|
|
152
|
+
// null, not undefined: it survives JSON and reads as "top texture on every face".
|
|
153
|
+
sideTextureUrl,
|
|
154
|
+
textureSize: TEXTURE_SIZE,
|
|
155
|
+
createdAt: new Date().toISOString(),
|
|
156
|
+
},
|
|
157
|
+
}], {
|
|
158
|
+
blockTypeName: input.name,
|
|
159
|
+
textureUrl,
|
|
160
|
+
...(sideTextureUrl ? { sideTextureUrl } : {}),
|
|
161
|
+
billable: true,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=block-type.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-type.js","sourceRoot":"","sources":["../../src/generators/block-type.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,14 +7,14 @@ export declare const characterParamsSchema: z.ZodObject<{
|
|
|
7
7
|
headScale: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
8
8
|
assetId: z.ZodOptional<z.ZodString>;
|
|
9
9
|
}, "strict", z.ZodTypeAny, {
|
|
10
|
-
prompt: string;
|
|
11
10
|
name: string;
|
|
11
|
+
prompt: string;
|
|
12
12
|
applyToPlayer: boolean;
|
|
13
13
|
headScale: number;
|
|
14
14
|
assetId?: string | undefined;
|
|
15
15
|
}, {
|
|
16
|
-
prompt: string;
|
|
17
16
|
name: string;
|
|
17
|
+
prompt: string;
|
|
18
18
|
assetId?: string | undefined;
|
|
19
19
|
applyToPlayer?: boolean | undefined;
|
|
20
20
|
headScale?: number | undefined;
|
|
@@ -8,15 +8,15 @@ export declare const imageAssetParamsSchema: z.ZodObject<{
|
|
|
8
8
|
referenceImageUrl: z.ZodOptional<z.ZodString>;
|
|
9
9
|
assetId: z.ZodOptional<z.ZodString>;
|
|
10
10
|
}, "strict", z.ZodTypeAny, {
|
|
11
|
-
prompt: string;
|
|
12
11
|
name: string;
|
|
12
|
+
prompt: string;
|
|
13
13
|
width: number;
|
|
14
14
|
height: number;
|
|
15
15
|
referenceImageUrl?: string | undefined;
|
|
16
16
|
assetId?: string | undefined;
|
|
17
17
|
}, {
|
|
18
|
-
prompt: string;
|
|
19
18
|
name: string;
|
|
19
|
+
prompt: string;
|
|
20
20
|
referenceImageUrl?: string | undefined;
|
|
21
21
|
assetId?: string | undefined;
|
|
22
22
|
width?: number | undefined;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { AssetResultBase, GenerateDeps } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* A 3D prop as VOXELS, from a prompt — with no mesh in between.
|
|
5
|
+
*
|
|
6
|
+
* The sibling of `prop.ts`, and the same shape: it returns no world.json patches, because a
|
|
7
|
+
* generated thing is only half an asset until the engine has baked it, and baking happens in a
|
|
8
|
+
* browser on the caller's side of the seam. What differs is what the Forger is asked for.
|
|
9
|
+
*
|
|
10
|
+
* `prop.ts` asks for a GLB. For a voxel asset that mesh is discarded work: TRELLIS.2 is already a
|
|
11
|
+
* sparse voxel generator internally, and the polygon mesh is produced afterwards by a decimation,
|
|
12
|
+
* remesh and PBR bake that a voxelizer immediately throws away. `/ai/v1/forge-voxels` taps the
|
|
13
|
+
* voxel field directly, so what comes back is a sub-megabyte HFVX master rather than a 50-100 MB
|
|
14
|
+
* textured mesh. Measured on staging: 219 KB and ~8.5s warm, against minutes and megabytes.
|
|
15
|
+
*
|
|
16
|
+
* The host bakes the master with `CREATE_ASSET_FROM_VXL_MASTER` instead of
|
|
17
|
+
* `CREATE_ASSET_FROM_GLB_URL`; both are answered on the same reply channel, so only the request
|
|
18
|
+
* differs. `game-play-agent/src/mastra/utils/hq-asset-jobs.ts` already picks between those two
|
|
19
|
+
* shapes — the web lane's HQ job takes this same fork when a local forge produced a master.
|
|
20
|
+
*
|
|
21
|
+
* Billable on acceptance, exactly as `prop.ts` is: the vendor charges for the job it ran, whatever
|
|
22
|
+
* it answered with.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Grids the master packer accepts, and why this is an allowlist rather than a positive integer.
|
|
26
|
+
*
|
|
27
|
+
* The packer maps native coordinates into the target grid by integer division, so a grid that does
|
|
28
|
+
* not divide the native resolution pushes the top coordinate past the grid — and reports that as
|
|
29
|
+
* "axis mapping is wrong", which sends the reader hunting somewhere nothing is wrong. asset-forger
|
|
30
|
+
* validates this too; validating here as well keeps a bad request from costing a GPU minute.
|
|
31
|
+
*
|
|
32
|
+
* 0 means "keep TRELLIS's own resolution" — the master every later re-voxelization downsamples
|
|
33
|
+
* from, rather than a working asset.
|
|
34
|
+
*/
|
|
35
|
+
export declare const propVoxelGrids: readonly [0, 32, 64, 128, 256, 512];
|
|
36
|
+
export declare const propVoxelParamsSchema: z.ZodObject<{
|
|
37
|
+
prompt: z.ZodString;
|
|
38
|
+
/**
|
|
39
|
+
* 128 matches what the web lane has always produced voxel assets at. A project should not get a
|
|
40
|
+
* different-looking asset depending on which lane asked for it.
|
|
41
|
+
*/
|
|
42
|
+
grid: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodLiteral<0>, z.ZodLiteral<32>, z.ZodLiteral<64>, z.ZodLiteral<128>, z.ZodLiteral<256>, z.ZodLiteral<512>]>>>;
|
|
43
|
+
}, "strict", z.ZodTypeAny, {
|
|
44
|
+
prompt: string;
|
|
45
|
+
grid: 0 | 64 | 32 | 256 | 512 | 128;
|
|
46
|
+
}, {
|
|
47
|
+
prompt: string;
|
|
48
|
+
grid?: 0 | 64 | 32 | 256 | 512 | 128 | undefined;
|
|
49
|
+
}>;
|
|
50
|
+
export type PropVoxelParams = z.input<typeof propVoxelParamsSchema>;
|
|
51
|
+
export type PropVoxelResult = AssetResultBase & {
|
|
52
|
+
/** Where the HFVX voxel master landed. The host bakes this into a `.vxl`. */
|
|
53
|
+
masterUrl?: string;
|
|
54
|
+
/** TRELLIS's own resolution, which the master was downsampled from. */
|
|
55
|
+
resolution?: number;
|
|
56
|
+
voxelCount?: number;
|
|
57
|
+
billable?: boolean;
|
|
58
|
+
};
|
|
59
|
+
export declare function generatePropVoxels(params: PropVoxelParams, deps: GenerateDeps): Promise<PropVoxelResult>;
|
|
60
|
+
//# sourceMappingURL=prop-voxel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prop-voxel.d.ts","sourceRoot":"","sources":["../../src/generators/prop-voxel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIjE;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,qCAAsC,CAAC;AAElE,eAAO,MAAM,qBAAqB;;IAIhC;;;OAGG;;;;;;;;EAKM,CAAC;AAEZ,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG;IAC9C,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAiBF,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,eAAe,CAAC,CAoD1B"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { fail, ok } from '../result.js';
|
|
3
|
+
import { formatZodIssues } from '../validation.js';
|
|
4
|
+
/**
|
|
5
|
+
* A 3D prop as VOXELS, from a prompt — with no mesh in between.
|
|
6
|
+
*
|
|
7
|
+
* The sibling of `prop.ts`, and the same shape: it returns no world.json patches, because a
|
|
8
|
+
* generated thing is only half an asset until the engine has baked it, and baking happens in a
|
|
9
|
+
* browser on the caller's side of the seam. What differs is what the Forger is asked for.
|
|
10
|
+
*
|
|
11
|
+
* `prop.ts` asks for a GLB. For a voxel asset that mesh is discarded work: TRELLIS.2 is already a
|
|
12
|
+
* sparse voxel generator internally, and the polygon mesh is produced afterwards by a decimation,
|
|
13
|
+
* remesh and PBR bake that a voxelizer immediately throws away. `/ai/v1/forge-voxels` taps the
|
|
14
|
+
* voxel field directly, so what comes back is a sub-megabyte HFVX master rather than a 50-100 MB
|
|
15
|
+
* textured mesh. Measured on staging: 219 KB and ~8.5s warm, against minutes and megabytes.
|
|
16
|
+
*
|
|
17
|
+
* The host bakes the master with `CREATE_ASSET_FROM_VXL_MASTER` instead of
|
|
18
|
+
* `CREATE_ASSET_FROM_GLB_URL`; both are answered on the same reply channel, so only the request
|
|
19
|
+
* differs. `game-play-agent/src/mastra/utils/hq-asset-jobs.ts` already picks between those two
|
|
20
|
+
* shapes — the web lane's HQ job takes this same fork when a local forge produced a master.
|
|
21
|
+
*
|
|
22
|
+
* Billable on acceptance, exactly as `prop.ts` is: the vendor charges for the job it ran, whatever
|
|
23
|
+
* it answered with.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Grids the master packer accepts, and why this is an allowlist rather than a positive integer.
|
|
27
|
+
*
|
|
28
|
+
* The packer maps native coordinates into the target grid by integer division, so a grid that does
|
|
29
|
+
* not divide the native resolution pushes the top coordinate past the grid — and reports that as
|
|
30
|
+
* "axis mapping is wrong", which sends the reader hunting somewhere nothing is wrong. asset-forger
|
|
31
|
+
* validates this too; validating here as well keeps a bad request from costing a GPU minute.
|
|
32
|
+
*
|
|
33
|
+
* 0 means "keep TRELLIS's own resolution" — the master every later re-voxelization downsamples
|
|
34
|
+
* from, rather than a working asset.
|
|
35
|
+
*/
|
|
36
|
+
export const propVoxelGrids = [0, 32, 64, 128, 256, 512];
|
|
37
|
+
export const propVoxelParamsSchema = z.object({
|
|
38
|
+
prompt: z.string()
|
|
39
|
+
.min(1)
|
|
40
|
+
.describe('What the object should be (e.g. "a weathered stone archway covered in moss")'),
|
|
41
|
+
/**
|
|
42
|
+
* 128 matches what the web lane has always produced voxel assets at. A project should not get a
|
|
43
|
+
* different-looking asset depending on which lane asked for it.
|
|
44
|
+
*/
|
|
45
|
+
grid: z.union([
|
|
46
|
+
z.literal(0), z.literal(32), z.literal(64),
|
|
47
|
+
z.literal(128), z.literal(256), z.literal(512),
|
|
48
|
+
]).optional().default(128),
|
|
49
|
+
}).strict();
|
|
50
|
+
/**
|
|
51
|
+
* The async job route, not the synchronous one. A cold GPU container compiles CUDA kernels and
|
|
52
|
+
* loads a 4B model before it can generate, so a first request runs to minutes — well past what a
|
|
53
|
+
* single HTTP request should hold open.
|
|
54
|
+
*/
|
|
55
|
+
const ENDPOINT = '/ai/v1/forge-voxels/jobs';
|
|
56
|
+
export async function generatePropVoxels(params, deps) {
|
|
57
|
+
const { forger, logger, onProgress } = deps;
|
|
58
|
+
const parsed = propVoxelParamsSchema.safeParse(params);
|
|
59
|
+
if (!parsed.success) {
|
|
60
|
+
logger.warn({ err: parsed.error }, '[prop-voxel] Invalid parameters');
|
|
61
|
+
return fail(formatZodIssues(parsed.error));
|
|
62
|
+
}
|
|
63
|
+
const input = parsed.data;
|
|
64
|
+
if (!forger.hasCredentials()) {
|
|
65
|
+
logger.error('[prop-voxel] Missing Asset Forger credentials');
|
|
66
|
+
return fail(forger.getMissingCredentialsMessage());
|
|
67
|
+
}
|
|
68
|
+
onProgress(`Generating voxels for: ${input.prompt}`);
|
|
69
|
+
logger.info(`[prop-voxel] Starting forge-voxels for "${input.prompt}" at grid ${input.grid}`);
|
|
70
|
+
let response;
|
|
71
|
+
try {
|
|
72
|
+
response = await forger.postJobAndWait(ENDPOINT, { prompt: input.prompt, grid: input.grid }, 'prop-voxel');
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
const message = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
76
|
+
logger.error({ err: error }, '[prop-voxel] Error during generation');
|
|
77
|
+
return fail(message);
|
|
78
|
+
}
|
|
79
|
+
const masterUrl = response.data?.masterUrl;
|
|
80
|
+
if (!masterUrl) {
|
|
81
|
+
// Billable: the Forger accepted and ran the job, and was paid for it, whatever it answered.
|
|
82
|
+
return { ...fail('Asset Forger returned no master URL for the generated voxels'), billable: true };
|
|
83
|
+
}
|
|
84
|
+
const { resolution, voxelCount } = response.data ?? {};
|
|
85
|
+
logger.info(`[prop-voxel] Generated ${masterUrl} (${voxelCount ?? '?'} voxels)`);
|
|
86
|
+
return ok(`Voxels generated: ${masterUrl}`,
|
|
87
|
+
// Deliberately empty — see the file header. The asset does not exist until the host has baked
|
|
88
|
+
// this master, and writing a half-made entry would leave world.json describing an asset the
|
|
89
|
+
// engine cannot load.
|
|
90
|
+
[], {
|
|
91
|
+
masterUrl,
|
|
92
|
+
...(resolution === undefined ? {} : { resolution }),
|
|
93
|
+
...(voxelCount === undefined ? {} : { voxelCount }),
|
|
94
|
+
billable: true,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=prop-voxel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prop-voxel.js","sourceRoot":"","sources":["../../src/generators/prop-voxel.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,10 @@ export { AssetForgerClient } from './forger-client.js';
|
|
|
5
5
|
export type { AssetForgerCredentials } from './forger-client.js';
|
|
6
6
|
export { generateSkybox, skyboxParamsSchema } from './generators/skybox.js';
|
|
7
7
|
export type { SkyboxParams, SkyboxResult } from './generators/skybox.js';
|
|
8
|
+
export { generateBackground, backgroundParamsSchema } from './generators/background.js';
|
|
9
|
+
export type { BackgroundParams, BackgroundResult } from './generators/background.js';
|
|
10
|
+
export { generateBlockType, blockTypeParamsSchema } from './generators/block-type.js';
|
|
11
|
+
export type { BlockTypeParams, BlockTypeResult } from './generators/block-type.js';
|
|
8
12
|
export { generateSoundEffect, soundEffectParamsSchema } from './generators/sound-effect.js';
|
|
9
13
|
export type { SoundEffectParams, SoundEffectResult } from './generators/sound-effect.js';
|
|
10
14
|
export { generateImageAsset, imageAssetParamsSchema } from './generators/image.js';
|
|
@@ -13,10 +17,16 @@ export { generateCharacter, characterParamsSchema } from './generators/character
|
|
|
13
17
|
export type { CharacterParams, CharacterResult } from './generators/character.js';
|
|
14
18
|
export { generateProp, propParamsSchema } from './generators/prop.js';
|
|
15
19
|
export type { PropParams, PropResult } from './generators/prop.js';
|
|
20
|
+
export { generatePropVoxels, propVoxelParamsSchema, propVoxelGrids } from './generators/prop-voxel.js';
|
|
21
|
+
export type { PropVoxelParams, PropVoxelResult } from './generators/prop-voxel.js';
|
|
16
22
|
export { generateAnimationAsset, animationParamsSchema, getConfiguredAnimationModel, ANIMATION_MODEL_TEXT_TO_MOTION_3, ANIMATION_MODEL_TEXT_TO_MOTION_2, ANIMATION_MODEL_OLDER, } from './generators/animation.js';
|
|
17
23
|
export type { AnimationParams, AnimationResult } from './generators/animation.js';
|
|
18
24
|
export { fetchContentLength } from './http.js';
|
|
19
25
|
export { PIXAR_VOXEL_STYLE, MAX_COVER_DESIGN_CHARS, buildCoverArtPrompt } from './cover-prompt.js';
|
|
26
|
+
export { JUDGE_DIMENSIONS, judgeScorecardSchema } from './judge-scorecard.js';
|
|
27
|
+
export type { JudgeDimension, JudgeScorecard } from './judge-scorecard.js';
|
|
28
|
+
export { JUDGE_GENRE_HINTS, JUDGE_RUBRIC, MAX_JUDGE_DESIGN_CHARS, MAX_JUDGE_SCENE_FACTS_CHARS, MAX_JUDGE_VERIFY_CHARS, buildJudgeRubric, } from './judge-prompt.js';
|
|
29
|
+
export type { JudgeRubricInput } from './judge-prompt.js';
|
|
20
30
|
export { TERMINAL_FAILURE_CODES, TERMINAL_FAILURE_VERSION, buildTerminalFailure, isTerminalFailureCode, legacyErrorTypeFor, parseTerminalFailure, } from './terminal-failure.js';
|
|
21
31
|
export type { BuildTerminalFailureInput, LegacyErrorType, TerminalFailure, TerminalFailureCode, TerminalFailureDetail, } from './terminal-failure.js';
|
|
22
32
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,UAAU,EACV,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC/F,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5E,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAC5F,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACnF,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACrF,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACtE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,2BAA2B,EAC3B,gCAAgC,EAChC,gCAAgC,EAChC,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,UAAU,EACV,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC/F,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5E,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACxF,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACtF,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AACnF,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAC5F,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACnF,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACrF,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACtE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACvG,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AACnF,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,2BAA2B,EAC3B,gCAAgC,EAChC,gCAAgC,EAChC,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACnG,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC9E,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,sBAAsB,EACtB,2BAA2B,EAC3B,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAM1D,OAAO,EACL,sBAAsB,EACtB,wBAAwB,EACxB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,yBAAyB,EACzB,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,13 +2,18 @@ export { buildUpsertPredicate, isUpsertRootPatch, validateWorldPatch } from './w
|
|
|
2
2
|
export { fail, ok } from './result.js';
|
|
3
3
|
export { AssetForgerClient } from './forger-client.js';
|
|
4
4
|
export { generateSkybox, skyboxParamsSchema } from './generators/skybox.js';
|
|
5
|
+
export { generateBackground, backgroundParamsSchema } from './generators/background.js';
|
|
6
|
+
export { generateBlockType, blockTypeParamsSchema } from './generators/block-type.js';
|
|
5
7
|
export { generateSoundEffect, soundEffectParamsSchema } from './generators/sound-effect.js';
|
|
6
8
|
export { generateImageAsset, imageAssetParamsSchema } from './generators/image.js';
|
|
7
9
|
export { generateCharacter, characterParamsSchema } from './generators/character.js';
|
|
8
10
|
export { generateProp, propParamsSchema } from './generators/prop.js';
|
|
11
|
+
export { generatePropVoxels, propVoxelParamsSchema, propVoxelGrids } from './generators/prop-voxel.js';
|
|
9
12
|
export { generateAnimationAsset, animationParamsSchema, getConfiguredAnimationModel, ANIMATION_MODEL_TEXT_TO_MOTION_3, ANIMATION_MODEL_TEXT_TO_MOTION_2, ANIMATION_MODEL_OLDER, } from './generators/animation.js';
|
|
10
13
|
export { fetchContentLength } from './http.js';
|
|
11
14
|
export { PIXAR_VOXEL_STYLE, MAX_COVER_DESIGN_CHARS, buildCoverArtPrompt } from './cover-prompt.js';
|
|
15
|
+
export { JUDGE_DIMENSIONS, judgeScorecardSchema } from './judge-scorecard.js';
|
|
16
|
+
export { JUDGE_GENRE_HINTS, JUDGE_RUBRIC, MAX_JUDGE_DESIGN_CHARS, MAX_JUDGE_SCENE_FACTS_CHARS, MAX_JUDGE_VERIFY_CHARS, buildJudgeRubric, } from './judge-prompt.js';
|
|
12
17
|
// The agent↔client terminal-failure contract. Also published as the
|
|
13
18
|
// `@bitmagic/asset-core/terminal-failure` subpath: this barrel eagerly pulls in
|
|
14
19
|
// the generators (and therefore zod, the forger client, and node http helpers),
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC/F,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5E,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAE5F,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAEnF,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAErF,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEtE,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,2BAA2B,EAC3B,gCAAgC,EAChC,gCAAgC,EAChC,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC/F,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5E,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAExF,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEtF,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAE5F,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAEnF,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAErF,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEtE,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEvG,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,2BAA2B,EAC3B,gCAAgC,EAChC,gCAAgC,EAChC,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACnG,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE9E,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,sBAAsB,EACtB,2BAA2B,EAC3B,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAG3B,oEAAoE;AACpE,gFAAgF;AAChF,gFAAgF;AAChF,oEAAoE;AACpE,OAAO,EACL,sBAAsB,EACtB,wBAAwB,EACxB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quality-judge rubric composition for `bitmagic judge`. Like cover-prompt.ts
|
|
3
|
+
* this module composes strings only — no network, no credentials — and lives
|
|
4
|
+
* here so any lane that judges a game frame grades against the same rubric.
|
|
5
|
+
*
|
|
6
|
+
* Split of responsibilities: the CLI composes everything project-derived (the
|
|
7
|
+
* design excerpt, the genre hint, the verify summary) via buildJudgeRubric;
|
|
8
|
+
* the api-server owns the fixed judge instructions and the output-schema
|
|
9
|
+
* directive, so the instructions and judgeScorecardSchema cannot drift apart.
|
|
10
|
+
*/
|
|
11
|
+
/** How much of GAME-DESIGN.md reaches the judge; the rest is dropped. */
|
|
12
|
+
export declare const MAX_JUDGE_DESIGN_CHARS = 4000;
|
|
13
|
+
/** How much verify context (run state + console tail) reaches the judge. */
|
|
14
|
+
export declare const MAX_JUDGE_VERIFY_CHARS = 4000;
|
|
15
|
+
/** How much engine scene inventory reaches the judge. */
|
|
16
|
+
export declare const MAX_JUDGE_SCENE_FACTS_CHARS = 2500;
|
|
17
|
+
/**
|
|
18
|
+
* The generic rubric: every dimension with concrete anchors for what a low and
|
|
19
|
+
* a high score look like, so two runs of a small model land near each other.
|
|
20
|
+
* Keep anchors about what is VISIBLE IN A FRAME — the judge sees screenshots,
|
|
21
|
+
* not gameplay.
|
|
22
|
+
*/
|
|
23
|
+
export declare const JUDGE_RUBRIC = "Score each dimension from 1 to 10:\n\n- visual-quality: Does the frame look like a finished game? 3 = flat default\n materials, untextured primitives, harsh unshadowed lighting. 8 = cohesive\n palette, deliberate lighting, materials and shapes that read as one art style.\n- readability: Could a new player tell from this frame alone what to do and\n where to go? 3 = player, goals and hazards blend into the background. 8 = the\n player character, the route and the interactive elements pop at a glance.\n- design-fidelity: Does the frame match the game the design document describes?\n 3 = generic scene that could be any game. 8 = the described setting, mood and\n core mechanic are recognizably on screen.\n- polish: 3 = placeholder blocks, floating or intersecting props, empty default\n skybox, raw default HUD. 8 = grounded objects, dressed edges of the world,\n HUD that belongs to this game.";
|
|
24
|
+
/**
|
|
25
|
+
* Genre-specific bullets, keyed loosely on the engine's mechanic recipe names
|
|
26
|
+
* (mechanic-platformer.md and friends). Deliberately short: these steer the
|
|
27
|
+
* judge's attention, they do not restate the recipes — the full recipes are
|
|
28
|
+
* engine implementation docs and never reach the model.
|
|
29
|
+
*/
|
|
30
|
+
export declare const JUDGE_GENRE_HINTS: Record<string, string>;
|
|
31
|
+
export interface JudgeRubricInput {
|
|
32
|
+
/** GAME-DESIGN.md content; empty when the project has none. */
|
|
33
|
+
designText: string;
|
|
34
|
+
/** Optional genre key; unknown keys are ignored. */
|
|
35
|
+
genre?: string;
|
|
36
|
+
/** One-paragraph verify summary: run state plus a console tail. */
|
|
37
|
+
verifySummary?: string;
|
|
38
|
+
/**
|
|
39
|
+
* A deterministic inventory of the scene (object counts and spread, assets,
|
|
40
|
+
* world settings), composed in code from world.json. The frame shows one
|
|
41
|
+
* camera angle; this is the whole world — the rubric tells the judge to
|
|
42
|
+
* trust it over inference from pixels, which is what stops findings like
|
|
43
|
+
* "the world is empty" when the content is simply behind the camera.
|
|
44
|
+
*/
|
|
45
|
+
sceneFacts?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Compose the project-derived half of the judge prompt. The result is sent as
|
|
49
|
+
* the text part alongside the screenshot(s); the server prepends its fixed
|
|
50
|
+
* judging instructions and appends the JSON output directive.
|
|
51
|
+
*/
|
|
52
|
+
export declare function buildJudgeRubric({ designText, genre, verifySummary, sceneFacts }: JudgeRubricInput): string;
|
|
53
|
+
//# sourceMappingURL=judge-prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"judge-prompt.d.ts","sourceRoot":"","sources":["../src/judge-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,yEAAyE;AACzE,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAE3C,4EAA4E;AAC5E,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAE3C,yDAAyD;AACzD,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,YAAY,i5BAaQ,CAAC;AAElC;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAmBpD,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAOD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,EAAE,gBAAgB,GAAG,MAAM,CAmC3G"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quality-judge rubric composition for `bitmagic judge`. Like cover-prompt.ts
|
|
3
|
+
* this module composes strings only — no network, no credentials — and lives
|
|
4
|
+
* here so any lane that judges a game frame grades against the same rubric.
|
|
5
|
+
*
|
|
6
|
+
* Split of responsibilities: the CLI composes everything project-derived (the
|
|
7
|
+
* design excerpt, the genre hint, the verify summary) via buildJudgeRubric;
|
|
8
|
+
* the api-server owns the fixed judge instructions and the output-schema
|
|
9
|
+
* directive, so the instructions and judgeScorecardSchema cannot drift apart.
|
|
10
|
+
*/
|
|
11
|
+
import { JUDGE_DIMENSIONS } from './judge-scorecard.js';
|
|
12
|
+
/** How much of GAME-DESIGN.md reaches the judge; the rest is dropped. */
|
|
13
|
+
export const MAX_JUDGE_DESIGN_CHARS = 4000;
|
|
14
|
+
/** How much verify context (run state + console tail) reaches the judge. */
|
|
15
|
+
export const MAX_JUDGE_VERIFY_CHARS = 4000;
|
|
16
|
+
/** How much engine scene inventory reaches the judge. */
|
|
17
|
+
export const MAX_JUDGE_SCENE_FACTS_CHARS = 2500;
|
|
18
|
+
/**
|
|
19
|
+
* The generic rubric: every dimension with concrete anchors for what a low and
|
|
20
|
+
* a high score look like, so two runs of a small model land near each other.
|
|
21
|
+
* Keep anchors about what is VISIBLE IN A FRAME — the judge sees screenshots,
|
|
22
|
+
* not gameplay.
|
|
23
|
+
*/
|
|
24
|
+
export const JUDGE_RUBRIC = `Score each dimension from 1 to 10:
|
|
25
|
+
|
|
26
|
+
- visual-quality: Does the frame look like a finished game? 3 = flat default
|
|
27
|
+
materials, untextured primitives, harsh unshadowed lighting. 8 = cohesive
|
|
28
|
+
palette, deliberate lighting, materials and shapes that read as one art style.
|
|
29
|
+
- readability: Could a new player tell from this frame alone what to do and
|
|
30
|
+
where to go? 3 = player, goals and hazards blend into the background. 8 = the
|
|
31
|
+
player character, the route and the interactive elements pop at a glance.
|
|
32
|
+
- design-fidelity: Does the frame match the game the design document describes?
|
|
33
|
+
3 = generic scene that could be any game. 8 = the described setting, mood and
|
|
34
|
+
core mechanic are recognizably on screen.
|
|
35
|
+
- polish: 3 = placeholder blocks, floating or intersecting props, empty default
|
|
36
|
+
skybox, raw default HUD. 8 = grounded objects, dressed edges of the world,
|
|
37
|
+
HUD that belongs to this game.`;
|
|
38
|
+
/**
|
|
39
|
+
* Genre-specific bullets, keyed loosely on the engine's mechanic recipe names
|
|
40
|
+
* (mechanic-platformer.md and friends). Deliberately short: these steer the
|
|
41
|
+
* judge's attention, they do not restate the recipes — the full recipes are
|
|
42
|
+
* engine implementation docs and never reach the model.
|
|
43
|
+
*/
|
|
44
|
+
export const JUDGE_GENRE_HINTS = {
|
|
45
|
+
platformer: `- Is the traversal route readable — can you see where to jump next?
|
|
46
|
+
- Do platforms and hazards contrast with the backdrop?
|
|
47
|
+
- Does the camera frame the player with room in the direction of travel?`,
|
|
48
|
+
racing: `- Is the track boundary unmistakable at speed?
|
|
49
|
+
- Do track, walls and scenery separate visually?
|
|
50
|
+
- Is there a sense of direction — where the next corner goes?`,
|
|
51
|
+
shooter: `- Are potential targets visible against the environment?
|
|
52
|
+
- Is there a usable crosshair/aim affordance and ammo/health HUD?
|
|
53
|
+
- Does the scene offer readable cover and sightlines?`,
|
|
54
|
+
combat: `- Do combatants stand out from the arena?
|
|
55
|
+
- Are health/stamina affordances present and legible?
|
|
56
|
+
- Does the arena read as a bounded fighting space?`,
|
|
57
|
+
driving: `- Is the drivable surface clearly distinct from off-road?
|
|
58
|
+
- Does the vehicle sit convincingly on the ground?
|
|
59
|
+
- Are speed cues (road markings, scenery density) present?`,
|
|
60
|
+
'tower-defense': `- Is the creep path readable end to end?
|
|
61
|
+
- Do buildable spots and built towers read differently?
|
|
62
|
+
- Is the economy/wave HUD legible?`,
|
|
63
|
+
};
|
|
64
|
+
/** A section's text, trimmed and capped — empty when there is nothing to send. */
|
|
65
|
+
function excerpt(text, maxChars) {
|
|
66
|
+
return text?.trim().slice(0, maxChars) ?? '';
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Compose the project-derived half of the judge prompt. The result is sent as
|
|
70
|
+
* the text part alongside the screenshot(s); the server prepends its fixed
|
|
71
|
+
* judging instructions and appends the JSON output directive.
|
|
72
|
+
*/
|
|
73
|
+
export function buildJudgeRubric({ designText, genre, verifySummary, sceneFacts }) {
|
|
74
|
+
const sections = [JUDGE_RUBRIC];
|
|
75
|
+
const genreKey = genre?.trim().toLowerCase() ?? '';
|
|
76
|
+
const genreHints = JUDGE_GENRE_HINTS[genreKey];
|
|
77
|
+
if (genreHints) {
|
|
78
|
+
sections.push(`Genre-specific checks (${genreKey}):\n${genreHints}`);
|
|
79
|
+
}
|
|
80
|
+
const design = excerpt(designText, MAX_JUDGE_DESIGN_CHARS);
|
|
81
|
+
sections.push(design
|
|
82
|
+
? `The game's design document (judge design-fidelity against this):\n${design}`
|
|
83
|
+
: 'No design document was provided; score design-fidelity on internal coherence — does the frame look like one deliberate game rather than assorted parts?');
|
|
84
|
+
const facts = excerpt(sceneFacts, MAX_JUDGE_SCENE_FACTS_CHARS);
|
|
85
|
+
if (facts) {
|
|
86
|
+
sections.push('Ground truth from the engine — the frame shows ONE camera angle of this world. Trust these '
|
|
87
|
+
+ 'facts over anything you infer from the frame, and never report a finding they '
|
|
88
|
+
+ `contradict (e.g. "the world is empty" when objects exist off-camera):\n${facts}`);
|
|
89
|
+
}
|
|
90
|
+
const verify = excerpt(verifySummary, MAX_JUDGE_VERIFY_CHARS);
|
|
91
|
+
if (verify) {
|
|
92
|
+
sections.push(`Context from the automated boot check of this exact build:\n${verify}`);
|
|
93
|
+
}
|
|
94
|
+
sections.push(`Findings must be specific, visible in the frame, and actionable by a developer — name the object or region and say what to change. Report at most 3, most severe first. Dimensions to score: ${JUDGE_DIMENSIONS.join(', ')}.`);
|
|
95
|
+
return sections.join('\n\n');
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=judge-prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"judge-prompt.js","sourceRoot":"","sources":["../src/judge-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExD,yEAAyE;AACzE,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAE3C,4EAA4E;AAC5E,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAE3C,yDAAyD;AACzD,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC;AAEhD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;iCAaK,CAAC;AAElC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAA2B;IACvD,UAAU,EAAE;;yEAE2D;IACvE,MAAM,EAAE;;8DAEoD;IAC5D,OAAO,EAAE;;sDAE2C;IACpD,MAAM,EAAE;;mDAEyC;IACjD,OAAO,EAAE;;2DAEgD;IACzD,eAAe,EAAE;;mCAEgB;CAClC,CAAC;AAmBF,kFAAkF;AAClF,SAAS,OAAO,CAAC,IAAwB,EAAE,QAAgB;IACzD,OAAO,IAAI,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAoB;IACjG,MAAM,QAAQ,GAAa,CAAC,YAAY,CAAC,CAAC;IAE1C,MAAM,QAAQ,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC;IACnD,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC/C,IAAI,UAAU,EAAE,CAAC;QACf,QAAQ,CAAC,IAAI,CAAC,0BAA0B,QAAQ,OAAO,UAAU,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC;IAC3D,QAAQ,CAAC,IAAI,CACX,MAAM;QACJ,CAAC,CAAC,qEAAqE,MAAM,EAAE;QAC/E,CAAC,CAAC,yJAAyJ,CAC9J,CAAC;IAEF,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,EAAE,2BAA2B,CAAC,CAAC;IAC/D,IAAI,KAAK,EAAE,CAAC;QACV,QAAQ,CAAC,IAAI,CACX,6FAA6F;cACzF,gFAAgF;cAChF,0EAA0E,KAAK,EAAE,CACtF,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,sBAAsB,CAAC,CAAC;IAC9D,IAAI,MAAM,EAAE,CAAC;QACX,QAAQ,CAAC,IAAI,CAAC,+DAA+D,MAAM,EAAE,CAAC,CAAC;IACzF,CAAC;IAED,QAAQ,CAAC,IAAI,CACX,gMAAgM,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC/N,CAAC;IAEF,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* The quality-judge scorecard contract, shared by the api-server judge route
|
|
4
|
+
* (which validates what the vision model returned) and the CLI (which
|
|
5
|
+
* validates the terminal frame before writing `.bitmagic/judge.json`). It
|
|
6
|
+
* lives here so the two sides can never drift: a scorecard the server accepts
|
|
7
|
+
* is by construction one the CLI can record and render.
|
|
8
|
+
*
|
|
9
|
+
* Dimension names are a fixed enum, not free text, so scores stay comparable
|
|
10
|
+
* run over run — "readability: 4 → 7" only means something if both runs were
|
|
11
|
+
* grading the same axis.
|
|
12
|
+
*/
|
|
13
|
+
export declare const JUDGE_DIMENSIONS: readonly ["visual-quality", "readability", "design-fidelity", "polish"];
|
|
14
|
+
export type JudgeDimension = (typeof JUDGE_DIMENSIONS)[number];
|
|
15
|
+
export declare const judgeScorecardSchema: z.ZodObject<{
|
|
16
|
+
overall: z.ZodNumber;
|
|
17
|
+
dimensions: z.ZodArray<z.ZodObject<{
|
|
18
|
+
name: z.ZodEnum<["visual-quality", "readability", "design-fidelity", "polish"]>;
|
|
19
|
+
score: z.ZodNumber;
|
|
20
|
+
note: z.ZodString;
|
|
21
|
+
}, "strip", z.ZodTypeAny, {
|
|
22
|
+
name: "visual-quality" | "readability" | "design-fidelity" | "polish";
|
|
23
|
+
score: number;
|
|
24
|
+
note: string;
|
|
25
|
+
}, {
|
|
26
|
+
name: "visual-quality" | "readability" | "design-fidelity" | "polish";
|
|
27
|
+
score: number;
|
|
28
|
+
note: string;
|
|
29
|
+
}>, "many">;
|
|
30
|
+
topFindings: z.ZodArray<z.ZodObject<{
|
|
31
|
+
title: z.ZodString;
|
|
32
|
+
detail: z.ZodString;
|
|
33
|
+
severity: z.ZodEnum<["high", "medium", "low"]>;
|
|
34
|
+
}, "strip", z.ZodTypeAny, {
|
|
35
|
+
title: string;
|
|
36
|
+
detail: string;
|
|
37
|
+
severity: "high" | "medium" | "low";
|
|
38
|
+
}, {
|
|
39
|
+
title: string;
|
|
40
|
+
detail: string;
|
|
41
|
+
severity: "high" | "medium" | "low";
|
|
42
|
+
}>, "many">;
|
|
43
|
+
}, "strip", z.ZodTypeAny, {
|
|
44
|
+
overall: number;
|
|
45
|
+
dimensions: {
|
|
46
|
+
name: "visual-quality" | "readability" | "design-fidelity" | "polish";
|
|
47
|
+
score: number;
|
|
48
|
+
note: string;
|
|
49
|
+
}[];
|
|
50
|
+
topFindings: {
|
|
51
|
+
title: string;
|
|
52
|
+
detail: string;
|
|
53
|
+
severity: "high" | "medium" | "low";
|
|
54
|
+
}[];
|
|
55
|
+
}, {
|
|
56
|
+
overall: number;
|
|
57
|
+
dimensions: {
|
|
58
|
+
name: "visual-quality" | "readability" | "design-fidelity" | "polish";
|
|
59
|
+
score: number;
|
|
60
|
+
note: string;
|
|
61
|
+
}[];
|
|
62
|
+
topFindings: {
|
|
63
|
+
title: string;
|
|
64
|
+
detail: string;
|
|
65
|
+
severity: "high" | "medium" | "low";
|
|
66
|
+
}[];
|
|
67
|
+
}>;
|
|
68
|
+
export type JudgeScorecard = z.infer<typeof judgeScorecardSchema>;
|
|
69
|
+
//# sourceMappingURL=judge-scorecard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"judge-scorecard.d.ts","sourceRoot":"","sources":["../src/judge-scorecard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,gBAAgB,yEAKnB,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/D,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuB/B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* The quality-judge scorecard contract, shared by the api-server judge route
|
|
4
|
+
* (which validates what the vision model returned) and the CLI (which
|
|
5
|
+
* validates the terminal frame before writing `.bitmagic/judge.json`). It
|
|
6
|
+
* lives here so the two sides can never drift: a scorecard the server accepts
|
|
7
|
+
* is by construction one the CLI can record and render.
|
|
8
|
+
*
|
|
9
|
+
* Dimension names are a fixed enum, not free text, so scores stay comparable
|
|
10
|
+
* run over run — "readability: 4 → 7" only means something if both runs were
|
|
11
|
+
* grading the same axis.
|
|
12
|
+
*/
|
|
13
|
+
export const JUDGE_DIMENSIONS = [
|
|
14
|
+
'visual-quality',
|
|
15
|
+
'readability',
|
|
16
|
+
'design-fidelity',
|
|
17
|
+
'polish',
|
|
18
|
+
];
|
|
19
|
+
export const judgeScorecardSchema = z.object({
|
|
20
|
+
overall: z.number().min(1).max(10),
|
|
21
|
+
dimensions: z
|
|
22
|
+
.array(z.object({
|
|
23
|
+
name: z.enum(JUDGE_DIMENSIONS),
|
|
24
|
+
score: z.number().min(1).max(10),
|
|
25
|
+
note: z.string().max(300),
|
|
26
|
+
}))
|
|
27
|
+
.min(1)
|
|
28
|
+
.max(JUDGE_DIMENSIONS.length),
|
|
29
|
+
topFindings: z
|
|
30
|
+
.array(z.object({
|
|
31
|
+
title: z.string().max(120),
|
|
32
|
+
// The detail must say what to CHANGE, not just what is wrong; the
|
|
33
|
+
// prompt enforces the intent, the length cap enforces the discipline.
|
|
34
|
+
detail: z.string().max(500),
|
|
35
|
+
severity: z.enum(['high', 'medium', 'low']),
|
|
36
|
+
}))
|
|
37
|
+
.max(3),
|
|
38
|
+
});
|
|
39
|
+
//# sourceMappingURL=judge-scorecard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"judge-scorecard.js","sourceRoot":"","sources":["../src/judge-scorecard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;GAUG;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,gBAAgB;IAChB,aAAa;IACb,iBAAiB;IACjB,QAAQ;CACA,CAAC;AAIX,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IAClC,UAAU,EAAE,CAAC;SACV,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC;QAC9B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;QAChC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;KAC1B,CAAC,CACH;SACA,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,gBAAgB,CAAC,MAAM,CAAC;IAC/B,WAAW,EAAE,CAAC;SACX,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;QACP,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;QAC1B,kEAAkE;QAClE,sEAAsE;QACtE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;QAC3B,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;KAC5C,CAAC,CACH;SACA,GAAG,CAAC,CAAC,CAAC;CACV,CAAC,CAAC"}
|