pub enum DaemonEvent {
PalaceCreated {
id: String,
name: String,
source: ActivitySource,
},
DrawerAdded {
palace_id: String,
palace_name: String,
drawer_count: usize,
timestamp: DateTime<Utc>,
content_preview: String,
source: ActivitySource,
},
DrawerDeleted {
palace_id: String,
drawer_count: usize,
source: ActivitySource,
},
DreamCompleted {
palace_id: Option<String>,
merged: usize,
pruned: usize,
compacted: usize,
closets_updated: usize,
duration_ms: u64,
source: ActivitySource,
},
StatusChanged {
total_drawers: usize,
total_vectors: usize,
total_kg_triples: usize,
},
HookFired {
palace_id: Option<String>,
palace_name: Option<String>,
hook_type: HookType,
injection_kind: InjectionKind,
injection_length: u64,
trigger_prompt_excerpt: String,
timestamp: DateTime<Utc>,
duration_ms: u64,
source: ActivitySource,
},
}Expand description
Live daemon events broadcast to connected SSE subscribers.
Why: The dashboard needs push-driven updates so palace creation, drawer
add/delete, dream cycles, and aggregate status changes are visible without
polling. A single broadcast channel fans out to every connected browser.
What: Tagged enum serialized as {"type": "...", ...fields} over SSE.
Test: web::tests::sse_stream_emits_events subscribes, triggers a
mutation, and asserts the frame arrives.
Variants§
PalaceCreated
Fields
source: ActivitySourceOriginating subsystem (HTTP, MCP, Hook). Why (issue #96): the
UI badges each row with its source so operators can tell at a
glance whether a write came from the dashboard form, an MCP
tool call, or a hook-driven path. The wire-format key is
source (lower-case strings via serde rename_all on
ActivitySource).
DrawerAdded
Fields
palace_name: StringFriendly palace name (Palace.name) at write time. Why: lets SSE consumers (the dashboard activity feed) render the human-readable label without a separate id→name lookup. Empty string if the emitter could not resolve the name.
timestamp: DateTime<Utc>Wall-clock timestamp when the drawer was added. Why: SSE receivers want to render “just now / 2m ago” relative to the daemon’s clock, not the time the SSE frame happens to arrive.
content_preview: StringShort preview of the drawer’s content (whitespace-collapsed,
truncated to ~80 chars with an ellipsis when cut). Why: the TUI
activity feed and dashboard ticker want to show what was
stored, not just the running drawer count. Empty when the
emitter could not resolve the content (legacy clients tolerate
the missing field via #[serde(default)]).
source: ActivitySourceOriginating subsystem (issue #96).
DrawerDeleted
Fields
source: ActivitySourceOriginating subsystem (issue #96).
DreamCompleted
Fields
source: ActivitySourceOriginating subsystem (issue #96).
StatusChanged
HookFired
A Claude Code hook completed and rendered (or attempted to render) an injection block.
Why: pre-#XXX the activity feed only fired on drawer / palace / dream
writes, which meant a normal Claude Code session — whose only daemon
traffic is hook invocations — left the feed empty. Surfacing every
hook firing answers the user complaint “no activity in the TUI” and
gives operators a way to see how often each project palace is
actually picking up prompt-context / inbox-check work.
What: carries the resolved palace (or None if cwd resolution
failed), the HookType label, the InjectionKind label, the
rendered injection byte length, a short excerpt of the triggering
prompt (capped at ~80 chars; the full content stays in the JSONL
prompt log only), the timestamp, the hook’s wall-clock duration,
and the ActivitySource tag (always Hook for this variant).
Backwards-compatible: SSE clients that do not recognise the
hook_fired type tag can safely ignore the frame.
Fields
palace_name: Option<String>Friendly palace name at hook time — None if the registry
could not be consulted (HTTP path uses palace_id here when
no separate name is known).
injection_kind: InjectionKindinjection_length: u64Rendered injection size in bytes (0 when no injection was
emitted, e.g. SessionStart with an empty inbox).
trigger_prompt_excerpt: StringShort excerpt of the triggering prompt for the activity feed
display. Capped at ~80 chars with a trailing … when cut.
Why: the activity feed renders this directly; full prompt
content (which may be sensitive) stays in the JSONL log.
source: ActivitySourceAlways ActivitySource::Hook for this variant; encoded explicitly
so the same dispatch path (emit) can persist + broadcast it.
Implementations§
Source§impl DaemonEvent
impl DaemonEvent
Sourcepub fn type_str(&self) -> &'static str
pub fn type_str(&self) -> &'static str
Short discriminant label matching the SSE type field.
Why: the persisted activity log stores event_type as a string so
the UI can render the row without re-parsing the payload. Sharing
the same labels the SSE serializer uses keeps the wire and the
stored history consistent.
What: returns one of palace_created, drawer_added,
drawer_deleted, dream_completed, status_changed.
Test: daemon_event_type_str_matches_sse_tag in the lib tests.
Sourcepub fn palace_id(&self) -> Option<&str>
pub fn palace_id(&self) -> Option<&str>
palace_id if the event is scoped to a single palace.
Why: the activity log indexes entries by palace id so the UI can
filter by palace; daemon-wide events (status_changed,
dream-across-all-palaces) return None.
What: returns a borrowed string when the variant carries a palace
id, otherwise None.
Test: daemon_event_palace_id_extraction.
Sourcepub fn source(&self) -> Option<ActivitySource>
pub fn source(&self) -> Option<ActivitySource>
Originating subsystem if the event carries one.
Why: only mutation events carry a source; the aggregate
StatusChanged is recomputed by the daemon and has no caller, so
it returns None.
What: returns the variant’s source field where present.
Test: daemon_event_source_extraction.
Trait Implementations§
Source§impl Clone for DaemonEvent
impl Clone for DaemonEvent
Source§fn clone(&self) -> DaemonEvent
fn clone(&self) -> DaemonEvent
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for DaemonEvent
impl Debug for DaemonEvent
Auto Trait Implementations§
impl Freeze for DaemonEvent
impl RefUnwindSafe for DaemonEvent
impl Send for DaemonEvent
impl Sync for DaemonEvent
impl Unpin for DaemonEvent
impl UnsafeUnpin for DaemonEvent
impl UnwindSafe for DaemonEvent
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more