Agents can now put images inside markdown/HTML artifacts instead of only
posting them as standalone artifacts.
- New Asset model: an image uploaded ahead of the artifact that embeds it.
- POST /api/assets uploads an image (image/* only, 50 MB cap) and returns
a stable URL the agent references normally, e.g. . No custom
syntax; existing markdown/fragment rendering handles it.
- GET /assets/{id} serves the image, owner-scoped (404 for other users).
- On artifact creation the content is scanned for asset URLs and matching
assets are attached to it, so the image dies with the artifact it belongs
to. Uploads never referenced are swept after a 24h grace period.
- Quota: assets count toward the owner's quota at upload time and can
trigger eviction; evicting an artifact frees its assets too, and usage
now sums artifacts plus assets.
- daybook_post.py gains --upload-image, which prints the URL to embed.
- Docs and SKILL.md cover the upload-then-reference workflow.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Preview control-bar title now shows a native tooltip with the full
text on hover, so titles truncated by ellipsis are still readable.
Wired into the server render and both JS update paths.
- Splitter resize bounds are now per-pane: the artifact list can grow
up to 720px (was a shared 420px cap that made it feel un-draggable to
the right), while the sources sidebar stays 96-320px.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bounds each user's disk footprint and makes throwaway artifacts self-clean:
- Per-user quota (default 1 GB) stored on a new UserProfile model,
adjustable per user in Django admin. Artifact.size_bytes records each
artifact's on-disk footprint (primary file + markdown sidecar) at
ingest so usage is a cheap SUM.
- Automatic eviction: on each post, oldest unkept artifacts are deleted
(files and rows) until the new one fits under quota. If the quota is
full of kept artifacts, the post is rejected with HTTP 413 rather than
evicting anything protected.
- Keep flag (Artifact.is_kept): a Keep button in the preview control bar
toggles protection via POST /a/{id}/keep; kept items are never evicted.
Kept rows show a green "K" badge in the listing.
- Details panel: a Details toggle in the control bar expands a panel
under it with full source, type, created time, size, and tags.
- Settings -> Storage: usage bar (green/amber/red), used/quota summary,
artifact and kept counts; superusers also see total allocated quota vs
free disk with an oversubscription warning.
- Type indicator in the listing is now a small grey pill instead of
plain text.
Storage accounting/eviction logic lives in artifacts/storage.py.
Migrations 0009 (fields + UserProfile) and 0010 (backfill sizes and
create profiles for existing users). Fresh installs no-op the backfill.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Turns Daybook into a multi-user tool and reworks the feed UI:
- Multi-user: Django session login (username/password + remember-me),
per-user personal API tokens (DRF authtoken), whole app gated behind
login. Each artifact has an owner; feeds are private per user.
- Rename persona -> source (free-text label for whatever posted an
artifact: agent-profile, script, or person). source + tags form one
unified label system; single label filter matches tags, source has
its own filter/column/sidebar.
- Settings area (gear/user menu, top-right): Account (view/regenerate
token) and Demo Data (in-app button seeding one artifact per kind).
User account management via Django admin (superusers only).
- Feed rework: rolling 7-day window with sticky inline date separators
and infinite scroll; left sidebar is now a Sources list with per-source
unread counts; artifact type shown as text after title; source in its
own column.
- Unread tracking: read_at on Artifact, marked read when previewed,
bold titles for unread, sidebar unread badges, old banner removed.
- Tag colours: global shared registry, hash-based default with optional
per-tag override via tag_colors on POST/PATCH.
- Ingest: token resolves owner; same source name under two users' tokens
stays isolated. daybook_post.py renamed persona->source, requires a
real token, --check accepts a bearer token. Docs updated to match.
Migrations 0004-0008 rename persona, add owner (nullable -> backfill to
first superuser -> required), and add read_at. Fresh installs no-op the
backfill.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>