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
+1 -1
View File
@@ -9,4 +9,4 @@ mod analytics;
mod store;
pub use analytics::{JobStats, ToolStats};
pub use store::Store;
pub use store::{LlmCallRecord, Store};
+22 -19
View File
@@ -9,6 +9,19 @@ use crate::config::DatabaseConfig;
use crate::context::{ActionRecord, JobContext, JobState};
use crate::error::DatabaseError;
/// Record for an LLM call to be persisted.
#[derive(Debug, Clone)]
pub struct LlmCallRecord<'a> {
pub job_id: Option<Uuid>,
pub conversation_id: Option<Uuid>,
pub provider: &'a str,
pub model: &'a str,
pub input_tokens: u32,
pub output_tokens: u32,
pub cost: Decimal,
pub purpose: Option<&'a str>,
}
/// Database store for the agent.
pub struct Store {
pool: Pool,
@@ -335,17 +348,7 @@ impl Store {
// ==================== LLM Calls ====================
/// Record an LLM call.
pub async fn record_llm_call(
&self,
job_id: Option<Uuid>,
conversation_id: Option<Uuid>,
provider: &str,
model: &str,
input_tokens: u32,
output_tokens: u32,
cost: Decimal,
purpose: Option<&str>,
) -> Result<Uuid, DatabaseError> {
pub async fn record_llm_call(&self, record: &LlmCallRecord<'_>) -> Result<Uuid, DatabaseError> {
let conn = self.conn().await?;
let id = Uuid::new_v4();
@@ -356,14 +359,14 @@ impl Store {
"#,
&[
&id,
&job_id,
&conversation_id,
&provider,
&model,
&(input_tokens as i32),
&(output_tokens as i32),
&cost,
&purpose,
&record.job_id,
&record.conversation_id,
&record.provider,
&record.model,
&(record.input_tokens as i32),
&(record.output_tokens as i32),
&record.cost,
&record.purpose,
],
)
.await?;