magi-code 0.96.1

Repository-aware CLI coding agent for terminal work
Documentation
use super::*;

impl AgentSession {
    pub(super) fn prepare_initial_run<'run, 'sink>(
        &self,
        run: &AgentRunRequest<'run, 'sink>,
        tool_configuration: ToolRequestConfiguration,
        preflight_projection: Option<PreflightRequestProjection>,
    ) -> anyhow::Result<PreparedInitialRun<'run>> {
        let effective_prompt = run
            .effective_prompt
            .map(str::to_string)
            .or_else(|| self.effective_prompt_for_run(run));
        let provider_prompt = effective_prompt
            .as_deref()
            .unwrap_or(run.prompt)
            .to_string();
        // Always revalidate replay, even when preflight supplied a projection. External
        // edits, rotation, and fresh diagnostics must be observed before persisting input.
        let InitialConversation {
            conversation,
            session_read_diagnostics,
            replay_warnings,
        } = self.build_initial_conversation(&provider_prompt, run.session, run.tools)?;
        let base_conversation = Arc::<[ProviderConversationItem]>::from(conversation);
        let prompt_cache_key = prompt_cache_key_for_run(run, &self.provider_id, &self.model);
        let conversation_id = run
            .session
            .map(|session| crate::providers::conversation_id_for_session(session.id()));
        // Transport identity/timeout are applied to the actual turn request, not token input.
        let initial_request = self.request_from_shared_conversation(
            Arc::clone(&base_conversation),
            &[],
            None,
            None,
            &tool_configuration,
        );
        let projection = preflight_projection.and_then(|preflight| {
            preflight.matching_projection(&self.provider_id, &initial_request, run.session)
        });
        let request_projection_cache = match projection {
            Some(projection) => RequestTokenProjectionCache::new_with_base_projection(
                &self.provider_id,
                &self.model,
                projection,
            ),
            None => {
                RequestTokenProjectionCache::new_for_request(&self.provider_id, &initial_request)
            }
        };
        if self.context_budget.enabled {
            self.ensure_request_context_fits(
                request_projection_cache.initial_projection(),
                ContextBudgetPhase::Initial,
            )?;
        }
        Ok(PreparedInitialRun {
            provider_prompt,
            session_read_diagnostics,
            replay_warnings,
            base_conversation,
            prompt_cache_key,
            conversation_id,
            request_projection_cache,
            session_persistence: SessionPersistence::new(run.session, run.cwd)
                .with_task_scope(run.tools),
            tool_request_configuration: tool_configuration,
        })
    }

    pub(super) fn effective_prompt_for_run(&self, run: &AgentRunRequest<'_, '_>) -> Option<String> {
        if run.prompt_origin == crate::output::UserPromptOrigin::AutomaticCompaction {
            return None;
        }
        Self::expand_effective_prompt(
            run.prompt,
            run.tools,
            run.invocation_mode,
            run.cwd,
            &run.cancellation,
        )
    }

    pub(crate) fn effective_prompt_for_projection(
        prompt: &str,
        tools: Option<&ToolRuntime>,
        invocation_mode: InvocationMode,
    ) -> String {
        tools
            .and_then(|tools| {
                Self::expand_effective_prompt(
                    prompt,
                    Some(tools),
                    invocation_mode,
                    tools.cwd_canonical(),
                    &AgentCancellation::default(),
                )
            })
            .unwrap_or_else(|| prompt.to_string())
    }

    pub(super) fn expand_effective_prompt(
        prompt: &str,
        tools: Option<&ToolRuntime>,
        invocation_mode: InvocationMode,
        cwd: &Path,
        cancellation: &AgentCancellation,
    ) -> Option<String> {
        if !should_expand_prompt_context(invocation_mode) {
            return None;
        }
        prompt_injections::expand_user_prompt(prompt, cwd, cancellation, |definition| match tools {
            Some(tools) => tools.dispatch_with_context(
                "bash",
                json!({
                    "command": definition.command,
                    "intent": definition.description,
                    "timeout": 30,
                }),
                crate::output::ToolDispatchContext::new_with_hook_context_and_cancellation(
                    None,
                    None,
                    HookContextMetadata::new(invocation_mode),
                    cancellation.clone(),
                ),
            ),
            None => ToolResult {
                tool_name: "context_injection".to_string(),
                success: false,
                content: "Context command unavailable: tools are disabled".to_string(),
                metadata: serde_json::Value::Null,
                display: Default::default(),
            },
        })
    }

    pub(super) fn record_initial_user_input(
        &self,
        prepared: &mut PreparedInitialRun<'_>,
        run: &mut AgentRunRequest<'_, '_>,
    ) -> anyhow::Result<()> {
        let mut payload =
            json!({"text": prepared.provider_prompt, "origin": run.prompt_origin.label()});
        // A real caller resuming a delegated session can clarify its task, not raise its ceiling.
        // Child launch/schema-recovery prompts are runtime-generated, not new authorization.
        if run.prompt_origin == crate::output::UserPromptOrigin::User
            && run.invocation_mode != InvocationMode::Subagent
            && run.tools.is_some_and(|tools| tools.task_scope.is_some())
        {
            payload["task_clarification"] = true.into();
        }
        if self.reasoning_updates_enabled {
            payload["reasoning_selection"] = self.reasoning_selection_payload();
        }
        prepared
            .session_persistence
            .record_required(SessionEventKind::UserInput, payload)
            .map_err(RequiredUserInputPersistenceError::new)?;
        if run.prompt_origin == crate::output::UserPromptOrigin::User
            && let Some(sink) = run.output_sink.as_deref_mut()
        {
            sink.user_input_persisted()?;
        }
        Ok(())
    }

    pub(super) fn update_tool_checkpoint_context(
        &self,
        run: &AgentRunRequest<'_, '_>,
    ) -> anyhow::Result<()> {
        let Some(tools) = run.tools else {
            return Ok(());
        };
        let Some(session) = run.session else {
            tools.set_checkpoint_context(None)?;
            return Ok(());
        };
        let Some(paths) = tools.checkpoint_paths() else {
            tools.set_checkpoint_context(None)?;
            return Ok(());
        };
        let user_turn = crate::sessions::session_user_input_count(session)? as u64;
        tools.set_checkpoint_context(Some(crate::checkpoints::SnapshotContext {
            store: crate::checkpoints::CheckpointStore::from_paths(&paths),
            paths,
            session_id: session.id().to_string(),
            user_turn,
        }))
    }

    pub(super) fn start_title_generation_guard(
        &self,
        run: &mut AgentRunRequest<'_, '_>,
        cancellation: AgentCancellation,
    ) -> TitleGenerationGuard {
        let mut title_handle = None;
        if let (Some(session), Some(mut job)) = (run.session, run.session_title_job.take())
            && let Ok(metadata) =
                crate::session_titles::should_start_title_generation_metadata(session)
            && metadata.latest_title.is_none()
            && metadata.user_input_count == 1
        {
            if let Some(first_prompt) = metadata.first_user_input_text {
                job.first_prompt = first_prompt;
            }
            if let Some(reporter) =
                filtered_herdr_reporter(run.invocation_mode, run.herdr_reporter.clone())
            {
                let notifier = job.notifier.take();
                job.notifier = Some(Arc::new(move |update| {
                    reporter
                        .rename_workspace_from_generated_title(&update.session_id, &update.title);
                    if let Some(notifier) = &notifier {
                        notifier(update);
                    }
                }));
            }
            title_handle = Some(crate::session_titles::spawn_background(job));
        }
        TitleGenerationGuard::new(title_handle, run.invocation_mode, cancellation)
    }
    pub(super) fn emit_initial_user_prompt_and_replay_diagnostics(
        &self,
        run: &mut AgentRunRequest<'_, '_>,
        provider_prompt: String,
        diagnostics: &[SessionReadDiagnostic],
        warnings: &[String],
    ) -> anyhow::Result<()> {
        if let Some(sink) = run.output_sink.as_deref_mut() {
            let event = match run.prompt_origin {
                crate::output::UserPromptOrigin::AutomaticCompaction => {
                    OutputEvent::AutomaticUserPrompt {
                        text: provider_prompt,
                    }
                }
                crate::output::UserPromptOrigin::Steering => OutputEvent::SteeringInjected {
                    text: provider_prompt,
                },
                crate::output::UserPromptOrigin::User => OutputEvent::UserPrompt {
                    text: provider_prompt,
                },
            };
            sink.output_event(event)?;
        }
        emit_replay_diagnostics(&mut run.output_sink, diagnostics, warnings)
    }
    pub(super) fn build_hook_context(&self, run: &AgentRunRequest<'_, '_>) -> HookContextMetadata {
        HookContextMetadata {
            session_id: run.session.map(|session| session.id().to_string()),
            session_path: run.session.map(|session| session.path().to_path_buf()),
            provider_id: Some(self.provider_id.clone()),
            model_id: Some(self.model.clone()),
            agent_id: run.agent_id.clone(),
            invocation_mode: run.invocation_mode,
            turn_id: None,
            message_id: None,
            subagent: matches!(run.invocation_mode, InvocationMode::Subagent),
        }
    }

    pub(super) fn start_print_turn_state<'run>(
        &self,
        prepared: PreparedInitialRun<'run>,
        run: &AgentRunRequest<'run, '_>,
        title_guard: TitleGenerationGuard,
        request_sequence: Arc<AtomicU64>,
    ) -> PrintTurnState<'run> {
        PrintTurnState {
            base_conversation: prepared.base_conversation,
            tool_request_configuration: prepared.tool_request_configuration,
            prompt_cache_key: prepared.prompt_cache_key,
            conversation_id: prepared.conversation_id,
            codex_turn_context: crate::providers::CodexTurnContext::default(),
            session_persistence: prepared.session_persistence,
            subdir_instruction_state: SubdirInstructionState::new(
                run.initial_instructions,
                run.session,
            ),
            assistant_chunk_batch: AssistantChunkBatch::new(run.session, run.cwd),
            turn_state: AgentTurnState::default(),
            output: AgentRunOutput::default(),
            request_projection_cache: prepared.request_projection_cache,
            request_sequence,
            hook_context: self.build_hook_context(run),
            herdr_reporter: filtered_herdr_reporter(
                run.invocation_mode,
                run.herdr_reporter.clone(),
            ),
            title_guard,
        }
    }
}