codex_protocol/user_input.rs
1use schemars::JsonSchema;
2use serde::Deserialize;
3use serde::Serialize;
4use ts_rs::TS;
5
6use crate::models::ImageDetail;
7
8/// Conservative cap so one user message cannot monopolize a large context window.
9pub const MAX_USER_INPUT_TEXT_CHARS: usize = 1 << 20;
10
11/// User input
12#[non_exhaustive]
13#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, TS, JsonSchema)]
14#[serde(tag = "type", rename_all = "snake_case")]
15pub enum UserInput {
16 Text {
17 text: String,
18 /// UI-defined spans within `text` that should be treated as special elements.
19 /// These are byte ranges into the UTF-8 `text` buffer and are used to render
20 /// or persist rich input markers (e.g., image placeholders) across history
21 /// and resume without mutating the literal text.
22 #[serde(default)]
23 text_elements: Vec<TextElement>,
24 },
25 /// Pre‑encoded data: URI image.
26 Image {
27 image_url: String,
28 #[serde(default, skip_serializing_if = "Option::is_none")]
29 #[ts(optional)]
30 detail: Option<ImageDetail>,
31 },
32 /// Local image path provided by the user. This will be converted to an
33 /// `Image` variant (base64 data URL) during request serialization.
34 LocalImage {
35 path: std::path::PathBuf,
36 #[serde(default, skip_serializing_if = "Option::is_none")]
37 #[ts(optional)]
38 detail: Option<ImageDetail>,
39 },
40 /// Pre-encoded audio data URI forwarded to the Responses API.
41 Audio { audio_url: String },
42 /// Local audio path converted to an `Audio` data URI during request serialization.
43 LocalAudio { path: std::path::PathBuf },
44
45 /// Skill selected by the user (name + path to SKILL.md).
46 Skill {
47 name: String,
48 path: std::path::PathBuf,
49 },
50 /// Explicit structured mention selected by the user.
51 ///
52 /// `path` identifies the exact mention target, for example
53 /// `app://<connector-id>` or `plugin://<plugin-name>@<marketplace-name>`.
54 Mention { name: String, path: String },
55}
56
57#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, TS, JsonSchema)]
58pub struct TextElement {
59 /// Byte range in the parent `text` buffer that this element occupies.
60 pub byte_range: ByteRange,
61 /// Optional human-readable placeholder for the element, displayed in the UI.
62 placeholder: Option<String>,
63}
64
65impl TextElement {
66 pub fn new(byte_range: ByteRange, placeholder: Option<String>) -> Self {
67 Self {
68 byte_range,
69 placeholder,
70 }
71 }
72
73 /// Returns a copy of this element with a remapped byte range.
74 ///
75 /// The placeholder is preserved as-is; callers must ensure the new range
76 /// still refers to the same logical element (and same placeholder)
77 /// within the new text.
78 pub fn map_range<F>(&self, map: F) -> Self
79 where
80 F: FnOnce(ByteRange) -> ByteRange,
81 {
82 Self {
83 byte_range: map(self.byte_range),
84 placeholder: self.placeholder.clone(),
85 }
86 }
87
88 pub fn set_placeholder(&mut self, placeholder: Option<String>) {
89 self.placeholder = placeholder;
90 }
91
92 /// Returns the stored placeholder without falling back to the text buffer.
93 ///
94 /// This must only be used inside `From<TextElement>` implementations on equivalent
95 /// protocol types where the source text is unavailable. Prefer `placeholder(text)`
96 /// everywhere else.
97 #[doc(hidden)]
98 pub fn _placeholder_for_conversion_only(&self) -> Option<&str> {
99 self.placeholder.as_deref()
100 }
101
102 pub fn placeholder<'a>(&'a self, text: &'a str) -> Option<&'a str> {
103 self.placeholder
104 .as_deref()
105 .or_else(|| text.get(self.byte_range.start..self.byte_range.end))
106 }
107}
108
109#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, TS, JsonSchema)]
110pub struct ByteRange {
111 /// Start byte offset (inclusive) within the UTF-8 text buffer.
112 pub start: usize,
113 /// End byte offset (exclusive) within the UTF-8 text buffer.
114 pub end: usize,
115}
116
117impl From<std::ops::Range<usize>> for ByteRange {
118 fn from(range: std::ops::Range<usize>) -> Self {
119 Self {
120 start: range.start,
121 end: range.end,
122 }
123 }
124}