feat: session lifecycle audit log — append-only JSONL at ~/.claudeman/session-lifecycle.jsonl

Records every session create, start, exit, delete, recover, stale-clean,
mux-died, and server start/stop with timestamps, names, reasons, and exit
codes. Survives server restarts (unlike in-memory RunSummary).

- New SessionLifecycleLog singleton (src/session-lifecycle-log.ts)
- LifecycleEventType + LifecycleEntry types in types.ts
- GET /api/session-lifecycle endpoint with sessionId/event/since/limit filters
- cleanupSession() now takes reason parameter for audit trail
- state-store cleanupStaleSessions returns cleaned session IDs+names
- UI: lifecycle log modal accessible from header clipboard icon
- Auto-trims to 8k entries when exceeding 10k on server start

chore: bump version to 0.1556

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-02-19 18:30:14 +01:00
co-authored by Claude Opus 4.6
parent 342a2add10
commit 383ed5e5a0
9 changed files with 333 additions and 79 deletions
+7 -6
View File
@@ -368,25 +368,26 @@ export class StateStore {
* @param activeSessionIds - Set of currently active session IDs
* @returns Number of sessions cleaned up
*/
cleanupStaleSessions(activeSessionIds: Set<string>): number {
cleanupStaleSessions(activeSessionIds: Set<string>): { count: number; cleaned: Array<{ id: string; name?: string }> } {
const allSessionIds = Object.keys(this.state.sessions);
let cleanedCount = 0;
const cleaned: Array<{ id: string; name?: string }> = [];
for (const sessionId of allSessionIds) {
if (!activeSessionIds.has(sessionId)) {
const name = this.state.sessions[sessionId]?.name;
cleaned.push({ id: sessionId, name });
delete this.state.sessions[sessionId];
// Also clean up Ralph state for this session
this.ralphStates.delete(sessionId);
cleanedCount++;
}
}
if (cleanedCount > 0) {
console.log(`[StateStore] Cleaned up ${cleanedCount} stale session(s) from state`);
if (cleaned.length > 0) {
console.log(`[StateStore] Cleaned up ${cleaned.length} stale session(s) from state`);
this.save();
}
return cleanedCount;
return { count: cleaned.length, cleaned };
}
/** Returns all task states keyed by task ID. */