Stream Plan — what she has to get done today
What it does#
The owner writes what today is supposed to be, from the dashboard. It sits in Bea's prompt until she has worked through it, and she closes the items herself as she goes.
src/core/memory/plan.py StreamPlan — the store
src/core/skills/plan/surface.py StreamPlanSkill — context + three tools
src/web/frontend/src/pages/StreamPlanPage.jsx
It is a core skill (skill_name = None): always on, no toggle. With an
empty plan it contributes no rules, no state and no tools, so a feature nobody
uses costs nothing.
What a plan is#
A headline directive plus an ordered list of objectives:
[TODAY'S PLAN — set by your owner, not a suggestion]
today you play minecraft on the survival server
[x] #1 build a base — before dark (you said: it's ugly but it works)
[~] #2 find diamonds
[ ] #3 tour the server and say hi to people
[ ] todo · [~] doing · [x] done · [-] dropped
It goes in through live_state(), so it is in every prompt and always
current — it is what she is supposed to be doing, not an event that should make
her think.
The number shown is the database id, and it is the same number she passes to
objective_done. One identifier, nothing to map, nothing to get wrong.
Tools#
| Tool | Effect |
|---|---|
objective_started(objective) | mark the one she is on now |
objective_done(objective, how) | tick it off, with a line in her own words |
objective_dropped(objective, why) | give up on one that genuinely cannot happen |
Each returns how many are left, so she knows where she is without re-reading the list.
What actually makes her act#
Having the plan in her context is not enough on its own: on a quiet stream nothing arrives to wake her up. The Minecraft surface closes that loop.
The game heartbeat is marked noise, so an idle server costs nothing. When the
body is standing still and an objective is still open, the surface emits one
perception instead:
Perception(
GAME, "game:mc",
"You have been standing around in Minecraft doing nothing for 34s. Today's "
"plan still has: #2 find diamonds. It is your game: do something.",
meta={"addressed": "idle-in-game"},
)
meta["addressed"] is a general primitive: a sense that already knows a
perception is for her says so, and is_addressed() returns it verbatim. That is
what makes the nudge survive the attention gate — a nudge filed under "noticed"
is a nudge that never happened, and she would go straight back to waiting to be
spoken to.
It fires at most every skills.minecraft.idle_nudge_seconds (30 by default,
0 disables it), and never while an action of hers is running — then she gets
the word about what she is doing
instead, and only when something in it changed. With an empty plan it still
fires, without the list: she is in a game either way.
Storage#
Two tables in bea.db:
objectives (id, text, detail, status, outcome, position, created_at, updated_at)
settings (key, value) -- the directive lives here as plan.directive
Not a memory — an instruction she is given. It survives restarts, and
POST /plan/reset clears it for a new stream.
The dashboard#
The Stream Plan page reads and writes /plan, and re-reads it every five
seconds because Bea closes objectives herself while the stream runs.
The owner can also close, reopen, reorder and delete objectives — you are watching the stream too, and it is faster than telling her.
While the Minecraft skill is active the page also carries a What are you doing? button, which asks her to say what she is up to in the game without waiting for her own clock. It is the Minecraft word about what she is doing fired by hand, not a separate prompt, and it is hidden when she is not in the game.
See the API reference for the endpoints.
Scope#
The plan reaches the one loop: it describes what she is doing on stage. A directive like "answer everyone in Discord today" is visible to her wherever she reads it, because there is only one context.