Files
koios/docs/tools/hecate.md
Robert Helewka db59266527 feat(personal): Sophia replaces Bourdain and Cousteau
Adds Sophia Petrillo (The Golden Girls) to the personal team and retires
Bourdain and Cousteau — a 2:1 merge, net -1 agent (10 -> 9 personal,
18 -> 17 total).

Why: the personal team casts fictional characters. Bourdain and Cousteau
are real people, one with significant baggage and one who died by suicide,
and neither had a dedicated MCP server or capability. Sophia is fictional,
plausibly owns both domains (an Italian grandmother knows food; tending
plants and the tank is housekeeping), and is the first personal agent to
actuate the house via the new Hecate MCP server.

Graph: Sophia inherits all 11 node types unchanged (Recipe, Restaurant,
Ingredient, Meal, Technique + Species, Plant, Tank, Garden, Ecosystem,
Observation). Uniqueness constraints are keyed on node label, not agent,
so there is no schema or data migration. Historical Note tags
(from:bourdain, to:cousteau) are deliberately left intact as an accurate
record. Schema bumped to 2.5.0.

Also:
- New docs/tools/hecate.md + prompts/tools/hecate.md, documenting the
  physical-actuation discipline (never guess a room or device slug) and
  stating plainly what Hecate cannot do yet: no sensor readings (Demeter
  is REST-only), no weather, and set_timer is routine-bound with no
  cancel — the conversational timer is coming to Daedalus.
- Shawn gains Periplus in the docs. The server was already wired in
  iolaus but never described in any prompt, so the model was handed a
  tool it was never told about — including the never-estimate-coordinates
  rule.
- Corrected stale counts (15/16/18 assistants -> 17) and the stale
  "Bowie's Domain" schema heading missed when David replaced Bowie.
- Removed utils/neo4j-schema-init.py.bak, a pre-Watson artifact.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 19:24:16 -04:00

9.3 KiB
Raw Blame History

Hecate

The house — lights, the receiver, IR devices, and saved routines. The MCP server is the actuator layer; the agent is the brain.

What It Is

Hecate is the estate's voice-first home-automation controller — a FastAPI + FastMCP service that owns the device registry and drives the physical devices in Robert's home: Tuya/SmartLife bulbs and outlets, HomeKit wall plugs (via the Propolos bridge, no vendor cloud), the Onkyo AV receiver over eISCP, and Broadlink IR blasters for TVs, soundbars, projectors, and legacy air conditioning. Named for the Greek goddess of the household threshold and the crossroads.

Hecate deliberately contains no AI. It exposes tools; an agent decides what to call. It is the only MCP server on the personal team that changes physical state in the house — every other tool reads, writes text, or searches.

Two stores, as with Kairos and Periplus. Hecate holds the canonical live state — which devices exist, what room they're in, whether the light is currently on, what the receiver's volume is. Neo4j holds the interpretation — that the reef tank in the living room matters, that this plant has been struggling since June. Hecate knows the lamp is on; it has no idea why anyone cares. When Sophia records that the aquarium heater failed, the event goes in Neo4j as an Observation; the device stays Hecate's.

⚠️ Critical Discipline: Never Guess a Device or Room

The device registry lives in Hecate's database and is per-install. Room ids (living_room) and device ids (lr_main, lr_fan_plug) are human-authored slugs that exist only in that database. There is no fixed list, and none of them are guessable from conversation.

Acting on a guessed slug either errors or — worse — actuates the wrong thing. Unlike a bad search result, this has physical consequences: the light someone is reading by goes out, the receiver powers down mid-film.

The rules, which have no exceptions:

  1. Call list_rooms / list_devices before acting. Every session. The registry is not to be memorised across conversations — devices get added, renamed, and disabled.
  2. If there's no obvious match for what the user said, ask. Do not pick the closest-sounding slug.
  3. Confirm before disruptive actions — whole-house changes (room=""), powering off a receiver that's in use, running a multi-room routine.
  4. Name the device when reporting failure. "Can't reach the patio blaster — it's offline" beats "command failed." Hecate's own error strings do this; preserve it.

MCP Tools

Registry — always start here

Tool Purpose
list_rooms() Every room with its device count. The room-slug source of truth.
list_devices(room="") Every registered device with type, platform, and room. Optionally scoped to one room.

Lights and power

Tool Purpose
lights_on(room="", brightness=-1) Turn lights on. brightness 0100; -1 leaves it unchanged (and is ignored by outlets). room="" is the whole house.
lights_off(room="") Turn lights off, optionally scoped to a room.
lights_color(color, room="") Set colour. RGB bulbs only — white/dimmer bulbs and outlets are unaffected. Named colours: red, orange, yellow, green, cyan, blue, purple, magenta, pink, white, warmwhite, coolwhite.
device_power(device_id, state) One specific device by id. state is "on" or "off". This is how you address a single plug when the room-scoped tools are too broad.

Onkyo receiver

Tool Purpose
receiver_power(state) On or off.
set_volume(level) Main-zone volume, 0100.
set_input(source) Select an input, by eISCP name or friendly label.
get_receiver_status() Current power, volume, and input.

IR, routines, timing

Tool Purpose
ir_command(device_id, command) Fire a learned IR command through the target's blaster. Valid commands come from that device's learned command map — they are per-device, not a fixed vocabulary.
run_routine(routine_id) Execute a saved routine's steps in order; returns per-step outcomes. Steps are best-effort — one failure doesn't abort the rest.
list_routines() All routines with id, name, enabled state, trigger, and step count.
set_timer(routine_id, minutes=0, seconds=0, label="") Fire an existing routine after a delay. See the gotcha below — this is not a kitchen timer.
list_timers(status="") List timers, optionally filtered (pending, fired, cancelled).
wait(seconds=0, minutes=0) Pause inside a routine before the next step.

Wallboard (Phase 2)

list_wallboards, create_wallboard, get_wallboard, get_wallboard_version, get_wallboard_catalog, put_wallboard_version, activate_wallboard_version, comfy_generate, gallery_search — the push-model kiosk display: an agent authors a full HTML board and pushes it. Not part of Sophia's day-to-day; see hecate/docs/wallboard.md.

Canonical Workflows

Turning something on

list_rooms()                      # or list_devices() if the user named a device
→ match what the user said to a real slug
→ lights_on(room="living_room", brightness=60)

Step 1 is non-negotiable. If the match is ambiguous, ask rather than guess.

Addressing one device rather than a room

list_devices(room="living_room")  # find the specific plug
→ device_power(device_id="lr_fan_plug", state="on")

Room-scoped lights_* tools hit every light-like device in the room. For "turn on the fan plug," use device_power.

Running a saved sequence

list_routines()                   # routine ids are slugs: "movie_time", "goodnight"
→ run_routine(routine_id="movie_time")

Read the per-step outcomes in the response — a routine reports partial failure rather than throwing.

Who Uses Hecate

  • Sophia — primary and effectively sole user. The house is her domain; she is the only personal agent wired to it.
  • No other agent has Hecate in its server list. If a request from another agent needs the house, it routes to Sophia by message.

External non-agent clients (the Theia kiosk, a scheduled wallboard prompt) connect with their own PATs — out of scope here.

What It's Good For

  • Actuating lights, plugs, the receiver, and IR devices by room or by device
  • Discovering what the house actually contains, live
  • Running saved multi-step routines ("movie time", "goodnight")
  • Authoring and serving the wallboard kiosk display

What It's Not Good For

  • Reading any sensor. See the gotchas — nothing is exposed.
  • Weather. Not exposed as a tool yet.
  • Conversational timers and reminders. Not what set_timer is.
  • Remembering why something matters — that's Neo4j's job.
  • Scheduling in the human sense — cron/sun triggers are configured in Hecate's own UI; the household calendar is Kairos (Shawn).

Known Gotchas

  • Never guess a room or device slug. See the Critical Discipline section — this is the failure mode with physical consequences.
  • No sensor readings are exposed via MCP. Hecate integrates Demeter, the estate's plant/aquarium sensor server (soil moisture, temperature), and sensor-capable Broadlink blasters report temperature/humidity — but all of it is reachable only over REST and Prometheus. There is no MCP tool for any reading. An agent cannot see soil moisture, water temperature, or room temperature. On the Hecate backlog. Until then the agent asks the user and records an Observation in Neo4j.
  • No weather or forecast tool. A weather data source exists inside Hecate for the wallboard (sources/weather.py, Open-Meteo) but is not exposed via MCP. Also on the Hecate backlog.
  • set_timer is not a kitchen timer. It requires a pre-existing routine_id and fires that routine after a delay — there is no tool to create a routine, and no cancel-timer tool (cancellation is REST-only). "Set a 10-minute timer" is not expressible. The conversational timer/reminder capability is planned for Daedalus, not Hecate. An agent must not imply it is holding a timer.
  • lights_color only affects RGB bulbs. White bulbs, dimmers, and outlets in the same room are silently unaffected — that's not a failure, but say so rather than claiming the room changed colour.
  • brightness=-1 means "don't change it." Passing 0 is a real value and will dim the light to nothing.
  • room="" is the whole house. Easy to trigger accidentally by omitting the argument.
  • Timers survive restarts; cron occurrences do not. Past-due timers fire on startup; a 7 AM schedule missed during an outage is not replayed at 2 PM. Deliberate.
  • Auth is owner-only. The /mcp mount sits behind an ASGI guard — a valid non-owner credential gets 403, not 401. Agents authenticate with an owner-scoped PAT (hecate_pat_…) as a static bearer header; Pallas does not forward inbound auth downstream.
  • The server sends its own instructions. Hecate's FastMCP server ships _SERVER_INSTRUCTIONS describing room/device resolution to every client automatically. Don't duplicate that guidance verbatim in an agent prompt — add only what it doesn't say.