From 8803095b57e2b4ac43663e14a046c2de14396fe6 Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Thu, 11 Jun 2026 10:15:33 +0800 Subject: [PATCH] {"schema":"decodex/commit/1","summary":"Persist upstream Radar review artifacts","authority":"manual"} --- .../github/bundles/openai-codex-pr-25018.json | 487 ++++++++++++ .../github/bundles/openai-codex-pr-26041.json | 290 +++++++ .../github/bundles/openai-codex-pr-27259.json | 191 +++++ .../github/bundles/openai-codex-pr-27304.json | 438 +++++++++++ .../github/impact/openai-codex-pr-25018.json | 47 ++ .../github/impact/openai-codex-pr-26041.json | 47 ++ .../github/impact/openai-codex-pr-27259.json | 47 ++ .../github/impact/openai-codex-pr-27304.json | 47 ++ .../review-queue/openai-codex-latest.json | 706 ++++++++---------- .../reviews/openai-codex-pr-25018.review.json | 96 +++ .../reviews/openai-codex-pr-26041.review.json | 92 +++ .../reviews/openai-codex-pr-27259.review.json | 75 ++ .../reviews/openai-codex-pr-27304.review.json | 67 ++ .../openai-codex-pr-25018.json | 56 ++ .../openai-codex-pr-26041.json | 56 ++ .../openai-codex-pr-27259.json | 56 ++ .../openai-codex-pr-27304.json | 56 ++ 17 files changed, 2472 insertions(+), 382 deletions(-) create mode 100644 artifacts/github/bundles/openai-codex-pr-25018.json create mode 100644 artifacts/github/bundles/openai-codex-pr-26041.json create mode 100644 artifacts/github/bundles/openai-codex-pr-27259.json create mode 100644 artifacts/github/bundles/openai-codex-pr-27304.json create mode 100644 artifacts/github/impact/openai-codex-pr-25018.json create mode 100644 artifacts/github/impact/openai-codex-pr-26041.json create mode 100644 artifacts/github/impact/openai-codex-pr-27259.json create mode 100644 artifacts/github/impact/openai-codex-pr-27304.json create mode 100644 artifacts/github/reviews/openai-codex-pr-25018.review.json create mode 100644 artifacts/github/reviews/openai-codex-pr-26041.review.json create mode 100644 artifacts/github/reviews/openai-codex-pr-27259.review.json create mode 100644 artifacts/github/reviews/openai-codex-pr-27304.review.json create mode 100644 artifacts/github/social-candidates/openai-codex-pr-25018.json create mode 100644 artifacts/github/social-candidates/openai-codex-pr-26041.json create mode 100644 artifacts/github/social-candidates/openai-codex-pr-27259.json create mode 100644 artifacts/github/social-candidates/openai-codex-pr-27304.json diff --git a/artifacts/github/bundles/openai-codex-pr-25018.json b/artifacts/github/bundles/openai-codex-pr-25018.json new file mode 100644 index 000000000..ede79a7d8 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-25018.json @@ -0,0 +1,487 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "etraut-openai", + "committed_at": "2026-05-29T05:21:11Z", + "message": "Add app-server thread/delete API", + "sha": "6a8eca9c764324003e5244ff1350763b92bfd55c", + "url": "https://github.com/openai/codex/commit/6a8eca9c764324003e5244ff1350763b92bfd55c" + }, + { + "author": "etraut-openai", + "committed_at": "2026-05-29T05:32:30Z", + "message": "Simplify thread delete handling", + "sha": "2ffeebb96a53d069ec9a4b27ac855399ac2ed5e0", + "url": "https://github.com/openai/codex/commit/2ffeebb96a53d069ec9a4b27ac855399ac2ed5e0" + }, + { + "author": "etraut-openai", + "committed_at": "2026-05-29T05:51:03Z", + "message": "Validate thread delete before teardown", + "sha": "e3474e6f4e8bb5806b2f4d86057b4af3e6cc89df", + "url": "https://github.com/openai/codex/commit/e3474e6f4e8bb5806b2f4d86057b4af3e6cc89df" + }, + { + "author": "etraut-openai", + "committed_at": "2026-05-29T06:02:49Z", + "message": "Handle thread deleted notifications in TUI", + "sha": "2292718bf4af345f4bb5b5e0f8ff8bfa287c62d8", + "url": "https://github.com/openai/codex/commit/2292718bf4af345f4bb5b5e0f8ff8bfa287c62d8" + }, + { + "author": "etraut-openai", + "committed_at": "2026-05-29T06:53:35Z", + "message": "Simplify thread delete changes", + "sha": "2adf4ac97da874f89611fb3b33d8f301e04fbe75", + "url": "https://github.com/openai/codex/commit/2adf4ac97da874f89611fb3b33d8f301e04fbe75" + }, + { + "author": "etraut-openai", + "committed_at": "2026-05-29T07:28:30Z", + "message": "Fix thread delete argument comments", + "sha": "cb6f55ebed68ab47a383eeb01d7dd8b258c051f4", + "url": "https://github.com/openai/codex/commit/cb6f55ebed68ab47a383eeb01d7dd8b258c051f4" + }, + { + "author": "etraut-openai", + "committed_at": "2026-05-31T17:15:18Z", + "message": "Require rollout deletion before thread delete succeeds", + "sha": "b54292ea752847fafacd90568d9585f686bbe6af", + "url": "https://github.com/openai/codex/commit/b54292ea752847fafacd90568d9585f686bbe6af" + }, + { + "author": "etraut-openai", + "committed_at": "2026-05-31T17:30:00Z", + "message": "Notify clients for partial thread deletes", + "sha": "4e4d0b03381e9f8ff1e95428865f28bea4d25280", + "url": "https://github.com/openai/codex/commit/4e4d0b03381e9f8ff1e95428865f28bea4d25280" + }, + { + "author": "etraut-openai", + "committed_at": "2026-05-31T17:55:08Z", + "message": "Simplify thread delete tests", + "sha": "e221c3526b18ee401a4b9dfaad6218dc79165c9f", + "url": "https://github.com/openai/codex/commit/e221c3526b18ee401a4b9dfaad6218dc79165c9f" + }, + { + "author": "etraut-openai", + "committed_at": "2026-05-31T18:09:41Z", + "message": "Add live subagent delete coverage", + "sha": "cba630a49f83b92c89e893f843f0f6f434c616f1", + "url": "https://github.com/openai/codex/commit/cba630a49f83b92c89e893f843f0f6f434c616f1" + }, + { + "author": "etraut-openai", + "committed_at": "2026-05-31T18:33:46Z", + "message": "Handle unloaded roots in thread delete", + "sha": "75e10774593faffca4cb842a934cd357084fd44b", + "url": "https://github.com/openai/codex/commit/75e10774593faffca4cb842a934cd357084fd44b" + }, + { + "author": "etraut-openai", + "committed_at": "2026-05-31T18:36:46Z", + "message": "Fix thread delete argument comments", + "sha": "cc77f6475b243804c31a1530428f9c0e6ef2963d", + "url": "https://github.com/openai/codex/commit/cc77f6475b243804c31a1530428f9c0e6ef2963d" + }, + { + "author": "etraut-openai", + "committed_at": "2026-05-31T19:21:53Z", + "message": "Tighten thread delete cleanup", + "sha": "d2feb3b324d4a8553f9b60d481869a3e87bcc4ac", + "url": "https://github.com/openai/codex/commit/d2feb3b324d4a8553f9b60d481869a3e87bcc4ac" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-02T05:44:17Z", + "message": "Fix thread delete tests after main merge", + "sha": "ab817ffd6963c3d74e18e88d3bddb6f61d78ae3d", + "url": "https://github.com/openai/codex/commit/ab817ffd6963c3d74e18e88d3bddb6f61d78ae3d" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-02T06:39:38Z", + "message": "Harden thread delete cleanup", + "sha": "c6280684fc1329dcf081a70c19b7e3721bd7e53e", + "url": "https://github.com/openai/codex/commit/c6280684fc1329dcf081a70c19b7e3721bd7e53e" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-02T06:46:57Z", + "message": "Keep recorder until thread delete succeeds", + "sha": "adf45c442742593ff61765a11e48c572e20a5212", + "url": "https://github.com/openai/codex/commit/adf45c442742593ff61765a11e48c572e20a5212" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-02T07:13:50Z", + "message": "Handle thread delete edge cases", + "sha": "e31a612db2e4f6e0b9c77c2f5605fc80757c26c5", + "url": "https://github.com/openai/codex/commit/e31a612db2e4f6e0b9c77c2f5605fc80757c26c5" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-08T04:40:47Z", + "message": "Fix thread delete test after rebase", + "sha": "14aa743b37972f342d7383ae48d76b410ca48f7e", + "url": "https://github.com/openai/codex/commit/14aa743b37972f342d7383ae48d76b410ca48f7e" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-09T20:23:19Z", + "message": "Fix remote store test after rebase", + "sha": "879d73a0f5e96407f31875d62c430b47d8add678", + "url": "https://github.com/openai/codex/commit/879d73a0f5e96407f31875d62c430b47d8add678" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-10T17:05:14Z", + "message": "Remove deleted thread names from session index", + "sha": "126b310bbef358ecd7a2d25b9cc51e6040eafb95", + "url": "https://github.com/openai/codex/commit/126b310bbef358ecd7a2d25b9cc51e6040eafb95" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-10T17:29:01Z", + "message": "codex: address PR review feedback (#25018)", + "sha": "2f4f9ed4921ee874dd5c20d09bdd1ba52d66c45d", + "url": "https://github.com/openai/codex/commit/2f4f9ed4921ee874dd5c20d09bdd1ba52d66c45d" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-10T18:03:28Z", + "message": "codex: cancel deleted runner jobs (#25018)", + "sha": "71471d9e7917d7815414da4bc49f39441a984267", + "url": "https://github.com/openai/codex/commit/71471d9e7917d7815414da4bc49f39441a984267" + } + ], + "default_branch": "main", + "docs_refs": [ + "codex-rs/app-server/README.md" + ], + "examples_refs": [], + "extracted_flags": [ + "API", + "TUI", + "GENERATED", + "CODE", + "NOT", + "MODIFY", + "HAND", + "JSONL", + "JSONRPCE", + "JSON", + "DEFAULT_READ_TIMEOUT", + "DEFAULT_IN_PROCESS_CHANNEL_CAPACITY", + "JSONRPCR", + "SESSION_INDEX_FILE", + "READ_CHUNK_SIZE", + "SESSION_INDEX_LOCK", + "CONFLICT", + "UPDATE", + "SET", + "DELETE", + "FROM", + "WHERE", + "AND", + "SELECT", + "JOIN", + "NULL", + "INSERT", + "INTO", + "VALUES", + "COUNT", + "INFO", + "SQL", + "ARCHIVED_SESSIONS_SUBDIR", + "SESSIONS_SUBDIR" + ], + "files": [ + { + "additions": 35, + "deletions": 0, + "patch_excerpt": "@@ -3203,6 +3203,17 @@\n ],\n \"type\": \"object\"\n },\n+ \"ThreadDeleteParams\": {\n+ \"properties\": {\n+ \"threadId\": {\n+ \"type\": \"string\"\n+ }\n+ },\n+ \"required\": [\n+ \"threadId\"\n+ ],\n+ \"type\": \"object\"\n+ },\n \"ThreadForkParams\": {\n \"description\": \"There are two ways to fork a thread: 1. By thread_id: load the thread from disk by thread_id and fork it into a new thread. 2. By path: load the thread from disk by path and fork it into a new thread.\\n\\nIf using a non-empty path, the thread_id param will be ignored. Empty string path values are treated as absent.\\n\\nPrefer using thread_id whenever possible.\",\n \"properties\": {\n@@ -4514,6 +4525,30 @@\n \"title\": \"Thread/archiveRequest\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"id\": {\n+ \"$ref\": \"#/definitions/RequestId\"\n+ ...", + "path": "codex-rs/app-server-protocol/schema/json/ClientRequest.json", + "status": "modified" + }, + { + "additions": 31, + "deletions": 0, + "patch_excerpt": "@@ -3523,6 +3523,17 @@\n ],\n \"type\": \"object\"\n },\n+ \"ThreadDeletedNotification\": {\n+ \"properties\": {\n+ \"threadId\": {\n+ \"type\": \"string\"\n+ }\n+ },\n+ \"required\": [\n+ \"threadId\"\n+ ],\n+ \"type\": \"object\"\n+ },\n \"ThreadGoal\": {\n \"properties\": {\n \"createdAt\": {\n@@ -5447,6 +5458,26 @@\n \"title\": \"Thread/archivedNotification\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"method\": {\n+ \"enum\": [\n+ \"thread/deleted\"\n+ ],\n+ \"title\": \"Thread/deletedNotificationMethod\",\n+ \"type\": \"string\"\n+ },\n+ \"params\": {\n+ \"$ref\": \"#/definitions/ThreadDeletedNotification\"\n+ }\n+ },\n+ \"required\": [\n+ \"method\",\n+ \"params\"\n+ ],\n+ \"title\": \"Thread/deletedNotification\",\n+ \"type\": \"object...", + "path": "codex-rs/app-server-protocol/schema/json/ServerNotification.json", + "status": "modified" + }, + { + "additions": 75, + "deletions": 0, + "patch_excerpt": "@@ -324,6 +324,30 @@\n \"title\": \"Thread/archiveRequest\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"id\": {\n+ \"$ref\": \"#/definitions/v2/RequestId\"\n+ },\n+ \"method\": {\n+ \"enum\": [\n+ \"thread/delete\"\n+ ],\n+ \"title\": \"Thread/deleteRequestMethod\",\n+ \"type\": \"string\"\n+ },\n+ \"params\": {\n+ \"$ref\": \"#/definitions/v2/ThreadDeleteParams\"\n+ }\n+ },\n+ \"required\": [\n+ \"id\",\n+ \"method\",\n+ \"params\"\n+ ],\n+ \"title\": \"Thread/deleteRequest\",\n+ \"type\": \"object\"\n+ },\n {\n \"properties\": {\n \"id\": {\n@@ -4061,6 +4085,26 @@\n \"title\": \"Thread/archivedNotification\",\n \"type\": \"object\"\n ...", + "path": "codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json", + "status": "modified" + }, + { + "additions": 75, + "deletions": 0, + "patch_excerpt": "@@ -1263,6 +1263,30 @@\n \"title\": \"Thread/archiveRequest\",\n \"type\": \"object\"\n },\n+ {\n+ \"properties\": {\n+ \"id\": {\n+ \"$ref\": \"#/definitions/RequestId\"\n+ },\n+ \"method\": {\n+ \"enum\": [\n+ \"thread/delete\"\n+ ],\n+ \"title\": \"Thread/deleteRequestMethod\",\n+ \"type\": \"string\"\n+ },\n+ \"params\": {\n+ \"$ref\": \"#/definitions/ThreadDeleteParams\"\n+ }\n+ },\n+ \"required\": [\n+ \"id\",\n+ \"method\",\n+ \"params\"\n+ ],\n+ \"title\": \"Thread/deleteRequest\",\n+ \"type\": \"object\"\n+ },\n {\n \"properties\": {\n \"id\": {\n@@ -11726,6 +11750,26 @@\n \"title\": \"Thread/archivedNotification\",\n \"type\": \"object\"\n ...", + "path": "codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json", + "status": "modified" + }, + { + "additions": 13, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,13 @@\n+{\n+ \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n+ \"properties\": {\n+ \"threadId\": {\n+ \"type\": \"string\"\n+ }\n+ },\n+ \"required\": [\n+ \"threadId\"\n+ ],\n+ \"title\": \"ThreadDeleteParams\",\n+ \"type\": \"object\"\n+}\n\\ No newline at end of file", + "path": "codex-rs/app-server-protocol/schema/json/v2/ThreadDeleteParams.json", + "status": "added" + }, + { + "additions": 5, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,5 @@\n+{\n+ \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n+ \"title\": \"ThreadDeleteResponse\",\n+ \"type\": \"object\"\n+}\n\\ No newline at end of file", + "path": "codex-rs/app-server-protocol/schema/json/v2/ThreadDeleteResponse.json", + "status": "added" + }, + { + "additions": 13, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,13 @@\n+{\n+ \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n+ \"properties\": {\n+ \"threadId\": {\n+ \"type\": \"string\"\n+ }\n+ },\n+ \"required\": [\n+ \"threadId\"\n+ ],\n+ \"title\": \"ThreadDeletedNotification\",\n+ \"type\": \"object\"\n+}\n\\ No newline at end of file", + "path": "codex-rs/app-server-protocol/schema/json/v2/ThreadDeletedNotification.json", + "status": "added" + }, + { + "additions": 2, + "deletions": 1, + "patch_excerpt": "@@ -62,6 +62,7 @@ import type { SkillsListParams } from \"./v2/SkillsListParams\";\n import type { ThreadApproveGuardianDeniedActionParams } from \"./v2/ThreadApproveGuardianDeniedActionParams\";\n import type { ThreadArchiveParams } from \"./v2/ThreadArchiveParams\";\n import type { ThreadCompactStartParams } from \"./v2/ThreadCompactStartParams\";\n+import type { ThreadDeleteParams } from \"./v2/ThreadDeleteParams\";\n import type { ThreadForkParams } from \"./v2/ThreadForkParams\";\n import type { ThreadGoalClearParams } from \"./v2/ThreadGoalClearParams\";\n import type { ThreadGoalGetParams } from \"./v2/ThreadGoalGetParams\";\n@@ -86,4 +87,4 @@ import type { WindowsSandboxSetupStartParams } from \"./v2/WindowsSandboxSetupSta\n /**\n * Request from the client to the server.\n */\n-export type ClientRequest ={ \"method\": \"initialize\", id: RequestId, params: InitializeParams, } | { \"method\": \"thread/start\", id: ...", + "path": "codex-rs/app-server-protocol/schema/typescript/ClientRequest.ts", + "status": "modified" + }, + { + "additions": 2, + "deletions": 1, + "patch_excerpt": "@@ -43,6 +43,7 @@ import type { SkillsChangedNotification } from \"./v2/SkillsChangedNotification\";\n import type { TerminalInteractionNotification } from \"./v2/TerminalInteractionNotification\";\n import type { ThreadArchivedNotification } from \"./v2/ThreadArchivedNotification\";\n import type { ThreadClosedNotification } from \"./v2/ThreadClosedNotification\";\n+import type { ThreadDeletedNotification } from \"./v2/ThreadDeletedNotification\";\n import type { ThreadGoalClearedNotification } from \"./v2/ThreadGoalClearedNotification\";\n import type { ThreadGoalUpdatedNotification } from \"./v2/ThreadGoalUpdatedNotification\";\n import type { ThreadNameUpdatedNotification } from \"./v2/ThreadNameUpdatedNotification\";\n@@ -71,4 +72,4 @@ import type { WindowsWorldWritableWarningNotification } from \"./v2/WindowsWorldW\n /**\n * Notification sent from the server to the client.\n */\n-export type ServerNotificati...", + "path": "codex-rs/app-server-protocol/schema/typescript/ServerNotification.ts", + "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 ThreadDeleteParams = { threadId: string, };", + "path": "codex-rs/app-server-protocol/schema/typescript/v2/ThreadDeleteParams.ts", + "status": "added" + }, + { + "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 ThreadDeleteResponse = Record;", + "path": "codex-rs/app-server-protocol/schema/typescript/v2/ThreadDeleteResponse.ts", + "status": "added" + }, + { + "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 ThreadDeletedNotification = { threadId: string, };", + "path": "codex-rs/app-server-protocol/schema/typescript/v2/ThreadDeletedNotification.ts", + "status": "added" + }, + { + "additions": 3, + "deletions": 0, + "patch_excerpt": "@@ -374,6 +374,9 @@ export type { ThreadArchivedNotification } from \"./ThreadArchivedNotification\";\n export type { ThreadClosedNotification } from \"./ThreadClosedNotification\";\n export type { ThreadCompactStartParams } from \"./ThreadCompactStartParams\";\n export type { ThreadCompactStartResponse } from \"./ThreadCompactStartResponse\";\n+export type { ThreadDeleteParams } from \"./ThreadDeleteParams\";\n+export type { ThreadDeleteResponse } from \"./ThreadDeleteResponse\";\n+export type { ThreadDeletedNotification } from \"./ThreadDeletedNotification\";\n export type { ThreadForkParams } from \"./ThreadForkParams\";\n export type { ThreadForkResponse } from \"./ThreadForkResponse\";\n export type { ThreadGoal } from \"./ThreadGoal\";", + "path": "codex-rs/app-server-protocol/schema/typescript/v2/index.ts", + "status": "modified" + }, + { + "additions": 6, + "deletions": 0, + "patch_excerpt": "@@ -480,6 +480,11 @@ client_request_definitions! {\n serialization: thread_id(params.thread_id),\n response: v2::ThreadArchiveResponse,\n },\n+ ThreadDelete => \"thread/delete\" {\n+ params: v2::ThreadDeleteParams,\n+ serialization: thread_id(params.thread_id),\n+ response: v2::ThreadDeleteResponse,\n+ },\n ThreadUnsubscribe => \"thread/unsubscribe\" {\n params: v2::ThreadUnsubscribeParams,\n serialization: thread_id(params.thread_id),\n@@ -1522,6 +1527,7 @@ server_notification_definitions! {\n ThreadStarted => \"thread/started\" (v2::ThreadStartedNotification),\n ThreadStatusChanged => \"thread/status/changed\" (v2::ThreadStatusChangedNotification),\n ThreadArchived => \"thread/archived\" (v2::ThreadArchivedNotification),\n+ ThreadDeleted => \"thread/deleted\" (v2::ThreadDeletedNotification),\n ThreadUnarchived => \"thread/unarchiv...", + "path": "codex-rs/app-server-protocol/src/protocol/common.rs", + "status": "modified" + }, + { + "additions": 19, + "deletions": 0, + "patch_excerpt": "@@ -594,6 +594,18 @@ pub struct ThreadArchiveParams {\n #[ts(export_to = \"v2/\")]\n pub struct ThreadArchiveResponse {}\n \n+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]\n+#[serde(rename_all = \"camelCase\")]\n+#[ts(export_to = \"v2/\")]\n+pub struct ThreadDeleteParams {\n+ pub thread_id: String,\n+}\n+\n+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]\n+#[serde(rename_all = \"camelCase\")]\n+#[ts(export_to = \"v2/\")]\n+pub struct ThreadDeleteResponse {}\n+\n #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]\n #[serde(rename_all = \"camelCase\")]\n #[ts(export_to = \"v2/\")]\n@@ -1330,6 +1342,13 @@ pub struct ThreadArchivedNotification {\n pub thread_id: String,\n }\n \n+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]\n+#[serde(rename_all = \"camelCase\")]\n+#[ts(export_to = \"v2/\")]\n+pub struct ThreadDeletedNoti...", + "path": "codex-rs/app-server-protocol/src/protocol/v2/thread.rs", + "status": "modified" + }, + { + "additions": 11, + "deletions": 0, + "patch_excerpt": "@@ -151,6 +151,7 @@ Example with notification opt-out:\n - `thread/settings/updated` — experimental notification emitted to subscribed clients when a loaded thread’s effective next-turn settings change; includes `threadId` and the full `threadSettings`.\n - `thread/status/changed` — notification emitted when a loaded thread’s status changes (`threadId` + new `status`).\n - `thread/archive` — move a thread’s rollout file into the archived directory and attempt to move any spawned descendant thread rollout files; returns `{}` on success and emits `thread/archived` for each archived thread.\n+- `thread/delete` — hard-delete an active or archived thread and any spawned descendant threads; returns `{}` on success and emits `thread/deleted` for each deleted thread.\n - `thread/unsubscribe` — unsubscribe this connection from thread turn/item events. If this was the last subscriber, the server keeps ...", + "path": "codex-rs/app-server/README.md", + "status": "modified" + }, + { + "additions": 7, + "deletions": 1, + "patch_excerpt": "@@ -400,7 +400,7 @@ impl MessageProcessor {\n Arc::clone(&thread_manager),\n Arc::clone(&config),\n feedback,\n- log_db,\n+ log_db.clone(),\n state_db.clone(),\n );\n let git_processor = GitRequestProcessor::new();\n@@ -454,6 +454,7 @@ impl MessageProcessor {\n Arc::clone(&thread_list_state_permit),\n thread_goal_processor.clone(),\n state_db,\n+ log_db,\n Arc::clone(&skills_watcher),\n );\n let turn_processor = TurnRequestProcessor::new(\n@@ -1071,6 +1072,11 @@ impl MessageProcessor {\n .thread_archive(request_id.clone(), params)\n .await\n }\n+ ClientRequest::ThreadDelete { params, .. } => {\n+ self.thread_processor\n+ .thread_delete(request_id.clone(), p...", + "path": "codex-rs/app-server/src/message_processor.rs", + "status": "modified" + }, + { + "additions": 5, + "deletions": 0, + "patch_excerpt": "@@ -178,6 +178,9 @@ use codex_app_server_protocol::ThreadCompactStartParams;\n use codex_app_server_protocol::ThreadCompactStartResponse;\n use codex_app_server_protocol::ThreadDecrementElicitationParams;\n use codex_app_server_protocol::ThreadDecrementElicitationResponse;\n+use codex_app_server_protocol::ThreadDeleteParams;\n+use codex_app_server_protocol::ThreadDeleteResponse;\n+use codex_app_server_protocol::ThreadDeletedNotification;\n use codex_app_server_protocol::ThreadForkParams;\n use codex_app_server_protocol::ThreadForkResponse;\n use codex_app_server_protocol::ThreadGoal;\n@@ -414,6 +417,7 @@ use codex_rollout::state_db::reconcile_rollout;\n use codex_state::ThreadMetadata;\n use codex_state::log_db::LogDbLayer;\n use codex_thread_store::ArchiveThreadParams as StoreArchiveThreadParams;\n+use codex_thread_store::DeleteThreadParams as StoreDeleteThreadParams;\n use codex_thread_store::GitInfo...", + "path": "codex-rs/app-server/src/request_processors.rs", + "status": "modified" + }, + { + "additions": 188, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,188 @@\n+//! `thread/delete` request handling.\n+\n+use super::thread_processor::core_thread_write_error;\n+use super::thread_processor::unsupported_thread_store_operation;\n+use super::*;\n+\n+impl ThreadRequestProcessor {\n+ pub(crate) async fn thread_delete(\n+ &self,\n+ request_id: ConnectionRequestId,\n+ params: ThreadDeleteParams,\n+ ) -> Result, JSONRPCErrorError> {\n+ let mut deleted_thread_ids = Vec::new();\n+ let result = {\n+ let _thread_list_state_permit = self.acquire_thread_list_state_permit().await?;\n+ self.thread_delete_response(params, &mut deleted_thread_ids)\n+ .await\n+ };\n+ match result {\n+ Ok(response) => {\n+ self.outgoing\n+ .send_response(request_id.clone(), response)\n+ .await;\n+ ...", + "path": "codex-rs/app-server/src/request_processors/thread_delete.rs", + "status": "added" + }, + { + "additions": 38, + "deletions": 22, + "patch_excerpt": "@@ -325,6 +325,7 @@ pub(crate) struct ThreadRequestProcessor {\n pub(super) thread_list_state_permit: Arc,\n pub(super) thread_goal_processor: ThreadGoalRequestProcessor,\n pub(super) state_db: Option,\n+ pub(super) log_db: Option,\n pub(super) background_tasks: TaskTracker,\n pub(super) skills_watcher: Arc,\n }\n@@ -356,6 +357,7 @@ impl ThreadRequestProcessor {\n thread_list_state_permit: Arc,\n thread_goal_processor: ThreadGoalRequestProcessor,\n state_db: Option,\n+ log_db: Option,\n skills_watcher: Arc,\n ) -> Self {\n Self {\n@@ -372,6 +374,7 @@ impl ThreadRequestProcessor {\n thread_list_state_permit,\n thread_goal_processor,\n state_db,\n+ log_db,\n background_tasks:...", + "path": "codex-rs/app-server/src/request_processors/thread_processor.rs", + "status": "modified" + }, + { + "additions": 10, + "deletions": 0, + "patch_excerpt": "@@ -79,6 +79,7 @@ use codex_app_server_protocol::SkillsExtraRootsSetParams;\n use codex_app_server_protocol::SkillsListParams;\n use codex_app_server_protocol::ThreadArchiveParams;\n use codex_app_server_protocol::ThreadCompactStartParams;\n+use codex_app_server_protocol::ThreadDeleteParams;\n use codex_app_server_protocol::ThreadForkParams;\n use codex_app_server_protocol::ThreadInjectItemsParams;\n use codex_app_server_protocol::ThreadListParams;\n@@ -456,6 +457,15 @@ impl TestAppServer {\n self.send_request(\"thread/archive\", params).await\n }\n \n+ /// Send a `thread/delete` JSON-RPC request.\n+ pub async fn send_thread_delete_request(\n+ &mut self,\n+ params: ThreadDeleteParams,\n+ ) -> anyhow::Result {\n+ let params = Some(serde_json::to_value(params)?);\n+ self.send_request(\"thread/delete\", params).await\n+ }\n+\n /// Send a `thread/name/set`...", + "path": "codex-rs/app-server/tests/common/test_app_server.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -50,6 +50,7 @@ mod review;\n mod safety_check_downgrade;\n mod skills_list;\n mod thread_archive;\n+mod thread_delete;\n mod thread_fork;\n mod thread_inject_items;\n mod thread_list;", + "path": "codex-rs/app-server/tests/suite/v2/mod.rs", + "status": "modified" + }, + { + "additions": 71, + "deletions": 37, + "patch_excerpt": "@@ -20,13 +20,16 @@ use std::sync::Arc;\n use anyhow::Result;\n use app_test_support::create_mock_responses_server_repeating_assistant;\n use codex_app_server::in_process;\n+use codex_app_server::in_process::InProcessClientHandle;\n use codex_app_server::in_process::InProcessServerEvent;\n use codex_app_server::in_process::InProcessStartArgs;\n use codex_app_server_protocol::ClientInfo;\n use codex_app_server_protocol::ClientRequest;\n use codex_app_server_protocol::InitializeParams;\n use codex_app_server_protocol::RequestId;\n use codex_app_server_protocol::ServerNotification;\n+use codex_app_server_protocol::ThreadDeleteParams;\n+use codex_app_server_protocol::ThreadDeleteResponse;\n use codex_app_server_protocol::ThreadListParams;\n use codex_app_server_protocol::ThreadListResponse;\n use codex_app_server_protocol::ThreadResumeParams;\n@@ -42,8 +45,14 @@ use codex_core::config::Config;\n use codex_cor...", + "path": "codex-rs/app-server/tests/suite/v2/remote_thread_store.rs", + "status": "modified" + }, + { + "additions": 200, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,200 @@\n+use anyhow::Result;\n+use app_test_support::TestAppServer;\n+use app_test_support::create_fake_rollout;\n+use app_test_support::to_response;\n+use codex_app_server_protocol::JSONRPCError;\n+use codex_app_server_protocol::JSONRPCResponse;\n+use codex_app_server_protocol::RequestId;\n+use codex_app_server_protocol::ThreadDeleteParams;\n+use codex_app_server_protocol::ThreadDeleteResponse;\n+use codex_app_server_protocol::ThreadDeletedNotification;\n+use codex_app_server_protocol::ThreadLoadedListParams;\n+use codex_app_server_protocol::ThreadLoadedListResponse;\n+use codex_app_server_protocol::ThreadStartParams;\n+use codex_app_server_protocol::ThreadStartResponse;\n+use codex_core::find_thread_path_by_id_str;\n+use codex_protocol::ThreadId;\n+use codex_state::DirectionalThreadSpawnEdgeStatus;\n+use codex_state::StateRuntime;\n+use pretty_assertions::assert_eq;\n+use std::path::Path;\n+use ...", + "path": "codex-rs/app-server/tests/suite/v2/thread_delete.rs", + "status": "added" + }, + { + "additions": 48, + "deletions": 0, + "patch_excerpt": "@@ -43,7 +43,12 @@ use codex_app_server_protocol::ServerRequest;\n use codex_app_server_protocol::ServerRequestResolvedNotification;\n use codex_app_server_protocol::SubAgentActivityKind;\n use codex_app_server_protocol::TextElement;\n+use codex_app_server_protocol::ThreadDeleteParams;\n+use codex_app_server_protocol::ThreadDeleteResponse;\n+use codex_app_server_protocol::ThreadDeletedNotification;\n use codex_app_server_protocol::ThreadItem;\n+use codex_app_server_protocol::ThreadLoadedListParams;\n+use codex_app_server_protocol::ThreadLoadedListResponse;\n use codex_app_server_protocol::ThreadSource;\n use codex_app_server_protocol::ThreadStartParams;\n use codex_app_server_protocol::ThreadStartResponse;\n@@ -3370,6 +3375,49 @@ async fn turn_start_emits_spawn_agent_item_with_model_metadata_v2() -> Result<()\n assert_eq!(turn_completed.thread_id, thread.id);\n assert_eq!(turn_completed.turn.id...", + "path": "codex-rs/app-server/tests/suite/v2/turn_start.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 1, + "patch_excerpt": "@@ -2491,7 +2491,7 @@ async fn list_agent_subtree_thread_ids_includes_anonymous_and_closed_descendants\n }\n \n #[tokio::test]\n-async fn list_agent_subtree_thread_ids_includes_live_descendants_without_state_db() {\n+async fn list_agent_subtree_thread_ids_finds_live_descendants_of_unloaded_root() {\n let (_home, config) = test_config().await;\n let manager = ThreadManager::with_models_provider_home_and_state_for_tests(\n CodexAuth::from_api_key(\"dummy\"),\n@@ -2536,6 +2536,8 @@ async fn list_agent_subtree_thread_ids_includes_live_descendants_without_state_d\n .await\n .expect(\"grandchild spawn should succeed\");\n \n+ manager.remove_thread(&parent_thread_id).await;\n+\n let mut subtree_thread_ids = manager\n .list_agent_subtree_thread_ids(parent_thread_id)\n .await", + "path": "codex-rs/core/src/agent/control_tests.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 8, + "patch_excerpt": "@@ -515,14 +515,12 @@ impl ThreadManager {\n &self,\n thread_id: ThreadId,\n ) -> CodexResult> {\n- let thread = self.state.get_thread(thread_id).await?;\n-\n let mut subtree_thread_ids = Vec::new();\n let mut seen_thread_ids = HashSet::new();\n subtree_thread_ids.push(thread_id);\n seen_thread_ids.insert(thread_id);\n \n- if let Some(state_db_ctx) = thread.state_db() {\n+ if let Some(state_db_ctx) = self.state.state_db() {\n for status in [\n DirectionalThreadSpawnEdgeStatus::Open,\n DirectionalThreadSpawnEdgeStatus::Closed,\n@@ -541,11 +539,8 @@ impl ThreadManager {\n }\n }\n \n- for descendant_id in thread\n- .codex\n- .session\n- .services\n- .agent_control\n+ for descendant_id in self\n+ .age...", + "path": "codex-rs/core/src/thread_manager.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -73,6 +73,7 @@ pub use session_index::append_thread_name;\n pub use session_index::find_thread_meta_by_name_str;\n pub use session_index::find_thread_name_by_id;\n pub use session_index::find_thread_names_by_ids;\n+pub use session_index::remove_thread_name_entries;\n pub use state_db::StateDbHandle;\n pub use state_db::sqlite_telemetry_recorder;", + "path": "codex-rs/rollout/src/lib.rs", + "status": "modified" + }, + { + "additions": 48, + "deletions": 8, + "patch_excerpt": "@@ -1,21 +1,25 @@\n use std::collections::HashMap;\n use std::collections::HashSet;\n use std::fs::File;\n+use std::io::ErrorKind;\n use std::io::Read;\n use std::io::Seek;\n use std::io::SeekFrom;\n+use std::io::Write;\n use std::path::Path;\n use std::path::PathBuf;\n+use std::sync::LazyLock;\n+use std::sync::Mutex;\n \n use codex_protocol::ThreadId;\n use codex_protocol::protocol::SessionMetaLine;\n use serde::Deserialize;\n use serde::Serialize;\n use tokio::io::AsyncBufReadExt;\n-use tokio::io::AsyncWriteExt;\n \n const SESSION_INDEX_FILE: &str = \"session_index.jsonl\";\n const READ_CHUNK_SIZE: usize = 8192;\n+static SESSION_INDEX_LOCK: LazyLock> = LazyLock::new(|| Mutex::new(()));\n \n #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]\n pub struct SessionIndexEntry {\n@@ -25,7 +29,7 @@ pub struct SessionIndexEntry {\n }\n \n /// Append a thread name update to the session index.\n-/// The index ...", + "path": "codex-rs/rollout/src/session_index.rs", + "status": "modified" + }, + { + "additions": 284, + "deletions": 8, + "patch_excerpt": "@@ -884,17 +884,117 @@ ON CONFLICT(id) DO UPDATE SET\n self.upsert_thread(&metadata).await\n }\n \n- /// Delete a thread metadata row by id.\n+ /// Delete a thread and all associated state by id.\n pub async fn delete_thread(&self, thread_id: ThreadId) -> anyhow::Result {\n- let result = sqlx::query(\"DELETE FROM threads WHERE id = ?\")\n- .bind(thread_id.to_string())\n- .execute(self.pool.as_ref())\n+ self.delete_threads_strict(&[thread_id]).await\n+ }\n+\n+ /// Delete a set of threads and all associated state.\n+ ///\n+ /// Spawn edges and thread rows are deleted last so a failed delete can be retried with enough\n+ /// state left to rediscover the same spawned subtree.\n+ pub async fn delete_threads_strict(&self, thread_ids: &[ThreadId]) -> anyhow::Result {\n+ if thread_ids.is_empty() {\n+ return Ok(0);...", + "path": "codex-rs/state/src/runtime/threads.rs", + "status": "modified" + }, + { + "additions": 21, + "deletions": 0, + "patch_excerpt": "@@ -18,6 +18,7 @@ use codex_protocol::protocol::ThreadMemoryMode;\n use crate::AppendThreadItemsParams;\n use crate::ArchiveThreadParams;\n use crate::CreateThreadParams;\n+use crate::DeleteThreadParams;\n use crate::ListThreadsParams;\n use crate::LoadThreadHistoryParams;\n use crate::ReadThreadByRolloutPathParams;\n@@ -115,6 +116,7 @@ pub struct InMemoryThreadStoreCalls {\n pub update_thread_metadata: usize,\n pub archive_thread: usize,\n pub unarchive_thread: usize,\n+ pub delete_thread: usize,\n }\n \n /// In-memory [`ThreadStore`] implementation for tests and debug configs.\n@@ -333,6 +335,25 @@ impl ThreadStore for InMemoryThreadStore {\n state.calls.unarchive_thread += 1;\n stored_thread_from_state(&state, params.thread_id, /*include_history*/ false)\n }\n+\n+ async fn delete_thread(&self, params: DeleteThreadParams) -> ThreadStoreResult<()> {\n+ let mut sta...", + "path": "codex-rs/thread-store/src/in_memory.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -25,6 +25,7 @@ pub use types::AppendThreadItemsParams;\n pub use types::ArchiveThreadParams;\n pub use types::ClearableField;\n pub use types::CreateThreadParams;\n+pub use types::DeleteThreadParams;\n pub use types::ExtraConfig;\n pub use types::GitInfoPatch;\n pub use types::ItemPage;", + "path": "codex-rs/thread-store/src/lib.rs", + "status": "modified" + }, + { + "additions": 210, + "deletions": 0, + "patch_excerpt": "@@ -0,0 +1,210 @@\n+//! Local hard-delete support for persisted threads.\n+//!\n+//! Existing rollout files are deleted before this operation reports success. A rollout file that\n+//! vanishes after discovery counts as already deleted. SQLite cleanup happens at the app-server\n+//! layer after every associated rollout has been removed so failed deletes can be retried.\n+\n+use std::io::ErrorKind;\n+use std::path::Path;\n+\n+use codex_rollout::ARCHIVED_SESSIONS_SUBDIR;\n+use codex_rollout::SESSIONS_SUBDIR;\n+use codex_rollout::find_archived_thread_path_by_id_str;\n+use codex_rollout::find_thread_path_by_id_str;\n+use codex_rollout::remove_thread_name_entries;\n+\n+use super::LocalThreadStore;\n+use super::helpers::matching_rollout_file_name;\n+use super::helpers::scoped_rollout_path;\n+use crate::DeleteThreadParams;\n+use crate::ThreadStoreError;\n+use crate::ThreadStoreResult;\n+\n+pub(super) async fn delete_...", + "path": "codex-rs/thread-store/src/local/delete_thread.rs", + "status": "added" + }, + { + "additions": 6, + "deletions": 0, + "patch_excerpt": "@@ -1,5 +1,6 @@\n mod archive_thread;\n mod create_thread;\n+mod delete_thread;\n mod helpers;\n mod list_threads;\n mod live_writer;\n@@ -24,6 +25,7 @@ use tokio::sync::Mutex;\n use crate::AppendThreadItemsParams;\n use crate::ArchiveThreadParams;\n use crate::CreateThreadParams;\n+use crate::DeleteThreadParams;\n use crate::ListThreadsParams;\n use crate::LoadThreadHistoryParams;\n use crate::ReadThreadByRolloutPathParams;\n@@ -288,6 +290,10 @@ impl ThreadStore for LocalThreadStore {\n ) -> ThreadStoreResult {\n unarchive_thread::unarchive_thread(self, params).await\n }\n+\n+ async fn delete_thread(&self, params: DeleteThreadParams) -> ThreadStoreResult<()> {\n+ delete_thread::delete_thread(self, params).await\n+ }\n }\n \n #[cfg(test)]", + "path": "codex-rs/thread-store/src/local/mod.rs", + "status": "modified" + }, + { + "additions": 4, + "deletions": 0, + "patch_excerpt": "@@ -5,6 +5,7 @@ use std::any::Any;\n use crate::AppendThreadItemsParams;\n use crate::ArchiveThreadParams;\n use crate::CreateThreadParams;\n+use crate::DeleteThreadParams;\n use crate::ItemPage;\n use crate::ListItemsParams;\n use crate::ListThreadsParams;\n@@ -119,4 +120,7 @@ pub trait ThreadStore: Any + Send + Sync {\n &self,\n params: ArchiveThreadParams,\n ) -> ThreadStoreResult;\n+\n+ /// Deletes a thread's persisted rollout data and associated metadata.\n+ async fn delete_thread(&self, params: DeleteThreadParams) -> ThreadStoreResult<()>;\n }", + "path": "codex-rs/thread-store/src/store.rs", + "status": "modified" + }, + { + "additions": 7, + "deletions": 0, + "patch_excerpt": "@@ -653,6 +653,13 @@ pub struct ArchiveThreadParams {\n pub thread_id: ThreadId,\n }\n \n+/// Parameters for deleting a thread.\n+#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]\n+pub struct DeleteThreadParams {\n+ /// Thread id to delete.\n+ pub thread_id: ThreadId,\n+}\n+\n #[cfg(test)]\n mod tests {\n use pretty_assertions::assert_eq;", + "path": "codex-rs/thread-store/src/types.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -49,6 +49,7 @@ pub(super) fn server_notification_thread_target(\n Some(notification.thread_id.as_str())\n }\n ServerNotification::ThreadArchived(notification) => Some(notification.thread_id.as_str()),\n+ ServerNotification::ThreadDeleted(notification) => Some(notification.thread_id.as_str()),\n ServerNotification::ThreadUnarchived(notification) => Some(notification.thread_id.as_str()),\n ServerNotification::ThreadClosed(notification) => Some(notification.thread_id.as_str()),\n ServerNotification::ThreadNameUpdated(notification) => {", + "path": "codex-rs/tui/src/app/app_server_event_targets.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -224,6 +224,7 @@ impl ChatWidget {\n | ServerNotification::ThreadStarted(_)\n | ServerNotification::ThreadStatusChanged(_)\n | ServerNotification::ThreadArchived(_)\n+ | ServerNotification::ThreadDeleted(_)\n | ServerNotification::ThreadUnarchived(_)\n | ServerNotification::RawResponseItemCompleted(_)\n | ServerNotification::CommandExecOutputDelta(_)", + "path": "codex-rs/tui/src/chatwidget/protocol.rs", + "status": "modified" + } + ], + "linked_issues": [ + "#25018" + ], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Why\n\nClients can archive and unarchive threads today, but there is no app-server API for permanently removing a thread. Deletion also needs to cover the full session tree: deleting a main thread should remove spawned subagent threads and the related local metadata instead of leaving orphaned rollout files, goals, or subagent state behind.\n\n## What\n\n- Adds the v2 `thread/delete` request and `thread/deleted` notification, with the response shape kept consistent with `thread/archive`.\n- Implements local hard delete for active and archived rollout files.\n- Deletes the requested thread's state DB row as the commit point, then best-effort cleans associated state including spawned descendants, goals, spawn edges, logs, dynamic tools, and agent job assignments.\n- Updates app-server API docs and generated protocol schema/TypeScript fixtures.", + "labels": [ + "code-reviewed" + ], + "merged_at": "2026-06-10T18:22:12Z", + "number": 25018, + "state": "merged", + "title": "Add app-server `thread/delete` API", + "url": "https://github.com/openai/codex/pull/25018" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-26041.json b/artifacts/github/bundles/openai-codex-pr-26041.json new file mode 100644 index 000000000..6f2d6606c --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-26041.json @@ -0,0 +1,290 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "etraut-openai", + "committed_at": "2026-06-03T04:51:52Z", + "message": "feat(app-server): add background terminal process APIs", + "sha": "e4cb80ffaed2e6ad90a42e0993eabd0d00ddcad3", + "url": "https://github.com/openai/codex/commit/e4cb80ffaed2e6ad90a42e0993eabd0d00ddcad3" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-03T07:39:32Z", + "message": "Simplify background terminal list API", + "sha": "3a5856f7ed4241b1d78aa834b47cea80b6607770", + "url": "https://github.com/openai/codex/commit/3a5856f7ed4241b1d78aa834b47cea80b6607770" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-03T07:44:39Z", + "message": "Reduce background terminal test churn", + "sha": "353ed1b6bb945a60e859384cf279976e9b280ecf", + "url": "https://github.com/openai/codex/commit/353ed1b6bb945a60e859384cf279976e9b280ecf" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-03T23:43:34Z", + "message": "Restore background terminal metadata fields", + "sha": "0d37f064d817b8f0f3aa63d8b4fc27901f6c0b5b", + "url": "https://github.com/openai/codex/commit/0d37f064d817b8f0f3aa63d8b4fc27901f6c0b5b" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-04T00:04:42Z", + "message": "Restore background terminal start time", + "sha": "b90d036c9c45fad1a7abf3474d0aa600e787f116", + "url": "https://github.com/openai/codex/commit/b90d036c9c45fad1a7abf3474d0aa600e787f116" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-04T00:25:08Z", + "message": "codex: address PR review feedback (#26041)", + "sha": "4d1537dba9be19227807e924bb8f551ba2385200", + "url": "https://github.com/openai/codex/commit/4d1537dba9be19227807e924bb8f551ba2385200" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-04T00:35:58Z", + "message": "codex: address PR review feedback (#26041)", + "sha": "e0f9c328df1ff2d337d3baeb029884b04fe69b35", + "url": "https://github.com/openai/codex/commit/e0f9c328df1ff2d337d3baeb029884b04fe69b35" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-04T00:55:54Z", + "message": "codex: address PR review feedback (#26041)", + "sha": "64dc0d2bc88f179dd3d71de57586d2a77bb86ab8", + "url": "https://github.com/openai/codex/commit/64dc0d2bc88f179dd3d71de57586d2a77bb86ab8" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-04T01:25:21Z", + "message": "codex: address PR review feedback (#26041)", + "sha": "cce9ca7334ae9bbb5b1086dbcfc136264226b27f", + "url": "https://github.com/openai/codex/commit/cce9ca7334ae9bbb5b1086dbcfc136264226b27f" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-04T01:45:52Z", + "message": "Merge branch 'main' into etraut/background-terminal-apis", + "sha": "33a8e24459749ee78aeed292adebc176724b1bcb", + "url": "https://github.com/openai/codex/commit/33a8e24459749ee78aeed292adebc176724b1bcb" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-04T01:47:33Z", + "message": "codex: stabilize background terminal termination tests", + "sha": "61c5d9c84bdae60c98006a819ec8d601255eb165", + "url": "https://github.com/openai/codex/commit/61c5d9c84bdae60c98006a819ec8d601255eb165" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-04T02:29:09Z", + "message": "codex: make background terminal tests deterministic", + "sha": "e78df1ea9d485e8b06fff4b4c075591d556be156", + "url": "https://github.com/openai/codex/commit/e78df1ea9d485e8b06fff4b4c075591d556be156" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-09T20:59:51Z", + "message": "Merge branch 'main' into etraut/background-terminal-apis", + "sha": "ff2201af2929455c6b826161b4f818663cfe7ebd", + "url": "https://github.com/openai/codex/commit/ff2201af2929455c6b826161b4f818663cfe7ebd" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-09T22:57:30Z", + "message": "Simplify background terminal APIs", + "sha": "b59232841e28b88559ec0f7d5c7cc602fbd2363a", + "url": "https://github.com/openai/codex/commit/b59232841e28b88559ec0f7d5c7cc602fbd2363a" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-10T00:01:33Z", + "message": "codex: address background terminal review feedback", + "sha": "b949dc3b1dbe0e25448dfcc4bba207deeb43a72a", + "url": "https://github.com/openai/codex/commit/b949dc3b1dbe0e25448dfcc4bba207deeb43a72a" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-10T00:30:51Z", + "message": "codex: simplify background terminal tests", + "sha": "7daf14053e74673697bca70290a4ddcee2d412d9", + "url": "https://github.com/openai/codex/commit/7daf14053e74673697bca70290a4ddcee2d412d9" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-10T17:29:14Z", + "message": "Address background terminal review comments", + "sha": "dcd1b73bce47bd514839ff4d71a48c12280ae413", + "url": "https://github.com/openai/codex/commit/dcd1b73bce47bd514839ff4d71a48c12280ae413" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-10T17:31:55Z", + "message": "Merge branch 'main' into etraut/background-terminal-apis", + "sha": "28b3f1ea589a2bc0090577c4a4d309f261318c57", + "url": "https://github.com/openai/codex/commit/28b3f1ea589a2bc0090577c4a4d309f261318c57" + }, + { + "author": "etraut-openai", + "committed_at": "2026-06-10T17:59:29Z", + "message": "codex: address PR review feedback (#26041)", + "sha": "cac8884c2c0540a586472f35c17dfb1cbe7a2697", + "url": "https://github.com/openai/codex/commit/cac8884c2c0540a586472f35c17dfb1cbe7a2697" + } + ], + "default_branch": "main", + "docs_refs": [ + "codex-rs/app-server/README.md" + ], + "examples_refs": [], + "extracted_flags": [ + "API", + "PID", + "GENERATED_TS_HEADER", + "GENERATED", + "CODE", + "NOT", + "MODIFY", + "HAND", + "IGNORED_DEFINITIONS", + "JSON_V1_ALLOWLIST", + "EXPERIMENTAL_CLIENT_METHOD_DEPENDENCY_TYPES", + "SPECIAL_DEFINITIONS", + "JSONRPCE", + "CODEX_THREAD_ID_ENV_VAR" + ], + "files": [ + { + "additions": 5, + "deletions": 2, + "patch_excerpt": "@@ -39,8 +39,11 @@ use ts_rs::TS;\n pub(crate) const GENERATED_TS_HEADER: &str = \"// GENERATED CODE! DO NOT MODIFY BY HAND!\\n\\n\";\n const IGNORED_DEFINITIONS: &[&str] = &[\"Option<()>\"];\n const JSON_V1_ALLOWLIST: &[&str] = &[\"InitializeParams\", \"InitializeResponse\"];\n-const EXPERIMENTAL_CLIENT_METHOD_DEPENDENCY_TYPES: &[&str] =\n- &[\"RemoteControlClient\", \"RemoteControlClientsListOrder\"];\n+const EXPERIMENTAL_CLIENT_METHOD_DEPENDENCY_TYPES: &[&str] = &[\n+ \"RemoteControlClient\",\n+ \"RemoteControlClientsListOrder\",\n+ \"ThreadBackgroundTerminal\",\n+];\n const SPECIAL_DEFINITIONS: &[&str] = &[\n \"ClientNotification\",\n \"ClientRequest\",", + "path": "codex-rs/app-server-protocol/src/export.rs", + "status": "modified" + }, + { + "additions": 60, + "deletions": 0, + "patch_excerpt": "@@ -574,6 +574,18 @@ client_request_definitions! {\n serialization: thread_id(params.thread_id),\n response: v2::ThreadBackgroundTerminalsCleanResponse,\n },\n+ #[experimental(\"thread/backgroundTerminals/list\")]\n+ ThreadBackgroundTerminalsList => \"thread/backgroundTerminals/list\" {\n+ params: v2::ThreadBackgroundTerminalsListParams,\n+ serialization: thread_id(params.thread_id),\n+ response: v2::ThreadBackgroundTerminalsListResponse,\n+ },\n+ #[experimental(\"thread/backgroundTerminals/terminate\")]\n+ ThreadBackgroundTerminalsTerminate => \"thread/backgroundTerminals/terminate\" {\n+ params: v2::ThreadBackgroundTerminalsTerminateParams,\n+ serialization: thread_id(params.thread_id),\n+ response: v2::ThreadBackgroundTerminalsTerminateResponse,\n+ },\n ThreadRollback => \"thread/rollback\" {\n params: v2::ThreadRollback...", + "path": "codex-rs/app-server-protocol/src/protocol/common.rs", + "status": "modified" + }, + { + "additions": 51, + "deletions": 0, + "patch_excerpt": "@@ -935,6 +935,57 @@ pub struct ThreadBackgroundTerminalsCleanParams {\n #[ts(export_to = \"v2/\")]\n pub struct ThreadBackgroundTerminalsCleanResponse {}\n \n+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]\n+#[serde(rename_all = \"camelCase\")]\n+#[ts(export_to = \"v2/\")]\n+pub struct ThreadBackgroundTerminalsListParams {\n+ pub thread_id: String,\n+ /// Opaque pagination cursor returned by a previous call.\n+ #[ts(optional = nullable)]\n+ pub cursor: Option,\n+ /// Optional page size.\n+ #[ts(optional = nullable)]\n+ pub limit: Option,\n+}\n+\n+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]\n+#[serde(rename_all = \"camelCase\")]\n+#[ts(export_to = \"v2/\")]\n+pub struct ThreadBackgroundTerminal {\n+ pub item_id: String,\n+ pub process_id: String,\n+ pub command: String,\n+ pub cwd: AbsolutePathBuf,\n+ pub os_pid: Op...", + "path": "codex-rs/app-server-protocol/src/protocol/v2/thread.rs", + "status": "modified" + }, + { + "additions": 28, + "deletions": 0, + "patch_excerpt": "@@ -157,6 +157,8 @@ Example with notification opt-out:\n - `thread/compact/start` — trigger conversation history compaction for a thread; returns `{}` immediately while progress streams through standard turn/item notifications.\n - `thread/shellCommand` — run a user-initiated `!` shell command against a thread; this runs unsandboxed with full access rather than inheriting the thread sandbox policy. Returns `{}` immediately while progress streams through standard turn/item notifications and any active turn receives the formatted output in its message stream.\n - `thread/backgroundTerminals/clean` — terminate all running background terminals for a thread (experimental; requires `capabilities.experimentalApi`); returns `{}` when the cleanup request is accepted.\n+- `thread/backgroundTerminals/list` — list running background terminals for a loaded thread (experimental; requires `capabilities.exp...", + "path": "codex-rs/app-server/README.md", + "status": "modified" + }, + { + "additions": 10, + "deletions": 0, + "patch_excerpt": "@@ -1126,6 +1126,16 @@ impl MessageProcessor {\n .thread_background_terminals_clean(&request_id, params)\n .await\n }\n+ ClientRequest::ThreadBackgroundTerminalsList { params, .. } => {\n+ self.thread_processor\n+ .thread_background_terminals_list(params)\n+ .await\n+ }\n+ ClientRequest::ThreadBackgroundTerminalsTerminate { params, .. } => {\n+ self.thread_processor\n+ .thread_background_terminals_terminate(params)\n+ .await\n+ }\n ClientRequest::ThreadRollback { params, .. } => {\n self.thread_processor\n .thread_rollback(&request_id, params)", + "path": "codex-rs/app-server/src/message_processor.rs", + "status": "modified" + }, + { + "additions": 5, + "deletions": 0, + "patch_excerpt": "@@ -171,8 +171,13 @@ use codex_app_server_protocol::ThreadApproveGuardianDeniedActionResponse;\n use codex_app_server_protocol::ThreadArchiveParams;\n use codex_app_server_protocol::ThreadArchiveResponse;\n use codex_app_server_protocol::ThreadArchivedNotification;\n+use codex_app_server_protocol::ThreadBackgroundTerminal;\n use codex_app_server_protocol::ThreadBackgroundTerminalsCleanParams;\n use codex_app_server_protocol::ThreadBackgroundTerminalsCleanResponse;\n+use codex_app_server_protocol::ThreadBackgroundTerminalsListParams;\n+use codex_app_server_protocol::ThreadBackgroundTerminalsListResponse;\n+use codex_app_server_protocol::ThreadBackgroundTerminalsTerminateParams;\n+use codex_app_server_protocol::ThreadBackgroundTerminalsTerminateResponse;\n use codex_app_server_protocol::ThreadClosedNotification;\n use codex_app_server_protocol::ThreadCompactStartParams;\n use codex_app_server_protocol:...", + "path": "codex-rs/app-server/src/request_processors.rs", + "status": "modified" + }, + { + "additions": 94, + "deletions": 0, + "patch_excerpt": "@@ -569,6 +569,24 @@ impl ThreadRequestProcessor {\n .map(|response| Some(response.into()))\n }\n \n+ pub(crate) async fn thread_background_terminals_list(\n+ &self,\n+ params: ThreadBackgroundTerminalsListParams,\n+ ) -> Result, JSONRPCErrorError> {\n+ self.thread_background_terminals_list_inner(params)\n+ .await\n+ .map(|response| Some(response.into()))\n+ }\n+\n+ pub(crate) async fn thread_background_terminals_terminate(\n+ &self,\n+ params: ThreadBackgroundTerminalsTerminateParams,\n+ ) -> Result, JSONRPCErrorError> {\n+ self.thread_background_terminals_terminate_inner(params)\n+ .await\n+ .map(|response| Some(response.into()))\n+ }\n+\n pub(crate) async fn thread_rollback(\n &self,\n request_id: &ConnectionReq...", + "path": "codex-rs/app-server/src/request_processors/thread_processor.rs", + "status": "modified" + }, + { + "additions": 59, + "deletions": 0, + "patch_excerpt": "@@ -36,6 +36,65 @@ mod thread_list_cwd_filter_tests {\n }\n }\n \n+mod background_terminal_pagination_tests {\n+ use super::super::paginate_background_terminals;\n+ use codex_app_server_protocol::ThreadBackgroundTerminal;\n+ use codex_utils_absolute_path::AbsolutePathBuf;\n+ use pretty_assertions::assert_eq;\n+\n+ fn terminal(process_id: &str) -> ThreadBackgroundTerminal {\n+ let cwd = if cfg!(windows) { r\"C:\\tmp\" } else { \"/tmp\" };\n+\n+ ThreadBackgroundTerminal {\n+ item_id: format!(\"item-{process_id}\"),\n+ process_id: process_id.to_string(),\n+ command: format!(\"command-{process_id}\"),\n+ cwd: AbsolutePathBuf::from_absolute_path(cwd).expect(\"absolute cwd\"),\n+ os_pid: None,\n+ cpu_percent: None,\n+ rss_kb: None,\n+ }\n+ }\n+\n+ #[test]\n+ fn paginates_with_process_id_cursor() {\n+ ...", + "path": "codex-rs/app-server/src/request_processors/thread_processor_tests.rs", + "status": "modified" + }, + { + "additions": 19, + "deletions": 0, + "patch_excerpt": "@@ -159,6 +159,14 @@ pub struct CodexThread {\n out_of_band_elicitation_count: Mutex,\n }\n \n+#[derive(Debug, Eq, PartialEq)]\n+pub struct BackgroundTerminalInfo {\n+ pub item_id: String,\n+ pub process_id: String,\n+ pub command: String,\n+ pub cwd: AbsolutePathBuf,\n+}\n+\n /// Conduit for the bidirectional stream of messages that compose a thread\n /// (formerly called a conversation) in Codex.\n impl CodexThread {\n@@ -396,6 +404,17 @@ impl CodexThread {\n self.codex.agent_status().await\n }\n \n+ pub async fn list_background_terminals(&self) -> Vec {\n+ self.codex.session.list_background_terminals().await\n+ }\n+\n+ pub async fn terminate_background_terminal(&self, process_id: i32) -> bool {\n+ self.codex\n+ .session\n+ .terminate_background_terminal(process_id)\n+ .await\n+ }\n+\n pub(crate) ...", + "path": "codex-rs/core/src/codex_thread.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -19,6 +19,7 @@ mod codex_thread;\n mod compact_remote;\n mod compact_remote_v2;\n mod config_lock;\n+pub use codex_thread::BackgroundTerminalInfo;\n pub use codex_thread::CodexThread;\n pub use codex_thread::CodexThreadSettingsOverrides;\n pub use codex_thread::ThreadConfigSnapshot;", + "path": "codex-rs/core/src/lib.rs", + "status": "modified" + }, + { + "additions": 12, + "deletions": 0, + "patch_excerpt": "@@ -21,6 +21,7 @@ use tracing::info_span;\n use tracing::trace;\n use tracing::warn;\n \n+use crate::codex_thread::BackgroundTerminalInfo;\n use crate::config::Config;\n use crate::context::ContextualUserFragment;\n use crate::hook_runtime::inspect_pending_input;\n@@ -782,6 +783,17 @@ impl Session {\n .await;\n }\n \n+ pub(crate) async fn list_background_terminals(&self) -> Vec {\n+ self.services.unified_exec_manager.list_processes().await\n+ }\n+\n+ pub(crate) async fn terminate_background_terminal(&self, process_id: i32) -> bool {\n+ self.services\n+ .unified_exec_manager\n+ .terminate_process(process_id)\n+ .await\n+ }\n+\n async fn handle_task_abort(self: &Arc, task: RunningTask, reason: TurnAbortReason) {\n let sub_id = task.turn_context.sub_id.clone();\n if task.cancellation_token.is...", + "path": "codex-rs/core/src/tasks/mod.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 0, + "patch_excerpt": "@@ -155,6 +155,8 @@ struct ProcessEntry {\n process: Arc,\n call_id: String,\n process_id: i32,\n+ cwd: AbsolutePathBuf,\n+ initial_exec_command_active: Arc,\n hook_command: String,\n tty: bool,\n network_approval: Option,", + "path": "codex-rs/core/src/unified_exec/mod.rs", + "status": "modified" + }, + { + "additions": 260, + "deletions": 0, + "patch_excerpt": "@@ -1,5 +1,6 @@\n use super::head_tail_buffer::HeadTailBuffer;\n use super::*;\n+use crate::codex_thread::BackgroundTerminalInfo;\n use crate::exec::ExecCapturePolicy;\n use crate::exec::ExecExpiration;\n use crate::sandboxing::ExecRequest;\n@@ -9,6 +10,16 @@ use crate::session::turn_context::TurnContext;\n use crate::tools::context::ExecCommandToolOutput;\n use crate::unified_exec::WriteStdinRequest;\n use crate::unified_exec::process::OutputHandles;\n+use async_trait::async_trait;\n+use codex_exec_server::ExecProcess;\n+use codex_exec_server::ExecProcessEventReceiver;\n+use codex_exec_server::ExecServerError;\n+use codex_exec_server::ProcessId;\n+use codex_exec_server::ProcessSignal;\n+use codex_exec_server::ReadResponse;\n+use codex_exec_server::StartedExecProcess;\n+use codex_exec_server::WriteResponse;\n+use codex_exec_server::WriteStatus;\n use codex_sandboxing::SandboxType;\n use codex_utils_output_tru...", + "path": "codex-rs/core/src/unified_exec/mod_tests.rs", + "status": "modified" + }, + { + "additions": 23, + "deletions": 4, + "patch_excerpt": "@@ -195,9 +195,16 @@ impl UnifiedExecProcess {\n }\n }\n \n- pub(super) fn terminate(&self) {\n+ fn finish_termination(&self) {\n self.output_closed.store(true, Ordering::Release);\n self.output_closed_notify.notify_waiters();\n+ self.cancellation_token.cancel();\n+ if let Some(output_task) = &self.output_task {\n+ output_task.abort();\n+ }\n+ }\n+\n+ pub(super) fn terminate(&self) {\n match &self.process_handle {\n ProcessHandle::Local(process_handle) => process_handle.terminate(),\n ProcessHandle::ExecServer(process_handle) => {\n@@ -207,10 +214,22 @@ impl UnifiedExecProcess {\n });\n }\n }\n- self.cancellation_token.cancel();\n- if let Some(output_task) = &self.output_task {\n- output_task.abort();\n+ self.finish_termination();\n+ }\n+\n+ ...", + "path": "codex-rs/core/src/unified_exec/process.rs", + "status": "modified" + }, + { + "additions": 105, + "deletions": 26, + "patch_excerpt": "@@ -11,6 +11,7 @@ use tokio::time::Duration;\n use tokio::time::Instant;\n use tokio_util::sync::CancellationToken;\n \n+use crate::codex_thread::BackgroundTerminalInfo;\n use crate::exec_env::CODEX_THREAD_ID_ENV_VAR;\n use crate::exec_env::create_env;\n use crate::exec_policy::ExecApprovalRequest;\n@@ -175,11 +176,22 @@ struct PreparedProcessHandles {\n pause_state: Option>,\n session: Option>,\n network_approval: Option,\n+ call_id: String,\n hook_command: String,\n process_id: i32,\n tty: bool,\n }\n \n+struct InitialExecCommandGuard {\n+ active: Arc,\n+}\n+\n+impl Drop for InitialExecCommandGuard {\n+ fn drop(&mut self) {\n+ self.active.store(false, Ordering::Release);\n+ }\n+}\n+\n fn exec_server_process_id(process_id: i32) -> String {\n process_id.to_string()\n }\n@@ -41...", + "path": "codex-rs/core/src/unified_exec/process_manager.rs", + "status": "modified" + }, + { + "additions": 39, + "deletions": 4, + "patch_excerpt": "@@ -22,6 +22,7 @@ struct MockExecProcess {\n process_id: ProcessId,\n write_response: WriteResponse,\n read_responses: Mutex>,\n+ terminate_error: Option,\n wake_tx: watch::Sender,\n }\n \n@@ -69,11 +70,17 @@ impl ExecProcess for MockExecProcess {\n }\n \n async fn terminate(&self) -> Result<(), ExecServerError> {\n+ if let Some(message) = &self.terminate_error {\n+ return Err(ExecServerError::Protocol(message.clone()));\n+ }\n Ok(())\n }\n }\n \n-async fn remote_process(write_status: WriteStatus) -> UnifiedExecProcess {\n+async fn remote_process(\n+ write_status: WriteStatus,\n+ terminate_error: Option,\n+) -> UnifiedExecProcess {\n let (wake_tx, _wake_rx) = watch::channel(0);\n let started = StartedExecProcess {\n process: Arc::new(MockExecProcess {\n@@ -82,6 +89,7 @@ async fn remote_...", + "path": "codex-rs/core/src/unified_exec/process_tests.rs", + "status": "modified" + } + ], + "linked_issues": [ + "#26041" + ], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Summary\r\n\r\nCodex Apps needs app-server as the source of truth for chat-started background terminals instead of guessing from local process trees.\r\n\r\nThis PR adds experimental v2 APIs to list and terminate background terminals for a loaded thread using app-server process ids, so clients can manage background terminals without local PID discovery.\r\n\r\n## Changes\r\n\r\n- `thread/backgroundTerminals/list` returns paginated background terminal records with `itemId`, app-server `processId`, `command`, `cwd`, nullable `osPid`, nullable `cpuPercent`, and nullable `rssKb`.\r\n- `thread/backgroundTerminals/terminate` terminates one running background terminal by app-server `processId` and returns whether a process was terminated.\r\n- Background terminal list and terminate operations use unified-exec process manager state as their source of truth.\r\n", + "labels": [], + "merged_at": "2026-06-10T18:18:09Z", + "number": 26041, + "state": "merged", + "title": "Add app-server background terminal process APIs", + "url": "https://github.com/openai/codex/pull/26041" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-27259.json b/artifacts/github/bundles/openai-codex-pr-27259.json new file mode 100644 index 000000000..9675be691 --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27259.json @@ -0,0 +1,191 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "charliemarsh-oai", + "committed_at": "2026-06-08T23:42:05Z", + "message": "Abort startup prewarm during shutdown", + "sha": "935cbc2e7b35c10516254001991b9a0bf7c2f069", + "url": "https://github.com/openai/codex/commit/935cbc2e7b35c10516254001991b9a0bf7c2f069" + }, + { + "author": "charliemarsh-oai", + "committed_at": "2026-06-10T13:17:51Z", + "message": "Use latest-wins MCP manager replacement", + "sha": "cc60f5411157f765bb97ece8f79ef25d3795101f", + "url": "https://github.com/openai/codex/commit/cc60f5411157f765bb97ece8f79ef25d3795101f" + }, + { + "author": "charliemarsh-oai", + "committed_at": "2026-06-10T14:14:30Z", + "message": "Use ArcSwap for MCP manager publication", + "sha": "6adf517d65bfdf08aaac21e35e5dfa7f6ce08b19", + "url": "https://github.com/openai/codex/commit/6adf517d65bfdf08aaac21e35e5dfa7f6ce08b19" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "MCP", + "CODEX_APPS_MCP_SERVER_NAME", + "REMOTE_GLOBAL_MARKETPLACE_NAME" + ], + "files": [ + { + "additions": 4, + "deletions": 14, + "patch_excerpt": "@@ -348,24 +348,14 @@ impl McpConnectionManager {\n !self.clients.is_empty()\n }\n \n- /// Drain all MCP clients from this manager and return a future that stops\n- /// them and terminates their stdio server processes.\n- pub fn begin_shutdown(&mut self) -> impl std::future::Future + Send + 'static {\n+ /// Stop all MCP clients owned by this manager and terminate stdio server processes.\n+ pub async fn shutdown(&self) {\n self.startup_cancellation_token.cancel();\n- let clients = std::mem::take(&mut self.clients);\n- self.server_metadata.clear();\n- async move {\n- for client in clients.into_values() {\n- client.shutdown().await;\n- }\n+ for client in self.clients.values() {\n+ client.shutdown().await;\n }\n }\n \n- /// Stop all MCP clients owned by this manager and termi...", + "path": "codex-rs/codex-mcp/src/connection_manager.rs", + "status": "modified" + }, + { + "additions": 42, + "deletions": 0, + "patch_excerpt": "@@ -980,6 +980,48 @@ async fn list_all_tools_blocks_while_client_is_pending_without_cached_tool_info_\n assert!(timeout_result.is_err());\n }\n \n+#[tokio::test]\n+async fn shutdown_cancels_pending_tool_listing() {\n+ let cancel_token = CancellationToken::new();\n+ let cancel_token_for_startup = cancel_token.clone();\n+ let (started_tx, started_rx) = tokio::sync::oneshot::channel();\n+ let pending_client = async move {\n+ let _ = started_tx.send(());\n+ cancel_token_for_startup.cancelled().await;\n+ Err(StartupOutcomeError::Cancelled)\n+ }\n+ .boxed()\n+ .shared();\n+ let approval_policy = Constrained::allow_any(AskForApproval::OnFailure);\n+ let permission_profile = Constrained::allow_any(PermissionProfile::default());\n+ let mut manager = McpConnectionManager::new_uninitialized(\n+ &approval_policy,\n+ &permission_profile,\n+ /*pre...", + "path": "codex-rs/codex-mcp/src/connection_manager_tests.rs", + "status": "modified" + }, + { + "additions": 4, + "deletions": 36, + "patch_excerpt": "@@ -452,7 +452,7 @@ async fn delegated_mcp_guardian_abort_returns_synthetic_decline_answer() {\n }\n \n #[tokio::test]\n-async fn delegated_mcp_user_reviewer_waits_for_metadata_lookup() {\n+async fn delegated_mcp_user_reviewer_returns_none_without_metadata() {\n let (parent_session, parent_ctx, _rx_events) =\n crate::session::tests::make_session_and_context_with_rx().await;\n let pending_mcp_invocations = Arc::new(Mutex::new(HashMap::from([(\n@@ -464,21 +464,6 @@ async fn delegated_mcp_user_reviewer_waits_for_metadata_lookup() {\n },\n )])));\n let cancel_token = CancellationToken::new();\n- let manager = Arc::clone(&parent_session.services.mcp_connection_manager);\n- let (manager_locked_tx, manager_locked_rx) = std::sync::mpsc::sync_channel(0);\n- let (release_manager_tx, release_manager_rx) = std::sync::mpsc::sync_channel(0);\n- let manager_lock = tokio::tas...", + "path": "codex-rs/core/src/codex_delegate_tests.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -287,7 +287,7 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_mcp_manager(\n drop(rx_event);\n \n let cancel_token = CancellationToken::new();\n- let mut mcp_connection_manager = McpConnectionManager::new(\n+ let mcp_connection_manager = McpConnectionManager::new(\n &mcp_servers,\n config.mcp_oauth_credentials_store_mode,\n auth_status_entries,", + "path": "codex-rs/core/src/connectors.rs", + "status": "modified" + }, + { + "additions": 7, + "deletions": 28, + "patch_excerpt": "@@ -319,8 +319,7 @@ async fn handle_approved_mcp_tool_call(\n let server_origin = sess\n .services\n .mcp_connection_manager\n- .read()\n- .await\n+ .load_full()\n .server_origin(&server)\n .map(str::to_string);\n \n@@ -606,8 +605,7 @@ async fn maybe_request_codex_apps_auth_elicitation(\n if !sess\n .services\n .mcp_connection_manager\n- .read()\n- .await\n+ .load_full()\n .is_host_owned_codex_apps_server(server)\n {\n return result;\n@@ -669,13 +667,9 @@ async fn maybe_request_codex_apps_auth_elicitation(\n auth_elicitation_completed_result(&plan.auth_failure, result.meta)\n }\n \n-#[expect(\n- clippy::await_holding_invalid_type,\n- reason = \"Codex Apps cache refresh reads through the session-owned manager guard\"\n-)]\n async fn refresh_codex_apps_after_connector_auth(sess: &Session, turn...", + "path": "codex-rs/core/src/mcp_tool_call.rs", + "status": "modified" + }, + { + "additions": 4, + "deletions": 1, + "patch_excerpt": "@@ -1280,7 +1280,10 @@ async fn install_host_owned_codex_apps_manager(session: &Session, turn_context:\n /*elicitation_reviewer*/ None,\n )\n .await;\n- *session.services.mcp_connection_manager.write().await = manager;\n+ session\n+ .services\n+ .mcp_connection_manager\n+ .store(Arc::new(manager));\n }\n \n #[tokio::test]", + "path": "codex-rs/core/src/mcp_tool_call_tests.rs", + "status": "modified" + }, + { + "additions": 8, + "deletions": 5, + "patch_excerpt": "@@ -580,6 +580,9 @@ pub async fn set_thread_memory_mode(sess: &Arc, sub_id: String, mode: T\n }\n \n async fn shutdown_session_runtime(sess: &Arc) {\n+ if let Some(startup_prewarm) = sess.take_session_startup_prewarm().await {\n+ startup_prewarm.abort().await;\n+ }\n sess.abort_all_tasks(TurnAbortReason::Interrupted).await;\n let _ = sess.conversation.shutdown().await;\n sess.services\n@@ -589,11 +592,11 @@ async fn shutdown_session_runtime(sess: &Arc) {\n if let Err(err) = sess.services.code_mode_service.shutdown().await {\n warn!(\"failed to shutdown code mode session: {err}\");\n }\n- let mcp_shutdown = {\n- let mut manager = sess.services.mcp_connection_manager.write().await;\n- manager.begin_shutdown()\n- };\n- mcp_shutdown.await;\n+ sess.services\n+ .mcp_connection_manager\n+ .load_full()\n+ ...", + "path": "codex-rs/core/src/session/handlers.rs", + "status": "modified" + }, + { + "additions": 10, + "deletions": 34, + "patch_excerpt": "@@ -91,8 +91,7 @@ impl Session {\n if self\n .services\n .mcp_connection_manager\n- .read()\n- .await\n+ .load_full()\n .elicitations_auto_deny()\n {\n return McpServerElicitationOutcome {\n@@ -226,67 +225,47 @@ impl Session {\n \n self.services\n .mcp_connection_manager\n- .read()\n- .await\n+ .load_full()\n .resolve_elicitation(server_name, id, response)\n .await\n }\n \n- #[expect(\n- clippy::await_holding_invalid_type,\n- reason = \"MCP resource calls are serialized through the session-owned manager guard\"\n- )]\n pub async fn list_resources(\n &self,\n server: &str,\n params: Option,\n ) -> anyhow::Result {\n self.services\n ...", + "path": "codex-rs/core/src/session/mcp.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -793,7 +793,7 @@ impl Codex {\n ..Default::default()\n })\n .await?;\n- let mcp_connection_manager = self.session.services.mcp_connection_manager.read().await;\n+ let mcp_connection_manager = self.session.services.mcp_connection_manager.load_full();\n mcp_connection_manager.set_elicitations_auto_deny(mcp_elicitations_auto_deny);\n Ok(())\n }\n@@ -2748,10 +2748,6 @@ impl Session {\n }\n }\n \n- #[expect(\n- clippy::await_holding_invalid_type,\n- reason = \"MCP app context rendering reads through the session-owned manager guard\"\n- )]\n pub(crate) async fn build_initial_context(\n &self,\n turn_context: &TurnContext,\n@@ -2844,7 +2840,7 @@ impl Session {\n }\n }\n if turn_context.config.include_apps_instructions && turn_context.apps_enabled() {\n- l...", + "path": "codex-rs/core/src/session/mod.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 2, + "patch_excerpt": "@@ -981,13 +981,13 @@ impl Session {\n // before any MCP-related events. It is reasonable to consider\n // changing this to use Option or OnceCell, though the current\n // setup is straightforward enough and performs well.\n- mcp_connection_manager: Arc::new(RwLock::new(\n+ mcp_connection_manager: arc_swap::ArcSwap::from_pointee(\n McpConnectionManager::new_uninitialized_with_permission_profile(\n &config.permissions.approval_policy,\n config.permissions.permission_profile(),\n config.prefix_mcp_tool_names(),\n ),\n- )),\n+ ),\n mcp_startup_cancellation_token: Mutex::new(CancellationToken::new()),\n unified_exec_manager: UnifiedExecProcessManager::new(\n ...", + "path": "codex-rs/core/src/session/session.rs", + "status": "modified" + }, + { + "additions": 6, + "deletions": 12, + "patch_excerpt": "@@ -319,8 +319,7 @@ async fn request_mcp_server_elicitation_auto_accepts_when_auto_deny_is_enabled()\n session\n .services\n .mcp_connection_manager\n- .read()\n- .await\n+ .load_full()\n .set_elicitations_auto_deny(/*auto_deny*/ true);\n \n let requested_schema: McpElicitationSchema = serde_json::from_value(json!({\n@@ -4816,13 +4815,13 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {\n );\n \n let services = SessionServices {\n- mcp_connection_manager: Arc::new(RwLock::new(\n+ mcp_connection_manager: arc_swap::ArcSwap::from_pointee(\n McpConnectionManager::new_uninitialized_with_permission_profile(\n &config.permissions.approval_policy,\n config.permissions.permission_profile(),\n config.prefix_mcp_tool_names(),\n ),\n- )),\n+ ...", + "path": "codex-rs/core/src/session/tests.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 17, + "patch_excerpt": "@@ -432,10 +432,6 @@ async fn run_hooks_and_record_inputs(\n blocked_input && !accepted_user_input\n }\n \n-#[expect(\n- clippy::await_holding_invalid_type,\n- reason = \"MCP tool listing borrows the read guard across cancellation-aware await\"\n-)]\n #[instrument(level = \"trace\", skip_all)]\n async fn build_skills_and_plugins(\n sess: &Arc,\n@@ -473,8 +469,7 @@ async fn build_skills_and_plugins(\n match sess\n .services\n .mcp_connection_manager\n- .read()\n- .await\n+ .load_full()\n .list_all_tools()\n .or_cancel(cancellation_token)\n .await\n@@ -1078,10 +1073,6 @@ async fn run_sampling_request(\n }\n }\n \n-#[expect(\n- clippy::await_holding_invalid_type,\n- reason = \"tool router construction reads through the session-owned manager guard\"\n-)]\n #[instrument(level = \"trace\",\n ski...", + "path": "codex-rs/core/src/session/turn.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 1, + "patch_excerpt": "@@ -724,7 +724,7 @@ impl Session {\n .unwrap_or_else(|| session_configuration.cwd().clone());\n let per_turn_config = Self::build_per_turn_config(&session_configuration, cwd.clone());\n {\n- let mcp_connection_manager = self.services.mcp_connection_manager.read().await;\n+ let mcp_connection_manager = self.services.mcp_connection_manager.load_full();\n mcp_connection_manager.set_approval_policy(&session_configuration.approval_policy);\n mcp_connection_manager\n .set_permission_profile(session_configuration.permission_profile());", + "path": "codex-rs/core/src/session/turn_context.rs", + "status": "modified" + }, + { + "additions": 8, + "deletions": 2, + "patch_excerpt": "@@ -4,6 +4,7 @@ use std::time::Instant;\n \n use tokio::task::JoinHandle;\n use tokio_util::sync::CancellationToken;\n+use tokio_util::task::AbortOnDropHandle;\n use tracing::info;\n use tracing::warn;\n \n@@ -19,7 +20,7 @@ use codex_protocol::error::Result as CodexResult;\n use codex_protocol::models::BaseInstructions;\n \n pub(crate) struct SessionStartupPrewarmHandle {\n- task: JoinHandle>,\n+ task: AbortOnDropHandle>,\n started_at: Instant,\n timeout: Duration,\n }\n@@ -40,12 +41,17 @@ impl SessionStartupPrewarmHandle {\n timeout: Duration,\n ) -> Self {\n Self {\n- task,\n+ task: AbortOnDropHandle::new(task),\n started_at,\n timeout,\n }\n }\n \n+ pub(crate) async fn abort(self) {\n+ self.task.abort();\n+ let _ = self.task.await;\n+ }\n+\n ...", + "path": "codex-rs/core/src/session_startup_prewarm.rs", + "status": "modified" + }, + { + "additions": 4, + "deletions": 9, + "patch_excerpt": "@@ -35,12 +35,12 @@ use codex_thread_store::ThreadStore;\n use std::path::PathBuf;\n use tokio::runtime::Handle;\n use tokio::sync::Mutex;\n-use tokio::sync::RwLock;\n use tokio::sync::watch;\n use tokio_util::sync::CancellationToken;\n \n pub(crate) struct SessionServices {\n- pub(crate) mcp_connection_manager: Arc>,\n+ /// The latest manager; callers retain an owned handle while performing MCP I/O.\n+ pub(crate) mcp_connection_manager: ArcSwap,\n pub(crate) mcp_startup_cancellation_token: Mutex,\n pub(crate) unified_exec_manager: UnifiedExecProcessManager,\n #[cfg_attr(not(unix), allow(dead_code))]\n@@ -87,18 +87,13 @@ pub(crate) struct SessionServices {\n impl SessionServices {\n /// Installs the manager before validating required servers so startup-time elicitation can\n /// resolve through the session's...", + "path": "codex-rs/core/src/state/service.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 6, + "patch_excerpt": "@@ -40,10 +40,6 @@ impl ToolExecutor for ListMcpResourceTemplatesHandler {\n true\n }\n \n- #[expect(\n- clippy::await_holding_invalid_type,\n- reason = \"MCP resource template listing reads through the session-owned manager guard\"\n- )]\n async fn handle(\n &self,\n invocation: ToolInvocation,\n@@ -107,8 +103,7 @@ impl ToolExecutor for ListMcpResourceTemplatesHandler {\n let templates = session\n .services\n .mcp_connection_manager\n- .read()\n- .await\n+ .load_full()\n .list_all_resource_templates()\n .await;\n Ok(ListResourceTemplatesPayload::from_all_servers(templates))", + "path": "codex-rs/core/src/tools/handlers/mcp_resource/list_mcp_resource_templates.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 6, + "patch_excerpt": "@@ -40,10 +40,6 @@ impl ToolExecutor for ListMcpResourcesHandler {\n true\n }\n \n- #[expect(\n- clippy::await_holding_invalid_type,\n- reason = \"MCP resource listing reads through the session-owned manager guard\"\n- )]\n async fn handle(\n &self,\n invocation: ToolInvocation,\n@@ -105,8 +101,7 @@ impl ToolExecutor for ListMcpResourcesHandler {\n let resources = session\n .services\n .mcp_connection_manager\n- .read()\n- .await\n+ .load_full()\n .list_all_resources()\n .await;\n Ok(ListResourcesPayload::from_all_servers(resources))", + "path": "codex-rs/core/src/tools/handlers/mcp_resource/list_mcp_resources.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 5, + "patch_excerpt": "@@ -304,10 +304,6 @@ fn is_remote_plugin_install_suggestion(plugin_id: &str) -> bool {\n .is_some_and(|(_, marketplace_name)| marketplace_name == REMOTE_GLOBAL_MARKETPLACE_NAME)\n }\n \n-#[expect(\n- clippy::await_holding_invalid_type,\n- reason = \"connector cache refresh reads through the session-owned manager guard\"\n-)]\n async fn refresh_missing_requested_connectors(\n session: &crate::session::session::Session,\n turn: &crate::session::turn_context::TurnContext,\n@@ -319,7 +315,7 @@ async fn refresh_missing_requested_connectors(\n return Some(Vec::new());\n }\n \n- let manager = session.services.mcp_connection_manager.read().await;\n+ let manager = session.services.mcp_connection_manager.load_full();\n let mcp_tools = manager.list_all_tools().await;\n let accessible_connectors = connectors::with_app_enabled_state(\n connectors::accessible_connect...", + "path": "codex-rs/core/src/tools/handlers/request_plugin_install.rs", + "status": "modified" + }, + { + "additions": 1, + "deletions": 6, + "patch_excerpt": "@@ -95,17 +95,12 @@ fn extension_tool_test_registry() -> Arc> {\n }\n \n #[tokio::test]\n-#[expect(\n- clippy::await_holding_invalid_type,\n- reason = \"test builds a router from session-owned MCP manager state\"\n-)]\n async fn parallel_support_does_not_match_namespaced_local_tool_names() -> anyhow::Result<()> {\n let (session, turn) = make_session_and_context().await;\n let mcp_tools = session\n .services\n .mcp_connection_manager\n- .read()\n- .await\n+ .load_full()\n .list_all_tools()\n .await;\n let router = ToolRouter::from_turn_context(", + "path": "codex-rs/core/src/tools/router_tests.rs", + "status": "modified" + }, + { + "additions": 46, + "deletions": 0, + "patch_excerpt": "@@ -558,6 +558,52 @@ async fn stdio_server_round_trip() -> anyhow::Result<()> {\n Ok(())\n }\n \n+#[tokio::test(flavor = \"multi_thread\", worker_threads = 2)]\n+async fn shutdown_cancels_startup_prewarm_waiting_for_mcp_startup() -> anyhow::Result<()> {\n+ skip_if_no_network!(Ok(()));\n+\n+ let server = responses::start_websocket_server(vec![vec![vec![\n+ responses::ev_response_created(\"warm-1\"),\n+ responses::ev_completed(\"warm-1\"),\n+ ]]])\n+ .await;\n+ let pending_mcp_listener = tokio::net::TcpListener::bind(\"127.0.0.1:0\").await?;\n+ let pending_mcp_url = format!(\"http://{}/mcp\", pending_mcp_listener.local_addr()?);\n+\n+ let fixture = test_codex()\n+ .with_config(move |config| {\n+ insert_mcp_server(\n+ config,\n+ \"shutdown_prewarm\",\n+ McpServerTransportConfig::StreamableHttp {\n+ url: p...", + "path": "codex-rs/core/tests/suite/rmcp_client.rs", + "status": "modified" + } + ], + "linked_issues": [], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Summary\n\nWe originally addressed startup prewarming holding the read side of `RwLock` by snapshotting tool-list state. Review feedback identified the broader ownership problem: the outer synchronization should only publish or retrieve the current manager, while MCP operations rely on the manager's internal synchronization. A follow-up preserved operation retirement with a separate gate, but further review questioned whether that synchronization was actually required and whether we could support latest-wins replacement instead.\n\nThis PR now stores the current MCP manager in `ArcSwap`. Each operation uses `load_full()` to obtain an owned `Arc`, then performs MCP I/O without retaining the publication mechanism. Refresh cancels obsolete startup work, constructs a replacement, and atomically publishes it. New operations see the latest manager, while operations that already loaded the previous manager retain a valid handle. Refresh happens at a turn boundary, so there should be no active user tool calls to drain.\n\nGit history supports dropping the outer `RwLock`. It was introduced in `03ffe4d595` on November 17, 2025 for non-blocking MCP startup: the session published an empty manager, startup initialized that same object while holding the write lock, and readers waited for initialization. `7cd2e84026` on February 19, 2026 removed that two-phase initialization in favor of constructing a fresh manager and swapping it in, explicitly noting that `Option` or `OnceCell` could replace the placeholder design. Hot reload later reused the existing lock to publish a replacement, but I found no indication that the lock was introduced to guarantee in-flight tool calls finish before refresh or shutdown.\n\nTerminal shutdown remains separate from refresh: it aborts startup prewarming and active tasks before shutting down the current manager, so tool calls may be interrupted and no model WebSocket work continues after shutdown. Focused regression coverage exercises pending tool-list cancellation, deferred refresh, and startup-prewarm shutdown.\n", + "labels": [], + "merged_at": "2026-06-10T15:33:21Z", + "number": 27259, + "state": "merged", + "title": "Use latest-wins MCP manager replacement", + "url": "https://github.com/openai/codex/pull/27259" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/bundles/openai-codex-pr-27304.json b/artifacts/github/bundles/openai-codex-pr-27304.json new file mode 100644 index 000000000..cc775fcef --- /dev/null +++ b/artifacts/github/bundles/openai-codex-pr-27304.json @@ -0,0 +1,438 @@ +{ + "analysis_mode": "pr_first", + "commits": [ + { + "author": "anp-oai", + "committed_at": "2026-06-10T05:40:28Z", + "message": "Remove async trait from tool executor", + "sha": "fffd0036afdb23efb940be1527158f985bda5d6e", + "url": "https://github.com/openai/codex/commit/fffd0036afdb23efb940be1527158f985bda5d6e" + } + ], + "default_branch": "main", + "docs_refs": [], + "examples_refs": [], + "extracted_flags": [ + "PUBLIC_TOOL_NAME", + "WAIT_TOOL_NAME", + "LIST_AVAILABLE_PLUGINS_TO_INSTALL_TOOL_NAME", + "MULTI_AGENT_V1_NAMESPACE", + "REQUEST_PLUGIN_INSTALL_TOOL_NAME", + "REQUEST_USER_INPUT_TOOL_NAME", + "BARRIERS", + "TOOL_SEARCH_TOOL_NAME", + "WEB_NAMESPACE", + "RUN_TOOL_NAME" + ], + "files": [ + { + "additions": 0, + "deletions": 5, + "patch_excerpt": "@@ -3047,7 +3047,6 @@ name = \"codex-goal-extension\"\n version = \"0.0.0\"\n dependencies = [\n \"anyhow\",\n- \"async-trait\",\n \"chrono\",\n \"codex-analytics\",\n \"codex-core\",\n@@ -3101,7 +3100,6 @@ dependencies = [\n name = \"codex-image-generation-extension\"\n version = \"0.0.0\"\n dependencies = [\n- \"async-trait\",\n \"codex-api\",\n \"codex-core\",\n \"codex-extension-api\",\n@@ -3301,7 +3299,6 @@ dependencies = [\n name = \"codex-memories-extension\"\n version = \"0.0.0\"\n dependencies = [\n- \"async-trait\",\n \"codex-core\",\n \"codex-extension-api\",\n \"codex-features\",\n@@ -3916,7 +3913,6 @@ dependencies = [\n name = \"codex-tools\"\n version = \"0.0.0\"\n dependencies = [\n- \"async-trait\",\n \"codex-app-server-protocol\",\n \"codex-code-mode\",\n \"codex-features\",\n@@ -4297,7 +4293,6 @@ dependencies = [\n name = \"codex-web-search-extension\"\n version = \"0.0.0\"\n dependencies = [\n- \"async-trait\",\n \"codex-api\",\n \"codex-core\",\n \"c...", + "path": "codex-rs/Cargo.lock", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -91,7 +91,6 @@ impl CodeModeExecuteHandler {\n }\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for CodeModeExecuteHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(PUBLIC_TOOL_NAME)\n@@ -101,11 +100,8 @@ impl ToolExecutor for CodeModeExecuteHandler {\n self.spec.clone()\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/code_mode/execute_handler.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -44,7 +44,6 @@ where\n })\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for CodeModeWaitHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(WAIT_TOOL_NAME)\n@@ -54,11 +53,8 @@ impl ToolExecutor for CodeModeWaitHandler {\n create_wait_tool()\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/code_mode/wait_handler.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -13,7 +13,6 @@ use super::*;\n \n pub struct ReportAgentJobResultHandler;\n \n-#[async_trait::async_trait]\n impl ToolExecutor for ReportAgentJobResultHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"report_agent_job_result\")\n@@ -23,11 +22,8 @@ impl ToolExecutor for ReportAgentJobResultHandler {\n create_report_agent_job_result_tool()\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/agent_jobs/report_agent_job_result.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -14,7 +14,6 @@ use super::*;\n \n pub struct SpawnAgentsOnCsvHandler;\n \n-#[async_trait::async_trait]\n impl ToolExecutor for SpawnAgentsOnCsvHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"spawn_agents_on_csv\")\n@@ -24,11 +23,8 @@ impl ToolExecutor for SpawnAgentsOnCsvHandler {\n create_spawn_agents_on_csv_tool()\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/agent_jobs/spawn_agents_on_csv.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -305,7 +305,6 @@ async fn effective_patch_permissions(\n )\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for ApplyPatchHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"apply_patch\")\n@@ -315,11 +314,8 @@ impl ToolExecutor for ApplyPatchHandler {\n create_apply_patch_freeform_tool(self.multi_environment)\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/apply_patch.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -61,7 +61,6 @@ impl DynamicToolHandler {\n }\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for DynamicToolHandler {\n fn tool_name(&self) -> ToolName {\n self.tool_name.clone()\n@@ -86,11 +85,8 @@ impl ToolExecutor for DynamicToolHandler {\n )\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/dynamic.rs", + "status": "modified" + }, + { + "additions": 21, + "deletions": 34, + "patch_excerpt": "@@ -11,13 +11,11 @@ use codex_tools::ToolSpec;\n use codex_tools::TurnItemEmissionFuture;\n use codex_tools::TurnItemEmitter;\n \n-use crate::function_tool::FunctionCallError;\n use crate::session::session::Session;\n use crate::session::turn_context::TurnContext;\n use crate::stream_events_utils::TurnItemContributorPolicy;\n use crate::stream_events_utils::finalize_turn_item;\n use crate::tools::context::ToolInvocation;\n-use crate::tools::context::ToolOutput;\n use crate::tools::context::ToolPayload;\n use crate::tools::registry::CoreToolRuntime;\n use crate::tools::registry::ToolExecutor;\n@@ -30,7 +28,6 @@ impl ExtensionToolAdapter {\n }\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for ExtensionToolAdapter {\n fn tool_name(&self) -> ToolName {\n self.0.tool_name()\n@@ -52,11 +49,8 @@ impl ToolExecutor for ExtensionToolAdapter {\n self.0.se...", + "path": "codex-rs/core/src/tools/handlers/extension_tools.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -54,7 +54,6 @@ impl ListAvailablePluginsToInstallHandler {\n }\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for ListAvailablePluginsToInstallHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(LIST_AVAILABLE_PLUGINS_TO_INSTALL_TOOL_NAME)\n@@ -68,11 +67,8 @@ impl ToolExecutor for ListAvailablePluginsToInstallHandler {\n false\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/list_available_plugins_to_install.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -64,7 +64,6 @@ fn ensure_mcp_prefix(name: &str) -> String {\n }\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for McpHandler {\n fn tool_name(&self) -> ToolName {\n self.tool_info.canonical_tool_name()\n@@ -113,11 +112,8 @@ impl ToolExecutor for McpHandler {\n )\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/mcp.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -26,7 +26,6 @@ use super::serialize_function_output;\n \n pub struct ListMcpResourceTemplatesHandler;\n \n-#[async_trait::async_trait]\n impl ToolExecutor for ListMcpResourceTemplatesHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"list_mcp_resource_templates\")\n@@ -40,11 +39,8 @@ impl ToolExecutor for ListMcpResourceTemplatesHandler {\n true\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/mcp_resource/list_mcp_resource_templates.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -26,7 +26,6 @@ use super::serialize_function_output;\n \n pub struct ListMcpResourcesHandler;\n \n-#[async_trait::async_trait]\n impl ToolExecutor for ListMcpResourcesHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"list_mcp_resources\")\n@@ -40,11 +39,8 @@ impl ToolExecutor for ListMcpResourcesHandler {\n true\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/mcp_resource/list_mcp_resources.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -26,7 +26,6 @@ use super::serialize_function_output;\n \n pub struct ReadMcpResourceHandler;\n \n-#[async_trait::async_trait]\n impl ToolExecutor for ReadMcpResourceHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"read_mcp_resource\")\n@@ -40,11 +39,8 @@ impl ToolExecutor for ReadMcpResourceHandler {\n true\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/mcp_resource/read_mcp_resource.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -6,7 +6,6 @@ use codex_tools::ToolSpec;\n \n pub(crate) struct Handler;\n \n-#[async_trait::async_trait]\n impl ToolExecutor for Handler {\n fn tool_name(&self) -> ToolName {\n ToolName::namespaced(MULTI_AGENT_V1_NAMESPACE, \"close_agent\")\n@@ -23,11 +22,8 @@ impl ToolExecutor for Handler {\n )\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- handle_close_agent(invocation).await.map(boxed_tool_output)\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(async move { handle_close_agent(invocation).await.map(boxed_tool_output) })\n }\n }", + "path": "codex-rs/core/src/tools/handlers/multi_agents/close_agent.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -7,7 +7,6 @@ use std::sync::Arc;\n \n pub(crate) struct Handler;\n \n-#[async_trait::async_trait]\n impl ToolExecutor for Handler {\n fn tool_name(&self) -> ToolName {\n ToolName::namespaced(MULTI_AGENT_V1_NAMESPACE, \"resume_agent\")\n@@ -24,11 +23,8 @@ impl ToolExecutor for Handler {\n )\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- handle_resume_agent(invocation).await.map(boxed_tool_output)\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(async move { handle_resume_agent(invocation).await.map(boxed_tool_output) })\n }\n }", + "path": "codex-rs/core/src/tools/handlers/multi_agents/resume_agent.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -6,7 +6,6 @@ use codex_tools::ToolSpec;\n \n pub(crate) struct Handler;\n \n-#[async_trait::async_trait]\n impl ToolExecutor for Handler {\n fn tool_name(&self) -> ToolName {\n ToolName::namespaced(MULTI_AGENT_V1_NAMESPACE, \"send_input\")\n@@ -23,11 +22,8 @@ impl ToolExecutor for Handler {\n )\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/multi_agents/send_input.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -22,7 +22,6 @@ impl Handler {\n }\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for Handler {\n fn tool_name(&self) -> ToolName {\n ToolName::namespaced(MULTI_AGENT_V1_NAMESPACE, \"spawn_agent\")\n@@ -39,11 +38,8 @@ impl ToolExecutor for Handler {\n )\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- handle_spawn_agent(invocation).await.map(boxed_tool_output)\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(async move { handle_spawn_agent(invocation).await.map(boxed_tool_output) })\n }\n }", + "path": "codex-rs/core/src/tools/handlers/multi_agents/spawn.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -27,7 +27,6 @@ impl Handler {\n }\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for Handler {\n fn tool_name(&self) -> ToolName {\n ToolName::namespaced(MULTI_AGENT_V1_NAMESPACE, \"wait_agent\")\n@@ -44,11 +43,8 @@ impl ToolExecutor for Handler {\n )\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/multi_agents/wait.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -7,7 +7,6 @@ use codex_tools::ToolSpec;\n \n pub(crate) struct Handler;\n \n-#[async_trait::async_trait]\n impl ToolExecutor for Handler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"followup_task\")\n@@ -17,11 +16,8 @@ impl ToolExecutor for Handler {\n create_followup_task_tool()\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/multi_agents_v2/followup_task.rs", + "status": "modified" + }, + { + "additions": 6, + "deletions": 8, + "patch_excerpt": "@@ -6,7 +6,6 @@ use codex_tools::ToolSpec;\n \n pub(crate) struct Handler;\n \n-#[async_trait::async_trait]\n impl ToolExecutor for Handler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"interrupt_agent\")\n@@ -16,13 +15,12 @@ impl ToolExecutor for Handler {\n create_interrupt_agent_tool_v2()\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- handle_interrupt_agent(invocation)\n- .await\n- .map(boxed_tool_output)\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(async move {\n+ handle_interrupt_agent(invocation)\n+ .await\n+ .map(boxed_tool_output)\n+ })\n }\n }", + "path": "codex-rs/core/src/tools/handlers/multi_agents_v2/interrupt_agent.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -5,7 +5,6 @@ use codex_tools::ToolSpec;\n \n pub(crate) struct Handler;\n \n-#[async_trait::async_trait]\n impl ToolExecutor for Handler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"list_agents\")\n@@ -15,11 +14,8 @@ impl ToolExecutor for Handler {\n create_list_agents_tool()\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/multi_agents_v2/list_agents.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -7,7 +7,6 @@ use codex_tools::ToolSpec;\n \n pub(crate) struct Handler;\n \n-#[async_trait::async_trait]\n impl ToolExecutor for Handler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"send_message\")\n@@ -17,11 +16,8 @@ impl ToolExecutor for Handler {\n create_send_message_tool()\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/multi_agents_v2/send_message.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -22,7 +22,6 @@ impl Handler {\n }\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for Handler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"spawn_agent\")\n@@ -32,11 +31,8 @@ impl ToolExecutor for Handler {\n create_spawn_agent_tool_v2(self.options.clone())\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- handle_spawn_agent(invocation).await.map(boxed_tool_output)\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(async move { handle_spawn_agent(invocation).await.map(boxed_tool_output) })\n }\n }", + "path": "codex-rs/core/src/tools/handlers/multi_agents_v2/spawn.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -19,7 +19,6 @@ impl Handler {\n }\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for Handler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"wait_agent\")\n@@ -29,11 +28,8 @@ impl ToolExecutor for Handler {\n create_wait_agent_tool_v2(self.options)\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/multi_agents_v2/wait.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -45,7 +45,6 @@ impl ToolOutput for PlanToolOutput {\n }\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for PlanHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"update_plan\")\n@@ -55,11 +54,8 @@ impl ToolExecutor for PlanHandler {\n create_update_plan_tool()\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/plan.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -25,7 +25,6 @@ struct RequestPermissionsEnvironmentArgs {\n environment_id: Option,\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for RequestPermissionsHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"request_permissions\")\n@@ -35,11 +34,8 @@ impl ToolExecutor for RequestPermissionsHandler {\n create_request_permissions_tool(request_permissions_tool_description())\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/request_permissions.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -48,7 +48,6 @@ impl RequestPluginInstallHandler {\n }\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for RequestPluginInstallHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(REQUEST_PLUGIN_INSTALL_TOOL_NAME)\n@@ -62,11 +61,8 @@ impl ToolExecutor for RequestPluginInstallHandler {\n true\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/request_plugin_install.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -20,7 +20,6 @@ pub struct RequestUserInputHandler {\n pub available_modes: Vec,\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for RequestUserInputHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(REQUEST_USER_INPUT_TOOL_NAME)\n@@ -30,11 +29,8 @@ impl ToolExecutor for RequestUserInputHandler {\n create_request_user_input_tool(request_user_input_tool_description(&self.available_modes))\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/request_user_input.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -124,7 +124,6 @@ impl From for ShellCommandHandler {\n }\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for ShellCommandHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"shell_command\")\n@@ -141,11 +140,8 @@ impl ToolExecutor for ShellCommandHandler {\n true\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/shell/shell_command.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -57,7 +57,6 @@ fn barrier_map() -> &'static tokio::sync::Mutex> {\n BARRIERS.get_or_init(|| tokio::sync::Mutex::new(HashMap::new()))\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for TestSyncHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"test_sync_tool\")\n@@ -71,11 +70,8 @@ impl ToolExecutor for TestSyncHandler {\n true\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/test_sync.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -53,7 +53,6 @@ impl ToolSearchHandler {\n }\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for ToolSearchHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(TOOL_SEARCH_TOOL_NAME)\n@@ -67,11 +66,8 @@ impl ToolExecutor for ToolSearchHandler {\n true\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/tool_search.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -71,7 +71,6 @@ impl ExecCommandHandler {\n }\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for ExecCommandHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"exec_command\")\n@@ -92,11 +91,8 @@ impl ToolExecutor for ExecCommandHandler {\n true\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/unified_exec/exec_command.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -31,7 +31,6 @@ struct WriteStdinArgs {\n \n pub struct WriteStdinHandler;\n \n-#[async_trait::async_trait]\n impl ToolExecutor for WriteStdinHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"write_stdin\")\n@@ -41,11 +40,8 @@ impl ToolExecutor for WriteStdinHandler {\n create_write_stdin_tool()\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/unified_exec/write_stdin.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -64,7 +64,6 @@ enum ViewImageDetail {\n Original,\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for ViewImageHandler {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(\"view_image\")\n@@ -78,11 +77,8 @@ impl ToolExecutor for ViewImageHandler {\n true\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handle_call(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(invocation))\n }\n }", + "path": "codex-rs/core/src/tools/handlers/view_image.rs", + "status": "modified" + }, + { + "additions": 10, + "deletions": 16, + "patch_excerpt": "@@ -257,7 +257,6 @@ mod tests {\n tool_name: codex_tools::ToolName,\n }\n \n- #[async_trait::async_trait]\n impl ToolExecutor for ImmediateHandler {\n fn tool_name(&self) -> codex_tools::ToolName {\n self.tool_name.clone()\n@@ -274,14 +273,13 @@ mod tests {\n })\n }\n \n- async fn handle(\n- &self,\n- _invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- Ok(Box::new(FunctionToolOutput::from_text(\n- \"ok\".to_string(),\n- Some(true),\n- )))\n+ fn handle(&self, _invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(async {\n+ Ok(\n+ Box::new(FunctionToolOutput::from_text(\"ok\".to_string(), Some(true)))\n+ ...", + "path": "codex-rs/core/src/tools/parallel.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 6, + "patch_excerpt": "@@ -250,7 +250,6 @@ struct ExposureOverride {\n exposure: ToolExposure,\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for ExposureOverride {\n fn tool_name(&self) -> ToolName {\n self.handler.tool_name()\n@@ -272,11 +271,8 @@ impl ToolExecutor for ExposureOverride {\n self.handler.search_info()\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- self.handler.handle(invocation).await\n+ fn handle(&self, invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ self.handler.handle(invocation)\n }\n }", + "path": "codex-rs/core/src/tools/registry.rs", + "status": "modified" + }, + { + "additions": 13, + "deletions": 15, + "patch_excerpt": "@@ -5,7 +5,6 @@ struct TestHandler {\n tool_name: codex_tools::ToolName,\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for TestHandler {\n fn tool_name(&self) -> codex_tools::ToolName {\n self.tool_name.clone()\n@@ -15,13 +14,15 @@ impl ToolExecutor for TestHandler {\n test_spec(&self.tool_name)\n }\n \n- async fn handle(\n- &self,\n- _invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- Ok(Box::new(\n- crate::tools::context::FunctionToolOutput::from_text(\"ok\".to_string(), Some(true)),\n- ))\n+ fn handle(&self, _invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(async {\n+ Ok(\n+ Box::new(crate::tools::context::FunctionToolOutput::from_text(\n+ \"ok\".to_str...", + "path": "codex-rs/core/src/tools/registry_tests.rs", + "status": "modified" + }, + { + "additions": 3, + "deletions": 7, + "patch_excerpt": "@@ -44,7 +44,6 @@ impl codex_extension_api::ToolContributor for ExtensionEchoContributor {\n \n struct ExtensionEchoExecutor;\n \n-#[async_trait::async_trait]\n impl ToolExecutor for ExtensionEchoExecutor {\n fn tool_name(&self) -> ToolName {\n ToolName::namespaced(\"extension/\", \"echo\")\n@@ -73,11 +72,8 @@ impl ToolExecutor for ExtensionEchoExecutor {\n })\n }\n \n- async fn handle(\n- &self,\n- call: ExtensionToolCall,\n- ) -> Result, codex_tools::FunctionCallError> {\n- self.handle_call(call).await\n+ fn handle(&self, call: ExtensionToolCall) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(call))\n }\n }\n \n@@ -93,7 +89,7 @@ impl ExtensionEchoExecutor {\n \"callId\": call.call_id,\n \"conversationHistory\": call.conversation_history....", + "path": "codex-rs/core/src/tools/router_tests.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 7, + "patch_excerpt": "@@ -72,7 +72,6 @@ use codex_tools::ToolCall as ExtensionToolCall;\n use codex_tools::ToolEnvironmentMode;\n use codex_tools::ToolExecutor;\n use codex_tools::ToolName;\n-use codex_tools::ToolOutput;\n use codex_tools::ToolSearchInfo;\n use codex_tools::ToolSpec;\n use codex_tools::UnifiedExecShellMode;\n@@ -946,7 +945,6 @@ struct MultiAgentV2NamespaceOverride {\n namespace: String,\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for MultiAgentV2NamespaceOverride {\n fn tool_name(&self) -> ToolName {\n ToolName::namespaced(self.namespace.clone(), self.handler.tool_name().name)\n@@ -975,11 +973,8 @@ impl ToolExecutor for MultiAgentV2NamespaceOverride {\n self.handler.search_info()\n }\n \n- async fn handle(\n- &self,\n- invocation: ToolInvocation,\n- ) -> Result, codex_tools::FunctionCallError> {\n- ...", + "path": "codex-rs/core/src/tools/spec_plan.rs", + "status": "modified" + }, + { + "additions": 6, + "deletions": 12, + "patch_excerpt": "@@ -285,7 +285,6 @@ fn use_bedrock_provider(turn: &mut TurnContext) {\n \n struct WebRunExtensionTool;\n \n-#[async_trait::async_trait]\n impl ToolExecutor for WebRunExtensionTool {\n fn tool_name(&self) -> ToolName {\n ToolName::namespaced(\"web\", \"run\")\n@@ -306,17 +305,15 @@ impl ToolExecutor for WebRunExtensionTool {\n })\n }\n \n- async fn handle(\n- &self,\n- _call: ExtensionToolCall,\n- ) -> Result, codex_tools::FunctionCallError> {\n- Ok(Box::new(codex_tools::JsonToolOutput::new(json!({}))))\n+ fn handle(&self, _call: ExtensionToolCall) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(async {\n+ Ok(Box::new(codex_tools::JsonToolOutput::new(json!({}))) as Box)\n+ })\n }\n }\n \n struct DeferredExtensionTool;\n \n-#[async_trait::async_trait]\n impl To...", + "path": "codex-rs/core/src/tools/spec_plan_tests.rs", + "status": "modified" + }, + { + "additions": 7, + "deletions": 9, + "patch_excerpt": "@@ -30,7 +30,6 @@ struct TestHandler {\n tool_name: codex_tools::ToolName,\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for TestHandler {\n fn tool_name(&self) -> codex_tools::ToolName {\n self.tool_name.clone()\n@@ -47,14 +46,13 @@ impl ToolExecutor for TestHandler {\n })\n }\n \n- async fn handle(\n- &self,\n- _invocation: ToolInvocation,\n- ) -> Result, FunctionCallError> {\n- Ok(Box::new(FunctionToolOutput::from_text(\n- \"ok\".to_string(),\n- Some(true),\n- )))\n+ fn handle(&self, _invocation: ToolInvocation) -> codex_tools::ToolExecutorFuture<'_> {\n+ Box::pin(async {\n+ Ok(\n+ Box::new(FunctionToolOutput::from_text(\"ok\".to_string(), Some(true)))\n+ as Box for GoalToolExecutor {\n fn tool_name(&self) -> ToolName {\n ToolName::plain(match self.kind {\n@@ -150,12 +148,14 @@ impl ToolExecutor for GoalToolExecutor {\n }\n }\n \n- async fn handle(&self, invocation: ToolCall) -> Result, FunctionCallError> {\n- match self.kind {\n- GoalToolKind::Get => self.handle_get(invocation).await,\n- GoalToolKind::Create => self.handle_create(invocation).await,\n- GoalToolKind::Update => self.handle_update(invocation).await,\n- }\n+ fn handle(&self, invocation: ToolCall) -> co...", + "path": "codex-rs/ext/goal/src/tool.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 1, + "patch_excerpt": "@@ -13,7 +13,6 @@ doctest = false\n workspace = true\n \n [dependencies]\n-async-trait = { workspace = true }\n codex-api = { workspace = true }\n codex-core = { workspace = true }\n codex-extension-api = { workspace = true }", + "path": "codex-rs/ext/image-generation/Cargo.toml", + "status": "modified" + }, + { + "additions": 2, + "deletions": 3, + "patch_excerpt": "@@ -78,7 +78,6 @@ struct ImagegenArgs {\n num_last_images_to_include: Option,\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for ImageGenerationTool {\n /// Keeps the tool in the existing image-generation Responses namespace.\n fn tool_name(&self) -> ToolName {\n@@ -96,8 +95,8 @@ impl ToolExecutor for ImageGenerationTool {\n }\n \n /// Executes the selected image operation and returns the completed image result.\n- async fn handle(&self, call: ToolCall) -> Result, FunctionCallError> {\n- self.handle_call(call).await\n+ fn handle(&self, call: ToolCall) -> codex_extension_api::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(call))\n }\n }", + "path": "codex-rs/ext/image-generation/src/tool.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 1, + "patch_excerpt": "@@ -13,7 +13,6 @@ doctest = false\n workspace = true\n \n [dependencies]\n-async-trait = { workspace = true }\n codex-core = { workspace = true }\n codex-extension-api = { workspace = true }\n codex-features = { workspace = true }", + "path": "codex-rs/ext/memories/Cargo.toml", + "status": "modified" + }, + { + "additions": 2, + "deletions": 7, + "patch_excerpt": "@@ -41,7 +41,6 @@ pub(super) struct AddAdHocNoteTool {\n pub(super) metrics_client: Option,\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for AddAdHocNoteTool\n where\n B: MemoriesBackend,\n@@ -57,12 +56,8 @@ where\n )\n }\n \n- async fn handle(\n- &self,\n- call: ToolCall,\n- ) -> Result, codex_extension_api::FunctionCallError>\n- {\n- self.handle_call(call).await\n+ fn handle(&self, call: ToolCall) -> codex_extension_api::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(call))\n }\n }", + "path": "codex-rs/ext/memories/src/tools/ad_hoc_note.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 7, + "patch_excerpt": "@@ -39,7 +39,6 @@ pub(super) struct ListTool {\n pub(super) metrics_client: Option,\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for ListTool\n where\n B: MemoriesBackend,\n@@ -55,12 +54,8 @@ where\n )\n }\n \n- async fn handle(\n- &self,\n- call: ToolCall,\n- ) -> Result, codex_extension_api::FunctionCallError>\n- {\n- self.handle_call(call).await\n+ fn handle(&self, call: ToolCall) -> codex_extension_api::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(call))\n }\n }", + "path": "codex-rs/ext/memories/src/tools/list.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 7, + "patch_excerpt": "@@ -38,7 +38,6 @@ pub(super) struct ReadTool {\n pub(super) metrics_client: Option,\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for ReadTool\n where\n B: MemoriesBackend,\n@@ -54,12 +53,8 @@ where\n )\n }\n \n- async fn handle(\n- &self,\n- call: ToolCall,\n- ) -> Result, codex_extension_api::FunctionCallError>\n- {\n- self.handle_call(call).await\n+ fn handle(&self, call: ToolCall) -> codex_extension_api::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(call))\n }\n }", + "path": "codex-rs/ext/memories/src/tools/read.rs", + "status": "modified" + }, + { + "additions": 2, + "deletions": 7, + "patch_excerpt": "@@ -47,7 +47,6 @@ pub(super) struct SearchTool {\n pub(super) metrics_client: Option,\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for SearchTool\n where\n B: MemoriesBackend,\n@@ -63,12 +62,8 @@ where\n )\n }\n \n- async fn handle(\n- &self,\n- call: ToolCall,\n- ) -> Result, codex_extension_api::FunctionCallError>\n- {\n- self.handle_call(call).await\n+ fn handle(&self, call: ToolCall) -> codex_extension_api::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(call))\n }\n }", + "path": "codex-rs/ext/memories/src/tools/search.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 1, + "patch_excerpt": "@@ -13,7 +13,6 @@ doctest = false\n workspace = true\n \n [dependencies]\n-async-trait = { workspace = true }\n codex-api = { workspace = true }\n codex-core = { workspace = true }\n codex-extension-api = { workspace = true }", + "path": "codex-rs/ext/web-search/Cargo.toml", + "status": "modified" + }, + { + "additions": 2, + "deletions": 3, + "patch_excerpt": "@@ -39,7 +39,6 @@ pub(crate) struct WebSearchTool {\n pub(crate) settings: SearchSettings,\n }\n \n-#[async_trait::async_trait]\n impl ToolExecutor for WebSearchTool {\n fn tool_name(&self) -> ToolName {\n ToolName::namespaced(WEB_NAMESPACE, RUN_TOOL_NAME)\n@@ -74,8 +73,8 @@ impl ToolExecutor for WebSearchTool {\n true\n }\n \n- async fn handle(&self, call: ToolCall) -> Result, FunctionCallError> {\n- self.handle_call(call).await\n+ fn handle(&self, call: ToolCall) -> codex_extension_api::ToolExecutorFuture<'_> {\n+ Box::pin(self.handle_call(call))\n }\n }", + "path": "codex-rs/ext/web-search/src/tool.rs", + "status": "modified" + }, + { + "additions": 0, + "deletions": 1, + "patch_excerpt": "@@ -8,7 +8,6 @@ version.workspace = true\n workspace = true\n \n [dependencies]\n-async-trait = { workspace = true }\n codex-app-server-protocol = { workspace = true }\n codex-code-mode = { workspace = true }\n codex-features = { workspace = true }", + "path": "codex-rs/tools/Cargo.toml", + "status": "modified" + }, + { + "additions": 1, + "deletions": 0, + "patch_excerpt": "@@ -93,6 +93,7 @@ pub use tool_discovery::ToolSearchSourceInfo;\n pub use tool_discovery::collect_request_plugin_install_entries;\n pub use tool_discovery::filter_request_plugin_install_discoverable_tools_for_client;\n pub use tool_executor::ToolExecutor;\n+pub use tool_executor::ToolExecutorFuture;\n pub use tool_executor::ToolExposure;\n pub use tool_output::JsonToolOutput;\n pub use tool_output::ToolOutput;", + "path": "codex-rs/tools/src/lib.rs", + "status": "modified" + }, + { + "additions": 7, + "deletions": 5, + "patch_excerpt": "@@ -3,6 +3,12 @@ use crate::ToolName;\n use crate::ToolOutput;\n use crate::ToolSearchInfo;\n use crate::ToolSpec;\n+use std::future::Future;\n+use std::pin::Pin;\n+\n+/// The boxed future returned by [`ToolExecutor::handle`].\n+pub type ToolExecutorFuture<'a> =\n+ Pin, FunctionCallError>> + Send + 'a>>;\n \n /// Controls where a tool is exposed to the model.\n #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n@@ -40,7 +46,6 @@ impl ToolExposure {\n /// Implementations keep the model-visible spec tied to the executable runtime.\n /// Host crates can layer routing, hooks, telemetry, or other orchestration on\n /// top without reopening the spec/runtime split.\n-#[async_trait::async_trait]\n pub trait ToolExecutor: Send + Sync {\n /// The concrete tool name handled by this runtime instance.\n fn tool_name(&self) -> ToolName;\n@@ -60,8 +65,5 ...", + "path": "codex-rs/tools/src/tool_executor.rs", + "status": "modified" + } + ], + "linked_issues": [ + "#27299" + ], + "notes": [ + "Built from GitHub pull-request, commits, files, and repo endpoints." + ], + "primary_pr": { + "body": "## Why\r\n\r\nWe're now [discouraging use of `async_trait`](https://github.com/openai/codex/pull/20242).\r\n\r\nRemoving use of `async_trait` from `ToolExecutor` yields a `codex_core` debug test build speedup of ~78% (from 227.5s to 50.3s) on my machine.\r\n\r\nStacked on #27299, this PR applies the trait change after the handler bodies have been outlined.\r\n\r\n## What\r\n\r\nChanged `ToolExecutor::handle` to return an explicit boxed `ToolExecutorFuture` instead of using `async_trait`.\r\n\r\nUpdated ToolExecutor implementors to return `Box::pin(...)`, reexported the future alias through `codex-tools` and `codex-extension-api`, and removed `codex-tools` direct `async-trait` dependency.", + "labels": [], + "merged_at": "2026-06-10T17:26:53Z", + "number": 27304, + "state": "merged", + "title": "[codex] Remove async_trait from ToolExecutor", + "url": "https://github.com/openai/codex/pull/27304" + }, + "repo": "openai/codex", + "schema": "github_change_bundle/v1" +} diff --git a/artifacts/github/impact/openai-codex-pr-25018.json b/artifacts/github/impact/openai-codex-pr-25018.json new file mode 100644 index 000000000..76ad172cb --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-25018.json @@ -0,0 +1,47 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-25018", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Add app-server `thread/delete` API", + "url": "https://github.com/openai/codex/pull/25018", + "meta": "Merged 2026-06-10T18:22:12Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/25018", + "meta": "artifacts/github/reviews/openai-codex-pr-25018.review.json" + } + ] + }, + "observed_change": "Codex added app-server v2 `thread/delete` request and `thread/deleted` notification support for hard-deleting active or archived threads plus spawned descendants and associated local state.", + "public_signal_decision": "publish", + "control_plane_impact": "compat_risk", + "publisher_angle": "operator_impact", + "confidence": "confirmed", + "evidence": [ + "The upstream review records new `ThreadDelete` request and `ThreadDeleted` notification protocol wiring.", + "The app-server README documents hard-delete semantics for active or archived threads and spawned descendants.", + "Local thread-store code deletes rollout files before reporting success.", + "State DB deletion expands to associated goals, spawn edges, logs, dynamic tools, and agent job assignments." + ], + "candidate_followups": [ + "Gate Decodex hard-delete UX behind explicit operator confirmation and clear archive-versus-delete wording.", + "Update thread list and subagent caches on `thread/deleted` notifications.", + "Define Decodex policy for remote thread stores before exposing a generic delete path." + ], + "social_notes": [ + "Public copy should stress hard delete and descendant cleanup, not just a new method name.", + "Avoid presenting delete as reversible archive.", + "Mention `thread/deleted` notifications because clients must update local state." + ], + "caveats": [ + "Remote or hosted thread-store behavior may differ until implemented.", + "The operation is intentionally destructive.", + "The PR is app-server and local-store source evidence, not an endorsement to auto-delete user threads." + ] +} diff --git a/artifacts/github/impact/openai-codex-pr-26041.json b/artifacts/github/impact/openai-codex-pr-26041.json new file mode 100644 index 000000000..b7c211231 --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-26041.json @@ -0,0 +1,47 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-26041", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Add app-server background terminal process APIs", + "url": "https://github.com/openai/codex/pull/26041", + "meta": "Merged 2026-06-10T18:18:09Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/26041", + "meta": "artifacts/github/reviews/openai-codex-pr-26041.review.json" + } + ] + }, + "observed_change": "Codex added experimental app-server v2 APIs for listing and terminating background terminal processes for a loaded thread, using unified-exec process manager state as the source of truth.", + "public_signal_decision": "publish", + "control_plane_impact": "adopt_now", + "publisher_angle": "operator_impact", + "confidence": "confirmed", + "evidence": [ + "The upstream review records `thread/backgroundTerminals/list` and `thread/backgroundTerminals/terminate` as new experimental app-server methods.", + "Protocol structs include background terminal params, records, pagination, terminate params, and terminate response fields.", + "Unified-exec process manager state now carries metadata needed for background terminal records.", + "The app-server README documents the capability gate and method behavior." + ], + "candidate_followups": [ + "Create a bounded Decodex adoption issue for app-server background terminal list and terminate support.", + "Prefer app-server process ids over local OS PID discovery in operator UI where the new API is available.", + "Preserve nullability for OS PID, CPU, RSS, and other host-dependent terminal metadata." + ], + "social_notes": [ + "Public copy can present this as background terminal management moving into app-server protocol.", + "Call out that the APIs are experimental and capability-gated.", + "Use direct PR reference for the method names and fields." + ], + "caveats": [ + "The methods are experimental.", + "The APIs apply to loaded-thread background terminals managed by unified-exec.", + "Some process metadata fields are nullable." + ] +} diff --git a/artifacts/github/impact/openai-codex-pr-27259.json b/artifacts/github/impact/openai-codex-pr-27259.json new file mode 100644 index 000000000..1c3d76d93 --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27259.json @@ -0,0 +1,47 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27259", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Use latest-wins MCP manager replacement", + "url": "https://github.com/openai/codex/pull/27259", + "meta": "Merged 2026-06-10T15:33:21Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27259", + "meta": "artifacts/github/reviews/openai-codex-pr-27259.review.json" + } + ] + }, + "observed_change": "Codex replaced the session-owned `RwLock` publication path with latest-wins `ArcSwap` handles, so each MCP operation keeps the manager it loaded while refresh atomically publishes a replacement.", + "public_signal_decision": "publish", + "control_plane_impact": "compat_risk", + "publisher_angle": "operator_impact", + "confidence": "confirmed", + "evidence": [ + "The upstream review records the `ArcSwap` replacement in session services.", + "MCP tool, resource, elicitation, and connector paths now use `.load_full()` instead of holding an async read guard.", + "Session shutdown explicitly aborts startup prewarm before loading and shutting down the current MCP manager.", + "Regression coverage exercises pending tool-list cancellation and startup-prewarm shutdown." + ], + "candidate_followups": [ + "Audit Decodex MCP refresh and hosted-plugin probes for assumptions that manager refresh drains in-flight calls.", + "Treat manager handles as per-operation snapshots when reasoning about MCP tool, resource, and elicitation behavior.", + "Document any Control Plane expectations around terminal shutdown interrupting MCP operations." + ], + "social_notes": [ + "Frame this as a synchronization model change for MCP operators, not as a new MCP user command.", + "Mention latest-wins replacement and owned loaded handles.", + "Preserve the caveat that refresh is expected at turn boundaries." + ], + "caveats": [ + "The change is internal to Codex MCP manager ownership.", + "The PR does not claim arbitrary mid-tool-call refresh safety beyond retained manager handles.", + "Shutdown may still interrupt active terminal or model work." + ] +} diff --git a/artifacts/github/impact/openai-codex-pr-27304.json b/artifacts/github/impact/openai-codex-pr-27304.json new file mode 100644 index 000000000..ef17169af --- /dev/null +++ b/artifacts/github/impact/openai-codex-pr-27304.json @@ -0,0 +1,47 @@ +{ + "schema": "upstream_impact/v1", + "slug": "openai-codex-pr-27304", + "repo": "openai/codex", + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "[codex] Remove async_trait from ToolExecutor", + "url": "https://github.com/openai/codex/pull/27304", + "meta": "Merged 2026-06-10T17:26:53Z" + }, + { + "kind": "pull_request", + "title": "Source-backed Decodex upstream review", + "url": "https://github.com/openai/codex/pull/27304", + "meta": "artifacts/github/reviews/openai-codex-pr-27304.review.json" + } + ] + }, + "observed_change": "Codex removed `async_trait` from the `ToolExecutor` trait and converted handlers to return a boxed `ToolExecutorFuture` directly.", + "public_signal_decision": "publish", + "control_plane_impact": "compat_risk", + "publisher_angle": "practical_explainer", + "confidence": "confirmed", + "evidence": [ + "`ToolExecutorFuture<'a>` is added and exported from `codex-tools` and `codex-extension-api`.", + "`ToolExecutor` no longer carries the `#[async_trait::async_trait]` annotation.", + "Bundled extensions remove direct `async-trait` dependencies.", + "Tool handlers across core and extension surfaces now return explicit boxed futures." + ], + "candidate_followups": [ + "Check Decodex-owned extension examples for old async `ToolExecutor::handle` implementations.", + "Update migration notes or snippets to use `ToolExecutorFuture` and `Box::pin` when relevant.", + "Track stacked PR #27299 if a later review needs the full handler-outline context." + ], + "social_notes": [ + "Useful public copy should target extension authors and explain the new trait implementation shape.", + "Avoid implying a model-visible runtime feature was added.", + "Mention the PR body's build-speed motivation only as author-reported context." + ], + "caveats": [ + "The change is source/API facing for extension and tool implementors.", + "The PR body says it is stacked on #27299.", + "No app-server method or CLI command is added by this PR alone." + ] +} diff --git a/artifacts/github/review-queue/openai-codex-latest.json b/artifacts/github/review-queue/openai-codex-latest.json index 575c66f5d..765ed6e6e 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": 18, - "high": 11, + "critical": 17, + "high": 9, "low": 2, - "normal": 9, + "normal": 12, "published_subjects_seen": 0, "recent_commits_scanned": 40, "subjects_queued": 40 }, - "generated_at": "2026-06-10T20:05:18.910141Z", + "generated_at": "2026-06-11T02:04:53.013782Z", "repo": "openai/codex", "schema": "upstream_review_queue/v1", "source": { @@ -17,286 +17,6 @@ "signals_dir": "site/src/content/signals" }, "subjects": [ - { - "attention_flags": [ - "auth_account", - "breaking_change", - "deprecated_removed", - "new_feature", - "protocol_change", - "security_policy" - ], - "changed_file_count": 19, - "commit_shas": [ - "b564da44fb798e9e8e5c89ff912ab72a70a2ba9c", - "5b5caa9431c818837eadc6d01cdd64e3b597ce9d", - "83c0b87e952501c98f96957c3df2b2efa45352f7", - "9ff7581214ba17137fd351893766e6be3612ff05", - "e605b381502ef535c95f6e57798cc78a08703912", - "eb4878b26439e36e1446fc72da6a8b25b495bfd1" - ], - "committed_at": "2026-06-09T22:10:17Z", - "next_step": "ai_review_required", - "pr_number": 26734, - "pr_url": "https://github.com/openai/codex/pull/26734", - "review_priority": "critical", - "review_reason": "Needs AI review for auth_account, breaking_change, deprecated_removed, new_feature, protocol_change, security_policy.", - "sample_paths": [ - "codex-rs/core/src/unified_exec/process.rs", - "codex-rs/core/src/unified_exec/process_manager.rs", - "codex-rs/core/src/unified_exec/process_tests.rs", - "codex-rs/core/tests/suite/unified_exec.rs", - "codex-rs/exec-server/src/client.rs", - "codex-rs/exec-server/src/lib.rs", - "codex-rs/exec-server/src/local_process.rs", - "codex-rs/exec-server/src/process.rs", - "codex-rs/exec-server/src/protocol.rs", - "codex-rs/exec-server/src/remote_process.rs", - "codex-rs/exec-server/src/server/handler.rs", - "codex-rs/exec-server/src/server/process_handler.rs" - ], - "source_state": "merged", - "subject_id": "26734", - "subject_kind": "pr", - "surface_hints": [ - "app_server_protocol", - "cli_tui", - "tests_ci" - ], - "title": "[codex] Handle Ctrl-C for non-TTY unified exec", - "url": "https://github.com/openai/codex/pull/26734" - }, - { - "attention_flags": [ - "auth_account", - "deprecated_removed", - "new_feature", - "protocol_change" - ], - "changed_file_count": 7, - "commit_shas": [ - "09ce7e176bb3e35108cce7c74f9fe566125b6c87", - "821b679505e5021c4fc20a3c79070d96ff0aaa24", - "e992bdf9805440b068ffa2c031dbf3be0e244107", - "7c54723762ae9ebb3d2bbb95e71ac5000771c9c9", - "e4dca2440722489eb1d774ba0b273a4260f8d0e8", - "5f2b14e092e169c3f4c9f4b93c40e2560f95e2ca", - "d4b826175776c26f977b2e10a9dc9bc4a3030e41", - "daf24cccd232bb9cc3035eb9985d2bf3ee24cab5", - "b0a300fb9b9d8ee73be8d40f718eb7b9464a28be", - "6e4c8dfa31a5fbb6216e8465337d737b7327fb6a", - "dee789ab91d6883ca16fbc86aeadbf524d95c1ef", - "08503d6d13e7b7efc817299c30bcf34bd7b5b076", - "75e3421f94a530314154b3ff9dd51a1064f861f6", - "d1596f4595d17a2e2a94a8fc45c7ceb116079868", - "2e23b88414a393e9800c328bded2e6f2bd7ba7a7", - "b8760d31e0e00e07aa8db22afae4ab3fb9797af4", - "1f6a75d2ac149df0a0d8f99a531aeb65243c5d1b", - "9dfb4be0ec69162ea9b1b10008c160a333a99df1", - "ce34025915ff7df1c060ff0343c5b42c4f3453f9", - "7a381f591d4bd26aabb5312d89b5c5b3d47d5a9d", - "4f29a9e89e8f39e784336e83bc71dffc270f632d" - ], - "committed_at": "2026-06-09T22:49:48Z", - "next_step": "ai_review_required", - "pr_number": 25147, - "pr_url": "https://github.com/openai/codex/pull/25147", - "review_priority": "critical", - "review_reason": "Needs AI review for auth_account, deprecated_removed, new_feature, protocol_change.", - "sample_paths": [ - "codex-rs/rmcp-client/src/bin/test_streamable_http_server.rs", - "codex-rs/rmcp-client/src/http_client_adapter.rs", - "codex-rs/rmcp-client/src/rmcp_client.rs", - "codex-rs/rmcp-client/src/streamable_http_retry.rs", - "codex-rs/rmcp-client/src/streamable_http_retry_tests.rs", - "codex-rs/rmcp-client/tests/streamable_http_recovery.rs", - "codex-rs/rmcp-client/tests/streamable_http_test_support.rs" - ], - "source_state": "merged", - "subject_id": "25147", - "subject_kind": "pr", - "surface_hints": [ - "cli_tui", - "mcp_plugins", - "tests_ci" - ], - "title": "[codex] Retry streamable HTTP initialize failures", - "url": "https://github.com/openai/codex/pull/25147" - }, - { - "attention_flags": [ - "auth_account", - "deprecated_removed", - "new_feature", - "protocol_change", - "rate_limit", - "release_packaging", - "security_policy" - ], - "changed_file_count": 7, - "commit_shas": [ - "52f9ec871bedbd53a0c0741d0b7bc47e3dae3da9", - "4d32a96c7580b022e5d906bcbb2eb283b80338f7", - "ddd48cab1b5d617e8a01737c0665f4025b351421", - "44448048dcad4a75a1d7056f23cc87691438268c", - "ccf0f0d9892fd9b253401466d6dee7a1886ac407", - "9da50461f3d23990d484b3ba29b5cb293269d23f" - ], - "committed_at": "2026-06-09T23:34:38Z", - "next_step": "ai_review_required", - "pr_number": 26701, - "pr_url": "https://github.com/openai/codex/pull/26701", - "review_priority": "critical", - "review_reason": "Needs AI review for auth_account, deprecated_removed, new_feature, protocol_change, rate_limit, release_packaging, security_policy.", - "sample_paths": [ - "codex-rs/tui/src/app.rs", - "codex-rs/tui/src/app/background_requests.rs", - "codex-rs/tui/src/app/event_dispatch.rs", - "codex-rs/tui/src/app_event.rs", - "codex-rs/tui/src/chatwidget/plugins.rs", - "codex-rs/tui/src/chatwidget/tests/helpers.rs", - "codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs" - ], - "source_state": "merged", - "subject_id": "26701", - "subject_kind": "pr", - "surface_hints": [ - "cli_tui", - "config_hooks", - "mcp_plugins", - "tests_ci" - ], - "title": "TUI Plugin Sharing 1 - add remote plugin identity", - "url": "https://github.com/openai/codex/pull/26701" - }, - { - "attention_flags": [ - "auth_account", - "deprecated_removed", - "new_feature", - "protocol_change", - "security_policy" - ], - "changed_file_count": 9, - "commit_shas": [ - "00f1e544854c67163899cd0c6c3091c6d4a7694e", - "38b487f2a4eb2291eab6ccea41d24455acfbe9c0", - "b3dce407ff660c49d616be8920bc50a74ced8ac1", - "717e4b299bf0815a2fd1bd77d61ed5f3fbc7ae40" - ], - "committed_at": "2026-06-09T23:49:09Z", - "next_step": "ai_review_required", - "pr_number": 27129, - "pr_url": "https://github.com/openai/codex/pull/27129", - "review_priority": "critical", - "review_reason": "Needs AI review for auth_account, deprecated_removed, new_feature, protocol_change, security_policy.", - "sample_paths": [ - "codex-rs/Cargo.lock", - "codex-rs/memories/write/Cargo.toml", - "codex-rs/memories/write/src/lib.rs", - "codex-rs/memories/write/src/phase1.rs", - "codex-rs/memories/write/src/phase2.rs", - "codex-rs/memories/write/src/runtime.rs", - "codex-rs/memories/write/src/startup_tests.rs", - "codex-rs/model-provider/src/amazon_bedrock/mod.rs", - "codex-rs/model-provider/src/provider.rs" - ], - "source_state": "merged", - "subject_id": "27129", - "subject_kind": "pr", - "surface_hints": [ - "config_hooks", - "model_provider", - "tests_ci" - ], - "title": "feat: use provider defaults for memory models", - "url": "https://github.com/openai/codex/pull/27129" - }, - { - "attention_flags": [ - "auth_account", - "breaking_change", - "deprecated_removed", - "new_feature", - "protocol_change" - ], - "changed_file_count": 23, - "commit_shas": [ - "2d1ffe5d740f39a4288dfa09be7c6a5f5f00ec3c" - ], - "committed_at": "2026-06-10T01:45:54Z", - "next_step": "ai_review_required", - "pr_number": 27078, - "pr_url": "https://github.com/openai/codex/pull/27078", - "review_priority": "critical", - "review_reason": "Needs AI review for auth_account, breaking_change, deprecated_removed, new_feature, protocol_change.", - "sample_paths": [ - "codex-rs/Cargo.lock", - "codex-rs/analytics/Cargo.toml", - "codex-rs/analytics/src/client.rs", - "codex-rs/analytics/src/events.rs", - "codex-rs/analytics/src/facts.rs", - "codex-rs/analytics/src/lib.rs", - "codex-rs/analytics/src/reducer.rs", - "codex-rs/app-server/src/extensions.rs", - "codex-rs/app-server/src/mcp_refresh.rs", - "codex-rs/app-server/src/message_processor.rs", - "codex-rs/app-server/tests/suite/v2/analytics.rs", - "codex-rs/app-server/tests/suite/v2/thread_resume.rs" - ], - "source_state": "merged", - "subject_id": "27078", - "subject_kind": "pr", - "surface_hints": [ - "app_server_protocol", - "cli_tui", - "config_hooks", - "mcp_plugins", - "model_provider", - "tests_ci" - ], - "title": "[codex-analytics] emit goal lifecycle analytics", - "url": "https://github.com/openai/codex/pull/27078" - }, - { - "attention_flags": [ - "auth_account", - "deprecated_removed", - "protocol_change", - "release_packaging" - ], - "changed_file_count": 5, - "commit_shas": [ - "e2348d4ce90e1991f9ea47acea2d2139e8d19c0c", - "3cb6016b3ea47d94f0966e50c18f6d6b8d3b6d2c", - "ea56e2180a47bb14d9914f800e8e726a8b593414", - "28e836c8888fbc001fceb444e2fccfff52e0ff01", - "675cdc7a2696bf763f5e18cb226c325ea61d1d5b" - ], - "committed_at": "2026-06-10T03:52:09Z", - "next_step": "ai_review_required", - "pr_number": 27285, - "pr_url": "https://github.com/openai/codex/pull/27285", - "review_priority": "critical", - "review_reason": "Needs AI review for auth_account, deprecated_removed, protocol_change, release_packaging.", - "sample_paths": [ - "codex-rs/analytics/src/reducer.rs", - "codex-rs/app-server/src/extensions.rs", - "codex-rs/app-server/src/mcp_refresh.rs", - "codex-rs/app-server/src/message_processor.rs", - "codex-rs/app-server/tests/suite/v2/turn_start.rs" - ], - "source_state": "merged", - "subject_id": "27285", - "subject_kind": "pr", - "surface_hints": [ - "app_server_protocol", - "mcp_plugins", - "tests_ci" - ], - "title": "[codex] Fix post-merge analytics integration failures", - "url": "https://github.com/openai/codex/pull/27285" - }, { "attention_flags": [ "auth_account", @@ -863,141 +583,211 @@ }, { "attention_flags": [ - "auth_account", + "breaking_change", + "deprecated_removed", "new_feature", "protocol_change" ], - "changed_file_count": 3, + "changed_file_count": 7, "commit_shas": [ - "9ffed3cafd48244f2927e48fe05b82b9476b8378", - "f5101b8ff6b4c57d4a56b3c1c27487c8ae9bec2a" + "a323ed156e4bf7c1ae57cb0bb2d7f7d0d8a83b75" ], - "committed_at": "2026-06-09T21:18:24Z", + "committed_at": "2026-06-10T20:11:09Z", "next_step": "ai_review_required", - "pr_number": 26713, - "pr_url": "https://github.com/openai/codex/pull/26713", - "review_priority": "high", - "review_reason": "Needs AI review for auth_account, new_feature, protocol_change.", + "pr_number": 27311, + "pr_url": "https://github.com/openai/codex/pull/27311", + "review_priority": "critical", + "review_reason": "Needs AI review for breaking_change, deprecated_removed, new_feature, protocol_change.", "sample_paths": [ - "codex-rs/rmcp-client/src/auth_status.rs", - "codex-rs/rmcp-client/src/oauth.rs", - "codex-rs/rmcp-client/tests/streamable_http_oauth_startup.rs" + "codex-rs/app-server/src/config/external_agent_config.rs", + "codex-rs/app-server/src/request_processors/plugins.rs", + "codex-rs/cli/src/plugin_cmd.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/src/tools/handlers/request_plugin_install.rs" ], "source_state": "merged", - "subject_id": "26713", + "subject_id": "27311", "subject_kind": "pr", "surface_hints": [ - "auth_accounts", + "app_server_protocol", "cli_tui", + "config_hooks", + "mcp_plugins", + "release_packaging", + "tests_ci" + ], + "title": "[codex] Skip local curated discovery for remote plugins", + "url": "https://github.com/openai/codex/pull/27311" + }, + { + "attention_flags": [ + "auth_account", + "deprecated_removed", + "new_feature" + ], + "changed_file_count": 2, + "commit_shas": [ + "679f3b6b5677d685581358f37e7a3a7ddce2466d", + "d1db1d33d190781ea06aa44692e9c31c4c45c683" + ], + "committed_at": "2026-06-10T20:11:20Z", + "next_step": "ai_review_required", + "pr_number": 27414, + "pr_url": "https://github.com/openai/codex/pull/27414", + "review_priority": "critical", + "review_reason": "Needs AI review for auth_account, deprecated_removed, new_feature.", + "sample_paths": [ + "codex-rs/core/src/mcp.rs", + "codex-rs/ext/mcp/tests/hosted_apps_mcp.rs" + ], + "source_state": "merged", + "subject_id": "27414", + "subject_kind": "pr", + "surface_hints": [ "mcp_plugins", "tests_ci" ], - "title": "[codex] Report unusable MCP OAuth credentials as logged out", - "url": "https://github.com/openai/codex/pull/26713" + "title": "[codex] Preserve disabled MCP servers across runtime overlays", + "url": "https://github.com/openai/codex/pull/27414" }, { "attention_flags": [ "auth_account", + "deprecated_removed", "new_feature", - "protocol_change", - "release_packaging" + "protocol_change" ], - "changed_file_count": 5, + "changed_file_count": 12, "commit_shas": [ - "ad5d6c462554bc75fdf458dbd9c4a9cec6b6e9d1" + "8e0cb424640c7776a0c6a03f27ff2e5d637d6389" ], - "committed_at": "2026-06-09T22:32:15Z", + "committed_at": "2026-06-11T00:33:56Z", "next_step": "ai_review_required", - "pr_number": 27111, - "pr_url": "https://github.com/openai/codex/pull/27111", - "review_priority": "high", - "review_reason": "Needs AI review for auth_account, new_feature, protocol_change, release_packaging.", + "pr_number": 27465, + "pr_url": "https://github.com/openai/codex/pull/27465", + "review_priority": "critical", + "review_reason": "Needs AI review for auth_account, deprecated_removed, new_feature, protocol_change.", "sample_paths": [ - "sdk/python/src/openai_codex/_goal.py", - "sdk/python/src/openai_codex/_message_router.py", - "sdk/python/src/openai_codex/async_client.py", - "sdk/python/src/openai_codex/client.py", - "sdk/python/tests/test_app_server_goal_operations.py" + "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/PluginInstallResponse.json", + "codex-rs/app-server-protocol/schema/json/v2/PluginReadResponse.json", + "codex-rs/app-server-protocol/schema/typescript/v2/AppSummary.ts", + "codex-rs/app-server-protocol/src/protocol/v2/apps.rs", + "codex-rs/app-server/README.md", + "codex-rs/app-server/src/request_processors/plugins.rs", + "codex-rs/app-server/tests/suite/v2/plugin_install.rs", + "codex-rs/app-server/tests/suite/v2/plugin_read.rs", + "codex-rs/tui/src/chatwidget/tests/helpers.rs", + "codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs" ], "source_state": "merged", - "subject_id": "27111", + "subject_id": "27465", "subject_kind": "pr", "surface_hints": [ "app_server_protocol", "cli_tui", + "config_hooks", + "docs_examples", + "mcp_plugins", + "release_packaging", "tests_ci" ], - "title": "[2/6] Add private Python goal operations", - "url": "https://github.com/openai/codex/pull/27111" + "title": "[codex] Remove redundant plugin app auth state", + "url": "https://github.com/openai/codex/pull/27465" }, { "attention_flags": [ + "auth_account", + "breaking_change", + "deprecated_removed", "new_feature", "protocol_change", + "rate_limit", "security_policy" ], - "changed_file_count": 1, + "changed_file_count": 13, "commit_shas": [ - "78e5f38713bfb1575127c7aa5a6ea6b11337fd6e" + "96708f40a2274cdaf0e06b54b12fabbd29d70cdf", + "ca92a59065f9e9a4e4eae113b4dc0a322c8dee85" ], - "committed_at": "2026-06-09T23:07:34Z", + "committed_at": "2026-06-11T00:55:49Z", "next_step": "ai_review_required", - "pr_number": 27257, - "pr_url": "https://github.com/openai/codex/pull/27257", - "review_priority": "high", - "review_reason": "Needs AI review for new_feature, protocol_change, security_policy.", + "pr_number": 27484, + "pr_url": "https://github.com/openai/codex/pull/27484", + "review_priority": "critical", + "review_reason": "Needs AI review for auth_account, breaking_change, deprecated_removed, new_feature, protocol_change, rate_limit, security_policy.", "sample_paths": [ - "codex-rs/codex-mcp/src/connection_manager.rs" + "codex-rs/app-server-client/src/lib.rs", + "codex-rs/tui/src/app/test_support.rs", + "codex-rs/tui/src/app/tests.rs", + "codex-rs/tui/src/app/tests/model_catalog.rs", + "codex-rs/tui/src/chatwidget.rs", + "codex-rs/tui/src/chatwidget/tests.rs", + "codex-rs/tui/src/chatwidget/tests/helpers.rs", + "codex-rs/tui/src/chatwidget/tests/plan_mode.rs", + "codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs", + "codex-rs/tui/src/chatwidget/tests/status_and_layout.rs", + "codex-rs/tui/src/debug_config.rs", + "codex-rs/tui/src/status/tests.rs" ], "source_state": "merged", - "subject_id": "27257", + "subject_id": "27484", "subject_kind": "pr", "surface_hints": [ - "mcp_plugins" + "app_server_protocol", + "cli_tui", + "config_hooks", + "model_provider", + "tests_ci" ], - "title": "[codex] Tighten MCP connection manager API visibility and order", - "url": "https://github.com/openai/codex/pull/27257" + "title": "Remove TUI legacy core test_support dependencies", + "url": "https://github.com/openai/codex/pull/27484" }, { "attention_flags": [ + "breaking_change", + "deprecated_removed", "new_feature", - "protocol_change", - "release_packaging" + "protocol_change" ], "changed_file_count": 11, "commit_shas": [ - "6e4cd755e5195f95dfbff628f55d6c9ed592bb11" + "02968853641aed4bd153b0bc0fbe102adf8e8ed4", + "093f205cd1263db388ec05f101f4f17c98257cdd", + "0817892a2a847baeb01ec977ca1988564632e09a" ], - "committed_at": "2026-06-10T00:54:32Z", + "committed_at": "2026-06-11T01:04:02Z", "next_step": "ai_review_required", - "pr_number": 24999, - "pr_url": "https://github.com/openai/codex/pull/24999", - "review_priority": "high", - "review_reason": "Needs AI review for new_feature, protocol_change, release_packaging.", + "pr_number": 27476, + "pr_url": "https://github.com/openai/codex/pull/27476", + "review_priority": "critical", + "review_reason": "Needs AI review for breaking_change, deprecated_removed, new_feature, protocol_change.", "sample_paths": [ - "codex-rs/app-server-protocol/schema/json/ClientRequest.json", - "codex-rs/app-server-protocol/src/protocol/common.rs", - "codex-rs/app-server-protocol/src/protocol/v2/realtime.rs", - "codex-rs/app-server/README.md", - "codex-rs/app-server/src/request_processors/turn_processor.rs", - "codex-rs/app-server/tests/suite/v2/experimental_api.rs", - "codex-rs/app-server/tests/suite/v2/realtime_conversation.rs", - "codex-rs/core/src/realtime_conversation.rs", - "codex-rs/core/tests/suite/compact_remote.rs", - "codex-rs/core/tests/suite/realtime_conversation.rs", - "codex-rs/protocol/src/protocol.rs" + "codex-rs/cli/src/main.rs", + "codex-rs/cli/tests/delete.rs", + "codex-rs/tui/src/app/event_dispatch.rs", + "codex-rs/tui/src/app_event.rs", + "codex-rs/tui/src/app_server_session.rs", + "codex-rs/tui/src/chatwidget/slash_dispatch.rs", + "codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__slash_delete_confirmation_popup.snap", + "codex-rs/tui/src/chatwidget/tests/slash_commands.rs", + "codex-rs/tui/src/lib.rs", + "codex-rs/tui/src/session_archive_commands.rs", + "codex-rs/tui/src/slash_command.rs" ], "source_state": "merged", - "subject_id": "24999", + "subject_id": "27476", "subject_kind": "pr", "surface_hints": [ "app_server_protocol", "cli_tui", - "docs_examples", "tests_ci" ], - "title": "Add per-session realtime model and version overrides", - "url": "https://github.com/openai/codex/pull/24999" + "title": "Add session delete commands in CLI and TUI", + "url": "https://github.com/openai/codex/pull/27476" }, { "attention_flags": [ @@ -1263,65 +1053,69 @@ { "attention_flags": [ "auth_account", - "deprecated_removed", "new_feature", "protocol_change", "release_packaging", "security_policy" ], - "changed_file_count": 6, + "changed_file_count": 4, "commit_shas": [ - "3c85d78cc93fa9f77fb57704cfe009c9c98324eb" + "f5d7eacdae35f4e764a4e0b4e902dec29434387a", + "942e9da661b86172ab6a56b6e0798a7362311ced" ], - "committed_at": "2026-06-09T22:20:01Z", + "committed_at": "2026-06-10T21:32:29Z", "next_step": "ai_review_required", - "pr_number": 27116, - "pr_url": "https://github.com/openai/codex/pull/27116", - "review_priority": "normal", - "review_reason": "Needs AI review for auth_account, deprecated_removed, new_feature, protocol_change, release_packaging, security_policy.", + "pr_number": 27319, + "pr_url": "https://github.com/openai/codex/pull/27319", + "review_priority": "high", + "review_reason": "Needs AI review for auth_account, new_feature, protocol_change, release_packaging, security_policy.", "sample_paths": [ + "codex-rs/app-server/tests/suite/v2/realtime_conversation.rs", + "codex-rs/codex-api/src/endpoint/realtime_websocket/methods.rs", "codex-rs/core/src/realtime_conversation.rs", - "codex-rs/core/src/session/handlers.rs", - "codex-rs/core/src/session/mod.rs", - "codex-rs/core/tests/suite/realtime_conversation.rs", - "codex-rs/core/tests/suite/snapshots/all__suite__realtime_conversation__conversation_user_text_turn_is_capped_when_mirrored_to_realtime.snap", - "codex-rs/core/tests/suite/snapshots/all__suite__realtime_conversation__conversation_user_text_turn_is_sent_to_realtime_when_active.snap" + "codex-rs/core/src/session/mod.rs" ], "source_state": "merged", - "subject_id": "27116", + "subject_id": "27319", "subject_kind": "pr", "surface_hints": [ + "app_server_protocol", "tests_ci" ], - "title": "Stop mirroring Codex user input into realtime", - "url": "https://github.com/openai/codex/pull/27116" + "title": "Forward standalone assistant output to realtime", + "url": "https://github.com/openai/codex/pull/27319" }, { "attention_flags": [ - "new_feature" + "new_feature", + "protocol_change" ], - "changed_file_count": 1, + "changed_file_count": 4, "commit_shas": [ - "e4b0f97aa21f7b723bceec78f76b64f9131b0435", - "affbd8bb42cc7501060fb05c0f0bedd4cc55d2f3" + "424af29908b304199dd33c9fa1ca506a70afebe5" ], - "committed_at": "2026-06-10T00:49:59Z", + "committed_at": "2026-06-10T22:27:34Z", "next_step": "ai_review_required", - "pr_number": 27094, - "pr_url": "https://github.com/openai/codex/pull/27094", - "review_priority": "normal", - "review_reason": "Needs AI review for new_feature.", + "pr_number": 27245, + "pr_url": "https://github.com/openai/codex/pull/27245", + "review_priority": "high", + "review_reason": "Needs AI review for new_feature, protocol_change.", "sample_paths": [ - "codex-rs/core/src/tools/spec_plan.rs" + "codex-rs/protocol/src/models.rs", + "codex-rs/utils/image/src/error.rs", + "codex-rs/utils/image/src/image_tests.rs", + "codex-rs/utils/image/src/lib.rs" ], "source_state": "merged", - "subject_id": "27094", + "subject_id": "27245", "subject_kind": "pr", "surface_hints": [ - "internal_churn" + "app_server_protocol", + "model_provider", + "tests_ci" ], - "title": "Add spans to build_tool_router", - "url": "https://github.com/openai/codex/pull/27094" + "title": "image: add shared data URL preparation utilities", + "url": "https://github.com/openai/codex/pull/27245" }, { "attention_flags": [ @@ -1553,6 +1347,154 @@ "title": "[codex] Move release platform rules into bazel package", "url": "https://github.com/openai/codex/pull/27321" }, + { + "attention_flags": [ + "deprecated_removed", + "protocol_change", + "release_packaging" + ], + "changed_file_count": 2, + "commit_shas": [ + "d3376022f8a0c0b89267e20793ce0046a3ea6355" + ], + "committed_at": "2026-06-10T20:15:43Z", + "next_step": "ai_review_required", + "pr_number": 27312, + "pr_url": "https://github.com/openai/codex/pull/27312", + "review_priority": "normal", + "review_reason": "Needs AI review for deprecated_removed, protocol_change, release_packaging.", + "sample_paths": [ + ".github/workflows/rust-release.yml", + "scripts/stage_npm_packages.py" + ], + "source_state": "merged", + "subject_id": "27312", + "subject_kind": "pr", + "surface_hints": [ + "release_packaging", + "tests_ci" + ], + "title": "[codex] reuse release artifacts for npm staging", + "url": "https://github.com/openai/codex/pull/27312" + }, + { + "attention_flags": [ + "new_feature" + ], + "changed_file_count": 3, + "commit_shas": [ + "bf85303072730e20274518f6aab387d2044d3173" + ], + "committed_at": "2026-06-10T21:36:38Z", + "next_step": "ai_review_required", + "pr_number": 27057, + "pr_url": "https://github.com/openai/codex/pull/27057", + "review_priority": "normal", + "review_reason": "Needs AI review for new_feature.", + "sample_paths": [ + "codex-rs/otel/src/metrics/client.rs", + "codex-rs/otel/tests/suite/otlp_http_loopback.rs", + "codex-rs/otel/tests/suite/send.rs" + ], + "source_state": "merged", + "subject_id": "27057", + "subject_kind": "pr", + "surface_hints": [ + "cli_tui", + "tests_ci" + ], + "title": "[codex] Add reusable OTEL gauge instruments", + "url": "https://github.com/openai/codex/pull/27057" + }, + { + "attention_flags": [ + "auth_account", + "protocol_change" + ], + "changed_file_count": 2, + "commit_shas": [ + "ac6ad0791d9a0f799a5ac2291905e1143796e7a9" + ], + "committed_at": "2026-06-10T22:35:41Z", + "next_step": "ai_review_required", + "pr_number": 27392, + "pr_url": "https://github.com/openai/codex/pull/27392", + "review_priority": "normal", + "review_reason": "Needs AI review for auth_account, protocol_change.", + "sample_paths": [ + "codex-rs/analytics/src/analytics_client_tests.rs", + "codex-rs/analytics/src/reducer.rs" + ], + "source_state": "merged", + "subject_id": "27392", + "subject_kind": "pr", + "surface_hints": [ + "cli_tui", + "tests_ci" + ], + "title": "[codex-analytics] emit internally started turn events", + "url": "https://github.com/openai/codex/pull/27392" + }, + { + "attention_flags": [ + "new_feature", + "protocol_change", + "release_packaging" + ], + "changed_file_count": 3, + "commit_shas": [ + "32f4194ba0c9e89962a825376f3eb78c662553bf" + ], + "committed_at": "2026-06-11T00:08:35Z", + "next_step": "ai_review_required", + "pr_number": 27322, + "pr_url": "https://github.com/openai/codex/pull/27322", + "review_priority": "normal", + "review_reason": "Needs AI review for new_feature, protocol_change, release_packaging.", + "sample_paths": [ + "MODULE.bazel", + "patches/BUILD.bazel", + "patches/rules_rs_build_script_deps_annotation.patch" + ], + "source_state": "merged", + "subject_id": "27322", + "subject_kind": "pr", + "surface_hints": [ + "internal_churn" + ], + "title": "[codex] Preserve build-script dependencies in rules_rs annotations", + "url": "https://github.com/openai/codex/pull/27322" + }, + { + "attention_flags": [ + "protocol_change" + ], + "changed_file_count": 3, + "commit_shas": [ + "b53fd26ab95c4b2cee0740fa7dde899125a55b5f", + "56421478f4ad0965e294e9a0a4147f419c0241da", + "ce89871d06af8127572a9c237d6678c79f05a2e6" + ], + "committed_at": "2026-06-11T00:17:44Z", + "next_step": "ai_review_required", + "pr_number": 27489, + "pr_url": "https://github.com/openai/codex/pull/27489", + "review_priority": "normal", + "review_reason": "Needs AI review for protocol_change.", + "sample_paths": [ + "codex-rs/core/src/tools/events.rs", + "codex-rs/core/src/turn_diff_tracker.rs", + "codex-rs/core/src/turn_diff_tracker_tests.rs" + ], + "source_state": "merged", + "subject_id": "27489", + "subject_kind": "pr", + "surface_hints": [ + "tests_ci" + ], + "title": "core: cache turn diff rendering", + "url": "https://github.com/openai/codex/pull/27489" + }, { "attention_flags": [], "changed_file_count": 1, diff --git a/artifacts/github/reviews/openai-codex-pr-25018.review.json b/artifacts/github/reviews/openai-codex-pr-25018.review.json new file mode 100644 index 000000000..2cb4d287a --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-25018.review.json @@ -0,0 +1,96 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-25018", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "25018", + "commit_shas": [ + "6a8eca9c764324003e5244ff1350763b92bfd55c", + "2ffeebb96a53d069ec9a4b27ac855399ac2ed5e0", + "e3474e6f4e8bb5806b2f4d86057b4af3e6cc89df", + "2292718bf4af345f4bb5b5e0f8ff8bfa287c62d8", + "2adf4ac97da874f89611fb3b33d8f301e04fbe75", + "cb6f55ebed68ab47a383eeb01d7dd8b258c051f4", + "b54292ea752847fafacd90568d9585f686bbe6af", + "4e4d0b03381e9f8ff1e95428865f28bea4d25280", + "e221c3526b18ee401a4b9dfaad6218dc79165c9f", + "cba630a49f83b92c89e893f843f0f6f434c616f1", + "75e10774593faffca4cb842a934cd357084fd44b", + "cc77f6475b243804c31a1530428f9c0e6ef2963d", + "d2feb3b324d4a8553f9b60d481869a3e87bcc4ac", + "ab817ffd6963c3d74e18e88d3bddb6f61d78ae3d", + "c6280684fc1329dcf081a70c19b7e3721bd7e53e", + "adf45c442742593ff61765a11e48c572e20a5212", + "e31a612db2e4f6e0b9c77c2f5605fc80757c26c5", + "14aa743b37972f342d7383ae48d76b410ca48f7e", + "879d73a0f5e96407f31875d62c430b47d8add678", + "126b310bbef358ecd7a2d25b9cc51e6040eafb95", + "2f4f9ed4921ee874dd5c20d09bdd1ba52d66c45d", + "71471d9e7917d7815414da4bc49f39441a984267" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Add app-server `thread/delete` API", + "url": "https://github.com/openai/codex/pull/25018", + "meta": "Merged 2026-06-10T18:22:12Z" + }, + { + "kind": "commit", + "title": "Add app-server thread/delete API", + "url": "https://github.com/openai/codex/commit/6a8eca9c764324003e5244ff1350763b92bfd55c" + }, + { + "kind": "commit", + "title": "Require rollout deletion before thread delete succeeds", + "url": "https://github.com/openai/codex/commit/b54292ea752847fafacd90568d9585f686bbe6af" + } + ] + }, + "reviewed_at": "2026-06-11T02:06:39Z", + "observed_change": "Codex added app-server v2 `thread/delete` request and `thread/deleted` notification support for hard-deleting active or archived threads plus spawned descendants and associated local state.", + "changed_surfaces": [ + "app-server protocol request, notification, JSON schema, and TypeScript schema generation", + "thread delete request processor", + "local thread-store hard-delete support", + "state database cleanup for threads, goals, spawn edges, logs, dynamic tools, and agent jobs", + "TUI app-server notification targeting", + "thread delete integration tests" + ], + "user_visible_path": "App-server clients can call `thread/delete` with a `threadId`; successful deletion returns `{}` and emits `thread/deleted` notifications for each deleted thread.", + "control_plane_relevance": "High for Decodex Control Plane because it introduces a destructive lifecycle operation over threads, descendant subagents, rollout files, and local metadata.", + "compatibility_risk": "High for clients that mirror thread lists, spawned-agent state, goals, or rollout paths because delete now removes the root and associated descendants instead of only hiding or archiving a thread.", + "adoption_opportunity": "Use the new API only behind explicit operator intent, update thread-list caches on `thread/deleted`, and distinguish hard delete from archive/unarchive flows in Decodex operator surfaces.", + "community_value": "High for app-server client builders because it documents the first protocol-level path for permanent thread removal and descendant cleanup.", + "deprecated_or_breaking_notes": "`thread/delete` is additive, but its behavior is intentionally destructive. Clients should not treat it as a reversible archive operation.", + "confidence": "confirmed", + "evidence": [ + "PR #25018 states that clients lacked a permanent thread removal API and that deletion must cover the full session tree.", + "`codex-rs/app-server-protocol/src/protocol/common.rs` adds the `ThreadDelete` client request and `ThreadDeleted` server notification wiring.", + "`codex-rs/app-server-protocol/src/protocol/v2/thread.rs` adds `ThreadDeleteParams`, `ThreadDeleteResponse`, and `ThreadDeletedNotification` types.", + "`codex-rs/app-server/README.md` documents `thread/delete` as hard-deleting active or archived threads and spawned descendants.", + "`codex-rs/app-server/src/request_processors/thread_delete.rs` implements request handling and sends `thread/deleted` notifications for deleted thread ids.", + "`codex-rs/thread-store/src/local/delete_thread.rs` deletes active or archived rollout files before reporting success and removes thread-name entries.", + "`codex-rs/state/src/runtime/threads.rs` expands deletion from a single metadata row to associated state including goals, spawn edges, logs, dynamic tools, and agent job assignments.", + "`codex-rs/tui/src/app/app_server_event_targets.rs` and chat widget protocol handling add support for `ThreadDeleted` notifications.", + "The normalized bundle `artifacts/github/bundles/openai-codex-pr-25018.json` records 38 changed files for the merged PR." + ], + "caveats": "The review confirms local app-server and thread-store behavior from the PR source; remote or hosted thread-store implementations may need their own delete semantics before exposing the same UX safely.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "The destructive thread lifecycle API creates direct Control Plane compatibility and adoption work." + }, + { + "type": "social_candidate", + "reason": "`thread/delete` has a clear public protocol and operator-safety angle." + }, + { + "type": "linear_followup", + "reason": "Decodex should gate any hard-delete UI or automation path behind explicit operator confirmation and cache invalidation rules." + } + ] +} diff --git a/artifacts/github/reviews/openai-codex-pr-26041.review.json b/artifacts/github/reviews/openai-codex-pr-26041.review.json new file mode 100644 index 000000000..cc6b81fef --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-26041.review.json @@ -0,0 +1,92 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-26041", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "26041", + "commit_shas": [ + "e4cb80ffaed2e6ad90a42e0993eabd0d00ddcad3", + "3a5856f7ed4241b1d78aa834b47cea80b6607770", + "353ed1b6bb945a60e859384cf279976e9b280ecf", + "0d37f064d817b8f0f3aa63d8b4fc27901f6c0b5b", + "b90d036c9c45fad1a7abf3474d0aa600e787f116", + "4d1537dba9be19227807e924bb8f551ba2385200", + "e0f9c328df1ff2d337d3baeb029884b04fe69b35", + "64dc0d2bc88f179dd3d71de57586d2a77bb86ab8", + "cce9ca7334ae9bbb5b1086dbcfc136264226b27f", + "33a8e24459749ee78aeed292adebc176724b1bcb", + "61c5d9c84bdae60c98006a819ec8d601255eb165", + "e78df1ea9d485e8b06fff4b4c075591d556be156", + "ff2201af2929455c6b826161b4f818663cfe7ebd", + "b59232841e28b88559ec0f7d5c7cc602fbd2363a", + "b949dc3b1dbe0e25448dfcc4bba207deeb43a72a", + "7daf14053e74673697bca70290a4ddcee2d412d9", + "dcd1b73bce47bd514839ff4d71a48c12280ae413", + "28b3f1ea589a2bc0090577c4a4d309f261318c57", + "cac8884c2c0540a586472f35c17dfb1cbe7a2697" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Add app-server background terminal process APIs", + "url": "https://github.com/openai/codex/pull/26041", + "meta": "Merged 2026-06-10T18:18:09Z" + }, + { + "kind": "commit", + "title": "feat(app-server): add background terminal process APIs", + "url": "https://github.com/openai/codex/commit/e4cb80ffaed2e6ad90a42e0993eabd0d00ddcad3" + }, + { + "kind": "commit", + "title": "Restore background terminal metadata fields", + "url": "https://github.com/openai/codex/commit/0d37f064d817b8f0f3aa63d8b4fc27901f6c0b5b" + } + ] + }, + "reviewed_at": "2026-06-11T02:06:39Z", + "observed_change": "Codex added experimental app-server v2 APIs for listing and terminating background terminal processes for a loaded thread, using unified-exec process manager state as the source of truth.", + "changed_surfaces": [ + "app-server protocol request and TypeScript schema generation", + "thread background terminal list and terminate request processors", + "unified-exec process manager metadata and termination behavior", + "app-server README method documentation", + "background-terminal pagination and termination tests" + ], + "user_visible_path": "App-server clients with experimental API capability can call `thread/backgroundTerminals/list` to page through running background terminals and `thread/backgroundTerminals/terminate` to stop one by app-server `processId`.", + "control_plane_relevance": "High for Decodex Control Plane because it gives app-server clients a protocol-level way to observe and terminate background shell processes without local PID discovery.", + "compatibility_risk": "Low to medium: the APIs are additive and experimental, but clients must use app-server `processId` values and should not treat nullable `osPid`, CPU, or RSS fields as always available.", + "adoption_opportunity": "Adopt these APIs for Decodex operator views that need background-terminal inventory or targeted termination, and stop relying on host-local process tree guessing where app-server state is available.", + "community_value": "High for Codex app-server operators because the change exposes background process management through the same JSON-RPC surface that owns thread state.", + "deprecated_or_breaking_notes": "No removed method is evident. The new methods are explicitly experimental and require `capabilities.experimentalApi` according to the app-server README update.", + "confidence": "confirmed", + "evidence": [ + "PR #26041 says `thread/backgroundTerminals/list` returns paginated records with `itemId`, app-server `processId`, command, cwd, nullable OS PID, CPU, and RSS fields.", + "PR #26041 says `thread/backgroundTerminals/terminate` terminates one running background terminal by app-server `processId` and reports whether a process was terminated.", + "`codex-rs/app-server-protocol/src/protocol/common.rs` adds experimental client methods for `thread/backgroundTerminals/list` and `thread/backgroundTerminals/terminate`.", + "`codex-rs/app-server-protocol/src/protocol/v2/thread.rs` adds list params, terminal record data, terminate params, and response structures.", + "`codex-rs/app-server/src/request_processors/thread_processor.rs` adds list and terminate request handlers for background terminals.", + "`codex-rs/core/src/unified_exec/process_manager.rs` stores call id, cwd, active initial-command state, and process id data needed for background terminal records.", + "`codex-rs/app-server/README.md` documents the two new experimental methods and their capability requirement.", + "`codex-rs/core/src/unified_exec/mod_tests.rs` and process tests add coverage around process listing and termination behavior.", + "The normalized bundle `artifacts/github/bundles/openai-codex-pr-26041.json` records 16 changed files for the merged PR." + ], + "caveats": "The APIs cover loaded-thread background terminals and are experimental; OS-level metadata may be null, and the review does not claim support for terminals outside the app-server unified-exec manager.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "The protocol change creates a concrete Control Plane adoption opportunity." + }, + { + "type": "social_candidate", + "reason": "Background terminal list and terminate methods are a clear public operator-facing capability." + }, + { + "type": "linear_followup", + "reason": "Decodex should evaluate replacing local process-tree guesses with app-server background terminal APIs where available." + } + ] +} diff --git a/artifacts/github/reviews/openai-codex-pr-27259.review.json b/artifacts/github/reviews/openai-codex-pr-27259.review.json new file mode 100644 index 000000000..347536dfd --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27259.review.json @@ -0,0 +1,75 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27259", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "27259", + "commit_shas": [ + "935cbc2e7b35c10516254001991b9a0bf7c2f069", + "cc60f5411157f765bb97ece8f79ef25d3795101f", + "6adf517d65bfdf08aaac21e35e5dfa7f6ce08b19" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "Use latest-wins MCP manager replacement", + "url": "https://github.com/openai/codex/pull/27259", + "meta": "Merged 2026-06-10T15:33:21Z" + }, + { + "kind": "commit", + "title": "Use latest-wins MCP manager replacement", + "url": "https://github.com/openai/codex/commit/cc60f5411157f765bb97ece8f79ef25d3795101f" + }, + { + "kind": "commit", + "title": "Use ArcSwap for MCP manager publication", + "url": "https://github.com/openai/codex/commit/6adf517d65bfdf08aaac21e35e5dfa7f6ce08b19" + } + ] + }, + "reviewed_at": "2026-06-11T02:06:39Z", + "observed_change": "Codex replaced the session-owned `RwLock` publication path with latest-wins `ArcSwap` handles, so each MCP operation keeps the manager it loaded while refresh atomically publishes a replacement.", + "changed_surfaces": [ + "MCP manager publication and hot-refresh ownership", + "session shutdown and startup-prewarm cancellation", + "MCP tool, resource, elicitation, and connector call sites", + "delegated MCP reviewer metadata lookup behavior", + "MCP shutdown regression coverage" + ], + "user_visible_path": "MCP refresh and shutdown should be less likely to block unrelated MCP operations; callers that already loaded an older manager keep using that handle while new calls see the refreshed manager.", + "control_plane_relevance": "High for Decodex Control Plane because MCP tools, hosted plugins, resource reads, and connector discovery all depend on Codex MCP manager lifetime and refresh semantics.", + "compatibility_risk": "Medium for orchestration code that assumed refresh or shutdown drained all active MCP operations through the outer lock; the new behavior favors atomic latest-wins publication and separate terminal shutdown cancellation.", + "adoption_opportunity": "Model Decodex MCP probes around owned manager handles and latest-wins refresh instead of expecting a global manager write lock to serialize all MCP I/O.", + "community_value": "High for MCP and hosted-plugin operators because the change explains why refresh can swap managers without holding a lock across tool, resource, or elicitation awaits.", + "deprecated_or_breaking_notes": "The old mutable `begin_shutdown` drain path and outer read/write manager guard are removed internally; this is not a public CLI flag removal, but it changes refresh and shutdown synchronization assumptions.", + "confidence": "confirmed", + "evidence": [ + "PR #27259 states that MCP operations now call `load_full()` to obtain an owned manager handle, while refresh constructs a replacement and atomically publishes it.", + "`codex-rs/core/src/state/service.rs` changes `mcp_connection_manager` from `Arc>` to `ArcSwap`.", + "`codex-rs/core/src/session/mcp.rs`, `mcp_tool_call.rs`, and MCP resource handlers replace manager `.read().await` calls with `.load_full()` before MCP I/O.", + "`codex-rs/codex-mcp/src/connection_manager.rs` replaces the mutable `begin_shutdown` drain with async `shutdown(&self)` over the manager's current clients.", + "`codex-rs/core/src/session/handlers.rs` aborts session startup prewarm before shutting down conversation, tasks, code mode, and the loaded MCP manager.", + "`codex-rs/core/src/session_startup_prewarm.rs` wraps the prewarm join handle in `AbortOnDropHandle` and adds an explicit `abort` method.", + "`codex-rs/core/tests/suite/rmcp_client.rs` adds coverage for shutdown cancelling startup prewarm while MCP startup is pending.", + "The normalized bundle `artifacts/github/bundles/openai-codex-pr-27259.json` records 20 changed files for the merged PR." + ], + "caveats": "The PR body says refresh happens at a turn boundary, so the review does not claim arbitrary mid-tool-call hot replacement is safe beyond loaded manager handle ownership.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "MCP manager lifetime affects Control Plane compatibility and hosted-plugin adoption assumptions." + }, + { + "type": "social_candidate", + "reason": "Latest-wins MCP refresh has a clear public operator angle for Codex MCP users." + }, + { + "type": "linear_followup", + "reason": "Decodex should audit MCP refresh, shutdown, and hosted-plugin probes for any reliance on old manager-lock draining semantics." + } + ] +} diff --git a/artifacts/github/reviews/openai-codex-pr-27304.review.json b/artifacts/github/reviews/openai-codex-pr-27304.review.json new file mode 100644 index 000000000..bc911467e --- /dev/null +++ b/artifacts/github/reviews/openai-codex-pr-27304.review.json @@ -0,0 +1,67 @@ +{ + "schema": "upstream_review/v1", + "slug": "openai-codex-pr-27304", + "repo": "openai/codex", + "subject": { + "subject_kind": "pr", + "subject_id": "27304", + "commit_shas": [ + "fffd0036afdb23efb940be1527158f985bda5d6e" + ] + }, + "source_refs": { + "items": [ + { + "kind": "pull_request", + "title": "[codex] Remove async_trait from ToolExecutor", + "url": "https://github.com/openai/codex/pull/27304", + "meta": "Merged 2026-06-10T17:26:53Z" + }, + { + "kind": "commit", + "title": "Remove async trait from tool executor", + "url": "https://github.com/openai/codex/commit/fffd0036afdb23efb940be1527158f985bda5d6e" + } + ] + }, + "reviewed_at": "2026-06-11T02:06:39Z", + "observed_change": "Codex removed `async_trait` from the `ToolExecutor` trait and converted handlers to return a boxed `ToolExecutorFuture` directly.", + "changed_surfaces": [ + "core tool executor trait signature", + "tool handler implementations across shell, patch, MCP, user-input, image, and multi-agent tools", + "extension API reexports", + "bundled extension crate dependencies", + "test helper executors" + ], + "user_visible_path": "Extension or internal Codex tool authors now implement `ToolExecutor::handle` as a synchronous method returning `Box::pin(async move { ... })` or another `ToolExecutorFuture` instead of relying on `#[async_trait]`.", + "control_plane_relevance": "Moderate for Decodex Control Plane because Decodex-adjacent extension and tool integrations may need source updates if they implement upstream Codex `ToolExecutor` directly.", + "compatibility_risk": "Medium for out-of-tree extension crates that still use `#[async_trait]` with the old async `handle` method shape; existing compiled integrations must adopt the explicit future return alias.", + "adoption_opportunity": "Remove direct `async-trait` dependencies from compatible extension crates and use the `ToolExecutorFuture` alias reexported by `codex-tools` and `codex-extension-api`.", + "community_value": "Medium for Codex extension contributors because the PR gives a concrete migration pattern and reports a large debug test build-speed improvement for `codex_core`.", + "deprecated_or_breaking_notes": "`ToolExecutor::handle` is no longer an async trait method. The PR body says it is stacked on #27299, so handler-body outlining is part of the migration context.", + "confidence": "confirmed", + "evidence": [ + "PR #27304 says `ToolExecutor::handle` now returns an explicit boxed `ToolExecutorFuture` instead of using `async_trait`.", + "`codex-rs/tools/src/tool_executor.rs` adds the `ToolExecutorFuture<'a>` type alias and removes the `#[async_trait::async_trait]` annotation from the trait.", + "`codex-rs/ext/extension-api/src/lib.rs` reexports `ToolExecutorFuture` for extension authors.", + "`codex-rs/tools/Cargo.toml` removes the direct `async-trait` dependency from `codex-tools`.", + "Bundled extension crates such as goal, image-generation, memories, and web-search remove their direct `async-trait` dependencies.", + "Tool handlers across code mode, shell, MCP, resource, plugin, user-input, image, and multi-agent surfaces remove `#[async_trait]` and return boxed futures.", + "The normalized bundle `artifacts/github/bundles/openai-codex-pr-27304.json` records 56 changed files for the merged PR." + ], + "caveats": "This is primarily a source-level extension and build-performance change; it does not add a new model-visible tool or app-server method by itself.", + "next_actions": [ + { + "type": "upstream_impact", + "reason": "The trait signature change affects extension and tool implementations that may sit near Decodex integration code." + }, + { + "type": "social_candidate", + "reason": "The migration has a concise public explanation for Codex extension authors." + }, + { + "type": "linear_followup", + "reason": "Audit any Decodex-owned upstream extension examples or generated helpers that still assume async-trait ToolExecutor implementations." + } + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-25018.json b/artifacts/github/social-candidates/openai-codex-pr-25018.json new file mode 100644 index 000000000..92df08fd3 --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-25018.json @@ -0,0 +1,56 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-25018", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "operator_impact", + "priority": "critical", + "audience": "Codex app-server client and operator-console builders", + "candidate_text": [ + "Codex added app-server thread/delete: clients can hard-delete active or archived threads, descendants, rollout files, and related state, with thread/deleted notifications. PR: https://github.com/openai/codex/pull/25018" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-25018.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-25018.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/25018" + ] + }, + "evidence_notes": [ + "`thread/delete` is added as an app-server request.", + "`thread/deleted` is added as a notification that targets affected thread ids.", + "Local thread-store delete removes rollout files before success.", + "State cleanup covers descendants, goals, spawn edges, logs, dynamic tools, and agent jobs." + ], + "claims": [ + { + "text": "Codex app-server now has a protocol method for permanent thread deletion.", + "evidence": "artifacts/github/reviews/openai-codex-pr-25018.review.json", + "confidence": "confirmed" + }, + { + "text": "`thread/delete` removes spawned descendants and associated local state, not just the root row.", + "evidence": "artifacts/github/impact/openai-codex-pr-25018.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "publish", + "reason": "The PR adds a destructive app-server lifecycle method that client builders need to handle carefully.", + "idempotency_key": "x:decodexspace:openai-codex-pr-25018:operator_impact" + }, + "caveats": [ + "This is hard delete, not archive.", + "Remote thread-store behavior may require separate implementation.", + "Operator UIs should require explicit confirmation before using it." + ], + "next_steps": [ + "Let Publisher automation decide whether to reserve or post this candidate.", + "Use the upstream impact record to shape Decodex hard-delete UX and cache invalidation policy." + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-26041.json b/artifacts/github/social-candidates/openai-codex-pr-26041.json new file mode 100644 index 000000000..7e7b7e12a --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-26041.json @@ -0,0 +1,56 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-26041", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "operator_impact", + "priority": "critical", + "audience": "Codex app-server and operator-console builders", + "candidate_text": [ + "Codex added experimental app-server APIs to list and terminate thread background terminals by app-server processId, using unified-exec as source of truth. PR: https://github.com/openai/codex/pull/26041" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-26041.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-26041.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/26041" + ] + }, + "evidence_notes": [ + "`thread/backgroundTerminals/list` returns paginated background terminal records.", + "`thread/backgroundTerminals/terminate` targets app-server `processId` values.", + "Unified-exec process manager state is the source of truth for records and termination.", + "The README marks the methods experimental and capability-gated." + ], + "claims": [ + { + "text": "Codex app-server can list loaded-thread background terminals through an experimental v2 method.", + "evidence": "artifacts/github/reviews/openai-codex-pr-26041.review.json", + "confidence": "confirmed" + }, + { + "text": "Codex app-server can terminate one background terminal by app-server `processId`.", + "evidence": "artifacts/github/impact/openai-codex-pr-26041.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "publish", + "reason": "The PR exposes a concrete app-server operator capability with direct Control Plane adoption value.", + "idempotency_key": "x:decodexspace:openai-codex-pr-26041:operator_impact" + }, + "caveats": [ + "The methods are experimental and capability-gated.", + "Only loaded-thread unified-exec background terminals are in scope.", + "OS PID and resource metrics may be null." + ], + "next_steps": [ + "Let Publisher automation decide whether to reserve or post this candidate.", + "Create an adoption issue if Decodex operator surfaces still infer background terminals from local process trees." + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-27259.json b/artifacts/github/social-candidates/openai-codex-pr-27259.json new file mode 100644 index 000000000..aad1a800e --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-27259.json @@ -0,0 +1,56 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-27259", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "operator_impact", + "priority": "critical", + "audience": "Codex MCP, hosted-plugin, and Control Plane operators", + "candidate_text": [ + "Codex changed MCP manager replacement to latest-wins ArcSwap: refresh publishes a new manager, existing operations keep their loaded handle, and shutdown aborts startup prewarm before MCP shutdown. PR: https://github.com/openai/codex/pull/27259" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-27259.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-27259.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/27259" + ] + }, + "evidence_notes": [ + "Session services now publish the MCP manager through `ArcSwap`.", + "MCP calls load an owned manager handle with `.load_full()` before async I/O.", + "Shutdown aborts startup prewarm and then shuts down the currently loaded MCP manager.", + "The PR preserves turn-boundary refresh caveats." + ], + "claims": [ + { + "text": "Codex MCP manager refresh now uses latest-wins publication.", + "evidence": "artifacts/github/reviews/openai-codex-pr-27259.review.json", + "confidence": "confirmed" + }, + { + "text": "Existing MCP operations keep the manager handle they loaded before refresh.", + "evidence": "artifacts/github/impact/openai-codex-pr-27259.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "publish", + "reason": "The change alters MCP refresh and shutdown assumptions that Codex operators can act on.", + "idempotency_key": "x:decodexspace:openai-codex-pr-27259:operator_impact" + }, + "caveats": [ + "This is an internal synchronization model change.", + "Refresh is expected at turn boundaries.", + "Shutdown can still interrupt active runtime work." + ], + "next_steps": [ + "Let Publisher automation decide whether to reserve or post this candidate.", + "Use the upstream impact record to shape a Decodex MCP refresh audit if needed." + ] +} diff --git a/artifacts/github/social-candidates/openai-codex-pr-27304.json b/artifacts/github/social-candidates/openai-codex-pr-27304.json new file mode 100644 index 000000000..6f0207997 --- /dev/null +++ b/artifacts/github/social-candidates/openai-codex-pr-27304.json @@ -0,0 +1,56 @@ +{ + "schema": "social_candidate/v1", + "slug": "openai-codex-pr-27304", + "repo": "openai/codex", + "channel": "x", + "target_account": "decodexspace", + "mode": "practical_explainer", + "priority": "critical", + "audience": "Codex extension and tool authors", + "candidate_text": [ + "Codex removed async_trait from ToolExecutor: handlers now return a boxed ToolExecutorFuture, extension crates reexport the alias, and direct async-trait deps were dropped from bundled extensions. PR: https://github.com/openai/codex/pull/27304" + ], + "source_refs": { + "upstream_reviews": [ + "artifacts/github/reviews/openai-codex-pr-27304.review.json" + ], + "upstream_impacts": [ + "artifacts/github/impact/openai-codex-pr-27304.json" + ], + "urls": [ + "https://github.com/openai/codex/pull/27304" + ] + }, + "evidence_notes": [ + "`ToolExecutorFuture` is added to `codex-tools` and reexported by `codex-extension-api`.", + "`ToolExecutor` no longer uses `#[async_trait::async_trait]`.", + "Bundled extension crates remove direct `async-trait` dependencies.", + "Core handlers now return explicit boxed futures." + ], + "claims": [ + { + "text": "Codex `ToolExecutor` implementations now return an explicit boxed future.", + "evidence": "artifacts/github/reviews/openai-codex-pr-27304.review.json", + "confidence": "confirmed" + }, + { + "text": "Bundled extension crates dropped direct `async-trait` dependencies for their tool executors.", + "evidence": "artifacts/github/impact/openai-codex-pr-27304.json", + "confidence": "confirmed" + } + ], + "decision": { + "worthiness": "publish", + "reason": "The source-level migration is useful to Codex extension authors and has clear PR-backed evidence.", + "idempotency_key": "x:decodexspace:openai-codex-pr-27304:practical_explainer" + }, + "caveats": [ + "This is a source/API change for tool implementors, not a new model-visible tool.", + "The PR body says the work is stacked on #27299.", + "The build-speed figure is author-reported in the PR body." + ], + "next_steps": [ + "Let Publisher automation decide whether to reserve or post this candidate.", + "Review #27299 if a later migration note needs full handler-outline context." + ] +}