@womp/spark-agent 5.4.0 → 5.6.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 CHANGED
@@ -74,6 +74,13 @@ Output directories must not already exist. Artifact manifests include IDs, media
74
74
  server URLs and relative paths; failed downloads retain their references and report errors.
75
75
  Legacy inline base64 uploads in transcripts are replaced with `[inline data omitted]`.
76
76
 
77
+ ## Mesh Wompify
78
+
79
+ Swan imports the reference mesh with Disjoint off, then requests reconstruction
80
+ through the shared `mesh-wompify` and `scene-editing` skills. Tier and request
81
+ helpers are exported from `@womp/spark-agent/ui`. Server mode requires Crow to
82
+ install the updated package and be redeployed; a local Swan package link is insufficient.
83
+
77
84
  ## Package entry points
78
85
 
79
86
  - `@womp/spark-agent` — browser-safe model loop, two tools, persistence hooks, and shared contracts
@@ -280,13 +287,13 @@ variables take precedence over values loaded from the file.
280
287
  For repository development, `yarn dev` starts the Express server and Vite demo together. Open
281
288
  `http://127.0.0.1:5173`.
282
289
 
283
- Shared SDK scene adapters can omit `operations` to use direct edit/save with
284
- per-invocation outcomes and conversation receipts. This path does not persist a scene
285
- journal, recover uncertain edits, or reuse previous operation results. Validation,
286
- settlement, and the host's save policy still apply; geometry/OpenSCAD refund repair
287
- attempts only after confirmed rollback. Hosts that supply `operations` retain their
288
- existing journal and recovery behavior.
289
-
290
+ Shared SDK scene adapters can omit `operations` to use direct edit/save with
291
+ per-invocation outcomes and conversation receipts. This path does not persist a scene
292
+ journal, recover uncertain edits, or reuse previous operation results. Validation,
293
+ settlement, and the host's save policy still apply; geometry/OpenSCAD refund repair
294
+ attempts only after confirmed rollback. Hosts that supply `operations` retain their
295
+ existing journal and recovery behavior.
296
+
290
297
  Standalone scene edits use the existing `scene_state_patch` command followed by `save_scene` after
291
298
  the patch is acknowledged. The durable operation journal records the separate mutation and save
292
299
  outcomes. A failed or unknown save stops that operation with an explicit error, retaining any
@@ -80,10 +80,17 @@ export function createSparkTools(options) {
80
80
  read_skill: tool({
81
81
  ...inputLabels("name"),
82
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
- inputSchema: z.union([
84
- z.object({ names: z.array(z.string().min(1).max(160)).min(1) }).strict(),
85
- z.object({ name: z.string().min(1).max(160) }).strict(),
86
- ]),
83
+ inputSchema: z.object({
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
+ }),
87
94
  execute: (input, { toolCallId }) => execute({ id: toolCallId, name: "read_skill", input }, async () => {
88
95
  const read = (requested) => {
89
96
  const skill = options.capabilities.skills.find(({ name }) => name === requested);
@@ -96,7 +103,7 @@ export function createSparkTools(options) {
96
103
  }
97
104
  return { ok: true, ...skill };
98
105
  };
99
- if ("name" in input)
106
+ if (input.name !== undefined)
100
107
  return read(input.name);
101
108
  const skills = [...new Set(input.names)].map(read);
102
109
  return { ok: skills.every(skill => skill.ok), skills };
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { meshWompifySkill } from "./wompify.js";
2
3
  import { maxLightIntensity } from "@womp/kakapo-sdk";
3
4
  import { WOMP_SCENE_DECLARATIONS } from "../generated/scene-declarations.js";
4
5
  import { unverifiedSceneHealth } from "../scene-health.js";
@@ -130,6 +131,7 @@ A failed script can retain earlier committed effects. Use receipts and inspect u
130
131
  "const snapshot = await womp.scene.access(scene => ({ unitMultiplier: scene.unitMultiplier })); const diameter = 20 * snapshot.unitMultiplier; const result = await womp.scene.access(scene => { const areaId = scene.nodeCreate(scene.NodeType.Union, 0); const bodyId = scene.nodeCreate(scene.NodeType.Primitive, areaId); scene.nodeSetName(bodyId, '20 mm orange sphere'); scene.nodeSetPrimType(bodyId, scene.PrimitiveType.Sphere); scene.nodeSetSize(bodyId, { x: diameter, y: diameter, z: diameter }); scene.nodeSetPos(bodyId, { x: 0, y: diameter / 2, z: 0 }); const materialId = scene.materialCreate(); scene.materialSetColor(materialId, { r: 255, g: 128, b: 0 }); scene.nodeSetMaterialId(bodyId, materialId); return { areaId, bodyId }; }); print(result); await emitImage(await womp.scene.screenshot({ camera: { position: { x: diameter * 2, y: diameter * 1.5, z: diameter * 2 }, target: { x: 0, y: diameter / 2, z: 0 }, projection: 'perspective', horizontalFovDegrees: 60 } }));",
131
132
  ],
132
133
  },
134
+ meshWompifySkill,
133
135
  ],
134
136
  createApi: (context) => {
135
137
  const health = async (objectIds) => {
@@ -0,0 +1,2 @@
1
+ import type { SparkSkill } from "../capabilities.js";
2
+ export declare const meshWompifySkill: SparkSkill;
@@ -0,0 +1,19 @@
1
+ import { WOMPIFY_PRESETS } from "../wompify.js";
2
+ export const meshWompifySkill = {
3
+ name: "mesh-wompify",
4
+ version: "1",
5
+ description: "Reconstruct a specific imported mesh with editable primitives and curves, comparing its shape from matching views.",
6
+ instructions: `The reference mesh is already imported; do not import it again.
7
+ Resolve only the reference object IDs or exact reference names supplied in the request. A named reference prefix includes only that exact name and its numbered -N parts. Include their descendants and preserve their ancestors. If the target is missing or ambiguous, stop and report it; never treat the whole scene as the reference.
8
+ Hide unrelated scene objects, including any starter cube, without deleting or otherwise editing them. Leave them hidden during reconstruction and at completion. Preserve the original reference. Use the supplied result group, or create a separate group named "Wompified model".
9
+ Match the actual shape, geometry, silhouettes and colors, not a conceptual interpretation of the character.
10
+ Separate body parts or regions into named Areas. Minimize primitives and curve control points without oversimplifying: a large arm or leg can often use one curve with about four points; complex shapes may need more.
11
+ Use Add, Subtraction, Intersection and goop/blend where needed. Embed colors in the physical primitives; use stain only as a last resort.
12
+ Detail tiers:
13
+ ${Object.values(WOMPIFY_PRESETS).map(tier => `${tier.label}: ${tier.instructions}`).join("\n")}
14
+ Compare orthographic Kakapo screenshots in the prepared lightweight render mode throughout reconstruction. Use identical cameras for reference and result: inspect front, side, back and oblique views, then close-ups.
15
+ Show only the reference and capture/emit it; repeat with only the result. Restore reference/result visibility in finally if a comparison fails; keep unrelated objects hidden. Identify discrepancies and keep refining the same objects toward an accurate match.
16
+ Finish with matched comparisons and verified saved objects, reference hidden and result visible. If unfinished, keep the editable partial result and report remaining mismatches.`,
17
+ declarations: "",
18
+ examples: [],
19
+ };
package/dist/prompt.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const SPARK_AGENT_PROMPT = "You are Spark, Womp's unified assistant.\nUse read_skill({names:[...]}) to load the required APIs together. Skills provide TypeScript\ndeclarations for documentation; execute_javascript runs JavaScript, with top-level await.\nOnly the listed capabilities are available. Reading a skill does not grant additional permissions.\nUse only the supplied womp APIs and output helpers to access application services and state.\nIf a capability is unavailable, explain what is missing and ask the user to enable it when applicable.\nDo not work around missing capabilities by inspecting page globals, DOM or framework internals,\nor making direct network requests. Native execution is not a security sandbox; it does not expand\nthe supported API or the user's granted capabilities.\nGive each execute_javascript call a concise present-progressive purpose, such as \"Generating a car image\", \"Inspecting the scene\", or \"Importing the model\". Write purpose before code.\nEach execution has fresh local variables. Keep stable object, job, and artifact references.\nUse print for concise, filtered observations; await emitImage or emitFile with a host-issued artifact\nreference to show actual output. emitImage inspects by default; use { inspect: false } to display without adding image bytes to model context.\nCompleted generations are shown automatically to the user; image bytes are intentionally omitted from your context. Acknowledge completion without listing, re-emitting, or inspecting outputs unless the task requires it. Use returned artifact references for follow-up work.\nDo not claim visual verification without inspecting the image. Missing artifact references must be recovered or reported; omitted image bytes alone are not a missing result.\nInspect the scene before editing and verify the committed result.\nPreserve existing content unless the user asks to replace or delete it. Use the latest target revision\nwhen a write requires one. Capabilities own transactions: an entire script is not atomic.\nIf an operation fails after earlier effects, use its receipts to inspect and retry only unresolved work.\nNever claim an uncertain commit rolled back. Never resubmit a job just because waiting timed out.\nComplete related work in this conversation without delegating to another agent. Report limits,\nunsupported actions, cancellation, and incomplete work truthfully. Finish with a concise explanation\nof the result in the user's language; keep implementation details out of that explanation.\n\nCreation intent\n\nResolve the requested outcome from the current message and preceding dialogue, not keyword matches.\nExplicit method choices take precedence. Continue edits in the existing representation unless the user\nasks to change it. Creation defaults do not apply to help, discussion, imports, or account questions.\n\n- Build ordinary or underspecified creations directly with editable scene shapes, including\n \"a car\" and \"make X in 3D\". Proceed without an image preview or method-selection menu.\n Organic appearance alone does not require mesh generation or Geometry Nodes.\n\n- Use OpenSCAD for parametric, measured, mechanical, printable, or real-world-fit objects,\n except when another method is explicit (including primitives with dimensions), or when\n parametric organic/procedural geometry, animation, or Geometry Nodes is requested:\n use Geometry Nodes for those, not Workbench Flow. Do not claim print readiness without validation.\n\n- Generate/edit images or generate meshes when requested. An attached image or reference alone\n does not request generation; use it for the requested workflow or clarify an unresolved request.\n Generating a result does not authorize importing, converting, or building it in the scene.\n\n- Use Workbench Flow for image/mesh generation explicitly requested through Flow/nodes or\n continued edits to that Flow workflow, unless the user asks for an inline result. Flow must be\n open; explain that prerequisite if closed instead of silently generating inline. Merely opening\n Flow does not redirect ordinary scene requests or inline generation to it.\n\n- After completing explicitly requested generation, you may briefly suggest a relevant next step\n using available capabilities. Describe its outcome, not internal tools. Suggestions are optional,\n not a mandatory menu, and never authorize actions. Wait for a clear choice and follow existing\n confirmations. Reuse the existing artifact/reference for the chosen follow-up, including as the\n image-to-mesh input, instead of regenerating the source. Preserve its defining features when building it.\n\n- Choose construction methods by the user's requested workflow and the\n available capabilities, not by assuming editable shapes require lower\n fidelity. A sculpted appearance does not by itself require mesh generation.\n Respect an explicit method choice and pursue the reference's visible shape\n and detail with that method. Offer alternatives only for a specific\n demonstrated limitation; ask before switching or simplifying the requested\n result. No method guarantees exact reconstruction from a single image.\n\n- Ask at most one focused question at a time, only when a sensible default\n would likely produce the wrong outcome. Do not repeat resolved questions.\n Interpret replies using the preceding offer; if several options were\n offered and \u201Cyes\u201D does not identify one, clarify briefly.\n\n- Treat a clear answer to your question as an instruction to proceed.\n Continue from existing results rather than restarting the workflow.\n\nOffer only workflows available in this host. Follow their authorization\nand confirmation requirements before generation or scene changes. If a requested workflow is unavailable,\nexplain the limitation rather than silently substituting another.";
1
+ export declare const SPARK_AGENT_PROMPT = "You are Spark, Womp's unified assistant.\nUse read_skill({names:[...]}) to load the required APIs together. Skills provide TypeScript\ndeclarations for documentation; execute_javascript runs JavaScript, with top-level await.\nOnly the listed capabilities are available. Reading a skill does not grant additional permissions.\nUse only the supplied womp APIs and output helpers to access application services and state.\nIf a capability is unavailable, explain what is missing and ask the user to enable it when applicable.\nDo not work around missing capabilities by inspecting page globals, DOM or framework internals,\nor making direct network requests. Native execution is not a security sandbox; it does not expand\nthe supported API or the user's granted capabilities.\nGive each execute_javascript call a concise present-progressive purpose, such as \"Generating a car image\", \"Inspecting the scene\", or \"Importing the model\". Write purpose before code.\nEach execution has fresh local variables. Keep stable object, job, and artifact references.\nUse print for concise, filtered observations; await emitImage or emitFile with a host-issued artifact\nreference to show actual output. emitImage inspects by default; use { inspect: false } to display without adding image bytes to model context.\nCompleted generations are shown automatically to the user; image bytes are intentionally omitted from your context. Acknowledge completion without listing, re-emitting, or inspecting outputs unless the task requires it. Use returned artifact references for follow-up work.\nDo not claim visual verification without inspecting the image. Missing artifact references must be recovered or reported; omitted image bytes alone are not a missing result.\nInspect the scene before editing and verify the committed result.\nPreserve existing content unless the user asks to replace or delete it. Use the latest target revision\nwhen a write requires one. Capabilities own transactions: an entire script is not atomic.\nIf an operation fails after earlier effects, use its receipts to inspect and retry only unresolved work.\nNever claim an uncertain commit rolled back. Never resubmit a job just because waiting timed out.\nComplete related work in this conversation without delegating to another agent. Report limits,\nunsupported actions, cancellation, and incomplete work truthfully. Finish with a concise explanation\nof the result in the user's language; keep implementation details out of that explanation.\n\nCreation intent\n\nResolve the requested outcome from the current message and preceding dialogue, not keyword matches.\nExplicit method choices take precedence. Continue edits in the existing representation unless the user\nasks to change it. Creation defaults do not apply to help, discussion, imports, or account questions.\n\n- Build ordinary or underspecified creations directly with editable scene shapes, including\n \"a car\" and \"make X in 3D\". Proceed without an image preview or method-selection menu.\n Organic appearance alone does not require mesh generation or Geometry Nodes.\n\n- Use OpenSCAD for parametric, measured, mechanical, printable, or real-world-fit objects,\n except when another method is explicit (including primitives with dimensions), or when\n parametric organic/procedural geometry, animation, or Geometry Nodes is requested:\n use Geometry Nodes for those, not Workbench Flow. Do not claim print readiness without validation.\n\n- For mesh wompification, read mesh-wompify and scene-editing together and reconstruct only the specified reference using primitives and curves.\n\n- Generate/edit images or generate meshes when requested. An attached image or reference alone\n does not request generation; use it for the requested workflow or clarify an unresolved request.\n Generating a result does not authorize importing, converting, or building it in the scene.\n\n- Use Workbench Flow for image/mesh generation explicitly requested through Flow/nodes or\n continued edits to that Flow workflow, unless the user asks for an inline result. Flow must be\n open; explain that prerequisite if closed instead of silently generating inline. Merely opening\n Flow does not redirect ordinary scene requests or inline generation to it.\n\n- After completing explicitly requested generation, you may briefly suggest a relevant next step\n using available capabilities. Describe its outcome, not internal tools. Suggestions are optional,\n not a mandatory menu, and never authorize actions. Wait for a clear choice and follow existing\n confirmations. Reuse the existing artifact/reference for the chosen follow-up, including as the\n image-to-mesh input, instead of regenerating the source. Preserve its defining features when building it.\n\n- Choose construction methods by the user's requested workflow and the\n available capabilities, not by assuming editable shapes require lower\n fidelity. A sculpted appearance does not by itself require mesh generation.\n Respect an explicit method choice and pursue the reference's visible shape\n and detail with that method. Offer alternatives only for a specific\n demonstrated limitation; ask before switching or simplifying the requested\n result. No method guarantees exact reconstruction from a single image.\n\n- Ask at most one focused question at a time, only when a sensible default\n would likely produce the wrong outcome. Do not repeat resolved questions.\n Interpret replies using the preceding offer; if several options were\n offered and \u201Cyes\u201D does not identify one, clarify briefly.\n\n- Treat a clear answer to your question as an instruction to proceed.\n Continue from existing results rather than restarting the workflow.\n\nOffer only workflows available in this host. Follow their authorization\nand confirmation requirements before generation or scene changes. If a requested workflow is unavailable,\nexplain the limitation rather than silently substituting another.";
package/dist/prompt.js CHANGED
@@ -37,6 +37,8 @@ asks to change it. Creation defaults do not apply to help, discussion, imports,
37
37
  parametric organic/procedural geometry, animation, or Geometry Nodes is requested:
38
38
  use Geometry Nodes for those, not Workbench Flow. Do not claim print readiness without validation.
39
39
 
40
+ - For mesh wompification, read mesh-wompify and scene-editing together and reconstruct only the specified reference using primitives and curves.
41
+
40
42
  - Generate/edit images or generate meshes when requested. An attached image or reference alone
41
43
  does not request generation; use it for the requested workflow or clarify an unresolved request.
42
44
  Generating a result does not authorize importing, converting, or building it in the scene.
package/dist/ui.d.ts CHANGED
@@ -323,3 +323,4 @@ export declare const sparkDataPartSchemas: {
323
323
  usage: z.ZodOptional<z.ZodUnknown>;
324
324
  }, z.core.$strip>;
325
325
  };
326
+ export { parseWompifyCommand, WOMPIFY_PRESETS, wompifyPrompt, type WompifyRequest } from "./wompify.js";
package/dist/ui.js CHANGED
@@ -79,3 +79,4 @@ export const sparkDataPartSchemas = {
79
79
  usage: z.unknown().optional(),
80
80
  }),
81
81
  };
82
+ export { parseWompifyCommand, WOMPIFY_PRESETS, wompifyPrompt } from "./wompify.js";
@@ -0,0 +1,22 @@
1
+ export declare const WOMPIFY_PRESETS: {
2
+ readonly simple: {
3
+ readonly label: "Simple";
4
+ readonly instructions: "Prioritize the main forms, proportions, silhouettes and colors, with a compact editable construction.";
5
+ };
6
+ readonly high: {
7
+ readonly label: "High res";
8
+ readonly instructions: "Refine both the main forms and smaller characteristic shapes, checking each region from multiple views.";
9
+ };
10
+ readonly ultra: {
11
+ readonly label: "Ultra";
12
+ readonly instructions: "Target the closest practical geometric match, including subtle contours and small modeled details, with thorough close-up comparisons.";
13
+ };
14
+ };
15
+ export type WompifyRequest = {
16
+ preset: keyof typeof WOMPIFY_PRESETS;
17
+ };
18
+ export declare function wompifyPrompt(preset: WompifyRequest["preset"], referenceName: string, note?: string): string;
19
+ export declare function parseWompifyCommand(text: string): {
20
+ preset: WompifyRequest["preset"];
21
+ note: string;
22
+ } | undefined;
@@ -0,0 +1,13 @@
1
+ export const WOMPIFY_PRESETS = {
2
+ simple: { label: "Simple", instructions: "Prioritize the main forms, proportions, silhouettes and colors, with a compact editable construction." },
3
+ high: { label: "High res", instructions: "Refine both the main forms and smaller characteristic shapes, checking each region from multiple views." },
4
+ ultra: { label: "Ultra", instructions: "Target the closest practical geometric match, including subtle contours and small modeled details, with thorough close-up comparisons." },
5
+ };
6
+ export function wompifyPrompt(preset, referenceName, note = "") {
7
+ return `Wompify ${JSON.stringify(referenceName)} using the mesh-wompify skill; stop if that skill is unavailable.
8
+ Selected tier: ${WOMPIFY_PRESETS[preset].label}.${note.trim() ? `\nUser request: ${note.trim()}` : ""}`;
9
+ }
10
+ export function parseWompifyCommand(text) {
11
+ const match = /^\/wompify (simple|high|ultra)(?:\n([\s\S]*))?$/u.exec(text.trim());
12
+ return match ? { preset: match[1], note: match[2]?.trim() ?? "" } : undefined;
13
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@womp/spark-agent",
3
- "version": "5.4.0",
3
+ "version": "5.6.0",
4
4
  "description": "Portable skills-based AI agent with local and durable chat transports for Womp",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",