use std::path::Path;
use std::sync::Arc;
use theway_core::multiagent::graph::engine::DagEngine;
use theway_core::multiagent::jobs::SubagentJobRegistry;
use theway_core::multiagent::types::ToolSetResolver;
use theway_core::{AgentTool, StreamFn};
use theway_llm_provider::Model;
use super::dag_tools;
use super::install_skill;
use super::memory::MemoryTool;
use super::remove_skill;
use super::set_skill_state;
use super::skill::{self, SkillHarnessCell};
use super::skill_builder;
use super::subagent::{SubagentTool, SubagentToolsFn};
use theway_core::executor::ExecutorKind;
use theway_core::multiagent::types::AgentRunResolver;
pub const LOCAL_ONLY_ENGINE_TOOL_NAMES: &[&str] = &[
"memory",
"install_skill",
"skill_builder",
"set_skill_state",
"remove_skill",
];
fn warn_sandbox_omitted_engine_tools(kind: ExecutorKind) {
if kind == ExecutorKind::Sandbox {
tracing::warn!(
omitted = ?LOCAL_ONLY_ENGINE_TOOL_NAMES,
"sandbox execution mode: engine tools that write directly to the host FS are NOT \
registered (fail closed); the in-memory `skill` lookup and `reload` remain"
);
}
}
#[path = "reload.rs"]
pub mod reload;
pub type LocalToolsFn = Arc<dyn Fn() -> Vec<Arc<dyn AgentTool>> + Send + Sync>;
#[allow(clippy::too_many_arguments)]
pub fn engine_tools(
memory_dir: &Path,
base_dir: &Path,
dag_engine: &Arc<DagEngine>,
subagent_registry: &SubagentJobRegistry,
subagent_tools: SubagentToolsFn,
launch_resolver: AgentRunResolver,
spec_names: Vec<String>,
model: Option<&Model>,
stream_fn: Option<&StreamFn>,
skill_harness_cell: &SkillHarnessCell,
session_id: &str,
reload_runtime: reload::ReloadRuntimeSlot,
) -> Vec<Arc<dyn AgentTool>> {
engine_tools_for_kind(
memory_dir,
base_dir,
dag_engine,
subagent_registry,
subagent_tools,
launch_resolver,
spec_names,
model,
stream_fn,
skill_harness_cell,
session_id,
reload_runtime,
ExecutorKind::Local,
)
}
#[allow(clippy::too_many_arguments)]
pub fn engine_tools_for_kind(
memory_dir: &Path,
base_dir: &Path,
dag_engine: &Arc<DagEngine>,
subagent_registry: &SubagentJobRegistry,
subagent_tools: SubagentToolsFn,
launch_resolver: AgentRunResolver,
spec_names: Vec<String>,
model: Option<&Model>,
stream_fn: Option<&StreamFn>,
skill_harness_cell: &SkillHarnessCell,
session_id: &str,
reload_runtime: reload::ReloadRuntimeSlot,
kind: ExecutorKind,
) -> Vec<Arc<dyn AgentTool>> {
let mut tools = Vec::new();
tools.extend(dag_tools::DagTools::new(
dag_engine.clone(),
Some(session_id.to_string()),
spec_names.clone(),
subagent_registry.clone(),
));
tools.push(Arc::new(
SubagentTool::new(
model.cloned(),
stream_fn.cloned(),
subagent_tools,
launch_resolver,
spec_names,
subagent_registry.clone(),
)
.with_session_id(Some(session_id.to_string())),
));
tools.extend(skill_family(kind, skill_harness_cell, base_dir));
tools.push(Arc::new(reload::ReloadTool::new(
skill_harness_cell.clone(),
reload_runtime,
)));
push_memory_tool(&mut tools, memory_dir, kind);
warn_sandbox_omitted_engine_tools(kind);
tools
}
pub fn subagent_engine_tools(
memory_dir: &Path,
base_dir: &Path,
skill_harness_cell: &SkillHarnessCell,
) -> Vec<Arc<dyn AgentTool>> {
subagent_engine_tools_for_kind(
memory_dir,
base_dir,
skill_harness_cell,
ExecutorKind::Local,
)
}
pub fn subagent_engine_tools_for_kind(
memory_dir: &Path,
base_dir: &Path,
skill_harness_cell: &SkillHarnessCell,
kind: ExecutorKind,
) -> Vec<Arc<dyn AgentTool>> {
let mut tools = skill_family(kind, skill_harness_cell, base_dir);
push_memory_tool(&mut tools, memory_dir, kind);
warn_sandbox_omitted_engine_tools(kind);
tools
}
fn push_memory_tool(tools: &mut Vec<Arc<dyn AgentTool>>, memory_dir: &Path, kind: ExecutorKind) {
if kind == ExecutorKind::Local {
tools.push(Arc::new(MemoryTool::new(memory_dir.to_path_buf())));
}
}
pub fn subagent_tools(
memory_dir: &Path,
base_dir: &Path,
skill_harness_cell: &SkillHarnessCell,
local_tools: LocalToolsFn,
) -> ToolSetResolver {
subagent_tools_for_kind(
memory_dir,
base_dir,
skill_harness_cell,
local_tools,
ExecutorKind::Local,
)
}
pub fn subagent_tools_for_kind(
memory_dir: &Path,
base_dir: &Path,
skill_harness_cell: &SkillHarnessCell,
local_tools: LocalToolsFn,
kind: ExecutorKind,
) -> ToolSetResolver {
let memory_dir = memory_dir.to_path_buf();
let base_dir = base_dir.to_path_buf();
let cell = skill_harness_cell.clone();
Arc::new(move |_spec_name: &str| {
let mut tools = subagent_engine_tools_for_kind(&memory_dir, &base_dir, &cell, kind);
tools.extend(local_tools());
tools
})
}
fn skill_family(
kind: ExecutorKind,
skill_harness_cell: &SkillHarnessCell,
base_dir: &Path,
) -> Vec<Arc<dyn AgentTool>> {
let mut tools: Vec<Arc<dyn AgentTool>> =
vec![Arc::new(skill::SkillTool::new(skill_harness_cell.clone()))];
if kind == ExecutorKind::Local {
let skills_root = base_dir.join("skills");
tools.extend([
Arc::new(install_skill::InstallSkillTool::with_skills_root(
skill_harness_cell.clone(),
skills_root.clone(),
)) as Arc<dyn AgentTool>,
Arc::new(skill_builder::SkillBuilderTool::with_skills_root(
skill_harness_cell.clone(),
skills_root,
)),
Arc::new(set_skill_state::SetSkillStateTool::with_base_dir(
skill_harness_cell.clone(),
base_dir.to_path_buf(),
)),
Arc::new(remove_skill::RemoveSkillTool::with_base_dir(
skill_harness_cell.clone(),
base_dir.to_path_buf(),
)),
]);
}
tools
}