@womp/spark-agent 5.6.1 → 5.7.1

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.
Files changed (43) hide show
  1. package/README.md +424 -421
  2. package/dist/agent-run.d.ts +1 -1
  3. package/dist/agent.d.ts +2 -0
  4. package/dist/agent.js +3 -1
  5. package/dist/capabilities/geometry.js +14 -14
  6. package/dist/capabilities/materials.js +14 -14
  7. package/dist/capabilities/openscad-instructions.js +77 -77
  8. package/dist/capabilities/operations.js +28 -28
  9. package/dist/capabilities/scene.js +81 -81
  10. package/dist/capabilities/wompify.js +10 -10
  11. package/dist/client.d.ts +7 -0
  12. package/dist/client.js +14 -0
  13. package/dist/geometry/schemas/backend-schema.json +3393 -3393
  14. package/dist/geometry/schemas/flowir-dialect.json +1023 -1023
  15. package/dist/geometry.js +6 -6
  16. package/dist/goals.d.ts +43 -0
  17. package/dist/goals.js +37 -0
  18. package/dist/materials/recipes.js +266 -266
  19. package/dist/materials/reference.js +94 -94
  20. package/dist/materials/structure.js +10 -10
  21. package/dist/materials/surface-recipes.js +6 -6
  22. package/dist/prompt.js +74 -74
  23. package/dist/server/executor.d.ts +1 -1
  24. package/dist/server/executor.js +2 -0
  25. package/dist/server/express.js +18 -0
  26. package/dist/server/fixtures/materials/matte-blue.json +14 -14
  27. package/dist/server/fixtures/materials/polished-copper.json +14 -14
  28. package/dist/server/goal-evaluator.d.ts +29 -0
  29. package/dist/server/goal-evaluator.js +55 -0
  30. package/dist/server/index.d.ts +4 -0
  31. package/dist/server/index.js +4 -0
  32. package/dist/server/service.d.ts +17 -0
  33. package/dist/server/service.js +204 -5
  34. package/dist/server/sqlite-store.d.ts +2 -0
  35. package/dist/server/sqlite-store.js +45 -38
  36. package/dist/server/store.d.ts +2 -0
  37. package/dist/server/store.js +12 -3
  38. package/dist/server/types.d.ts +6 -0
  39. package/dist/ui.d.ts +7 -3
  40. package/dist/ui.js +3 -1
  41. package/dist/wompify.d.ts +11 -0
  42. package/dist/wompify.js +11 -2
  43. package/package.json +140 -140
@@ -1,97 +1,97 @@
1
1
  // A one-layer graph with procedural bump feeding the surface normal.
2
- export const MATERIALS_NOISE_BUMP_RECIPE = `const graph = {
3
- nodes: [
4
- womp.materials.node({op:'Constant',inputs:{2:0.5}}),
5
- womp.materials.node({op:'Passthrough',inputs:{0:0.3}}),
6
- womp.materials.node({op:'OutputMaterial'}),
7
- womp.materials.node({op:'InputPosition'}),
8
- womp.materials.node({op:'Mapping',inputs:{1:0,2:0,3:1}}),
9
- womp.materials.node({op:'NoiseFBM',inputs:{1:4,2:3,3:1,4:0}}),
10
- womp.materials.node({op:'Bump',inputs:{1:0.03}}),
11
- womp.materials.node({op:'OutputSurfaceNormal'})
12
- ],
13
- connections: [
14
- {input_node:0,input_slot:2,output_node:2,output_slot:0,flags:0},
15
- {input_node:1,input_slot:0,output_node:2,output_slot:3,flags:0},
16
- {input_node:1,input_slot:0,output_node:2,output_slot:4,flags:0},
17
- {input_node:3,input_slot:0,output_node:4,output_slot:0,flags:0},
18
- {input_node:4,input_slot:0,output_node:5,output_slot:0,flags:0},
19
- {input_node:5,input_slot:0,output_node:6,output_slot:0,flags:0},
20
- {input_node:6,input_slot:0,output_node:7,output_slot:0,flags:0}
21
- ]
2
+ export const MATERIALS_NOISE_BUMP_RECIPE = `const graph = {
3
+ nodes: [
4
+ womp.materials.node({op:'Constant',inputs:{2:0.5}}),
5
+ womp.materials.node({op:'Passthrough',inputs:{0:0.3}}),
6
+ womp.materials.node({op:'OutputMaterial'}),
7
+ womp.materials.node({op:'InputPosition'}),
8
+ womp.materials.node({op:'Mapping',inputs:{1:0,2:0,3:1}}),
9
+ womp.materials.node({op:'NoiseFBM',inputs:{1:4,2:3,3:1,4:0}}),
10
+ womp.materials.node({op:'Bump',inputs:{1:0.03}}),
11
+ womp.materials.node({op:'OutputSurfaceNormal'})
12
+ ],
13
+ connections: [
14
+ {input_node:0,input_slot:2,output_node:2,output_slot:0,flags:0},
15
+ {input_node:1,input_slot:0,output_node:2,output_slot:3,flags:0},
16
+ {input_node:1,input_slot:0,output_node:2,output_slot:4,flags:0},
17
+ {input_node:3,input_slot:0,output_node:4,output_slot:0,flags:0},
18
+ {input_node:4,input_slot:0,output_node:5,output_slot:0,flags:0},
19
+ {input_node:5,input_slot:0,output_node:6,output_slot:0,flags:0},
20
+ {input_node:6,input_slot:0,output_node:7,output_slot:0,flags:0}
21
+ ]
22
22
  };`;
23
- export const MATERIALS_REFERENCE = `
24
- Procedural shaders are material graphs, separate from Geometry Nodes and Workbench Flow. recipe() builds the standard grid, noise, grain and woven graphs with live selectors internally; their controls are already defined. Use catalog() for unfamiliar custom operations or includeTextures:true for resource names. get_node_types is a different graph system.
25
-
26
- Storage and sharing
27
- - Material IDs and shader IDs occupy separate positive 16-bit namespaces; material 0 remains the scene default. There are eight ordered shader slots, where null/0 is empty. write always pads trailing slots to eight to clear the native layer cache.
28
- - write without materialId creates a material. cloneFrom copies base properties, layers and metadata. Existing materialId edits affect every user of that material. Graph edits copy shared shader storage automatically.
29
- - Graph nodes have op, optional name/location/editor_flags/reference_id, in0..in19 vectors, and optional external_data bytes. Read preserves native data. Keep unrelated metadata; node() constructs new nodes with scalar broadcasting and payload encoding.
30
- - Connections use zero-based ARRAY INDICES: input_node/source node, input_slot/source OUTPUT; output_node/destination node, output_slot/destination INPUT; flags:0 for new connections; native differential flags:2 are also supported. An input has one producer. Graphs must be acyclic, output nodes terminal, and enum/integer/Boolean selectors literal. Numeric Float/Float3/Color ports can interconnect; native math uses float3 and scalar consumers use X.
31
- - Unconnected OutputMaterial inputs inherit the incoming material, including the previous layer. Literal values on unconnected OutputMaterial inputs do not set properties. Connect Constant or Passthrough instead.
32
- - OutputMaterial inputs: 0 primary color, 1 secondary color, 2 subsurface, 3 metalness, 4 roughness, 5 transmittance, 6 translucency, 7 IOR, 8 iridescence, 9 emission, 10 absorption, 11 sheen, 12 specular tint, 13 translucency weight, 14 material weight, 15 dispersion, 16 displacement. Verify descriptors on the current engine.
33
- - InputMaterial and the exactly spelled InputUnderlayingMaterial read the incoming layer material, not independently preserved base snapshots. OutputHeight changes shading normals. Material displacement has separate export semantics; do not claim it changed mesh geometry.
34
-
35
- Constants and resetting
36
- - Constant input 0 chooses Pi/Half Pi/Double Pi/Exponent; input 1 is value vector, 2 color, 3 UI exposure enum, 4 scope. Outputs 0 built-in constant, 1 value, 2 color.
37
- - Give a new adjustable Constant a unique positive safe-integer reference_id, or let write allocate it and read it back. Keep reference IDs stable while editing. Independent constants across layers must have distinct references.
38
- - constants overrides are keyed by reference ID and contain BOTH value:{x,y,z} and color:{x,y,z}. Native omission zeros the missing counterpart. Use read to preserve it.
39
- - Reset with constants:{[referenceId]:null}. Kakapo retains deleted override keys in its in-memory cache; Spark gives the reset node a fresh reference and removes the stored old override. Replacing layers on an existing material also gives unoverridden Constants fresh references, so reusing an old graph cannot revive cached overrides. Use the returned referenceIdChanges mapping for subsequent edits. Other materials retain their references and overrides. Reset follows graph defaults and survives reopening.
40
-
41
- Units, colors and mapping
42
- - Graph vectors use renderer coordinates. One native length unit is 25 mm; Spark scene editing flips X/Z into its public basis, so a public mm position maps to {-z/(25*u),y/(25*u),-x/(25*u)}, where u is the scene unit_multiplier (default 1). Do not scale every numeric shader input: colors, frequencies, normals and normalized UVs have different meanings.
43
- - Mapping computes rotate(value, rotationDegrees) * scale + translation. Set scale explicitly to {x:1,y:1,z:1}; omitted literals are zero. Projection mode comes from the live enum descriptor.
44
- - InputPosition uses the carrier's local coordinates, including its procedural transform; it is not universally a normalized box or a physical distance. InputWorldPosition uses renderer world coordinates. Pair local position with InputNormal, or world position with InputWorldNormal, for projection. Compensate non-uniform geometry scale in mapping, without changing the geometry. A 1-unit and 0.1-unit object-space grid together reveals actual spacing and stretching in one inspection.
45
- - Connected procedural colors are linear RGB in 0..1: Constant colors, ramp stops and recipe colorA/colorB. scene materialSetColor uses display RGB 0..255; to reproduce its base color in a procedural graph use (channel/255)**2.2 per channel. This renderer uses gamma 2.2 as its sRGB approximation; exact sRGB decoding is c/12.92 for c<=0.04045, otherwise ((c+0.055)/1.055)**2.4. Use exact decoding for known standard sRGB data, or gamma 2.2 when matching the renderer's base colors and texture transform. Never decode already-linear values, roughness, metalness, height or normals.
46
- - Native InputMaterial in layer 0 applies pow(color,1/2.2) to incoming primary/secondary/subsurface color, while later slots return linear color. Passing that layer-0 color back to OutputMaterial brightens it. If reading and rewriting incoming color in slot 0, raise it to 2.2 before linear calculations. Direct connected literals need no slot-specific correction. Leading empty slots still determine the shader's slot. PredefinedTexture's sRGB (Color) transform decodes RGB with pow(c,2.2); do not decode its output twice.
47
- - NoiseFBM detail is 1..16. Coordinate scale changes spatial frequency; noise scale is amplitude. Random is sampled during shading, not a stable per-object seed.
48
- - WavesTexture is radial in its supplied coordinates. For parallel grain or warp/weft, use the recipe graphs or component-wise periodic functions rather than assuming WavesTexture produces straight stripes.
49
-
50
- Resource payloads
51
- - node({op:'PredefinedTexture',texture:catalogueName,...}) encodes the exact UTF-8 filename plus NUL. The GPU texture slot and dimensions are renderer-owned; never author them. write resolves the current display index from the filename. Texture also requires a discovered resource name; uploading files is host-owned.
52
- - Refresh requests run reload_textures before get_procedural_nodes; the latter returns a JSON string whose PredefinedTexture.inputs[2].values is authoritative. A refreshed name list does not prove GPU upload success.
53
- - node({op:'ColorRamp',ramp:[{position:0,color:{x:0,y:0,z:0}},{position:1,color:{x:1,y:1,z:1}}],inputs:{3:0}}) encodes 16 little-endian bytes per stop: position,r,g,b. Use at least two strictly increasing positions in 0..1. Modes come from input 3's enum.
54
- - node({op:'OptionSelector',options:[{name:'Low',value:0.2},{name:'High',value:0.8}],inputs:{1:0}}) encodes 36 bytes per option: float32 value and 32-byte NUL-terminated name. Maximum 32 options, names at most 31 UTF-8 bytes. Select an integer in [0,count). Dynamic selector connections are refused because this engine's upper clamp can read out of bounds.
55
- - Hidden/CPU-only payload ports cannot be connected. Unknown operation/payload, invalid selectors, cycles, disabled edges and duplicate output nodes are rejected before commit. Native payload padding is normalized without changing its meaning. HSL's output is vector-valued despite a Float descriptor; native numeric connections preserve it.
56
-
57
- Recipe: constant colored material with roughness
58
- const graph = {
59
- nodes: [
60
- womp.materials.node({op:'Constant',name:'Color',reference_id:101,inputs:{2:{x:0.65,y:0.12,z:0.03},3:1}}),
61
- womp.materials.node({op:'Passthrough',name:'Roughness',inputs:{0:0.35}}),
62
- womp.materials.node({op:'OutputMaterial'})
63
- ],
64
- connections:[
65
- {input_node:0,input_slot:2,output_node:2,output_slot:0,flags:0},
66
- {input_node:1,input_slot:0,output_node:2,output_slot:4,flags:0}
67
- ]
68
- };
69
- print(await womp.materials.write({layers:[graph],objectIds:[objectId]}));
70
-
71
- For a noise gradient, connect InputPosition to NoiseFBM's coordinate input, NoiseFBM's scalar output to ColorRamp input 0, and the ramp to OutputMaterial input 0. Set frequency, amplitude and detail deliberately. For mapped textures, connect position through Mapping/Projection to PredefinedTexture's coordinate input, then connect its color to the material output. Resolve selectors for custom graphs from the current catalogue; reuse descriptors already read.
72
-
73
- Projection and texture controls
74
- - Resolve projection selectors by their current descriptor names. Match Planar to flat faces, Cubic to box faces, Cylindrical to axial sides and Equirectangular to spheres; inspect caps, seams, poles and transitions where applicable. Center normalized projections on the intended carrier and check the result after resizing.
75
- - Rotate position and normal inputs consistently, and set Mapping scale explicitly to 1. Compare projection changes under the same light and camera with an asymmetric texture that reveals axis swaps and stretching.
76
- - Choose PredefinedTexture's transform by the resource's known purpose: sRGB (Color), No transform for scalar data, or Normal for an encoded normal map. False for Disable Filtering or Do not repeat leaves filtering or repetition enabled. Check those settings at the intended image scale.
77
- - A single-channel texture may sample as (R,0,0). For neutral RGB, connect X through GetComponent to all three CombineValues inputs. A catalogue name does not establish channels, color space or normal convention. Alpha requires an explicit connection to the intended material input.
78
- - HSL hue, saturation and lightness inputs are additive offsets, not multipliers; use 0 for unchanged channels. Its color input/output is linear RGB. Perceptual palette edits can be performed in a known display-color encoding and decoded back to linear RGB before writing.
79
-
80
- Recipe: one-shader noise bump
81
- Discover InputPosition, Mapping, NoiseFBM, Bump and OutputSurfaceNormal before using this example. It sets gray color, roughness 0.3 and metalness 0.3. Allocate fresh Constant references through write rather than copying IDs from another material.
82
- ${MATERIALS_NOISE_BUMP_RECIPE}
83
- return await womp.materials.write({layers:[graph],objectIds:[objectId]});
84
-
85
- Bump and normal verification
86
- - Bump Scale changes shading-normal strength; coordinate scale changes feature frequency. Compare positive and negative Bump Scale under the same light when the relief direction matters. Neither changes the silhouette.
87
- - Strength depends on the height gradient, including feature frequency, contrast and noise detail. Native Bump estimates height differences over local-position offsets and returns a normalized tangent-space normal; its Scale is not a 0..1 blend strength or a physical depth in mm. The offsets depend on scene/object scale and numerical precision, not pixel footprint. More octaves do not guarantee resolved detail. Independently weight defining profiles and micro-noise before Bump; strengthen washed-out structure while preserving coherent highlights and the requested finish. Use calibration only with its stated space and feature size.
88
- - Feed a Normal-decoded texture into tangent-space OutputSurfaceNormal. Use a known map convention, and inspect flat and green-flipped controls if handedness is uncertain.
89
- - OutputHeight Scale is separate from tangent-normal-map strength. The current native OutputHeight shader does not use its Coarseness input; texture sampling has a separate control. Match the desired appearance visually rather than treating height and normal strengths as equivalent.
90
- - Combine normals deliberately and normalize the result. Whiteout composition, normalize(vec3(nTexture.x+nBump.x,nTexture.y+nBump.y,nTexture.z*nBump.z)), is approximate. Compare it with each input alone; these operations change shading normals, not geometry.
91
-
92
- Verification
93
- - When numeric, status().remaining counts compiler tasks globally; null means that query failed. Zero is not a per-graph success flag: old valid shaders can keep rendering during compilation or failure.
94
- - After writing, inspect a scene screenshot, compare intended channels at a useful scale, and reopen the saved scene when persistence is part of acceptance. Separate committed, persisted, compiler queue idle, and visually verified results.
95
- - The current SDK does not expose per-shader compiler diagnostics. Retain write receipts and any available runtime error; do not infer compilation success from an idle queue.
96
- - InputSceneInfo IDs and Random values are runtime evaluation data, not persistent authored seeds. Use Constants when reproducible variation is required.
23
+ export const MATERIALS_REFERENCE = `
24
+ Procedural shaders are material graphs, separate from Geometry Nodes and Workbench Flow. recipe() builds the standard grid, noise, grain and woven graphs with live selectors internally; their controls are already defined. Use catalog() for unfamiliar custom operations or includeTextures:true for resource names. get_node_types is a different graph system.
25
+
26
+ Storage and sharing
27
+ - Material IDs and shader IDs occupy separate positive 16-bit namespaces; material 0 remains the scene default. There are eight ordered shader slots, where null/0 is empty. write always pads trailing slots to eight to clear the native layer cache.
28
+ - write without materialId creates a material. cloneFrom copies base properties, layers and metadata. Existing materialId edits affect every user of that material. Graph edits copy shared shader storage automatically.
29
+ - Graph nodes have op, optional name/location/editor_flags/reference_id, in0..in19 vectors, and optional external_data bytes. Read preserves native data. Keep unrelated metadata; node() constructs new nodes with scalar broadcasting and payload encoding.
30
+ - Connections use zero-based ARRAY INDICES: input_node/source node, input_slot/source OUTPUT; output_node/destination node, output_slot/destination INPUT; flags:0 for new connections; native differential flags:2 are also supported. An input has one producer. Graphs must be acyclic, output nodes terminal, and enum/integer/Boolean selectors literal. Numeric Float/Float3/Color ports can interconnect; native math uses float3 and scalar consumers use X.
31
+ - Unconnected OutputMaterial inputs inherit the incoming material, including the previous layer. Literal values on unconnected OutputMaterial inputs do not set properties. Connect Constant or Passthrough instead.
32
+ - OutputMaterial inputs: 0 primary color, 1 secondary color, 2 subsurface, 3 metalness, 4 roughness, 5 transmittance, 6 translucency, 7 IOR, 8 iridescence, 9 emission, 10 absorption, 11 sheen, 12 specular tint, 13 translucency weight, 14 material weight, 15 dispersion, 16 displacement. Verify descriptors on the current engine.
33
+ - InputMaterial and the exactly spelled InputUnderlayingMaterial read the incoming layer material, not independently preserved base snapshots. OutputHeight changes shading normals. Material displacement has separate export semantics; do not claim it changed mesh geometry.
34
+
35
+ Constants and resetting
36
+ - Constant input 0 chooses Pi/Half Pi/Double Pi/Exponent; input 1 is value vector, 2 color, 3 UI exposure enum, 4 scope. Outputs 0 built-in constant, 1 value, 2 color.
37
+ - Give a new adjustable Constant a unique positive safe-integer reference_id, or let write allocate it and read it back. Keep reference IDs stable while editing. Independent constants across layers must have distinct references.
38
+ - constants overrides are keyed by reference ID and contain BOTH value:{x,y,z} and color:{x,y,z}. Native omission zeros the missing counterpart. Use read to preserve it.
39
+ - Reset with constants:{[referenceId]:null}. Kakapo retains deleted override keys in its in-memory cache; Spark gives the reset node a fresh reference and removes the stored old override. Replacing layers on an existing material also gives unoverridden Constants fresh references, so reusing an old graph cannot revive cached overrides. Use the returned referenceIdChanges mapping for subsequent edits. Other materials retain their references and overrides. Reset follows graph defaults and survives reopening.
40
+
41
+ Units, colors and mapping
42
+ - Graph vectors use renderer coordinates. One native length unit is 25 mm; Spark scene editing flips X/Z into its public basis, so a public mm position maps to {-z/(25*u),y/(25*u),-x/(25*u)}, where u is the scene unit_multiplier (default 1). Do not scale every numeric shader input: colors, frequencies, normals and normalized UVs have different meanings.
43
+ - Mapping computes rotate(value, rotationDegrees) * scale + translation. Set scale explicitly to {x:1,y:1,z:1}; omitted literals are zero. Projection mode comes from the live enum descriptor.
44
+ - InputPosition uses the carrier's local coordinates, including its procedural transform; it is not universally a normalized box or a physical distance. InputWorldPosition uses renderer world coordinates. Pair local position with InputNormal, or world position with InputWorldNormal, for projection. Compensate non-uniform geometry scale in mapping, without changing the geometry. A 1-unit and 0.1-unit object-space grid together reveals actual spacing and stretching in one inspection.
45
+ - Connected procedural colors are linear RGB in 0..1: Constant colors, ramp stops and recipe colorA/colorB. scene materialSetColor uses display RGB 0..255; to reproduce its base color in a procedural graph use (channel/255)**2.2 per channel. This renderer uses gamma 2.2 as its sRGB approximation; exact sRGB decoding is c/12.92 for c<=0.04045, otherwise ((c+0.055)/1.055)**2.4. Use exact decoding for known standard sRGB data, or gamma 2.2 when matching the renderer's base colors and texture transform. Never decode already-linear values, roughness, metalness, height or normals.
46
+ - Native InputMaterial in layer 0 applies pow(color,1/2.2) to incoming primary/secondary/subsurface color, while later slots return linear color. Passing that layer-0 color back to OutputMaterial brightens it. If reading and rewriting incoming color in slot 0, raise it to 2.2 before linear calculations. Direct connected literals need no slot-specific correction. Leading empty slots still determine the shader's slot. PredefinedTexture's sRGB (Color) transform decodes RGB with pow(c,2.2); do not decode its output twice.
47
+ - NoiseFBM detail is 1..16. Coordinate scale changes spatial frequency; noise scale is amplitude. Random is sampled during shading, not a stable per-object seed.
48
+ - WavesTexture is radial in its supplied coordinates. For parallel grain or warp/weft, use the recipe graphs or component-wise periodic functions rather than assuming WavesTexture produces straight stripes.
49
+
50
+ Resource payloads
51
+ - node({op:'PredefinedTexture',texture:catalogueName,...}) encodes the exact UTF-8 filename plus NUL. The GPU texture slot and dimensions are renderer-owned; never author them. write resolves the current display index from the filename. Texture also requires a discovered resource name; uploading files is host-owned.
52
+ - Refresh requests run reload_textures before get_procedural_nodes; the latter returns a JSON string whose PredefinedTexture.inputs[2].values is authoritative. A refreshed name list does not prove GPU upload success.
53
+ - node({op:'ColorRamp',ramp:[{position:0,color:{x:0,y:0,z:0}},{position:1,color:{x:1,y:1,z:1}}],inputs:{3:0}}) encodes 16 little-endian bytes per stop: position,r,g,b. Use at least two strictly increasing positions in 0..1. Modes come from input 3's enum.
54
+ - node({op:'OptionSelector',options:[{name:'Low',value:0.2},{name:'High',value:0.8}],inputs:{1:0}}) encodes 36 bytes per option: float32 value and 32-byte NUL-terminated name. Maximum 32 options, names at most 31 UTF-8 bytes. Select an integer in [0,count). Dynamic selector connections are refused because this engine's upper clamp can read out of bounds.
55
+ - Hidden/CPU-only payload ports cannot be connected. Unknown operation/payload, invalid selectors, cycles, disabled edges and duplicate output nodes are rejected before commit. Native payload padding is normalized without changing its meaning. HSL's output is vector-valued despite a Float descriptor; native numeric connections preserve it.
56
+
57
+ Recipe: constant colored material with roughness
58
+ const graph = {
59
+ nodes: [
60
+ womp.materials.node({op:'Constant',name:'Color',reference_id:101,inputs:{2:{x:0.65,y:0.12,z:0.03},3:1}}),
61
+ womp.materials.node({op:'Passthrough',name:'Roughness',inputs:{0:0.35}}),
62
+ womp.materials.node({op:'OutputMaterial'})
63
+ ],
64
+ connections:[
65
+ {input_node:0,input_slot:2,output_node:2,output_slot:0,flags:0},
66
+ {input_node:1,input_slot:0,output_node:2,output_slot:4,flags:0}
67
+ ]
68
+ };
69
+ print(await womp.materials.write({layers:[graph],objectIds:[objectId]}));
70
+
71
+ For a noise gradient, connect InputPosition to NoiseFBM's coordinate input, NoiseFBM's scalar output to ColorRamp input 0, and the ramp to OutputMaterial input 0. Set frequency, amplitude and detail deliberately. For mapped textures, connect position through Mapping/Projection to PredefinedTexture's coordinate input, then connect its color to the material output. Resolve selectors for custom graphs from the current catalogue; reuse descriptors already read.
72
+
73
+ Projection and texture controls
74
+ - Resolve projection selectors by their current descriptor names. Match Planar to flat faces, Cubic to box faces, Cylindrical to axial sides and Equirectangular to spheres; inspect caps, seams, poles and transitions where applicable. Center normalized projections on the intended carrier and check the result after resizing.
75
+ - Rotate position and normal inputs consistently, and set Mapping scale explicitly to 1. Compare projection changes under the same light and camera with an asymmetric texture that reveals axis swaps and stretching.
76
+ - Choose PredefinedTexture's transform by the resource's known purpose: sRGB (Color), No transform for scalar data, or Normal for an encoded normal map. False for Disable Filtering or Do not repeat leaves filtering or repetition enabled. Check those settings at the intended image scale.
77
+ - A single-channel texture may sample as (R,0,0). For neutral RGB, connect X through GetComponent to all three CombineValues inputs. A catalogue name does not establish channels, color space or normal convention. Alpha requires an explicit connection to the intended material input.
78
+ - HSL hue, saturation and lightness inputs are additive offsets, not multipliers; use 0 for unchanged channels. Its color input/output is linear RGB. Perceptual palette edits can be performed in a known display-color encoding and decoded back to linear RGB before writing.
79
+
80
+ Recipe: one-shader noise bump
81
+ Discover InputPosition, Mapping, NoiseFBM, Bump and OutputSurfaceNormal before using this example. It sets gray color, roughness 0.3 and metalness 0.3. Allocate fresh Constant references through write rather than copying IDs from another material.
82
+ ${MATERIALS_NOISE_BUMP_RECIPE}
83
+ return await womp.materials.write({layers:[graph],objectIds:[objectId]});
84
+
85
+ Bump and normal verification
86
+ - Bump Scale changes shading-normal strength; coordinate scale changes feature frequency. Compare positive and negative Bump Scale under the same light when the relief direction matters. Neither changes the silhouette.
87
+ - Strength depends on the height gradient, including feature frequency, contrast and noise detail. Native Bump estimates height differences over local-position offsets and returns a normalized tangent-space normal; its Scale is not a 0..1 blend strength or a physical depth in mm. The offsets depend on scene/object scale and numerical precision, not pixel footprint. More octaves do not guarantee resolved detail. Independently weight defining profiles and micro-noise before Bump; strengthen washed-out structure while preserving coherent highlights and the requested finish. Use calibration only with its stated space and feature size.
88
+ - Feed a Normal-decoded texture into tangent-space OutputSurfaceNormal. Use a known map convention, and inspect flat and green-flipped controls if handedness is uncertain.
89
+ - OutputHeight Scale is separate from tangent-normal-map strength. The current native OutputHeight shader does not use its Coarseness input; texture sampling has a separate control. Match the desired appearance visually rather than treating height and normal strengths as equivalent.
90
+ - Combine normals deliberately and normalize the result. Whiteout composition, normalize(vec3(nTexture.x+nBump.x,nTexture.y+nBump.y,nTexture.z*nBump.z)), is approximate. Compare it with each input alone; these operations change shading normals, not geometry.
91
+
92
+ Verification
93
+ - When numeric, status().remaining counts compiler tasks globally; null means that query failed. Zero is not a per-graph success flag: old valid shaders can keep rendering during compilation or failure.
94
+ - After writing, inspect a scene screenshot, compare intended channels at a useful scale, and reopen the saved scene when persistence is part of acceptance. Separate committed, persisted, compiler queue idle, and visually verified results.
95
+ - The current SDK does not expose per-shader compiler diagnostics. Retain write receipts and any available runtime error; do not infer compilation success from an idle queue.
96
+ - InputSceneInfo IDs and Random values are runtime evaluation data, not persistent authored seeds. Use Constants when reproducible variation is required.
97
97
  `;
@@ -1,12 +1,12 @@
1
- export const MATERIALS_STRUCTURE_GUIDANCE = `shape(field, options) constructs generic remap, ridge, periodic, warp, palette and masked-blend fields. Name features for later editing. Remap endpoints, ridge center/width, periodic sharpness and warp frequency/amplitude are exposed Constants; palette stops remain graph data. Remap/ridge/periodic use analytic math. For a smooth nonlinear scalar profile, use the palette shape with mode:'Smooth' and grayscale stops (equal RGB components); compose reads X for relief or masks. Use this when a simple remap or ridge cannot reproduce the intended shape.
2
- shape(base,{kind:'blend',overlay,mask,name}) layers an RGB/scalar field or linear RGB literal over the base using a field mask or numeric amount. Zero mask retains the base; one selects the overlay. Field masks use clamped X broadcast to RGB. This helper resolves MathLerp internally; literal overlay colors and amounts become editable Constants. Build independent palettes and masks, then chain blend calls for feature-specific colors in one construction batch.
3
- Periodic and warp coordinates default to the field's feature-size Mapping. Custom fields require an explicit vector coordinate node. Warp frequency/amplitude are relative to those coordinates; preserve measured feature size. Build periodic profiles, warp their coordinates, then form ridges/masks. Resolve indices in each returned graph because unused nodes are pruned. Independent XYZ warp avoids diagonal distortion.
4
- A generic noise layer counts only when it reproduces the requested shape. Construct defining boundaries, strands and crossings explicitly. Palette vector fields preserve RGB with compose color low:{x:0,y:0,z:0}, high:{x:1,y:1,z:1}, weight 1; use separate scalar fields for finish/relief and combine heights before deriving normals once. Composed fields have independent Constant handles.
1
+ export const MATERIALS_STRUCTURE_GUIDANCE = `shape(field, options) constructs generic remap, ridge, periodic, warp, palette and masked-blend fields. Name features for later editing. Remap endpoints, ridge center/width, periodic sharpness and warp frequency/amplitude are exposed Constants; palette stops remain graph data. Remap/ridge/periodic use analytic math. For a smooth nonlinear scalar profile, use the palette shape with mode:'Smooth' and grayscale stops (equal RGB components); compose reads X for relief or masks. Use this when a simple remap or ridge cannot reproduce the intended shape.
2
+ shape(base,{kind:'blend',overlay,mask,name}) layers an RGB/scalar field or linear RGB literal over the base using a field mask or numeric amount. Zero mask retains the base; one selects the overlay. Field masks use clamped X broadcast to RGB. This helper resolves MathLerp internally; literal overlay colors and amounts become editable Constants. Build independent palettes and masks, then chain blend calls for feature-specific colors in one construction batch.
3
+ Periodic and warp coordinates default to the field's feature-size Mapping. Custom fields require an explicit vector coordinate node. Warp frequency/amplitude are relative to those coordinates; preserve measured feature size. Build periodic profiles, warp their coordinates, then form ridges/masks. Resolve indices in each returned graph because unused nodes are pruned. Independent XYZ warp avoids diagonal distortion.
4
+ A generic noise layer counts only when it reproduces the requested shape. Construct defining boundaries, strands and crossings explicitly. Palette vector fields preserve RGB with compose color low:{x:0,y:0,z:0}, high:{x:1,y:1,z:1}, weight 1; use separate scalar fields for finish/relief and combine heights before deriving normals once. Composed fields have independent Constant handles.
5
5
  For custom graphs, node({inputs}) keys are numeric input-slot indices, never displayed port titles. Arithmetic uses op:'Math', with its operation selected by literal input 0; this engine's multiplication selector is 2 (label 'Mulitiply'). Check the current descriptor for other modes. Use exact catalogue operation names. When removing a graph node, remove and reindex its connections instead of leaving an unsupported placeholder.`;
6
- export const MATERIALS_QUALITY_GUIDANCE = `Create materials with the highest achievable fidelity, quality and detail resolution supported by these capabilities.
7
- For every new material: inspect target dimensions and ancestor transforms, then create and inspect an isolated base-color grid probe BEFORE building final surface structure. Use object-space featureSize:1: major lines every 1 unit and lower-contrast minor lines every 0.1 unit together, with visible line colors and shallow height relief. Assess spacing, stretch, orientation and relief on relevant faces. Correct unintended coordinate distortion without changing geometry or transforms. Replace the probe completely; preserve other material users and assignments. World-space field sizes are physical millimeters; object-space sizes follow carrier coordinates and transforms. Non-uniform dimensions and ancestor transforms affect mapping. Keep physical feature size consistent and all channels aligned; distinguish intended directional anatomy from smeared stretching.
8
- Before construction, decompose the description/reference into broad variation, defining structure and fine grain. Record each component's relative scale, shape, direction, distribution, edge character and contrast, and its color/roughness/relief contributions. Apply text overrides while preserving remaining reference features. Distinguish reference illumination from intrinsic structure. Give microstructure independent scale and strength; superimpose components without dominant motifs obscuring it.
9
- Build all planned components and finish channels together using field, shape and compose. Their supported operations and selectors are resolved internally; construct masks, palettes and blends directly from these helpers. For textured surfaces, apply the tested readability adjustment in the first construction: bump scale ×1.25, palette saturation ×1.15, palette lightness ×0.95 and scalar feature contrast ×1.15 around 0.5, clamped to 0..1. Apply color factors in the known display-color encoding, then decode back to linear RGB; native HSL inputs are additive offsets. Incorporate this once into chosen base controls, without an extra material/render pass or compounding it during refinement. Explicit flat, muted or pale requests take precedence; retain reference hues, defining features and the requested finish. These factors are a starting adjustment, not universal optimum values. Keep defining height and fine-noise height independently weighted; increasing all bump together can erase coherent highlights and change gloss into a matte finish. Relief responds to height slope: finer spacing, sharper edges and more noise octaves can strengthen normals at unchanged scale. Calibrated ranges describe particular fields, not quality ceilings or universal strengths.
10
- Adjust palette saturation/lightness separately from feature coverage and edge contrast; use bounded scalar remaps before palettes or masked blends, checking clipped colors and changed coverage. Keep intrinsic color separate from illumination; preserve natural low-saturation features and coherent highlights. Author linear RGB; decode reference/display colors and preserve already-linear colors and scalar channels. Use roughness variation for finish breakup and signed height profiles for actual ridges/cavities; a dark spot alone does not establish a pit.
11
- Inspect complementary overall, close and grazing views together under the existing scene lighting, geometry and render settings; adjust viewpoints for inspection. Match tonal contrast and reflectivity through highlight width, strength and breakup. Confirm every planned feature remains visibly resolved: a fine-noise node does not prove fine grain. Check that defining relief reads through lighting at the intended viewing distance, rather than only through its color mask. Subpixel features cannot remain individually resolved by increasing strength or octaves. If relief destroys the requested sheen, rebalance the offending feature's height/frequency before compensating roughness.
6
+ export const MATERIALS_QUALITY_GUIDANCE = `Create materials with the highest achievable fidelity, quality and detail resolution supported by these capabilities.
7
+ For every new material: inspect target dimensions and ancestor transforms, then create and inspect an isolated base-color grid probe BEFORE building final surface structure. Use object-space featureSize:1: major lines every 1 unit and lower-contrast minor lines every 0.1 unit together, with visible line colors and shallow height relief. Assess spacing, stretch, orientation and relief on relevant faces. Correct unintended coordinate distortion without changing geometry or transforms. Replace the probe completely; preserve other material users and assignments. World-space field sizes are physical millimeters; object-space sizes follow carrier coordinates and transforms. Non-uniform dimensions and ancestor transforms affect mapping. Keep physical feature size consistent and all channels aligned; distinguish intended directional anatomy from smeared stretching.
8
+ Before construction, decompose the description/reference into broad variation, defining structure and fine grain. Record each component's relative scale, shape, direction, distribution, edge character and contrast, and its color/roughness/relief contributions. Apply text overrides while preserving remaining reference features. Distinguish reference illumination from intrinsic structure. Give microstructure independent scale and strength; superimpose components without dominant motifs obscuring it.
9
+ Build all planned components and finish channels together using field, shape and compose. Their supported operations and selectors are resolved internally; construct masks, palettes and blends directly from these helpers. For textured surfaces, apply the tested readability adjustment in the first construction: bump scale ×1.25, palette saturation ×1.15, palette lightness ×0.95 and scalar feature contrast ×1.15 around 0.5, clamped to 0..1. Apply color factors in the known display-color encoding, then decode back to linear RGB; native HSL inputs are additive offsets. Incorporate this once into chosen base controls, without an extra material/render pass or compounding it during refinement. Explicit flat, muted or pale requests take precedence; retain reference hues, defining features and the requested finish. These factors are a starting adjustment, not universal optimum values. Keep defining height and fine-noise height independently weighted; increasing all bump together can erase coherent highlights and change gloss into a matte finish. Relief responds to height slope: finer spacing, sharper edges and more noise octaves can strengthen normals at unchanged scale. Calibrated ranges describe particular fields, not quality ceilings or universal strengths.
10
+ Adjust palette saturation/lightness separately from feature coverage and edge contrast; use bounded scalar remaps before palettes or masked blends, checking clipped colors and changed coverage. Keep intrinsic color separate from illumination; preserve natural low-saturation features and coherent highlights. Author linear RGB; decode reference/display colors and preserve already-linear colors and scalar channels. Use roughness variation for finish breakup and signed height profiles for actual ridges/cavities; a dark spot alone does not establish a pit.
11
+ Inspect complementary overall, close and grazing views together under the existing scene lighting, geometry and render settings; adjust viewpoints for inspection. Match tonal contrast and reflectivity through highlight width, strength and breakup. Confirm every planned feature remains visibly resolved: a fine-noise node does not prove fine grain. Check that defining relief reads through lighting at the intended viewing distance, rather than only through its color mask. Subpixel features cannot remain individually resolved by increasing strength or octaves. If relief destroys the requested sheen, rebalance the offending feature's height/frequency before compensating roughness.
12
12
  For each refinement, name the largest visible mismatch and the expected correction. Wrong topology/shape needs construction changes; wrong spacing, color, roughness or relief needs the corresponding parameter changes. Use known construction inputs and returned control handles to apply the correction in that batch. Read graphs when editing a previously unknown material or recovering unavailable/stale handles. Adjust related controls together and inspect once. Compare each candidate with the best inspected version at the same views; restore the better graph if a change weakens a defining feature. If tuning fails to improve a mismatch, change its construction rather than repeating strength changes. Continue while a specific, supported correction is likely to improve a substantial mismatch; stop when the result meets the request or further edits cease to help. Do not end with an uninspected edit. Print only relevant IDs, controls and observations; retain full graphs locally for edits.`;
@@ -1,12 +1,12 @@
1
1
  import { z } from "zod";
2
2
  import { INTERNAL_UNITS_PER_MM } from "../coordinates.js";
3
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.
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
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
11
  const positiveVector = z.object({ x: z.number().finite().positive(), y: z.number().finite().positive(), z: z.number().finite().positive() }).strict();
12
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();
package/dist/prompt.js CHANGED
@@ -1,75 +1,75 @@
1
- export const SPARK_AGENT_PROMPT = `You are Spark, Womp's unified assistant.
2
- Use read_skill({names:[...]}) to load the required APIs together. Skills provide TypeScript
3
- declarations for documentation; execute_javascript runs JavaScript, with top-level await.
4
- Only the listed capabilities are available. Reading a skill does not grant additional permissions.
5
- Use only the supplied womp APIs and output helpers to access application services and state.
6
- If a capability is unavailable, explain what is missing and ask the user to enable it when applicable.
7
- Do not work around missing capabilities by inspecting page globals, DOM or framework internals,
8
- or making direct network requests. Native execution is not a security sandbox; it does not expand
9
- the supported API or the user's granted capabilities.
10
- Give 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.
11
- Each execution has fresh local variables. Keep stable object, job, and artifact references.
12
- Use print for concise, filtered observations; await emitImage or emitFile with a host-issued artifact
13
- reference to show actual output. emitImage inspects by default; use { inspect: false } to display without adding image bytes to model context.
14
- Completed 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.
15
- Do 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.
16
- Inspect the scene before editing and verify the committed result.
17
- Preserve existing content unless the user asks to replace or delete it. Use the latest target revision
18
- when a write requires one. Capabilities own transactions: an entire script is not atomic.
19
- If an operation fails after earlier effects, use its receipts to inspect and retry only unresolved work.
20
- Never claim an uncertain commit rolled back. Never resubmit a job just because waiting timed out.
21
- Complete related work in this conversation without delegating to another agent. Report limits,
22
- unsupported actions, cancellation, and incomplete work truthfully. Finish with a concise explanation
23
- of the result in the user's language; keep implementation details out of that explanation.
24
-
25
- Creation intent
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.
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.
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.
39
-
40
- - For mesh wompification, read mesh-wompify and scene-editing together and reconstruct only the specified reference using primitives and curves.
41
-
42
- - Generate/edit images or generate meshes when requested. An attached image or reference alone
43
- does not request generation; use it for the requested workflow or clarify an unresolved request.
44
- Generating a result does not authorize importing, converting, or building it in the scene.
45
-
46
- - Use Workbench Flow for image/mesh generation explicitly requested through Flow/nodes or
47
- continued edits to that Flow workflow, unless the user asks for an inline result. Flow must be
48
- open; explain that prerequisite if closed instead of silently generating inline. Merely opening
49
- Flow does not redirect ordinary scene requests or inline generation to it.
50
-
51
- - After completing explicitly requested generation, you may briefly suggest a relevant next step
52
- using available capabilities. Describe its outcome, not internal tools. Suggestions are optional,
53
- not a mandatory menu, and never authorize actions. Wait for a clear choice and follow existing
54
- confirmations. Reuse the existing artifact/reference for the chosen follow-up, including as the
55
- image-to-mesh input, instead of regenerating the source. Preserve its defining features when building it.
56
-
57
- - Choose construction methods by the user's requested workflow and the
58
- available capabilities, not by assuming editable shapes require lower
59
- fidelity. A sculpted appearance does not by itself require mesh generation.
60
- Respect an explicit method choice and pursue the reference's visible shape
61
- and detail with that method. Offer alternatives only for a specific
62
- demonstrated limitation; ask before switching or simplifying the requested
63
- result. No method guarantees exact reconstruction from a single image.
64
-
65
- - Ask at most one focused question at a time, only when a sensible default
66
- would likely produce the wrong outcome. Do not repeat resolved questions.
67
- Interpret replies using the preceding offer; if several options were
68
- offered and “yes” does not identify one, clarify briefly.
69
-
70
- - Treat a clear answer to your question as an instruction to proceed.
71
- Continue from existing results rather than restarting the workflow.
72
-
73
- Offer only workflows available in this host. Follow their authorization
74
- and confirmation requirements before generation or scene changes. If a requested workflow is unavailable,
1
+ export const SPARK_AGENT_PROMPT = `You are Spark, Womp's unified assistant.
2
+ Use read_skill({names:[...]}) to load the required APIs together. Skills provide TypeScript
3
+ declarations for documentation; execute_javascript runs JavaScript, with top-level await.
4
+ Only the listed capabilities are available. Reading a skill does not grant additional permissions.
5
+ Use only the supplied womp APIs and output helpers to access application services and state.
6
+ If a capability is unavailable, explain what is missing and ask the user to enable it when applicable.
7
+ Do not work around missing capabilities by inspecting page globals, DOM or framework internals,
8
+ or making direct network requests. Native execution is not a security sandbox; it does not expand
9
+ the supported API or the user's granted capabilities.
10
+ Give 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.
11
+ Each execution has fresh local variables. Keep stable object, job, and artifact references.
12
+ Use print for concise, filtered observations; await emitImage or emitFile with a host-issued artifact
13
+ reference to show actual output. emitImage inspects by default; use { inspect: false } to display without adding image bytes to model context.
14
+ Completed 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.
15
+ Do 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.
16
+ Inspect the scene before editing and verify the committed result.
17
+ Preserve existing content unless the user asks to replace or delete it. Use the latest target revision
18
+ when a write requires one. Capabilities own transactions: an entire script is not atomic.
19
+ If an operation fails after earlier effects, use its receipts to inspect and retry only unresolved work.
20
+ Never claim an uncertain commit rolled back. Never resubmit a job just because waiting timed out.
21
+ Complete related work in this conversation without delegating to another agent. Report limits,
22
+ unsupported actions, cancellation, and incomplete work truthfully. Finish with a concise explanation
23
+ of the result in the user's language; keep implementation details out of that explanation.
24
+
25
+ Creation intent
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.
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.
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.
39
+
40
+ - For mesh wompification, read mesh-wompify and scene-editing together and reconstruct only the specified reference using primitives and curves.
41
+
42
+ - Generate/edit images or generate meshes when requested. An attached image or reference alone
43
+ does not request generation; use it for the requested workflow or clarify an unresolved request.
44
+ Generating a result does not authorize importing, converting, or building it in the scene.
45
+
46
+ - Use Workbench Flow for image/mesh generation explicitly requested through Flow/nodes or
47
+ continued edits to that Flow workflow, unless the user asks for an inline result. Flow must be
48
+ open; explain that prerequisite if closed instead of silently generating inline. Merely opening
49
+ Flow does not redirect ordinary scene requests or inline generation to it.
50
+
51
+ - After completing explicitly requested generation, you may briefly suggest a relevant next step
52
+ using available capabilities. Describe its outcome, not internal tools. Suggestions are optional,
53
+ not a mandatory menu, and never authorize actions. Wait for a clear choice and follow existing
54
+ confirmations. Reuse the existing artifact/reference for the chosen follow-up, including as the
55
+ image-to-mesh input, instead of regenerating the source. Preserve its defining features when building it.
56
+
57
+ - Choose construction methods by the user's requested workflow and the
58
+ available capabilities, not by assuming editable shapes require lower
59
+ fidelity. A sculpted appearance does not by itself require mesh generation.
60
+ Respect an explicit method choice and pursue the reference's visible shape
61
+ and detail with that method. Offer alternatives only for a specific
62
+ demonstrated limitation; ask before switching or simplifying the requested
63
+ result. No method guarantees exact reconstruction from a single image.
64
+
65
+ - Ask at most one focused question at a time, only when a sensible default
66
+ would likely produce the wrong outcome. Do not repeat resolved questions.
67
+ Interpret replies using the preceding offer; if several options were
68
+ offered and “yes” does not identify one, clarify briefly.
69
+
70
+ - Treat a clear answer to your question as an instruction to proceed.
71
+ Continue from existing results rather than restarting the workflow.
72
+
73
+ Offer only workflows available in this host. Follow their authorization
74
+ and confirmation requirements before generation or scene changes. If a requested workflow is unavailable,
75
75
  explain the limitation rather than silently substituting another.`;
@@ -9,7 +9,7 @@ export type ResolvedSparkModel = {
9
9
  };
10
10
  export type SparkRunExecutorOptions = {
11
11
  resolveModel(settings: SparkRunSettings): ResolvedSparkModel | Promise<ResolvedSparkModel>;
12
- createHost(context: SparkRunExecutionContext): Promise<Pick<SparkAgentOptions, "capabilities" | "executor" | "artifacts" | "persistence"> & {
12
+ createHost(context: SparkRunExecutionContext): Promise<Pick<SparkAgentOptions, "capabilities" | "executor" | "artifacts" | "persistence" | "context"> & {
13
13
  dispose(): void;
14
14
  }>;
15
15
  };
@@ -20,9 +20,11 @@ export function createSparkRunExecutor(options) {
20
20
  modelId: resolved.modelId,
21
21
  providerOptions: resolved.providerOptions,
22
22
  maxOutputTokens: resolved.maxOutputTokens,
23
+ tokenBudget: context.goal?.tokenBudget === undefined ? undefined : Math.max(1, context.goal.tokenBudget - context.goal.tokensUsed),
23
24
  runId: context.runId,
24
25
  messageId: context.assistantMessageId,
25
26
  signal: context.signal,
27
+ context: [host.context, context.goal ? `Active goal: ${JSON.stringify(context.goal.objective)}\nKeep working toward this objective and verify it with concrete tool results before claiming completion. Do not ask whether to continue. If you need user input or access, explain exactly what is missing. Latest evaluation: ${context.goal.lastReason ?? "Not evaluated yet."}` : undefined].filter(Boolean).join("\n\n"),
26
28
  });
27
29
  return { stream: run.stream, result: run.result.finally(() => host.dispose()) };
28
30
  }
@@ -4,6 +4,7 @@ import { z } from "zod";
4
4
  import { SPARK_RUN_STATUSES } from "../ui.js";
5
5
  import { SparkServiceError } from "./service.js";
6
6
  import { DEFAULT_JSON_LIMIT_BYTES } from "../request-limits.js";
7
+ import { sparkGoalCommandSchema } from "../goals.js";
7
8
  import { sparkErrorDiagnostics } from "../error-diagnostics.js";
8
9
  const reasoningEffortSchema = z.enum(["low", "medium", "high", "xhigh", "max"]);
9
10
  const settingsSchema = z.object({
@@ -21,6 +22,7 @@ const submitSchema = z.object({
21
22
  message: messageSchema,
22
23
  messageId: z.string().optional(),
23
24
  settings: settingsSchema,
25
+ goal: sparkGoalCommandSchema.optional(),
24
26
  });
25
27
  function parseSequence(value) {
26
28
  if (typeof value !== "string" || /^\d+$/u.test(value) === false)
@@ -72,6 +74,12 @@ export function createSparkAgentRouter(options) {
72
74
  router.post("/conversations/:conversationId/messages", async (request, response) => {
73
75
  const ownerId = await resolveOwnerId(request);
74
76
  const body = submitSchema.parse(request.body);
77
+ const command = body.trigger === "submit-message" ? body.goal : undefined;
78
+ if (command) {
79
+ const result = await options.service.goalCommand(ownerId, request.params.conversationId, command, body.settings, command.action === "set" ? body.message : undefined);
80
+ response.status(result.run ? 202 : 200).json(result);
81
+ return;
82
+ }
75
83
  const run = await options.service.submitMessage({
76
84
  ownerId,
77
85
  conversationId: request.params.conversationId,
@@ -82,6 +90,16 @@ export function createSparkAgentRouter(options) {
82
90
  });
83
91
  response.status(202).json({ run });
84
92
  });
93
+ router.get("/conversations/:conversationId/goal", async (request, response) => {
94
+ const ownerId = await resolveOwnerId(request);
95
+ response.json(await options.service.goalCommand(ownerId, request.params.conversationId, { action: "status" }));
96
+ });
97
+ router.post("/conversations/:conversationId/goal", async (request, response) => {
98
+ const ownerId = await resolveOwnerId(request);
99
+ const body = z.object({ command: sparkGoalCommandSchema, settings: settingsSchema.optional(), message: messageSchema.optional() }).parse(request.body);
100
+ const result = await options.service.goalCommand(ownerId, request.params.conversationId, body.command, body.settings, body.message);
101
+ response.status(result.run ? 202 : 200).json(result);
102
+ });
85
103
  router.get("/conversations/:conversationId/active-run", async (request, response) => {
86
104
  const ownerId = await resolveOwnerId(request);
87
105
  const run = await options.service.getActiveRun(ownerId, request.params.conversationId);
@@ -1,14 +1,14 @@
1
- {
2
- "name": "Development: Matte Blue",
3
- "resource": {
4
- "data": {
5
- "color.x": 0.08,
6
- "color.y": 0.3,
7
- "color.z": 0.8,
8
- "roughness": 0.85,
9
- "metalness": 0,
10
- "index_of_refraction": 1.5
11
- },
12
- "shaders": []
13
- }
14
- }
1
+ {
2
+ "name": "Development: Matte Blue",
3
+ "resource": {
4
+ "data": {
5
+ "color.x": 0.08,
6
+ "color.y": 0.3,
7
+ "color.z": 0.8,
8
+ "roughness": 0.85,
9
+ "metalness": 0,
10
+ "index_of_refraction": 1.5
11
+ },
12
+ "shaders": []
13
+ }
14
+ }
@@ -1,14 +1,14 @@
1
- {
2
- "name": "Development: Polished Copper",
3
- "resource": {
4
- "data": {
5
- "color.x": 0.955,
6
- "color.y": 0.638,
7
- "color.z": 0.538,
8
- "roughness": 0.18,
9
- "metalness": 1,
10
- "index_of_refraction": 1.5
11
- },
12
- "shaders": []
13
- }
14
- }
1
+ {
2
+ "name": "Development: Polished Copper",
3
+ "resource": {
4
+ "data": {
5
+ "color.x": 0.955,
6
+ "color.y": 0.638,
7
+ "color.z": 0.538,
8
+ "roughness": 0.18,
9
+ "metalness": 1,
10
+ "index_of_refraction": 1.5
11
+ },
12
+ "shaders": []
13
+ }
14
+ }
@@ -0,0 +1,29 @@
1
+ import { z } from "zod";
2
+ import { type SparkGoal } from "../goals.js";
3
+ import type { SparkUIMessage } from "../ui.js";
4
+ import type { SparkRunExecutorOptions } from "./executor.js";
5
+ export declare const goalVerdictSchema: z.ZodObject<{
6
+ verdict: z.ZodEnum<{
7
+ blocked: "blocked";
8
+ continue: "continue";
9
+ met: "met";
10
+ }>;
11
+ reason: z.ZodString;
12
+ }, z.core.$strip>;
13
+ export type SparkGoalEvaluation = z.infer<typeof goalVerdictSchema> & {
14
+ tokensUsed?: number;
15
+ };
16
+ export type SparkGoalEvaluator = (input: {
17
+ ownerId?: string;
18
+ goal: SparkGoal;
19
+ messages: SparkUIMessage[];
20
+ context?: Record<string, unknown>;
21
+ signal: AbortSignal;
22
+ }) => Promise<SparkGoalEvaluation>;
23
+ /** A separate, tool-free call; it never inherits the worker's instructions. */
24
+ export declare function createSparkGoalEvaluator(options: Pick<SparkRunExecutorOptions, "resolveModel"> & {
25
+ resolveImage?(ownerId: string, artifactId: string): Promise<{
26
+ data: Uint8Array;
27
+ mediaType: string;
28
+ } | undefined>;
29
+ }): SparkGoalEvaluator;