← Home · docs/guides/observer_guide.md

Observer & Controller Guide

How to observe, control, and intervene in a running Singing Bird simulation from outside.

Starting the API Server

# Basic (open access)
PYTHONPATH=src uvicorn singing_bird.api.app:app --host 0.0.0.0 --port 8420

# With auth enabled
SINGING_BIRD_API_TOKEN=my-secret PYTHONPATH=src uvicorn singing_bird.api.app:app --port 8420
# Then pass: Authorization: Bearer my-secret

The OpenAPI docs are at http://localhost:8420/docs.

Auth and Scopes

Set SINGING_BIRD_API_TOKEN to enable bearer token auth. Three scopes:

Scope Access
read Query lane only: overview, resolve, objects, events, reports, stream
operate Read + control: step, pause, resume, turns, sessions, restore
admin Full access: patches, stimuli, inner monologue, belief injection

When auth is disabled (no token set), all requests have admin access.

Core Concepts

Three Execution Lanes

Lane Purpose Safety
Query Read-only inspection No mutation
Turn External turns routed through the director Full cycle with rollback
Admin patch Direct world/synth modification Validated, audited, transactional

Sessions

One session = one simulation lineage. Each session owns a scenario, a runtime, a snapshot chain, and an audit trail.

Stable Refs

Every entity, location, synth, and process has a stable UUID ref. Use refs for all write operations. Use resolve for name → ref lookup.

Workflow

1. Create a Session

curl -X POST http://localhost:8420/v1/sessions \
  -H 'Content-Type: application/json' \
  -d '{"scenario_path": "scenarios/lighthouse.yaml"}'

Returns session_id. The session starts paused.

2. Inspect the World

# Overview: synths, environment, time
curl http://localhost:8420/v1/sessions/{sid}/overview

# Resolve a name to refs
curl "http://localhost:8420/v1/sessions/{sid}/resolve?q=Thomas&kind=synth"

# Get synth detail (beliefs, mood, memories, available actions)
curl http://localhost:8420/v1/sessions/{sid}/objects/synth/{ref}

# Get location detail (entities, adjacency)
curl http://localhost:8420/v1/sessions/{sid}/objects/location/{ref}

# Get entity detail (components, containment)
curl http://localhost:8420/v1/sessions/{sid}/objects/entity/{ref}

3. Step Forward

# Advance one cycle
curl -X POST http://localhost:8420/v1/sessions/{sid}/step \
  -d '{"n": 1}'

# Advance three cycles
curl -X POST http://localhost:8420/v1/sessions/{sid}/step \
  -d '{"n": 3}'

Each step runs a full director cycle: perception → LLM cognition → intent resolution → commit.

4. See What Happened

# Last cycle report (deterministic, from audit trail)
curl http://localhost:8420/v1/sessions/{sid}/reports/last-cycle

# Raw audit events since sequence N
curl "http://localhost:8420/v1/sessions/{sid}/events?since=0&limit=20"

The last-cycle report includes: - World events (weather changes, process transitions) - Synth responses (inner monologue, mood, chosen intents) - Speech (who said what, who heard it) - Actions (what each synth did, what happened) - Belief changes (what changed, how they learned it)

5. Intervene

All interventions go through apply_patch with typed operations:

curl -X POST http://localhost:8420/v1/sessions/{sid}/patches \
  -H 'Content-Type: application/json' \
  -d '{
    "operations": [
      {"op": "move_entity", "entity_ref": "abc123", "destination_ref": "def456"}
    ],
    "mode": "commit"
  }'

Patch Modes

Mode What happens
dry_run Validate only — no mutation, no snapshot
commit Apply + validate + commit snapshot
commit_and_step Apply + commit + run one cycle

Available Operations

Op Required fields What it does
move_entity entity_ref, destination_ref Teleport entity to location
create_entity name, location_ref Spawn new entity (optional: components, description, mobility)
remove_entity entity_ref Remove entity from world
set_component_property entity_ref, component_type, property, value Change a component property (e.g., fix a lamp)
set_environment_field field_path, value Change environment (e.g., weather.conditionstorm)
add_belief synth_ref, subject, predicate, value Inject a belief (optional: confidence, source_modality, source_agent)
add_memory synth_ref, content Inject a memory (optional: memory_type, salience)
set_mood synth_ref Override mood (optional: mood, valence, arousal, stress)

Examples

Fix a broken lamp:

{
  "operations": [
    {"op": "set_component_property", "entity_ref": "LAMP_REF",
     "component_type": "light_source", "property": "functional", "value": true},
    {"op": "set_component_property", "entity_ref": "LAMP_REF",
     "component_type": "repairable", "property": "condition", "value": "repaired"}
  ],
  "mode": "commit_and_step"
}

Create a new item and tell a synth about it:

{
  "operations": [
    {"op": "create_entity", "name": "Letter", "location_ref": "LOC_REF",
     "description": "A sealed letter addressed to Margaret.",
     "components": [{"type": "portable"}]},
    {"op": "add_belief", "synth_ref": "THOMAS_REF",
     "subject": "a letter", "predicate": "has arrived for",
     "value": "Margaret", "source_modality": "visual"}
  ],
  "mode": "commit_and_step"
}

Make the weather terrible:

{
  "operations": [
    {"op": "set_environment_field", "field_path": "weather.condition", "value": "hurricane"},
    {"op": "set_environment_field", "field_path": "weather.wind_speed", "value": 60.0},
    {"op": "set_environment_field", "field_path": "ambient.light_level", "value": 0.05}
  ],
  "mode": "commit"
}

6. Undo

# List snapshots
curl http://localhost:8420/v1/sessions/{sid}/snapshots

# Restore a previous one
curl -X POST http://localhost:8420/v1/sessions/{sid}/restore \
  -d '{"snapshot_ref": "snapshot_000002"}'

7. Submit External Turns

For in-world interaction (rather than admin overrides):

curl -X POST http://localhost:8420/v1/sessions/{sid}/turns \
  -d '{"origin_type": "human", "origin_ref": "operator",
       "payload": {"message": "A ship horn sounds in the distance."}}'

This routes through the director and triggers a full cycle. The message field in the payload is converted to a mediated auditory stimulus delivered through the perception path.

8. Queue External Stimuli

For stimuli that should arrive through the mediated perception path:

# Queue a stimulus for next step
curl -X POST http://localhost:8420/v1/sessions/{sid}/stimuli \
  -H 'Content-Type: application/json' \
  -d '{"content": "A bell tolls in the distance.", "channel": "auditory",
       "source": "environment", "delivery": "next_cycle"}'

# Queue and immediately step
curl -X POST http://localhost:8420/v1/sessions/{sid}/stimuli \
  -d '{"content": "Lightning strikes nearby.", "channel": "visual",
       "delivery": "immediate_with_step"}'

Stimuli are queued on the session and delivered through compose_sensory_bundle on the next step. They enter through the same mediated path as all other observations.

9. Track Changes

# What changed since event sequence 5?
curl "http://localhost:8420/v1/sessions/{sid}/changes?since=5"

# Report since cursor
curl "http://localhost:8420/v1/sessions/{sid}/reports/since?since=5"

10. Live Event Stream (SSE)

# Connect to SSE stream
curl -N "http://localhost:8420/v1/sessions/{sid}/stream?since=0"

Events arrive as Server-Sent Events with event: type and data: JSON payload. Use id: for cursor tracking.

Using with an LLM Agent

Tool Manifest

curl http://localhost:8420/v1/tooling/manifest

Returns 20 tool definitions with method, path, scope, and description. Pass these to an LLM as available tools.

Typical Agent Loop

  1. Agent calls get_overview to understand current state
  2. Agent calls step_session to advance
  3. Agent calls get_last_cycle_report to see what happened
  4. Agent decides whether to intervene
  5. If yes: apply_patch with typed operations
  6. If curious about a synth: get_object with kind=synth, view=both
  7. Repeat

Example Agent Conversation

User: What's happening in the lighthouse?

Agent: [calls get_overview]
It's 1847-10-14 at 19:30. Civil twilight. Thomas is in the Lighthouse Tower
feeling anxious. Margaret is on the Ground Floor, concerned. James is adrift
in Open Water, desperate. The beacon light is broken.

User: Advance one step.

Agent: [calls step_session with n=1]
[calls get_last_cycle_report]

Thomas inspected the tool chest and found a wrench, oil can, and spare wicks.
Margaret called up the stairs: "Thomas! The wind's picking up. Come down and
have something to eat." James is waiting in the lifeboat.

User: What does Margaret believe about the boat?

Agent: [calls resolve_ref with q=Margaret, kind=synth]
[calls get_object with kind=synth, ref=..., view=subjective]

Margaret has no beliefs about any boat. She cannot see (blind embodiment)
and no one has told her about it yet.

User: Make the weather worse.

Agent: [calls apply_patch with set_environment_field operations]
Done. Weather is now "storm" with 40-knot winds. Light level dropped to 0.1.

User: Step forward and see how they react.

Agent: [calls step_session]
[calls get_last_cycle_report]
...

API Reference

All routes are under /v1/.

Sessions

Method Path Description
POST /sessions Create session from scenario
GET /sessions List active sessions
GET /sessions/{id} Get session overview
DELETE /sessions/{id} Close session
GET /sessions/{id}/snapshots List snapshots
POST /sessions/{id}/restore Restore snapshot

Control

Method Path Description
POST /sessions/{id}/step Advance N cycles
POST /sessions/{id}/pause Pause auto-advance
POST /sessions/{id}/resume Resume auto-advance
POST /sessions/{id}/turns Submit external turn

Observation

Method Path Description
GET /sessions/{id}/overview Compact state summary
GET /sessions/{id}/resolve Name → ref resolution
GET /sessions/{id}/objects/{kind}/{ref} Object detail
GET /sessions/{id}/changes Diff since cursor
GET /sessions/{id}/events Audit events since cursor
GET /sessions/{id}/reports/last-cycle Last cycle report
GET /sessions/{id}/reports/since Report since cursor
GET /sessions/{id}/stream SSE live event stream

Intervention

Method Path Description
POST /sessions/{id}/patches Apply typed operations
POST /sessions/{id}/stimuli Queue external stimulus

Tooling

Method Path Description
GET /tooling/manifest Tool definitions for LLM agents