diff --git a/codex-rs/app-server-protocol/schema/json/ClientRequest.json b/codex-rs/app-server-protocol/schema/json/ClientRequest.json index ba3a0dae7b..8c530ac641 100644 --- a/codex-rs/app-server-protocol/schema/json/ClientRequest.json +++ b/codex-rs/app-server-protocol/schema/json/ClientRequest.json @@ -2213,7 +2213,7 @@ "type": "string" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -2223,7 +2223,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/ServerNotification.json b/codex-rs/app-server-protocol/schema/json/ServerNotification.json index 1e546d90e5..b48ed37529 100644 --- a/codex-rs/app-server-protocol/schema/json/ServerNotification.json +++ b/codex-rs/app-server-protocol/schema/json/ServerNotification.json @@ -3015,7 +3015,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -3025,7 +3025,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json index f50f76d4e7..f2a55aff69 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json @@ -15361,7 +15361,7 @@ "type": "string" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -15371,7 +15371,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json index 00375ed018..926d0578df 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json @@ -11373,7 +11373,7 @@ "type": "string" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -11383,7 +11383,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ItemCompletedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/ItemCompletedNotification.json index 61ea1e89bf..beb79b2311 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ItemCompletedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ItemCompletedNotification.json @@ -650,7 +650,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -660,7 +660,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ItemStartedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/ItemStartedNotification.json index 8ea3e73125..92ed6ce659 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ItemStartedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ItemStartedNotification.json @@ -650,7 +650,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -660,7 +660,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/RawResponseItemCompletedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/RawResponseItemCompletedNotification.json index 2b0ac2129c..068d591707 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/RawResponseItemCompletedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/RawResponseItemCompletedNotification.json @@ -389,7 +389,7 @@ "type": "string" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -399,7 +399,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json index e953db7aa3..928e56e572 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json @@ -798,7 +798,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -808,7 +808,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json index d99230ee4a..9e1b212d6a 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json @@ -902,7 +902,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -912,7 +912,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadItemsListResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadItemsListResponse.json index 69549fe455..80fb6a9501 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadItemsListResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadItemsListResponse.json @@ -650,7 +650,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -660,7 +660,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json index 00fc581f9d..70bf0d107f 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json @@ -824,7 +824,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -834,7 +834,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json index b9f59501d0..d90c52e1e4 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json @@ -824,7 +824,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -834,7 +834,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json index caa6edca81..d86656672e 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json @@ -824,7 +824,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -834,7 +834,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeParams.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeParams.json index a0831997e8..80d7a6a917 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeParams.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeParams.json @@ -451,7 +451,7 @@ "type": "string" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -461,7 +461,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json index dfba3c1c99..5a8248c1d8 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json @@ -918,7 +918,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -928,7 +928,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadRevertResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadRevertResponse.json index 80cc673e6b..1639c808ee 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadRevertResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadRevertResponse.json @@ -824,7 +824,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -834,7 +834,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json index 3e45ae081a..5f8b018216 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json @@ -902,7 +902,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -912,7 +912,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json index 54bc31ab0e..1195bf7602 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json @@ -824,7 +824,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -834,7 +834,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadTurnsListResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadTurnsListResponse.json index 687e64c5c6..1b885d1b6d 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadTurnsListResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadTurnsListResponse.json @@ -798,7 +798,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -808,7 +808,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json index a0648ceeab..2fd357b835 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json @@ -824,7 +824,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -834,7 +834,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json index 6d9605d2ed..1d72c8cd2a 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json @@ -798,7 +798,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -808,7 +808,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json b/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json index dc7dc549fe..e99476202c 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json @@ -798,7 +798,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -808,7 +808,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json index 32ae5760ea..2a4b666554 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json @@ -798,7 +798,7 @@ "type": "object" }, "MessagePhase": { - "description": "Classifies an assistant message as interim commentary or final answer text.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", + "description": "Classifies assistant text as commentary, a partial answer, or a terminal answer.\n\nProviders do not emit this consistently, so callers must treat `None` as \"phase unknown\" and keep compatibility behavior for legacy models.", "oneOf": [ { "description": "Mid-turn assistant text (for example preamble/progress narration).\n\nAdditional tool calls or assistant output may follow before turn completion.", @@ -808,7 +808,14 @@ "type": "string" }, { - "description": "The assistant's terminal answer text for the current turn.", + "description": "Stable answer text that may be followed by more assistant output or tools.", + "enum": [ + "partial_answer" + ], + "type": "string" + }, + { + "description": "The assistant's declared terminal answer text for the current turn.", "enum": [ "final_answer" ], diff --git a/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-experimental.json.zst b/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-experimental.json.zst index 951bb6b29f..4e3aa5a77f 100644 Binary files a/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-experimental.json.zst and b/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-experimental.json.zst differ diff --git a/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst b/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst index 518a22048a..3f816c600c 100644 Binary files a/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst and b/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst differ diff --git a/codex-rs/app-server-protocol/schema/typescript/MessagePhase.ts b/codex-rs/app-server-protocol/schema/typescript/MessagePhase.ts index 9e16021b54..737f1431df 100644 --- a/codex-rs/app-server-protocol/schema/typescript/MessagePhase.ts +++ b/codex-rs/app-server-protocol/schema/typescript/MessagePhase.ts @@ -3,9 +3,9 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. /** - * Classifies an assistant message as interim commentary or final answer text. + * Classifies assistant text as commentary, a partial answer, or a terminal answer. * * Providers do not emit this consistently, so callers must treat `None` as * "phase unknown" and keep compatibility behavior for legacy models. */ -export type MessagePhase = "commentary" | "final_answer"; +export type MessagePhase = "commentary" | "partial_answer" | "final_answer"; diff --git a/codex-rs/codex-api/src/sse/responses.rs b/codex-rs/codex-api/src/sse/responses.rs index ef02dfdf9a..78885f43de 100644 --- a/codex-rs/codex-api/src/sse/responses.rs +++ b/codex-rs/codex-api/src/sse/responses.rs @@ -734,6 +734,46 @@ mod tests { Duration::from_millis(1000) } + #[tokio::test] + async fn partial_answer_phase_survives_item_start_and_completion() { + let item = json!({ + "id": "msg_partial", + "type": "message", + "role": "assistant", + "content": [], + "phase": "partial_answer" + }); + let added = json!({"type": "response.output_item.added", "item": item}); + let delta = json!({"type": "response.output_text.delta", "delta": "First result."}); + let mut done_item = item; + done_item["content"] = json!([{"type": "output_text", "text": "First result."}]); + let done = json!({"type": "response.output_item.done", "item": done_item}); + let completed = json!({ + "type": "response.completed", + "response": {"id": "resp1", "end_turn": false} + }); + let stream = [added, delta, done, completed] + .into_iter() + .map(|event| format!("data: {event}\n\n")) + .collect::(); + + let events = collect_events(&[stream.as_bytes()]).await; + assert_matches!(events.as_slice(), [ + Ok(ResponseEvent::OutputItemAdded(ResponseItem::Message { + phase: Some(MessagePhase::PartialAnswer), content: start_content, .. + })), + Ok(ResponseEvent::OutputTextDelta(text)), + Ok(ResponseEvent::OutputItemDone(ResponseItem::Message { + phase: Some(MessagePhase::PartialAnswer), content: done_content, .. + })), + Ok(ResponseEvent::Completed { end_turn: Some(false), .. }), + ] if start_content.is_empty() + && text == "First result." + && done_content == &vec![codex_protocol::models::ContentItem::OutputText { + text: "First result.".to_string() + }]); + } + #[tokio::test] async fn parses_items_and_completed() { let item1 = json!({ diff --git a/codex-rs/core/src/realtime_conversation.rs b/codex-rs/core/src/realtime_conversation.rs index 4c9269ee21..42090f9180 100644 --- a/codex-rs/core/src/realtime_conversation.rs +++ b/codex-rs/core/src/realtime_conversation.rs @@ -928,6 +928,8 @@ impl RealtimeConversationManager { if handoff.client_managed_handoffs { return Ok(()); } + // TODO: Preserve explicit PartialAnswer phases through completed and streamed BEM + // routing, with integration coverage asserting the speakable channel. let phase = if handoff.routes_handoff_by_bem() { match bem_message_phase( &output_text, @@ -942,6 +944,8 @@ impl RealtimeConversationManager { } else { phase }; + // TODO: Treat PartialAnswer as nonterminal for V1 handoffs, omitting the final-message + // prefix for streamed, completed-item, and standalone output. let is_commentary = matches!(phase, Some(MessagePhase::Commentary)); let active_handoff = handoff.stream.lock().await.active_handoff.clone(); let output = match active_handoff { @@ -2272,7 +2276,9 @@ fn v3_output_writer( CodexResponseHandoffMode::Thinking => None, CodexResponseHandoffMode::Commentary => Some(RealtimeContextAppendChannel::Commentary), CodexResponseHandoffMode::BemTags => match phase { - Some(MessagePhase::FinalAnswer) => Some(RealtimeContextAppendChannel::Speakable), + Some(MessagePhase::PartialAnswer | MessagePhase::FinalAnswer) => { + Some(RealtimeContextAppendChannel::Speakable) + } Some(MessagePhase::Commentary) => Some(RealtimeContextAppendChannel::Commentary), None => Some(RealtimeContextAppendChannel::Speakable), }, diff --git a/codex-rs/core/src/session/turn.rs b/codex-rs/core/src/session/turn.rs index c4e3dfc9cf..2e6ec258c4 100644 --- a/codex-rs/core/src/session/turn.rs +++ b/codex-rs/core/src/session/turn.rs @@ -2750,6 +2750,8 @@ async fn try_run_sampling_request( cancellation_token: cancellation_token.child_token(), }; + // TODO: Allow mailbox preemption after PartialAnswer and keep delivery open + // in the completed-item deferral predicates. let preempt_for_mailbox_mail = match &item { ResponseItem::Message { role, phase, .. } => { role == "assistant" && matches!(phase, Some(MessagePhase::Commentary)) diff --git a/codex-rs/protocol/src/models.rs b/codex-rs/protocol/src/models.rs index 1665c31e8f..af3e53aa8f 100644 --- a/codex-rs/protocol/src/models.rs +++ b/codex-rs/protocol/src/models.rs @@ -937,7 +937,7 @@ pub const DEFAULT_IMAGE_DETAIL: ImageDetail = ImageDetail::High; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "snake_case")] -/// Classifies an assistant message as interim commentary or final answer text. +/// Classifies assistant text as commentary, a partial answer, or a terminal answer. /// /// Providers do not emit this consistently, so callers must treat `None` as /// "phase unknown" and keep compatibility behavior for legacy models. @@ -947,7 +947,9 @@ pub enum MessagePhase { /// Additional tool calls or assistant output may follow before turn /// completion. Commentary, - /// The assistant's terminal answer text for the current turn. + /// Stable answer text that may be followed by more assistant output or tools. + PartialAnswer, + /// The assistant's declared terminal answer text for the current turn. FinalAnswer, } @@ -2518,27 +2520,38 @@ mod tests { } #[test] - fn response_input_message_conversion_preserves_phase() { - let item = ResponseItem::from(ResponseInputItem::Message { - role: "assistant".to_string(), - content: vec![ContentItem::OutputText { - text: "still working".to_string(), - }], - phase: Some(MessagePhase::Commentary), - }); - - assert_eq!( - item, - ResponseItem::Message { + fn response_input_message_conversion_preserves_phase() -> Result<()> { + for phase in [ + None, + Some(MessagePhase::Commentary), + Some(MessagePhase::PartialAnswer), + Some(MessagePhase::FinalAnswer), + ] { + let input = ResponseInputItem::Message { + role: "assistant".to_string(), + content: vec![ContentItem::OutputText { + text: "answer text".to_string(), + }], + phase: phase.clone(), + }; + let wire = serde_json::to_value(input)?; + let item = ResponseItem::from(serde_json::from_value::(wire)?); + let expected = ResponseItem::Message { id: None, role: "assistant".to_string(), content: vec![ContentItem::OutputText { - text: "still working".to_string(), + text: "answer text".to_string(), }], - phase: Some(MessagePhase::Commentary), + phase, internal_chat_message_metadata_passthrough: None, - } - ); + }; + assert_eq!(item, expected); + assert_eq!( + serde_json::from_value::(serde_json::to_value(item)?)?, + expected + ); + } + Ok(()) } #[test] diff --git a/codex-rs/thread-store/src/local/thread_history.rs b/codex-rs/thread-store/src/local/thread_history.rs index 81364f5142..aca9fdc835 100644 --- a/codex-rs/thread-store/src/local/thread_history.rs +++ b/codex-rs/thread-store/src/local/thread_history.rs @@ -520,7 +520,7 @@ WHERE thread_id = ? .map_err(thread_history_error)?; } ThreadItem::AgentMessage { - phase: Some(MessagePhase::Commentary) | None, + phase: Some(MessagePhase::Commentary | MessagePhase::PartialAnswer) | None, .. } | ThreadItem::HookPrompt { .. } diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__single_line_partial_answer_keeps_working_status.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__single_line_partial_answer_keeps_working_status.snap new file mode 100644 index 0000000000..f5b6cdd6e1 --- /dev/null +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__single_line_partial_answer_keeps_working_status.snap @@ -0,0 +1,14 @@ +--- +source: tui/src/chatwidget/tests/status_and_layout.rs +expression: normalize_snapshot_paths(terminal.backend().vt100().screen().contents()) +--- + + +• 1 + +• Working (0s • esc to interrupt) + + +› Ask Codex to do anything + + gpt-5 default · /tmp/project diff --git a/codex-rs/tui/src/chatwidget/streaming.rs b/codex-rs/tui/src/chatwidget/streaming.rs index 3fb130d563..1957cc4b37 100644 --- a/codex-rs/tui/src/chatwidget/streaming.rs +++ b/codex-rs/tui/src/chatwidget/streaming.rs @@ -482,7 +482,7 @@ impl ChatWidget { Some(MessagePhase::FinalAnswer) | None => { !self.input_queue.pending_steers.is_empty() } - Some(MessagePhase::Commentary) => true, + Some(MessagePhase::Commentary | MessagePhase::PartialAnswer) => true, }; self.maybe_restore_status_indicator_after_stream_idle(); } diff --git a/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs b/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs index 523722086e..307e1f5128 100644 --- a/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs +++ b/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs @@ -2205,42 +2205,51 @@ async fn streaming_final_answer_keeps_task_running_state() { } #[tokio::test] -async fn single_line_final_answer_hides_working_status_snapshot() { - let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5")).await; - chat.thread_id = Some(ThreadId::new()); +async fn answer_phase_controls_working_status_snapshot() { + for (phase, working_visible, snapshot) in [ + ( + MessagePhase::FinalAnswer, + false, + "single_line_final_answer_hides_working_status", + ), + ( + MessagePhase::PartialAnswer, + true, + "single_line_partial_answer_keeps_working_status", + ), + ] { + let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(Some("gpt-5")).await; + chat.thread_id = Some(ThreadId::new()); - complete_user_message(&mut chat, "user-1", "count to 1"); - chat.on_task_started(); - complete_assistant_message( - &mut chat, - "msg-final-single-line", - "1", - Some(MessagePhase::FinalAnswer), - ); + complete_user_message(&mut chat, "user-1", "count to 1"); + chat.on_task_started(); + complete_assistant_message(&mut chat, "msg-final-single-line", "1", Some(phase)); - assert!(chat.bottom_pane.is_task_running()); - assert!(!chat.bottom_pane.status_indicator_visible()); + assert!(chat.bottom_pane.is_task_running()); + assert_eq!(chat.bottom_pane.status_indicator_visible(), working_visible); - let width: u16 = 40; - let vt_height: u16 = 10; - let ui_height = chat.desired_height(width); - let viewport = Rect::new(0, vt_height - ui_height - 1, width, ui_height); - let backend = VT100Backend::new(width, vt_height); - let mut terminal = crate::custom_terminal::Terminal::with_options(backend).expect("terminal"); - terminal.set_viewport_area(viewport); + let width: u16 = 40; + let vt_height: u16 = 10; + let ui_height = chat.desired_height(width); + let viewport = Rect::new(0, vt_height - ui_height - 1, width, ui_height); + let backend = VT100Backend::new(width, vt_height); + let mut terminal = + crate::custom_terminal::Terminal::with_options(backend).expect("terminal"); + terminal.set_viewport_area(viewport); - for lines in drain_insert_history(&mut rx) { - crate::insert_history::insert_history_lines(&mut terminal, lines) - .expect("insert history lines"); + for lines in drain_insert_history(&mut rx) { + crate::insert_history::insert_history_lines(&mut terminal, lines) + .expect("insert history lines"); + } + + terminal + .draw(|frame| chat.render(frame.area(), frame.buffer_mut())) + .expect("draw final answer"); + assert_chatwidget_snapshot!( + snapshot, + normalize_snapshot_paths(terminal.backend().vt100().screen().contents()) + ); } - - terminal - .draw(|frame| chat.render(frame.area(), frame.buffer_mut())) - .expect("draw final answer"); - assert_chatwidget_snapshot!( - "single_line_final_answer_hides_working_status", - normalize_snapshot_paths(terminal.backend().vt100().screen().contents()) - ); } #[tokio::test] diff --git a/sdk/python/src/openai_codex/generated/v2_all.py b/sdk/python/src/openai_codex/generated/v2_all.py index cb1ad32ee8..58f45ffcf2 100644 --- a/sdk/python/src/openai_codex/generated/v2_all.py +++ b/sdk/python/src/openai_codex/generated/v2_all.py @@ -2750,6 +2750,7 @@ class MergeStrategy(Enum): class MessagePhase(Enum): commentary = "commentary" + partial_answer = "partial_answer" final_answer = "final_answer"