diff --git a/lyra/self_state.py b/lyra/self_state.py
index 273c4ad..458350d 100644
--- a/lyra/self_state.py
+++ b/lyra/self_state.py
@@ -150,6 +150,20 @@ def _safe_json(s: str) -> dict | None:
return None
+def _fmt_reflection(label: str, d: dict | None) -> str:
+ """Readable block of a reflection's key fields, for the live-log inspector."""
+ if not d:
+ return f"{label}:\n (none)"
+ keys = ("mood", "valence", "energy", "confidence", "curiosity",
+ "self_narrative", "relationship", "new_reflections")
+ lines = [f"{label}:"]
+ for k in keys:
+ if k in d and d[k] not in (None, "", []):
+ v = " | ".join(d[k]) if isinstance(d[k], list) else d[k]
+ lines.append(f" {k}: {v}")
+ return "\n".join(lines)
+
+
def reflect(backend: Backend | None = None, session_id: str | None = None) -> dict:
"""Reflect on recent activity and update the self-state. Returns new state.
@@ -189,7 +203,7 @@ def reflect(backend: Backend | None = None, session_id: str | None = None) -> di
))
# Step 2 โ examine her own draft and revise it into a more honest version.
- update, critique = draft, None
+ update, critique, revised = draft, None, None
if draft:
examine_body = body + "\n\nYOUR DRAFT REFLECTION:\n" + json.dumps(draft, indent=2)
revised = _safe_json(llm.complete(
@@ -217,9 +231,18 @@ def reflect(backend: Backend | None = None, session_id: str | None = None) -> di
state["interaction_count"] = state.get("interaction_count", 0) + 1
memory.set_self_state(state)
- logbus.log("info", "self-state updated", mood=state.get("mood"),
- interactions=state["interaction_count"], parsed=bool(update),
- critiqued=bool(critique))
+
+ # Surface the actual self-correction (draft -> revised -> critique) to the live
+ # log as an expandable block, so the two-step reflection is observable.
+ detail = (
+ _fmt_reflection("DRAFT (first pass)", draft) + "\n\n"
+ + _fmt_reflection("REVISED (committed)",
+ revised if revised else None)
+ + ("" if revised else "\n (examine step didn't parse โ kept the draft)")
+ + "\n\nSELF-CRITIQUE:\n " + (critique or "(none recorded this pass)")
+ )
+ logbus.log("info", "reflection", mood=state.get("mood"),
+ critiqued=bool(critique), detail=detail)
return state
diff --git a/lyra/web/server.py b/lyra/web/server.py
index bf79b3e..e39a837 100644
--- a/lyra/web/server.py
+++ b/lyra/web/server.py
@@ -125,6 +125,13 @@ def create_app() -> FastAPI:
"""Lyra's current interiority + when it last changed."""
return {"state": self_state.load(), "updated_at": memory.self_state_updated_at()}
+ @app.post("/self/reflect")
+ async def self_reflect() -> dict:
+ """Run one two-step reflection now, in this process, so the draft ->
+ revised -> critique lands in the live log (/logs)."""
+ state = await asyncio.to_thread(self_state.reflect)
+ return {"ok": True, "mood": state.get("mood")}
+
@app.get("/stream/logs")
async def stream_logs(request: Request) -> StreamingResponse:
"""Live activity feed: replay the recent buffer, then stream new events."""
diff --git a/lyra/web/static/index.html b/lyra/web/static/index.html
index b8cc3f9..a418060 100644
--- a/lyra/web/static/index.html
+++ b/lyra/web/static/index.html
@@ -756,7 +756,7 @@
${escapeHtml(level)}
${escapeHtml(event.msg || '')}
${fieldStr ? `${escapeHtml(fieldStr)}` : ''}
- ${detail ? `view full prompt
${escapeHtml(detail)}view details
${escapeHtml(detail)}
${esc(detail)}${esc(detail)}