Files
zesdex/crates/zesdex-backend/src/app/subagent/spawn.rs
T
asepharyana 9a67137954 refactor: massive codebase restructuring — naming, splitting, DRY
Crate renames:
  - zesdex-entities::seaorm → domain (misleading name, no SeaORM used)
  - zesdex-dto → merged into zesdex-entities (100% re-exports)
  - zesdex-libs → zesdex-infra (vague name)

Module renames:
  - app/harness → guard (misleading: safety gatekeeper, not test harness)
  - runtime/commands → action_dispatch (name clashed with controller/command)
  - resources → prompts (embedded prompt text, not general resources)
  - tool/seqthink → sequential_think (unreadable abbreviation)
  - msglog/query → insert (module only inserts, never queries)

Dead code removal:
  - app/mode/help.rs (orphaned — not declared in mod.rs)
  - app/mode/loading.rs (orphaned — not declared in mod.rs)

File splitting (71 new files, avg ~115 lines/file):
  - app/runtime/actions/: 1→8 files (was 2030 lines)
  - view/overlays/: 1→16 files (was 1167 lines)
  - tool/lsp/: 1→8 per-tool files (was 909 lines)
  - main.rs: 1→5 files (session, daemon, attach, event_loop)
  - workflow/engine + hive_mind: 2→10 files
  - subagent/engine + auto: 2→9 files
  - lsp/provisioner: 1→5 files
  - review/: 1→6 files
  - guard/: 1→2 files (extracted patterns)
  - state/misc: 1→3 files (input, scroll)
  - mcp/: 1→3 files (transport, adapter)
  - stream/json_repair extracted from turn.rs

DRY:
  - Pattern constants (STUB_PATTERNS etc) in guard/patterns shared with subagent
  - 3 near-identical background spawners → 1 generic + thin wrappers
  - Shared spawn_subagent_with_drain() extracted
  - Shared create_session() in main
  - write_osc52 deduplicated

Bug fixes:
  - archive_message(): sess.db → db (wrong variable name)
  - execute_one_tool(): wrong parameter name
  - check_credential_read() function was missing (restored from test expectations)
2026-07-17 09:08:41 +07:00

104 lines
3.3 KiB
Rust

//! `AgentDefinition` -- declarative specification for instantiating a
//! subagent from workflow scripts or programmatic calls.
//!
//! Also provides a shared [`spawn_subagent_with_drain`] helper that
//! eliminates the channel-creation + drain-thread boilerplate duplicated
//! across `auto/mod.rs`, `review/mod.rs`, and `workflow/engine/mod.rs`.
use super::event::SubagentEvent;
use serde::{Deserialize, Serialize};
/// Declarative specification for instantiating a subagent: name, role,
/// optional system prompt, allowed tools, step budget, and temperature.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentDefinition {
pub name: String,
pub role: String,
pub system_prompt: Option<String>,
pub allowed_tools: Option<Vec<String>>,
pub max_steps: Option<usize>,
pub temperature: Option<f32>,
}
impl AgentDefinition {
/// Create an agent definition with the required name and role; all
/// optional fields start as `None`.
pub fn new(name: String, role: String) -> Self {
AgentDefinition {
name,
role,
system_prompt: None,
allowed_tools: None,
max_steps: None,
temperature: None,
}
}
/// Builder method: set the system prompt for this agent.
pub fn with_system_prompt(mut self, prompt: String) -> Self {
self.system_prompt = Some(prompt);
self
}
/// Builder method: set the allowed tool list for this agent.
pub fn with_allowed_tools(mut self, tools: Vec<String>) -> Self {
self.allowed_tools = Some(tools);
self
}
/// Builder method: set the maximum step count for this agent.
pub fn with_max_steps(mut self, steps: usize) -> Self {
self.max_steps = Some(steps);
self
}
}
/// Shared subagent spawning utility: creates an mpsc channel and spawns a
/// drain thread that forwards every [`SubagentEvent`] to `on_event`.
///
/// Returns the sender half (for passing to [`run_subagent`](super::engine::run_subagent))
/// and the drain thread's join handle so the caller can keep it alive for
/// the duration of the subagent run.
///
/// # Example
///
/// ```ignore
/// let (tx, _drain) = spawn_subagent_with_drain(|event| {
/// match &event {
/// SubagentEvent::ToolCall { tool, .. } => tracing::debug!("tool: {tool}"),
/// SubagentEvent::Completed => tracing::debug!("done"),
/// _ => {}
/// }
/// });
/// let verdict = run_subagent(&ctx, &tx)?;
/// ```
///
/// # Duplication eliminated
///
/// Previously every subagent caller inlined the same 5-line pattern:
///
/// ```ignore
/// let (tx, mut rx) = tokio::sync::mpsc::channel(32);
/// let _drain = std::thread::spawn(move || {
/// while let Some(event) = rx.blocking_recv() { ... }
/// });
/// ```
///
/// Callers that need a larger buffer (e.g. workflow engine uses 64) should
/// create the channel manually instead of using this helper.
pub fn spawn_subagent_with_drain<F>(
on_event: F,
) -> (tokio::sync::mpsc::Sender<SubagentEvent>, std::thread::JoinHandle<()>)
where
F: Fn(SubagentEvent) + Send + 'static,
{
let (tx, mut rx) = tokio::sync::mpsc::channel(32);
let drain = std::thread::spawn(move || {
while let Some(event) = rx.blocking_recv() {
on_event(event);
}
});
(tx, drain)
}