diff --git a/hive-c0re/assets/app.js b/hive-c0re/assets/app.js index 39facb8..ced15d2 100644 --- a/hive-c0re/assets/app.js +++ b/hive-c0re/assets/app.js @@ -509,6 +509,14 @@ { class: 'meta', title: 'sha currently locked in /meta/flake.lock' }, `deployed:${c.deployed_sha}`)); } + if (c.pending_reminders && c.pending_reminders > 0) { + head.append(el('span', + { + class: 'badge badge-reminder', + title: 'pending reminders queued for this agent — see the reminders section to view / cancel', + }, + `⏰ ${c.pending_reminders}`)); + } li.append(head); // ── line 2: action buttons ─────────────────────────────────── diff --git a/hive-c0re/assets/dashboard.css b/hive-c0re/assets/dashboard.css index b1ffe7f..46eeed6 100644 --- a/hive-c0re/assets/dashboard.css +++ b/hive-c0re/assets/dashboard.css @@ -119,6 +119,10 @@ a:hover { color: var(--muted); border-color: var(--purple-dim); background: rgba(127, 132, 156, 0.08); } +.badge-reminder { + color: var(--cyan); border-color: var(--cyan); + text-shadow: 0 0 6px rgba(137, 220, 235, 0.4); +} .container-row.tombstone { border-style: dashed; background: rgba(24, 24, 37, 0.35); diff --git a/hive-c0re/src/container_view.rs b/hive-c0re/src/container_view.rs index 869eea8..a946a88 100644 --- a/hive-c0re/src/container_view.rs +++ b/hive-c0re/src/container_view.rs @@ -29,6 +29,13 @@ pub struct ContainerView { /// for this agent's input. #[serde(skip_serializing_if = "Option::is_none")] pub deployed_sha: Option, + /// Count of this agent's pending reminders. Computed during + /// `build_all` via `Broker::count_pending_reminders_for`; the + /// dashboard renders a small chip when > 0. Updates with the + /// 10s `crash_watch` rescan + every container mutation site; + /// not real-time on remind/cancel-reminder but close enough. + #[serde(default)] + pub pending_reminders: u64, } /// Build the full container list. Wraps `lifecycle::list()` and @@ -53,6 +60,20 @@ pub async fn build_all(coord: &Coordinator) -> Vec { let deployed_sha = locked .get(&format!("agent-{logical}")) .map(|s| s[..s.len().min(12)].to_owned()); + // Recipient name the broker uses for this agent — sub-agents + // are addressed by logical name, the manager by the + // MANAGER_AGENT constant. Mirrors the rest of the broker + // surface so the count matches what `mcp__hyperhive__remind` + // queued. + let reminder_recipient = if is_manager { + hive_sh4re::MANAGER_AGENT + } else { + logical.as_str() + }; + let pending_reminders = coord + .broker + .count_pending_reminders_for(reminder_recipient) + .unwrap_or(0); out.push(ContainerView { port: lifecycle::agent_web_port(&logical), running: lifecycle::is_running(&logical).await, @@ -62,6 +83,7 @@ pub async fn build_all(coord: &Coordinator) -> Vec { needs_update, needs_login, deployed_sha, + pending_reminders, }); } out