feat: add step-by-step visual replay and harden process lifecycle

- UI: Implement dual-mode playback in FloatingVideoPlayer supporting video and step-by-step frame replay with scrub controls and auto-fallback
- Runtime: Prevent asyncio event loop hangs on Darwin and Linux by avoiding psutil.wait_procs on direct child processes
- Task Queue: Add worker exit watchdog and automatic recording recovery for completed or interrupted sessions
- Recording: Add ffmpeg ultrafast transcode fallback, shield recording finalization against cancellation, and handle on-the-fly MP4 conversion
- Docs: Polish README_CN styling and streamline flash runner agent prompt
This commit is contained in:
somew1nd committed 2026-08-26 00:53:24 -07:00
1 parent 06643e955b
commit a36a66ce6d
26 files changed
+1152 -117

No files matched your search

+12 -8
View File
@@ -102,14 +102,18 @@ async def get_session_video(session_id: str):
}
if recording_status == "failed":
return {
"session_id": session_id,
"status": "failed",
"has_video": False,
"video_url": None,
"video_segments": [],
"message": recording.get("error") or "Recording finalization failed",
}
v_url = media_service.resolve_video_url(row_dict, video_rec_map, video_idx)
if not v_url:
return {
"session_id": session_id,
"status": "failed",
"has_video": False,
"video_url": None,
"video_segments": [],
"message": recording.get("error") or "Recording finalization failed",
}
# If a video was recovered/found, update DB to ready and proceed to serve it
session_repo.mark_recording_ready(session_id, v_url)
v_url = media_service.resolve_video_url(row_dict, video_rec_map, video_idx)
video_segments = media_service.resolve_video_segments(v_url)
+8 -3
View File
@@ -63,12 +63,17 @@ async def list_sessions():
orphaned_ids.append(s_id)
recording_status = str((recording or {}).get("status") or "unavailable")
row_dict["recording_status"] = recording_status
row_dict["video_url"] = (
resolved_v_url = (
media_service.resolve_video_url(row_dict, video_rec_map, video_idx)
if recording_status in ("ready", "unavailable")
if recording_status != "recording"
else None
)
if resolved_v_url:
recording_status = "ready"
row_dict["video_url"] = resolved_v_url
else:
row_dict["video_url"] = None
row_dict["recording_status"] = recording_status
agent_names = agent_names_by_session.get(s_id, [])
sess_profile = model_service.resolve_session_profile(