aidaemon 0.11.1

A personal AI agent that runs as a background daemon, accessible via Telegram, Slack, or Discord, with tool use, MCP integration, and persistent memory
Documentation
use super::completion_phase::CompletionCtx;
use super::*;
use crate::execution_policy::PolicyBundle;
use crate::traits::ProviderResponse;

pub(super) enum ResponsePhaseOutcome {
    ContinueLoop,
    Return(anyhow::Result<String>),
    ProceedToToolExecution,
}

#[allow(dead_code)]
pub(super) struct ResponsePhaseCtx<'a> {
    pub resp: &'a mut ProviderResponse,
    pub emitter: &'a crate::events::EventEmitter,
    pub task_id: &'a str,
    pub session_id: &'a str,
    pub user_text: &'a str,
    pub iteration: usize,
    pub task_start: Instant,
    pub task_tokens_used: u64,
    pub learning_ctx: &'a mut LearningContext,
    pub pending_system_messages: &'a mut Vec<SystemDirective>,
    pub tool_defs: &'a mut Vec<Value>,
    pub base_tool_defs: &'a mut Vec<Value>,
    pub available_capabilities: &'a mut HashMap<String, ToolCapabilities>,
    pub policy_bundle: &'a mut PolicyBundle,
    pub tools_allowed_for_user: bool,
    pub restrict_to_personal_memory_tools: bool,
    pub is_personal_memory_recall_turn: bool,
    pub is_reaffirmation_challenge_turn: bool,
    pub requests_external_verification: bool,
    pub llm_provider: Arc<dyn ModelProvider>,
    pub llm_router: Option<Router>,
    pub model: &'a mut String,
    pub user_role: UserRole,
    pub channel_ctx: ChannelContext,
    pub status_tx: Option<mpsc::Sender<StatusUpdate>>,
    pub total_successful_tool_calls: usize,
    pub stall_count: &'a mut usize,
    pub consecutive_clean_iterations: &'a mut usize,
    pub deferred_no_tool_streak: &'a mut usize,
    pub deferred_no_tool_model_switches: &'a mut usize,
    pub fallback_expanded_once: &'a mut bool,
    pub empty_response_retry_used: &'a mut bool,
    pub empty_response_retry_pending: &'a mut bool,
    pub empty_response_retry_note: &'a mut Option<String>,
    pub identity_prefill_text: &'a mut Option<String>,
    pub pending_background_ack: &'a mut Option<String>,
    pub pending_external_action_ack: &'a mut Option<String>,
    pub require_file_recheck_before_answer: &'a mut bool,
    pub completion_progress: &'a mut CompletionProgress,
    pub turn_context: &'a TurnContext,
    pub needs_tools_for_turn: &'a mut bool,
    pub force_text_response: &'a mut bool,
    pub execution_state: &'a mut ExecutionState,
    pub validation_state: &'a mut ValidationState,
}

pub(super) async fn run_response_phase(
    services: &super::services::AgentServices<'_>,
    ctx: &mut ResponsePhaseCtx<'_>,
) -> anyhow::Result<ResponsePhaseOutcome> {
    let completion_outcome = super::completion_phase::run_completion_phase(
        services,
        &mut CompletionCtx {
            resp: &mut *ctx.resp,
            emitter: ctx.emitter,
            task_id: ctx.task_id,
            session_id: ctx.session_id,
            user_text: ctx.user_text,
            iteration: ctx.iteration,
            task_start: ctx.task_start,
            learning_ctx: &mut *ctx.learning_ctx,
            pending_system_messages: &mut *ctx.pending_system_messages,
            tool_defs: &mut *ctx.tool_defs,
            base_tool_defs: &mut *ctx.base_tool_defs,
            available_capabilities: &mut *ctx.available_capabilities,
            policy_bundle: &mut *ctx.policy_bundle,
            restrict_to_personal_memory_tools: ctx.restrict_to_personal_memory_tools,
            llm_provider: ctx.llm_provider.clone(),
            llm_router: ctx.llm_router.clone(),
            model: &mut *ctx.model,
            channel_ctx: ctx.channel_ctx.clone(),
            user_role: ctx.user_role,
            total_successful_tool_calls: ctx.total_successful_tool_calls,
            stall_count: &mut *ctx.stall_count,
            consecutive_clean_iterations: &mut *ctx.consecutive_clean_iterations,
            deferred_no_tool_streak: &mut *ctx.deferred_no_tool_streak,
            deferred_no_tool_model_switches: &mut *ctx.deferred_no_tool_model_switches,
            fallback_expanded_once: &mut *ctx.fallback_expanded_once,
            empty_response_retry_used: &mut *ctx.empty_response_retry_used,
            empty_response_retry_pending: &mut *ctx.empty_response_retry_pending,
            empty_response_retry_note: &mut *ctx.empty_response_retry_note,
            identity_prefill_text: &mut *ctx.identity_prefill_text,
            pending_background_ack: &mut *ctx.pending_background_ack,
            pending_external_action_ack: &mut *ctx.pending_external_action_ack,
            require_file_recheck_before_answer: &mut *ctx.require_file_recheck_before_answer,
            completion_progress: &mut *ctx.completion_progress,
            turn_context: ctx.turn_context,
            needs_tools_for_turn: *ctx.needs_tools_for_turn,
            force_text_response: &mut *ctx.force_text_response,
            execution_state: &mut *ctx.execution_state,
            validation_state: &mut *ctx.validation_state,
        },
    )
    .await?;
    if let Some(outcome) = completion_outcome {
        return Ok(outcome);
    }

    Ok(ResponsePhaseOutcome::ProceedToToolExecution)
}