Remove detached delivery from review/start (#57667)

## What changed

`review/start` now supports only inline reviews on the supplied thread and returns that thread's id as `reviewThreadId`. Clients can omit `delivery`, send `null`, or specify `"inline"`; `"detached"` is no longer supported.

For a separate review thread, call `thread/start` followed by `review/start`. To preserve conversation history with custom review instructions, use `thread/fork` followed by `turn/start`.

Remove detached review execution and its app-server dependency on `codex-agent-extension`, and update the protocol schemas and generated clients. Preserve `ReviewDelivery.inline` as a Python enum member.

## Testing

Add a Python regression test verifying that inline review delivery parses as `ReviewDelivery.inline` and round-trips to the original JSON.

GitOrigin-RevId: ed16c5271c3315ae3afcb3ee78883ed421394b4d
This commit is contained in:
yansenzhou-oai
2026-10-28 07:12:32 +00:00
committed by copyberry
parent 154e8736a5
commit 26ad281fd1
26 changed files with 90 additions and 570 deletions
@@ -547,6 +547,9 @@ def _normalized_schema_bundle_text(schema_dir: Path) -> str:
required.remove(field)
for name, definition in definitions.items():
if isinstance(definition, dict):
if name == "ReviewDelivery":
# Preserve ReviewDelivery.inline when only one delivery mode remains.
definition["x-enum-field-as-literal"] = False
if name == "CodexErrorInfo":
_normalize_open_ended_enum(definition)
_flatten_string_enum_one_of(definition)
@@ -4135,7 +4135,6 @@ class ResponsesApiWebSearchAction(
class ReviewDelivery(Enum):
inline = "inline"
detached = "detached"
class UncommittedChangesReviewTarget(BaseModel):
@@ -9460,7 +9459,7 @@ class ReviewStartParams(BaseModel):
delivery: Annotated[
ReviewDelivery | None,
Field(
description="Where to run the review: inline (default) on the current thread or detached on a new thread (returned in `reviewThreadId`). Detached delivery is deprecated and emits `deprecationNotice`. Use `thread/start` followed by an inline review for a separate review thread."
description="Reviews run inline on the supplied thread. Omit this field or pass `inline`. To review on a separate thread, start a thread first and use its id."
),
] = None
target: ReviewTarget
@@ -12565,11 +12564,7 @@ class ReviewStartResponse(BaseModel):
populate_by_name=True,
)
review_thread_id: Annotated[
str,
Field(
alias="reviewThreadId",
description="Identifies the thread where the review runs.\n\nFor inline reviews, this is the original thread id. For detached reviews, this is the id of the new review thread.",
),
str, Field(alias="reviewThreadId", description="The thread id supplied to `review/start`.")
]
turn: Turn
@@ -23,6 +23,8 @@ from openai_codex.generated.v2_all import (
PlanType,
ReasoningEffort,
ReasoningEffortOption,
ReviewDelivery,
ReviewStartParams,
ThreadForkParams,
ThreadListParams,
ThreadQueueChangedNotification,
@@ -39,6 +41,14 @@ from openai_codex.types import ThreadSource
ROOT = Path(__file__).resolve().parents[1]
def test_inline_review_delivery_round_trips_as_an_enum() -> None:
wire = {"threadId": "thread-1", "target": {"type": "uncommittedChanges"}, "delivery": "inline"}
params = ReviewStartParams.model_validate(wire)
assert params.delivery is ReviewDelivery.inline
assert params.model_dump(mode="json", by_alias=True) == wire
@pytest.mark.parametrize(
("model", "fields"),
[