use crate::cmd::summary::{CompletionSummary, format_summary_for_injection};
use crate::{agent, config::project, store::Store, config::team, prompt::templates, config::toolbox, types::*};
use anyhow::Result;
use serde_json;
mod prompt_context;
#[path = "prompt_project.rs"]
mod prompt_project;
#[path = "output.rs"]
mod run_output;
#[path = "scope.rs"]
mod run_scope;
#[path = "verify.rs"]
mod run_verify;
pub(super) use run_output::{
ResultDelivery, clean_output_if_jsonl, extract_output_fallback_from_path,
fill_empty_output_from_log, output_file_instruction, persist_result_file,
};
pub(super) use run_scope::warn_agent_committed_files_outside_scope;
pub(super) use run_verify::{
maybe_auto_retry_after_checklist_miss_impl, maybe_auto_retry_after_verify_failure_impl,
maybe_verify_impl, record_verify_not_run,
};
#[path = "process.rs"]
mod run_process;
#[path = "prompt_helpers.rs"]
mod run_prompt_helpers;
use super::RunArgs;
pub(super) use run_process::*;
pub(super) use run_prompt_helpers::*;
#[path = "prompt_injections.rs"]
mod injections;
use injections::*;
#[path = "prompt_auxiliary.rs"]
mod auxiliary;
use auxiliary::*;
const VERIFY_RETRY_FEEDBACK: &str =
"Verification failed. Please fix the compilation/test errors and try again.";
pub(crate) struct PromptBundle {
pub effective_prompt: String,
pub context_files: Vec<String>,
pub prompt_tokens: i64,
pub injected_memory_ids: Vec<String>,
}
pub(super) fn build_prompt_bundle(
store: &Store,
args: &RunArgs,
agent_kind: &AgentKind,
workgroup: Option<&Workgroup>,
requested_skills: &[String],
current_task_id: &str,
detected_project: Option<&project::ProjectConfig>,
project_root: Option<&std::path::Path>,
) -> Result<PromptBundle> {
let (composed, context_files, category, suppress_implementation_scaffolding) =
prepare_composed_prompt(store, args, agent_kind, workgroup)?;
let mut omissions = composed.omissions;
let mut effective_prompt = inject_related_context(
store,
args,
agent_kind,
current_task_id,
requested_skills,
suppress_implementation_scaffolding,
composed.text,
&mut omissions,
)?;
let injected_memory_ids = inject_stored_context(
store,
args,
project_root,
detected_project,
category,
&mut effective_prompt,
&mut omissions,
)?;
effective_prompt =
inject_delivery_context(args, agent_kind, category, effective_prompt, &mut omissions)?;
record_prompt_omissions(store, current_task_id, &omissions)?;
let prompt_tokens = templates::estimate_tokens(&effective_prompt) as i64;
Ok(PromptBundle {
effective_prompt,
context_files,
prompt_tokens,
injected_memory_ids,
})
}
fn prepare_composed_prompt(
store: &Store,
args: &RunArgs,
agent_kind: &AgentKind,
workgroup: Option<&Workgroup>,
) -> Result<(
crate::task::workgroup::ComposedPrompt,
Vec<String>,
agent::classifier::TaskCategory,
bool,
)> {
let (mut composed, context_files, prompt) =
compose_task_context(store, args, agent_kind, workgroup)?;
let mut task_profile = agent::classifier::classify(
&prompt,
agent::classifier::count_file_mentions(&prompt),
prompt.len(),
);
task_profile.category = effective_category(task_profile.category, args.kind);
let suppress_implementation_scaffolding =
crate::cmd::report_mode::suppresses_implementation_scaffolding(
&prompt,
args.read_only,
args.kind,
);
let (edit_guard, milestone_instr) = templates::shared_system_fragments(&prompt);
if let Some(guard) = edit_guard {
composed.text = format!("{guard}{}", composed.text);
}
composed.text.push_str(milestone_instr);
if !suppress_implementation_scaffolding {
composed.text.push_str(templates::git_staging_guard());
}
Ok((
composed,
context_files,
task_profile.category,
suppress_implementation_scaffolding,
))
}
fn compose_task_context(
store: &Store,
args: &RunArgs,
agent_kind: &AgentKind,
workgroup: Option<&Workgroup>,
) -> Result<(crate::task::workgroup::ComposedPrompt, Vec<String>, String)> {
let (file_context, context_files) = build_context_flags(agent_kind, &args.context)?;
let milestones = if let Some(group_id) = args.group.as_deref() {
store.get_workgroup_milestones(group_id)?
} else {
vec![]
};
let findings = if let Some(group_id) = args.group.as_deref() {
store.list_findings(group_id)?
} else {
vec![]
};
let prompt = resolve_prompt(&args.prompt, args.template.as_deref())?;
let composed = crate::task::workgroup::compose_prompt(
&prompt,
file_context.as_deref(),
workgroup,
&milestones,
&findings,
);
Ok((composed, context_files, prompt))
}
#[cfg(test)]
#[path = "prompt_tests.rs"]
mod tests;
fn effective_category(
guessed: crate::agent::classifier::TaskCategory,
declared: Option<crate::agent::classifier::TaskCategory>,
) -> crate::agent::classifier::TaskCategory {
declared.unwrap_or(guessed)
}
#[cfg(test)]
mod effective_category_tests {
use super::effective_category;
use crate::agent::classifier::TaskCategory;
#[test]
fn a_declared_kind_overrides_the_guess() {
assert_eq!(
effective_category(TaskCategory::Frontend, Some(TaskCategory::Research)),
TaskCategory::Research
);
}
#[test]
fn the_guess_stands_when_nothing_is_declared() {
assert_eq!(
effective_category(TaskCategory::Frontend, None),
TaskCategory::Frontend
);
}
#[test]
fn declaring_the_guessed_kind_changes_nothing() {
assert_eq!(
effective_category(TaskCategory::Testing, Some(TaskCategory::Testing)),
TaskCategory::Testing
);
}
}