diff --git a/artifacts/github/bundles/openai-codex-pr-27007.json b/artifacts/github/bundles/openai-codex-pr-27007.json new file mode 100644 index 00000000..82472b68 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27007.json @@ -0,0 +1,475 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "jif-oai", + "committed_at": "2026-06-08T14:45:30Z", + "message": "feat: new tui mav2", + "sha": "6bba7d7f0403b1190af5f6def0d08da41946c516", + "url": "https://github.com/openai/codex/commit/6bba7d7f0403b1190af5f6def0d08da41946c516" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-08T16:26:11Z", + "message": "fix multi-agent activity tracking regressions", + "sha": "f5696a6365d98f2e083a5ca7681c49be08acfd7b", + "url": "https://github.com/openai/codex/commit/f5696a6365d98f2e083a5ca7681c49be08acfd7b" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-08T16:38:53Z", + "message": "fix argument comment lint", + "sha": "3b2e64054eb41fe13c3e71e4b914db352212746c", + "url": "https://github.com/openai/codex/commit/3b2e64054eb41fe13c3e71e4b914db352212746c" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-09T09:22:41Z", + "message": "fix comments", + "sha": "ba4061d420e62206e42e94d09bba3d05f6063ae6", + "url": "https://github.com/openai/codex/commit/ba4061d420e62206e42e94d09bba3d05f6063ae6" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-09T09:54:33Z", + "message": "fix", + "sha": "87f4b5f9cad0d5941821cc5dfbbf81f0d75e76e1", + "url": "https://github.com/openai/codex/commit/87f4b5f9cad0d5941821cc5dfbbf81f0d75e76e1" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "TUI", + "GENERATED", + "CODE", + "NOT", + "MODIFY", + "HAND", + "DEFAULT_ROLE_NAME", + "AGENT_STATUS_PREVIEW_LINES", + "AGENT_STATUS_PREVIEW_ITEMS", + "AGENT_STATUS_PREVIEW_GRAPHEMES", + "AGENT_STATUS_PREVIEW_INDENT", + "MAX", + "MCP", + "PAGE_SIZE" + ], + "files": [ + { + "additions": 9, + "deletions": 2, + "patch_excerpt": "@@ -108,6 +108,7 @@ use codex_app_server_protocol::ServerNotification;\n use codex_app_server_protocol::ServerRequest;\n use codex_app_server_protocol::ServerResponse;\n use codex_app_server_protocol::SessionSource as AppServerSessionSource;\n+use codex_app_server_protocol::SubAgentActivityKind;\n use codex_app_server_protocol::Thread;\n use codex_app_server_protocol::ThreadArchiveParams;\n use codex_app_server_protocol::ThreadArchiveResponse;\n@@ -3788,6 +3789,12 @@ async fn turn_event_counts_completed_tool_items() {\n reasoning_effort: None,\n agents_states: Default::default(),\n },\n+ ThreadItem::SubAgentActivity {\n+ id: \"sub-agent-activity-1\".to_string(),\n+ kind: SubAgentActivityKind::Interacted,\n+ agent_thread_id: \"thread-child\".to_string(),\n+ agent_path: \"/root/child\".to_string(),\n+ },\n ThreadItem...", + "path": "codex-rs/analytics/src/analytics_client_tests.rs", + "status": "modified" + }, + { + "additions": 16, + "deletions": 1, + "patch_excerpt": "@@ -360,7 +360,9 @@ impl TurnToolCounts {\n ThreadItem::FileChange { .. } => self.file_change += 1,\n ThreadItem::McpToolCall { .. } => self.mcp_tool_call += 1,\n ThreadItem::DynamicToolCall { .. } => self.dynamic_tool_call += 1,\n- ThreadItem::CollabAgentToolCall { .. } => self.subagent_tool_call += 1,\n+ ThreadItem::CollabAgentToolCall { .. } | ThreadItem::SubAgentActivity { .. } => {\n+ self.subagent_tool_call += 1;\n+ }\n ThreadItem::WebSearch { .. } => self.web_search += 1,\n ThreadItem::ImageGeneration { .. } => self.image_generation += 1,\n ThreadItem::UserMessage { .. }\n@@ -1091,6 +1093,18 @@ impl AnalyticsReducer {\n );\n }\n ServerNotification::ItemCompleted(notification) => {\n+ if matches!(notification.item, ThreadIte...", + "path": "codex-rs/analytics/src/reducer.rs", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -3177,6 +3177,14 @@\n ],\n \"type\": \"object\"\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"SubAgentSource\": {\n \"oneOf\": [\n {\n@@ -4107,6 +4115,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAgentActivity\"\n+ ],\n+ \"title\": \"SubAgentActivityThre...", + "path": "codex-rs/app-server-protocol/schema/json/ServerNotification.json", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -15580,6 +15580,14 @@\n ],\n \"type\": \"object\"\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"SubAgentSource\": {\n \"oneOf\": [\n {\n@@ -16892,6 +16900,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/v2/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAge...", + "path": "codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -13397,6 +13397,14 @@\n ],\n \"type\": \"object\"\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"SubAgentSource\": {\n \"oneOf\": [\n {\n@@ -14709,6 +14717,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAgentActivity\"\n+ ],\n+ \"title\": \"SubAgentActivity...", + "path": "codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -466,6 +466,14 @@\n \"minLength\": 1,\n \"type\": \"string\"\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"TextElement\": {\n \"properties\": {\n \"byteRange\": {\n@@ -989,6 +997,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAgentActivity\"\n+ ],\n+ \"title...", + "path": "codex-rs/app-server-protocol/schema/json/v2/ItemCompletedNotification.json", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -466,6 +466,14 @@\n \"minLength\": 1,\n \"type\": \"string\"\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"TextElement\": {\n \"properties\": {\n \"byteRange\": {\n@@ -989,6 +997,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAgentActivity\"\n+ ],\n+ \"title...", + "path": "codex-rs/app-server-protocol/schema/json/v2/ItemStartedNotification.json", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -610,6 +610,14 @@\n \"minLength\": 1,\n \"type\": \"string\"\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"TextElement\": {\n \"properties\": {\n \"byteRange\": {\n@@ -1133,6 +1141,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAgentActivity\"\n+ ],\n+ \"tit...", + "path": "codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -861,6 +861,14 @@\n }\n ]\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"SubAgentSource\": {\n \"oneOf\": [\n {\n@@ -1617,6 +1625,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAgentActivity\"\n+ ],\n+ \"title\": \"SubAgentActivityThreadItemType\",\n+ ...", + "path": "codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -676,6 +676,14 @@\n }\n ]\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"SubAgentSource\": {\n \"oneOf\": [\n {\n@@ -1432,6 +1440,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAgentActivity\"\n+ ],\n+ \"title\": \"SubAgentActivityThreadItemType\",\n+ ...", + "path": "codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -676,6 +676,14 @@\n }\n ]\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"SubAgentSource\": {\n \"oneOf\": [\n {\n@@ -1432,6 +1440,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAgentActivity\"\n+ ],\n+ \"title\": \"SubAgentActivityThreadItemType\",\n+ ...", + "path": "codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -676,6 +676,14 @@\n }\n ]\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"SubAgentSource\": {\n \"oneOf\": [\n {\n@@ -1432,6 +1440,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAgentActivity\"\n+ ],\n+ \"title\": \"SubAgentActivityThreadItemType\",\n+ ...", + "path": "codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -861,6 +861,14 @@\n }\n ]\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"SubAgentSource\": {\n \"oneOf\": [\n {\n@@ -1617,6 +1625,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAgentActivity\"\n+ ],\n+ \"title\": \"SubAgentActivityThreadItemType\",\n+ ...", + "path": "codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -676,6 +676,14 @@\n }\n ]\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"SubAgentSource\": {\n \"oneOf\": [\n {\n@@ -1432,6 +1440,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAgentActivity\"\n+ ],\n+ \"title\": \"SubAgentActivityThreadItemType\",\n+ ...", + "path": "codex-rs/app-server-protocol/schema/json/v2/ThreadRollbackResponse.json", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -861,6 +861,14 @@\n }\n ]\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"SubAgentSource\": {\n \"oneOf\": [\n {\n@@ -1617,6 +1625,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAgentActivity\"\n+ ],\n+ \"title\": \"SubAgentActivityThreadItemType\",\n+ ...", + "path": "codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -676,6 +676,14 @@\n }\n ]\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"SubAgentSource\": {\n \"oneOf\": [\n {\n@@ -1432,6 +1440,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAgentActivity\"\n+ ],\n+ \"title\": \"SubAgentActivityThreadItemType\",\n+ ...", + "path": "codex-rs/app-server-protocol/schema/json/v2/ThreadStartedNotification.json", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -676,6 +676,14 @@\n }\n ]\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"SubAgentSource\": {\n \"oneOf\": [\n {\n@@ -1432,6 +1440,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAgentActivity\"\n+ ],\n+ \"title\": \"SubAgentActivityThreadItemType\",\n+ ...", + "path": "codex-rs/app-server-protocol/schema/json/v2/ThreadUnarchiveResponse.json", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -610,6 +610,14 @@\n \"minLength\": 1,\n \"type\": \"string\"\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"TextElement\": {\n \"properties\": {\n \"byteRange\": {\n@@ -1133,6 +1141,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAgentActivity\"\n+ ],\n+ \"tit...", + "path": "codex-rs/app-server-protocol/schema/json/v2/TurnCompletedNotification.json", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -610,6 +610,14 @@\n \"minLength\": 1,\n \"type\": \"string\"\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"TextElement\": {\n \"properties\": {\n \"byteRange\": {\n@@ -1133,6 +1141,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAgentActivity\"\n+ ],\n+ \"tit...", + "path": "codex-rs/app-server-protocol/schema/json/v2/TurnStartResponse.json", + "status": "modified" + }, + { + "additions": 40, + "deletions": 0, + "patch_excerpt": "@@ -610,6 +610,14 @@\n \"minLength\": 1,\n \"type\": \"string\"\n },\n+ \"SubAgentActivityKind\": {\n+ \"enum\": [\n+ \"started\",\n+ \"interacted\",\n+ \"interrupted\"\n+ ],\n+ \"type\": \"string\"\n+ },\n \"TextElement\": {\n \"properties\": {\n \"byteRange\": {\n@@ -1133,6 +1141,38 @@\n \"title\": \"CollabAgentToolCallThreadItem\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"agentPath\": {\n+ \"type\": \"string\"\n+ },\n+ \"agentThreadId\": {\n+ \"type\": \"string\"\n+ },\n+ \"id\": {\n+ \"type\": \"string\"\n+ },\n+ \"kind\": {\n+ \"$ref\": \"#/definitions/SubAgentActivityKind\"\n+ },\n+ \"type\": {\n+ \"enum\": [\n+ \"subAgentActivity\"\n+ ],\n+ \"tit...", + "path": "codex-rs/app-server-protocol/schema/json/v2/TurnStartedNotification.json", + "status": "modified" + }, + { + "additions": 5, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,5 @@\n+// GENERATED CODE! DO NOT MODIFY BY HAND!\n+\n+// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.\n+\n+export type SubAgentActivityKind = \"started\" | \"interacted\" | \"interrupted\";", + "path": "codex-rs/app-server-protocol/schema/typescript/v2/SubAgentActivityKind.ts", + "status": "added" + }, + { + "additions": 2, + "deletions": 1, + "patch_excerpt": "@@ -20,6 +20,7 @@ import type { McpToolCallResult } from \"./McpToolCallResult\";\n import type { McpToolCallStatus } from \"./McpToolCallStatus\";\n import type { MemoryCitation } from \"./MemoryCitation\";\n import type { PatchApplyStatus } from \"./PatchApplyStatus\";\n+import type { SubAgentActivityKind } from \"./SubAgentActivityKind\";\n import type { UserInput } from \"./UserInput\";\n import type { WebSearchAction } from \"./WebSearchAction\";\n \n@@ -98,4 +99,4 @@ reasoningEffort: ReasoningEffort | null,\n /**\n * Last known status of the target agents, when available.\n */\n-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?: AbsolutePat...", + "path": "codex-rs/app-server-protocol/schema/typescript/v2/ThreadItem.ts", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -356,6 +356,7 @@ export type { SkillsListParams } from \"./SkillsListParams\";\n export type { SkillsListResponse } from \"./SkillsListResponse\";\n export type { SortDirection } from \"./SortDirection\";\n export type { SpendControlLimitSnapshot } from \"./SpendControlLimitSnapshot\";\n+export type { SubAgentActivityKind } from \"./SubAgentActivityKind\";\n export type { SubagentMigration } from \"./SubagentMigration\";\n export type { TerminalInteractionNotification } from \"./TerminalInteractionNotification\";\n export type { TextElement } from \"./TextElement\";", + "path": "codex-rs/app-server-protocol/schema/typescript/v2/index.ts", + "status": "modified" + }, + { + "additions": 14, + "deletions": 0, + "patch_excerpt": "@@ -178,6 +178,20 @@ pub fn item_event_to_server_notification(\n completed_at_ms: end_event.completed_at_ms,\n })\n }\n+ EventMsg::SubAgentActivity(activity) => {\n+ let item = ThreadItem::SubAgentActivity {\n+ id: activity.event_id,\n+ kind: activity.kind.into(),\n+ agent_thread_id: activity.agent_thread_id.to_string(),\n+ agent_path: String::from(activity.agent_path),\n+ };\n+ ServerNotification::ItemCompleted(ItemCompletedNotification {\n+ thread_id,\n+ turn_id,\n+ item,\n+ completed_at_ms: activity.occurred_at_ms,\n+ })\n+ }\n EventMsg::CollabWaitingBegin(begin_event) => {\n let receiver_thread_ids = begin_event\n .receiver_thread_ids", + "path": "codex-rs/app-server-protocol/src/protocol/event_mapping.rs", + "status": "modified" + }, + { + "additions": 13, + "deletions": 0, + "patch_excerpt": "@@ -203,6 +203,7 @@ impl ThreadHistoryBuilder {\n EventMsg::CollabAgentInteractionEnd(payload) => {\n self.handle_collab_agent_interaction_end(payload)\n }\n+ EventMsg::SubAgentActivity(payload) => self.handle_sub_agent_activity(payload),\n EventMsg::CollabWaitingBegin(payload) => self.handle_collab_waiting_begin(payload),\n EventMsg::CollabWaitingEnd(payload) => self.handle_collab_waiting_end(payload),\n EventMsg::CollabCloseBegin(payload) => self.handle_collab_close_begin(payload),\n@@ -702,6 +703,18 @@ impl ThreadHistoryBuilder {\n });\n }\n \n+ fn handle_sub_agent_activity(\n+ &mut self,\n+ payload: &codex_protocol::protocol::SubAgentActivityEvent,\n+ ) {\n+ self.upsert_item_in_current_turn(ThreadItem::SubAgentActivity {\n+ id: payload.event_id.clone(),\n+ ...", + "path": "codex-rs/app-server-protocol/src/protocol/thread_history.rs", + "status": "modified" + }, + { + "additions": 29, + "deletions": 0, + "patch_excerpt": "@@ -29,6 +29,7 @@ use codex_protocol::protocol::GuardianRiskLevel as CoreGuardianRiskLevel;\n use codex_protocol::protocol::GuardianUserAuthorization as CoreGuardianUserAuthorization;\n use codex_protocol::protocol::PatchApplyStatus as CorePatchApplyStatus;\n use codex_protocol::protocol::ReviewDecision as CoreReviewDecision;\n+use codex_protocol::protocol::SubAgentActivityKind as CoreSubAgentActivityKind;\n use codex_utils_absolute_path::AbsolutePathBuf;\n use schemars::JsonSchema;\n use serde::Deserialize;\n@@ -336,6 +337,14 @@ pub enum ThreadItem {\n },\n #[serde(rename_all = \"camelCase\")]\n #[ts(rename_all = \"camelCase\")]\n+ SubAgentActivity {\n+ id: String,\n+ kind: SubAgentActivityKind,\n+ agent_thread_id: String,\n+ agent_path: String,\n+ },\n+ #[serde(rename_all = \"camelCase\")]\n+ #[ts(rename_all = \"camelCase\")]\n WebSearch {\n id: Strin...", + "path": "codex-rs/app-server-protocol/src/protocol/v2/item.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -844,6 +844,7 @@ pub(crate) async fn apply_bespoke_event_handling(\n | EventMsg::CollabAgentSpawnEnd(_)\n | EventMsg::CollabAgentInteractionBegin(_)\n | EventMsg::CollabAgentInteractionEnd(_)\n+ | EventMsg::SubAgentActivity(_)\n | EventMsg::CollabWaitingBegin(_)\n | EventMsg::CollabWaitingEnd(_)\n | EventMsg::CollabCloseBegin(_)", + "path": "codex-rs/app-server/src/bespoke_event_handling.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 1, + "patch_excerpt": "@@ -1431,7 +1431,8 @@ pub(super) fn realtime_text_for_event(msg: &EventMsg) -> Option {\n | EventMsg::CollabCloseBegin(_)\n | EventMsg::CollabCloseEnd(_)\n | EventMsg::CollabResumeBegin(_)\n- | EventMsg::CollabResumeEnd(_) => None,\n+ | EventMsg::CollabResumeEnd(_)\n+ | EventMsg::SubAgentActivity(_) => None,\n }\n }", + "path": "codex-rs/core/src/session/turn.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -14,15 +14,11 @@ use crate::tools::registry::ToolExecutor;\n use codex_protocol::AgentPath;\n use codex_protocol::models::ResponseInputItem;\n use codex_protocol::openai_models::ReasoningEffort;\n-use codex_protocol::protocol::CollabAgentInteractionBeginEvent;\n-use codex_protocol::protocol::CollabAgentInteractionEndEvent;\n-use codex_protocol::protocol::CollabAgentSpawnBeginEvent;\n-use codex_protocol::protocol::CollabAgentSpawnEndEvent;\n-use codex_protocol::protocol::CollabCloseBeginEvent;\n-use codex_protocol::protocol::CollabCloseEndEvent;\n use codex_protocol::protocol::CollabWaitingBeginEvent;\n use codex_protocol::protocol::CollabWaitingEndEvent;\n use codex_protocol::protocol::InterAgentCommunication;\n+use codex_protocol::protocol::SubAgentActivityEvent;\n+use codex_protocol::protocol::SubAgentActivityKind;\n use codex_protocol::user_input::UserInput;\n use codex_tools::ToolName;\n use serde...", + "path": "codex-rs/core/src/tools/handlers/multi_agents_v2.rs", + "status": "modified" + }, + { + "additions": 10, + "deletions": 21, + "patch_excerpt": "@@ -59,18 +59,9 @@ async fn handle_interrupt_agent(\n .to_string(),\n ));\n }\n- session\n- .send_event(\n- &turn,\n- CollabCloseBeginEvent {\n- call_id: call_id.clone(),\n- started_at_ms: now_unix_timestamp_ms(),\n- sender_thread_id: session.thread_id,\n- receiver_thread_id: agent_id,\n- }\n- .into(),\n- )\n- .await;\n+ let receiver_agent_path = receiver_agent.agent_path.clone().ok_or_else(|| {\n+ FunctionCallError::RespondToModel(\"target agent is missing an agent_path\".to_string())\n+ })?;\n let status = session.services.agent_control.get_status(agent_id).await;\n let result = match session\n .services\n@@ -81,22 +72,20 @@ async fn handle_interrupt_agent(\n Ok(_) | Err(CodexErr::ThreadNotFound(_)) | Err(CodexErr::Inter...", + "path": "codex-rs/core/src/tools/handlers/multi_agents_v2/interrupt_agent.rs", + "status": "modified" + }, + { + "additions": 9, + "deletions": 30, + "patch_excerpt": "@@ -69,7 +69,6 @@ pub(crate) async fn handle_message_string_tool(\n call_id,\n ..\n } = invocation;\n- let prompt = String::new();\n let receiver_thread_id = resolve_agent_target(&session, &turn, &target).await?;\n let receiver_agent = session\n .services\n@@ -96,52 +95,32 @@ pub(crate) async fn handle_message_string_tool(\n .ensure_v2_agent_loaded(resume_config, receiver_thread_id)\n .await\n .map_err(|err| collab_agent_error(receiver_thread_id, err))?;\n- session\n- .send_event(\n- &turn,\n- CollabAgentInteractionBeginEvent {\n- call_id: call_id.clone(),\n- started_at_ms: now_unix_timestamp_ms(),\n- sender_thread_id: session.thread_id,\n- receiver_thread_id,\n- prompt: prompt.clone(),\n- }\n- .into(),\n- )\n- ...", + "path": "codex-rs/core/src/tools/handlers/multi_agents_v2/message_tool.rs", + "status": "modified" + }, + { + "additions": 28, + "deletions": 79, + "patch_excerpt": "@@ -61,24 +61,8 @@ async fn handle_spawn_agent(\n \n let message = args.message.clone();\n let initial_operation = parse_collab_input(Some(args.message), /*items*/ None)?;\n- let prompt = String::new();\n-\n let session_source = turn.session_source.clone();\n let child_depth = next_thread_spawn_depth(&session_source);\n- session\n- .send_event(\n- &turn,\n- CollabAgentSpawnBeginEvent {\n- call_id: call_id.clone(),\n- started_at_ms: now_unix_timestamp_ms(),\n- sender_thread_id: session.thread_id,\n- prompt: prompt.clone(),\n- model: args.model.clone().unwrap_or_default(),\n- reasoning_effort: args.reasoning_effort.clone().unwrap_or_default(),\n- }\n- .into(),\n- )\n- .await;\n let mut config =\n build_agent_spawn_config(&sess...", + "path": "codex-rs/core/src/tools/handlers/multi_agents_v2/spawn.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -384,6 +384,7 @@ async fn run_codex_tool_session_inner(\n | EventMsg::CollabCloseEnd(_)\n | EventMsg::CollabResumeBegin(_)\n | EventMsg::CollabResumeEnd(_)\n+ | EventMsg::SubAgentActivity(_)\n | EventMsg::RealtimeConversationStarted(_)\n | EventMsg::RealtimeConversationSdp(_)\n | EventMsg::RealtimeConversationRealtime(_)", + "path": "codex-rs/mcp-server/src/codex_tool_runner.rs", + "status": "modified" + }, + { + "additions": 30, + "deletions": 0, + "patch_excerpt": "@@ -1324,6 +1324,9 @@ pub enum EventMsg {\n CollabResumeBegin(CollabResumeBeginEvent),\n /// Collab interaction: resume end.\n CollabResumeEnd(CollabResumeEndEvent),\n+\n+ /// Path-based v2 sub-agent activity.\n+ SubAgentActivity(SubAgentActivityEvent),\n }\n \n #[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, JsonSchema, TS, EnumIter)]\n@@ -1543,6 +1546,12 @@ impl From for EventMsg {\n }\n }\n \n+impl From for EventMsg {\n+ fn from(event: SubAgentActivityEvent) -> Self {\n+ EventMsg::SubAgentActivity(event)\n+ }\n+}\n+\n /// Agent lifecycle status, derived from emitted events.\n #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, JsonSchema, TS, Default)]\n #[serde(rename_all = \"snake_case\")]\n@@ -3865,6 +3874,27 @@ pub struct CollabAgentInteractionEndEvent {\n pub status: AgentStatus,\n }\n \n+#[deriv...", + "path": "codex-rs/protocol/src/protocol.rs", + "status": "modified" + }, + { + "additions": 14, + "deletions": 1, + "patch_excerpt": "@@ -21,6 +21,7 @@ use codex_protocol::protocol::McpToolCallEndEvent;\n use codex_protocol::protocol::PatchApplyBeginEvent;\n use codex_protocol::protocol::PatchApplyEndEvent;\n use codex_protocol::protocol::PatchApplyStatus;\n+use codex_protocol::protocol::SubAgentActivityEvent;\n use codex_protocol::protocol::TurnAbortReason;\n use serde::Serialize;\n \n@@ -110,6 +111,7 @@ pub(crate) enum ToolRuntimePayload<'a> {\n CollabWaitingEnd(&'a codex_protocol::protocol::CollabWaitingEndEvent),\n CollabCloseBegin(&'a codex_protocol::protocol::CollabCloseBeginEvent),\n CollabCloseEnd(&'a codex_protocol::protocol::CollabCloseEndEvent),\n+ SubAgentActivity(&'a SubAgentActivityEvent),\n }\n \n impl Serialize for ToolRuntimePayload<'_> {\n@@ -132,6 +134,7 @@ impl Serialize for ToolRuntimePayload<'_> {\n ToolRuntimePayload::CollabWaitingEnd(event) => event.serialize(serializer),\n ...", + "path": "codex-rs/rollout-trace/src/protocol_event.rs", + "status": "modified" + }, + { + "additions": 46, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,46 @@\n+use codex_protocol::AgentPath;\n+use codex_protocol::ThreadId;\n+use codex_protocol::protocol::EventMsg;\n+use codex_protocol::protocol::SubAgentActivityEvent;\n+use codex_protocol::protocol::SubAgentActivityKind;\n+use pretty_assertions::assert_eq;\n+use serde_json::json;\n+\n+use super::ToolRuntimeTraceEvent;\n+use super::tool_runtime_trace_event;\n+use crate::ExecutionStatus;\n+\n+#[test]\n+fn sub_agent_activity_is_a_terminal_tool_runtime_event() -> anyhow::Result<()> {\n+ let agent_thread_id = ThreadId::new();\n+ let event = EventMsg::SubAgentActivity(SubAgentActivityEvent {\n+ event_id: \"call-spawn\".to_string(),\n+ occurred_at_ms: 1234,\n+ agent_thread_id,\n+ agent_path: AgentPath::try_from(\"/root/reviewer\").map_err(anyhow::Error::msg)?,\n+ kind: SubAgentActivityKind::Started,\n+ });\n+\n+ let Some(ToolRuntimeTraceEvent::Ended {\n+ too...", + "path": "codex-rs/rollout-trace/src/protocol_event_tests.rs", + "status": "added" + }, + { + "additions": 139, + "deletions": 5, + "patch_excerpt": "@@ -7,6 +7,10 @@ use codex_protocol::protocol::CollabAgentSpawnEndEvent;\n use codex_protocol::protocol::CollabCloseBeginEvent;\n use codex_protocol::protocol::CollabCloseEndEvent;\n use codex_protocol::protocol::InterAgentCommunication;\n+use codex_protocol::protocol::SubAgentActivityEvent;\n+use codex_protocol::protocol::SubAgentActivityKind;\n+use serde::Deserialize;\n+use serde_json::Value;\n \n use super::super::TraceReducer;\n use crate::model::ConversationItem;\n@@ -52,6 +56,11 @@ pub(in crate::reducer) struct ObservedAgentResultEdge {\n pub(in crate::reducer) carried_payload: Option,\n }\n \n+#[derive(Deserialize)]\n+struct AgentMessageInvocationArgs {\n+ message: String,\n+}\n+\n /// Builds the stable edge id for the spawn relationship between two threads.\n pub(in crate::reducer) fn spawn_edge_id(parent_thread_id: &str, child_thread_id: &str) -> String {\n format!(\"edge:spa...", + "path": "codex-rs/rollout-trace/src/reducer/tool/agents.rs", + "status": "modified" + }, + { + "additions": 258, + "deletions": 0, + "patch_excerpt": "@@ -194,6 +194,92 @@ fn spawn_runtime_payload_falls_back_to_child_thread_without_delivery_item() -> a\n Ok(())\n }\n \n+#[test]\n+fn sub_agent_started_activity_creates_spawn_edge() -> anyhow::Result<()> {\n+ let temp = TempDir::new()?;\n+ let writer = create_started_agent_writer(&temp)?;\n+ start_agent_turn(&writer, \"turn-1\")?;\n+ let child_thread_id = \"019d0000-0000-7000-8000-000000000002\";\n+ let invocation_payload = writer.write_json_payload(\n+ RawPayloadKind::ToolInvocation,\n+ &json!({\n+ \"tool_name\": \"spawn_agent\",\n+ \"payload\": {\n+ \"type\": \"function\",\n+ \"arguments\": \"{\\\"message\\\":\\\"review this\\\",\\\"task_name\\\":\\\"reviewer\\\"}\"\n+ }\n+ }),\n+ )?;\n+ writer.append_with_context(\n+ trace_context_for_agent(\"turn-1\"),\n+ RawTraceEventPayload::ToolCallStarted {\n+ tool_call_id:...", + "path": "codex-rs/rollout-trace/src/reducer/tool/agents_tests.rs", + "status": "modified" + }, + { + "additions": 14, + "deletions": 1, + "patch_excerpt": "@@ -269,7 +269,7 @@ fn dispatched_tool_kind(tool_name: &str, _payload: &ToolDispatchPayload) -> Tool\n \"send_message\" => ToolCallKind::SendMessage,\n \"followup_task\" | \"assign_task\" => ToolCallKind::AssignAgentTask,\n \"wait_agent\" => ToolCallKind::WaitAgent,\n- \"close_agent\" => ToolCallKind::CloseAgent,\n+ \"close_agent\" | \"interrupt_agent\" => ToolCallKind::CloseAgent,\n other => ToolCallKind::Other {\n name: other.to_string(),\n },\n@@ -425,6 +425,19 @@ mod tests {\n )));\n }\n \n+ #[test]\n+ fn classifies_interrupt_agent_as_close_agent() {\n+ assert_eq!(\n+ dispatched_tool_kind(\n+ \"interrupt_agent\",\n+ &ToolDispatchPayload::Function {\n+ arguments: r#\"{\"target\":\"/root/child\"}\"#.to_string(),\n+ },\n+ ),\n+ ToolCallKind::Clo...", + "path": "codex-rs/rollout-trace/src/tool_dispatch.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 1, + "patch_excerpt": "@@ -91,7 +91,8 @@ pub fn should_persist_event_msg(ev: &EventMsg) -> bool {\n | EventMsg::TurnStarted(_)\n | EventMsg::TurnComplete(_)\n | EventMsg::WebSearchEnd(_)\n- | EventMsg::ImageGenerationEnd(_) => true,\n+ | EventMsg::ImageGenerationEnd(_)\n+ | EventMsg::SubAgentActivity(_) => true,\n EventMsg::ItemCompleted(event) => {\n // Plan items are derived from streaming tags and are not part of the\n // raw ResponseItem history, so we persist their completion to replay", + "path": "codex-rs/rollout/src/policy.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -327,6 +327,7 @@ async fn run_turn(thread: &CodexThread, thread_id: &str, prompt: String) -> anyh\n | EventMsg::CollabCloseEnd(_)\n | EventMsg::CollabResumeBegin(_)\n | EventMsg::CollabResumeEnd(_)\n+ | EventMsg::SubAgentActivity(_)\n | EventMsg::AgentMessageContentDelta(_)\n | EventMsg::PlanDelta(_)\n | EventMsg::ReasoningContentDelta(_)", + "path": "codex-rs/thread-manager-sample/src/main.rs", + "status": "modified" + }, + { + "additions": 16, + "deletions": 0, + "patch_excerpt": "@@ -62,6 +62,7 @@ use crate::multi_agents::agent_picker_status_dot_spans;\n use crate::multi_agents::format_agent_picker_item_name;\n use crate::multi_agents::next_agent_shortcut_matches;\n use crate::multi_agents::previous_agent_shortcut_matches;\n+use crate::multi_agents::sub_agent_activity_display;\n use crate::pager_overlay::Overlay;\n use crate::render::highlight::highlight_bash_to_lines;\n use crate::render::renderable::Renderable;\n@@ -197,6 +198,7 @@ use toml::Value as TomlValue;\n use uuid::Uuid;\n mod agent_message_consolidation;\n mod agent_navigation;\n+mod agent_status_feed;\n mod app_server_event_targets;\n mod app_server_events;\n pub(crate) mod app_server_requests;\n@@ -266,6 +268,20 @@ fn collab_receiver_thread_ids(notification: &ServerNotification) -> Option<&[Str\n }\n }\n \n+fn sub_agent_activity_item(notification: &ServerNotification) -> Option<&ThreadItem> {\n+ match notification...", + "path": "codex-rs/tui/src/app.rs", + "status": "modified" + }, + { + "additions": 66, + "deletions": 0, + "patch_excerpt": "@@ -19,6 +19,7 @@\n //! updated or marked closed.\n \n use crate::multi_agents::AgentPickerThreadEntry;\n+use crate::multi_agents::SubAgentActivityDisplay;\n use crate::multi_agents::format_agent_picker_item_name;\n use crate::multi_agents::next_agent_shortcut;\n use crate::multi_agents::previous_agent_shortcut;\n@@ -86,16 +87,56 @@ impl AgentNavigationState {\n if !self.threads.contains_key(&thread_id) {\n self.order.push(thread_id);\n }\n+ let (previous_agent_path, previous_is_running) = self\n+ .threads\n+ .get(&thread_id)\n+ .map(|entry| (entry.agent_path.clone(), entry.is_running))\n+ .unwrap_or((None, false));\n self.threads.insert(\n thread_id,\n AgentPickerThreadEntry {\n agent_nickname,\n agent_role,\n+ agent_path: previous_agent_path,\n+ ...", + "path": "codex-rs/tui/src/app/agent_navigation.rs", + "status": "modified" + }, + { + "additions": 209, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,209 @@\n+//! Bounded, best-effort previews for the v2 `/agent` status output.\n+\n+use super::ThreadBufferedEvent;\n+use super::ThreadEventStore;\n+use crate::history_cell::HistoryCell;\n+use crate::history_cell::plain_lines;\n+use crate::text_formatting::truncate_text;\n+use codex_app_server_protocol::CollabAgentTool;\n+use codex_app_server_protocol::ServerNotification;\n+use codex_app_server_protocol::SubAgentActivityKind;\n+use codex_app_server_protocol::ThreadItem;\n+use ratatui::style::Stylize;\n+use ratatui::text::Line;\n+use std::collections::HashSet;\n+\n+const AGENT_STATUS_PREVIEW_LINES: usize = 3;\n+const AGENT_STATUS_PREVIEW_ITEMS: usize = 6;\n+const AGENT_STATUS_PREVIEW_GRAPHEMES: usize = 240;\n+const AGENT_STATUS_PREVIEW_INDENT: u16 = 4;\n+\n+#[derive(Debug)]\n+pub(super) struct AgentStatusHistoryCell {\n+ entries: Vec,\n+}\n+\n+impl AgentStatusHistoryCell {\n+ ...", + "path": "codex-rs/tui/src/app/agent_status_feed.rs", + "status": "added" + }, + { + "additions": 109, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,109 @@\n+use super::*;\n+use codex_app_server_protocol::CommandExecutionSource;\n+use codex_app_server_protocol::CommandExecutionStatus;\n+use codex_app_server_protocol::ItemCompletedNotification;\n+use codex_utils_absolute_path::AbsolutePathBuf;\n+\n+#[test]\n+fn agent_status_uses_bounded_buffered_activity() {\n+ let mut store = ThreadEventStore::new(/*capacity*/ 8);\n+ store.push_notification(ServerNotification::ItemCompleted(\n+ ItemCompletedNotification {\n+ item: ThreadItem::CommandExecution {\n+ id: \"command-1\".to_string(),\n+ command: \"cargo test -p codex-tui\".to_string(),\n+ cwd: AbsolutePathBuf::try_from(\"/workspace\").expect(\"absolute path\"),\n+ process_id: None,\n+ source: CommandExecutionSource::Agent,\n+ status: CommandExecutionStatus::Completed,\n+ command...", + "path": "codex-rs/tui/src/app/agent_status_feed_tests.rs", + "status": "added" + }, + { + "additions": 22, + "deletions": 10, + "patch_excerpt": "@@ -14,8 +14,10 @@\n //! `SessionSource::SubAgent(ThreadSpawn { parent_thread_id, .. })` edges until no new children are\n //! found. The primary thread itself is never included in the output.\n \n+use codex_app_server_protocol::SessionSource;\n use codex_app_server_protocol::Thread;\n use codex_protocol::ThreadId;\n+use codex_protocol::protocol::SubAgentSource;\n use std::collections::HashMap;\n use std::collections::HashSet;\n \n@@ -26,6 +28,7 @@ pub(crate) struct LoadedSubagentThread {\n pub(crate) thread_id: ThreadId,\n pub(crate) agent_nickname: Option,\n pub(crate) agent_role: Option,\n+ pub(crate) agent_path: Option,\n }\n \n /// Walks the spawn tree rooted at `primary_thread_id` and returns every descendant subagent.\n@@ -84,23 +87,30 @@ pub(crate) fn find_loaded_subagent_threads_for_primary(\n thread_id,\n agent_nickname:...", + "path": "codex-rs/tui/src/app/loaded_threads.rs", + "status": "modified" + }, + { + "additions": 78, + "deletions": 9, + "patch_excerpt": "@@ -8,6 +8,60 @@ use super::*;\n \n impl App {\n pub(super) async fn open_agent_picker(&mut self, app_server: &mut AppServerSession) {\n+ self.backfill_loaded_subagent_threads(app_server).await;\n+ // V2 subagents are identified by canonical paths observed from activity events or loaded\n+ // thread metadata. Prefer local buffered turn state for liveness, and fall back to\n+ // thread/read only when no local event channel exists.\n+ let path_backed_thread_ids: Vec<_> = self\n+ .agent_navigation\n+ .ordered_path_backed_subagent_threads(self.primary_thread_id)\n+ .into_iter()\n+ .map(|(thread_id, _)| thread_id)\n+ .collect();\n+ for thread_id in path_backed_thread_ids {\n+ if let Some(channel) = self.thread_event_channels.get(&thread_id)\n+ && channel.attachment() == ThreadEventAt...", + "path": "codex-rs/tui/src/app/session_lifecycle.rs", + "status": "modified" + }, + { + "additions": 93, + "deletions": 0, + "patch_excerpt": "@@ -22,6 +22,7 @@ use crate::history_cell::PlainHistoryCell;\n use crate::history_cell::UserHistoryCell;\n use crate::history_cell::new_session_info;\n use crate::multi_agents::AgentPickerThreadEntry;\n+use crate::multi_agents::SubAgentActivityDisplay;\n use assert_matches::assert_matches;\n \n use crate::app_command::AppCommand as Op;\n@@ -1164,6 +1165,8 @@ async fn collab_receiver_notification_caches_thread_without_app_server_read() {\n Some(&AgentPickerThreadEntry {\n agent_nickname: None,\n agent_role: None,\n+ agent_path: None,\n+ is_running: false,\n is_closed: false,\n })\n );\n@@ -1223,6 +1226,8 @@ async fn open_agent_picker_keeps_missing_threads_for_replay() -> Result<()> {\n Some(&AgentPickerThreadEntry {\n agent_nickname: None,\n agent_role: None,\n+ agent_path: None,\n+ ...", + "path": "codex-rs/tui/src/app/tests.rs", + "status": "modified" + }, + { + "additions": 17, + "deletions": 0, + "patch_excerpt": "@@ -31,6 +31,12 @@ pub(super) struct FeedbackThreadEvent {\n pub(super) result: Result,\n }\n \n+#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n+pub(super) enum ThreadEventAttachment {\n+ Live,\n+ ReplayOnly,\n+}\n+\n #[derive(Debug)]\n pub(super) struct ThreadEventStore {\n pub(super) session: Option,\n@@ -285,6 +291,7 @@ pub(super) struct ThreadEventChannel {\n pub(super) sender: mpsc::Sender,\n pub(super) receiver: Option>,\n pub(super) store: Arc>,\n+ attachment: ThreadEventAttachment,\n }\n \n impl ThreadEventChannel {\n@@ -294,9 +301,18 @@ impl ThreadEventChannel {\n sender,\n receiver: Some(receiver),\n store: Arc::new(Mutex::new(ThreadEventStore::new(capacity))),\n+ attachment: ThreadEventAttachment::Live,\n ...", + "path": "codex-rs/tui/src/app/thread_events.rs", + "status": "modified" + }, + { + "additions": 8, + "deletions": 0, + "patch_excerpt": "@@ -896,6 +896,14 @@ impl App {\n &mut self,\n notification: &ServerNotification,\n ) {\n+ if let Some(activity) =\n+ sub_agent_activity_item(notification).and_then(sub_agent_activity_display)\n+ {\n+ self.agent_navigation.record_sub_agent_activity(activity);\n+ self.sync_active_agent_label();\n+ return;\n+ }\n+\n let Some(receiver_thread_ids) = collab_receiver_thread_ids(notification) else {\n return;\n };", + "path": "codex-rs/tui/src/app/thread_routing.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -327,6 +327,7 @@ impl ChatWidget {\n reasoning_effort,\n agents_states,\n }),\n+ item @ ThreadItem::SubAgentActivity { .. } => self.on_sub_agent_activity(item),\n ThreadItem::EnteredReviewMode { review, .. } if !from_replay => {\n self.enter_review_mode_with_hint(review, /*from_replay*/ false);\n }", + "path": "codex-rs/tui/src/chatwidget/protocol.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -190,6 +190,7 @@ impl ChatWidget {\n reasoning_effort,\n agents_states,\n }),\n+ item @ ThreadItem::SubAgentActivity { .. } => self.on_sub_agent_activity(item),\n ThreadItem::DynamicToolCall { .. } => {}\n }", + "path": "codex-rs/tui/src/chatwidget/replay.rs", + "status": "modified" + }, + { + "additions": 7, + "deletions": 0, + "patch_excerpt": "@@ -144,6 +144,13 @@ impl ChatWidget {\n }\n }\n \n+ pub(super) fn on_sub_agent_activity(&mut self, item: ThreadItem) {\n+ self.record_visible_turn_activity();\n+ if let Some(cell) = multi_agents::sub_agent_activity_history_cell(&item) {\n+ self.on_collab_event(cell);\n+ }\n+ }\n+\n pub(crate) fn handle_file_change_completed_now(&mut self, item: ThreadItem) {\n let ThreadItem::FileChange { status, .. } = item else {\n return;", + "path": "codex-rs/tui/src/chatwidget/tool_lifecycle.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -191,6 +191,7 @@ mod terminal_title;\n mod terminal_visualization_instructions;\n mod text_formatting;\n mod theme_picker;\n+mod thread_transcript;\n mod token_usage;\n mod tooltips;\n mod transcript_reflow;", + "path": "codex-rs/tui/src/lib.rs", + "status": "modified" + }, + { + "additions": 62, + "deletions": 0, + "patch_excerpt": "@@ -11,6 +11,7 @@ use codex_app_server_protocol::CollabAgentState;\n use codex_app_server_protocol::CollabAgentStatus;\n use codex_app_server_protocol::CollabAgentTool;\n use codex_app_server_protocol::CollabAgentToolCallStatus;\n+use codex_app_server_protocol::SubAgentActivityKind;\n use codex_app_server_protocol::ThreadItem;\n use codex_protocol::ThreadId;\n use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;\n@@ -35,10 +36,21 @@ pub(crate) struct AgentPickerThreadEntry {\n pub(crate) agent_nickname: Option,\n /// Agent type shown in brackets when present, for example `worker`.\n pub(crate) agent_role: Option,\n+ /// Canonical v2 agent path, when the thread was observed through v2 activity.\n+ pub(crate) agent_path: Option,\n+ /// Whether the latest liveness refresh says the agent thread is actively working.\n+ pub(crate) is_run...", + "path": "codex-rs/tui/src/multi_agents.rs", + "status": "modified" + }, + { + "additions": 6, + "deletions": 8, + "patch_excerpt": "@@ -4,8 +4,6 @@ use std::path::Path;\n use std::path::PathBuf;\n use std::sync::Arc;\n \n-mod transcript;\n-\n use crate::app_server_session::AppServerSession;\n use crate::clipboard_paste::normalize_pasted_search_query;\n use crate::color::blend;\n@@ -25,6 +23,9 @@ use crate::status::format_directory_display;\n use crate::terminal_palette::best_color;\n use crate::terminal_palette::default_bg;\n use crate::text_formatting::truncate_text;\n+use crate::thread_transcript::RawReasoningVisibility;\n+use crate::thread_transcript::TranscriptCells;\n+use crate::thread_transcript::load_session_transcript;\n use crate::tui::FrameRequester;\n use crate::tui::Tui;\n use crate::tui::TuiEvent;\n@@ -60,9 +61,6 @@ use tokio::sync::mpsc;\n use tokio_stream::StreamExt;\n use tokio_stream::wrappers::UnboundedReceiverStream;\n use tracing::warn;\n-use transcript::RawReasoningVisibility;\n-use transcript::TranscriptCells;\n-use tra...", + "path": "codex-rs/tui/src/resume_picker.rs", + "status": "modified" + }, + { + "additions": 8, + "deletions": 0, + "patch_excerpt": "@@ -1,3 +1,5 @@\n+//! Render persisted thread turns into history-cell building blocks.\n+\n use std::sync::Arc;\n \n use crate::app_server_session::AppServerSession;\n@@ -7,6 +9,7 @@ use crate::history_cell::HistoryCell;\n use crate::history_cell::PlainHistoryCell;\n use crate::history_cell::ReasoningSummaryCell;\n use crate::history_cell::UserHistoryCell;\n+use crate::multi_agents::sub_agent_activity_summary;\n use codex_app_server_protocol::Thread;\n use codex_app_server_protocol::ThreadItem;\n use codex_protocol::ThreadId;\n@@ -191,6 +194,11 @@ fn fallback_transcript_cell(item: &ThreadItem) -> Option {\n ThreadItem::CollabAgentToolCall { tool, status, .. } => {\n vec![format!(\"agent tool: {tool:?} ยท {status:?}\").dim().into()]\n }\n+ ThreadItem::SubAgentActivity {\n+ kind, agent_path, ..\n+ } => {\n+ vec![sub_agent_activity_s...", + "path": "codex-rs/tui/src/thread_transcript.rs", + "status": "renamed" + } + ], + "linked_issues": [], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Why\n\nMulti-agent v2 identifies agents by canonical paths, but its tool handlers still emitted the larger legacy collaboration begin/end events built around nickname and role metadata. App-server, rollout-trace, analytics, and TUI consumers therefore lacked one compact path-based completion signal that behaved consistently across live events and replay.\n\nThe TUI also needs a bounded `/agent` status surface for v2 agents. It should use recent local activity for previews, refresh liveness without loading full histories, and keep the legacy picker available when no path-backed v2 agent is known.\n\n## What changed\n\n- Replace the v2 `spawn_agent`, `send_message`, `followup_task`, and `interrupt_agent` legacy lifecycle emissions with a success-only `SubAgentActivity` event. The event records the tool call ID, occurrence time, affected thread, canonical agent path, and `started`, `interacted`, or `interrupted` kind.\n- Expose the activity as a completion-only app-server v2 `subAgentActivity` thread item in live notifications and reconstructed history, regenerate the protocol schemas, and count it in sub-agent tool analytics.\n- Track canonical paths from live activity and loaded-thread metadata in the TUI, and render the activity in live and replayed transcripts.\n- Make `/agent` list running path-backed agents with summaries from bounded local event buffers. Each summary is capped at 240 graphemes, the scan is capped at six recent items, only the last three wrapped lines are shown, and command output is omitted. Liveness falls back to metadata-only `thread/read` when local turn state is unavailable.\n- Persist the activity as a terminal rollout-trace runtime payload and reduce it to the corresponding spawn, send, follow-up, or close interaction edge. `interrupt_agent` is classified as a close-edge operation.\n- Preserve the legacy picker when no path-backed v2 agent is known.\n\n## Compatibility\n\nApp-server v2 clients that consumed `collabAgentToolCall` begin/end pairs for these tools must handle the new completion-only `subAgentActivity` item. Legacy v1 collaboration behavior is unchanged.\n\n## Screenshot\n\n\"Screenshot\n\n## Testing\n\n- `just test -p codex-app-server-protocol`\n- `just test -p codex-rollout-trace`\n- Added focused coverage for activity analytics, terminal trace serialization, spawn-edge reduction, `interrupt_agent` classification, TUI status rendering without aggregated command output, and clearing stale running state after a completed turn.\n", + "labels": [], + "merged_at": "2026-06-09T10:14:48Z", + "number": 27007, + "state": "merged", + "title": "multi-agent: add path-based v2 activity tracking", + "url": "https://github.com/openai/codex/pull/27007" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-27035.json b/artifacts/github/bundles/openai-codex-pr-27035.json new file mode 100644 index 00000000..1e488297 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27035.json @@ -0,0 +1,125 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "viyatb-oai", + "committed_at": "2026-06-08T18:15:31Z", + "message": "fix(cli): enforce configured sandbox network proxy", + "sha": "d783face3686a981e14453bc228913eac1d74059", + "url": "https://github.com/openai/codex/commit/d783face3686a981e14453bc228913eac1d74059" + }, + { + "author": "viyatb-oai", + "committed_at": "2026-06-08T18:25:45Z", + "message": "fix(ci): provide bwrap to CLI integration tests", + "sha": "2a15fb7d4eadc623a70b44db8aec1c9243418279", + "url": "https://github.com/openai/codex/commit/2a15fb7d4eadc623a70b44db8aec1c9243418279" + }, + { + "author": "viyatb-oai", + "committed_at": "2026-06-08T19:13:44Z", + "message": "test(cli): skip proxy sandbox test without bwrap", + "sha": "965e080940e4aeea1d3549c1c4f651bd16af1819", + "url": "https://github.com/openai/codex/commit/965e080940e4aeea1d3549c1c4f651bd16af1819" + }, + { + "author": "viyatb-oai", + "committed_at": "2026-06-08T20:38:17Z", + "message": "fix(cli): preserve proxy routing with legacy landlock", + "sha": "3928dc19b1a780206384dd5e0a1427c25188c6f0", + "url": "https://github.com/openai/codex/commit/3928dc19b1a780206384dd5e0a1427c25188c6f0" + }, + { + "author": "viyatb-oai", + "committed_at": "2026-06-08T20:46:07Z", + "message": "test(cli): use curl for direct network probe", + "sha": "6d445696505735e1cc41ef38ee20979282ed4d1f", + "url": "https://github.com/openai/codex/commit/6d445696505735e1cc41ef38ee20979282ed4d1f" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "CLI", + "MACOS_WEBRTC_RUSTC_LINK_FLAGS", + "BWRAP_UNAVAILABLE_ERR", + "CODEX_HOME", + "--permissions-profile", + "--noproxy", + "--silent", + "--show-error", + "--connect-timeout", + "--max-time", + "--permission-profile", + "--use-legacy-landlock", + "--command-cwd", + "--allow-network-for-proxy" + ], + "files": [ + { + "additions": 3, + "deletions": 0, + "patch_excerpt": "@@ -3,6 +3,9 @@ load(\"//:defs.bzl\", \"MACOS_WEBRTC_RUSTC_LINK_FLAGS\", \"codex_rust_crate\", \"multip\n codex_rust_crate(\n name = \"cli\",\n crate_name = \"codex_cli\",\n+ extra_binaries = [\n+ \"//codex-rs/bwrap:bwrap\",\n+ ],\n rustc_flags_extra = MACOS_WEBRTC_RUSTC_LINK_FLAGS,\n )", + "path": "codex-rs/cli/BUILD.bazel", + "status": "modified" + }, + { + "additions": 5, + "deletions": 2, + "patch_excerpt": "@@ -258,6 +258,9 @@ async fn run_command_under_sandbox(\n let network = network_proxy\n .as_ref()\n .map(codex_core::config::StartedNetworkProxy::proxy);\n+ // Proxy containment depends on whether a proxy is active, not whether its\n+ // policy came from managed requirements.\n+ let enforce_managed_network = network.is_some();\n let managed_mitm_ca_trust_bundle_path = match network.as_ref() {\n Some(network) => network.managed_mitm_ca_trust_bundle_path(),\n None => None,\n@@ -278,7 +281,7 @@ async fn run_command_under_sandbox(\n file_system_sandbox_policy: &file_system_sandbox_policy,\n network_sandbox_policy,\n sandbox_policy_cwd: sandbox_policy_cwd.as_path(),\n- enforce_managed_network: false,\n+ enforce_managed_network,\n network: network.as_ref(),\n ...", + "path": "codex-rs/cli/src/debug_sandbox.rs", + "status": "modified" + }, + { + "additions": 70, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,70 @@\n+#![cfg(target_os = \"linux\")]\n+\n+use std::net::TcpListener;\n+\n+use anyhow::Result;\n+use tempfile::TempDir;\n+\n+const BWRAP_UNAVAILABLE_ERR: &str = \"bubblewrap is unavailable\";\n+\n+#[test]\n+fn sandbox_with_network_proxy_blocks_direct_loopback_access() -> Result<()> {\n+ let codex_home = TempDir::new()?;\n+ let listener = TcpListener::bind(\"127.0.0.2:0\")?;\n+ let port = listener.local_addr()?.port();\n+ std::fs::write(\n+ codex_home.path().join(\"config.toml\"),\n+ r#\"\n+default_permissions = \"network-test\"\n+\n+[features]\n+network_proxy = true\n+use_legacy_landlock = true\n+\n+[permissions.network-test]\n+extends = \":workspace\"\n+\n+[permissions.network-test.network]\n+enabled = true\n+mode = \"full\"\n+\"#,\n+ )?;\n+\n+ let url = format!(\"http://127.0.0.2:{port}/\");\n+ let output = std::process::Command::new(codex_utils_cargo_bin::cargo_bin(\"codex\")?)\n+ .env...", + "path": "codex-rs/cli/tests/sandbox_network_proxy.rs", + "status": "added" + }, + { + "additions": 4, + "deletions": 2, + "patch_excerpt": "@@ -47,7 +47,8 @@ pub fn create_linux_sandbox_command_args_for_permission_profile(\n \"--permission-profile\".to_string(),\n permission_profile_json,\n ];\n- if use_legacy_landlock {\n+ // Proxy-only networking requires bubblewrap's isolated network namespace.\n+ if use_legacy_landlock && !allow_network_for_proxy {\n linux_cmd.push(\"--use-legacy-landlock\".to_string());\n }\n if allow_network_for_proxy {\n@@ -83,7 +84,8 @@ fn create_linux_sandbox_command_args(\n \"--command-cwd\".to_string(),\n command_cwd,\n ];\n- if use_legacy_landlock {\n+ // Proxy-only networking requires bubblewrap's isolated network namespace.\n+ if use_legacy_landlock && !allow_network_for_proxy {\n linux_cmd.push(\"--use-legacy-landlock\".to_string());\n }\n if allow_network_for_proxy {", + "path": "codex-rs/sandboxing/src/landlock.rs", + "status": "modified" + }, + { + "additions": 5, + "deletions": 2, + "patch_excerpt": "@@ -33,14 +33,16 @@ fn legacy_landlock_flag_is_included_when_requested() {\n }\n \n #[test]\n-fn proxy_flag_is_included_when_requested() {\n+fn proxy_flag_takes_precedence_over_legacy_landlock() {\n let command = vec![\"/bin/true\".to_string()];\n let command_cwd = Path::new(\"/tmp/link\");\n let cwd = Path::new(\"/tmp\");\n+ let permission_profile = PermissionProfile::read_only();\n \n- let args = create_linux_sandbox_command_args(\n+ let args = create_linux_sandbox_command_args_for_permission_profile(\n command,\n command_cwd,\n+ &permission_profile,\n cwd,\n /*use_legacy_landlock*/ true,\n /*allow_network_for_proxy*/ true,\n@@ -49,6 +51,7 @@ fn proxy_flag_is_included_when_requested() {\n args.contains(&\"--allow-network-for-proxy\".to_string()),\n true\n );\n+ assert_eq!(args.contains(&\"--use-legacy-landlock\".to_string()), fa...", + "path": "codex-rs/sandboxing/src/landlock_tests.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -338,8 +338,8 @@ fn ensure_linux_bubblewrap_is_supported(\n allow_network_for_proxy: bool,\n is_wsl1: bool,\n ) -> Result<(), SandboxTransformError> {\n- let requires_bubblewrap = !use_legacy_landlock\n- && (!file_system_sandbox_policy.has_full_disk_write_access() || allow_network_for_proxy);\n+ let requires_bubblewrap = allow_network_for_proxy\n+ || (!use_legacy_landlock && !file_system_sandbox_policy.has_full_disk_write_access());\n if is_wsl1 && requires_bubblewrap {\n return Err(SandboxTransformError::Wsl1UnsupportedForBubblewrap);\n }", + "path": "codex-rs/sandboxing/src/manager.rs", + "status": "modified" + }, + { + "additions": 9, + "deletions": 0, + "patch_excerpt": "@@ -342,6 +342,15 @@ fn wsl1_rejects_linux_bubblewrap_path() {\n ),\n Err(super::SandboxTransformError::Wsl1UnsupportedForBubblewrap)\n ));\n+ assert!(matches!(\n+ super::ensure_linux_bubblewrap_is_supported(\n+ &FileSystemSandboxPolicy::unrestricted(),\n+ /*use_legacy_landlock*/ true,\n+ /*allow_network_for_proxy*/ true,\n+ /*is_wsl1*/ true,\n+ ),\n+ Err(super::SandboxTransformError::Wsl1UnsupportedForBubblewrap)\n+ ));\n }\n \n #[cfg(target_os = \"linux\")]", + "path": "codex-rs/sandboxing/src/manager_tests.rs", + "status": "modified" + } + ], + "linked_issues": [], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Why\n\n`codex sandbox` can start a network proxy from a configured permission profile. Previously, sandbox-level containment was tied to managed network requirements rather than whether a proxy was actually active. This meant config-driven proxy policies were not consistently enforced as the sandbox's only network path.\n\n## What changed\n\n- Enable proxy-only network containment whenever `codex sandbox` starts a network proxy.\n- Apply the same active-proxy check to the macOS and Linux sandbox paths.\n- Add a Linux regression test that verifies a sandboxed command cannot establish a direct connection while the configured proxy is active.\n\n## Test plan\n\n- `just test -p codex-cli debug_sandbox::tests`\n- `sandbox_with_network_proxy_blocks_direct_loopback_access` runs on Linux to cover the config-driven proxy path end to end.\n", + "labels": [], + "merged_at": "2026-06-08T21:03:37Z", + "number": 27035, + "state": "merged", + "title": "Enforce configured network proxy in codex sandbox", + "url": "https://github.com/openai/codex/pull/27035" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-27068.json b/artifacts/github/bundles/openai-codex-pr-27068.json new file mode 100644 index 00000000..43f89354 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27068.json @@ -0,0 +1,65 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "canvrno-oai", + "committed_at": "2026-06-08T21:36:22Z", + "message": "Fix debug config sandbox mode rendering", + "sha": "c84c6bbd105eeef517ee1411a75fc7f06f2a9515", + "url": "https://github.com/openai/codex/commit/c84c6bbd105eeef517ee1411a75fc7f06f2a9515" + }, + { + "author": "canvrno-oai", + "committed_at": "2026-06-08T21:57:15Z", + "message": "Merge branch 'main' into codex/debug-config-effective-sandbox-modes", + "sha": "aef829d185388be2dfa51b4d66be78836176ab92", + "url": "https://github.com/openai/codex/commit/aef829d185388be2dfa51b4d66be78836176ab92" + }, + { + "author": "canvrno-oai", + "committed_at": "2026-06-08T22:24:03Z", + "message": "Add debug config sandbox mode snapshot", + "sha": "65c5b125134e8afa0c7dd86014d95c490e791671", + "url": "https://github.com/openai/codex/commit/65c5b125134e8afa0c7dd86014d95c490e791671" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "TOML", + "CONFIG_TOML_FILE", + "MDM" + ], + "files": [ + { + "additions": 143, + "deletions": 11, + "patch_excerpt": "@@ -1,5 +1,6 @@\n use crate::history_cell::PlainHistoryCell;\n use crate::legacy_core::config::Config;\n+use crate::legacy_core::config::Permissions;\n use crate::session_state::SessionNetworkProxyRuntime;\n use codex_app_server_protocol::ConfigLayerSource;\n use codex_config::CONFIG_TOML_FILE;\n@@ -15,6 +16,8 @@ use codex_config::ResidencyRequirement;\n use codex_config::SandboxModeRequirement;\n use codex_config::WebSearchModeRequirement;\n use codex_config::format_config_layer_source;\n+use codex_protocol::models::PermissionProfile;\n+use codex_protocol::permissions::NetworkSandboxPolicy;\n use ratatui::style::Stylize;\n use ratatui::text::Line;\n use toml::Value as TomlValue;\n@@ -23,7 +26,9 @@ pub(crate) fn new_debug_config_output(\n config: &Config,\n session_network_proxy: Option<&SessionNetworkProxyRuntime>,\n ) -> PlainHistoryCell {\n- let mut lines = render_debug_config_lines(&config.co...", + "path": "codex-rs/tui/src/debug_config.rs", + "status": "modified" + }, + { + "additions": 12, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,12 @@\n+---\n+source: tui/src/debug_config.rs\n+expression: rendered.as_str()\n+---\n+/debug-config\n+\n+Config layer stack (lowest precedence first):\n+ \n+\n+Requirements:\n+ - allowed_sandbox_modes: read-only, workspace-write (source: /etc/codex/requirements.toml)\n+ - permissions.filesystem.deny_read: /home/alice/.gitconfig (source: /etc/codex/requirements.toml)", + "path": "codex-rs/tui/src/snapshots/codex_tui__debug_config__tests__debug_config_effective_sandbox_modes_with_deny_read.snap", + "status": "added" + } + ], + "linked_issues": [], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Summary\n- Render `/debug-config`'s `allowed_sandbox_modes` from the finalized permission constraints instead of the raw requirements list.\n- Add regression coverage for configured full-access and external sandbox modes being omitted when effective permissions reject them.\n\n## Details\n`allowed_sandbox_modes` comes from managed requirements, but the final permissions can be further constrained by derived validation rules. For example, `permissions.filesystem.deny_read` requires sandbox enforcement, so modes that disable or externalize Codex's sandbox are not actually usable even if they were present in the raw requirements TOML.\n\nThe debug renderer now enumerates the configured sandbox-mode labels and keeps only those accepted by `Config.permissions`. That makes `/debug-config` reflect the same effective permission-profile constraint path used by runtime config validation, while preserving the existing source/provenance display.\n\n## Validation\n- Added a regression test for effective sandbox-mode filtering in `/debug-config`.\n", + "labels": [], + "merged_at": "2026-06-09T00:03:53Z", + "number": 27068, + "state": "merged", + "title": "Show effective sandbox modes in /debug-config", + "url": "https://github.com/openai/codex/pull/27068" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-27098.json b/artifacts/github/bundles/openai-codex-pr-27098.json new file mode 100644 index 00000000..8c99d646 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27098.json @@ -0,0 +1,99 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "xl-openai", + "committed_at": "2026-06-09T00:49:57Z", + "message": "Return workspace directory installed plugins", + "sha": "2ff3136992b56306ac00b4f9e894102acaa5237f", + "url": "https://github.com/openai/codex/commit/2ff3136992b56306ac00b4f9e894102acaa5237f" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "OPENAI_CURATED_MARKETPLACE_NAME", + "REMOTE_GLOBAL_MARKETPLACE_NAME", + "REMOTE_WORKSPACE_MARKETPLACE_NAME", + "REMOTE_WORKSPACE_SHARED_WITH_ME_MARKETPLACE_NAME", + "REMOTE_WORKSPACE_SHARED_WITH_ME_PRIVATE_MARKETPLACE_NAME", + "REMOTE_WORKSPACE_SHARED_WITH_ME_UNLISTED_MARKETPLACE_NAME", + "LISTED", + "PRIVATE", + "GLOBAL", + "WORKSPACE", + "DEFAULT_TIMEOUT", + "JSONRPCR", + "TOOL_SUGGEST_DISCOVERABLE_PLUGIN_ALLOWLIST", + "TEST_CURATED_PLUGIN_CACHE_VERSION", + "TEST_CURATED_PLUGIN_SHA", + "OPENAI_BUNDLED_MARKETPLACE_NAME" + ], + "files": [ + { + "additions": 19, + "deletions": 13, + "patch_excerpt": "@@ -9,8 +9,11 @@ use codex_config::types::McpServerConfig;\n use codex_core_plugins::OPENAI_CURATED_MARKETPLACE_NAME;\n use codex_core_plugins::PluginListBackgroundTaskOptions;\n use codex_core_plugins::remote::REMOTE_GLOBAL_MARKETPLACE_NAME;\n+use codex_core_plugins::remote::REMOTE_WORKSPACE_MARKETPLACE_NAME;\n+use codex_core_plugins::remote::REMOTE_WORKSPACE_SHARED_WITH_ME_MARKETPLACE_NAME;\n+use codex_core_plugins::remote::REMOTE_WORKSPACE_SHARED_WITH_ME_PRIVATE_MARKETPLACE_NAME;\n+use codex_core_plugins::remote::REMOTE_WORKSPACE_SHARED_WITH_ME_UNLISTED_MARKETPLACE_NAME;\n use codex_core_plugins::remote::RemoteAppTemplateUnavailableReason;\n-use codex_core_plugins::remote::RemotePluginScope;\n use codex_core_plugins::remote::is_valid_remote_plugin_id;\n use codex_core_plugins::remote::validate_remote_plugin_id;\n use codex_mcp::McpOAuthLoginSupport;\n@@ -149,15 +152,18 @@ fn convert_configured_mar...", + "path": "codex-rs/app-server/src/request_processors/plugins.rs", + "status": "modified" + }, + { + "additions": 86, + "deletions": 0, + "patch_excerpt": "@@ -2371,6 +2371,92 @@ plugin_sharing = true\n Ok(())\n }\n \n+#[tokio::test]\n+async fn plugin_installed_includes_workspace_directory_without_plugin_sharing() -> Result<()> {\n+ let codex_home = TempDir::new()?;\n+ let server = MockServer::start().await;\n+ std::fs::write(\n+ codex_home.path().join(\"config.toml\"),\n+ format!(\n+ r#\"chatgpt_base_url = \"{}/backend-api/\"\n+\n+[features]\n+plugins = true\n+remote_plugin = false\n+plugin_sharing = false\n+\"#,\n+ server.uri()\n+ ),\n+ )?;\n+ write_chatgpt_auth(\n+ codex_home.path(),\n+ ChatGptAuthFixture::new(\"chatgpt-token\")\n+ .account_id(\"account-123\")\n+ .chatgpt_user_id(\"user-123\")\n+ .chatgpt_account_id(\"account-123\"),\n+ AuthCredentialsStoreMode::File,\n+ )?;\n+ let mut workspace_installed_body: serde_json::Value =\n+ serde_json::from_str...", + "path": "codex-rs/app-server/tests/suite/v2/plugin_list.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 2, + "patch_excerpt": "@@ -14,7 +14,6 @@ use crate::PluginsConfigInput;\n use crate::PluginsManager;\n use crate::marketplace::MarketplacePluginInstallPolicy;\n use crate::remote::REMOTE_GLOBAL_MARKETPLACE_NAME;\n-use crate::remote::RemotePluginScope;\n \n const TOOL_SUGGEST_DISCOVERABLE_PLUGIN_ALLOWLIST: &[&str] = &[\n \"github@openai-curated\",\n@@ -100,7 +99,9 @@ impl PluginsManager {\n .collect::>();\n installed_app_connector_ids.extend(input.loaded_plugin_app_connector_ids.iter().cloned());\n let remote_installed_marketplaces = if input.plugins.remote_plugin_enabled {\n- self.build_remote_installed_plugin_marketplaces_from_cache(&[RemotePluginScope::Global])\n+ self.build_remote_installed_plugin_marketplaces_from_cache(&[\n+ REMOTE_GLOBAL_MARKETPLACE_NAME,\n+ ])\n } else {\n None\n };", + "path": "codex-rs/core-plugins/src/discoverable.rs", + "status": "modified" + }, + { + "additions": 12, + "deletions": 6, + "patch_excerpt": "@@ -38,7 +38,6 @@ use crate::marketplace_upgrade::configured_git_marketplace_names;\n use crate::marketplace_upgrade::upgrade_configured_git_marketplaces;\n use crate::remote::RemoteInstalledPlugin;\n use crate::remote::RemotePluginCatalogError;\n-use crate::remote::RemotePluginScope;\n use crate::remote::RemotePluginServiceConfig;\n use crate::remote_legacy::RemotePluginFetchError;\n use crate::remote_legacy::RemotePluginMutationError;\n@@ -560,14 +559,19 @@ impl PluginsManager {\n \n pub fn build_remote_installed_plugin_marketplaces_from_cache(\n &self,\n- visible_scopes: &[RemotePluginScope],\n+ visible_marketplaces: &[&str],\n ) -> Option> {\n let cache = match self.remote_installed_plugins_cache.read() {\n Ok(cache) => cache,\n Err(err) => err.into_inner(),\n };\n let plugins = cache.as_...", + "path": "codex-rs/core-plugins/src/manager.rs", + "status": "modified" + }, + { + "additions": 46, + "deletions": 4, + "patch_excerpt": "@@ -6,8 +6,10 @@ use crate::loader::load_plugins_from_layer_stack;\n use crate::loader::refresh_non_curated_plugin_cache;\n use crate::loader::refresh_non_curated_plugin_cache_force_reinstall;\n use crate::marketplace::MarketplacePluginInstallPolicy;\n+use crate::remote::REMOTE_GLOBAL_MARKETPLACE_NAME;\n+use crate::remote::REMOTE_WORKSPACE_MARKETPLACE_NAME;\n+use crate::remote::REMOTE_WORKSPACE_SHARED_WITH_ME_MARKETPLACE_NAME;\n use crate::remote::RemoteInstalledPlugin;\n-use crate::remote::RemotePluginScope;\n use crate::startup_sync::curated_plugins_repo_path;\n use crate::test_support::TEST_CURATED_PLUGIN_CACHE_VERSION;\n use crate::test_support::TEST_CURATED_PLUGIN_SHA;\n@@ -137,8 +139,15 @@ fn remote_installed_linear_plugin() -> RemoteInstalledPlugin {\n }\n \n fn remote_installed_plugin(name: &str) -> RemoteInstalledPlugin {\n+ remote_installed_plugin_in_marketplace(name, REMOTE_GLOBAL_MARKETPL...", + "path": "codex-rs/core-plugins/src/manager_tests.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 4, + "patch_excerpt": "@@ -845,14 +845,12 @@ pub(crate) async fn fetch_remote_installed_plugins(\n \n pub fn group_remote_installed_plugins_by_marketplaces(\n plugins: &[RemoteInstalledPlugin],\n- visible_scopes: &[RemotePluginScope],\n+ visible_marketplaces: &[&str],\n ) -> Vec {\n let mut plugins_by_marketplace = BTreeMap::>::new();\n \n for plugin in plugins {\n- if !RemotePluginScope::from_marketplace_name(&plugin.marketplace_name)\n- .is_some_and(|scope| visible_scopes.contains(&scope))\n- {\n+ if !visible_marketplaces.contains(&plugin.marketplace_name.as_str()) {\n continue;\n }\n let Ok(plugin_id) = PluginId::new(plugin.name.clone(), plugin.marketplace_name.clone())", + "path": "codex-rs/core-plugins/src/remote.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -7,7 +7,7 @@ use crate::plugins::test_support::write_plugins_feature_config;\n use codex_core_plugins::OPENAI_BUNDLED_MARKETPLACE_NAME;\n use codex_core_plugins::PluginInstallRequest;\n use codex_core_plugins::PluginsManager;\n-use codex_core_plugins::remote::RemotePluginScope;\n+use codex_core_plugins::remote::REMOTE_GLOBAL_MARKETPLACE_NAME;\n use codex_core_plugins::remote::RemotePluginServiceConfig;\n use codex_core_plugins::remote::fetch_and_cache_global_remote_plugin_catalog;\n use codex_core_plugins::startup_sync::curated_plugins_repo_path;\n@@ -362,7 +362,7 @@ remote_plugin = true\n .build_and_cache_remote_installed_plugin_marketplaces(\n &config.plugins_config_input(),\n Some(&auth),\n- &[RemotePluginScope::Global],\n+ &[REMOTE_GLOBAL_MARKETPLACE_NAME],\n /*on_effective_plugins_changed*/ None,\n )\n .await", + "path": "codex-rs/core/src/plugins/discoverable_tests.rs", + "status": "modified" + } + ], + "linked_issues": [], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Summary\n\n- return installed `workspace-directory` remote plugins by default in `plugin/installed`\n- keep shared-with-me installed plugins gated behind `plugin_sharing`\n- filter remote installed plugin marketplaces by canonical marketplace name instead of coarse workspace scope\n\n## Validation\n\n- `just fmt`\n- `just test -p codex-core-plugins`\n- `just test -p codex-app-server`\n- `just fix -p codex-core-plugins`\n- `just fix -p codex-app-server`\n- `$xin-build` targeted verification:\n - `just test -p codex-core-plugins build_remote_installed_plugin_marketplaces_from_cache_filters_by_marketplace_name`\n - `just test -p codex-app-server plugin_installed_includes_workspace_directory_without_plugin_sharing`\n - `just test -p codex-app-server plugin_installed_includes_remote_shared_with_me_plugins`\n - `just test -p codex-app-server plugin_list_omits_shared_with_me_kind_when_plugin_sharing_disabled`\n", + "labels": [], + "merged_at": "2026-06-09T08:23:17Z", + "number": 27098, + "state": "merged", + "title": "[codex] Return workspace directory installed plugins", + "url": "https://github.com/openai/codex/pull/27098" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-27166.json b/artifacts/github/bundles/openai-codex-pr-27166.json new file mode 100644 index 00000000..80e7210b --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27166.json @@ -0,0 +1,52 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "jif-oai", + "committed_at": "2026-06-09T10:44:42Z", + "message": "fix: app-server", + "sha": "634643a5e480b62d16468d27ad0b91295b0b74b2", + "url": "https://github.com/openai/codex/commit/634643a5e480b62d16468d27ad0b91295b0b74b2" + }, + { + "author": "jif-oai", + "committed_at": "2026-06-09T10:58:25Z", + "message": "nit fix", + "sha": "339810a9339783c34d7b65b293f08f657ba3c93d", + "url": "https://github.com/openai/codex/commit/339810a9339783c34d7b65b293f08f657ba3c93d" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "JSONRPCE", + "CHANNEL_CAPACITY" + ], + "files": [ + { + "additions": 109, + "deletions": 1, + "patch_excerpt": "@@ -100,6 +100,7 @@ use codex_protocol::protocol::Op;\n use codex_protocol::protocol::RealtimeEvent;\n use codex_protocol::protocol::ReviewDecision;\n use codex_protocol::protocol::ReviewOutputEvent;\n+use codex_protocol::protocol::SubAgentActivityKind;\n use codex_protocol::protocol::TokenCountEvent;\n use codex_protocol::protocol::TurnAbortedEvent;\n use codex_protocol::protocol::TurnCompleteEvent;\n@@ -844,7 +845,6 @@ pub(crate) async fn apply_bespoke_event_handling(\n | EventMsg::CollabAgentSpawnEnd(_)\n | EventMsg::CollabAgentInteractionBegin(_)\n | EventMsg::CollabAgentInteractionEnd(_)\n- | EventMsg::SubAgentActivity(_)\n | EventMsg::CollabWaitingBegin(_)\n | EventMsg::CollabWaitingEnd(_)\n | EventMsg::CollabCloseBegin(_)\n@@ -862,6 +862,24 @@ pub(crate) async fn apply_bespoke_event_handling(\n );\n outgoing.send_server_...", + "path": "codex-rs/app-server/src/bespoke_event_handling.rs", + "status": "modified" + } + ], + "linked_issues": [ + "#27007" + ], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Why\n\nPR #27007 moved MultiAgentV2 interruption reporting from the legacy collaboration close event to `SubAgentActivity::Interrupted`. App-server's missing-thread cleanup still ran only for the legacy event, so an interrupted child that had already been unloaded could remain marked as loaded and running in `ThreadWatchManager`. That leaves thread status and running-turn accounting stale, including the count used during graceful shutdown.\n\n## What changed\n\n- Handle `SubAgentActivity::Interrupted` separately in app-server event processing.\n- Remove the child's thread watch when `ThreadManager` no longer has that thread.\n- Continue forwarding the same completed sub-agent activity notification to clients.\n\n## Testing\n\n- Added a regression test that starts with a running watch for an unloaded child, applies the interrupted activity event, and verifies the watch is removed, the running count returns to zero, and the client notification is still emitted.\n", + "labels": [], + "merged_at": "2026-06-09T11:27:54Z", + "number": 27166, + "state": "merged", + "title": "app-server: clear stale thread watches after v2 agent interruption", + "url": "https://github.com/openai/codex/pull/27166" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/impact/openai-codex-pr-27007.json b/artifacts/github/impact/openai-codex-pr-27007.json new file mode 100644 index 00000000..99efa5d6 --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27007.json @@ -0,0 +1,45 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27007", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "multi-agent: add path-based v2 activity tracking", + "url": "https://github.com/openai/codex/pull/27007", + "meta": "Merged 2026-06-09T10:14:48Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27007", + "meta": "artifacts/github/reviews/openai-codex-pr-27007.review.json" + } + ] + }, + "observed_change": "Codex multi-agent v2 now emits a path-based completion-only `SubAgentActivity` thread item for spawn, message, follow-up, and interrupt activity instead of legacy collaboration begin/end items.", + "public_signal_decision": "publish", + "control_plane_impact": "compat_risk", + "publisher_angle": "operator_impact", + "confidence": "confirmed", + "evidence": [ + "The upstream review records a schema-backed `subAgentActivity` thread item with `agentPath`, `agentThreadId`, and activity kind.", + "The PR compatibility note says v2 clients that consumed legacy collaboration begin/end pairs must handle the new completion-only item.", + "Event mapping and thread-history code both convert `SubAgentActivity` into app-server-visible thread items.", + "TUI code adds path-backed agent status and transcript rendering." + ], + "candidate_followups": [ + "Update Decodex app-server v2 parsers and dashboard reducers to recognize `subAgentActivity` items.", + "Check retained-lane child-agent liveness and replay code for assumptions about legacy collaboration begin/end pairs.", + "Use `agentPath` as the stable v2 child-agent identity when available." + ], + "social_notes": [ + "Public copy should say this is an app-server v2 and multi-agent v2 change.", + "Avoid implying legacy v1 collaboration behavior changed." + ], + "caveats": [ + "The change is scoped to multi-agent v2.", + "Clients still need to tolerate historical thread data that contains legacy collaboration items." + ] +} diff --git a/artifacts/github/impact/openai-codex-pr-27035.json b/artifacts/github/impact/openai-codex-pr-27035.json new file mode 100644 index 00000000..0230aa6d --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27035.json @@ -0,0 +1,45 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27035", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Enforce configured network proxy in codex sandbox", + "url": "https://github.com/openai/codex/pull/27035", + "meta": "Merged 2026-06-08T21:03:37Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27035", + "meta": "artifacts/github/reviews/openai-codex-pr-27035.review.json" + } + ] + }, + "observed_change": "Codex sandbox now treats any active configured network proxy as the condition for proxy-only network containment across macOS and Linux sandbox paths.", + "public_signal_decision": "publish", + "control_plane_impact": "compat_risk", + "publisher_angle": "operator_impact", + "confidence": "confirmed", + "evidence": [ + "The upstream review records `enforce_managed_network` being derived from an active proxy rather than managed requirements.", + "Linux sandbox arguments now let proxy-only networking take precedence over legacy landlock.", + "Sandbox manager checks make an active proxy require bubblewrap support.", + "The Linux regression test asserts a sandboxed direct loopback connection is blocked." + ], + "candidate_followups": [ + "Surface bubblewrap and WSL1 constraints in Decodex sandbox preflight when a network proxy is active.", + "Update Decodex network-policy readbacks to distinguish active-proxy containment from raw managed-requirements state.", + "Check any Linux retained-lane fixtures that combine legacy landlock and network proxy permissions." + ], + "social_notes": [ + "Public copy should frame this as proxy containment, not a new proxy feature.", + "Mention Linux bubblewrap caveats only if there is room." + ], + "caveats": [ + "The direct-network regression test is Linux-only.", + "Bubblewrap availability can decide whether proxy containment can run on a given Linux host." + ] +} diff --git a/artifacts/github/impact/openai-codex-pr-27068.json b/artifacts/github/impact/openai-codex-pr-27068.json new file mode 100644 index 00000000..268fa1a8 --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27068.json @@ -0,0 +1,45 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27068", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Show effective sandbox modes in /debug-config", + "url": "https://github.com/openai/codex/pull/27068", + "meta": "Merged 2026-06-09T00:03:53Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27068", + "meta": "artifacts/github/reviews/openai-codex-pr-27068.review.json" + } + ] + }, + "observed_change": "Codex `/debug-config` now displays `allowed_sandbox_modes` after finalized permission constraints filter out modes that are not actually usable.", + "public_signal_decision": "publish", + "control_plane_impact": "candidate", + "publisher_angle": "practical_explainer", + "confidence": "confirmed", + "evidence": [ + "The upstream review records the output semantics changing from raw requirements to effective allowed sandbox modes.", + "The code filters sandbox modes through `permissions.can_set_permission_profile`.", + "The deny-read regression test omits `danger-full-access` and `external-sandbox` from rendered output.", + "The snapshot captures the corrected output for a deny-read requirement." + ], + "candidate_followups": [ + "Align Decodex diagnostics with Codex effective sandbox-mode readback.", + "Avoid recommending full-access or external-sandbox options when finalized permission constraints reject them.", + "Use this upstream behavior when explaining managed permission conflicts." + ], + "social_notes": [ + "Public copy should center on `/debug-config` troubleshooting.", + "Avoid describing this as a new permission mode." + ], + "caveats": [ + "This is diagnostic readback behavior.", + "It does not change the permission constraint model itself." + ] +} diff --git a/artifacts/github/impact/openai-codex-pr-27098.json b/artifacts/github/impact/openai-codex-pr-27098.json new file mode 100644 index 00000000..e9edda45 --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27098.json @@ -0,0 +1,45 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27098", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "[codex] Return workspace directory installed plugins", + "url": "https://github.com/openai/codex/pull/27098", + "meta": "Merged 2026-06-09T08:23:17Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27098", + "meta": "artifacts/github/reviews/openai-codex-pr-27098.review.json" + } + ] + }, + "observed_change": "Codex app-server `plugin/installed` now includes installed `workspace-directory` remote plugins by default while keeping shared-with-me marketplaces gated behind `plugin_sharing`.", + "public_signal_decision": "publish", + "control_plane_impact": "candidate", + "publisher_angle": "operator_impact", + "confidence": "confirmed", + "evidence": [ + "The upstream review records `REMOTE_WORKSPACE_MARKETPLACE_NAME` being included in visible marketplaces by default.", + "Shared-with-me marketplace names remain gated behind the `plugin_sharing` feature.", + "Remote installed plugins are filtered by explicit marketplace name instead of coarse scope.", + "App-server tests assert workspace-directory installed plugins appear without plugin sharing." + ], + "candidate_followups": [ + "Use app-server `plugin/installed` readback as Decodex's source for workspace-directory installed plugin visibility.", + "Keep Decodex shared-with-me plugin UI gated unless upstream `plugin_sharing` is enabled.", + "Make plugin inventory code tolerant of an additional `workspace-directory` marketplace." + ], + "social_notes": [ + "Public copy should distinguish workspace-directory installed plugins from shared-with-me plugins.", + "Avoid implying remote plugin discovery is enabled globally by this change." + ], + "caveats": [ + "The change is readback-focused.", + "Shared-with-me plugins are still feature-gated." + ] +} diff --git a/artifacts/github/impact/openai-codex-pr-27166.json b/artifacts/github/impact/openai-codex-pr-27166.json new file mode 100644 index 00000000..df7d2732 --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27166.json @@ -0,0 +1,43 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27166", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "app-server: clear stale thread watches after v2 agent interruption", + "url": "https://github.com/openai/codex/pull/27166", + "meta": "Merged 2026-06-09T11:27:54Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27166", + "meta": "artifacts/github/reviews/openai-codex-pr-27166.review.json" + } + ] + }, + "observed_change": "Codex app-server now clears stale thread watches when a v2 `SubAgentActivity::Interrupted` event references a child thread that is no longer loaded.", + "public_signal_decision": "defer", + "control_plane_impact": "compat_risk", + "publisher_angle": "watch_note", + "confidence": "confirmed", + "evidence": [ + "The upstream review ties this fix directly to PR #27007's move to `SubAgentActivity::Interrupted`.", + "App-server bespoke event handling now removes a missing interrupted child thread from `ThreadWatchManager`.", + "The regression test asserts loaded status becomes `NotLoaded` and running count returns to zero.", + "The app-server still forwards the completed activity notification after cleanup." + ], + "candidate_followups": [ + "Verify Decodex running-turn and shutdown readbacks do not carry stale child-agent watches after adopting PR #27007.", + "Avoid duplicate cleanup that would conflict with upstream app-server watch removal." + ], + "social_notes": [ + "Treat this as a supporting correctness follow-up to the path-based activity change unless a release rollup needs it." + ], + "caveats": [ + "This fix is primarily relevant after the PR #27007 protocol change.", + "It is not a standalone user workflow." + ] +} diff --git a/artifacts/github/review-queue/openai-codex-latest.json b/artifacts/github/review-queue/openai-codex-latest.json index b6b26885..197adfbd 100644 --- a/artifacts/github/review-queue/openai-codex-latest.json +++ b/artifacts/github/review-queue/openai-codex-latest.json @@ -1,14 +1,14 @@ { "counts": { - "critical": 13, - "high": 9, + "critical": 14, + "high": 8, "low": 2, "normal": 16, "published_subjects_seen": 0, "recent_commits_scanned": 40, "subjects_queued": 40 }, - "generated_at": "2026-06-09T08:06:27.782925Z", + "generated_at": "2026-06-09T14:04:59.634102Z", "repo": "openai/codex", "schema": "upstream_review_queue/v1", "source": { @@ -17,93 +17,6 @@ "signals_dir": "site/src/content/signals" }, "subjects": [ - { - "attention_flags": [ - "auth_account", - "breaking_change", - "deprecated_removed", - "new_feature", - "protocol_change", - "release_packaging" - ], - "changed_file_count": 9, - "commit_shas": [ - "4d1e4042a966351e21fdd7e6240c453eb15e2e29" - ], - "committed_at": "2026-06-08T00:35:33Z", - "next_step": "ai_review_required", - "pr_number": 24820, - "pr_url": "https://github.com/openai/codex/pull/24820", - "review_priority": "critical", - "review_reason": "Needs AI review for auth_account, breaking_change, deprecated_removed, new_feature, protocol_change, release_packaging.", - "sample_paths": [ - "MODULE.bazel.lock", - "codex-rs/.cargo/audit.toml", - "codex-rs/Cargo.lock", - "codex-rs/Cargo.toml", - "codex-rs/deny.toml", - "codex-rs/execpolicy-legacy/src/arg_matcher.rs", - "codex-rs/execpolicy-legacy/src/opt.rs", - "codex-rs/execpolicy-legacy/src/policy_parser.rs", - "codex-rs/execpolicy/src/parser.rs" - ], - "source_state": "merged", - "subject_id": "24820", - "subject_kind": "pr", - "surface_hints": [ - "config_hooks", - "sandbox_permissions" - ], - "title": "deps: update starlark to 0.14.2", - "url": "https://github.com/openai/codex/pull/24820" - }, - { - "attention_flags": [ - "auth_account", - "breaking_change", - "new_feature", - "protocol_change", - "rate_limit" - ], - "changed_file_count": 18, - "commit_shas": [ - "b5eae7326947ad4e7e7003a3f6925f370fb85407", - "84b8997c6c294c8e63ea1e85c53e9ef01ed022de", - "1b50304606151d6071fb68c7dbb09597b6d8c100" - ], - "committed_at": "2026-06-08T03:12:05Z", - "next_step": "ai_review_required", - "pr_number": 26639, - "pr_url": "https://github.com/openai/codex/pull/26639", - "review_priority": "critical", - "review_reason": "Needs AI review for auth_account, breaking_change, new_feature, protocol_change, rate_limit.", - "sample_paths": [ - "codex-rs/app-server-protocol/schema/json/ServerNotification.json", - "codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json", - "codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json", - "codex-rs/app-server-protocol/schema/json/v2/McpServerStatusUpdatedNotification.json", - "codex-rs/app-server-protocol/schema/typescript/v2/McpServerStatusUpdatedNotification.ts", - "codex-rs/app-server-protocol/src/protocol/v2/mcp.rs", - "codex-rs/app-server-protocol/src/protocol/v2/tests.rs", - "codex-rs/app-server/README.md", - "codex-rs/app-server/src/bespoke_event_handling.rs", - "codex-rs/app-server/tests/suite/v2/thread_start.rs", - "codex-rs/tui/src/app/app_server_event_targets.rs", - "codex-rs/tui/src/app/app_server_events.rs" - ], - "source_state": "merged", - "subject_id": "26639", - "subject_kind": "pr", - "surface_hints": [ - "app_server_protocol", - "cli_tui", - "docs_examples", - "mcp_plugins", - "tests_ci" - ], - "title": "fix(tui): scope MCP startup status by thread", - "url": "https://github.com/openai/codex/pull/26639" - }, { "attention_flags": [ "deprecated_removed", @@ -568,38 +481,120 @@ { "attention_flags": [ "auth_account", + "deprecated_removed", "new_feature", "protocol_change", + "release_packaging", "security_policy" ], - "changed_file_count": 6, + "changed_file_count": 7, "commit_shas": [ - "e28d2a19b431e3507482c05c2e292aeb9c68bf8c", - "02724b98d36c0285a4ded52dd8eeb5cae7dba445" + "2ff3136992b56306ac00b4f9e894102acaa5237f" ], - "committed_at": "2026-06-08T06:18:23Z", + "committed_at": "2026-06-09T08:23:16Z", "next_step": "ai_review_required", - "pr_number": 26719, - "pr_url": "https://github.com/openai/codex/pull/26719", - "review_priority": "high", - "review_reason": "Needs AI review for auth_account, new_feature, protocol_change, security_policy.", + "pr_number": 27098, + "pr_url": "https://github.com/openai/codex/pull/27098", + "review_priority": "critical", + "review_reason": "Needs AI review for auth_account, deprecated_removed, new_feature, protocol_change, release_packaging, security_policy.", "sample_paths": [ - "codex-rs/app-server/tests/suite/v2/web_search.rs", - "codex-rs/codex-api/src/endpoint/search.rs", - "codex-rs/codex-api/src/search.rs", - "codex-rs/core/tests/suite/code_mode.rs", - "codex-rs/ext/web-search/src/output.rs", - "codex-rs/ext/web-search/src/tool.rs" + "codex-rs/app-server/src/request_processors/plugins.rs", + "codex-rs/app-server/tests/suite/v2/plugin_list.rs", + "codex-rs/core-plugins/src/discoverable.rs", + "codex-rs/core-plugins/src/manager.rs", + "codex-rs/core-plugins/src/manager_tests.rs", + "codex-rs/core-plugins/src/remote.rs", + "codex-rs/core/src/plugins/discoverable_tests.rs" ], "source_state": "merged", - "subject_id": "26719", + "subject_id": "27098", "subject_kind": "pr", "surface_hints": [ "app_server_protocol", + "mcp_plugins", "tests_ci" ], - "title": "[codex] Enable standalone web search in code mode", - "url": "https://github.com/openai/codex/pull/26719" + "title": "[codex] Return workspace directory installed plugins", + "url": "https://github.com/openai/codex/pull/27098" + }, + { + "attention_flags": [ + "auth_account", + "breaking_change", + "new_feature", + "protocol_change" + ], + "changed_file_count": 57, + "commit_shas": [ + "6bba7d7f0403b1190af5f6def0d08da41946c516", + "f5696a6365d98f2e083a5ca7681c49be08acfd7b", + "3b2e64054eb41fe13c3e71e4b914db352212746c", + "ba4061d420e62206e42e94d09bba3d05f6063ae6", + "87f4b5f9cad0d5941821cc5dfbbf81f0d75e76e1" + ], + "committed_at": "2026-06-09T10:14:48Z", + "next_step": "ai_review_required", + "pr_number": 27007, + "pr_url": "https://github.com/openai/codex/pull/27007", + "review_priority": "critical", + "review_reason": "Needs AI review for auth_account, breaking_change, new_feature, protocol_change.", + "sample_paths": [ + "codex-rs/analytics/src/analytics_client_tests.rs", + "codex-rs/analytics/src/reducer.rs", + "codex-rs/app-server-protocol/schema/json/ServerNotification.json", + "codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json", + "codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json", + "codex-rs/app-server-protocol/schema/json/v2/ItemCompletedNotification.json", + "codex-rs/app-server-protocol/schema/json/v2/ItemStartedNotification.json", + "codex-rs/app-server-protocol/schema/json/v2/ReviewStartResponse.json", + "codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json", + "codex-rs/app-server-protocol/schema/json/v2/ThreadListResponse.json", + "codex-rs/app-server-protocol/schema/json/v2/ThreadMetadataUpdateResponse.json", + "codex-rs/app-server-protocol/schema/json/v2/ThreadReadResponse.json" + ], + "source_state": "merged", + "subject_id": "27007", + "subject_kind": "pr", + "surface_hints": [ + "app_server_protocol", + "cli_tui", + "docs_examples", + "mcp_plugins", + "sandbox_permissions", + "tests_ci" + ], + "title": "multi-agent: add path-based v2 activity tracking", + "url": "https://github.com/openai/codex/pull/27007" + }, + { + "attention_flags": [ + "auth_account", + "breaking_change", + "deprecated_removed", + "protocol_change" + ], + "changed_file_count": 1, + "commit_shas": [ + "634643a5e480b62d16468d27ad0b91295b0b74b2", + "339810a9339783c34d7b65b293f08f657ba3c93d" + ], + "committed_at": "2026-06-09T11:27:53Z", + "next_step": "ai_review_required", + "pr_number": 27166, + "pr_url": "https://github.com/openai/codex/pull/27166", + "review_priority": "critical", + "review_reason": "Needs AI review for auth_account, breaking_change, deprecated_removed, protocol_change.", + "sample_paths": [ + "codex-rs/app-server/src/bespoke_event_handling.rs" + ], + "source_state": "merged", + "subject_id": "27166", + "subject_kind": "pr", + "surface_hints": [ + "app_server_protocol" + ], + "title": "app-server: clear stale thread watches after v2 agent interruption", + "url": "https://github.com/openai/codex/pull/27166" }, { "attention_flags": [ diff --git a/artifacts/github/reviews/openai-codex-pr-27007.review.json b/artifacts/github/reviews/openai-codex-pr-27007.review.json new file mode 100644 index 00000000..54cf65c9 --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27007.review.json @@ -0,0 +1,76 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27007", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "27007", + "commit_shas": [ + "6bba7d7f0403b1190af5f6def0d08da41946c516", + "f5696a6365d98f2e083a5ca7681c49be08acfd7b", + "3b2e64054eb41fe13c3e71e4b914db352212746c", + "ba4061d420e62206e42e94d09bba3d05f6063ae6", + "87f4b5f9cad0d5941821cc5dfbbf81f0d75e76e1" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "multi-agent: add path-based v2 activity tracking", + "url": "https://github.com/openai/codex/pull/27007", + "meta": "Merged 2026-06-09T10:14:48Z" + }, + { + "kind": "commit", + "title": "feat: new tui mav2", + "url": "https://github.com/openai/codex/commit/6bba7d7f0403b1190af5f6def0d08da41946c516" + }, + { + "kind": "commit", + "title": "fix multi-agent activity tracking regressions", + "url": "https://github.com/openai/codex/commit/f5696a6365d98f2e083a5ca7681c49be08acfd7b" + } + ] + }, + "reviewed_at": "2026-06-09T14:07:34Z", + "observed_change": "Codex multi-agent v2 now emits a path-based completion-only `SubAgentActivity` thread item for spawn, message, follow-up, and interrupt activity instead of legacy collaboration begin/end items.", + "changed_surfaces": [ + "app-server v2 protocol schemas and TypeScript exports", + "multi-agent v2 tool event emission", + "app-server live notification and thread-history mapping", + "TUI `/agent` status and transcript rendering", + "analytics and rollout-trace reducers" + ], + "user_visible_path": "Codex TUI users get a bounded `/agent` status surface for path-backed v2 agents, while app-server v2 clients observe `subAgentActivity` items with `started`, `interacted`, or `interrupted` kinds.", + "control_plane_relevance": "Decodex app-server consumers and retained-lane dashboards should parse `ThreadItem::SubAgentActivity` and use `agentPath` as the stable v2 child-agent identity signal.", + "compatibility_risk": "High for strict app-server v2 clients that depended on `collabAgentToolCall` begin/end pairs for multi-agent v2 tools; the PR explicitly moves those tools to completion-only `subAgentActivity` items while leaving legacy v1 collaboration behavior unchanged.", + "adoption_opportunity": "Adopt the path-based activity item in Decodex lane activity displays, child-agent liveness, and replay reducers instead of inferring v2 state from nickname or role-based legacy events.", + "community_value": "High for Codex operators because the change exposes a compact, schema-backed signal for multi-agent v2 activity and makes `/agent` status more useful without loading full histories.", + "deprecated_or_breaking_notes": "The PR compatibility note says v2 clients that consumed legacy collaboration begin/end pairs for these tools must handle the new completion-only `subAgentActivity` item.", + "confidence": "confirmed", + "evidence": [ + "PR #27007 states that v2 `spawn_agent`, `send_message`, `followup_task`, and `interrupt_agent` replace legacy lifecycle emissions with a success-only `SubAgentActivity` event.", + "The app-server protocol schema adds `SubAgentActivityKind` and a `subAgentActivity` thread-item shape carrying `id`, `kind`, `agentThreadId`, and `agentPath`.", + "codex-rs/app-server-protocol/src/protocol/event_mapping.rs maps `EventMsg::SubAgentActivity` to `ItemCompletedNotification`.", + "codex-rs/app-server-protocol/src/protocol/thread_history.rs reconstructs `SubAgentActivity` items for thread history.", + "multi-agent v2 handlers remove legacy collaboration spawn/interaction/close event imports and emit `SubAgentActivityEvent` values.", + "TUI files add path-backed agent state, transcript rendering, and `/agent` status support with bounded summaries.", + "The normalized bundle artifacts/github/bundles/openai-codex-pr-27007.json records 57 changed files for the merged PR." + ], + "caveats": "The change is scoped to multi-agent v2; the PR says legacy v1 collaboration behavior is unchanged.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "This is a schema-backed app-server v2 protocol change with Decodex Control Plane compatibility implications." + }, + { + "type": "social_candidate", + "reason": "The new path-based activity signal has a clear operator-facing public angle." + }, + { + "type": "linear_followup", + "reason": "Decodex should verify its app-server v2 consumers parse and display `subAgentActivity` correctly." + } + ] +} diff --git a/artifacts/github/reviews/openai-codex-pr-27035.review.json b/artifacts/github/reviews/openai-codex-pr-27035.review.json new file mode 100644 index 00000000..87604077 --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27035.review.json @@ -0,0 +1,70 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27035", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "27035", + "commit_shas": [ + "d783face3686a981e14453bc228913eac1d74059", + "2a15fb7d4eadc623a70b44db8aec1c9243418279", + "965e080940e4aeea1d3549c1c4f651bd16af1819", + "3928dc19b1a780206384dd5e0a1427c25188c6f0", + "6d445696505735e1cc41ef38ee20979282ed4d1f" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Enforce configured network proxy in codex sandbox", + "url": "https://github.com/openai/codex/pull/27035", + "meta": "Merged 2026-06-08T21:03:37Z" + }, + { + "kind": "commit", + "title": "fix(cli): enforce configured sandbox network proxy", + "url": "https://github.com/openai/codex/commit/d783face3686a981e14453bc228913eac1d74059" + }, + { + "kind": "commit", + "title": "fix(cli): preserve proxy routing with legacy landlock", + "url": "https://github.com/openai/codex/commit/3928dc19b1a780206384dd5e0a1427c25188c6f0" + } + ] + }, + "reviewed_at": "2026-06-09T14:07:34Z", + "observed_change": "Codex sandbox now treats any active configured network proxy as the condition for proxy-only network containment across macOS and Linux sandbox paths.", + "changed_surfaces": [ + "codex sandbox CLI network-proxy handling", + "Linux sandbox argument construction", + "bubblewrap support checks", + "sandbox network-proxy integration tests" + ], + "user_visible_path": "Users running `codex sandbox` with a permission profile that starts the network proxy get direct network access blocked so traffic must route through the proxy path.", + "control_plane_relevance": "Decodex sandbox launchers and diagnostics should expect active network proxies to force proxy-only containment and, on Linux, to require bubblewrap even when legacy landlock is configured.", + "compatibility_risk": "Moderate: configurations that previously combined legacy landlock or unrestricted filesystem access with an active network proxy may now require bubblewrap support, and WSL1 is rejected for that bubblewrap path.", + "adoption_opportunity": "Use the active-proxy rule in Decodex sandbox preflight messaging and network-policy readbacks so operators know why direct network probes are blocked.", + "community_value": "High for security-conscious Codex users because the change closes a path where a configured proxy policy did not consistently become the sandbox's only network route.", + "deprecated_or_breaking_notes": "No CLI flag is removed, but Linux legacy-landlock behavior changes when a proxy is active because proxy-only networking now takes precedence.", + "confidence": "confirmed", + "evidence": [ + "PR #27035 says proxy-only network containment is enabled whenever `codex sandbox` starts a network proxy.", + "codex-rs/cli/src/debug_sandbox.rs derives `enforce_managed_network` from `network.is_some()` and passes it into macOS and Linux sandbox setup.", + "codex-rs/sandboxing/src/landlock.rs omits `--use-legacy-landlock` when `--allow-network-for-proxy` is present.", + "codex-rs/sandboxing/src/manager.rs treats `allow_network_for_proxy` as requiring bubblewrap.", + "codex-rs/cli/tests/sandbox_network_proxy.rs adds a Linux regression test that succeeds only when a direct loopback connection is blocked.", + "The normalized bundle artifacts/github/bundles/openai-codex-pr-27035.json records 7 changed files for the merged PR." + ], + "caveats": "The direct network regression test is Linux-only and skips when bubblewrap is unavailable.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "Sandbox proxy enforcement affects Decodex permission and network-policy assumptions." + }, + { + "type": "social_candidate", + "reason": "The proxy-only enforcement change has clear security and operator value." + } + ] +} diff --git a/artifacts/github/reviews/openai-codex-pr-27068.review.json b/artifacts/github/reviews/openai-codex-pr-27068.review.json new file mode 100644 index 00000000..6c44f833 --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27068.review.json @@ -0,0 +1,67 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27068", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "27068", + "commit_shas": [ + "c84c6bbd105eeef517ee1411a75fc7f06f2a9515", + "aef829d185388be2dfa51b4d66be78836176ab92", + "65c5b125134e8afa0c7dd86014d95c490e791671" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Show effective sandbox modes in /debug-config", + "url": "https://github.com/openai/codex/pull/27068", + "meta": "Merged 2026-06-09T00:03:53Z" + }, + { + "kind": "commit", + "title": "Fix debug config sandbox mode rendering", + "url": "https://github.com/openai/codex/commit/c84c6bbd105eeef517ee1411a75fc7f06f2a9515" + }, + { + "kind": "commit", + "title": "Add debug config sandbox mode snapshot", + "url": "https://github.com/openai/codex/commit/65c5b125134e8afa0c7dd86014d95c490e791671" + } + ] + }, + "reviewed_at": "2026-06-09T14:07:34Z", + "observed_change": "Codex `/debug-config` now displays `allowed_sandbox_modes` after finalized permission constraints filter out modes that are not actually usable.", + "changed_surfaces": [ + "TUI `/debug-config` rendering", + "permission-profile constraint checks", + "sandbox mode requirement formatting", + "snapshot and unit regression coverage" + ], + "user_visible_path": "Users running `/debug-config` see sandbox modes that remain valid after effective permission constraints such as `permissions.filesystem.deny_read` are applied.", + "control_plane_relevance": "Decodex operator diagnostics can treat `/debug-config` allowed sandbox modes as effective options rather than raw managed requirements.", + "compatibility_risk": "Low to moderate: the command output may omit `danger-full-access` or `external-sandbox` even when those modes appear in raw requirements, so parsers or docs that expected raw lists should update their interpretation.", + "adoption_opportunity": "Mirror the effective-mode framing in Decodex permission diagnostics and avoid recommending sandbox modes that the finalized permission profile rejects.", + "community_value": "Medium for Codex users debugging managed permissions because it prevents `/debug-config` from advertising sandbox modes that cannot actually be selected.", + "deprecated_or_breaking_notes": "No config key is removed; this is a diagnostic readback correction that changes output semantics from raw requirements to effective allowed modes.", + "confidence": "confirmed", + "evidence": [ + "PR #27068 says `/debug-config` renders `allowed_sandbox_modes` from finalized permission constraints instead of the raw requirements list.", + "codex-rs/tui/src/debug_config.rs adds `sandbox_mode_is_allowed_by_permissions` and filters modes through `permissions.can_set_permission_profile`.", + "The regression test constructs deny-read filesystem requirements and asserts only `read-only` and `workspace-write` remain in the rendered output.", + "The new snapshot records `allowed_sandbox_modes: read-only, workspace-write` alongside a deny-read requirement source.", + "The normalized bundle artifacts/github/bundles/openai-codex-pr-27068.json records 2 changed files for the merged PR." + ], + "caveats": "The change improves diagnostic output; it does not add a new sandbox mode or permission profile.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "Effective sandbox-mode readback affects Decodex operator diagnostics." + }, + { + "type": "social_candidate", + "reason": "The corrected `/debug-config` output has a concrete, user-visible troubleshooting angle." + } + ] +} diff --git a/artifacts/github/reviews/openai-codex-pr-27098.review.json b/artifacts/github/reviews/openai-codex-pr-27098.review.json new file mode 100644 index 00000000..8db7df76 --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27098.review.json @@ -0,0 +1,61 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27098", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "27098", + "commit_shas": [ + "2ff3136992b56306ac00b4f9e894102acaa5237f" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "[codex] Return workspace directory installed plugins", + "url": "https://github.com/openai/codex/pull/27098", + "meta": "Merged 2026-06-09T08:23:17Z" + }, + { + "kind": "commit", + "title": "Return workspace directory installed plugins", + "url": "https://github.com/openai/codex/commit/2ff3136992b56306ac00b4f9e894102acaa5237f" + } + ] + }, + "reviewed_at": "2026-06-09T14:07:34Z", + "observed_change": "Codex app-server `plugin/installed` now includes installed `workspace-directory` remote plugins by default while keeping shared-with-me marketplaces gated behind `plugin_sharing`.", + "changed_surfaces": [ + "app-server v2 plugin installed request processing", + "core plugin remote-installed marketplace filtering", + "remote plugin marketplace constants", + "plugin installed and manager regression tests" + ], + "user_visible_path": "App-server clients calling `plugin/installed` can see installed workspace-directory remote plugins even when `remote_plugin` and `plugin_sharing` feature gates are disabled.", + "control_plane_relevance": "Decodex plugin inventory and installation surfaces should expect workspace-directory installed plugins to appear as ordinary installed entries from app-server readback.", + "compatibility_risk": "Moderate for clients that assumed `plugin/installed` would omit workspace-directory remote plugins unless sharing was enabled; the response may now contain an additional `workspace-directory` marketplace.", + "adoption_opportunity": "Use the app-server readback instead of separately inferring workspace-directory installed plugins, and keep shared-with-me entries gated by `plugin_sharing`.", + "community_value": "High for plugin operators because installed workspace plugins become visible through the primary app-server installed-plugin path.", + "deprecated_or_breaking_notes": "No field is removed, but the filtering model changes from coarse remote scopes to explicit canonical marketplace names.", + "confidence": "confirmed", + "evidence": [ + "PR #27098 says `plugin/installed` returns installed `workspace-directory` remote plugins by default and keeps shared-with-me plugins behind `plugin_sharing`.", + "codex-rs/app-server/src/request_processors/plugins.rs always adds `REMOTE_WORKSPACE_MARKETPLACE_NAME` to visible marketplaces and adds shared-with-me marketplaces only when `Feature::PluginSharing` is enabled.", + "codex-rs/core-plugins/src/remote.rs filters remote installed plugins by explicit marketplace names instead of `RemotePluginScope`.", + "codex-rs/app-server/tests/suite/v2/plugin_list.rs adds coverage for `workspace-directory` appearing without plugin sharing while shared plugins are excluded.", + "codex-rs/core-plugins/src/manager_tests.rs adds marketplace-name filtering coverage.", + "The normalized bundle artifacts/github/bundles/openai-codex-pr-27098.json records 7 changed files for the merged PR." + ], + "caveats": "The change is about installed plugin readback; it does not make shared-with-me plugin marketplaces visible without the `plugin_sharing` feature.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "Plugin installed readback affects Decodex Control Plane plugin inventory." + }, + { + "type": "social_candidate", + "reason": "The workspace-directory installed-plugin readback has a clear operator-facing public angle." + } + ] +} diff --git a/artifacts/github/reviews/openai-codex-pr-27166.review.json b/artifacts/github/reviews/openai-codex-pr-27166.review.json new file mode 100644 index 00000000..71a456a4 --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27166.review.json @@ -0,0 +1,63 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27166", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "27166", + "commit_shas": [ + "634643a5e480b62d16468d27ad0b91295b0b74b2", + "339810a9339783c34d7b65b293f08f657ba3c93d" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "app-server: clear stale thread watches after v2 agent interruption", + "url": "https://github.com/openai/codex/pull/27166", + "meta": "Merged 2026-06-09T11:27:54Z" + }, + { + "kind": "commit", + "title": "fix: app-server", + "url": "https://github.com/openai/codex/commit/634643a5e480b62d16468d27ad0b91295b0b74b2" + }, + { + "kind": "commit", + "title": "nit fix", + "url": "https://github.com/openai/codex/commit/339810a9339783c34d7b65b293f08f657ba3c93d" + } + ] + }, + "reviewed_at": "2026-06-09T14:07:34Z", + "observed_change": "Codex app-server now clears stale thread watches when a v2 `SubAgentActivity::Interrupted` event references a child thread that is no longer loaded.", + "changed_surfaces": [ + "app-server bespoke event handling", + "ThreadWatchManager cleanup behavior", + "v2 `SubAgentActivity::Interrupted` notification handling", + "app-server regression coverage" + ], + "user_visible_path": "App-server clients should see more accurate child-thread loaded/running status after an interrupted v2 agent has already been unloaded.", + "control_plane_relevance": "Decodex running-turn counters, graceful shutdown checks, and child-agent dashboards should benefit from upstream cleanup of stale v2 agent watches.", + "compatibility_risk": "Moderate if Decodex consumers had compensating cleanup for stale interrupted child-agent watches; after this fix, upstream app-server may remove those watches itself before forwarding the completed activity item.", + "adoption_opportunity": "Prefer upstream `SubAgentActivity::Interrupted` watch cleanup when Decodex adopts PR #27007 path-based activity handling.", + "community_value": "Medium for app-server integrators because stale running-child status can affect shutdown and status displays, but the fix is mainly a correctness follow-up to PR #27007.", + "deprecated_or_breaking_notes": "No protocol field is removed; the behavior change is cleanup of stale server-side watch state on interrupted v2 agent activity.", + "confidence": "confirmed", + "evidence": [ + "PR #27166 says PR #27007 moved MultiAgentV2 interruption reporting to `SubAgentActivity::Interrupted` and that missing-thread cleanup previously only ran for the legacy close event.", + "codex-rs/app-server/src/bespoke_event_handling.rs removes `EventMsg::SubAgentActivity` from the no-op branch and handles it explicitly.", + "The new branch removes the child thread from `ThreadWatchManager` when `activity.kind == Interrupted` and `thread_manager.get_thread(activity.agent_thread_id)` fails.", + "The same branch still converts the activity into an app-server notification with `item_event_to_server_notification`.", + "The regression test seeds a running watch for a missing child thread, sends an interrupted `SubAgentActivity`, and asserts the watch becomes `NotLoaded` with running count zero.", + "The normalized bundle artifacts/github/bundles/openai-codex-pr-27166.json records 1 changed file for the merged PR." + ], + "caveats": "This is a follow-up to the path-based v2 activity change in PR #27007 and is most relevant when that protocol behavior is present.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "Stale watch cleanup affects Decodex Control Plane status and shutdown assumptions." + } + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-27007.json b/artifacts/github/social-candidates/openai-codex-pr-27007.json new file mode 100644 index 00000000..b7ab761f --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-27007.json @@ -0,0 +1,54 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-27007", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "operator_impact", + "priority": "critical", + "audience": "Codex app-server and multi-agent operators", + "candidate_text": [ + "Codex multi-agent v2 now emits completion-only `subAgentActivity` thread items with canonical agent paths, and v2 clients must stop assuming legacy collab begin/end pairs for those tools. PR: https://github.com/openai/codex/pull/27007" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-27007.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-27007.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/27007" + ] + }, + "evidence_notes": [ + "The PR compatibility note says app-server v2 clients must handle completion-only `subAgentActivity` items.", + "The schema adds `SubAgentActivityKind` and a `subAgentActivity` thread-item shape.", + "Event mapping and thread history both expose the item to app-server consumers.", + "The PR says legacy v1 collaboration behavior is unchanged." + ], + "claims": [ + { + "text": "Multi-agent v2 now emits completion-only `subAgentActivity` thread items.", + "evidence": "artifacts/github/reviews/openai-codex-pr-27007.review.json", + "confidence": "confirmed" + }, + { + "text": "App-server v2 clients that consumed legacy collaboration begin/end pairs must handle the new item.", + "evidence": "artifacts/github/impact/openai-codex-pr-27007.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "publish", + "reason": "The PR changes a concrete app-server v2 protocol shape used by multi-agent operators.", + "idempotency_key": "x:decodexspace:openai-codex-pr-27007:operator_impact" + }, + "caveats": [ + "The change is scoped to multi-agent v2.", + "Legacy v1 collaboration behavior is unchanged." + ], + "next_steps": [ + "Let Publisher automation decide whether to reserve or post this candidate." + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-27035.json b/artifacts/github/social-candidates/openai-codex-pr-27035.json new file mode 100644 index 00000000..b353c9d3 --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-27035.json @@ -0,0 +1,54 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-27035", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "operator_impact", + "priority": "high", + "audience": "Codex sandbox and security-policy operators", + "candidate_text": [ + "Codex sandbox now enforces an active configured network proxy as the only network path, including Linux legacy-landlock cases that now require bubblewrap isolation. PR: https://github.com/openai/codex/pull/27035" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-27035.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-27035.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/27035" + ] + }, + "evidence_notes": [ + "`debug_sandbox.rs` enables managed-network enforcement when a proxy is active.", + "Linux sandbox code lets proxy-only networking take precedence over legacy landlock.", + "Sandbox manager checks make active proxy containment require bubblewrap.", + "The Linux test verifies direct loopback access is blocked when the proxy policy is active." + ], + "claims": [ + { + "text": "An active configured network proxy now triggers proxy-only sandbox containment.", + "evidence": "artifacts/github/reviews/openai-codex-pr-27035.review.json", + "confidence": "confirmed" + }, + { + "text": "Linux proxy containment takes precedence over legacy landlock and may require bubblewrap.", + "evidence": "artifacts/github/impact/openai-codex-pr-27035.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "publish", + "reason": "The PR changes sandbox network enforcement in a way security-focused operators can act on.", + "idempotency_key": "x:decodexspace:openai-codex-pr-27035:operator_impact" + }, + "caveats": [ + "The direct-network regression test is Linux-only.", + "Bubblewrap availability remains host-dependent." + ], + "next_steps": [ + "Let Publisher automation decide whether to reserve or post this candidate." + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-27068.json b/artifacts/github/social-candidates/openai-codex-pr-27068.json new file mode 100644 index 00000000..d84d3c0b --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-27068.json @@ -0,0 +1,54 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-27068", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "practical_explainer", + "priority": "normal", + "audience": "Codex users debugging managed sandbox permissions", + "candidate_text": [ + "Codex `/debug-config` now reports sandbox modes after effective permission constraints, so `deny_read` can hide `danger-full-access` and `external-sandbox` from the usable list. PR: https://github.com/openai/codex/pull/27068" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-27068.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-27068.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/27068" + ] + }, + "evidence_notes": [ + "`/debug-config` filters sandbox modes through finalized permission constraints.", + "The deny-read regression test omits full-access and external sandbox modes from rendered output.", + "The snapshot captures the corrected effective-mode output.", + "The change is diagnostic readback, not a new sandbox mode." + ], + "claims": [ + { + "text": "`/debug-config` now reports effective allowed sandbox modes rather than raw requirements.", + "evidence": "artifacts/github/reviews/openai-codex-pr-27068.review.json", + "confidence": "confirmed" + }, + { + "text": "Deny-read constraints can remove `danger-full-access` and `external-sandbox` from the displayed usable modes.", + "evidence": "artifacts/github/impact/openai-codex-pr-27068.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "publish", + "reason": "The PR gives users a concrete troubleshooting improvement for sandbox permission conflicts.", + "idempotency_key": "x:decodexspace:openai-codex-pr-27068:practical_explainer" + }, + "caveats": [ + "This is diagnostic readback behavior.", + "It does not add a new permission profile." + ], + "next_steps": [ + "Let Publisher automation decide whether to reserve or post this candidate." + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-27098.json b/artifacts/github/social-candidates/openai-codex-pr-27098.json new file mode 100644 index 00000000..d707215e --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-27098.json @@ -0,0 +1,54 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-27098", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "operator_impact", + "priority": "high", + "audience": "Codex plugin and app-server operators", + "candidate_text": [ + "Codex app-server `plugin/installed` now returns installed `workspace-directory` plugins by default, while keeping shared-with-me plugins behind `plugin_sharing`. PR: https://github.com/openai/codex/pull/27098" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-27098.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-27098.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/27098" + ] + }, + "evidence_notes": [ + "`plugin/installed` includes `REMOTE_WORKSPACE_MARKETPLACE_NAME` in visible marketplaces by default.", + "Shared-with-me marketplaces remain gated behind the `plugin_sharing` feature.", + "Filtering now uses explicit marketplace names.", + "App-server tests cover workspace-directory installed plugin visibility without plugin sharing." + ], + "claims": [ + { + "text": "App-server `plugin/installed` now returns installed workspace-directory plugins by default.", + "evidence": "artifacts/github/reviews/openai-codex-pr-27098.review.json", + "confidence": "confirmed" + }, + { + "text": "Shared-with-me plugin marketplaces remain gated by `plugin_sharing`.", + "evidence": "artifacts/github/impact/openai-codex-pr-27098.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "publish", + "reason": "The PR changes installed-plugin readback for a concrete app-server operator path.", + "idempotency_key": "x:decodexspace:openai-codex-pr-27098:operator_impact" + }, + "caveats": [ + "The change is readback-focused.", + "Shared-with-me plugins remain feature-gated." + ], + "next_steps": [ + "Let Publisher automation decide whether to reserve or post this candidate." + ] +}