yepanywhere 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/bundled/@yep-anywhere/shared/dist/app-types.d.ts +25 -0
  2. package/bundled/@yep-anywhere/shared/dist/app-types.d.ts.map +1 -1
  3. package/bundled/@yep-anywhere/shared/dist/app-types.js.map +1 -1
  4. package/bundled/@yep-anywhere/shared/dist/codex-schema/session.d.ts +179 -14
  5. package/bundled/@yep-anywhere/shared/dist/codex-schema/session.d.ts.map +1 -1
  6. package/bundled/@yep-anywhere/shared/dist/codex-schema/session.js +89 -0
  7. package/bundled/@yep-anywhere/shared/dist/codex-schema/session.js.map +1 -1
  8. package/bundled/@yep-anywhere/shared/dist/codex-tool-correlation.d.ts +25 -0
  9. package/bundled/@yep-anywhere/shared/dist/codex-tool-correlation.d.ts.map +1 -0
  10. package/bundled/@yep-anywhere/shared/dist/codex-tool-correlation.js +52 -0
  11. package/bundled/@yep-anywhere/shared/dist/codex-tool-correlation.js.map +1 -0
  12. package/bundled/@yep-anywhere/shared/dist/codex-web-run.d.ts +46 -0
  13. package/bundled/@yep-anywhere/shared/dist/codex-web-run.d.ts.map +1 -0
  14. package/bundled/@yep-anywhere/shared/dist/codex-web-run.js +14 -0
  15. package/bundled/@yep-anywhere/shared/dist/codex-web-run.js.map +1 -0
  16. package/bundled/@yep-anywhere/shared/dist/git-status.d.ts +29 -6
  17. package/bundled/@yep-anywhere/shared/dist/git-status.d.ts.map +1 -1
  18. package/bundled/@yep-anywhere/shared/dist/git-status.js +1 -6
  19. package/bundled/@yep-anywhere/shared/dist/git-status.js.map +1 -1
  20. package/bundled/@yep-anywhere/shared/dist/index.d.ts +7 -2
  21. package/bundled/@yep-anywhere/shared/dist/index.d.ts.map +1 -1
  22. package/bundled/@yep-anywhere/shared/dist/index.js +2 -0
  23. package/bundled/@yep-anywhere/shared/dist/index.js.map +1 -1
  24. package/bundled/@yep-anywhere/shared/dist/server-capabilities.d.ts +248 -0
  25. package/bundled/@yep-anywhere/shared/dist/server-capabilities.d.ts.map +1 -0
  26. package/bundled/@yep-anywhere/shared/dist/server-capabilities.js +251 -0
  27. package/bundled/@yep-anywhere/shared/dist/server-capabilities.js.map +1 -0
  28. package/bundled/@yep-anywhere/shared/dist/tool-display-actions.d.ts +26 -0
  29. package/bundled/@yep-anywhere/shared/dist/tool-display-actions.d.ts.map +1 -0
  30. package/bundled/@yep-anywhere/shared/dist/tool-display-actions.js +9 -0
  31. package/bundled/@yep-anywhere/shared/dist/tool-display-actions.js.map +1 -0
  32. package/bundled/@yep-anywhere/shared/package.json +1 -1
  33. package/client-dist/assets/de-BNhVdIq4.js +6 -0
  34. package/client-dist/assets/es-Csn9n24J.js +6 -0
  35. package/client-dist/assets/fr-DKx4tSpo.js +6 -0
  36. package/client-dist/assets/index-1y2h7U7U.js +582 -0
  37. package/client-dist/assets/index-Cn_ZDP6u.css +1 -0
  38. package/client-dist/assets/ja-DAkhkwQZ.js +6 -0
  39. package/client-dist/assets/zh-CN-B7lY0cc6.js +6 -0
  40. package/client-dist/index.html +2 -2
  41. package/dist/app.d.ts.map +1 -1
  42. package/dist/app.js.map +1 -1
  43. package/dist/codex/codeModeExec.d.ts +30 -0
  44. package/dist/codex/codeModeExec.d.ts.map +1 -0
  45. package/dist/codex/codeModeExec.js +419 -0
  46. package/dist/codex/codeModeExec.js.map +1 -0
  47. package/dist/codex/displayActions.d.ts +48 -0
  48. package/dist/codex/displayActions.d.ts.map +1 -0
  49. package/dist/codex/displayActions.js +674 -0
  50. package/dist/codex/displayActions.js.map +1 -0
  51. package/dist/codex/normalization.d.ts +10 -7
  52. package/dist/codex/normalization.d.ts.map +1 -1
  53. package/dist/codex/normalization.js +179 -502
  54. package/dist/codex/normalization.js.map +1 -1
  55. package/dist/codex/webRun.d.ts +42 -0
  56. package/dist/codex/webRun.d.ts.map +1 -0
  57. package/dist/codex/webRun.js +199 -0
  58. package/dist/codex/webRun.js.map +1 -0
  59. package/dist/indexes/SessionIndexService.d.ts +1 -1
  60. package/dist/indexes/SessionIndexService.d.ts.map +1 -1
  61. package/dist/indexes/SessionIndexService.js +16 -4
  62. package/dist/indexes/SessionIndexService.js.map +1 -1
  63. package/dist/routes/git-status.d.ts.map +1 -1
  64. package/dist/routes/git-status.js +70 -0
  65. package/dist/routes/git-status.js.map +1 -1
  66. package/dist/routes/project-queue.d.ts.map +1 -1
  67. package/dist/routes/project-queue.js.map +1 -1
  68. package/dist/routes/public-shares.d.ts +1 -1
  69. package/dist/routes/public-shares.d.ts.map +1 -1
  70. package/dist/routes/public-shares.js +4 -6
  71. package/dist/routes/public-shares.js.map +1 -1
  72. package/dist/routes/session-metadata-patch.d.ts +24 -0
  73. package/dist/routes/session-metadata-patch.d.ts.map +1 -0
  74. package/dist/routes/session-metadata-patch.js +119 -0
  75. package/dist/routes/session-metadata-patch.js.map +1 -0
  76. package/dist/routes/session-project-routing.d.ts +25 -0
  77. package/dist/routes/session-project-routing.d.ts.map +1 -0
  78. package/dist/routes/session-project-routing.js +32 -0
  79. package/dist/routes/session-project-routing.js.map +1 -0
  80. package/dist/routes/session-provider-resolution.d.ts +28 -0
  81. package/dist/routes/session-provider-resolution.d.ts.map +1 -0
  82. package/dist/routes/session-provider-resolution.js +22 -0
  83. package/dist/routes/session-provider-resolution.js.map +1 -0
  84. package/dist/routes/session-queue-summaries.d.ts +21 -0
  85. package/dist/routes/session-queue-summaries.d.ts.map +1 -0
  86. package/dist/routes/session-queue-summaries.js +79 -0
  87. package/dist/routes/session-queue-summaries.js.map +1 -0
  88. package/dist/routes/session-recovered-queue.d.ts +43 -0
  89. package/dist/routes/session-recovered-queue.d.ts.map +1 -0
  90. package/dist/routes/session-recovered-queue.js +132 -0
  91. package/dist/routes/session-recovered-queue.js.map +1 -0
  92. package/dist/routes/session-thinking-options.d.ts +12 -0
  93. package/dist/routes/session-thinking-options.d.ts.map +1 -0
  94. package/dist/routes/session-thinking-options.js +7 -0
  95. package/dist/routes/session-thinking-options.js.map +1 -0
  96. package/dist/routes/sessions.d.ts.map +1 -1
  97. package/dist/routes/sessions.js +110 -409
  98. package/dist/routes/sessions.js.map +1 -1
  99. package/dist/routes/settings-parsers.d.ts +37 -0
  100. package/dist/routes/settings-parsers.d.ts.map +1 -0
  101. package/dist/routes/settings-parsers.js +896 -0
  102. package/dist/routes/settings-parsers.js.map +1 -0
  103. package/dist/routes/settings.d.ts.map +1 -1
  104. package/dist/routes/settings.js +4 -894
  105. package/dist/routes/settings.js.map +1 -1
  106. package/dist/routes/version.d.ts +0 -2
  107. package/dist/routes/version.d.ts.map +1 -1
  108. package/dist/routes/version.js +8 -10
  109. package/dist/routes/version.js.map +1 -1
  110. package/dist/sdk/cli-detection.d.ts +15 -1
  111. package/dist/sdk/cli-detection.d.ts.map +1 -1
  112. package/dist/sdk/cli-detection.js +159 -28
  113. package/dist/sdk/cli-detection.js.map +1 -1
  114. package/dist/sdk/messageQueue.d.ts +5 -0
  115. package/dist/sdk/messageQueue.d.ts.map +1 -1
  116. package/dist/sdk/messageQueue.js +21 -0
  117. package/dist/sdk/messageQueue.js.map +1 -1
  118. package/dist/sdk/providers/codex-model-catalog.d.ts +34 -0
  119. package/dist/sdk/providers/codex-model-catalog.d.ts.map +1 -0
  120. package/dist/sdk/providers/codex-model-catalog.js +378 -0
  121. package/dist/sdk/providers/codex-model-catalog.js.map +1 -0
  122. package/dist/sdk/providers/codex-notification-guards.d.ts +15 -0
  123. package/dist/sdk/providers/codex-notification-guards.d.ts.map +1 -0
  124. package/dist/sdk/providers/codex-notification-guards.js +159 -0
  125. package/dist/sdk/providers/codex-notification-guards.js.map +1 -0
  126. package/dist/sdk/providers/codex-protocol/generated/ImageGenerationItem.d.ts +9 -0
  127. package/dist/sdk/providers/codex-protocol/generated/ImageGenerationItem.d.ts.map +1 -0
  128. package/dist/sdk/providers/codex-protocol/generated/ImageGenerationItem.js +3 -0
  129. package/dist/sdk/providers/codex-protocol/generated/ImageGenerationItem.js.map +1 -0
  130. package/dist/sdk/providers/codex-protocol/generated/MultiAgentMode.d.ts +5 -5
  131. package/dist/sdk/providers/codex-protocol/generated/MultiAgentMode.d.ts.map +1 -1
  132. package/dist/sdk/providers/codex-protocol/generated/ResponseItem.d.ts +1 -0
  133. package/dist/sdk/providers/codex-protocol/generated/ResponseItem.d.ts.map +1 -1
  134. package/dist/sdk/providers/codex-protocol/generated/ThreadId.d.ts +5 -0
  135. package/dist/sdk/providers/codex-protocol/generated/ThreadId.d.ts.map +1 -1
  136. package/dist/sdk/providers/codex-protocol/generated/WebSearchItem.d.ts +7 -0
  137. package/dist/sdk/providers/codex-protocol/generated/WebSearchItem.d.ts.map +1 -0
  138. package/dist/sdk/providers/codex-protocol/generated/WebSearchItem.js +3 -0
  139. package/dist/sdk/providers/codex-protocol/generated/WebSearchItem.js.map +1 -0
  140. package/dist/sdk/providers/codex-protocol/generated/v2/AskForApproval.d.ts +1 -1
  141. package/dist/sdk/providers/codex-protocol/generated/v2/AskForApproval.d.ts.map +1 -1
  142. package/dist/sdk/providers/codex-protocol/generated/v2/CapabilityRootLocation.d.ts +3 -0
  143. package/dist/sdk/providers/codex-protocol/generated/v2/CapabilityRootLocation.d.ts.map +1 -1
  144. package/dist/sdk/providers/codex-protocol/generated/v2/CodexErrorInfo.d.ts +1 -1
  145. package/dist/sdk/providers/codex-protocol/generated/v2/CodexErrorInfo.d.ts.map +1 -1
  146. package/dist/sdk/providers/codex-protocol/generated/v2/McpToolCallAppContext.d.ts +3 -0
  147. package/dist/sdk/providers/codex-protocol/generated/v2/McpToolCallAppContext.d.ts.map +1 -1
  148. package/dist/sdk/providers/codex-protocol/generated/v2/Thread.d.ts +13 -0
  149. package/dist/sdk/providers/codex-protocol/generated/v2/Thread.d.ts.map +1 -1
  150. package/dist/sdk/providers/codex-protocol/generated/v2/ThreadExtra.d.ts +5 -0
  151. package/dist/sdk/providers/codex-protocol/generated/v2/ThreadExtra.d.ts.map +1 -0
  152. package/dist/sdk/providers/codex-protocol/generated/v2/ThreadExtra.js +3 -0
  153. package/dist/sdk/providers/codex-protocol/generated/v2/ThreadExtra.js.map +1 -0
  154. package/dist/sdk/providers/codex-protocol/generated/v2/ThreadForkParams.d.ts +7 -0
  155. package/dist/sdk/providers/codex-protocol/generated/v2/ThreadForkParams.d.ts.map +1 -1
  156. package/dist/sdk/providers/codex-protocol/generated/v2/ThreadHistoryMode.d.ts +2 -0
  157. package/dist/sdk/providers/codex-protocol/generated/v2/ThreadHistoryMode.d.ts.map +1 -0
  158. package/dist/sdk/providers/codex-protocol/generated/v2/ThreadHistoryMode.js +3 -0
  159. package/dist/sdk/providers/codex-protocol/generated/v2/ThreadHistoryMode.js.map +1 -0
  160. package/dist/sdk/providers/codex-protocol/generated/v2/ThreadItem.d.ts +5 -13
  161. package/dist/sdk/providers/codex-protocol/generated/v2/ThreadItem.d.ts.map +1 -1
  162. package/dist/sdk/providers/codex-protocol/generated/v2/ThreadRollbackParams.d.ts +3 -0
  163. package/dist/sdk/providers/codex-protocol/generated/v2/ThreadRollbackParams.d.ts.map +1 -1
  164. package/dist/sdk/providers/codex-protocol/generated/v2/ThreadStartParams.d.ts +10 -0
  165. package/dist/sdk/providers/codex-protocol/generated/v2/ThreadStartParams.d.ts.map +1 -1
  166. package/dist/sdk/providers/codex-protocol/generated/v2/Turn.d.ts +3 -0
  167. package/dist/sdk/providers/codex-protocol/generated/v2/Turn.d.ts.map +1 -1
  168. package/dist/sdk/providers/codex-summary-helpers.d.ts +41 -0
  169. package/dist/sdk/providers/codex-summary-helpers.d.ts.map +1 -0
  170. package/dist/sdk/providers/codex-summary-helpers.js +207 -0
  171. package/dist/sdk/providers/codex-summary-helpers.js.map +1 -0
  172. package/dist/sdk/providers/codex.d.ts +0 -25
  173. package/dist/sdk/providers/codex.d.ts.map +1 -1
  174. package/dist/sdk/providers/codex.js +160 -703
  175. package/dist/sdk/providers/codex.js.map +1 -1
  176. package/dist/sdk/providers/opencode-models.d.ts +17 -0
  177. package/dist/sdk/providers/opencode-models.d.ts.map +1 -0
  178. package/dist/sdk/providers/opencode-models.js +83 -0
  179. package/dist/sdk/providers/opencode-models.js.map +1 -0
  180. package/dist/sdk/providers/opencode.d.ts +1 -9
  181. package/dist/sdk/providers/opencode.d.ts.map +1 -1
  182. package/dist/sdk/providers/opencode.js +1 -82
  183. package/dist/sdk/providers/opencode.js.map +1 -1
  184. package/dist/sdk/types.d.ts +3 -1
  185. package/dist/sdk/types.d.ts.map +1 -1
  186. package/dist/services/ProjectQueueService.d.ts.map +1 -1
  187. package/dist/services/ProjectQueueService.js +3 -1
  188. package/dist/services/ProjectQueueService.js.map +1 -1
  189. package/dist/sessions/claude-summary.d.ts.map +1 -1
  190. package/dist/sessions/claude-summary.js +13 -1
  191. package/dist/sessions/claude-summary.js.map +1 -1
  192. package/dist/sessions/codex-reader.d.ts +0 -2
  193. package/dist/sessions/codex-reader.d.ts.map +1 -1
  194. package/dist/sessions/codex-reader.js +46 -84
  195. package/dist/sessions/codex-reader.js.map +1 -1
  196. package/dist/sessions/codex-user-turn-provenance.d.ts +40 -0
  197. package/dist/sessions/codex-user-turn-provenance.d.ts.map +1 -0
  198. package/dist/sessions/codex-user-turn-provenance.js +165 -0
  199. package/dist/sessions/codex-user-turn-provenance.js.map +1 -0
  200. package/dist/sessions/normalization.d.ts.map +1 -1
  201. package/dist/sessions/normalization.js +80 -51
  202. package/dist/sessions/normalization.js.map +1 -1
  203. package/dist/sessions/pagination.d.ts +6 -0
  204. package/dist/sessions/pagination.d.ts.map +1 -1
  205. package/dist/sessions/pagination.js +42 -2
  206. package/dist/sessions/pagination.js.map +1 -1
  207. package/dist/subscriptions.d.ts.map +1 -1
  208. package/dist/subscriptions.js +1 -0
  209. package/dist/subscriptions.js.map +1 -1
  210. package/dist/supervisor/Process.d.ts +12 -0
  211. package/dist/supervisor/Process.d.ts.map +1 -1
  212. package/dist/supervisor/Process.js +178 -22
  213. package/dist/supervisor/Process.js.map +1 -1
  214. package/dist/supervisor/Supervisor.d.ts +5 -1
  215. package/dist/supervisor/Supervisor.d.ts.map +1 -1
  216. package/dist/supervisor/Supervisor.js +47 -0
  217. package/dist/supervisor/Supervisor.js.map +1 -1
  218. package/package.json +2 -2
  219. package/client-dist/assets/de-CHMDbVD5.js +0 -6
  220. package/client-dist/assets/es-CK8MPF_C.js +0 -6
  221. package/client-dist/assets/fr-49PXQiUL.js +0 -6
  222. package/client-dist/assets/index-B-09fVut.js +0 -557
  223. package/client-dist/assets/index-uJnfvD1f.css +0 -1
  224. package/client-dist/assets/ja-CrCGPmoH.js +0 -6
  225. package/client-dist/assets/zh-CN-AFJxv5ke.js +0 -6
@@ -6,18 +6,20 @@
6
6
  */
7
7
  import { execFile, spawn } from "node:child_process";
8
8
  import { homedir } from "node:os";
9
- import { promisify } from "node:util";
10
- import { HELPER_SIDE_MODEL_CHEAPEST, } from "../../../bundled/@yep-anywhere/shared/dist/index.js";
9
+ import { CODEX_TOOL_CORRELATION_FIELD, createCodexToolCorrelation, } from "../../../bundled/@yep-anywhere/shared/dist/index.js";
11
10
  import { isCodexCorrelationDebugEnabled, logCodexCorrelationDebug, summarizeCodexNormalizedMessage, } from "../../codex/correlationDebugLogger.js";
12
- import { canonicalizeCodexToolName, isCodexBackgroundProcessOutput, isCodexInterruptedToolOutput, normalizeCodexCommandActionInvocation, normalizeCodexCommandExecutionOutput, normalizeCodexToolInvocation, normalizeCodexToolOutputWithContext, parseCodexToolArguments, } from "../../codex/normalization.js";
11
+ import { canonicalizeCodexToolName, isCodexBackgroundProcessOutput, isCodexInterruptedToolOutput, normalizeCodexCommandExecutionOutput, normalizeCodexCustomToolInvocation, normalizeCodexToolInvocation, normalizeCodexToolOutputWithContext, parseCodexToolArguments, } from "../../codex/normalization.js";
13
12
  import { getLogger } from "../../logging/logger.js";
14
- import { findCodexCliPath } from "../cli-detection.js";
13
+ import { findCodexCliPath, getCodexCliVersion } from "../cli-detection.js";
15
14
  import { logSDKMessage } from "../messageLogger.js";
16
15
  import { MessageQueue } from "../messageQueue.js";
17
16
  import { createAgentctlSessionEnvBridge } from "./agentctl-session-env.js";
17
+ import { getFallbackCodexModelsForCliVersion, normalizeCodexModelList, normalizeSemver, } from "./codex-model-catalog.js";
18
+ import { asCodexAgentMessageDeltaNotification, asCodexCommandExecutionOutputDeltaNotification, asCodexErrorNotification, asCodexFileChangeOutputDeltaNotification, asCodexItemCompletedNotification, asCodexItemStartedNotification, asCodexPlanDeltaNotification, asCodexRawResponseItemCompletedNotification, asCodexReasoningSummaryTextDeltaNotification, asCodexThreadTokenUsageUpdatedNotification, asCodexTurnCompletedNotification, isCodexLiveDeltaNotificationMethod, isCodexLiveDeltaSuppressionEnabled, } from "./codex-notification-guards.js";
19
+ import { captureCodexSummaryTextFromNotification, captureCodexSummaryTextFromTurnItems, cleanCodexRecapText, cleanCodexSummaryText, CODEX_RECAP_TIMEOUT_MS, CODEX_SUMMARY_TIMEOUT_MS, createCodexForkSummaryPrompt, createCodexForkSummaryThreadResumeParams, createCodexRecapPrompt, joinCodexSummaryText, resolveCodexRecapHelperModel, } from "./codex-summary-helpers.js";
18
20
  import { CODEX_BUILTIN_COMMANDS } from "./staticSlashCommands.js";
19
21
  const log = getLogger().child({ component: "codex-provider" });
20
- const execFileAsync = promisify(execFile);
22
+ const CODEX_DESKTOP_BROWSER_SKILL_NAME = "browser:control-in-app-browser";
21
23
  function logSdkCorrelationDebug(sessionId, message, metadata = {}) {
22
24
  if (!isCodexCorrelationDebugEnabled())
23
25
  return;
@@ -56,31 +58,8 @@ const MODEL_LIST_TIMEOUT_MS = 8000;
56
58
  const APP_SERVER_INIT_REQUEST_ID = 1;
57
59
  const APP_SERVER_MODEL_LIST_REQUEST_ID = 2;
58
60
  const APP_SERVER_SHUTDOWN_GRACE_MS = 1500;
59
- const CODEX_CLI_GPT55_MIN_VERSION = "0.124.0";
60
61
  const CODEX_FAILURE_TRACE_LIMIT = 12;
61
62
  const CODEX_FAILURE_PREVIEW_CHARS = 240;
62
- const CODEX_RECAP_TIMEOUT_MS = 20_000;
63
- const CODEX_SUMMARY_TIMEOUT_MS = 60_000;
64
- const CODEX_RECAP_MAX_TOTAL_CHARS = 6000;
65
- const CODEX_RECAP_CHEAPEST_MODEL_PREFERENCES = [
66
- "gpt-5.4-mini",
67
- "gpt-5.1-codex-mini",
68
- "gpt-5.3-codex-spark",
69
- ];
70
- const CODEX_DISABLE_LIVE_DELTAS_ENV = "YEP_CODEX_DISABLE_LIVE_DELTAS";
71
- const CODEX_LIVE_DELTA_NOTIFICATION_METHODS = new Set([
72
- "item/agentMessage/delta",
73
- "item/plan/delta",
74
- "item/reasoning/summaryTextDelta",
75
- "item/commandExecution/outputDelta",
76
- "item/fileChange/outputDelta",
77
- ]);
78
- function isCodexLiveDeltaSuppressionEnabled() {
79
- return process.env[CODEX_DISABLE_LIVE_DELTAS_ENV] === "true";
80
- }
81
- function isCodexLiveDeltaNotificationMethod(method) {
82
- return CODEX_LIVE_DELTA_NOTIFICATION_METHODS.has(method);
83
- }
84
63
  const CODEX_THINKING_OFF_MIN_REASONING_EFFORT_PREFIXES = [
85
64
  "gpt-5.3-codex-spark",
86
65
  ];
@@ -104,162 +83,6 @@ const CODEX_POLICY_OVERRIDES = {
104
83
  const DECLARE_CODEX_ORIGINATOR = false;
105
84
  const DECLARED_CODEX_ORIGINATOR = "Codex Desktop";
106
85
  const YEP_ANYWHERE_ORIGINATOR = "yep-anywhere";
107
- const PREFERRED_MODEL_ORDER = [
108
- "gpt-5.5",
109
- "gpt-5.4",
110
- "gpt-5.4-mini",
111
- "gpt-5.3-codex",
112
- "gpt-5.3-codex-spark",
113
- "gpt-5.2-codex",
114
- "gpt-5.1-codex-max",
115
- "gpt-5.2",
116
- "gpt-5.1-codex-mini",
117
- ];
118
- const FALLBACK_CODEX_MODELS = [
119
- {
120
- id: "gpt-5.5",
121
- name: "GPT-5.5",
122
- description: "Frontier model for complex coding, research, and real-world work.",
123
- defaultReasoningEffort: "medium",
124
- supportedReasoningEfforts: [
125
- {
126
- reasoningEffort: "low",
127
- description: "Fast responses with lighter reasoning",
128
- },
129
- {
130
- reasoningEffort: "medium",
131
- description: "Balances speed and reasoning depth for everyday tasks",
132
- },
133
- {
134
- reasoningEffort: "high",
135
- description: "Greater reasoning depth for complex problems",
136
- },
137
- {
138
- reasoningEffort: "xhigh",
139
- description: "Extra high reasoning depth for complex problems",
140
- },
141
- ],
142
- inputModalities: ["text", "image"],
143
- supportsPersonality: true,
144
- serviceTiers: [
145
- {
146
- id: "priority",
147
- name: "Fast",
148
- description: "1.5x speed, increased usage",
149
- },
150
- ],
151
- },
152
- {
153
- id: "gpt-5.4",
154
- name: "GPT-5.4",
155
- description: "Strong model for everyday coding.",
156
- isDefault: true,
157
- defaultReasoningEffort: "medium",
158
- supportedReasoningEfforts: [
159
- {
160
- reasoningEffort: "low",
161
- description: "Fast responses with lighter reasoning",
162
- },
163
- {
164
- reasoningEffort: "medium",
165
- description: "Balances speed and reasoning depth for everyday tasks",
166
- },
167
- {
168
- reasoningEffort: "high",
169
- description: "Greater reasoning depth for complex problems",
170
- },
171
- {
172
- reasoningEffort: "xhigh",
173
- description: "Extra high reasoning depth for complex problems",
174
- },
175
- ],
176
- inputModalities: ["text", "image"],
177
- supportsPersonality: true,
178
- serviceTiers: [
179
- {
180
- id: "priority",
181
- name: "Fast",
182
- description: "1.5x speed, increased usage",
183
- },
184
- ],
185
- },
186
- {
187
- id: "gpt-5.4-mini",
188
- name: "GPT-5.4-Mini",
189
- description: "Small, fast, and cost-efficient model for simpler coding tasks.",
190
- defaultReasoningEffort: "medium",
191
- supportedReasoningEfforts: [
192
- {
193
- reasoningEffort: "low",
194
- description: "Fast responses with lighter reasoning",
195
- },
196
- {
197
- reasoningEffort: "medium",
198
- description: "Balances speed and reasoning depth for everyday tasks",
199
- },
200
- {
201
- reasoningEffort: "high",
202
- description: "Greater reasoning depth for complex problems",
203
- },
204
- {
205
- reasoningEffort: "xhigh",
206
- description: "Extra high reasoning depth for complex problems",
207
- },
208
- ],
209
- inputModalities: ["text", "image"],
210
- supportsPersonality: true,
211
- },
212
- { id: "gpt-5.3-codex", name: "GPT-5.3-Codex" },
213
- { id: "gpt-5.3-codex-spark", name: "GPT-5.3-Codex-Spark" },
214
- { id: "gpt-5.2", name: "GPT-5.2" },
215
- ];
216
- const LEGACY_FALLBACK_CODEX_MODELS = [
217
- { id: "gpt-5.3-codex", name: "GPT-5.3-Codex" },
218
- { id: "gpt-5.2-codex", name: "GPT-5.2-Codex" },
219
- { id: "gpt-5.1-codex-max", name: "GPT-5.1-Codex-Max" },
220
- { id: "gpt-5.2", name: "GPT-5.2" },
221
- { id: "gpt-5.1-codex-mini", name: "GPT-5.1-Codex-Mini" },
222
- ];
223
- function normalizeSemver(raw) {
224
- if (!raw)
225
- return null;
226
- const match = raw.match(/(\d+)\.(\d+)\.(\d+)(?:-([\w.]+))?/);
227
- if (!match)
228
- return null;
229
- const [, major, minor, patch, pre] = match;
230
- return pre
231
- ? `${major}.${minor}.${patch}-${pre}`
232
- : `${major}.${minor}.${patch}`;
233
- }
234
- function compareSemver(a, b) {
235
- const parsedA = splitSemver(a);
236
- const parsedB = splitSemver(b);
237
- for (let i = 0; i < 3; i++) {
238
- const partA = parsedA.parts[i] ?? 0;
239
- const partB = parsedB.parts[i] ?? 0;
240
- if (partA !== partB)
241
- return partA < partB ? -1 : 1;
242
- }
243
- if (parsedA.pre === null && parsedB.pre === null)
244
- return 0;
245
- if (parsedA.pre === null)
246
- return 1;
247
- if (parsedB.pre === null)
248
- return -1;
249
- return parsedA.pre < parsedB.pre ? -1 : parsedA.pre > parsedB.pre ? 1 : 0;
250
- }
251
- function splitSemver(version) {
252
- const dashIndex = version.indexOf("-");
253
- const core = dashIndex === -1 ? version : version.slice(0, dashIndex);
254
- const pre = dashIndex === -1 ? null : version.slice(dashIndex + 1);
255
- return {
256
- parts: core.split(".").map((part) => {
257
- const parsed = Number.parseInt(part, 10);
258
- return Number.isFinite(parsed) ? parsed : 0;
259
- }),
260
- pre,
261
- };
262
- }
263
86
  async function terminateChildProcess(child, graceMs = APP_SERVER_SHUTDOWN_GRACE_MS) {
264
87
  if (!child?.pid || child.killed || child.exitCode !== null) {
265
88
  return;
@@ -390,6 +213,9 @@ class CodexAppServerClient {
390
213
  this.env = env;
391
214
  this.shouldSuppressNotification = shouldSuppressNotification;
392
215
  }
216
+ get isClosed() {
217
+ return this.closed;
218
+ }
393
219
  setServerRequestHandler(handler) {
394
220
  this.onServerRequest = handler;
395
221
  }
@@ -598,6 +424,7 @@ class CodexAppServerClient {
598
424
  params: {
599
425
  error: { message: error.message },
600
426
  willRetry: false,
427
+ codexProcessExit: true,
601
428
  },
602
429
  });
603
430
  this.notifications.close(error);
@@ -724,7 +551,7 @@ export class CodexProvider {
724
551
  async getModelsFromAppServer() {
725
552
  try {
726
553
  const appServerModels = await this.requestAppServerModelList();
727
- return this.normalizeModelList(appServerModels);
554
+ return normalizeCodexModelList(appServerModels);
728
555
  }
729
556
  catch (error) {
730
557
  log.debug({ error }, "Failed to query Codex app-server model list, using fallback models");
@@ -839,153 +666,20 @@ export class CodexProvider {
839
666
  })}\n`);
840
667
  });
841
668
  }
842
- normalizeModelList(models) {
843
- const orderLookup = new Map(PREFERRED_MODEL_ORDER.map((id, idx) => [id, idx]));
844
- const deduped = new Map();
845
- for (const [serverIndex, model] of models.entries()) {
846
- if (model.hidden === true)
847
- continue;
848
- const modelId = (model.model || model.id || "").trim();
849
- if (!modelId)
850
- continue;
851
- deduped.set(modelId, {
852
- model: {
853
- id: modelId,
854
- name: this.formatModelName(model.displayName || modelId),
855
- description: model.description,
856
- ...(model.isDefault === true ? { isDefault: true } : {}),
857
- ...this.normalizeModelReasoningMetadata(model),
858
- ...(Array.isArray(model.inputModalities)
859
- ? { inputModalities: model.inputModalities }
860
- : {}),
861
- ...(typeof model.supportsPersonality === "boolean"
862
- ? { supportsPersonality: model.supportsPersonality }
863
- : {}),
864
- ...this.normalizeModelServiceTierMetadata(model),
865
- },
866
- serverIndex,
867
- });
868
- const upgradeId = model.upgrade?.trim() ||
869
- (typeof model.upgradeInfo?.model === "string"
870
- ? model.upgradeInfo.model.trim()
871
- : "");
872
- if (upgradeId && !deduped.has(upgradeId)) {
873
- deduped.set(upgradeId, {
874
- model: {
875
- id: upgradeId,
876
- name: this.formatModelName(upgradeId),
877
- },
878
- serverIndex,
879
- });
880
- }
881
- }
882
- return [...deduped.values()]
883
- .map((entry, index) => ({
884
- model: entry.model,
885
- index,
886
- rank: this.getModelSortRank(entry.model, entry.serverIndex, orderLookup),
887
- }))
888
- .sort((a, b) => a.rank - b.rank || a.index - b.index)
889
- .map((entry) => entry.model);
890
- }
891
- normalizeModelReasoningMetadata(model) {
892
- const metadata = {};
893
- if (typeof model.defaultReasoningEffort === "string") {
894
- metadata.defaultReasoningEffort = model.defaultReasoningEffort;
895
- }
896
- if (Array.isArray(model.supportedReasoningEfforts)) {
897
- const efforts = model.supportedReasoningEfforts
898
- .map((effort) => {
899
- if (typeof effort.reasoningEffort !== "string")
900
- return null;
901
- return {
902
- reasoningEffort: effort.reasoningEffort,
903
- ...(typeof effort.description === "string"
904
- ? { description: effort.description }
905
- : {}),
906
- };
907
- })
908
- .filter((effort) => effort !== null);
909
- if (efforts.length > 0) {
910
- metadata.supportedReasoningEfforts = efforts;
911
- }
912
- }
913
- return metadata;
914
- }
915
- normalizeModelServiceTierMetadata(model) {
916
- if (!Array.isArray(model.serviceTiers)) {
917
- return {};
918
- }
919
- const serviceTiers = model.serviceTiers
920
- .map((tier) => {
921
- const id = typeof tier.id === "string" ? tier.id.trim() : "";
922
- const name = typeof tier.name === "string" ? tier.name.trim() : "";
923
- if (!id || !name)
924
- return null;
925
- return {
926
- id,
927
- name,
928
- ...(typeof tier.description === "string"
929
- ? { description: tier.description }
930
- : {}),
931
- };
932
- })
933
- .filter((tier) => tier !== null);
934
- return serviceTiers.length > 0 ? { serviceTiers } : {};
935
- }
936
- getModelSortRank(model, serverIndex, orderLookup) {
937
- if (model.id === "gpt-5.5") {
938
- return 0;
939
- }
940
- if (model.isDefault) {
941
- return 1;
942
- }
943
- const preferredRank = orderLookup.get(model.id);
944
- if (preferredRank !== undefined) {
945
- return 2 + preferredRank;
946
- }
947
- return 2 + PREFERRED_MODEL_ORDER.length + serverIndex;
948
- }
949
669
  async getFallbackCodexModels() {
950
670
  const version = await this.getInstalledCodexCliVersion();
951
- if (version && compareSemver(version, CODEX_CLI_GPT55_MIN_VERSION) < 0) {
952
- return LEGACY_FALLBACK_CODEX_MODELS;
953
- }
954
- return FALLBACK_CODEX_MODELS;
671
+ return getFallbackCodexModelsForCliVersion(version);
955
672
  }
956
673
  async getInstalledCodexCliVersion() {
957
674
  try {
958
675
  const codexCommand = await this.resolveCodexCommand();
959
- const { stdout } = await execFileAsync(codexCommand, ["--version"], {
960
- encoding: "utf-8",
961
- timeout: 3000,
962
- });
963
- return normalizeSemver(stdout);
676
+ const version = await getCodexCliVersion(codexCommand);
677
+ return normalizeSemver(version);
964
678
  }
965
679
  catch {
966
680
  return null;
967
681
  }
968
682
  }
969
- formatModelName(value) {
970
- return value
971
- .trim()
972
- .split("-")
973
- .map((part) => {
974
- const lower = part.toLowerCase();
975
- if (lower === "gpt")
976
- return "GPT";
977
- if (lower === "codex")
978
- return "Codex";
979
- if (lower === "mini")
980
- return "Mini";
981
- if (lower === "max")
982
- return "Max";
983
- if (lower.length === 0)
984
- return "";
985
- return lower.charAt(0).toUpperCase() + lower.slice(1);
986
- })
987
- .join("-");
988
- }
989
683
  mapEffortToReasoningEffort(effort, thinking, model) {
990
684
  if (thinking?.type === "disabled") {
991
685
  const normalizedModel = model?.trim().toLowerCase();
@@ -1436,10 +1130,13 @@ export class CodexProvider {
1436
1130
  turnResult.turn.error?.message) {
1437
1131
  yield {
1438
1132
  type: "error",
1133
+ uuid: `codex-error-${turnResult.turn.id}`,
1439
1134
  session_id: sessionId,
1440
1135
  error: turnResult.turn.error.message,
1441
1136
  codexErrorInfo: turnResult.turn.error.codexErrorInfo ?? null,
1442
1137
  codexAdditionalDetails: turnResult.turn.error.additionalDetails ?? null,
1138
+ codexWillRetry: false,
1139
+ codexTurnId: turnResult.turn.id,
1443
1140
  codexFailureTrace: this.snapshotCodexFailureTrace(failureTrace),
1444
1141
  codexFailureSummary: this.formatCodexFailureTrace(failureTrace),
1445
1142
  codexRequestId: this.extractOpenAIRequestId(turnResult.turn.error, turnResult.turn.error.additionalDetails, turnResult.turn.error.message),
@@ -1455,8 +1152,16 @@ export class CodexProvider {
1455
1152
  const codexFailureTrace = this.snapshotCodexFailureTrace(failureTrace);
1456
1153
  log.error({ error, codexFailureTrace }, "Error in codex app-server session");
1457
1154
  if (!signal.aborted) {
1155
+ const isProcessFailure = appServer.isClosed;
1458
1156
  yield {
1459
1157
  type: "error",
1158
+ ...(isProcessFailure
1159
+ ? {
1160
+ uuid: `codex-error-${sessionId || "unknown"}-process-exit`,
1161
+ codexWillRetry: false,
1162
+ codexErrorScope: "app_server_process",
1163
+ }
1164
+ : {}),
1460
1165
  session_id: sessionId,
1461
1166
  error: error instanceof Error ? error.message : String(error),
1462
1167
  codexFailureTrace,
@@ -1491,18 +1196,24 @@ export class CodexProvider {
1491
1196
  }
1492
1197
  isTurnTerminalNotification(notification, turnId) {
1493
1198
  if (notification.method === "turn/completed") {
1494
- const params = this.asTurnCompletedNotification(notification.params);
1199
+ const params = asCodexTurnCompletedNotification(notification.params);
1495
1200
  return params?.turn.id === turnId;
1496
1201
  }
1497
1202
  if (notification.method === "error") {
1498
- const params = this.asErrorNotification(notification.params);
1499
- return params?.turnId === turnId && !params.willRetry;
1203
+ const params = asCodexErrorNotification(notification.params);
1204
+ if (params) {
1205
+ return params.turnId === turnId && !params.willRetry;
1206
+ }
1207
+ const rawParams = notification.params && typeof notification.params === "object"
1208
+ ? notification.params
1209
+ : null;
1210
+ return (rawParams?.codexProcessExit === true && rawParams.willRetry === false);
1500
1211
  }
1501
1212
  return false;
1502
1213
  }
1503
1214
  updateBackgroundProcessTracking(notification, runtimeState) {
1504
1215
  if (notification.method === "rawResponseItem/completed") {
1505
- const params = this.asRawResponseItemCompletedNotification(notification.params);
1216
+ const params = asCodexRawResponseItemCompletedNotification(notification.params);
1506
1217
  const item = params?.item && typeof params.item === "object"
1507
1218
  ? params.item
1508
1219
  : null;
@@ -1530,7 +1241,7 @@ export class CodexProvider {
1530
1241
  return;
1531
1242
  }
1532
1243
  if (notification.method === "item/completed") {
1533
- const params = this.asItemCompletedNotification(notification.params);
1244
+ const params = asCodexItemCompletedNotification(notification.params);
1534
1245
  if (!params)
1535
1246
  return;
1536
1247
  const normalized = this.normalizeThreadItem(params.item);
@@ -1541,7 +1252,7 @@ export class CodexProvider {
1541
1252
  return;
1542
1253
  }
1543
1254
  if (notification.method === "item/started") {
1544
- const params = this.asItemStartedNotification(notification.params);
1255
+ const params = asCodexItemStartedNotification(notification.params);
1545
1256
  if (!params)
1546
1257
  return;
1547
1258
  const normalized = this.normalizeThreadItem(params.item);
@@ -1605,7 +1316,7 @@ export class CodexProvider {
1605
1316
  threadId: options.sessionId,
1606
1317
  cwd: options.cwd,
1607
1318
  ...this.buildThreadPermissionParams(policy),
1608
- config: null,
1319
+ config: this.buildThreadConfigOverrides({}),
1609
1320
  };
1610
1321
  if (experimentalApiEnabled) {
1611
1322
  params.excludeTurns = true;
@@ -1673,11 +1384,25 @@ export class CodexProvider {
1673
1384
  };
1674
1385
  }
1675
1386
  buildThreadConfigOverrides(options) {
1387
+ // The OpenAI browser plugin controls a desktop-owned browser backend that
1388
+ // YA's Codex app-server host does not provide. Suppress the unavailable
1389
+ // skill at thread scope so Codex follows YA's Playwright fallback instead
1390
+ // of advertising a browser that fails during backend discovery.
1391
+ const config = {
1392
+ skills: {
1393
+ config: [
1394
+ {
1395
+ name: CODEX_DESKTOP_BROWSER_SKILL_NAME,
1396
+ enabled: false,
1397
+ },
1398
+ ],
1399
+ },
1400
+ };
1676
1401
  const reasoningEffort = this.mapEffortToReasoningEffort(options.effort, options.thinking, options.model);
1677
- if (!reasoningEffort) {
1678
- return null;
1402
+ if (reasoningEffort) {
1403
+ config.model_reasoning_effort = reasoningEffort;
1679
1404
  }
1680
- return { model_reasoning_effort: reasoningEffort };
1405
+ return config;
1681
1406
  }
1682
1407
  createTurnStartParams(threadId, userPrompt, options, turnPolicy = null) {
1683
1408
  return {
@@ -1712,8 +1437,8 @@ export class CodexProvider {
1712
1437
  }
1713
1438
  }
1714
1439
  async generateSideSessionRecap(recentAssistantText, requestedModel) {
1715
- const userPrompt = this.createRecapPrompt(recentAssistantText);
1716
- const model = await this.resolveRecapHelperModel(requestedModel);
1440
+ const userPrompt = createCodexRecapPrompt(recentAssistantText);
1441
+ const model = await resolveCodexRecapHelperModel(requestedModel, () => this.getAvailableModels());
1717
1442
  const codexCommand = await this.resolveCodexCommand();
1718
1443
  const abortController = new AbortController();
1719
1444
  let timedOut = false;
@@ -1745,18 +1470,19 @@ export class CodexProvider {
1745
1470
  summary: "auto",
1746
1471
  });
1747
1472
  const textByItemId = new Map();
1748
- this.captureRecapTextFromTurnItems(turnResult.turn.items, textByItemId);
1473
+ const normalizeThreadItem = this.normalizeThreadItem.bind(this);
1474
+ captureCodexSummaryTextFromTurnItems(turnResult.turn.items, textByItemId, normalizeThreadItem);
1749
1475
  if (turnResult.turn.status === "failed") {
1750
1476
  throw new Error(turnResult.turn.error?.message ?? "Codex recap generation failed");
1751
1477
  }
1752
1478
  let turnComplete = turnResult.turn.status !== "inProgress";
1753
1479
  while (!turnComplete && !abortController.signal.aborted) {
1754
1480
  const notification = await appServer.nextNotification(abortController.signal);
1755
- this.captureRecapTextFromNotification(notification, textByItemId);
1481
+ captureCodexSummaryTextFromNotification(notification, textByItemId, normalizeThreadItem);
1756
1482
  if (notification.method !== "turn/completed") {
1757
1483
  continue;
1758
1484
  }
1759
- const completed = this.asTurnCompletedNotification(notification.params);
1485
+ const completed = asCodexTurnCompletedNotification(notification.params);
1760
1486
  if (completed?.turn.status === "failed") {
1761
1487
  throw new Error(completed.turn.error?.message ?? "Codex recap generation failed");
1762
1488
  }
@@ -1765,10 +1491,7 @@ export class CodexProvider {
1765
1491
  if (abortController.signal.aborted) {
1766
1492
  throw new Error("Timed out generating Codex recap");
1767
1493
  }
1768
- const cleaned = [...textByItemId.values()]
1769
- .join("\n")
1770
- .replace(/\s*\(disable recaps in \/config\)\s*$/u, "")
1771
- .trim();
1494
+ const cleaned = cleanCodexRecapText(joinCodexSummaryText(textByItemId));
1772
1495
  if (!cleaned) {
1773
1496
  throw new Error("Recap generation returned empty text");
1774
1497
  }
@@ -1786,46 +1509,8 @@ export class CodexProvider {
1786
1509
  await appServer.close();
1787
1510
  }
1788
1511
  }
1789
- createRecapPrompt(recentAssistantText) {
1790
- const trimmed = recentAssistantText
1791
- .map((text) => text.trim())
1792
- .filter((text) => text.length > 0);
1793
- if (trimmed.length === 0) {
1794
- throw new Error("No recent assistant text to summarize");
1795
- }
1796
- let total = 0;
1797
- const tail = [];
1798
- for (let i = trimmed.length - 1; i >= 0; i--) {
1799
- const entry = trimmed[i] ?? "";
1800
- if (total + entry.length > CODEX_RECAP_MAX_TOTAL_CHARS) {
1801
- break;
1802
- }
1803
- tail.unshift(entry);
1804
- total += entry.length;
1805
- }
1806
- if (tail.length === 0) {
1807
- const last = trimmed[trimmed.length - 1] ?? "";
1808
- tail.push(last.slice(-CODEX_RECAP_MAX_TOTAL_CHARS));
1809
- }
1810
- const transcript = tail
1811
- .map((text, index) => `--- Assistant turn ${index + 1} ---\n${text}`)
1812
- .join("\n\n");
1813
- return [
1814
- "The user stepped away and is coming back. Recap in under 40 words,",
1815
- "1-2 plain sentences, no markdown. Lead with the overall thrust of what",
1816
- "the assistant did or is doing; mention any pending next action.",
1817
- "Do not greet, do not ask a question, do not add a sign-off.",
1818
- "",
1819
- "Recent assistant output:",
1820
- transcript,
1821
- ].join("\n");
1822
- }
1823
1512
  async generateForkBackedSummary(request) {
1824
- const userPrompt = request.purpose === "session-retitle"
1825
- ? this.createSessionRetitlePrompt(request)
1826
- : request.purpose === "recap"
1827
- ? this.createForkedRecapPrompt()
1828
- : this.createForkAfterSummaryPrompt(request);
1513
+ const userPrompt = createCodexForkSummaryPrompt(request);
1829
1514
  const codexCommand = await this.resolveCodexCommand();
1830
1515
  const appServer = new CodexAppServerClient(codexCommand, request.cwd, this.getCodexEnv());
1831
1516
  const abortController = new AbortController();
@@ -1856,7 +1541,7 @@ export class CodexProvider {
1856
1541
  await appServer.connect();
1857
1542
  const experimentalApiEnabled = await this.initializeAppServer(appServer);
1858
1543
  appServer.notify("initialized");
1859
- const threadResult = await appServer.request("thread/resume", this.createForkSummaryThreadResumeParams(request, experimentalApiEnabled));
1544
+ const threadResult = await appServer.request("thread/resume", createCodexForkSummaryThreadResumeParams(request, experimentalApiEnabled));
1860
1545
  const threadId = threadResult.thread.id || request.generatorSessionId;
1861
1546
  const turnResult = await appServer.request("turn/start", {
1862
1547
  threadId,
@@ -1866,7 +1551,8 @@ export class CodexProvider {
1866
1551
  approvalPolicy: "untrusted",
1867
1552
  });
1868
1553
  const textByItemId = new Map();
1869
- this.captureRecapTextFromTurnItems(turnResult.turn.items, textByItemId);
1554
+ const normalizeThreadItem = this.normalizeThreadItem.bind(this);
1555
+ captureCodexSummaryTextFromTurnItems(turnResult.turn.items, textByItemId, normalizeThreadItem);
1870
1556
  if (turnResult.turn.status === "failed") {
1871
1557
  throw new Error(turnResult.turn.error?.message ?? "Codex summary generation failed");
1872
1558
  }
@@ -1874,13 +1560,13 @@ export class CodexProvider {
1874
1560
  let turnComplete = turnResult.turn.status !== "inProgress";
1875
1561
  while (!turnComplete && !abortController.signal.aborted) {
1876
1562
  const notification = await appServer.nextNotification(abortController.signal);
1877
- this.captureRecapTextFromNotification(notification, textByItemId);
1563
+ captureCodexSummaryTextFromNotification(notification, textByItemId, normalizeThreadItem);
1878
1564
  if (notification.method === "turn/completed") {
1879
- const completed = this.asTurnCompletedNotification(notification.params);
1565
+ const completed = asCodexTurnCompletedNotification(notification.params);
1880
1566
  if (completed?.turn.id !== turnId) {
1881
1567
  continue;
1882
1568
  }
1883
- this.captureRecapTextFromTurnItems(completed.turn.items, textByItemId);
1569
+ captureCodexSummaryTextFromTurnItems(completed.turn.items, textByItemId, normalizeThreadItem);
1884
1570
  if (completed.turn.status === "failed") {
1885
1571
  throw new Error(completed.turn.error?.message ??
1886
1572
  "Codex summary generation failed");
@@ -1889,7 +1575,7 @@ export class CodexProvider {
1889
1575
  continue;
1890
1576
  }
1891
1577
  if (notification.method === "error") {
1892
- const error = this.asErrorNotification(notification.params);
1578
+ const error = asCodexErrorNotification(notification.params);
1893
1579
  if (error?.turnId === turnId && !error.willRetry) {
1894
1580
  throw new Error(error.error.message ?? "Codex summary generation failed");
1895
1581
  }
@@ -1901,7 +1587,7 @@ export class CodexProvider {
1901
1587
  }
1902
1588
  throw new Error("Timed out generating Codex summary");
1903
1589
  }
1904
- const cleaned = [...textByItemId.values()].join("\n").trim();
1590
+ const cleaned = cleanCodexSummaryText(joinCodexSummaryText(textByItemId));
1905
1591
  if (!cleaned) {
1906
1592
  throw new Error("Summary generation returned empty text");
1907
1593
  }
@@ -1923,84 +1609,6 @@ export class CodexProvider {
1923
1609
  await appServer.close();
1924
1610
  }
1925
1611
  }
1926
- createForkSummaryThreadResumeParams(request, experimentalApiEnabled = false) {
1927
- const params = {
1928
- threadId: request.generatorSessionId,
1929
- model: null,
1930
- cwd: request.cwd,
1931
- approvalPolicy: "untrusted",
1932
- sandbox: "read-only",
1933
- config: null,
1934
- developerInstructions: request.purpose === "session-retitle"
1935
- ? "You are a title helper. Reply with the session title only, no preamble. Do not call tools."
1936
- : request.purpose === "recap"
1937
- ? "You are a recap helper. Reply with the recap text only, no preamble. Do not call tools."
1938
- : "You are a handoff summary helper. Reply with the summary text only, no preamble. Do not call tools.",
1939
- };
1940
- if (experimentalApiEnabled) {
1941
- params.excludeTurns = true;
1942
- }
1943
- return params;
1944
- }
1945
- createForkAfterSummaryPrompt(request) {
1946
- const instructions = request.instructions?.trim();
1947
- const boundaryContext = request.afterTurnContext?.trim();
1948
- return [
1949
- "The first non-empty line must be a concise title of at most 120 characters, with no trailing period.",
1950
- "Write it as: Title: <title>",
1951
- "Then leave one blank line before the handoff summary.",
1952
- "",
1953
- "Summarize the useful state after the retained fork boundary for a peer-agent handoff.",
1954
- `The target fork retains the conversation through completed-turn message id ${request.afterTurnMessageId}.`,
1955
- boundaryContext
1956
- ? `The retained boundary is the completed turn ending with this excerpt:\n${boundaryContext}`
1957
- : undefined,
1958
- "The target fork already includes the original request and the assistant/tool work through that selected completed turn.",
1959
- "Do not repeat setup, instruction loading, initial repository orientation, or investigation already present in that retained prefix.",
1960
- "Preserve decisions, constraints, current state, changed files, verification evidence, open risks, and the next useful action.",
1961
- "Do not continue the task. Write text that can be submitted as the next user turn in the target fork.",
1962
- instructions ? "" : undefined,
1963
- instructions ? "Additional user instructions:" : undefined,
1964
- instructions || undefined,
1965
- ]
1966
- .filter((part) => part !== undefined)
1967
- .join("\n");
1968
- }
1969
- createForkedRecapPrompt() {
1970
- return [
1971
- "The user stepped away and is coming back.",
1972
- "Recap the current session state in under 40 words, 1-2 plain sentences, no markdown.",
1973
- "Lead with what the assistant did or is doing; mention any pending next action.",
1974
- "Do not greet, do not ask a question, do not add a sign-off.",
1975
- ].join("\n");
1976
- }
1977
- createSessionRetitlePrompt(request) {
1978
- const lengthTarget = request.lengthTarget ?? 80;
1979
- const currentTitle = request.currentTitle?.trim();
1980
- return [
1981
- "What is a good new title for this session?",
1982
- "",
1983
- `Target length: under ${lengthTarget} characters.`,
1984
- currentTitle ? `Current title: ${currentTitle}` : undefined,
1985
- "Prefer a concrete task/result phrase over a generic chat title.",
1986
- "Return only the title. Do not quote it. Do not add a trailing period.",
1987
- ]
1988
- .filter((part) => part !== undefined)
1989
- .join("\n");
1990
- }
1991
- async resolveRecapHelperModel(requestedModel) {
1992
- if (!requestedModel || requestedModel !== HELPER_SIDE_MODEL_CHEAPEST) {
1993
- return requestedModel ?? null;
1994
- }
1995
- const models = await this.getAvailableModels();
1996
- for (const preferred of CODEX_RECAP_CHEAPEST_MODEL_PREFERENCES) {
1997
- if (models.some((model) => model.id === preferred)) {
1998
- return preferred;
1999
- }
2000
- }
2001
- return (models.find((model) => model.id.toLowerCase().includes("mini"))?.id ??
2002
- null);
2003
- }
2004
1612
  handleRecapServerRequest(request) {
2005
1613
  return this.handleNonTurnServerRequest(request, "recap helper");
2006
1614
  }
@@ -2029,63 +1637,6 @@ export class CodexProvider {
2029
1637
  return Promise.resolve({});
2030
1638
  }
2031
1639
  }
2032
- captureRecapTextFromTurnItems(items, textByItemId) {
2033
- for (const item of items) {
2034
- const normalized = this.normalizeThreadItem(item);
2035
- if (normalized?.type === "agent_message" && normalized.text.trim()) {
2036
- textByItemId.set(normalized.id, normalized.text);
2037
- }
2038
- }
2039
- }
2040
- captureRecapTextFromNotification(notification, textByItemId) {
2041
- if (notification.method === "item/agentMessage/delta") {
2042
- const params = this.asAgentMessageDeltaNotification(notification.params);
2043
- if (!params?.delta)
2044
- return;
2045
- textByItemId.set(params.itemId, `${textByItemId.get(params.itemId) ?? ""}${params.delta}`);
2046
- return;
2047
- }
2048
- if (notification.method === "item/completed") {
2049
- const params = this.asItemCompletedNotification(notification.params);
2050
- if (!params || textByItemId.has(params.item.id))
2051
- return;
2052
- const normalized = this.normalizeThreadItem(params.item);
2053
- if (normalized?.type === "agent_message" && normalized.text.trim()) {
2054
- textByItemId.set(normalized.id, normalized.text);
2055
- }
2056
- return;
2057
- }
2058
- if (notification.method !== "rawResponseItem/completed") {
2059
- return;
2060
- }
2061
- const params = this.asRawResponseItemCompletedNotification(notification.params);
2062
- const text = this.extractRawResponseMessageText(params?.item);
2063
- if (params && text) {
2064
- textByItemId.set(`raw-${params.turnId}-${textByItemId.size}`, text);
2065
- }
2066
- }
2067
- extractRawResponseMessageText(item) {
2068
- if (!item || typeof item !== "object")
2069
- return null;
2070
- const record = item;
2071
- if (record.type !== "message" || record.role !== "assistant") {
2072
- return null;
2073
- }
2074
- if (!Array.isArray(record.content)) {
2075
- return null;
2076
- }
2077
- const parts = record.content
2078
- .map((contentItem) => {
2079
- if (!contentItem || typeof contentItem !== "object")
2080
- return "";
2081
- const contentRecord = contentItem;
2082
- return contentRecord.type === "output_text"
2083
- ? (this.getOptionalString(contentRecord.text) ?? "")
2084
- : "";
2085
- })
2086
- .filter((text) => text.length > 0);
2087
- return parts.length > 0 ? parts.join("\n") : null;
2088
- }
2089
1640
  createSessionConfigAckMessage(sessionId, model, requestedModel, reasoningEffort, requestedReasoningEffort) {
2090
1641
  const parts = [];
2091
1642
  const normalizedModel = typeof model === "string" ? model.trim() : "";
@@ -2184,8 +1735,8 @@ export class CodexProvider {
2184
1735
  case "item/started":
2185
1736
  case "item/completed": {
2186
1737
  const params = notification.method === "item/started"
2187
- ? this.asItemStartedNotification(notification.params)
2188
- : this.asItemCompletedNotification(notification.params);
1738
+ ? asCodexItemStartedNotification(notification.params)
1739
+ : asCodexItemCompletedNotification(notification.params);
2189
1740
  const item = params?.item && typeof params.item === "object"
2190
1741
  ? params.item
2191
1742
  : null;
@@ -2224,7 +1775,7 @@ export class CodexProvider {
2224
1775
  });
2225
1776
  }
2226
1777
  case "rawResponseItem/completed": {
2227
- const params = this.asRawResponseItemCompletedNotification(notification.params);
1778
+ const params = asCodexRawResponseItemCompletedNotification(notification.params);
2228
1779
  const item = params?.item && typeof params.item === "object"
2229
1780
  ? params.item
2230
1781
  : null;
@@ -2250,7 +1801,7 @@ export class CodexProvider {
2250
1801
  });
2251
1802
  }
2252
1803
  case "turn/completed": {
2253
- const params = this.asTurnCompletedNotification(notification.params);
1804
+ const params = asCodexTurnCompletedNotification(notification.params);
2254
1805
  return base({
2255
1806
  sourceEvent: notification.method,
2256
1807
  turnId: params?.turn.id,
@@ -2263,15 +1814,22 @@ export class CodexProvider {
2263
1814
  });
2264
1815
  }
2265
1816
  case "error": {
2266
- const params = this.asErrorNotification(notification.params);
1817
+ const params = asCodexErrorNotification(notification.params);
2267
1818
  const fallbackError = this.extractErrorRecord(notification.params);
1819
+ const rawParams = notification.params && typeof notification.params === "object"
1820
+ ? notification.params
1821
+ : null;
1822
+ const willRetry = params?.willRetry ??
1823
+ (typeof rawParams?.willRetry === "boolean"
1824
+ ? rawParams.willRetry
1825
+ : false);
2268
1826
  const errorMessage = params?.error.message ??
2269
1827
  this.getOptionalString(fallbackError?.message) ??
2270
1828
  "Codex turn failed";
2271
1829
  return base({
2272
1830
  sourceEvent: notification.method,
2273
1831
  turnId: params?.turnId,
2274
- phase: params?.willRetry ? "retrying" : "terminal",
1832
+ phase: willRetry ? "retrying" : "terminal",
2275
1833
  errorMessage,
2276
1834
  codexErrorInfo: params?.error.codexErrorInfo ??
2277
1835
  fallbackError?.codexErrorInfo ??
@@ -2422,7 +1980,7 @@ export class CodexProvider {
2422
1980
  return `${trimmed.slice(0, CODEX_FAILURE_PREVIEW_CHARS)}...`;
2423
1981
  }
2424
1982
  extractTurnUsage(params) {
2425
- const notification = this.asThreadTokenUsageUpdatedNotification(params);
1983
+ const notification = asCodexThreadTokenUsageUpdatedNotification(params);
2426
1984
  if (!notification)
2427
1985
  return null;
2428
1986
  return {
@@ -2679,7 +2237,7 @@ export class CodexProvider {
2679
2237
  return [message];
2680
2238
  }
2681
2239
  case "turn/completed": {
2682
- const params = this.asTurnCompletedNotification(notification.params);
2240
+ const params = asCodexTurnCompletedNotification(notification.params);
2683
2241
  const turnId = params?.turn.id ?? null;
2684
2242
  const turnStatus = params?.turn.status;
2685
2243
  const usage = turnId ? usageByTurnId.get(turnId) : undefined;
@@ -2756,20 +2314,37 @@ export class CodexProvider {
2756
2314
  return messages;
2757
2315
  }
2758
2316
  case "error": {
2759
- const params = this.asErrorNotification(notification.params);
2760
- const errorMessage = params?.error.message;
2317
+ const params = asCodexErrorNotification(notification.params);
2318
+ const rawParams = notification.params && typeof notification.params === "object"
2319
+ ? notification.params
2320
+ : null;
2321
+ const fallbackError = this.extractErrorRecord(notification.params);
2322
+ const errorMessage = params?.error.message ??
2323
+ this.getOptionalString(fallbackError?.message);
2324
+ const willRetry = params?.willRetry ??
2325
+ (typeof rawParams?.willRetry === "boolean"
2326
+ ? rawParams.willRetry
2327
+ : false);
2328
+ const isProcessExit = rawParams?.codexProcessExit === true;
2761
2329
  const message = (typeof errorMessage === "string" && errorMessage) ||
2762
- (typeof notification.params?.message ===
2763
- "string"
2764
- ? notification.params.message
2330
+ (typeof rawParams?.message === "string"
2331
+ ? rawParams.message
2765
2332
  : "Codex turn failed");
2766
2333
  const errorEvent = {
2767
2334
  type: "error",
2335
+ uuid: params?.turnId
2336
+ ? `codex-error-${params.turnId}`
2337
+ : `codex-error-${sessionId}-${Date.now()}`,
2768
2338
  session_id: sessionId,
2769
2339
  error: message,
2770
- codexErrorInfo: params?.error.codexErrorInfo ?? null,
2771
- codexAdditionalDetails: params?.error.additionalDetails ?? null,
2772
- codexWillRetry: params?.willRetry ?? false,
2340
+ codexErrorInfo: params?.error.codexErrorInfo ??
2341
+ fallbackError?.codexErrorInfo ??
2342
+ null,
2343
+ codexAdditionalDetails: params?.error.additionalDetails ??
2344
+ this.getOptionalString(fallbackError?.additionalDetails) ??
2345
+ null,
2346
+ codexWillRetry: willRetry,
2347
+ codexErrorScope: isProcessExit ? "app_server_process" : "turn",
2773
2348
  codexThreadId: params?.threadId,
2774
2349
  codexTurnId: params?.turnId,
2775
2350
  codexRequestId: this.extractOpenAIRequestId(notification.params, params?.error, message),
@@ -2784,8 +2359,8 @@ export class CodexProvider {
2784
2359
  case "item/started":
2785
2360
  case "item/completed": {
2786
2361
  const params = notification.method === "item/started"
2787
- ? this.asItemStartedNotification(notification.params)
2788
- : this.asItemCompletedNotification(notification.params);
2362
+ ? asCodexItemStartedNotification(notification.params)
2363
+ : asCodexItemCompletedNotification(notification.params);
2789
2364
  if (!params)
2790
2365
  return [];
2791
2366
  const normalized = this.normalizeThreadItem(params.item);
@@ -2805,7 +2380,7 @@ export class CodexProvider {
2805
2380
  return messages;
2806
2381
  }
2807
2382
  case "item/agentMessage/delta": {
2808
- const params = this.asAgentMessageDeltaNotification(notification.params);
2383
+ const params = asCodexAgentMessageDeltaNotification(notification.params);
2809
2384
  if (!params?.delta)
2810
2385
  return [];
2811
2386
  return [
@@ -2813,7 +2388,7 @@ export class CodexProvider {
2813
2388
  ];
2814
2389
  }
2815
2390
  case "item/plan/delta": {
2816
- const params = this.asPlanDeltaNotification(notification.params);
2391
+ const params = asCodexPlanDeltaNotification(notification.params);
2817
2392
  if (!params?.delta)
2818
2393
  return [];
2819
2394
  return [
@@ -2821,7 +2396,7 @@ export class CodexProvider {
2821
2396
  ];
2822
2397
  }
2823
2398
  case "item/reasoning/summaryTextDelta": {
2824
- const params = this.asReasoningSummaryTextDeltaNotification(notification.params);
2399
+ const params = asCodexReasoningSummaryTextDeltaNotification(notification.params);
2825
2400
  if (!params?.delta)
2826
2401
  return [];
2827
2402
  return [
@@ -2829,7 +2404,7 @@ export class CodexProvider {
2829
2404
  ];
2830
2405
  }
2831
2406
  case "item/commandExecution/outputDelta": {
2832
- const params = this.asCommandExecutionOutputDeltaNotification(notification.params);
2407
+ const params = asCodexCommandExecutionOutputDeltaNotification(notification.params);
2833
2408
  if (!params?.delta)
2834
2409
  return [];
2835
2410
  return [
@@ -2837,7 +2412,7 @@ export class CodexProvider {
2837
2412
  ];
2838
2413
  }
2839
2414
  case "item/fileChange/outputDelta": {
2840
- const params = this.asFileChangeOutputDeltaNotification(notification.params);
2415
+ const params = asCodexFileChangeOutputDeltaNotification(notification.params);
2841
2416
  if (!params?.delta)
2842
2417
  return [];
2843
2418
  return [
@@ -2845,7 +2420,7 @@ export class CodexProvider {
2845
2420
  ];
2846
2421
  }
2847
2422
  case "rawResponseItem/completed": {
2848
- const params = this.asRawResponseItemCompletedNotification(notification.params);
2423
+ const params = asCodexRawResponseItemCompletedNotification(notification.params);
2849
2424
  if (!params)
2850
2425
  return [];
2851
2426
  return this.convertRawResponseItemToSDKMessages(params, sessionId, liveEventState);
@@ -2885,6 +2460,7 @@ export class CodexProvider {
2885
2460
  : Array.isArray(itemRecord.command_actions)
2886
2461
  ? itemRecord.command_actions
2887
2462
  : undefined) ?? undefined;
2463
+ const cwd = this.getOptionalString(itemRecord.cwd);
2888
2464
  return {
2889
2465
  id,
2890
2466
  type: "command_execution",
@@ -2895,7 +2471,11 @@ export class CodexProvider {
2895
2471
  exit_code: this.getOptionalNumber(itemRecord.exit_code) ??
2896
2472
  this.getOptionalNumber(itemRecord.exitCode) ??
2897
2473
  undefined,
2474
+ durationMs: this.getOptionalNumber(itemRecord.duration_ms) ??
2475
+ this.getOptionalNumber(itemRecord.durationMs) ??
2476
+ undefined,
2898
2477
  status: this.normalizeStatus(itemRecord.status),
2478
+ ...(cwd ? { cwd } : {}),
2899
2479
  ...(commandActions ? { commandActions } : {}),
2900
2480
  };
2901
2481
  }
@@ -3072,52 +2652,6 @@ export class CodexProvider {
3072
2652
  return "unknown";
3073
2653
  return status.replace(/[A-Z]/g, (m) => `_${m.toLowerCase()}`);
3074
2654
  }
3075
- asTurnCompletedNotification(params) {
3076
- if (!params || typeof params !== "object")
3077
- return null;
3078
- const record = params;
3079
- if (typeof record.threadId !== "string" ||
3080
- !record.turn ||
3081
- typeof record.turn !== "object" ||
3082
- typeof record.turn.id !== "string") {
3083
- return null;
3084
- }
3085
- return params;
3086
- }
3087
- asErrorNotification(params) {
3088
- if (!params || typeof params !== "object")
3089
- return null;
3090
- const record = params;
3091
- if (typeof record.threadId !== "string" ||
3092
- typeof record.turnId !== "string" ||
3093
- typeof record.willRetry !== "boolean" ||
3094
- !record.error ||
3095
- typeof record.error !== "object" ||
3096
- typeof record.error.message !== "string") {
3097
- return null;
3098
- }
3099
- return params;
3100
- }
3101
- asThreadTokenUsageUpdatedNotification(params) {
3102
- if (!params || typeof params !== "object")
3103
- return null;
3104
- const record = params;
3105
- const tokenUsage = record.tokenUsage && typeof record.tokenUsage === "object"
3106
- ? record.tokenUsage
3107
- : null;
3108
- const last = tokenUsage?.last && typeof tokenUsage.last === "object"
3109
- ? tokenUsage.last
3110
- : null;
3111
- if (typeof record.threadId !== "string" ||
3112
- typeof record.turnId !== "string" ||
3113
- !last ||
3114
- typeof last.inputTokens !== "number" ||
3115
- typeof last.outputTokens !== "number" ||
3116
- typeof last.cachedInputTokens !== "number") {
3117
- return null;
3118
- }
3119
- return params;
3120
- }
3121
2655
  asCommandExecutionRequestApprovalParams(params) {
3122
2656
  if (!params || typeof params !== "object")
3123
2657
  return null;
@@ -3166,116 +2700,17 @@ export class CodexProvider {
3166
2700
  }
3167
2701
  return params;
3168
2702
  }
3169
- asItemStartedNotification(params) {
3170
- if (!params || typeof params !== "object")
3171
- return null;
3172
- const record = params;
3173
- if (typeof record.threadId !== "string" ||
3174
- typeof record.turnId !== "string" ||
3175
- !record.item ||
3176
- typeof record.item !== "object") {
3177
- return null;
3178
- }
3179
- return params;
3180
- }
3181
- asItemCompletedNotification(params) {
3182
- if (!params || typeof params !== "object")
3183
- return null;
3184
- const record = params;
3185
- if (typeof record.threadId !== "string" ||
3186
- typeof record.turnId !== "string" ||
3187
- !record.item ||
3188
- typeof record.item !== "object") {
3189
- return null;
3190
- }
3191
- return params;
3192
- }
3193
- asAgentMessageDeltaNotification(params) {
3194
- if (!params || typeof params !== "object")
3195
- return null;
3196
- const record = params;
3197
- if (typeof record.threadId !== "string" ||
3198
- typeof record.turnId !== "string" ||
3199
- typeof record.itemId !== "string" ||
3200
- typeof record.delta !== "string") {
3201
- return null;
3202
- }
3203
- return params;
3204
- }
3205
- asPlanDeltaNotification(params) {
3206
- if (!params || typeof params !== "object")
3207
- return null;
3208
- const record = params;
3209
- if (typeof record.threadId !== "string" ||
3210
- typeof record.turnId !== "string" ||
3211
- typeof record.itemId !== "string" ||
3212
- typeof record.delta !== "string") {
3213
- return null;
3214
- }
3215
- return params;
3216
- }
3217
- asReasoningSummaryTextDeltaNotification(params) {
3218
- if (!params || typeof params !== "object")
3219
- return null;
3220
- const record = params;
3221
- if (typeof record.threadId !== "string" ||
3222
- typeof record.turnId !== "string" ||
3223
- typeof record.itemId !== "string" ||
3224
- typeof record.delta !== "string" ||
3225
- typeof record.summaryIndex !== "number") {
3226
- return null;
3227
- }
3228
- return params;
3229
- }
3230
- asCommandExecutionOutputDeltaNotification(params) {
3231
- if (!params || typeof params !== "object")
3232
- return null;
3233
- const record = params;
3234
- if (typeof record.threadId !== "string" ||
3235
- typeof record.turnId !== "string" ||
3236
- typeof record.itemId !== "string" ||
3237
- typeof record.delta !== "string") {
3238
- return null;
3239
- }
3240
- return params;
3241
- }
3242
- asFileChangeOutputDeltaNotification(params) {
3243
- if (!params || typeof params !== "object")
3244
- return null;
3245
- const record = params;
3246
- if (typeof record.threadId !== "string" ||
3247
- typeof record.turnId !== "string" ||
3248
- typeof record.itemId !== "string" ||
3249
- typeof record.delta !== "string") {
3250
- return null;
3251
- }
3252
- return params;
3253
- }
3254
- asRawResponseItemCompletedNotification(params) {
3255
- if (!params || typeof params !== "object")
3256
- return null;
3257
- const record = params;
3258
- if (typeof record.threadId !== "string" ||
3259
- typeof record.turnId !== "string" ||
3260
- !record.item ||
3261
- typeof record.item !== "object" ||
3262
- typeof record.item.type !== "string") {
3263
- return null;
3264
- }
3265
- return params;
3266
- }
3267
2703
  buildItemEventKey(turnId, itemId) {
3268
2704
  return `${turnId}:${itemId}`;
3269
2705
  }
3270
2706
  buildItemMessageUuid(turnId, itemId) {
3271
2707
  return `${itemId}-${turnId}`;
3272
2708
  }
3273
- // Tool items carry Codex's globally-unique call_id as their thread item id,
3274
- // and the durable rollout persists the same call_id on the matching response
3275
- // item. Key the rendered uuid on call_id alone (no turn scoping — call_id is
3276
- // already unique) so the streamed message and its durable backfill row share
3277
- // a uuid and dedup by id instead of the content+timestamp backstop. See
3278
- // topics/stream-durable-id-dedup.md (Codex tool calls).
2709
+ // Native tool thread items carry Codex's globally-unique call_id as item.id,
2710
+ // so this uuid aligns directly with the durable response item. A code-mode
2711
+ // commandExecution instead carries an inner exec-* id while rollout stores
2712
+ // the outer call_* id; the client reconciles that scoped exception via
2713
+ // _codexToolCorrelation. See topics/stream-durable-id-dedup.md.
3279
2714
  buildItemToolUuid(callId) {
3280
2715
  return callId;
3281
2716
  }
@@ -3373,6 +2808,11 @@ export class CodexProvider {
3373
2808
  session_id: sessionId,
3374
2809
  uuid: this.buildItemResultUuid(itemId),
3375
2810
  _isStreaming: true,
2811
+ ...(sourceEvent === "command_output_delta"
2812
+ ? {
2813
+ [CODEX_TOOL_CORRELATION_FIELD]: createCodexToolCorrelation("command_execution", turnId, itemId),
2814
+ }
2815
+ : {}),
3376
2816
  message: {
3377
2817
  role: "user",
3378
2818
  content: [
@@ -3433,6 +2873,9 @@ export class CodexProvider {
3433
2873
  id: callId,
3434
2874
  name: normalizedInvocation.toolName,
3435
2875
  input: normalizedInvocation.input,
2876
+ ...(normalizedInvocation.displayActions
2877
+ ? { _displayActions: normalizedInvocation.displayActions }
2878
+ : {}),
3436
2879
  },
3437
2880
  ],
3438
2881
  },
@@ -3493,7 +2936,7 @@ export class CodexProvider {
3493
2936
  const input = this.getOptionalString(item.input);
3494
2937
  if (!callId || !rawToolName)
3495
2938
  return [];
3496
- const normalizedInvocation = normalizeCodexToolInvocation(canonicalizeCodexToolName(rawToolName), parseCodexToolArguments(input ?? undefined));
2939
+ const normalizedInvocation = normalizeCodexCustomToolInvocation(rawToolName, input ?? "");
3497
2940
  liveEventState.toolCallContexts.set(callId, {
3498
2941
  toolName: normalizedInvocation.toolName,
3499
2942
  input: normalizedInvocation.input,
@@ -3505,6 +2948,7 @@ export class CodexProvider {
3505
2948
  type: "assistant",
3506
2949
  session_id: sessionId,
3507
2950
  uuid: this.buildItemToolUuid(callId),
2951
+ [CODEX_TOOL_CORRELATION_FIELD]: createCodexToolCorrelation("custom_tool_call", params.turnId, callId),
3508
2952
  message: {
3509
2953
  role: "assistant",
3510
2954
  content: [
@@ -3513,6 +2957,9 @@ export class CodexProvider {
3513
2957
  id: callId,
3514
2958
  name: normalizedInvocation.toolName,
3515
2959
  input: normalizedInvocation.input,
2960
+ ...(normalizedInvocation.displayActions
2961
+ ? { _displayActions: normalizedInvocation.displayActions }
2962
+ : {}),
3516
2963
  },
3517
2964
  ],
3518
2965
  },
@@ -3549,6 +2996,7 @@ export class CodexProvider {
3549
2996
  type: "user",
3550
2997
  session_id: sessionId,
3551
2998
  uuid: this.buildItemResultUuid(callId),
2999
+ [CODEX_TOOL_CORRELATION_FIELD]: createCodexToolCorrelation("custom_tool_call", params.turnId, callId),
3552
3000
  message: {
3553
3001
  role: "user",
3554
3002
  content: [toolResult],
@@ -3594,9 +3042,10 @@ export class CodexProvider {
3594
3042
  convertItemToSDKMessages(item, sessionId, turnId, sourceEvent) {
3595
3043
  const isComplete = sourceEvent === "item/completed";
3596
3044
  const observedAt = new Date().toISOString();
3597
- // Tool items key the uuid on call_id (item.id) so stream and durable rows
3598
- // dedup by id; message/reasoning items use a counter id (item-N) with no
3599
- // durable equivalent, so they stay turn-scoped and rely on the backstop.
3045
+ // Native tool items key the uuid on call_id (item.id). Code-mode
3046
+ // commandExecution items temporarily key on exec-* and carry correlation
3047
+ // metadata for adoption of the outer durable call_* id client-side.
3048
+ // Message/reasoning counters have no durable equivalent and stay scoped.
3600
3049
  const uuid = this.isToolBackedThreadItem(item)
3601
3050
  ? this.buildItemToolUuid(item.id)
3602
3051
  : `${item.id}-${turnId}`;
@@ -3646,10 +3095,13 @@ export class CodexProvider {
3646
3095
  }
3647
3096
  case "command_execution": {
3648
3097
  const messages = [];
3649
- const normalizedInvocation = normalizeCodexCommandActionInvocation(item.command, item.commandActions) ??
3650
- normalizeCodexToolInvocation("Bash", {
3651
- command: item.command,
3652
- });
3098
+ const correlationStartedAt = item.durationMs !== undefined
3099
+ ? new Date(Date.parse(observedAt) - Math.max(0, item.durationMs)).toISOString()
3100
+ : observedAt;
3101
+ const normalizedInvocation = normalizeCodexToolInvocation("Bash", {
3102
+ command: item.command,
3103
+ ...(item.cwd ? { cwd: item.cwd } : {}),
3104
+ });
3653
3105
  const toolContext = {
3654
3106
  toolName: normalizedInvocation.toolName,
3655
3107
  input: normalizedInvocation.input,
@@ -3661,6 +3113,7 @@ export class CodexProvider {
3661
3113
  type: "assistant",
3662
3114
  session_id: sessionId,
3663
3115
  uuid,
3116
+ [CODEX_TOOL_CORRELATION_FIELD]: createCodexToolCorrelation("command_execution", turnId, item.id, correlationStartedAt),
3664
3117
  message: {
3665
3118
  role: "assistant",
3666
3119
  content: [
@@ -3669,6 +3122,9 @@ export class CodexProvider {
3669
3122
  id: item.id,
3670
3123
  name: normalizedInvocation.toolName,
3671
3124
  input: normalizedInvocation.input,
3125
+ ...(normalizedInvocation.displayActions
3126
+ ? { _displayActions: normalizedInvocation.displayActions }
3127
+ : {}),
3672
3128
  },
3673
3129
  ],
3674
3130
  },
@@ -3702,6 +3158,7 @@ export class CodexProvider {
3702
3158
  type: "user",
3703
3159
  session_id: sessionId,
3704
3160
  uuid: `${uuid}-result`,
3161
+ [CODEX_TOOL_CORRELATION_FIELD]: createCodexToolCorrelation("command_execution", turnId, item.id, correlationStartedAt),
3705
3162
  message: {
3706
3163
  role: "user",
3707
3164
  content: [toolResultBlock],