personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add .mp4 to RAW_VIDEO_EXTENSIONS in retention

observe/utils.py already included .mp4 in VIDEO_EXTENSIONS, but retention was out of sync. This change aligns retention with the existing video extension handling.\n\nIt also ensures the is_segment_complete() guard correctly blocks purging segments that contain screen.mp4 when screen.jsonl has not been produced yet.

+16 -5
+1 -1
docs/JOURNAL.md
··· 180 180 - `raw_media_days` (integer or null) – Number of days to retain raw media when mode is `"days"`. Required when `raw_media` is `"days"`, ignored otherwise. 181 181 - `per_stream` (object) – Per-stream overrides keyed by stream name. Each entry supports `raw_media` and `raw_media_days`. Omitted fields inherit from the global retention settings. 182 182 183 - "Raw media" means layer 1 capture files only: audio files (`.flac`, `.opus`, `.ogg`, `.m4a`), video files (`.webm`, `.mov`), and screen diffs (`monitor_*_diff.png`). 183 + "Raw media" means layer 1 capture files only: audio files (`.flac`, `.opus`, `.ogg`, `.m4a`), video files (`.webm`, `.mov`, `.mp4`), and screen diffs (`monitor_*_diff.png`). 184 184 185 185 All layer 2 and layer 3 content is always preserved regardless of retention policy: transcripts (`audio.jsonl`, `screen.jsonl`), agent outputs (`agents/*.md`), speaker labels (`agents/speaker_labels.json`), facet events (`events/*.jsonl`), entity data, segment metadata (`stream.json`), and search index entries. 186 186
+13 -2
tests/test_retention.py
··· 31 31 assert is_raw_media(p), f"{ext} should be raw media" 32 32 33 33 def test_video_extensions(self, tmp_path): 34 - for ext in (".webm", ".mov"): 34 + for ext in (".webm", ".mov", ".mp4"): 35 35 p = tmp_path / f"screen{ext}" 36 36 p.touch() 37 37 assert is_raw_media(p), f"{ext} should be raw media" ··· 93 93 *, 94 94 audio=False, 95 95 video=False, 96 + video_name="screen.webm", 96 97 embeddings=False, 97 98 audio_extract=True, 98 99 screen_extract=True, ··· 108 109 if audio: 109 110 (seg / "audio.flac").write_bytes(b"audio") 110 111 if video: 111 - (seg / "screen.webm").write_bytes(b"video") 112 + (seg / video_name).write_bytes(b"video") 112 113 if embeddings: 113 114 (seg / "audio.npz").write_bytes(b"npz") 114 115 if audio and audio_extract: ··· 144 145 def test_incomplete_missing_screen_extract(self, tmp_path): 145 146 seg = _make_segment(tmp_path, video=True, screen_extract=False) 146 147 assert not is_segment_complete(seg) 148 + 149 + def test_incomplete_missing_screen_extract_for_mp4(self, tmp_path): 150 + seg = _make_segment( 151 + tmp_path, video=True, video_name="screen.mp4", screen_extract=False 152 + ) 153 + assert not is_segment_complete(seg) 154 + 155 + def test_complete_mp4_with_screen_extract(self, tmp_path): 156 + seg = _make_segment(tmp_path, video=True, video_name="screen.mp4") 157 + assert is_segment_complete(seg) 147 158 148 159 def test_incomplete_missing_speaker_labels(self, tmp_path): 149 160 seg = _make_segment(tmp_path, audio=True, embeddings=True, speaker_labels=False)
+2 -2
think/retention.py
··· 32 32 # --------------------------------------------------------------------------- 33 33 34 34 RAW_AUDIO_EXTENSIONS = frozenset({".flac", ".opus", ".ogg", ".m4a"}) 35 - RAW_VIDEO_EXTENSIONS = frozenset({".webm", ".mov"}) 35 + RAW_VIDEO_EXTENSIONS = frozenset({".webm", ".mov", ".mp4"}) 36 36 RAW_MEDIA_EXTENSIONS = RAW_AUDIO_EXTENSIONS | RAW_VIDEO_EXTENSIONS 37 37 38 38 ··· 40 40 """Check if a file is raw media (layer 1 capture). 41 41 42 42 Raw media: *.flac, *.opus, *.ogg, *.m4a (audio), 43 - *.webm, *.mov (video), monitor_*_diff.png (screen diffs). 43 + *.webm, *.mov, *.mp4 (video), monitor_*_diff.png (screen diffs). 44 44 """ 45 45 if path.suffix.lower() in RAW_MEDIA_EXTENSIONS: 46 46 return True