Class: ClaudeMemory::Dashboard::Moments
- Inherits:
-
Object
- Object
- ClaudeMemory::Dashboard::Moments
- Defined in:
- lib/claude_memory/dashboard/moments.rb
Overview
Turns the flat activity_events log into enriched “moments” — the user-visible primitive for the feed-first dashboard. Each moment inlines the data needed to render its card (content preview, linked facts, resolved top_fact_ids) so the client never needs a second round trip per row.
A moment’s :kind is a stable narrative category the client uses to pick a card renderer. It’s derived from event_type + status so the client doesn’t have to re-derive the same mapping.
Constant Summary collapse
- DEFAULT_LIMIT =
50- CONTENT_PREVIEW_BYTES =
800- FEED_EVENT_TYPES =
%w[hook_context recall store_extraction hook_ingest hook_sweep].freeze
- KIND_TO_EVENT_TYPES =
Kind → underlying event_type(s). Used to pull only relevant rows from the DB when the caller specifies kinds; without this, a noisy stream of ingests pushes the value moments past the query limit.
{ "context_injection" => %w[hook_context], "context_skipped" => %w[hook_context], "recall_hit" => %w[recall], "recall_empty" => %w[recall], "extraction" => %w[store_extraction], "ingest" => %w[hook_ingest], "ingest_skipped" => %w[hook_ingest], "sweep" => %w[hook_sweep] }.freeze
Instance Method Summary collapse
-
#initialize(manager) ⇒ Moments
constructor
A new instance of Moments.
- #list(params = {}) ⇒ Object
Constructor Details
#initialize(manager) ⇒ Moments
Returns a new instance of Moments.
34 35 36 |
# File 'lib/claude_memory/dashboard/moments.rb', line 34 def initialize(manager) @manager = manager end |
Instance Method Details
#list(params = {}) ⇒ Object
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/claude_memory/dashboard/moments.rb', line 42 def list(params = {}) store = default_store return empty_response unless store limit = (params["limit"] || DEFAULT_LIMIT).to_i.clamp(1, 200) before = params["before"] kinds = parse_kinds(params["kinds"]) event_types = resolve_event_types(kinds) dataset = store.activity_events .where(event_type: event_types) .order(Sequel.desc(:occurred_at)) dataset = dataset.where { occurred_at < before } if before && !before.empty? # Fetch up to 2x limit so per-kind filtering still produces a full # page (e.g. recall_hit vs recall_empty both live under event_type=recall). rows = dataset.limit(limit * 2).all events = rows.map { |r| r[:details] = r[:detail_json] ? JSON.parse(r[:detail_json], symbolize_names: true) : nil r.delete(:detail_json) r } moments = events.map { |e| build_moment(store, e) } moments = moments.select { |m| kinds.include?(m[:kind]) } unless kinds.empty? has_more = moments.size > limit moments = moments.first(limit) attach_feedback(store, moments) { moments: moments, next_before: moments.last&.dig(:occurred_at), has_more: has_more } end |