refactor: rename the daemon socket API Herdr* -> Colibri* (Sam & Claude)
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:
parent
604f1c8088
commit
b11bff2b00
8 changed files with 111 additions and 109 deletions
|
|
@ -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"] }
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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" }
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue