@womp/spark-agent 3.0.2 → 4.0.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.
Files changed (70) hide show
  1. package/README.md +59 -33
  2. package/dist/agent-run-cli.d.ts +2 -0
  3. package/dist/agent-run-cli.js +30 -0
  4. package/dist/agent-run-input.d.ts +11 -0
  5. package/dist/agent-run-input.js +59 -0
  6. package/dist/agent-run.d.ts +81 -0
  7. package/dist/agent-run.js +234 -0
  8. package/dist/agent-tools.js +3 -2
  9. package/dist/agent.d.ts +0 -2
  10. package/dist/agent.js +4 -36
  11. package/dist/capabilities/geometry.js +1 -1
  12. package/dist/capabilities/openscad-instructions.d.ts +1 -1
  13. package/dist/capabilities/openscad-instructions.js +4 -1
  14. package/dist/capabilities/scene.d.ts +11 -3
  15. package/dist/capabilities/scene.js +88 -27
  16. package/dist/capabilities.d.ts +1 -2
  17. package/dist/capabilities.js +0 -1
  18. package/dist/client.d.ts +5 -0
  19. package/dist/client.js +22 -0
  20. package/dist/coordinates.d.ts +0 -7
  21. package/dist/coordinates.js +8 -47
  22. package/dist/generated/scene-declarations.d.ts +1 -1
  23. package/dist/generated/scene-declarations.js +1 -1
  24. package/dist/geometry/schemas/flowir-dialect.json +2 -2
  25. package/dist/index.d.ts +0 -1
  26. package/dist/index.js +0 -1
  27. package/dist/models.d.ts +7 -0
  28. package/dist/models.js +10 -0
  29. package/dist/request-limits.d.ts +1 -0
  30. package/dist/request-limits.js +1 -0
  31. package/dist/run-metrics.d.ts +15 -0
  32. package/dist/run-metrics.js +30 -0
  33. package/dist/runtime.d.ts +3 -5
  34. package/dist/scene-metadata.js +1 -1
  35. package/dist/sdk-scene-host.d.ts +11 -13
  36. package/dist/sdk-scene-host.js +1 -0
  37. package/dist/sdk-scene-screenshot.d.ts +4 -6
  38. package/dist/sdk-scene-screenshot.js +14 -15
  39. package/dist/server/artifacts.js +1 -5
  40. package/dist/server/executor.d.ts +0 -1
  41. package/dist/server/executor.js +0 -1
  42. package/dist/server/express.js +17 -5
  43. package/dist/server/scene-actions.d.ts +2 -0
  44. package/dist/server/scene-actions.js +20 -0
  45. package/dist/server/service.d.ts +2 -0
  46. package/dist/server/service.js +10 -0
  47. package/dist/server/sqlite-store.d.ts +3 -1
  48. package/dist/server/sqlite-store.js +26 -3
  49. package/dist/server/standalone.d.ts +2 -0
  50. package/dist/server/standalone.js +54 -3
  51. package/dist/server/store.d.ts +3 -1
  52. package/dist/server/store.js +21 -1
  53. package/dist/server/types.d.ts +5 -0
  54. package/dist/simple-bench-cli.d.ts +2 -0
  55. package/dist/simple-bench-cli.js +64 -0
  56. package/dist/simple-bench-prompts.d.ts +28 -0
  57. package/dist/simple-bench-prompts.js +11 -0
  58. package/dist/simple-bench.d.ts +15 -0
  59. package/dist/simple-bench.js +175 -0
  60. package/dist/types.d.ts +4 -13
  61. package/dist/ui.d.ts +3 -8
  62. package/dist/womp-scene-api.d.ts +14 -3
  63. package/dist/womp-scene-api.js +91 -72
  64. package/package.json +7 -3
  65. package/dist/capabilities/health.d.ts +0 -9
  66. package/dist/capabilities/health.js +0 -27
  67. package/dist/context.d.ts +0 -15
  68. package/dist/context.js +0 -266
  69. package/dist/generated/health-declarations.d.ts +0 -1
  70. package/dist/generated/health-declarations.js +0 -3
package/dist/ui.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { InferUIMessageChunk, UIMessage } from "ai";
2
+ import type { SceneScreenshotOptions } from "./capabilities/scene.js";
2
3
  import { z } from "zod";
3
4
  import type { ExecutionReceipt, SparkSkill } from "./capabilities.js";
4
5
  export { DEFAULT_MODEL_ID, getSparkModel, REASONING_EFFORT_LABELS, resolveReasoningEffort, SPARK_MODELS, } from "./models.js";
@@ -95,14 +96,8 @@ export type ExecuteJavaScriptOutput = {
95
96
  }[];
96
97
  error?: string;
97
98
  };
98
- export type ScreenshotInput = {
99
- targetId?: number;
100
- view?: "front" | "back" | "left" | "right" | "top" | "bottom" | "isometric";
101
- };
102
- export type ScreenshotOutput = SparkArtifact & {
103
- targetId: number;
104
- view: string;
105
- };
99
+ export type ScreenshotInput = SceneScreenshotOptions;
100
+ export type ScreenshotOutput = SparkArtifact;
106
101
  export type SparkUITools = {
107
102
  execute_javascript: {
108
103
  input: ExecuteJavaScriptInput;
@@ -79,7 +79,10 @@ export type WompSceneApiContext = {
79
79
  };
80
80
  export type WompSceneApi = ReturnType<typeof createWompSceneApi>;
81
81
  export declare function sceneNodeType(kind: NodeKind): number;
82
+ /** Align local +Z toward target. Both points are in the same parent-local coordinates. */
82
83
  declare function lookAt(origin: Vec3, target: Vec3): Vec3;
84
+ /** XYZ degrees aligning the selected positive local axis to a parent-local direction. */
85
+ declare function alignAxis(direction: Vec3, axis: "x" | "y" | "z"): Vec3;
83
86
  export declare function createWompSceneApi(context: WompSceneApiContext): {
84
87
  listSuperMaterials: () => string[];
85
88
  _getParentId: (childId: number) => number;
@@ -120,6 +123,12 @@ export declare function createWompSceneApi(context: WompSceneApiContext): {
120
123
  nodeCreate: (type: number, parentId?: number) => number;
121
124
  nodeDelete: (id: number) => void;
122
125
  nodeClone: (id: number) => number;
126
+ /** Deep clone; preserves sibling order and remaps internal references. Await before other edits. */
127
+ cloneSubtree: (id: number, options?: {
128
+ parentId?: number;
129
+ name?: string;
130
+ index?: number;
131
+ }) => Promise<number>;
123
132
  nodeList: () => number[];
124
133
  nodeChildren: (parentId: number) => number[];
125
134
  nodeSetParent: (id: number, parentId: number, index?: number) => void;
@@ -139,7 +148,9 @@ export declare function createWompSceneApi(context: WompSceneApiContext): {
139
148
  nodeSetHide: (id: number, hidden: boolean) => void;
140
149
  nodeGetOp: (id: number) => number;
141
150
  nodeSetOp: (id: number, operation: number) => void;
151
+ /** Blend radius in millimetres before parent scaling, independent of project units. */
142
152
  nodeGetBlend: (id: number) => number;
153
+ /** Set blend radius in millimetres before parent scaling; clamps to 0–500. */
143
154
  nodeSetBlend: (id: number, blend: number) => void;
144
155
  nodeGetRound: (id: number) => number;
145
156
  nodeSetRound: (id: number, round: number) => void;
@@ -148,7 +159,7 @@ export declare function createWompSceneApi(context: WompSceneApiContext): {
148
159
  nodeGetMaterialId: (id: number) => number | null;
149
160
  nodeSetMaterialId: (id: number, materialId: number) => void;
150
161
  nodeGetResolution: (id: number) => number;
151
- nodeSetResolution: (id: number, resolution: number) => void;
162
+ nodeSetUnionResolution: (unionId: number, resolution: number) => void;
152
163
  nodeGetLightType: (id: number) => number;
153
164
  nodeSetLightType: (id: number, lightType: number) => void;
154
165
  nodeGetPower: (id: number) => number;
@@ -190,8 +201,7 @@ export declare function createWompSceneApi(context: WompSceneApiContext): {
190
201
  curvePointSetHandleIn: (curveId: number, pointIndex: number, value: Vec3) => void;
191
202
  curvePointSetHandleOut: (curveId: number, pointIndex: number, value: Vec3) => void;
192
203
  curvePointCount: (curveId: number) => number;
193
- curvePointAdd: (curveId: number, point?: CurvePointInput) => number;
194
- curvePointInsert: (curveId: number, pointIndex: number, point?: CurvePointInput) => number;
204
+ curvePointAdd: (curveId: number, point: CurvePointInput, pointIndex?: number) => number;
195
205
  curvePointRemove: (curveId: number, pointIndex: number) => void;
196
206
  curvePointGetName: (curveId: number, pointIndex: number) => string;
197
207
  curvePointSetName: (curveId: number, pointIndex: number, name: string) => void;
@@ -262,6 +272,7 @@ export declare function createWompSceneApi(context: WompSceneApiContext): {
262
272
  aabbCenter: (box: AABB) => Vec3;
263
273
  aabbRadius: (box: AABB) => number;
264
274
  lookAt: typeof lookAt;
275
+ alignAxis: typeof alignAxis;
265
276
  scenePropertiesRead: () => {
266
277
  hdr_name: string | undefined;
267
278
  hdr_multiplier: number | undefined;
@@ -1,6 +1,7 @@
1
+ import { xyzRotationFromMatrix, MAX_BLEND_RADIUS_MM, MAX_MIRROR_BLEND_RADIUS_MM } from "@womp/kakapo-sdk";
1
2
  import { metadataObject, readMaterialMetadata, removeMaterialMetadata, shiftCurvePointMetadata, writeMaterialMetadata, } from "./scene-metadata.js";
2
3
  import { createSceneEnvironment } from "./scene-environment.js";
3
- import { convertRotationBasis, sceneUnitMultiplier, fromEngineDimensions, fromEngineBlend, fromEngineMirrorRotation, fromEnginePosition, toEngineBlend, toEngineDimensions, toEngineMirrorGoop, toEngineMirrorRotation, toEnginePosition, } from "./coordinates.js";
4
+ import { convertRotationBasis, sceneUnitMultiplier, fromEngineDimensions, fromEngineMirrorRotation, fromEnginePosition, toEngineDimensions, toEngineMirrorRotation, toEnginePosition, } from "./coordinates.js";
4
5
  export const NodeType = {
5
6
  Primitive: 0,
6
7
  Union: 1,
@@ -143,12 +144,25 @@ function convertAABB(box, unitMultiplier) {
143
144
  },
144
145
  };
145
146
  }
147
+ /** Align local +Z toward target. Both points are in the same parent-local coordinates. */
146
148
  function lookAt(origin, target) {
147
- const forward = normalize({
149
+ if (![origin.x, origin.y, origin.z, target.x, target.y, target.z].every(Number.isFinite)) {
150
+ throw new Error("lookAt requires finite points in the same parent coordinate system.");
151
+ }
152
+ if (Math.hypot(target.x - origin.x, target.y - origin.y, target.z - origin.z) === 0) {
153
+ throw new Error("lookAt requires distinct origin and target points.");
154
+ }
155
+ return alignAxis({
148
156
  x: target.x - origin.x,
149
157
  y: target.y - origin.y,
150
158
  z: target.z - origin.z,
151
- });
159
+ }, "z");
160
+ }
161
+ /** XYZ degrees aligning the selected positive local axis to a parent-local direction. */
162
+ function alignAxis(direction, axis) {
163
+ if (!["x", "y", "z"].includes(axis))
164
+ throw new Error("Alignment axis must be x, y, or z.");
165
+ const forward = normalize(direction);
152
166
  let up = { x: 0, y: 1, z: 0 };
153
167
  let right = cross(up, forward);
154
168
  if (Math.hypot(right.x, right.y, right.z) < 1e-6) {
@@ -157,17 +171,19 @@ function lookAt(origin, target) {
157
171
  }
158
172
  right = normalize(right);
159
173
  const correctedUp = cross(forward, right);
160
- return {
161
- x: (Math.asin(clamp(correctedUp.z, -1, 1)) * 180) / Math.PI,
162
- y: (Math.atan2(-right.z, forward.z) * 180) / Math.PI,
163
- z: (Math.atan2(-correctedUp.x, correctedUp.y) * 180) / Math.PI,
164
- };
174
+ const [x, y, z] = axis === "x" ? [forward, right, correctedUp]
175
+ : axis === "y" ? [right, forward, scale(correctedUp, -1)] : [right, correctedUp, forward];
176
+ return xyzRotationFromMatrix([
177
+ [x.x, y.x, z.x],
178
+ [x.y, y.y, z.y],
179
+ [x.z, y.z, z.z],
180
+ ]);
165
181
  }
166
182
  function normalize(value) {
167
183
  const length = Math.hypot(value.x, value.y, value.z);
168
- return length < 1e-10
169
- ? { x: 0, y: 0, z: 0 }
170
- : { x: value.x / length, y: value.y / length, z: value.z / length };
184
+ if (!Number.isFinite(length) || length === 0)
185
+ throw new Error("Cannot orient a zero-length or non-finite direction.");
186
+ return { x: value.x / length, y: value.y / length, z: value.z / length };
171
187
  }
172
188
  function cross(left, right) {
173
189
  return {
@@ -286,12 +302,6 @@ export function createWompSceneApi(context) {
286
302
  rotation: { ...plane.rotation },
287
303
  ...(plane.customRotation ? { customRotation: { ...plane.customRotation } } : {}),
288
304
  }));
289
- const curvePoints = (id) => requireCurve(id).points.map((point) => ({
290
- ...point,
291
- position: { ...point.position },
292
- rotation: { ...point.rotation },
293
- scale: { ...point.scale },
294
- }));
295
305
  const requirePoint = (curveId, pointIndex) => {
296
306
  const index = Math.round(finite(pointIndex, "pointIndex"));
297
307
  const count = requireCurve(curveId).points.length;
@@ -338,7 +348,18 @@ export function createWompSceneApi(context) {
338
348
  },
339
349
  }]);
340
350
  };
341
- const applyPointInput = (curveId, index, input) => {
351
+ const addCurvePoint = (curveId, point, pointIndex) => {
352
+ const curve = requireCurve(curveId);
353
+ const count = curve.points.length;
354
+ if (count >= MAX_CURVE_POINTS)
355
+ throw new Error("A curve cannot exceed 100 points");
356
+ const index = pointIndex === undefined ? count : pointIndex;
357
+ if (!Number.isInteger(index) || index < 0 || index > count)
358
+ throw new Error(`Curve ${curveId}: index must be an integer from 0 through ${count}`);
359
+ const input = {
360
+ pos: { x: 0, y: 0, z: 0 }, size: { x: 1, y: 1, z: 1 },
361
+ materialId: curve.materialId ?? context.getDefaultMaterialId(), ...point,
362
+ };
342
363
  const patch = {};
343
364
  const parentScale = ancestorScale(curveId);
344
365
  if (input.pos) {
@@ -358,10 +379,11 @@ export function createWompSceneApi(context) {
358
379
  context.requireMaterial(materialId);
359
380
  patch.materialId = materialId;
360
381
  }
361
- if (Object.keys(patch).length)
362
- setPoint(curveId, index, patch);
363
- if (input.name !== undefined)
364
- setPointName(curveId, index, input.name);
382
+ context.call("addCurvePoint", [curveId, patch, index], () => scene.addCurvePoint(curveId, patch, index));
383
+ shiftCurvePointMetadata(scene, curveId, index, 1);
384
+ if (point.name !== undefined)
385
+ setPointName(curveId, index, point.name);
386
+ return index;
365
387
  };
366
388
  const setNodeSize = (id, sizeValue) => {
367
389
  const node = requireEditableTransform(id);
@@ -374,8 +396,12 @@ export function createWompSceneApi(context) {
374
396
  update(id, { size: { x: converted.z, y: converted.y } });
375
397
  return;
376
398
  }
377
- if (!("z" in sizeValue))
378
- throw new Error("size.z must be a finite number");
399
+ if (!("z" in sizeValue)) {
400
+ if (node.kind !== "primitive" ||
401
+ !["cylinder", "cone", "torus", "pyramid"].includes(node.primitive))
402
+ throw new Error("size.z must be a finite number");
403
+ sizeValue = { ...sizeValue, z: sizeValue.x };
404
+ }
379
405
  const requestedSize = vector(sizeValue, "size");
380
406
  const size = node.kind === "primitive"
381
407
  ? normalizePrimitiveSize(node.primitive, requestedSize)
@@ -404,21 +430,6 @@ export function createWompSceneApi(context) {
404
430
  }
405
431
  return converted;
406
432
  };
407
- const insertCurvePoint = (curveId, pointIndex, point = {}) => {
408
- const points = curvePoints(curveId);
409
- if (points.length >= MAX_CURVE_POINTS)
410
- throw new Error("A curve cannot exceed 100 points");
411
- const index = Math.round(finite(pointIndex, "pointIndex"));
412
- if (index < 0 || index > points.length)
413
- throw new Error("Curve insertion index is out of range");
414
- if (points.length === 0)
415
- throw new Error("Cannot add to an empty curve");
416
- const sourceIndex = Math.max(0, index - 1);
417
- context.call("cloneCurvePoint", [curveId, sourceIndex, index], () => scene.cloneCurvePoint(curveId, sourceIndex, index));
418
- shiftCurvePointMetadata(scene, curveId, index, 1);
419
- applyPointInput(curveId, index, point);
420
- return index;
421
- };
422
433
  const api = {
423
434
  ...createSceneEnvironment(scene, context.hdrs, context.print),
424
435
  listSuperMaterials: () => {
@@ -471,29 +482,12 @@ export function createWompSceneApi(context) {
471
482
  closed: false,
472
483
  primitive: "sphere",
473
484
  operation: "union",
474
- blend: toEngineBlend(0.36),
485
+ blend: 0,
475
486
  density: 80,
476
487
  roundness: 2,
477
488
  smoothing: 0.02,
478
489
  materialId: context.getDefaultMaterialId(),
479
- points: [
480
- {
481
- position: scale(toEnginePosition({ x: 0, y: 2, z: 0 }), 1 / parentScale),
482
- rotation: { x: 0, y: 0, z: 0 },
483
- scale: scale(toEngineDimensions({ x: 4, y: 4, z: 4 }), 1 / parentScale),
484
- materialId: context.getDefaultMaterialId(),
485
- fixed: false,
486
- round: 0,
487
- },
488
- {
489
- position: scale(toEnginePosition({ x: 6, y: 2, z: 0 }), 1 / parentScale),
490
- rotation: { x: 0, y: 0, z: 0 },
491
- scale: scale(toEngineDimensions({ x: 4, y: 4, z: 4 }), 1 / parentScale),
492
- materialId: context.getDefaultMaterialId(),
493
- fixed: false,
494
- round: 0,
495
- },
496
- ],
490
+ points: [],
497
491
  }
498
492
  : kind === "text"
499
493
  ? {
@@ -533,6 +527,27 @@ export function createWompSceneApi(context) {
533
527
  context.trackNode(clone.id);
534
528
  return clone.id;
535
529
  },
530
+ /** Deep clone; preserves sibling order and remaps internal references. Await before other edits. */
531
+ cloneSubtree: async (id, options = {}) => {
532
+ if (id === context.rootId)
533
+ throw new Error("The scene root cannot be cloned");
534
+ const check = (nodeId) => {
535
+ requireNode(nodeId);
536
+ for (const child of scene.getNodeChildren(nodeId))
537
+ check(child.id);
538
+ };
539
+ check(id);
540
+ if (options.parentId !== undefined)
541
+ requireNode(options.parentId);
542
+ const clone = await context.callAsync("cloneSubtree", [id, options], () => scene.cloneSubtree(id, options));
543
+ const track = (nodeId) => {
544
+ context.trackNode(nodeId);
545
+ for (const child of scene.getNodeChildren(nodeId))
546
+ track(child.id);
547
+ };
548
+ track(clone.id);
549
+ return clone.id;
550
+ },
536
551
  nodeList: () => scene
537
552
  .listNodes()
538
553
  .filter((node) => node.id !== context.rootId)
@@ -642,11 +657,13 @@ export function createWompSceneApi(context) {
642
657
  }
643
658
  context.call("setNodeOperation", [id, value], () => scene.setNodeOperation(id, value));
644
659
  },
660
+ /** Blend radius in millimetres before parent scaling, independent of project units. */
645
661
  nodeGetBlend: (id) => {
646
662
  const node = requireNode(id);
647
- return "blend" in node ? fromEngineBlend(node.blend) : 0;
663
+ return "blend" in node ? node.blend : 0;
648
664
  },
649
- nodeSetBlend: (id, blend) => update(id, { blend: toEngineBlend(finite(blend, "blend")) }),
665
+ /** Set blend radius in millimetres before parent scaling; clamps to 0–500. */
666
+ nodeSetBlend: (id, blend) => update(id, { blend: Math.min(MAX_BLEND_RADIUS_MM, Math.max(0, finite(blend, "Blend radius (mm)"))) }),
650
667
  nodeGetRound: (id) => {
651
668
  const node = requireNode(id);
652
669
  if (node.kind !== "primitive" && node.kind !== "text")
@@ -673,11 +690,11 @@ export function createWompSceneApi(context) {
673
690
  throw new Error("Resolution is only valid for Unions");
674
691
  return node.resolution;
675
692
  },
676
- nodeSetResolution: (id, resolution) => {
677
- const node = requireNode(id);
693
+ nodeSetUnionResolution: (unionId, resolution) => {
694
+ const node = requireNode(unionId);
678
695
  if (node.kind !== "union")
679
- throw new Error("Resolution is only valid for Unions");
680
- update(id, { resolution: clamp(finite(resolution, "resolution"), 0, 0.65) });
696
+ throw new Error(`Node ${unionId} (${node.kind}): resolution belongs to an Area/Union, not this node`);
697
+ update(unionId, { resolution: clamp(finite(resolution, "resolution"), 0.01, 0.65) });
681
698
  },
682
699
  nodeGetLightType: (id) => LIGHT_TYPES.indexOf(requireLight(id).lightType),
683
700
  nodeSetLightType: (id, lightType) => {
@@ -687,7 +704,12 @@ export function createWompSceneApi(context) {
687
704
  update(id, { lightType: value });
688
705
  },
689
706
  nodeGetPower: (id) => requireLight(id).power,
690
- nodeSetPower: (id, power) => update(id, { power: clamp(finite(power, "power"), 0, 100) }),
707
+ nodeSetPower: (id, power) => {
708
+ requireLight(id);
709
+ if (!Number.isFinite(power) || power < 0 || power > 100)
710
+ throw new Error(`Light ${id}: power must be a finite number from 0 through 100`);
711
+ update(id, { power });
712
+ },
691
713
  nodeGetText: (id) => requireText(id).text,
692
714
  nodeSetText: (id, text) => update(id, { text: String(text) }),
693
715
  nodeGetFont: (id) => requireText(id).fontFamily,
@@ -768,7 +790,7 @@ export function createWompSceneApi(context) {
768
790
  return {
769
791
  axis: axis ?? "custom",
770
792
  planePosition: axis ? position[axis] : 0,
771
- goop: fromEngineBlend(plane.value),
793
+ goop: plane.value,
772
794
  };
773
795
  },
774
796
  nodeSetMirror: (id, slot, axis, planePosition = 0, goop = 0) => {
@@ -790,7 +812,7 @@ export function createWompSceneApi(context) {
790
812
  mirrors[index] = {
791
813
  position: scale(toEnginePosition(publicPosition, unitMultiplier), 1 / ancestorScale(id)),
792
814
  rotation,
793
- value: toEngineMirrorGoop(finite(goop, "Mirror goop")),
815
+ value: Math.min(MAX_MIRROR_BLEND_RADIUS_MM, Math.max(0, finite(goop, "Mirror radius (mm)"))),
794
816
  hide: 0,
795
817
  hideMirrorPlane: true,
796
818
  constraint,
@@ -815,12 +837,8 @@ export function createWompSceneApi(context) {
815
837
  curvePointSetHandleIn: (curveId, pointIndex, value) => setPointHandle(curveId, pointIndex, "handleIn", value),
816
838
  curvePointSetHandleOut: (curveId, pointIndex, value) => setPointHandle(curveId, pointIndex, "handleOut", value),
817
839
  curvePointCount: (curveId) => requireCurve(curveId).points.length,
818
- curvePointAdd: (curveId, point = {}) => insertCurvePoint(curveId, requireCurve(curveId).points.length, point),
819
- curvePointInsert: insertCurvePoint,
840
+ curvePointAdd: addCurvePoint,
820
841
  curvePointRemove: (curveId, pointIndex) => {
821
- const points = curvePoints(curveId);
822
- if (points.length <= 1)
823
- throw new Error("A curve must keep at least one point");
824
842
  const index = requirePoint(curveId, pointIndex);
825
843
  context.call("deleteCurvePoint", [curveId, index], () => scene.deleteCurvePoint(curveId, index));
826
844
  shiftCurvePointMetadata(scene, curveId, index, -1);
@@ -969,6 +987,7 @@ export function createWompSceneApi(context) {
969
987
  }),
970
988
  aabbRadius: (box) => Math.hypot(box.max.x - box.min.x, box.max.y - box.min.y, box.max.z - box.min.z) / 2,
971
989
  lookAt,
990
+ alignAxis,
972
991
  };
973
992
  return api;
974
993
  }
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "@womp/spark-agent",
3
- "version": "3.0.2",
3
+ "version": "4.0.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",
7
7
  "types": "./dist/index.d.ts",
8
8
  "bin": {
9
- "spark-agent": "dist/cli.js"
9
+ "spark-agent": "dist/cli.js",
10
+ "simple-bench": "dist/simple-bench-cli.js",
11
+ "agent-run": "dist/agent-run-cli.js"
10
12
  },
11
13
  "exports": {
12
14
  "./browser": {
@@ -92,6 +94,8 @@
92
94
  "lint": "yarn typecheck",
93
95
  "dev": "yarn build && concurrently --kill-others \"yarn dev:server\" \"yarn dev:web\"",
94
96
  "dev:server": "tsx src/cli.ts",
97
+ "simple-bench": "tsx src/simple-bench-cli.ts",
98
+ "agent-run": "tsx src/agent-run-cli.ts",
95
99
  "dev:demo-account": "yarn build && concurrently --kill-others \"yarn dev:server --profile demo-account\" \"yarn dev:web\"",
96
100
  "dev:web": "vite --config web/vite.config.ts",
97
101
  "start": "node dist/cli.js",
@@ -106,7 +110,7 @@
106
110
  "@ai-sdk/openai": "4.0.63",
107
111
  "@jitl/quickjs-wasmfile-release-sync": "0.32.0",
108
112
  "@womp/flowir": "0.3.0",
109
- "@womp/kakapo-sdk": "0.3.0",
113
+ "@womp/kakapo-sdk": "0.4.0",
110
114
  "ai": "7.0.95",
111
115
  "eventsource-parser": "^3.0.6",
112
116
  "express": "^5.2.1",
@@ -1,9 +0,0 @@
1
- import type { SparkCapability, SparkInvocationContext } from "../capabilities.js";
2
- import type { SceneHealth } from "../scene-health.js";
3
- export type WompHealthApi = {
4
- read(objectIds: number[]): Promise<SceneHealth>;
5
- };
6
- export interface SceneHealthHost {
7
- read(objectIds: number[], context: SparkInvocationContext): Promise<SceneHealth>;
8
- }
9
- export declare function createSceneHealthCapability(host: SceneHealthHost): SparkCapability<WompHealthApi>;
@@ -1,27 +0,0 @@
1
- import { z } from "zod";
2
- import { WOMP_HEALTH_DECLARATIONS } from "../generated/health-declarations.js";
3
- export function createSceneHealthCapability(host) {
4
- return {
5
- skills: [
6
- {
7
- name: "scene-health",
8
- version: "1",
9
- description: "Required before scene checks or procedural edits: engine limits, empty Areas, fresh geometry feedback, and bounded repairs.",
10
- instructions: `Before checking or editing a model, call womp.health.read([]) for current scene health. A health read is a snapshot and does not prove that the latest edit finished evaluating. Inspect scene edit receipts before claiming success.
11
- For confirmed primitive or depth overflow, repair the same target while preserving design, dimensions, active parameter overrides, and unrelated objects. At most two geometry repairs are allowed per target in one logical run. Unverified feedback must not trigger blind regeneration or a claim that the model passed. Empty Areas may be pending, intentionally empty, mesh-only, or failed. Overflow flags take precedence over possibly stale counts. max_total_depth is a scene hierarchy limit, not measured Boolean depth.`,
12
- declarations: WOMP_HEALTH_DECLARATIONS,
13
- examples: ["print(await womp.health.read([]));"],
14
- },
15
- ],
16
- createApi: (context) => ({
17
- read: (objectIds) => {
18
- const parsed = z
19
- .array(z.number().int().positive())
20
- .safeParse(objectIds);
21
- if (parsed.success === false)
22
- return Promise.reject(parsed.error);
23
- return host.read(parsed.data, context);
24
- },
25
- }),
26
- };
27
- }
package/dist/context.d.ts DELETED
@@ -1,15 +0,0 @@
1
- import type { ModelMessage } from "ai";
2
- /** Host input budget, not a claim about a provider's maximum context window. */
3
- export type SparkContextPolicy = {
4
- maxInputTokens: number;
5
- };
6
- export declare class SparkContextLimitError extends Error {
7
- constructor();
8
- }
9
- /** Heuristic, not provider tokenization. Encoded media bytes are not text tokens. */
10
- export declare function estimateSparkContextTokens(value: unknown): number;
11
- /**
12
- * Project model input only. Never mutate persisted messages, remove call/result
13
- * pairs, summarize user requests, or discard receipts, references, and failures.
14
- */
15
- export declare function prepareSparkContext(messages: ModelMessage[], system: string, policy: SparkContextPolicy): ModelMessage[];