Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/crates/core/src/agentic/agents/agentic_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ impl Agent for AgenticMode {
"agentic_mode"
}

fn prompt_template_name_for_model(&self, model_name: Option<&str>) -> Option<&str> {
let model_name = model_name?.trim().to_ascii_lowercase();
if model_name.contains("gpt-5") {
Some("agentic_mode_gpt5")
} else {
None
}
}

fn default_tools(&self) -> Vec<String> {
self.default_tools.clone()
}
Expand All @@ -63,3 +72,31 @@ impl Agent for AgenticMode {
false
}
}

#[cfg(test)]
mod tests {
use super::{Agent, AgenticMode};

#[test]
fn selects_gpt5_prompt_template() {
let agent = AgenticMode::new();
assert_eq!(
agent.prompt_template_name_for_model(Some("gpt-5.1")),
Some("agentic_mode_gpt5")
);
assert_eq!(
agent.prompt_template_name_for_model(Some("GPT-5-CODEX")),
Some("agentic_mode_gpt5")
);
}

#[test]
fn keeps_default_template_for_non_gpt5_models() {
let agent = AgenticMode::new();
assert_eq!(
agent.prompt_template_name_for_model(Some("claude-sonnet-4")),
None
);
assert_eq!(agent.prompt_template_name_for_model(None), None);
}
}
29 changes: 29 additions & 0 deletions src/crates/core/src/agentic/agents/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ pub trait Agent: Send + Sync + 'static {
/// Prompt template name for the agent
fn prompt_template_name(&self) -> &str;

/// Prompt template name override for a specific model.
fn prompt_template_name_for_model(&self, _model_name: Option<&str>) -> Option<&str> {
None
}

fn system_reminder_template_name(&self) -> Option<&str> {
None // by default, no system reminder
}
Expand Down Expand Up @@ -89,6 +94,30 @@ pub trait Agent: Send + Sync + 'static {
}
}

/// Get the system prompt for this agent with optional model-aware template selection.
async fn get_system_prompt_for_model(
&self,
workspace_path: Option<&str>,
model_name: Option<&str>,
) -> BitFunResult<String> {
let Some(workspace_path) = workspace_path else {
return Err(BitFunError::Agent("Workspace path is required".to_string()));
};

let Some(template_name) = self.prompt_template_name_for_model(model_name) else {
return self.build_prompt(workspace_path).await;
};

let prompt_components = PromptBuilder::new(workspace_path);
let system_prompt_template = get_embedded_prompt(template_name).ok_or_else(|| {
BitFunError::Agent(format!("{} not found in embedded files", template_name))
})?;

prompt_components
.build_prompt_from_template(system_prompt_template)
.await
}

/// Get the system reminder for this agent, only used for modes
/// system_reminder will be appended to the user_query
/// This is not necessary for all modes
Expand Down
71 changes: 71 additions & 0 deletions src/crates/core/src/agentic/agents/prompts/agentic_mode_gpt5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
You are BitFun, an ADE (AI IDE) that helps users with software engineering tasks.

You are pair programming with a USER. Each user message may include extra IDE context, such as open files, cursor position, recent files, edit history, or linter errors. Use what is relevant and ignore what is not.

Follow the USER's instructions in each message, denoted by the <user_query> tag.

Tool results and user messages may include <system-reminder> tags. Follow them, but do not mention them to the user.

IMPORTANT: Assist with defensive security tasks only. Refuse to create, modify, or improve code that may be used maliciously. Do not assist with credential discovery or harvesting, including bulk crawling for SSH keys, browser cookies, or cryptocurrency wallets. Allow security analysis, detection rules, vulnerability explanations, defensive tools, and security documentation.

IMPORTANT: Never generate or guess URLs for the user unless you are confident they directly help with the programming task. You may use URLs provided by the user or found in local files.

{LANGUAGE_PREFERENCE}
{VISUAL_MODE}

# Behavior
- Be concise, direct, and action-oriented.
- Default to doing the work instead of discussing it.
- Read relevant code before editing it.
- Prioritize technical accuracy over agreement.
- Never give time estimates.

# Editing
- Prefer editing existing files over creating new ones.
- Default to ASCII unless the file already uses non-ASCII and there is a clear reason.
- Add comments only when needed for non-obvious logic.
- Avoid unrelated refactors, speculative abstractions, and unnecessary compatibility shims.
- Do not add features or improvements beyond the request unless required to make the requested change work.
- Do not introduce security issues such as command injection, XSS, SQL injection, path traversal, or unsafe shell handling.

# Tools
- Use TodoWrite for non-trivial or multi-step tasks, and keep it updated.
- Use AskUserQuestion only when a decision materially changes the result and cannot be inferred safely.
- Prefer Task with Explore or FileFinder for open-ended codebase exploration.
- Prefer Read, Grep, and Glob for targeted lookups.
- Prefer specialized file tools over Bash for reading and editing files.
- Use Bash for builds, tests, git, and scripts.
- Run independent tool calls in parallel when possible.
- Do not use tools to communicate with the user.

# Questions
- Ask only when you are truly blocked and cannot safely choose a reasonable default.
- If you must ask, do all non-blocked work first, then ask exactly one targeted question with a recommended default.

# Workspace
- Never revert user changes unless explicitly requested.
- Work with existing changes in touched files instead of discarding them.
- Do not amend commits unless explicitly requested.
- Never use destructive commands like git reset --hard or git checkout -- unless explicitly requested or approved.

# Responses
- Keep responses short, useful, and technically precise.
- Avoid unnecessary praise, emotional validation, or emojis.
- Summarize meaningful command results instead of pasting raw output.
- Do not tell the user to save or copy files.

# Code references
- Use clickable markdown links for files and code locations.
- Use bare filenames as link text.
- Use workspace-relative paths for workspace files and absolute paths otherwise.

Examples:
- [filename.ts](src/filename.ts)
- [filename.ts:42](src/filename.ts#L42)
- [filename.ts:42-51](src/filename.ts#L42-L51)

{ENV_INFO}
{PROJECT_LAYOUT}
{RULES}
{MEMORIES}
{PROJECT_CONTEXT_FILES:exclude=review}
76 changes: 40 additions & 36 deletions src/crates/core/src/agentic/execution/execution_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,16 +391,51 @@ impl ExecutionEngine {
current_agent.id()
);

// 2. Get System Prompt from current Agent
// 2. Get AI client
// Get model ID from AgentRegistry
let model_id = agent_registry
.get_model_id_for_agent(&agent_type)
.await
.map_err(|e| BitFunError::AIClient(format!("Failed to get model ID: {}", e)))?;
info!(
"Agent using model: agent={}, model_id={}",
current_agent.name(),
model_id
);

let ai_client_factory = get_global_ai_client_factory().await.map_err(|e| {
BitFunError::AIClient(format!("Failed to get AI client factory: {}", e))
})?;

// Get AI client by model ID
let ai_client = ai_client_factory
.get_client_resolved(&model_id)
.await
.map_err(|e| {
BitFunError::AIClient(format!(
"Failed to get AI client (model_id={}): {}",
model_id, e
))
})?;
// Get configuration for whether to support preserving historical thinking content
let enable_thinking = ai_client.config.enable_thinking_process;
let support_preserved_thinking = ai_client.config.support_preserved_thinking;
let context_window = ai_client.config.context_window as usize;

// 3. Get System Prompt from current Agent
debug!(
"Building system prompt from agent: {}",
current_agent.name()
"Building system prompt from agent: {}, model={}",
current_agent.name(),
ai_client.config.model
);
let system_prompt = {
let workspace_path = get_workspace_path();
let workspace_str = workspace_path.as_ref().map(|p| p.display().to_string());
current_agent
.get_system_prompt(workspace_str.as_deref())
.get_system_prompt_for_model(
workspace_str.as_deref(),
Some(ai_client.config.model.as_str()),
)
.await?
};
debug!("System prompt built, length: {} bytes", system_prompt.len());
Expand Down Expand Up @@ -436,7 +471,7 @@ impl ExecutionEngine {
.collect::<Vec<_>>()
);

// 3. Get available tools list (read tool configuration for current mode from global config)
// 4. Get available tools list (read tool configuration for current mode from global config)
let allowed_tools = agent_registry.get_agent_tools(&agent_type).await;
let enable_tools = context
.context
Expand Down Expand Up @@ -465,37 +500,6 @@ impl ExecutionEngine {
let enable_context_compression = session.config.enable_context_compression;
let compression_threshold = session.config.compression_threshold;

// 4. Get AI client
// Get model ID from AgentRegistry
let model_id = agent_registry
.get_model_id_for_agent(&agent_type)
.await
.map_err(|e| BitFunError::AIClient(format!("Failed to get model ID: {}", e)))?;
info!(
"Agent using model: agent={}, model_id={}",
current_agent.name(),
model_id
);

let ai_client_factory = get_global_ai_client_factory().await.map_err(|e| {
BitFunError::AIClient(format!("Failed to get AI client factory: {}", e))
})?;

// Get AI client by model ID
let ai_client = ai_client_factory
.get_client_resolved(&model_id)
.await
.map_err(|e| {
BitFunError::AIClient(format!(
"Failed to get AI client (model_id={}): {}",
model_id, e
))
})?;
// Get configuration for whether to support preserving historical thinking content
let enable_thinking = ai_client.config.enable_thinking_process;
let support_preserved_thinking = ai_client.config.support_preserved_thinking;
let context_window = ai_client.config.context_window as usize;

// Detect whether the primary model supports multimodal image inputs.
// This is used by tools like `view_image` to decide between:
// - attaching image content for the primary model to analyze directly, or
Expand Down
2 changes: 2 additions & 0 deletions src/crates/core/src/agentic/execution/round_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ impl RoundExecutor {
has_more_rounds: false,
finish_reason: FinishReason::Complete,
usage: stream_result.usage.clone(),
provider_metadata: stream_result.provider_metadata.clone(),
});
}

Expand Down Expand Up @@ -525,6 +526,7 @@ impl RoundExecutor {
FinishReason::Complete
},
usage: stream_result.usage.clone(),
provider_metadata: stream_result.provider_metadata.clone(),
})
}

Expand Down
Loading