codex_runtime/runtime/client/
profile.rs1use std::borrow::Cow;
2use std::sync::Arc;
3use std::time::Duration;
4
5use serde_json::Value;
6
7use crate::plugin::{PostHook, PreHook};
8use crate::runtime::api::{
9 ApprovalPolicy, PromptAttachment, PromptRunParams, ReasoningEffort, SandboxPolicy,
10 SandboxPreset, ThreadStartParams, DEFAULT_REASONING_EFFORT,
11};
12use crate::runtime::hooks::RuntimeHookConfig;
13
14#[derive(Clone, Debug, PartialEq)]
15struct ProfileCore {
16 model: Option<String>,
17 effort: ReasoningEffort,
18 approval_policy: ApprovalPolicy,
19 sandbox_policy: SandboxPolicy,
20 privileged_escalation_approved: bool,
21 attachments: Vec<PromptAttachment>,
22 timeout: Duration,
23 output_schema: Option<Value>,
24 hooks: RuntimeHookConfig,
25}
26
27impl Default for ProfileCore {
28 fn default() -> Self {
29 Self {
30 model: None,
31 effort: DEFAULT_REASONING_EFFORT,
32 approval_policy: ApprovalPolicy::Never,
33 sandbox_policy: SandboxPolicy::Preset(SandboxPreset::ReadOnly),
34 privileged_escalation_approved: false,
35 attachments: Vec::new(),
36 timeout: Duration::from_secs(120),
37 output_schema: None,
38 hooks: RuntimeHookConfig::default(),
39 }
40 }
41}
42
43impl ProfileCore {
44 fn into_run_profile(self) -> RunProfile {
45 RunProfile {
46 model: self.model,
47 effort: self.effort,
48 approval_policy: self.approval_policy,
49 sandbox_policy: self.sandbox_policy,
50 privileged_escalation_approved: self.privileged_escalation_approved,
51 attachments: self.attachments,
52 timeout: self.timeout,
53 output_schema: self.output_schema,
54 hooks: self.hooks,
55 }
56 }
57
58 fn into_session_config(self, cwd: String) -> SessionConfig {
59 SessionConfig {
60 cwd,
61 model: self.model,
62 effort: self.effort,
63 approval_policy: self.approval_policy,
64 sandbox_policy: self.sandbox_policy,
65 privileged_escalation_approved: self.privileged_escalation_approved,
66 attachments: self.attachments,
67 timeout: self.timeout,
68 output_schema: self.output_schema,
69 hooks: self.hooks,
70 }
71 }
72
73 fn into_prompt_params(self, cwd: String, prompt: String) -> PromptRunParams {
74 PromptRunParams {
75 cwd,
76 prompt,
77 model: self.model,
78 effort: Some(self.effort),
79 approval_policy: self.approval_policy,
80 sandbox_policy: self.sandbox_policy,
81 privileged_escalation_approved: self.privileged_escalation_approved,
82 attachments: self.attachments,
83 timeout: self.timeout,
84 output_schema: self.output_schema,
85 }
86 }
87
88 fn into_thread_start_params(self, cwd: String) -> ThreadStartParams {
89 ThreadStartParams {
90 model: self.model,
91 cwd: Some(cwd),
92 approval_policy: Some(self.approval_policy),
93 sandbox_policy: Some(self.sandbox_policy),
94 privileged_escalation_approved: self.privileged_escalation_approved,
95 ..ThreadStartParams::default()
96 }
97 }
98}
99
100impl From<RunProfile> for ProfileCore {
101 fn from(profile: RunProfile) -> Self {
102 Self {
103 model: profile.model,
104 effort: profile.effort,
105 approval_policy: profile.approval_policy,
106 sandbox_policy: profile.sandbox_policy,
107 privileged_escalation_approved: profile.privileged_escalation_approved,
108 attachments: profile.attachments,
109 timeout: profile.timeout,
110 output_schema: profile.output_schema,
111 hooks: profile.hooks,
112 }
113 }
114}
115
116impl From<&SessionConfig> for ProfileCore {
117 fn from(config: &SessionConfig) -> Self {
118 Self {
119 model: config.model.clone(),
120 effort: config.effort,
121 approval_policy: config.approval_policy,
122 sandbox_policy: config.sandbox_policy.clone(),
123 privileged_escalation_approved: config.privileged_escalation_approved,
124 attachments: config.attachments.clone(),
125 timeout: config.timeout,
126 output_schema: config.output_schema.clone(),
127 hooks: config.hooks.clone(),
128 }
129 }
130}
131
132pub(super) struct PreparedPromptRun<'a> {
133 pub(super) params: PromptRunParams,
134 pub(super) hooks: Cow<'a, RuntimeHookConfig>,
135}
136
137macro_rules! impl_profile_builder_methods {
138 () => {
139 pub fn with_model(mut self, model: impl Into<String>) -> Self {
142 self.model = Some(model.into());
143 self
144 }
145
146 pub fn with_effort(mut self, effort: ReasoningEffort) -> Self {
149 self.effort = effort;
150 self
151 }
152
153 pub fn with_approval_policy(mut self, approval_policy: ApprovalPolicy) -> Self {
156 self.approval_policy = approval_policy;
157 self
158 }
159
160 pub fn with_sandbox_policy(mut self, sandbox_policy: SandboxPolicy) -> Self {
163 self.sandbox_policy = sandbox_policy;
164 self
165 }
166
167 pub fn allow_privileged_escalation(mut self) -> Self {
169 self.privileged_escalation_approved = true;
170 self
171 }
172
173 pub fn with_timeout(mut self, timeout: Duration) -> Self {
176 self.timeout = timeout;
177 self
178 }
179
180 pub fn with_output_schema(mut self, output_schema: Value) -> Self {
182 self.output_schema = Some(output_schema);
183 self
184 }
185
186 pub fn with_attachment(mut self, attachment: PromptAttachment) -> Self {
189 self.attachments.push(attachment);
190 self
191 }
192
193 pub fn attach_path(self, path: impl Into<String>) -> Self {
196 self.with_attachment(PromptAttachment::AtPath {
197 path: path.into(),
198 placeholder: None,
199 })
200 }
201
202 pub fn attach_path_with_placeholder(
205 self,
206 path: impl Into<String>,
207 placeholder: impl Into<String>,
208 ) -> Self {
209 self.with_attachment(PromptAttachment::AtPath {
210 path: path.into(),
211 placeholder: Some(placeholder.into()),
212 })
213 }
214
215 pub fn attach_image_url(self, url: impl Into<String>) -> Self {
218 self.with_attachment(PromptAttachment::ImageUrl { url: url.into() })
219 }
220
221 pub fn attach_local_image(self, path: impl Into<String>) -> Self {
224 self.with_attachment(PromptAttachment::LocalImage { path: path.into() })
225 }
226
227 pub fn attach_skill(self, name: impl Into<String>, path: impl Into<String>) -> Self {
230 self.with_attachment(PromptAttachment::Skill {
231 name: name.into(),
232 path: path.into(),
233 })
234 }
235
236 pub fn with_hooks(mut self, hooks: RuntimeHookConfig) -> Self {
239 self.hooks = hooks;
240 self
241 }
242
243 pub fn with_pre_hook(mut self, hook: Arc<dyn PreHook>) -> Self {
246 self.hooks.pre_hooks.push(hook);
247 self
248 }
249
250 pub fn with_post_hook(mut self, hook: Arc<dyn PostHook>) -> Self {
253 self.hooks.post_hooks.push(hook);
254 self
255 }
256
257 pub fn with_pre_tool_use_hook(mut self, hook: Arc<dyn PreHook>) -> Self {
260 self.hooks.pre_tool_use_hooks.push(hook);
261 self
262 }
263 };
264}
265
266#[derive(Clone, Debug, PartialEq)]
267pub struct RunProfile {
268 pub model: Option<String>,
269 pub effort: ReasoningEffort,
270 pub approval_policy: ApprovalPolicy,
271 pub sandbox_policy: SandboxPolicy,
272 pub privileged_escalation_approved: bool,
274 pub attachments: Vec<PromptAttachment>,
275 pub timeout: Duration,
276 pub output_schema: Option<Value>,
277 pub hooks: RuntimeHookConfig,
278}
279
280impl Default for RunProfile {
281 fn default() -> Self {
282 ProfileCore::default().into_run_profile()
283 }
284}
285
286impl RunProfile {
287 pub fn new() -> Self {
290 Self::default()
291 }
292
293 impl_profile_builder_methods!();
294}
295
296#[derive(Clone, Debug, PartialEq)]
297pub struct SessionConfig {
298 pub cwd: String,
299 pub model: Option<String>,
300 pub effort: ReasoningEffort,
301 pub approval_policy: ApprovalPolicy,
302 pub sandbox_policy: SandboxPolicy,
303 pub privileged_escalation_approved: bool,
305 pub attachments: Vec<PromptAttachment>,
306 pub timeout: Duration,
307 pub output_schema: Option<Value>,
308 pub hooks: RuntimeHookConfig,
309}
310
311impl SessionConfig {
312 pub fn new(cwd: impl Into<String>) -> Self {
315 Self::from_profile(cwd, RunProfile::default())
316 }
317
318 pub fn from_profile(cwd: impl Into<String>, profile: RunProfile) -> Self {
321 ProfileCore::from(profile).into_session_config(cwd.into())
322 }
323
324 pub fn profile(&self) -> RunProfile {
327 ProfileCore::from(self).into_run_profile()
328 }
329
330 impl_profile_builder_methods!();
331}
332
333#[cfg(test)]
336pub(super) fn session_prompt_params(
337 config: &SessionConfig,
338 prompt: impl Into<String>,
339) -> PromptRunParams {
340 session_prepared_prompt_run(config, prompt).params
341}
342
343#[cfg(test)]
346pub(super) fn profile_to_prompt_params(
347 cwd: String,
348 prompt: impl Into<String>,
349 profile: RunProfile,
350) -> PromptRunParams {
351 prepared_prompt_run_from_profile(cwd, prompt, profile).params
352}
353
354pub(super) fn session_thread_start_params(config: &SessionConfig) -> ThreadStartParams {
357 ProfileCore::from(config).into_thread_start_params(config.cwd.clone())
358}
359
360pub(super) fn session_prepared_prompt_run<'a>(
361 config: &'a SessionConfig,
362 prompt: impl Into<String>,
363) -> PreparedPromptRun<'a> {
364 PreparedPromptRun {
365 params: ProfileCore::from(config).into_prompt_params(config.cwd.clone(), prompt.into()),
366 hooks: Cow::Borrowed(&config.hooks),
367 }
368}
369
370pub(super) fn prepared_prompt_run_from_profile<'a>(
371 cwd: String,
372 prompt: impl Into<String>,
373 mut profile: RunProfile,
374) -> PreparedPromptRun<'a> {
375 let hooks = std::mem::take(&mut profile.hooks);
376 PreparedPromptRun {
377 params: ProfileCore::from(profile).into_prompt_params(cwd, prompt.into()),
378 hooks: Cow::Owned(hooks),
379 }
380}