From 158fe58e7c5ed957613ab91e99067aeaa98d3de9 Mon Sep 17 00:00:00 2001 From: rhan-oai Date: Thu, 30 Apr 2026 23:01:42 -0700 Subject: [PATCH 1/2] [codex-analytics] expose item timing through app server --- .../schema/json/ServerNotification.json | 144 ++++++++++++++++++ .../codex_app_server_protocol.schemas.json | 144 ++++++++++++++++++ .../codex_app_server_protocol.v2.schemas.json | 144 ++++++++++++++++++ .../json/v2/ItemCompletedNotification.json | 144 ++++++++++++++++++ .../json/v2/ItemStartedNotification.json | 144 ++++++++++++++++++ .../schema/json/v2/ReviewStartResponse.json | 144 ++++++++++++++++++ .../schema/json/v2/ThreadForkResponse.json | 144 ++++++++++++++++++ .../schema/json/v2/ThreadListResponse.json | 144 ++++++++++++++++++ .../json/v2/ThreadMetadataUpdateResponse.json | 144 ++++++++++++++++++ .../schema/json/v2/ThreadReadResponse.json | 144 ++++++++++++++++++ .../schema/json/v2/ThreadResumeResponse.json | 144 ++++++++++++++++++ .../json/v2/ThreadRollbackResponse.json | 144 ++++++++++++++++++ .../schema/json/v2/ThreadStartResponse.json | 144 ++++++++++++++++++ .../json/v2/ThreadStartedNotification.json | 144 ++++++++++++++++++ .../json/v2/ThreadUnarchiveResponse.json | 144 ++++++++++++++++++ .../json/v2/TurnCompletedNotification.json | 144 ++++++++++++++++++ .../schema/json/v2/TurnStartResponse.json | 144 ++++++++++++++++++ .../json/v2/TurnStartedNotification.json | 144 ++++++++++++++++++ .../schema/typescript/v2/ThreadItem.ts | 76 ++++++++- .../src/protocol/event_mapping.rs | 50 ++++++ .../src/protocol/item_builders.rs | 19 +++ .../src/protocol/thread_history.rs | 90 +++++++++++ .../app-server-protocol/src/protocol/v2.rs | 63 ++++++++ .../app-server/src/bespoke_event_handling.rs | 11 ++ .../tests/suite/v2/dynamic_tools.rs | 8 + .../app-server/tests/suite/v2/mcp_tool.rs | 2 + .../tests/suite/v2/thread_resume.rs | 28 +++- .../app-server/tests/suite/v2/turn_start.rs | 57 +++++-- 28 files changed, 2973 insertions(+), 23 deletions(-) diff --git a/codex-rs/app-server-protocol/schema/json/ServerNotification.json b/codex-rs/app-server-protocol/schema/json/ServerNotification.json index 82914f3a6f22..36a703ac95ff 100644 --- a/codex-rs/app-server-protocol/schema/json/ServerNotification.json +++ b/codex-rs/app-server-protocol/schema/json/ServerNotification.json @@ -3314,6 +3314,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -3356,6 +3364,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/CommandExecutionStatus" }, @@ -3386,9 +3402,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/PatchApplyStatus" }, @@ -3412,6 +3452,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -3452,6 +3500,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/McpToolCallStatus" }, @@ -3480,6 +3536,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/DynamicToolCallOutputContentItem" @@ -3506,6 +3570,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/DynamicToolCallStatus" }, @@ -3545,6 +3617,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -3585,6 +3673,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -3633,12 +3729,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -3681,6 +3801,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -3703,6 +3839,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json index fdd975c4e55a..d030485618a8 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json @@ -15453,6 +15453,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -15495,6 +15503,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/v2/CommandExecutionStatus" }, @@ -15525,9 +15541,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/v2/PatchApplyStatus" }, @@ -15551,6 +15591,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -15591,6 +15639,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/v2/McpToolCallStatus" }, @@ -15619,6 +15675,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/v2/DynamicToolCallOutputContentItem" @@ -15645,6 +15709,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/v2/DynamicToolCallStatus" }, @@ -15684,6 +15756,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -15724,6 +15812,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -15772,12 +15868,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -15820,6 +15940,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -15842,6 +15978,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json index ab4b8bf653fe..c5807fc4bb83 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json @@ -13339,6 +13339,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -13381,6 +13389,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/CommandExecutionStatus" }, @@ -13411,9 +13427,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/PatchApplyStatus" }, @@ -13437,6 +13477,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -13477,6 +13525,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/McpToolCallStatus" }, @@ -13505,6 +13561,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/DynamicToolCallOutputContentItem" @@ -13531,6 +13595,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/DynamicToolCallStatus" }, @@ -13570,6 +13642,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -13610,6 +13698,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -13658,12 +13754,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -13706,6 +13826,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -13728,6 +13864,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/json/v2/ItemCompletedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/ItemCompletedNotification.json index 0831483a327f..df86d4d4f2f3 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ItemCompletedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ItemCompletedNotification.json @@ -668,6 +668,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -710,6 +718,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/CommandExecutionStatus" }, @@ -740,9 +756,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/PatchApplyStatus" }, @@ -766,6 +806,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -806,6 +854,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/McpToolCallStatus" }, @@ -834,6 +890,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/DynamicToolCallOutputContentItem" @@ -860,6 +924,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/DynamicToolCallStatus" }, @@ -899,6 +971,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -939,6 +1027,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -987,12 +1083,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -1035,6 +1155,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -1057,6 +1193,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/json/v2/ItemStartedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/ItemStartedNotification.json index 16bfeece144a..c9742a09b636 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ItemStartedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ItemStartedNotification.json @@ -668,6 +668,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -710,6 +718,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/CommandExecutionStatus" }, @@ -740,9 +756,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/PatchApplyStatus" }, @@ -766,6 +806,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -806,6 +854,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/McpToolCallStatus" }, @@ -834,6 +890,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/DynamicToolCallOutputContentItem" @@ -860,6 +924,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/DynamicToolCallStatus" }, @@ -899,6 +971,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -939,6 +1027,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -987,12 +1083,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -1035,6 +1155,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -1057,6 +1193,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json index 16abcd7806a5..75995b7e4df2 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json @@ -812,6 +812,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -854,6 +862,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/CommandExecutionStatus" }, @@ -884,9 +900,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/PatchApplyStatus" }, @@ -910,6 +950,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -950,6 +998,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/McpToolCallStatus" }, @@ -978,6 +1034,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/DynamicToolCallOutputContentItem" @@ -1004,6 +1068,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/DynamicToolCallStatus" }, @@ -1043,6 +1115,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -1083,6 +1171,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -1131,12 +1227,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -1179,6 +1299,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -1201,6 +1337,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json index 653c5f238773..2326d8c0ae3a 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json @@ -1638,6 +1638,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -1680,6 +1688,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/CommandExecutionStatus" }, @@ -1710,9 +1726,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/PatchApplyStatus" }, @@ -1736,6 +1776,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -1776,6 +1824,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/McpToolCallStatus" }, @@ -1804,6 +1860,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/DynamicToolCallOutputContentItem" @@ -1830,6 +1894,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/DynamicToolCallStatus" }, @@ -1869,6 +1941,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -1909,6 +1997,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -1957,12 +2053,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -2005,6 +2125,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -2027,6 +2163,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json index 2f5cbb95002d..07c1b149e580 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json @@ -1088,6 +1088,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -1130,6 +1138,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/CommandExecutionStatus" }, @@ -1160,9 +1176,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/PatchApplyStatus" }, @@ -1186,6 +1226,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -1226,6 +1274,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/McpToolCallStatus" }, @@ -1254,6 +1310,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/DynamicToolCallOutputContentItem" @@ -1280,6 +1344,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/DynamicToolCallStatus" }, @@ -1319,6 +1391,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -1359,6 +1447,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -1407,12 +1503,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -1455,6 +1575,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -1477,6 +1613,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json index b9ae59708aa6..e608cf06be8d 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json @@ -1088,6 +1088,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -1130,6 +1138,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/CommandExecutionStatus" }, @@ -1160,9 +1176,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/PatchApplyStatus" }, @@ -1186,6 +1226,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -1226,6 +1274,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/McpToolCallStatus" }, @@ -1254,6 +1310,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/DynamicToolCallOutputContentItem" @@ -1280,6 +1344,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/DynamicToolCallStatus" }, @@ -1319,6 +1391,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -1359,6 +1447,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -1407,12 +1503,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -1455,6 +1575,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -1477,6 +1613,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json index cda474c2947b..6ac2584e96a2 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json @@ -1088,6 +1088,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -1130,6 +1138,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/CommandExecutionStatus" }, @@ -1160,9 +1176,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/PatchApplyStatus" }, @@ -1186,6 +1226,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -1226,6 +1274,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/McpToolCallStatus" }, @@ -1254,6 +1310,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/DynamicToolCallOutputContentItem" @@ -1280,6 +1344,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/DynamicToolCallStatus" }, @@ -1319,6 +1391,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -1359,6 +1447,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -1407,12 +1503,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -1455,6 +1575,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -1477,6 +1613,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json index 27cf47f2fc58..56670abf3a13 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json @@ -1638,6 +1638,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -1680,6 +1688,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/CommandExecutionStatus" }, @@ -1710,9 +1726,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/PatchApplyStatus" }, @@ -1736,6 +1776,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -1776,6 +1824,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/McpToolCallStatus" }, @@ -1804,6 +1860,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/DynamicToolCallOutputContentItem" @@ -1830,6 +1894,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/DynamicToolCallStatus" }, @@ -1869,6 +1941,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -1909,6 +1997,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -1957,12 +2053,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -2005,6 +2125,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -2027,6 +2163,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json index e5339f4e996f..09793ab1b1e5 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json @@ -1088,6 +1088,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -1130,6 +1138,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/CommandExecutionStatus" }, @@ -1160,9 +1176,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/PatchApplyStatus" }, @@ -1186,6 +1226,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -1226,6 +1274,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/McpToolCallStatus" }, @@ -1254,6 +1310,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/DynamicToolCallOutputContentItem" @@ -1280,6 +1344,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/DynamicToolCallStatus" }, @@ -1319,6 +1391,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -1359,6 +1447,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -1407,12 +1503,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -1455,6 +1575,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -1477,6 +1613,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json index 7d93606aa43c..56a18998a521 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json @@ -1638,6 +1638,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -1680,6 +1688,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/CommandExecutionStatus" }, @@ -1710,9 +1726,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/PatchApplyStatus" }, @@ -1736,6 +1776,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -1776,6 +1824,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/McpToolCallStatus" }, @@ -1804,6 +1860,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/DynamicToolCallOutputContentItem" @@ -1830,6 +1894,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/DynamicToolCallStatus" }, @@ -1869,6 +1941,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -1909,6 +1997,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -1957,12 +2053,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -2005,6 +2125,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -2027,6 +2163,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json index 774686e46ae2..ffdc4de1e884 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json @@ -1088,6 +1088,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -1130,6 +1138,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/CommandExecutionStatus" }, @@ -1160,9 +1176,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/PatchApplyStatus" }, @@ -1186,6 +1226,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -1226,6 +1274,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/McpToolCallStatus" }, @@ -1254,6 +1310,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/DynamicToolCallOutputContentItem" @@ -1280,6 +1344,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/DynamicToolCallStatus" }, @@ -1319,6 +1391,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -1359,6 +1447,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -1407,12 +1503,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -1455,6 +1575,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -1477,6 +1613,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json index 64179af7e1f0..0ee4af9c752d 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json @@ -1088,6 +1088,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -1130,6 +1138,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/CommandExecutionStatus" }, @@ -1160,9 +1176,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/PatchApplyStatus" }, @@ -1186,6 +1226,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -1226,6 +1274,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/McpToolCallStatus" }, @@ -1254,6 +1310,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/DynamicToolCallOutputContentItem" @@ -1280,6 +1344,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/DynamicToolCallStatus" }, @@ -1319,6 +1391,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -1359,6 +1447,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -1407,12 +1503,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -1455,6 +1575,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -1477,6 +1613,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json index 0739fa31bc48..c91aedce9efa 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json @@ -812,6 +812,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -854,6 +862,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/CommandExecutionStatus" }, @@ -884,9 +900,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/PatchApplyStatus" }, @@ -910,6 +950,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -950,6 +998,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/McpToolCallStatus" }, @@ -978,6 +1034,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/DynamicToolCallOutputContentItem" @@ -1004,6 +1068,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/DynamicToolCallStatus" }, @@ -1043,6 +1115,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -1083,6 +1171,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -1131,12 +1227,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -1179,6 +1299,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -1201,6 +1337,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json b/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json index bc5917ef15a7..12e5340ef551 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json @@ -812,6 +812,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -854,6 +862,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/CommandExecutionStatus" }, @@ -884,9 +900,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/PatchApplyStatus" }, @@ -910,6 +950,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -950,6 +998,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/McpToolCallStatus" }, @@ -978,6 +1034,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/DynamicToolCallOutputContentItem" @@ -1004,6 +1068,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/DynamicToolCallStatus" }, @@ -1043,6 +1115,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -1083,6 +1171,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -1131,12 +1227,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -1179,6 +1299,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -1201,6 +1337,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json index 22ad85d906dc..01d3a232d810 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json @@ -812,6 +812,14 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "cwd": { "allOf": [ { @@ -854,6 +862,14 @@ ], "default": "agent" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when command execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/CommandExecutionStatus" }, @@ -884,9 +900,33 @@ }, "type": "array" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of patch application in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when patch application started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/PatchApplyStatus" }, @@ -910,6 +950,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "durationMs": { "description": "The duration of the MCP tool call in milliseconds.", "format": "int64", @@ -950,6 +998,14 @@ "server": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when MCP tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/McpToolCallStatus" }, @@ -978,6 +1034,14 @@ { "properties": { "arguments": true, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "contentItems": { "items": { "$ref": "#/definitions/DynamicToolCallOutputContentItem" @@ -1004,6 +1068,14 @@ "null" ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when dynamic tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "$ref": "#/definitions/DynamicToolCallStatus" }, @@ -1043,6 +1115,22 @@ "description": "Last known status of the target agents, when available.", "type": "object" }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the collab tool execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "description": "Unique identifier for this collab tool call.", "type": "string" @@ -1083,6 +1171,14 @@ "description": "Thread ID of the agent issuing the collab request.", "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when collab tool execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "allOf": [ { @@ -1131,12 +1227,36 @@ } ] }, + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of the web search execution in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, "query": { "type": "string" }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when web search execution started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "type": { "enum": [ "webSearch" @@ -1179,6 +1299,22 @@ }, { "properties": { + "completedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation completed, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, + "durationMs": { + "description": "The duration of image generation in milliseconds.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "id": { "type": "string" }, @@ -1201,6 +1337,14 @@ } ] }, + "startedAtMs": { + "description": "Unix timestamp (in milliseconds) when image generation started, if known.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "status": { "type": "string" }, diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadItem.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadItem.ts index f7880c9d32ca..cd45b4b34a69 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadItem.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadItem.ts @@ -50,14 +50,50 @@ aggregatedOutput: string | null, * The command's exit code. */ exitCode: number | null, +/** + * Unix timestamp (in milliseconds) when command execution started, if known. + */ +startedAtMs: number | null, +/** + * Unix timestamp (in milliseconds) when command execution completed, if known. + */ +completedAtMs: number | null, /** * The duration of the command execution in milliseconds. */ -durationMs: number | null, } | { "type": "fileChange", id: string, changes: Array, status: PatchApplyStatus, } | { "type": "mcpToolCall", id: string, server: string, tool: string, status: McpToolCallStatus, arguments: JsonValue, mcpAppResourceUri?: string, result: McpToolCallResult | null, error: McpToolCallError | null, +durationMs: number | null, } | { "type": "fileChange", id: string, changes: Array, status: PatchApplyStatus, +/** + * Unix timestamp (in milliseconds) when patch application started, if known. + */ +startedAtMs: number | null, +/** + * Unix timestamp (in milliseconds) when patch application completed, if known. + */ +completedAtMs: number | null, +/** + * The duration of patch application in milliseconds. + */ +durationMs: number | null, } | { "type": "mcpToolCall", id: string, server: string, tool: string, status: McpToolCallStatus, arguments: JsonValue, mcpAppResourceUri?: string, result: McpToolCallResult | null, error: McpToolCallError | null, +/** + * Unix timestamp (in milliseconds) when MCP tool execution started, if known. + */ +startedAtMs: number | null, +/** + * Unix timestamp (in milliseconds) when MCP tool execution completed, if known. + */ +completedAtMs: number | null, /** * The duration of the MCP tool call in milliseconds. */ durationMs: number | null, } | { "type": "dynamicToolCall", id: string, namespace: string | null, tool: string, arguments: JsonValue, status: DynamicToolCallStatus, contentItems: Array | null, success: boolean | null, +/** + * Unix timestamp (in milliseconds) when dynamic tool execution started, if known. + */ +startedAtMs: number | null, +/** + * Unix timestamp (in milliseconds) when dynamic tool execution completed, if known. + */ +completedAtMs: number | null, /** * The duration of the dynamic tool call in milliseconds. */ @@ -98,4 +134,40 @@ reasoningEffort: ReasoningEffort | null, /** * Last known status of the target agents, when available. */ -agentsStates: { [key in string]?: CollabAgentState }, } | { "type": "webSearch", id: string, query: string, action: WebSearchAction | null, } | { "type": "imageView", id: string, path: AbsolutePathBuf, } | { "type": "imageGeneration", id: string, status: string, revisedPrompt: string | null, result: string, savedPath?: AbsolutePathBuf, } | { "type": "enteredReviewMode", id: string, review: string, } | { "type": "exitedReviewMode", id: string, review: string, } | { "type": "contextCompaction", id: string, }; +agentsStates: { [key in string]?: CollabAgentState }, +/** + * Unix timestamp (in milliseconds) when collab tool execution started, if known. + */ +startedAtMs: number | null, +/** + * Unix timestamp (in milliseconds) when collab tool execution completed, if known. + */ +completedAtMs: number | null, +/** + * The duration of the collab tool execution in milliseconds. + */ +durationMs: number | null, } | { "type": "webSearch", id: string, query: string, action: WebSearchAction | null, +/** + * Unix timestamp (in milliseconds) when web search execution started, if known. + */ +startedAtMs: number | null, +/** + * Unix timestamp (in milliseconds) when web search execution completed, if known. + */ +completedAtMs: number | null, +/** + * The duration of the web search execution in milliseconds. + */ +durationMs: number | null, } | { "type": "imageView", id: string, path: AbsolutePathBuf, } | { "type": "imageGeneration", id: string, status: string, revisedPrompt: string | null, result: string, savedPath?: AbsolutePathBuf, +/** + * Unix timestamp (in milliseconds) when image generation started, if known. + */ +startedAtMs: number | null, +/** + * Unix timestamp (in milliseconds) when image generation completed, if known. + */ +completedAtMs: number | null, +/** + * The duration of image generation in milliseconds. + */ +durationMs: number | null, } | { "type": "enteredReviewMode", id: string, review: string, } | { "type": "exitedReviewMode", id: string, review: string, } | { "type": "contextCompaction", id: string, }; diff --git a/codex-rs/app-server-protocol/src/protocol/event_mapping.rs b/codex-rs/app-server-protocol/src/protocol/event_mapping.rs index 56219818fc28..e6d44f67b36e 100644 --- a/codex-rs/app-server-protocol/src/protocol/event_mapping.rs +++ b/codex-rs/app-server-protocol/src/protocol/event_mapping.rs @@ -68,6 +68,8 @@ pub fn item_event_to_server_notification( .collect(), ), success: Some(response.success), + started_at_ms: response.started_at_ms, + completed_at_ms: response.completed_at_ms, duration_ms, }; ServerNotification::ItemCompleted(ItemCompletedNotification { @@ -86,6 +88,8 @@ pub fn item_event_to_server_notification( mcp_app_resource_uri: begin_event.mcp_app_resource_uri, result: None, error: None, + started_at_ms: begin_event.started_at_ms, + completed_at_ms: None, duration_ms: None, }; ServerNotification::ItemStarted(ItemStartedNotification { @@ -126,6 +130,8 @@ pub fn item_event_to_server_notification( mcp_app_resource_uri: end_event.mcp_app_resource_uri, result, error, + started_at_ms: end_event.started_at_ms, + completed_at_ms: end_event.completed_at_ms, duration_ms, }; ServerNotification::ItemCompleted(ItemCompletedNotification { @@ -145,6 +151,9 @@ pub fn item_event_to_server_notification( model: Some(begin_event.model), reasoning_effort: Some(begin_event.reasoning_effort), agents_states: HashMap::new(), + started_at_ms: begin_event.started_at_ms, + completed_at_ms: None, + duration_ms: None, }; ServerNotification::ItemStarted(ItemStartedNotification { thread_id, @@ -183,6 +192,9 @@ pub fn item_event_to_server_notification( model: Some(end_event.model), reasoning_effort: Some(end_event.reasoning_effort), agents_states, + started_at_ms: end_event.started_at_ms, + completed_at_ms: end_event.completed_at_ms, + duration_ms: end_event.duration_ms, }; ServerNotification::ItemCompleted(ItemCompletedNotification { thread_id, @@ -202,6 +214,9 @@ pub fn item_event_to_server_notification( model: None, reasoning_effort: None, agents_states: HashMap::new(), + started_at_ms: begin_event.started_at_ms, + completed_at_ms: None, + duration_ms: None, }; ServerNotification::ItemStarted(ItemStartedNotification { thread_id, @@ -229,6 +244,9 @@ pub fn item_event_to_server_notification( model: None, reasoning_effort: None, agents_states: [(receiver_id, received_status)].into_iter().collect(), + started_at_ms: end_event.started_at_ms, + completed_at_ms: end_event.completed_at_ms, + duration_ms: end_event.duration_ms, }; ServerNotification::ItemCompleted(ItemCompletedNotification { thread_id, @@ -252,6 +270,9 @@ pub fn item_event_to_server_notification( model: None, reasoning_effort: None, agents_states: HashMap::new(), + started_at_ms: begin_event.started_at_ms, + completed_at_ms: None, + duration_ms: None, }; ServerNotification::ItemStarted(ItemStartedNotification { thread_id, @@ -287,6 +308,9 @@ pub fn item_event_to_server_notification( model: None, reasoning_effort: None, agents_states, + started_at_ms: end_event.started_at_ms, + completed_at_ms: end_event.completed_at_ms, + duration_ms: end_event.duration_ms, }; ServerNotification::ItemCompleted(ItemCompletedNotification { thread_id, @@ -305,6 +329,9 @@ pub fn item_event_to_server_notification( model: None, reasoning_effort: None, agents_states: HashMap::new(), + started_at_ms: begin_event.started_at_ms, + completed_at_ms: None, + duration_ms: None, }; ServerNotification::ItemStarted(ItemStartedNotification { thread_id, @@ -337,6 +364,9 @@ pub fn item_event_to_server_notification( model: None, reasoning_effort: None, agents_states, + started_at_ms: end_event.started_at_ms, + completed_at_ms: end_event.completed_at_ms, + duration_ms: end_event.duration_ms, }; ServerNotification::ItemCompleted(ItemCompletedNotification { thread_id, @@ -355,6 +385,9 @@ pub fn item_event_to_server_notification( model: None, reasoning_effort: None, agents_states: HashMap::new(), + started_at_ms: begin_event.started_at_ms, + completed_at_ms: None, + duration_ms: None, }; ServerNotification::ItemStarted(ItemStartedNotification { thread_id, @@ -387,6 +420,9 @@ pub fn item_event_to_server_notification( model: None, reasoning_effort: None, agents_states, + started_at_ms: end_event.started_at_ms, + completed_at_ms: end_event.completed_at_ms, + duration_ms: end_event.duration_ms, }; ServerNotification::ItemCompleted(ItemCompletedNotification { thread_id, @@ -583,6 +619,9 @@ mod tests { model: None, reasoning_effort: None, agents_states: HashMap::new(), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, }, ); @@ -628,6 +667,9 @@ mod tests { )] .into_iter() .collect(), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, }, ); @@ -665,6 +707,8 @@ mod tests { mcp_app_resource_uri: Some("ui://widget/list-resources.html".to_string()), result: None, error: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, }, }, @@ -703,6 +747,8 @@ mod tests { mcp_app_resource_uri: None, result: None, error: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, }, }, @@ -763,6 +809,8 @@ mod tests { })), })), error: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: Some(0), }, }, @@ -806,6 +854,8 @@ mod tests { error: Some(McpToolCallError { message: "boom".to_string(), }), + started_at_ms: None, + completed_at_ms: None, duration_ms: Some(1), }, }, diff --git a/codex-rs/app-server-protocol/src/protocol/item_builders.rs b/codex-rs/app-server-protocol/src/protocol/item_builders.rs index 546fb1b6796a..461e315b7315 100644 --- a/codex-rs/app-server-protocol/src/protocol/item_builders.rs +++ b/codex-rs/app-server-protocol/src/protocol/item_builders.rs @@ -45,6 +45,9 @@ pub fn build_file_change_approval_request_item( id: payload.call_id.clone(), changes: convert_patch_changes(&payload.changes), status: PatchApplyStatus::InProgress, + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, } } @@ -53,6 +56,9 @@ pub fn build_file_change_begin_item(payload: &PatchApplyBeginEvent) -> ThreadIte id: payload.call_id.clone(), changes: convert_patch_changes(&payload.changes), status: PatchApplyStatus::InProgress, + started_at_ms: payload.started_at_ms, + completed_at_ms: None, + duration_ms: None, } } @@ -61,6 +67,9 @@ pub fn build_file_change_end_item(payload: &PatchApplyEndEvent) -> ThreadItem { id: payload.call_id.clone(), changes: convert_patch_changes(&payload.changes), status: (&payload.status).into(), + started_at_ms: payload.started_at_ms, + completed_at_ms: payload.completed_at_ms, + duration_ms: payload.duration_ms, } } @@ -82,6 +91,8 @@ pub fn build_command_execution_approval_request_item( .collect(), aggregated_output: None, exit_code: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, } } @@ -102,6 +113,8 @@ pub fn build_command_execution_begin_item(payload: &ExecCommandBeginEvent) -> Th .collect(), aggregated_output: None, exit_code: None, + started_at_ms: payload.started_at_ms, + completed_at_ms: None, duration_ms: None, } } @@ -129,6 +142,8 @@ pub fn build_command_execution_end_item(payload: &ExecCommandEndEvent) -> Thread .collect(), aggregated_output, exit_code: Some(payload.exit_code), + started_at_ms: payload.started_at_ms, + completed_at_ms: payload.completed_at_ms, duration_ms: Some(duration_ms), } } @@ -158,6 +173,8 @@ pub fn build_item_from_guardian_event( command_actions, aggregated_output: None, exit_code: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, }) } @@ -194,6 +211,8 @@ pub fn build_item_from_guardian_event( command_actions, aggregated_output: None, exit_code: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, }) } diff --git a/codex-rs/app-server-protocol/src/protocol/thread_history.rs b/codex-rs/app-server-protocol/src/protocol/thread_history.rs index 62f635efb7ea..2814101fb0a7 100644 --- a/codex-rs/app-server-protocol/src/protocol/thread_history.rs +++ b/codex-rs/app-server-protocol/src/protocol/thread_history.rs @@ -388,6 +388,9 @@ impl ThreadHistoryBuilder { id: payload.call_id.clone(), query: String::new(), action: None, + started_at_ms: payload.started_at_ms, + completed_at_ms: None, + duration_ms: None, }; self.upsert_item_in_current_turn(item); } @@ -397,6 +400,9 @@ impl ThreadHistoryBuilder { id: payload.call_id.clone(), query: payload.query.clone(), action: Some(WebSearchAction::from(payload.action.clone())), + started_at_ms: payload.started_at_ms, + completed_at_ms: payload.completed_at_ms, + duration_ms: payload.duration_ms, }; self.upsert_item_in_current_turn(item); } @@ -474,6 +480,8 @@ impl ThreadHistoryBuilder { status: DynamicToolCallStatus::InProgress, content_items: None, success: None, + started_at_ms: payload.started_at_ms, + completed_at_ms: None, duration_ms: None, }; if payload.turn_id.is_empty() { @@ -498,6 +506,8 @@ impl ThreadHistoryBuilder { status, content_items: Some(convert_dynamic_tool_content_items(&payload.content_items)), success: Some(payload.success), + started_at_ms: payload.started_at_ms, + completed_at_ms: payload.completed_at_ms, duration_ms, }; if payload.turn_id.is_empty() { @@ -521,6 +531,8 @@ impl ThreadHistoryBuilder { mcp_app_resource_uri: payload.mcp_app_resource_uri.clone(), result: None, error: None, + started_at_ms: payload.started_at_ms, + completed_at_ms: None, duration_ms: None, }; self.upsert_item_in_current_turn(item); @@ -562,6 +574,8 @@ impl ThreadHistoryBuilder { mcp_app_resource_uri: payload.mcp_app_resource_uri.clone(), result, error, + started_at_ms: payload.started_at_ms, + completed_at_ms: payload.completed_at_ms, duration_ms, }; self.upsert_item_in_current_turn(item); @@ -582,6 +596,9 @@ impl ThreadHistoryBuilder { revised_prompt: None, result: String::new(), saved_path: None, + started_at_ms: payload.started_at_ms, + completed_at_ms: None, + duration_ms: None, }; self.upsert_item_in_current_turn(item); } @@ -593,6 +610,9 @@ impl ThreadHistoryBuilder { revised_prompt: payload.revised_prompt.clone(), result: payload.result.clone(), saved_path: payload.saved_path.clone(), + started_at_ms: payload.started_at_ms, + completed_at_ms: payload.completed_at_ms, + duration_ms: payload.duration_ms, }; self.upsert_item_in_current_turn(item); } @@ -611,6 +631,9 @@ impl ThreadHistoryBuilder { model: Some(payload.model.clone()), reasoning_effort: Some(payload.reasoning_effort), agents_states: HashMap::new(), + started_at_ms: payload.started_at_ms, + completed_at_ms: None, + duration_ms: None, }; self.upsert_item_in_current_turn(item); } @@ -646,6 +669,9 @@ impl ThreadHistoryBuilder { model: Some(payload.model.clone()), reasoning_effort: Some(payload.reasoning_effort), agents_states, + started_at_ms: payload.started_at_ms, + completed_at_ms: payload.completed_at_ms, + duration_ms: payload.duration_ms, }); } @@ -663,6 +689,9 @@ impl ThreadHistoryBuilder { model: None, reasoning_effort: None, agents_states: HashMap::new(), + started_at_ms: payload.started_at_ms, + completed_at_ms: None, + duration_ms: None, }; self.upsert_item_in_current_turn(item); } @@ -687,6 +716,9 @@ impl ThreadHistoryBuilder { model: None, reasoning_effort: None, agents_states: [(receiver_id, received_status)].into_iter().collect(), + started_at_ms: payload.started_at_ms, + completed_at_ms: payload.completed_at_ms, + duration_ms: payload.duration_ms, }); } @@ -708,6 +740,9 @@ impl ThreadHistoryBuilder { model: None, reasoning_effort: None, agents_states: HashMap::new(), + started_at_ms: payload.started_at_ms, + completed_at_ms: None, + duration_ms: None, }; self.upsert_item_in_current_turn(item); } @@ -743,6 +778,9 @@ impl ThreadHistoryBuilder { model: None, reasoning_effort: None, agents_states, + started_at_ms: payload.started_at_ms, + completed_at_ms: payload.completed_at_ms, + duration_ms: payload.duration_ms, }); } @@ -760,6 +798,9 @@ impl ThreadHistoryBuilder { model: None, reasoning_effort: None, agents_states: HashMap::new(), + started_at_ms: payload.started_at_ms, + completed_at_ms: None, + duration_ms: None, }; self.upsert_item_in_current_turn(item); } @@ -786,6 +827,9 @@ impl ThreadHistoryBuilder { model: None, reasoning_effort: None, agents_states, + started_at_ms: payload.started_at_ms, + completed_at_ms: payload.completed_at_ms, + duration_ms: payload.duration_ms, }); } @@ -803,6 +847,9 @@ impl ThreadHistoryBuilder { model: None, reasoning_effort: None, agents_states: HashMap::new(), + started_at_ms: payload.started_at_ms, + completed_at_ms: None, + duration_ms: None, }; self.upsert_item_in_current_turn(item); } @@ -832,6 +879,9 @@ impl ThreadHistoryBuilder { model: None, reasoning_effort: None, agents_states, + started_at_ms: payload.started_at_ms, + completed_at_ms: payload.completed_at_ms, + duration_ms: payload.duration_ms, }); } @@ -1464,6 +1514,9 @@ mod tests { revised_prompt: Some("final prompt".into()), result: "Zm9v".into(), saved_path: Some(test_path_buf("/tmp/ig_123.png").abs()), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, ], } @@ -1869,6 +1922,9 @@ mod tests { query: Some("codex".into()), queries: None, }), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, } ); assert_eq!( @@ -1885,6 +1941,8 @@ mod tests { }], aggregated_output: Some("hello world\n".into()), exit_code: Some(0), + started_at_ms: None, + completed_at_ms: None, duration_ms: Some(12), } ); @@ -1901,6 +1959,8 @@ mod tests { error: Some(McpToolCallError { message: "boom".into(), }), + started_at_ms: None, + completed_at_ms: None, duration_ms: Some(8), } ); @@ -1966,6 +2026,8 @@ mod tests { })), })), error: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: Some(8), } ); @@ -2032,6 +2094,8 @@ mod tests { text: "Ticket is open".into(), }]), success: Some(true), + started_at_ms: None, + completed_at_ms: None, duration_ms: Some(42), } ); @@ -2113,6 +2177,8 @@ mod tests { }], aggregated_output: Some("exec command rejected by user".into()), exit_code: Some(-1), + started_at_ms: None, + completed_at_ms: None, duration_ms: Some(0), } ); @@ -2126,6 +2192,9 @@ mod tests { diff: "hello\n".into(), }], status: PatchApplyStatus::Declined, + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, } ); } @@ -2204,6 +2273,8 @@ mod tests { }], aggregated_output: None, exit_code: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, } ); @@ -2265,6 +2336,8 @@ mod tests { }], aggregated_output: None, exit_code: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, } ); @@ -2358,6 +2431,8 @@ mod tests { }], aggregated_output: Some("done\n".into()), exit_code: Some(0), + started_at_ms: None, + completed_at_ms: None, duration_ms: Some(5), } ); @@ -2509,6 +2584,9 @@ mod tests { diff: "hello\n".into(), }], status: PatchApplyStatus::InProgress, + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, ] ); @@ -2574,6 +2652,9 @@ mod tests { diff: "hello\n".into(), }], status: PatchApplyStatus::InProgress, + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, ] ); @@ -2792,6 +2873,9 @@ mod tests { )] .into_iter() .collect(), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, } ); } @@ -2852,6 +2936,9 @@ mod tests { )] .into_iter() .collect(), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, } ); } @@ -2924,6 +3011,9 @@ mod tests { )] .into_iter() .collect(), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, } ); } diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index 7ca57ef1f6eb..515e7345bbd0 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -5869,6 +5869,12 @@ pub enum ThreadItem { aggregated_output: Option, /// The command's exit code. exit_code: Option, + /// Unix timestamp (in milliseconds) when command execution started, if known. + #[ts(type = "number | null")] + started_at_ms: Option, + /// Unix timestamp (in milliseconds) when command execution completed, if known. + #[ts(type = "number | null")] + completed_at_ms: Option, /// The duration of the command execution in milliseconds. #[ts(type = "number | null")] duration_ms: Option, @@ -5879,6 +5885,15 @@ pub enum ThreadItem { id: String, changes: Vec, status: PatchApplyStatus, + /// Unix timestamp (in milliseconds) when patch application started, if known. + #[ts(type = "number | null")] + started_at_ms: Option, + /// Unix timestamp (in milliseconds) when patch application completed, if known. + #[ts(type = "number | null")] + completed_at_ms: Option, + /// The duration of patch application in milliseconds. + #[ts(type = "number | null")] + duration_ms: Option, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] @@ -5893,6 +5908,12 @@ pub enum ThreadItem { mcp_app_resource_uri: Option, result: Option>, error: Option, + /// Unix timestamp (in milliseconds) when MCP tool execution started, if known. + #[ts(type = "number | null")] + started_at_ms: Option, + /// Unix timestamp (in milliseconds) when MCP tool execution completed, if known. + #[ts(type = "number | null")] + completed_at_ms: Option, /// The duration of the MCP tool call in milliseconds. #[ts(type = "number | null")] duration_ms: Option, @@ -5907,6 +5928,12 @@ pub enum ThreadItem { status: DynamicToolCallStatus, content_items: Option>, success: Option, + /// Unix timestamp (in milliseconds) when dynamic tool execution started, if known. + #[ts(type = "number | null")] + started_at_ms: Option, + /// Unix timestamp (in milliseconds) when dynamic tool execution completed, if known. + #[ts(type = "number | null")] + completed_at_ms: Option, /// The duration of the dynamic tool call in milliseconds. #[ts(type = "number | null")] duration_ms: Option, @@ -5933,6 +5960,15 @@ pub enum ThreadItem { reasoning_effort: Option, /// Last known status of the target agents, when available. agents_states: HashMap, + /// Unix timestamp (in milliseconds) when collab tool execution started, if known. + #[ts(type = "number | null")] + started_at_ms: Option, + /// Unix timestamp (in milliseconds) when collab tool execution completed, if known. + #[ts(type = "number | null")] + completed_at_ms: Option, + /// The duration of the collab tool execution in milliseconds. + #[ts(type = "number | null")] + duration_ms: Option, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] @@ -5940,6 +5976,15 @@ pub enum ThreadItem { id: String, query: String, action: Option, + /// Unix timestamp (in milliseconds) when web search execution started, if known. + #[ts(type = "number | null")] + started_at_ms: Option, + /// Unix timestamp (in milliseconds) when web search execution completed, if known. + #[ts(type = "number | null")] + completed_at_ms: Option, + /// The duration of the web search execution in milliseconds. + #[ts(type = "number | null")] + duration_ms: Option, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] @@ -5954,6 +5999,15 @@ pub enum ThreadItem { #[serde(default, skip_serializing_if = "Option::is_none")] #[ts(optional)] saved_path: Option, + /// Unix timestamp (in milliseconds) when image generation started, if known. + #[ts(type = "number | null")] + started_at_ms: Option, + /// Unix timestamp (in milliseconds) when image generation completed, if known. + #[ts(type = "number | null")] + completed_at_ms: Option, + /// The duration of image generation in milliseconds. + #[ts(type = "number | null")] + duration_ms: Option, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] @@ -6418,6 +6472,9 @@ impl From for ThreadItem { id: search.id, query: search.query, action: Some(WebSearchAction::from(search.action)), + started_at_ms: search.started_at_ms, + completed_at_ms: search.completed_at_ms, + duration_ms: search.duration_ms, }, CoreTurnItem::ImageGeneration(image) => ThreadItem::ImageGeneration { id: image.id, @@ -6425,6 +6482,9 @@ impl From for ThreadItem { revised_prompt: image.revised_prompt, result: image.result, saved_path: image.saved_path, + started_at_ms: image.started_at_ms, + completed_at_ms: image.completed_at_ms, + duration_ms: image.duration_ms, }, CoreTurnItem::ContextCompaction(compaction) => { ThreadItem::ContextCompaction { id: compaction.id } @@ -10316,6 +10376,9 @@ mod tests { query: Some("docs".to_string()), queries: None, }), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, } ); } diff --git a/codex-rs/app-server/src/bespoke_event_handling.rs b/codex-rs/app-server/src/bespoke_event_handling.rs index e8a6cb9cc0f6..3bf3f4de95ed 100644 --- a/codex-rs/app-server/src/bespoke_event_handling.rs +++ b/codex-rs/app-server/src/bespoke_event_handling.rs @@ -841,6 +841,8 @@ pub(crate) async fn apply_bespoke_event_handling( status: DynamicToolCallStatus::InProgress, content_items: None, success: None, + started_at_ms: request.started_at_ms, + completed_at_ms: None, duration_ms: None, }; let notification = ItemStartedNotification { @@ -1483,6 +1485,8 @@ async fn start_command_execution_item( command_actions, aggregated_output: None, exit_code: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, }, }; @@ -1527,6 +1531,8 @@ async fn complete_command_execution_item( command_actions, aggregated_output: None, exit_code: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, }; let notification = ItemCompletedNotification { @@ -2068,6 +2074,9 @@ async fn on_file_change_request_approval_response( id: item_id.clone(), changes, status, + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, event_turn_id.clone(), &outgoing, @@ -2568,6 +2577,8 @@ mod tests { command_actions: completion_item.command_actions.clone(), aggregated_output: None, exit_code: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, } ); diff --git a/codex-rs/app-server/tests/suite/v2/dynamic_tools.rs b/codex-rs/app-server/tests/suite/v2/dynamic_tools.rs index 7ee21a2068f1..7b8344e822bd 100644 --- a/codex-rs/app-server/tests/suite/v2/dynamic_tools.rs +++ b/codex-rs/app-server/tests/suite/v2/dynamic_tools.rs @@ -333,6 +333,8 @@ async fn dynamic_tool_call_round_trip_sends_text_content_items_to_model() -> Res status, content_items, success, + started_at_ms, + completed_at_ms, duration_ms, } = started.item else { @@ -345,6 +347,8 @@ async fn dynamic_tool_call_round_trip_sends_text_content_items_to_model() -> Res assert_eq!(status, DynamicToolCallStatus::InProgress); assert_eq!(content_items, None); assert_eq!(success, None); + assert!(started_at_ms.is_some()); + assert_eq!(completed_at_ms, None); assert_eq!(duration_ms, None); // Read the tool call request from the app server. @@ -389,6 +393,8 @@ async fn dynamic_tool_call_round_trip_sends_text_content_items_to_model() -> Res status, content_items, success, + started_at_ms, + completed_at_ms, duration_ms, } = completed.item else { @@ -406,6 +412,8 @@ async fn dynamic_tool_call_round_trip_sends_text_content_items_to_model() -> Res }]) ); assert_eq!(success, Some(true)); + assert!(started_at_ms.is_some()); + assert!(completed_at_ms.is_some()); assert!(duration_ms.is_some()); timeout( diff --git a/codex-rs/app-server/tests/suite/v2/mcp_tool.rs b/codex-rs/app-server/tests/suite/v2/mcp_tool.rs index 03f3db95f143..954ca5477e44 100644 --- a/codex-rs/app-server/tests/suite/v2/mcp_tool.rs +++ b/codex-rs/app-server/tests/suite/v2/mcp_tool.rs @@ -286,6 +286,8 @@ url = "{mcp_server_url}/mcp" mcp_app_resource_uri: None, result: Some(result), error: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, })?; assert!(serialized_item.len() < DEFAULT_OUTPUT_BYTES_CAP * 2 + 2048); diff --git a/codex-rs/app-server/tests/suite/v2/thread_resume.rs b/codex-rs/app-server/tests/suite/v2/thread_resume.rs index d9f5f039de78..7d2b295d33b3 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_resume.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_resume.rs @@ -2193,16 +2193,30 @@ async fn thread_resume_replays_pending_file_change_request_approval() -> Result< }) .await??; let expected_readme_path = workspace.join("README.md"); - let expected_file_change = ThreadItem::FileChange { - id: "patch-call".to_string(), - changes: vec![codex_app_server_protocol::FileUpdateChange { + let ThreadItem::FileChange { + id, + changes, + status, + started_at_ms, + completed_at_ms, + duration_ms, + } = original_started + else { + unreachable!("loop ensures we break on file change items"); + }; + assert_eq!(id, "patch-call"); + assert_eq!( + changes, + vec![codex_app_server_protocol::FileUpdateChange { path: expected_readme_path.to_string_lossy().into_owned(), kind: PatchChangeKind::Add, diff: "new line\n".to_string(), - }], - status: PatchApplyStatus::InProgress, - }; - assert_eq!(original_started, expected_file_change); + }] + ); + assert_eq!(status, PatchApplyStatus::InProgress); + assert!(started_at_ms.is_some()); + assert_eq!(completed_at_ms, None); + assert_eq!(duration_ms, None); let original_request = timeout( DEFAULT_READ_TIMEOUT, diff --git a/codex-rs/app-server/tests/suite/v2/turn_start.rs b/codex-rs/app-server/tests/suite/v2/turn_start.rs index 3c5bbd3b610e..c6c0ceb959ab 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_start.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_start.rs @@ -2158,6 +2158,7 @@ async fn turn_start_file_change_approval_v2() -> Result<()> { ref id, status, ref changes, + .. } = started_file_change else { unreachable!("loop ensures we break on file change items"); @@ -2594,20 +2595,35 @@ async fn turn_start_emits_spawn_agent_item_with_model_metadata_v2() -> Result<() } }) .await??; - assert_eq!( - spawn_started, - ThreadItem::CollabAgentToolCall { - id: SPAWN_CALL_ID.to_string(), - tool: CollabAgentTool::SpawnAgent, - status: CollabAgentToolCallStatus::InProgress, - sender_thread_id: thread.id.clone(), - receiver_thread_ids: Vec::new(), - prompt: Some(CHILD_PROMPT.to_string()), - model: Some(REQUESTED_MODEL.to_string()), - reasoning_effort: Some(REQUESTED_REASONING_EFFORT), - agents_states: HashMap::new(), - } - ); + let ThreadItem::CollabAgentToolCall { + id, + tool, + status, + sender_thread_id, + receiver_thread_ids, + prompt, + model, + reasoning_effort, + agents_states, + started_at_ms, + completed_at_ms, + duration_ms, + } = spawn_started + else { + panic!("expected collab agent tool call item"); + }; + assert_eq!(id, SPAWN_CALL_ID); + assert_eq!(tool, CollabAgentTool::SpawnAgent); + assert_eq!(status, CollabAgentToolCallStatus::InProgress); + assert_eq!(sender_thread_id, thread.id); + assert_eq!(receiver_thread_ids, Vec::::new()); + assert_eq!(prompt.as_deref(), Some(CHILD_PROMPT)); + assert_eq!(model.as_deref(), Some(REQUESTED_MODEL)); + assert_eq!(reasoning_effort, Some(REQUESTED_REASONING_EFFORT)); + assert_eq!(agents_states, HashMap::new()); + assert!(started_at_ms.is_some()); + assert_eq!(completed_at_ms, None); + assert_eq!(duration_ms, None); let spawn_completed = timeout(DEFAULT_READ_TIMEOUT, async { loop { @@ -2634,6 +2650,9 @@ async fn turn_start_emits_spawn_agent_item_with_model_metadata_v2() -> Result<() model, reasoning_effort, agents_states, + started_at_ms, + completed_at_ms, + duration_ms, } = spawn_completed else { unreachable!("loop ensures we break on collab agent tool call items"); @@ -2645,6 +2664,9 @@ async fn turn_start_emits_spawn_agent_item_with_model_metadata_v2() -> Result<() assert_eq!(id, SPAWN_CALL_ID); assert_eq!(tool, CollabAgentTool::SpawnAgent); assert_eq!(status, CollabAgentToolCallStatus::Completed); + assert!(started_at_ms.is_some()); + assert!(completed_at_ms.is_some()); + assert!(duration_ms.is_some()); assert_eq!(sender_thread_id, thread.id); assert_eq!(receiver_thread_ids, vec![receiver_thread_id.clone()]); assert_eq!(prompt, Some(CHILD_PROMPT.to_string())); @@ -2818,6 +2840,9 @@ config_file = "./custom-role.toml" model, reasoning_effort, agents_states, + started_at_ms, + completed_at_ms, + duration_ms, } = spawn_completed else { unreachable!("loop ensures we break on collab agent tool call items"); @@ -2829,6 +2854,9 @@ config_file = "./custom-role.toml" assert_eq!(id, SPAWN_CALL_ID); assert_eq!(tool, CollabAgentTool::SpawnAgent); assert_eq!(status, CollabAgentToolCallStatus::Completed); + assert!(started_at_ms.is_some()); + assert!(completed_at_ms.is_some()); + assert!(duration_ms.is_some()); assert_eq!(sender_thread_id, thread.id); assert_eq!(receiver_thread_ids, vec![receiver_thread_id.clone()]); assert_eq!(prompt, Some(CHILD_PROMPT.to_string())); @@ -3126,6 +3154,7 @@ async fn turn_start_file_change_approval_decline_v2() -> Result<()> { ref id, status, ref changes, + .. } = started_file_change else { unreachable!("loop ensures we break on file change items"); From 736e107435d1cf88a79b0caf5f46a63a714eac41 Mon Sep 17 00:00:00 2001 From: rhan-oai Date: Thu, 30 Apr 2026 23:03:05 -0700 Subject: [PATCH 2/2] [codex-analytics] replay protocol-native item timing --- .../src/event_processor_with_jsonl_output.rs | 1 + .../tests/event_processor_with_json_output.rs | 39 +++++++++++++++++++ codex-rs/tui/src/app/tests.rs | 6 +++ codex-rs/tui/src/chatwidget.rs | 16 +++++++- codex-rs/tui/src/chatwidget/interrupts.rs | 2 + .../tui/src/chatwidget/tests/app_server.rs | 25 ++++++++++++ .../tui/src/chatwidget/tests/exec_flow.rs | 2 + codex-rs/tui/src/chatwidget/tests/helpers.rs | 15 +++++++ .../src/chatwidget/tests/status_and_layout.rs | 4 ++ codex-rs/tui/src/multi_agents.rs | 21 ++++++++++ 10 files changed, 130 insertions(+), 1 deletion(-) diff --git a/codex-rs/exec/src/event_processor_with_jsonl_output.rs b/codex-rs/exec/src/event_processor_with_jsonl_output.rs index 1641398ae69f..7198e9a07a3b 100644 --- a/codex-rs/exec/src/event_processor_with_jsonl_output.rs +++ b/codex-rs/exec/src/event_processor_with_jsonl_output.rs @@ -296,6 +296,7 @@ impl EventProcessorWithJsonOutput { id: raw_id, query, action, + .. } => Some(ExecThreadItem { id: make_id(), details: ThreadItemDetails::WebSearch(WebSearchItem { diff --git a/codex-rs/exec/tests/event_processor_with_json_output.rs b/codex-rs/exec/tests/event_processor_with_json_output.rs index f6f6d35f2151..d5569c030c91 100644 --- a/codex-rs/exec/tests/event_processor_with_json_output.rs +++ b/codex-rs/exec/tests/event_processor_with_json_output.rs @@ -173,6 +173,8 @@ fn command_execution_started_and_completed_translate_to_thread_events() { command_actions: Vec::::new(), aggregated_output: None, exit_code: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, }; @@ -212,6 +214,8 @@ fn command_execution_started_and_completed_translate_to_thread_events() { command_actions: Vec::::new(), aggregated_output: Some("a.txt\n".to_string()), exit_code: Some(0), + started_at_ms: None, + completed_at_ms: None, duration_ms: Some(3), }, thread_id: "thread-1".to_string(), @@ -359,6 +363,9 @@ fn web_search_completion_preserves_query_and_action() { query: Some("rust async await".to_string()), queries: None, }), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, thread_id: "thread-1".to_string(), turn_id: "turn-1".to_string(), @@ -396,6 +403,9 @@ fn web_search_start_and_completion_reuse_item_id() { id: "search-1".to_string(), query: String::new(), action: None, + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, thread_id: "thread-1".to_string(), turn_id: "turn-1".to_string(), @@ -410,6 +420,9 @@ fn web_search_start_and_completion_reuse_item_id() { query: Some("rust async await".to_string()), queries: None, }), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, thread_id: "thread-1".to_string(), turn_id: "turn-1".to_string(), @@ -468,6 +481,8 @@ fn mcp_tool_call_begin_and_end_emit_item_events() { mcp_app_resource_uri: None, result: None, error: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, }, thread_id: "thread-1".to_string(), @@ -488,6 +503,8 @@ fn mcp_tool_call_begin_and_end_emit_item_events() { meta: None, })), error: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: Some(1_000), }, thread_id: "thread-1".to_string(), @@ -555,6 +572,8 @@ fn mcp_tool_call_failure_sets_failed_status() { error: Some(McpToolCallError { message: "tool exploded".to_string(), }), + started_at_ms: None, + completed_at_ms: None, duration_ms: Some(5), }, thread_id: "thread-1".to_string(), @@ -600,6 +619,8 @@ fn mcp_tool_call_defaults_arguments_and_preserves_structured_content() { mcp_app_resource_uri: None, result: None, error: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, }, thread_id: "thread-1".to_string(), @@ -623,6 +644,8 @@ fn mcp_tool_call_defaults_arguments_and_preserves_structured_content() { meta: None, })), error: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: Some(10), }, thread_id: "thread-1".to_string(), @@ -692,6 +715,9 @@ fn collab_spawn_begin_and_end_emit_item_events() { model: Some("gpt-5".to_string()), reasoning_effort: None, agents_states: std::collections::HashMap::new(), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, thread_id: "thread-parent".to_string(), turn_id: "turn-1".to_string(), @@ -714,6 +740,9 @@ fn collab_spawn_begin_and_end_emit_item_events() { message: None, }, )]), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, thread_id: "thread-parent".to_string(), turn_id: "turn-1".to_string(), @@ -792,6 +821,9 @@ fn file_change_completion_maps_change_kinds() { }, ], status: ApiPatchApplyStatus::Completed, + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, thread_id: "thread-1".to_string(), turn_id: "turn-1".to_string(), @@ -842,6 +874,9 @@ fn file_change_declined_maps_to_failed_status() { diff: "@@ -1 +1 @@".to_string(), }], status: ApiPatchApplyStatus::Declined, + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, thread_id: "thread-1".to_string(), turn_id: "turn-1".to_string(), @@ -1292,6 +1327,8 @@ fn turn_completion_reconciles_started_items_from_turn_items() { command_actions: Vec::::new(), aggregated_output: None, exit_code: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, }, thread_id: "thread-1".to_string(), @@ -1330,6 +1367,8 @@ fn turn_completion_reconciles_started_items_from_turn_items() { command_actions: Vec::::new(), aggregated_output: Some("a.txt\n".to_string()), exit_code: Some(0), + started_at_ms: None, + completed_at_ms: None, duration_ms: Some(3), }], status: TurnStatus::Completed, diff --git a/codex-rs/tui/src/app/tests.rs b/codex-rs/tui/src/app/tests.rs index 9caa4a93a625..a5bd89337886 100644 --- a/codex-rs/tui/src/app/tests.rs +++ b/codex-rs/tui/src/app/tests.rs @@ -2602,6 +2602,9 @@ async fn inactive_thread_file_change_approval_recovers_buffered_changes() { diff: "hello\n".to_string(), }], status: codex_app_server_protocol::PatchApplyStatus::InProgress, + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, }), ) @@ -4705,6 +4708,9 @@ async fn replace_chat_widget_reseeds_collab_agent_metadata_for_replay() { model: None, reasoning_effort: None, agents_states: HashMap::new(), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, }, ), diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 021686679f19..6040bad57914 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -6041,7 +6041,9 @@ impl ChatWidget { } => {} item @ ThreadItem::FileChange { .. } => self.on_file_change_completed(item), item @ ThreadItem::McpToolCall { .. } => self.on_mcp_tool_call_completed(item), - ThreadItem::WebSearch { id, query, action } => { + ThreadItem::WebSearch { + id, query, action, .. + } => { self.on_web_search_begin(id.clone()); self.on_web_search_end( id, @@ -6082,6 +6084,9 @@ impl ChatWidget { model, reasoning_effort, agents_states, + started_at_ms, + completed_at_ms, + duration_ms, } => self.on_collab_agent_tool_call(ThreadItem::CollabAgentToolCall { id, tool, @@ -6092,6 +6097,9 @@ impl ChatWidget { model, reasoning_effort, agents_states, + started_at_ms, + completed_at_ms, + duration_ms, }), ThreadItem::DynamicToolCall { .. } => {} } @@ -6454,6 +6462,9 @@ impl ChatWidget { model, reasoning_effort, agents_states, + started_at_ms, + completed_at_ms, + duration_ms, } => self.on_collab_agent_tool_call(ThreadItem::CollabAgentToolCall { id, tool, @@ -6464,6 +6475,9 @@ impl ChatWidget { model, reasoning_effort, agents_states, + started_at_ms, + completed_at_ms, + duration_ms, }), ThreadItem::EnteredReviewMode { review, .. } => { if !from_replay { diff --git a/codex-rs/tui/src/chatwidget/interrupts.rs b/codex-rs/tui/src/chatwidget/interrupts.rs index 0b8dec2f2bf5..49f5c12b0791 100644 --- a/codex-rs/tui/src/chatwidget/interrupts.rs +++ b/codex-rs/tui/src/chatwidget/interrupts.rs @@ -182,6 +182,8 @@ mod tests { command_actions: Vec::new(), aggregated_output: None, exit_code: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, } } diff --git a/codex-rs/tui/src/chatwidget/tests/app_server.rs b/codex-rs/tui/src/chatwidget/tests/app_server.rs index 13b86e2afbfd..c2583b646540 100644 --- a/codex-rs/tui/src/chatwidget/tests/app_server.rs +++ b/codex-rs/tui/src/chatwidget/tests/app_server.rs @@ -26,6 +26,9 @@ async fn collab_spawn_end_shows_requested_model_and_effort() { model: Some("gpt-5".to_string()), reasoning_effort: Some(ReasoningEffortConfig::High), agents_states: HashMap::new(), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, }), /*replay_kind*/ None, @@ -50,6 +53,9 @@ async fn collab_spawn_end_shows_requested_model_and_effort() { message: None, }, )]), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, }), /*replay_kind*/ None, @@ -295,6 +301,9 @@ async fn live_app_server_file_change_item_started_preserves_changes() { diff: "hello\n".to_string(), }], status: AppServerPatchApplyStatus::InProgress, + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, }), /*replay_kind*/ None, @@ -332,6 +341,8 @@ async fn live_app_server_command_execution_strips_shell_wrapper() { }], aggregated_output: None, exit_code: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, }, }), @@ -353,6 +364,8 @@ async fn live_app_server_command_execution_strips_shell_wrapper() { }], aggregated_output: Some("Hello, world!\n".to_string()), exit_code: Some(0), + started_at_ms: None, + completed_at_ms: None, duration_ms: Some(5), }, }), @@ -409,6 +422,9 @@ async fn live_app_server_collab_wait_items_render_history() { model: None, reasoning_effort: None, agents_states: HashMap::new(), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, }), /*replay_kind*/ None, @@ -446,6 +462,9 @@ async fn live_app_server_collab_wait_items_render_history() { }, ), ]), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, }), /*replay_kind*/ None, @@ -481,6 +500,9 @@ async fn live_app_server_collab_spawn_completed_renders_requested_model_and_effo model: Some("gpt-5".to_string()), reasoning_effort: Some(ReasoningEffortConfig::High), agents_states: HashMap::new(), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, }), /*replay_kind*/ None, @@ -506,6 +528,9 @@ async fn live_app_server_collab_spawn_completed_renders_requested_model_and_effo message: None, }, )]), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, }), /*replay_kind*/ None, diff --git a/codex-rs/tui/src/chatwidget/tests/exec_flow.rs b/codex-rs/tui/src/chatwidget/tests/exec_flow.rs index c9789be14d45..7fc09e20cbd9 100644 --- a/codex-rs/tui/src/chatwidget/tests/exec_flow.rs +++ b/codex-rs/tui/src/chatwidget/tests/exec_flow.rs @@ -364,6 +364,8 @@ async fn exec_end_without_begin_uses_event_command() { command_actions, aggregated_output: Some("done".to_string()), exit_code: Some(0), + started_at_ms: None, + completed_at_ms: None, duration_ms: Some(5), }, ); diff --git a/codex-rs/tui/src/chatwidget/tests/helpers.rs b/codex-rs/tui/src/chatwidget/tests/helpers.rs index bb17359e7078..914a284190ed 100644 --- a/codex-rs/tui/src/chatwidget/tests/helpers.rs +++ b/codex-rs/tui/src/chatwidget/tests/helpers.rs @@ -758,6 +758,9 @@ pub(super) fn handle_patch_apply_begin( id: call_id.into(), changes: file_update_changes_from_tui(changes), status: AppServerPatchApplyStatus::InProgress, + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, }), /*replay_kind*/ None, @@ -779,6 +782,9 @@ pub(super) fn handle_patch_apply_end( id: call_id.into(), changes: file_update_changes_from_tui(changes), status, + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, }), /*replay_kind*/ None, @@ -819,6 +825,9 @@ pub(super) fn handle_image_generation_end( revised_prompt, result: String::new(), saved_path, + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, }), /*replay_kind*/ None, @@ -933,6 +942,8 @@ pub(super) fn begin_exec_with_source( command_actions, aggregated_output: None, exit_code: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, }; handle_exec_begin(chat, item.clone()); @@ -956,6 +967,8 @@ pub(super) fn begin_unified_exec_startup( command_actions: Vec::new(), aggregated_output: None, exit_code: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, }; handle_exec_begin(chat, item.clone()); @@ -1179,6 +1192,8 @@ pub(super) fn end_exec( command_actions, aggregated_output: (!aggregated.is_empty()).then_some(aggregated), exit_code: Some(exit_code), + started_at_ms: None, + completed_at_ms: None, duration_ms: Some(5), }, ); diff --git a/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs b/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs index bce32210d96d..64e37a0f5457 100644 --- a/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs +++ b/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs @@ -2693,6 +2693,8 @@ async fn chatwidget_exec_and_status_layout_vt100_snapshot() { command_actions: command_actions.clone(), aggregated_output: None, exit_code: None, + started_at_ms: None, + completed_at_ms: None, duration_ms: None, }, ); @@ -2708,6 +2710,8 @@ async fn chatwidget_exec_and_status_layout_vt100_snapshot() { command_actions, aggregated_output: None, exit_code: Some(0), + started_at_ms: None, + completed_at_ms: None, duration_ms: Some(16000), }, ); diff --git a/codex-rs/tui/src/multi_agents.rs b/codex-rs/tui/src/multi_agents.rs index 4fb5c8c265c4..d41a7fcd97d9 100644 --- a/codex-rs/tui/src/multi_agents.rs +++ b/codex-rs/tui/src/multi_agents.rs @@ -635,6 +635,9 @@ mod tests { robie_id.to_string(), agent_state(CollabAgentStatus::PendingInit, /*message*/ None), )]), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, /*cached_spawn_request*/ None, |thread_id| metadata_for(thread_id, robie_id, bob_id), @@ -655,6 +658,9 @@ mod tests { robie_id.to_string(), agent_state(CollabAgentStatus::Running, /*message*/ None), )]), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, /*cached_spawn_request*/ None, |thread_id| metadata_for(thread_id, robie_id, bob_id), @@ -672,6 +678,9 @@ mod tests { model: None, reasoning_effort: None, agents_states: HashMap::new(), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, /*cached_spawn_request*/ None, |thread_id| metadata_for(thread_id, robie_id, bob_id), @@ -698,6 +707,9 @@ mod tests { agent_state(CollabAgentStatus::Errored, Some("tool timeout")), ), ]), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, /*cached_spawn_request*/ None, |thread_id| metadata_for(thread_id, robie_id, bob_id), @@ -718,6 +730,9 @@ mod tests { robie_id.to_string(), agent_state(CollabAgentStatus::Completed, Some("39916800")), )]), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, /*cached_spawn_request*/ None, |thread_id| metadata_for(thread_id, robie_id, bob_id), @@ -802,6 +817,9 @@ mod tests { robie_id.to_string(), agent_state(CollabAgentStatus::PendingInit, /*message*/ None), )]), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, /*cached_spawn_request*/ None, |thread_id| metadata_for(thread_id, robie_id, ThreadId::new()), @@ -841,6 +859,9 @@ mod tests { robie_id.to_string(), agent_state(CollabAgentStatus::Interrupted, /*message*/ None), )]), + started_at_ms: None, + completed_at_ms: None, + duration_ms: None, }, /*cached_spawn_request*/ None, |thread_id| metadata_for(thread_id, robie_id, ThreadId::new()),