mirror of
https://github.com/openai/codex.git
synced 2026-10-23 18:08:45 +00:00
codex: attribute warning lifecycle origins
This commit is contained in:
@@ -5208,6 +5208,15 @@
|
||||
"description": "Concise warning message for the user.",
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/WarningSource"
|
||||
}
|
||||
],
|
||||
"default": "runtime",
|
||||
"description": "Lifecycle source that produced the warning."
|
||||
},
|
||||
"threadId": {
|
||||
"description": "Optional thread target when the warning applies to a specific thread.",
|
||||
"type": [
|
||||
@@ -5221,6 +5230,31 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"WarningSource": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Warning without a more specific startup origin.",
|
||||
"enum": [
|
||||
"runtime"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Warning loaded with configuration before constructing the thread.",
|
||||
"enum": [
|
||||
"config"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Warning discovered while constructing a thread before its first turn.",
|
||||
"enum": [
|
||||
"threadStartup"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"WebSearchAction": {
|
||||
"oneOf": [
|
||||
{
|
||||
|
||||
+34
@@ -19354,6 +19354,15 @@
|
||||
"description": "Concise warning message for the user.",
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/WarningSource"
|
||||
}
|
||||
],
|
||||
"default": "runtime",
|
||||
"description": "Lifecycle source that produced the warning."
|
||||
},
|
||||
"threadId": {
|
||||
"description": "Optional thread target when the warning applies to a specific thread.",
|
||||
"type": [
|
||||
@@ -19368,6 +19377,31 @@
|
||||
"title": "WarningNotification",
|
||||
"type": "object"
|
||||
},
|
||||
"WarningSource": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Warning without a more specific startup origin.",
|
||||
"enum": [
|
||||
"runtime"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Warning loaded with configuration before constructing the thread.",
|
||||
"enum": [
|
||||
"config"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Warning discovered while constructing a thread before its first turn.",
|
||||
"enum": [
|
||||
"threadStartup"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"WebSearchAction": {
|
||||
"oneOf": [
|
||||
{
|
||||
|
||||
+34
@@ -17171,6 +17171,15 @@
|
||||
"description": "Concise warning message for the user.",
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/WarningSource"
|
||||
}
|
||||
],
|
||||
"default": "runtime",
|
||||
"description": "Lifecycle source that produced the warning."
|
||||
},
|
||||
"threadId": {
|
||||
"description": "Optional thread target when the warning applies to a specific thread.",
|
||||
"type": [
|
||||
@@ -17185,6 +17194,31 @@
|
||||
"title": "WarningNotification",
|
||||
"type": "object"
|
||||
},
|
||||
"WarningSource": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Warning without a more specific startup origin.",
|
||||
"enum": [
|
||||
"runtime"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Warning loaded with configuration before constructing the thread.",
|
||||
"enum": [
|
||||
"config"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Warning discovered while constructing a thread before its first turn.",
|
||||
"enum": [
|
||||
"threadStartup"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"WebSearchAction": {
|
||||
"oneOf": [
|
||||
{
|
||||
|
||||
@@ -1,10 +1,46 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"definitions": {
|
||||
"WarningSource": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Warning without a more specific startup origin.",
|
||||
"enum": [
|
||||
"runtime"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Warning loaded with configuration before constructing the thread.",
|
||||
"enum": [
|
||||
"config"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"description": "Warning discovered while constructing a thread before its first turn.",
|
||||
"enum": [
|
||||
"threadStartup"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"message": {
|
||||
"description": "Concise warning message for the user.",
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/WarningSource"
|
||||
}
|
||||
],
|
||||
"default": "runtime",
|
||||
"description": "Lifecycle source that produced the warning."
|
||||
},
|
||||
"threadId": {
|
||||
"description": "Optional thread target when the warning applies to a specific thread.",
|
||||
"type": [
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { WarningSource } from "./WarningSource";
|
||||
|
||||
export type WarningNotification = {
|
||||
/**
|
||||
@@ -10,4 +11,8 @@ threadId: string | null,
|
||||
/**
|
||||
* Concise warning message for the user.
|
||||
*/
|
||||
message: string, };
|
||||
message: string,
|
||||
/**
|
||||
* Lifecycle source that produced the warning.
|
||||
*/
|
||||
source: WarningSource, };
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type WarningSource = "runtime" | "config" | "threadStartup";
|
||||
@@ -469,6 +469,7 @@ export type { TurnSteerResponse } from "./TurnSteerResponse";
|
||||
export type { TurnsPage } from "./TurnsPage";
|
||||
export type { UserInput } from "./UserInput";
|
||||
export type { WarningNotification } from "./WarningNotification";
|
||||
export type { WarningSource } from "./WarningSource";
|
||||
export type { WebSearchAction } from "./WebSearchAction";
|
||||
export type { WindowsSandboxReadiness } from "./WindowsSandboxReadiness";
|
||||
export type { WindowsSandboxReadinessResponse } from "./WindowsSandboxReadinessResponse";
|
||||
|
||||
@@ -23,6 +23,22 @@ pub struct WarningNotification {
|
||||
pub thread_id: Option<String>,
|
||||
/// Concise warning message for the user.
|
||||
pub message: String,
|
||||
/// Lifecycle source that produced the warning.
|
||||
#[serde(default)]
|
||||
pub source: WarningSource,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Default, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(rename_all = "camelCase", export_to = "v2/")]
|
||||
pub enum WarningSource {
|
||||
/// Warning without a more specific startup origin.
|
||||
#[default]
|
||||
Runtime,
|
||||
/// Warning loaded with configuration before constructing the thread.
|
||||
Config,
|
||||
/// Warning discovered while constructing a thread before its first turn.
|
||||
ThreadStartup,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
|
||||
@@ -1243,7 +1243,7 @@ Thread realtime uses a separate thread-scoped notification surface. `thread/real
|
||||
|
||||
Recoverable configuration and initialization warnings use the existing `configWarning` notification: `{ summary, details?, path?, range? }`. App-server may emit it during initialization for config parsing and related setup diagnostics.
|
||||
|
||||
Generic runtime warnings use the `warning` notification: `{ threadId?, message }`. App-server emits this for non-fatal warnings from the core event stream, including cases where not all enabled skills are included in the model-visible skills list for a session.
|
||||
Thread-scoped warnings use the `warning` notification: `{ threadId?, message, source }`. The `source` is `config` for warnings preloaded with configuration, `threadStartup` for warnings discovered while constructing a thread, such as unreadable project instruction files, and `runtime` for warnings without a more specific startup origin, including cases where not all enabled skills are included in the model-visible skills list for a session.
|
||||
|
||||
### Notification opt-out
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ use codex_app_server_protocol::TurnPlanUpdatedNotification;
|
||||
use codex_app_server_protocol::TurnStartedNotification;
|
||||
use codex_app_server_protocol::TurnStatus;
|
||||
use codex_app_server_protocol::WarningNotification;
|
||||
use codex_app_server_protocol::WarningSource;
|
||||
use codex_app_server_protocol::build_item_from_guardian_event;
|
||||
use codex_app_server_protocol::guardian_auto_approval_review_notification;
|
||||
use codex_app_server_protocol::item_event_to_server_notification;
|
||||
@@ -226,6 +227,27 @@ pub(crate) async fn apply_bespoke_event_handling(
|
||||
let notification = WarningNotification {
|
||||
thread_id: Some(conversation_id.to_string()),
|
||||
message: warning_event.message,
|
||||
source: WarningSource::Runtime,
|
||||
};
|
||||
outgoing
|
||||
.send_server_notification(ServerNotification::Warning(notification))
|
||||
.await;
|
||||
}
|
||||
EventMsg::ConfigWarning(warning_event) => {
|
||||
let notification = WarningNotification {
|
||||
thread_id: Some(conversation_id.to_string()),
|
||||
message: warning_event.message,
|
||||
source: WarningSource::Config,
|
||||
};
|
||||
outgoing
|
||||
.send_server_notification(ServerNotification::Warning(notification))
|
||||
.await;
|
||||
}
|
||||
EventMsg::ThreadStartupWarning(warning_event) => {
|
||||
let notification = WarningNotification {
|
||||
thread_id: Some(conversation_id.to_string()),
|
||||
message: warning_event.message,
|
||||
source: WarningSource::ThreadStartup,
|
||||
};
|
||||
outgoing
|
||||
.send_server_notification(ServerNotification::Warning(notification))
|
||||
|
||||
@@ -21,6 +21,8 @@ use codex_app_server_protocol::ThreadStartedNotification;
|
||||
use codex_app_server_protocol::ThreadStatus;
|
||||
use codex_app_server_protocol::ThreadStatusChangedNotification;
|
||||
use codex_app_server_protocol::TurnEnvironmentParams;
|
||||
use codex_app_server_protocol::WarningNotification;
|
||||
use codex_app_server_protocol::WarningSource;
|
||||
use codex_config::loader::project_trust_key;
|
||||
use codex_config::types::AuthCredentialsStoreMode;
|
||||
use codex_core::config::set_project_trust_level;
|
||||
@@ -399,6 +401,103 @@ async fn thread_start_response_excludes_empty_project_instruction_source() -> Re
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn thread_start_warning_identifies_thread_startup_source() -> Result<()> {
|
||||
let server = create_mock_responses_server_repeating_assistant("Done").await;
|
||||
let codex_home = TempDir::new()?;
|
||||
create_config_toml_without_approval_policy(codex_home.path(), &server.uri())?;
|
||||
let workspace = TempDir::new()?;
|
||||
let project_agents_path = workspace.path().join("AGENTS.md");
|
||||
std::fs::write(&project_agents_path, b"project\xFFinstructions")?;
|
||||
|
||||
let mut mcp = TestAppServer::new(codex_home.path()).await?;
|
||||
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
|
||||
|
||||
let request_id = mcp
|
||||
.send_thread_start_request(ThreadStartParams {
|
||||
cwd: Some(workspace.path().display().to_string()),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
let response: JSONRPCResponse = timeout(
|
||||
DEFAULT_READ_TIMEOUT,
|
||||
mcp.read_stream_until_response_message(RequestId::Integer(request_id)),
|
||||
)
|
||||
.await??;
|
||||
let ThreadStartResponse { thread, .. } = to_response::<ThreadStartResponse>(response)?;
|
||||
|
||||
let notification = timeout(
|
||||
DEFAULT_READ_TIMEOUT,
|
||||
mcp.read_stream_until_notification_message("warning"),
|
||||
)
|
||||
.await??;
|
||||
let warning: WarningNotification =
|
||||
serde_json::from_value(notification.params.expect("thread startup warning params"))?;
|
||||
|
||||
assert_eq!(
|
||||
warning,
|
||||
WarningNotification {
|
||||
thread_id: Some(thread.id),
|
||||
message: format!(
|
||||
"Project AGENTS.md instructions from `{}` contain invalid UTF-8: invalid utf-8 sequence of 1 bytes from index 7. Invalid byte sequences were replaced.",
|
||||
project_agents_path.display()
|
||||
),
|
||||
source: WarningSource::ThreadStartup,
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn thread_start_warning_identifies_config_source() -> Result<()> {
|
||||
let server = create_mock_responses_server_repeating_assistant("Done").await;
|
||||
let codex_home = TempDir::new()?;
|
||||
create_config_toml_without_approval_policy(codex_home.path(), &server.uri())?;
|
||||
let global_agents_path = codex_home.path().join("AGENTS.md");
|
||||
std::fs::write(&global_agents_path, b"global\xFFinstructions")?;
|
||||
let global_agents_path = global_agents_path.canonicalize()?;
|
||||
let workspace = TempDir::new()?;
|
||||
|
||||
let mut mcp = TestAppServer::new(codex_home.path()).await?;
|
||||
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
|
||||
|
||||
let request_id = mcp
|
||||
.send_thread_start_request(ThreadStartParams {
|
||||
cwd: Some(workspace.path().display().to_string()),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
let response: JSONRPCResponse = timeout(
|
||||
DEFAULT_READ_TIMEOUT,
|
||||
mcp.read_stream_until_response_message(RequestId::Integer(request_id)),
|
||||
)
|
||||
.await??;
|
||||
let ThreadStartResponse { thread, .. } = to_response::<ThreadStartResponse>(response)?;
|
||||
|
||||
let notification = timeout(
|
||||
DEFAULT_READ_TIMEOUT,
|
||||
mcp.read_stream_until_notification_message("warning"),
|
||||
)
|
||||
.await??;
|
||||
let warning: WarningNotification =
|
||||
serde_json::from_value(notification.params.expect("configuration warning params"))?;
|
||||
|
||||
assert_eq!(
|
||||
warning,
|
||||
WarningNotification {
|
||||
thread_id: Some(thread.id),
|
||||
message: format!(
|
||||
"Global AGENTS.md instructions from `{}` contain invalid UTF-8: invalid utf-8 sequence of 1 bytes from index 6. Invalid byte sequences were replaced.",
|
||||
global_agents_path.display()
|
||||
),
|
||||
source: WarningSource::Config,
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn thread_start_without_selected_environment_excludes_instruction_sources() -> Result<()> {
|
||||
let server = create_mock_responses_server_repeating_assistant("Done").await;
|
||||
|
||||
@@ -62,6 +62,7 @@ use codex_app_server_protocol::TurnStatus;
|
||||
use codex_app_server_protocol::TurnSteerParams;
|
||||
use codex_app_server_protocol::UserInput as V2UserInput;
|
||||
use codex_app_server_protocol::WarningNotification;
|
||||
use codex_app_server_protocol::WarningSource;
|
||||
use codex_config::config_toml::ConfigToml;
|
||||
use codex_core::personality_migration::PERSONALITY_MIGRATION_FILENAME;
|
||||
use codex_core::test_support::all_model_presets;
|
||||
@@ -643,6 +644,7 @@ async fn turn_start_emits_thread_scoped_warning_notification_for_trimmed_skills(
|
||||
let warning: WarningNotification =
|
||||
serde_json::from_value(params).expect("deserialize warning notification");
|
||||
assert_eq!(warning.thread_id.as_deref(), Some(thread.id.as_str()));
|
||||
assert_eq!(warning.source, WarningSource::Runtime);
|
||||
assert_eq!(
|
||||
warning.message,
|
||||
"Exceeded skills context budget of 2%. All skill descriptions were removed and 7 additional skills were not included in the model-visible skills list."
|
||||
|
||||
@@ -476,7 +476,7 @@ impl Codex {
|
||||
|
||||
async fn spawn_internal(args: CodexSpawnArgs) -> CodexResult<CodexSpawnOk> {
|
||||
let CodexSpawnArgs {
|
||||
mut config,
|
||||
config,
|
||||
installation_id,
|
||||
auth_manager,
|
||||
models_manager,
|
||||
@@ -517,7 +517,6 @@ impl Codex {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
config.startup_warnings.extend(user_instruction_warnings);
|
||||
|
||||
let exec_policy = if crate::guardian::is_guardian_reviewer_source(&session_source) {
|
||||
// Guardian review should rely on the built-in shell safety checks,
|
||||
@@ -658,6 +657,14 @@ impl Codex {
|
||||
error!("Failed to create session: {e:#}");
|
||||
map_session_init_error(&e, &config.codex_home)
|
||||
})?;
|
||||
for message in user_instruction_warnings {
|
||||
session
|
||||
.send_event_raw(Event {
|
||||
id: INITIAL_SUBMIT_ID.to_owned(),
|
||||
msg: EventMsg::ThreadStartupWarning(WarningEvent { message }),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
let thread_id = session.thread_id;
|
||||
|
||||
// This task will run until Op::Shutdown is received.
|
||||
|
||||
@@ -711,7 +711,7 @@ impl Session {
|
||||
for message in &config.startup_warnings {
|
||||
post_session_configured_events.push(Event {
|
||||
id: "".to_owned(),
|
||||
msg: EventMsg::Warning(WarningEvent {
|
||||
msg: EventMsg::ConfigWarning(WarningEvent {
|
||||
message: message.clone(),
|
||||
}),
|
||||
});
|
||||
@@ -732,7 +732,7 @@ impl Session {
|
||||
if config.permissions.approval_policy.value() == AskForApproval::OnFailure {
|
||||
post_session_configured_events.push(Event {
|
||||
id: "".to_owned(),
|
||||
msg: EventMsg::Warning(WarningEvent {
|
||||
msg: EventMsg::ThreadStartupWarning(WarningEvent {
|
||||
message: "`on-failure` approval policy is deprecated and will be removed in a future release. Use `on-request` for interactive approvals or `never` for non-interactive runs.".to_string(),
|
||||
}),
|
||||
});
|
||||
@@ -922,7 +922,7 @@ impl Session {
|
||||
for warning in hooks.startup_warnings() {
|
||||
post_session_configured_events.push(Event {
|
||||
id: INITIAL_SUBMIT_ID.to_owned(),
|
||||
msg: EventMsg::Warning(WarningEvent {
|
||||
msg: EventMsg::ThreadStartupWarning(WarningEvent {
|
||||
message: warning.clone(),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1365,6 +1365,8 @@ pub(super) fn realtime_text_for_event(msg: &EventMsg) -> Option<String> {
|
||||
},
|
||||
EventMsg::Error(_)
|
||||
| EventMsg::Warning(_)
|
||||
| EventMsg::ConfigWarning(_)
|
||||
| EventMsg::ThreadStartupWarning(_)
|
||||
| EventMsg::GuardianWarning(_)
|
||||
| EventMsg::RealtimeConversationStarted(_)
|
||||
| EventMsg::RealtimeConversationSdp(_)
|
||||
|
||||
@@ -474,7 +474,7 @@ async fn global_loading_warning_surfaces_during_thread_creation() -> Result<()>
|
||||
let mut builder = test_codex().with_home(home);
|
||||
let test = builder.build(&server).await?;
|
||||
let warning = wait_for_event_match(&test.codex, |event| match event {
|
||||
EventMsg::Warning(warning)
|
||||
EventMsg::ConfigWarning(warning)
|
||||
if warning
|
||||
.message
|
||||
.contains(source.as_path().display().to_string().as_str()) =>
|
||||
@@ -500,6 +500,38 @@ async fn global_loading_warning_surfaces_during_thread_creation() -> Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn project_loading_warning_is_attributed_to_thread_startup() -> Result<()> {
|
||||
let server = responses::start_mock_server().await;
|
||||
let mut builder = test_codex().with_workspace_setup(|cwd, fs| async move {
|
||||
fs.write_file(
|
||||
&cwd.join(GLOBAL_AGENTS_FILENAME),
|
||||
b"project\xFFinstructions".to_vec(),
|
||||
/*sandbox*/ None,
|
||||
)
|
||||
.await?;
|
||||
Ok::<(), anyhow::Error>(())
|
||||
});
|
||||
let test = builder.build_with_remote_env(&server).await?;
|
||||
let source = test.config.cwd.join(GLOBAL_AGENTS_FILENAME);
|
||||
|
||||
let warning = wait_for_event_match(&test.codex, |event| match event {
|
||||
EventMsg::ThreadStartupWarning(warning)
|
||||
if warning
|
||||
.message
|
||||
.contains(source.as_path().display().to_string().as_str()) =>
|
||||
{
|
||||
Some(warning.message.clone())
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.await;
|
||||
|
||||
assert!(warning.contains("invalid UTF-8"), "{warning}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO(anp): Align cold-resume instruction sources with the historical instructions replayed to
|
||||
// the model so the API source list and model-visible context describe the same files.
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
|
||||
@@ -51,8 +51,11 @@ async fn emits_warning_when_unstable_features_enabled_via_config() {
|
||||
.await
|
||||
.expect("spawn conversation");
|
||||
|
||||
let warning = wait_for_event(&conversation, |ev| matches!(ev, EventMsg::Warning(_))).await;
|
||||
let EventMsg::Warning(WarningEvent { message }) = warning else {
|
||||
let warning = wait_for_event(&conversation, |ev| {
|
||||
matches!(ev, EventMsg::ThreadStartupWarning(_))
|
||||
})
|
||||
.await;
|
||||
let EventMsg::ThreadStartupWarning(WarningEvent { message }) = warning else {
|
||||
panic!("expected warning event");
|
||||
};
|
||||
assert!(message.contains("child_agents_md"));
|
||||
@@ -99,7 +102,9 @@ async fn suppresses_warning_when_configured() {
|
||||
|
||||
let warning = timeout(
|
||||
Duration::from_millis(150),
|
||||
wait_for_event(&conversation, |ev| matches!(ev, EventMsg::Warning(_))),
|
||||
wait_for_event(&conversation, |ev| {
|
||||
matches!(ev, EventMsg::ThreadStartupWarning(_))
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
assert!(warning.is_err());
|
||||
|
||||
@@ -1282,7 +1282,7 @@ pub fn unstable_features_warning_event(
|
||||
);
|
||||
Some(Event {
|
||||
id: String::new(),
|
||||
msg: EventMsg::Warning(WarningEvent { message }),
|
||||
msg: EventMsg::ThreadStartupWarning(WarningEvent { message }),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -705,7 +705,7 @@ fn unstable_warning_event_only_mentions_enabled_under_development_features() {
|
||||
)
|
||||
.expect("warning event");
|
||||
|
||||
let EventMsg::Warning(WarningEvent { message }) = warning.msg else {
|
||||
let EventMsg::ThreadStartupWarning(WarningEvent { message }) = warning.msg else {
|
||||
panic!("expected warning event");
|
||||
};
|
||||
assert!(message.contains("child_agents_md"));
|
||||
|
||||
@@ -263,6 +263,8 @@ async fn run_codex_tool_session_inner(
|
||||
break;
|
||||
}
|
||||
EventMsg::Warning(_)
|
||||
| EventMsg::ConfigWarning(_)
|
||||
| EventMsg::ThreadStartupWarning(_)
|
||||
| EventMsg::GuardianWarning(_)
|
||||
| EventMsg::ModelVerification(_)
|
||||
| EventMsg::TurnModerationMetadata(_) => {
|
||||
|
||||
@@ -1167,6 +1167,12 @@ pub enum EventMsg {
|
||||
/// indicates the turn continued but the user should still be notified.
|
||||
Warning(WarningEvent),
|
||||
|
||||
/// Warning loaded with configuration before constructing the thread.
|
||||
ConfigWarning(WarningEvent),
|
||||
|
||||
/// Warning discovered while constructing a thread, before its first turn.
|
||||
ThreadStartupWarning(WarningEvent),
|
||||
|
||||
/// Warning issued by the guardian automatic approval reviewer.
|
||||
GuardianWarning(WarningEvent),
|
||||
|
||||
|
||||
@@ -225,6 +225,8 @@ pub(crate) fn tool_runtime_trace_event(event: &EventMsg) -> Option<ToolRuntimeTr
|
||||
}),
|
||||
EventMsg::Error(_)
|
||||
| EventMsg::Warning(_)
|
||||
| EventMsg::ConfigWarning(_)
|
||||
| EventMsg::ThreadStartupWarning(_)
|
||||
| EventMsg::GuardianWarning(_)
|
||||
| EventMsg::RealtimeConversationStarted(_)
|
||||
| EventMsg::RealtimeConversationRealtime(_)
|
||||
@@ -298,6 +300,8 @@ pub(crate) fn wrapped_protocol_event_type(event: &EventMsg) -> Option<&'static s
|
||||
EventMsg::ThreadRolledBack(_) => Some("thread_rolled_back"),
|
||||
EventMsg::Error(_) => Some("error"),
|
||||
EventMsg::Warning(_) => Some("warning"),
|
||||
EventMsg::ConfigWarning(_) => Some("config_warning"),
|
||||
EventMsg::ThreadStartupWarning(_) => Some("thread_startup_warning"),
|
||||
EventMsg::ShutdownComplete => Some("shutdown_complete"),
|
||||
EventMsg::GuardianWarning(_)
|
||||
| EventMsg::RealtimeConversationStarted(_)
|
||||
|
||||
@@ -111,6 +111,8 @@ pub fn should_persist_event_msg(ev: &EventMsg) -> bool {
|
||||
| EventMsg::DynamicToolCallRequest(_)
|
||||
| EventMsg::DynamicToolCallResponse(_)
|
||||
| EventMsg::Warning(_)
|
||||
| EventMsg::ConfigWarning(_)
|
||||
| EventMsg::ThreadStartupWarning(_)
|
||||
| EventMsg::GuardianWarning(_)
|
||||
| EventMsg::RealtimeConversationStarted(_)
|
||||
| EventMsg::RealtimeConversationSdp(_)
|
||||
|
||||
@@ -235,6 +235,7 @@ mod tests {
|
||||
let notification = ServerNotification::Warning(WarningNotification {
|
||||
thread_id: None,
|
||||
message: "warning".to_string(),
|
||||
source: codex_app_server_protocol::WarningSource::Runtime,
|
||||
});
|
||||
|
||||
let target = server_notification_thread_target(¬ification);
|
||||
@@ -248,6 +249,7 @@ mod tests {
|
||||
let notification = ServerNotification::Warning(WarningNotification {
|
||||
thread_id: Some(thread_id.to_string()),
|
||||
message: "warning".to_string(),
|
||||
source: codex_app_server_protocol::WarningSource::Runtime,
|
||||
});
|
||||
|
||||
let target = server_notification_thread_target(¬ification);
|
||||
|
||||
@@ -2343,6 +2343,7 @@ async fn replay_snapshot_with_pending_request_suppresses_replay_notices() {
|
||||
WarningNotification {
|
||||
thread_id: Some(thread_id.to_string()),
|
||||
message: stale_warning.to_string(),
|
||||
source: codex_app_server_protocol::WarningSource::Runtime,
|
||||
},
|
||||
)),
|
||||
ThreadBufferedEvent::Request(exec_approval_request(
|
||||
|
||||
@@ -411,6 +411,7 @@ async fn live_app_server_warning_notification_renders_message() {
|
||||
ServerNotification::Warning(WarningNotification {
|
||||
thread_id: None,
|
||||
message: "Exceeded skills context budget of 2%. All skill descriptions were removed and 2 additional skills were not included in the model-visible skills list.".to_string(),
|
||||
source: codex_app_server_protocol::WarningSource::Runtime,
|
||||
}),
|
||||
/*replay_kind*/ None,
|
||||
);
|
||||
|
||||
@@ -466,6 +466,7 @@ pub(super) fn handle_warning(chat: &mut ChatWidget, message: impl Into<String>)
|
||||
ServerNotification::Warning(WarningNotification {
|
||||
thread_id: Some(thread_id(chat)),
|
||||
message: message.into(),
|
||||
source: codex_app_server_protocol::WarningSource::Runtime,
|
||||
}),
|
||||
/*replay_kind*/ None,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user