@compilr-dev/sdk 0.40.0 → 0.41.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.
@@ -25,7 +25,7 @@ export type SceneOp =
25
25
  objects: SceneObjectInput[];
26
26
  index?: number;
27
27
  }
28
- /** Shallow merge; arrays replaced whole; `null` clears an optional field; id/type rejected. */
28
+ /** Shallow merge; arrays replaced whole; `material` MERGES; `null` clears; id/type rejected. */
29
29
  | {
30
30
  op: 'update';
31
31
  id: string;
@@ -171,6 +171,30 @@ function showVal(v) {
171
171
  return fmtVec(v);
172
172
  return JSON.stringify(v);
173
173
  }
174
+ /**
175
+ * `material` MERGES, where every other object-valued field is replaced whole.
176
+ *
177
+ * ⚠️ IT USED TO REPLACE, AND THAT COST SOMEONE A SURFACE. A material is a bag of independent
178
+ * knobs — roughness, metalness, opacity, emissive, wireframe — so "make this glow" is naturally
179
+ * `{ material: { emissive: "#f59e0b" } }`, and under replace that ALSO reset the metalness and
180
+ * roughness that were there. Reported from real use: a ring patched to glow quietly lost its
181
+ * metal finish, and the agent only caught it because it re-read the scene. Nothing errored, and
182
+ * the patch did exactly what it said.
183
+ *
184
+ * Arrays still replace whole (a position is one value, not three independent ones). To clear a
185
+ * single knob, pass it as `null`; to clear the whole material, pass `material: null`, which is
186
+ * the same rule one level up.
187
+ */
188
+ function mergeMaterial(before, patch) {
189
+ const base = isRecord(before) ? { ...before } : {};
190
+ for (const [k, v] of Object.entries(patch)) {
191
+ if (v === null)
192
+ Reflect.deleteProperty(base, k);
193
+ else
194
+ base[k] = clone(v);
195
+ }
196
+ return base;
197
+ }
174
198
  function updateObject(scene, id, patch, opts) {
175
199
  const idx = scene.objects.findIndex((o) => o.id === id);
176
200
  if (idx < 0)
@@ -205,8 +229,10 @@ function updateObject(scene, id, patch, opts) {
205
229
  const from = before[k];
206
230
  if (v === null)
207
231
  Reflect.deleteProperty(next, k);
232
+ else if (k === 'material' && isRecord(v))
233
+ next[k] = mergeMaterial(before[k], v);
208
234
  else
209
- next[k] = clone(v); // shallow merge: arrays and nested objects replaced whole
235
+ next[k] = clone(v); // shallow merge: arrays replaced whole
210
236
  const to = next[k];
211
237
  if (JSON.stringify(from) === JSON.stringify(to))
212
238
  continue;
@@ -233,7 +233,10 @@ const OBJECT_PROPERTIES = {
233
233
  type: 'string',
234
234
  description: 'label only: the words to show (≤ 120, ONE line — no newline; use two labels). The scene is ' +
235
235
  'otherwise unlabelled solids, so name the rooms and the parts: a screenshot with no text in ' +
236
- 'it cannot be read back by you or by anyone else.',
236
+ 'it cannot be read back by you or by anyone else. ⚠️ SIZE IT AGAINST WHAT IT NAMES: the text ' +
237
+ 'is in METRES like everything else, so the 0.25 default is right for a desk and unreadable ' +
238
+ 'across a building. Roughly a tenth of the thing it labels — a 4 m wall wants ~0.4, a whole ' +
239
+ 'floor plan wants more. A label you have to zoom in to read is one nobody reads.',
237
240
  },
238
241
  background: {
239
242
  type: 'string',
@@ -538,7 +541,10 @@ export function createSceneTools(config, options) {
538
541
  const sceneUpdateObjectTool = defineTool({
539
542
  name: 'scene_update_object',
540
543
  description: 'Change fields of one object in a 3D scene. patch is a SHALLOW merge — arrays (position, size, ' +
541
- 'points, rotation) and material are replaced whole; null clears an optional field (e.g. group). ' +
544
+ 'points, rotation) are replaced whole, since a position is one value and not three independent ' +
545
+ 'ones. MATERIAL IS THE EXCEPTION: it merges, so { material: { emissive: "#f59e0b" } } adds a glow ' +
546
+ 'and leaves the roughness, metalness and opacity that were already there. null clears an optional ' +
547
+ 'field (group, or material itself); a null INSIDE material clears that one knob. ' +
542
548
  'id and type cannot change: remove the object and add it again. Read ids with scene_get first.',
543
549
  inputSchema: {
544
550
  type: 'object',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compilr-dev/sdk",
3
- "version": "0.40.0",
3
+ "version": "0.41.0",
4
4
  "description": "Universal agent runtime for building AI-powered applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",