mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
## Why The TUI's `/goal` usage text only advertised the objective form even though `/goal clear`, `/goal edit`, `/goal pause`, and `/goal resume` are implemented. This made the lifecycle controls difficult to discover and allowed the duplicated help text to drift from actual behavior. Fixes #25530. ## What changed - Show the complete `/goal [<objective>|clear|edit|pause|resume]` syntax in usage messages. - Share one usage string across slash-command dispatch and goal-related app messages. - Add inline snapshot coverage for the control-command usage path.
112 lines
3.7 KiB
Rust
112 lines
3.7 KiB
Rust
use crate::status::format_tokens_compact;
|
|
use codex_app_server_protocol::ThreadGoal;
|
|
use codex_app_server_protocol::ThreadGoalStatus;
|
|
|
|
pub(crate) const GOAL_USAGE: &str = "Usage: /goal [<objective>|clear|edit|pause|resume]";
|
|
|
|
pub(crate) fn format_goal_elapsed_seconds(seconds: i64) -> String {
|
|
let seconds = seconds.max(0) as u64;
|
|
if seconds < 60 {
|
|
return format!("{seconds}s");
|
|
}
|
|
|
|
let minutes = seconds / 60;
|
|
if minutes < 60 {
|
|
return format!("{minutes}m");
|
|
}
|
|
|
|
let hours = minutes / 60;
|
|
let remaining_minutes = minutes % 60;
|
|
if hours >= 24 {
|
|
let days = hours / 24;
|
|
let remaining_hours = hours % 24;
|
|
return format!("{days}d {remaining_hours}h {remaining_minutes}m");
|
|
}
|
|
|
|
if remaining_minutes == 0 {
|
|
format!("{hours}h")
|
|
} else {
|
|
format!("{hours}h {remaining_minutes}m")
|
|
}
|
|
}
|
|
|
|
pub(crate) fn goal_status_label(status: ThreadGoalStatus) -> &'static str {
|
|
match status {
|
|
ThreadGoalStatus::Active => "active",
|
|
ThreadGoalStatus::Paused => "paused",
|
|
ThreadGoalStatus::Blocked => "blocked",
|
|
ThreadGoalStatus::UsageLimited => "usage limited",
|
|
ThreadGoalStatus::BudgetLimited => "limited by budget",
|
|
ThreadGoalStatus::Complete => "complete",
|
|
}
|
|
}
|
|
|
|
pub(crate) fn goal_usage_summary(goal: &ThreadGoal) -> String {
|
|
let mut parts = vec![format!("Objective: {}", goal.objective)];
|
|
if goal.time_used_seconds > 0 {
|
|
parts.push(format!(
|
|
"Time: {}.",
|
|
format_goal_elapsed_seconds(goal.time_used_seconds)
|
|
));
|
|
}
|
|
if let Some(token_budget) = goal.token_budget {
|
|
parts.push(format!(
|
|
"Tokens: {}/{}.",
|
|
format_tokens_compact(goal.tokens_used),
|
|
format_tokens_compact(token_budget)
|
|
));
|
|
}
|
|
parts.join(" ")
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use codex_app_server_protocol::ThreadGoal;
|
|
use codex_app_server_protocol::ThreadGoalStatus;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
#[test]
|
|
fn format_goal_elapsed_seconds_is_compact() {
|
|
assert_eq!(format_goal_elapsed_seconds(/*seconds*/ 0), "0s");
|
|
assert_eq!(format_goal_elapsed_seconds(/*seconds*/ 59), "59s");
|
|
assert_eq!(format_goal_elapsed_seconds(/*seconds*/ 60), "1m");
|
|
assert_eq!(format_goal_elapsed_seconds(30 * 60), "30m");
|
|
assert_eq!(format_goal_elapsed_seconds(90 * 60), "1h 30m");
|
|
assert_eq!(format_goal_elapsed_seconds(2 * 60 * 60), "2h");
|
|
let just_before_one_day = 24 * 60 * 60 - 1;
|
|
assert_eq!(format_goal_elapsed_seconds(just_before_one_day), "23h 59m");
|
|
|
|
let one_day = 24 * 60 * 60;
|
|
assert_eq!(format_goal_elapsed_seconds(one_day), "1d 0h 0m");
|
|
|
|
let almost_three_days = 2 * 24 * 60 * 60 + 23 * 60 * 60 + 42 * 60;
|
|
assert_eq!(format_goal_elapsed_seconds(almost_three_days), "2d 23h 42m");
|
|
}
|
|
|
|
fn test_thread_goal(token_budget: Option<i64>, tokens_used: i64) -> ThreadGoal {
|
|
ThreadGoal {
|
|
thread_id: "thread-1".to_string(),
|
|
objective: "Complete the task described in ../gameboy-long-running-prompt5.txt"
|
|
.to_string(),
|
|
status: ThreadGoalStatus::BudgetLimited,
|
|
token_budget,
|
|
tokens_used,
|
|
time_used_seconds: 120,
|
|
created_at: 0,
|
|
updated_at: 0,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn goal_usage_summary_formats_time_and_budgeted_tokens() {
|
|
assert_eq!(
|
|
goal_usage_summary(&test_thread_goal(
|
|
/*token_budget*/ Some(50_000),
|
|
/*tokens_used*/ 63_876,
|
|
)),
|
|
"Objective: Complete the task described in ../gameboy-long-running-prompt5.txt Time: 2m. Tokens: 63.9K/50K."
|
|
);
|
|
}
|
|
}
|