mirror of
https://github.com/openai/codex.git
synced 2026-11-02 19:43:24 +00:00
Part 2
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
use crate::bottom_pane::FeedbackAudience;
|
||||
use codex_app_server_protocol::Account;
|
||||
use codex_protocol::account::PlanType;
|
||||
|
||||
pub(crate) fn account_is_chatgpt(account: Option<&Account>) -> bool {
|
||||
matches!(account, Some(Account::Chatgpt { .. }))
|
||||
}
|
||||
|
||||
pub(crate) fn account_plan_type(account: Option<&Account>) -> Option<PlanType> {
|
||||
match account {
|
||||
Some(Account::Chatgpt { plan_type, .. }) => Some(*plan_type),
|
||||
Some(Account::ApiKey {}) | None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn feedback_audience_from_account(account: Option<&Account>) -> FeedbackAudience {
|
||||
let is_openai_employee = matches!(
|
||||
account,
|
||||
Some(Account::Chatgpt { email, .. }) if email.ends_with("@openai.com")
|
||||
);
|
||||
if is_openai_employee {
|
||||
FeedbackAudience::OpenAiEmployee
|
||||
} else {
|
||||
FeedbackAudience::External
|
||||
}
|
||||
}
|
||||
+77
-24
@@ -1,3 +1,4 @@
|
||||
use crate::account_state::feedback_audience_from_account;
|
||||
use crate::app_backtrack::BacktrackState;
|
||||
use crate::app_event::AppEvent;
|
||||
use crate::app_event::ExitMode;
|
||||
@@ -10,6 +11,8 @@ use crate::bottom_pane::FeedbackAudience;
|
||||
use crate::bottom_pane::McpServerElicitationFormRequest;
|
||||
use crate::bottom_pane::SelectionItem;
|
||||
use crate::bottom_pane::SelectionViewParams;
|
||||
use crate::bottom_pane::feedback_upload_classification;
|
||||
use crate::bottom_pane::feedback_upload_success_lines;
|
||||
use crate::bottom_pane::popup_consts::standard_popup_hint_line;
|
||||
use crate::chatwidget::ChatWidget;
|
||||
use crate::chatwidget::ExternalEditorState;
|
||||
@@ -42,7 +45,6 @@ use codex_ansi_escape::ansi_escape_line;
|
||||
use codex_app_server_client::InProcessAppServerClient;
|
||||
use codex_app_server_protocol::ConfigLayerSource;
|
||||
use codex_app_server_protocol::RequestId;
|
||||
use codex_core::AuthManager;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::config::ConfigBuilder;
|
||||
use codex_core::config::ConfigOverrides;
|
||||
@@ -661,7 +663,6 @@ pub(crate) struct App {
|
||||
pub(crate) session_telemetry: SessionTelemetry,
|
||||
pub(crate) app_event_tx: AppEventSender,
|
||||
pub(crate) chat_widget: ChatWidget,
|
||||
pub(crate) auth_manager: Arc<AuthManager>,
|
||||
available_models: Vec<ModelPreset>,
|
||||
available_collaboration_modes: Vec<CollaborationModeMask>,
|
||||
/// Config is stored here so we can recreate ChatWidgets as needed.
|
||||
@@ -774,7 +775,7 @@ impl App {
|
||||
// Fork/resume bootstraps here don't carry any prefilled message content.
|
||||
initial_user_message: None,
|
||||
enhanced_keys_supported: self.enhanced_keys_supported,
|
||||
auth_manager: self.auth_manager.clone(),
|
||||
account: self.chat_widget.account().cloned(),
|
||||
available_models: self.available_models.clone(),
|
||||
available_collaboration_modes: self.available_collaboration_modes.clone(),
|
||||
feedback: self.feedback.clone(),
|
||||
@@ -1802,7 +1803,7 @@ impl App {
|
||||
// New sessions start without prefilled message content.
|
||||
initial_user_message: None,
|
||||
enhanced_keys_supported: self.enhanced_keys_supported,
|
||||
auth_manager: self.auth_manager.clone(),
|
||||
account: self.chat_widget.account().cloned(),
|
||||
available_models: self.available_models.clone(),
|
||||
available_collaboration_modes: self.available_collaboration_modes.clone(),
|
||||
feedback: self.feedback.clone(),
|
||||
@@ -1962,7 +1963,6 @@ impl App {
|
||||
|
||||
let harness_overrides =
|
||||
normalize_harness_overrides_for_cwd(harness_overrides, &config.cwd)?;
|
||||
let auth_manager = app_server.auth_manager();
|
||||
let available_models = App::list_models_via_app_server(&app_server)
|
||||
.await
|
||||
.map_err(|err| color_eyre::eyre::eyre!(err))?;
|
||||
@@ -2019,14 +2019,7 @@ impl App {
|
||||
codex_app_server_protocol::Account::ApiKey {} => TelemetryAuthMode::ApiKey,
|
||||
codex_app_server_protocol::Account::Chatgpt { .. } => TelemetryAuthMode::Chatgpt,
|
||||
});
|
||||
let feedback_audience = if account_email
|
||||
.as_deref()
|
||||
.is_some_and(|email| email.ends_with("@openai.com"))
|
||||
{
|
||||
FeedbackAudience::OpenAiEmployee
|
||||
} else {
|
||||
FeedbackAudience::External
|
||||
};
|
||||
let feedback_audience = feedback_audience_from_account(account.as_ref());
|
||||
let session_telemetry = SessionTelemetry::new(
|
||||
ThreadId::new(),
|
||||
model.as_str(),
|
||||
@@ -2071,7 +2064,7 @@ impl App {
|
||||
Vec::new(),
|
||||
),
|
||||
enhanced_keys_supported,
|
||||
auth_manager: auth_manager.clone(),
|
||||
account: account.clone(),
|
||||
available_models: available_models.clone(),
|
||||
available_collaboration_modes: available_collaboration_modes.clone(),
|
||||
feedback: feedback.clone(),
|
||||
@@ -2095,7 +2088,6 @@ impl App {
|
||||
session_telemetry: session_telemetry.clone(),
|
||||
app_event_tx,
|
||||
chat_widget,
|
||||
auth_manager: auth_manager.clone(),
|
||||
available_models,
|
||||
available_collaboration_modes,
|
||||
config,
|
||||
@@ -2710,7 +2702,15 @@ impl App {
|
||||
self.open_url_in_browser(url);
|
||||
}
|
||||
AppEvent::RefreshConnectors { force_refetch } => {
|
||||
self.chat_widget.refresh_connectors(force_refetch);
|
||||
let thread_id = self
|
||||
.active_thread_id
|
||||
.or(self.chat_widget.thread_id())
|
||||
.map(|thread_id| thread_id.to_string());
|
||||
let result = Self::list_apps_via_app_server(app_server, thread_id, force_refetch)
|
||||
.await
|
||||
.map(|connectors| crate::app_event::ConnectorsSnapshot { connectors })
|
||||
.map_err(|err| format!("Failed to load apps: {err}"));
|
||||
self.chat_widget.on_connectors_loaded(result, true);
|
||||
}
|
||||
AppEvent::StartFileSearch(query) => {
|
||||
self.file_search.on_user_query(query);
|
||||
@@ -2721,9 +2721,70 @@ impl App {
|
||||
AppEvent::RateLimitSnapshotFetched(snapshot) => {
|
||||
self.chat_widget.on_rate_limit_snapshot(Some(snapshot));
|
||||
}
|
||||
AppEvent::RefreshRateLimits => {
|
||||
match Self::read_account_rate_limits_via_app_server(app_server).await {
|
||||
Ok(response) => {
|
||||
self.chat_widget.on_rate_limit_snapshot(None);
|
||||
if let Some(rate_limits_by_limit_id) = response.rate_limits_by_limit_id {
|
||||
for snapshot in rate_limits_by_limit_id.into_values() {
|
||||
self.chat_widget.on_rate_limit_snapshot(Some(
|
||||
app_server_adapter::rate_limit_snapshot_from_api(snapshot),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
self.chat_widget.on_rate_limit_snapshot(Some(
|
||||
app_server_adapter::rate_limit_snapshot_from_api(
|
||||
response.rate_limits,
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::debug!(error = %err, "failed to refresh rate limits via app-server");
|
||||
}
|
||||
}
|
||||
}
|
||||
AppEvent::ConnectorsLoaded { result, is_final } => {
|
||||
self.chat_widget.on_connectors_loaded(result, is_final);
|
||||
}
|
||||
AppEvent::UploadFeedback {
|
||||
category,
|
||||
thread_id,
|
||||
note,
|
||||
include_logs,
|
||||
rollout_path,
|
||||
feedback_audience,
|
||||
} => {
|
||||
match Self::upload_feedback_via_app_server(
|
||||
app_server,
|
||||
self.next_app_server_request_id(),
|
||||
feedback_upload_classification(category).to_string(),
|
||||
note,
|
||||
thread_id.map(|thread_id| thread_id.to_string()),
|
||||
include_logs,
|
||||
include_logs.then(|| rollout_path.into_iter().collect()),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(thread_id) => {
|
||||
self.chat_widget
|
||||
.add_to_history(history_cell::PlainHistoryCell::new(
|
||||
feedback_upload_success_lines(
|
||||
category,
|
||||
include_logs,
|
||||
&thread_id,
|
||||
feedback_audience,
|
||||
),
|
||||
));
|
||||
}
|
||||
Err(err) => {
|
||||
self.chat_widget
|
||||
.add_to_history(history_cell::new_error_event(format!(
|
||||
"Failed to upload feedback: {err}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
AppEvent::UpdateReasoningEffort(effort) => {
|
||||
self.on_update_reasoning_effort(effort);
|
||||
self.refresh_status_line();
|
||||
@@ -6341,9 +6402,6 @@ smart_approvals = true
|
||||
let available_collaboration_modes = thread_manager
|
||||
.get_models_manager()
|
||||
.list_collaboration_modes();
|
||||
let auth_manager = codex_core::test_support::auth_manager_from_auth(
|
||||
CodexAuth::from_api_key("Test API Key"),
|
||||
);
|
||||
let file_search = FileSearchManager::new(config.cwd.clone(), app_event_tx.clone());
|
||||
let model = codex_core::test_support::get_model_offline(config.model.as_deref());
|
||||
let session_telemetry = test_session_telemetry(&config, model.as_str());
|
||||
@@ -6352,7 +6410,6 @@ smart_approvals = true
|
||||
session_telemetry,
|
||||
app_event_tx,
|
||||
chat_widget,
|
||||
auth_manager,
|
||||
available_models,
|
||||
available_collaboration_modes,
|
||||
config,
|
||||
@@ -6417,9 +6474,6 @@ smart_approvals = true
|
||||
let available_collaboration_modes = thread_manager
|
||||
.get_models_manager()
|
||||
.list_collaboration_modes();
|
||||
let auth_manager = codex_core::test_support::auth_manager_from_auth(
|
||||
CodexAuth::from_api_key("Test API Key"),
|
||||
);
|
||||
let file_search = FileSearchManager::new(config.cwd.clone(), app_event_tx.clone());
|
||||
let model = codex_core::test_support::get_model_offline(config.model.as_deref());
|
||||
let session_telemetry = test_session_telemetry(&config, model.as_str());
|
||||
@@ -6429,7 +6483,6 @@ smart_approvals = true
|
||||
session_telemetry,
|
||||
app_event_tx,
|
||||
chat_widget,
|
||||
auth_manager,
|
||||
available_models,
|
||||
available_collaboration_modes,
|
||||
config,
|
||||
|
||||
@@ -4,13 +4,18 @@ use std::path::PathBuf;
|
||||
use super::App;
|
||||
use codex_app_server_client::InProcessAppServerClient;
|
||||
use codex_app_server_client::InProcessServerEvent;
|
||||
use codex_app_server_protocol::AppsListParams;
|
||||
use codex_app_server_protocol::AppsListResponse;
|
||||
use codex_app_server_protocol::ClientRequest;
|
||||
use codex_app_server_protocol::CollaborationModeListParams;
|
||||
use codex_app_server_protocol::CollaborationModeListResponse;
|
||||
use codex_app_server_protocol::CommandExecutionRequestApprovalResponse;
|
||||
use codex_app_server_protocol::FeedbackUploadParams;
|
||||
use codex_app_server_protocol::FeedbackUploadResponse;
|
||||
use codex_app_server_protocol::FileChangeApprovalDecision;
|
||||
use codex_app_server_protocol::FileChangeRequestApprovalResponse;
|
||||
use codex_app_server_protocol::GetAccountParams;
|
||||
use codex_app_server_protocol::GetAccountRateLimitsResponse;
|
||||
use codex_app_server_protocol::GetAccountResponse;
|
||||
use codex_app_server_protocol::JSONRPCErrorError;
|
||||
use codex_app_server_protocol::JSONRPCNotification;
|
||||
@@ -136,6 +141,69 @@ impl App {
|
||||
Ok(response.account)
|
||||
}
|
||||
|
||||
pub(super) async fn read_account_rate_limits_via_app_server(
|
||||
app_server_client: &InProcessAppServerClient,
|
||||
) -> Result<GetAccountRateLimitsResponse, String> {
|
||||
send_request_with_response(
|
||||
app_server_client,
|
||||
ClientRequest::GetAccountRateLimits {
|
||||
request_id: RequestId::Integer(0),
|
||||
params: None,
|
||||
},
|
||||
"account/rateLimits/read",
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(super) async fn list_apps_via_app_server(
|
||||
app_server_client: &InProcessAppServerClient,
|
||||
thread_id: Option<String>,
|
||||
force_refetch: bool,
|
||||
) -> Result<Vec<codex_app_server_protocol::AppInfo>, String> {
|
||||
let response: AppsListResponse = send_request_with_response(
|
||||
app_server_client,
|
||||
ClientRequest::AppsList {
|
||||
request_id: RequestId::Integer(0),
|
||||
params: AppsListParams {
|
||||
cursor: None,
|
||||
limit: None,
|
||||
thread_id,
|
||||
force_refetch,
|
||||
},
|
||||
},
|
||||
"app/list",
|
||||
)
|
||||
.await?;
|
||||
Ok(response.data)
|
||||
}
|
||||
|
||||
pub(super) async fn upload_feedback_via_app_server(
|
||||
app_server_client: &InProcessAppServerClient,
|
||||
request_id: RequestId,
|
||||
classification: String,
|
||||
reason: Option<String>,
|
||||
thread_id: Option<String>,
|
||||
include_logs: bool,
|
||||
extra_log_files: Option<Vec<PathBuf>>,
|
||||
) -> Result<String, String> {
|
||||
let response: FeedbackUploadResponse = send_request_with_response(
|
||||
app_server_client,
|
||||
ClientRequest::FeedbackUpload {
|
||||
request_id,
|
||||
params: FeedbackUploadParams {
|
||||
classification,
|
||||
reason,
|
||||
thread_id,
|
||||
include_logs,
|
||||
extra_log_files,
|
||||
},
|
||||
},
|
||||
"feedback/upload",
|
||||
)
|
||||
.await?;
|
||||
Ok(response.thread_id)
|
||||
}
|
||||
|
||||
pub(super) fn next_app_server_request_id(&mut self) -> RequestId {
|
||||
let request_id = self.next_app_server_request_id;
|
||||
self.next_app_server_request_id = self.next_app_server_request_id.saturating_add(1);
|
||||
@@ -718,7 +786,7 @@ impl App {
|
||||
|
||||
async fn handle_server_notification(
|
||||
&mut self,
|
||||
_app_server_client: &InProcessAppServerClient,
|
||||
app_server_client: &InProcessAppServerClient,
|
||||
notification: ServerNotification,
|
||||
) {
|
||||
match notification {
|
||||
@@ -762,6 +830,33 @@ impl App {
|
||||
}
|
||||
}
|
||||
}
|
||||
ServerNotification::AccountUpdated(_) => {
|
||||
match Self::read_account_via_app_server(app_server_client).await {
|
||||
Ok(account) => {
|
||||
self.feedback_audience =
|
||||
crate::account_state::feedback_audience_from_account(account.as_ref());
|
||||
self.chat_widget.set_account(account);
|
||||
self.refresh_status_line();
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!(error = %err, "failed to refresh account via app-server");
|
||||
}
|
||||
}
|
||||
}
|
||||
ServerNotification::AccountRateLimitsUpdated(payload) => {
|
||||
self.chat_widget
|
||||
.on_rate_limit_snapshot(Some(rate_limit_snapshot_from_api(
|
||||
payload.rate_limits,
|
||||
)));
|
||||
}
|
||||
ServerNotification::AppListUpdated(payload) => {
|
||||
self.chat_widget.on_connectors_loaded(
|
||||
Ok(crate::app_event::ConnectorsSnapshot {
|
||||
connectors: payload.data,
|
||||
}),
|
||||
true,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -1175,6 +1270,39 @@ fn collaboration_mode_mask_from_api(
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn rate_limit_snapshot_from_api(
|
||||
snapshot: codex_app_server_protocol::RateLimitSnapshot,
|
||||
) -> codex_protocol::protocol::RateLimitSnapshot {
|
||||
codex_protocol::protocol::RateLimitSnapshot {
|
||||
limit_id: snapshot.limit_id,
|
||||
limit_name: snapshot.limit_name,
|
||||
primary: snapshot.primary.map(rate_limit_window_from_api),
|
||||
secondary: snapshot.secondary.map(rate_limit_window_from_api),
|
||||
credits: snapshot.credits.map(credits_snapshot_from_api),
|
||||
plan_type: snapshot.plan_type,
|
||||
}
|
||||
}
|
||||
|
||||
fn rate_limit_window_from_api(
|
||||
window: codex_app_server_protocol::RateLimitWindow,
|
||||
) -> codex_protocol::protocol::RateLimitWindow {
|
||||
codex_protocol::protocol::RateLimitWindow {
|
||||
used_percent: f64::from(window.used_percent),
|
||||
window_minutes: window.window_duration_mins,
|
||||
resets_at: window.resets_at,
|
||||
}
|
||||
}
|
||||
|
||||
fn credits_snapshot_from_api(
|
||||
credits: codex_app_server_protocol::CreditsSnapshot,
|
||||
) -> codex_protocol::protocol::CreditsSnapshot {
|
||||
codex_protocol::protocol::CreditsSnapshot {
|
||||
has_credits: credits.has_credits,
|
||||
unlimited: credits.unlimited,
|
||||
balance: credits.balance,
|
||||
}
|
||||
}
|
||||
|
||||
struct DecodedLegacyNotification {
|
||||
conversation_id: Option<String>,
|
||||
event: Event,
|
||||
|
||||
@@ -131,6 +131,9 @@ pub(crate) enum AppEvent {
|
||||
/// Result of refreshing rate limits
|
||||
RateLimitSnapshotFetched(RateLimitSnapshot),
|
||||
|
||||
/// Request a fresh account rate-limit read via app-server.
|
||||
RefreshRateLimits,
|
||||
|
||||
/// Result of prefetching connectors.
|
||||
ConnectorsLoaded {
|
||||
result: Result<ConnectorsSnapshot, String>,
|
||||
@@ -432,6 +435,15 @@ pub(crate) enum AppEvent {
|
||||
include_logs: bool,
|
||||
},
|
||||
|
||||
UploadFeedback {
|
||||
category: FeedbackCategory,
|
||||
thread_id: Option<ThreadId>,
|
||||
note: Option<String>,
|
||||
include_logs: bool,
|
||||
rollout_path: Option<PathBuf>,
|
||||
feedback_audience: crate::bottom_pane::FeedbackAudience,
|
||||
},
|
||||
|
||||
/// Open the upload consent popup for feedback after selecting a category.
|
||||
OpenFeedbackConsent {
|
||||
category: FeedbackCategory,
|
||||
|
||||
@@ -19,9 +19,8 @@ use ratatui::widgets::Widget;
|
||||
use crate::app_event::AppEvent;
|
||||
use crate::app_event::FeedbackCategory;
|
||||
use crate::app_event_sender::AppEventSender;
|
||||
use crate::history_cell;
|
||||
use crate::render::renderable::Renderable;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
use codex_protocol::ThreadId;
|
||||
|
||||
use super::CancellationEvent;
|
||||
use super::bottom_pane_view::BottomPaneView;
|
||||
@@ -48,7 +47,7 @@ pub(crate) enum FeedbackAudience {
|
||||
/// both logs and rollout with classification + metadata.
|
||||
pub(crate) struct FeedbackNoteView {
|
||||
category: FeedbackCategory,
|
||||
snapshot: codex_feedback::FeedbackSnapshot,
|
||||
thread_id: Option<ThreadId>,
|
||||
rollout_path: Option<PathBuf>,
|
||||
app_event_tx: AppEventSender,
|
||||
include_logs: bool,
|
||||
@@ -63,7 +62,7 @@ pub(crate) struct FeedbackNoteView {
|
||||
impl FeedbackNoteView {
|
||||
pub(crate) fn new(
|
||||
category: FeedbackCategory,
|
||||
snapshot: codex_feedback::FeedbackSnapshot,
|
||||
thread_id: Option<ThreadId>,
|
||||
rollout_path: Option<PathBuf>,
|
||||
app_event_tx: AppEventSender,
|
||||
include_logs: bool,
|
||||
@@ -71,7 +70,7 @@ impl FeedbackNoteView {
|
||||
) -> Self {
|
||||
Self {
|
||||
category,
|
||||
snapshot,
|
||||
thread_id,
|
||||
rollout_path,
|
||||
app_event_tx,
|
||||
include_logs,
|
||||
@@ -84,90 +83,14 @@ impl FeedbackNoteView {
|
||||
|
||||
fn submit(&mut self) {
|
||||
let note = self.textarea.text().trim().to_string();
|
||||
let reason_opt = if note.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(note.as_str())
|
||||
};
|
||||
let attachment_paths = if self.include_logs {
|
||||
self.rollout_path.iter().cloned().collect::<Vec<_>>()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let classification = feedback_classification(self.category);
|
||||
|
||||
let mut thread_id = self.snapshot.thread_id.clone();
|
||||
|
||||
let result = self.snapshot.upload_feedback(
|
||||
classification,
|
||||
reason_opt,
|
||||
self.include_logs,
|
||||
&attachment_paths,
|
||||
Some(SessionSource::Cli),
|
||||
None,
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(()) => {
|
||||
let prefix = if self.include_logs {
|
||||
"• Feedback uploaded."
|
||||
} else {
|
||||
"• Feedback recorded (no logs)."
|
||||
};
|
||||
let issue_url =
|
||||
issue_url_for_category(self.category, &thread_id, self.feedback_audience);
|
||||
let mut lines = vec![Line::from(match issue_url.as_ref() {
|
||||
Some(_) if self.feedback_audience == FeedbackAudience::OpenAiEmployee => {
|
||||
format!("{prefix} Please report this in #codex-feedback:")
|
||||
}
|
||||
Some(_) => format!("{prefix} Please open an issue using the following URL:"),
|
||||
None => format!("{prefix} Thanks for the feedback!"),
|
||||
})];
|
||||
match issue_url {
|
||||
Some(url) if self.feedback_audience == FeedbackAudience::OpenAiEmployee => {
|
||||
lines.extend([
|
||||
"".into(),
|
||||
Line::from(vec![" ".into(), url.cyan().underlined()]),
|
||||
"".into(),
|
||||
Line::from(" Share this and add some info about your problem:"),
|
||||
Line::from(vec![
|
||||
" ".into(),
|
||||
format!("https://go/codex-feedback/{thread_id}").bold(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
Some(url) => {
|
||||
lines.extend([
|
||||
"".into(),
|
||||
Line::from(vec![" ".into(), url.cyan().underlined()]),
|
||||
"".into(),
|
||||
Line::from(vec![
|
||||
" Or mention your thread ID ".into(),
|
||||
std::mem::take(&mut thread_id).bold(),
|
||||
" in an existing issue.".into(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
None => {
|
||||
lines.extend([
|
||||
"".into(),
|
||||
Line::from(vec![
|
||||
" Thread ID: ".into(),
|
||||
std::mem::take(&mut thread_id).bold(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
self.app_event_tx.send(AppEvent::InsertHistoryCell(Box::new(
|
||||
history_cell::PlainHistoryCell::new(lines),
|
||||
)));
|
||||
}
|
||||
Err(e) => {
|
||||
self.app_event_tx.send(AppEvent::InsertHistoryCell(Box::new(
|
||||
history_cell::new_error_event(format!("Failed to upload feedback: {e}")),
|
||||
)));
|
||||
}
|
||||
}
|
||||
self.app_event_tx.send(AppEvent::UploadFeedback {
|
||||
category: self.category,
|
||||
thread_id: self.thread_id,
|
||||
note: (!note.is_empty()).then_some(note),
|
||||
include_logs: self.include_logs,
|
||||
rollout_path: self.rollout_path.clone(),
|
||||
feedback_audience: self.feedback_audience,
|
||||
});
|
||||
self.complete = true;
|
||||
}
|
||||
}
|
||||
@@ -392,6 +315,64 @@ fn feedback_classification(category: FeedbackCategory) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn feedback_upload_classification(category: FeedbackCategory) -> &'static str {
|
||||
feedback_classification(category)
|
||||
}
|
||||
|
||||
pub(crate) fn feedback_upload_success_lines(
|
||||
category: FeedbackCategory,
|
||||
include_logs: bool,
|
||||
thread_id: &str,
|
||||
feedback_audience: FeedbackAudience,
|
||||
) -> Vec<Line<'static>> {
|
||||
let prefix = if include_logs {
|
||||
"• Feedback uploaded."
|
||||
} else {
|
||||
"• Feedback recorded (no logs)."
|
||||
};
|
||||
let issue_url = issue_url_for_category(category, thread_id, feedback_audience);
|
||||
let mut lines = vec![Line::from(match issue_url.as_ref() {
|
||||
Some(_) if feedback_audience == FeedbackAudience::OpenAiEmployee => {
|
||||
format!("{prefix} Please report this in #codex-feedback:")
|
||||
}
|
||||
Some(_) => format!("{prefix} Please open an issue using the following URL:"),
|
||||
None => format!("{prefix} Thanks for the feedback!"),
|
||||
})];
|
||||
match issue_url {
|
||||
Some(url) if feedback_audience == FeedbackAudience::OpenAiEmployee => {
|
||||
lines.extend([
|
||||
"".into(),
|
||||
Line::from(vec![" ".into(), url.cyan().underlined()]),
|
||||
"".into(),
|
||||
Line::from(" Share this and add some info about your problem:"),
|
||||
Line::from(vec![
|
||||
" ".into(),
|
||||
format!("https://go/codex-feedback/{thread_id}").bold(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
Some(url) => {
|
||||
lines.extend([
|
||||
"".into(),
|
||||
Line::from(vec![" ".into(), url.cyan().underlined()]),
|
||||
"".into(),
|
||||
Line::from(vec![
|
||||
" Or mention your thread ID ".into(),
|
||||
thread_id.to_string().bold(),
|
||||
" in an existing issue.".into(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
None => {
|
||||
lines.extend([
|
||||
"".into(),
|
||||
Line::from(vec![" Thread ID: ".into(), thread_id.to_string().bold()]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
lines
|
||||
}
|
||||
|
||||
fn issue_url_for_category(
|
||||
category: FeedbackCategory,
|
||||
thread_id: &str,
|
||||
@@ -628,15 +609,7 @@ mod tests {
|
||||
fn make_view(category: FeedbackCategory) -> FeedbackNoteView {
|
||||
let (tx_raw, _rx) = tokio::sync::mpsc::unbounded_channel::<AppEvent>();
|
||||
let tx = AppEventSender::new(tx_raw);
|
||||
let snapshot = codex_feedback::CodexFeedback::new().snapshot(None);
|
||||
FeedbackNoteView::new(
|
||||
category,
|
||||
snapshot,
|
||||
None,
|
||||
tx,
|
||||
true,
|
||||
FeedbackAudience::External,
|
||||
)
|
||||
FeedbackNoteView::new(category, None, None, tx, true, FeedbackAudience::External)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -678,7 +651,7 @@ mod tests {
|
||||
fn feedback_view_with_connectivity_diagnostics() {
|
||||
let (tx_raw, _rx) = tokio::sync::mpsc::unbounded_channel::<AppEvent>();
|
||||
let tx = AppEventSender::new(tx_raw);
|
||||
let diagnostics = FeedbackDiagnostics::new(vec![
|
||||
let _diagnostics = FeedbackDiagnostics::new(vec![
|
||||
FeedbackDiagnostic {
|
||||
headline: "Proxy environment variables are set and may affect connectivity."
|
||||
.to_string(),
|
||||
@@ -689,12 +662,9 @@ mod tests {
|
||||
details: vec!["OPENAI_BASE_URL = https://example.com/v1".to_string()],
|
||||
},
|
||||
]);
|
||||
let snapshot = codex_feedback::CodexFeedback::new()
|
||||
.snapshot(None)
|
||||
.with_feedback_diagnostics(diagnostics);
|
||||
let view = FeedbackNoteView::new(
|
||||
FeedbackCategory::Bug,
|
||||
snapshot,
|
||||
None,
|
||||
None,
|
||||
tx,
|
||||
false,
|
||||
|
||||
@@ -94,7 +94,9 @@ mod feedback_view;
|
||||
pub(crate) use feedback_view::FeedbackAudience;
|
||||
pub(crate) use feedback_view::feedback_disabled_params;
|
||||
pub(crate) use feedback_view::feedback_selection_params;
|
||||
pub(crate) use feedback_view::feedback_upload_classification;
|
||||
pub(crate) use feedback_view::feedback_upload_consent_params;
|
||||
pub(crate) use feedback_view::feedback_upload_success_lines;
|
||||
pub(crate) use skills_toggle_view::SkillsToggleItem;
|
||||
pub(crate) use skills_toggle_view::SkillsToggleView;
|
||||
pub(crate) use status_line_setup::StatusLineItem;
|
||||
|
||||
+61
-129
@@ -51,7 +51,6 @@ use crate::status::rate_limit_snapshot_display_for_limit;
|
||||
use crate::text_formatting::proper_join;
|
||||
use crate::version::CODEX_CLI_VERSION;
|
||||
use codex_app_server_protocol::ConfigLayerSource;
|
||||
use codex_backend_client::Client as BackendClient;
|
||||
use codex_chatgpt::connectors;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::config::Constrained;
|
||||
@@ -211,6 +210,9 @@ fn queued_message_edit_binding_for_terminal(terminal_name: TerminalName) -> KeyB
|
||||
}
|
||||
}
|
||||
|
||||
use crate::account_state::account_is_chatgpt;
|
||||
use crate::account_state::account_plan_type;
|
||||
use crate::account_state::feedback_audience_from_account;
|
||||
use crate::app_event::AppEvent;
|
||||
use crate::app_event::ConnectorsSnapshot;
|
||||
use crate::app_event::ExitMode;
|
||||
@@ -292,8 +294,7 @@ use crate::streaming::controller::PlanStreamController;
|
||||
use crate::streaming::controller::StreamController;
|
||||
|
||||
use chrono::Local;
|
||||
use codex_core::AuthManager;
|
||||
use codex_core::CodexAuth;
|
||||
use codex_app_server_protocol::Account;
|
||||
use codex_core::ThreadManager;
|
||||
use codex_file_search::FileMatch;
|
||||
use codex_protocol::openai_models::InputModality;
|
||||
@@ -474,7 +475,7 @@ pub(crate) struct ChatWidgetInit {
|
||||
pub(crate) app_event_tx: AppEventSender,
|
||||
pub(crate) initial_user_message: Option<UserMessage>,
|
||||
pub(crate) enhanced_keys_supported: bool,
|
||||
pub(crate) auth_manager: Arc<AuthManager>,
|
||||
pub(crate) account: Option<Account>,
|
||||
pub(crate) available_models: Vec<ModelPreset>,
|
||||
pub(crate) available_collaboration_modes: Vec<CollaborationModeMask>,
|
||||
pub(crate) feedback: codex_feedback::CodexFeedback,
|
||||
@@ -653,7 +654,7 @@ pub(crate) struct ChatWidget {
|
||||
current_collaboration_mode: CollaborationMode,
|
||||
/// The currently active collaboration mask, if any.
|
||||
active_collaboration_mask: Option<CollaborationModeMask>,
|
||||
auth_manager: Arc<AuthManager>,
|
||||
account: Option<Account>,
|
||||
available_models: Vec<ModelPreset>,
|
||||
available_collaboration_modes: Vec<CollaborationModeMask>,
|
||||
session_telemetry: SessionTelemetry,
|
||||
@@ -1412,9 +1413,7 @@ impl ChatWidget {
|
||||
event,
|
||||
self.show_welcome_banner,
|
||||
startup_tooltip_override,
|
||||
self.auth_manager
|
||||
.auth_cached()
|
||||
.and_then(|auth| auth.account_plan_type()),
|
||||
account_plan_type(self.account.as_ref()),
|
||||
show_fast_status,
|
||||
);
|
||||
self.apply_session_info_cell(session_info_cell);
|
||||
@@ -1497,27 +1496,57 @@ impl ChatWidget {
|
||||
self.bottom_pane.set_skills(skills);
|
||||
}
|
||||
|
||||
pub(crate) fn account(&self) -> Option<&Account> {
|
||||
self.account.as_ref()
|
||||
}
|
||||
|
||||
pub(crate) fn set_account(&mut self, account: Option<Account>) {
|
||||
let connectors_were_enabled = self.connectors_enabled();
|
||||
let should_prefetch_rate_limits = self.should_prefetch_rate_limits();
|
||||
let account_changed = self.account != account;
|
||||
|
||||
self.feedback_audience = feedback_audience_from_account(account.as_ref());
|
||||
self.plan_type = account_plan_type(account.as_ref()).or(self.plan_type);
|
||||
self.account = account;
|
||||
|
||||
let connectors_enabled = self.connectors_enabled();
|
||||
self.bottom_pane.set_connectors_enabled(connectors_enabled);
|
||||
self.sync_fast_command_enabled();
|
||||
|
||||
if connectors_enabled {
|
||||
if account_changed || !connectors_were_enabled {
|
||||
self.refresh_connectors(account_changed);
|
||||
}
|
||||
} else {
|
||||
self.connectors_cache = ConnectorsCacheState::default();
|
||||
self.connectors_partial_snapshot = None;
|
||||
self.bottom_pane.set_connectors_snapshot(None);
|
||||
}
|
||||
|
||||
if self.should_prefetch_rate_limits() {
|
||||
if account_changed || !should_prefetch_rate_limits {
|
||||
self.prefetch_rate_limits();
|
||||
}
|
||||
} else {
|
||||
self.stop_rate_limit_poller();
|
||||
self.on_rate_limit_snapshot(None);
|
||||
}
|
||||
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
pub(crate) fn open_feedback_note(
|
||||
&mut self,
|
||||
category: crate::app_event::FeedbackCategory,
|
||||
include_logs: bool,
|
||||
) {
|
||||
if let Some(chatgpt_user_id) = self
|
||||
.auth_manager
|
||||
.auth_cached()
|
||||
.and_then(|auth| auth.get_chatgpt_user_id())
|
||||
{
|
||||
tracing::info!(target: "feedback_tags", chatgpt_user_id);
|
||||
}
|
||||
let snapshot = self.feedback.snapshot(self.thread_id);
|
||||
self.show_feedback_note(category, include_logs, snapshot);
|
||||
self.show_feedback_note(category, include_logs);
|
||||
}
|
||||
|
||||
fn show_feedback_note(
|
||||
&mut self,
|
||||
category: crate::app_event::FeedbackCategory,
|
||||
include_logs: bool,
|
||||
snapshot: codex_feedback::FeedbackSnapshot,
|
||||
) {
|
||||
let rollout = if include_logs {
|
||||
self.current_rollout_path.clone()
|
||||
@@ -1526,7 +1555,7 @@ impl ChatWidget {
|
||||
};
|
||||
let view = crate::bottom_pane::FeedbackNoteView::new(
|
||||
category,
|
||||
snapshot,
|
||||
self.thread_id,
|
||||
rollout,
|
||||
self.app_event_tx.clone(),
|
||||
include_logs,
|
||||
@@ -1543,13 +1572,6 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
pub(crate) fn open_feedback_consent(&mut self, category: crate::app_event::FeedbackCategory) {
|
||||
if let Some(chatgpt_user_id) = self
|
||||
.auth_manager
|
||||
.auth_cached()
|
||||
.and_then(|auth| auth.get_chatgpt_user_id())
|
||||
{
|
||||
tracing::info!(target: "feedback_tags", chatgpt_user_id);
|
||||
}
|
||||
let snapshot = self.feedback.snapshot(self.thread_id);
|
||||
let params = crate::bottom_pane::feedback_upload_consent_params(
|
||||
self.app_event_tx.clone(),
|
||||
@@ -3520,7 +3542,7 @@ impl ChatWidget {
|
||||
app_event_tx,
|
||||
initial_user_message,
|
||||
enhanced_keys_supported,
|
||||
auth_manager,
|
||||
account,
|
||||
available_models,
|
||||
available_collaboration_modes,
|
||||
feedback,
|
||||
@@ -3589,7 +3611,7 @@ impl ChatWidget {
|
||||
skills_initial_state: None,
|
||||
current_collaboration_mode,
|
||||
active_collaboration_mask,
|
||||
auth_manager,
|
||||
account,
|
||||
available_models,
|
||||
available_collaboration_modes,
|
||||
session_telemetry,
|
||||
@@ -3712,7 +3734,7 @@ impl ChatWidget {
|
||||
app_event_tx,
|
||||
initial_user_message,
|
||||
enhanced_keys_supported,
|
||||
auth_manager,
|
||||
account,
|
||||
available_models,
|
||||
available_collaboration_modes,
|
||||
feedback,
|
||||
@@ -3780,7 +3802,7 @@ impl ChatWidget {
|
||||
skills_initial_state: None,
|
||||
current_collaboration_mode,
|
||||
active_collaboration_mask,
|
||||
auth_manager,
|
||||
account,
|
||||
available_models,
|
||||
available_collaboration_modes,
|
||||
session_telemetry,
|
||||
@@ -3895,7 +3917,7 @@ impl ChatWidget {
|
||||
app_event_tx,
|
||||
initial_user_message,
|
||||
enhanced_keys_supported,
|
||||
auth_manager,
|
||||
account,
|
||||
available_models,
|
||||
available_collaboration_modes,
|
||||
feedback,
|
||||
@@ -3963,7 +3985,7 @@ impl ChatWidget {
|
||||
skills_initial_state: None,
|
||||
current_collaboration_mode,
|
||||
active_collaboration_mask,
|
||||
auth_manager,
|
||||
account,
|
||||
available_models,
|
||||
available_collaboration_modes,
|
||||
session_telemetry,
|
||||
@@ -5697,7 +5719,7 @@ impl ChatWidget {
|
||||
.collect();
|
||||
self.add_to_history(crate::status::new_status_output_with_rate_limits(
|
||||
&self.config,
|
||||
self.auth_manager.as_ref(),
|
||||
self.account.as_ref(),
|
||||
token_info,
|
||||
total_usage,
|
||||
&self.thread_id,
|
||||
@@ -6027,61 +6049,8 @@ impl ChatWidget {
|
||||
if !matches!(self.connectors_cache, ConnectorsCacheState::Ready(_)) {
|
||||
self.connectors_cache = ConnectorsCacheState::Loading;
|
||||
}
|
||||
|
||||
let config = self.config.clone();
|
||||
let app_event_tx = self.app_event_tx.clone();
|
||||
tokio::spawn(async move {
|
||||
let accessible_result =
|
||||
match connectors::list_accessible_connectors_from_mcp_tools_with_options_and_status(
|
||||
&config,
|
||||
force_refetch,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(connectors) => connectors,
|
||||
Err(err) => {
|
||||
app_event_tx.send(AppEvent::ConnectorsLoaded {
|
||||
result: Err(format!("Failed to load apps: {err}")),
|
||||
is_final: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
let should_schedule_force_refetch =
|
||||
!force_refetch && !accessible_result.codex_apps_ready;
|
||||
let accessible_connectors = accessible_result.connectors;
|
||||
|
||||
app_event_tx.send(AppEvent::ConnectorsLoaded {
|
||||
result: Ok(ConnectorsSnapshot {
|
||||
connectors: accessible_connectors.clone(),
|
||||
}),
|
||||
is_final: false,
|
||||
});
|
||||
|
||||
let result: Result<ConnectorsSnapshot, String> = async {
|
||||
let all_connectors =
|
||||
connectors::list_all_connectors_with_options(&config, force_refetch).await?;
|
||||
let connectors = connectors::merge_connectors_with_accessible(
|
||||
all_connectors,
|
||||
accessible_connectors,
|
||||
true,
|
||||
);
|
||||
Ok(ConnectorsSnapshot { connectors })
|
||||
}
|
||||
.await
|
||||
.map_err(|err: anyhow::Error| format!("Failed to load apps: {err}"));
|
||||
|
||||
app_event_tx.send(AppEvent::ConnectorsLoaded {
|
||||
result,
|
||||
is_final: true,
|
||||
});
|
||||
|
||||
if should_schedule_force_refetch {
|
||||
app_event_tx.send(AppEvent::RefreshConnectors {
|
||||
force_refetch: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
self.app_event_tx
|
||||
.send(AppEvent::RefreshConnectors { force_refetch });
|
||||
}
|
||||
|
||||
fn prefetch_rate_limits(&mut self) {
|
||||
@@ -6091,21 +6060,13 @@ impl ChatWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
let base_url = self.config.chatgpt_base_url.clone();
|
||||
let app_event_tx = self.app_event_tx.clone();
|
||||
let auth_manager = Arc::clone(&self.auth_manager);
|
||||
|
||||
let handle = tokio::spawn(async move {
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(60));
|
||||
|
||||
loop {
|
||||
if let Some(auth) = auth_manager.auth().await
|
||||
&& auth.is_chatgpt_auth()
|
||||
{
|
||||
for snapshot in fetch_rate_limits(base_url.clone(), auth).await {
|
||||
app_event_tx.send(AppEvent::RateLimitSnapshotFetched(snapshot));
|
||||
}
|
||||
}
|
||||
app_event_tx.send(AppEvent::RefreshRateLimits);
|
||||
interval.tick().await;
|
||||
}
|
||||
});
|
||||
@@ -6114,14 +6075,7 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
fn should_prefetch_rate_limits(&self) -> bool {
|
||||
if !self.config.model_provider.requires_openai_auth {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.auth_manager
|
||||
.auth_cached()
|
||||
.as_ref()
|
||||
.is_some_and(CodexAuth::is_chatgpt_auth)
|
||||
self.config.model_provider.requires_openai_auth && account_is_chatgpt(self.account.as_ref())
|
||||
}
|
||||
|
||||
fn lower_cost_preset(&self) -> Option<ModelPreset> {
|
||||
@@ -7957,11 +7911,7 @@ impl ChatWidget {
|
||||
) -> bool {
|
||||
model == FAST_STATUS_MODEL
|
||||
&& matches!(service_tier, Some(ServiceTier::Fast))
|
||||
&& self
|
||||
.auth_manager
|
||||
.auth_cached()
|
||||
.as_ref()
|
||||
.is_some_and(CodexAuth::is_chatgpt_auth)
|
||||
&& account_is_chatgpt(self.account.as_ref())
|
||||
}
|
||||
|
||||
fn fast_mode_enabled(&self) -> bool {
|
||||
@@ -8268,9 +8218,7 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
fn connectors_enabled(&self) -> bool {
|
||||
self.config
|
||||
.features
|
||||
.apps_enabled_cached(Some(self.auth_manager.as_ref()))
|
||||
self.config.features.enabled(Feature::Apps) && account_is_chatgpt(self.account.as_ref())
|
||||
}
|
||||
|
||||
fn connectors_for_mentions(&self) -> Option<&[connectors::AppInfo]> {
|
||||
@@ -9421,22 +9369,6 @@ fn hook_event_label(event_name: codex_protocol::protocol::HookEventName) -> &'st
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_rate_limits(base_url: String, auth: CodexAuth) -> Vec<RateLimitSnapshot> {
|
||||
match BackendClient::from_auth(base_url, &auth) {
|
||||
Ok(client) => match client.get_rate_limits_many().await {
|
||||
Ok(snapshots) => snapshots,
|
||||
Err(err) => {
|
||||
debug!(error = ?err, "failed to fetch rate limits from /usage");
|
||||
Vec::new()
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
debug!(error = ?err, "failed to construct backend client for rate limits");
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn show_review_commit_picker_with_entries(
|
||||
chat: &mut ChatWidget,
|
||||
|
||||
@@ -1752,15 +1752,13 @@ async fn helpers_are_available_and_do_not_panic() {
|
||||
cfg.model_provider.clone(),
|
||||
),
|
||||
);
|
||||
let auth_manager =
|
||||
codex_core::test_support::auth_manager_from_auth(CodexAuth::from_api_key("test"));
|
||||
let init = ChatWidgetInit {
|
||||
config: cfg,
|
||||
frame_requester: FrameRequester::test_dummy(),
|
||||
app_event_tx: tx,
|
||||
initial_user_message: None,
|
||||
enhanced_keys_supported: false,
|
||||
auth_manager,
|
||||
account: Some(codex_app_server_protocol::Account::ApiKey {}),
|
||||
available_models: thread_manager
|
||||
.get_models_manager()
|
||||
.try_list_models()
|
||||
@@ -1844,7 +1842,7 @@ async fn make_chatwidget_manual(
|
||||
let codex_home = cfg.codex_home.clone();
|
||||
let models_manager = Arc::new(ModelsManager::new(
|
||||
codex_home,
|
||||
auth_manager.clone(),
|
||||
auth_manager,
|
||||
None,
|
||||
CollaborationModesConfig::default(),
|
||||
));
|
||||
@@ -1871,7 +1869,7 @@ async fn make_chatwidget_manual(
|
||||
config: cfg,
|
||||
current_collaboration_mode,
|
||||
active_collaboration_mask,
|
||||
auth_manager,
|
||||
account: Some(codex_app_server_protocol::Account::ApiKey {}),
|
||||
available_models,
|
||||
available_collaboration_modes,
|
||||
session_telemetry,
|
||||
@@ -1988,12 +1986,16 @@ fn assert_no_submit_op(op_rx: &mut tokio::sync::mpsc::UnboundedReceiver<Op>) {
|
||||
}
|
||||
|
||||
pub(crate) fn set_chatgpt_auth(chat: &mut ChatWidget) {
|
||||
chat.auth_manager = codex_core::test_support::auth_manager_from_auth(
|
||||
let auth_manager = codex_core::test_support::auth_manager_from_auth(
|
||||
CodexAuth::create_dummy_chatgpt_auth_for_testing(),
|
||||
);
|
||||
chat.set_account(Some(codex_app_server_protocol::Account::Chatgpt {
|
||||
email: "test@example.com".to_string(),
|
||||
plan_type: codex_protocol::account::PlanType::Plus,
|
||||
}));
|
||||
let models_manager = Arc::new(ModelsManager::new(
|
||||
chat.config.codex_home.clone(),
|
||||
chat.auth_manager.clone(),
|
||||
auth_manager,
|
||||
None,
|
||||
CollaborationModesConfig::default(),
|
||||
));
|
||||
@@ -2347,9 +2349,7 @@ async fn rate_limit_snapshots_keep_separate_entries_per_limit_id() {
|
||||
#[tokio::test]
|
||||
async fn rate_limit_switch_prompt_skips_when_on_lower_cost_model() {
|
||||
let (mut chat, _, _) = make_chatwidget_manual(Some(NUDGE_MODEL_SLUG)).await;
|
||||
chat.auth_manager = codex_core::test_support::auth_manager_from_auth(
|
||||
CodexAuth::create_dummy_chatgpt_auth_for_testing(),
|
||||
);
|
||||
set_chatgpt_auth(&mut chat);
|
||||
|
||||
chat.on_rate_limit_snapshot(Some(snapshot(95.0)));
|
||||
|
||||
@@ -2361,9 +2361,8 @@ async fn rate_limit_switch_prompt_skips_when_on_lower_cost_model() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn rate_limit_switch_prompt_skips_non_codex_limit() {
|
||||
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let (mut chat, _, _) = make_chatwidget_manual(Some("gpt-5")).await;
|
||||
chat.auth_manager = codex_core::test_support::auth_manager_from_auth(auth);
|
||||
set_chatgpt_auth(&mut chat);
|
||||
|
||||
chat.on_rate_limit_snapshot(Some(RateLimitSnapshot {
|
||||
limit_id: Some("codex_other".to_string()),
|
||||
@@ -2386,9 +2385,8 @@ async fn rate_limit_switch_prompt_skips_non_codex_limit() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn rate_limit_switch_prompt_shows_once_per_session() {
|
||||
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let (mut chat, _, _) = make_chatwidget_manual(Some("gpt-5")).await;
|
||||
chat.auth_manager = codex_core::test_support::auth_manager_from_auth(auth);
|
||||
set_chatgpt_auth(&mut chat);
|
||||
|
||||
chat.on_rate_limit_snapshot(Some(snapshot(90.0)));
|
||||
assert!(
|
||||
@@ -2410,9 +2408,8 @@ async fn rate_limit_switch_prompt_shows_once_per_session() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn rate_limit_switch_prompt_respects_hidden_notice() {
|
||||
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let (mut chat, _, _) = make_chatwidget_manual(Some("gpt-5")).await;
|
||||
chat.auth_manager = codex_core::test_support::auth_manager_from_auth(auth);
|
||||
set_chatgpt_auth(&mut chat);
|
||||
chat.config.notices.hide_rate_limit_model_nudge = Some(true);
|
||||
|
||||
chat.on_rate_limit_snapshot(Some(snapshot(95.0)));
|
||||
@@ -2425,9 +2422,8 @@ async fn rate_limit_switch_prompt_respects_hidden_notice() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn rate_limit_switch_prompt_defers_until_task_complete() {
|
||||
let auth = CodexAuth::create_dummy_chatgpt_auth_for_testing();
|
||||
let (mut chat, _, _) = make_chatwidget_manual(Some("gpt-5")).await;
|
||||
chat.auth_manager = codex_core::test_support::auth_manager_from_auth(auth);
|
||||
set_chatgpt_auth(&mut chat);
|
||||
|
||||
chat.bottom_pane.set_task_running(true);
|
||||
chat.on_rate_limit_snapshot(Some(snapshot(90.0)));
|
||||
@@ -2447,9 +2443,7 @@ async fn rate_limit_switch_prompt_defers_until_task_complete() {
|
||||
#[tokio::test]
|
||||
async fn rate_limit_switch_prompt_popup_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5")).await;
|
||||
chat.auth_manager = codex_core::test_support::auth_manager_from_auth(
|
||||
CodexAuth::create_dummy_chatgpt_auth_for_testing(),
|
||||
);
|
||||
set_chatgpt_auth(&mut chat);
|
||||
|
||||
chat.on_rate_limit_snapshot(Some(snapshot(92.0)));
|
||||
chat.maybe_show_pending_rate_limit_prompt();
|
||||
@@ -3250,9 +3244,7 @@ async fn plan_implementation_popup_shows_after_new_plan_follows_steer() {
|
||||
#[tokio::test]
|
||||
async fn plan_implementation_popup_skips_when_rate_limit_prompt_pending() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5")).await;
|
||||
chat.auth_manager = codex_core::test_support::auth_manager_from_auth(
|
||||
CodexAuth::create_dummy_chatgpt_auth_for_testing(),
|
||||
);
|
||||
set_chatgpt_auth(&mut chat);
|
||||
chat.set_feature_enabled(Feature::CollaborationModes, true);
|
||||
let plan_mask =
|
||||
collaboration_modes::mask_for_kind(&chat.available_collaboration_modes, ModeKind::Plan)
|
||||
@@ -5633,15 +5625,13 @@ async fn collaboration_modes_defaults_to_code_on_startup() {
|
||||
cfg.model_provider.clone(),
|
||||
),
|
||||
);
|
||||
let auth_manager =
|
||||
codex_core::test_support::auth_manager_from_auth(CodexAuth::from_api_key("test"));
|
||||
let init = ChatWidgetInit {
|
||||
config: cfg,
|
||||
frame_requester: FrameRequester::test_dummy(),
|
||||
app_event_tx: AppEventSender::new(unbounded_channel::<AppEvent>().0),
|
||||
initial_user_message: None,
|
||||
enhanced_keys_supported: false,
|
||||
auth_manager,
|
||||
account: Some(codex_app_server_protocol::Account::ApiKey {}),
|
||||
available_models: thread_manager
|
||||
.get_models_manager()
|
||||
.try_list_models()
|
||||
@@ -5689,15 +5679,13 @@ async fn experimental_mode_plan_is_ignored_on_startup() {
|
||||
cfg.model_provider.clone(),
|
||||
),
|
||||
);
|
||||
let auth_manager =
|
||||
codex_core::test_support::auth_manager_from_auth(CodexAuth::from_api_key("test"));
|
||||
let init = ChatWidgetInit {
|
||||
config: cfg,
|
||||
frame_requester: FrameRequester::test_dummy(),
|
||||
app_event_tx: AppEventSender::new(unbounded_channel::<AppEvent>().0),
|
||||
initial_user_message: None,
|
||||
enhanced_keys_supported: false,
|
||||
auth_manager,
|
||||
account: Some(codex_app_server_protocol::Account::ApiKey {}),
|
||||
available_models: thread_manager
|
||||
.get_models_manager()
|
||||
.try_list_models()
|
||||
|
||||
@@ -65,6 +65,7 @@ use tracing_subscriber::EnvFilter;
|
||||
use tracing_subscriber::prelude::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
mod account_state;
|
||||
mod additional_dirs;
|
||||
mod app;
|
||||
mod app_backtrack;
|
||||
|
||||
@@ -42,7 +42,7 @@ use super::rate_limits::format_status_limit_summary;
|
||||
use super::rate_limits::render_status_limit_progress_bar;
|
||||
use crate::wrapping::RtOptions;
|
||||
use crate::wrapping::adaptive_wrap_lines;
|
||||
use codex_core::AuthManager;
|
||||
use codex_app_server_protocol::Account;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct StatusContextWindowData {
|
||||
@@ -80,7 +80,7 @@ struct StatusHistoryCell {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new_status_output(
|
||||
config: &Config,
|
||||
auth_manager: &AuthManager,
|
||||
account: Option<&Account>,
|
||||
token_info: Option<&TokenUsageInfo>,
|
||||
total_usage: &TokenUsage,
|
||||
session_id: &Option<ThreadId>,
|
||||
@@ -96,7 +96,7 @@ pub(crate) fn new_status_output(
|
||||
let snapshots = rate_limits.map(std::slice::from_ref).unwrap_or_default();
|
||||
new_status_output_with_rate_limits(
|
||||
config,
|
||||
auth_manager,
|
||||
account,
|
||||
token_info,
|
||||
total_usage,
|
||||
session_id,
|
||||
@@ -114,7 +114,7 @@ pub(crate) fn new_status_output(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new_status_output_with_rate_limits(
|
||||
config: &Config,
|
||||
auth_manager: &AuthManager,
|
||||
account: Option<&Account>,
|
||||
token_info: Option<&TokenUsageInfo>,
|
||||
total_usage: &TokenUsage,
|
||||
session_id: &Option<ThreadId>,
|
||||
@@ -130,7 +130,7 @@ pub(crate) fn new_status_output_with_rate_limits(
|
||||
let command = PlainHistoryCell::new(vec!["/status".magenta().into()]);
|
||||
let card = StatusHistoryCell::new(
|
||||
config,
|
||||
auth_manager,
|
||||
account,
|
||||
token_info,
|
||||
total_usage,
|
||||
session_id,
|
||||
@@ -151,7 +151,7 @@ impl StatusHistoryCell {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn new(
|
||||
config: &Config,
|
||||
auth_manager: &AuthManager,
|
||||
account: Option<&Account>,
|
||||
token_info: Option<&TokenUsageInfo>,
|
||||
total_usage: &TokenUsage,
|
||||
session_id: &Option<ThreadId>,
|
||||
@@ -227,7 +227,7 @@ impl StatusHistoryCell {
|
||||
};
|
||||
let agents_summary = compose_agents_summary(config);
|
||||
let model_provider = format_model_provider(config);
|
||||
let account = compose_account_display(auth_manager, plan_type);
|
||||
let account = compose_account_display(account, plan_type);
|
||||
let session_id = session_id.as_ref().map(std::string::ToString::to_string);
|
||||
let forked_from = forked_from.map(|id| id.to_string());
|
||||
let default_usage = TokenUsage::default();
|
||||
|
||||
@@ -2,8 +2,7 @@ use crate::exec_command::relativize_to_home;
|
||||
use crate::text_formatting;
|
||||
use chrono::DateTime;
|
||||
use chrono::Local;
|
||||
use codex_core::AuthManager;
|
||||
use codex_core::auth::AuthMode as CoreAuthMode;
|
||||
use codex_app_server_protocol::Account;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::project_doc::discover_project_doc_paths;
|
||||
use codex_protocol::account::PlanType;
|
||||
@@ -85,20 +84,22 @@ pub(crate) fn compose_agents_summary(config: &Config) -> String {
|
||||
}
|
||||
|
||||
pub(crate) fn compose_account_display(
|
||||
auth_manager: &AuthManager,
|
||||
plan: Option<PlanType>,
|
||||
account: Option<&Account>,
|
||||
fallback_plan: Option<PlanType>,
|
||||
) -> Option<StatusAccountDisplay> {
|
||||
let auth = auth_manager.auth_cached()?;
|
||||
|
||||
match auth.auth_mode() {
|
||||
CoreAuthMode::ApiKey => Some(StatusAccountDisplay::ApiKey),
|
||||
CoreAuthMode::Chatgpt => {
|
||||
let email = auth.get_account_email();
|
||||
let plan = plan
|
||||
match account {
|
||||
Some(Account::ApiKey {}) => Some(StatusAccountDisplay::ApiKey),
|
||||
Some(Account::Chatgpt { email, plan_type }) => {
|
||||
let plan = fallback_plan
|
||||
.or(Some(*plan_type))
|
||||
.map(|plan_type| title_case(format!("{plan_type:?}").as_str()))
|
||||
.or_else(|| Some("Unknown".to_string()));
|
||||
Some(StatusAccountDisplay::ChatGpt { email, plan })
|
||||
Some(StatusAccountDisplay::ChatGpt {
|
||||
email: Some(email.clone()),
|
||||
plan,
|
||||
})
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ use crate::history_cell::HistoryCell;
|
||||
use chrono::Duration as ChronoDuration;
|
||||
use chrono::TimeZone;
|
||||
use chrono::Utc;
|
||||
use codex_core::AuthManager;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::config::ConfigBuilder;
|
||||
use codex_protocol::ThreadId;
|
||||
@@ -31,14 +30,6 @@ async fn test_config(temp_home: &TempDir) -> Config {
|
||||
.expect("load config")
|
||||
}
|
||||
|
||||
fn test_auth_manager(config: &Config) -> AuthManager {
|
||||
AuthManager::new(
|
||||
config.codex_home.clone(),
|
||||
false,
|
||||
config.cli_auth_credentials_store_mode,
|
||||
)
|
||||
}
|
||||
|
||||
fn token_info_for(model_slug: &str, config: &Config, usage: &TokenUsage) -> TokenUsageInfo {
|
||||
let context_window =
|
||||
codex_core::test_support::construct_model_info_offline(model_slug, config).context_window;
|
||||
@@ -111,7 +102,6 @@ async fn status_snapshot_includes_reasoning_details() {
|
||||
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
|
||||
let auth_manager = test_auth_manager(&config);
|
||||
let usage = TokenUsage {
|
||||
input_tokens: 1_200,
|
||||
cached_input_tokens: 200,
|
||||
@@ -148,7 +138,7 @@ async fn status_snapshot_includes_reasoning_details() {
|
||||
let reasoning_effort_override = Some(Some(ReasoningEffort::High));
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
None,
|
||||
Some(&token_info),
|
||||
&usage,
|
||||
&None,
|
||||
@@ -195,7 +185,6 @@ async fn status_permissions_non_default_workspace_write_is_custom() {
|
||||
.expect("set sandbox policy");
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
|
||||
let auth_manager = test_auth_manager(&config);
|
||||
let usage = TokenUsage::default();
|
||||
let captured_at = chrono::Local
|
||||
.with_ymd_and_hms(2024, 1, 2, 3, 4, 5)
|
||||
@@ -205,7 +194,7 @@ async fn status_permissions_non_default_workspace_write_is_custom() {
|
||||
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
None,
|
||||
None,
|
||||
&usage,
|
||||
&None,
|
||||
@@ -244,7 +233,6 @@ async fn status_snapshot_includes_forked_from() {
|
||||
config.model_provider_id = "openai".to_string();
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
|
||||
let auth_manager = test_auth_manager(&config);
|
||||
let usage = TokenUsage {
|
||||
input_tokens: 800,
|
||||
cached_input_tokens: 0,
|
||||
@@ -267,7 +255,7 @@ async fn status_snapshot_includes_forked_from() {
|
||||
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
None,
|
||||
Some(&token_info),
|
||||
&usage,
|
||||
&Some(session_id),
|
||||
@@ -298,7 +286,6 @@ async fn status_snapshot_includes_monthly_limit() {
|
||||
config.model_provider_id = "openai".to_string();
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
|
||||
let auth_manager = test_auth_manager(&config);
|
||||
let usage = TokenUsage {
|
||||
input_tokens: 800,
|
||||
cached_input_tokens: 0,
|
||||
@@ -329,7 +316,7 @@ async fn status_snapshot_includes_monthly_limit() {
|
||||
let token_info = token_info_for(&model_slug, &config, &usage);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
None,
|
||||
Some(&token_info),
|
||||
&usage,
|
||||
&None,
|
||||
@@ -356,7 +343,6 @@ async fn status_snapshot_includes_monthly_limit() {
|
||||
async fn status_snapshot_shows_unlimited_credits() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let config = test_config(&temp_home).await;
|
||||
let auth_manager = test_auth_manager(&config);
|
||||
let usage = TokenUsage::default();
|
||||
let captured_at = chrono::Local
|
||||
.with_ymd_and_hms(2024, 2, 3, 4, 5, 6)
|
||||
@@ -379,7 +365,7 @@ async fn status_snapshot_shows_unlimited_credits() {
|
||||
let token_info = token_info_for(&model_slug, &config, &usage);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
None,
|
||||
Some(&token_info),
|
||||
&usage,
|
||||
&None,
|
||||
@@ -405,7 +391,6 @@ async fn status_snapshot_shows_unlimited_credits() {
|
||||
async fn status_snapshot_shows_positive_credits() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let config = test_config(&temp_home).await;
|
||||
let auth_manager = test_auth_manager(&config);
|
||||
let usage = TokenUsage::default();
|
||||
let captured_at = chrono::Local
|
||||
.with_ymd_and_hms(2024, 3, 4, 5, 6, 7)
|
||||
@@ -428,7 +413,7 @@ async fn status_snapshot_shows_positive_credits() {
|
||||
let token_info = token_info_for(&model_slug, &config, &usage);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
None,
|
||||
Some(&token_info),
|
||||
&usage,
|
||||
&None,
|
||||
@@ -454,7 +439,6 @@ async fn status_snapshot_shows_positive_credits() {
|
||||
async fn status_snapshot_hides_zero_credits() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let config = test_config(&temp_home).await;
|
||||
let auth_manager = test_auth_manager(&config);
|
||||
let usage = TokenUsage::default();
|
||||
let captured_at = chrono::Local
|
||||
.with_ymd_and_hms(2024, 4, 5, 6, 7, 8)
|
||||
@@ -477,7 +461,7 @@ async fn status_snapshot_hides_zero_credits() {
|
||||
let token_info = token_info_for(&model_slug, &config, &usage);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
None,
|
||||
Some(&token_info),
|
||||
&usage,
|
||||
&None,
|
||||
@@ -501,7 +485,6 @@ async fn status_snapshot_hides_zero_credits() {
|
||||
async fn status_snapshot_hides_when_has_no_credits_flag() {
|
||||
let temp_home = TempDir::new().expect("temp home");
|
||||
let config = test_config(&temp_home).await;
|
||||
let auth_manager = test_auth_manager(&config);
|
||||
let usage = TokenUsage::default();
|
||||
let captured_at = chrono::Local
|
||||
.with_ymd_and_hms(2024, 5, 6, 7, 8, 9)
|
||||
@@ -524,7 +507,7 @@ async fn status_snapshot_hides_when_has_no_credits_flag() {
|
||||
let token_info = token_info_for(&model_slug, &config, &usage);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
None,
|
||||
Some(&token_info),
|
||||
&usage,
|
||||
&None,
|
||||
@@ -551,7 +534,6 @@ async fn status_card_token_usage_excludes_cached_tokens() {
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
|
||||
let auth_manager = test_auth_manager(&config);
|
||||
let usage = TokenUsage {
|
||||
input_tokens: 1_200,
|
||||
cached_input_tokens: 200,
|
||||
@@ -569,7 +551,7 @@ async fn status_card_token_usage_excludes_cached_tokens() {
|
||||
let token_info = token_info_for(&model_slug, &config, &usage);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
None,
|
||||
Some(&token_info),
|
||||
&usage,
|
||||
&None,
|
||||
@@ -599,7 +581,6 @@ async fn status_snapshot_truncates_in_narrow_terminal() {
|
||||
config.model_reasoning_summary = Some(ReasoningSummary::Detailed);
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
|
||||
let auth_manager = test_auth_manager(&config);
|
||||
let usage = TokenUsage {
|
||||
input_tokens: 1_200,
|
||||
cached_input_tokens: 200,
|
||||
@@ -631,7 +612,7 @@ async fn status_snapshot_truncates_in_narrow_terminal() {
|
||||
let reasoning_effort_override = Some(Some(ReasoningEffort::High));
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
None,
|
||||
Some(&token_info),
|
||||
&usage,
|
||||
&None,
|
||||
@@ -662,7 +643,6 @@ async fn status_snapshot_shows_missing_limits_message() {
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
|
||||
let auth_manager = test_auth_manager(&config);
|
||||
let usage = TokenUsage {
|
||||
input_tokens: 500,
|
||||
cached_input_tokens: 0,
|
||||
@@ -680,7 +660,7 @@ async fn status_snapshot_shows_missing_limits_message() {
|
||||
let token_info = token_info_for(&model_slug, &config, &usage);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
None,
|
||||
Some(&token_info),
|
||||
&usage,
|
||||
&None,
|
||||
@@ -710,7 +690,6 @@ async fn status_snapshot_includes_credits_and_limits() {
|
||||
config.model = Some("gpt-5.1-codex".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
|
||||
let auth_manager = test_auth_manager(&config);
|
||||
let usage = TokenUsage {
|
||||
input_tokens: 1_500,
|
||||
cached_input_tokens: 100,
|
||||
@@ -749,7 +728,7 @@ async fn status_snapshot_includes_credits_and_limits() {
|
||||
let token_info = token_info_for(&model_slug, &config, &usage);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
None,
|
||||
Some(&token_info),
|
||||
&usage,
|
||||
&None,
|
||||
@@ -779,7 +758,6 @@ async fn status_snapshot_shows_empty_limits_message() {
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
|
||||
let auth_manager = test_auth_manager(&config);
|
||||
let usage = TokenUsage {
|
||||
input_tokens: 500,
|
||||
cached_input_tokens: 0,
|
||||
@@ -806,7 +784,7 @@ async fn status_snapshot_shows_empty_limits_message() {
|
||||
let token_info = token_info_for(&model_slug, &config, &usage);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
None,
|
||||
Some(&token_info),
|
||||
&usage,
|
||||
&None,
|
||||
@@ -836,7 +814,6 @@ async fn status_snapshot_shows_stale_limits_message() {
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
|
||||
let auth_manager = test_auth_manager(&config);
|
||||
let usage = TokenUsage {
|
||||
input_tokens: 1_200,
|
||||
cached_input_tokens: 200,
|
||||
@@ -872,7 +849,7 @@ async fn status_snapshot_shows_stale_limits_message() {
|
||||
let token_info = token_info_for(&model_slug, &config, &usage);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
None,
|
||||
Some(&token_info),
|
||||
&usage,
|
||||
&None,
|
||||
@@ -902,7 +879,6 @@ async fn status_snapshot_cached_limits_hide_credits_without_flag() {
|
||||
config.model = Some("gpt-5.1-codex".to_string());
|
||||
config.cwd = PathBuf::from("/workspace/tests");
|
||||
|
||||
let auth_manager = test_auth_manager(&config);
|
||||
let usage = TokenUsage {
|
||||
input_tokens: 900,
|
||||
cached_input_tokens: 200,
|
||||
@@ -942,7 +918,7 @@ async fn status_snapshot_cached_limits_hide_credits_without_flag() {
|
||||
let token_info = token_info_for(&model_slug, &config, &usage);
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
None,
|
||||
Some(&token_info),
|
||||
&usage,
|
||||
&None,
|
||||
@@ -971,7 +947,6 @@ async fn status_context_window_uses_last_usage() {
|
||||
let mut config = test_config(&temp_home).await;
|
||||
config.model_context_window = Some(272_000);
|
||||
|
||||
let auth_manager = test_auth_manager(&config);
|
||||
let total_usage = TokenUsage {
|
||||
input_tokens: 12_800,
|
||||
cached_input_tokens: 0,
|
||||
@@ -1000,7 +975,7 @@ async fn status_context_window_uses_last_usage() {
|
||||
};
|
||||
let composite = new_status_output(
|
||||
&config,
|
||||
&auth_manager,
|
||||
None,
|
||||
Some(&token_info),
|
||||
&total_usage,
|
||||
&None,
|
||||
|
||||
Reference in New Issue
Block a user