refactor: rename the daemon socket API Herdr* -> Colibri* (Sam & Claude)
Some checks failed
CI / rust (pull_request) Has been cancelled
CI / markdown (pull_request) Has been cancelled

The colibri-daemon's own control-plane socket was named after Herdr (the AGPL
Linux supervision tool whose protocol shape it borrowed), which made logs/types
("Herdr socket API listening", HerdrCommand/HerdrResponse) look like a Herdr
dependency. There is none — no herdr crate, process, or network call. zot is the
agent; this is Colibri's control-plane socket.

Renamed Colibri's OWN API only:
- HerdrCommand -> ColibriCommand, HerdrResponse -> ColibriResponse (daemon defs +
  socket.rs + colibri-client usages).
- log/doc/Cargo strings: "Herdr socket API"/"operator dashboard"/"Herdr Unix
  socket" -> "Colibri control-plane socket" (daemon, clawdie).

Wire-compatible: the JSON `cmd` values come from #[serde(rename=...)] and are
unchanged. Kept legitimate references to Herdr *the tool* (glasspane lineage
"reimplements Herdr's glasspane capability", "Herdr's 5-state model"; client
"display clients (Herdr on Linux…)"; tui "herdr-like").

build + test + clippy -D warnings + fmt --check clean; runtime confirms the
daemon now logs "Colibri control-plane socket listening".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Sam & Claude 2026-06-13 11:07:58 +02:00
parent 604f1c8088
commit b11bff2b00
8 changed files with 111 additions and 109 deletions

View file

@ -3,7 +3,7 @@ name = "clawdie"
version = "0.0.1"
edition = "2021"
license = "AGPL-3.0-only"
description = "Clawdie — the simplified, operator-friendly Colibri agent (glasspane + herdr + DeepSeek/Telegram) as one small binary"
description = "Clawdie — the simplified, operator-friendly Colibri agent (glasspane + supervision + DeepSeek/Telegram) as one small binary"
[[bin]]
name = "clawdie"
@ -11,7 +11,7 @@ path = "src/main.rs"
[dependencies]
# Reuse the proven control-plane core: glasspane (supervision radar),
# the Herdr Unix-socket API, the coordination loop, and session lifecycle.
# the Colibri control-plane socket, the coordination loop, and session lifecycle.
colibri-daemon = { path = "../colibri-daemon" }
colibri-glasspane = { path = "../colibri-glasspane" }
tokio = { version = "1", features = ["full"] }

View file

@ -1,7 +1,7 @@
//! clawdie — the simplified, operator-friendly Colibri agent.
//!
//! One small binary, two credentials, zero ceremony. It bundles the proven
//! control-plane core (glasspane supervision radar + the Herdr Unix-socket API +
//! control-plane core (glasspane supervision radar + the Colibri control-plane socket +
//! the coordination loop — the "split brain") and puts a DeepSeek-backed
//! Telegram bot in front of it. That is the entire out-of-the-box surface.
//!
@ -70,7 +70,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
"credentials resolved (build flags + runtime env)"
);
// ── Bring up the control-plane core (glasspane + Herdr socket + loop) ────
// ── Bring up the control-plane core (glasspane + control-plane socket + loop) ────
let config = DaemonConfig::from_env();
info!(
host = %config.host,
@ -103,7 +103,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}
}
// Herdr Unix-socket API.
// Colibri control-plane socket.
let socket_state = state.clone();
let socket_shutdown = state.shutdown_rx.resubscribe();
let socket_handle = tokio::spawn(async move {

View file

@ -7,7 +7,7 @@
use std::path::{Path, PathBuf};
use colibri_daemon::{HerdrCommand, HerdrResponse};
use colibri_daemon::{ColibriCommand, ColibriResponse};
use colibri_glasspane::GlasspaneSnapshot;
use serde::de::DeserializeOwned;
use thiserror::Error;
@ -48,7 +48,7 @@ impl DaemonClient {
}
/// Send one command and parse the daemon's generic response envelope.
pub async fn send(&self, command: &HerdrCommand) -> Result<HerdrResponse, ClientError> {
pub async fn send(&self, command: &ColibriCommand) -> Result<ColibriResponse, ClientError> {
let stream = UnixStream::connect(&self.socket_path).await?;
let (reader, mut writer) = stream.into_split();
let mut reader = BufReader::new(reader);
@ -70,7 +70,7 @@ impl DaemonClient {
/// Send one command and deserialize its `data` payload to a typed value.
pub async fn request<T: DeserializeOwned>(
&self,
command: &HerdrCommand,
command: &ColibriCommand,
) -> Result<T, ClientError> {
let response = self.send(command).await?;
if !response.ok {
@ -85,15 +85,15 @@ impl DaemonClient {
}
pub async fn status(&self) -> Result<serde_json::Value, ClientError> {
self.request(&HerdrCommand::Status).await
self.request(&ColibriCommand::Status).await
}
pub async fn glasspane_snapshot(&self) -> Result<GlasspaneSnapshot, ClientError> {
self.request(&HerdrCommand::GlasspaneSnapshot).await
self.request(&ColibriCommand::GlasspaneSnapshot).await
}
pub async fn list_sessions(&self) -> Result<serde_json::Value, ClientError> {
self.request(&HerdrCommand::ListSessions).await
self.request(&ColibriCommand::ListSessions).await
}
pub async fn spawn_agent(
@ -103,7 +103,7 @@ impl DaemonClient {
session_id: Option<String>,
system_prompt: Option<String>,
) -> Result<serde_json::Value, ClientError> {
self.request(&HerdrCommand::SpawnAgent {
self.request(&ColibriCommand::SpawnAgent {
provider: provider.into(),
model: model.into(),
session_id,
@ -117,7 +117,7 @@ impl DaemonClient {
&self,
agent_id: impl Into<String>,
) -> Result<serde_json::Value, ClientError> {
self.request(&HerdrCommand::KillAgent {
self.request(&ColibriCommand::KillAgent {
agent_id: agent_id.into(),
})
.await
@ -127,7 +127,7 @@ impl DaemonClient {
&self,
session_id: impl Into<String>,
) -> Result<serde_json::Value, ClientError> {
self.request(&HerdrCommand::GetSession {
self.request(&ColibriCommand::GetSession {
session_id: session_id.into(),
})
.await
@ -137,7 +137,7 @@ impl DaemonClient {
&self,
session_id: impl Into<String>,
) -> Result<serde_json::Value, ClientError> {
self.request(&HerdrCommand::CompactSession {
self.request(&ColibriCommand::CompactSession {
session_id: session_id.into(),
})
.await
@ -147,7 +147,7 @@ impl DaemonClient {
&self,
status: Option<String>,
) -> Result<serde_json::Value, ClientError> {
self.request(&HerdrCommand::ListTasks { status }).await
self.request(&ColibriCommand::ListTasks { status }).await
}
pub async fn create_task(
@ -155,7 +155,7 @@ impl DaemonClient {
title: impl Into<String>,
description: Option<String>,
) -> Result<serde_json::Value, ClientError> {
self.request(&HerdrCommand::CreateTask {
self.request(&ColibriCommand::CreateTask {
title: title.into(),
description,
})
@ -168,7 +168,7 @@ impl DaemonClient {
description: Option<String>,
capabilities: Vec<String>,
) -> Result<serde_json::Value, ClientError> {
self.request(&HerdrCommand::IntakeTask {
self.request(&ColibriCommand::IntakeTask {
title: title.into(),
description,
capabilities: Some(capabilities),
@ -177,7 +177,7 @@ impl DaemonClient {
}
pub async fn list_skills(&self) -> Result<serde_json::Value, ClientError> {
self.request(&HerdrCommand::ListSkills).await
self.request(&ColibriCommand::ListSkills).await
}
pub async fn register_skill(
@ -186,7 +186,7 @@ impl DaemonClient {
description: Option<String>,
category: Option<String>,
) -> Result<serde_json::Value, ClientError> {
self.request(&HerdrCommand::RegisterSkill {
self.request(&ColibriCommand::RegisterSkill {
name: name.into(),
description,
category,

View file

@ -3,7 +3,7 @@ name = "colibri-daemon"
version = "0.0.1"
edition = "2021"
license = "AGPL-3.0-only"
description = "Always-on Rust service: agent session lifecycle, subprocess spawner, Herdr Unix socket API"
description = "Always-on Rust service: agent session lifecycle, subprocess spawner, Colibri control-plane socket API"
[dependencies]
colibri-contracts = { path = "../colibri-contracts" }

View file

@ -15,7 +15,7 @@ use serde::{Deserialize, Serialize};
pub struct DaemonConfig {
/// Directory for session JSONL files and state.
pub data_dir: PathBuf,
/// Path for the Herdr Unix socket.
/// Path for the Colibri control-plane socket.
pub socket_path: PathBuf,
/// Maximum bytes per session before automatic rollover.
pub session_max_bytes: u64,

View file

@ -4,7 +4,7 @@
//! Core responsibilities:
//! - Session lifecycle (JSONL write/read/prune + context-window management)
//! - Agent subprocess spawner (provider/model config, retry/backoff)
//! - Unix socket API for Herdr operator dashboard
//! - Unix socket API for the Colibri control plane
//! - Provider routing: DeepSeek primary, OpenRouter/Anthropic fallback
//! - DeepSeek cache discipline: immutable system prefix + appendable log +
//! volatile scratch (the 3-region prompt model)
@ -27,10 +27,10 @@ use serde::{Deserialize, Serialize};
// Wire types for the socket API
// ---------------------------------------------------------------------------
/// Inbound command from Herdr over the Unix socket.
/// Inbound command over the Colibri control-plane socket.
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "cmd")]
pub enum HerdrCommand {
pub enum ColibriCommand {
#[serde(rename = "status")]
Status,
#[serde(rename = "glasspane-snapshot")]
@ -91,9 +91,9 @@ pub enum HerdrCommand {
SetCostMode { mode: String },
}
/// Outbound response to Herdr.
/// Outbound control-plane response.
#[derive(Debug, Serialize, Deserialize)]
pub struct HerdrResponse {
pub struct ColibriResponse {
pub ok: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
@ -101,7 +101,7 @@ pub struct HerdrResponse {
pub data: Option<serde_json::Value>,
}
impl HerdrResponse {
impl ColibriResponse {
pub fn ok(data: serde_json::Value) -> Self {
Self {
ok: true,

View file

@ -3,7 +3,7 @@
//! Starts:
//! - Session manager (JSONL persistence + context window management)
//! - Agent spawner (subprocess lifecycle + provider routing)
//! - Herdr Unix socket API
//! - Colibri control-plane socket API
//!
//! Graceful shutdown on SIGTERM/SIGINT (or Ctrl+C).
@ -68,7 +68,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}
}
// Start the Herdr socket server
// Start the Colibri control-plane socket server
let socket_state = state.clone();
let socket_shutdown = state.shutdown_rx.resubscribe();
let socket_handle = tokio::spawn(async move {

View file

@ -1,4 +1,4 @@
//! Unix socket API for Herdr operator dashboard.
//! Unix socket API for the Colibri control plane (operator CLI/TUI).
//!
//! Listens on a Unix domain socket, accepts newline-delimited JSON commands,
//! and dispatches to the session manager and agent spawner.
@ -20,13 +20,13 @@ use tokio::sync::broadcast;
use tracing::{debug, error, info, trace, warn};
use crate::spawner::{AgentSpawnConfig, Provider, Spawner};
use crate::{HerdrCommand, HerdrResponse, SharedState};
use crate::{ColibriCommand, ColibriResponse, SharedState};
// ---------------------------------------------------------------------------
// Socket server
// ---------------------------------------------------------------------------
/// Run the Herdr socket API server. Binds to the configured Unix socket path
/// Run the Colibri control-plane socket server. Binds to the configured Unix socket path
/// and processes inbound commands until a shutdown signal is received.
pub async fn serve(state: SharedState, mut shutdown_rx: broadcast::Receiver<()>) {
let socket_path = state.config.socket_path.clone();
@ -86,7 +86,7 @@ pub async fn serve(state: SharedState, mut shutdown_rx: broadcast::Receiver<()>)
}
}
info!(path = %socket_path.display(), "Herdr socket API listening");
info!(path = %socket_path.display(), "Colibri control-plane socket listening");
loop {
select! {
@ -110,7 +110,7 @@ pub async fn serve(state: SharedState, mut shutdown_rx: broadcast::Receiver<()>)
// Clean up socket file on graceful exit
let _ = tokio::fs::remove_file(&socket_path).await;
info!("Herdr socket API shut down");
info!("Colibri control-plane socket shut down");
}
// ---------------------------------------------------------------------------
@ -136,9 +136,9 @@ async fn handle_connection(stream: UnixStream, state: SharedState) {
continue;
}
let response = match serde_json::from_str::<HerdrCommand>(trimmed) {
let response = match serde_json::from_str::<ColibriCommand>(trimmed) {
Ok(cmd) => dispatch(cmd, &state).await,
Err(e) => HerdrResponse::err(format!("invalid command: {e}")),
Err(e) => ColibriResponse::err(format!("invalid command: {e}")),
};
let mut response_json = serde_json::to_string(&response).unwrap_or_default();
@ -161,12 +161,12 @@ async fn handle_connection(stream: UnixStream, state: SharedState) {
// Command dispatch
// ---------------------------------------------------------------------------
async fn dispatch(cmd: HerdrCommand, state: &SharedState) -> HerdrResponse {
async fn dispatch(cmd: ColibriCommand, state: &SharedState) -> ColibriResponse {
match cmd {
HerdrCommand::Status => cmd_status(state).await,
HerdrCommand::GlasspaneSnapshot => cmd_glasspane_snapshot(state).await,
HerdrCommand::ListSessions => cmd_list_sessions(state).await,
HerdrCommand::SpawnAgent {
ColibriCommand::Status => cmd_status(state).await,
ColibriCommand::GlasspaneSnapshot => cmd_glasspane_snapshot(state).await,
ColibriCommand::ListSessions => cmd_list_sessions(state).await,
ColibriCommand::SpawnAgent {
provider,
model,
session_id,
@ -183,40 +183,42 @@ async fn dispatch(cmd: HerdrCommand, state: &SharedState) -> HerdrResponse {
)
.await
}
HerdrCommand::KillAgent { agent_id } => cmd_kill_agent(state, agent_id).await,
HerdrCommand::GetSession { session_id } => cmd_get_session(state, session_id).await,
HerdrCommand::CompactSession { session_id } => cmd_compact_session(state, session_id).await,
ColibriCommand::KillAgent { agent_id } => cmd_kill_agent(state, agent_id).await,
ColibriCommand::GetSession { session_id } => cmd_get_session(state, session_id).await,
ColibriCommand::CompactSession { session_id } => {
cmd_compact_session(state, session_id).await
}
// ── Coordination board ────────────────────────────────
HerdrCommand::ListTasks { status } => cmd_list_tasks(state, status).await,
HerdrCommand::CreateTask { title, description } => {
ColibriCommand::ListTasks { status } => cmd_list_tasks(state, status).await,
ColibriCommand::CreateTask { title, description } => {
cmd_create_task(state, title, description).await
}
HerdrCommand::TransitionTask { task_id, status } => {
ColibriCommand::TransitionTask { task_id, status } => {
cmd_transition_task(state, task_id, status).await
}
HerdrCommand::ClaimTask { task_id, agent_id } => {
ColibriCommand::ClaimTask { task_id, agent_id } => {
cmd_claim_task(state, task_id, agent_id).await
}
HerdrCommand::ListAgents => cmd_list_agents(state).await,
HerdrCommand::RegisterAgent { name, capabilities } => {
ColibriCommand::ListAgents => cmd_list_agents(state).await,
ColibriCommand::RegisterAgent { name, capabilities } => {
cmd_register_agent(state, name, capabilities).await
}
HerdrCommand::ListSkills => cmd_list_skills(state).await,
HerdrCommand::RegisterSkill {
ColibriCommand::ListSkills => cmd_list_skills(state).await,
ColibriCommand::RegisterSkill {
name,
description,
category,
} => cmd_register_skill(state, name, description, category).await,
HerdrCommand::IntakeTask {
ColibriCommand::IntakeTask {
title,
description,
capabilities,
} => cmd_intake_task(state, title, description, capabilities).await,
HerdrCommand::SetCostMode { mode } => cmd_set_cost_mode(state, mode).await,
ColibriCommand::SetCostMode { mode } => cmd_set_cost_mode(state, mode).await,
}
}
async fn cmd_status(state: &SharedState) -> HerdrResponse {
async fn cmd_status(state: &SharedState) -> ColibriResponse {
let session_count = state.sessions.len();
let agent_count = state.agents.len();
let pane_count = state
@ -261,7 +263,7 @@ async fn cmd_status(state: &SharedState) -> HerdrResponse {
let last_warm_hit = *state.last_warm_cache_hit.read().await;
let last_warm_tokens = *state.last_warm_hit_tokens.read().await;
HerdrResponse::ok(serde_json::json!({
ColibriResponse::ok(serde_json::json!({
"daemon": "colibri-daemon",
"version": env!("CARGO_PKG_VERSION"),
"host": state.config.host,
@ -293,19 +295,19 @@ async fn cmd_status(state: &SharedState) -> HerdrResponse {
}))
}
async fn cmd_glasspane_snapshot(state: &SharedState) -> HerdrResponse {
async fn cmd_glasspane_snapshot(state: &SharedState) -> ColibriResponse {
let snapshot = state.glasspane.read().await.snapshot_at(
state.config.host.clone(),
SystemTime::now(),
DEFAULT_STALL_AFTER,
);
match serde_json::to_value(snapshot) {
Ok(value) => HerdrResponse::ok(value),
Err(e) => HerdrResponse::err(format!("snapshot serialization failed: {e}")),
Ok(value) => ColibriResponse::ok(value),
Err(e) => ColibriResponse::err(format!("snapshot serialization failed: {e}")),
}
}
async fn cmd_list_sessions(state: &SharedState) -> HerdrResponse {
async fn cmd_list_sessions(state: &SharedState) -> ColibriResponse {
let mut sessions = Vec::new();
for entry in state.sessions.iter() {
let session = entry.value();
@ -317,7 +319,7 @@ async fn cmd_list_sessions(state: &SharedState) -> HerdrResponse {
}));
}
HerdrResponse::ok(serde_json::json!({
ColibriResponse::ok(serde_json::json!({
"sessions": sessions,
}))
}
@ -329,13 +331,13 @@ async fn cmd_spawn_agent(
session_id: Option<String>,
system_prompt: Option<String>,
local_args: Option<Vec<String>>,
) -> HerdrResponse {
) -> ColibriResponse {
let provider = match provider_str.to_lowercase().as_str() {
"deepseek" => Provider::DeepSeek,
"openrouter" => Provider::OpenRouter,
"anthropic" => Provider::Anthropic,
"local" => Provider::Local,
other => return HerdrResponse::err(format!("unknown provider: {other}")),
other => return ColibriResponse::err(format!("unknown provider: {other}")),
};
let (binary, args) = if provider == Provider::Local {
@ -424,16 +426,16 @@ async fn cmd_spawn_agent(
});
}
HerdrResponse::ok(serde_json::json!({
ColibriResponse::ok(serde_json::json!({
"agent_id": id,
"status": "running",
}))
}
Err(e) => HerdrResponse::err(format!("spawn failed: {e}")),
Err(e) => ColibriResponse::err(format!("spawn failed: {e}")),
}
}
async fn cmd_kill_agent(state: &SharedState, agent_id: String) -> HerdrResponse {
async fn cmd_kill_agent(state: &SharedState, agent_id: String) -> ColibriResponse {
match state.agents.remove(&agent_id) {
Some((_id, handle)) => match handle.kill().await {
Ok(()) => {
@ -442,24 +444,24 @@ async fn cmd_kill_agent(state: &SharedState, agent_id: String) -> HerdrResponse
r#"{"type":"error"}"#,
SystemTime::now(),
);
HerdrResponse::ok(serde_json::json!({
ColibriResponse::ok(serde_json::json!({
"agent_id": agent_id,
"status": "stopped",
}))
}
Err(e) => HerdrResponse::err(format!("kill failed: {e}")),
Err(e) => ColibriResponse::err(format!("kill failed: {e}")),
},
None => HerdrResponse::err(format!("agent not found: {agent_id}")),
None => ColibriResponse::err(format!("agent not found: {agent_id}")),
}
}
async fn cmd_get_session(state: &SharedState, session_id: String) -> HerdrResponse {
async fn cmd_get_session(state: &SharedState, session_id: String) -> ColibriResponse {
match state.sessions.get(&session_id) {
Some(session) => {
let turns = session.value().turns().await;
let messages = session.value().build_prompt_messages().await;
HerdrResponse::ok(serde_json::json!({
ColibriResponse::ok(serde_json::json!({
"session_id": session_id,
"turn_count": turns.len(),
"byte_count": session.value().byte_count().await,
@ -468,7 +470,7 @@ async fn cmd_get_session(state: &SharedState, session_id: String) -> HerdrRespon
"prompt_messages": messages,
}))
}
None => HerdrResponse::err(format!("session not found: {session_id}")),
None => ColibriResponse::err(format!("session not found: {session_id}")),
}
}
@ -512,27 +514,27 @@ async fn stream_agent_stdout_to_glasspane(
}
}
async fn cmd_compact_session(state: &SharedState, session_id: String) -> HerdrResponse {
async fn cmd_compact_session(state: &SharedState, session_id: String) -> ColibriResponse {
match state.sessions.get(&session_id) {
Some(session) => match session.value().compact_oldest_turns().await {
Ok(()) => HerdrResponse::ok(serde_json::json!({
Ok(()) => ColibriResponse::ok(serde_json::json!({
"session_id": session_id,
"turn_count": session.value().turn_count().await,
"status": "compacted",
})),
Err(e) => HerdrResponse::err(format!("compaction failed: {e}")),
Err(e) => ColibriResponse::err(format!("compaction failed: {e}")),
},
None => HerdrResponse::err(format!("session not found: {session_id}")),
None => ColibriResponse::err(format!("session not found: {session_id}")),
}
}
// ── Coordination board handlers ──────────────────────────────────
async fn cmd_list_tasks(state: &SharedState, status: Option<String>) -> HerdrResponse {
async fn cmd_list_tasks(state: &SharedState, status: Option<String>) -> ColibriResponse {
let filter = status.and_then(|s| colibri_store::TaskStatus::parse_status(&s));
match state.store.lock().unwrap().list_tasks(filter) {
Ok(tasks) => HerdrResponse::ok(serde_json::to_value(tasks).unwrap_or_default()),
Err(e) => HerdrResponse::err(format!("list tasks failed: {e}")),
Ok(tasks) => ColibriResponse::ok(serde_json::to_value(tasks).unwrap_or_default()),
Err(e) => ColibriResponse::err(format!("list tasks failed: {e}")),
}
}
@ -540,15 +542,15 @@ async fn cmd_create_task(
state: &SharedState,
title: String,
description: Option<String>,
) -> HerdrResponse {
) -> ColibriResponse {
match state
.store
.lock()
.unwrap()
.create_task(&title, description.as_deref())
{
Ok(task) => HerdrResponse::ok(serde_json::to_value(task).unwrap_or_default()),
Err(e) => HerdrResponse::err(format!("create task failed: {e}")),
Ok(task) => ColibriResponse::ok(serde_json::to_value(task).unwrap_or_default()),
Err(e) => ColibriResponse::err(format!("create task failed: {e}")),
}
}
@ -556,10 +558,10 @@ async fn cmd_transition_task(
state: &SharedState,
task_id: String,
status: String,
) -> HerdrResponse {
) -> ColibriResponse {
let new_status = match colibri_store::TaskStatus::parse_status(&status) {
Some(s) => s,
None => return HerdrResponse::err(format!("invalid status: {status}")),
None => return ColibriResponse::err(format!("invalid status: {status}")),
};
match state
.store
@ -567,22 +569,22 @@ async fn cmd_transition_task(
.unwrap()
.transition_task(&task_id, new_status)
{
Ok(task) => HerdrResponse::ok(serde_json::to_value(task).unwrap_or_default()),
Err(e) => HerdrResponse::err(format!("transition task failed: {e}")),
Ok(task) => ColibriResponse::ok(serde_json::to_value(task).unwrap_or_default()),
Err(e) => ColibriResponse::err(format!("transition task failed: {e}")),
}
}
async fn cmd_claim_task(state: &SharedState, task_id: String, agent_id: String) -> HerdrResponse {
async fn cmd_claim_task(state: &SharedState, task_id: String, agent_id: String) -> ColibriResponse {
match state.store.lock().unwrap().claim_task(&task_id, &agent_id) {
Ok(task) => HerdrResponse::ok(serde_json::to_value(task).unwrap_or_default()),
Err(e) => HerdrResponse::err(format!("claim task failed: {e}")),
Ok(task) => ColibriResponse::ok(serde_json::to_value(task).unwrap_or_default()),
Err(e) => ColibriResponse::err(format!("claim task failed: {e}")),
}
}
async fn cmd_list_agents(state: &SharedState) -> HerdrResponse {
async fn cmd_list_agents(state: &SharedState) -> ColibriResponse {
match state.store.lock().unwrap().list_agents() {
Ok(agents) => HerdrResponse::ok(serde_json::to_value(agents).unwrap_or_default()),
Err(e) => HerdrResponse::err(format!("list agents failed: {e}")),
Ok(agents) => ColibriResponse::ok(serde_json::to_value(agents).unwrap_or_default()),
Err(e) => ColibriResponse::err(format!("list agents failed: {e}")),
}
}
@ -590,18 +592,18 @@ async fn cmd_register_agent(
state: &SharedState,
name: String,
capabilities: Option<serde_json::Value>,
) -> HerdrResponse {
) -> ColibriResponse {
let caps = capabilities.unwrap_or(serde_json::json!([]));
match state.store.lock().unwrap().register_agent(&name, caps) {
Ok(agent) => HerdrResponse::ok(serde_json::to_value(agent).unwrap_or_default()),
Err(e) => HerdrResponse::err(format!("register agent failed: {e}")),
Ok(agent) => ColibriResponse::ok(serde_json::to_value(agent).unwrap_or_default()),
Err(e) => ColibriResponse::err(format!("register agent failed: {e}")),
}
}
async fn cmd_list_skills(state: &SharedState) -> HerdrResponse {
async fn cmd_list_skills(state: &SharedState) -> ColibriResponse {
match state.store.lock().unwrap().list_skills() {
Ok(skills) => HerdrResponse::ok(serde_json::to_value(skills).unwrap_or_default()),
Err(e) => HerdrResponse::err(format!("list skills failed: {e}")),
Ok(skills) => ColibriResponse::ok(serde_json::to_value(skills).unwrap_or_default()),
Err(e) => ColibriResponse::err(format!("list skills failed: {e}")),
}
}
@ -610,14 +612,14 @@ async fn cmd_register_skill(
name: String,
description: Option<String>,
category: Option<String>,
) -> HerdrResponse {
) -> ColibriResponse {
match state.store.lock().unwrap().register_skill(
&name,
description.as_deref(),
category.as_deref(),
) {
Ok(skill) => HerdrResponse::ok(serde_json::to_value(skill).unwrap_or_default()),
Err(e) => HerdrResponse::err(format!("register skill failed: {e}")),
Ok(skill) => ColibriResponse::ok(serde_json::to_value(skill).unwrap_or_default()),
Err(e) => ColibriResponse::err(format!("register skill failed: {e}")),
}
}
@ -626,7 +628,7 @@ async fn cmd_intake_task(
title: String,
description: Option<String>,
capabilities: Option<Vec<String>>,
) -> HerdrResponse {
) -> ColibriResponse {
let caps = capabilities.unwrap_or_default();
let mut scheduler = state.scheduler.lock().await;
scheduler.submit(crate::scheduler::TaskRequest {
@ -634,10 +636,10 @@ async fn cmd_intake_task(
description,
required_capabilities: caps,
});
HerdrResponse::ok(serde_json::json!({"status": "queued"}))
ColibriResponse::ok(serde_json::json!({"status": "queued"}))
}
async fn cmd_set_cost_mode(state: &SharedState, mode: String) -> HerdrResponse {
async fn cmd_set_cost_mode(state: &SharedState, mode: String) -> ColibriResponse {
match crate::cost::CostMode::parse(&mode) {
Some(cost_mode) => {
info!(
@ -650,13 +652,13 @@ async fn cmd_set_cost_mode(state: &SharedState, mode: String) -> HerdrResponse {
// For now, we acknowledge and log the request.
// T1.4 scope: runtime-only. Persistence (COLIBRI_COST_MODE in
// config/socket-update) is post-Phase-5.
HerdrResponse::ok(serde_json::json!({
ColibriResponse::ok(serde_json::json!({
"previous": state.config.cost_mode,
"requested": cost_mode.as_str(),
"status": "acknowledged",
}))
}
None => HerdrResponse::err(format!(
None => ColibriResponse::err(format!(
"invalid cost mode: {mode}. Use fast, smart, or max."
)),
}
@ -700,8 +702,8 @@ mod tests {
#[test]
fn glasspane_snapshot_command_deserializes() {
let cmd: HerdrCommand = serde_json::from_str(r#"{"cmd":"glasspane-snapshot"}"#).unwrap();
assert!(matches!(cmd, HerdrCommand::GlasspaneSnapshot));
let cmd: ColibriCommand = serde_json::from_str(r#"{"cmd":"glasspane-snapshot"}"#).unwrap();
assert!(matches!(cmd, ColibriCommand::GlasspaneSnapshot));
}
#[tokio::test]