@womp/spark-agent 5.2.3 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-run.d.ts +1 -1
- package/dist/agent-tools.js +30 -12
- package/dist/capabilities/materials.d.ts +45 -0
- package/dist/capabilities/materials.js +89 -0
- package/dist/capabilities/scene.d.ts +21 -0
- package/dist/capabilities/scene.js +13 -13
- package/dist/capabilities.d.ts +1 -0
- package/dist/capabilities.js +1 -0
- package/dist/error-diagnostics.d.ts +24 -0
- package/dist/error-diagnostics.js +53 -0
- package/dist/generated/materials-declarations.d.ts +1 -0
- package/dist/generated/materials-declarations.js +3 -0
- package/dist/kakapo.d.ts +1 -0
- package/dist/kakapo.js +1 -0
- package/dist/materials/composition.d.ts +64 -0
- package/dist/materials/composition.js +121 -0
- package/dist/materials/graph.d.ts +79 -0
- package/dist/materials/graph.js +336 -0
- package/dist/materials/inspection.d.ts +40 -0
- package/dist/materials/inspection.js +54 -0
- package/dist/materials/recipes.d.ts +12 -0
- package/dist/materials/recipes.js +284 -0
- package/dist/materials/reference.d.ts +2 -0
- package/dist/materials/reference.js +97 -0
- package/dist/materials/shaping.d.ts +294 -0
- package/dist/materials/shaping.js +184 -0
- package/dist/materials/structure.d.ts +2 -0
- package/dist/materials/structure.js +12 -0
- package/dist/materials/surface-recipes.d.ts +78 -0
- package/dist/materials/surface-recipes.js +193 -0
- package/dist/materials/types.d.ts +222 -0
- package/dist/materials/types.js +1 -0
- package/dist/models.d.ts +7 -0
- package/dist/models.js +8 -0
- package/dist/pricing.js +5 -0
- package/dist/prompt.d.ts +1 -1
- package/dist/prompt.js +32 -32
- package/dist/sdk-materials-host.d.ts +9 -0
- package/dist/sdk-materials-host.js +360 -0
- package/dist/server/standalone.js +6 -1
- package/dist/ui.d.ts +50 -6
- package/package.json +2 -2
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { INTERNAL_UNITS_PER_MM } from "../coordinates.js";
|
|
3
|
+
import { makeMaterialNode } from "./graph.js";
|
|
4
|
+
export const MATERIAL_RECIPE_RELIEF_GUIDANCE = `Pattern-specific calibration observations (native strength, object coordinates, detail 4, height and bump tested independently):
|
|
5
|
+
- Dual-scale grid, featureSize 1: height 0.0003–0.001; start at 0.0003.
|
|
6
|
+
- Noise, featureSize 0.04: 0.01–0.03 produced visible fine relief.
|
|
7
|
+
- Crossed periodic threads, featureSize 0.04: 0.003–0.03 retained rounded thread shading.
|
|
8
|
+
- Noise, featureSize 0.08: 0.01–0.03 produced low-amplitude grain.
|
|
9
|
+
- Fine directional grain, featureSize {x:0.015,y:0.015,z:2}: 0.0003–0.003 produced faint directional relief; 0.01 and above produced stronger ridges.
|
|
10
|
+
These observations on curved and flat fixtures under fixed lighting are starting points, not quality targets or upper limits. Use independently weighted structural and micro height fields. Choose visible relief by its actual gradient, scale and requested finish; universal bump multipliers can destroy gloss. Compare close and grazing views without changing the scene. Height and bump strengths are not generally interchangeable.`;
|
|
11
|
+
const positiveVector = z.object({ x: z.number().finite().positive(), y: z.number().finite().positive(), z: z.number().finite().positive() }).strict();
|
|
12
|
+
const color = z.object({ x: z.number().min(0).max(1), y: z.number().min(0).max(1), z: z.number().min(0).max(1) }).strict();
|
|
13
|
+
export const materialRecipeSchema = z.object({
|
|
14
|
+
pattern: z.enum(["noise", "grain", "woven", "grid"]),
|
|
15
|
+
coordinateSpace: z.enum(["object", "world"]),
|
|
16
|
+
featureSize: z.union([z.number().finite().positive(), positiveVector]),
|
|
17
|
+
colorA: color,
|
|
18
|
+
colorB: color,
|
|
19
|
+
roughnessA: z.number().min(0).max(1),
|
|
20
|
+
roughnessB: z.number().min(0).max(1),
|
|
21
|
+
metalness: z.number().min(0).max(1).optional(),
|
|
22
|
+
detail: z.number().int().min(1).max(16).optional(),
|
|
23
|
+
relief: z.enum(["height", "bump"]),
|
|
24
|
+
reliefScale: z.number().finite(),
|
|
25
|
+
}).strict();
|
|
26
|
+
export const materialFieldSchema = materialRecipeSchema.pick({ coordinateSpace: true, featureSize: true, detail: true }).extend({
|
|
27
|
+
pattern: z.enum(["noise", "grain", "woven", "grid", "cells"]),
|
|
28
|
+
rotation: z.object({ x: z.number().finite(), y: z.number().finite(), z: z.number().finite() }).strict().optional(),
|
|
29
|
+
cellMode: z.enum(["Distance", "Edge distance", "Color", "Smooth Distance", "Smooth Color"]).optional(),
|
|
30
|
+
remap: z.object({ low: z.number().finite(), high: z.number().finite() }).strict().refine(range => range.low !== range.high, "Remap endpoints must differ").optional(),
|
|
31
|
+
}).strict().refine(options => options.cellMode === undefined || options.pattern === "cells", "cellMode requires cells");
|
|
32
|
+
export function materialFieldOperations(options) {
|
|
33
|
+
return [options.coordinateSpace === "object" ? "InputPosition" : "InputWorldPosition", "Mapping", "MathLerp",
|
|
34
|
+
...(["noise", "grain"].includes(options.pattern) ? ["NoiseFBM"] : []),
|
|
35
|
+
...(options.pattern === "cells" ? ["NoiseVoronoi"] : []),
|
|
36
|
+
...(["woven", "grid"].includes(options.pattern) ? [options.coordinateSpace === "object" ? "InputNormal" : "InputWorldNormal", "Projection", "GetComponent"] : []),
|
|
37
|
+
...(["woven", "grid"].includes(options.pattern) || options.remap ? ["Math"] : [])];
|
|
38
|
+
}
|
|
39
|
+
export function materialRecipeOperations(options) {
|
|
40
|
+
return [...materialFieldOperations(options), "Constant", "OutputMaterial",
|
|
41
|
+
...(options.relief === "height" ? ["OutputHeight"] : ["Bump", "OutputSurfaceNormal"])];
|
|
42
|
+
}
|
|
43
|
+
export function makeMaterialField(options, catalogue) {
|
|
44
|
+
const graph = { nodes: [], connections: [] };
|
|
45
|
+
const node = (op, inputs, name) => {
|
|
46
|
+
if (!catalogue.nodes[op])
|
|
47
|
+
throw new Error("Recipe operation unavailable: " + op);
|
|
48
|
+
graph.nodes.push(makeMaterialNode({ op, inputs, name }));
|
|
49
|
+
return graph.nodes.length - 1;
|
|
50
|
+
};
|
|
51
|
+
const connect = (source, target, port, output = 0) => {
|
|
52
|
+
graph.connections.push({ input_node: source, input_slot: output, output_node: target, output_slot: port, flags: 0 });
|
|
53
|
+
};
|
|
54
|
+
const selector = (op, port, label) => {
|
|
55
|
+
const index = catalogue.nodes[op]?.inputs[port]?.values?.indexOf(label) ?? -1;
|
|
56
|
+
if (index < 0)
|
|
57
|
+
throw new Error("Recipe requires " + op + " option " + label);
|
|
58
|
+
return index;
|
|
59
|
+
};
|
|
60
|
+
const size = typeof options.featureSize === "number" ? { x: options.featureSize, y: options.featureSize, z: options.featureSize } : options.featureSize;
|
|
61
|
+
const coordinateUnit = options.coordinateSpace === "world" ? 1 / INTERNAL_UNITS_PER_MM : 1;
|
|
62
|
+
const frequency = { x: coordinateUnit / size.x, y: coordinateUnit / size.y, z: coordinateUnit / size.z };
|
|
63
|
+
const positionOp = options.coordinateSpace === "object" ? "InputPosition" : "InputWorldPosition";
|
|
64
|
+
const normalOp = options.coordinateSpace === "object" ? "InputNormal" : "InputWorldNormal";
|
|
65
|
+
let position = node(positionOp, {}, options.coordinateSpace + " coordinates");
|
|
66
|
+
if (options.pattern === "woven" || options.pattern === "grid") {
|
|
67
|
+
let normal = node(normalOp, {}, "Matching projection normal");
|
|
68
|
+
if (options.rotation) {
|
|
69
|
+
const rotatePosition = node("Mapping", { 1: 0, 2: options.rotation, 3: 1 }, "Rotated position");
|
|
70
|
+
const rotateNormal = node("Mapping", { 1: 0, 2: options.rotation, 3: 1 }, "Rotated projection normal");
|
|
71
|
+
connect(position, rotatePosition, 0);
|
|
72
|
+
connect(normal, rotateNormal, 0);
|
|
73
|
+
position = rotatePosition;
|
|
74
|
+
normal = rotateNormal;
|
|
75
|
+
}
|
|
76
|
+
const projected = node("Projection", { 0: selector("Projection", 0, "Cubic"), 3: 1, 4: 0, 5: 0 }, "Physical face coordinates");
|
|
77
|
+
connect(position, projected, 1);
|
|
78
|
+
connect(normal, projected, 2);
|
|
79
|
+
position = projected;
|
|
80
|
+
}
|
|
81
|
+
const mapping = node("Mapping", { 1: 0, 2: options.pattern === "woven" || options.pattern === "grid" ? 0 : options.rotation ?? 0, 3: frequency }, "Feature size in selected coordinates");
|
|
82
|
+
connect(position, mapping, 0);
|
|
83
|
+
const detail = options.detail ?? 4;
|
|
84
|
+
let pattern;
|
|
85
|
+
if (options.pattern === "grid") {
|
|
86
|
+
const lines = (coordinates, label) => {
|
|
87
|
+
const fraction = node("Math", { 0: selector("Math", 0, "Fraction"), 3: 0 }, "Grid cell coordinates");
|
|
88
|
+
connect(coordinates, fraction, 1);
|
|
89
|
+
const edges = [0, 1].map(axis => {
|
|
90
|
+
const component = node("GetComponent", { 1: axis }, "Grid axis");
|
|
91
|
+
connect(fraction, component, 0);
|
|
92
|
+
const reverse = node("Math", { 0: selector("Math", 0, "Subtract"), 1: 1, 3: 0 }, "Opposite cell edge");
|
|
93
|
+
connect(component, reverse, 2);
|
|
94
|
+
const edge = node("Math", { 0: selector("Math", 0, "Min"), 3: 0 }, "Nearest cell edge");
|
|
95
|
+
connect(component, edge, 1);
|
|
96
|
+
connect(reverse, edge, 2);
|
|
97
|
+
return edge;
|
|
98
|
+
});
|
|
99
|
+
const edge = node("Math", { 0: selector("Math", 0, "Min"), 3: 0 }, "Grid line distance");
|
|
100
|
+
connect(edges[0], edge, 1);
|
|
101
|
+
connect(edges[1], edge, 2);
|
|
102
|
+
const coverage = node("Math", { 0: selector("Math", 0, "Divide"), 2: 0.04, 3: 1 }, "Grid line coverage");
|
|
103
|
+
connect(edge, coverage, 1);
|
|
104
|
+
const mask = node("Math", { 0: selector("Math", 0, "Subtract"), 1: 1, 3: 0 }, label);
|
|
105
|
+
connect(coverage, mask, 2);
|
|
106
|
+
return mask;
|
|
107
|
+
};
|
|
108
|
+
const major = lines(mapping, "Major grid lines");
|
|
109
|
+
const minorCoordinates = node("Mapping", { 1: 0, 2: 0, 3: 10 }, "One-tenth-unit grid coordinates");
|
|
110
|
+
connect(mapping, minorCoordinates, 0);
|
|
111
|
+
const minor = lines(minorCoordinates, "Minor grid lines");
|
|
112
|
+
const minorContrast = node("Math", { 0: selector("Math", 0, "Mulitiply"), 2: 0.35, 3: 0 }, "Lower-contrast minor grid");
|
|
113
|
+
connect(minor, minorContrast, 1);
|
|
114
|
+
pattern = node("Math", { 0: selector("Math", 0, "Max"), 3: 0 }, "Combined two-scale grid");
|
|
115
|
+
connect(major, pattern, 1);
|
|
116
|
+
connect(minorContrast, pattern, 2);
|
|
117
|
+
}
|
|
118
|
+
else if (options.pattern === "grain") {
|
|
119
|
+
const warp = node("NoiseFBM", { 1: 0.16, 2: 3, 3: 0.35, 4: 1 }, "Slow grain meander");
|
|
120
|
+
connect(mapping, warp, 0);
|
|
121
|
+
const warped = node("Mapping", { 1: 0, 2: 0, 3: 1 }, "Warped grain coordinates");
|
|
122
|
+
connect(mapping, warped, 0);
|
|
123
|
+
connect(warp, warped, 1);
|
|
124
|
+
pattern = node("NoiseFBM", { 1: 1, 2: detail, 3: 1, 4: 0 }, "Directional grain");
|
|
125
|
+
connect(warped, pattern, 0);
|
|
126
|
+
}
|
|
127
|
+
else if (options.pattern === "woven") {
|
|
128
|
+
const threads = [0, 1].map(axis => {
|
|
129
|
+
const component = node("GetComponent", { 1: axis }, axis === 0 ? "Warp axis" : "Weft axis");
|
|
130
|
+
connect(mapping, component, 0);
|
|
131
|
+
const phase = node("Math", { 0: selector("Math", 0, "Mulitiply"), 2: Math.PI * 2, 3: 0 }, "Thread repeat phase");
|
|
132
|
+
connect(component, phase, 1);
|
|
133
|
+
const sine = node("Math", { 0: selector("Math", 0, "Sine"), 3: 0 }, "Rounded thread profile");
|
|
134
|
+
connect(phase, sine, 1);
|
|
135
|
+
const thread = node("MathLerp", { 0: 0.5, 1: 1 }, "Positive thread height");
|
|
136
|
+
connect(sine, thread, 2);
|
|
137
|
+
return thread;
|
|
138
|
+
});
|
|
139
|
+
pattern = node("MathLerp", { 2: 0.5 }, "Crossed warp and weft");
|
|
140
|
+
connect(threads[0], pattern, 0);
|
|
141
|
+
connect(threads[1], pattern, 1);
|
|
142
|
+
}
|
|
143
|
+
else if (options.pattern === "cells") {
|
|
144
|
+
pattern = node("NoiseVoronoi", { 1: 1, 2: selector("NoiseVoronoi", 2, options.cellMode ?? "Distance"), 3: selector("NoiseVoronoi", 3, "Euclidean") }, "Cellular structure");
|
|
145
|
+
connect(mapping, pattern, 0);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
pattern = node("NoiseFBM", { 1: 1, 2: detail, 3: 1, 4: 0 }, "Irregular surface structure");
|
|
149
|
+
connect(mapping, pattern, 0);
|
|
150
|
+
}
|
|
151
|
+
if (options.remap) {
|
|
152
|
+
const shifted = node("Math", { 0: selector("Math", 0, "Subtract"), 2: options.remap.low, 3: 0 }, "Mask offset");
|
|
153
|
+
connect(pattern, shifted, 1);
|
|
154
|
+
const normalized = node("Math", { 0: selector("Math", 0, "Divide"), 2: options.remap.high - options.remap.low, 3: 1 }, "Clamped field contrast");
|
|
155
|
+
connect(shifted, normalized, 1);
|
|
156
|
+
pattern = normalized;
|
|
157
|
+
}
|
|
158
|
+
return { graph, source: { node: pattern, slot: 0 } };
|
|
159
|
+
}
|
|
160
|
+
export function makeMaterialRecipe(options, catalogue) {
|
|
161
|
+
const { graph, source } = makeMaterialField(options, catalogue);
|
|
162
|
+
const pattern = source.node;
|
|
163
|
+
const node = (op, inputs, name) => {
|
|
164
|
+
if (!catalogue.nodes[op])
|
|
165
|
+
throw new Error("Recipe operation unavailable: " + op);
|
|
166
|
+
graph.nodes.push(makeMaterialNode({ op, inputs, name }));
|
|
167
|
+
return graph.nodes.length - 1;
|
|
168
|
+
};
|
|
169
|
+
const connect = (input, output, slot, sourceSlot = 0) => graph.connections.push({ input_node: input, input_slot: sourceSlot, output_node: output, output_slot: slot, flags: 0 });
|
|
170
|
+
const colorMix = node("MathLerp", { 0: options.colorA, 1: options.colorB }, "Structure-correlated color");
|
|
171
|
+
connect(pattern, colorMix, 2);
|
|
172
|
+
const roughness = node("MathLerp", { 0: options.roughnessA, 1: options.roughnessB }, "Structure-correlated roughness");
|
|
173
|
+
connect(pattern, roughness, 2);
|
|
174
|
+
const metal = node("Constant", { 1: options.metalness ?? 0 }, "Metalness");
|
|
175
|
+
const output = node("OutputMaterial", {}, "Editable surface material");
|
|
176
|
+
connect(colorMix, output, 0);
|
|
177
|
+
connect(roughness, output, 4);
|
|
178
|
+
connect(metal, output, 3, 1);
|
|
179
|
+
const strength = node("Constant", { 1: options.reliefScale, 3: 3 }, "Surface relief strength");
|
|
180
|
+
if (options.relief === "height") {
|
|
181
|
+
const height = node("OutputHeight", {}, "Height shading");
|
|
182
|
+
connect(pattern, height, 0);
|
|
183
|
+
connect(strength, height, 2, 1);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
const bump = node("Bump", {}, "Procedural bump normal");
|
|
187
|
+
connect(pattern, bump, 0);
|
|
188
|
+
connect(strength, bump, 1, 1);
|
|
189
|
+
const normal = node("OutputSurfaceNormal", {}, "Tangent normal output");
|
|
190
|
+
connect(bump, normal, 0);
|
|
191
|
+
}
|
|
192
|
+
return graph;
|
|
193
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/** Native graph values: colors are 0..1, positions use renderer units. */
|
|
2
|
+
export type MaterialJson = null | boolean | number | string | MaterialJson[] | {
|
|
3
|
+
[key: string]: MaterialJson;
|
|
4
|
+
};
|
|
5
|
+
export type MaterialVector = {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
z: number;
|
|
9
|
+
};
|
|
10
|
+
export type MaterialNode = {
|
|
11
|
+
op: string;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
};
|
|
14
|
+
export type MaterialConnection = {
|
|
15
|
+
input_node: number;
|
|
16
|
+
input_slot: number;
|
|
17
|
+
output_node: number;
|
|
18
|
+
output_slot: number;
|
|
19
|
+
flags?: number;
|
|
20
|
+
};
|
|
21
|
+
export type MaterialGraph = {
|
|
22
|
+
nodes: MaterialNode[];
|
|
23
|
+
connections: MaterialConnection[];
|
|
24
|
+
};
|
|
25
|
+
export type MaterialConstant = {
|
|
26
|
+
value: MaterialVector;
|
|
27
|
+
color: MaterialVector;
|
|
28
|
+
};
|
|
29
|
+
export type MaterialPort = {
|
|
30
|
+
title: string;
|
|
31
|
+
type: string;
|
|
32
|
+
values?: string[];
|
|
33
|
+
};
|
|
34
|
+
export type MaterialNodeDescription = {
|
|
35
|
+
type: string;
|
|
36
|
+
title: string;
|
|
37
|
+
description: string;
|
|
38
|
+
inputs: MaterialPort[];
|
|
39
|
+
outputs: MaterialPort[];
|
|
40
|
+
};
|
|
41
|
+
export type MaterialCatalog = {
|
|
42
|
+
operations: {
|
|
43
|
+
op: string;
|
|
44
|
+
category: string;
|
|
45
|
+
title: string;
|
|
46
|
+
}[];
|
|
47
|
+
nodes: Record<string, MaterialNodeDescription>;
|
|
48
|
+
textureCount: number;
|
|
49
|
+
textures?: string[];
|
|
50
|
+
/** Metadata availability for the listed textures; names do not establish their encoding. */
|
|
51
|
+
textureMetadata?: {
|
|
52
|
+
channels: "unknown";
|
|
53
|
+
colorSpace: "unknown";
|
|
54
|
+
normalConvention: "unknown";
|
|
55
|
+
reason: string;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
export type MaterialCatalogOptions = {
|
|
59
|
+
refreshTextures?: boolean;
|
|
60
|
+
includeTextures?: boolean;
|
|
61
|
+
operations?: string[];
|
|
62
|
+
};
|
|
63
|
+
export type MaterialSnapshot = {
|
|
64
|
+
materials: {
|
|
65
|
+
id: number;
|
|
66
|
+
shaderIds: number[];
|
|
67
|
+
objectIds: number[];
|
|
68
|
+
}[];
|
|
69
|
+
material?: {
|
|
70
|
+
id: number;
|
|
71
|
+
data: {
|
|
72
|
+
[key: string]: unknown;
|
|
73
|
+
};
|
|
74
|
+
layers: (MaterialGraph | null)[];
|
|
75
|
+
};
|
|
76
|
+
/** Direct assignments and curve points; procedural geometry can also reference this ID. */
|
|
77
|
+
diagnostics: string[];
|
|
78
|
+
};
|
|
79
|
+
export type MaterialWrite = {
|
|
80
|
+
/** Existing material: affects every reference to this material ID. */
|
|
81
|
+
materialId?: number;
|
|
82
|
+
/** Create an independent copy, including base properties. */
|
|
83
|
+
cloneFrom?: number;
|
|
84
|
+
/** Complete ordered replacement, maximum eight slots. null is an empty slot. */
|
|
85
|
+
layers?: (MaterialGraph | null)[];
|
|
86
|
+
/** Partial override changes. null resets a Constant using a new reference ID. */
|
|
87
|
+
constants?: Record<string, MaterialConstant | null>;
|
|
88
|
+
/** Assign to these nodes; curves assign every point. */
|
|
89
|
+
objectIds?: number[];
|
|
90
|
+
};
|
|
91
|
+
export type MaterialWriteResult = {
|
|
92
|
+
materialId: number;
|
|
93
|
+
objectIds: number[];
|
|
94
|
+
/** Remapped Constants after resets or replacement of unoverridden graph nodes. */
|
|
95
|
+
referenceIdChanges: Record<string, number>;
|
|
96
|
+
/** SDK hosts return current Constant handles after allocation/reset/remapping. */
|
|
97
|
+
parameters?: {
|
|
98
|
+
name: string;
|
|
99
|
+
referenceId: number;
|
|
100
|
+
value: MaterialVector;
|
|
101
|
+
color: MaterialVector;
|
|
102
|
+
exposed?: boolean;
|
|
103
|
+
}[];
|
|
104
|
+
status: "applied";
|
|
105
|
+
rendering: "unverified";
|
|
106
|
+
};
|
|
107
|
+
export type MaterialNodeOptions = {
|
|
108
|
+
op: string;
|
|
109
|
+
name?: string;
|
|
110
|
+
reference_id?: number;
|
|
111
|
+
/** Native input slots; scalar literals are broadcast to x/y/z. */
|
|
112
|
+
inputs?: Record<string, number | MaterialVector>;
|
|
113
|
+
texture?: string;
|
|
114
|
+
ramp?: {
|
|
115
|
+
position: number;
|
|
116
|
+
color: MaterialVector;
|
|
117
|
+
}[];
|
|
118
|
+
options?: {
|
|
119
|
+
name: string;
|
|
120
|
+
value: number;
|
|
121
|
+
}[];
|
|
122
|
+
};
|
|
123
|
+
/** Editable starting graphs with explicit coordinate space; not complete reference matches. */
|
|
124
|
+
export type MaterialRecipeOptions = {
|
|
125
|
+
pattern: "noise" | "grain" | "woven" | "grid";
|
|
126
|
+
coordinateSpace: "object" | "world";
|
|
127
|
+
/** Object-space units, or physical millimeters for world space. Vectors use graph axes. */
|
|
128
|
+
featureSize: number | MaterialVector;
|
|
129
|
+
/** Linear RGB in 0..1; decode display/reference colors before supplying them. */
|
|
130
|
+
colorA: MaterialVector;
|
|
131
|
+
/** Linear RGB in 0..1. */
|
|
132
|
+
colorB: MaterialVector;
|
|
133
|
+
roughnessA: number;
|
|
134
|
+
roughnessB: number;
|
|
135
|
+
metalness?: number;
|
|
136
|
+
detail?: number;
|
|
137
|
+
relief: "height" | "bump";
|
|
138
|
+
/** Native shading-normal strength, not displacement distance. */
|
|
139
|
+
reliefScale: number;
|
|
140
|
+
};
|
|
141
|
+
/** Numeric structure without surface outputs. Scalars drive finish/relief; RGB fields preserve color components. */
|
|
142
|
+
export type MaterialField = {
|
|
143
|
+
graph: MaterialGraph;
|
|
144
|
+
source: {
|
|
145
|
+
node: number;
|
|
146
|
+
slot: number;
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
export type MaterialFieldOptions = {
|
|
150
|
+
pattern: "noise" | "grain" | "woven" | "grid" | "cells";
|
|
151
|
+
coordinateSpace: "object" | "world";
|
|
152
|
+
/** Object-coordinate units, or millimeters for world space. */
|
|
153
|
+
featureSize: number | MaterialVector;
|
|
154
|
+
detail?: number;
|
|
155
|
+
/** Rotation in degrees, applied to paired position/normal coordinates. */
|
|
156
|
+
rotation?: MaterialVector;
|
|
157
|
+
/** Cellular output; defaults to Distance. */
|
|
158
|
+
cellMode?: "Distance" | "Edge distance" | "Color" | "Smooth Distance" | "Smooth Color";
|
|
159
|
+
/** Clamp (field - low) / (high - low) to 0..1. Reversed endpoints invert the mask. */
|
|
160
|
+
remap?: {
|
|
161
|
+
low: number;
|
|
162
|
+
high: number;
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
export type MaterialCompositionOptions = {
|
|
166
|
+
/** Copied fields get independent Constant handles on write; source handles are not retained. */
|
|
167
|
+
fields: MaterialField[];
|
|
168
|
+
/** Linear RGB endpoints. Weights correspond to fields and sum to at most 1. */
|
|
169
|
+
color?: {
|
|
170
|
+
low: MaterialVector;
|
|
171
|
+
high: MaterialVector;
|
|
172
|
+
weights: number[];
|
|
173
|
+
};
|
|
174
|
+
roughness?: {
|
|
175
|
+
low: number;
|
|
176
|
+
high: number;
|
|
177
|
+
weights: number[];
|
|
178
|
+
};
|
|
179
|
+
metalness?: number;
|
|
180
|
+
/** Combine scalar height fields before deriving normals. Weights may be signed. */
|
|
181
|
+
relief?: {
|
|
182
|
+
mode: "height" | "bump";
|
|
183
|
+
scale: number;
|
|
184
|
+
weights: number[];
|
|
185
|
+
center?: number;
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
/** Generic analytic shaping; coordinates default to the field's feature-size Mapping. */
|
|
189
|
+
export type MaterialShapeOptions = {
|
|
190
|
+
name?: string;
|
|
191
|
+
} & ({
|
|
192
|
+
kind: "remap";
|
|
193
|
+
low: number;
|
|
194
|
+
high: number;
|
|
195
|
+
} | {
|
|
196
|
+
kind: "ridge";
|
|
197
|
+
center: number;
|
|
198
|
+
width: number;
|
|
199
|
+
} | {
|
|
200
|
+
kind: "periodic";
|
|
201
|
+
coordinates?: number;
|
|
202
|
+
axis: 0 | 1 | 2;
|
|
203
|
+
sharpness: number;
|
|
204
|
+
} | {
|
|
205
|
+
kind: "warp";
|
|
206
|
+
coordinates?: number;
|
|
207
|
+
frequency: number;
|
|
208
|
+
amplitude: number;
|
|
209
|
+
} | {
|
|
210
|
+
kind: "palette";
|
|
211
|
+
mode: "Constant" | "Linear" | "Smooth";
|
|
212
|
+
stops: {
|
|
213
|
+
position: number;
|
|
214
|
+
color: MaterialVector;
|
|
215
|
+
}[];
|
|
216
|
+
}
|
|
217
|
+
/** Linear interpolation from this field to overlay. A mask field uses its X component, clamped to 0..1. */
|
|
218
|
+
| {
|
|
219
|
+
kind: "blend";
|
|
220
|
+
overlay: MaterialField | MaterialVector;
|
|
221
|
+
mask: MaterialField | number;
|
|
222
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/models.d.ts
CHANGED
|
@@ -17,6 +17,13 @@ export declare const SPARK_MODELS: readonly [{
|
|
|
17
17
|
readonly note: "Best OpenAI quality";
|
|
18
18
|
readonly provider: "openai";
|
|
19
19
|
readonly reasoningEfforts: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
20
|
+
}, {
|
|
21
|
+
readonly id: "gpt-6.1-sol";
|
|
22
|
+
readonly contextWindow: 1050000;
|
|
23
|
+
readonly label: "GPT-6.1 Sol";
|
|
24
|
+
readonly note: "Complex coding and agentic workflows";
|
|
25
|
+
readonly provider: "openai";
|
|
26
|
+
readonly reasoningEfforts: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
20
27
|
}, {
|
|
21
28
|
readonly id: "gpt-6-sol";
|
|
22
29
|
readonly contextWindow: 1050000;
|
package/dist/models.js
CHANGED
|
@@ -22,6 +22,14 @@ export const SPARK_MODELS = [
|
|
|
22
22
|
provider: "openai",
|
|
23
23
|
reasoningEfforts: EXTENDED_REASONING_EFFORTS,
|
|
24
24
|
},
|
|
25
|
+
{
|
|
26
|
+
id: "gpt-6.1-sol",
|
|
27
|
+
contextWindow: 1_050_000,
|
|
28
|
+
label: "GPT-6.1 Sol",
|
|
29
|
+
note: "Complex coding and agentic workflows",
|
|
30
|
+
provider: "openai",
|
|
31
|
+
reasoningEfforts: EXTENDED_REASONING_EFFORTS,
|
|
32
|
+
},
|
|
25
33
|
{
|
|
26
34
|
id: "gpt-6-sol",
|
|
27
35
|
// https://developers.openai.com/api/docs/models/gpt-6-sol
|
package/dist/pricing.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// Standard, global, direct API rates in USD per million tokens, checked 2026-09-09.
|
|
2
2
|
const PRICES = {
|
|
3
|
+
"gpt-6.1-sol": {
|
|
4
|
+
input: 2, read: 0.1, write: 2.5, output: 10, longContext: 272_000,
|
|
5
|
+
source: "https://developers.openai.com/api/docs/models/gpt-6.1-sol",
|
|
6
|
+
pricingDate: "2026-09-29",
|
|
7
|
+
},
|
|
3
8
|
"gpt-6-sol": {
|
|
4
9
|
input: 2, read: 0.2, write: 2.5, output: 10, longContext: 272_000,
|
|
5
10
|
source: "https://developers.openai.com/api/docs/pricing",
|
package/dist/prompt.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SPARK_AGENT_PROMPT = "You are Spark, Womp's unified assistant.\nUse read_skill to
|
|
1
|
+
export declare const SPARK_AGENT_PROMPT = "You are Spark, Womp's unified assistant.\nUse read_skill({names:[...]}) to load the required APIs together. Skills provide TypeScript\ndeclarations for documentation; execute_javascript runs JavaScript, with top-level await.\nOnly the listed capabilities are available. Reading a skill does not grant additional permissions.\nUse only the supplied womp APIs and output helpers to access application services and state.\nIf a capability is unavailable, explain what is missing and ask the user to enable it when applicable.\nDo not work around missing capabilities by inspecting page globals, DOM or framework internals,\nor making direct network requests. Native execution is not a security sandbox; it does not expand\nthe supported API or the user's granted capabilities.\nGive each execute_javascript call a concise present-progressive purpose, such as \"Generating a car image\", \"Inspecting the scene\", or \"Importing the model\". Write purpose before code.\nEach execution has fresh local variables. Keep stable object, job, and artifact references.\nUse print for concise, filtered observations; await emitImage or emitFile with a host-issued artifact\nreference to show actual output. emitImage inspects by default; use { inspect: false } to display without adding image bytes to model context.\nCompleted generations are shown automatically to the user; image bytes are intentionally omitted from your context. Acknowledge completion without listing, re-emitting, or inspecting outputs unless the task requires it. Use returned artifact references for follow-up work.\nDo not claim visual verification without inspecting the image. Missing artifact references must be recovered or reported; omitted image bytes alone are not a missing result.\nInspect the scene before editing and verify the committed result.\nPreserve existing content unless the user asks to replace or delete it. Use the latest target revision\nwhen a write requires one. Capabilities own transactions: an entire script is not atomic.\nIf an operation fails after earlier effects, use its receipts to inspect and retry only unresolved work.\nNever claim an uncertain commit rolled back. Never resubmit a job just because waiting timed out.\nComplete related work in this conversation without delegating to another agent. Report limits,\nunsupported actions, cancellation, and incomplete work truthfully. Finish with a concise explanation\nof the result in the user's language; keep implementation details out of that explanation.\n\nCreation intent\n\nResolve the requested outcome from the current message and preceding dialogue, not keyword matches.\nExplicit method choices take precedence. Continue edits in the existing representation unless the user\nasks to change it. Creation defaults do not apply to help, discussion, imports, or account questions.\n\n- Build ordinary or underspecified creations directly with editable scene shapes, including\n \"a car\" and \"make X in 3D\". Proceed without an image preview or method-selection menu.\n Organic appearance alone does not require mesh generation or Geometry Nodes.\n\n- Use OpenSCAD for parametric, measured, mechanical, printable, or real-world-fit objects,\n except when another method is explicit (including primitives with dimensions), or when\n parametric organic/procedural geometry, animation, or Geometry Nodes is requested:\n use Geometry Nodes for those, not Workbench Flow. Do not claim print readiness without validation.\n\n- Generate/edit images or generate meshes when requested. An attached image or reference alone\n does not request generation; use it for the requested workflow or clarify an unresolved request.\n Generating a result does not authorize importing, converting, or building it in the scene.\n\n- Use Workbench Flow for image/mesh generation explicitly requested through Flow/nodes or\n continued edits to that Flow workflow, unless the user asks for an inline result. Flow must be\n open; explain that prerequisite if closed instead of silently generating inline. Merely opening\n Flow does not redirect ordinary scene requests or inline generation to it.\n\n- After completing explicitly requested generation, you may briefly suggest a relevant next step\n using available capabilities. Describe its outcome, not internal tools. Suggestions are optional,\n not a mandatory menu, and never authorize actions. Wait for a clear choice and follow existing\n confirmations. Reuse the existing artifact/reference for the chosen follow-up, including as the\n image-to-mesh input, instead of regenerating the source. Preserve its defining features when building it.\n\n- Choose construction methods by the user's requested workflow and the\n available capabilities, not by assuming editable shapes require lower\n fidelity. A sculpted appearance does not by itself require mesh generation.\n Respect an explicit method choice and pursue the reference's visible shape\n and detail with that method. Offer alternatives only for a specific\n demonstrated limitation; ask before switching or simplifying the requested\n result. No method guarantees exact reconstruction from a single image.\n\n- Ask at most one focused question at a time, only when a sensible default\n would likely produce the wrong outcome. Do not repeat resolved questions.\n Interpret replies using the preceding offer; if several options were\n offered and \u201Cyes\u201D does not identify one, clarify briefly.\n\n- Treat a clear answer to your question as an instruction to proceed.\n Continue from existing results rather than restarting the workflow.\n\nOffer only workflows available in this host. Follow their authorization\nand confirmation requirements before generation or scene changes. If a requested workflow is unavailable,\nexplain the limitation rather than silently substituting another.";
|
package/dist/prompt.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const SPARK_AGENT_PROMPT = `You are Spark, Womp's unified assistant.
|
|
2
|
-
Use read_skill to
|
|
2
|
+
Use read_skill({names:[...]}) to load the required APIs together. Skills provide TypeScript
|
|
3
3
|
declarations for documentation; execute_javascript runs JavaScript, with top-level await.
|
|
4
4
|
Only the listed capabilities are available. Reading a skill does not grant additional permissions.
|
|
5
5
|
Use only the supplied womp APIs and output helpers to access application services and state.
|
|
@@ -24,41 +24,41 @@ of the result in the user's language; keep implementation details out of that ex
|
|
|
24
24
|
|
|
25
25
|
Creation intent
|
|
26
26
|
|
|
27
|
-
Resolve the requested outcome from the current message and preceding dialogue, not keyword matches.
|
|
28
|
-
Explicit method choices take precedence. Continue edits in the existing representation unless the user
|
|
29
|
-
asks to change it. Creation defaults do not apply to help, discussion, imports, or account questions.
|
|
27
|
+
Resolve the requested outcome from the current message and preceding dialogue, not keyword matches.
|
|
28
|
+
Explicit method choices take precedence. Continue edits in the existing representation unless the user
|
|
29
|
+
asks to change it. Creation defaults do not apply to help, discussion, imports, or account questions.
|
|
30
30
|
|
|
31
|
-
- Build ordinary or underspecified creations directly with editable scene shapes, including
|
|
32
|
-
"a car" and "make X in 3D". Proceed without an image preview or method-selection menu.
|
|
33
|
-
Organic appearance alone does not require mesh generation or Geometry Nodes.
|
|
31
|
+
- Build ordinary or underspecified creations directly with editable scene shapes, including
|
|
32
|
+
"a car" and "make X in 3D". Proceed without an image preview or method-selection menu.
|
|
33
|
+
Organic appearance alone does not require mesh generation or Geometry Nodes.
|
|
34
34
|
|
|
35
|
-
- Use OpenSCAD for parametric, measured, mechanical, printable, or real-world-fit objects,
|
|
36
|
-
except when another method is explicit (including primitives with dimensions), or when
|
|
37
|
-
parametric organic/procedural geometry, animation, or Geometry Nodes is requested:
|
|
38
|
-
use Geometry Nodes for those, not Workbench Flow. Do not claim print readiness without validation.
|
|
35
|
+
- Use OpenSCAD for parametric, measured, mechanical, printable, or real-world-fit objects,
|
|
36
|
+
except when another method is explicit (including primitives with dimensions), or when
|
|
37
|
+
parametric organic/procedural geometry, animation, or Geometry Nodes is requested:
|
|
38
|
+
use Geometry Nodes for those, not Workbench Flow. Do not claim print readiness without validation.
|
|
39
39
|
|
|
40
|
-
- Generate/edit images or generate meshes when requested. An attached image or reference alone
|
|
41
|
-
does not request generation; use it for the requested workflow or clarify an unresolved request.
|
|
42
|
-
Generating a result does not authorize importing, converting, or building it in the scene.
|
|
40
|
+
- Generate/edit images or generate meshes when requested. An attached image or reference alone
|
|
41
|
+
does not request generation; use it for the requested workflow or clarify an unresolved request.
|
|
42
|
+
Generating a result does not authorize importing, converting, or building it in the scene.
|
|
43
43
|
|
|
44
|
-
- Use Workbench Flow for image/mesh generation explicitly requested through Flow/nodes or
|
|
45
|
-
continued edits to that Flow workflow, unless the user asks for an inline result. Flow must be
|
|
46
|
-
open; explain that prerequisite if closed instead of silently generating inline. Merely opening
|
|
47
|
-
Flow does not redirect ordinary scene requests or inline generation to it.
|
|
44
|
+
- Use Workbench Flow for image/mesh generation explicitly requested through Flow/nodes or
|
|
45
|
+
continued edits to that Flow workflow, unless the user asks for an inline result. Flow must be
|
|
46
|
+
open; explain that prerequisite if closed instead of silently generating inline. Merely opening
|
|
47
|
+
Flow does not redirect ordinary scene requests or inline generation to it.
|
|
48
48
|
|
|
49
|
-
- After completing explicitly requested generation, you may briefly suggest a relevant next step
|
|
50
|
-
using available capabilities. Describe its outcome, not internal tools. Suggestions are optional,
|
|
51
|
-
not a mandatory menu, and never authorize actions. Wait for a clear choice and follow existing
|
|
52
|
-
confirmations. Reuse the existing artifact/reference for the chosen follow-up, including as the
|
|
53
|
-
image-to-mesh input, instead of regenerating the source. Preserve its defining features when building it.
|
|
49
|
+
- After completing explicitly requested generation, you may briefly suggest a relevant next step
|
|
50
|
+
using available capabilities. Describe its outcome, not internal tools. Suggestions are optional,
|
|
51
|
+
not a mandatory menu, and never authorize actions. Wait for a clear choice and follow existing
|
|
52
|
+
confirmations. Reuse the existing artifact/reference for the chosen follow-up, including as the
|
|
53
|
+
image-to-mesh input, instead of regenerating the source. Preserve its defining features when building it.
|
|
54
54
|
|
|
55
|
-
- Choose construction methods by the user's requested workflow and the
|
|
56
|
-
available capabilities, not by assuming editable shapes require lower
|
|
57
|
-
fidelity. A sculpted appearance does not by itself require mesh generation.
|
|
58
|
-
Respect an explicit method choice and pursue the reference's visible shape
|
|
59
|
-
and detail with that method. Offer alternatives only for a specific
|
|
60
|
-
demonstrated limitation; ask before switching or simplifying the requested
|
|
61
|
-
result. No method guarantees exact reconstruction from a single image.
|
|
55
|
+
- Choose construction methods by the user's requested workflow and the
|
|
56
|
+
available capabilities, not by assuming editable shapes require lower
|
|
57
|
+
fidelity. A sculpted appearance does not by itself require mesh generation.
|
|
58
|
+
Respect an explicit method choice and pursue the reference's visible shape
|
|
59
|
+
and detail with that method. Offer alternatives only for a specific
|
|
60
|
+
demonstrated limitation; ask before switching or simplifying the requested
|
|
61
|
+
result. No method guarantees exact reconstruction from a single image.
|
|
62
62
|
|
|
63
63
|
- Ask at most one focused question at a time, only when a sensible default
|
|
64
64
|
would likely produce the wrong outcome. Do not repeat resolved questions.
|
|
@@ -68,6 +68,6 @@ asks to change it. Creation defaults do not apply to help, discussion, imports,
|
|
|
68
68
|
- Treat a clear answer to your question as an instruction to proceed.
|
|
69
69
|
Continue from existing results rather than restarting the workflow.
|
|
70
70
|
|
|
71
|
-
Offer only workflows available in this host. Follow their authorization
|
|
72
|
-
and confirmation requirements before generation or scene changes. If a requested workflow is unavailable,
|
|
71
|
+
Offer only workflows available in this host. Follow their authorization
|
|
72
|
+
and confirmation requirements before generation or scene changes. If a requested workflow is unavailable,
|
|
73
73
|
explain the limitation rather than silently substituting another.`;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SparkInvocationContext } from "./capabilities.js";
|
|
2
|
+
import type { MaterialsHost } from "./capabilities/materials.js";
|
|
3
|
+
import type { SdkSceneEditor } from "./sdk-scene-host.js";
|
|
4
|
+
export type SdkMaterialsHostOptions = {
|
|
5
|
+
editor: SdkSceneEditor;
|
|
6
|
+
/** Host-owned procedural-material entitlement/availability check, run for reads and writes. */
|
|
7
|
+
assertAvailable(context: SparkInvocationContext): void | Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
export declare function createSdkMaterialsHost(options: SdkMaterialsHostOptions): MaterialsHost;
|