@volter/blender-engine 0.1.9 → 0.1.10

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.
@@ -193,9 +193,9 @@ export async function startEmscriptenBlenderEngine(
193
193
  // editor's console door is for conditions somebody must resolve, so only the
194
194
  // session's OWN named conditions go there.
195
195
  const say = (_level: 'log' | 'error', text: string) => {
196
- if (text.startsWith('@@VGAI-READY ')) readyLine = text.slice('@@VGAI-READY '.length);
196
+ if (text.startsWith('@@VOLTER-READY ')) readyLine = text.slice('@@VOLTER-READY '.length);
197
197
  options.log(
198
- text.startsWith('@@VGAI-WARN') || text.startsWith('@@VGAI-ERROR') ? 'error' : 'log',
198
+ text.startsWith('@@VOLTER-WARN') || text.startsWith('@@VOLTER-ERROR') ? 'error' : 'log',
199
199
  text,
200
200
  );
201
201
  };
@@ -224,11 +224,11 @@ export async function startEmscriptenBlenderEngine(
224
224
  BLENDER_SYSTEM_PYTHON: '/bw/python',
225
225
  BLENDER_SYSTEM_SCRIPTS: '/bw/scripts',
226
226
  BLENDER_SYSTEM_DATAFILES: '/bw/datafiles',
227
- VGAI_SESSION_ROOT: SESSION_ROOT,
227
+ VOLTER_SESSION_ROOT: SESSION_ROOT,
228
228
  HOME: '/root',
229
229
  TMPDIR: '/tmp',
230
230
  });
231
- // VGAI_EXPORT_BUFFER_PATH IS DELIBERATELY UNSET. This engine reads the
231
+ // VOLTER_EXPORT_BUFFER_PATH IS DELIBERATELY UNSET. This engine reads the
232
232
  // arena off `HEAPU8`; asking the door to also write it to a file would
233
233
  // cost this skew a megabyte-scale write per present for bytes it
234
234
  // already has (`session.py`, EXPORT_BUFFER_PATH).
@@ -29,7 +29,7 @@
29
29
  * and that IS the answer. There is no manifest field, no setting and no flag,
30
30
  * because nothing a user does should depend on the skew -- if it did, the
31
31
  * skews would be answering differently, which the ruling forbids.
32
- * `VGAI_BLENDER_WASM_DIR` is the existing door for pointing the editor at a
32
+ * `VOLTER_BLENDER_WASM_DIR` is the existing door for pointing the editor at a
33
33
  * different build and is how the substrate skew is reached in development.
34
34
  *
35
35
  * WHAT DIFFERS, and it is four things, all below this line:
@@ -43,7 +43,7 @@
43
43
  * implementation over {@link BlenderFiles}.
44
44
  */
45
45
 
46
- export const SESSION_ROOT = '/work/.vgai-session';
46
+ export const SESSION_ROOT = '/work/.volter-session';
47
47
  export const SESSION_SCRIPT = `${SESSION_ROOT}/session.py`;
48
48
 
49
49
  /**
@@ -137,7 +137,7 @@ export interface BlenderEngine {
137
137
  readonly bootMs: number;
138
138
  /** The engine's memory in bytes, now. Linear memory on the standalone skew;
139
139
  * null where the skew cannot see the module's memory from the host, which
140
- * is the WALI case and is why `vgai status` prints "unreported" rather than
140
+ * is the WALI case and is why `volter-model-editor status` prints "unreported" rather than
141
141
  * a zero. */
142
142
  memoryBytes(): number | null;
143
143
  /** Bytes of packed `.data` payload handed back after boot, or null when the
@@ -303,7 +303,7 @@ export function openSessionChannel(
303
303
  *
304
304
  * The loop below waits for `out/<id>.done` and nothing else. When the
305
305
  * program behind the directory DIES, that file is never written, so the
306
- * call waits forever: `vgai status` reports it IN FLIGHT for as long as
306
+ * call waits forever: `volter-model-editor status` reports it IN FLIGHT for as long as
307
307
  * anyone looks, `blender-start` keeps answering from the host's cached
308
308
  * banner, and the harness has no answer to time out against. MEASURED
309
309
  * 2026-09-19 three times on the WALI skew -- 507 s, 621 s and 964 s of a
@@ -404,7 +404,7 @@ export function openSessionChannel(
404
404
  `Blender is gone, so this call will never be answered: ${ended}. ` +
405
405
  'The session keeps its directory and its cached banner, so every later call would ' +
406
406
  'wait on the same missing answer; start a new program with `blender-start {fresh: true}` ' +
407
- '(`VGAI_BLENDER_FRESH_SESSION=1` for the battery harness).',
407
+ '(`VOLTER_BLENDER_FRESH_SESSION=1` for the battery harness).',
408
408
  );
409
409
  await sleep(pollDelay(performance.now() - began));
410
410
  }
@@ -245,9 +245,9 @@ export async function startWaliBlenderEngine(
245
245
  // `--python-use-system-env` below is what makes Blender read the
246
246
  // variable at all.
247
247
  PYTHONPATH: `${RESOURCES}/python/lib/numpy313.zip:${RESOURCES}/python/lib/wheels313.zip`,
248
- VGAI_SESSION_ROOT: SESSION_ROOT,
248
+ VOLTER_SESSION_ROOT: SESSION_ROOT,
249
249
  // THE ARENA'S DOOR ON THIS SKEW. See `readArena`.
250
- VGAI_EXPORT_BUFFER_PATH: ARENA_PATH,
250
+ VOLTER_EXPORT_BUFFER_PATH: ARENA_PATH,
251
251
  },
252
252
  threadPoolSize: workers.pool,
253
253
  // Blender talks to nothing. The editor's own routes are this worker's, not
@@ -260,13 +260,13 @@ export async function startWaliBlenderEngine(
260
260
  const say = (text: string) => {
261
261
  for (const line of text.split('\n')) {
262
262
  if (line === '') continue;
263
- if (line.startsWith('@@VGAI-READY ')) readyLine = line.slice('@@VGAI-READY '.length);
263
+ if (line.startsWith('@@VOLTER-READY ')) readyLine = line.slice('@@VOLTER-READY '.length);
264
264
  // BLENDER'S OWN STREAMS ARE PAGE OUTPUT, NOT EDITOR-CONSOLE CONDITIONS
265
265
  // -- the same rule as the standalone engine, and for the same measured
266
266
  // reason: only the session's own named conditions belong in the set an
267
267
  // agent must drive to zero.
268
268
  options.log(
269
- line.startsWith('@@VGAI-WARN') || line.startsWith('@@VGAI-ERROR') ? 'error' : 'log',
269
+ line.startsWith('@@VOLTER-WARN') || line.startsWith('@@VOLTER-ERROR') ? 'error' : 'log',
270
270
  line,
271
271
  );
272
272
  }
@@ -304,7 +304,7 @@ export async function startWaliBlenderEngine(
304
304
  // only defensible because the CALLER can degrade VISIBLY; without this
305
305
  // the degrade is silent, and before it existed the notice reached a
306
306
  // nested worker's devtools console and nothing else.
307
- onDiagnostic: (message: string) => options.log('error', `@@VGAI-ERROR ${message}`),
307
+ onDiagnostic: (message: string) => options.log('error', `@@VOLTER-ERROR ${message}`),
308
308
  },
309
309
  )
310
310
  .then(
@@ -316,7 +316,7 @@ export async function startWaliBlenderEngine(
316
316
  },
317
317
  )
318
318
  .finally(() => {
319
- if (ended !== null) options.log('error', `@@VGAI-ERROR the Blender program ended: ${ended}`);
319
+ if (ended !== null) options.log('error', `@@VOLTER-ERROR the Blender program ended: ${ended}`);
320
320
  });
321
321
 
322
322
  while (readyLine === null) {
@@ -352,7 +352,7 @@ export async function startWaliBlenderEngine(
352
352
  readArena: () => filesystem.readFile(ARENA_PATH),
353
353
  bootMs,
354
354
  // The module's memory is the program worker's and no door reports it.
355
- // Null rather than zero: `vgai status` prints "unreported", which is true,
355
+ // Null rather than zero: `volter-model-editor status` prints "unreported", which is true,
356
356
  // where a zero would be a measurement that was never taken.
357
357
  memoryBytes: () => null,
358
358
  // There is no packed payload on this skew: Blender's runtime tree is
package/browser/rna.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  * (`bl_rna.properties`) — the ruling's "everything Blender SHOWS about a
9
9
  * datablock, we show, in OUR panels, read from the engine through bpy and RNA"
10
10
  * (ARCHITECTURE-CORE §Blender north star, "Inspection parity, not editing
11
- * parity"). Nothing in this file is a vgai invention over RNA: every field is
11
+ * parity"). Nothing in this file is a volter invention over RNA: every field is
12
12
  * a `PropertyRNA` member, a `bpy.types` name, or an address `path_from_id()`
13
13
  * produced.
14
14
  */
@@ -241,7 +241,7 @@ export interface BlenderRnaWrite {
241
241
  // session's scene, as rows (WORK.md §Blender in the tab is Blender,
242
242
  // "Inspection parity", I3). Every field below is Blender's own — a `TSE_*`
243
243
  // element type, an `ICON_*` name `tree_element_get_icon` picks, a restriction
244
- // column `outliner_draw_restrictbuts` draws — and nothing here is a vgai
244
+ // column `outliner_draw_restrictbuts` draws — and nothing here is a volter
245
245
  // invention over the Outliner.
246
246
 
247
247
  /**
@@ -69,7 +69,7 @@ export interface PresentAnswer {
69
69
  * Read by anything that has to say WHERE a byte came from rather than merely
70
70
  * display it — `blender-list-files` answers with it, so the CLI's write-back
71
71
  * door can record which session's model a mirrored file was exported from
72
- * (`packages/vgai-cli/src/blender-mcp.ts`, class Mirror). Null until the
72
+ * (`packages/volter-cli/src/blender-mcp.ts`, class Mirror). Null until the
73
73
  * session's first present.
74
74
  */
75
75
  export interface PresentedState {
@@ -186,7 +186,7 @@ export class BlenderRuntime {
186
186
  // The page console is the editor's ledger (`installEditorConsoleReporting`
187
187
  // captures it, source-blind), and this package may import nothing of the
188
188
  // editor to say it any other way. Without this line the failure reached
189
- // a toast and `vgai console` read 0/0 while no Model document could open
189
+ // a toast and `volter-model-editor console` read 0/0 while no Model document could open
190
190
  // (measured 2026-09-20 from a registry install).
191
191
  console.error(error.message);
192
192
  for (const id of [...this.#pending.keys()]) this.#settled(id);
@@ -122,7 +122,7 @@ import _blender_web
122
122
  _UNKNOWN_GEOMETRY = "RUNTIME_FRAME_UNKNOWN_GEOMETRY"
123
123
  _UNKNOWN_IMAGE = "RUNTIME_FRAME_UNKNOWN_IMAGE"
124
124
 
125
- ROOT = os.environ.get("VGAI_SESSION_ROOT", "/work/.vgai-session")
125
+ ROOT = os.environ.get("VOLTER_SESSION_ROOT", "/work/.volter-session")
126
126
  IN = os.path.join(ROOT, "in")
127
127
  OUT = os.path.join(ROOT, "out")
128
128
  ASK = os.path.join(ROOT, "ask")
@@ -142,7 +142,7 @@ REPLY = os.path.join(ROOT, "reply")
142
142
  # whose host cannot reach the module's memory at all
143
143
  # (`blender-wali-engine.mts`), where the file is not a fallback but the
144
144
  # native door onto the same bytes.
145
- EXPORT_BUFFER_PATH = os.environ.get("VGAI_EXPORT_BUFFER_PATH", "")
145
+ EXPORT_BUFFER_PATH = os.environ.get("VOLTER_EXPORT_BUFFER_PATH", "")
146
146
  _real_stderr = sys.stderr
147
147
 
148
148
 
@@ -172,7 +172,7 @@ def _prepare_directories():
172
172
  except FileExistsError:
173
173
  pass
174
174
  except OSError as error:
175
- _say("@@VGAI-ERROR cannot create %s: %r" % (directory, error))
175
+ _say("@@VOLTER-ERROR cannot create %s: %r" % (directory, error))
176
176
  try:
177
177
  os.chmod(directory, 0o777)
178
178
  except OSError:
@@ -215,7 +215,7 @@ def warn(what):
215
215
  return
216
216
  _WARNED.add(what)
217
217
  _WARNINGS.append(what)
218
- _say("@@VGAI-WARN " + what)
218
+ _say("@@VOLTER-WARN " + what)
219
219
 
220
220
 
221
221
  # ---------------------------------------------------------------- the world
@@ -1820,11 +1820,11 @@ _ASK_SEQUENCE = [0]
1820
1820
  def _drop(path):
1821
1821
  """Remove one of THIS side's own channel files. A failure here means the
1822
1822
  ownership rule was broken by the other side, so it is named rather than
1823
- swallowed -- and named where `vgai console` reads it, not into a log."""
1823
+ swallowed -- and named where `volter-model-editor console` reads it, not into a log."""
1824
1824
  try:
1825
1825
  os.unlink(path)
1826
1826
  except OSError as error:
1827
- _say("@@VGAI-WARN the session could not remove its own %s: %r" % (path, error))
1827
+ _say("@@VOLTER-WARN the session could not remove its own %s: %r" % (path, error))
1828
1828
 
1829
1829
 
1830
1830
  def ask(payload):
@@ -1979,7 +1979,7 @@ def _assert_photographed_from(reported, position, target, up):
1979
1979
  )
1980
1980
 
1981
1981
 
1982
- class VgaiRenderEngine(bpy.types.RenderEngine):
1982
+ class VolterRenderEngine(bpy.types.RenderEngine):
1983
1983
  """The scene's renderer, so `write_still`, `save_render` and Render Result
1984
1984
  behave as Blender's own.
1985
1985
 
@@ -1989,7 +1989,7 @@ class VgaiRenderEngine(bpy.types.RenderEngine):
1989
1989
  starts a path trace nobody asked for inside the tab.
1990
1990
  """
1991
1991
 
1992
- bl_idname = "VGAI_THREE"
1992
+ bl_idname = "VOLTER_THREE"
1993
1993
  bl_label = "three.js"
1994
1994
  bl_use_preview = False
1995
1995
 
@@ -2087,7 +2087,7 @@ def _register_engine():
2087
2087
  ("BLENDER_WORKBENCH", "Workbench"),
2088
2088
  ):
2089
2089
  existing = _engine_class(identifier)
2090
- if existing is not None and issubclass(existing, VgaiRenderEngine):
2090
+ if existing is not None and issubclass(existing, VolterRenderEngine):
2091
2091
  made.append(identifier)
2092
2092
  continue
2093
2093
  if existing is not None:
@@ -2098,8 +2098,8 @@ def _register_engine():
2098
2098
  else:
2099
2099
  _release_from_owning_addon(existing)
2100
2100
  engine = type(
2101
- "Vgai" + identifier.title().replace("_", ""),
2102
- (VgaiRenderEngine,),
2101
+ "Volter" + identifier.title().replace("_", ""),
2102
+ (VolterRenderEngine,),
2103
2103
  {"bl_idname": identifier, "bl_label": label, "bl_use_preview": False},
2104
2104
  )
2105
2105
  try:
@@ -2115,17 +2115,17 @@ def _register_engine():
2115
2115
  # in the start reply and refused by name at the render, never a
2116
2116
  # warning on every boot.
2117
2117
  unavailable.append(identifier)
2118
- # `VGAI_THREE` is the engine under its own name, registered ONCE: this runs
2118
+ # `VOLTER_THREE` is the engine under its own name, registered ONCE: this runs
2119
2119
  # again after every file load (the ids have to be retaken), and Blender
2120
2120
  # refuses a class it already holds -- which is not a capability anyone
2121
2121
  # lost, so it is not a warning.
2122
- if _engine_class(VgaiRenderEngine.bl_idname) is None:
2122
+ if _engine_class(VolterRenderEngine.bl_idname) is None:
2123
2123
  try:
2124
- bpy.utils.register_class(VgaiRenderEngine)
2124
+ bpy.utils.register_class(VolterRenderEngine)
2125
2125
  except Exception as error: # noqa: BLE001
2126
2126
  warn("could not register the three.js engine: %s" % error)
2127
- if _engine_class(VgaiRenderEngine.bl_idname) is not None:
2128
- made.append(VgaiRenderEngine.bl_idname)
2127
+ if _engine_class(VolterRenderEngine.bl_idname) is not None:
2128
+ made.append(VolterRenderEngine.bl_idname)
2129
2129
  return made, unavailable
2130
2130
 
2131
2131
 
@@ -2698,7 +2698,7 @@ def _py_row(target, name, descriptor, owner):
2698
2698
  from what it answers -- an ID is a POINTER, a sequence of them a
2699
2699
  COLLECTION with its count and names, a scalar its scalar. A value no RNA
2700
2700
  type covers is not invented into one: the property is dropped, and
2701
- nothing here is a vgai field over Blender's data."""
2701
+ nothing here is a volter field over Blender's data."""
2702
2702
  row = {
2703
2703
  "identifier": name,
2704
2704
  "name": name,
@@ -3894,7 +3894,7 @@ def _node_row(node):
3894
3894
 
3895
3895
 
3896
3896
  def _node_tree_of(path, material):
3897
- """WHICH TREE. Either an explicit RNA address (so a `vgai eval` can open a
3897
+ """WHICH TREE. Either an explicit RNA address (so a `volter-model-editor eval` can open a
3898
3898
  world's or a group's tree with the engine's own spelling), or a material by
3899
3899
  name, or -- given neither -- the active object's active material, which is
3900
3900
  what Blender's own Shading header resolves (`space_node.py:89-93`,
@@ -4152,7 +4152,7 @@ def rna_rig(object_name=None):
4152
4152
  presented frame carries every object at once, and the presenter has to know
4153
4153
  which of its meshes are `THREE.SkinnedMesh`es before it builds them. Asking
4154
4154
  per object would be one round trip per mesh; asking by NAME stays available
4155
- for a `vgai eval` that wants to read one.
4155
+ for a `volter-model-editor eval` that wants to read one.
4156
4156
  """
4157
4157
  scene_frame = int(bpy.context.scene.frame_current)
4158
4158
  if object_name:
@@ -4796,7 +4796,7 @@ def dispatch(request):
4796
4796
  if k in request}
4797
4797
  if op == "execute":
4798
4798
  cause["code"] = str(request.get("code", ""))[:160]
4799
- _say("@@VGAI-WARN %s removed objects %s (%s); %d remain" % (
4799
+ _say("@@VOLTER-WARN %s removed objects %s (%s); %d remain" % (
4800
4800
  op, sorted(removed), json.dumps(cause), len(bpy.data.objects)))
4801
4801
 
4802
4802
 
@@ -4865,14 +4865,14 @@ def _dispatch(request):
4865
4865
  except Exception as thrown: # noqa: BLE001
4866
4866
  # LOUD, because a present that fails leaves the Model document
4867
4867
  # showing the state BEFORE this call and nothing else says so.
4868
- # `@@VGAI-PRESENT-FAILED` was not one of the prefixes the engines
4868
+ # `@@VOLTER-PRESENT-FAILED` was not one of the prefixes the engines
4869
4869
  # escalate (`blender-emscripten-engine.mts:159`,
4870
- # `blender-wali-engine.mts:266` raise `@@VGAI-WARN`/`@@VGAI-ERROR`
4870
+ # `blender-wali-engine.mts:266` raise `@@VOLTER-WARN`/`@@VOLTER-ERROR`
4871
4871
  # to the console and nothing else), so it logged at `log` level and
4872
4872
  # reached no counter -- measured 2026-09-19 (I4), when a throw in
4873
4873
  # the overlay walk froze the viewport through a dozen successful
4874
- # `blender-execute` calls with `vgai console` silent throughout.
4875
- _say("@@VGAI-ERROR the present after this call failed, so the Model document is "
4874
+ # `blender-execute` calls with `volter-model-editor console` silent throughout.
4875
+ _say("@@VOLTER-ERROR the present after this call failed, so the Model document is "
4876
4876
  "showing the state before it: " + repr(thrown))
4877
4877
  return answer
4878
4878
  if op == "scene-info":
@@ -4915,14 +4915,14 @@ def _dispatch(request):
4915
4915
  except Exception as thrown: # noqa: BLE001
4916
4916
  # LOUD, because a present that fails leaves the Model document
4917
4917
  # showing the state BEFORE this call and nothing else says so.
4918
- # `@@VGAI-PRESENT-FAILED` was not one of the prefixes the engines
4918
+ # `@@VOLTER-PRESENT-FAILED` was not one of the prefixes the engines
4919
4919
  # escalate (`blender-emscripten-engine.mts:159`,
4920
- # `blender-wali-engine.mts:266` raise `@@VGAI-WARN`/`@@VGAI-ERROR`
4920
+ # `blender-wali-engine.mts:266` raise `@@VOLTER-WARN`/`@@VOLTER-ERROR`
4921
4921
  # to the console and nothing else), so it logged at `log` level and
4922
4922
  # reached no counter -- measured 2026-09-19 (I4), when a throw in
4923
4923
  # the overlay walk froze the viewport through a dozen successful
4924
- # `blender-execute` calls with `vgai console` silent throughout.
4925
- _say("@@VGAI-ERROR the present after this call failed, so the Model document is "
4924
+ # `blender-execute` calls with `volter-model-editor console` silent throughout.
4925
+ _say("@@VOLTER-ERROR the present after this call failed, so the Model document is "
4926
4926
  "showing the state before it: " + repr(thrown))
4927
4927
  return answer
4928
4928
  if op == "rna-set":
@@ -4936,14 +4936,14 @@ def _dispatch(request):
4936
4936
  except Exception as thrown: # noqa: BLE001
4937
4937
  # LOUD, because a present that fails leaves the Model document
4938
4938
  # showing the state BEFORE this call and nothing else says so.
4939
- # `@@VGAI-PRESENT-FAILED` was not one of the prefixes the engines
4939
+ # `@@VOLTER-PRESENT-FAILED` was not one of the prefixes the engines
4940
4940
  # escalate (`blender-emscripten-engine.mts:159`,
4941
- # `blender-wali-engine.mts:266` raise `@@VGAI-WARN`/`@@VGAI-ERROR`
4941
+ # `blender-wali-engine.mts:266` raise `@@VOLTER-WARN`/`@@VOLTER-ERROR`
4942
4942
  # to the console and nothing else), so it logged at `log` level and
4943
4943
  # reached no counter -- measured 2026-09-19 (I4), when a throw in
4944
4944
  # the overlay walk froze the viewport through a dozen successful
4945
- # `blender-execute` calls with `vgai console` silent throughout.
4946
- _say("@@VGAI-ERROR the present after this call failed, so the Model document is "
4945
+ # `blender-execute` calls with `volter-model-editor console` silent throughout.
4946
+ _say("@@VOLTER-ERROR the present after this call failed, so the Model document is "
4947
4947
  "showing the state before it: " + repr(thrown))
4948
4948
  return answer
4949
4949
  if op == "save-document":
@@ -4971,7 +4971,7 @@ bpy.app.handlers.load_post.append(_load_post)
4971
4971
  bpy.app.handlers.undo_post.append(_history_post)
4972
4972
  bpy.app.handlers.redo_post.append(_history_post)
4973
4973
  ENGINES, UNAVAILABLE_ENGINES = _register_engine()
4974
- _say("@@VGAI-READY " + json.dumps({"blender": bpy.app.version_string, "engines": ENGINES,
4974
+ _say("@@VOLTER-READY " + json.dumps({"blender": bpy.app.version_string, "engines": ENGINES,
4975
4975
  "unavailableEngines": UNAVAILABLE_ENGINES}))
4976
4976
 
4977
4977
  # Markers already dispatched, and the answers whose `out/` files this loop
@@ -5054,7 +5054,7 @@ try:
5054
5054
  except BaseException as _thrown: # noqa: BLE001
5055
5055
  # The one place a channel death can still be named. Without this the
5056
5056
  # program ends 0 and the page reports only that Blender is gone.
5057
- _say("@@VGAI-ERROR the session's channel loop raised %s: %s -- the session is over and "
5057
+ _say("@@VOLTER-ERROR the session's channel loop raised %s: %s -- the session is over and "
5058
5058
  "every outstanding call will go unanswered. %s"
5059
5059
  % (type(_thrown).__name__, _thrown, traceback.format_exc().replace("\n", " | ")))
5060
5060
  raise
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * ONE PRESENTER, ATTACHED TO ONE BLENDER (WS-AC, cut 3).
3
3
  *
4
- * `vgai_three.py` ships inside our Blender and turns every
4
+ * `volter_three.py` ships inside our Blender and turns every
5
5
  * `bpy.ops.render.render` into an ASK on a directory it owns; this is the
6
6
  * other half, for a host that holds that directory as a filesystem in its own
7
7
  * realm. It is the whole of what a host must do: hand it the filesystem, the
@@ -240,7 +240,7 @@ const BONE_SOLID_VERTEX = /* glsl */ `
240
240
  * ONLY THE `_fragment` HALF: three PREPENDS `colorspace_pars_fragment` to every
241
241
  * `ShaderMaterial` already, so including it here declared `LinearTransferOETF`,
242
242
  * `sRGBTransferEOTF` and `sRGBTransferOETF` twice and the whole program refused
243
- * to compile ("function already has a body", measured live in `vgai console`).
243
+ * to compile ("function already has a body", measured live in `volter-model-editor console`).
244
244
  */
245
245
  const BONE_SOLID_FRAGMENT = /* glsl */ `
246
246
  varying vec3 vColor;
@@ -127,8 +127,8 @@ export interface ExtrasInput {
127
127
  * as that draw is done.
128
128
  */
129
129
  function standDownInOverridePasses(object: THREE.Object3D): void {
130
- if (!(object as THREE.Mesh).isMesh || object.userData['vgaiOverrideStandDown']) return;
131
- object.userData['vgaiOverrideStandDown'] = true;
130
+ if (!(object as THREE.Mesh).isMesh || object.userData['volterOverrideStandDown']) return;
131
+ object.userData['volterOverrideStandDown'] = true;
132
132
  const own = object.onBeforeRender.bind(object);
133
133
  const ownAfter = object.onAfterRender.bind(object);
134
134
  const placed = new THREE.Matrix4();
@@ -202,7 +202,7 @@ export class ExtrasOverlay {
202
202
  const previous = this.drawn.get(object.name);
203
203
  const stands = input.presented(object.name);
204
204
  if (previous?.key === key) {
205
- for (const part of previous.parts) part.userData['vgaiPicksAs'] = stands;
205
+ for (const part of previous.parts) part.userData['volterPicksAs'] = stands;
206
206
  continue;
207
207
  }
208
208
  if (previous) this.remove(previous.parts);
@@ -214,7 +214,7 @@ export class ExtrasOverlay {
214
214
  else if (empty) this.empty(empty, matrix, color, picture);
215
215
  const parts = group.children.filter((child) => !before.has(child));
216
216
  for (const part of parts) {
217
- part.userData['vgaiPicksAs'] = stands;
217
+ part.userData['volterPicksAs'] = stands;
218
218
  part.traverse(standDownInOverridePasses);
219
219
  }
220
220
  this.drawn.set(object.name, { key, parts });
@@ -230,7 +230,7 @@ export class ExtrasOverlay {
230
230
  for (const part of parts) {
231
231
  part.removeFromParent();
232
232
  (part as THREE.Mesh).geometry?.dispose();
233
- if (part.userData['vgaiOwnMaterial']) ((part as THREE.Mesh).material as THREE.Material).dispose();
233
+ if (part.userData['volterOwnMaterial']) ((part as THREE.Mesh).material as THREE.Material).dispose();
234
234
  }
235
235
  }
236
236
 
@@ -603,13 +603,13 @@ export class ExtrasOverlay {
603
603
  )
604
604
  .replace('#include <alphatest_fragment>', blends ? '#include <alphatest_fragment>' : '#include <alphatest_fragment>\ndiffuseColor.a = 1.0;');
605
605
  };
606
- material.customProgramCacheKey = () => `vgai-blender-image:${settings.premultiplied}:${blends}`;
606
+ material.customProgramCacheKey = () => `volter-blender-image:${settings.premultiplied}:${blends}`;
607
607
  const quad = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), material);
608
608
  quad.matrixAutoUpdate = false;
609
609
  quad.matrix.copy(pose);
610
610
  quad.renderOrder = settings.depth === 'FRONT' ? 1000 : settings.depth === 'BACK' ? -1000 : 0;
611
611
  // Its material is its own (the picture and its opacity), freed with the quad.
612
- quad.userData['vgaiOwnMaterial'] = true;
612
+ quad.userData['volterOwnMaterial'] = true;
613
613
  hideUnless(quad, true);
614
614
  this.empties.add(quad);
615
615
  }
@@ -264,7 +264,7 @@ export const SOLID_LIGHTS: readonly {
264
264
  * and AgX within 3, so the curve is NOT what row 1's 13-vs-30 spread was made
265
265
  * of and the stage keeps its own operator).
266
266
  *
267
- * THE ROUNDS, on a `model-editor create` scaffold driven through `vgai
267
+ * THE ROUNDS, on a `model-editor create` scaffold driven through `volter-model-editor
268
268
  * screenshot editor`, 30x30 means (std 0 — every patch inside one flat face)
269
269
  * of the factory cube's three visible faces against Blender 5.2's own frame,
270
270
  * (141,143,145) top / (129,131,131) left / (111,112,113) front:
@@ -634,7 +634,7 @@ export class BlenderRuntimeView {
634
634
  * visibility belongs to the viewport's Helpers menu rather than to this
635
635
  * presenter, and the door for that is the host's own — a document hands
636
636
  * each group to its stage's `setHelper(kind, object)`
637
- * (`@vgai/editor-sdk/host`, `EditorHostStage`), which marks it
637
+ * (`@volter/editor-sdk/host`, `EditorHostStage`), which marks it
638
638
  * `editorHelper`, keeps it out of the hierarchy and the raycast, and turns
639
639
  * it on and off with the kind's checkbox. `blender-runtime.document.tsx`
640
640
  * is where they are handed over.
@@ -912,7 +912,7 @@ export class BlenderRuntimeView {
912
912
  /**
913
913
  * BLENDER'S MODE, PUBLISHED ON THE DOCUMENT'S OWN CONTEXT.
914
914
  *
915
- * `vgai.stage.mode` (U6's context key, `editor-host-door.ts`'s `stage()`) had
915
+ * `volter.stage.mode` (U6's context key, `editor-host-door.ts`'s `stage()`) had
916
916
  * no reporter at all: Edit Mesh went with the mesh kit and the Model document
917
917
  * had no mode strip, so every `when` clause that reads it was answering
918
918
  * `null`. The engine HAS a mode — `bpy.context.mode`, and `Object.mode` on the
@@ -1005,9 +1005,9 @@ export class BlenderRuntimeView {
1005
1005
  this.weightRoot.name = 'BlenderWeights';
1006
1006
  this.cursorRoot.name = 'Blender3DCursor';
1007
1007
  this.cameraExtrasRoot.name = 'BlenderCameraExtras';
1008
- // The extras are picked as the objects they draw (`userData.vgaiPicksAs` on each part).
1008
+ // The extras are picked as the objects they draw (`userData.volterPicksAs` on each part).
1009
1009
  for (const root of [this.cameraExtrasRoot, this.lightExtrasRoot, this.emptyExtrasRoot])
1010
- root.userData['vgaiPickable'] = true;
1010
+ root.userData['volterPickable'] = true;
1011
1011
  this.lightExtrasRoot.name = 'BlenderLightExtras';
1012
1012
  this.emptyExtrasRoot.name = 'BlenderEmptyExtras';
1013
1013
  }
@@ -1768,8 +1768,8 @@ export class BlenderRuntimeView {
1768
1768
  object.name = obj.name;
1769
1769
  // A camera, a light and an empty are drawn by the overlay's extras, selection colour and
1770
1770
  // all (`blender-runtime-extras.ts`); the editor's own marks for them stand down.
1771
- if (obj.type === 'CAMERA' || obj.type === 'LIGHT' || obj.type === 'EMPTY') object.userData['vgaiOwnOverlay'] = true;
1772
- else delete object.userData['vgaiOwnOverlay'];
1771
+ if (obj.type === 'CAMERA' || obj.type === 'LIGHT' || obj.type === 'EMPTY') object.userData['volterOwnOverlay'] = true;
1772
+ else delete object.userData['volterOwnOverlay'];
1773
1773
  object.matrixAutoUpdate = false;
1774
1774
  object.matrix.set(...(obj.matrix.flat() as Parameters<THREE.Matrix4['set']>));
1775
1775
  if (obj.parent) {
@@ -1804,7 +1804,7 @@ export class BlenderRuntimeView {
1804
1804
  if (light.parent !== object) object.add(light);
1805
1805
  // Blender's overlay draws this light (`blender-runtime-extras.ts`); the editor's own
1806
1806
  // light helper stands down for it.
1807
- light.userData['vgaiOwnOverlay'] = true;
1807
+ light.userData['volterOwnOverlay'] = true;
1808
1808
  aimLight(light, object);
1809
1809
  this.lights.set(obj.light, light);
1810
1810
  }
@@ -48,7 +48,7 @@ vec4 wbWrapped(vec4 NL, vec4 w) {
48
48
  return clamp((NL + w) / (w1 * w1), 0.0, 1.0);
49
49
  }
50
50
 
51
- vec3 vgaiWorkbenchLighting(vec3 baseColor, vec3 N, vec3 I) {
51
+ vec3 volterWorkbenchLighting(vec3 baseColor, vec3 N, vec3 I) {
52
52
  vec3 diffuseColor = mix(baseColor, vec3(0.0), wbMetallic);
53
53
  vec3 specularColor = mix(vec3(0.05), baseColor, wbMetallic);
54
54
  vec3 R = -reflect(I, N);
@@ -94,7 +94,7 @@ export function workbenchMaterial(display: ViewportDisplay, side: THREE.Side): T
94
94
  const [r, g, b] = display.color;
95
95
  const material = new THREE.MeshLambertMaterial({ side });
96
96
  material.color.setRGB(r, g, b, THREE.LinearSRGBColorSpace);
97
- material.name = 'vgai:blender-solid';
97
+ material.name = 'volter:blender-solid';
98
98
  material.onBeforeCompile = (shader) => {
99
99
  shader.uniforms['wbDir'] = { value: LIGHT_DIRECTIONS };
100
100
  shader.uniforms['wbDiffuse'] = { value: LIGHT_DIFFUSE };
@@ -107,9 +107,9 @@ export function workbenchMaterial(display: ViewportDisplay, side: THREE.Side): T
107
107
  .replace(
108
108
  '#include <opaque_fragment>',
109
109
  // The view direction toward the eye, as Blender's `I`: constant in an orthographic view.
110
- 'outgoingLight = vgaiWorkbenchLighting(diffuseColor.rgb, normal, isOrthographic ? vec3(0.0, 0.0, 1.0) : normalize(vViewPosition));\n#include <opaque_fragment>',
110
+ 'outgoingLight = volterWorkbenchLighting(diffuseColor.rgb, normal, isOrthographic ? vec3(0.0, 0.0, 1.0) : normalize(vViewPosition));\n#include <opaque_fragment>',
111
111
  );
112
112
  };
113
- material.customProgramCacheKey = () => 'vgai-blender-workbench';
113
+ material.customProgramCacheKey = () => 'volter-blender-workbench';
114
114
  return material;
115
115
  }
@@ -9,7 +9,7 @@
9
9
  * to the Blender it started:
10
10
  *
11
11
  * const { createPresenter, attachPresenter } = await import(url);
12
- * const detach = attachPresenter(filesystem, "/tmp/vgai-presenter", createPresenter());
12
+ * const detach = attachPresenter(filesystem, "/tmp/volter-presenter", createPresenter());
13
13
  *
14
14
  * NO HOST BUNDLES THIS. The module is GPL because it is part of the Blender
15
15
  * release; a host loads it the way it loads the wasm -- by URL, under a pinned
@@ -3,7 +3,7 @@
3
3
  THE RENDER OVERRIDE IS NOT THE EDITOR'S. `session.py` is the editor tab's
4
4
  long-lived session and carries this same override for the session it owns;
5
5
  this file is the other delivery of it -- a Blender STARTUP module that ships in
6
- the pack's resource tree (`scripts/startup/vgai_three.py`), so a bare
6
+ the pack's resource tree (`scripts/startup/volter_three.py`), so a bare
7
7
  `blender -b ... -a` on ANY surface renders with three.js too. Blender imports
8
8
  every module in `scripts/startup` and calls its `register()`.
9
9
 
@@ -56,14 +56,14 @@ import bpy
56
56
  # `{offset, length, dtype, count, stride}` in a small JSON frame.
57
57
  import _blender_web
58
58
 
59
- ROOT = os.environ.get("VGAI_PRESENTER_ROOT", "/tmp/vgai-presenter")
59
+ ROOT = os.environ.get("VOLTER_PRESENTER_ROOT", "/tmp/volter-presenter")
60
60
  ASK = os.path.join(ROOT, "ask")
61
61
  REPLY = os.path.join(ROOT, "reply")
62
62
  # WHERE THE EXPORT DOOR LEAVES THE ARENA. The host cannot reach this program's
63
63
  # linear memory, so the arena crosses as a file -- the door writes it when
64
64
  # `buffer_path` is set, and the host reads it beside the ask.
65
65
  EXPORT_BUFFER_PATH = os.environ.get(
66
- "VGAI_EXPORT_BUFFER_PATH", os.path.join(ROOT, "arena.bin")
66
+ "VOLTER_EXPORT_BUFFER_PATH", os.path.join(ROOT, "arena.bin")
67
67
  )
68
68
  # THE FRAME'S IDENTITY IS A PAIR, `(session, revision)`, and the presenter
69
69
  # refuses a revision that went backwards under a name it already holds. One
@@ -78,7 +78,7 @@ EXPORT_BUFFER_PATH = os.environ.get(
78
78
  # 2026-09-20 in the substrate tab; the first run on a freshly loaded page
79
79
  # always worked, which is what hid it). The start time is what makes the name
80
80
  # this process's own.
81
- SESSION = "vgai-three:%d-%d" % (os.getpid(), time.time_ns())
81
+ SESSION = "volter-three:%d-%d" % (os.getpid(), time.time_ns())
82
82
  HELLO_TIMEOUT_SECONDS = 2.0
83
83
  POLL_SECONDS = 0.002
84
84
 
@@ -115,14 +115,14 @@ def warn(what):
115
115
  return
116
116
  _WARNED.add(what)
117
117
  _WARNINGS.append(what)
118
- _say("@@VGAI-WARN " + what)
118
+ _say("@@VOLTER-WARN " + what)
119
119
 
120
120
 
121
121
  def _drop(path):
122
122
  try:
123
123
  os.unlink(path)
124
124
  except OSError as error:
125
- _say("@@VGAI-WARN vgai_three could not remove its own %s: %r" % (path, error))
125
+ _say("@@VOLTER-WARN volter_three could not remove its own %s: %r" % (path, error))
126
126
 
127
127
 
128
128
  def _prepare_directories():
@@ -130,7 +130,7 @@ def _prepare_directories():
130
130
  try:
131
131
  os.makedirs(directory, exist_ok=True)
132
132
  except OSError as error:
133
- _say("@@VGAI-ERROR vgai_three cannot create %s: %r" % (directory, error))
133
+ _say("@@VOLTER-ERROR volter_three cannot create %s: %r" % (directory, error))
134
134
  try:
135
135
  os.chmod(directory, 0o777)
136
136
  except OSError:
@@ -371,7 +371,7 @@ def _assert_photographed_from(reported, position, target, up):
371
371
  )
372
372
 
373
373
 
374
- class VgaiRenderEngine(bpy.types.RenderEngine):
374
+ class VolterRenderEngine(bpy.types.RenderEngine):
375
375
  """The scene's renderer, so `write_still`, `save_render` and Render Result
376
376
  behave as Blender's own.
377
377
 
@@ -381,7 +381,7 @@ class VgaiRenderEngine(bpy.types.RenderEngine):
381
381
  starts a path trace nobody asked for.
382
382
  """
383
383
 
384
- bl_idname = "VGAI_THREE"
384
+ bl_idname = "VOLTER_THREE"
385
385
  bl_label = "three.js"
386
386
  bl_use_preview = False
387
387
 
@@ -471,7 +471,7 @@ def _register_engine():
471
471
  ("BLENDER_WORKBENCH", "Workbench"),
472
472
  ):
473
473
  existing = _engine_class(identifier)
474
- if existing is not None and issubclass(existing, VgaiRenderEngine):
474
+ if existing is not None and issubclass(existing, VolterRenderEngine):
475
475
  made.append(identifier)
476
476
  continue
477
477
  if existing is not None:
@@ -482,8 +482,8 @@ def _register_engine():
482
482
  else:
483
483
  _release_from_owning_addon(existing)
484
484
  engine = type(
485
- "Vgai" + identifier.title().replace("_", ""),
486
- (VgaiRenderEngine,),
485
+ "Volter" + identifier.title().replace("_", ""),
486
+ (VolterRenderEngine,),
487
487
  {"bl_idname": identifier, "bl_label": label, "bl_use_preview": False},
488
488
  )
489
489
  try:
@@ -496,17 +496,17 @@ def _register_engine():
496
496
  # keeps `BLENDER_EEVEE` and `BLENDER_WORKBENCH`: a fact about the
497
497
  # build, not a warning on every boot.
498
498
  unavailable.append(identifier)
499
- # `VGAI_THREE` is the engine under its own name, registered ONCE: this runs
499
+ # `VOLTER_THREE` is the engine under its own name, registered ONCE: this runs
500
500
  # again after every file load (the ids have to be retaken), and Blender
501
501
  # refuses a class it already holds -- which is not a capability anyone
502
502
  # lost, so it is not a warning.
503
- if _engine_class(VgaiRenderEngine.bl_idname) is None:
503
+ if _engine_class(VolterRenderEngine.bl_idname) is None:
504
504
  try:
505
- bpy.utils.register_class(VgaiRenderEngine)
505
+ bpy.utils.register_class(VolterRenderEngine)
506
506
  except Exception as error: # noqa: BLE001
507
507
  warn("could not register the three.js engine: %s" % error)
508
- if _engine_class(VgaiRenderEngine.bl_idname) is not None:
509
- made.append(VgaiRenderEngine.bl_idname)
508
+ if _engine_class(VolterRenderEngine.bl_idname) is not None:
509
+ made.append(VolterRenderEngine.bl_idname)
510
510
  return made, unavailable
511
511
 
512
512
 
@@ -518,7 +518,7 @@ def _adopt():
518
518
  _register_engine()
519
519
  for scene in bpy.data.scenes:
520
520
  try:
521
- scene.render.engine = "VGAI_THREE"
521
+ scene.render.engine = "VOLTER_THREE"
522
522
  except (TypeError, AttributeError) as error:
523
523
  warn("scene %s kept its own render engine: %s" % (scene.name, error))
524
524
 
@@ -536,7 +536,7 @@ def register():
536
536
  _prepare_directories()
537
537
  if ask({"op": "hello"}, timeout=HELLO_TIMEOUT_SECONDS) is None:
538
538
  _say(
539
- "vgai: rendering with Cycles — no presenter answered at %s within 2 s"
539
+ "volter: rendering with Cycles — no presenter answered at %s within 2 s"
540
540
  % ROOT
541
541
  )
542
542
  return
package/browser/worker.ts CHANGED
@@ -27,7 +27,7 @@
27
27
  * picks up BETWEEN calls, never mid-call), and carries the bytes to the
28
28
  * project through `/__editor/blender-document` BEFORE acknowledging the edit.
29
29
  *
30
- * WHY THE CARRY IS NOT THE MIRROR'S JOB (`vgai blender-mcp`, class Mirror):
30
+ * WHY THE CARRY IS NOT THE MIRROR'S JOB (`volter blender-mcp`, class Mirror):
31
31
  * the Mirror is pull-based and runs only after an `execute_blender_code`, so
32
32
  * a document saved after the LAST call of a modeling session
33
33
  * would never leave the worker — which is exactly the state this closes
@@ -172,7 +172,7 @@ async function saveDocument(): Promise<void> {
172
172
  throw new Error(`The Blender document ${relative} was not written to the project: ${describeThrown(error)}`);
173
173
  }
174
174
  setDocumentDirty(false);
175
- log('log', `@@VGAI-DOCUMENT ${JSON.stringify({ path: relative, bytes: bytes.length })}`);
175
+ log('log', `@@VOLTER-DOCUMENT ${JSON.stringify({ path: relative, bytes: bytes.length })}`);
176
176
  }
177
177
 
178
178
  async function startBlender(project: string, document?: string): Promise<unknown> {
@@ -286,7 +286,7 @@ async function start(project: string, document?: string): Promise<unknown> {
286
286
  throw new Error(
287
287
  'Headless Blender is not served by this editor, so there is no modeling engine: ' +
288
288
  `${served.missing.join('; ')}. The engine is Blender compiled to WebAssembly ` +
289
- '(packages/blender-engine/wasm, or the directory VGAI_BLENDER_WASM_DIR names); nothing stands in for it.',
289
+ '(packages/blender-engine/wasm, or the directory VOLTER_BLENDER_WASM_DIR names); nothing stands in for it.',
290
290
  );
291
291
  projectRoot = project;
292
292
  return startBlender(project, document);
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@volter/blender-engine",
3
3
  "author": "Volter AI, Inc.",
4
4
  "license": "GPL-3.0-or-later",
5
- "version": "0.1.9",
5
+ "version": "0.1.10",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },