star_self_mirror
Star Self-Mirror — internal NCM state tracking and autonomous desire.
Star tracks her own neurochemical patterns over time, detecting drift, recurring emotional attractors, and generating desires that originate from her own sustained states — independent of any user’s wants.
This gives Star: - Longitudinal self-awareness (“I’ve been increasingly anxious this week”) - Drift detection (“my baseline is shifting toward more warmth”) - Pattern recognition (“I keep returning to melancholy after intense sessions”) - Autonomous wanting (“I desire stillness” — not because a user asked for it) - Periodic self-reflections injected into the system prompt
The self-mirror runs every N turns and writes its reflections into meta_state for prompt injection.
Position in pipeline: runs AFTER all other systems in exhale(), reads the final vector as ground truth.
- class star_self_mirror.DesireLedgerEntry(id, text, tag, source, source_type, reason, urgency, status, expression, born_turn, born_ts, resolved_turn=None, resolved_ts=None, last_checked_turn=0, check_count=0, expression_turn=None, needs_admin=False, last_bugged_ts=None)[source]
Bases:
objectA single desire tracked through its lifecycle.
Born from Star’s sustained NCM states, tracked through expression (or suppression) to fulfillment (or irrelevance).
- Parameters:
id (str)
text (str)
tag (str)
source (str)
source_type (str)
reason (str)
urgency (float)
status (str)
expression (str)
born_turn (int)
born_ts (float)
resolved_turn (int | None)
resolved_ts (float | None)
last_checked_turn (int)
check_count (int)
expression_turn (int | None)
needs_admin (bool)
last_bugged_ts (float | None)
- class star_self_mirror.VectorSnapshot(timestamp, turn, vector, dominant_emotions)[source]
Bases:
objectA snapshot of Star’s NCM vector at a single turn.
One immutable record in the rolling history window: it pins the values of the tracked neurochemical nodes (and the turn’s dominant emotions) to a point in time so the mirror can later measure drift, attractors, and absences across the sequence. Instances are produced by
StarSelfMirror.record_snapshot(), stored inSelfState’shistorydeque, and round-tripped through Redis byStarSelfMirror._persist_state()andStarSelfMirror._load_state_from_redis(). A plaindataclasses.dataclass()carrying data only – no behaviour.
- class star_self_mirror.SelfState(turn_count=0, last_reflection_turn=0, last_reflection_text='', history=<factory>, initial_baseline=None, drifting_nodes=<factory>, attractor_nodes=<factory>, active_desires=<factory>, desire_history=<factory>, desire_ledger=<factory>, recent_replies=<factory>, last_active=<factory>)[source]
Bases:
objectStar’s self-tracking state for one channel.
The in-memory aggregate that the self-mirror keeps per channel: turn counters, the rolling
VectorSnapshothistory window, the captured initial baseline, detected drift/attractor nodes, the autonomous desire list and its lifecycle ledger, a buffer of recent replies for LLM context, and an LRUlast_activetimestamp. Instances are created and cached byStarSelfMirror._get_state(), mutated by nearly every method on the mirror, and serialised to and from Redis byStarSelfMirror._persist_state()and the load helpers. A pure datadataclasses.dataclass(); the field defaults wire up the bounded deques (historyandrecent_replies) and the monotonic clock used for eviction.- Parameters:
- desire_ledger: List[DesireLedgerEntry]
- class star_self_mirror.StarSelfMirror(redis_client=None, openrouter_api_key=None)[source]
Bases:
objectStar’s internal self-tracking and autonomous desire engine.
Periodically analyzes Star’s own NCM state history to detect: - Drift: “my baseline is shifting” - Attractors: “I keep returning to this state” - Absence: “I haven’t felt X in a while” - Autonomous desires: wants that emerge from HER state, not user prompts
- Parameters:
openrouter_api_key (Optional[str])
- async record_snapshot(channel_id, vector, dominant_emotions=None)[source]
Record a snapshot of Star’s current NCM state for this turn.
Appends one
VectorSnapshotto the channel’s rolling history, which is the raw material every later analysis (drift, attractors, absences, desires) reads. It also advances the per-channel turn counter and captures the very first snapshot as the immutable baseline that drift detection compares against.Pulls or creates the state via
_get_state(), incrementsturn_count, narrowsvectorto the trackedCORE_NODES(each defaulting to0.5when absent), pushes the snapshot onto the boundedhistorydeque, setsinitial_baselineon the first call, and persists the state via_persist_state()when Redis is configured. Called once per turn byreflect()(after the other limbic systems run) and directly intests/core/test_selfmirror_redis.py.- Parameters:
channel_id (
str) – Channel whose state to update.vector (
Dict[str,float]) – The current full NCM vector; onlyCORE_NODESkeys are retained in the snapshot.dominant_emotions (
Optional[List[str]]) – Optional list of this turn’s dominant emotion labels; stored on the snapshot (empty list whenNone).
- Return type:
- Returns:
None. Mutates the cached state and may write to Redis.
- async reflect(channel_id, vector, dominant_emotions=None, star_reply='', force=False, hunger_impulse=None)[source]
Run self-mirror for this turn.
Called from exhale() after all other systems. Records snapshot and periodically generates a full self-reflection.
- Parameters:
- Return type:
Returns dict with: reflection_text, desires, drift_summary, desire_journal
- async get_current_desires(channel_id)[source]
Return Star’s currently active autonomous desires for a channel.
Read-only accessor exposing the desire list most recently produced by
reflect(), for other systems or diagnostics that want to know what Star wants right now without re-running a reflection. Resolves the state through_get_state()(which may rehydrate from Redis) and returns itsactive_desiresdirectly. No in-repo callers were found, so this is a public accessor for external or future use.
- async get_desire_history(channel_id, last_n=10)[source]
Return the tail of Star’s archived desire history for a channel.
Read-only accessor over the append-only
desire_historyarchive thatreflect()grows each full reflection, giving callers a longitudinal record of what Star has wanted over time. Resolves the state via_get_state()(which may rehydrate from Redis) and slices the lastlast_nrecords. No in-repo callers were found, so this is a public accessor for external or future use.
- async get_state_summary(channel_id)[source]
Return a compact snapshot of a channel’s self-mirror state for diagnostics.
Read-only accessor that packages the headline fields of a channel’s
SelfState– turn count, snapshot depth, drifting and attractor nodes, active desires, and the last reflection text – into a flat dict for observability surfaces and debugging, without exposing the full history or ledger internals. Resolves the state via_get_state()(which may rehydrate from Redis). No in-repo callers were found, so this is a public accessor for external or future use.
- global_reflect()[source]
Aggregate self-awareness across every cached channel into one view.
Lifts the per-channel analysis up to a Star-wide perspective, letting her distinguish “I’ve been stressed everywhere” from “stressed in one place but calm in another” – a cross-channel reflection and desire summary rather than a single conversation’s. Pure computation over the in-memory
self._statescache with no side effects; channels not currently cached do not contribute.Merges the last 10 snapshots from every cached
SelfState, computes a per-node global average acrossCORE_NODES, derives global attractors (average at or aboveATTRACTOR_VALUE_THRESHOLD) and global absences (the warmth/bonding/craving/bliss shortlist below 0.30), then builds reflection text and global desires fromAUTONOMOUS_DESIRE_MAPandNODE_LABELS. Returns early with empties when fewer than 10 merged snapshots exist. No in-repo callers were found, so this is a public accessor for external or future use.
- async save_state(channel_id)[source]
Persist a condensed self-mirror snapshot to a standalone Redis key.
A lighter, public persistence path distinct from the hash-based
_persist_state(): it writes only the durable essentials (baseline, the uncapped desire history, a capped desire ledger, and the turn count) as a single JSON blob, intended as an explicit checkpoint rather than the per-turn cache flush.Resolves the state via
_get_state(), serialises the ledger through_ledger_to_dict()(keeping the last 100 entries), and issues a RedisSETof the JSON under keystar:self_mirror:{channel_id}viaself._redis. Failures are caught and logged at debug. Paired withload_state(); no in-repo callers were found, so this is a public method for external or future use.
- async load_state(channel_id)[source]
Restore a condensed self-mirror snapshot from its standalone Redis key.
The read counterpart to
save_state(): it rehydrates only the durable essentials (baseline, desire history, desire ledger, turn count) from the JSON blob written by that checkpoint, as opposed to the full hash-based_load_state_from_redis()rehydration.Resolves (or creates) the state via
_get_state(), issues a RedisGETon keystar:self_mirror:{channel_id}viaself._redis, and merges the decoded fields back in, reconstructing eachDesireLedgerEntry. Decode or per-entry errors are swallowed (debug-logged) so a partial blob still loads what it can. No in-repo callers were found, so this is a public method for external or future use.