operator inbox view on dashboard; agent ui doesn't clobber typing
This commit is contained in:
parent
070b237d03
commit
06ea0cf283
9 changed files with 132 additions and 12 deletions
|
|
@ -112,7 +112,7 @@ async fn dispatch(req: &AgentRequest, agent: &str, broker: &Broker) -> AgentResp
|
|||
},
|
||||
},
|
||||
AgentRequest::OperatorMsg { body } => match broker.send(&Message {
|
||||
from: "operator".to_owned(),
|
||||
from: hive_sh4re::OPERATOR_RECIPIENT.to_owned(),
|
||||
to: agent.to_owned(),
|
||||
body: body.clone(),
|
||||
}) {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,16 @@ CREATE INDEX IF NOT EXISTS idx_messages_undelivered
|
|||
/// may drop events past this; we send a `lagged` notice in their stream.
|
||||
const EVENT_CHANNEL: usize = 256;
|
||||
|
||||
/// One row in a `recent_for()` query — the broker's flat view of a
|
||||
/// message addressed to a given recipient.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct InboxRow {
|
||||
pub id: i64,
|
||||
pub from: String,
|
||||
pub body: String,
|
||||
pub at: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "snake_case", tag = "kind")]
|
||||
pub enum MessageEvent {
|
||||
|
|
@ -86,6 +96,32 @@ impl Broker {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Latest `limit` messages addressed to `recipient`, newest-first.
|
||||
/// Includes delivered + undelivered alike — used for the operator
|
||||
/// inbox view on the dashboard. Caller decides what to show.
|
||||
pub fn recent_for(&self, recipient: &str, limit: u64) -> Result<Vec<InboxRow>> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let limit_i =
|
||||
i64::try_from(limit.min(i64::MAX as u64)).unwrap_or(i64::MAX);
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT id, sender, body, sent_at
|
||||
FROM messages
|
||||
WHERE recipient = ?1
|
||||
ORDER BY id DESC
|
||||
LIMIT ?2",
|
||||
)?;
|
||||
let rows = stmt.query_map(params![recipient, limit_i], |row| {
|
||||
Ok(InboxRow {
|
||||
id: row.get(0)?,
|
||||
from: row.get(1)?,
|
||||
body: row.get(2)?,
|
||||
at: row.get(3)?,
|
||||
})
|
||||
})?;
|
||||
rows.collect::<rusqlite::Result<Vec<_>>>()
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Number of undelivered messages addressed to `recipient`. Non-mutating
|
||||
/// — used by the harness to surface "N unread" in tool-result status
|
||||
/// lines without popping the queue.
|
||||
|
|
|
|||
|
|
@ -92,6 +92,10 @@ struct StateSnapshot {
|
|||
containers: Vec<ContainerView>,
|
||||
transients: Vec<TransientView>,
|
||||
approvals: Vec<ApprovalView>,
|
||||
/// Latest messages addressed to `operator` — surfaces agent replies
|
||||
/// asynchronously so the operator can see them without watching the
|
||||
/// live panel during a turn.
|
||||
operator_inbox: Vec<crate::broker::InboxRow>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
|
@ -217,6 +221,12 @@ async fn api_state(
|
|||
approval_views.push(view);
|
||||
}
|
||||
|
||||
let operator_inbox = state
|
||||
.coord
|
||||
.broker
|
||||
.recent_for(hive_sh4re::OPERATOR_RECIPIENT, 50)
|
||||
.unwrap_or_default();
|
||||
|
||||
axum::Json(StateSnapshot {
|
||||
hostname,
|
||||
manager_port: MANAGER_PORT,
|
||||
|
|
@ -224,6 +234,7 @@ async fn api_state(
|
|||
containers,
|
||||
transients,
|
||||
approvals: approval_views,
|
||||
operator_inbox,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ async fn dispatch(req: &ManagerRequest, coord: &Coordinator) -> ManagerResponse
|
|||
},
|
||||
},
|
||||
ManagerRequest::OperatorMsg { body } => match coord.broker.send(&Message {
|
||||
from: "operator".to_owned(),
|
||||
from: hive_sh4re::OPERATOR_RECIPIENT.to_owned(),
|
||||
to: MANAGER_AGENT.to_owned(),
|
||||
body: body.clone(),
|
||||
}) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue