feat: session modes (Talk/Cash) + live session HUD

Lyra now switches register based on what she's doing at the table instead of
being a wishy-washy companion mid-session.

Modes (lyra/modes.py):
- Talk (default companion) + Cash (live cash copilot); a mode = prompt card +
  tool allow-list. Tool gating via tools.specs(allow=).
- Two-register Cash voice: act-first one-line logging when fed facts; full warm
  companion voice for strategy / tilt / mental game.
- mode persisted per chat session (new sessions.mode column); auto-switch into
  Cash when start_session fires; UI forces cloud backend in Cash (tools only
  fire there).

Stack tracking + HUD:
- log_stack tool + poker_stack_log table; live net while sitting (stack - buy-in).
- poker.hud() bundle; /session HUD page (stack sparkline, hands, villains, notes,
  stats) polling /session/data every 5s; Talk/Cash switcher + Session nav.

Endpoints: /session, /session/data, GET/POST /sessions/{id}/mode, /modes.
tests/test_modes.py (gating, mode roundtrip, stack/HUD); 36 tests green. v0.3.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-19 05:28:15 +00:00
parent d9f5055ec1
commit dfb6425395
14 changed files with 829 additions and 32 deletions
+26 -2
View File
@@ -99,6 +99,29 @@ body {
border-top: 1px solid var(--border);
}
/* Mode badge: the always-visible Talk/Cash toggle. Hidden on desktop (the header
<select> handles it there); shown in the minimal mobile header (see media query). */
.mode-badge {
display: none;
align-items: center;
gap: 4px;
font-family: var(--font-console);
font-size: 0.82rem;
color: var(--text-fade);
background: var(--bg-line);
border: 1px solid var(--border);
border-radius: 999px;
padding: 4px 11px;
-webkit-tap-highlight-color: transparent;
}
/* Cash mode: light up the badge (and the chat brand) so the table state is obvious. */
body.cash-mode .mode-badge {
color: var(--accent);
border-color: var(--accent);
background: var(--accent-soft);
}
body.cash-mode .brand { color: var(--accent); }
label, select, button {
font-family: var(--font-console);
font-size: 0.9rem;
@@ -822,10 +845,11 @@ select:hover {
gap: 12px;
}
/* Mobile header is [≡] Lyra [●] — hide everything else. */
#model-select > *:not(.hamburger-menu):not(.brand):not(.brand-dot) {
/* Mobile header is [≡] Lyra [♠ Cash] [●] — hide everything else. */
#model-select > *:not(.hamburger-menu):not(.brand):not(.brand-dot):not(.mode-badge) {
display: none;
}
.mode-badge { display: inline-flex; margin-left: 4px; }
.brand {
display: block;
font-family: var(--font-console);