feat: thought loop — Lyra's threaded, surfaceable train of thought #4
Reference in New Issue
Block a user
Delete Branch "feat/thought-loop"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Feedback: the push broadcast her raw internal thought ("Eelis Parssinen's victory is a reminder...") — read like a journal entry, not her texting him. Now the flow matches the intent: she thinks/journals, then *decides* "I should tell Brian about this." think() asks for an optional `reach_out` — a real text message addressed TO him in her own voice, written only when she chooses to. The ping sends that message (title "Lyra", like a text from her), never the internal thought. No reach_out = nothing sent (most thoughts stay hers). - Pinging decoupled from the salience score: her decision (a reach_out) drives it, not a threshold. PING_SALIENCE is now an optional floor (default 0.0). - Defensive: reject the placeholder echo ("reach_out"), too-short junk, or the thought pasted back as the message. - notify.push: title now optional (omitted -> cleaner text-style notification). Verified live: 3 passes kept private; a decided reach-out lands as a personal text. Suite 67 green, ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>Replaces the thought loop's grist (recent-convo + her own saved narrative, the feedback-loop attractor) with a model of how a thought actually arises: seed (salience-weighted: a recent moment / resurfaced memory / feed item) -> spreading activation: embed the seed, let it light up associatively-near material across ALL her stores (conversations, gists, her own journal/ thoughts), blended by relevance + recency + noise; optional 2nd hop for leaps -> her self-narrative stays the LENS (supplied as interiority), not the input -> the thought is generated from what lit up, routed through a faculty (notice / connect / abstract / project / feel) -> journaled + embedded, so it can light up in future cycles This breaks the feedback loop structurally: the narrative is no longer reread and paraphrased each cycle; grist is genuinely associative and varied; and her past thoughts re-activate (continuity without calcification). - lyra/cognition.py (new): spontaneous_seed, activate (spreading activation), constellation_block, faculties. - memory.py: journal entries now embedded; recall_journal(); backfill_journal_embeddings() (ran once: 341 past entries embedded so her history is associatively retrievable). - thoughts.think(): new-thread mode now uses the associative engine; dropped _grist(). - tests: test_cognition.py (recall_journal ranking, activation, seeding) + fixture reloads cognition. Suite 72 green, ruff clean. Honest scope: this fixes the mechanism (how thoughts arise). The residual "be useful for Brian" voice drift is the separate model/fine-tune problem. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>The control plane gains senses — cheap, deterministic, no LLM: - lyra/perceive.py: lexicon+signal heuristic → {sentiment, intensity, tilt, kind: emotional|strategic|meta|build|casual}. Good at the action-relevant signal, especially tilt (the mental-game core). Word-boundary matching so 'line' doesn't fire inside 'pipeline'. - mind: _perceive fills ctx.moment; _route keeps the manual mode as the dominant frame but, on a genuinely charged moment, adds a per-turn register nudge — tilt → "meet him there, warm and steady, don't clip into logging"; up/energized → "match his energy." Neutral turns get nothing (don't over-narrate). Injected via build_messages(moment=...). Logged to /logs for observability. - tests: perceive read (tilt/strategy/up/build/casual) + route nudge on/off. Suite 92 green, ruff clean. Complements modes (manual frame) — perceive refines register within it, doesn't override. Model routing (mind/mouth) is P3. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>