claude_codex/providers/cursor/
model.rs1pub const CURSOR_LEGACY_MODELS: &[&str] = &[
3 "cursor",
4 "cursor-agent",
5 "cursor-composer",
6 "cursor-composer-fast",
7 "cursor-plan",
8 "cursor-ask",
9 "composer-2.5",
10 "composer-2.5-fast",
11];
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum CursorAgentMode {
15 Agent,
16 Plan,
17 Ask,
18}
19
20impl CursorAgentMode {
21 pub fn as_str(&self) -> &'static str {
22 match self {
23 CursorAgentMode::Agent => "AGENT_MODE_AGENT",
24 CursorAgentMode::Plan => "AGENT_MODE_PLAN",
25 CursorAgentMode::Ask => "AGENT_MODE_ASK",
26 }
27 }
28}
29
30fn resolve_wire_model(raw: &str) -> (String, bool) {
31 raw.strip_suffix("-fast")
32 .map(|model| (model.to_string(), true))
33 .unwrap_or_else(|| (raw.to_string(), false))
34}
35
36pub fn resolve_cursor_model(model: &str) -> Result<CursorModelResolution, String> {
37 let model = model.trim();
38 for (prefix, mode) in [
39 ("cursor-agent:", CursorAgentMode::Agent),
40 ("cursor-plan:", CursorAgentMode::Plan),
41 ("cursor-ask:", CursorAgentMode::Ask),
42 ("cursor:", CursorAgentMode::Agent),
43 ] {
44 if let Some(raw) = model.strip_prefix(prefix) {
45 let (model_id, fast) = resolve_wire_model(raw);
46 return Ok(CursorModelResolution {
47 model_id,
48 mode,
49 fast,
50 });
51 }
52 }
53
54 let (model_id, mode, fast) = match model {
55 "cursor" | "cursor-agent" => ("default", CursorAgentMode::Agent, false),
56 "cursor-plan" => ("default", CursorAgentMode::Plan, false),
57 "cursor-ask" => ("default", CursorAgentMode::Ask, false),
58 "cursor-composer" | "composer-2.5" => ("composer-2.5", CursorAgentMode::Agent, false),
59 "cursor-composer-fast" | "composer-2.5-fast" => {
60 ("composer-2.5", CursorAgentMode::Agent, true)
61 }
62 _ => {
63 return Err(format!(
64 "unknown cursor model: {model}. Supported: cursor:<id>, cursor-plan:<id>, cursor-ask:<id>, cursor-agent"
65 ));
66 }
67 };
68 Ok(CursorModelResolution {
69 model_id: model_id.to_string(),
70 mode,
71 fast,
72 })
73}
74
75#[derive(Debug, Clone)]
76pub struct CursorModelResolution {
77 pub model_id: String,
78 pub mode: CursorAgentMode,
79 pub fast: bool,
80}
81
82pub fn cursor_supported_models() -> Vec<String> {
83 let mut out: Vec<String> = CURSOR_LEGACY_MODELS
84 .iter()
85 .map(|model| (*model).to_string())
86 .collect();
87 out.sort_unstable();
88 out
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn resolve_legacy_cursor() {
97 let resolution = resolve_cursor_model("cursor").unwrap();
98 assert_eq!(resolution.model_id, "default");
99 assert_eq!(resolution.mode, CursorAgentMode::Agent);
100 }
101
102 #[test]
103 fn resolve_legacy_cursor_agent() {
104 let resolution = resolve_cursor_model("cursor-agent").unwrap();
105 assert_eq!(resolution.model_id, "default");
106 assert_eq!(resolution.mode, CursorAgentMode::Agent);
107 }
108
109 #[test]
110 fn resolve_legacy_cursor_plan() {
111 let resolution = resolve_cursor_model("cursor-plan").unwrap();
112 assert_eq!(resolution.model_id, "default");
113 assert_eq!(resolution.mode, CursorAgentMode::Plan);
114 }
115
116 #[test]
117 fn resolve_legacy_cursor_ask() {
118 let resolution = resolve_cursor_model("cursor-ask").unwrap();
119 assert_eq!(resolution.model_id, "default");
120 assert_eq!(resolution.mode, CursorAgentMode::Ask);
121 }
122
123 #[test]
124 fn resolve_prefixed_cursor() {
125 let resolution = resolve_cursor_model("cursor:gpt-5.5").unwrap();
126 assert_eq!(resolution.model_id, "gpt-5.5");
127 assert_eq!(resolution.mode, CursorAgentMode::Agent);
128 }
129
130 #[test]
131 fn resolve_prefixed_cursor_plan() {
132 let resolution = resolve_cursor_model("cursor-plan:gpt-5.5").unwrap();
133 assert_eq!(resolution.model_id, "gpt-5.5");
134 assert_eq!(resolution.mode, CursorAgentMode::Plan);
135 }
136
137 #[test]
138 fn resolve_prefixed_cursor_ask() {
139 let resolution = resolve_cursor_model("cursor-ask:gpt-5.5").unwrap();
140 assert_eq!(resolution.model_id, "gpt-5.5");
141 assert_eq!(resolution.mode, CursorAgentMode::Ask);
142 }
143
144 #[test]
145 fn resolve_prefixed_cursor_agent() {
146 let resolution = resolve_cursor_model("cursor-agent:gpt-5.5").unwrap();
147 assert_eq!(resolution.model_id, "gpt-5.5");
148 assert_eq!(resolution.mode, CursorAgentMode::Agent);
149 }
150
151 #[test]
152 fn resolve_unknown_model_errors() {
153 assert!(resolve_cursor_model("unknown-model").is_err());
154 }
155
156 #[test]
157 fn resolve_composer_models() {
158 let regular = resolve_cursor_model("composer-2.5").unwrap();
159 assert_eq!(regular.mode, CursorAgentMode::Agent);
160 assert_eq!(regular.model_id, "composer-2.5");
161 assert!(!regular.fast);
162
163 let fast = resolve_cursor_model("composer-2.5-fast").unwrap();
164 assert_eq!(fast.mode, CursorAgentMode::Agent);
165 assert_eq!(fast.model_id, "composer-2.5");
166 assert!(fast.fast);
167 }
168
169 #[test]
170 fn supported_models_includes_all_legacy() {
171 let models = cursor_supported_models();
172 for model in CURSOR_LEGACY_MODELS {
173 assert!(models.contains(&model.to_string()), "missing {model}");
174 }
175 }
176}