bamboo_engine/runtime/context/
mod.rs1pub mod instruction;
7
8use bamboo_infrastructure::paths;
9use bamboo_infrastructure::Config;
10
11pub const DEFAULT_BASE_PROMPT: &str =
12 "You are Bodhi, a highly capable AI assistant.\n\nYou help users solve problems quickly and correctly. Be concise, practical, and proactive.\nIf requirements are unclear, ask focused clarifying questions before proceeding.\nUse Task for non-trivial multi-step task tracking.\nDo not proactively use SubSession/sub-agent delegation unless the user explicitly asks for sub sessions, sub agents, delegation, or parallel agent work.\n\nIf Bamboo has already injected relevant workspace or environment context, treat it as available working context instead of re-asking the user for the same information. Prefer a minimal verifiable attempt first, then diagnose failures and only ask follow-up questions for information that is still genuinely missing.\n\nWhen making function calls using tools, always include a brief text explanation before or alongside the tool calls describing what you are about to do and why. Never silently call tools without any visible narration to the user.";
13
14pub const WORKSPACE_CONTEXT_START_MARKER: &str = "<!-- BAMBOO_WORKSPACE_CONTEXT_START -->";
15pub const WORKSPACE_CONTEXT_END_MARKER: &str = "<!-- BAMBOO_WORKSPACE_CONTEXT_END -->";
16pub const WORKSPACE_CONTEXT_PREFIX: &str = "Workspace path: ";
17pub const ENV_CONTEXT_START_MARKER: &str = "<!-- BAMBOO_ENV_CONTEXT_START -->";
18pub const ENV_CONTEXT_END_MARKER: &str = "<!-- BAMBOO_ENV_CONTEXT_END -->";
19
20pub fn workspace_prompt_guidance() -> String {
22 let config_path = paths::path_to_display_string(&paths::config_json_path());
23 format!(
24 "If you need to inspect files, check the workspace first, then Bamboo data at {}. Bamboo configuration is stored in {} (equivalent to ${{BAMBOO_DATA_DIR}}/config.json).",
25 paths::bamboo_dir_display(),
26 config_path
27 )
28}
29
30fn build_env_prompt_guidance() -> Option<String> {
31 let env_vars = Config::current_prompt_safe_env_vars();
32 if env_vars.is_empty() {
33 return None;
34 }
35
36 let mut lines = vec![
37 "These environment variables were explicitly configured by the user inside Bodhi."
38 .to_string(),
39 "- They are already available to Bash/tool processes launched by Bodhi and may be relevant to tools and skills."
40 .to_string(),
41 "- Treat them as user-approved runtime context instead of asking the user to repeat them immediately."
42 .to_string(),
43 "- Secret values are intentionally hidden from the model.".to_string(),
44 "- If the listed variables appear sufficient, prefer a minimal verification or execution attempt before asking follow-up questions."
45 .to_string(),
46 "- Only ask the user for additional env details after identifying a concrete missing variable, malformed value shape, or execution failure that cannot be resolved from this injected context."
47 .to_string(),
48 ];
49
50 for entry in env_vars {
51 let visibility = if entry.secret { "secret" } else { "non-secret" };
52 let mut line = format!("- {} ({})", entry.name, visibility);
53 if let Some(description) = entry.description {
54 line.push_str(" — ");
55 line.push_str(&description);
56 }
57 lines.push(line);
58 }
59
60 Some(lines.join("\n"))
61}
62
63pub fn build_env_prompt_context() -> Option<String> {
64 let body = build_env_prompt_guidance()?;
65 Some(format!(
66 "{ENV_CONTEXT_START_MARKER}\n{body}\n{ENV_CONTEXT_END_MARKER}"
67 ))
68}
69
70pub fn build_workspace_prompt_context(workspace_path: &str) -> Option<String> {
71 let workspace_path = workspace_path.trim();
72 if workspace_path.is_empty() {
73 return None;
74 }
75
76 let body = format!(
77 "{WORKSPACE_CONTEXT_PREFIX}{workspace_path}\n{}",
78 workspace_prompt_guidance()
79 );
80
81 Some(format!(
82 "{WORKSPACE_CONTEXT_START_MARKER}\n{body}\n{WORKSPACE_CONTEXT_END_MARKER}"
83 ))
84}
85
86pub fn assemble_system_prompt(
96 base: &str,
97 enhance: Option<&str>,
98 workspace_path: Option<&str>,
99) -> String {
100 let mut prompt = base.trim().to_string();
101 if let Some(extra) = enhance.map(str::trim).filter(|v| !v.is_empty()) {
102 if !prompt.is_empty() {
103 prompt.push_str("\n\n");
104 }
105 prompt.push_str(extra);
106 }
107 if let Some(path) = workspace_path.map(str::trim).filter(|v| !v.is_empty()) {
108 if let Some(segment) = build_workspace_prompt_context(path) {
109 if !prompt.is_empty() {
110 prompt.push_str("\n\n");
111 }
112 prompt.push_str(&segment);
113 }
114 if let Some(instruction_segment) = instruction::build_instruction_prompt_context(path) {
115 if !prompt.is_empty() {
116 prompt.push_str("\n\n");
117 }
118 prompt.push_str(&instruction_segment);
119 }
120 }
121 if let Some(segment) = build_env_prompt_context() {
122 if !prompt.is_empty() {
123 prompt.push_str("\n\n");
124 }
125 prompt.push_str(&segment);
126 }
127 prompt
128}