feat(v2): complete multi-LLM providers, TUI redesign, and advanced agent features
Multi-LLM Provider Support: - Add llm-core crate with LlmProvider trait abstraction - Implement Anthropic Claude API client with streaming - Implement OpenAI API client with streaming - Add token counting with SimpleTokenCounter and ClaudeTokenCounter - Add retry logic with exponential backoff and jitter Borderless TUI Redesign: - Rewrite theme system with terminal capability detection (Full/Unicode256/Basic) - Add provider tabs component with keybind switching [1]/[2]/[3] - Implement vim-modal input (Normal/Insert/Visual/Command modes) - Redesign chat panel with timestamps and streaming indicators - Add multi-provider status bar with cost tracking - Add Nerd Font icons with graceful ASCII fallbacks - Add syntax highlighting (syntect) and markdown rendering (pulldown-cmark) Advanced Agent Features: - Add system prompt builder with configurable components - Enhance subagent orchestration with parallel execution - Add git integration module for safe command detection - Add streaming tool results via channels - Expand tool set: AskUserQuestion, TodoWrite, LS, MultiEdit, BashOutput, KillShell - Add WebSearch with provider abstraction Plugin System Enhancement: - Add full agent definition parsing from YAML frontmatter - Add skill system with progressive disclosure - Wire plugin hooks into HookManager 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
114
crates/core/agent/tests/tool_context.rs
Normal file
114
crates/core/agent/tests/tool_context.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
// Test that ToolContext properly wires up the placeholder tools
|
||||
use agent_core::{ToolContext, execute_tool};
|
||||
use permissions::{Mode, PermissionManager};
|
||||
use tools_todo::{TodoList, TodoStatus};
|
||||
use tools_bash::ShellManager;
|
||||
use serde_json::json;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_todo_write_with_context() {
|
||||
let todo_list = TodoList::new();
|
||||
let ctx = ToolContext::new().with_todo_list(todo_list.clone());
|
||||
let perms = PermissionManager::new(Mode::Code); // Allow all tools
|
||||
|
||||
let arguments = json!({
|
||||
"todos": [
|
||||
{
|
||||
"content": "First task",
|
||||
"status": "pending",
|
||||
"active_form": "Working on first task"
|
||||
},
|
||||
{
|
||||
"content": "Second task",
|
||||
"status": "in_progress",
|
||||
"active_form": "Working on second task"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
let result = execute_tool("todo_write", &arguments, &perms, &ctx).await;
|
||||
assert!(result.is_ok(), "TodoWrite should succeed: {:?}", result);
|
||||
|
||||
// Verify the todos were written
|
||||
let todos = todo_list.read();
|
||||
assert_eq!(todos.len(), 2);
|
||||
assert_eq!(todos[0].content, "First task");
|
||||
assert_eq!(todos[1].status, TodoStatus::InProgress);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_todo_write_without_context() {
|
||||
let ctx = ToolContext::new(); // No todo_list
|
||||
let perms = PermissionManager::new(Mode::Code);
|
||||
|
||||
let arguments = json!({
|
||||
"todos": []
|
||||
});
|
||||
|
||||
let result = execute_tool("todo_write", &arguments, &perms, &ctx).await;
|
||||
assert!(result.is_err(), "TodoWrite should fail without TodoList");
|
||||
assert!(result.unwrap_err().to_string().contains("not available"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_bash_output_with_context() {
|
||||
let manager = ShellManager::new();
|
||||
let ctx = ToolContext::new().with_shell_manager(manager.clone());
|
||||
let perms = PermissionManager::new(Mode::Code);
|
||||
|
||||
// Start a shell and run a command
|
||||
let shell_id = manager.start_shell().await.unwrap();
|
||||
let _ = manager.execute(&shell_id, "echo test", None).await.unwrap();
|
||||
|
||||
let arguments = json!({
|
||||
"shell_id": shell_id
|
||||
});
|
||||
|
||||
let result = execute_tool("bash_output", &arguments, &perms, &ctx).await;
|
||||
assert!(result.is_ok(), "BashOutput should succeed: {:?}", result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_bash_output_without_context() {
|
||||
let ctx = ToolContext::new(); // No shell_manager
|
||||
let perms = PermissionManager::new(Mode::Code);
|
||||
|
||||
let arguments = json!({
|
||||
"shell_id": "fake-id"
|
||||
});
|
||||
|
||||
let result = execute_tool("bash_output", &arguments, &perms, &ctx).await;
|
||||
assert!(result.is_err(), "BashOutput should fail without ShellManager");
|
||||
assert!(result.unwrap_err().to_string().contains("not available"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_kill_shell_with_context() {
|
||||
let manager = ShellManager::new();
|
||||
let ctx = ToolContext::new().with_shell_manager(manager.clone());
|
||||
let perms = PermissionManager::new(Mode::Code);
|
||||
|
||||
// Start a shell
|
||||
let shell_id = manager.start_shell().await.unwrap();
|
||||
|
||||
let arguments = json!({
|
||||
"shell_id": shell_id
|
||||
});
|
||||
|
||||
let result = execute_tool("kill_shell", &arguments, &perms, &ctx).await;
|
||||
assert!(result.is_ok(), "KillShell should succeed: {:?}", result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_ask_user_without_context() {
|
||||
let ctx = ToolContext::new(); // No ask_sender
|
||||
let perms = PermissionManager::new(Mode::Code);
|
||||
|
||||
let arguments = json!({
|
||||
"questions": []
|
||||
});
|
||||
|
||||
let result = execute_tool("ask_user", &arguments, &perms, &ctx).await;
|
||||
assert!(result.is_err(), "AskUser should fail without AskSender");
|
||||
assert!(result.unwrap_err().to_string().contains("not available"));
|
||||
}
|
||||
Reference in New Issue
Block a user