@hypersoniclabs/helix-mcp 0.2.4 → 0.2.12
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 +81 -11
- package/dist/continuumCanary.d.ts +17 -0
- package/dist/continuumCanary.js +17 -0
- package/dist/continuumCanary.js.map +1 -0
- package/dist/server.d.ts +14 -1
- package/dist/server.js +4058 -162
- package/dist/server.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/vehicleTools.d.ts +86 -0
- package/dist/vehicleTools.js +229 -0
- package/dist/vehicleTools.js.map +1 -0
- package/docs/avatar-face.md +115 -0
- package/docs/bridge.md +98 -0
- package/docs/bring-your-world.md +117 -0
- package/docs/catalog.md +69 -1
- package/docs/character-animation.md +442 -0
- package/docs/character-attachments.md +166 -0
- package/docs/character-world.md +785 -130
- package/docs/continuum.md +153 -0
- package/docs/items.md +73 -0
- package/docs/lighting-world.md +667 -0
- package/docs/locomotion-clip-spec.md +294 -0
- package/docs/manifest.md +31 -6
- package/docs/multiplayer-logic.md +460 -17
- package/docs/multiplayer-templates/chrono-orchard.md +36 -22
- package/docs/multiplayer-templates/collect-a-thon.md +28 -38
- package/docs/multiplayer-templates/collections.md +24 -24
- package/docs/multiplayer-templates/hangout.md +124 -111
- package/docs/multiplayer-templates/npc-wave.md +310 -0
- package/docs/multiplayer-templates/obby.md +13 -16
- package/docs/multiplayer-templates/persistent-progress.md +218 -0
- package/docs/multiplayer-templates/physics-bumper.md +27 -9
- package/docs/multiplayer-templates/physics-football.md +22 -8
- package/docs/multiplayer-templates/relic-bearers.md +12 -15
- package/docs/multiplayer-templates/server-motion.md +16 -19
- package/docs/multiplayer-templates/shooter-range.md +275 -0
- package/docs/multiplayer-templates/team-control.md +28 -15
- package/docs/multiplayer-templates/turn-arena.md +31 -22
- package/docs/multiplayer-templates/voice-radio.md +166 -0
- package/docs/multiplayer-templates/wave-survival.md +7 -8
- package/docs/multiplayer-templates/world-shop.md +240 -0
- package/docs/multiplayer-world.md +222 -129
- package/docs/npc-world.md +623 -0
- package/docs/publishing.md +108 -28
- package/docs/purchases.md +223 -0
- package/docs/scene-performance.md +64 -0
- package/docs/screenshots.md +140 -0
- package/docs/sdk.md +324 -5
- package/docs/shooter-worlds.md +537 -0
- package/docs/terrain.md +173 -0
- package/docs/upgrades.md +324 -0
- package/docs/vehicles.md +727 -0
- package/docs/world-inspect.md +156 -0
- package/docs/world-look.md +241 -0
- package/docs/world-recipe.md +65 -6
- package/package.json +15 -4
- package/skills/README.md +91 -0
- package/skills/helix-assets/SKILL.md +491 -0
- package/skills/helix-assets/references/asset-sources.md +143 -0
- package/skills/helix-assets/references/vault-api.md +105 -0
- package/skills/helix-avatar-qa/SKILL.md +85 -0
- package/skills/helix-avatars/SKILL.md +206 -0
- package/skills/helix-avatars/references/contract.md +166 -0
- package/skills/helix-avatars/references/dynamics.md +367 -0
- package/skills/helix-avatars/references/face.md +50 -0
- package/skills/helix-avatars/references/publish.md +76 -0
- package/skills/helix-avatars/references/qa.md +251 -0
- package/skills/helix-avatars/references/rigging.md +88 -0
- package/skills/helix-avatars/references/source-generated.md +190 -0
- package/skills/helix-avatars/references/source-model.md +90 -0
- package/skills/helix-avatars/references/source-rigid.md +90 -0
- package/skills/helix-avatars/references/source-vrm.md +61 -0
- package/skills/helix-gauntlet/SKILL.md +128 -0
- package/skills/helix-multiplayer/SKILL.md +150 -0
- package/skills/helix-multiplayer/references/dsl-capability-map.md +107 -0
- package/skills/helix-vehicles/SKILL.md +218 -0
- package/skills/helix-vehicles/references/addons.md +212 -0
- package/skills/helix-vehicles/references/appearance.md +339 -0
- package/skills/helix-vehicles/references/audio-import.md +138 -0
- package/skills/helix-vehicles/references/audio.md +580 -0
- package/skills/helix-vehicles/references/cabin.md +225 -0
- package/skills/helix-vehicles/references/host-manifest.md +174 -0
- package/skills/helix-vehicles/references/physics-from-specs.md +463 -0
- package/skills/helix-vehicles/references/publish.md +214 -0
- package/skills/helix-vehicles/references/qa.md +177 -0
- package/skills/helix-vehicles/references/reference-package.json +3481 -0
- package/skills/helix-vehicles/references/reference-package.md +69 -0
- package/skills/helix-vehicles/references/source-beamng.md +167 -0
- package/skills/helix-vehicles/references/source-concept.md +38 -0
- package/skills/helix-vehicles/references/source-model.md +100 -0
- package/skills/helix-vehicles/references/source-scratch.md +60 -0
- package/skills/helix-world-build/SKILL.md +376 -0
- package/skills/helix-world-build/references/config-gates.md +104 -0
- package/skills/helix-world-director/SKILL.md +210 -0
- package/skills/helix-world-qa/SKILL.md +371 -0
- package/skills/helix-world-qa/references/perf-budgets.md +240 -0
- package/skills/helix-world-qa/references/perf-handle.md +125 -0
- package/skills/helix-world-qa/references/visual-scorecard.md +85 -0
package/skills/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# HELIX Agent Skills
|
|
2
|
+
|
|
3
|
+
Nine first-party skills for agents building HELIX Instant worlds, vehicles and avatars. They ship inside
|
|
4
|
+
`@hypersoniclabs/helix-mcp` and are served to MCP-only agents by `read_skill` (SKILL.md and
|
|
5
|
+
`references/*.md`); executable behavior ships separately in the creator CLI.
|
|
6
|
+
|
|
7
|
+
## The doctrine
|
|
8
|
+
|
|
9
|
+
**Tools own capability. Docs own the contract. Skills own judgment. Every gate reads an
|
|
10
|
+
artifact.**
|
|
11
|
+
|
|
12
|
+
The MCP server teaches and routes; the creator CLI does things — scaffold, install, validate, inspect, measure,
|
|
13
|
+
capture, publish — and already *states* things, in 26 docs served by `read_doc` and
|
|
14
|
+
`read_template`. These skills duplicate neither. They carry the part that is neither a
|
|
15
|
+
capability nor a contract: what to do first, what "good" means, what blocks shipping, and
|
|
16
|
+
how you prove it.
|
|
17
|
+
|
|
18
|
+
The gate rule is the whole design. A gate in these skills never asks the agent to certify
|
|
19
|
+
its own diligence. It names a thing that exists outside the agent's output — a validator
|
|
20
|
+
result, a JSON snapshot, a pixel count, a file on disk, an HTTP response, a process exit
|
|
21
|
+
code — and passes or fails on that. Where a rule could only be satisfied by the agent
|
|
22
|
+
asserting it did the work, we either turned it into a CLI command that exits non-zero on
|
|
23
|
+
failure or cut it. Checklists survive as prompts for what to look at; none of them unlocks
|
|
24
|
+
"done".
|
|
25
|
+
|
|
26
|
+
**The gate rule has a second half, learned by dogfooding these skills on two real worlds:
|
|
27
|
+
a gate must measure the OUTCOME, not a proxy for it.** Every defect those two builds found in
|
|
28
|
+
this skill set was the same shape — a check that read something correlated with the thing it
|
|
29
|
+
claimed to verify, and passed when it had measured nothing at all. `prove-live` looked for a
|
|
30
|
+
build number at three paths the API does not have, found none, warned, and exited 0. The
|
|
31
|
+
genre-gate check grepped the source for `allowModeToggle` and confirmed the *string* was
|
|
32
|
+
present, on a world where the runtime was ignoring the whole config block. The fallback check
|
|
33
|
+
counted honest-fallback log *sites*, so writing the fallback correctly earned a permanent
|
|
34
|
+
warning on a world where none ever fired.
|
|
35
|
+
|
|
36
|
+
So the question to ask of any check here is: **what would I have to observe to know this is
|
|
37
|
+
true, and am I observing that or something that correlates with it?** Where the honest answer
|
|
38
|
+
is "a proxy", the check either moves to where the outcome is observable — the runtime — or
|
|
39
|
+
says so about itself in the output. A gate that cannot prove its claim now exits non-zero; a
|
|
40
|
+
gate that measured nothing says `measured NOTHING` rather than staying silent.
|
|
41
|
+
|
|
42
|
+
**The third lesson, and the expensive one: a labelled proxy is still wrong, and the outcome you
|
|
43
|
+
refuse to gate on is the one that ships broken.** Both dogfooded worlds went live running
|
|
44
|
+
badly — Night Market at 25.5 ms p50 / 92.7 ms p95, 26% of frames over 33 ms — with every gate
|
|
45
|
+
green. QA measured triangles, bundle size and draw calls, printed frame rate as an
|
|
46
|
+
*informational line*, and never failed on it, so a world could run at 10 fps and pass. Worse,
|
|
47
|
+
the one metric that would have caught the cause was a regex: `lighting.census` counted
|
|
48
|
+
`new THREE.PointLight(` call sites and reported `PointLight×4` for a scene holding 18 point
|
|
49
|
+
lights and 4 spot lights — four sites inside loops, off by 4.5× in the direction that passes.
|
|
50
|
+
It was classified as an acceptable "labelled proxy". It was not. Frame time is now a blocking
|
|
51
|
+
gate (`helix world perf-gate`), the census walks the live scene, and the lighting rules that push toward
|
|
52
|
+
more lights are paired with a measured ceiling that pushes back.
|
|
53
|
+
|
|
54
|
+
This is a deliberate departure from the reference skill sets we studied. Theirs gate on
|
|
55
|
+
self-report — an auditor that greps the agent's own prose for the phrase "art direction",
|
|
56
|
+
a credential probe whose script cannot emit the string its checker looks for, a
|
|
57
|
+
"report new failures separately from baseline" rule repeated in four skills with no
|
|
58
|
+
baseline file anywhere. Each of those looks like rigor and enforces nothing. Where we kept
|
|
59
|
+
an idea from them, we kept the mechanism and threw away the ceremony.
|
|
60
|
+
|
|
61
|
+
## The nine
|
|
62
|
+
|
|
63
|
+
| Skill | Owns | Triggers on |
|
|
64
|
+
| --- | --- | --- |
|
|
65
|
+
| `helix-world-director` | The world end to end: phase ledger, ordering, what blocks what, when to stop. The only skill that decides a world is finished. | "build me a world", "make a HELIX game", any multi-phase world request, or resuming an unfinished world |
|
|
66
|
+
| `helix-world-build` | Discovery-first authoring. Catalog before code, config before API, manifest schema-valid, layout measured not eyeballed. | scaffolding, scene authoring, character configuration, placement, HUD, input, gestures |
|
|
67
|
+
| `helix-multiplayer` | The declarative DSL's real boundary — what `when`/`if`/`then` can express, what it provably cannot, and which escape hatch each gap takes. | multiplayer, co-op, PvP, shared state, rooms, entities, zones, teams, voice, single-player→multiplayer conversion |
|
|
68
|
+
| `helix-assets` | Reuse before generate. Vault search, provenance, budgets, loader allowlist, LIX cost, and the audio source chain. | sourcing models/textures/audio, "find an asset", generating assets, bundle-size problems, load failures |
|
|
69
|
+
| `helix-avatar-qa` | Publishing an avatar that renders with its textures and walks without twisting or tearing (in the world and on the Marketplace page): the hold-until-pass rule, what each visual-QA assert measures, and how to fix what it fails. | publishing or versioning an avatar, "held for visual QA", an avatar that looks white/grey/glowing/untextured/twisted |
|
|
70
|
+
| `helix-world-qa` | Proving the world plays — and that it plays at 60 fps. Mobile-first, artifact-gated, with the blocking metrics (including frame time) computed by script. | "is it ready", QA, playtest, pre-publish review, "why does it look flat", "why does it run badly" |
|
|
71
|
+
| `helix-vehicles` | A drivable vehicle item and its add-ons from any source — real car by name, BeamNG mod, downloaded model, concept image, from scratch — to a published, visually verified listing. The rules live in `read_doc({name:"vehicles"})`; the skill is the order of work and its gates. | "make me a <car>", convert/import a vehicle or mod, vehicle add-ons (wheels, kits, performance parts), republish or fix a car; routed by `get_started({kind:"vehicle"})` |
|
|
72
|
+
| `helix-avatars` | A published avatar from any source — VRM, Dreamer/Meshy image-to-3D, rigged game model or rip, robot/CAD/scan — rigged onto `helix-humanoid@1`, inside the all-LOD budgets, with `helix/dynamics@1` physics bones for hair, tails, ears and cloth, and verified moving against a control body. The face contract stays in `read_doc({name:"avatar-face"})`. | "make me an avatar of <character>", import/convert a VRM, game model or generated character, hair or cloth physics, fix or republish an avatar; routed by `get_started({kind:"avatar"})` |
|
|
73
|
+
| `helix-gauntlet` | **Mandatory** acceptance before any publish, new version, listing or "done", for every kind: an external bar (photos and specs of the real thing), the fixed inspection set rendered in the real runtime (for a car: every seat occupied, the driver steering, still hands), the agent's own look at every sheet, a fresh blind critic, two rounds per approach then diagnose, ship only on a pass; and re-checking listed items against today's gates. | before `publish_*`, `create_item_distribution`, a new version, "is it done"; updating a listed car; loaded by every publishing skill |
|
|
74
|
+
|
|
75
|
+
## Reading order for a fresh agent
|
|
76
|
+
|
|
77
|
+
`helix-world-director` first — it loads the others at phase entry and owns the ledger.
|
|
78
|
+
Everything else is loaded by the phase that needs it. A narrow request ("fix the lighting")
|
|
79
|
+
can load `helix-world-build` + `helix-world-qa` directly without the director.
|
|
80
|
+
|
|
81
|
+
## Retention rule
|
|
82
|
+
|
|
83
|
+
A skill survives only when it changes orchestration or judgment, or requires artifact-gated
|
|
84
|
+
behavior that the docs alone do not provide. Repeated command references and product contract
|
|
85
|
+
text belong in `read_doc`; executable checks belong in the CLI. Skills never package scripts.
|
|
86
|
+
|
|
87
|
+
## What these skills never do
|
|
88
|
+
|
|
89
|
+
Name a generation vendor, call a provider API, ask for an API key, or reason about
|
|
90
|
+
moderation. Asset generation, provider selection, moderation, LIX metering and publishing
|
|
91
|
+
are backend-owned. A skill that reaches around the platform for those is a bug.
|
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: helix-assets
|
|
3
|
+
description: Source assets for a HELIX world in the right order — platform packages, platform materials, Vault reuse, generation, then vetted CC0 — recording provenance that a script can verify and keeping the bundle inside platform budgets. Use whenever a world needs models, images, materials, environments or audio, when asking where to get an asset or which library is safe to take it from, when deciding whether to generate or reuse, when a model fails to load, or when the bundle is over budget.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# HELIX Assets
|
|
7
|
+
|
|
8
|
+
**Mandatory before any publish, new version, listing or "done": the `helix-gauntlet` loop** (`read_skill({ name: "helix-gauntlet" })`). For an item you publish (a wearable, prop or add-on): bar = product photos at matched angles; renders = its visual-QA frames fitted or placed in the runtime; your own look at each, then a fresh blind critic. Ship only on its PASS.
|
|
9
|
+
|
|
10
|
+
The default failure here is building a world out of grey boxes because sourcing felt like a
|
|
11
|
+
detour. It is not a detour. **Sourcing happens before authoring**, because a scene authored
|
|
12
|
+
around primitives does not get retrofitted with real assets — it gets rebuilt.
|
|
13
|
+
|
|
14
|
+
## Existing external content uses Bridge
|
|
15
|
+
|
|
16
|
+
When the human supplies an existing model, character, animation, scene, mod export, or engine
|
|
17
|
+
project—or asks to bring one into HELIX—use the Bridge tools before writing a converter:
|
|
18
|
+
|
|
19
|
+
1. `bridge_detect`
|
|
20
|
+
2. `bridge_inspect`
|
|
21
|
+
3. `bridge_plan` with every auxiliary input that the import will use
|
|
22
|
+
4. `bridge_import` with the reviewed `deterministicKey` as `expectedPlanKey` and every reported permission whenever a command-backed capability will run, including a packaged official adapter
|
|
23
|
+
5. `bridge_validate`
|
|
24
|
+
|
|
25
|
+
Read `read_doc({ name: "bridge" })` for plugin permissions, capability replacement, provenance,
|
|
26
|
+
and the MetaHuman eye-material path. Bridge is CLI-owned; do not recreate its conversion logic
|
|
27
|
+
inside a world or the MCP package. A command-backed adapter's
|
|
28
|
+
`unrestricted-host-execution` request means exactly that: only pass it when the human's task
|
|
29
|
+
authorizes running the reviewed adapter, even when it is packaged and HELIX Verified.
|
|
30
|
+
|
|
31
|
+
## Order of resort
|
|
32
|
+
|
|
33
|
+
1. **Platform packages** — `list_systems`, `list_abilities`. A behavior that exists as a
|
|
34
|
+
published ability is never re-implemented.
|
|
35
|
+
2. **Platform material pack** — `helix assets materials`, then `helix assets material <id>`. These are versioned,
|
|
36
|
+
validated surface definitions with stable map URLs. Search here before downloading or
|
|
37
|
+
generating any surface look. Keep the working palette bounded (the tool defaults to 12);
|
|
38
|
+
that is a per-world/runtime budget, never a cap on the global catalog or Vault.
|
|
39
|
+
A material may ship several texture resolutions — **you choose which one you get**, see
|
|
40
|
+
**Picking a material's texture resolution** below.
|
|
41
|
+
3. **Vault** — the shared asset library. Run `helix assets search --engine web` before you generate
|
|
42
|
+
anything. See `references/vault-api.md`.
|
|
43
|
+
4. **Generate** — backend-owned and LIX-metered. `helix assets generate` makes a prop or
|
|
44
|
+
character. When approved four-view character art already exists, use
|
|
45
|
+
`helix assets generate-reference <sheet> <prompt>` (or `generate_asset` with
|
|
46
|
+
`referenceSheetPath`) so the backend reuses the checksum-bound sheet and does not invoke
|
|
47
|
+
image generation. If transport fails after a paid job starts, resume that exact job with
|
|
48
|
+
`helix assets resume <job-id>`; it is idempotent and must never be replaced by a second
|
|
49
|
+
generation request. `helix assets generate-image` makes a standalone image;
|
|
50
|
+
`helix assets generate-audio` makes a sound effect, music track, or spoken line; and
|
|
51
|
+
`helix assets generate-material` makes a validated portable PBR bundle;
|
|
52
|
+
`helix assets generate-environment` makes an experimental SPZ environment for a
|
|
53
|
+
compatible renderer. Animation generation is an explicit unavailable boundary. There is
|
|
54
|
+
no animation generation fallback: reuse a skeleton-gated Vault clip or import one.
|
|
55
|
+
5. **A curated CC0 source** — a named author or team publishing their own work. Five of them,
|
|
56
|
+
with query recipes, in **Where CC0 assets actually come from** below.
|
|
57
|
+
6. **Procedural / primitive** — collision proxies, invisible volumes, blockout. Not a look.
|
|
58
|
+
|
|
59
|
+
Descending this list is normal. **Skipping a rung is the defect.** Apply the order separately
|
|
60
|
+
to every identity-bearing asset family: players and avatars, creatures, vehicles, weapons,
|
|
61
|
+
signature props, hero environment kits, and signature audio cue sets. A search or generation
|
|
62
|
+
receipt for one family says nothing about another family.
|
|
63
|
+
|
|
64
|
+
Before authoring one of those families, add one row to the world's asset plan with: family name,
|
|
65
|
+
typed Vault query and result, installed asset id or HELIX generation job receipt, and final
|
|
66
|
+
representation. “We loaded another model” and a passing world-wide loaded-mesh percentage are
|
|
67
|
+
not substitutes for that row. A mesh assembled from Three.js primitives is procedural geometry,
|
|
68
|
+
not a custom model.
|
|
69
|
+
|
|
70
|
+
## Never, in any skill or any world
|
|
71
|
+
|
|
72
|
+
Name a generation vendor. Call a provider API. Ask for or read an API key. Reason about
|
|
73
|
+
moderation policy. Asset generation, provider selection, moderation and LIX metering are
|
|
74
|
+
backend-owned; the platform surface is the whole surface. A world that reaches around it is
|
|
75
|
+
a bug, not a shortcut. **A missing vendor key is never evidence that HELIX generation is
|
|
76
|
+
unavailable.** Only the HELIX tool's own failure or capability-unavailable receipt can establish
|
|
77
|
+
that, because any provider credentials HELIX needs belong on the backend.
|
|
78
|
+
|
|
79
|
+
## Vault — search it first
|
|
80
|
+
|
|
81
|
+
Full endpoint contract, query parameters and the capability probe:
|
|
82
|
+
`references/vault-api.md`. The three things that matter most:
|
|
83
|
+
|
|
84
|
+
- **`engine=web` is the default filter.** It directly answers "give me something usable in
|
|
85
|
+
a web world" and stops you downloading Unreal packages the runtime cannot load.
|
|
86
|
+
- **`kind=audio` exists** — but audio has its own source chain below, which is *not* simply
|
|
87
|
+
"search Vault". Read it.
|
|
88
|
+
- **Install with `helix assets install`.** It downloads through
|
|
89
|
+
`GET /api/v1/vault/assets/:assetId/download`, writes the immutable asset id into the
|
|
90
|
+
world path, and records the receipt. Never grab a raw CDN
|
|
91
|
+
URL, even when the search response hands you one. The 302 is deliberate indirection: it is
|
|
92
|
+
where entitlement and paid-asset gating land. Code that grabs the CDN URL directly breaks
|
|
93
|
+
silently the day paid assets ship, and it breaks for the *creator*, not for you.
|
|
94
|
+
- **Material installs are lean by default.** `install_asset` / `helix assets install` selects the
|
|
95
|
+
runtime KTX2 family. Use `materialRenditions: "source"` / `--material-renditions source` only
|
|
96
|
+
when you need editable PNG maps, or `all` only when the project truly needs both. Selection stays
|
|
97
|
+
in the CLI so checksums, sizes, receipts, and stale-family cleanup remain one authoritative path.
|
|
98
|
+
|
|
99
|
+
The Vault endpoints may not exist on the lane you are running against. **Probe before you
|
|
100
|
+
rely on them** and fall back honestly — never fail the phase because a capability is not
|
|
101
|
+
deployed yet, and never claim you searched a library that returned 404.
|
|
102
|
+
|
|
103
|
+
## Where CC0 assets actually come from
|
|
104
|
+
|
|
105
|
+
Vault is the first reuse path, not the only one, and when that probe returns 404 it is not a
|
|
106
|
+
path at all — so "reuse before you generate" has to mean something else, and it means these.
|
|
107
|
+
All five were exercised end-to-end by two worlds; the counts are live, read from the
|
|
108
|
+
endpoints below, not estimates.
|
|
109
|
+
|
|
110
|
+
| Source | Endpoint | Key | Holds | Licence |
|
|
111
|
+
| --- | --- | --- | --- | --- |
|
|
112
|
+
| **Poly Haven** | `https://api.polyhaven.com` | none | 980 HDRIs · 786 textures · 521 models | CC0 |
|
|
113
|
+
| **ambientCG** | `https://ambientcg.com/api/v2/full_json` | none | 2,004 PBR materials | CC0 |
|
|
114
|
+
| **Kenney** | `kenney.nl` — no API, per-pack ZIP | none | low-poly kits, UI, and the SFX packs the audio chain's rung 2 depends on | CC0 |
|
|
115
|
+
| **Quaternius** | `quaternius.com` — no API, per-pack ZIP | none | stylized low-poly kits, atlas-textured — cheapest against the texture budget | CC0 |
|
|
116
|
+
| **Poly Pizza** | `https://api.poly.pizza/v1` | **free key required** | 10,600+ low-poly models incl. the rescued Google Poly archive | **mixed** CC-BY 3.0 / CC0, per asset |
|
|
117
|
+
|
|
118
|
+
Query recipes, response shapes and the per-source traps: `references/asset-sources.md`. Three
|
|
119
|
+
things worth knowing before you open it:
|
|
120
|
+
|
|
121
|
+
- **Poly Haven is the only source you can budget against before downloading.** All 521 model
|
|
122
|
+
records carry `polycount` and `max_resolution` (`ArmChair_01` is 5,626 tris against an 8k
|
|
123
|
+
character budget) — filter the list response, then fetch. Everywhere else you download first
|
|
124
|
+
and find out second. No free-text search: filter `categories=` and match `tags`. Models come
|
|
125
|
+
as `.gltf` + `.bin` + `textures/`, so fetch the whole `include` map from `/files`, not just
|
|
126
|
+
the `.gltf`; one provenance row on the directory covers the set.
|
|
127
|
+
- **ambientCG ships one material at up to 431 MB.** Its `downloads[]` entries carry `size` in
|
|
128
|
+
bytes — `1K-JPG` is 8.7 MB, `4K-JPG` is 111 MB, against a **50 MB whole-bundle cap**. Take
|
|
129
|
+
1K unless the surface is a hero, and take `_NormalGL`, not `_NormalDX`.
|
|
130
|
+
- **Poly Pizza 401s without a key** — literally `{"error":"You need an API key to do that
|
|
131
|
+
dingus"}`. That is a missing `x-auth-token`, not a dead source; one build read it as
|
|
132
|
+
unavailable and skipped 10,600 models. Its per-asset `licence` is CC-BY 3.0 **or** CC0, so
|
|
133
|
+
read it per asset. CC-BY obliges attribution the *player* can see, and **3.0 has no cure
|
|
134
|
+
period** — where 4.0 reinstates after a fix, a rendering bug that drops the credits panel
|
|
135
|
+
terminates 3.0. No credits surface yet ⇒ take only the CC0 rows.
|
|
136
|
+
|
|
137
|
+
**Not these:** **Sketchfab** — mixed licence with NonCommercial and NoDerivs in it.
|
|
138
|
+
**Objaverse-XL** — largely GitHub scrapes, no licence filtering. **Freesound**,
|
|
139
|
+
**OpenGameArt** — user-upload, so "CC0" is an unverified assertion by an anonymous uploader,
|
|
140
|
+
the same distinction the audio chain draws at rung 2. The line is not free-versus-paid; it is
|
|
141
|
+
**whether the licensor is demonstrably the author**.
|
|
142
|
+
|
|
143
|
+
**Three licence questions, and agents reliably ask only the first:**
|
|
144
|
+
|
|
145
|
+
1. **Commercial use?** — kills NonCommercial.
|
|
146
|
+
2. **Modification?** — kills NoDerivs, and this is the one that gets missed. We decimate,
|
|
147
|
+
generate LOD chains and atlas textures; each is plausibly a derivative. **CC-BY-ND permits
|
|
148
|
+
commercial use and still bars us**, so "it's fine, it's commercial-OK" is not an answer.
|
|
149
|
+
3. **Can the world shipping it stay proprietary?** — kills ShareAlike and GPL.
|
|
150
|
+
|
|
151
|
+
CC0 clears all three. Everything else gets checked against all three, per asset.
|
|
152
|
+
|
|
153
|
+
## Provenance — recorded, and verified by a script
|
|
154
|
+
|
|
155
|
+
Every asset in the world carries one of these values. This vocabulary exists so an agent
|
|
156
|
+
cannot describe a file it never shipped:
|
|
157
|
+
|
|
158
|
+
| Value | Means |
|
|
159
|
+
| --- | --- |
|
|
160
|
+
| `vault` | downloaded from Vault through the `/download` endpoint |
|
|
161
|
+
| `generated` | produced this session by the platform's generation path |
|
|
162
|
+
| `curated` | from a named allowlist source; the licensor is the author |
|
|
163
|
+
| `procedural` | authored by code or algorithm — no third-party source to attribute. Either built at runtime (no file ships) or baked to a file offline |
|
|
164
|
+
| `declared, not shipped` | **source references a path that is not in the bundle** |
|
|
165
|
+
|
|
166
|
+
`declared, not shipped` is the whole point of the enum. It names the exact failure where a
|
|
167
|
+
world's code points at `assets/hero.glb`, the report says the hero was sourced, and the file
|
|
168
|
+
does not exist.
|
|
169
|
+
|
|
170
|
+
**The axis this vocabulary splits on is *what authored the asset*, not *whether a file
|
|
171
|
+
ships*.** That is why `procedural` covers both a noise bed synthesised at runtime and the same
|
|
172
|
+
bed baked to a `.wav` at build time: the question provenance answers is "whose work is this,
|
|
173
|
+
and under what licence" — and for both of those the answer is "ours, none needed". Splitting
|
|
174
|
+
them into two values would divide the set along an axis nobody consuming provenance cares
|
|
175
|
+
about, while leaving the licence question exactly where it was. Ship-or-not is already
|
|
176
|
+
recorded, precisely, by whether the row's `path` resolves in `dist/`.
|
|
177
|
+
|
|
178
|
+
**Where it is recorded:** `public/helix.assets.json`, in **two arrays that answer different
|
|
179
|
+
questions**.
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{ "version": 1,
|
|
183
|
+
"vaultAssets": [
|
|
184
|
+
{ "assetId": "…", "version": 3, "kind": "prop",
|
|
185
|
+
"artifactKey": "vault-assets/…/v3/model.glb", "integrity": "sha256-…" }
|
|
186
|
+
],
|
|
187
|
+
"assets": [
|
|
188
|
+
{ "path": "assets/models/relic/relic.glb", "provenance": "generated",
|
|
189
|
+
"source": "generation job <id>", "licence": "platform-generated",
|
|
190
|
+
"notes": "6,037 tris · 4 textures" },
|
|
191
|
+
{ "path": "assets/models/moon_rock_02", "provenance": "curated",
|
|
192
|
+
"source": "<named CC0 pack / author page>", "licence": "CC0",
|
|
193
|
+
"attribution": "<author names>" },
|
|
194
|
+
{ "path": "assets/audio/wind.wav", "provenance": "procedural",
|
|
195
|
+
"source": "synthesised in-session — pink noise, filtered, loop-faded" },
|
|
196
|
+
{ "assetId": "…", "path": "assets/props/lamp.glb", "provenance": "vault",
|
|
197
|
+
"source": "Vault", "licence": "<as returned by Vault>" }
|
|
198
|
+
] }
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
- **`vaultAssets[]` is a locked machine contract** — `helix assets install` writes it and the Vault
|
|
202
|
+
session consumes it at publish to attribute usage. Its shape does not change. Never add
|
|
203
|
+
fields to it.
|
|
204
|
+
- **`assets[]` is the provenance table**, one row per asset, and it is the only place four of
|
|
205
|
+
the five provenance values can be recorded at all. `path` is bundle-relative and may name a
|
|
206
|
+
**directory** to cover everything under it (a Poly Haven model is a `.gltf` + a `.bin` + a
|
|
207
|
+
textures folder; one row) or contain a **`*`** to cover a set from one source
|
|
208
|
+
(`assets/materials/asphalt_*.jpg` — one licence, three maps). `procedural` rows omit `path`
|
|
209
|
+
when nothing ships.
|
|
210
|
+
|
|
211
|
+
`helix world audit --assets` verifies the **whole table, in both directions**: every declared
|
|
212
|
+
row must resolve to something in `dist/`, and every media file in `dist/` must be covered by
|
|
213
|
+
a row. It fails on an unknown provenance value, on a `curated` row with no source or licence,
|
|
214
|
+
on an explicit `declared, not shipped`, and on an absent table when the bundle ships media.
|
|
215
|
+
An asset nobody wrote down is exactly as much of a gap as a row that points at nothing.
|
|
216
|
+
|
|
217
|
+
**Credits are generated, never hand-written:**
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
helix assets credits --out ASSET-CREDITS.md
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Writing a credits file by hand and recording provenance in a schema is the same work done
|
|
224
|
+
twice, and the hand-written copy is the one that goes stale. The JSON is the source of truth;
|
|
225
|
+
the markdown is a render of it.
|
|
226
|
+
|
|
227
|
+
**Not in `helix.lock.json`, and this is load-bearing.** `install_world_packages` wipes and
|
|
228
|
+
rebuilds its own outputs deterministically, so a Vault section inside the lockfile would be
|
|
229
|
+
erased by a routine `--update`. At publish time that erasure is indistinguishable from the
|
|
230
|
+
creator having deleted every asset — which would zero their usage counts and later their
|
|
231
|
+
payouts. A sibling file we own cannot be wiped by a tool that does not know it exists.
|
|
232
|
+
|
|
233
|
+
`public/` is copied verbatim into `dist/` and `json` is in the manifest extension allowlist,
|
|
234
|
+
so the file reaches the bundle by the same route `public/helix.json` already takes.
|
|
235
|
+
|
|
236
|
+
**Two invariants, rules not suggestions:**
|
|
237
|
+
|
|
238
|
+
- **Written whole or not at all.** On any mid-install failure, leave the previous file
|
|
239
|
+
untouched rather than writing a partial one. A present, valid, empty array must genuinely
|
|
240
|
+
mean "this world uses no Vault assets".
|
|
241
|
+
- **Absence is never destructive.** A missing or unparseable file means *unknown*, never
|
|
242
|
+
*empty*. Anything consuming it treats absence as "change nothing".
|
|
243
|
+
|
|
244
|
+
**Where Vault is deployed, generated assets auto-publish to it by default**
|
|
245
|
+
(`vaultAutoPublishGeneratedAssets`, database default true); only an explicit false turns it off at
|
|
246
|
+
`/account#vault-auto-publish` under "Vault". This is shipped behaviour, not
|
|
247
|
+
forthcoming — but it is gated by the **same capability probe** as the rest of Vault
|
|
248
|
+
(`references/vault-api.md`), and on a lane where that probe 404s the generation job parks at
|
|
249
|
+
`preview_ready` with `publish` still queued and `publishedItemId` null.
|
|
250
|
+
|
|
251
|
+
So: **read `vaultAssetId` before you say an asset is in the library.** `publishedItemId`
|
|
252
|
+
identifies the storefront/product row and is not the reusable Vault handle. A default-on
|
|
253
|
+
registration failure is a failed operation, not a green generation with a warning; an
|
|
254
|
+
explicit opt-out is reported as `vaultAutoPublish: "disabled"`.
|
|
255
|
+
|
|
256
|
+
## Audio — an ordered chain, and a hard safety rail
|
|
257
|
+
|
|
258
|
+
Jack's rule stands: every player action, pickup, hit, UI press, state change and ambience
|
|
259
|
+
has a sound. A silent world is a defect. But *where* the sound comes from is gated.
|
|
260
|
+
|
|
261
|
+
**The reason, so you can handle a case this list does not cover:** audio is currently
|
|
262
|
+
unmoderated on the platform, on the assumption that it was moderated upstream by the
|
|
263
|
+
generation provider. That assumption holds only for audio we can confirm was generated.
|
|
264
|
+
|
|
265
|
+
Take the first rung that works:
|
|
266
|
+
|
|
267
|
+
1. **Generated in-session with `helix assets generate-audio`.** Provenance known by construction.
|
|
268
|
+
Preferred. Choose `--mode sound_effect` for one-shots/loops, `--mode music` for a track, or
|
|
269
|
+
`--mode text_to_speech --text ... --voice-id ...` for spoken audio. The CLI owns the
|
|
270
|
+
long-running job and default-on Vault publication; the MCP tool invokes that same packaged
|
|
271
|
+
command rather than carrying a second provider client. Before text-to-speech, call
|
|
272
|
+
`list_voices`, select a returned id, and use one of that voice's supported language codes.
|
|
273
|
+
Follow the opaque next-page token when needed; never invent an id or attempt cloning. TTS
|
|
274
|
+
language codes are lowercase two-letter codes, output is one of the portable MP3 formats, and
|
|
275
|
+
pronunciation dictionary ids must be unique (maximum three). If the backend reports that audio
|
|
276
|
+
generation is not configured, continue down this chain; never call a provider directly.
|
|
277
|
+
2. **A curated allowlist pack** — a single named author publishing hand-authored packs
|
|
278
|
+
(Kenney's SFX packs; see the source table above). The licensor is demonstrably the
|
|
279
|
+
author. Categorically different from a
|
|
280
|
+
user-upload site where the licence is an unverified assertion by an anonymous uploader;
|
|
281
|
+
apply that distinction, do not pattern-match on the string "CC0".
|
|
282
|
+
3. **Synthesised Web Audio.** A short procedural blip from an `OscillatorGain` envelope.
|
|
283
|
+
Always available, always safe, and it fully satisfies "every interaction has a sound".
|
|
284
|
+
This is a correct answer, not a placeholder.
|
|
285
|
+
4. **Vault `kind=audio`** — **only when the asset's origin is verifiably `generated`.**
|
|
286
|
+
Direct byte-first Vault uploads are retired. Generated assets are the supported route here;
|
|
287
|
+
Vault audio is trusted only when the server confirms its generated origin. Use
|
|
288
|
+
`GET /api/v1/vault/assets?kind=audio&engine=web&origin=generated` — filter server-side,
|
|
289
|
+
never fetch broadly and filter in the client. **A missing or unknown `origin` reads as
|
|
290
|
+
`uploaded`, not `generated`**: backfilled rows whose origin cannot be determined are
|
|
291
|
+
deliberately assigned the less-trusted value, and a safety field that guesses in the
|
|
292
|
+
trusting direction is decorative. If the response carries no `origin` field at all, this
|
|
293
|
+
rung is unavailable on this lane — drop to rung 3.
|
|
294
|
+
|
|
295
|
+
**Never:** arbitrary internet audio, arbitrary user-uploaded audio, a general-purpose upload
|
|
296
|
+
platform. If you find yourself weighing whether a source is probably fine, it is rung 3.
|
|
297
|
+
|
|
298
|
+
## Picking a material's texture resolution
|
|
299
|
+
|
|
300
|
+
A platform material can carry more than one texture resolution. Pass `resolution` to
|
|
301
|
+
`list_materials` / `use_material` (or `--resolution` to `helix assets materials` / `helix assets
|
|
302
|
+
material <id>`) to choose. Omit it and you get the pack's declared default.
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
helix assets material brick-block # the pack's default
|
|
306
|
+
helix assets material brick-block --resolution 2k # the 2K variant's map URLs
|
|
307
|
+
helix assets material brick-block --resolution 2048 # same — numeric spelling
|
|
308
|
+
helix assets materials --resolution 2k --limit 12 # only materials carrying 2K
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Valid values are the keys the catalog declares (`1k`, `2k`, …), case-insensitively, plus `1024`
|
|
312
|
+
and `2048` as aliases. Every material in the output carries a `resolution` block naming what was
|
|
313
|
+
resolved and what else exists, so **read it instead of assuming**:
|
|
314
|
+
|
|
315
|
+
```jsonc
|
|
316
|
+
"resolution": {
|
|
317
|
+
"requested": "2k", "resolved": "2k", "pixels": 2048, "default": "1k",
|
|
318
|
+
"available": [{ "key": "1k", "pixels": 1024 }, { "key": "2k", "pixels": 2048 }],
|
|
319
|
+
"applicable": true
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Three behaviors you can rely on, and must not work around:
|
|
324
|
+
|
|
325
|
+
- **A resolution the material does not carry is an error** that lists the ones it does. You will
|
|
326
|
+
never be handed a different resolution than the one you asked for. If you get that error, pick
|
|
327
|
+
from the listed values — do not retry blindly and do not assume the pack is broken.
|
|
328
|
+
- **A pack with a single resolution reports it as `default`**, not `1k`. Older packs
|
|
329
|
+
(`schemaVersion: 1`) do not state their texture size, so neither does the tooling. `--resolution
|
|
330
|
+
1k` against such a pack fails on purpose rather than guessing.
|
|
331
|
+
- **Procedural materials (`kind: glass`, `procedural_water`) are not offered.** The listing names
|
|
332
|
+
them under `unlisted`, and resolving one by id is an error saying why. Pick a photographed or
|
|
333
|
+
scanned material instead; `--include-unlisted` exists for maintainers, not for world building.
|
|
334
|
+
|
|
335
|
+
Bigger is not better: texture memory is a published budget (see the device-profile table below).
|
|
336
|
+
Reach for 2K on hero surfaces the camera gets close to, and leave background surfaces at the
|
|
337
|
+
default.
|
|
338
|
+
|
|
339
|
+
## Choosing a representation
|
|
340
|
+
|
|
341
|
+
| Need | Representation |
|
|
342
|
+
| --- | --- |
|
|
343
|
+
| Hero character, player, named NPC | sourced or generated rigged mesh; verify skeleton and scale |
|
|
344
|
+
| Environment kit, architecture, terrain dressing | sourced environment/prop set; instance repeats |
|
|
345
|
+
| Non-identity support prop whose sockets, collision or code-driven variants matter | procedural three.js geometry, deliberately |
|
|
346
|
+
| Repeated background filler | one sourced mesh, instanced — not twelve near-duplicates |
|
|
347
|
+
| Surface look | a real PBR material: `map` + `roughness` + `metalness`, or a sourced material |
|
|
348
|
+
| Collision proxy, trigger volume, blockout | primitive — invisible or temporary |
|
|
349
|
+
|
|
350
|
+
## The honest fallback
|
|
351
|
+
|
|
352
|
+
For an identity-bearing family, “unavailable” means that family's typed Vault search found no
|
|
353
|
+
suitable result **and** that family's HELIX generation call returned a real failure or
|
|
354
|
+
capability-unavailable result. Record both receipts. A missing external-provider key, another
|
|
355
|
+
family's receipt, or a favorable whole-world primitive ratio proves nothing.
|
|
356
|
+
|
|
357
|
+
Only then may a placeholder preserve **footprint, height, pivot, collider, sockets, facing,
|
|
358
|
+
motion timing and state readability**. It must be visually unmistakable and log itself exactly
|
|
359
|
+
once:
|
|
360
|
+
|
|
361
|
+
```ts
|
|
362
|
+
console.warn('[helix-assets] fallback: hero — Vault returned no engine=web character; using blockout capsule');
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
The log line is the artifact — and it is checked **at runtime, not in your source**. Writing
|
|
366
|
+
the fallback exactly as prescribed creates a log *site*; only a site that *fired* means a
|
|
367
|
+
placeholder actually stood in. `helix world source-audit --console qa/playtest/console.txt` reads the
|
|
368
|
+
console output your playtest captured and fails when a fallback fired there. (An earlier
|
|
369
|
+
version counted log sites in source, so following this section earned a permanent warning
|
|
370
|
+
claiming placeholders were standing in on a world where none ever ran. A gate that fires on
|
|
371
|
+
correct code teaches agents to stop writing correct code.) **Never silently substitute a
|
|
372
|
+
mismatched asset, and never call placeholder visuals production-ready.**
|
|
373
|
+
|
|
374
|
+
## Loader allowlist — GLTFLoader + KTX2Loader, and nothing else
|
|
375
|
+
|
|
376
|
+
A Draco- or meshopt-compressed model **fails to load at runtime**. Treat it as a blocking
|
|
377
|
+
error the moment you obtain the file, never as a warning to revisit:
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
helix assets check-loaders public/assets # exits non-zero on the first blocker
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
It reads **both** container forms, which is the point: `.glb` is a binary wrapper whose JSON
|
|
384
|
+
chunk length sits at byte 12, and `.gltf` is plain JSON beside a `.bin`. Poly Haven — the
|
|
385
|
+
most obvious CC0 model source there is — ships the second form, and a `readUInt32LE(12)`
|
|
386
|
+
one-liner throws on it rather than reporting anything.
|
|
387
|
+
|
|
388
|
+
Signatures: `KHR_draco_mesh_compression` or `EXT_meshopt_compression` in
|
|
389
|
+
`extensionsRequired`; at runtime, `THREE.GLTFLoader: No DRACOLoader instance provided`.
|
|
390
|
+
Textures ship as KTX2 or as png/jpg/webp — nothing else transcodes.
|
|
391
|
+
|
|
392
|
+
**`gltf-transform optimize` compresses with meshopt by default** — the exact compression this
|
|
393
|
+
runtime cannot decode. It is the tool you will reach for the moment you need to hit a poly or
|
|
394
|
+
texture budget, and it silently produces a model that does not load:
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
gltf-transform optimize in.glb out.glb --compress false --instance false
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Both flags are mandatory here. Run `helix assets check-loaders` after any pipeline step that rewrites
|
|
401
|
+
a model, not just after downloading one.
|
|
402
|
+
|
|
403
|
+
## Portable environment formats
|
|
404
|
+
|
|
405
|
+
**`.hdr` and `.exr` are first-class bundle formats.** Use `.hdr` when RGBE radiance is
|
|
406
|
+
enough and `.exr` when the source needs half/float precision or additional channels.
|
|
407
|
+
Keep the native radiance file for lighting and add a small tone-mapped JPEG/PNG only when
|
|
408
|
+
the UI needs a quick thumbnail:
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
# Lighting artifact: load with RGBELoader/EXRLoader + PMREM
|
|
412
|
+
# Thumbnail: deliberately tone-map a separate preview
|
|
413
|
+
ffmpeg -i sky.hdr -vf "tonemap=hable,scale=2048:1024" -q:v 3 public/assets/env/sky.jpg
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
Do not disguise HDR bytes as `.bin`: the extension is now allowlisted and native MIME
|
|
417
|
+
metadata is part of the portable contract. Record any tone-map transform in the preview
|
|
418
|
+
provenance row.
|
|
419
|
+
|
|
420
|
+
**Probe your encoders before choosing an audio format.** `.ogg` is in the allowlist, but a
|
|
421
|
+
given ffmpeg build may have no Vorbis encoder — `libvorbis` is a build-time option, and its
|
|
422
|
+
absence surfaces as a bare "Unknown encoder" mid-pipeline:
|
|
423
|
+
|
|
424
|
+
```bash
|
|
425
|
+
ffmpeg -hide_banner -encoders | grep -E 'libvorbis|libmp3lame|vorbis'
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Without it: **`.wav` for anything that loops** (an ambient bed, an engine hum) because it is
|
|
429
|
+
uncompressed and therefore gapless, and **`.mp3` for one-shots** where its encoder-delay
|
|
430
|
+
padding is inaudible. Never use `.mp3` for a gapless loop — the padding is the click you will
|
|
431
|
+
then spend an hour chasing. Budget for the size: a 10 s stereo `.wav` bed is ~1.7 MB against
|
|
432
|
+
a 50 MB bundle cap.
|
|
433
|
+
|
|
434
|
+
## Budgets
|
|
435
|
+
|
|
436
|
+
**Bundle** (enforced by `validate_world` at validate *and* publish): ≤200 files, ≤25 MB per
|
|
437
|
+
file, ≤50 MB total, no empty files, extension allowlist only.
|
|
438
|
+
|
|
439
|
+
**Scene-local resources** (enforced for `.helix-scene.json` publishes). Every reference is
|
|
440
|
+
an immutable `{assetId, version, checksumSha256}` pin. The backend downloads and hashes that
|
|
441
|
+
exact version, validates the referenced Vault kind, and derives these totals from stored
|
|
442
|
+
artifacts; client-supplied totals are only estimates and cannot attest compliance:
|
|
443
|
+
|
|
444
|
+
| Device profile | Unique materials | Draw calls | Decoded texture memory | Triangles | Particles |
|
|
445
|
+
| --- | ---: | ---: | ---: | ---: | ---: |
|
|
446
|
+
| mobile | 48 | 300 | 256 MiB | 750,000 | 5,000 |
|
|
447
|
+
| desktop | 128 | 1,200 | 1 GiB | 4,000,000 | 50,000 |
|
|
448
|
+
| cinematic | 256 | 4,000 | 4 GiB | 15,000,000 | 250,000 |
|
|
449
|
+
|
|
450
|
+
The Vault catalog can grow without limit; a world cannot. Start from the small built-in
|
|
451
|
+
material palette, reuse the same materials and texture sets across related surfaces, and
|
|
452
|
+
instance repeated meshes. Do not interpret “Vault contains thousands of materials” as
|
|
453
|
+
permission to use thousands of unique materials in one scene. These descriptor limits are
|
|
454
|
+
publish-contract ceilings, not performance promises; the live world performance gate still
|
|
455
|
+
applies and may be stricter for a specific runtime.
|
|
456
|
+
|
|
457
|
+
**VFX resources** use the renderer-neutral `.helix-vfx.json` draft and immutable texture or
|
|
458
|
+
decal pins with the same exact-version/checksum validation. They do not claim Unreal support:
|
|
459
|
+
no Unreal executor exists yet. Current hard ceilings are intentionally bounded:
|
|
460
|
+
|
|
461
|
+
| Device profile | Live particles | Spawn rate/sec | Max lifetime |
|
|
462
|
+
| --- | ---: | ---: | ---: |
|
|
463
|
+
| mobile | 1,250 | 500 | 15 s |
|
|
464
|
+
| desktop | 12,500 | 2,500 | 30 s |
|
|
465
|
+
| cinematic | 62,500 | 10,000 | 60 s |
|
|
466
|
+
|
|
467
|
+
The backend also rejects `spawn rate × maximum lifetime > live particles`, so a short burst
|
|
468
|
+
cannot smuggle in an unbounded steady-state effect.
|
|
469
|
+
|
|
470
|
+
**Platform assets are REJECTED if bundled.** Character bodies, faces, the 46 locomotion
|
|
471
|
+
clips and system textures stream from the CDN. A `.glb` or `.ktx2` under `helix_modules/`
|
|
472
|
+
fails validation — only installed *code* lives there. The built bundle of a character world
|
|
473
|
+
contains no `.glb` and no `.ktx2` from the platform.
|
|
474
|
+
|
|
475
|
+
**Characters** (what `import_character` targets, and what a sourced rig must meet): 8k
|
|
476
|
+
triangles, 75 bones, 4 skin influences per vertex, 3 draw calls. Textures capped at 1024px
|
|
477
|
+
after optimization. `import_character` conforms an external biped onto `helix-humanoid@1`
|
|
478
|
+
at its own proportions, generates the LOD chain and KTX2-encodes textures — use it rather
|
|
479
|
+
than hand-conforming a rig.
|
|
480
|
+
|
|
481
|
+
**LIX.** Generation is metered. Budget it: search Vault first, generate once with a good
|
|
482
|
+
prompt rather than three times with vague ones, and reuse across surfaces where the asset
|
|
483
|
+
genuinely fits. Do not regenerate to explore. The cost is the creator's.
|
|
484
|
+
|
|
485
|
+
## Definition of done
|
|
486
|
+
|
|
487
|
+
`public/helix.assets.json` exists and parses · every non-`procedural` provenance row resolves
|
|
488
|
+
to a path in `dist/` · **every media file in `dist/` is covered by a provenance row** ·
|
|
489
|
+
`ASSET-CREDITS.md` regenerated from it · `validate_world` passes the bundle budget · no model
|
|
490
|
+
in the bundle requires Draco or meshopt (`helix assets check-loaders` exit 0) · every audio cue
|
|
491
|
+
referenced in code resolves to a file that ships, or is synthesised.
|