Implement tool approval, fix tool definition refresh, and wire embeddings

This commit addresses three critical issues from code review:

1. Tool approval enforcement: Tools declaring requires_approval() (shell,
   http, file write/patch, build_software) now gate execution. Adds
   PendingApproval struct, session-scoped auto-approved tools set, and
   approval flow with yes/no/always commands.

2. Tool definition refresh: Tool definitions now refresh each iteration
   in both chat and job loops, so newly built tools become visible
   immediately within the same session.

3. Worker tool call handling: Changed respond() to respond_with_tools()
   when select_tools returns empty, properly executing tool calls instead
   of formatting them as text.

Also includes prior work from the plan:
- Wire embeddings provider (OpenAI + NEAR AI) to workspace
- Load workspace system prompt (identity files) into LLM context
- Route heartbeat notifications through channel manager
- Enable auto-context compaction when threshold exceeded
- Refactor to config structs (AgentDeps, WorkerDeps, LlmCallRecord)
- Fix clippy warnings (saturating_sub, too_many_arguments)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
Illia Polosukhin
2026-02-03 11:34:10 -08:00
co-authored by Claude Opus 4.5
parent 8af48390a9
commit 2cc9aed364
18 changed files with 1079 additions and 198 deletions
+28 -4
View File
@@ -121,12 +121,29 @@ pub struct Reasoning {
llm: Arc<dyn LlmProvider>,
#[allow(dead_code)] // Will be used for sanitizing tool outputs
safety: Arc<SafetyLayer>,
/// Optional workspace for loading identity/system prompts.
workspace_system_prompt: Option<String>,
}
impl Reasoning {
/// Create a new reasoning engine.
pub fn new(llm: Arc<dyn LlmProvider>, safety: Arc<SafetyLayer>) -> Self {
Self { llm, safety }
Self {
llm,
safety,
workspace_system_prompt: None,
}
}
/// Set a custom system prompt from workspace identity files.
///
/// This is typically loaded from workspace.system_prompt() which combines
/// AGENTS.md, SOUL.md, USER.md, and IDENTITY.md into a unified prompt.
pub fn with_system_prompt(mut self, prompt: String) -> Self {
if !prompt.is_empty() {
self.workspace_system_prompt = Some(prompt);
}
self
}
/// Generate a plan for completing a goal.
@@ -361,11 +378,18 @@ Respond with a JSON plan in this format:
.map(|t| format!(" - {}: {}", t.name, t.description))
.collect();
format!(
"\n\n## Available Tools\nYou have access to these tools:\n{}\n\nCall tools directly when needed.",
"\n\n## Available Tools\nYou have access to these tools:\n{}\n\nCall tools when they would help accomplish the task.",
tool_list.join("\n")
)
};
// Include workspace identity prompt if available
let identity_section = if let Some(ref identity) = self.workspace_system_prompt {
format!("\n\n---\n\n{}", identity)
} else {
String::new()
};
format!(
r#"You are NEAR AI Agent, an autonomous assistant.
@@ -388,8 +412,8 @@ Here's the solution: [actual response to user]
- For code, use appropriate code blocks with language tags
- Call tools when they would help accomplish the task{}
The user sees ONLY content outside <thinking> tags."#,
tools_section
The user sees ONLY content outside <thinking> tags.{}"#,
tools_section, identity_section
)
}