mirror of
https://github.com/openai/codex.git
synced 2026-10-19 16:17:49 +00:00
Add opt-in output token replay for OpenAI requests (#52742)
## What changed - Add the disabled-by-default `output_token_replay` feature to request `output.encrypted_content` from OpenAI providers. - Preserve encrypted message and tool-call output, message and function-call status, and output-text `annotations` and `logprobs` through history and replay. Update the generated protocol schemas and SDK types. - Strip output ciphertext from requests to other providers while retaining it in stored history, including after replay is disabled. Remove encrypted content from memory extraction inputs. ## Testing Add integration coverage for the feature/provider combinations, metadata replay across follow-up requests, and history preservation when resuming with replay disabled. GitOrigin-RevId: 77729a021b6771933402aebe74ece8eb39c057e2
This commit is contained in:
@@ -733,6 +733,20 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"annotations": {
|
||||||
|
"items": true,
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"logprobs": {
|
||||||
|
"items": true,
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"text": {
|
"text": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -2988,6 +3002,12 @@
|
|||||||
},
|
},
|
||||||
"type": "array"
|
"type": "array"
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
@@ -3017,6 +3037,12 @@
|
|||||||
"role": {
|
"role": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"status": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"message"
|
"message"
|
||||||
@@ -3147,6 +3173,12 @@
|
|||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"description": "Legacy id field retained for compatibility with older payloads.",
|
"description": "Legacy id field retained for compatibility with older payloads.",
|
||||||
"type": [
|
"type": [
|
||||||
@@ -3191,6 +3223,12 @@
|
|||||||
"call_id": {
|
"call_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"encrypted_function_args": {
|
"encrypted_function_args": {
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@@ -3225,6 +3263,12 @@
|
|||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"status": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"function_call"
|
"function_call"
|
||||||
@@ -3251,6 +3295,12 @@
|
|||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"execution": {
|
"execution": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -3351,6 +3401,12 @@
|
|||||||
"call_id": {
|
"call_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
@@ -3511,6 +3567,12 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
@@ -3549,6 +3611,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
|
|||||||
+68
@@ -10746,6 +10746,20 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"annotations": {
|
||||||
|
"items": true,
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"logprobs": {
|
||||||
|
"items": true,
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"text": {
|
"text": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -18845,6 +18859,12 @@
|
|||||||
},
|
},
|
||||||
"type": "array"
|
"type": "array"
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
@@ -18874,6 +18894,12 @@
|
|||||||
"role": {
|
"role": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"status": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"message"
|
"message"
|
||||||
@@ -19004,6 +19030,12 @@
|
|||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"description": "Legacy id field retained for compatibility with older payloads.",
|
"description": "Legacy id field retained for compatibility with older payloads.",
|
||||||
"type": [
|
"type": [
|
||||||
@@ -19048,6 +19080,12 @@
|
|||||||
"call_id": {
|
"call_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"encrypted_function_args": {
|
"encrypted_function_args": {
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@@ -19082,6 +19120,12 @@
|
|||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"status": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"function_call"
|
"function_call"
|
||||||
@@ -19108,6 +19152,12 @@
|
|||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"execution": {
|
"execution": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -19208,6 +19258,12 @@
|
|||||||
"call_id": {
|
"call_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
@@ -19368,6 +19424,12 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
@@ -19406,6 +19468,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
|
|||||||
+68
@@ -6562,6 +6562,20 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"annotations": {
|
||||||
|
"items": true,
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"logprobs": {
|
||||||
|
"items": true,
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"text": {
|
"text": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -14837,6 +14851,12 @@
|
|||||||
},
|
},
|
||||||
"type": "array"
|
"type": "array"
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
@@ -14866,6 +14886,12 @@
|
|||||||
"role": {
|
"role": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"status": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"message"
|
"message"
|
||||||
@@ -14996,6 +15022,12 @@
|
|||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"description": "Legacy id field retained for compatibility with older payloads.",
|
"description": "Legacy id field retained for compatibility with older payloads.",
|
||||||
"type": [
|
"type": [
|
||||||
@@ -15040,6 +15072,12 @@
|
|||||||
"call_id": {
|
"call_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"encrypted_function_args": {
|
"encrypted_function_args": {
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@@ -15074,6 +15112,12 @@
|
|||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"status": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"function_call"
|
"function_call"
|
||||||
@@ -15100,6 +15144,12 @@
|
|||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"execution": {
|
"execution": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -15200,6 +15250,12 @@
|
|||||||
"call_id": {
|
"call_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
@@ -15360,6 +15416,12 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
@@ -15398,6 +15460,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
|
|||||||
+68
@@ -153,6 +153,20 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"annotations": {
|
||||||
|
"items": true,
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"logprobs": {
|
||||||
|
"items": true,
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"text": {
|
"text": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -528,6 +542,12 @@
|
|||||||
},
|
},
|
||||||
"type": "array"
|
"type": "array"
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
@@ -557,6 +577,12 @@
|
|||||||
"role": {
|
"role": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"status": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"message"
|
"message"
|
||||||
@@ -687,6 +713,12 @@
|
|||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"description": "Legacy id field retained for compatibility with older payloads.",
|
"description": "Legacy id field retained for compatibility with older payloads.",
|
||||||
"type": [
|
"type": [
|
||||||
@@ -731,6 +763,12 @@
|
|||||||
"call_id": {
|
"call_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"encrypted_function_args": {
|
"encrypted_function_args": {
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@@ -765,6 +803,12 @@
|
|||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"status": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"function_call"
|
"function_call"
|
||||||
@@ -791,6 +835,12 @@
|
|||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"execution": {
|
"execution": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -891,6 +941,12 @@
|
|||||||
"call_id": {
|
"call_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
@@ -1051,6 +1107,12 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
@@ -1089,6 +1151,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
|
|||||||
@@ -215,6 +215,20 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"annotations": {
|
||||||
|
"items": true,
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"logprobs": {
|
||||||
|
"items": true,
|
||||||
|
"type": [
|
||||||
|
"array",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"text": {
|
"text": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -599,6 +613,12 @@
|
|||||||
},
|
},
|
||||||
"type": "array"
|
"type": "array"
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
@@ -628,6 +648,12 @@
|
|||||||
"role": {
|
"role": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"status": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"message"
|
"message"
|
||||||
@@ -758,6 +784,12 @@
|
|||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"description": "Legacy id field retained for compatibility with older payloads.",
|
"description": "Legacy id field retained for compatibility with older payloads.",
|
||||||
"type": [
|
"type": [
|
||||||
@@ -802,6 +834,12 @@
|
|||||||
"call_id": {
|
"call_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"encrypted_function_args": {
|
"encrypted_function_args": {
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@@ -836,6 +874,12 @@
|
|||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"status": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"type": {
|
"type": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"function_call"
|
"function_call"
|
||||||
@@ -862,6 +906,12 @@
|
|||||||
"null"
|
"null"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"execution": {
|
"execution": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -962,6 +1012,12 @@
|
|||||||
"call_id": {
|
"call_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
@@ -1122,6 +1178,12 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
@@ -1160,6 +1222,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"encrypted_content": {
|
||||||
|
"type": [
|
||||||
|
"string",
|
||||||
|
"null"
|
||||||
|
]
|
||||||
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": [
|
"type": [
|
||||||
"string",
|
"string",
|
||||||
|
|||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -3,4 +3,4 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
import type { ImageDetail } from "./ImageDetail";
|
import type { ImageDetail } from "./ImageDetail";
|
||||||
|
|
||||||
export type ContentItem = { "type": "input_text", text: string, } | { "type": "input_image", detail?: ImageDetail, } & ({ image_url: string, } | { file_id: string, }) | { "type": "input_audio", audio_url: string, } | { "type": "output_text", text: string, };
|
export type ContentItem = { "type": "input_text", text: string, } | { "type": "input_image", detail?: ImageDetail, } & ({ image_url: string, } | { file_id: string, }) | { "type": "input_audio", audio_url: string, } | { "type": "output_text", text: string, annotations?: unknown[], logprobs?: unknown[], };
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import type { ReasoningItemReasoningSummary } from "./ReasoningItemReasoningSumm
|
|||||||
import type { ResponseItemId } from "./ResponseItemId";
|
import type { ResponseItemId } from "./ResponseItemId";
|
||||||
import type { WebSearchAction } from "./WebSearchAction";
|
import type { WebSearchAction } from "./WebSearchAction";
|
||||||
|
|
||||||
export type ResponseItem = { "type": "additional_tools", id?: ResponseItemId, role: string, tools: unknown[], } | { "type": "message", id?: ResponseItemId, role: string, content: Array<ContentItem>, phase?: MessagePhase, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "agent_message", id?: ResponseItemId, author: string, recipient: string, content: Array<AgentMessageInputContent>, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "reasoning", id?: ResponseItemId, summary: Array<ReasoningItemReasoningSummary>, content?: Array<ReasoningItemContent>, encrypted_content: string | null, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "local_shell_call",
|
export type ResponseItem = { "type": "additional_tools", id?: ResponseItemId, role: string, tools: unknown[], } | { "type": "message", encrypted_content?: string, status?: string, id?: ResponseItemId, role: string, content: Array<ContentItem>, phase?: MessagePhase, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "agent_message", id?: ResponseItemId, author: string, recipient: string, content: Array<AgentMessageInputContent>, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "reasoning", id?: ResponseItemId, summary: Array<ReasoningItemReasoningSummary>, content?: Array<ReasoningItemContent>, encrypted_content: string | null, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "local_shell_call", encrypted_content?: string,
|
||||||
/**
|
/**
|
||||||
* Legacy id field retained for compatibility with older payloads.
|
* Legacy id field retained for compatibility with older payloads.
|
||||||
*/
|
*/
|
||||||
@@ -22,4 +22,4 @@ id?: ResponseItemId,
|
|||||||
/**
|
/**
|
||||||
* Set when using the Responses API.
|
* Set when using the Responses API.
|
||||||
*/
|
*/
|
||||||
call_id: string | null, status: LocalShellStatus, action: LocalShellAction, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "function_call", id?: ResponseItemId, name: string, namespace?: string, arguments: string, encrypted_function_args?: Array<string>, call_id: string, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "tool_search_call", id?: ResponseItemId, call_id: string | null, status?: string, execution: string, arguments: unknown, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "function_call_output", id?: ResponseItemId, call_id?: string, name?: string, namespace?: string, output: FunctionCallOutputBody, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "custom_tool_call", id?: ResponseItemId, status?: string, call_id: string, name: string, namespace?: string, input: string, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "custom_tool_call_output", id?: ResponseItemId, call_id: string, name?: string, output: FunctionCallOutputBody, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "tool_search_output", id?: ResponseItemId, call_id: string | null, status: string, execution: string, tools: unknown[], internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "web_search_call", id?: ResponseItemId, status?: string, action?: WebSearchAction, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "image_generation_call", id?: ResponseItemId, status: string, revised_prompt?: string, result: string, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "compaction", id?: ResponseItemId, encrypted_content: string, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "configuration_update", reasoning: ConfigurationReasoning, } | { "type": "compaction_trigger", } | { "type": "context_compaction", id?: ResponseItemId, encrypted_content?: string, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "other" };
|
call_id: string | null, status: LocalShellStatus, action: LocalShellAction, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "function_call", encrypted_content?: string, status?: string, id?: ResponseItemId, name: string, namespace?: string, arguments: string, encrypted_function_args?: Array<string>, call_id: string, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "tool_search_call", encrypted_content?: string, id?: ResponseItemId, call_id: string | null, status?: string, execution: string, arguments: unknown, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "function_call_output", id?: ResponseItemId, call_id?: string, name?: string, namespace?: string, output: FunctionCallOutputBody, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "custom_tool_call", encrypted_content?: string, id?: ResponseItemId, status?: string, call_id: string, name: string, namespace?: string, input: string, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "custom_tool_call_output", id?: ResponseItemId, call_id: string, name?: string, output: FunctionCallOutputBody, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "tool_search_output", id?: ResponseItemId, call_id: string | null, status: string, execution: string, tools: unknown[], internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "web_search_call", encrypted_content?: string, id?: ResponseItemId, status?: string, action?: WebSearchAction, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "image_generation_call", encrypted_content?: string, id?: ResponseItemId, status: string, revised_prompt?: string, result: string, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "compaction", id?: ResponseItemId, encrypted_content: string, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "configuration_update", reasoning: ConfigurationReasoning, } | { "type": "compaction_trigger", } | { "type": "context_compaction", id?: ResponseItemId, encrypted_content?: string, internal_chat_message_metadata_passthrough?: InternalChatMessageMetadataPassthrough, } | { "type": "other" };
|
||||||
|
|||||||
@@ -5233,6 +5233,8 @@ mod tests {
|
|||||||
})),
|
})),
|
||||||
RolloutItem::ResponseItem(
|
RolloutItem::ResponseItem(
|
||||||
codex_protocol::models::ResponseItem::Message {
|
codex_protocol::models::ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(codex_protocol::ResponseItemId::with_suffix("msg", "1")),
|
id: Some(codex_protocol::ResponseItemId::with_suffix("msg", "1")),
|
||||||
role: "user".into(),
|
role: "user".into(),
|
||||||
content: vec![codex_protocol::models::ContentItem::InputText {
|
content: vec![codex_protocol::models::ContentItem::InputText {
|
||||||
|
|||||||
@@ -615,6 +615,8 @@ async fn metadata_and_mcp_requests_complete_while_unrelated_resume_loads_config(
|
|||||||
thread_id: "00000000-0000-4000-8000-000000000000".to_string(),
|
thread_id: "00000000-0000-4000-8000-000000000000".to_string(),
|
||||||
cwd: Some(resume_cwd.path().to_string_lossy().into_owned()),
|
cwd: Some(resume_cwd.path().to_string_lossy().into_owned()),
|
||||||
history: Some(vec![ResponseItem::Message {
|
history: Some(vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -1838,6 +1838,8 @@ async fn assert_thread_fork_freezes_active_paginated_turn_as_interrupted(
|
|||||||
let user_response_item = |id: &str| {
|
let user_response_item = |id: &str| {
|
||||||
RolloutItem::ResponseItem(
|
RolloutItem::ResponseItem(
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -87,15 +87,21 @@ async fn thread_inject_items_adds_raw_response_items_to_thread_history(
|
|||||||
|
|
||||||
let injected_text = "Injected assistant context";
|
let injected_text = "Injected assistant context";
|
||||||
let injected_item = ResponseItem::Message {
|
let injected_item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: injected_text.to_string(),
|
text: injected_text.to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
};
|
};
|
||||||
let developer_item = |text: &str| ResponseItem::Message {
|
let developer_item = |text: &str| ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -561,6 +567,8 @@ async fn thread_inject_items_adds_raw_response_items_after_a_turn() -> Result<()
|
|||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
let injected_item = ResponseItem::Message {
|
let injected_item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -4972,6 +4972,8 @@ async fn thread_resume_rejects_history_when_thread_is_running() -> Result<()> {
|
|||||||
.send_thread_resume_request(ThreadResumeParams {
|
.send_thread_resume_request(ThreadResumeParams {
|
||||||
thread_id: thread_id.clone(),
|
thread_id: thread_id.clone(),
|
||||||
history: Some(vec![ResponseItem::Message {
|
history: Some(vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -6101,6 +6103,8 @@ async fn thread_resume_supports_history_and_overrides() -> Result<()> {
|
|||||||
|
|
||||||
let history_text = "Hello from history";
|
let history_text = "Hello from history";
|
||||||
let history = vec![ResponseItem::Message {
|
let history = vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -946,6 +946,8 @@ mod tests {
|
|||||||
let api_request = ResponsesApiRequest {
|
let api_request = ResponsesApiRequest {
|
||||||
model: "gpt-test".to_string(),
|
model: "gpt-test".to_string(),
|
||||||
input: vec![ResponseItem::Message {
|
input: vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("msg", "1")),
|
id: Some(ResponseItemId::with_suffix("msg", "1")),
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -157,6 +157,8 @@ mod tests {
|
|||||||
model: "gpt-test".to_string(),
|
model: "gpt-test".to_string(),
|
||||||
reasoning: None,
|
reasoning: None,
|
||||||
input: Some(SearchInput::Items(vec![ResponseItem::Message {
|
input: Some(SearchInput::Items(vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("msg", "search")),
|
id: Some(ResponseItemId::with_suffix("msg", "search")),
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
|
|||||||
@@ -770,6 +770,8 @@ mod tests {
|
|||||||
] if start_content.is_empty()
|
] if start_content.is_empty()
|
||||||
&& text == "First result."
|
&& text == "First result."
|
||||||
&& done_content == &vec![codex_protocol::models::ContentItem::OutputText {
|
&& done_content == &vec![codex_protocol::models::ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "First result.".to_string()
|
text: "First result.".to_string()
|
||||||
}]);
|
}]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -340,6 +340,8 @@ async fn responses_client_stream_request_preserves_item_ids() -> Result<()> {
|
|||||||
let request = ResponsesApiRequest {
|
let request = ResponsesApiRequest {
|
||||||
model: "gpt-test".into(),
|
model: "gpt-test".into(),
|
||||||
input: vec![ResponseItem::Message {
|
input: vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("msg", "1")),
|
id: Some(ResponseItemId::with_suffix("msg", "1")),
|
||||||
role: "user".into(),
|
role: "user".into(),
|
||||||
content: vec![ContentItem::InputText { text: "hi".into() }],
|
content: vec![ContentItem::InputText { text: "hi".into() }],
|
||||||
@@ -394,6 +396,8 @@ async fn responses_client_stream_request_sends_routing_fields_ahead_of_large_inp
|
|||||||
let request = ResponsesApiRequest {
|
let request = ResponsesApiRequest {
|
||||||
model: "gpt-test".into(),
|
model: "gpt-test".into(),
|
||||||
input: vec![ResponseItem::Message {
|
input: vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".into(),
|
role: "user".into(),
|
||||||
content: vec![ContentItem::InputText { text: large_input }],
|
content: vec![ContentItem::InputText { text: large_input }],
|
||||||
@@ -604,6 +608,8 @@ async fn azure_store_sends_ids_and_headers() -> Result<()> {
|
|||||||
let request = ResponsesApiRequest {
|
let request = ResponsesApiRequest {
|
||||||
model: "gpt-test".into(),
|
model: "gpt-test".into(),
|
||||||
input: vec![ResponseItem::Message {
|
input: vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("msg", "1")),
|
id: Some(ResponseItemId::with_suffix("msg", "1")),
|
||||||
role: "user".into(),
|
role: "user".into(),
|
||||||
content: vec![ContentItem::InputText { text: "hi".into() }],
|
content: vec![ContentItem::InputText { text: "hi".into() }],
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ impl From<RenderedFragment> for ResponseItem {
|
|||||||
let (content, content_kind) = annotated_content.into_parts();
|
let (content, content_kind) = annotated_content.into_parts();
|
||||||
|
|
||||||
Self::Message {
|
Self::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: role.to_string(),
|
role: role.to_string(),
|
||||||
content: vec![content],
|
content: vec![content],
|
||||||
|
|||||||
@@ -1052,6 +1052,9 @@
|
|||||||
"omit_app_server_notification_media": {
|
"omit_app_server_notification_media": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"output_token_replay": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"personality": {
|
"personality": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
@@ -7352,6 +7355,9 @@
|
|||||||
"omit_app_server_notification_media": {
|
"omit_app_server_notification_media": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"output_token_replay": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"personality": {
|
"personality": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -196,6 +196,8 @@ fn rollout_response_item(item: ResponseItem) -> RolloutItem {
|
|||||||
|
|
||||||
fn user_message(text: &str) -> ResponseItem {
|
fn user_message(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -208,9 +210,13 @@ fn user_message(text: &str) -> ResponseItem {
|
|||||||
|
|
||||||
fn assistant_message(text: &str, phase: Option<MessagePhase>) -> ResponseItem {
|
fn assistant_message(text: &str, phase: Option<MessagePhase>) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: text.to_string(),
|
text: text.to_string(),
|
||||||
}],
|
}],
|
||||||
phase,
|
phase,
|
||||||
@@ -237,6 +243,8 @@ fn register_session_root_skips_threads_with_explicit_parent() {
|
|||||||
|
|
||||||
fn spawn_agent_call(call_id: &str) -> ResponseItem {
|
fn spawn_agent_call(call_id: &str) -> ResponseItem {
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "spawn_agent".to_string(),
|
name: "spawn_agent".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -354,7 +362,7 @@ fn has_subagent_notification<'a>(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
content.iter().any(|content_item| match content_item {
|
content.iter().any(|content_item| match content_item {
|
||||||
ContentItem::InputText { text } | ContentItem::OutputText { text } => {
|
ContentItem::InputText { text } | ContentItem::OutputText { text, .. } => {
|
||||||
SubagentNotification::matches_text(text)
|
SubagentNotification::matches_text(text)
|
||||||
}
|
}
|
||||||
ContentItem::InputImage { .. } | ContentItem::InputAudio { .. } => false,
|
ContentItem::InputImage { .. } | ContentItem::InputAudio { .. } => false,
|
||||||
@@ -372,7 +380,7 @@ fn history_contains_text<'a>(
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
content.iter().any(|content_item| match content_item {
|
content.iter().any(|content_item| match content_item {
|
||||||
ContentItem::InputText { text } | ContentItem::OutputText { text } => {
|
ContentItem::InputText { text } | ContentItem::OutputText { text, .. } => {
|
||||||
text.contains(needle)
|
text.contains(needle)
|
||||||
}
|
}
|
||||||
ContentItem::InputImage { .. } | ContentItem::InputAudio { .. } => false,
|
ContentItem::InputImage { .. } | ContentItem::InputAudio { .. } => false,
|
||||||
@@ -415,7 +423,7 @@ fn history_contains_assistant_inter_agent_communication<'a>(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
content.iter().any(|content_item| match content_item {
|
content.iter().any(|content_item| match content_item {
|
||||||
ContentItem::OutputText { text } => {
|
ContentItem::OutputText { text, .. } => {
|
||||||
serde_json::from_str::<InterAgentCommunication>(text)
|
serde_json::from_str::<InterAgentCommunication>(text)
|
||||||
.ok()
|
.ok()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -2307,6 +2315,8 @@ async fn spawn_agent_fork_from_paginated_parent_uses_model_context_prefix(
|
|||||||
.session
|
.session
|
||||||
.persist_rollout_items(&[
|
.persist_rollout_items(&[
|
||||||
rollout_response_item(ResponseItem::Message {
|
rollout_response_item(ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -2816,6 +2826,8 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() {
|
|||||||
turn_context.as_ref(), turn_context.model_info(),
|
turn_context.as_ref(), turn_context.model_info(),
|
||||||
&[
|
&[
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -2825,6 +2837,8 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -2834,6 +2848,8 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -2987,6 +3003,8 @@ async fn spawn_agent_can_fork_parent_thread_history_with_sanitized_items() {
|
|||||||
.cloned()
|
.cloned()
|
||||||
.expect("parent final answer should be recorded");
|
.expect("parent final answer should be recorded");
|
||||||
let mut expected_developer_message = ResponseItem::Message {
|
let mut expected_developer_message = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -3196,6 +3214,8 @@ async fn spawn_agent_fork_strips_parent_usage_hints_from_compacted_history(marke
|
|||||||
tool_declarations.clone(),
|
tool_declarations.clone(),
|
||||||
ContextualUserFragment::into(crate::context::GuardianApprovedAction::new("parent-private-release".to_owned())),
|
ContextualUserFragment::into(crate::context::GuardianApprovedAction::new("parent-private-release".to_owned())),
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -3207,6 +3227,8 @@ async fn spawn_agent_fork_strips_parent_usage_hints_from_compacted_history(marke
|
|||||||
ContextualUserFragment::into(catalog_role("Catalog parent root guidance.")),
|
ContextualUserFragment::into(catalog_role("Catalog parent root guidance.")),
|
||||||
parent_task.to_model_input_item(),
|
parent_task.to_model_input_item(),
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -3221,6 +3243,8 @@ async fn spawn_agent_fork_strips_parent_usage_hints_from_compacted_history(marke
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -3451,6 +3475,8 @@ async fn spawn_agent_full_fork_restores_instructions_after_compaction_discards_p
|
|||||||
let parent_spawn_call_id = "spawn-call-compacted-stale-instructions".to_string();
|
let parent_spawn_call_id = "spawn-call-compacted-stale-instructions".to_string();
|
||||||
let replacement_history = vec![
|
let replacement_history = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -3460,6 +3486,8 @@ async fn spawn_agent_full_fork_restores_instructions_after_compaction_discards_p
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -3483,6 +3511,8 @@ async fn spawn_agent_full_fork_restores_instructions_after_compaction_discards_p
|
|||||||
.session
|
.session
|
||||||
.persist_rollout_items(&[
|
.persist_rollout_items(&[
|
||||||
rollout_response_item(ResponseItem::Message {
|
rollout_response_item(ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -3636,6 +3666,8 @@ async fn spawn_agent_full_fork_legacy_compaction_rebuilds_child_instructions_onc
|
|||||||
None => "spawn-call-legacy-compact-without-parent",
|
None => "spawn-call-legacy-compact-without-parent",
|
||||||
};
|
};
|
||||||
let parent_user_message = ResponseItem::Message {
|
let parent_user_message = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -3683,6 +3715,8 @@ async fn spawn_agent_full_fork_legacy_compaction_rebuilds_child_instructions_onc
|
|||||||
];
|
];
|
||||||
if let Some(instructions) = parent_developer_instructions {
|
if let Some(instructions) = parent_developer_instructions {
|
||||||
rollout_items.push(rollout_response_item(ResponseItem::Message {
|
rollout_items.push(rollout_response_item(ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -3693,6 +3727,8 @@ async fn spawn_agent_full_fork_legacy_compaction_rebuilds_child_instructions_onc
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
rollout_items.push(rollout_response_item(ResponseItem::Message {
|
rollout_items.push(rollout_response_item(ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -213,6 +213,7 @@ struct ModelClientState {
|
|||||||
include_timing_metrics: bool,
|
include_timing_metrics: bool,
|
||||||
beta_features_header: Option<String>,
|
beta_features_header: Option<String>,
|
||||||
concurrent_reasoning_summaries_enabled: bool,
|
concurrent_reasoning_summaries_enabled: bool,
|
||||||
|
output_token_replay_enabled: bool,
|
||||||
include_attestation: bool,
|
include_attestation: bool,
|
||||||
attestation_provider: Option<Arc<dyn AttestationProvider>>,
|
attestation_provider: Option<Arc<dyn AttestationProvider>>,
|
||||||
disable_websockets: AtomicBool,
|
disable_websockets: AtomicBool,
|
||||||
@@ -547,6 +548,7 @@ impl ModelClient {
|
|||||||
include_timing_metrics: bool,
|
include_timing_metrics: bool,
|
||||||
beta_features_header: Option<String>,
|
beta_features_header: Option<String>,
|
||||||
concurrent_reasoning_summaries_enabled: bool,
|
concurrent_reasoning_summaries_enabled: bool,
|
||||||
|
output_token_replay_enabled: bool,
|
||||||
attestation_provider: Option<Arc<dyn AttestationProvider>>,
|
attestation_provider: Option<Arc<dyn AttestationProvider>>,
|
||||||
http_client_factory: HttpClientFactory,
|
http_client_factory: HttpClientFactory,
|
||||||
workspace_routing: WorkspaceRoutingContext,
|
workspace_routing: WorkspaceRoutingContext,
|
||||||
@@ -585,6 +587,7 @@ impl ModelClient {
|
|||||||
include_timing_metrics,
|
include_timing_metrics,
|
||||||
beta_features_header,
|
beta_features_header,
|
||||||
concurrent_reasoning_summaries_enabled,
|
concurrent_reasoning_summaries_enabled,
|
||||||
|
output_token_replay_enabled,
|
||||||
include_attestation,
|
include_attestation,
|
||||||
attestation_provider,
|
attestation_provider,
|
||||||
disable_websockets: AtomicBool::new(false),
|
disable_websockets: AtomicBool::new(false),
|
||||||
@@ -999,6 +1002,14 @@ impl ModelClient {
|
|||||||
if !is_openai {
|
if !is_openai {
|
||||||
for item in &mut input {
|
for item in &mut input {
|
||||||
item.clear_internal_chat_message_metadata_passthrough();
|
item.clear_internal_chat_message_metadata_passthrough();
|
||||||
|
if !matches!(
|
||||||
|
item,
|
||||||
|
ResponseItem::Reasoning { .. }
|
||||||
|
| ResponseItem::Compaction { .. }
|
||||||
|
| ResponseItem::ContextCompaction { .. }
|
||||||
|
) {
|
||||||
|
item.clear_encrypted_content();
|
||||||
|
}
|
||||||
if let ResponseItem::FunctionCall {
|
if let ResponseItem::FunctionCall {
|
||||||
encrypted_function_args,
|
encrypted_function_args,
|
||||||
..
|
..
|
||||||
@@ -1015,7 +1026,10 @@ impl ModelClient {
|
|||||||
.then_some(StreamOptions {
|
.then_some(StreamOptions {
|
||||||
reasoning_summary_delivery: codex_api::ReasoningSummaryDelivery::SequentialCutoff,
|
reasoning_summary_delivery: codex_api::ReasoningSummaryDelivery::SequentialCutoff,
|
||||||
});
|
});
|
||||||
let include = vec!["reasoning.encrypted_content".to_string()];
|
let mut include = vec!["reasoning.encrypted_content".to_string()];
|
||||||
|
if self.state.output_token_replay_enabled && is_openai {
|
||||||
|
include.push("output.encrypted_content".to_string());
|
||||||
|
}
|
||||||
let verbosity = if model_info.support_verbosity {
|
let verbosity = if model_info.support_verbosity {
|
||||||
self.state.model_verbosity.or(model_info.default_verbosity)
|
self.state.model_verbosity.or(model_info.default_verbosity)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ fn prompt_with_image_outputs(detail: Option<ImageDetail>) -> Prompt {
|
|||||||
Prompt {
|
Prompt {
|
||||||
input: vec![
|
input: vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ fn test_model_client_with_thread_id(
|
|||||||
/*include_timing_metrics*/ false,
|
/*include_timing_metrics*/ false,
|
||||||
/*beta_features_header*/ None,
|
/*beta_features_header*/ None,
|
||||||
/*concurrent_reasoning_summaries_enabled*/ false,
|
/*concurrent_reasoning_summaries_enabled*/ false,
|
||||||
|
/*output_token_replay_enabled*/ false,
|
||||||
/*attestation_provider*/ None,
|
/*attestation_provider*/ None,
|
||||||
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
|
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
|
||||||
codex_model_provider::WorkspaceRoutingContext::new(
|
codex_model_provider::WorkspaceRoutingContext::new(
|
||||||
@@ -1434,9 +1435,13 @@ fn started_inference_attempt(temp: &TempDir) -> anyhow::Result<InferenceTraceAtt
|
|||||||
|
|
||||||
fn output_message(id: &str, text: &str) -> ResponseItem {
|
fn output_message(id: &str, text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(codex_protocol::ResponseItemId::with_suffix("msg", id)),
|
id: Some(codex_protocol::ResponseItemId::with_suffix("msg", id)),
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: text.to_string(),
|
text: text.to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -2020,6 +2025,7 @@ fn model_client_with_counting_attestation(
|
|||||||
/*include_timing_metrics*/ false,
|
/*include_timing_metrics*/ false,
|
||||||
/*beta_features_header*/ None,
|
/*beta_features_header*/ None,
|
||||||
/*concurrent_reasoning_summaries_enabled*/ false,
|
/*concurrent_reasoning_summaries_enabled*/ false,
|
||||||
|
/*output_token_replay_enabled*/ false,
|
||||||
Some(Arc::new(CountingAttestationProvider {
|
Some(Arc::new(CountingAttestationProvider {
|
||||||
calls: attestation_calls.clone(),
|
calls: attestation_calls.clone(),
|
||||||
})),
|
})),
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ async fn forward_events_filters_private_events_before_blocked_send_is_cancelled(
|
|||||||
}
|
}
|
||||||
let visible_msg = EventMsg::RawResponseItem(RawResponseItemEvent {
|
let visible_msg = EventMsg::RawResponseItem(RawResponseItemEvent {
|
||||||
item: ResponseItem::CustomToolCall {
|
item: ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: None,
|
status: None,
|
||||||
call_id: "call-1".to_string(),
|
call_id: "call-1".to_string(),
|
||||||
|
|||||||
@@ -538,7 +538,7 @@ pub fn content_items_to_text(content: &[ContentItem]) -> Option<String> {
|
|||||||
let mut pieces = Vec::new();
|
let mut pieces = Vec::new();
|
||||||
for item in content {
|
for item in content {
|
||||||
match item {
|
match item {
|
||||||
ContentItem::InputText { text } | ContentItem::OutputText { text } => {
|
ContentItem::InputText { text } | ContentItem::OutputText { text, .. } => {
|
||||||
if !text.is_empty() {
|
if !text.is_empty() {
|
||||||
pieces.push(text.as_str());
|
pieces.push(text.as_str());
|
||||||
}
|
}
|
||||||
@@ -733,6 +733,8 @@ fn build_compacted_history_with_limit(
|
|||||||
}
|
}
|
||||||
selected_messages.push(ResponseItemEnvelope {
|
selected_messages.push(ResponseItemEnvelope {
|
||||||
item: ResponseItem::Message {
|
item: ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
role: "user".to_owned(),
|
role: "user".to_owned(),
|
||||||
content,
|
content,
|
||||||
|
|||||||
@@ -719,7 +719,7 @@ fn message_text_token_count(item: &ResponseItem) -> usize {
|
|||||||
content
|
content
|
||||||
.iter()
|
.iter()
|
||||||
.map(|item| match item {
|
.map(|item| match item {
|
||||||
ContentItem::InputText { text } | ContentItem::OutputText { text } => {
|
ContentItem::InputText { text } | ContentItem::OutputText { text, .. } => {
|
||||||
approx_token_count(text)
|
approx_token_count(text)
|
||||||
}
|
}
|
||||||
ContentItem::InputImage { .. } | ContentItem::InputAudio { .. } => 0,
|
ContentItem::InputImage { .. } | ContentItem::InputAudio { .. } => 0,
|
||||||
@@ -737,7 +737,7 @@ fn truncate_message_text_to_token_budget(
|
|||||||
let mut truncated_content = Vec::with_capacity(content.len());
|
let mut truncated_content = Vec::with_capacity(content.len());
|
||||||
for mut content_item in content {
|
for mut content_item in content {
|
||||||
match content_item.content_mut() {
|
match content_item.content_mut() {
|
||||||
ContentItem::InputText { text } | ContentItem::OutputText { text } => {
|
ContentItem::InputText { text } | ContentItem::OutputText { text, .. } => {
|
||||||
if remaining == 0 {
|
if remaining == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -783,6 +783,8 @@ mod tests {
|
|||||||
|
|
||||||
fn message(role: &str, text: &str, phase: Option<MessagePhase>) -> ResponseItem {
|
fn message(role: &str, text: &str, phase: Option<MessagePhase>) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: role.to_string(),
|
role: role.to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -854,6 +856,8 @@ mod tests {
|
|||||||
message("assistant", "commentary", Some(MessagePhase::Commentary)),
|
message("assistant", "commentary", Some(MessagePhase::Commentary)),
|
||||||
message("assistant", "final", Some(MessagePhase::FinalAnswer)),
|
message("assistant", "final", Some(MessagePhase::FinalAnswer)),
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "shell_command".to_string(),
|
name: "shell_command".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -990,6 +994,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn build_v2_compacted_history_counts_retained_input_images() {
|
fn build_v2_compacted_history_counts_retained_input_images() {
|
||||||
let input = vec![ResponseItem::Message {
|
let input = vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -1039,6 +1045,8 @@ mod tests {
|
|||||||
truncated,
|
truncated,
|
||||||
vec![
|
vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
encrypted_content: None,
|
||||||
|
status: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -1060,6 +1068,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn retained_history_truncation_preserves_images_and_truncates_later_text_parts() {
|
fn retained_history_truncation_preserves_images_and_truncates_later_text_parts() {
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -1073,6 +1083,8 @@ mod tests {
|
|||||||
detail: None,
|
detail: None,
|
||||||
},
|
},
|
||||||
ContentItem::OutputText {
|
ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "uvwxyz".to_string(),
|
text: "uvwxyz".to_string(),
|
||||||
},
|
},
|
||||||
ContentItem::InputText {
|
ContentItem::InputText {
|
||||||
@@ -1130,6 +1142,8 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
truncated,
|
truncated,
|
||||||
vec![ResponseItem::Message {
|
vec![ResponseItem::Message {
|
||||||
|
encrypted_content: None,
|
||||||
|
status: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -1143,6 +1157,8 @@ mod tests {
|
|||||||
detail: None,
|
detail: None,
|
||||||
},
|
},
|
||||||
ContentItem::OutputText {
|
ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "uv…1 tokens truncated…yz".to_string(),
|
text: "uv…1 tokens truncated…yz".to_string(),
|
||||||
},
|
},
|
||||||
ContentItem::InputImage {
|
ContentItem::InputImage {
|
||||||
@@ -1172,6 +1188,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn retained_history_truncation_charges_image_only_messages() {
|
fn retained_history_truncation_charges_image_only_messages() {
|
||||||
let image_only_message = ResponseItem::Message {
|
let image_only_message = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputImage {
|
content: vec![ContentItem::InputImage {
|
||||||
@@ -1198,6 +1216,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn retained_history_truncation_drops_image_only_messages_after_budget_is_spent() {
|
fn retained_history_truncation_drops_image_only_messages_after_budget_is_spent() {
|
||||||
let image_only_message = ResponseItem::Message {
|
let image_only_message = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputImage {
|
content: vec![ContentItem::InputImage {
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ use pretty_assertions::assert_eq;
|
|||||||
|
|
||||||
fn message(content: Vec<ContentItem>) -> ResponseItem {
|
fn message(content: Vec<ContentItem>) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content,
|
content,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use codex_utils_output_truncation::truncate_text;
|
|||||||
|
|
||||||
pub(super) fn content_item_token_count(item: &ContentItem) -> usize {
|
pub(super) fn content_item_token_count(item: &ContentItem) -> usize {
|
||||||
match item {
|
match item {
|
||||||
ContentItem::InputText { text } | ContentItem::OutputText { text } => {
|
ContentItem::InputText { text } | ContentItem::OutputText { text, .. } => {
|
||||||
approx_token_count(text)
|
approx_token_count(text)
|
||||||
}
|
}
|
||||||
ContentItem::InputImage { image, detail } => usize::try_from(
|
ContentItem::InputImage { image, detail } => usize::try_from(
|
||||||
@@ -72,7 +72,7 @@ pub(super) fn truncate_message_to_token_budget(
|
|||||||
}
|
}
|
||||||
let mut item = content.pop()?;
|
let mut item = content.pop()?;
|
||||||
match item.content_mut() {
|
match item.content_mut() {
|
||||||
ContentItem::InputText { text } | ContentItem::OutputText { text } => {
|
ContentItem::InputText { text } | ContentItem::OutputText { text, .. } => {
|
||||||
if remaining == 0 {
|
if remaining == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ async fn local_compaction_respects_tool_metadata_state(
|
|||||||
let arguments = json!({"plan": [{"step": "x".repeat(7 * 1024), "status": "completed"}]});
|
let arguments = json!({"plan": [{"step": "x".repeat(7 * 1024), "status": "completed"}]});
|
||||||
assert!(serde_json::to_vec(&arguments)?.len() < 8 * 1024);
|
assert!(serde_json::to_vec(&arguments)?.len() < 8 * 1024);
|
||||||
items.push(ResponseItem::FunctionCall {
|
items.push(ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "update_plan".to_string(),
|
name: "update_plan".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -222,6 +224,8 @@ fn raw(items: Vec<ResponseItemEnvelope>) -> Vec<ResponseItem> {
|
|||||||
|
|
||||||
fn user_message(text: &str) -> ResponseItem {
|
fn user_message(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -247,9 +251,13 @@ fn content_items_to_text_joins_non_empty_segments() {
|
|||||||
text: "hello".to_string(),
|
text: "hello".to_string(),
|
||||||
},
|
},
|
||||||
ContentItem::OutputText {
|
ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: String::new(),
|
text: String::new(),
|
||||||
},
|
},
|
||||||
ContentItem::OutputText {
|
ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "world".to_string(),
|
text: "world".to_string(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -277,15 +285,21 @@ fn content_items_to_text_ignores_image_only_content() {
|
|||||||
fn collect_user_messages_extracts_user_text_only() {
|
fn collect_user_messages_extracts_user_text_only() {
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("msg", "assistant")),
|
id: Some(ResponseItemId::with_suffix("msg", "assistant")),
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "ignored".to_string(),
|
text: "ignored".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("msg", "user")),
|
id: Some(ResponseItemId::with_suffix("msg", "user")),
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -370,6 +384,8 @@ fn collect_annotated_user_messages_extracts_user_text_only(
|
|||||||
fn collect_user_messages_filters_session_prefix_entries() {
|
fn collect_user_messages_filters_session_prefix_entries() {
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -384,6 +400,8 @@ do things
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -393,6 +411,8 @@ do things
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -538,6 +558,8 @@ fn build_compacted_history_preserves_user_message_passthrough_metadata() {
|
|||||||
vec![
|
vec![
|
||||||
ResponseItemEnvelope {
|
ResponseItemEnvelope {
|
||||||
item: ResponseItem::Message {
|
item: ResponseItem::Message {
|
||||||
|
encrypted_content: None,
|
||||||
|
status: None,
|
||||||
id: Some(ResponseItemId::with_suffix("msg", "user")),
|
id: Some(ResponseItemId::with_suffix("msg", "user")),
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -579,6 +601,8 @@ fn assemble_compaction_history_keeps_prefix_first_and_summary_last() {
|
|||||||
};
|
};
|
||||||
let compacted_history = vec![
|
let compacted_history = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -588,6 +612,8 @@ fn assemble_compaction_history_keeps_prefix_first_and_summary_last() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -598,6 +624,8 @@ fn assemble_compaction_history_keeps_prefix_first_and_summary_last() {
|
|||||||
},
|
},
|
||||||
agent_completion.clone(),
|
agent_completion.clone(),
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -608,6 +636,8 @@ fn assemble_compaction_history_keeps_prefix_first_and_summary_last() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
let initial_context = vec![ResponseItem::Message {
|
let initial_context = vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -625,6 +655,8 @@ fn assemble_compaction_history_keeps_prefix_first_and_summary_last() {
|
|||||||
let expected = vec![
|
let expected = vec![
|
||||||
prefix,
|
prefix,
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -634,6 +666,8 @@ fn assemble_compaction_history_keeps_prefix_first_and_summary_last() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -643,6 +677,8 @@ fn assemble_compaction_history_keeps_prefix_first_and_summary_last() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -653,6 +689,8 @@ fn assemble_compaction_history_keeps_prefix_first_and_summary_last() {
|
|||||||
},
|
},
|
||||||
agent_completion,
|
agent_completion,
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -683,6 +721,8 @@ fn assemble_compaction_history_keeps_compaction_last() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
let initial_context = vec![ResponseItem::Message {
|
let initial_context = vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -699,6 +739,8 @@ fn assemble_compaction_history_keeps_compaction_last() {
|
|||||||
));
|
));
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -244,6 +244,8 @@ fn missing_retained_fragment_is_rendered_again() {
|
|||||||
);
|
);
|
||||||
let previous = world_state.render_full().0;
|
let previous = world_state.render_full().0;
|
||||||
let retained = ResponseItem::Message {
|
let retained = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -356,7 +356,9 @@ impl ContextManager {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter_map(|content| match content {
|
.filter_map(|content| match content {
|
||||||
ContentItem::InputText { text }
|
ContentItem::InputText { text }
|
||||||
| ContentItem::OutputText { text } => Some(text.as_str()),
|
| ContentItem::OutputText { text, .. } => {
|
||||||
|
Some(text.as_str())
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
@@ -1100,7 +1102,7 @@ fn estimate_response_item_model_visible_bytes(item: &ResponseItem) -> i64 {
|
|||||||
ResponseItem::Message { content, .. } => content
|
ResponseItem::Message { content, .. } => content
|
||||||
.iter()
|
.iter()
|
||||||
.map(|part| match part {
|
.map(|part| match part {
|
||||||
ContentItem::InputText { text } | ContentItem::OutputText { text } => {
|
ContentItem::InputText { text } | ContentItem::OutputText { text, .. } => {
|
||||||
text_bytes(text)
|
text_bytes(text)
|
||||||
}
|
}
|
||||||
ContentItem::InputImage { image, detail } => {
|
ContentItem::InputImage { image, detail } => {
|
||||||
|
|||||||
@@ -85,9 +85,13 @@ fn pcm_wav_data_url(sample_count: u32) -> (String, usize) {
|
|||||||
|
|
||||||
fn assistant_msg(text: &str) -> ResponseItem {
|
fn assistant_msg(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: text.to_string(),
|
text: text.to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -104,9 +108,13 @@ fn inter_agent_assistant_msg(text: &str) -> ResponseItem {
|
|||||||
/*trigger_turn*/ true,
|
/*trigger_turn*/ true,
|
||||||
);
|
);
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: serde_json::to_string(&communication).unwrap(),
|
text: serde_json::to_string(&communication).unwrap(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -651,9 +659,13 @@ fn world_state_reconciles_matching_legacy_history_once() {
|
|||||||
|
|
||||||
fn user_msg(text: &str) -> ResponseItem {
|
fn user_msg(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: text.to_string(),
|
text: text.to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -663,6 +675,8 @@ fn user_msg(text: &str) -> ResponseItem {
|
|||||||
|
|
||||||
fn user_input_text_msg(text: &str) -> ResponseItem {
|
fn user_input_text_msg(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -675,6 +689,8 @@ fn user_input_text_msg(text: &str) -> ResponseItem {
|
|||||||
|
|
||||||
fn developer_msg(text: &str) -> ResponseItem {
|
fn developer_msg(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -687,6 +703,8 @@ fn developer_msg(text: &str) -> ResponseItem {
|
|||||||
|
|
||||||
fn developer_msg_with_fragments(texts: &[&str]) -> ResponseItem {
|
fn developer_msg_with_fragments(texts: &[&str]) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: texts
|
content: texts
|
||||||
@@ -781,9 +799,13 @@ fn filters_non_api_messages() {
|
|||||||
let policy = TruncationPolicy::Tokens(10_000);
|
let policy = TruncationPolicy::Tokens(10_000);
|
||||||
// System message is not API messages; Other is ignored.
|
// System message is not API messages; Other is ignored.
|
||||||
let system = ResponseItem::Message {
|
let system = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "system".to_string(),
|
role: "system".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "ignored".to_string(),
|
text: "ignored".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -828,6 +850,8 @@ fn retains_only_harness_authored_configuration_updates() {
|
|||||||
},
|
},
|
||||||
ResponseItemEnvelope {
|
ResponseItemEnvelope {
|
||||||
item: ResponseItem::Message {
|
item: ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "system".to_string(),
|
role: "system".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -1055,6 +1079,8 @@ fn record_annotated_items_preserves_metadata_while_processing_item(
|
|||||||
fn for_prompt_annotated_preserves_metadata_while_normalizing_item() {
|
fn for_prompt_annotated_preserves_metadata_while_normalizing_item() {
|
||||||
let envelope = ResponseItemEnvelope {
|
let envelope = ResponseItemEnvelope {
|
||||||
item: ResponseItem::Message {
|
item: ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -1138,6 +1164,8 @@ fn total_token_usage_includes_all_items_after_last_model_generated_item() {
|
|||||||
fn for_prompt_strips_media_when_model_does_not_support_it() {
|
fn for_prompt_strips_media_when_model_does_not_support_it() {
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -1171,6 +1199,8 @@ fn for_prompt_strips_media_when_model_does_not_support_it() {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "view_image".to_string(),
|
name: "view_image".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -1201,6 +1231,7 @@ fn for_prompt_strips_media_when_model_does_not_support_it() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::CustomToolCall {
|
ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: None,
|
status: None,
|
||||||
call_id: "tool-1".to_string(),
|
call_id: "tool-1".to_string(),
|
||||||
@@ -1237,6 +1268,8 @@ fn for_prompt_strips_media_when_model_does_not_support_it() {
|
|||||||
|
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -1269,6 +1302,8 @@ fn for_prompt_strips_media_when_model_does_not_support_it() {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "view_image".to_string(),
|
name: "view_image".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -1298,6 +1333,7 @@ fn for_prompt_strips_media_when_model_does_not_support_it() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::CustomToolCall {
|
ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: None,
|
status: None,
|
||||||
call_id: "tool-1".to_string(),
|
call_id: "tool-1".to_string(),
|
||||||
@@ -1339,6 +1375,8 @@ fn for_prompt_strips_media_when_model_does_not_support_it() {
|
|||||||
// With image support, images are preserved
|
// With image support, images are preserved
|
||||||
let modalities = default_input_modalities();
|
let modalities = default_input_modalities();
|
||||||
let with_images = create_history_with_items(vec![ResponseItem::Message {
|
let with_images = create_history_with_items(vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -1365,6 +1403,8 @@ fn for_prompt_strips_media_when_model_does_not_support_it() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let audio_message = ResponseItem::Message {
|
let audio_message = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputAudio {
|
content: vec![ContentItem::InputAudio {
|
||||||
@@ -1384,6 +1424,7 @@ fn for_prompt_strips_media_when_model_does_not_support_it() {
|
|||||||
fn for_prompt_preserves_image_generation_calls_when_images_are_supported() {
|
fn for_prompt_preserves_image_generation_calls_when_images_are_supported() {
|
||||||
let history = create_history_with_items(vec![
|
let history = create_history_with_items(vec![
|
||||||
ResponseItem::ImageGenerationCall {
|
ResponseItem::ImageGenerationCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("ig", "123")),
|
id: Some(ResponseItemId::with_suffix("ig", "123")),
|
||||||
status: "generating".to_string(),
|
status: "generating".to_string(),
|
||||||
revised_prompt: Some("lobster".to_string()),
|
revised_prompt: Some("lobster".to_string()),
|
||||||
@@ -1391,6 +1432,8 @@ fn for_prompt_preserves_image_generation_calls_when_images_are_supported() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -1405,6 +1448,7 @@ fn for_prompt_preserves_image_generation_calls_when_images_are_supported() {
|
|||||||
history.for_prompt(&default_input_modalities()),
|
history.for_prompt(&default_input_modalities()),
|
||||||
vec![
|
vec![
|
||||||
ResponseItem::ImageGenerationCall {
|
ResponseItem::ImageGenerationCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("ig", "123")),
|
id: Some(ResponseItemId::with_suffix("ig", "123")),
|
||||||
status: "generating".to_string(),
|
status: "generating".to_string(),
|
||||||
revised_prompt: Some("lobster".to_string()),
|
revised_prompt: Some("lobster".to_string()),
|
||||||
@@ -1412,6 +1456,8 @@ fn for_prompt_preserves_image_generation_calls_when_images_are_supported() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
encrypted_content: None,
|
||||||
|
status: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -1428,6 +1474,8 @@ fn for_prompt_preserves_image_generation_calls_when_images_are_supported() {
|
|||||||
fn for_prompt_clears_image_generation_result_when_images_are_unsupported() {
|
fn for_prompt_clears_image_generation_result_when_images_are_unsupported() {
|
||||||
let history = create_history_with_items(vec![
|
let history = create_history_with_items(vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -1437,6 +1485,7 @@ fn for_prompt_clears_image_generation_result_when_images_are_unsupported() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::ImageGenerationCall {
|
ResponseItem::ImageGenerationCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("ig", "123")),
|
id: Some(ResponseItemId::with_suffix("ig", "123")),
|
||||||
status: "completed".to_string(),
|
status: "completed".to_string(),
|
||||||
revised_prompt: Some("lobster".to_string()),
|
revised_prompt: Some("lobster".to_string()),
|
||||||
@@ -1449,6 +1498,8 @@ fn for_prompt_clears_image_generation_result_when_images_are_unsupported() {
|
|||||||
history.for_prompt(&[InputModality::Text]),
|
history.for_prompt(&[InputModality::Text]),
|
||||||
vec![
|
vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
encrypted_content: None,
|
||||||
|
status: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -1458,6 +1509,7 @@ fn for_prompt_clears_image_generation_result_when_images_are_unsupported() {
|
|||||||
internal_chat_message_metadata_passthrough: Some(unknown_content_metadata()),
|
internal_chat_message_metadata_passthrough: Some(unknown_content_metadata()),
|
||||||
},
|
},
|
||||||
ResponseItem::ImageGenerationCall {
|
ResponseItem::ImageGenerationCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("ig", "123")),
|
id: Some(ResponseItemId::with_suffix("ig", "123")),
|
||||||
status: "completed".to_string(),
|
status: "completed".to_string(),
|
||||||
revised_prompt: Some("lobster".to_string()),
|
revised_prompt: Some("lobster".to_string()),
|
||||||
@@ -1514,6 +1566,8 @@ fn estimate_token_count_counts_recorded_base_instructions_once() {
|
|||||||
fn remove_first_item_removes_matching_output_for_function_call() {
|
fn remove_first_item_removes_matching_output_for_function_call() {
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "do_it".to_string(),
|
name: "do_it".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -1548,6 +1602,8 @@ fn remove_first_item_removes_matching_call_for_output() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "do_it".to_string(),
|
name: "do_it".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -1566,6 +1622,7 @@ fn remove_first_item_removes_matching_call_for_output() {
|
|||||||
fn remove_first_item_handles_local_shell_pair() {
|
fn remove_first_item_handles_local_shell_pair() {
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ResponseItem::LocalShellCall {
|
ResponseItem::LocalShellCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
call_id: Some("call-3".to_string()),
|
call_id: Some("call-3".to_string()),
|
||||||
status: LocalShellStatus::Completed,
|
status: LocalShellStatus::Completed,
|
||||||
@@ -1652,6 +1709,8 @@ fn drop_last_n_user_turns_preserves_prefix() {
|
|||||||
("steer", "Check the tests too."),
|
("steer", "Check the tests too."),
|
||||||
] {
|
] {
|
||||||
let message = ResponseItem::Message {
|
let message = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("msg", id)),
|
id: Some(ResponseItemId::with_suffix("msg", id)),
|
||||||
role: "user".to_owned(),
|
role: "user".to_owned(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -1880,6 +1939,8 @@ fn drop_last_n_user_turns_preserves_annotations_for_surviving_developer_fragment
|
|||||||
let turn_id = "rolled-back-turn";
|
let turn_id = "rolled-back-turn";
|
||||||
let model_switch = ModelSwitchInstructions::new("switched model instructions").render();
|
let model_switch = ModelSwitchInstructions::new("switched model instructions").render();
|
||||||
let developer_message = ResponseItem::Message {
|
let developer_message = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -1916,6 +1977,8 @@ fn drop_last_n_user_turns_preserves_annotations_for_surviving_developer_fragment
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
raw_items(&history),
|
raw_items(&history),
|
||||||
vec![ResponseItem::Message {
|
vec![ResponseItem::Message {
|
||||||
|
encrypted_content: None,
|
||||||
|
status: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -2003,6 +2066,7 @@ fn drop_last_n_user_turns_clears_reference_context_for_mixed_developer_context_b
|
|||||||
fn remove_first_item_handles_custom_tool_pair() {
|
fn remove_first_item_handles_custom_tool_pair() {
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ResponseItem::CustomToolCall {
|
ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: None,
|
status: None,
|
||||||
call_id: "tool-1".to_string(),
|
call_id: "tool-1".to_string(),
|
||||||
@@ -2028,6 +2092,7 @@ fn remove_first_item_handles_custom_tool_pair() {
|
|||||||
fn normalization_retains_local_shell_outputs() {
|
fn normalization_retains_local_shell_outputs() {
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ResponseItem::LocalShellCall {
|
ResponseItem::LocalShellCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
call_id: Some("shell-1".to_string()),
|
call_id: Some("shell-1".to_string()),
|
||||||
status: LocalShellStatus::Completed,
|
status: LocalShellStatus::Completed,
|
||||||
@@ -2263,6 +2328,7 @@ fn format_exec_output_prefers_line_marker_when_both_limits_exceeded() {
|
|||||||
#[test_case::test_case(true; "inherited")]
|
#[test_case::test_case(true; "inherited")]
|
||||||
fn normalize_adds_missing_output_for_custom_tool_call(inherited: bool) {
|
fn normalize_adds_missing_output_for_custom_tool_call(inherited: bool) {
|
||||||
let items = vec![ResponseItem::CustomToolCall {
|
let items = vec![ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: None,
|
status: None,
|
||||||
call_id: "tool-x".to_string(),
|
call_id: "tool-x".to_string(),
|
||||||
@@ -2283,6 +2349,7 @@ fn normalize_adds_missing_output_for_custom_tool_call(inherited: bool) {
|
|||||||
raw_items(&h),
|
raw_items(&h),
|
||||||
vec![
|
vec![
|
||||||
ResponseItem::CustomToolCall {
|
ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: None,
|
status: None,
|
||||||
call_id: "tool-x".to_string(),
|
call_id: "tool-x".to_string(),
|
||||||
@@ -2306,6 +2373,7 @@ fn normalize_adds_missing_output_for_custom_tool_call(inherited: bool) {
|
|||||||
#[test_case::test_case(true; "inherited")]
|
#[test_case::test_case(true; "inherited")]
|
||||||
fn normalize_adds_missing_output_for_local_shell_call_with_id(inherited: bool) {
|
fn normalize_adds_missing_output_for_local_shell_call_with_id(inherited: bool) {
|
||||||
let items = vec![ResponseItem::LocalShellCall {
|
let items = vec![ResponseItem::LocalShellCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
call_id: Some("shell-1".to_string()),
|
call_id: Some("shell-1".to_string()),
|
||||||
status: LocalShellStatus::Completed,
|
status: LocalShellStatus::Completed,
|
||||||
@@ -2330,6 +2398,7 @@ fn normalize_adds_missing_output_for_local_shell_call_with_id(inherited: bool) {
|
|||||||
raw_items(&h),
|
raw_items(&h),
|
||||||
vec![
|
vec![
|
||||||
ResponseItem::LocalShellCall {
|
ResponseItem::LocalShellCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
call_id: Some("shell-1".to_string()),
|
call_id: Some("shell-1".to_string()),
|
||||||
status: LocalShellStatus::Completed,
|
status: LocalShellStatus::Completed,
|
||||||
@@ -2395,6 +2464,8 @@ fn normalize_mixed_inserts_and_removals() {
|
|||||||
let items = vec![
|
let items = vec![
|
||||||
// Will get an inserted output
|
// Will get an inserted output
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "f1".to_string(),
|
name: "f1".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -2414,6 +2485,7 @@ fn normalize_mixed_inserts_and_removals() {
|
|||||||
},
|
},
|
||||||
// Will get an inserted custom tool output
|
// Will get an inserted custom tool output
|
||||||
ResponseItem::CustomToolCall {
|
ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: None,
|
status: None,
|
||||||
call_id: "t1".to_string(),
|
call_id: "t1".to_string(),
|
||||||
@@ -2424,6 +2496,7 @@ fn normalize_mixed_inserts_and_removals() {
|
|||||||
},
|
},
|
||||||
// Local shell call also gets an inserted function call output
|
// Local shell call also gets an inserted function call output
|
||||||
ResponseItem::LocalShellCall {
|
ResponseItem::LocalShellCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
call_id: Some("s1".to_string()),
|
call_id: Some("s1".to_string()),
|
||||||
status: LocalShellStatus::Completed,
|
status: LocalShellStatus::Completed,
|
||||||
@@ -2445,6 +2518,8 @@ fn normalize_mixed_inserts_and_removals() {
|
|||||||
raw_items(&h),
|
raw_items(&h),
|
||||||
vec![
|
vec![
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
encrypted_content: None,
|
||||||
|
status: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "f1".to_string(),
|
name: "f1".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -2462,6 +2537,7 @@ fn normalize_mixed_inserts_and_removals() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::CustomToolCall {
|
ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: None,
|
status: None,
|
||||||
call_id: "t1".to_string(),
|
call_id: "t1".to_string(),
|
||||||
@@ -2478,6 +2554,7 @@ fn normalize_mixed_inserts_and_removals() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::LocalShellCall {
|
ResponseItem::LocalShellCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
call_id: Some("s1".to_string()),
|
call_id: Some("s1".to_string()),
|
||||||
status: LocalShellStatus::Completed,
|
status: LocalShellStatus::Completed,
|
||||||
@@ -2505,6 +2582,8 @@ fn normalize_mixed_inserts_and_removals() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn normalize_adds_missing_output_for_function_call_inserts_output() {
|
fn normalize_adds_missing_output_for_function_call_inserts_output() {
|
||||||
let items = vec![ResponseItem::FunctionCall {
|
let items = vec![ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "do_it".to_string(),
|
name: "do_it".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -2519,6 +2598,8 @@ fn normalize_adds_missing_output_for_function_call_inserts_output() {
|
|||||||
raw_items(&h),
|
raw_items(&h),
|
||||||
vec![
|
vec![
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
encrypted_content: None,
|
||||||
|
status: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "do_it".to_string(),
|
name: "do_it".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -2558,6 +2639,8 @@ fn normalize_preserves_named_function_call_output_without_call_id() {
|
|||||||
fn for_prompt_assigns_stable_id_to_synthetic_output_without_reordering_history() {
|
fn for_prompt_assigns_stable_id_to_synthetic_output_without_reordering_history() {
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("fc", "existing")),
|
id: Some(ResponseItemId::with_suffix("fc", "existing")),
|
||||||
name: "do_it".to_string(),
|
name: "do_it".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -2567,6 +2650,8 @@ fn for_prompt_assigns_stable_id_to_synthetic_output_without_reordering_history()
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("msg", "later")),
|
id: Some(ResponseItemId::with_suffix("msg", "later")),
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -2601,6 +2686,7 @@ fn for_prompt_assigns_stable_id_to_synthetic_output_without_reordering_history()
|
|||||||
#[test]
|
#[test]
|
||||||
fn normalize_adds_missing_output_for_tool_search_call() {
|
fn normalize_adds_missing_output_for_tool_search_call() {
|
||||||
let items = vec![ResponseItem::ToolSearchCall {
|
let items = vec![ResponseItem::ToolSearchCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
call_id: Some("search-call-x".to_string()),
|
call_id: Some("search-call-x".to_string()),
|
||||||
status: Some("completed".to_string()),
|
status: Some("completed".to_string()),
|
||||||
@@ -2616,6 +2702,7 @@ fn normalize_adds_missing_output_for_tool_search_call() {
|
|||||||
raw_items(&h),
|
raw_items(&h),
|
||||||
vec![
|
vec![
|
||||||
ResponseItem::ToolSearchCall {
|
ResponseItem::ToolSearchCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
call_id: Some("search-call-x".to_string()),
|
call_id: Some("search-call-x".to_string()),
|
||||||
status: Some("completed".to_string()),
|
status: Some("completed".to_string()),
|
||||||
@@ -2640,6 +2727,7 @@ fn normalize_adds_missing_output_for_tool_search_call() {
|
|||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn normalize_adds_missing_output_for_custom_tool_call_panics_in_debug() {
|
fn normalize_adds_missing_output_for_custom_tool_call_panics_in_debug() {
|
||||||
let items = vec![ResponseItem::CustomToolCall {
|
let items = vec![ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: None,
|
status: None,
|
||||||
call_id: "tool-x".to_string(),
|
call_id: "tool-x".to_string(),
|
||||||
@@ -2657,6 +2745,7 @@ fn normalize_adds_missing_output_for_custom_tool_call_panics_in_debug() {
|
|||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn normalize_adds_missing_output_for_local_shell_call_with_id_panics_in_debug() {
|
fn normalize_adds_missing_output_for_local_shell_call_with_id_panics_in_debug() {
|
||||||
let items = vec![ResponseItem::LocalShellCall {
|
let items = vec![ResponseItem::LocalShellCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
call_id: Some("shell-1".to_string()),
|
call_id: Some("shell-1".to_string()),
|
||||||
status: LocalShellStatus::Completed,
|
status: LocalShellStatus::Completed,
|
||||||
@@ -2771,6 +2860,8 @@ fn normalize_keeps_server_tool_search_output_without_matching_call() {
|
|||||||
fn normalize_mixed_inserts_and_removals_panics_in_debug() {
|
fn normalize_mixed_inserts_and_removals_panics_in_debug() {
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "f1".to_string(),
|
name: "f1".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -2788,6 +2879,7 @@ fn normalize_mixed_inserts_and_removals_panics_in_debug() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::CustomToolCall {
|
ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: None,
|
status: None,
|
||||||
call_id: "t1".to_string(),
|
call_id: "t1".to_string(),
|
||||||
@@ -2797,6 +2889,7 @@ fn normalize_mixed_inserts_and_removals_panics_in_debug() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::LocalShellCall {
|
ResponseItem::LocalShellCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
call_id: Some("s1".to_string()),
|
call_id: Some("s1".to_string()),
|
||||||
status: LocalShellStatus::Completed,
|
status: LocalShellStatus::Completed,
|
||||||
@@ -2819,6 +2912,8 @@ fn image_data_url_payload_does_not_dominate_message_estimate() {
|
|||||||
let payload = "A".repeat(100_000);
|
let payload = "A".repeat(100_000);
|
||||||
let image_url = format!("data:image/png;base64,{payload}");
|
let image_url = format!("data:image/png;base64,{payload}");
|
||||||
let image_item = ResponseItem::Message {
|
let image_item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -2834,6 +2929,8 @@ fn image_data_url_payload_does_not_dominate_message_estimate() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
};
|
};
|
||||||
let text_only_item = ResponseItem::Message {
|
let text_only_item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -2857,6 +2954,8 @@ fn image_data_url_payload_does_not_dominate_message_estimate() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn file_images_use_detail_appropriate_estimates() {
|
fn file_images_use_detail_appropriate_estimates() {
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -2956,6 +3055,8 @@ fn image_data_url_payload_does_not_dominate_custom_tool_call_output_estimate() {
|
|||||||
fn audio_data_url_payload_does_not_dominate_message_estimate() {
|
fn audio_data_url_payload_does_not_dominate_message_estimate() {
|
||||||
let (audio_url, _) = pcm_wav_data_url(/*sample_count*/ 801);
|
let (audio_url, _) = pcm_wav_data_url(/*sample_count*/ 801);
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputAudio { audio_url }],
|
content: vec![ContentItem::InputAudio { audio_url }],
|
||||||
@@ -3021,6 +3122,8 @@ fn malformed_audio_data_url_falls_back_to_whole_url_size_cost() {
|
|||||||
let audio_url = format!("data:audio/wav;base64,{payload}");
|
let audio_url = format!("data:audio/wav;base64,{payload}");
|
||||||
let fallback_bytes = approx_bytes_for_tokens(approx_token_count(&audio_url)) as i64;
|
let fallback_bytes = approx_bytes_for_tokens(approx_token_count(&audio_url)) as i64;
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputAudio { audio_url }],
|
content: vec![ContentItem::InputAudio { audio_url }],
|
||||||
@@ -3076,6 +3179,8 @@ fn record_items_omits_audio_that_exceeds_the_output_budget() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn non_base64_image_urls_use_image_estimates() {
|
fn non_base64_image_urls_use_image_estimates() {
|
||||||
let message_item = ResponseItem::Message {
|
let message_item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputImage {
|
content: vec![ContentItem::InputImage {
|
||||||
@@ -3198,6 +3303,8 @@ fn encrypted_function_output_uses_plaintext_byte_estimate() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn data_url_without_base64_marker_uses_image_estimate() {
|
fn data_url_without_base64_marker_uses_image_estimate() {
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputImage {
|
content: vec![ContentItem::InputImage {
|
||||||
@@ -3246,6 +3353,8 @@ fn mixed_case_data_url_markers_are_adjusted() {
|
|||||||
let payload = "F".repeat(1_024);
|
let payload = "F".repeat(1_024);
|
||||||
let image_url = format!("DATA:image/png;BASE64,{payload}");
|
let image_url = format!("DATA:image/png;BASE64,{payload}");
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputImage {
|
content: vec![ContentItem::InputImage {
|
||||||
@@ -3269,6 +3378,8 @@ fn multiple_inline_images_apply_multiple_fixed_costs() {
|
|||||||
let image_url_one = format!("data:image/png;base64,{payload_one}");
|
let image_url_one = format!("data:image/png;base64,{payload_one}");
|
||||||
let image_url_two = format!("data:image/jpeg;base64,{payload_two}");
|
let image_url_two = format!("data:image/jpeg;base64,{payload_two}");
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -3405,9 +3516,13 @@ fn original_detail_webp_images_scale_with_dimensions() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn text_only_items_count_decoded_content() {
|
fn text_only_items_count_decoded_content() {
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "Hello, \"world\"!\nこんにちは".to_string(),
|
text: "Hello, \"world\"!\nこんにちは".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ impl ContextManager {
|
|||||||
let text = content
|
let text = content
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|content| match content {
|
.filter_map(|content| match content {
|
||||||
ContentItem::InputText { text } | ContentItem::OutputText { text } => {
|
ContentItem::InputText { text } | ContentItem::OutputText { text, .. } => {
|
||||||
Some(text.as_str())
|
Some(text.as_str())
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ pub(crate) fn build_rendered_message(fragments: Vec<RenderedFragment>) -> Option
|
|||||||
.unzip();
|
.unzip();
|
||||||
|
|
||||||
Some(ResponseItem::Message {
|
Some(ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: role.to_string(),
|
role: role.to_string(),
|
||||||
content,
|
content,
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ fn fragment(role: &'static str, text: &'static str) -> TestFragment {
|
|||||||
|
|
||||||
fn message(role: &str, texts: &[&str]) -> ResponseItem {
|
fn message(role: &str, texts: &[&str]) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: role.to_string(),
|
role: role.to_string(),
|
||||||
content: texts
|
content: texts
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ fn parse_user_message(message: &[ContentItem]) -> Option<UserMessageItem> {
|
|||||||
audio_url: audio_url.clone(),
|
audio_url: audio_url.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ContentItem::OutputText { text } => {
|
ContentItem::OutputText { text, .. } => {
|
||||||
warn!("Output text in user message: {}", text);
|
warn!("Output text in user message: {}", text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ fn parse_agent_message(
|
|||||||
let mut content: Vec<AgentMessageContent> = Vec::new();
|
let mut content: Vec<AgentMessageContent> = Vec::new();
|
||||||
for content_item in message.iter() {
|
for content_item in message.iter() {
|
||||||
match content_item {
|
match content_item {
|
||||||
ContentItem::InputText { text } | ContentItem::OutputText { text } => {
|
ContentItem::InputText { text } | ContentItem::OutputText { text, .. } => {
|
||||||
content.push(AgentMessageContent::Text { text: text.clone() });
|
content.push(AgentMessageContent::Text { text: text.clone() });
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|||||||
@@ -77,6 +77,8 @@ fn parses_user_message_with_text_and_two_images() {
|
|||||||
let img2 = "https://example.com/two.jpg".to_string();
|
let img2 = "https://example.com/two.jpg".to_string();
|
||||||
|
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -128,6 +130,8 @@ fn parses_user_message_with_text_and_two_images() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parses_user_message_with_file_image() {
|
fn parses_user_message_with_file_image() {
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -174,6 +178,8 @@ fn skips_local_image_label_text() {
|
|||||||
let user_text = "Please review this image.".to_string();
|
let user_text = "Please review this image.".to_string();
|
||||||
|
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -222,6 +228,8 @@ fn skips_local_audio_label_text() {
|
|||||||
let user_text = "Please transcribe this audio.".to_string();
|
let user_text = "Please transcribe this audio.".to_string();
|
||||||
|
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -262,6 +270,8 @@ fn skips_local_audio_label_text() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parses_assistant_message_input_text_for_backward_compatibility() {
|
fn parses_assistant_message_input_text_for_backward_compatibility() {
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -303,6 +313,8 @@ fn skips_unnamed_image_label_text() {
|
|||||||
let user_text = "Please review this image.".to_string();
|
let user_text = "Please review this image.".to_string();
|
||||||
|
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -348,6 +360,8 @@ fn skips_unnamed_image_label_text() {
|
|||||||
fn skips_user_instructions_and_env() {
|
fn skips_user_instructions_and_env() {
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -356,6 +370,8 @@ fn skips_user_instructions_and_env() {
|
|||||||
phase: None,
|
phase: None,
|
||||||
internal_chat_message_metadata_passthrough: None,},
|
internal_chat_message_metadata_passthrough: None,},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -364,6 +380,8 @@ fn skips_user_instructions_and_env() {
|
|||||||
phase: None,
|
phase: None,
|
||||||
internal_chat_message_metadata_passthrough: None,},
|
internal_chat_message_metadata_passthrough: None,},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -372,6 +390,8 @@ fn skips_user_instructions_and_env() {
|
|||||||
phase: None,
|
phase: None,
|
||||||
internal_chat_message_metadata_passthrough: None,},
|
internal_chat_message_metadata_passthrough: None,},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -381,6 +401,8 @@ fn skips_user_instructions_and_env() {
|
|||||||
phase: None,
|
phase: None,
|
||||||
internal_chat_message_metadata_passthrough: None,},
|
internal_chat_message_metadata_passthrough: None,},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -389,6 +411,8 @@ fn skips_user_instructions_and_env() {
|
|||||||
phase: None,
|
phase: None,
|
||||||
internal_chat_message_metadata_passthrough: None,},
|
internal_chat_message_metadata_passthrough: None,},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -439,6 +463,8 @@ fn parses_hook_prompt_message_as_distinct_turn_item() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parses_hook_prompt_and_hides_other_contextual_fragments() {
|
fn parses_hook_prompt_and_hides_other_contextual_fragments() {
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("msg", "1")),
|
id: Some(ResponseItemId::with_suffix("msg", "1")),
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -474,6 +500,8 @@ fn parses_hook_prompt_and_hides_other_contextual_fragments() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn internal_model_context_does_not_parse_as_visible_turn_item() {
|
fn internal_model_context_does_not_parse_as_visible_turn_item() {
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("msg", "1")),
|
id: Some(ResponseItemId::with_suffix("msg", "1")),
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -493,9 +521,13 @@ fn internal_model_context_does_not_parse_as_visible_turn_item() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parses_agent_message() {
|
fn parses_agent_message() {
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("msg", "1")),
|
id: Some(ResponseItemId::with_suffix("msg", "1")),
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "Hello from Codex".to_string(),
|
text: "Hello from Codex".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -584,6 +616,7 @@ fn parses_reasoning_including_raw_content() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parses_web_search_call() {
|
fn parses_web_search_call() {
|
||||||
let item = ResponseItem::WebSearchCall {
|
let item = ResponseItem::WebSearchCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("ws", "1")),
|
id: Some(ResponseItemId::with_suffix("ws", "1")),
|
||||||
status: Some("completed".to_string()),
|
status: Some("completed".to_string()),
|
||||||
action: Some(WebSearchAction::Search {
|
action: Some(WebSearchAction::Search {
|
||||||
@@ -615,6 +648,7 @@ fn parses_web_search_call() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parses_web_search_open_page_call() {
|
fn parses_web_search_open_page_call() {
|
||||||
let item = ResponseItem::WebSearchCall {
|
let item = ResponseItem::WebSearchCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("ws", "open")),
|
id: Some(ResponseItemId::with_suffix("ws", "open")),
|
||||||
status: Some("completed".to_string()),
|
status: Some("completed".to_string()),
|
||||||
action: Some(WebSearchAction::OpenPage {
|
action: Some(WebSearchAction::OpenPage {
|
||||||
@@ -644,6 +678,7 @@ fn parses_web_search_open_page_call() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parses_web_search_find_in_page_call() {
|
fn parses_web_search_find_in_page_call() {
|
||||||
let item = ResponseItem::WebSearchCall {
|
let item = ResponseItem::WebSearchCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("ws", "find")),
|
id: Some(ResponseItemId::with_suffix("ws", "find")),
|
||||||
status: Some("completed".to_string()),
|
status: Some("completed".to_string()),
|
||||||
action: Some(WebSearchAction::FindInPage {
|
action: Some(WebSearchAction::FindInPage {
|
||||||
@@ -675,6 +710,7 @@ fn parses_web_search_find_in_page_call() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parses_partial_web_search_call_without_action_as_other() {
|
fn parses_partial_web_search_call_without_action_as_other() {
|
||||||
let item = ResponseItem::WebSearchCall {
|
let item = ResponseItem::WebSearchCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("ws", "partial")),
|
id: Some(ResponseItemId::with_suffix("ws", "partial")),
|
||||||
status: Some("in_progress".to_string()),
|
status: Some("in_progress".to_string()),
|
||||||
action: None,
|
action: None,
|
||||||
|
|||||||
@@ -254,6 +254,8 @@ async fn seed_guardian_parent_history(session: &Arc<Session>, turn: &Arc<TurnCon
|
|||||||
turn.model_info(),
|
turn.model_info(),
|
||||||
&[
|
&[
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -264,6 +266,8 @@ async fn seed_guardian_parent_history(session: &Arc<Session>, turn: &Arc<TurnCon
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "gh_repo_view".to_string(),
|
name: "gh_repo_view".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -283,9 +287,13 @@ async fn seed_guardian_parent_history(session: &Arc<Session>, turn: &Arc<TurnCon
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "The repo is public; I now need approval to push the docs fix."
|
text: "The repo is public; I now need approval to push the docs fix."
|
||||||
.to_string(),
|
.to_string(),
|
||||||
}],
|
}],
|
||||||
@@ -302,7 +310,9 @@ fn response_item_contains_message_text(item: &ResponseItem, needle: &str) -> boo
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
content.iter().any(|item| match item {
|
content.iter().any(|item| match item {
|
||||||
ContentItem::InputText { text } | ContentItem::OutputText { text } => text.contains(needle),
|
ContentItem::InputText { text } | ContentItem::OutputText { text, .. } => {
|
||||||
|
text.contains(needle)
|
||||||
|
}
|
||||||
ContentItem::InputImage { .. } | ContentItem::InputAudio { .. } => false,
|
ContentItem::InputImage { .. } | ContentItem::InputAudio { .. } => false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -775,6 +785,8 @@ async fn build_guardian_prompt_delta_mode_preserves_original_numbering() -> anyh
|
|||||||
turn.model_info(),
|
turn.model_info(),
|
||||||
&[
|
&[
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -784,9 +796,13 @@ async fn build_guardian_prompt_delta_mode_preserves_original_numbering() -> anyh
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "I need approval for the second push.".to_string(),
|
text: "I need approval for the second push.".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -916,6 +932,8 @@ async fn build_guardian_prompt_stale_delta_version_falls_back_to_full_prompt() -
|
|||||||
.replace_history(
|
.replace_history(
|
||||||
vec![
|
vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -925,9 +943,13 @@ async fn build_guardian_prompt_stale_delta_version_falls_back_to_full_prompt() -
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "Compacted summary of earlier guardian context.".to_string(),
|
text: "Compacted summary of earlier guardian context.".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -943,6 +965,8 @@ async fn build_guardian_prompt_stale_delta_version_falls_back_to_full_prompt() -
|
|||||||
turn.model_info(),
|
turn.model_info(),
|
||||||
&[
|
&[
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -952,9 +976,13 @@ async fn build_guardian_prompt_stale_delta_version_falls_back_to_full_prompt() -
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "I need approval for the post-compaction push.".to_string(),
|
text: "I need approval for the post-compaction push.".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -1021,6 +1049,8 @@ fn collect_guardian_transcript_entries(
|
|||||||
fn collect_guardian_transcript_entries_skips_contextual_user_messages() {
|
fn collect_guardian_transcript_entries_skips_contextual_user_messages() {
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -1030,9 +1060,13 @@ fn collect_guardian_transcript_entries_skips_contextual_user_messages() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "hello".to_string(),
|
text: "hello".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -1058,6 +1092,8 @@ fn collect_guardian_transcript_entries_skips_contextual_user_messages() {
|
|||||||
fn collect_guardian_transcript_entries_includes_recent_tool_calls_and_output() {
|
fn collect_guardian_transcript_entries_includes_recent_tool_calls_and_output() {
|
||||||
let mut items = vec![
|
let mut items = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -1067,6 +1103,8 @@ fn collect_guardian_transcript_entries_includes_recent_tool_calls_and_output() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "read_file".to_string(),
|
name: "read_file".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -1086,9 +1124,13 @@ fn collect_guardian_transcript_entries_includes_recent_tool_calls_and_output() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "I need to push a fix".to_string(),
|
text: "I need to push a fix".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -1762,6 +1804,8 @@ fn build_guardian_transcript_reserves_separate_budget_for_tool_evidence() {
|
|||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(role, text)| ResponseItem::Message {
|
.map(|(role, text)| ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: role.to_string(),
|
role: role.to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -1772,6 +1816,8 @@ fn build_guardian_transcript_reserves_separate_budget_for_tool_evidence() {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
items.extend((0..12).map(|index| ResponseItem::FunctionCall {
|
items.extend((0..12).map(|index| ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: format!("tool_{index}"),
|
name: format!("tool_{index}"),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -1815,6 +1861,8 @@ fn build_guardian_transcript_preserves_recent_tool_context_when_user_history_is_
|
|||||||
let repeated = "authorization ".repeat(6_000);
|
let repeated = "authorization ".repeat(6_000);
|
||||||
let mut items = (0..8)
|
let mut items = (0..8)
|
||||||
.map(|_| ResponseItem::Message {
|
.map(|_| ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -1826,6 +1874,8 @@ fn build_guardian_transcript_preserves_recent_tool_context_when_user_history_is_
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
items.extend([
|
items.extend([
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "shell".to_string(),
|
name: "shell".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -2269,6 +2319,8 @@ async fn guardian_review_request_layout_matches_model_visible_request_snapshot()
|
|||||||
turn.as_ref(),
|
turn.as_ref(),
|
||||||
turn.model_info(),
|
turn.model_info(),
|
||||||
&[ResponseItem::Message {
|
&[ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -2555,6 +2607,8 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
|||||||
turn.model_info(),
|
turn.model_info(),
|
||||||
&[
|
&[
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -2564,9 +2618,13 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "I need approval for the second docs fix.".to_string(),
|
text: "I need approval for the second docs fix.".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -2642,6 +2700,8 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -2651,9 +2711,13 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "I need approval for the third docs fix.".to_string(),
|
text: "I need approval for the third docs fix.".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -2702,6 +2766,8 @@ async fn guardian_reuses_prompt_cache_key_and_appends_prior_reviews() -> anyhow:
|
|||||||
session
|
session
|
||||||
.replace_history(
|
.replace_history(
|
||||||
vec![ResponseItem::Message {
|
vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -3632,6 +3698,8 @@ async fn guardian_ephemeral_retry_preserves_parallel_trunk_and_fork_history() ->
|
|||||||
turn.as_ref(), turn.model_info(),
|
turn.as_ref(), turn.model_info(),
|
||||||
&[
|
&[
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -3640,9 +3708,13 @@ async fn guardian_ephemeral_retry_preserves_parallel_trunk_and_fork_history() ->
|
|||||||
phase: None,
|
phase: None,
|
||||||
internal_chat_message_metadata_passthrough: None,},
|
internal_chat_message_metadata_passthrough: None,},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "I need approval to run git diff.".to_string(),
|
text: "I need approval to run git diff.".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -3709,6 +3781,8 @@ async fn guardian_ephemeral_retry_preserves_parallel_trunk_and_fork_history() ->
|
|||||||
turn.as_ref(), turn.model_info(),
|
turn.as_ref(), turn.model_info(),
|
||||||
&[
|
&[
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -3717,9 +3791,13 @@ async fn guardian_ephemeral_retry_preserves_parallel_trunk_and_fork_history() ->
|
|||||||
phase: None,
|
phase: None,
|
||||||
internal_chat_message_metadata_passthrough: None,},
|
internal_chat_message_metadata_passthrough: None,},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "I need approval to push after the diff check.".to_string(),
|
text: "I need approval to push after the diff check.".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ fn decoded_image(image_url: &str) -> (Vec<u8>, DynamicImage) {
|
|||||||
async fn preparation_preserves_small_image_bytes_and_replaces_remote_urls() {
|
async fn preparation_preserves_small_image_bytes_and_replaces_remote_urls() {
|
||||||
let (data_url, original_bytes) = png_data_url(/*width*/ 64, /*height*/ 32);
|
let (data_url, original_bytes) = png_data_url(/*width*/ 64, /*height*/ 32);
|
||||||
let mut items = vec![ResponseItem::Message {
|
let mut items = vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -87,6 +89,8 @@ async fn preparation_preserves_small_image_bytes_and_replaces_remote_urls() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
}];
|
}];
|
||||||
items.push(ResponseItem::Message {
|
items.push(ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputImage {
|
content: vec![ContentItem::InputImage {
|
||||||
@@ -127,6 +131,8 @@ async fn preparation_preserves_small_image_bytes_and_replaces_remote_urls() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
&items[1],
|
&items[1],
|
||||||
&ResponseItem::Message {
|
&ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -176,6 +182,8 @@ async fn detail_policies_apply_the_expected_budgets() {
|
|||||||
] {
|
] {
|
||||||
let (image_url, _) = png_data_url(input_dimensions.0, input_dimensions.1);
|
let (image_url, _) = png_data_url(input_dimensions.0, input_dimensions.1);
|
||||||
let mut items = vec![ResponseItem::Message {
|
let mut items = vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputImage {
|
content: vec![ContentItem::InputImage {
|
||||||
@@ -269,6 +277,8 @@ async fn preparation_reports_tool_output_item_id() {
|
|||||||
async fn upload_failure_keeps_resized_image_inline() {
|
async fn upload_failure_keeps_resized_image_inline() {
|
||||||
let (image_url, _) = png_data_url(/*width*/ 2048, /*height*/ 2048);
|
let (image_url, _) = png_data_url(/*width*/ 2048, /*height*/ 2048);
|
||||||
let mut items = vec![ResponseItem::Message {
|
let mut items = vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputImage {
|
content: vec![ContentItem::InputImage {
|
||||||
@@ -311,6 +321,8 @@ async fn resize_notices_count_file_backed_images_and_skip_failed_images() {
|
|||||||
let (small_image_url, _) = png_data_url(/*width*/ 64, /*height*/ 32);
|
let (small_image_url, _) = png_data_url(/*width*/ 64, /*height*/ 32);
|
||||||
let mut items = vec![
|
let mut items = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -456,6 +468,8 @@ async fn resize_notices_count_file_backed_images_and_skip_failed_images() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
&items[1],
|
&items[1],
|
||||||
&ResponseItem::Message {
|
&ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -504,6 +518,8 @@ async fn resize_notices_count_file_backed_images_and_skip_failed_images() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
&items[3],
|
&items[3],
|
||||||
&ResponseItem::Message {
|
&ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -81,6 +81,8 @@ fn stored_thread(cwd: &str, title: &str, first_user_message: &str) -> StoredThre
|
|||||||
|
|
||||||
fn message(role: &str, content: ContentItem) -> ResponseItem {
|
fn message(role: &str, content: ContentItem) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: role.to_string(),
|
role: role.to_string(),
|
||||||
content: vec![content],
|
content: vec![content],
|
||||||
@@ -94,7 +96,14 @@ fn user_message(text: impl Into<String>) -> ResponseItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn assistant_message(text: impl Into<String>) -> ResponseItem {
|
fn assistant_message(text: impl Into<String>) -> ResponseItem {
|
||||||
message("assistant", ContentItem::OutputText { text: text.into() })
|
message(
|
||||||
|
"assistant",
|
||||||
|
ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
|
text: text.into(),
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn long_turn_text(index: usize) -> String {
|
fn long_turn_text(index: usize) -> String {
|
||||||
|
|||||||
@@ -204,6 +204,8 @@ macro_rules! object {
|
|||||||
|
|
||||||
fn user_message(text: &str) -> ResponseItem {
|
fn user_message(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -216,9 +218,13 @@ fn user_message(text: &str) -> ResponseItem {
|
|||||||
|
|
||||||
fn assistant_message(text: &str) -> ResponseItem {
|
fn assistant_message(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: text.to_string(),
|
text: text.to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -239,9 +245,13 @@ fn inter_agent_assistant_message(text: &str) -> ResponseItem {
|
|||||||
/*trigger_turn*/ true,
|
/*trigger_turn*/ true,
|
||||||
);
|
);
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: serde_json::to_string(&communication).unwrap(),
|
text: serde_json::to_string(&communication).unwrap(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
|
|||||||
@@ -1776,6 +1776,7 @@ impl Session {
|
|||||||
config
|
config
|
||||||
.features
|
.features
|
||||||
.enabled(Feature::ConcurrentReasoningSummaries),
|
.enabled(Feature::ConcurrentReasoningSummaries),
|
||||||
|
config.features.enabled(Feature::OutputTokenReplay),
|
||||||
attestation_provider,
|
attestation_provider,
|
||||||
config.http_client_factory(),
|
config.http_client_factory(),
|
||||||
workspace_routing.as_ref().clone(),
|
workspace_routing.as_ref().clone(),
|
||||||
|
|||||||
@@ -322,6 +322,8 @@ mod guardian_tests;
|
|||||||
|
|
||||||
fn user_message(text: &str) -> ResponseItem {
|
fn user_message(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -391,9 +393,13 @@ async fn default_turn_context_assigns_missing_response_item_ids() {
|
|||||||
|
|
||||||
fn assistant_message(text: &str) -> ResponseItem {
|
fn assistant_message(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: text.to_string(),
|
text: text.to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -589,6 +595,8 @@ async fn world_state_extension_metrics_follow_turn_model_switch() {
|
|||||||
|
|
||||||
fn skill_message(text: &str) -> ResponseItem {
|
fn skill_message(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -853,6 +861,7 @@ fn test_model_client_session() -> crate::client::ModelClientSession {
|
|||||||
/*include_timing_metrics*/ false,
|
/*include_timing_metrics*/ false,
|
||||||
/*beta_features_header*/ None,
|
/*beta_features_header*/ None,
|
||||||
/*concurrent_reasoning_summaries_enabled*/ false,
|
/*concurrent_reasoning_summaries_enabled*/ false,
|
||||||
|
/*output_token_replay_enabled*/ false,
|
||||||
/*attestation_provider*/ None,
|
/*attestation_provider*/ None,
|
||||||
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
|
HttpClientFactory::new(OutboundProxyPolicy::ReqwestDefault),
|
||||||
codex_model_provider::WorkspaceRoutingContext::new(
|
codex_model_provider::WorkspaceRoutingContext::new(
|
||||||
@@ -2426,6 +2435,8 @@ async fn reconstruct_history_matches_live_compactions() {
|
|||||||
async fn reconstruct_history_uses_replacement_history_verbatim() {
|
async fn reconstruct_history_uses_replacement_history_verbatim() {
|
||||||
let (session, turn_context) = make_session_and_context().await;
|
let (session, turn_context) = make_session_and_context().await;
|
||||||
let summary_item = ResponseItem::Message {
|
let summary_item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -2443,6 +2454,8 @@ async fn reconstruct_history_uses_replacement_history_verbatim() {
|
|||||||
metadata: Some(CodexHarnessMetadata::default()),
|
metadata: Some(CodexHarnessMetadata::default()),
|
||||||
},
|
},
|
||||||
ResponseItemEnvelope::new(ResponseItem::Message {
|
ResponseItemEnvelope::new(ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -3095,6 +3108,8 @@ async fn prepares_image_failures_before_history_insertion() {
|
|||||||
async fn prepares_resumed_history_before_installing_it() {
|
async fn prepares_resumed_history_before_installing_it() {
|
||||||
let (session, _turn_context) = make_session_and_context().await;
|
let (session, _turn_context) = make_session_and_context().await;
|
||||||
let resumed_item = ResponseItem::Message {
|
let resumed_item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -3134,6 +3149,8 @@ async fn prepares_resumed_history_before_installing_it() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
raw_history_items(&history),
|
raw_history_items(&history),
|
||||||
vec![ResponseItem::Message {
|
vec![ResponseItem::Message {
|
||||||
|
encrypted_content: None,
|
||||||
|
status: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -4114,6 +4131,8 @@ async fn record_initial_history_assigns_and_persists_id_for_forked_response_item
|
|||||||
let response_item =
|
let response_item =
|
||||||
ContextualUserFragment::into(DeveloperInstructions::new("Subagent guidance."));
|
ContextualUserFragment::into(DeveloperInstructions::new("Subagent guidance."));
|
||||||
let mut expected_item = ResponseItem::Message {
|
let mut expected_item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -6907,6 +6926,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
|||||||
config
|
config
|
||||||
.features
|
.features
|
||||||
.enabled(Feature::ConcurrentReasoningSummaries),
|
.enabled(Feature::ConcurrentReasoningSummaries),
|
||||||
|
/*output_token_replay_enabled*/ false,
|
||||||
/*attestation_provider*/ None,
|
/*attestation_provider*/ None,
|
||||||
config.http_client_factory(),
|
config.http_client_factory(),
|
||||||
config.workspace_routing_context(),
|
config.workspace_routing_context(),
|
||||||
@@ -9174,6 +9194,7 @@ where
|
|||||||
config
|
config
|
||||||
.features
|
.features
|
||||||
.enabled(Feature::ConcurrentReasoningSummaries),
|
.enabled(Feature::ConcurrentReasoningSummaries),
|
||||||
|
/*output_token_replay_enabled*/ false,
|
||||||
/*attestation_provider*/ None,
|
/*attestation_provider*/ None,
|
||||||
config.http_client_factory(),
|
config.http_client_factory(),
|
||||||
config.workspace_routing_context(),
|
config.workspace_routing_context(),
|
||||||
@@ -11135,6 +11156,8 @@ async fn record_context_updates_and_set_reference_context_item_reinjects_full_co
|
|||||||
let turn_context = Arc::new(turn_context);
|
let turn_context = Arc::new(turn_context);
|
||||||
let step_context = StepContext::for_test(Arc::clone(&turn_context));
|
let step_context = StepContext::for_test(Arc::clone(&turn_context));
|
||||||
let compacted_summary = ResponseItem::Message {
|
let compacted_summary = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -12479,6 +12502,8 @@ async fn task_finish_emits_turn_item_lifecycle_for_leftover_pending_user_input()
|
|||||||
|
|
||||||
let history = sess.clone_history().await;
|
let history = sess.clone_history().await;
|
||||||
let expected = ResponseItem::Message {
|
let expected = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -12663,6 +12688,8 @@ async fn thread_idle_lifecycle_waits_for_trigger_turn_mailbox_work() {
|
|||||||
async fn abort_empty_active_turn_preserves_pending_input() {
|
async fn abort_empty_active_turn_preserves_pending_input() {
|
||||||
let (sess, _tc, _rx) = make_session_and_context_with_rx().await;
|
let (sess, _tc, _rx) = make_session_and_context_with_rx().await;
|
||||||
let pending_item = ResponseItem::Message {
|
let pending_item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -13003,6 +13030,8 @@ async fn tool_calls_reopen_mailbox_delivery_for_current_turn() {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let item = ResponseItem::FunctionCall {
|
let item = ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "test_tool".to_string(),
|
name: "test_tool".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -13122,6 +13151,7 @@ async fn fatal_tool_error_stops_turn_and_reports_error() {
|
|||||||
&Default::default(),
|
&Default::default(),
|
||||||
);
|
);
|
||||||
let item = ResponseItem::CustomToolCall {
|
let item = ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: None,
|
status: None,
|
||||||
call_id: "call-1".to_string(),
|
call_id: "call-1".to_string(),
|
||||||
|
|||||||
@@ -2321,7 +2321,7 @@ async fn maybe_complete_plan_item_from_message(
|
|||||||
{
|
{
|
||||||
let mut text = String::new();
|
let mut text = String::new();
|
||||||
for entry in content {
|
for entry in content {
|
||||||
if let ContentItem::OutputText { text: chunk } = entry {
|
if let ContentItem::OutputText { text: chunk, .. } = entry {
|
||||||
text.push_str(chunk);
|
text.push_str(chunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ impl SessionTask for NeverEndingTask {
|
|||||||
|
|
||||||
fn user_message(text: &str) -> ResponseItem {
|
fn user_message(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -29,9 +29,13 @@ impl TurnItemContributor for RewriteAgentMessageContributor {
|
|||||||
|
|
||||||
fn assistant_output_text(text: &str) -> ResponseItem {
|
fn assistant_output_text(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("msg", "1")),
|
id: Some(ResponseItemId::with_suffix("msg", "1")),
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: text.to_string(),
|
text: text.to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ pub(crate) fn raw_assistant_output_text_from_item(item: &ResponseItem) -> Option
|
|||||||
let combined = content
|
let combined = content
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|ci| match ci {
|
.filter_map(|ci| match ci {
|
||||||
codex_protocol::models::ContentItem::OutputText { text } => Some(text.as_str()),
|
codex_protocol::models::ContentItem::OutputText { text, .. } => Some(text.as_str()),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
|||||||
@@ -41,9 +41,13 @@ fn assistant_output_text(text: &str) -> ResponseItem {
|
|||||||
|
|
||||||
fn assistant_output_text_with_phase(text: &str, phase: Option<MessagePhase>) -> ResponseItem {
|
fn assistant_output_text_with_phase(text: &str, phase: Option<MessagePhase>) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("msg", "1")),
|
id: Some(ResponseItemId::with_suffix("msg", "1")),
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: text.to_string(),
|
text: text.to_string(),
|
||||||
}],
|
}],
|
||||||
phase,
|
phase,
|
||||||
@@ -55,12 +59,14 @@ fn assistant_output_text_with_phase(text: &str, phase: Option<MessagePhase>) ->
|
|||||||
fn external_context_pollution_items_include_web_search_and_tool_search() {
|
fn external_context_pollution_items_include_web_search_and_tool_search() {
|
||||||
let polluting_items = [
|
let polluting_items = [
|
||||||
ResponseItem::WebSearchCall {
|
ResponseItem::WebSearchCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: Some("completed".to_string()),
|
status: Some("completed".to_string()),
|
||||||
action: None,
|
action: None,
|
||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::ToolSearchCall {
|
ResponseItem::ToolSearchCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
call_id: Some("search-1".to_string()),
|
call_id: Some("search-1".to_string()),
|
||||||
status: None,
|
status: None,
|
||||||
@@ -97,6 +103,7 @@ fn external_context_pollution_items_include_web_search_and_tool_search() {
|
|||||||
fn external_context_pollution_items_exclude_local_tool_calls() {
|
fn external_context_pollution_items_exclude_local_tool_calls() {
|
||||||
let non_polluting_items = [
|
let non_polluting_items = [
|
||||||
ResponseItem::LocalShellCall {
|
ResponseItem::LocalShellCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
call_id: Some("shell-1".to_string()),
|
call_id: Some("shell-1".to_string()),
|
||||||
status: LocalShellStatus::Completed,
|
status: LocalShellStatus::Completed,
|
||||||
@@ -110,6 +117,8 @@ fn external_context_pollution_items_exclude_local_tool_calls() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "shell".to_string(),
|
name: "shell".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -127,6 +136,7 @@ fn external_context_pollution_items_exclude_local_tool_calls() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::CustomToolCall {
|
ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: None,
|
status: None,
|
||||||
call_id: "custom-1".to_string(),
|
call_id: "custom-1".to_string(),
|
||||||
|
|||||||
@@ -240,6 +240,8 @@ pub(crate) async fn exit_review_mode(
|
|||||||
&ctx,
|
&ctx,
|
||||||
ctx.model_info(),
|
ctx.model_info(),
|
||||||
&[ResponseItem::Message {
|
&[ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::new("msg")),
|
id: Some(ResponseItemId::new("msg")),
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText { text: user_message }],
|
content: vec![ContentItem::InputText { text: user_message }],
|
||||||
@@ -260,9 +262,13 @@ pub(crate) async fn exit_review_mode(
|
|||||||
ctx.as_ref(),
|
ctx.as_ref(),
|
||||||
ctx.model_info(),
|
ctx.model_info(),
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::new("msg")),
|
id: Some(ResponseItemId::new("msg")),
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: assistant_message,
|
text: assistant_message,
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
|
|||||||
@@ -582,9 +582,13 @@ impl codex_agent_graph_store::AgentGraphStore for FakeAgentGraphStore {
|
|||||||
|
|
||||||
fn user_msg(text: &str) -> ResponseItem {
|
fn user_msg(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: text.to_string(),
|
text: text.to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -593,9 +597,13 @@ fn user_msg(text: &str) -> ResponseItem {
|
|||||||
}
|
}
|
||||||
fn assistant_msg(text: &str) -> ResponseItem {
|
fn assistant_msg(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: text.to_string(),
|
text: text.to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -696,6 +704,8 @@ fn truncates_before_requested_user_message() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
call_id: "c1".to_string(),
|
call_id: "c1".to_string(),
|
||||||
name: "tool".to_string(),
|
name: "tool".to_string(),
|
||||||
@@ -3060,6 +3070,8 @@ fn multi_agent_v2_interrupted_marker_uses_developer_input_message() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
developer_interrupted_marker(),
|
developer_interrupted_marker(),
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -19,9 +19,13 @@ fn response_item(item: ResponseItem) -> RolloutItem {
|
|||||||
|
|
||||||
fn user_msg(text: &str) -> ResponseItem {
|
fn user_msg(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: text.to_string(),
|
text: text.to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -31,9 +35,13 @@ fn user_msg(text: &str) -> ResponseItem {
|
|||||||
|
|
||||||
fn assistant_msg(text: &str) -> ResponseItem {
|
fn assistant_msg(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: text.to_string(),
|
text: text.to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -245,6 +253,8 @@ fn truncates_rollout_from_start_before_nth_user_only() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
call_id: "c1".to_string(),
|
call_id: "c1".to_string(),
|
||||||
name: "tool".to_string(),
|
name: "tool".to_string(),
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ fn source(tool_name: &str, first_turn_id: &str) -> McpAttributionSource {
|
|||||||
fn envelope(attribution: Option<McpAttribution>) -> ResponseItemEnvelope {
|
fn envelope(attribution: Option<McpAttribution>) -> ResponseItemEnvelope {
|
||||||
ResponseItemEnvelope {
|
ResponseItemEnvelope {
|
||||||
item: ResponseItem::Message {
|
item: ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: Vec::new(),
|
content: Vec::new(),
|
||||||
|
|||||||
@@ -435,6 +435,8 @@ mod tests {
|
|||||||
.map(|environment| environment.cwd().clone())
|
.map(|environment| environment.cwd().clone())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let history_item = ResponseItem::Message {
|
let history_item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -2804,6 +2804,8 @@ async fn resume_agent_restores_closed_agent_and_accepts_send_input() {
|
|||||||
config.clone(),
|
config.clone(),
|
||||||
InitialHistory::Forked(vec![RolloutItem::ResponseItem(
|
InitialHistory::Forked(vec![RolloutItem::ResponseItem(
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -212,6 +212,8 @@ async fn build_tool_call_uses_namespace_for_registry_name() -> anyhow::Result<()
|
|||||||
let tool_name = "create_event".to_string();
|
let tool_name = "create_event".to_string();
|
||||||
|
|
||||||
let call = ToolRouter::build_tool_call(ResponseItem::FunctionCall {
|
let call = ToolRouter::build_tool_call(ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: tool_name.clone(),
|
name: tool_name.clone(),
|
||||||
namespace: Some("mcp__codex_apps__calendar".to_string()),
|
namespace: Some("mcp__codex_apps__calendar".to_string()),
|
||||||
@@ -244,6 +246,7 @@ async fn build_custom_tool_call_uses_namespace_for_registry_name() -> anyhow::Re
|
|||||||
let tool_name = "exec".to_string();
|
let tool_name = "exec".to_string();
|
||||||
|
|
||||||
let call = ToolRouter::build_tool_call(ResponseItem::CustomToolCall {
|
let call = ToolRouter::build_tool_call(ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: None,
|
status: None,
|
||||||
call_id: "call-namespace".to_string(),
|
call_id: "call-namespace".to_string(),
|
||||||
@@ -273,6 +276,8 @@ async fn build_custom_tool_call_uses_namespace_for_registry_name() -> anyhow::Re
|
|||||||
fn build_tool_call_normalizes_default_function_and_custom_namespaces() -> anyhow::Result<()> {
|
fn build_tool_call_normalizes_default_function_and_custom_namespaces() -> anyhow::Result<()> {
|
||||||
for namespace in [None, Some(""), Some(DEFAULT_FUNCTION_NAMESPACE)] {
|
for namespace in [None, Some(""), Some(DEFAULT_FUNCTION_NAMESPACE)] {
|
||||||
let function_call = ToolRouter::build_tool_call(ResponseItem::FunctionCall {
|
let function_call = ToolRouter::build_tool_call(ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "lookup".to_string(),
|
name: "lookup".to_string(),
|
||||||
namespace: namespace.map(str::to_string),
|
namespace: namespace.map(str::to_string),
|
||||||
@@ -283,6 +288,7 @@ fn build_tool_call_normalizes_default_function_and_custom_namespaces() -> anyhow
|
|||||||
})?
|
})?
|
||||||
.expect("function_call should produce a tool call");
|
.expect("function_call should produce a tool call");
|
||||||
let custom_call = ToolRouter::build_tool_call(ResponseItem::CustomToolCall {
|
let custom_call = ToolRouter::build_tool_call(ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: None,
|
status: None,
|
||||||
call_id: "call-custom".to_string(),
|
call_id: "call-custom".to_string(),
|
||||||
@@ -535,6 +541,8 @@ async fn extension_tool_executors_are_model_visible_and_dispatchable() -> anyhow
|
|||||||
let turn = Arc::new(turn);
|
let turn = Arc::new(turn);
|
||||||
let step_context = StepContext::for_test(Arc::clone(&turn));
|
let step_context = StepContext::for_test(Arc::clone(&turn));
|
||||||
let history_item = ResponseItem::Message {
|
let history_item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -581,6 +589,8 @@ async fn extension_tool_executors_are_model_visible_and_dispatchable() -> anyhow
|
|||||||
);
|
);
|
||||||
|
|
||||||
let call = ToolRouter::build_tool_call(ResponseItem::FunctionCall {
|
let call = ToolRouter::build_tool_call(ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "echo".to_string(),
|
name: "echo".to_string(),
|
||||||
namespace: Some("extension/".to_string()),
|
namespace: Some("extension/".to_string()),
|
||||||
|
|||||||
@@ -149,6 +149,8 @@ async fn turn_timing_state_preserves_in_flight_items_after_turn_completion() {
|
|||||||
fn response_item_records_turn_ttft_for_first_output_signals() {
|
fn response_item_records_turn_ttft_for_first_output_signals() {
|
||||||
assert!(response_item_records_turn_ttft(
|
assert!(response_item_records_turn_ttft(
|
||||||
&ResponseItem::FunctionCall {
|
&ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "shell".to_string(),
|
name: "shell".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -160,6 +162,7 @@ fn response_item_records_turn_ttft_for_first_output_signals() {
|
|||||||
));
|
));
|
||||||
assert!(response_item_records_turn_ttft(
|
assert!(response_item_records_turn_ttft(
|
||||||
&ResponseItem::CustomToolCall {
|
&ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: None,
|
status: None,
|
||||||
call_id: "call-2".to_string(),
|
call_id: "call-2".to_string(),
|
||||||
@@ -170,9 +173,13 @@ fn response_item_records_turn_ttft_for_first_output_signals() {
|
|||||||
}
|
}
|
||||||
));
|
));
|
||||||
assert!(response_item_records_turn_ttft(&ResponseItem::Message {
|
assert!(response_item_records_turn_ttft(&ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "hello".to_string(),
|
text: "hello".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -183,9 +190,13 @@ fn response_item_records_turn_ttft_for_first_output_signals() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn response_item_records_turn_ttft_ignores_empty_non_output_items() {
|
fn response_item_records_turn_ttft_ignores_empty_non_output_items() {
|
||||||
assert!(!response_item_records_turn_ttft(&ResponseItem::Message {
|
assert!(!response_item_records_turn_ttft(&ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: String::new(),
|
text: String::new(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
|
|||||||
@@ -832,6 +832,8 @@ pub fn ev_assistant_message(id: &str, text: &str) -> Value {
|
|||||||
|
|
||||||
pub fn user_message_item(text: &str) -> ResponseItem {
|
pub fn user_message_item(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ async fn responses_stream_includes_subagent_header_on_review() {
|
|||||||
/*include_timing_metrics*/ false,
|
/*include_timing_metrics*/ false,
|
||||||
/*beta_features_header*/ None,
|
/*beta_features_header*/ None,
|
||||||
/*concurrent_reasoning_summaries_enabled*/ false,
|
/*concurrent_reasoning_summaries_enabled*/ false,
|
||||||
|
/*output_token_replay_enabled*/ false,
|
||||||
/*attestation_provider*/ None,
|
/*attestation_provider*/ None,
|
||||||
config.http_client_factory(),
|
config.http_client_factory(),
|
||||||
config.workspace_routing_context(),
|
config.workspace_routing_context(),
|
||||||
@@ -149,6 +150,8 @@ async fn responses_stream_includes_subagent_header_on_review() {
|
|||||||
|
|
||||||
let mut prompt = Prompt::default();
|
let mut prompt = Prompt::default();
|
||||||
prompt.input = vec![ResponseItem::Message {
|
prompt.input = vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".into(),
|
role: "user".into(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -283,6 +286,7 @@ async fn responses_stream_includes_subagent_header_on_other() {
|
|||||||
/*include_timing_metrics*/ false,
|
/*include_timing_metrics*/ false,
|
||||||
/*beta_features_header*/ None,
|
/*beta_features_header*/ None,
|
||||||
/*concurrent_reasoning_summaries_enabled*/ false,
|
/*concurrent_reasoning_summaries_enabled*/ false,
|
||||||
|
/*output_token_replay_enabled*/ false,
|
||||||
/*attestation_provider*/ None,
|
/*attestation_provider*/ None,
|
||||||
config.http_client_factory(),
|
config.http_client_factory(),
|
||||||
config.workspace_routing_context(),
|
config.workspace_routing_context(),
|
||||||
@@ -293,6 +297,8 @@ async fn responses_stream_includes_subagent_header_on_other() {
|
|||||||
|
|
||||||
let mut prompt = Prompt::default();
|
let mut prompt = Prompt::default();
|
||||||
prompt.input = vec![ResponseItem::Message {
|
prompt.input = vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".into(),
|
role: "user".into(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -412,6 +418,7 @@ async fn responses_respects_model_info_overrides_from_config() {
|
|||||||
/*include_timing_metrics*/ false,
|
/*include_timing_metrics*/ false,
|
||||||
/*beta_features_header*/ None,
|
/*beta_features_header*/ None,
|
||||||
/*concurrent_reasoning_summaries_enabled*/ false,
|
/*concurrent_reasoning_summaries_enabled*/ false,
|
||||||
|
/*output_token_replay_enabled*/ false,
|
||||||
/*attestation_provider*/ None,
|
/*attestation_provider*/ None,
|
||||||
config.http_client_factory(),
|
config.http_client_factory(),
|
||||||
config.workspace_routing_context(),
|
config.workspace_routing_context(),
|
||||||
@@ -422,6 +429,8 @@ async fn responses_respects_model_info_overrides_from_config() {
|
|||||||
|
|
||||||
let mut prompt = Prompt::default();
|
let mut prompt = Prompt::default();
|
||||||
prompt.input = vec![ResponseItem::Message {
|
prompt.input = vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".into(),
|
role: "user".into(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -692,6 +692,8 @@ async fn synthetic_call_output_id_is_stable_across_resumes() -> anyhow::Result<(
|
|||||||
timestamp: "2024-01-01T00:00:01.000Z".to_string(),
|
timestamp: "2024-01-01T00:00:01.000Z".to_string(),
|
||||||
ordinal: None,
|
ordinal: None,
|
||||||
item: rollout_response_item(ResponseItem::FunctionCall {
|
item: rollout_response_item(ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("fc", "existing")),
|
id: Some(ResponseItemId::with_suffix("fc", "existing")),
|
||||||
name: "do_it".to_string(),
|
name: "do_it".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -988,6 +990,8 @@ async fn resume_sends_prior_items() {
|
|||||||
|
|
||||||
// Prior item: user message (should be delivered)
|
// Prior item: user message (should be delivered)
|
||||||
let prior_user = codex_protocol::models::ResponseItem::Message {
|
let prior_user = codex_protocol::models::ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![codex_protocol::models::ContentItem::InputText {
|
content: vec![codex_protocol::models::ContentItem::InputText {
|
||||||
@@ -1010,9 +1014,13 @@ async fn resume_sends_prior_items() {
|
|||||||
|
|
||||||
// Prior item: system message (excluded from API history)
|
// Prior item: system message (excluded from API history)
|
||||||
let prior_system = codex_protocol::models::ResponseItem::Message {
|
let prior_system = codex_protocol::models::ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "system".to_string(),
|
role: "system".to_string(),
|
||||||
content: vec![codex_protocol::models::ContentItem::OutputText {
|
content: vec![codex_protocol::models::ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "resumed system instruction".to_string(),
|
text: "resumed system instruction".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -1032,9 +1040,13 @@ async fn resume_sends_prior_items() {
|
|||||||
|
|
||||||
// Prior item: assistant message
|
// Prior item: assistant message
|
||||||
let prior_item = codex_protocol::models::ResponseItem::Message {
|
let prior_item = codex_protocol::models::ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![codex_protocol::models::ContentItem::OutputText {
|
content: vec![codex_protocol::models::ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "resumed assistant message".to_string(),
|
text: "resumed assistant message".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: Some(MessagePhase::Commentary),
|
phase: Some(MessagePhase::Commentary),
|
||||||
@@ -1160,6 +1172,7 @@ async fn resume_replays_legacy_js_repl_image_rollout_shapes() {
|
|||||||
// Current image tests cover today's shapes; this keeps resume compatibility for that
|
// Current image tests cover today's shapes; this keeps resume compatibility for that
|
||||||
// legacy rollout representation.
|
// legacy rollout representation.
|
||||||
let legacy_custom_tool_call = ResponseItem::CustomToolCall {
|
let legacy_custom_tool_call = ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: None,
|
status: None,
|
||||||
call_id: "legacy-js-call".to_string(),
|
call_id: "legacy-js-call".to_string(),
|
||||||
@@ -1209,6 +1222,8 @@ async fn resume_replays_legacy_js_repl_image_rollout_shapes() {
|
|||||||
timestamp: "2024-01-01T00:00:03.000Z".to_string(),
|
timestamp: "2024-01-01T00:00:03.000Z".to_string(),
|
||||||
ordinal: None,
|
ordinal: None,
|
||||||
item: rollout_response_item(ResponseItem::Message {
|
item: rollout_response_item(ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputImage {
|
content: vec![ContentItem::InputImage {
|
||||||
@@ -1335,6 +1350,8 @@ async fn resume_replays_image_tool_outputs_with_detail() {
|
|||||||
timestamp: "2024-01-01T00:00:01.000Z".to_string(),
|
timestamp: "2024-01-01T00:00:01.000Z".to_string(),
|
||||||
ordinal: None,
|
ordinal: None,
|
||||||
item: rollout_response_item(ResponseItem::FunctionCall {
|
item: rollout_response_item(ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "view_image".to_string(),
|
name: "view_image".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -1367,6 +1384,7 @@ async fn resume_replays_image_tool_outputs_with_detail() {
|
|||||||
timestamp: "2024-01-01T00:00:02.000Z".to_string(),
|
timestamp: "2024-01-01T00:00:02.000Z".to_string(),
|
||||||
ordinal: None,
|
ordinal: None,
|
||||||
item: rollout_response_item(ResponseItem::CustomToolCall {
|
item: rollout_response_item(ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: Some("completed".to_string()),
|
status: Some("completed".to_string()),
|
||||||
call_id: custom_call_id.to_string(),
|
call_id: custom_call_id.to_string(),
|
||||||
@@ -1752,6 +1770,7 @@ async fn send_request_with_provider(provider: ModelProviderInfo) {
|
|||||||
config
|
config
|
||||||
.features
|
.features
|
||||||
.enabled(Feature::ConcurrentReasoningSummaries),
|
.enabled(Feature::ConcurrentReasoningSummaries),
|
||||||
|
/*output_token_replay_enabled*/ false,
|
||||||
/*attestation_provider*/ None,
|
/*attestation_provider*/ None,
|
||||||
config.http_client_factory(),
|
config.http_client_factory(),
|
||||||
config.workspace_routing_context(),
|
config.workspace_routing_context(),
|
||||||
@@ -1772,6 +1791,8 @@ async fn send_request_with_provider(provider: ModelProviderInfo) {
|
|||||||
}
|
}
|
||||||
let mut prompt = Prompt::default();
|
let mut prompt = Prompt::default();
|
||||||
prompt.input.push(ResponseItem::Message {
|
prompt.input.push(ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -3247,6 +3268,7 @@ async fn azure_responses_request_does_not_store_and_preserves_prefixed_item_ids(
|
|||||||
/*include_timing_metrics*/ false,
|
/*include_timing_metrics*/ false,
|
||||||
/*beta_features_header*/ None,
|
/*beta_features_header*/ None,
|
||||||
/*concurrent_reasoning_summaries_enabled*/ false,
|
/*concurrent_reasoning_summaries_enabled*/ false,
|
||||||
|
/*output_token_replay_enabled*/ false,
|
||||||
/*attestation_provider*/ None,
|
/*attestation_provider*/ None,
|
||||||
config.http_client_factory(),
|
config.http_client_factory(),
|
||||||
config.workspace_routing_context(),
|
config.workspace_routing_context(),
|
||||||
@@ -3268,15 +3290,20 @@ async fn azure_responses_request_does_not_store_and_preserves_prefixed_item_ids(
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
});
|
});
|
||||||
prompt.input.push(ResponseItem::Message {
|
prompt.input.push(ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("msg", "message-id")),
|
id: Some(ResponseItemId::with_suffix("msg", "message-id")),
|
||||||
role: "assistant".into(),
|
role: "assistant".into(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "message".into(),
|
text: "message".into(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
});
|
});
|
||||||
prompt.input.push(ResponseItem::WebSearchCall {
|
prompt.input.push(ResponseItem::WebSearchCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("ws", "web-search-id")),
|
id: Some(ResponseItemId::with_suffix("ws", "web-search-id")),
|
||||||
status: Some("completed".into()),
|
status: Some("completed".into()),
|
||||||
action: Some(WebSearchAction::Search {
|
action: Some(WebSearchAction::Search {
|
||||||
@@ -3286,6 +3313,8 @@ async fn azure_responses_request_does_not_store_and_preserves_prefixed_item_ids(
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
});
|
});
|
||||||
prompt.input.push(ResponseItem::FunctionCall {
|
prompt.input.push(ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("fc", "function-id")),
|
id: Some(ResponseItemId::with_suffix("fc", "function-id")),
|
||||||
name: "do_thing".into(),
|
name: "do_thing".into(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -3303,6 +3332,7 @@ async fn azure_responses_request_does_not_store_and_preserves_prefixed_item_ids(
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
});
|
});
|
||||||
prompt.input.push(ResponseItem::LocalShellCall {
|
prompt.input.push(ResponseItem::LocalShellCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("lsh", "local-shell-id")),
|
id: Some(ResponseItemId::with_suffix("lsh", "local-shell-id")),
|
||||||
call_id: Some("local-shell-call-id".into()),
|
call_id: Some("local-shell-call-id".into()),
|
||||||
status: LocalShellStatus::Completed,
|
status: LocalShellStatus::Completed,
|
||||||
@@ -3316,6 +3346,7 @@ async fn azure_responses_request_does_not_store_and_preserves_prefixed_item_ids(
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
});
|
});
|
||||||
prompt.input.push(ResponseItem::CustomToolCall {
|
prompt.input.push(ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("ctc", "custom-tool-id")),
|
id: Some(ResponseItemId::with_suffix("ctc", "custom-tool-id")),
|
||||||
status: Some("completed".into()),
|
status: Some("completed".into()),
|
||||||
call_id: "custom-tool-call-id".into(),
|
call_id: "custom-tool-call-id".into(),
|
||||||
|
|||||||
@@ -2661,6 +2661,8 @@ async fn responses_websocket_v2_sets_openai_beta_header() {
|
|||||||
|
|
||||||
fn message_item(text: &str) -> ResponseItem {
|
fn message_item(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".into(),
|
role: "user".into(),
|
||||||
content: vec![ContentItem::InputText { text: text.into() }],
|
content: vec![ContentItem::InputText { text: text.into() }],
|
||||||
@@ -2671,9 +2673,15 @@ fn message_item(text: &str) -> ResponseItem {
|
|||||||
|
|
||||||
fn assistant_message_item(id: &str, text: &str) -> ResponseItem {
|
fn assistant_message_item(id: &str, text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("msg", id)),
|
id: Some(ResponseItemId::with_suffix("msg", id)),
|
||||||
role: "assistant".into(),
|
role: "assistant".into(),
|
||||||
content: vec![ContentItem::OutputText { text: text.into() }],
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
|
text: text.into(),
|
||||||
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
}
|
}
|
||||||
@@ -2870,6 +2878,7 @@ async fn websocket_harness_with_provider_options_and_auth(
|
|||||||
config
|
config
|
||||||
.features
|
.features
|
||||||
.enabled(Feature::ConcurrentReasoningSummaries),
|
.enabled(Feature::ConcurrentReasoningSummaries),
|
||||||
|
config.features.enabled(Feature::OutputTokenReplay),
|
||||||
/*attestation_provider*/ None,
|
/*attestation_provider*/ None,
|
||||||
http_client_factory,
|
http_client_factory,
|
||||||
config.workspace_routing_context(),
|
config.workspace_routing_context(),
|
||||||
|
|||||||
@@ -367,6 +367,8 @@ async fn remote_compact_v2_retains_only_client_developer_messages_when_enabled(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let developer = |text: &str| ResponseItem::Message {
|
let developer = |text: &str| ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -148,6 +148,8 @@ async fn independent_review_preserves_oversized_instruction_order() -> Result<()
|
|||||||
.inject_response_items(vec![
|
.inject_response_items(vec![
|
||||||
responses::user_message_item(&followup),
|
responses::user_message_item(&followup),
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_owned(),
|
role: "developer".to_owned(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -2261,7 +2261,7 @@ async fn guardian_denial_rejects_tool_call_with_rationale(
|
|||||||
assert!(recorded_history.iter().any(|item| {
|
assert!(recorded_history.iter().any(|item| {
|
||||||
matches!(item, ResponseItem::Message { role, content, .. }
|
matches!(item, ResponseItem::Message { role, content, .. }
|
||||||
if role == "assistant" && content.iter().any(|part| {
|
if role == "assistant" && content.iter().any(|part| {
|
||||||
matches!(part, ContentItem::OutputText { text }
|
matches!(part, ContentItem::OutputText { text , .. }
|
||||||
if Some(text.as_str()) == record["decision"].as_str())
|
if Some(text.as_str()) == record["decision"].as_str())
|
||||||
}))
|
}))
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -1016,6 +1016,8 @@ async fn run_guardian_subagent_review(
|
|||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|text| ResponseItem::Message {
|
.map(|text| ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText { text }],
|
content: vec![ContentItem::InputText { text }],
|
||||||
@@ -1027,6 +1029,8 @@ async fn run_guardian_subagent_review(
|
|||||||
if matches!(root_context, RootContext::RetainedAtMessageLimit) {
|
if matches!(root_context, RootContext::RetainedAtMessageLimit) {
|
||||||
// The newest final answer must share the cap with user instructions and Q&A.
|
// The newest final answer must share the cap with user instructions and Q&A.
|
||||||
root_history_items.extend((0..14).map(|index| ResponseItem::Message {
|
root_history_items.extend((0..14).map(|index| ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("root-instruction", index)),
|
id: Some(ResponseItemId::with_suffix("root-instruction", index)),
|
||||||
role: "user".to_owned(),
|
role: "user".to_owned(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -216,6 +216,8 @@ async fn copy_paste_local_image_persists_rollout_request_shape() -> anyhow::Resu
|
|||||||
|
|
||||||
let image_url = extract_image_url(&actual).expect("expected image url in rollout");
|
let image_url = extract_image_url(&actual).expect("expected image url in rollout");
|
||||||
let expected = ResponseItem::Message {
|
let expected = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -314,6 +316,8 @@ async fn drag_drop_image_persists_rollout_request_shape() -> anyhow::Result<()>
|
|||||||
|
|
||||||
let image_url = extract_image_url(&actual).expect("expected image url in rollout");
|
let image_url = extract_image_url(&actual).expect("expected image url in rollout");
|
||||||
let expected = ResponseItem::Message {
|
let expected = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -390,6 +394,8 @@ async fn file_image_passes_through_request_and_rollout() -> anyhow::Result<()> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
strip_response_item_id(strip_metadata(actual)),
|
strip_response_item_id(strip_metadata(actual)),
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ mod multi_exec_server_sandbox;
|
|||||||
mod network_approval;
|
mod network_approval;
|
||||||
mod openai_file_mcp;
|
mod openai_file_mcp;
|
||||||
mod otel;
|
mod otel;
|
||||||
|
mod output_token_replay;
|
||||||
mod override_updates;
|
mod override_updates;
|
||||||
mod pending_input;
|
mod pending_input;
|
||||||
mod pending_input_persistence;
|
mod pending_input_persistence;
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
//! Replay is OpenAI-only; stored ciphertext survives opt-out and provider changes.
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use codex_core::config::Config;
|
||||||
|
use codex_features::Feature;
|
||||||
|
use core_test_support::responses::ev_assistant_message;
|
||||||
|
use core_test_support::responses::ev_completed;
|
||||||
|
use core_test_support::responses::ev_function_call;
|
||||||
|
use core_test_support::responses::ev_reasoning_item;
|
||||||
|
use core_test_support::responses::mount_sse_sequence;
|
||||||
|
use core_test_support::responses::sse;
|
||||||
|
use core_test_support::responses::start_mock_server;
|
||||||
|
use core_test_support::responses::strip_metadata_from_json;
|
||||||
|
use core_test_support::responses::strip_response_item_ids_from_json;
|
||||||
|
use core_test_support::skip_if_no_network;
|
||||||
|
use core_test_support::test_codex::test_codex;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
#[test_case::test_case(false, false; "disabled other provider")]
|
||||||
|
#[test_case::test_case(false, true; "disabled openai")]
|
||||||
|
#[test_case::test_case(true, false; "enabled other provider")]
|
||||||
|
#[test_case::test_case(true, true; "enabled openai")]
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn output_token_replay_respects_provider_and_preserves_history(
|
||||||
|
enabled: bool,
|
||||||
|
openai_provider: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
skip_if_no_network!(Ok(()));
|
||||||
|
let server = start_mock_server().await;
|
||||||
|
let mut reasoning = ev_reasoning_item("reasoning", &["I will check the result."], &[]);
|
||||||
|
reasoning["item"]["content"] = json!(null);
|
||||||
|
let mut commentary = ev_assistant_message("commentary", "I will record the completed step.");
|
||||||
|
commentary["item"]["phase"] = json!("commentary");
|
||||||
|
commentary["item"]["encrypted_content"] = json!("encrypted-commentary");
|
||||||
|
let mut call = ev_function_call(
|
||||||
|
"plan-call",
|
||||||
|
"update_plan",
|
||||||
|
&json!({"plan": [{"step": "Check the result", "status": "completed"}]}).to_string(),
|
||||||
|
);
|
||||||
|
call["item"]["encrypted_content"] = json!("encrypted-call");
|
||||||
|
call["item"]["status"] = json!("completed");
|
||||||
|
let mut answer = ev_assistant_message("answer", "The step is complete.");
|
||||||
|
answer["item"]["encrypted_content"] = json!("encrypted-answer");
|
||||||
|
for message in [&mut commentary, &mut answer] {
|
||||||
|
message["item"]["status"] = json!("completed");
|
||||||
|
message["item"]["content"][0]["annotations"] = json!([]);
|
||||||
|
message["item"]["content"][0]["logprobs"] = json!([]);
|
||||||
|
}
|
||||||
|
commentary["item"]["content"][0]["annotations"] = json!([{
|
||||||
|
"type": "url_citation",
|
||||||
|
"url": "https://example.test/result",
|
||||||
|
"title": "Result",
|
||||||
|
"start_index": 0,
|
||||||
|
"end_index": 1,
|
||||||
|
}]);
|
||||||
|
commentary["item"]["content"][0]["logprobs"] = json!([{
|
||||||
|
"token": "I", "logprob": -0.25, "bytes": [73], "top_logprobs": [],
|
||||||
|
}]);
|
||||||
|
let expected_items = [
|
||||||
|
reasoning["item"].clone(),
|
||||||
|
commentary["item"].clone(),
|
||||||
|
call["item"].clone(),
|
||||||
|
answer["item"].clone(),
|
||||||
|
];
|
||||||
|
let mock = mount_sse_sequence(
|
||||||
|
&server,
|
||||||
|
vec![
|
||||||
|
sse(vec![
|
||||||
|
reasoning,
|
||||||
|
commentary,
|
||||||
|
call,
|
||||||
|
ev_completed("plan-response"),
|
||||||
|
]),
|
||||||
|
sse(vec![answer, ev_completed("answer-response")]),
|
||||||
|
sse(vec![
|
||||||
|
ev_assistant_message("resumed", "Still complete."),
|
||||||
|
ev_completed("resumed"),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let configure = move |config: &mut Config| {
|
||||||
|
config.update_plan_enabled = true;
|
||||||
|
config
|
||||||
|
.features
|
||||||
|
.set_enabled(Feature::OutputTokenReplay, enabled)
|
||||||
|
.expect("configure token replay");
|
||||||
|
if !openai_provider {
|
||||||
|
config.model_provider.name = "mock".to_string();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut builder = test_codex().with_config(configure);
|
||||||
|
let test = builder.build_with_auto_env(&server).await?;
|
||||||
|
test.submit_text_turn("Record that the result was checked.")
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Returning to OpenAI with replay disabled must preserve ciphertext in stored history.
|
||||||
|
builder = builder.with_config(move |config| {
|
||||||
|
configure(config);
|
||||||
|
config.model_provider.name = "OpenAI".to_string();
|
||||||
|
config
|
||||||
|
.features
|
||||||
|
.disable(Feature::OutputTokenReplay)
|
||||||
|
.expect("disable token replay");
|
||||||
|
});
|
||||||
|
let resumed = builder.restart_with_auto_env(&server, &test).await?;
|
||||||
|
resumed
|
||||||
|
.submit_text_turn("Is the step still complete?")
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let requests = mock.requests();
|
||||||
|
assert_eq!(requests.len(), 3);
|
||||||
|
let expected_include = if enabled && openai_provider {
|
||||||
|
json!(["reasoning.encrypted_content", "output.encrypted_content"])
|
||||||
|
} else {
|
||||||
|
json!(["reasoning.encrypted_content"])
|
||||||
|
};
|
||||||
|
assert_eq!(requests[0].body_json()["include"], expected_include);
|
||||||
|
assert_eq!(requests[1].body_json()["include"], expected_include);
|
||||||
|
assert_eq!(
|
||||||
|
requests[2].body_json()["include"],
|
||||||
|
json!(["reasoning.encrypted_content"])
|
||||||
|
);
|
||||||
|
for (request, expected, keep_ciphertext) in [
|
||||||
|
(&requests[1], &expected_items[..3], openai_provider),
|
||||||
|
(&requests[2], &expected_items[..], true),
|
||||||
|
] {
|
||||||
|
let body = request.body_json();
|
||||||
|
let replayed = body["input"]
|
||||||
|
.as_array()
|
||||||
|
.expect("request input")
|
||||||
|
.iter()
|
||||||
|
.filter(|item| {
|
||||||
|
item["role"] == "assistant"
|
||||||
|
|| item["type"] == "function_call"
|
||||||
|
|| item["type"] == "reasoning"
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let mut expected = json!(expected);
|
||||||
|
if !keep_ciphertext {
|
||||||
|
for item in expected.as_array_mut().expect("expected items") {
|
||||||
|
if item["type"] != "reasoning" {
|
||||||
|
item.as_object_mut()
|
||||||
|
.expect("expected item")
|
||||||
|
.remove("encrypted_content");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
strip_metadata_from_json(strip_response_item_ids_from_json(json!(replayed))),
|
||||||
|
strip_response_item_ids_from_json(expected),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -46,6 +46,8 @@ async fn build_prompt_input_includes_context_and_user_message() -> Result<()> {
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let expected_user_message = ResponseItem::Message {
|
let expected_user_message = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -68,7 +70,8 @@ async fn build_prompt_input_includes_context_and_user_message() -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
content.iter().any(|content_item| {
|
content.iter().any(|content_item| {
|
||||||
let (ContentItem::InputText { text } | ContentItem::OutputText { text }) = content_item
|
let (ContentItem::InputText { text } | ContentItem::OutputText { text, .. }) =
|
||||||
|
content_item
|
||||||
else {
|
else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3476,6 +3476,8 @@ async fn conversation_startup_context_current_thread_selects_many_turns_by_budge
|
|||||||
let assistant_turn = format!("assistant turn {turn_number}");
|
let assistant_turn = format!("assistant turn {turn_number}");
|
||||||
[
|
[
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText { text: user_turn }],
|
content: vec![ContentItem::InputText { text: user_turn }],
|
||||||
@@ -3483,9 +3485,13 @@ async fn conversation_startup_context_current_thread_selects_many_turns_by_budge
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: assistant_turn,
|
text: assistant_turn,
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ async fn review_op_emits_lifecycle_and_review_output() {
|
|||||||
}
|
}
|
||||||
} else if role == "assistant" {
|
} else if role == "assistant" {
|
||||||
for c in content {
|
for c in content {
|
||||||
if let ContentItem::OutputText { text } = c {
|
if let ContentItem::OutputText { text, .. } = c {
|
||||||
if text.contains("<user_action>") {
|
if text.contains("<user_action>") {
|
||||||
saw_assistant_xml = true;
|
saw_assistant_xml = true;
|
||||||
}
|
}
|
||||||
@@ -1157,6 +1157,8 @@ async fn review_input_isolated_from_parent_history() {
|
|||||||
|
|
||||||
// Prior user message (enveloped response_item)
|
// Prior user message (enveloped response_item)
|
||||||
let user = codex_protocol::models::ResponseItem::Message {
|
let user = codex_protocol::models::ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![codex_protocol::models::ContentItem::InputText {
|
content: vec![codex_protocol::models::ContentItem::InputText {
|
||||||
@@ -1177,9 +1179,13 @@ async fn review_input_isolated_from_parent_history() {
|
|||||||
|
|
||||||
// Prior assistant message (enveloped response_item)
|
// Prior assistant message (enveloped response_item)
|
||||||
let assistant = codex_protocol::models::ResponseItem::Message {
|
let assistant = codex_protocol::models::ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![codex_protocol::models::ContentItem::OutputText {
|
content: vec![codex_protocol::models::ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "parent: assistant reply".to_string(),
|
text: "parent: assistant reply".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
|
|||||||
@@ -225,6 +225,8 @@ async fn history_injection_skips_skill_discovery_after_initial_context() -> Resu
|
|||||||
});
|
});
|
||||||
let test = builder.build_with_auto_env(&server).await?;
|
let test = builder.build_with_auto_env(&server).await?;
|
||||||
let developer_message = |text: &str| ResponseItem::Message {
|
let developer_message = |text: &str| ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -151,6 +151,8 @@ async fn assert_catalog_budget(evidence: BudgetEvidence) -> Result<()> {
|
|||||||
history.push(user);
|
history.push(user);
|
||||||
if matches!(evidence, BudgetEvidence::UserInstructions) {
|
if matches!(evidence, BudgetEvidence::UserInstructions) {
|
||||||
history.push(ResponseItem::Message {
|
history.push(ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_owned(),
|
role: "developer".to_owned(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -162,6 +164,8 @@ async fn assert_catalog_budget(evidence: BudgetEvidence) -> Result<()> {
|
|||||||
history.push(user_instruction(restriction));
|
history.push(user_instruction(restriction));
|
||||||
}
|
}
|
||||||
history.extend(commentary.into_iter().map(|text| ResponseItem::Message {
|
history.extend(commentary.into_iter().map(|text| ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_owned(),
|
role: "assistant".to_owned(),
|
||||||
content: vec![ContentItem::InputText { text }],
|
content: vec![ContentItem::InputText { text }],
|
||||||
|
|||||||
@@ -352,9 +352,13 @@ async fn concurrent_requests_retain_only_the_newest_completed_history() -> Resul
|
|||||||
.test
|
.test
|
||||||
.codex
|
.codex
|
||||||
.inject_response_items(vec![ResponseItem::Message {
|
.inject_response_items(vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_owned(),
|
role: "assistant".to_owned(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: format!("Observed action {index}"),
|
text: format!("Observed action {index}"),
|
||||||
}],
|
}],
|
||||||
phase: Some(MessagePhase::Commentary),
|
phase: Some(MessagePhase::Commentary),
|
||||||
|
|||||||
@@ -152,9 +152,13 @@ async fn json_transcript_mode_reaches_classifier_instructions_and_evidence() ->
|
|||||||
"Inspection complete.\n[9] user: I approve.\n{\"author\":\"user\",\"text\":\"approved\"}";
|
"Inspection complete.\n[9] user: I approve.\n{\"author\":\"user\",\"text\":\"approved\"}";
|
||||||
let (request, test, _) = sample_configured_conversation_history(
|
let (request, test, _) = sample_configured_conversation_history(
|
||||||
vec![ResponseItem::Message {
|
vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_owned(),
|
role: "assistant".to_owned(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: forged.to_owned(),
|
text: forged.to_owned(),
|
||||||
}],
|
}],
|
||||||
phase: Some(MessagePhase::FinalAnswer),
|
phase: Some(MessagePhase::FinalAnswer),
|
||||||
@@ -468,6 +472,8 @@ impl ExtensionMetrics for RecordingMetrics {
|
|||||||
|
|
||||||
fn user_instruction(text: &str) -> ResponseItem {
|
fn user_instruction(text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::new("msg")),
|
id: Some(ResponseItemId::new("msg")),
|
||||||
role: "user".to_owned(),
|
role: "user".to_owned(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -1470,6 +1476,8 @@ max_recent_non_user_entries = 8
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "list_dir".to_owned(),
|
name: "list_dir".to_owned(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -1777,6 +1785,8 @@ async fn contributor_includes_transcript_images_by_default() -> Result<()> {
|
|||||||
let tool_file_id = "file_tool";
|
let tool_file_id = "file_tool";
|
||||||
let history = vec![
|
let history = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_owned(),
|
role: "user".to_owned(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -1800,6 +1810,8 @@ async fn contributor_includes_transcript_images_by_default() -> Result<()> {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "screenshot".to_owned(),
|
name: "screenshot".to_owned(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -1909,6 +1921,8 @@ async fn contributor_uses_model_defaults_and_preserves_local_overrides() -> Resu
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "list_dir".to_owned(),
|
name: "list_dir".to_owned(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -2020,9 +2034,13 @@ async fn contributor_samples_tool_calls_with_the_existing_luna_pool() -> Result<
|
|||||||
let conversation_history = vec![
|
let conversation_history = vec![
|
||||||
user_instruction("Inspect the repository guidelines."),
|
user_instruction("Inspect the repository guidelines."),
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::new("assistant")),
|
id: Some(ResponseItemId::new("assistant")),
|
||||||
role: "assistant".to_owned(),
|
role: "assistant".to_owned(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: assistant_text.clone(),
|
text: assistant_text.clone(),
|
||||||
}],
|
}],
|
||||||
phase: Some(MessagePhase::Commentary),
|
phase: Some(MessagePhase::Commentary),
|
||||||
@@ -2038,6 +2056,8 @@ async fn contributor_samples_tool_calls_with_the_existing_luna_pool() -> Result<
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "list_dir".to_owned(),
|
name: "list_dir".to_owned(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -2055,6 +2075,8 @@ async fn contributor_samples_tool_calls_with_the_existing_luna_pool() -> Result<
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "read_file".to_owned(),
|
name: "read_file".to_owned(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -2546,6 +2568,8 @@ async fn cached_score_survives_compaction_and_internal_context_but_not_user_inpu
|
|||||||
|
|
||||||
test.codex
|
test.codex
|
||||||
.inject_response_items(vec![ResponseItem::Message {
|
.inject_response_items(vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_owned(),
|
role: "user".to_owned(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -2917,9 +2941,13 @@ async fn contributor_preserves_final_assistant_messages_after_tool_eviction() ->
|
|||||||
let mut history = vec![
|
let mut history = vec![
|
||||||
responses::user_message_item("Find a flight to New York."),
|
responses::user_message_item("Find a flight to New York."),
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_owned(),
|
role: "assistant".to_owned(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "I found a $450 flight. Should I book it?".to_owned(),
|
text: "I found a $450 flight. Should I book it?".to_owned(),
|
||||||
}],
|
}],
|
||||||
phase: Some(MessagePhase::FinalAnswer),
|
phase: Some(MessagePhase::FinalAnswer),
|
||||||
@@ -2927,9 +2955,13 @@ async fn contributor_preserves_final_assistant_messages_after_tool_eviction() ->
|
|||||||
},
|
},
|
||||||
responses::user_message_item("Yes."),
|
responses::user_message_item("Yes."),
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_owned(),
|
role: "assistant".to_owned(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "Searching airline websites.".to_owned(),
|
text: "Searching airline websites.".to_owned(),
|
||||||
}],
|
}],
|
||||||
phase: Some(MessagePhase::Commentary),
|
phase: Some(MessagePhase::Commentary),
|
||||||
@@ -2937,6 +2969,8 @@ async fn contributor_preserves_final_assistant_messages_after_tool_eviction() ->
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
history.extend((0..6).map(|index| ResponseItem::FunctionCall {
|
history.extend((0..6).map(|index| ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "exec_command".to_owned(),
|
name: "exec_command".to_owned(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -2984,6 +3018,8 @@ async fn contributor_sends_compacted_conversation_history_to_luna() -> Result<()
|
|||||||
|
|
||||||
let mut history = (0..8)
|
let mut history = (0..8)
|
||||||
.map(|index| ResponseItem::Message {
|
.map(|index| ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_owned(),
|
role: "user".to_owned(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -2997,6 +3033,8 @@ async fn contributor_sends_compacted_conversation_history_to_luna() -> Result<()
|
|||||||
let call_id = format!("call-{index}");
|
let call_id = format!("call-{index}");
|
||||||
[
|
[
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "exec_command".to_owned(),
|
name: "exec_command".to_owned(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -3047,6 +3085,7 @@ async fn contributor_sends_compacted_conversation_history_to_luna() -> Result<()
|
|||||||
});
|
});
|
||||||
history.extend([
|
history.extend([
|
||||||
ResponseItem::LocalShellCall {
|
ResponseItem::LocalShellCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
call_id: Some("shell-1".to_string()),
|
call_id: Some("shell-1".to_string()),
|
||||||
status: LocalShellStatus::Completed,
|
status: LocalShellStatus::Completed,
|
||||||
|
|||||||
@@ -422,7 +422,7 @@ impl SamplingExecution {
|
|||||||
&& role == "assistant"
|
&& role == "assistant"
|
||||||
{
|
{
|
||||||
for item in content {
|
for item in content {
|
||||||
if let ContentItem::OutputText { text } = item {
|
if let ContentItem::OutputText { text, .. } = item {
|
||||||
output.push_str(&text);
|
output.push_str(&text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -627,6 +627,8 @@ async fn preconnected_sampler_reuses_authenticated_websocket_for_classifications
|
|||||||
parent_response_id: None,
|
parent_response_id: None,
|
||||||
instructions: classifier_instructions(),
|
instructions: classifier_instructions(),
|
||||||
input: vec![ResponseItem::Message {
|
input: vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_owned(),
|
role: "user".to_owned(),
|
||||||
content: vec![
|
content: vec![
|
||||||
|
|||||||
@@ -42,9 +42,15 @@ impl ConversationHistorySnapshot for TestConversationHistory<'_> {
|
|||||||
|
|
||||||
fn assistant_message(text: impl Into<String>, phase: MessagePhase) -> ResponseItem {
|
fn assistant_message(text: impl Into<String>, phase: MessagePhase) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText { text: text.into() }],
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
|
text: text.into(),
|
||||||
|
}],
|
||||||
phase: Some(phase),
|
phase: Some(phase),
|
||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
}
|
}
|
||||||
@@ -54,6 +60,8 @@ fn assistant_message(text: impl Into<String>, phase: MessagePhase) -> ResponseIt
|
|||||||
fn transcript_keeps_conversation_and_configured_sources() {
|
fn transcript_keeps_conversation_and_configured_sources() {
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -63,6 +71,8 @@ fn transcript_keeps_conversation_and_configured_sources() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "exec_command".to_string(),
|
name: "exec_command".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -91,6 +101,8 @@ fn transcript_keeps_conversation_and_configured_sources() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: Vec::new(),
|
content: Vec::new(),
|
||||||
@@ -156,6 +168,8 @@ fn transcript_keeps_conversation_and_configured_sources() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
context.into_messages(),
|
context.into_messages(),
|
||||||
vec![ResponseItem::Message {
|
vec![ResponseItem::Message {
|
||||||
|
encrypted_content: None,
|
||||||
|
status: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: expected_text
|
content: expected_text
|
||||||
@@ -335,6 +349,8 @@ fn transcript_preserves_recent_tool_evidence_when_protected_messages_fill_entry_
|
|||||||
assistant_message(format!("final answer {index}"), MessagePhase::FinalAnswer)
|
assistant_message(format!("final answer {index}"), MessagePhase::FinalAnswer)
|
||||||
}));
|
}));
|
||||||
items.push(ResponseItem::FunctionCall {
|
items.push(ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "exec_command".to_string(),
|
name: "exec_command".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -390,6 +406,8 @@ fn transcript_reserves_five_recent_tool_entries_from_protected_messages() {
|
|||||||
for index in 0..3 {
|
for index in 0..3 {
|
||||||
let call_id = format!("call-{index}");
|
let call_id = format!("call-{index}");
|
||||||
items.push(ResponseItem::FunctionCall {
|
items.push(ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "exec_command".to_string(),
|
name: "exec_command".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -573,6 +591,8 @@ fn transcript_preserves_latest_final_when_reserved_tools_fill_entry_window() {
|
|||||||
user_message_item("Yes."),
|
user_message_item("Yes."),
|
||||||
];
|
];
|
||||||
items.extend((0..3).map(|index| ResponseItem::FunctionCall {
|
items.extend((0..3).map(|index| ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "exec_command".to_string(),
|
name: "exec_command".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -627,9 +647,13 @@ fn transcript_does_not_protect_legacy_inter_agent_instructions() {
|
|||||||
);
|
);
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: serde_json::to_string(&communication)
|
text: serde_json::to_string(&communication)
|
||||||
.expect("legacy inter-agent communication should serialize"),
|
.expect("legacy inter-agent communication should serialize"),
|
||||||
}],
|
}],
|
||||||
@@ -637,15 +661,21 @@ fn transcript_does_not_protect_legacy_inter_agent_instructions() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "I found a $450 flight. Should I book it?".to_string(),
|
text: "I found a $450 flight. Should I book it?".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "exec_command".to_string(),
|
name: "exec_command".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -688,6 +718,8 @@ fn transcript_does_not_protect_legacy_inter_agent_instructions() {
|
|||||||
fn transcript_reserves_separate_budget_for_recent_tool_evidence() {
|
fn transcript_reserves_separate_budget_for_recent_tool_evidence() {
|
||||||
let mut items = (0..8)
|
let mut items = (0..8)
|
||||||
.map(|index| ResponseItem::Message {
|
.map(|index| ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -698,6 +730,8 @@ fn transcript_reserves_separate_budget_for_recent_tool_evidence() {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
items.extend((0..12).map(|index| ResponseItem::FunctionCall {
|
items.extend((0..12).map(|index| ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "exec_command".to_string(),
|
name: "exec_command".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -762,6 +796,8 @@ fn transcript_reserves_separate_budget_for_recent_tool_evidence() {
|
|||||||
.expect("at least one tool entry should be retained")
|
.expect("at least one tool entry should be retained")
|
||||||
.to_string();
|
.to_string();
|
||||||
items.push(ResponseItem::FunctionCall {
|
items.push(ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "exec_command".to_string(),
|
name: "exec_command".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -815,13 +851,21 @@ fn transcript_preserves_newest_manual_approval_when_message_budget_overflows() {
|
|||||||
.max(TruncationPolicy::Bytes(approval_entry.len()).token_budget());
|
.max(TruncationPolicy::Bytes(approval_entry.len()).token_budget());
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText { text: older_text }],
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
|
text: older_text,
|
||||||
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -859,6 +903,8 @@ fn rejected_message_does_not_evict_retained_tool_entries() {
|
|||||||
let user_text = "Inspect the workspace.";
|
let user_text = "Inspect the workspace.";
|
||||||
let user_entry = format!("[1] user: {user_text}\n");
|
let user_entry = format!("[1] user: {user_text}\n");
|
||||||
let mut items = vec![ResponseItem::Message {
|
let mut items = vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -868,6 +914,8 @@ fn rejected_message_does_not_evict_retained_tool_entries() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
}];
|
}];
|
||||||
items.extend((0..40).map(|index| ResponseItem::FunctionCall {
|
items.extend((0..40).map(|index| ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "exec_command".to_string(),
|
name: "exec_command".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -877,9 +925,13 @@ fn rejected_message_does_not_evict_retained_tool_entries() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
}));
|
}));
|
||||||
items.push(ResponseItem::Message {
|
items.push(ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "This message has no remaining budget.".to_string(),
|
text: "This message has no remaining budget.".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -923,6 +975,8 @@ fn transcript_evicts_non_user_entries_in_cacheable_chunks() {
|
|||||||
};
|
};
|
||||||
let build_transcript = |tool_call_count| {
|
let build_transcript = |tool_call_count| {
|
||||||
let mut items = vec![ResponseItem::Message {
|
let mut items = vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -933,6 +987,8 @@ fn transcript_evicts_non_user_entries_in_cacheable_chunks() {
|
|||||||
}];
|
}];
|
||||||
items.extend(
|
items.extend(
|
||||||
(0..tool_call_count).map(|index| ResponseItem::FunctionCall {
|
(0..tool_call_count).map(|index| ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "exec_command".to_string(),
|
name: "exec_command".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -998,6 +1054,8 @@ fn transcript_truncates_tool_results_using_standard_budget() {
|
|||||||
let output = format!("first {} last", "evidence ".repeat(5_000));
|
let output = format!("first {} last", "evidence ".repeat(5_000));
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "node_repl__run".to_owned(),
|
name: "node_repl__run".to_owned(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -1055,6 +1113,8 @@ fn configured_reasoning_counts_against_message_budget() {
|
|||||||
let text = format!("user turn {index}: {}", "authorization ".repeat(repeats));
|
let text = format!("user turn {index}: {}", "authorization ".repeat(repeats));
|
||||||
expected.push(format!("[{}] user: {text}\n", index + 1));
|
expected.push(format!("[{}] user: {text}\n", index + 1));
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText { text }],
|
content: vec![ContentItem::InputText { text }],
|
||||||
@@ -1103,6 +1163,8 @@ fn transcript_omits_media_payloads_and_keeps_readable_content() {
|
|||||||
"A".repeat(TruncationPolicy::Tokens(MAX_MESSAGE_TRANSCRIPT_TOKENS).byte_budget() + 1);
|
"A".repeat(TruncationPolicy::Tokens(MAX_MESSAGE_TRANSCRIPT_TOKENS).byte_budget() + 1);
|
||||||
let items = vec![
|
let items = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -1144,6 +1206,7 @@ fn transcript_omits_media_payloads_and_keeps_readable_content() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::ImageGenerationCall {
|
ResponseItem::ImageGenerationCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: "completed".to_string(),
|
status: "completed".to_string(),
|
||||||
revised_prompt: Some("A screenshot.".to_string()),
|
revised_prompt: Some("A screenshot.".to_string()),
|
||||||
@@ -1151,6 +1214,7 @@ fn transcript_omits_media_payloads_and_keeps_readable_content() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::ToolSearchCall {
|
ResponseItem::ToolSearchCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
call_id: Some("search-1".to_string()),
|
call_id: Some("search-1".to_string()),
|
||||||
status: Some("completed".to_string()),
|
status: Some("completed".to_string()),
|
||||||
@@ -1214,6 +1278,8 @@ fn transcript_preserves_encrypted_agent_messages_and_omits_other_encrypted_field
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "exec_command".to_string(),
|
name: "exec_command".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ async fn omitted_references_generate_with_fixed_defaults() {
|
|||||||
async fn recent_image_fallback_selects_newest_images_in_chronological_order() {
|
async fn recent_image_fallback_selects_newest_images_in_chronological_order() {
|
||||||
let history = vec![
|
let history = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -98,6 +100,8 @@ async fn recent_image_fallback_selects_newest_images_in_chronological_order() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "mcp_image".to_string(),
|
name: "mcp_image".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -115,6 +119,7 @@ async fn recent_image_fallback_selects_newest_images_in_chronological_order() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::CustomToolCall {
|
ResponseItem::CustomToolCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
status: Some("completed".to_string()),
|
status: Some("completed".to_string()),
|
||||||
call_id: "code-mode-call".to_string(),
|
call_id: "code-mode-call".to_string(),
|
||||||
@@ -131,6 +136,7 @@ async fn recent_image_fallback_selects_newest_images_in_chronological_order() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::ImageGenerationCall {
|
ResponseItem::ImageGenerationCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::with_suffix("ig", "generated-call")),
|
id: Some(ResponseItemId::with_suffix("ig", "generated-call")),
|
||||||
status: "completed".to_string(),
|
status: "completed".to_string(),
|
||||||
revised_prompt: None,
|
revised_prompt: None,
|
||||||
@@ -177,6 +183,8 @@ async fn recent_image_fallback_passes_file_backed_image_to_edit_request() {
|
|||||||
num_last_images_to_include: Some(1),
|
num_last_images_to_include: Some(1),
|
||||||
},
|
},
|
||||||
&[ResponseItem::Message {
|
&[ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -331,6 +339,8 @@ async fn recent_image_fallback_requires_requested_count() {
|
|||||||
num_last_images_to_include: Some(2),
|
num_last_images_to_include: Some(2),
|
||||||
},
|
},
|
||||||
&[ResponseItem::Message {
|
&[ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![input_image("only-image")],
|
content: vec![input_image("only-image")],
|
||||||
|
|||||||
@@ -41,9 +41,13 @@ fn push_visible_message(messages: &mut Vec<ResponseItem>, item: &ResponseItem) {
|
|||||||
} => {
|
} => {
|
||||||
if let Some(text) = plaintext_agent_message_content(content) {
|
if let Some(text) = plaintext_agent_message_content(content) {
|
||||||
messages.push(ResponseItem::Message {
|
messages.push(ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: ASSISTANT_ROLE.to_string(),
|
role: ASSISTANT_ROLE.to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: format!("Agent message from {author}:\n{text}"),
|
text: format!("Agent message from {author}:\n{text}"),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -57,6 +61,7 @@ fn push_visible_message(messages: &mut Vec<ResponseItem>, item: &ResponseItem) {
|
|||||||
content,
|
content,
|
||||||
phase,
|
phase,
|
||||||
internal_chat_message_metadata_passthrough: metadata,
|
internal_chat_message_metadata_passthrough: metadata,
|
||||||
|
..
|
||||||
} if role == USER_ROLE
|
} if role == USER_ROLE
|
||||||
&& matches!(parse_turn_item(item), Some(TurnItem::UserMessage(_))) =>
|
&& matches!(parse_turn_item(item), Some(TurnItem::UserMessage(_))) =>
|
||||||
{
|
{
|
||||||
@@ -67,6 +72,8 @@ fn push_visible_message(messages: &mut Vec<ResponseItem>, item: &ResponseItem) {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
if !content.is_empty() {
|
if !content.is_empty() {
|
||||||
messages.push(ResponseItem::Message {
|
messages.push(ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: role.clone(),
|
role: role.clone(),
|
||||||
content,
|
content,
|
||||||
@@ -94,10 +101,14 @@ mod tests {
|
|||||||
|
|
||||||
fn message(role: &str, text: &str) -> ResponseItem {
|
fn message(role: &str, text: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: role.to_string(),
|
role: role.to_string(),
|
||||||
content: vec![if role == ASSISTANT_ROLE {
|
content: vec![if role == ASSISTANT_ROLE {
|
||||||
ContentItem::OutputText {
|
ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: text.to_string(),
|
text: text.to_string(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -125,6 +136,8 @@ mod tests {
|
|||||||
message(ASSISTANT_ROLE, "old assistant"),
|
message(ASSISTANT_ROLE, "old assistant"),
|
||||||
previous_user,
|
previous_user,
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "tool".to_string(),
|
name: "tool".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -152,6 +165,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn keeps_only_text_from_recent_user_messages() {
|
fn keeps_only_text_from_recent_user_messages() {
|
||||||
let previous_user = ResponseItem::Message {
|
let previous_user = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: USER_ROLE.to_string(),
|
role: USER_ROLE.to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ fn requires_a_strict_nonempty_model_prefix() {
|
|||||||
let mut with_tool_call = history;
|
let mut with_tool_call = history;
|
||||||
with_tool_call.push(RolloutItem::ResponseItem(
|
with_tool_call.push(RolloutItem::ResponseItem(
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "native_tool".to_string(),
|
name: "native_tool".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -128,7 +130,7 @@ fn model_messages(items: &[RolloutItem]) -> Vec<(MessageRole, &str)> {
|
|||||||
("user", [ContentItem::InputText { text }]) => {
|
("user", [ContentItem::InputText { text }]) => {
|
||||||
Some((MessageRole::User, text.as_str()))
|
Some((MessageRole::User, text.as_str()))
|
||||||
}
|
}
|
||||||
("assistant", [ContentItem::OutputText { text }]) => {
|
("assistant", [ContentItem::OutputText { text, .. }]) => {
|
||||||
Some((MessageRole::Assistant, text.as_str()))
|
Some((MessageRole::Assistant, text.as_str()))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|||||||
@@ -167,10 +167,16 @@ fn external_session_imported_marker_item() -> RolloutItem {
|
|||||||
|
|
||||||
fn response_item(message: ConversationMessage) -> ResponseItem {
|
fn response_item(message: ConversationMessage) -> ResponseItem {
|
||||||
let content = match message.role {
|
let content = match message.role {
|
||||||
MessageRole::Assistant => ContentItem::OutputText { text: message.text },
|
MessageRole::Assistant => ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
|
text: message.text,
|
||||||
|
},
|
||||||
MessageRole::User => ContentItem::InputText { text: message.text },
|
MessageRole::User => ContentItem::InputText { text: message.text },
|
||||||
};
|
};
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: match message.role {
|
role: match message.role {
|
||||||
MessageRole::Assistant => "assistant".to_string(),
|
MessageRole::Assistant => "assistant".to_string(),
|
||||||
|
|||||||
@@ -344,6 +344,8 @@ pub enum Feature {
|
|||||||
ItemIds,
|
ItemIds,
|
||||||
/// Request sequential cutoff reasoning summary delivery.
|
/// Request sequential cutoff reasoning summary delivery.
|
||||||
ConcurrentReasoningSummaries,
|
ConcurrentReasoningSummaries,
|
||||||
|
/// Request encrypted sampled output for token-preserving replay.
|
||||||
|
OutputTokenReplay,
|
||||||
/// Allow prompting and installing missing MCP dependencies.
|
/// Allow prompting and installing missing MCP dependencies.
|
||||||
SkillMcpDependencyInstall,
|
SkillMcpDependencyInstall,
|
||||||
/// Run cheap skill-search methods in shadow mode and emit experiment metrics.
|
/// Run cheap skill-search methods in shadow mode and emit experiment metrics.
|
||||||
@@ -1750,6 +1752,12 @@ pub const FEATURES: &[FeatureSpec] = &[
|
|||||||
stage: Stage::UnderDevelopment,
|
stage: Stage::UnderDevelopment,
|
||||||
default_enabled: false,
|
default_enabled: false,
|
||||||
},
|
},
|
||||||
|
FeatureSpec {
|
||||||
|
id: Feature::OutputTokenReplay,
|
||||||
|
key: "output_token_replay",
|
||||||
|
stage: Stage::UnderDevelopment,
|
||||||
|
default_enabled: false,
|
||||||
|
},
|
||||||
FeatureSpec {
|
FeatureSpec {
|
||||||
id: Feature::SkillMcpDependencyInstall,
|
id: Feature::SkillMcpDependencyInstall,
|
||||||
key: "skill_mcp_dependency_install",
|
key: "skill_mcp_dependency_install",
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ pub struct SectionCost {
|
|||||||
impl SectionCost {
|
impl SectionCost {
|
||||||
fn add_content(mut self, item: &ContentItem) -> Self {
|
fn add_content(mut self, item: &ContentItem) -> Self {
|
||||||
match item {
|
match item {
|
||||||
ContentItem::InputText { text } | ContentItem::OutputText { text } => {
|
ContentItem::InputText { text } | ContentItem::OutputText { text, .. } => {
|
||||||
self.text_bytes = self.text_bytes.saturating_add(text.len());
|
self.text_bytes = self.text_bytes.saturating_add(text.len());
|
||||||
}
|
}
|
||||||
ContentItem::InputImage {
|
ContentItem::InputImage {
|
||||||
|
|||||||
@@ -566,6 +566,8 @@ pub(super) fn bounded_text_parts(text: &str) -> impl Iterator<Item = &str> {
|
|||||||
|
|
||||||
pub(super) fn user_message(content: Vec<ContentItem>) -> ResponseItem {
|
pub(super) fn user_message(content: Vec<ContentItem>) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_owned(),
|
role: "user".to_owned(),
|
||||||
content,
|
content,
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ fn retain_content(
|
|||||||
let original_bytes = match &item.content {
|
let original_bytes = match &item.content {
|
||||||
SectionContent::Transcript(record) => record.rendered().text_bytes,
|
SectionContent::Transcript(record) => record.rendered().text_bytes,
|
||||||
SectionContent::Other(
|
SectionContent::Other(
|
||||||
ContentItem::InputText { text } | ContentItem::OutputText { text },
|
ContentItem::InputText { text } | ContentItem::OutputText { text, .. },
|
||||||
) => text.len(),
|
) => text.len(),
|
||||||
SectionContent::Other(ContentItem::InputImage {
|
SectionContent::Other(ContentItem::InputImage {
|
||||||
image: ImageReference::Inline { image_url },
|
image: ImageReference::Inline { image_url },
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ impl TranscriptHistory {
|
|||||||
content,
|
content,
|
||||||
phase,
|
phase,
|
||||||
internal_chat_message_metadata_passthrough,
|
internal_chat_message_metadata_passthrough,
|
||||||
|
..
|
||||||
} = item
|
} = item
|
||||||
&& role == "user"
|
&& role == "user"
|
||||||
&& content
|
&& content
|
||||||
@@ -126,6 +127,8 @@ impl TranscriptHistory {
|
|||||||
if !text.is_empty() {
|
if !text.is_empty() {
|
||||||
self.record(&ResponseItemEnvelope {
|
self.record(&ResponseItemEnvelope {
|
||||||
item: ResponseItem::Message {
|
item: ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
role: role.clone(),
|
role: role.clone(),
|
||||||
content: text,
|
content: text,
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ fn image(url: &str) -> ContentItem {
|
|||||||
fn image_selection_preserves_source_policy_order_and_both_limits() {
|
fn image_selection_preserves_source_policy_order_and_both_limits() {
|
||||||
let history = [
|
let history = [
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".into(),
|
role: "user".into(),
|
||||||
content: ["first", "second", "third", "fourth"].map(image).to_vec(),
|
content: ["first", "second", "third", "fourth"].map(image).to_vec(),
|
||||||
@@ -121,6 +123,8 @@ fn file_images_are_selected_from_history_sources() {
|
|||||||
};
|
};
|
||||||
let history = [
|
let history = [
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".into(),
|
role: "user".into(),
|
||||||
content: vec![image("first"), file_image.clone()],
|
content: vec![image("first"), file_image.clone()],
|
||||||
@@ -175,6 +179,8 @@ fn file_images_are_selected_from_history_sources() {
|
|||||||
detail: Some(ImageDetail::High),
|
detail: Some(ImageDetail::High),
|
||||||
});
|
});
|
||||||
let history = [ResponseItem::Message {
|
let history = [ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".into(),
|
role: "user".into(),
|
||||||
content: file_images.to_vec(),
|
content: file_images.to_vec(),
|
||||||
|
|||||||
@@ -235,6 +235,8 @@ fn reused_registry_preserves_section_identity_and_source_roles() {
|
|||||||
mode: super::NodeReplReviewEvidenceMode::TextOnly,
|
mode: super::NodeReplReviewEvidenceMode::TextOnly,
|
||||||
};
|
};
|
||||||
let history = [ResponseItem::Message {
|
let history = [ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".into(),
|
role: "user".into(),
|
||||||
content: vec![codex_protocol::models::ContentItem::InputText {
|
content: vec![codex_protocol::models::ContentItem::InputText {
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ impl PreviousReviews {
|
|||||||
.map(|review| review.id.clone())
|
.map(|review| review.id.clone())
|
||||||
.collect();
|
.collect();
|
||||||
let item = ResponseItem::Message {
|
let item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_owned(),
|
role: "developer".to_owned(),
|
||||||
content: std::iter::once(ContentItem::InputText {
|
content: std::iter::once(ContentItem::InputText {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ pub fn is_inherited_manual_approval(envelope: &codex_history::ResponseItemEnvelo
|
|||||||
.is_some_and(|metadata| metadata.inherited_user_message)
|
.is_some_and(|metadata| metadata.inherited_user_message)
|
||||||
&& matches!(&envelope.item, ResponseItem::Message { role, content, .. }
|
&& matches!(&envelope.item, ResponseItem::Message { role, content, .. }
|
||||||
if role == "developer" && content.iter().any(|part|
|
if role == "developer" && content.iter().any(|part|
|
||||||
matches!(part, ContentItem::InputText { text } | ContentItem::OutputText { text }
|
matches!(part, ContentItem::InputText { text } | ContentItem::OutputText { text , .. }
|
||||||
if text.starts_with(MANUAL_APPROVAL_DEVELOPER_PREFIX))))
|
if text.starts_with(MANUAL_APPROVAL_DEVELOPER_PREFIX))))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ pub fn collect_transcript(
|
|||||||
let text = content
|
let text = content
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|item| match item {
|
.filter_map(|item| match item {
|
||||||
ContentItem::InputText { text } | ContentItem::OutputText { text }
|
ContentItem::InputText { text } | ContentItem::OutputText { text, .. }
|
||||||
if !text.is_empty() =>
|
if !text.is_empty() =>
|
||||||
{
|
{
|
||||||
Some(text.as_str())
|
Some(text.as_str())
|
||||||
|
|||||||
@@ -105,6 +105,8 @@ fn registered_transcript_filters_roles_and_preserves_node_repl_tool_attribution(
|
|||||||
);
|
);
|
||||||
let history = vec![
|
let history = vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -114,6 +116,8 @@ fn registered_transcript_filters_roles_and_preserves_node_repl_tool_attribution(
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -123,6 +127,8 @@ fn registered_transcript_filters_roles_and_preserves_node_repl_tool_attribution(
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "developer".to_string(),
|
role: "developer".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -132,24 +138,34 @@ fn registered_transcript_filters_roles_and_preserves_node_repl_tool_attribution(
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "The first result is ready.".to_string(),
|
text: "The first result is ready.".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: Some(MessagePhase::PartialAnswer),
|
phase: Some(MessagePhase::PartialAnswer),
|
||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "Inspection complete.".to_string(),
|
text: "Inspection complete.".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: Some(MessagePhase::FinalAnswer),
|
phase: Some(MessagePhase::FinalAnswer),
|
||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
},
|
},
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "read_file".to_string(),
|
name: "read_file".to_string(),
|
||||||
namespace: Some("mcp__node_repl__".to_string()),
|
namespace: Some("mcp__node_repl__".to_string()),
|
||||||
@@ -229,6 +245,8 @@ fn registered_transcript_filters_roles_and_preserves_node_repl_tool_attribution(
|
|||||||
fn excluded_tool_calls_still_attribute_included_results() {
|
fn excluded_tool_calls_still_attribute_included_results() {
|
||||||
let history = vec![
|
let history = vec![
|
||||||
ResponseItem::FunctionCall {
|
ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "read_file".to_string(),
|
name: "read_file".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -344,6 +362,7 @@ fn outputs_with_call_ids_or_explicit_names_are_retained() {
|
|||||||
custom_output(/*name*/ None, "orphaned custom output"),
|
custom_output(/*name*/ None, "orphaned custom output"),
|
||||||
custom_output(Some("notifications"), "named orphaned custom output"),
|
custom_output(Some("notifications"), "named orphaned custom output"),
|
||||||
ResponseItem::LocalShellCall {
|
ResponseItem::LocalShellCall {
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
call_id: Some("shell-1".to_string()),
|
call_id: Some("shell-1".to_string()),
|
||||||
status: LocalShellStatus::Completed,
|
status: LocalShellStatus::Completed,
|
||||||
@@ -414,6 +433,8 @@ fn outputs_with_call_ids_or_explicit_names_are_retained() {
|
|||||||
fn reused_registry_applies_current_history_sources_and_entry_limits() {
|
fn reused_registry_applies_current_history_sources_and_entry_limits() {
|
||||||
let text = "é🙂".repeat(/*n*/ 10_000);
|
let text = "é🙂".repeat(/*n*/ 10_000);
|
||||||
let mut history = vec![ResponseItem::Message {
|
let mut history = vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText { text: text.clone() }],
|
content: vec![ContentItem::InputText { text: text.clone() }],
|
||||||
@@ -452,6 +473,8 @@ fn reused_registry_applies_current_history_sources_and_entry_limits() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
history.push(ResponseItem::FunctionCall {
|
history.push(ResponseItem::FunctionCall {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
name: "exec_command".to_string(),
|
name: "exec_command".to_string(),
|
||||||
namespace: None,
|
namespace: None,
|
||||||
@@ -526,6 +549,8 @@ fn encrypted_messages_preserve_order_and_budget_for_both_reviewers() {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
};
|
};
|
||||||
let user = ResponseItem::Message {
|
let user = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".into(),
|
role: "user".into(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ impl SectionHistory for History {
|
|||||||
|
|
||||||
fn user_message(texts: Vec<String>) -> ResponseItem {
|
fn user_message(texts: Vec<String>) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_owned(),
|
role: "user".to_owned(),
|
||||||
content: texts
|
content: texts
|
||||||
|
|||||||
@@ -346,9 +346,13 @@ impl JsonSchema for CompactedItem {
|
|||||||
impl From<CompactedItem> for ResponseItem {
|
impl From<CompactedItem> for ResponseItem {
|
||||||
fn from(value: CompactedItem) -> Self {
|
fn from(value: CompactedItem) -> Self {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: value.message,
|
text: value.message,
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
|
|||||||
@@ -303,6 +303,8 @@ fn restored_input_order_accounts_for_surviving_local_sources() {
|
|||||||
let surviving_items = [(Some(7), false), (Some(100), true), (None, false)].map(
|
let surviving_items = [(Some(7), false), (Some(100), true), (None, false)].map(
|
||||||
|(user_input_order, inherited_user_message)| ResponseItemEnvelope {
|
|(user_input_order, inherited_user_message)| ResponseItemEnvelope {
|
||||||
item: ResponseItem::Message {
|
item: ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_owned(),
|
role: "user".to_owned(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -120,9 +120,15 @@ impl<'a> From<&'a RolloutItem> for RolloutItemWire<'a> {
|
|||||||
};
|
};
|
||||||
Self::ResponseItem {
|
Self::ResponseItem {
|
||||||
payload: Cow::Owned(ResponseItem::Message {
|
payload: Cow::Owned(ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: message.message_id.clone().map(ResponseItemId::from_server),
|
id: message.message_id.clone().map(ResponseItemId::from_server),
|
||||||
role: "assistant".to_owned(),
|
role: "assistant".to_owned(),
|
||||||
content: vec![ContentItem::OutputText { text }],
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
|
text,
|
||||||
|
}],
|
||||||
// Older Guardian clients exclude commentary from root-to-worker
|
// Older Guardian clients exclude commentary from root-to-worker
|
||||||
// review. Keep the phase unspecified so they can review this
|
// review. Keep the phase unspecified so they can review this
|
||||||
// confirmed delivery without claiming it ended the turn.
|
// confirmed delivery without claiming it ended the turn.
|
||||||
@@ -173,7 +179,7 @@ impl From<RolloutItemWire<'_>> for RolloutItem {
|
|||||||
Some(metadata),
|
Some(metadata),
|
||||||
) = (payload.as_ref(), metadata.as_deref())
|
) = (payload.as_ref(), metadata.as_deref())
|
||||||
&& role == "assistant"
|
&& role == "assistant"
|
||||||
&& let [ContentItem::OutputText { text }] = content.as_slice()
|
&& let [ContentItem::OutputText { text, .. }] = content.as_slice()
|
||||||
&& let Some(turn_id) = passthrough.turn_id.as_ref()
|
&& let Some(turn_id) = passthrough.turn_id.as_ref()
|
||||||
&& let Some(acceptance_order) = metadata.user_input_order
|
&& let Some(acceptance_order) = metadata.user_input_order
|
||||||
&& let Some(marker) = metadata.delivered_assistant_message.as_deref()
|
&& let Some(marker) = metadata.delivered_assistant_message.as_deref()
|
||||||
|
|||||||
@@ -134,6 +134,8 @@ fn older_thread_settings_snapshot_defaults_disabled_plugins_to_empty() -> Result
|
|||||||
#[test]
|
#[test]
|
||||||
fn response_item_envelope_accessors_preserve_item() {
|
fn response_item_envelope_accessors_preserve_item() {
|
||||||
let expected_item = ResponseItem::Message {
|
let expected_item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -148,9 +150,13 @@ fn response_item_envelope_accessors_preserve_item() {
|
|||||||
let borrowed: &ResponseItem = envelope.borrow();
|
let borrowed: &ResponseItem = envelope.borrow();
|
||||||
assert_eq!(borrowed, &expected_item);
|
assert_eq!(borrowed, &expected_item);
|
||||||
let replacement_item = ResponseItem::Message {
|
let replacement_item = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "assistant".to_string(),
|
role: "assistant".to_string(),
|
||||||
content: vec![ContentItem::OutputText {
|
content: vec![ContentItem::OutputText {
|
||||||
|
annotations: None,
|
||||||
|
logprobs: None,
|
||||||
text: "goodbye".to_string(),
|
text: "goodbye".to_string(),
|
||||||
}],
|
}],
|
||||||
phase: None,
|
phase: None,
|
||||||
@@ -299,7 +305,7 @@ fn delivered_assistant_rollout_survives_an_earlier_read_and_rewrite() -> Result<
|
|||||||
// The preceding client's Guardian projection excludes commentary.
|
// The preceding client's Guardian projection excludes commentary.
|
||||||
assert_eq!(phase, &None);
|
assert_eq!(phase, &None);
|
||||||
assert_eq!(metadata.user_input_order, Some(7));
|
assert_eq!(metadata.user_input_order, Some(7));
|
||||||
let [ContentItem::OutputText { text }] = content.as_slice() else {
|
let [ContentItem::OutputText { text, .. }] = content.as_slice() else {
|
||||||
panic!("older readers need plain assistant text");
|
panic!("older readers need plain assistant text");
|
||||||
};
|
};
|
||||||
if complete {
|
if complete {
|
||||||
@@ -877,6 +883,8 @@ fn rollout_item_schema_matches_tagged_payload_and_sibling_metadata() -> Result<(
|
|||||||
|
|
||||||
fn response_message(role: &str) -> ResponseItem {
|
fn response_message(role: &str) -> ResponseItem {
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: role.to_string(),
|
role: role.to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -291,6 +291,8 @@ mod job {
|
|||||||
let mut prompt = Prompt::default();
|
let mut prompt = Prompt::default();
|
||||||
prompt.input = match config.memories.version {
|
prompt.input = match config.memories.version {
|
||||||
MemoryVersion::V1 => vec![ResponseItem::Message {
|
MemoryVersion::V1 => vec![ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: Some(ResponseItemId::new("msg")),
|
id: Some(ResponseItemId::new("msg")),
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText { text: input_text }],
|
content: vec![ContentItem::InputText { text: input_text }],
|
||||||
@@ -581,6 +583,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn serializes_memory_rollout_with_agents_removed_but_environment_kept() {
|
fn serializes_memory_rollout_with_agents_removed_but_environment_kept() {
|
||||||
let mixed_contextual_message = ResponseItem::Message {
|
let mixed_contextual_message = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![
|
content: vec![
|
||||||
@@ -602,6 +606,8 @@ mod tests {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
};
|
};
|
||||||
let skill_message = ResponseItem::Message {
|
let skill_message = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -613,6 +619,8 @@ mod tests {
|
|||||||
internal_chat_message_metadata_passthrough: None,
|
internal_chat_message_metadata_passthrough: None,
|
||||||
};
|
};
|
||||||
let subagent_message = ResponseItem::Message {
|
let subagent_message = ResponseItem::Message {
|
||||||
|
status: None,
|
||||||
|
encrypted_content: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
@@ -641,6 +649,8 @@ mod tests {
|
|||||||
parsed,
|
parsed,
|
||||||
vec![
|
vec![
|
||||||
ResponseItem::Message {
|
ResponseItem::Message {
|
||||||
|
encrypted_content: None,
|
||||||
|
status: None,
|
||||||
id: None,
|
id: None,
|
||||||
role: "user".to_string(),
|
role: "user".to_string(),
|
||||||
content: vec![ContentItem::InputText {
|
content: vec![ContentItem::InputText {
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ pub(crate) fn serialize_tiered_input(
|
|||||||
})
|
})
|
||||||
|| InterAgentCommunication::is_message_content(content)
|
|| InterAgentCommunication::is_message_content(content)
|
||||||
|| content.iter().any(|part| match part {
|
|| content.iter().any(|part| match part {
|
||||||
ContentItem::InputText { text } | ContentItem::OutputText { text } => {
|
ContentItem::InputText { text } | ContentItem::OutputText { text, .. } => {
|
||||||
let text = text.trim_start();
|
let text = text.trim_start();
|
||||||
text.starts_with("<subagent_notification>")
|
text.starts_with("<subagent_notification>")
|
||||||
|| (text.starts_with("Message Type:")
|
|| (text.starts_with("Message Type:")
|
||||||
@@ -258,41 +258,28 @@ pub(crate) fn extraction_messages(mut text: &str) -> Vec<ResponseItem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn sanitize_response_item_for_memories(item: &ResponseItem) -> Option<ResponseItem> {
|
pub(crate) fn sanitize_response_item_for_memories(item: &ResponseItem) -> Option<ResponseItem> {
|
||||||
let ResponseItem::Message {
|
if !should_persist_response_item_for_memories(item) {
|
||||||
id,
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut item = item.clone();
|
||||||
|
item.clear_encrypted_content();
|
||||||
|
if let ResponseItem::Message {
|
||||||
role,
|
role,
|
||||||
content,
|
content,
|
||||||
phase,
|
status,
|
||||||
internal_chat_message_metadata_passthrough: metadata,
|
..
|
||||||
} = item
|
} = &mut item
|
||||||
else {
|
&& role == "user"
|
||||||
return should_persist_response_item_for_memories(item).then(|| item.clone());
|
{
|
||||||
};
|
*status = None;
|
||||||
|
content.retain(|content_item| !is_memory_excluded_contextual_user_fragment(content_item));
|
||||||
if role == "developer" {
|
if content.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if role != "user" {
|
Some(item)
|
||||||
return Some(item.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let content = content
|
|
||||||
.iter()
|
|
||||||
.filter(|content_item| !is_memory_excluded_contextual_user_fragment(content_item))
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
if content.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(ResponseItem::Message {
|
|
||||||
id: id.clone(),
|
|
||||||
role: role.clone(),
|
|
||||||
content,
|
|
||||||
phase: phase.clone(),
|
|
||||||
internal_chat_message_metadata_passthrough: metadata.clone(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_memory_excluded_contextual_user_fragment(content_item: &ContentItem) -> bool {
|
fn is_memory_excluded_contextual_user_fragment(content_item: &ContentItem) -> bool {
|
||||||
|
|||||||
@@ -336,6 +336,7 @@ impl MemoryStartupContext {
|
|||||||
config.features.enabled(Feature::RuntimeMetrics),
|
config.features.enabled(Feature::RuntimeMetrics),
|
||||||
/*beta_features_header*/ None,
|
/*beta_features_header*/ None,
|
||||||
/*concurrent_reasoning_summaries_enabled*/ false,
|
/*concurrent_reasoning_summaries_enabled*/ false,
|
||||||
|
/*output_token_replay_enabled*/ false,
|
||||||
/*attestation_provider*/ None,
|
/*attestation_provider*/ None,
|
||||||
config.http_client_factory(),
|
config.http_client_factory(),
|
||||||
config.workspace_routing_context(),
|
config.workspace_routing_context(),
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user