feat: implement company pipeline orchestration with user commands for full, quick, and skip modes

This commit is contained in:
asepharyana
2026-07-13 05:33:04 +07:00
parent a6eed9e574
commit 2310c2df7f
9 changed files with 269 additions and 94 deletions
+41 -9
View File
@@ -36,6 +36,9 @@ pub struct AgentStatus {
pub started_at: Option<i64>,
pub completed_at: Option<i64>,
pub error: Option<String>,
/// Human-readable progress message (e.g. "editing src/main.rs",
/// "running cargo test"). Shown in the TUI panel alongside the state.
pub progress: Option<String>,
}
/// A single agent tracked within a workflow run.
@@ -124,6 +127,7 @@ fn spawn_single_agent(
started_at: Some(started_at),
completed_at: None,
error: None,
progress: None,
},
);
}
@@ -156,26 +160,52 @@ fn spawn_single_agent(
// long-running agents. No abort mechanism is wired yet at this level;
// future work can expose a kill-switch per agent via the live callback.
// Create an mpsc channel and drain events in a background thread so
// run_subagent's blocking_send never blocks (previously the _rx was
// dropped immediately, which would cause blocking_send to panic/fail
// on a closed channel).
// Create an mpsc channel and drain events in a background thread.
// The drain thread also pushes intra-division progress updates to the
// live callback (current tool being executed), so the TUI panel shows
// real-time "editing X" or "running build" instead of just "Running…".
let (tx, rx) = tokio::sync::mpsc::channel(64);
let drain_agent_id = agent_id.to_string();
let drain_agent_name = agent_name.to_string();
let drain_live = live.cloned();
let drain_started_at = started_at;
let _drain_thread = std::thread::spawn(move || {
// Drain all events so run_subagent's blocking_send never blocks.
// Individual SubagentEvent items are not surfaced to the TUI —
// the live state callbacks above handle coarse-grained Running /
// Completed / Failed status. ToolCall / ToolResult / StepCompleted
// events are traced at debug level for observability.
use crate::app::subagent::event::SubagentEvent;
let mut rx = rx;
while let Some(event) = rx.blocking_recv() {
match &event {
SubagentEvent::ToolCall { _tool, _args } => {
tracing::debug!("[subagent] tool call: {}", _tool);
// Push intra-division progress: which tool is running
if let Some(ref f) = drain_live {
f(
drain_agent_id.clone(),
drain_agent_name.clone(),
AgentStatus {
state: AgentState::Running,
started_at: Some(drain_started_at),
completed_at: None,
error: None,
progress: Some(format!("tool: {}", _tool)),
},
);
}
}
SubagentEvent::ToolResult { _tool, .. } => {
tracing::debug!("[subagent] tool result: {}", _tool);
if let Some(ref f) = drain_live {
f(
drain_agent_id.clone(),
drain_agent_name.clone(),
AgentStatus {
state: AgentState::Running,
started_at: Some(drain_started_at),
completed_at: None,
error: None,
progress: Some(format!("done: {}", _tool)),
},
);
}
}
SubagentEvent::StepCompleted { _step, .. } => {
tracing::trace!("[subagent] step {} completed", _step);
@@ -226,6 +256,7 @@ fn spawn_single_agent(
started_at: Some(started_at),
completed_at: Some(completed_at),
error: None,
progress: None,
},
),
Err(e) => f(
@@ -236,6 +267,7 @@ fn spawn_single_agent(
started_at: Some(started_at),
completed_at: Some(completed_at),
error: Some(e.to_string()),
progress: None,
},
),
}