Reference
ws://localhost:8765 (the broker). JSON, one message per line, both directions. Your engine sends state; the brain sends commands. Engines without sockets: run an HTTP shim on localhost:8766 that accepts POST /state and replies {"commands":[...]} with everything queued since the last post; pipeline/skyrim_bridge.py is the reference.
| field | required | meaning |
|---|---|---|
| type | yes | "state" |
| tab | yes | one id per running game instance |
| world | yes | equals the manifest's world |
player.x, player.z | yes | position on the horizontal plane, engine units |
| player.armed | yes | the threat rung |
| npcs[].i | yes | slot index; commands come back on it |
| npcs[].name | yes | identity (journal, manifest, receipts); crowd rows may be null |
npcs[].x, .z | yes | position |
| npcs[].handle | recommended | your stable id; echoed on commands |
| npcs[].armed | recommended | threat rung |
| npcs[].activeExpert | recommended | what the body is doing now |
| npcs[].identity | optional | { "klass": "...", "guard": true, "city": "..." } seeds an archetype |
npcs[].location, .loctype, .interior | optional | place tags for rules |
| objects[] | optional | { "label", "x", "z", "kind", "layer" }: things a plan can go to |
| cmd | fields | you do |
|---|---|---|
| loadScenario | npc, name, handle, title, target, modes[{cond, expert, target?, focus?}] | execute expert against the target until the next one for that slot |
| say | npc, name, text, title | render the line |
| castRole | name, modes | a scene lease (Architect); optional |
setSituation, setGoal, toast, buildScene | Architect and scenes; optional |
Engine actions (modes[].expert): approach, navigate-to, follow, guard, evade, retreat, confront, say, lead, sit, eat, lean, wander2, approach-object, guard-object.
lw serve (localhost:8767)| route | body | returns |
|---|---|---|
| POST /v1/perceive | {char, text} | the journal entry with its class and weight |
| POST /v1/command | {char, text} | an order on the command lane, two-minute lease |
| POST /v1/trigger | {cls, char, by?, value?, text?, dry_run?} | the chain as text |
| GET /v1/receipts | server-sent events: receipt, flywheel | |
| GET /v1/watch | one lw watch snapshot as rows | |
| GET /v1/mind/<char> | journal, beliefs, last receipt, open intention, the rendered why | |
| POST /v1/manifest/check | {manifest, executes?, adapter?} | the load report as JSON |
| GET /v1/health | broker, embedder, student, wake, session |
Errors: {"code": "LWxxxx", "error": "..."}.
localhost:8094)POST /start {name, persona, ocean?} → {session_id}. POST /turn {session_id, player_input} → server-sent events; the line is in response_generated.text, with cites and rejected.
lw mcp)The same operations as MCP tools over stdio: lw_health, lw_perceive, lw_command, lw_trigger, lw_watch, lw_mind, lw_manifest_check, lw_verbs, lw_verb_request, and read-only lw_escalations and lw_train_status. Not tools, by design: sending data up (lw sync --send) and requesting a retrain (lw train) are done by a person at the terminal.
LW_WAKE_URL)POST /v1/wake with Authorization: Bearer <key>: the wake request (pipeline/wake_client.py is the contract). 402 when the key is missing or lapsed. Also /v1/licence/activate, /v1/flywheel/<lane>, /v1/train, /v1/train/status, /v1/verbs (a verb request), /v1/trial, /v1/billing/checkout and /v1/billing/portal (with the key), /v1/billing/webhook (Stripe).