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.
- New scenario → new session
- Undo → restore a previous snapshot (same session)
- Default state → paused on creation
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.condition → storm) |
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
- Agent calls
get_overviewto understand current state - Agent calls
step_sessionto advance - Agent calls
get_last_cycle_reportto see what happened - Agent decides whether to intervene
- If yes:
apply_patchwith typed operations - If curious about a synth:
get_objectwithkind=synth,view=both - 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 |