diff --git a/codex-rs/analytics/src/reducer.rs b/codex-rs/analytics/src/reducer.rs index 43c8918551..36a1855007 100644 --- a/codex-rs/analytics/src/reducer.rs +++ b/codex-rs/analytics/src/reducer.rs @@ -495,6 +495,7 @@ impl TurnToolCounts { ThreadItem::UserMessage { .. } | ThreadItem::HookPrompt { .. } | ThreadItem::AgentMessage { .. } + | ThreadItem::FunctionCallOutput { .. } | ThreadItem::Plan { .. } | ThreadItem::Reasoning { .. } | ThreadItem::ImageView { .. } @@ -2438,6 +2439,7 @@ fn tracked_tool_item_id(item: &ThreadItem) -> Option<&str> { ThreadItem::UserMessage { .. } | ThreadItem::HookPrompt { .. } | ThreadItem::AgentMessage { .. } + | ThreadItem::FunctionCallOutput { .. } | ThreadItem::Plan { .. } | ThreadItem::Reasoning { .. } | ThreadItem::SubAgentActivity { .. } diff --git a/codex-rs/app-server-protocol/schema/json/ClientRequest.json b/codex-rs/app-server-protocol/schema/json/ClientRequest.json index 7ddd776fb8..2db63c2dc1 100644 --- a/codex-rs/app-server-protocol/schema/json/ClientRequest.json +++ b/codex-rs/app-server-protocol/schema/json/ClientRequest.json @@ -5395,6 +5395,16 @@ "threadId": { "type": "string" }, + "toolOutput": { + "anyOf": [ + { + "$ref": "#/definitions/TurnToolOutput" + }, + { + "type": "null" + } + ] + }, "turnTrigger": { "description": "Optional source classification for the caller that starts this turn. Ignored when this request steers an already-active turn.", "type": [ @@ -5438,6 +5448,27 @@ ], "type": "object" }, + "TurnToolOutput": { + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + } + }, + "required": [ + "name", + "output" + ], + "type": "object" + }, "UserInput": { "oneOf": [ { diff --git a/codex-rs/app-server-protocol/schema/json/ServerNotification.json b/codex-rs/app-server-protocol/schema/json/ServerNotification.json index 49218efb65..7334b25a94 100644 --- a/codex-rs/app-server-protocol/schema/json/ServerNotification.json +++ b/codex-rs/app-server-protocol/schema/json/ServerNotification.json @@ -1678,6 +1678,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "FuzzyFileSearchMatchType": { "enum": [ "file", @@ -4383,6 +4491,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { 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 6cc4bd3f98..a1b085a1a2 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 @@ -20697,6 +20697,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/v2/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { @@ -24337,6 +24371,16 @@ "threadId": { "type": "string" }, + "toolOutput": { + "anyOf": [ + { + "$ref": "#/definitions/v2/TurnToolOutput" + }, + { + "type": "null" + } + ] + }, "turnTrigger": { "description": "Optional source classification for the caller that starts this turn. Ignored when this request steers an already-active turn.", "type": [ @@ -24435,6 +24479,27 @@ "title": "TurnSteerResponse", "type": "object" }, + "TurnToolOutput": { + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/v2/FunctionCallOutputBody" + } + }, + "required": [ + "name", + "output" + ], + "type": "object" + }, "TurnsPage": { "properties": { "backwardsCursor": { 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 d619ffd275..55975acace 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 @@ -18432,6 +18432,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { @@ -22072,6 +22106,16 @@ "threadId": { "type": "string" }, + "toolOutput": { + "anyOf": [ + { + "$ref": "#/definitions/TurnToolOutput" + }, + { + "type": "null" + } + ] + }, "turnTrigger": { "description": "Optional source classification for the caller that starts this turn. Ignored when this request steers an already-active turn.", "type": [ @@ -22170,6 +22214,27 @@ "title": "TurnSteerResponse", "type": "object" }, + "TurnToolOutput": { + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + } + }, + "required": [ + "name", + "output" + ], + "type": "object" + }, "TurnsPage": { "properties": { "backwardsCursor": { 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 14f21e5de0..6eb695e5b7 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ItemCompletedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ItemCompletedNotification.json @@ -301,6 +301,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "HookPromptFragment": { "properties": { "hookRunId": { @@ -717,6 +825,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { 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 f43c997b8e..f304246faa 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ItemStartedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ItemStartedNotification.json @@ -301,6 +301,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "HookPromptFragment": { "properties": { "hookRunId": { @@ -717,6 +825,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { 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 b461409650..29a2e6db19 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json @@ -441,6 +441,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "HookPromptFragment": { "properties": { "hookRunId": { @@ -905,6 +1013,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { 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 2f89c32bf8..f8e52d28f1 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json @@ -522,6 +522,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "GitInfo": { "properties": { "branch": { @@ -1471,6 +1579,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { 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 24b2680f32..9ec9797426 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadItemsListResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadItemsListResponse.json @@ -301,6 +301,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "HookPromptFragment": { "properties": { "hookRunId": { @@ -717,6 +825,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { 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 737c5731d2..f1d70c77a1 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json @@ -444,6 +444,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "GitInfo": { "properties": { "branch": { @@ -1262,6 +1370,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { 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 6588235744..6de156b488 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json @@ -444,6 +444,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "GitInfo": { "properties": { "branch": { @@ -1262,6 +1370,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { 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 37b5b93809..ba130748ff 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json @@ -444,6 +444,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "GitInfo": { "properties": { "branch": { @@ -1262,6 +1370,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { 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 f267aeaf36..7016e7f077 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json @@ -522,6 +522,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "GitInfo": { "properties": { "branch": { @@ -1471,6 +1579,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { 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 1ff75a0c51..65dfd421b7 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadRevertResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadRevertResponse.json @@ -444,6 +444,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "GitInfo": { "properties": { "branch": { @@ -1262,6 +1370,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json index e898ed8f3c..210c784af9 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json @@ -444,6 +444,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "GitInfo": { "properties": { "branch": { @@ -1262,6 +1370,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { 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 99cba383f8..6d82e1c5b6 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json @@ -522,6 +522,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "GitInfo": { "properties": { "branch": { @@ -1471,6 +1579,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { 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 c07fcd44fd..33dc4dec94 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json @@ -444,6 +444,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "GitInfo": { "properties": { "branch": { @@ -1262,6 +1370,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { 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 5a71e6eea8..489c2103ce 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadTurnsListResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadTurnsListResponse.json @@ -441,6 +441,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "HookPromptFragment": { "properties": { "hookRunId": { @@ -905,6 +1013,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { 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 46fbe29c71..ed4edd1194 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json @@ -444,6 +444,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "GitInfo": { "properties": { "branch": { @@ -1262,6 +1370,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { 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 4ed3a6bc8c..ac0f055cc7 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json @@ -441,6 +441,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "HookPromptFragment": { "properties": { "hookRunId": { @@ -905,6 +1013,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json b/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json index 1dfb7ed708..2ba2e1f483 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json @@ -129,6 +129,114 @@ ], "type": "string" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "ImageDetail": { "enum": [ "auto", @@ -389,6 +497,27 @@ ], "type": "object" }, + "TurnToolOutput": { + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + } + }, + "required": [ + "name", + "output" + ], + "type": "object" + }, "UserInput": { "oneOf": [ { @@ -685,6 +814,16 @@ "threadId": { "type": "string" }, + "toolOutput": { + "anyOf": [ + { + "$ref": "#/definitions/TurnToolOutput" + }, + { + "type": "null" + } + ] + }, "turnTrigger": { "description": "Optional source classification for the caller that starts this turn. Ignored when this request steers an already-active turn.", "type": [ 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 0931d2cef8..1a62b8ad45 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json @@ -441,6 +441,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "HookPromptFragment": { "properties": { "hookRunId": { @@ -905,6 +1013,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { 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 7355dc3fed..e02f757f99 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json @@ -441,6 +441,114 @@ ], "type": "object" }, + "FunctionCallOutputBody": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "$ref": "#/definitions/FunctionCallOutputContentItem" + }, + "type": "array" + } + ] + }, + "FunctionCallOutputContentItem": { + "description": "Responses API compatible content items that can be returned by a tool call. This is a subset of ContentItem with the types we support as function call outputs.", + "oneOf": [ + { + "properties": { + "text": { + "type": "string" + }, + "type": { + "enum": [ + "input_text" + ], + "title": "InputTextFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "text", + "type" + ], + "title": "InputTextFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "detail": { + "anyOf": [ + { + "$ref": "#/definitions/ImageDetail" + }, + { + "type": "null" + } + ] + }, + "image_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_image" + ], + "title": "InputImageFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "image_url", + "type" + ], + "title": "InputImageFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "audio_url": { + "type": "string" + }, + "type": { + "enum": [ + "input_audio" + ], + "title": "InputAudioFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "audio_url", + "type" + ], + "title": "InputAudioFunctionCallOutputContentItem", + "type": "object" + }, + { + "properties": { + "encrypted_content": { + "type": "string" + }, + "type": { + "enum": [ + "encrypted_content" + ], + "title": "EncryptedContentFunctionCallOutputContentItemType", + "type": "string" + } + }, + "required": [ + "encrypted_content", + "type" + ], + "title": "EncryptedContentFunctionCallOutputContentItem", + "type": "object" + } + ] + }, "HookPromptFragment": { "properties": { "hookRunId": { @@ -905,6 +1013,40 @@ "title": "AgentMessageThreadItem", "type": "object" }, + { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "namespace": { + "type": [ + "string", + "null" + ] + }, + "output": { + "$ref": "#/definitions/FunctionCallOutputBody" + }, + "type": { + "enum": [ + "functionCallOutput" + ], + "title": "FunctionCallOutputThreadItemType", + "type": "string" + } + }, + "required": [ + "id", + "name", + "output", + "type" + ], + "title": "FunctionCallOutputThreadItem", + "type": "object" + }, { "description": "EXPERIMENTAL - proposed plan item content. The completed plan item is authoritative and may not match the concatenation of `PlanDelta` text.", "properties": { 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 5612ef3887..f7911b4f69 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 f55b252c93..65abc73962 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/v2/ThreadItem.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadItem.ts index 2ff6fbd4dc..d0dbf7c126 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadItem.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadItem.ts @@ -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 { FunctionCallOutputBody } from "../FunctionCallOutputBody"; import type { ImageGenerationItem } from "../ImageGenerationItem"; import type { LegacyAppPathString } from "../LegacyAppPathString"; import type { MessagePhase } from "../MessagePhase"; @@ -28,7 +29,7 @@ import type { PatchApplyStatus } from "./PatchApplyStatus"; import type { SubAgentActivityKind } from "./SubAgentActivityKind"; import type { UserInput } from "./UserInput"; -export type ThreadItem = { "type": "userMessage", id: string, clientId: string | null, content: Array, } | { "type": "hookPrompt", id: string, fragments: Array, } | { "type": "agentMessage", id: string, text: string, phase: MessagePhase | null, memoryCitation: MemoryCitation | null, delivery: AgentMessageDelivery | null, } | { "type": "plan", id: string, text: string, } | { "type": "reasoning", id: string, summary: Array, content: Array, } | { "type": "commandExecution", id: string, +export type ThreadItem = { "type": "userMessage", id: string, clientId: string | null, content: Array, } | { "type": "hookPrompt", id: string, fragments: Array, } | { "type": "agentMessage", id: string, text: string, phase: MessagePhase | null, memoryCitation: MemoryCitation | null, delivery: AgentMessageDelivery | null, } | { "type": "functionCallOutput", id: string, name: string, namespace: string | null, output: FunctionCallOutputBody, } | { "type": "plan", id: string, text: string, } | { "type": "reasoning", id: string, summary: Array, content: Array, } | { "type": "commandExecution", id: string, /** * Trusted first-party plugin id when this command resolves to one plugin script. */ diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/TurnStartParams.ts b/codex-rs/app-server-protocol/schema/typescript/v2/TurnStartParams.ts index 785ca22750..97ed892f8f 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/TurnStartParams.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/TurnStartParams.ts @@ -8,13 +8,14 @@ import type { JsonValue } from "../serde_json/JsonValue"; import type { ApprovalsReviewer } from "./ApprovalsReviewer"; import type { AskForApproval } from "./AskForApproval"; import type { SandboxPolicy } from "./SandboxPolicy"; +import type { TurnToolOutput } from "./TurnToolOutput"; import type { UserInput } from "./UserInput"; export type TurnStartParams = {threadId: string, clientUserMessageId?: string | null, input: Array, /** * Optional source classification for the caller that starts this turn. * Ignored when this request steers an already-active turn. */ -turnTrigger?: string | null, /** +turnTrigger?: string | null, toolOutput?: TurnToolOutput | null, /** * Override the working directory for this turn and subsequent turns. */ cwd?: string | null, /** diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/TurnToolOutput.ts b/codex-rs/app-server-protocol/schema/typescript/v2/TurnToolOutput.ts new file mode 100644 index 0000000000..77df888c83 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/TurnToolOutput.ts @@ -0,0 +1,6 @@ +// 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 { FunctionCallOutputBody } from "../FunctionCallOutputBody"; + +export type TurnToolOutput = { name: string, namespace: string | null, output: FunctionCallOutputBody, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts index 6023b44fd1..a5f10d734d 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts @@ -589,6 +589,7 @@ export type { TurnStartedNotification } from "./TurnStartedNotification"; export type { TurnStatus } from "./TurnStatus"; export type { TurnSteerParams } from "./TurnSteerParams"; export type { TurnSteerResponse } from "./TurnSteerResponse"; +export type { TurnToolOutput } from "./TurnToolOutput"; export type { TurnsPage } from "./TurnsPage"; export type { UserInput } from "./UserInput"; export type { WarningNotification } from "./WarningNotification"; diff --git a/codex-rs/app-server-protocol/src/protocol/thread_history.rs b/codex-rs/app-server-protocol/src/protocol/thread_history.rs index 8e17ed70d3..32cfe744e8 100644 --- a/codex-rs/app-server-protocol/src/protocol/thread_history.rs +++ b/codex-rs/app-server-protocol/src/protocol/thread_history.rs @@ -604,6 +604,7 @@ impl ThreadHistoryBuilder { let should_upsert = match item { codex_protocol::items::TurnItem::Plan(plan) => !plan.text.is_empty(), codex_protocol::items::TurnItem::HookPrompt(_) + | codex_protocol::items::TurnItem::FunctionCallOutput(_) | codex_protocol::items::TurnItem::CommandExecution(_) | codex_protocol::items::TurnItem::DynamicToolCall(_) | codex_protocol::items::TurnItem::CollabAgentToolCall(_) diff --git a/codex-rs/app-server-protocol/src/protocol/v2/item.rs b/codex-rs/app-server-protocol/src/protocol/v2/item.rs index 8a90f93bff..67b8c41d5b 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/item.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/item.rs @@ -34,6 +34,7 @@ use codex_protocol::items::McpToolCallStatus as CoreMcpToolCallStatus; use codex_protocol::items::TurnItem as CoreTurnItem; use codex_protocol::memory_citation::MemoryCitation as CoreMemoryCitation; use codex_protocol::memory_citation::MemoryCitationEntry as CoreMemoryCitationEntry; +use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::MessagePhase; use codex_protocol::models::ResponseItem; use codex_protocol::openai_models::ReasoningEffort; @@ -257,6 +258,14 @@ pub enum ThreadItem { }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] + FunctionCallOutput { + id: String, + name: String, + namespace: Option, + output: FunctionCallOutputBody, + }, + #[serde(rename_all = "camelCase")] + #[ts(rename_all = "camelCase")] /// EXPERIMENTAL - proposed plan item content. The completed plan item is /// authoritative and may not match the concatenation of `PlanDelta` text. Plan { @@ -429,6 +438,7 @@ impl ThreadItem { ThreadItem::UserMessage { id, .. } | ThreadItem::HookPrompt { id, .. } | ThreadItem::AgentMessage { id, .. } + | ThreadItem::FunctionCallOutput { id, .. } | ThreadItem::Plan { id, .. } | ThreadItem::Reasoning { id, .. } | ThreadItem::CommandExecution { id, .. } @@ -873,6 +883,12 @@ impl From for ThreadItem { delivery: agent.delivery, } } + CoreTurnItem::FunctionCallOutput(output) => ThreadItem::FunctionCallOutput { + id: output.id, + name: output.name, + namespace: output.namespace, + output: output.output, + }, CoreTurnItem::Plan(plan) => ThreadItem::Plan { id: plan.id, text: plan.text, diff --git a/codex-rs/app-server-protocol/src/protocol/v2/tests.rs b/codex-rs/app-server-protocol/src/protocol/v2/tests.rs index 372f0707ef..da95817a1e 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/tests.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/tests.rs @@ -4724,6 +4724,7 @@ fn turn_start_params_preserve_explicit_null_service_tier() { client_user_message_id: None, input: vec![], turn_trigger: None, + tool_output: None, responsesapi_client_metadata: None, additional_context: None, environments: None, diff --git a/codex-rs/app-server-protocol/src/protocol/v2/turn.rs b/codex-rs/app-server-protocol/src/protocol/v2/turn.rs index 9c3b588d23..3222bb38bb 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/turn.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/turn.rs @@ -9,6 +9,7 @@ use codex_protocol::config_types::CollaborationMode; use codex_protocol::config_types::MultiAgentMode; use codex_protocol::config_types::Personality; use codex_protocol::config_types::ReasoningSummary; +use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::ImageDetail; use codex_protocol::openai_models::ReasoningEffort; use codex_protocol::plan_tool::PlanItemArg as CorePlanItemArg; @@ -134,6 +135,15 @@ impl From for CoreCyberAccessProgram { } } +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export_to = "v2/")] +pub struct TurnToolOutput { + pub name: String, + pub namespace: Option, + pub output: FunctionCallOutputBody, +} + #[derive( Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS, ExperimentalApi, )] @@ -148,6 +158,8 @@ pub struct TurnStartParams { /// Ignored when this request steers an already-active turn. #[ts(optional = nullable)] pub turn_trigger: Option, + #[ts(optional = nullable)] + pub tool_output: Option>, /// Optional metadata to enrich Codex's ResponsesAPI turn metadata. /// /// Entries are flattened into the JSON string sent as diff --git a/codex-rs/app-server/README.md b/codex-rs/app-server/README.md index 07c9df0acf..8558cabb7f 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -209,8 +209,8 @@ Example with notification opt-out: - `thread/backgroundTerminals/terminate` — terminate one running background terminal by app-server `processId` (experimental; requires `capabilities.experimentalApi`); returns whether a process was terminated. - `thread/rollback` — deprecated and will be removed soon. Drop the last N turns from the agent’s in-memory context and persist a rollback marker in the rollout so future resumes see the pruned history; returns the updated `thread` (with `turns` populated) on success. Paginated threads do not support rollback. Parent-owned Multi-Agent V2 subagents reject direct rollback requests. - `thread/revert` — replace a loaded paginated thread's durable history with the prefix strictly before `beforeTurnId` while preserving its thread id. The operation interrupts an active turn if needed, leaves older rollout files immutable, reloads the thread, returns updated thread metadata with empty `turns` plus pagination cursors, and emits `thread/reverted`. It does not revert local file changes. Parent-owned Multi-Agent V2 subagents reject direct revert requests. -- `turn/start` — add user input to a thread and begin Codex generation; responds with the initial `turn` object and streams `turn/started`, `item/*`, and `turn/completed` notifications. Optional `turnTrigger` classifies who or what started a new turn and is sent as `turn_trigger` in Responses request metadata; it is ignored if the request steers an active turn. `clientUserMessageId` is optional; when supplied, the corresponding `userMessage` item echoes it as `clientId`. Experimental `runtimeWorkspaceRoots` supplies the default roots for newly resolved environment selections. Explicit `environments[].runtimeWorkspaceRoots` override that fallback with environment-native absolute paths. Prefer experimental `permissions` profile selection by id for permission overrides; the legacy `sandboxPolicy` field is still accepted but cannot be combined with `permissions`. For `collaborationMode`, `settings.developer_instructions: null` means "use built-in instructions for the selected mode". Deprecated experimental `multiAgentMode` is ignored; Ultra reasoning effort selects proactive behavior. Parent-owned Multi-Agent V2 subagents reject direct turns. -- `thread/inject_items` — append raw Responses API items to a loaded thread’s model-visible history without starting a user turn; returns `{}` on success. Parent-owned Multi-Agent V2 subagents reject direct item injection. +- `turn/start` — add user input or a named standalone function-call output to a thread and begin Codex generation; responds with the initial `turn` object and streams `turn/started`, `item/*`, and `turn/completed` notifications. For standalone outputs, provide `toolOutput` with an empty `input` array. Optional `turnTrigger` classifies who or what started a new turn and is sent as `turn_trigger` in Responses request metadata; it is ignored if the request steers an active turn. `clientUserMessageId` is optional; when supplied, the corresponding `userMessage` item echoes it as `clientId`. Experimental `runtimeWorkspaceRoots` supplies the default roots for newly resolved environment selections. Explicit `environments[].runtimeWorkspaceRoots` override that fallback with environment-native absolute paths. Prefer experimental `permissions` profile selection by id for permission overrides; the legacy `sandboxPolicy` field is still accepted but cannot be combined with `permissions`. For `collaborationMode`, `settings.developer_instructions: null` means "use built-in instructions for the selected mode". Deprecated experimental `multiAgentMode` is ignored; Ultra reasoning effort selects proactive behavior. Parent-owned Multi-Agent V2 subagents reject direct turns. +- `thread/inject_items` — append raw Responses API items to a loaded thread’s model-visible history without starting a turn; returns `{}` on success. Parent-owned Multi-Agent V2 subagents reject direct item injection. - `turn/settings/update` — experimental; publish a narrow model-settings patch to the exact live task identified by `threadId` and `turnId`, regardless of task kind. Requires `step_model_switching`; returns `status: "applied"` or `status: "targetUnavailable"`, or a request error if rejected. Future-thread settings and already captured steps are unchanged. Parent-owned Multi-Agent V2 subagents reject direct settings updates. - `turn/steer` — add user input to an already in-flight regular turn without starting a new turn; returns the active `turnId` that accepted the input. `clientUserMessageId` is optional; when supplied, the corresponding `userMessage` item echoes it as `clientId`. Review and manual compaction turns reject `turn/steer`. Parent-owned Multi-Agent V2 subagents reject direct steering. - `turn/interrupt` — request cancellation of an in-flight turn by `(thread_id, turn_id)`; success is an empty `{}` response and the turn finishes with `status: "interrupted"`. Also available for parent-owned Multi-Agent V2 subagents. @@ -1045,12 +1045,29 @@ Invoke a plugin by including a UI mention token such as `@sample` in the text in } } } ``` -### Example: Inject raw history items +### Example: Start a turn (standalone tool output) -Use `thread/inject_items` to append prebuilt Responses API items to a loaded thread’s prompt history without starting a user turn. These items are persisted to the rollout and included in subsequent model requests. A standalone `function_call_output` can omit `call_id` when it has a nonempty `name`; `namespace` is optional, and the output retains tool-tier authority. Any `input_image` items must use inline data URLs; remote HTTP(S) image URLs are rejected. +Provide a named `toolOutput` with an empty `input` array to start a real turn or join an active regular turn. `namespace` is nullable, and `output` can be text or structured content items. The output retains tool-tier authority and appears as a `functionCallOutput` item in durable history and standard item notifications; clients decide whether to display it. ```json -{ "method": "thread/inject_items", "id": 36, "params": { +{ "method": "turn/start", "id": 36, "params": { + "threadId": "thr_123", + "input": [], + "toolOutput": { + "name": "send_message_to_thread", + "namespace": "codex_app", + "output": "Another agent delegated this task." + } +} } +{ "id": 36, "result": { "turn": { "id": "turn_460", "status": "inProgress", "items": [], "error": null } } } +``` + +### Example: Inject raw history items + +Use `thread/inject_items` to append prebuilt Responses API items to a loaded thread’s prompt history without starting a turn. These items are persisted to the rollout and included in subsequent model requests. A standalone `function_call_output` can omit `call_id` when it has a nonempty `name`; `namespace` is optional, and the output retains tool-tier authority. Any `input_image` items must use inline data URLs; remote HTTP(S) image URLs are rejected. History-only outputs are not exposed as thread items. + +```json +{ "method": "thread/inject_items", "id": 37, "params": { "threadId": "thr_123", "items": [ { @@ -1066,7 +1083,7 @@ Use `thread/inject_items` to append prebuilt Responses API items to a loaded thr } ] } } -{ "id": 36, "result": {} } +{ "id": 37, "result": {} } ``` ### Example: Start realtime with WebRTC @@ -1706,6 +1723,7 @@ The app-server streams JSON-RPC notifications while a turn is running. Each turn `ThreadItem` is the tagged union carried in turn responses and `item/*` notifications. Currently we support events for the following items: - `userMessage` — `{id, clientId, content}` where `clientId` is the optional `clientUserMessageId` supplied to `turn/start` or `turn/steer`, and `content` is a list of user inputs (`text`, `image`, `localImage`, `audio`, or `localAudio`). +- `functionCallOutput` — `{id, name, namespace, output}` for a standalone function-call output without a `call_id`. `namespace` is nullable, and `output` is either a string or structured content items. Clients decide whether to render these tool-authority items; ordinary paired function-call outputs are not emitted separately. - `agentMessage` — `{id, text, phase, memoryCitation, delivery}` containing the accumulated agent reply. `delivery: "async"` identifies a user-visible message sent without ending the current turn; ordinary agent messages have `delivery: null`. - `plan` — `{id, text}` emitted for plan-mode turns; plan text can stream via `item/plan/delta` (experimental). - `reasoning` — `{id, summary, content}` where `summary` holds streamed reasoning summaries (applicable for most OpenAI models) and `content` holds raw reasoning blocks (applicable for e.g. open source models). diff --git a/codex-rs/app-server/src/message_processor_tracing_tests.rs b/codex-rs/app-server/src/message_processor_tracing_tests.rs index 2ffd5e76eb..842563e62b 100644 --- a/codex-rs/app-server/src/message_processor_tracing_tests.rs +++ b/codex-rs/app-server/src/message_processor_tracing_tests.rs @@ -664,6 +664,7 @@ async fn turn_start_jsonrpc_span_parents_core_turn_spans() -> Result<()> { text_elements: Vec::new(), }], turn_trigger: None, + tool_output: None, responsesapi_client_metadata: None, additional_context: None, cwd: None, diff --git a/codex-rs/app-server/src/request_processors/turn_processor.rs b/codex-rs/app-server/src/request_processors/turn_processor.rs index a209055d75..84047d2d2f 100644 --- a/codex-rs/app-server/src/request_processors/turn_processor.rs +++ b/codex-rs/app-server/src/request_processors/turn_processor.rs @@ -5,7 +5,9 @@ use codex_agent_extension::AgentRun; use codex_agent_extension::AgentRunner; use codex_protocol::error::CodexErrorDetails; use codex_protocol::models::ContentItem; +use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::FunctionCallOutputContentItem; +use codex_protocol::models::FunctionCallOutputPayload; use codex_protocol::protocol::AdditionalContextEntry as CoreAdditionalContextEntry; use codex_protocol::protocol::AdditionalContextKind as CoreAdditionalContextKind; use codex_protocol::protocol::TurnSettingsUpdate; @@ -509,7 +511,38 @@ impl TurnRequestProcessor { })?; self.ensure_direct_input_allowed(&request_id, thread.as_ref()) .await?; - if let Err(error) = Self::validate_v2_input_limit(¶ms.input) { + if let Some(tool_output) = ¶ms.tool_output { + if !params.input.is_empty() { + return Err(invalid_request( + "`toolOutput` cannot be combined with nonempty `input`", + )); + } + if tool_output.name.is_empty() { + return Err(invalid_request("`toolOutput.name` must not be empty")); + } + } + let actual_chars = params + .input + .iter() + .map(V2UserInput::text_char_count) + .sum::() + + params + .tool_output + .as_ref() + .map_or(0, |output| match &output.output { + FunctionCallOutputBody::Text(text) => text.chars().count(), + FunctionCallOutputBody::ContentItems(items) => items + .iter() + .map(|item| match item { + FunctionCallOutputContentItem::InputText { text } => { + text.chars().count() + } + _ => 0, + }) + .sum(), + }); + if actual_chars > MAX_USER_INPUT_TEXT_CHARS { + let error = Self::input_too_large_error(actual_chars); self.track_error_response( &request_id, &error, @@ -532,15 +565,32 @@ impl TurnRequestProcessor { let environment_selections = resolve_turn_environment_selections(self.thread_manager.as_ref(), params.environments)?; - // Map v2 input items to core input items. - let mapped_items: Vec = params - .input - .into_iter() - .map(V2UserInput::into_core) - .collect(); - let client_user_message_id = params.client_user_message_id; let additional_context = map_additional_context(params.additional_context); - let turn_has_input = !mapped_items.is_empty(); + let turn_has_input = !params.input.is_empty(); + let input = if let Some(tool_output) = params.tool_output { + let item = ResponseItem::FunctionCallOutput { + id: None, + call_id: None, + name: Some(tool_output.name), + namespace: tool_output.namespace, + output: FunctionCallOutputPayload { + body: tool_output.output, + success: None, + }, + internal_chat_message_metadata_passthrough: None, + }; + validate_response_item_image_urls(std::slice::from_ref(&item))?; + TurnInput::ResponseItem(item) + } else { + TurnInput::UserInput { + content: params + .input + .into_iter() + .map(V2UserInput::into_core) + .collect(), + client_id: params.client_user_message_id, + } + }; let cwd = resolve_request_cwd(params.cwd)?; let environments = self .build_environment_override( @@ -569,26 +619,25 @@ impl TurnRequestProcessor { }, ) .await?; - self.seal_realtime_transcript_before_user_input(thread_id, &mapped_items) - .await?; + if let TurnInput::UserInput { content, .. } = &input { + self.seal_realtime_transcript_before_user_input(thread_id, content) + .await?; + } let submission = thread .start_or_steer_turn( - TurnInputRequest::new(TurnInput::UserInput { - content: mapped_items, - client_id: client_user_message_id, - }) - .with_thread_settings(thread_settings) - .on_start(TurnStartOptions { - turn_trigger: params.turn_trigger, - final_output_json_schema: params.output_schema, - service_tier: params.service_tier_for_turn, - cyber_access_program: params.cyber_access_program.map(Into::into), - ..Default::default() - }) - .with_additional_context(additional_context) - .with_responses_metadata(params.responsesapi_client_metadata) - .with_trace(self.request_trace_context(&request_id).await), + TurnInputRequest::new(input) + .with_thread_settings(thread_settings) + .on_start(TurnStartOptions { + turn_trigger: params.turn_trigger, + final_output_json_schema: params.output_schema, + service_tier: params.service_tier_for_turn, + cyber_access_program: params.cyber_access_program.map(Into::into), + ..Default::default() + }) + .with_additional_context(additional_context) + .with_responses_metadata(params.responsesapi_client_metadata) + .with_trace(self.request_trace_context(&request_id).await), ) .await .map_err(|err| { diff --git a/codex-rs/app-server/tests/suite/v2/thread_inject_items.rs b/codex-rs/app-server/tests/suite/v2/thread_inject_items.rs index 20415effba..086604ec5c 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_inject_items.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_inject_items.rs @@ -4,17 +4,27 @@ use app_test_support::MockResponsesConfig; use app_test_support::TestAppServer; use codex_app_server_protocol::AdditionalContextEntry; use codex_app_server_protocol::AdditionalContextKind; +use codex_app_server_protocol::ItemCompletedNotification; +use codex_app_server_protocol::ItemStartedNotification; +use codex_app_server_protocol::ThreadHistoryMode; use codex_app_server_protocol::ThreadInjectItemsParams; use codex_app_server_protocol::ThreadInjectItemsResponse; +use codex_app_server_protocol::ThreadItem; +use codex_app_server_protocol::ThreadResumeParams; +use codex_app_server_protocol::ThreadResumeResponse; use codex_app_server_protocol::ThreadStartParams; use codex_app_server_protocol::ThreadStartResponse; use codex_app_server_protocol::TurnStartParams; use codex_app_server_protocol::TurnStartResponse; +use codex_app_server_protocol::TurnStartedNotification; +use codex_app_server_protocol::TurnStatus; +use codex_app_server_protocol::TurnToolOutput; use codex_app_server_protocol::UserInput as V2UserInput; use codex_core::RolloutRecorder; use codex_features::Feature; use codex_protocol::ThreadId; use codex_protocol::models::ContentItem; +use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::ResponseItem; use codex_rollout::InitialHistory; use codex_rollout::RolloutItem; @@ -27,12 +37,17 @@ use serde_json::Value; use serde_json::json; use std::collections::HashMap; use tempfile::TempDir; +use test_case::test_case; use tokio::time::timeout; const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); +#[test_case(ThreadHistoryMode::Legacy; "legacy")] +#[test_case(ThreadHistoryMode::Paginated; "paginated")] #[tokio::test] -async fn thread_inject_items_adds_raw_response_items_to_thread_history() -> Result<()> { +async fn thread_inject_items_adds_raw_response_items_to_thread_history( + history_mode: ThreadHistoryMode, +) -> Result<()> { let server = responses::start_mock_server().await; let body = responses::sse(vec![ responses::ev_response_created("resp-1"), @@ -61,6 +76,7 @@ async fn thread_inject_items_adds_raw_response_items_to_thread_history() -> Resu let thread_req = mcp .send_thread_start_request_with_auto_env(ThreadStartParams { model: Some("mock-model".to_string()), + history_mode: Some(history_mode), ..Default::default() }) .await?; @@ -110,6 +126,8 @@ async fn thread_inject_items_adds_raw_response_items_to_thread_history() -> Resu .await?; let _response: ThreadInjectItemsResponse = timeout(DEFAULT_READ_TIMEOUT, mcp.read_response(inject_req)).await??; + assert!(response_mock.requests().is_empty()); + assert_eq!( state_db .get_thread_memory_mode(ThreadId::from_string(&thread.id)?) @@ -266,6 +284,79 @@ async fn thread_inject_items_adds_raw_response_items_to_thread_history() -> Resu "named unpaired tool output should be sent in the next model request" ); + let delegated_mock = responses::mount_sse_once( + &server, + responses::sse(vec![responses::ev_completed("resp-delegated")]), + ) + .await; + mcp.clear_message_buffer(); + let delegated_req = mcp + .send_turn_start_request(TurnStartParams { + thread_id: thread.id.clone(), + input: Vec::new(), + tool_output: Some(Box::new(TurnToolOutput { + name: "send_message_to_thread".to_string(), + namespace: Some("codex_app".to_string()), + output: FunctionCallOutputBody::Text("Start a delegated turn.".to_string()), + })), + ..Default::default() + }) + .await?; + let TurnStartResponse { turn } = + timeout(DEFAULT_READ_TIMEOUT, mcp.read_response(delegated_req)).await??; + let turn_started: TurnStartedNotification = + timeout(DEFAULT_READ_TIMEOUT, mcp.read_notification("turn/started")).await??; + let item_started: ItemStartedNotification = + timeout(DEFAULT_READ_TIMEOUT, mcp.read_notification("item/started")).await??; + let item_completed: ItemCompletedNotification = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_notification("item/completed"), + ) + .await??; + timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_notification_message("turn/completed"), + ) + .await??; + assert_eq!(turn.id, turn_started.turn.id); + assert_eq!(turn_started.turn.status, TurnStatus::InProgress); + assert_eq!(item_completed.turn_id, turn_started.turn.id); + assert_eq!(item_started.item, item_completed.item); + assert!(matches!( + &item_completed.item, + ThreadItem::FunctionCallOutput { + name, + namespace, + output: FunctionCallOutputBody::Text(output), + .. + } if name == "send_message_to_thread" + && namespace.as_deref() == Some("codex_app") + && output == "Start a delegated turn." + )); + assert!( + delegated_mock + .single_request() + .input() + .into_iter() + .any(|item| item["type"] == "function_call_output" + && item["output"] == "Start a delegated turn."), + "the delegated turn must preserve the original model-visible tool output" + ); + let resume_req = mcp + .send_thread_resume_request(ThreadResumeParams { + thread_id: thread.id, + ..Default::default() + }) + .await?; + let ThreadResumeResponse { thread, .. } = + timeout(DEFAULT_READ_TIMEOUT, mcp.read_response(resume_req)).await??; + assert_eq!(thread.turns.len(), 2); + assert!(thread.turns.iter().any(|turn| { + turn.id == turn_started.turn.id + && turn.status == TurnStatus::Completed + && turn.items.contains(&item_completed.item) + })); + Ok(()) } diff --git a/codex-rs/app-server/tests/suite/v2/turn_start.rs b/codex-rs/app-server/tests/suite/v2/turn_start.rs index 76cf9d4148..8b7cff939b 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_start.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_start.rs @@ -1473,15 +1473,15 @@ async fn turn_start_rejects_combined_oversized_text_input() -> Result<()> { let turn_req = mcp .send_turn_start_request(TurnStartParams { - thread_id: thread.id, + thread_id: thread.id.clone(), client_user_message_id: None, input: vec![ V2UserInput::Text { - text: first, + text: first.clone(), text_elements: Vec::new(), }, V2UserInput::Text { - text: second, + text: second.clone(), text_elements: Vec::new(), }, ], @@ -1504,6 +1504,30 @@ async fn turn_start_rejects_combined_oversized_text_input() -> Result<()> { assert_eq!(data["max_chars"], MAX_USER_INPUT_TEXT_CHARS); assert_eq!(data["actual_chars"], actual_chars); + let tool_req = mcp + .send_raw_request( + "turn/start", + Some(json!({ + "threadId": thread.id, + "input": [], + "toolOutput": { + "name": "send_message_to_thread", + "output": [ + { "type": "input_text", "text": first }, + { "type": "input_text", "text": second }, + ], + }, + })), + ) + .await?; + let err: JSONRPCError = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_error_message(RequestId::Integer(tool_req)), + ) + .await??; + assert_eq!(err.error.code, INVALID_PARAMS_ERROR_CODE); + assert_eq!(err.error.data, Some(data)); + let turn_started = tokio::time::timeout( std::time::Duration::from_millis(250), mcp.read_stream_until_notification_message("turn/started"), @@ -3003,6 +3027,7 @@ async fn turn_start_explicit_local_environment_updates_legacy_cwd_between_turns( text_elements: Vec::new(), }], turn_trigger: None, + tool_output: None, responsesapi_client_metadata: None, additional_context: None, cwd: Some(first_cwd.clone()), @@ -3054,6 +3079,7 @@ async fn turn_start_explicit_local_environment_updates_legacy_cwd_between_turns( text_elements: Vec::new(), }], turn_trigger: None, + tool_output: None, responsesapi_client_metadata: None, additional_context: None, cwd: None, diff --git a/codex-rs/core/src/hook_runtime.rs b/codex-rs/core/src/hook_runtime.rs index b0f0f9cdc2..3f0b6b4b22 100644 --- a/codex-rs/core/src/hook_runtime.rs +++ b/codex-rs/core/src/hook_runtime.rs @@ -25,6 +25,7 @@ use codex_hooks::hook_execution_mode_label; use codex_hooks::hook_handler_type_label; use codex_otel::HOOK_RUN_DURATION_METRIC; use codex_otel::HOOK_RUN_METRIC; +use codex_protocol::items::FunctionCallOutputItem; use codex_protocol::items::TurnItem; use codex_protocol::items::UserMessageItem; use codex_protocol::models::ResponseItem; @@ -659,6 +660,24 @@ pub(crate) async fn record_pending_input( TurnInput::FunctionCallOutput(item) => { sess.record_conversation_items(turn_context, std::slice::from_ref(&item)) .await; + if let ResponseItem::FunctionCallOutput { + id: Some(id), + name: Some(name), + namespace, + output, + .. + } = item + { + let item = TurnItem::FunctionCallOutput(FunctionCallOutputItem { + id: id.to_string(), + name, + namespace, + output: output.body, + }); + sess.emit_turn_item_started(turn_context, &item).await; + sess.emit_turn_item_completed(turn_context, item).await; + } + sess.ensure_rollout_materialized(persist_context).await; } TurnInput::InterAgentCommunication(communication) => { sess.record_inter_agent_communication(turn_context, communication) diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index 5f2984e4c8..b449a7195f 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -976,6 +976,7 @@ async fn run_exec_session(args: ExecRunArgs) -> anyhow::Result<()> { turn_trigger: None, client_user_message_id: None, input: items.into_iter().map(Into::into).collect(), + tool_output: None, responsesapi_client_metadata: None, additional_context: None, environments: None, diff --git a/codex-rs/protocol/src/items.rs b/codex-rs/protocol/src/items.rs index d7f4dddc5a..d7d649f65d 100644 --- a/codex-rs/protocol/src/items.rs +++ b/codex-rs/protocol/src/items.rs @@ -5,6 +5,7 @@ use crate::dynamic_tools::DynamicToolCallOutputContentItem; use crate::mcp::CallToolResult; use crate::memory_citation::MemoryCitation; use crate::models::ContentItem; +use crate::models::FunctionCallOutputBody; use crate::models::ImageDetail; use crate::models::MessagePhase; use crate::models::ResponseItem; @@ -43,6 +44,7 @@ use ts_rs::TS; #[ts(tag = "type")] pub enum TurnItem { UserMessage(UserMessageItem), + FunctionCallOutput(FunctionCallOutputItem), HookPrompt(HookPromptItem), AgentMessage(AgentMessageItem), Plan(PlanItem), @@ -83,6 +85,16 @@ pub struct UserMessageItem { pub content: Vec, } +#[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema)] +pub struct FunctionCallOutputItem { + pub id: String, + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub namespace: Option, + pub output: FunctionCallOutputBody, +} + #[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema, PartialEq, Eq)] pub struct HookPromptItem { pub id: String, @@ -677,6 +689,7 @@ impl TurnItem { pub fn id(&self) -> String { match self { TurnItem::UserMessage(item) => item.id.clone(), + TurnItem::FunctionCallOutput(item) => item.id.clone(), TurnItem::HookPrompt(item) => item.id.clone(), TurnItem::AgentMessage(item) => item.id.clone(), TurnItem::Plan(item) => item.id.clone(), diff --git a/codex-rs/protocol/src/legacy_events.rs b/codex-rs/protocol/src/legacy_events.rs index 0d1a8d5cb3..10a807c1cd 100644 --- a/codex-rs/protocol/src/legacy_events.rs +++ b/codex-rs/protocol/src/legacy_events.rs @@ -520,7 +520,7 @@ impl TurnItem { pub fn as_legacy_events(&self, show_raw_agent_reasoning: bool) -> Vec { match self { TurnItem::UserMessage(item) => vec![item.as_legacy_event()], - TurnItem::HookPrompt(_) => Vec::new(), + TurnItem::FunctionCallOutput(_) | TurnItem::HookPrompt(_) => Vec::new(), TurnItem::AgentMessage(item) => item.as_legacy_events(), TurnItem::Plan(_) => Vec::new(), TurnItem::CommandExecution(_) diff --git a/codex-rs/rollout/src/persistence_metrics.rs b/codex-rs/rollout/src/persistence_metrics.rs index b26320cd8a..968578b60f 100644 --- a/codex-rs/rollout/src/persistence_metrics.rs +++ b/codex-rs/rollout/src/persistence_metrics.rs @@ -261,6 +261,7 @@ fn rollout_item_type(item: &RolloutItem) -> String { fn turn_item_type(item: &TurnItem) -> &'static str { match item { TurnItem::UserMessage(_) => "user_message", + TurnItem::FunctionCallOutput(_) => "function_call_output", TurnItem::HookPrompt(_) => "hook_prompt", TurnItem::AgentMessage(_) => "agent_message", TurnItem::Plan(_) => "plan", diff --git a/codex-rs/rollout/src/policy.rs b/codex-rs/rollout/src/policy.rs index 2334852b19..418f9ac345 100644 --- a/codex-rs/rollout/src/policy.rs +++ b/codex-rs/rollout/src/policy.rs @@ -96,7 +96,9 @@ pub fn should_persist_event_msg(ev: &EventMsg, history_mode: ThreadHistoryMode) matches!(history_mode, ThreadHistoryMode::Paginated) || matches!( event.item, - TurnItem::Plan(_) | TurnItem::Extension(ExtensionItem::Sleep(_)) + TurnItem::FunctionCallOutput(_) + | TurnItem::Plan(_) + | TurnItem::Extension(ExtensionItem::Sleep(_)) ) || matches!( &event.item, diff --git a/codex-rs/thread-store/src/local/thread_history.rs b/codex-rs/thread-store/src/local/thread_history.rs index 8f5ca8c6c3..7b31841842 100644 --- a/codex-rs/thread-store/src/local/thread_history.rs +++ b/codex-rs/thread-store/src/local/thread_history.rs @@ -518,6 +518,7 @@ WHERE thread_id = ? .. } | ThreadItem::HookPrompt { .. } + | ThreadItem::FunctionCallOutput { .. } | ThreadItem::Plan { .. } | ThreadItem::Reasoning { .. } | ThreadItem::CommandExecution { .. } diff --git a/codex-rs/thread-store/src/local/thread_history/search.rs b/codex-rs/thread-store/src/local/thread_history/search.rs index 28e0b55a31..f93fe61eb3 100644 --- a/codex-rs/thread-store/src/local/thread_history/search.rs +++ b/codex-rs/thread-store/src/local/thread_history/search.rs @@ -332,6 +332,7 @@ fn searchable_text(item: &ThreadItem) -> Option> { (!text.is_empty()).then_some(Cow::Owned(text)) } ThreadItem::HookPrompt { .. } + | ThreadItem::FunctionCallOutput { .. } | ThreadItem::Plan { .. } | ThreadItem::Reasoning { .. } | ThreadItem::CommandExecution { .. } diff --git a/codex-rs/tui/src/app/agent_status_feed.rs b/codex-rs/tui/src/app/agent_status_feed.rs index 081606e56d..1039b043ee 100644 --- a/codex-rs/tui/src/app/agent_status_feed.rs +++ b/codex-rs/tui/src/app/agent_status_feed.rs @@ -192,7 +192,10 @@ fn activity_summary(item: &ThreadItem) -> Option { ThreadItem::EnteredReviewMode { .. } => return Some("Entered review mode".to_string()), ThreadItem::ExitedReviewMode { .. } => return Some("Exited review mode".to_string()), ThreadItem::ContextCompaction { .. } => return Some("Compacted context".to_string()), - ThreadItem::UserMessage { .. } | ThreadItem::HookPrompt { .. } | ThreadItem::Sleep(_) => { + ThreadItem::UserMessage { .. } + | ThreadItem::HookPrompt { .. } + | ThreadItem::FunctionCallOutput { .. } + | ThreadItem::Sleep(_) => { return None; } }; diff --git a/codex-rs/tui/src/app_server_session.rs b/codex-rs/tui/src/app_server_session.rs index f4267a0f73..ae0a5977c7 100644 --- a/codex-rs/tui/src/app_server_session.rs +++ b/codex-rs/tui/src/app_server_session.rs @@ -1208,6 +1208,7 @@ impl AppServerSession { turn_trigger: None, client_user_message_id: None, input: items, + tool_output: None, responsesapi_client_metadata: None, additional_context: None, environments: None, diff --git a/codex-rs/tui/src/chatwidget/replay.rs b/codex-rs/tui/src/chatwidget/replay.rs index bbfa13a796..62cd7827d9 100644 --- a/codex-rs/tui/src/chatwidget/replay.rs +++ b/codex-rs/tui/src/chatwidget/replay.rs @@ -218,7 +218,7 @@ impl ChatWidget { ThreadItem::ContextCompaction { .. } => { self.add_info_message("Context compacted".to_string(), /*hint*/ None); } - ThreadItem::HookPrompt { .. } => {} + ThreadItem::HookPrompt { .. } | ThreadItem::FunctionCallOutput { .. } => {} ThreadItem::CollabAgentToolCall { id, tool, diff --git a/codex-rs/tui/src/dynamic_tools.rs b/codex-rs/tui/src/dynamic_tools.rs index 62f1d77e22..f1f9ccbcab 100644 --- a/codex-rs/tui/src/dynamic_tools.rs +++ b/codex-rs/tui/src/dynamic_tools.rs @@ -1061,6 +1061,7 @@ async fn execute_inner( "name": "webSearch", "status": null })), ThreadItem::UserMessage { .. } + | ThreadItem::FunctionCallOutput { .. } | ThreadItem::HookPrompt { .. } | ThreadItem::AgentMessage { .. } | ThreadItem::Plan { .. } @@ -1316,6 +1317,23 @@ fn turn_summary(turn: &Turn, include_outputs: bool, output_chars: usize) -> Valu ThreadItem::HookPrompt { id, fragments } => json!({ "type": "hookPrompt", "id": id, "fragmentCount": fragments.len() }), + ThreadItem::FunctionCallOutput { + id, + name, + namespace, + output, + } => { + let mut item = json!({ + "type": "functionCallOutput", "id": id, "name": name, "namespace": namespace + }); + if include_outputs { + item["output"] = output_summary( + output.to_text().as_deref().unwrap_or("[non-text output]"), + output_chars, + ); + } + item + } ThreadItem::AgentMessage { id, text, phase, .. } => json!({ "type": "agentMessage", "id": id, "text": truncate(text, DEFAULT_OUTPUT_CHARS), "phase": phase }), diff --git a/codex-rs/tui/src/thread_transcript.rs b/codex-rs/tui/src/thread_transcript.rs index 18ff7f41de..d6824661d8 100644 --- a/codex-rs/tui/src/thread_transcript.rs +++ b/codex-rs/tui/src/thread_transcript.rs @@ -281,6 +281,7 @@ fn fallback_transcript_cell(item: &ThreadItem) -> Option { } ThreadItem::UserMessage { .. } | ThreadItem::AgentMessage { .. } + | ThreadItem::FunctionCallOutput { .. } | ThreadItem::Plan { .. } | ThreadItem::Reasoning { .. } | ThreadItem::Sleep(_) => return None,