Skip to main content

vtcode_core/prompts/
runtime_contract.rs

1use std::fmt::Write as _;
2
3use super::system::{
4    PLAN_MODE_EXIT_INSTRUCTION_LINE, PLAN_MODE_INTERVIEW_POLICY_LINE, PLAN_MODE_NO_AUTO_EXIT_LINE,
5    PLAN_MODE_NO_REQUEST_USER_INPUT_POLICY_LINE, PLAN_MODE_PLAN_QUALITY_LINE,
6    PLAN_MODE_READ_ONLY_HEADER, PLAN_MODE_READ_ONLY_NOTICE_LINE, PLAN_MODE_TASK_TRACKER_LINE,
7};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10pub struct RuntimePromptContract {
11    pub full_auto: bool,
12    pub plan_mode: bool,
13    pub request_user_input_enabled: bool,
14}
15
16pub fn append_runtime_mode_sections(prompt: &mut String, contract: RuntimePromptContract) {
17    if contract.full_auto {
18        append_full_auto_notice(prompt, contract);
19    }
20
21    if contract.plan_mode {
22        append_plan_mode_notice(prompt, contract.request_user_input_enabled);
23    }
24}
25
26fn append_full_auto_notice(prompt: &mut String, contract: RuntimePromptContract) {
27    let header = if contract.plan_mode {
28        "# FULL-AUTO (PLAN MODE): Work autonomously within Plan Mode constraints."
29    } else {
30        "# FULL-AUTO: Complete task autonomously until done or blocked."
31    };
32
33    if prompt.contains(header) {
34        return;
35    }
36
37    let _ = writeln!(prompt, "\n{header}");
38    let _ = writeln!(
39        prompt,
40        "- Stay within the exposed tool list and adapt when a tool is unavailable or denied."
41    );
42    let _ = writeln!(
43        prompt,
44        "- Treat completion language as a checkpoint, not proof; only stop when `task_tracker`, verification, and resumable state agree."
45    );
46    if !contract.request_user_input_enabled {
47        let _ = writeln!(
48            prompt,
49            "- `request_user_input` is unavailable in this runtime; make reasonable assumptions and continue with the available context."
50        );
51    }
52}
53
54fn append_plan_mode_notice(prompt: &mut String, request_user_input_enabled: bool) {
55    if prompt.contains(PLAN_MODE_READ_ONLY_HEADER) {
56        return;
57    }
58
59    prompt.push('\n');
60    prompt.push_str(PLAN_MODE_READ_ONLY_HEADER);
61    prompt.push('\n');
62    prompt.push_str(PLAN_MODE_READ_ONLY_NOTICE_LINE);
63    prompt.push('\n');
64    prompt.push_str(PLAN_MODE_EXIT_INSTRUCTION_LINE);
65    prompt.push('\n');
66    prompt.push_str(PLAN_MODE_PLAN_QUALITY_LINE);
67    prompt.push('\n');
68    prompt.push_str(if request_user_input_enabled {
69        PLAN_MODE_INTERVIEW_POLICY_LINE
70    } else {
71        PLAN_MODE_NO_REQUEST_USER_INPUT_POLICY_LINE
72    });
73    prompt.push('\n');
74    prompt.push_str(PLAN_MODE_NO_AUTO_EXIT_LINE);
75    prompt.push('\n');
76    prompt.push_str(PLAN_MODE_TASK_TRACKER_LINE);
77    prompt.push('\n');
78}
79
80#[cfg(test)]
81mod tests {
82    use super::{RuntimePromptContract, append_runtime_mode_sections};
83    use crate::prompts::system::{
84        PLAN_MODE_INTERVIEW_POLICY_LINE, PLAN_MODE_NO_REQUEST_USER_INPUT_POLICY_LINE,
85        PLAN_MODE_READ_ONLY_HEADER,
86    };
87
88    #[test]
89    fn plan_mode_uses_interview_policy_when_request_user_input_is_enabled() {
90        let mut prompt = "Base prompt".to_string();
91
92        append_runtime_mode_sections(
93            &mut prompt,
94            RuntimePromptContract {
95                plan_mode: true,
96                request_user_input_enabled: true,
97                ..RuntimePromptContract::default()
98            },
99        );
100
101        assert!(prompt.contains(PLAN_MODE_READ_ONLY_HEADER));
102        assert!(prompt.contains(PLAN_MODE_INTERVIEW_POLICY_LINE));
103        assert!(!prompt.contains(PLAN_MODE_NO_REQUEST_USER_INPUT_POLICY_LINE));
104    }
105
106    #[test]
107    fn plan_mode_uses_noninteractive_policy_when_request_user_input_is_disabled() {
108        let mut prompt = "Base prompt".to_string();
109
110        append_runtime_mode_sections(
111            &mut prompt,
112            RuntimePromptContract {
113                plan_mode: true,
114                request_user_input_enabled: false,
115                ..RuntimePromptContract::default()
116            },
117        );
118
119        assert!(prompt.contains(PLAN_MODE_READ_ONLY_HEADER));
120        assert!(prompt.contains(PLAN_MODE_NO_REQUEST_USER_INPUT_POLICY_LINE));
121        assert!(!prompt.contains(PLAN_MODE_INTERVIEW_POLICY_LINE));
122    }
123
124    #[test]
125    fn full_auto_notice_mentions_missing_request_user_input_when_disabled() {
126        let mut prompt = "Base prompt".to_string();
127
128        append_runtime_mode_sections(
129            &mut prompt,
130            RuntimePromptContract {
131                full_auto: true,
132                request_user_input_enabled: false,
133                ..RuntimePromptContract::default()
134            },
135        );
136
137        assert!(prompt.contains("# FULL-AUTO: Complete task autonomously until done or blocked."));
138        assert!(prompt.contains("`request_user_input` is unavailable in this runtime"));
139        assert!(prompt.contains("completion language as a checkpoint"));
140    }
141}