feat: add Run Summary feature for session activity tracking

Implements a "what happened while you were away" view for sessions:
- Track significant events: session start/stop, respawn cycles, state changes,
  idle/working transitions, token milestones, auto-compact/clear, Ralph
  completions, AI check results, hook events, errors/warnings
- Stats dashboard: respawn cycles, peak tokens, active/idle time, issue counts
- Timeline view with color-coded severity and emoji icons
- Filter events by type (all/errors/warnings/respawn/idle)
- State stuck detection (warns when state unchanged for 10+ minutes)

Click the chart icon on any session tab to open the Run Summary modal.

Files:
- src/run-summary.ts: RunSummaryTracker class with event tracking
- src/types.ts: RunSummary, RunSummaryEvent types
- src/web/server.ts: Integration with session/respawn events, API endpoint
- src/web/public/: Modal UI, timeline rendering, filter controls
- docs/run-summary-plan.md: Implementation plan

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-01-25 22:05:28 +01:00
co-authored by Claude Opus 4.5
parent 8b0577d8aa
commit b811c8dc80
9 changed files with 1210 additions and 7 deletions
+145
View File
@@ -1960,6 +1960,151 @@ body {
max-width: 520px;
}
/* Run Summary Modal */
.run-summary-stats {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.75rem;
margin-bottom: 1rem;
}
.run-summary-stats .stat-card {
background: var(--bg-hover);
border: 1px solid var(--border);
border-radius: 6px;
padding: 0.75rem;
text-align: center;
}
.run-summary-stats .stat-value {
font-size: 1.5rem;
font-weight: 600;
color: var(--accent);
}
.run-summary-stats .stat-label {
font-size: 0.75rem;
color: var(--text-dim);
margin-top: 0.25rem;
}
.run-summary-filters {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.filter-btn {
padding: 0.375rem 0.75rem;
background: var(--bg-hover);
border: 1px solid var(--border);
border-radius: 4px;
color: var(--text-dim);
cursor: pointer;
font-size: 0.8rem;
transition: all 0.15s;
}
.filter-btn:hover {
background: var(--bg-card);
color: var(--text);
}
.filter-btn.active {
background: var(--accent);
border-color: var(--accent);
color: var(--bg);
}
.run-summary-timeline {
max-height: 400px;
overflow-y: auto;
padding-right: 0.5rem;
}
.timeline-event {
display: flex;
gap: 0.75rem;
padding: 0.75rem;
margin-bottom: 0.5rem;
background: var(--bg-hover);
border-radius: 6px;
border-left: 3px solid var(--border);
}
.timeline-event.event-success {
border-left-color: var(--green);
}
.timeline-event.event-warning {
border-left-color: var(--yellow);
}
.timeline-event.event-error {
border-left-color: var(--red);
}
.timeline-event.event-info {
border-left-color: var(--accent);
}
.event-icon {
font-size: 1.1rem;
flex-shrink: 0;
}
.event-content {
flex: 1;
min-width: 0;
}
.event-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 0.5rem;
}
.event-title {
font-weight: 500;
color: var(--text);
}
.event-time {
font-size: 0.75rem;
color: var(--text-dim);
flex-shrink: 0;
}
.event-details {
font-size: 0.8rem;
color: var(--text-dim);
margin-top: 0.25rem;
}
.run-summary-session-info {
font-size: 0.8rem;
color: var(--text-dim);
}
/* Tab summary icon */
.tab-summary {
opacity: 0;
cursor: pointer;
font-size: 0.85rem;
transition: opacity 0.15s;
margin-right: 0.25rem;
}
.session-tab:hover .tab-summary {
opacity: 0.5;
}
.session-tab:hover .tab-summary:hover {
opacity: 1;
}
.modal-warning-text {
color: var(--text-dim);
font-size: 0.85rem;