mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
f09e1936e0
## Why Long `/goal` definitions currently reach lower-level goal validation and can produce an opaque failure. This bug was reported by a user. Pasted instruction blocks are especially confusing because the composer can still contain a paste placeholder before expansion, which may otherwise fall into the generic prompt-size error path. There was also a related paste edge case where `/goal ` followed by a multiline block whose first pasted line was blank looked like a bare `/goal` command. That showed the goal usage/summary instead of setting the pasted objective. ## What Changed This adds TUI-side preflight validation for `/goal <objective>` using the shared `MAX_THREAD_GOAL_OBJECTIVE_CHARS` limit. Oversized typed, queued, and pasted goal objectives now fail locally with a goal-specific message that recommends putting longer instructions in a file and referencing that file from the goal. The TUI now also lets inline-argument slash commands consume later-line arguments before treating the first line as a bare command, so `/goal ` followed by blank lines and then objective text sets the goal instead of opening the bare `/goal` flow. ## Manual Testing 1. Start the TUI with goals enabled and an active session. 2. Submit `/goal ` followed by exactly 4,000 objective characters. It should continue through the normal goal-setting path. 3. Submit `/goal ` followed by 4,001 objective characters. It should not set a goal, and should show `Goal objective is too long: 4,001 characters. Limit: 4,000 characters.` followed by the guidance to put longer instructions in a file and reference that file from the goal. 4. Type `/goal `, paste a large block that becomes a `[Pasted Content ... chars]` placeholder, then submit. It should validate the expanded pasted text and show the goal-specific file guidance rather than the generic prompt-size error. 5. Type `/goal `, paste a multiline block whose first line is blank, then submit. It should set the objective from the non-blank pasted content instead of showing `Usage: /goal <objective>` or the bare goal summary. 6. While a turn is running, queue an oversized `/goal` command. When the queue drains, it should show the same goal-specific error and should not emit a goal-setting request.
65 lines
2.3 KiB
Rust
65 lines
2.3 KiB
Rust
//! Validation helpers for `/goal` objective text.
|
|
|
|
use super::*;
|
|
use crate::bottom_pane::ChatComposer;
|
|
use codex_protocol::num_format::format_with_separators;
|
|
use codex_protocol::protocol::MAX_THREAD_GOAL_OBJECTIVE_CHARS;
|
|
|
|
const GOAL_TOO_LONG_FILE_HINT: &str = "Put longer instructions in a file and refer to that file in the goal, for example: /goal follow the instructions in docs/goal.md.";
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
pub(super) enum GoalObjectiveValidationSource {
|
|
Live,
|
|
Queued,
|
|
}
|
|
|
|
impl ChatWidget {
|
|
pub(super) fn goal_objective_with_pending_pastes_is_allowed(
|
|
&mut self,
|
|
args: &str,
|
|
text_elements: &[TextElement],
|
|
) -> bool {
|
|
let pending_pastes = self.bottom_pane.composer_pending_pastes();
|
|
let objective_chars = if pending_pastes.is_empty() {
|
|
args.trim().chars().count()
|
|
} else {
|
|
let (expanded, _) =
|
|
ChatComposer::expand_pending_pastes(args, text_elements.to_vec(), &pending_pastes);
|
|
expanded.trim().chars().count()
|
|
};
|
|
self.goal_objective_char_count_is_allowed(
|
|
objective_chars,
|
|
GoalObjectiveValidationSource::Live,
|
|
)
|
|
}
|
|
|
|
pub(super) fn goal_objective_is_allowed(
|
|
&mut self,
|
|
objective: &str,
|
|
source: GoalObjectiveValidationSource,
|
|
) -> bool {
|
|
self.goal_objective_char_count_is_allowed(objective.chars().count(), source)
|
|
}
|
|
|
|
fn goal_objective_char_count_is_allowed(
|
|
&mut self,
|
|
actual_chars: usize,
|
|
source: GoalObjectiveValidationSource,
|
|
) -> bool {
|
|
if actual_chars <= MAX_THREAD_GOAL_OBJECTIVE_CHARS {
|
|
return true;
|
|
}
|
|
let actual_chars = format_with_separators(actual_chars as i64);
|
|
let max_chars = format_with_separators(MAX_THREAD_GOAL_OBJECTIVE_CHARS as i64);
|
|
self.add_error_message(format!(
|
|
"Goal objective is too long: {actual_chars} characters. Limit: {max_chars} characters. {GOAL_TOO_LONG_FILE_HINT}"
|
|
));
|
|
if source == GoalObjectiveValidationSource::Live {
|
|
self.bottom_pane
|
|
.set_composer_text(String::new(), Vec::new(), Vec::new());
|
|
self.bottom_pane.drain_pending_submission_state();
|
|
}
|
|
false
|
|
}
|
|
}
|