# CURRENT.md — session state Last updated: 2026-09-16 This file tracks the work state. Read it first. Update it before you stop. ## Current focus (broad) The text layer is done. Voice actions and audio work. The local LLM harness (Veskslop agent, Go + Eino) is scaffolded and smoke-tested; the daemon-side task push is next. The web UI has a first mobile draft at 390px. ## In progress - Media archive project, branch `media-scrape-project` on `veskslop-t`. Design approved 2026-09-16 (docs: [media.md](docs/media.md), [media-scraper.md](docs/media-scraper.md), [slopbot.md](docs/slopbot.md), [media-mcp.md](docs/media-mcp.md)). Decisions: scrape the channel the command is sent in; slopbot (discord.js bot core) for guild channels and the puppet core bridge for group DMs; one self-contained SQLite + media dir per scrape under `media-scrape/db/` (gitignored); links indexed by default, downloads optional, third-party embeds never downloaded; 30 GiB default limit with a 90% warning and 5% no-delete overrun, usage bar and clean button in Settings → Bot; Media and Bot top-level tabs; MCP hooks designed for C8. The slopbot scaffold (config loader, invite helper) and its discord.js dependency were added 2026-09-16. C5 smoke test: `/api/media/status` and `/api/media` answer on a daemon with no core. Checkpoints, push after each: - [x] C1 design docs, AGENTS/INDEX pointers, `.gitignore` - [x] C2 core message shape: embeds, forwards, attachment metadata - [x] C3 media-scrape store and puppet walker, tests - [x] C4 slopbot backend, tests - [x] C5 daemon wiring: `scrape media` command, MediaService, `/api/media/*` - [x] C6 Media and Bot tabs in the Web UI - [x] C7a storage limits, retention, warning/confirm flow, storage and clean API - [x] C7b Settings group: limit field with info hover, usage bar, clean modal - [x] C8 MCP service hooks (`scrape_media`, scrape reads, storage, clean; reads in `run_commands`) - [x] C10 downloader: attachment and sticker bytes, hash-addressed, serial, size-capped, retries; embeds excluded in SQL; local files served at `/api/media///file` and preferred by the Media tab. Live run 2026-09-16: scrape `...-20260916083401` fetched the 784 KB attachment; storage counted actual bytes. - [x] C11 preview caching: Discord-hosted image/GIF previews (WebP 320px, hash-addressed, 5 MiB cap, one attempt) run after every walk in both modes. Live run `...-20260916090722`: `previewed: 2`; thumbs served over `/api/media///thumb` (200). - [x] C21 Playback of non-downloaded and expired media. `play_url` stores the embed's direct video (gifv/Tenor mp4); a new `/api/media///stream` route serves the local file or re-fetches the source message through slopbot to refresh expired signed CDN URLs and streams it with Range support. The viewer and **View full** use it, so Discord attachment videos play without a prior download and old links no longer dead-end. Live: 2.5 MB webm streamed 200 with a refreshed URL; play_url populated for a media.discordapp.net gifv mp4 and provider redirectors. - [x] C20 Cross-scrape dedupe and viewer polish. `queryMedia` and `stats` deduplicate by URL (newest scrape wins), so overlapping scrapes no longer duplicate cards or counters (live: 17 items / 17 unique). Viewer: big play icon top-right; play embeds a provider player or a direct mp4/webm/m4v, otherwise opens the external warning; **View full** opens the embedded media/player in a new tab while the link button opens the original page; duration is stored (Discord attachment duration, ffprobe for downloads, 0 normalized to unknown); favicon added. - [x] C19 External embed previews and MIME fixes. Embeds with no thumbnail in Discord's data are resolved via provider oEmbed/fxtwitter and an `og:image` fallback; preview bytes keep their real extension (`preview_ext`), so JPEG thumbnails serve as `image/jpeg` instead of mislabeled WebP (the browser "Content-Type image/webp is not supported" errors). The grid no longer uses embed page URLs as media, and first-frame `