WorldPacks
WorldPacks are the authoring unit for Cenosis worlds — ZIP archives containing all the data that defines a game world: personas, locations, objects, storylets, relationships, and custom needs. All data, zero code.
Pack Format
A WorldPack is a ZIP archive named <pack-id>-<version>.npcpack. Every file inside is validated JSON against the engine's bundled JSON schemas. No executables, no scripts — the v0.4 security boundary does not move.
my-village-1.0.0/
├── manifest.json # Required: id, version, dependencies, namespace
├── personas/ # NPC definitions, schedules, needs, constraints
│ ├── innkeeper.json
│ └── blacksmith.json
├── locations/ # Place graph with smart objects + affordances
│ ├── tavern.json
│ └── smithy.json
├── objects/ # Sims-style interactive objects advertising actions
│ └── forge.json
├── storylets/ # Director trigger/effect data
│ └── rivalry.json
├── beliefs/ # Seeded agent beliefs
├── needs/ # Custom need definitions (v1.6+)
├── relationships/ # Seeded relationships + kind definitions
├── conversations/ # Reusable conversation templates
├── storylines/ # Storyline templates
├── factions/ # Faction definitions + stance
├── economy/ # Goods, recipes, jobs (requires economy_enabled)
├── lifecycle/ # Life stages, kinship definitions
├── institutions/ # Businesses, norms, governance roles
└── schemas/ # JSON schema definitions for this pack's entities
manifest.json
{
"id": "my.village",
"version": "1.0.0",
"namespace": "my",
"display_name": "My Village",
"description": "A small fishing village with a dark secret.",
"requires": {
"cenosis.base": "^4.0.0"
},
"optional": {
"cenosis.economy": "^2.1.0"
}
}
| Field | Description |
|---|---|
id | Globally unique, dot-separated identifier. Convention: author.packname. |
version | Semver string. Used for dependency resolution. |
namespace | Short prefix for entity addressing. Entities are referenced as namespace.name. |
requires | Hard dependencies with semver range constraints. |
optional | Soft dependencies — pack degrades gracefully if absent. |
Personas
A persona defines a single NPC's identity, schedule, needs, and constraints.
{
"id": "my.innkeeper",
"name": "Mira",
"description": "Runs the Golden Goose tavern. Practical, secretive.",
"traits": ["pragmatic", "secretive", "hospitable"],
"honesty": 0.6,
"hard_constraints": [
{ "type": "ForbidTopic", "topic": "the_murder" }
],
"schedule": [
{ "hour": 6, "activity": "prep_tavern", "location": "my.tavern" },
{ "hour": 10, "activity": "serve_customers", "location": "my.tavern" },
{ "hour": 22, "activity": "sleep", "location": "my.tavern_quarters" }
],
"needs": {
"social": { "initial": 0.7, "decay_rate_per_hour": 0.03 },
"safety": { "initial": 0.4, "decay_rate_per_hour": 0.01 }
}
}
Storylets
Storylets are data-driven drama events. They define conditions and effects — no code, no scripting:
{
"id": "my.rivalry_confrontation",
"cooldown_hours": 24,
"weight": 1.5,
"conditions": [
{ "type": "RelationshipBelow", "dimension": "Trust", "threshold": 0.3 },
{ "type": "AgentAtLocation", "location": "my.tavern" },
{ "type": "TimeOfDay", "after": 18, "before": 23 }
],
"effects": [
{ "type": "MoodDelta", "dimension": "tension", "delta": 0.3 },
{ "type": "RelationshipDelta", "dimension": "Fear", "delta": 0.2 },
{ "type": "InjectDialogSeed", "seed": "You know what you did." }
]
}
Smart Objects & Affordances
Locations contain SmartObjects that advertise affordances — possible actions an agent can take. This is the Sims-style model: agents pick from advertised affordances, never invent actions.
{
"id": "my.hearth",
"display_name": "Tavern Hearth",
"affordances": [
{
"action": "warm_by_fire",
"need_deltas": { "comfort": 0.15, "social": 0.05 },
"mood_delta": 0.1,
"duration_minutes": 20
}
]
}
Custom Needs (v1.6)
Any pack can define additional need types. They integrate fully with the planner, director triggers, and LLM prompts — no engine changes required.
{
"id": "my.safety",
"display_name": "Safety",
"decay_rate_per_hour": 0.01,
"initial_value": 0.8,
"critical_threshold": 0.2
}
Dependency Resolution
The DependencyResolver performs deterministic backtracking constraint solving of semver requires ranges across packs:
- Newest-compatible version selection with lexical tie-breaking
- Same algorithm as the v3.2
CapabilityRegistryfor extensions - Deterministic by design — same pack set always resolves to the same versions
Namespace Registry
Entities are addressed as <namespace>.<name>. The NamespaceRegistry denies cross-pack references outside the dependency closure — you cannot reference other.tavern unless other is in your requires or optional.
URL Pack Installation (v1.0)
Packs can be installed from a URL directly over the C ABI:
int npc_engine_install_pack(EngineHandle*, const char* url_with_hash);
// URL format: https://cdn.example.com/my-village-1.0.0.npcpack#<blake3-hex>
Downloads are: Blake3-verified, capped at 10 MB, extracted through a strict JSON-only sandbox, cached by checksum, then validated by the pack loader. The preflight check prevents partial installs on buffer-size errors.
Visual Pack Editor
The editor/ directory contains a Tauri 2 desktop shell with a full-featured GUI for authoring WorldPacks:
- Schema validation as-you-type with inline error markers
- Cross-reference checking — warns on undefined entity references
- Live preview — ticks a headless engine instance and reports per-agent need/relationship deltas
- Lossless open/save round-tripping — JSON key order is preserved
Detached workspace
The editor is in a detached Tauri workspace (editor/) excluded from the main engine workspace — Tauri/WebView dependencies don't affect cargo check/clippy/test --workspace.