Reviewers were asked to include or exclude learning trajectories while
only seeing metadata — credit class, confidence, status. The decisions
and their redacted context turns were already captured and stored, but
no endpoint returned them, so the evidence was unreachable from the UI.
Add GET /api/learning/trajectories/:id and an expandable row on the
learning page that fetches on demand.
Also widen SentMessageSchema to accept matrixEventId, imageOutcomeCredit
and imageOutcomeAt. The learning loop added these to SentMessageRecord
and they flow out of /api/messages, but the strict contract rejected
them, breaking the dashboard.
Enable generation with shadow mode off so eligible examples reach reply
prompts.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds a Notifications tab to Settings so an operator can pick which events
reach their phone, published to a single self-hosted ntfy topic.
The notifier subscribes to the realtime event hub the dashboard already
consumes, so every notifiable event keeps exactly one publisher. Inbound
media had no event, so `media.received` is added and published from the
existing durable `gotImage` gate in the chat service — one notification
per conversation, never repeated when the room re-syncs. It feeds the
in-app notification bell as well.
- Best-effort delivery: a rejecting or unreachable ntfy server is logged
and never propagated into the path that produced the event.
- Events buffered before the notifier attaches are skipped, so a restart
cannot replay a backlog to somebody's phone.
- Settings are applied field by field onto the live config object, so a
save takes effect without a restart.
- The access token is environment-owned (NTFY_AUTH_TOKEN) like the other
secrets: stripped before the settings record is written, never returned
by the API, which reports only `hasAuthToken`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>