Connectivity, Tools, Flows & Prompts
This commit is contained in:
parent
65d92ed22e
commit
ad42c0e51f
5 changed files with 57 additions and 37 deletions
|
|
@ -1,7 +1,7 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/* ExploreAgent is intentionally limited to read-only tools so it can be
|
||||
dispatched without approval for safe, unrestricted codebase exploration. */
|
||||
dispatched without approval for safe, unrestricted codebase exploration. */
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AgentType {
|
||||
|
|
@ -101,17 +101,17 @@ impl AgentPolicy {
|
|||
max_depth: 3,
|
||||
require_preview: false,
|
||||
timeout_seconds: 300,
|
||||
max_tokens: 16384,
|
||||
max_tokens: 131072,
|
||||
allowed_tools: vec![],
|
||||
forbidden_mutations: vec!["Cargo.toml".into(), "package.json".into()],
|
||||
},
|
||||
AgentType::Planner => Self {
|
||||
agent_type: agent_type.clone(),
|
||||
auto_approve: false,
|
||||
max_depth: 3,
|
||||
max_depth: 5,
|
||||
require_preview: true,
|
||||
timeout_seconds: 120,
|
||||
max_tokens: 16384,
|
||||
max_tokens: 131072,
|
||||
allowed_tools: vec![],
|
||||
forbidden_mutations: vec![],
|
||||
},
|
||||
|
|
@ -126,7 +126,7 @@ impl AgentPolicy {
|
|||
forbidden_mutations: vec![],
|
||||
},
|
||||
/* ExploreAgent: auto-approved, read-only tools only, short timeout.
|
||||
Forbidden mutations set to wildcard to block all writes. */
|
||||
Forbidden mutations set to wildcard to block all writes. */
|
||||
AgentType::ExploreAgent => Self {
|
||||
agent_type: agent_type.clone(),
|
||||
auto_approve: true,
|
||||
|
|
@ -149,8 +149,8 @@ impl AgentPolicy {
|
|||
}
|
||||
|
||||
/* Per-agent prompt configuration. Stored in the DB and resolved on task
|
||||
dispatch. Resolution order: task instructions > DB override > this config
|
||||
> compiled-in default > tool protocol. */
|
||||
dispatch. Resolution order: task instructions > DB override > this config
|
||||
> compiled-in default > tool protocol. */
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AgentPromptConfig {
|
||||
pub agent_type: AgentType,
|
||||
|
|
@ -208,7 +208,12 @@ mod tests {
|
|||
for agent_type in &types {
|
||||
let s = agent_type.as_str();
|
||||
let parsed = AgentType::from_str(s);
|
||||
assert_eq!(parsed.as_ref(), Some(agent_type), "roundtrip failed for {:?}", agent_type);
|
||||
assert_eq!(
|
||||
parsed.as_ref(),
|
||||
Some(agent_type),
|
||||
"roundtrip failed for {:?}",
|
||||
agent_type
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
18
src/lib.rs
18
src/lib.rs
|
|
@ -18,7 +18,9 @@ pub mod task;
|
|||
pub mod todo;
|
||||
pub mod tools;
|
||||
|
||||
pub use agents::{AgentPolicy, AgentPromptConfig, AgentType, ApprovalMode, PlannedAction, SimulationResult};
|
||||
pub use agents::{
|
||||
AgentPolicy, AgentPromptConfig, AgentType, ApprovalMode, PlannedAction, SimulationResult,
|
||||
};
|
||||
pub use ai_response::{Conversation, Message};
|
||||
pub use conclusion::{
|
||||
Artifact, ArtifactType, ConclusionCard, ConclusionMetrics, ConclusionTrigger,
|
||||
|
|
@ -32,11 +34,11 @@ pub use project::{Project, ProjectFile, ProjectId, ProjectSettings};
|
|||
pub use task::{Task, TaskResult};
|
||||
pub use todo::{Dependency, ToDo};
|
||||
pub use tools::{
|
||||
format_tool_error, format_tools_json, format_tool_result, parse_tool_call_blocks,
|
||||
parse_tool_call_stream, tool_definitions, tool_ids_for_agent_type, ApprovalRequirement,
|
||||
CompositionStep, ContextVisibility, ExecutionContext, MarketplaceSearchResults, ParsedToolCall,
|
||||
REEF_PROXY_TOOLS, GLOBAL_TOOLS, is_reef_proxy_tool, is_global_tool,
|
||||
TestAction, ToolComposition, ToolDefinition, ToolDependency, ToolDocumentation, ToolExample,
|
||||
ToolListing, ToolParser, ToolPlugin, ToolExecutor, ToolRegistry, ToolTest, ToolTestResult, ToolTestSuite,
|
||||
ToolTestSuiteResult, ToolVersion,
|
||||
format_tool_error, format_tool_result, format_tools_json, is_global_tool, is_reef_proxy_tool,
|
||||
parse_tool_call_blocks, parse_tool_call_stream, tool_definitions, tool_ids_for_agent_type,
|
||||
ApprovalRequirement, CompositionStep, ContextVisibility, ExecutionContext,
|
||||
MarketplaceSearchResults, ParsedToolCall, TestAction, ToolComposition, ToolDefinition,
|
||||
ToolDependency, ToolDocumentation, ToolExample, ToolExecutor, ToolListing, ToolParser,
|
||||
ToolPlugin, ToolRegistry, ToolTest, ToolTestResult, ToolTestSuite, ToolTestSuiteResult,
|
||||
ToolVersion, GLOBAL_TOOLS, REEF_PROXY_TOOLS,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::path::{Path, PathBuf, Component};
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
|
||||
/* Maximum allowed path depth as a safety measure */
|
||||
const MAX_PATH_DEPTH: usize = 64;
|
||||
|
|
@ -54,15 +54,15 @@ pub fn resolve_sandboxed_path(validated: &Path, sandbox_root: &Path) -> Result<P
|
|||
};
|
||||
|
||||
let canonical = if full_path.exists() {
|
||||
full_path.canonicalize().map_err(|e| {
|
||||
format!("Failed to canonicalize path: {}", e)
|
||||
})?
|
||||
full_path
|
||||
.canonicalize()
|
||||
.map_err(|e| format!("Failed to canonicalize path: {}", e))?
|
||||
} else {
|
||||
if let Some(parent) = full_path.parent() {
|
||||
if parent.exists() {
|
||||
let canonical_parent = parent.canonicalize().map_err(|e| {
|
||||
format!("Failed to canonicalize parent path: {}", e)
|
||||
})?;
|
||||
let canonical_parent = parent
|
||||
.canonicalize()
|
||||
.map_err(|e| format!("Failed to canonicalize parent path: {}", e))?;
|
||||
if !canonical_parent.starts_with(sandbox_root) {
|
||||
return Err("Path escapes sandbox root boundaries".to_string());
|
||||
}
|
||||
|
|
@ -81,7 +81,8 @@ pub fn resolve_sandboxed_path(validated: &Path, sandbox_root: &Path) -> Result<P
|
|||
}
|
||||
|
||||
fn has_directory_traversal(path: &Path) -> bool {
|
||||
path.components().any(|comp| matches!(comp, Component::ParentDir))
|
||||
path.components()
|
||||
.any(|comp| matches!(comp, Component::ParentDir))
|
||||
}
|
||||
|
||||
fn contains_null_bytes(path: &Path) -> bool {
|
||||
|
|
@ -90,10 +91,11 @@ fn contains_null_bytes(path: &Path) -> bool {
|
|||
|
||||
fn contains_shell_metacharacters(path: &Path) -> bool {
|
||||
const SHELL_METACHARACTERS: &[char] = &[
|
||||
'|', ';', '&', '$', '`', '>', '<', '(', ')', '{', '}',
|
||||
'!', '#', '*', '?', '[', ']', '~', '\n', '\r',
|
||||
'|', ';', '&', '$', '`', '>', '<', '(', ')', '{', '}', '!', '#', '*', '?', '[', ']', '~',
|
||||
'\n', '\r',
|
||||
];
|
||||
path.to_str().map_or(true, |s| s.contains(SHELL_METACHARACTERS))
|
||||
path.to_str()
|
||||
.map_or(true, |s| s.contains(SHELL_METACHARACTERS))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -152,7 +154,12 @@ mod tests {
|
|||
#[test]
|
||||
fn test_rejects_path_with_shell_metacharacters() {
|
||||
let attacks = vec![
|
||||
"file|echo", "file;rm", "file$(id)", "file`id`", "file>out", "file<in",
|
||||
"file|echo",
|
||||
"file;rm",
|
||||
"file$(id)",
|
||||
"file`id`",
|
||||
"file>out",
|
||||
"file<in",
|
||||
];
|
||||
for path in attacks {
|
||||
let result = validate_path(path);
|
||||
|
|
|
|||
22
src/todo.rs
22
src/todo.rs
|
|
@ -102,7 +102,10 @@ impl ToDo {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn validate(&self, deps: &std::collections::HashMap<Uuid, Vec<Uuid>>) -> Result<(), ValidationError> {
|
||||
pub fn validate(
|
||||
&self,
|
||||
deps: &std::collections::HashMap<Uuid, Vec<Uuid>>,
|
||||
) -> Result<(), ValidationError> {
|
||||
if self.title.is_empty() {
|
||||
return Err(ValidationError::EmptyTitle);
|
||||
}
|
||||
|
|
@ -127,8 +130,10 @@ impl ToDo {
|
|||
}
|
||||
|
||||
/* DFS cycle detection over a full dependency graph. Returns the first
|
||||
todo_id found in a cycle, or None when the graph is acyclic. */
|
||||
pub fn detect_cycle_in_graph(deps: &std::collections::HashMap<Uuid, Vec<Uuid>>) -> Option<Uuid> {
|
||||
todo_id found in a cycle, or None when the graph is acyclic. */
|
||||
pub fn detect_cycle_in_graph(
|
||||
deps: &std::collections::HashMap<Uuid, Vec<Uuid>>,
|
||||
) -> Option<Uuid> {
|
||||
let mut visited: HashSet<Uuid> = HashSet::new();
|
||||
let mut in_stack: HashSet<Uuid> = HashSet::new();
|
||||
|
||||
|
|
@ -210,10 +215,7 @@ impl ToDo {
|
|||
}
|
||||
|
||||
/// Walk the parent_todo_id chain and return the depth (1 = root, 2 = child, etc.)
|
||||
pub fn calculate_depth(
|
||||
todo_id: Uuid,
|
||||
get_parent: impl Fn(Uuid) -> Option<Uuid>,
|
||||
) -> usize {
|
||||
pub fn calculate_depth(todo_id: Uuid, get_parent: impl Fn(Uuid) -> Option<Uuid>) -> usize {
|
||||
let mut depth = 1usize;
|
||||
let mut current_id = get_parent(todo_id);
|
||||
let mut visited = HashSet::new();
|
||||
|
|
@ -264,7 +266,6 @@ impl ToDo {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
@ -287,7 +288,10 @@ mod tests {
|
|||
fn test_todo_validate_empty_title() {
|
||||
let todo = ToDo::default();
|
||||
let deps = std::collections::HashMap::new();
|
||||
assert!(matches!(todo.validate(&deps), Err(ValidationError::EmptyTitle)));
|
||||
assert!(matches!(
|
||||
todo.validate(&deps),
|
||||
Err(ValidationError::EmptyTitle)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -1867,6 +1867,8 @@ pub fn tool_ids_for_agent_type(agent_type: &crate::agents::AgentType) -> Option<
|
|||
"get_documentation_context",
|
||||
"documentation_tree",
|
||||
"store_file_doc",
|
||||
"kanban_create_todo",
|
||||
"kanban_update_todo",
|
||||
]),
|
||||
crate::agents::AgentType::TestJudge => curated(&[
|
||||
"read_file",
|
||||
|
|
|
|||
Loading…
Reference in a new issue