@womp/spark-agent 5.6.1 → 5.7.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/README.md +420 -420
- package/dist/agent-run.d.ts +1 -1
- package/dist/agent-tools.js +13 -3
- package/dist/agent.d.ts +2 -0
- package/dist/agent.js +3 -1
- package/dist/capabilities/geometry.js +14 -14
- package/dist/capabilities/materials.js +14 -14
- package/dist/capabilities/openscad-instructions.js +77 -77
- package/dist/capabilities/operations.js +28 -28
- package/dist/capabilities/scene.js +81 -81
- package/dist/capabilities/wompify.js +10 -10
- package/dist/client.d.ts +7 -0
- package/dist/client.js +14 -0
- package/dist/geometry/schemas/backend-schema.json +3393 -3393
- package/dist/geometry/schemas/flowir-dialect.json +1023 -1023
- package/dist/geometry.js +6 -6
- package/dist/goals.d.ts +43 -0
- package/dist/goals.js +37 -0
- package/dist/materials/recipes.js +266 -266
- package/dist/materials/reference.js +94 -94
- package/dist/materials/structure.js +10 -10
- package/dist/materials/surface-recipes.js +6 -6
- package/dist/prompt.js +74 -74
- package/dist/server/executor.d.ts +1 -1
- package/dist/server/executor.js +2 -0
- package/dist/server/express.js +18 -0
- package/dist/server/fixtures/materials/matte-blue.json +14 -14
- package/dist/server/fixtures/materials/polished-copper.json +14 -14
- package/dist/server/goal-evaluator.d.ts +29 -0
- package/dist/server/goal-evaluator.js +55 -0
- package/dist/server/index.d.ts +4 -0
- package/dist/server/index.js +4 -0
- package/dist/server/service.d.ts +17 -0
- package/dist/server/service.js +204 -5
- package/dist/server/sqlite-store.d.ts +2 -0
- package/dist/server/sqlite-store.js +45 -38
- package/dist/server/store.d.ts +2 -0
- package/dist/server/store.js +12 -3
- package/dist/server/types.d.ts +6 -0
- package/dist/ui.d.ts +12 -4
- package/dist/ui.js +3 -1
- package/dist/wompify.d.ts +11 -0
- package/dist/wompify.js +11 -2
- package/package.json +140 -140
package/dist/agent-run.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ export declare function runAgent(options: RunOptions): Promise<{
|
|
|
43
43
|
} | undefined;
|
|
44
44
|
conversationId: string | null;
|
|
45
45
|
runId: string | null;
|
|
46
|
-
status: "
|
|
46
|
+
status: "completed" | "queued" | "running" | "requires_action" | "failed" | "cancelled" | "incomplete";
|
|
47
47
|
finalAnswer: string;
|
|
48
48
|
durationMs: number | null;
|
|
49
49
|
steps: number;
|
package/dist/agent-tools.js
CHANGED
|
@@ -79,10 +79,18 @@ export function createSparkTools(options) {
|
|
|
79
79
|
return {
|
|
80
80
|
read_skill: tool({
|
|
81
81
|
...inputLabels("name"),
|
|
82
|
-
description: "Read
|
|
82
|
+
description: "Read required skills together with names, or one skill with name. Returns instructions, TypeScript API declarations, and JavaScript examples from the available directory.",
|
|
83
83
|
inputSchema: z.object({
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
name: z.string().min(1).max(160).optional(),
|
|
85
|
+
names: z.array(z.string().min(1).max(160)).min(1).optional(),
|
|
86
|
+
}).strict().transform((input, context) => {
|
|
87
|
+
if (input.name !== undefined && input.names === undefined)
|
|
88
|
+
return { name: input.name };
|
|
89
|
+
if (input.names !== undefined && input.name === undefined)
|
|
90
|
+
return { names: input.names };
|
|
91
|
+
context.addIssue({ code: "custom", message: "Provide exactly one of name or names." });
|
|
92
|
+
return z.NEVER;
|
|
93
|
+
}),
|
|
86
94
|
execute: (input, { toolCallId }) => execute({ id: toolCallId, name: "read_skill", input }, async () => {
|
|
87
95
|
const read = (requested) => {
|
|
88
96
|
const skill = options.capabilities.skills.find(({ name }) => name === requested);
|
|
@@ -95,6 +103,8 @@ export function createSparkTools(options) {
|
|
|
95
103
|
}
|
|
96
104
|
return { ok: true, ...skill };
|
|
97
105
|
};
|
|
106
|
+
if (input.name !== undefined)
|
|
107
|
+
return read(input.name);
|
|
98
108
|
const skills = [...new Set(input.names)].map(read);
|
|
99
109
|
return { ok: skills.every(skill => skill.ok), skills };
|
|
100
110
|
}),
|
package/dist/agent.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export type SparkAgentOptions = {
|
|
|
26
26
|
instructions?: string;
|
|
27
27
|
maxSteps?: number;
|
|
28
28
|
maxOutputTokens?: number;
|
|
29
|
+
/** Stops after the completed step that reaches the total-token limit. */
|
|
30
|
+
tokenBudget?: number;
|
|
29
31
|
executionTimeoutMs?: number;
|
|
30
32
|
persistence?: SparkPersistence;
|
|
31
33
|
prepareMessages?(messages: ModelMessage[]): Promise<ModelMessage[]>;
|
package/dist/agent.js
CHANGED
|
@@ -3,6 +3,7 @@ import { createUIMessageStream, stepCountIs, streamText, } from "ai";
|
|
|
3
3
|
import { createSparkTools } from "./agent-tools.js";
|
|
4
4
|
import { SPARK_AGENT_PROMPT } from "./prompt.js";
|
|
5
5
|
import { calculateStepCost } from "./pricing.js";
|
|
6
|
+
import { goalTokenCount } from "./goals.js";
|
|
6
7
|
import { contextWindowFor, fitMessagesToContext } from "./context-budget.js";
|
|
7
8
|
function errorMessage(error) {
|
|
8
9
|
return sparkErrorDiagnostics(error).message;
|
|
@@ -138,7 +139,8 @@ export function runSparkAgent(options) {
|
|
|
138
139
|
tools,
|
|
139
140
|
toolChoice: "auto",
|
|
140
141
|
abortSignal: controller.signal,
|
|
141
|
-
stopWhen: stepCountIs(maxSteps),
|
|
142
|
+
stopWhen: [stepCountIs(maxSteps), ({ steps }) => options.tokenBudget !== undefined &&
|
|
143
|
+
steps.reduce((total, step) => total + goalTokenCount(step.usage), 0) >= options.tokenBudget],
|
|
142
144
|
maxOutputTokens: options.maxOutputTokens,
|
|
143
145
|
providerOptions: options.providerOptions,
|
|
144
146
|
prepareStep: async ({ messages }) => {
|
|
@@ -14,20 +14,20 @@ export function createGeometryCapability(host, reference) {
|
|
|
14
14
|
name: "geometry-nodes",
|
|
15
15
|
version: "5",
|
|
16
16
|
description: "Create and edit visual Geometry Nodes graphs with adjustable parameters and animation.",
|
|
17
|
-
instructions: `Use womp.geometry for requested Geometry Nodes, parametric organic/procedural geometry, or animation.
|
|
18
|
-
Keep follow-up edits to an existing Geometry Nodes object in its graph, preserving its adjustable parameters and instances.
|
|
19
|
-
Read the target after any scene edit this build needs. read(0) reads the scene root as a creation container.
|
|
20
|
-
write updates an existing shared graph, attaches a new graph to an Area without one, or creates a new Area beneath the root/Group target. Use the returned objectId for follow-up edits.
|
|
21
|
-
Read geometry-nodes-reference before writing source. Supply the complete FlowIR text after inspecting the target. JavaScript calls execute JavaScript; source is a FlowIR string, not JavaScript or JSON.
|
|
22
|
-
Inspect the current target before replacing its graph; preserve unrelated geometry and parameter overrides.
|
|
23
|
-
If source is null and diagnostics are present, the existing graph cannot be edited without loss. Explain the limitation; never treat it as empty or create a replacement to bypass the refusal.
|
|
24
|
-
Preserve unrelated statements, parameter names, and FlowIR node IDs from the read. Shared graph edits affect all listed instances; report the affected objects. Valid overrides migrate by parameter name and type. Removed or type-changed parameters return to graph defaults and are reported.
|
|
25
|
-
Use min/max/step for naturally bounded parameters and omit ranges for free-form positions/rotations. Meaningful parameter names become editor labels. Do not invent label fields.
|
|
26
|
-
SetMaterial assigns one scene material to every primitive it receives. Its Material input is a scene material ID, not a color; 0 selects the scene default material.
|
|
27
|
-
Obtain that ID from the scene-editing skill inside one scene.access, where those lookups live: materialList with materialGetName and materialGetColor to reuse an existing material, materialCreate with materialSetColor and materialSetName for a new one, or listSuperMaterials with materialImport for a library material. Prefer reusing a material that already matches the request over creating another, and do not recolor a shared material unless every object using it should change.
|
|
28
|
-
Create or import the material inside scene.access before writing the geometry target, all within one JavaScript call. Each call has a fresh scope and cannot read an earlier call's variables.
|
|
29
|
-
Positions and sizes are millimetres; Boolean Blend uses the conversion in geometry-nodes-reference. Coordinates are right-handed, +X right, +Y up, +Z forward. Rotations are degrees, intrinsic X-Z-Y.
|
|
30
|
-
On compiler errors, repair the source in this same conversation and retry with a fresh read. A rejected write made no change. Committed/unknown effects remain in execution receipts even if a later call fails; inspect before retrying.
|
|
17
|
+
instructions: `Use womp.geometry for requested Geometry Nodes, parametric organic/procedural geometry, or animation.
|
|
18
|
+
Keep follow-up edits to an existing Geometry Nodes object in its graph, preserving its adjustable parameters and instances.
|
|
19
|
+
Read the target after any scene edit this build needs. read(0) reads the scene root as a creation container.
|
|
20
|
+
write updates an existing shared graph, attaches a new graph to an Area without one, or creates a new Area beneath the root/Group target. Use the returned objectId for follow-up edits.
|
|
21
|
+
Read geometry-nodes-reference before writing source. Supply the complete FlowIR text after inspecting the target. JavaScript calls execute JavaScript; source is a FlowIR string, not JavaScript or JSON.
|
|
22
|
+
Inspect the current target before replacing its graph; preserve unrelated geometry and parameter overrides.
|
|
23
|
+
If source is null and diagnostics are present, the existing graph cannot be edited without loss. Explain the limitation; never treat it as empty or create a replacement to bypass the refusal.
|
|
24
|
+
Preserve unrelated statements, parameter names, and FlowIR node IDs from the read. Shared graph edits affect all listed instances; report the affected objects. Valid overrides migrate by parameter name and type. Removed or type-changed parameters return to graph defaults and are reported.
|
|
25
|
+
Use min/max/step for naturally bounded parameters and omit ranges for free-form positions/rotations. Meaningful parameter names become editor labels. Do not invent label fields.
|
|
26
|
+
SetMaterial assigns one scene material to every primitive it receives. Its Material input is a scene material ID, not a color; 0 selects the scene default material.
|
|
27
|
+
Obtain that ID from the scene-editing skill inside one scene.access, where those lookups live: materialList with materialGetName and materialGetColor to reuse an existing material, materialCreate with materialSetColor and materialSetName for a new one, or listSuperMaterials with materialImport for a library material. Prefer reusing a material that already matches the request over creating another, and do not recolor a shared material unless every object using it should change.
|
|
28
|
+
Create or import the material inside scene.access before writing the geometry target, all within one JavaScript call. Each call has a fresh scope and cannot read an earlier call's variables.
|
|
29
|
+
Positions and sizes are millimetres; Boolean Blend uses the conversion in geometry-nodes-reference. Coordinates are right-handed, +X right, +Y up, +Z forward. Rotations are degrees, intrinsic X-Z-Y.
|
|
30
|
+
On compiler errors, repair the source in this same conversation and retry with a fresh read. A rejected write made no change. Committed/unknown effects remain in execution receipts even if a later call fails; inspect before retrying.
|
|
31
31
|
After applying, read the returned object and use the scene screenshot API with emitImage to inspect the applied result.`,
|
|
32
32
|
declarations: WOMP_GEOMETRY_DECLARATIONS,
|
|
33
33
|
examples: [
|
|
@@ -14,20 +14,20 @@ export function createMaterialsCapability(host, captures) {
|
|
|
14
14
|
name: "procedural-materials",
|
|
15
15
|
version: "15",
|
|
16
16
|
description: "Discover live texture resources, build procedural shader layers, edit material constants, clone and assign materials.",
|
|
17
|
-
instructions: `Use womp.materials for procedural surface appearance.
|
|
18
|
-
recipe() builds editable grid, noise, grain and woven starting graphs and resolves live operation selectors internally. Use it directly for these patterns; no preliminary descriptor dump is needed. It does not write or assign a material. featureSize is in object-coordinate units for coordinateSpace:'object', or physical millimeters for 'world'; vector components use graph axes. Object mapping follows the owning carrier and its transforms; world mapping stays fixed in the scene. Grid includes major cells at featureSize and minor cells at one tenth of it, with lower minor contrast. colorA is the base and colorB the grid-line color. Use shallow relief for probes.
|
|
19
|
-
field() builds terminal-free noise, grain, woven, grid or cellular structures. It shares the recipe coordinate/feature-size convention; rotation is in degrees. cellMode selects a cellular pattern or boundary mask; remap controls contrast and can invert a mask. compose() merges these fields or arbitrary custom {graph,source:{node,slot}} fields, wiring independent color, roughness and combined height/bump weights. Color/roughness weights are nonnegative and sum to at most 1; fields must be normalized to 0..1. Relief weights may be signed. This is generic construction, not a finished material preset. Keep broad variation, defining shapes and fine roughness/relief separate. Independent feature colors use shape palette and blend; compose preserves their RGB with color low zero, high one, and weight one. Scalar channels consume X.
|
|
20
|
-
Coordinate axes are known: scene.access public X corresponds to negative renderer Z, public Y to renderer Y, public Z to negative renderer X. World-space featureSize uses renderer axes in physical mm: for grain running along public X, make Z the largest feature-size component, such as {x:1,y:1,z:40}; public Z uses the largest X component. These are orientation examples; choose sizes from the target. Object-space coordinates additionally follow carrier transforms and primitive/mesh mapping. A symmetric grid measures spacing/stretch but cannot establish axis identity. Use the known basis plus carrier transforms, or physical world mapping when local axis identity is uncertain; world mapping remains scene anchored.
|
|
21
|
-
${MATERIALS_STRUCTURE_GUIDANCE}
|
|
22
|
-
Composed controls are named Constants. Palette colors, roughness endpoints, metalness and overall relief are exposed for editing; structural weights and centering stay internal. SDK write results include parameters with current referenceId, value and color; print only the controls relevant to refinement. Change those through constants without rebuilding the graph, preserving the counterpart value/color. Use the new handles after resets/replacements; when a host omits parameters, recover them from read().
|
|
23
|
-
${MATERIAL_RECIPE_RELIEF_GUIDANCE}
|
|
24
|
-
${MATERIALS_QUALITY_GUIDANCE}
|
|
25
|
-
For custom operations, use exact names from catalog().operations; request catalog({operations:[...]}) only for descriptors not already known in this conversation. Request includeTextures:true when you need texture resource names. Request refreshTextures:true after adding/replacing texture resources or switching resource context. Never infer availability from files on the agent's machine or persist catalogue indices as texture identity.
|
|
26
|
-
read() lists materials and direct users; read(id) returns native base data and graph layers. Existing material edits affect every reference, including Geometry Nodes/OpenSCAD references not listed as direct users. To isolate an edit, use cloneFrom with objectIds. Shader storage shared with another material is copied automatically.
|
|
27
|
-
Use node() to construct literals and encode ramps, texture names and option payloads. For custom nodes, discover unfamiliar descriptors before connecting output slots to input slots. OutputMaterial needs connections; its unconnected ports inherit the incoming material.
|
|
28
|
-
write replaces the full layers array when supplied. Preserve unrelated layers, nodes and references from read. Omit layers for assignment/constant-only edits. When rebuilding an existing graph, carry forward intended Constant overrides; omitted overrides reset with replacement nodes, including finish controls. constants entries require both value and color; preserve the counterpart when editing one. A null entry resets a Constant and returns its new reference ID: read again before further edits.
|
|
29
|
-
Base properties and names remain available through scene.access; material names have a 63-character limit. Procedural colors, including recipe palettes, Constant colors and ramp stops, are linear RGB in 0..1. Decode display RGB before supplying it; for this renderer's gamma-2.2 base colors use (channel/255)**2.2. Do not apply this conversion to roughness, metalness, height or normals, or to colors already linear. Graph coordinates use native conventions, not scene.access's mm convenience API.
|
|
30
|
-
Writes use the host's protected scene access, persistence, permissions and receipts. Read current layers before replacing them. After an uncertain commit/save, inspect receipts and scene state; do not replay blindly.
|
|
17
|
+
instructions: `Use womp.materials for procedural surface appearance.
|
|
18
|
+
recipe() builds editable grid, noise, grain and woven starting graphs and resolves live operation selectors internally. Use it directly for these patterns; no preliminary descriptor dump is needed. It does not write or assign a material. featureSize is in object-coordinate units for coordinateSpace:'object', or physical millimeters for 'world'; vector components use graph axes. Object mapping follows the owning carrier and its transforms; world mapping stays fixed in the scene. Grid includes major cells at featureSize and minor cells at one tenth of it, with lower minor contrast. colorA is the base and colorB the grid-line color. Use shallow relief for probes.
|
|
19
|
+
field() builds terminal-free noise, grain, woven, grid or cellular structures. It shares the recipe coordinate/feature-size convention; rotation is in degrees. cellMode selects a cellular pattern or boundary mask; remap controls contrast and can invert a mask. compose() merges these fields or arbitrary custom {graph,source:{node,slot}} fields, wiring independent color, roughness and combined height/bump weights. Color/roughness weights are nonnegative and sum to at most 1; fields must be normalized to 0..1. Relief weights may be signed. This is generic construction, not a finished material preset. Keep broad variation, defining shapes and fine roughness/relief separate. Independent feature colors use shape palette and blend; compose preserves their RGB with color low zero, high one, and weight one. Scalar channels consume X.
|
|
20
|
+
Coordinate axes are known: scene.access public X corresponds to negative renderer Z, public Y to renderer Y, public Z to negative renderer X. World-space featureSize uses renderer axes in physical mm: for grain running along public X, make Z the largest feature-size component, such as {x:1,y:1,z:40}; public Z uses the largest X component. These are orientation examples; choose sizes from the target. Object-space coordinates additionally follow carrier transforms and primitive/mesh mapping. A symmetric grid measures spacing/stretch but cannot establish axis identity. Use the known basis plus carrier transforms, or physical world mapping when local axis identity is uncertain; world mapping remains scene anchored.
|
|
21
|
+
${MATERIALS_STRUCTURE_GUIDANCE}
|
|
22
|
+
Composed controls are named Constants. Palette colors, roughness endpoints, metalness and overall relief are exposed for editing; structural weights and centering stay internal. SDK write results include parameters with current referenceId, value and color; print only the controls relevant to refinement. Change those through constants without rebuilding the graph, preserving the counterpart value/color. Use the new handles after resets/replacements; when a host omits parameters, recover them from read().
|
|
23
|
+
${MATERIAL_RECIPE_RELIEF_GUIDANCE}
|
|
24
|
+
${MATERIALS_QUALITY_GUIDANCE}
|
|
25
|
+
For custom operations, use exact names from catalog().operations; request catalog({operations:[...]}) only for descriptors not already known in this conversation. Request includeTextures:true when you need texture resource names. Request refreshTextures:true after adding/replacing texture resources or switching resource context. Never infer availability from files on the agent's machine or persist catalogue indices as texture identity.
|
|
26
|
+
read() lists materials and direct users; read(id) returns native base data and graph layers. Existing material edits affect every reference, including Geometry Nodes/OpenSCAD references not listed as direct users. To isolate an edit, use cloneFrom with objectIds. Shader storage shared with another material is copied automatically.
|
|
27
|
+
Use node() to construct literals and encode ramps, texture names and option payloads. For custom nodes, discover unfamiliar descriptors before connecting output slots to input slots. OutputMaterial needs connections; its unconnected ports inherit the incoming material.
|
|
28
|
+
write replaces the full layers array when supplied. Preserve unrelated layers, nodes and references from read. Omit layers for assignment/constant-only edits. When rebuilding an existing graph, carry forward intended Constant overrides; omitted overrides reset with replacement nodes, including finish controls. constants entries require both value and color; preserve the counterpart when editing one. A null entry resets a Constant and returns its new reference ID: read again before further edits.
|
|
29
|
+
Base properties and names remain available through scene.access; material names have a 63-character limit. Procedural colors, including recipe palettes, Constant colors and ramp stops, are linear RGB in 0..1. Decode display RGB before supplying it; for this renderer's gamma-2.2 base colors use (channel/255)**2.2. Do not apply this conversion to roughness, metalness, height or normals, or to colors already linear. Graph coordinates use native conventions, not scene.access's mm convenience API.
|
|
30
|
+
Writes use the host's protected scene access, persistence, permissions and receipts. Read current layers before replacing them. After an uncertain commit/save, inspect receipts and scene state; do not replay blindly.
|
|
31
31
|
status() reports independent global compiler-queue and pending-resource observations. A null field means its query failed; errors identifies the source. Per-shader diagnostics are unavailable. Catalogue names do not establish texture channels, color space or normal convention; choose decoding only when the resource encoding is known. Applied means committed. When inspect is available, use inspect({views:[...]}) after writes; it primes registration once, polls status without screenshot loops and captures the angles sequentially. Emit every returned frame within the same execution for joint comparison. queueSettled describes observations, not visual correctness; Report pending/unavailable observations honestly; fewer frames than requested means inspection is incomplete. When inspect is unavailable, prime with a screenshot, then observe status and capture the needed angles.`,
|
|
32
32
|
declarations: WOMP_MATERIALS_DECLARATIONS,
|
|
33
33
|
examples: [
|
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
export const OPENSCAD_INSTRUCTIONS = `Use womp.openscad to create or update one adjustable CAD part. Read an existing part before editing it.
|
|
2
|
-
Keep follow-up edits to an existing CAD part in that part unless the user asks to change representation.
|
|
1
|
+
export const OPENSCAD_INSTRUCTIONS = `Use womp.openscad to create or update one adjustable CAD part. Read an existing part before editing it.
|
|
2
|
+
Keep follow-up edits to an existing CAD part in that part unless the user asks to change representation.
|
|
3
3
|
read(0) returns the root container for creation. read(id) returns an existing
|
|
4
|
-
part or container. Use the scene-editing skill when available to discover other scene objects.
|
|
5
|
-
create requires a parentId (0 means the scene root); update requires the existing objectId.
|
|
6
|
-
Both methods compile the effective source and parameters, apply one undoable change, and check wall thickness.
|
|
4
|
+
part or container. Use the scene-editing skill when available to discover other scene objects.
|
|
5
|
+
create requires a parentId (0 means the scene root); update requires the existing objectId.
|
|
6
|
+
Both methods compile the effective source and parameters, apply one undoable change, and check wall thickness.
|
|
7
7
|
Preserve intervening user changes. Ask the user when the intended edits conflict or remain ambiguous.
|
|
8
8
|
Existing overrides are preserved for parameters still present;
|
|
9
|
-
params supplies overrides to merge into the current values. Keep top-level parameter names stable.
|
|
10
|
-
Return complete source on every update and preserve unrelated geometry. Never create a replacement to repair a part.
|
|
11
|
-
Compile errors return status rejected without applying. Creation requires validated placement bounds.
|
|
12
|
-
An update may apply with validation unavailable; that is a diagnostic, not proof that it compiles.
|
|
13
|
-
An applied part remains in the scene even if thickness fails or is unavailable. Use the returned objectId for repairs.
|
|
14
|
-
When thickness fails, increase thin walls and fragile connections while preserving requested dimensions and openings.
|
|
15
|
-
At most four applied printability repairs are allowed per logical build/edit in this run. Respect the returned limit;
|
|
16
|
-
do not bypass it by recreating the part. Unavailable thickness is not a pass. Report unresolved issues honestly.
|
|
17
|
-
Build connected geometry and avoid features below 1.4 mm. OpenSCAD source dimensions use millimetres,
|
|
18
|
-
independently of the scene display unit. New parts are centered and grounded from compiled bounds.
|
|
19
|
-
OpenSCAD is Z-up; the scene-editing API is Y-up. For an unrotated part at its creation-time scale and an identity parent transform,
|
|
20
|
-
a source point [x,y,z] mm maps to scene {x:y, y:z, z:-x} times the scene.unitMultiplier inside the access callback, plus nodeGetPos(partId).
|
|
21
|
-
Account for the part and parent transforms when they are not identity; source coordinates are not scene coordinates.
|
|
22
|
-
After a major change, use the scene screenshot API and emitImage to inspect the applied result.
|
|
23
|
-
A committed effect can survive later failure. Inspect receipts and the existing part before retrying.
|
|
24
|
-
Do not print source in the final answer unless requested; describe the result and any unresolved diagnostics.
|
|
25
|
-
|
|
26
|
-
RUNTIME
|
|
27
|
-
- Produce a single OpenSCAD file.
|
|
28
|
-
- Do not use imports, includes, external libraries, text, hull, minkowski, polyhedron, or extrudes.
|
|
29
|
-
- Prefer cube, sphere, cylinder, translate, rotate, color, union, difference, and intersection.
|
|
30
|
-
- Do not use $fn, $fa, or $fs; tessellation controls are ignored.
|
|
31
|
-
- Build faceted geometry by combining supported primitives with CSG operations and transforms.
|
|
32
|
-
- Do not use scale(); size primitives directly instead.
|
|
33
|
-
- Use color("#rrggbb") around distinct visible parts.
|
|
34
|
-
- Keep the model centered near the origin and use millimeter-like units consistently.
|
|
35
|
-
- For organic blending, use goop(radius_mm) { ... }. The radius is in millimetres before parent scaling, matching scene-editing; do not multiply by project unitMultiplier. It is ordered: the first child seeds with blend 0, and each later child uses radius_mm.
|
|
36
|
-
- Put core primitives first. Avoid wrapping goop children in union() when they should blend individually.
|
|
37
|
-
|
|
38
|
-
SCREWS AND THREADS
|
|
39
|
-
- Copy helical_ridge below for rounded screw threads, necks, caps, and coils. It joins spheres with cylinders; never use stacked rings or disconnected spheres.
|
|
40
|
-
- Dimensions are mm: radius = ridge centerline radius; pitch = axial travel/turn; tube_radius = half ridge thickness; handedness = 1 (right) or -1 (left).
|
|
41
|
-
- For a hand control use thread_hand = 1; // [-1:Left, 1:Right] and pass it directly. Ternary expressions (?:) are unsupported.
|
|
42
|
-
- Embed the ridge in the shaft/neck (e.g. radius = shaft_radius). Hollow fittings: subtract the bore from body + thread. Internal threads: subtract the helix from the socket wall with mating clearance. Mating parts need matching pitch and handedness.
|
|
43
|
-
- Expose diameter, pitch, thread length, and thickness in Customizer; derive sampling. Keep 2 * tube_radius < pitch and allow tube_radius beyond both path endpoints for rounded ends. This is a rounded profile, not ISO/metric V-thread; never claim standards compliance.
|
|
44
|
-
- Build heads/grips separately using cylinders, tapered cylinders, and radial ribs/cutters, not thread samples.
|
|
45
|
-
|
|
46
|
-
module helical_ridge(radius, pitch, turns, tube_radius, handedness) {
|
|
47
|
-
if (radius > 0 && pitch > 0 && turns > 0 && tube_radius > 0) {
|
|
48
|
-
// Limit chord error to 0.03 mm or 3% of the ridge radius.
|
|
49
|
-
chord_error = min(0.03, tube_radius * 0.03);
|
|
50
|
-
steps_per_turn = max(48, ceil(180 / acos(1 - min(1, chord_error / radius))));
|
|
51
|
-
steps = ceil(turns * steps_per_turn);
|
|
52
|
-
for (i = [0:steps]) {
|
|
53
|
-
angle = handedness * 360 * turns * i / steps;
|
|
54
|
-
p = [radius * cos(angle), radius * sin(angle), pitch * turns * i / steps];
|
|
55
|
-
translate(p) sphere(r = tube_radius);
|
|
56
|
-
if (i < steps) {
|
|
57
|
-
next_angle = handedness * 360 * turns * (i + 1) / steps;
|
|
58
|
-
dx = radius * cos(next_angle) - p[0];
|
|
59
|
-
dy = radius * sin(next_angle) - p[1];
|
|
60
|
-
dz = pitch * turns / steps;
|
|
61
|
-
segment_length = sqrt(dx * dx + dy * dy + dz * dz);
|
|
62
|
-
translate(p)
|
|
63
|
-
rotate([0, acos(dz / segment_length), atan2(dy, dx)])
|
|
64
|
-
cylinder(h = segment_length, r = tube_radius);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
PARAMETERS
|
|
71
|
-
- Make important top-level constants user-adjustable with OpenSCAD Customizer comments:
|
|
72
|
-
- Numbers with ranges: body_radius = 12; // [4:0.5:40]
|
|
73
|
-
- Counts: leg_count = 4; // [2:8]
|
|
74
|
-
- Booleans: show_base = true;
|
|
75
|
-
- Options: style = "round"; // [round, sharp, chunky]
|
|
76
|
-
- Prefer stable size/count/ratio/style controls; keep fragile dependent dimensions as local expressions.
|
|
77
|
-
|
|
78
|
-
SUPPORTED SUBSET
|
|
79
|
-
- Export is best-effort from a supported CSG subset: cube, sphere, cylinder, translate, rotate, color, union, difference, intersection, and goop.
|
|
80
|
-
- difference() cutters become subtractive primitives, so use clean cutters that fully pass through the target solid.
|
|
9
|
+
params supplies overrides to merge into the current values. Keep top-level parameter names stable.
|
|
10
|
+
Return complete source on every update and preserve unrelated geometry. Never create a replacement to repair a part.
|
|
11
|
+
Compile errors return status rejected without applying. Creation requires validated placement bounds.
|
|
12
|
+
An update may apply with validation unavailable; that is a diagnostic, not proof that it compiles.
|
|
13
|
+
An applied part remains in the scene even if thickness fails or is unavailable. Use the returned objectId for repairs.
|
|
14
|
+
When thickness fails, increase thin walls and fragile connections while preserving requested dimensions and openings.
|
|
15
|
+
At most four applied printability repairs are allowed per logical build/edit in this run. Respect the returned limit;
|
|
16
|
+
do not bypass it by recreating the part. Unavailable thickness is not a pass. Report unresolved issues honestly.
|
|
17
|
+
Build connected geometry and avoid features below 1.4 mm. OpenSCAD source dimensions use millimetres,
|
|
18
|
+
independently of the scene display unit. New parts are centered and grounded from compiled bounds.
|
|
19
|
+
OpenSCAD is Z-up; the scene-editing API is Y-up. For an unrotated part at its creation-time scale and an identity parent transform,
|
|
20
|
+
a source point [x,y,z] mm maps to scene {x:y, y:z, z:-x} times the scene.unitMultiplier inside the access callback, plus nodeGetPos(partId).
|
|
21
|
+
Account for the part and parent transforms when they are not identity; source coordinates are not scene coordinates.
|
|
22
|
+
After a major change, use the scene screenshot API and emitImage to inspect the applied result.
|
|
23
|
+
A committed effect can survive later failure. Inspect receipts and the existing part before retrying.
|
|
24
|
+
Do not print source in the final answer unless requested; describe the result and any unresolved diagnostics.
|
|
25
|
+
|
|
26
|
+
RUNTIME
|
|
27
|
+
- Produce a single OpenSCAD file.
|
|
28
|
+
- Do not use imports, includes, external libraries, text, hull, minkowski, polyhedron, or extrudes.
|
|
29
|
+
- Prefer cube, sphere, cylinder, translate, rotate, color, union, difference, and intersection.
|
|
30
|
+
- Do not use $fn, $fa, or $fs; tessellation controls are ignored.
|
|
31
|
+
- Build faceted geometry by combining supported primitives with CSG operations and transforms.
|
|
32
|
+
- Do not use scale(); size primitives directly instead.
|
|
33
|
+
- Use color("#rrggbb") around distinct visible parts.
|
|
34
|
+
- Keep the model centered near the origin and use millimeter-like units consistently.
|
|
35
|
+
- For organic blending, use goop(radius_mm) { ... }. The radius is in millimetres before parent scaling, matching scene-editing; do not multiply by project unitMultiplier. It is ordered: the first child seeds with blend 0, and each later child uses radius_mm.
|
|
36
|
+
- Put core primitives first. Avoid wrapping goop children in union() when they should blend individually.
|
|
37
|
+
|
|
38
|
+
SCREWS AND THREADS
|
|
39
|
+
- Copy helical_ridge below for rounded screw threads, necks, caps, and coils. It joins spheres with cylinders; never use stacked rings or disconnected spheres.
|
|
40
|
+
- Dimensions are mm: radius = ridge centerline radius; pitch = axial travel/turn; tube_radius = half ridge thickness; handedness = 1 (right) or -1 (left).
|
|
41
|
+
- For a hand control use thread_hand = 1; // [-1:Left, 1:Right] and pass it directly. Ternary expressions (?:) are unsupported.
|
|
42
|
+
- Embed the ridge in the shaft/neck (e.g. radius = shaft_radius). Hollow fittings: subtract the bore from body + thread. Internal threads: subtract the helix from the socket wall with mating clearance. Mating parts need matching pitch and handedness.
|
|
43
|
+
- Expose diameter, pitch, thread length, and thickness in Customizer; derive sampling. Keep 2 * tube_radius < pitch and allow tube_radius beyond both path endpoints for rounded ends. This is a rounded profile, not ISO/metric V-thread; never claim standards compliance.
|
|
44
|
+
- Build heads/grips separately using cylinders, tapered cylinders, and radial ribs/cutters, not thread samples.
|
|
45
|
+
|
|
46
|
+
module helical_ridge(radius, pitch, turns, tube_radius, handedness) {
|
|
47
|
+
if (radius > 0 && pitch > 0 && turns > 0 && tube_radius > 0) {
|
|
48
|
+
// Limit chord error to 0.03 mm or 3% of the ridge radius.
|
|
49
|
+
chord_error = min(0.03, tube_radius * 0.03);
|
|
50
|
+
steps_per_turn = max(48, ceil(180 / acos(1 - min(1, chord_error / radius))));
|
|
51
|
+
steps = ceil(turns * steps_per_turn);
|
|
52
|
+
for (i = [0:steps]) {
|
|
53
|
+
angle = handedness * 360 * turns * i / steps;
|
|
54
|
+
p = [radius * cos(angle), radius * sin(angle), pitch * turns * i / steps];
|
|
55
|
+
translate(p) sphere(r = tube_radius);
|
|
56
|
+
if (i < steps) {
|
|
57
|
+
next_angle = handedness * 360 * turns * (i + 1) / steps;
|
|
58
|
+
dx = radius * cos(next_angle) - p[0];
|
|
59
|
+
dy = radius * sin(next_angle) - p[1];
|
|
60
|
+
dz = pitch * turns / steps;
|
|
61
|
+
segment_length = sqrt(dx * dx + dy * dy + dz * dz);
|
|
62
|
+
translate(p)
|
|
63
|
+
rotate([0, acos(dz / segment_length), atan2(dy, dx)])
|
|
64
|
+
cylinder(h = segment_length, r = tube_radius);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
PARAMETERS
|
|
71
|
+
- Make important top-level constants user-adjustable with OpenSCAD Customizer comments:
|
|
72
|
+
- Numbers with ranges: body_radius = 12; // [4:0.5:40]
|
|
73
|
+
- Counts: leg_count = 4; // [2:8]
|
|
74
|
+
- Booleans: show_base = true;
|
|
75
|
+
- Options: style = "round"; // [round, sharp, chunky]
|
|
76
|
+
- Prefer stable size/count/ratio/style controls; keep fragile dependent dimensions as local expressions.
|
|
77
|
+
|
|
78
|
+
SUPPORTED SUBSET
|
|
79
|
+
- Export is best-effort from a supported CSG subset: cube, sphere, cylinder, translate, rotate, color, union, difference, intersection, and goop.
|
|
80
|
+
- difference() cutters become subtractive primitives, so use clean cutters that fully pass through the target solid.
|
|
81
81
|
- Unsupported OpenSCAD may validate but can warn or skip during export; prefer the supported subset above.`;
|
|
@@ -79,39 +79,39 @@ export function createOperationsCapability(host) {
|
|
|
79
79
|
name: "generation",
|
|
80
80
|
version: "7",
|
|
81
81
|
description: "Generate or edit images and meshes, inspect jobs, and wait for their outputs.",
|
|
82
|
-
instructions: `Read operations.catalog() for the currently available models and selected defaults.
|
|
82
|
+
instructions: `Read operations.catalog() for the currently available models and selected defaults.
|
|
83
83
|
Use generation for requested inline image/mesh results, not unsolicited previews of scene creations.
|
|
84
84
|
For generation explicitly requested through Flow/nodes or continued edits to that Flow workflow, use flow-graphs
|
|
85
85
|
unless the user asks for an inline result. If Flow is unavailable or closed, explain the prerequisite; do not substitute inline generation.
|
|
86
|
-
Use generate() for image/mesh generation, image editing, upscaling, or background removal. Model IDs must
|
|
87
|
-
come from the catalog and support the requested operation. An unavailable or restricted explicit choice
|
|
88
|
-
is an error; do not silently substitute another model. Omit model IDs to use the user's defaults.
|
|
89
|
-
For text-to-mesh, imageParams configures the image step and params configures the mesh step. For
|
|
90
|
-
image-to-mesh, an optional prompt requests an image-editing step first. Advanced params are JSON
|
|
91
|
-
values passed to the existing service; use documented options and do not invent provider settings.
|
|
92
|
-
Author the full text-to-mesh image prompt yourself, preserving the user's requested subject and details.
|
|
93
|
-
For a standalone object, describe a clear three-quarter view with a plain background and even lighting
|
|
94
|
-
unless the user requests otherwise. Do not rely on another model to expand or rewrite this prompt.
|
|
95
|
-
Inputs are image references returned by the available assets API or a previous completed operation.
|
|
96
|
-
Do not fabricate references, replace a missing input with another image, or pass arbitrary URLs.
|
|
97
|
-
Assign a short, distinct key to each intended job. Reuse that key when checking the same submission;
|
|
98
|
-
never choose a new key merely to retry a timeout or unknown outcome. A key cannot identify two
|
|
99
|
-
different requests. Host receipts and saved message IDs record submitted effects.
|
|
100
|
-
generate() waits for cost confirmation and then for completion, failure, or cancellation.
|
|
101
|
-
Use exactly one generation per execute_javascript call and print its result. Put follow-up work in
|
|
102
|
-
another call. Do not poll, start background work, use Promise.all, or report success before completion.
|
|
103
|
-
The host waits without consuming model steps. Stop ends agent execution; the host handles cancellation of its accepted jobs.
|
|
104
|
-
Refresh recovery reconnects the same job. It never resumes the interrupted JavaScript stack.
|
|
105
|
-
Read the recovered status and artifact references, then continue any requested remaining work in a new call; do not repeat generation.
|
|
106
|
-
generate() returns the terminal status and job.outputs artifact references directly, including after recovery.
|
|
107
|
-
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.
|
|
86
|
+
Use generate() for image/mesh generation, image editing, upscaling, or background removal. Model IDs must
|
|
87
|
+
come from the catalog and support the requested operation. An unavailable or restricted explicit choice
|
|
88
|
+
is an error; do not silently substitute another model. Omit model IDs to use the user's defaults.
|
|
89
|
+
For text-to-mesh, imageParams configures the image step and params configures the mesh step. For
|
|
90
|
+
image-to-mesh, an optional prompt requests an image-editing step first. Advanced params are JSON
|
|
91
|
+
values passed to the existing service; use documented options and do not invent provider settings.
|
|
92
|
+
Author the full text-to-mesh image prompt yourself, preserving the user's requested subject and details.
|
|
93
|
+
For a standalone object, describe a clear three-quarter view with a plain background and even lighting
|
|
94
|
+
unless the user requests otherwise. Do not rely on another model to expand or rewrite this prompt.
|
|
95
|
+
Inputs are image references returned by the available assets API or a previous completed operation.
|
|
96
|
+
Do not fabricate references, replace a missing input with another image, or pass arbitrary URLs.
|
|
97
|
+
Assign a short, distinct key to each intended job. Reuse that key when checking the same submission;
|
|
98
|
+
never choose a new key merely to retry a timeout or unknown outcome. A key cannot identify two
|
|
99
|
+
different requests. Host receipts and saved message IDs record submitted effects.
|
|
100
|
+
generate() waits for cost confirmation and then for completion, failure, or cancellation.
|
|
101
|
+
Use exactly one generation per execute_javascript call and print its result. Put follow-up work in
|
|
102
|
+
another call. Do not poll, start background work, use Promise.all, or report success before completion.
|
|
103
|
+
The host waits without consuming model steps. Stop ends agent execution; the host handles cancellation of its accepted jobs.
|
|
104
|
+
Refresh recovery reconnects the same job. It never resumes the interrupted JavaScript stack.
|
|
105
|
+
Read the recovered status and artifact references, then continue any requested remaining work in a new call; do not repeat generation.
|
|
106
|
+
generate() returns the terminal status and job.outputs artifact references directly, including after recovery.
|
|
107
|
+
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.
|
|
108
108
|
A brief, relevant follow-up suggestion is optional after completion. Offer only available actions; wait for a clear choice
|
|
109
109
|
and required confirmations before acting. Clarify ambiguous acceptance of multiple options. Reuse returned artifacts for the chosen follow-up.
|
|
110
|
-
Inspect images deliberately when needed for verification or reference comparisons; never claim visual verification without inspecting the image.
|
|
111
|
-
Omitted image bytes are expected; missing artifact references are not. If a completed job has no outputs, recover the references or report the missing result.
|
|
112
|
-
Reserve list() for older jobs or missing references. Follow nextFrom even when a page has no jobs.
|
|
113
|
-
Verify an uncertain submission's job status, never issue a replacement request.
|
|
114
|
-
Only returned outputs exist. Generating a mesh does not import it into the scene. Use the assets skill for requested imports.
|
|
110
|
+
Inspect images deliberately when needed for verification or reference comparisons; never claim visual verification without inspecting the image.
|
|
111
|
+
Omitted image bytes are expected; missing artifact references are not. If a completed job has no outputs, recover the references or report the missing result.
|
|
112
|
+
Reserve list() for older jobs or missing references. Follow nextFrom even when a page has no jobs.
|
|
113
|
+
Verify an uncertain submission's job status, never issue a replacement request.
|
|
114
|
+
Only returned outputs exist. Generating a mesh does not import it into the scene. Use the assets skill for requested imports.
|
|
115
115
|
Never resume stopped work or resubmit a job because cancellation or submission is uncertain.`,
|
|
116
116
|
declarations: WOMP_OPERATIONS_DECLARATIONS,
|
|
117
117
|
examples: [
|