@openclaw/codex 2026.9.1 → 2026.9.3

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 (109) hide show
  1. package/README.md +2 -0
  2. package/dist/api.js +18 -23
  3. package/dist/apply-B6el6hdO.js +357 -0
  4. package/dist/{attempt-client-cleanup-DYHcRQAa.js → attempt-client-cleanup-D6cR54TV.js} +113 -49
  5. package/dist/{attempt-context-DmQUNWwT.js → attempt-context-AO4DSjE0.js} +11 -9
  6. package/dist/{event-projector-usage-C5mcrGjp.js → attempt-notifications--H8zXR5-.js} +3 -89
  7. package/dist/auth-cache-key-Kq6oerr6.js +498 -0
  8. package/dist/auth-profile-CwzO4_RG.js +43 -0
  9. package/dist/auth-profile-selection-DFu9e167.js +45 -0
  10. package/dist/{binding-connection-E6uDsPlx.js → binding-connection-QqgcPCgk.js} +18 -3
  11. package/dist/{bounded-turn-wzJrwKKe.js → bounded-turn-lRpmZHFv.js} +54 -150
  12. package/dist/build-state-6RL6qohz.js +19 -0
  13. package/dist/{capabilities-dIjNVCYH.js → capabilities-BeqLPbtM.js} +2 -1
  14. package/dist/cli-metadata.js +1 -1
  15. package/dist/client-BEKu4yDx.js +3983 -0
  16. package/dist/command-dispatch-CisT9Jqq.js +21 -0
  17. package/dist/{command-formatters-CLHhEB1Q.js → command-formatters-DeTfrapr.js} +2 -2
  18. package/dist/{command-handlers-CTGb2csu.js → command-handlers-BfIsJEuA.js} +158 -76
  19. package/dist/command-rpc-BMqRQOFX.js +226 -0
  20. package/dist/{compact-_t0HQrUS.js → compact-CGJZ2rlb.js} +89 -49
  21. package/dist/config-JDfTI4VI.js +106 -0
  22. package/dist/{config-BhmfC1s6.js → config-options-jnIFvwxD.js} +208 -654
  23. package/dist/config-runtime-BWf6JLWG.js +11 -0
  24. package/dist/config-utils-C57JiJrU.js +69 -0
  25. package/dist/conversation-binding-CxOlHiix.js +889 -0
  26. package/dist/{plugin-marketplace-discovery-CfFlKqDl.js → conversation-control-CUCrSIQI.js} +36 -155
  27. package/dist/doctor-contract-api.js +8 -9
  28. package/dist/{dynamic-tools-BU9tmlYm.js → dynamic-tools-D-JBoaLt.js} +734 -698
  29. package/dist/{effective-mcp-catalog-DzYBW4h1.js → effective-mcp-catalog-J_5Ep5Um.js} +4 -3
  30. package/dist/{elicitation-input-DMkeki8d.js → elicitation-input-DPoK8F7w.js} +8 -22
  31. package/dist/ephemeral-turn-DSwzP8YY.js +101 -0
  32. package/dist/{event-projector-assistant-message-DHRFXKbS.js → event-projector-assistant-message-BmBM4qBV.js} +23 -27
  33. package/dist/event-projector-usage-CIyrYYid.js +102 -0
  34. package/dist/{harness-DXVZ5II5.js → harness-FR6eQgyO.js} +47 -17
  35. package/dist/harness.js +1 -1
  36. package/dist/history-rejection-B4nOXNXF.js +13 -0
  37. package/dist/{image-payload-sanitizer-CnfoUPCE.js → image-payload-sanitizer-tupAr-2o.js} +29 -15
  38. package/dist/incognito-session-uhrBF6wJ.js +15 -0
  39. package/dist/index.js +413 -2893
  40. package/dist/{isolated-completion-kg9kv4iH.js → isolated-completion-DHBefrzg.js} +12 -21
  41. package/dist/{managed-binary-4-Vjz5GH.js → managed-binary-DrjjRRUD.js} +17 -84
  42. package/dist/managed-thread-store-D1t5jh3G.js +66 -0
  43. package/dist/media-understanding-provider.js +4 -1
  44. package/dist/{model-catalog-CUM68Hkl.js → model-catalog-B_6M04Wo.js} +6 -5
  45. package/dist/{models-sYnEc0gd.js → models-P9k2qAof.js} +4 -4
  46. package/dist/native-execution-policy-umd0tgh4.js +128 -0
  47. package/dist/{native-subagent-monitor-CnH147jH.js → native-subagent-monitor-BbCUKRwC.js} +76 -48
  48. package/dist/node-cli-sessions-rj0ysJwM.js +527 -0
  49. package/dist/{node-exec-server.runtime-CstNHCmC.js → node-exec-server.runtime-BY_ctYnT.js} +54 -23
  50. package/dist/plan-BMJdMOEF.js +711 -0
  51. package/dist/plugin-activation-BQK-f5sV.js +133 -0
  52. package/dist/{thread-resume-C7pyVKeW.js → plugin-app-cache-key-CdSQzWyX.js} +5 -53
  53. package/dist/{plugin-approval-roundtrip-CNe-Xrjx.js → plugin-approval-roundtrip-DeW95wB5.js} +1 -1
  54. package/dist/plugin-inventory-CoRCzWW4.js +325 -0
  55. package/dist/plugin-marketplace-discovery-3akBizzF.js +134 -0
  56. package/dist/{plugin-metadata-cache-BFrPhb2c.js → plugin-metadata-cache-DgNfRAGA.js} +13 -9
  57. package/dist/protocol-CAAcVIKF.js +275 -0
  58. package/dist/{provider-capabilities-DW5p4k9w.js → provider-capabilities-W4tfK5Y5.js} +2 -2
  59. package/dist/{rate-limits-CawQJfUS.js → rate-limits-Bqnx1N4z.js} +1 -1
  60. package/dist/request-CGvGOpeR.js +198 -0
  61. package/dist/{run-attempt-D1hJO4fX.js → run-attempt-DwKds3DN.js} +1498 -1028
  62. package/dist/{runtime-artifact-C3jVimFn.js → runtime-artifact-T-NwqeSH.js} +28 -28
  63. package/dist/sandbox-guard-BcT66fWy.js +165 -0
  64. package/dist/{session-binding-BHuTCRZQ.js → session-binding-CKSepwIC.js} +118 -324
  65. package/dist/session-binding-record-CaZjYwNS.js +256 -0
  66. package/dist/session-catalog-HaDcp7Sv.js +1533 -0
  67. package/dist/session-catalog-adoption-BKLtSrwi.js +251 -0
  68. package/dist/session-catalog-archive-D0i2pGj2.js +56 -0
  69. package/dist/session-catalog-home-id-D7-aLiNt.js +190 -0
  70. package/dist/session-catalog-node-adoption-BUuDYY8R.js +182 -0
  71. package/dist/session-catalog-node-continue-CBtc4wkI.js +238 -0
  72. package/dist/session-catalog-parsing-CdD3JhdM.js +280 -0
  73. package/dist/session-catalog-provenance-DkJHo735.js +124 -0
  74. package/dist/{session-cli-CmFEXMAM.js → session-cli-CbgaHh0f.js} +3 -2
  75. package/dist/session-history-Cz-tOOYj.js +96 -0
  76. package/dist/{session-upstream-marker-C351-5Qw.js → session-history-import-DFN7sWRU.js} +5 -49
  77. package/dist/session-history-read-cxPUxyVV.js +50 -0
  78. package/dist/session-history-worker-runtime.js +60 -0
  79. package/dist/{settled-turn-context-D3SNWtUn.js → session-history.worker-CNEvGaU5.js} +75 -69
  80. package/dist/session-history.worker.js +2 -0
  81. package/dist/{session-initialization-BeWzjGiR.js → session-initialization-Jo86C60I.js} +2 -1
  82. package/dist/{session-permission-policy-DeWVTCMj.js → session-permission-policy-B22InTp_.js} +4 -7
  83. package/dist/{session-retirement-BujDjXVF.js → session-retirement-BSNG_mhf.js} +8 -7
  84. package/dist/session-upstream-marker-D15C9NHp.js +46 -0
  85. package/dist/settled-turn-context-Du6gdUDd.js +45 -0
  86. package/dist/{settled-turn-finalizer-BVJx4yoc.js → settled-turn-finalizer-BuLUrZl2.js} +54 -42
  87. package/dist/shared-client-CVhez2Uo.js +2911 -0
  88. package/dist/{side-question-BTZAII4l.js → side-question-DLbr2vUU.js} +135 -191
  89. package/dist/source-Da-h6f7e.js +518 -0
  90. package/dist/thread-archive-guard-D1bP04Tf.js +53 -0
  91. package/dist/{thread-lifecycle-DAcEJ_Om.js → thread-lifecycle-D1pzRwHB.js} +467 -977
  92. package/dist/{thread-ownership-zGokd8i1.js → thread-ownership-D6oASVRu.js} +15 -17
  93. package/dist/{thread-policy-g4KbJPzp.js → thread-policy-D8kI7yAO.js} +2 -2
  94. package/dist/thread-resume-T5cveyxA.js +52 -0
  95. package/dist/timeout-BVw8aaM1.js +33 -0
  96. package/dist/transcript-mirror-attestation-DibE0krH.js +89 -0
  97. package/dist/{transcript-mirror-DTUxFWa_.js → transcript-mirror-t5TOccct.js} +111 -379
  98. package/dist/{transport-stdio-CyPldczm.js → transport-stdio-BGjHoYLr.js} +80 -48
  99. package/dist/{upstream-session-fork-CYlBIGVt.js → upstream-session-fork-De-sLJ2r.js} +94 -166
  100. package/dist/{usage-Bj-rrzkR.js → usage-CPPTmBvZ.js} +4 -3
  101. package/dist/version-omm6IPI3.js +12 -0
  102. package/dist/{web-search-provider.runtime-D2FWSK4w.js → web-search-provider.runtime-CSVgQL0w.js} +3 -3
  103. package/openclaw.plugin.json +22 -1
  104. package/package.json +6 -6
  105. package/dist/request-DYAI5FQE.js +0 -475
  106. package/dist/session-catalog-yyvTWFOw.js +0 -3120
  107. package/dist/session-history-C5J1AKzG.js +0 -87
  108. package/dist/shared-client-DxcfF9KX.js +0 -7494
  109. package/dist/{auth-binding-Ds_QVm-X.js → auth-binding-Co2Vfcf_.js} +1 -1
@@ -0,0 +1,889 @@
1
+ import { l as sessionBindingIdentity, n as assertCodexBindingMayBeReplaced } from "./session-binding-record-CaZjYwNS.js";
2
+ import { b as readCodexRequirementsToml, n as codexSandboxPolicyForTurn } from "./config-options-jnIFvwxD.js";
3
+ import { o as isJsonObject, x as readCodexPluginConfig } from "./protocol-CAAcVIKF.js";
4
+ import { _ as assertCodexThreadAcceptsDirectInput, b as assertCodexThreadStartResponse, l as isCodexAppServerOverloadError, m as CodexThreadDirectInputError, s as isCodexAppServerIndeterminateRequestCancellationError } from "./client-BEKu4yDx.js";
5
+ import { o as resolveCodexDefaultWorkspaceDir } from "./conversation-binding-data-BiodpIzq.js";
6
+ import { i as projectBoundedCodexVisibleSessionHistory } from "./session-catalog-home-id-D7-aLiNt.js";
7
+ import { n as resolveOpenClawExecPolicyForCodexAppServer, t as canUseCodexModelBackedApprovalsReviewerForModel } from "./config-JDfTI4VI.js";
8
+ import { n as resolveCodexAppServerRuntimeOptions } from "./config-runtime-BWf6JLWG.js";
9
+ import { n as normalizeCodexAppServerBindingModelProvider, t as isCodexAppServerNativeAuthProfile } from "./auth-profile-CwzO4_RG.js";
10
+ import { L as hasCodexAppServerLiveThread, P as consumeCodexAppServerLiveThread, R as isCodexAppServerClientRuntimeLive, U as releaseCodexAppServerLiveThread, _ as retainSharedCodexAppServerClientByInstanceId, c as getLeasedSharedCodexAppServerClient, m as releaseCodexAppServerClientLease, x as withLeasedCodexAppServerClientStartSelectionRetry, z as isCodexAppServerLiveThreadClaimed } from "./shared-client-CVhez2Uo.js";
11
+ import { a as closeCodexStartupClientBestEffort, d as unsubscribeCodexThreadBestEffort, h as isCodexNotificationForTurn, l as retireUnsafeCodexTurnClientBestEffort, o as interruptCodexTurnAndWaitBestEffort, p as getCodexAppServerTurnRouter, s as isCodexAppServerUnsafeSubscriptionError } from "./attempt-client-cleanup-D6cR54TV.js";
12
+ import { n as isAssistantCommentaryCompletionNotification } from "./attempt-notifications--H8zXR5-.js";
13
+ import { E as resolveCodexAppServerRequestModelSelection, Tt as mergeCodexThreadConfigs, k as buildCodexProjectDocThreadConfig, w as CODEX_NATIVE_PERSONALITY_NONE, wt as buildDisabledAppsConfigPatch } from "./thread-lifecycle-D1pzRwHB.js";
14
+ import "./session-binding-CKSepwIC.js";
15
+ import { t as resumeCodexAppServerThread } from "./thread-resume-T5cveyxA.js";
16
+ import { a as rollbackCodexAppServerBindingSubscription, n as releaseCodexAppServerBindingSubscription, r as retainCodexAppServerBindingSubscription, s as withExclusiveCodexAppServerThread, t as isSameCodexAppServerThreadOwner } from "./thread-ownership-D6oASVRu.js";
17
+ import { a as resolveCodexAppServerForModelProvider, i as resolveCodexSessionPermissionCwd, n as applyCodexSessionPermissionPolicy, t as CODEX_SESSION_PERMISSION_EXEC_MODES } from "./session-permission-policy-B22InTp_.js";
18
+ import { u as trackCodexConversationActiveTurn } from "./conversation-control-CUCrSIQI.js";
19
+ import { asOptionalRecord, normalizeOptionalString, normalizeSingleOrTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
20
+ import { resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
21
+ import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
22
+ import path from "node:path";
23
+ import { fileURLToPath } from "node:url";
24
+ import { resolveAgentWorkspaceDir } from "openclaw/plugin-sdk/agent-runtime";
25
+ import { embeddedAgentLog, formatErrorMessage, resolveActiveEmbeddedRunSessionId, resolveSandboxContext } from "openclaw/plugin-sdk/agent-harness-runtime";
26
+ import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
27
+ import { getSessionEntry, resolveStorePath, resolveTranscriptSessionKeyBySessionId } from "openclaw/plugin-sdk/session-store-runtime";
28
+ import { readVisibleSessionTranscriptMessageEntries } from "openclaw/plugin-sdk/session-transcript-runtime";
29
+ //#region extensions/codex/src/conversation-binding-preparation.ts
30
+ /** Canonical conversation thread preparation, policy, and binding ownership. */
31
+ const NATIVE_CONVERSATION_INTERACTIVE_APPROVALS_UNAVAILABLE = "OpenClaw native Codex conversation binding cannot route interactive approvals yet; use the Codex harness or explicit /acp spawn codex for that workflow.";
32
+ async function resolveConversationAppServerRuntime(params) {
33
+ const source = params.source;
34
+ const agentId = source?.agentId ?? params.agentId ?? (params.config ? resolveSessionAgentIdsStrict({
35
+ sessionKey: params.sessionKey,
36
+ config: params.config
37
+ }).sessionAgentId : void 0);
38
+ const storePath = agentId && (source || params.sessionKey) ? resolveStorePath(params.config?.session?.store, { agentId }) : void 0;
39
+ const sessionKey = source ? source.sessionKey ?? (storePath ? resolveTranscriptSessionKeyBySessionId({
40
+ agentId: source.agentId,
41
+ sessionId: source.sessionId,
42
+ storePath
43
+ }) : void 0) : params.sessionKey;
44
+ const storedEntry = sessionKey && storePath ? getSessionEntry({
45
+ agentId,
46
+ storePath,
47
+ sessionKey,
48
+ readConsistency: "latest"
49
+ }) : void 0;
50
+ const entry = !source || storedEntry?.sessionId === source.sessionId ? storedEntry : void 0;
51
+ if (source && !entry) throw new Error("Codex conversation source session is missing or no longer current; rebind this conversation before retrying.");
52
+ const permissionMode = entry?.permissionMode;
53
+ const sessionRoot = permissionMode ? entry?.sessionRoot : void 0;
54
+ const agentWorkspaceDir = params.config && agentId ? resolveAgentWorkspaceDir(params.config, agentId) : resolveCodexDefaultWorkspaceDir(params.pluginConfig);
55
+ const execPolicy = resolveOpenClawExecPolicyForCodexAppServer({
56
+ config: params.config,
57
+ agentId,
58
+ permissionMode,
59
+ execOverrides: permissionMode ? { mode: CODEX_SESSION_PERMISSION_EXEC_MODES[permissionMode] } : void 0,
60
+ approvals: permissionMode === "full" ? void 0 : loadExecApprovals()
61
+ });
62
+ const sandboxForPolicy = execPolicy.touched && execPolicy.security === "full" && execPolicy.ask !== "off" ? await resolveSandboxContext({
63
+ config: params.config,
64
+ sessionKey,
65
+ workspaceDir: agentWorkspaceDir
66
+ }) : void 0;
67
+ const configuredRuntime = resolveCodexAppServerRuntimeOptions({
68
+ pluginConfig: params.pluginConfig,
69
+ execPolicy,
70
+ modelProvider: params.modelProvider,
71
+ model: params.model,
72
+ config: params.config,
73
+ agentDir: params.agentDir,
74
+ openClawSandboxActive: Boolean(sandboxForPolicy?.enabled)
75
+ });
76
+ const canUseAutoReview = canUseCodexModelBackedApprovalsReviewerForModel({
77
+ modelProvider: params.modelProvider,
78
+ model: params.model,
79
+ config: params.config,
80
+ env: process.env,
81
+ agentDir: params.agentDir
82
+ });
83
+ return {
84
+ runtime: applyCodexSessionPermissionPolicy({
85
+ appServer: configuredRuntime,
86
+ permissionMode,
87
+ sessionRoot,
88
+ defaultRoot: agentWorkspaceDir,
89
+ pluginConfig: readCodexPluginConfig(params.pluginConfig),
90
+ canUseAutoReview,
91
+ requirementsToml: readCodexRequirementsToml({}),
92
+ execMode: execPolicy.mode
93
+ }),
94
+ workspaceDir: resolveCodexSessionPermissionCwd({
95
+ permissionMode,
96
+ sessionRoot,
97
+ defaultRoot: agentWorkspaceDir,
98
+ requestedCwd: params.workspaceDir,
99
+ fallbackCwd: params.workspaceDir
100
+ })
101
+ };
102
+ }
103
+ const CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS = "This Codex thread is bound to an OpenClaw conversation. Answer normally; OpenClaw will deliver your final response back to the conversation.";
104
+ async function resolveThreadBindingRuntime(params) {
105
+ const agentLookup = buildCodexConversationAgentLookup({
106
+ agentDir: params.agentDir,
107
+ config: params.config
108
+ });
109
+ const modelProvider = resolveThreadRequestModelProvider({
110
+ authProfileId: params.authProfileId,
111
+ modelProvider: params.modelProvider,
112
+ ...agentLookup
113
+ });
114
+ const modelSelection = resolveOptionalThreadRequestModelSelection({
115
+ model: params.model,
116
+ modelProvider,
117
+ authProfileId: params.authProfileId,
118
+ ...agentLookup
119
+ });
120
+ const reviewerModelProvider = resolveModelBackedReviewerPolicyProvider({
121
+ authProfileId: params.authProfileId,
122
+ modelProvider: params.modelProvider,
123
+ ...agentLookup
124
+ });
125
+ const { runtime, workspaceDir } = await resolveConversationAppServerRuntime({
126
+ pluginConfig: params.pluginConfig,
127
+ config: params.config,
128
+ agentId: params.agentId,
129
+ sessionKey: params.sessionKey,
130
+ source: params.source,
131
+ workspaceDir: params.workspaceDir,
132
+ modelProvider: reviewerModelProvider,
133
+ model: params.model,
134
+ agentDir: params.agentDir
135
+ });
136
+ const modelScopedRuntime = resolveCodexAppServerForModelProvider({
137
+ appServer: runtime,
138
+ provider: reviewerModelProvider,
139
+ model: params.model,
140
+ config: params.config,
141
+ env: process.env,
142
+ agentDir: params.agentDir
143
+ });
144
+ assertNativeConversationApprovalPolicySupported(modelScopedRuntime);
145
+ const clientOptions = {
146
+ startOptions: runtime.start,
147
+ timeoutMs: runtime.requestTimeoutMs,
148
+ authProfileId: params.authProfileId,
149
+ ...agentLookup
150
+ };
151
+ return {
152
+ runtime: modelScopedRuntime,
153
+ workspaceDir,
154
+ agentLookup,
155
+ model: modelSelection?.model,
156
+ modelProvider: modelSelection?.modelProvider ?? modelProvider,
157
+ clientOptions
158
+ };
159
+ }
160
+ function buildConversationThreadRequest(resolved, serviceTier) {
161
+ return {
162
+ cwd: resolved.workspaceDir,
163
+ ...resolved.model ? { model: resolved.model } : {},
164
+ ...resolved.modelProvider ? { modelProvider: resolved.modelProvider } : {},
165
+ personality: CODEX_NATIVE_PERSONALITY_NONE,
166
+ approvalPolicy: resolved.runtime.approvalPolicy,
167
+ approvalsReviewer: resolved.runtime.approvalsReviewer,
168
+ ...resolved.runtime.sessionRoot ? { runtimeWorkspaceRoots: [resolved.runtime.sessionRoot] } : {},
169
+ ...codexConversationSandboxOrPermissions(resolved.runtime, resolved.runtime.sandbox),
170
+ ...serviceTier ? { serviceTier } : {}
171
+ };
172
+ }
173
+ function codexConversationSandboxOrPermissions(runtime, sandbox) {
174
+ const networkProxy = runtime.networkProxy;
175
+ const config = buildCodexProjectDocThreadConfig(mergeCodexThreadConfigs(networkProxy?.configPatch, buildDisabledAppsConfigPatch()));
176
+ return networkProxy ? { config } : {
177
+ sandbox,
178
+ config
179
+ };
180
+ }
181
+ async function writeThreadBindingFromResponse(params, resolved, client, response, requestOptions) {
182
+ let retained = false;
183
+ let sameOwner = false;
184
+ try {
185
+ const current = params.bindingStore.read(params.identity);
186
+ assertCodexBindingMayBeReplaced(current, "storing a conversation-bound Codex thread");
187
+ const trackSubscription = !params.incognito && isCodexAppServerClientRuntimeLive(client);
188
+ sameOwner = isSameCodexAppServerThreadOwner(current, {
189
+ threadId: response.thread.id,
190
+ clientId: client.getInstanceId()
191
+ });
192
+ requestOptions();
193
+ assertCodexThreadAcceptsDirectInput(response.thread);
194
+ if (trackSubscription) {
195
+ retained = await retainCodexAppServerBindingSubscription(client, response.thread.id);
196
+ if (!retained) throw new Error("Codex conversation thread lost its native subscription owner.");
197
+ }
198
+ if (current && !sameOwner) {
199
+ const { assertCurrent } = requestOptions();
200
+ await releaseCodexAppServerBindingSubscription(current, { assertCurrent });
201
+ }
202
+ requestOptions();
203
+ if (!await params.bindingStore.mutate(params.identity, {
204
+ kind: "set",
205
+ binding: {
206
+ threadId: response.thread.id,
207
+ clientId: client.getInstanceId(),
208
+ cwd: resolved.workspaceDir,
209
+ authProfileId: params.authProfileId,
210
+ model: response.model ?? resolved.model ?? params.model,
211
+ modelProvider: normalizeCodexAppServerBindingModelProvider({
212
+ authProfileId: params.authProfileId,
213
+ modelProvider: response.modelProvider ?? resolved.modelProvider ?? params.modelProvider,
214
+ ...resolved.agentLookup
215
+ }),
216
+ serviceTier: params.serviceTier ?? resolved.runtime.serviceTier ?? void 0,
217
+ networkProxyProfileName: resolved.runtime.networkProxy?.profileName,
218
+ networkProxyConfigFingerprint: resolved.runtime.networkProxy?.configFingerprint
219
+ }
220
+ }, requestOptions)) throw new Error("Codex conversation binding changed while storing its thread.");
221
+ } catch (error) {
222
+ if (retained && !sameOwner || !hasCodexAppServerLiveThread(client, response.thread.id)) await rollbackCodexAppServerBindingSubscription(client, response.thread.id, retained);
223
+ throw error;
224
+ }
225
+ }
226
+ async function bindThread(params, threadId) {
227
+ const current = params.bindingStore.read(params.identity);
228
+ assertCodexBindingMayBeReplaced(current, "binding a conversation-bound Codex thread");
229
+ const resolved = await resolveThreadBindingRuntime(params);
230
+ const clientLease = { client: await getLeasedSharedCodexAppServerClient(resolved.clientOptions) };
231
+ try {
232
+ await withLeasedCodexAppServerClientStartSelectionRetry({
233
+ lease: clientLease,
234
+ options: resolved.clientOptions,
235
+ run: async (client, requestOptions) => {
236
+ const request = buildConversationThreadRequest(resolved, params.serviceTier ?? resolved.runtime.serviceTier);
237
+ let response;
238
+ if (threadId && !resolved.runtime.networkProxy) {
239
+ if (isCodexAppServerLiveThreadClaimed(client, threadId)) throw new Error(`Codex thread ${threadId} has an active run; stop it before binding its conversation.`);
240
+ const { thread } = await client.request("thread/read", {
241
+ threadId,
242
+ includeTurns: false
243
+ }, requestOptions());
244
+ assertCodexThreadAcceptsDirectInput(thread);
245
+ const { assertCurrent } = requestOptions();
246
+ await releaseCodexAppServerLiveThread(client, threadId, assertCurrent);
247
+ if (isCodexAppServerLiveThreadClaimed(client, threadId)) throw new Error(`Codex thread ${threadId} has an active run; stop it before binding its conversation.`);
248
+ response = await resumeCodexAppServerThread({
249
+ client,
250
+ abandonClient: () => closeCodexStartupClientBestEffort(client),
251
+ request: {
252
+ ...request,
253
+ threadId
254
+ },
255
+ requestResume: (resumeRequest) => client.request("thread/resume", resumeRequest, requestOptions())
256
+ });
257
+ } else response = await client.request("thread/start", {
258
+ ...request,
259
+ developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
260
+ experimentalRawEvents: true,
261
+ ...params.incognito ? { ephemeral: true } : {}
262
+ }, requestOptions());
263
+ await writeThreadBindingFromResponse(params, resolved, client, response, requestOptions);
264
+ }
265
+ });
266
+ } finally {
267
+ releaseCodexAppServerClientLease(clientLease);
268
+ }
269
+ }
270
+ function assertNativeConversationApprovalPolicySupported(runtime) {
271
+ if (runtime.approvalPolicy !== "never" && runtime.approvalsReviewer === "user") throw new Error(NATIVE_CONVERSATION_INTERACTIVE_APPROVALS_UNAVAILABLE);
272
+ }
273
+ async function prepareCodexConversationBinding(params, options = {}) {
274
+ const identity = {
275
+ kind: "conversation",
276
+ bindingId: params.data.bindingId
277
+ };
278
+ const snapshot = params.bindingStore.read(identity);
279
+ const run = () => params.bindingStore.withLease(identity, async () => {
280
+ const current = params.bindingStore.read(identity);
281
+ if (current?.threadId !== snapshot?.threadId || current?.clientId !== snapshot?.clientId) throw new Error("Codex conversation binding changed before preparation.");
282
+ const requested = params.data.start && current?.conversationStartId !== params.data.start.id ? params.data.start : void 0;
283
+ if (current && !requested && !options.forceNew) return;
284
+ const sourceIdentity = params.data.source ? sessionBindingIdentity({
285
+ agentId: params.data.source.agentId,
286
+ sessionId: params.data.source.sessionId,
287
+ sessionKey: params.data.source.sessionKey,
288
+ config: params.config
289
+ }) : void 0;
290
+ const sourceBinding = sourceIdentity ? params.bindingStore.read(sourceIdentity) : void 0;
291
+ assertCodexBindingMayBeReplaced(current, "initializing a conversation-bound Codex thread");
292
+ assertCodexBindingMayBeReplaced(sourceBinding, "transferring a session into a conversation-bound Codex thread");
293
+ const inherited = current ?? sourceBinding;
294
+ const agentLookup = buildCodexConversationAgentLookup({
295
+ agentDir: params.data.agentDir,
296
+ config: params.config
297
+ });
298
+ const bindingParams = {
299
+ bindingStore: params.bindingStore,
300
+ identity,
301
+ pluginConfig: params.pluginConfig,
302
+ workspaceDir: requested ? params.data.workspaceDir : inherited?.cwd ?? params.data.workspaceDir,
303
+ ...agentLookup,
304
+ model: requested?.model ?? inherited?.model,
305
+ modelProvider: requested?.modelProvider ?? inherited?.modelProvider,
306
+ authProfileId: requested?.authProfileId ?? inherited?.authProfileId,
307
+ serviceTier: inherited?.serviceTier,
308
+ config: params.config,
309
+ sessionKey: params.data.legacyBinding ? params.sessionKey : params.data.source?.sessionKey,
310
+ source: params.data.source,
311
+ incognito: params.incognito,
312
+ agentId: params.data.source?.agentId ?? params.data.agentId
313
+ };
314
+ const threadId = requested?.threadId;
315
+ await bindThread(bindingParams, options.forceNew ? void 0 : threadId);
316
+ const stored = params.bindingStore.read(identity);
317
+ if (!stored) throw new Error("Codex conversation binding disappeared while initializing its thread.");
318
+ if (sourceIdentity && params.data.source && !current?.conversationSourceTransferComplete) await params.bindingStore.withLease(sourceIdentity, async () => {
319
+ const source = params.bindingStore.read(sourceIdentity);
320
+ if (source && source.threadId === params.data.source?.threadId) {
321
+ const sourceSessionKey = sourceIdentity.sessionKey ?? resolveTranscriptSessionKeyBySessionId({
322
+ agentId: sourceIdentity.agentId,
323
+ sessionId: sourceIdentity.sessionId,
324
+ storePath: resolveStorePath(params.config?.session?.store, { agentId: sourceIdentity.agentId })
325
+ });
326
+ if (sourceSessionKey && resolveActiveEmbeddedRunSessionId(sourceSessionKey) === sourceIdentity.sessionId) throw new Error("Codex source session has an active run; stop it before binding this conversation.");
327
+ if (source.threadId !== stored.threadId) {
328
+ await releaseCodexAppServerBindingSubscription(source);
329
+ await projectConversationSourceHistory(params.data.source, stored, params.config);
330
+ }
331
+ await params.bindingStore.mutate(sourceIdentity, {
332
+ kind: "clear",
333
+ threadId: source.threadId
334
+ });
335
+ }
336
+ });
337
+ if (!await params.bindingStore.mutate(identity, {
338
+ kind: "patch",
339
+ threadId: stored.threadId,
340
+ patch: {
341
+ ...params.data.start ? { conversationStartId: params.data.start.id } : {},
342
+ ...sourceIdentity ? { conversationSourceTransferComplete: true } : {}
343
+ }
344
+ })) throw new Error("Codex conversation binding changed while initializing its thread.");
345
+ });
346
+ const threadId = params.data.start?.threadId ?? snapshot?.threadId;
347
+ if (threadId) await withExclusiveCodexAppServerThread({
348
+ bindingStore: params.bindingStore,
349
+ identity,
350
+ threadId,
351
+ run
352
+ });
353
+ else await run();
354
+ }
355
+ async function projectConversationSourceHistory(source, target, config) {
356
+ const storePath = resolveStorePath(config?.session?.store, { agentId: source.agentId });
357
+ const sessionKey = source.sessionKey ?? resolveTranscriptSessionKeyBySessionId({
358
+ agentId: source.agentId,
359
+ sessionId: source.sessionId,
360
+ storePath
361
+ });
362
+ if (!sessionKey) return;
363
+ const entries = await readVisibleSessionTranscriptMessageEntries({
364
+ agentId: source.agentId,
365
+ sessionId: source.sessionId,
366
+ sessionKey,
367
+ storePath
368
+ });
369
+ const history = projectBoundedCodexVisibleSessionHistory(entries);
370
+ if (history.length === 0) return;
371
+ const clientLease = retainSharedCodexAppServerClientByInstanceId(target.clientId);
372
+ if (!clientLease) throw new Error("Codex conversation source history lost its bound client owner.");
373
+ try {
374
+ await clientLease.client.request("thread/inject_items", {
375
+ threadId: target.threadId,
376
+ items: history
377
+ });
378
+ } finally {
379
+ clientLease.release();
380
+ }
381
+ }
382
+ function resolveThreadRequestModelProvider(params) {
383
+ const modelProvider = params.modelProvider?.trim();
384
+ if (!modelProvider || modelProvider.toLowerCase() === "codex") return;
385
+ if (isCodexAppServerNativeAuthProfile(params) && modelProvider.toLowerCase() === "openai") return;
386
+ return modelProvider.toLowerCase() === "openai" ? "openai" : modelProvider;
387
+ }
388
+ function resolveOptionalThreadRequestModelSelection(params) {
389
+ if (!params.model?.trim()) return;
390
+ return resolveCodexAppServerRequestModelSelection({
391
+ model: params.model,
392
+ modelProvider: params.modelProvider,
393
+ authProfileId: params.authProfileId,
394
+ agentDir: params.agentDir,
395
+ config: params.config
396
+ });
397
+ }
398
+ function resolveModelBackedReviewerPolicyProvider(params) {
399
+ const modelProvider = params.modelProvider?.trim();
400
+ if (modelProvider && modelProvider.toLowerCase() !== "codex") return modelProvider.toLowerCase() === "openai" ? "openai" : modelProvider;
401
+ return isCodexAppServerNativeAuthProfile(params) ? "openai" : void 0;
402
+ }
403
+ function buildCodexConversationAgentLookup(params) {
404
+ const agentDir = params.agentDir?.trim();
405
+ return {
406
+ ...agentDir ? { agentDir } : {},
407
+ ...params.config ? { config: params.config } : {}
408
+ };
409
+ }
410
+ //#endregion
411
+ //#region extensions/codex/src/conversation-turn-collector.ts
412
+ /** Identifies a timer that expired in the bound-turn collector itself. */
413
+ var CodexConversationTurnTimeoutError = class extends Error {
414
+ constructor() {
415
+ super("codex app-server bound turn timed out");
416
+ this.name = "CodexConversationTurnTimeoutError";
417
+ }
418
+ };
419
+ function createCodexConversationTurnCollector(threadId) {
420
+ let turnId;
421
+ let completed = false;
422
+ let failedError;
423
+ let timeout;
424
+ const assistantTextByItem = /* @__PURE__ */ new Map();
425
+ let resolveCompletion;
426
+ let rejectCompletion;
427
+ const collectReplyText = () => {
428
+ return [...assistantTextByItem.values()].map((text) => text.trim()).filter(Boolean).at(-1) ?? "";
429
+ };
430
+ const clearWaitState = () => {
431
+ if (timeout) {
432
+ clearTimeout(timeout);
433
+ timeout = void 0;
434
+ }
435
+ resolveCompletion = void 0;
436
+ rejectCompletion = void 0;
437
+ };
438
+ const finish = () => {
439
+ if (completed) return;
440
+ completed = true;
441
+ if (failedError) rejectCompletion?.(new Error(failedError));
442
+ else resolveCompletion?.({ replyText: collectReplyText() });
443
+ clearWaitState();
444
+ };
445
+ const handleNotification = (notification) => {
446
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
447
+ if (!params || !turnId || !isCodexNotificationForTurn(params, threadId, turnId)) return;
448
+ if (notification.method === "item/agentMessage/delta") {
449
+ const itemId = normalizeOptionalString(params.itemId) ?? "assistant";
450
+ const delta = readTextString(params, "delta");
451
+ if (!delta) return;
452
+ assistantTextByItem.set(itemId, `${assistantTextByItem.get(itemId) ?? ""}${delta}`);
453
+ return;
454
+ }
455
+ if (notification.method === "item/completed") {
456
+ const item = isJsonObject(params.item) ? params.item : void 0;
457
+ if (item?.type === "agentMessage") {
458
+ const itemId = normalizeOptionalString(item.id) ?? normalizeOptionalString(params.itemId) ?? "assistant";
459
+ assistantTextByItem.delete(itemId);
460
+ if (isAssistantCommentaryCompletionNotification(notification)) return;
461
+ const text = readTextString(item, "text");
462
+ if (text?.trim()) assistantTextByItem.set(itemId, text);
463
+ }
464
+ return;
465
+ }
466
+ if (notification.method === "turn/completed") {
467
+ const turn = isJsonObject(params.turn) ? params.turn : void 0;
468
+ const status = normalizeOptionalString(turn?.status);
469
+ if (status === "failed") failedError = normalizeOptionalString(asOptionalRecord(turn?.error)?.message) ?? "codex app-server turn failed";
470
+ else if (status === "interrupted") failedError = "codex app-server turn interrupted";
471
+ else if (status !== "completed") failedError = "codex app-server turn completed without a valid terminal status";
472
+ if (status === "completed") {
473
+ const items = Array.isArray(turn?.items) ? turn.items : [];
474
+ for (const item of items) {
475
+ if (!isJsonObject(item) || item.type !== "agentMessage") continue;
476
+ const itemId = normalizeOptionalString(item.id) ?? `assistant-${assistantTextByItem.size + 1}`;
477
+ assistantTextByItem.delete(itemId);
478
+ const text = item.phase === "commentary" ? void 0 : readTextString(item, "text");
479
+ if (text?.trim()) assistantTextByItem.set(itemId, text);
480
+ }
481
+ }
482
+ finish();
483
+ }
484
+ };
485
+ return {
486
+ setTurnId(nextTurnId) {
487
+ turnId = nextTurnId;
488
+ },
489
+ handleNotification,
490
+ wait(params) {
491
+ if (completed) return failedError ? Promise.reject(new Error(failedError)) : Promise.resolve({ replyText: collectReplyText() });
492
+ return new Promise((resolve, reject) => {
493
+ resolveCompletion = resolve;
494
+ rejectCompletion = reject;
495
+ timeout = setTimeout(() => {
496
+ completed = true;
497
+ reject(new CodexConversationTurnTimeoutError());
498
+ clearWaitState();
499
+ }, resolveTimerTimeoutMs(params.timeoutMs, 100, 100));
500
+ timeout.unref?.();
501
+ });
502
+ }
503
+ };
504
+ }
505
+ function readTextString(record, key) {
506
+ const value = record?.[key];
507
+ return typeof value === "string" && value.length > 0 ? value : void 0;
508
+ }
509
+ //#endregion
510
+ //#region extensions/codex/src/conversation-turn-input.ts
511
+ const IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
512
+ ".avif",
513
+ ".gif",
514
+ ".jpeg",
515
+ ".jpg",
516
+ ".png",
517
+ ".webp"
518
+ ]);
519
+ function buildCodexConversationTurnInput(params) {
520
+ return [{
521
+ type: "text",
522
+ text: params.prompt,
523
+ text_elements: []
524
+ }, ...extractInboundMedia(params.event).map(toCodexImageInput).filter((item) => item !== void 0)];
525
+ }
526
+ function extractInboundMedia(event) {
527
+ const metadata = event.metadata ?? {};
528
+ const paths = normalizeSingleOrTrimmedStringList(metadata.mediaPaths).concat(normalizeSingleOrTrimmedStringList(metadata.mediaPath));
529
+ const urls = normalizeSingleOrTrimmedStringList(metadata.mediaUrls).concat(normalizeSingleOrTrimmedStringList(metadata.mediaUrl));
530
+ const mimeTypes = normalizeSingleOrTrimmedStringList(metadata.mediaTypes).concat(normalizeSingleOrTrimmedStringList(metadata.mediaType));
531
+ const count = Math.max(paths.length, urls.length, mimeTypes.length);
532
+ const media = [];
533
+ for (let index = 0; index < count; index += 1) media.push({
534
+ path: paths[index],
535
+ url: urls[index],
536
+ mimeType: mimeTypes[index] ?? mimeTypes[0]
537
+ });
538
+ return media;
539
+ }
540
+ function toCodexImageInput(media) {
541
+ if (!isImageMedia(media)) return;
542
+ const localPath = media.path ?? readLocalMediaPath(media.url);
543
+ if (localPath) {
544
+ const normalized = normalizeFileUrl(localPath);
545
+ return normalized ? {
546
+ type: "localImage",
547
+ path: normalized
548
+ } : void 0;
549
+ }
550
+ return media.url ? {
551
+ type: "image",
552
+ url: media.url
553
+ } : void 0;
554
+ }
555
+ function isImageMedia(media) {
556
+ if (media.mimeType?.toLowerCase().startsWith("image/")) return true;
557
+ const candidate = media.path ?? media.url;
558
+ if (!candidate) return false;
559
+ return IMAGE_EXTENSIONS.has(path.extname(candidate.split(/[?#]/, 1)[0] ?? "").toLowerCase());
560
+ }
561
+ function normalizeFileUrl(value) {
562
+ if (!/^file:\/\//iu.test(value)) return value;
563
+ try {
564
+ const fileUrl = new URL(value);
565
+ decodeURIComponent(fileUrl.pathname);
566
+ return fileURLToPath(fileUrl);
567
+ } catch {
568
+ return;
569
+ }
570
+ }
571
+ function readLocalMediaPath(value) {
572
+ if (!value) return;
573
+ if (/^file:\/\//iu.test(value)) return value;
574
+ if (value.startsWith("//")) return;
575
+ if (path.isAbsolute(value) || path.win32.isAbsolute(value)) return value;
576
+ return /^[a-z][a-z0-9+.-]*:/i.test(value) ? void 0 : value;
577
+ }
578
+ //#endregion
579
+ //#region extensions/codex/src/conversation-binding.ts
580
+ const DEFAULT_BOUND_TURN_TIMEOUT_MS = 12e5;
581
+ async function runBoundTurn(params) {
582
+ const agentLookup = buildCodexConversationAgentLookup({
583
+ agentDir: params.data.agentDir,
584
+ config: params.config
585
+ });
586
+ const identity = {
587
+ kind: "conversation",
588
+ bindingId: params.data.bindingId
589
+ };
590
+ const binding = params.bindingStore.read(identity);
591
+ if (!binding?.threadId) throw new Error("bound Codex conversation has no thread binding");
592
+ return await withExclusiveCodexAppServerThread({
593
+ bindingStore: params.bindingStore,
594
+ identity,
595
+ threadId: binding.threadId,
596
+ run: async () => {
597
+ const current = params.bindingStore.read(identity);
598
+ if (!isSameCodexAppServerThreadOwner(current, binding)) throw new Error("Codex conversation binding changed before its turn.");
599
+ assertCodexBindingMayBeReplaced(binding, "running a conversation-bound Codex thread");
600
+ let threadId = binding.threadId;
601
+ const requestedWorkspaceDir = binding.cwd || params.data.workspaceDir;
602
+ const reviewerModelProvider = resolveModelBackedReviewerPolicyProvider({
603
+ authProfileId: binding.authProfileId,
604
+ modelProvider: binding.modelProvider,
605
+ ...agentLookup
606
+ });
607
+ const { runtime, workspaceDir } = await resolveConversationAppServerRuntime({
608
+ pluginConfig: params.pluginConfig,
609
+ config: params.config,
610
+ agentId: params.data.source?.agentId ?? params.data.agentId,
611
+ sessionKey: params.data.legacyBinding ? params.sessionKey : params.data.source?.sessionKey,
612
+ source: params.data.source,
613
+ workspaceDir: requestedWorkspaceDir,
614
+ modelProvider: reviewerModelProvider,
615
+ model: binding.model,
616
+ agentDir: params.data.agentDir
617
+ });
618
+ const modelScopedRuntime = resolveCodexAppServerForModelProvider({
619
+ appServer: runtime,
620
+ provider: reviewerModelProvider,
621
+ model: binding.model,
622
+ config: params.config,
623
+ env: process.env,
624
+ agentDir: params.data.agentDir
625
+ });
626
+ const sessionRoot = modelScopedRuntime.sessionRoot;
627
+ const approvalPolicy = modelScopedRuntime.approvalPolicy;
628
+ const sandbox = modelScopedRuntime.sandbox;
629
+ const permissionProfile = modelScopedRuntime.networkProxy?.profileName;
630
+ const networkProxyConfigFingerprint = modelScopedRuntime.networkProxy?.configFingerprint;
631
+ const networkProxyBindingChanged = binding.networkProxyProfileName !== permissionProfile || binding.networkProxyConfigFingerprint !== networkProxyConfigFingerprint;
632
+ const serviceTier = binding.serviceTier ?? runtime.serviceTier;
633
+ let useStickyNetworkProfile = permissionProfile !== void 0 && binding.networkProxyProfileName === permissionProfile && binding.networkProxyConfigFingerprint === networkProxyConfigFingerprint;
634
+ assertNativeConversationApprovalPolicySupported(modelScopedRuntime);
635
+ const modelSelection = binding.model ? resolveCodexAppServerRequestModelSelection({
636
+ model: binding.model,
637
+ modelProvider: binding.modelProvider,
638
+ authProfileId: binding.authProfileId,
639
+ ...agentLookup
640
+ }) : void 0;
641
+ const threadRequestRuntime = {
642
+ runtime: modelScopedRuntime,
643
+ workspaceDir,
644
+ ...modelSelection
645
+ };
646
+ const clientOptions = {
647
+ startOptions: runtime.start,
648
+ timeoutMs: runtime.requestTimeoutMs,
649
+ authProfileId: binding.authProfileId,
650
+ ...agentLookup
651
+ };
652
+ let client = await getLeasedSharedCodexAppServerClient(clientOptions);
653
+ const clientLease = { client };
654
+ let activeTurnId;
655
+ let activeTurnCleanup = () => void 0;
656
+ let isolatedSubscriptionClient;
657
+ let turnRoute;
658
+ let liveThreadOwnership;
659
+ let ownsNativeSubscription = false;
660
+ let turnSucceeded = false;
661
+ const assertResumeInputAllowed = async () => {
662
+ const { thread } = await client.request("thread/read", {
663
+ threadId,
664
+ includeTurns: false
665
+ }, { timeoutMs: runtime.requestTimeoutMs });
666
+ assertCodexThreadAcceptsDirectInput(thread);
667
+ };
668
+ try {
669
+ if (!networkProxyBindingChanged && binding.clientId !== client.getInstanceId()) await assertResumeInputAllowed();
670
+ if (!params.incognito && isCodexAppServerClientRuntimeLive(client)) {
671
+ const ownership = await consumeCodexAppServerLiveThread(client, threadId);
672
+ if (ownership) {
673
+ liveThreadOwnership = {
674
+ client,
675
+ threadId,
676
+ ownership
677
+ };
678
+ ownsNativeSubscription = true;
679
+ }
680
+ }
681
+ if (networkProxyBindingChanged) {
682
+ const response = assertCodexThreadStartResponse(await withLeasedCodexAppServerClientStartSelectionRetry({
683
+ lease: clientLease,
684
+ options: clientOptions,
685
+ run: async (requestClient, requestOptions) => await requestClient.request("thread/start", {
686
+ ...buildConversationThreadRequest(threadRequestRuntime, serviceTier),
687
+ developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
688
+ experimentalRawEvents: true,
689
+ ...params.incognito ? { ephemeral: true } : {}
690
+ }, requestOptions()),
691
+ onClientChange: (nextClient) => {
692
+ client = nextClient;
693
+ }
694
+ }));
695
+ threadId = response.thread.id;
696
+ ownsNativeSubscription = true;
697
+ assertCodexThreadAcceptsDirectInput(response.thread);
698
+ if (liveThreadOwnership && (liveThreadOwnership.threadId !== threadId || liveThreadOwnership.client !== client)) {
699
+ const previousOwnership = liveThreadOwnership;
700
+ try {
701
+ await previousOwnership.ownership.release(previousOwnership.threadId);
702
+ } catch (error) {
703
+ if (!(isCodexAppServerClientRuntimeLive(previousOwnership.client) && await retainCodexAppServerBindingSubscription(previousOwnership.client, previousOwnership.threadId, previousOwnership.ownership).catch(() => false))) await closeCodexStartupClientBestEffort(previousOwnership.client);
704
+ liveThreadOwnership = void 0;
705
+ throw error;
706
+ }
707
+ liveThreadOwnership = void 0;
708
+ } else if (binding.threadId !== threadId) await releaseCodexAppServerBindingSubscription(binding);
709
+ if (!await params.bindingStore.mutate(identity, {
710
+ kind: "set",
711
+ binding: {
712
+ threadId,
713
+ clientId: client.getInstanceId(),
714
+ cwd: response.thread.cwd ?? workspaceDir,
715
+ authProfileId: binding.authProfileId,
716
+ model: response.model ?? modelSelection?.model ?? binding.model,
717
+ modelProvider: normalizeCodexAppServerBindingModelProvider({
718
+ authProfileId: binding.authProfileId,
719
+ modelProvider: response.modelProvider ?? modelSelection?.modelProvider ?? binding.modelProvider,
720
+ ...agentLookup
721
+ }),
722
+ serviceTier: serviceTier ?? void 0,
723
+ networkProxyProfileName: modelScopedRuntime.networkProxy?.profileName,
724
+ networkProxyConfigFingerprint: modelScopedRuntime.networkProxy?.configFingerprint,
725
+ conversationStartId: binding.conversationStartId,
726
+ conversationSourceTransferComplete: binding.conversationSourceTransferComplete,
727
+ historyCoveredThrough: binding.historyCoveredThrough
728
+ }
729
+ })) throw new Error("Codex conversation binding changed while rotating its thread.");
730
+ useStickyNetworkProfile = modelScopedRuntime.networkProxy !== void 0;
731
+ } else if (binding.clientId !== client.getInstanceId() || isCodexAppServerClientRuntimeLive(client) && !params.incognito && !liveThreadOwnership) {
732
+ if (binding.clientId === client.getInstanceId()) await assertResumeInputAllowed();
733
+ const response = await withLeasedCodexAppServerClientStartSelectionRetry({
734
+ lease: clientLease,
735
+ options: clientOptions,
736
+ run: async (requestClient, requestOptions) => await resumeCodexAppServerThread({
737
+ client: requestClient,
738
+ onSubscriptionReleased: () => {
739
+ isolatedSubscriptionClient = requestClient;
740
+ },
741
+ abandonClient: async () => {
742
+ await closeCodexStartupClientBestEffort(requestClient);
743
+ isolatedSubscriptionClient = requestClient;
744
+ },
745
+ request: {
746
+ threadId,
747
+ ...buildConversationThreadRequest(threadRequestRuntime, serviceTier)
748
+ },
749
+ requestResume: (request) => requestClient.request("thread/resume", request, requestOptions())
750
+ }),
751
+ onClientChange: (nextClient) => {
752
+ client = nextClient;
753
+ }
754
+ });
755
+ threadId = response.thread.id;
756
+ ownsNativeSubscription = true;
757
+ assertCodexThreadAcceptsDirectInput(response.thread);
758
+ if (!isSameCodexAppServerThreadOwner(binding, {
759
+ threadId,
760
+ clientId: client.getInstanceId()
761
+ })) await releaseCodexAppServerBindingSubscription(binding);
762
+ if (!await params.bindingStore.mutate(identity, {
763
+ kind: "patch",
764
+ threadId: binding.threadId,
765
+ patch: {
766
+ clientId: client.getInstanceId(),
767
+ cwd: response.thread.cwd ?? binding.cwd,
768
+ model: response.model ?? modelSelection?.model ?? binding.model,
769
+ modelProvider: normalizeCodexAppServerBindingModelProvider({
770
+ authProfileId: binding.authProfileId,
771
+ modelProvider: response.modelProvider ?? modelSelection?.modelProvider ?? binding.modelProvider,
772
+ ...agentLookup
773
+ })
774
+ }
775
+ })) throw new Error("Codex conversation binding changed while resuming on a new client.");
776
+ }
777
+ const turnCollector = createCodexConversationTurnCollector(threadId);
778
+ turnRoute = getCodexAppServerTurnRouter(client).reserveThread({
779
+ threadId,
780
+ onNotification: turnCollector.handleNotification
781
+ });
782
+ turnRoute.armTurn();
783
+ activeTurnId = (await client.request("turn/start", {
784
+ threadId,
785
+ input: buildCodexConversationTurnInput({
786
+ prompt: params.prompt,
787
+ event: params.event
788
+ }),
789
+ cwd: workspaceDir,
790
+ ...sessionRoot ? { runtimeWorkspaceRoots: [sessionRoot] } : {},
791
+ approvalPolicy,
792
+ approvalsReviewer: modelScopedRuntime.approvalsReviewer,
793
+ ...useStickyNetworkProfile ? {} : { sandboxPolicy: codexSandboxPolicyForTurn(sandbox, sessionRoot ?? workspaceDir) },
794
+ ...modelSelection?.model ? { model: modelSelection.model } : {},
795
+ personality: CODEX_NATIVE_PERSONALITY_NONE,
796
+ ...serviceTier ? { serviceTier } : {}
797
+ }, { timeoutMs: runtime.requestTimeoutMs })).turn.id;
798
+ activeTurnCleanup = trackCodexConversationActiveTurn({
799
+ identity,
800
+ client,
801
+ threadId,
802
+ turnId: activeTurnId
803
+ });
804
+ turnCollector.setTurnId(activeTurnId);
805
+ await turnRoute.bindTurn(activeTurnId);
806
+ const replyText = (await turnCollector.wait({ timeoutMs: params.timeoutMs ?? DEFAULT_BOUND_TURN_TIMEOUT_MS })).replyText.trim();
807
+ turnSucceeded = true;
808
+ return { reply: { text: replyText || "Codex completed without a text reply." } };
809
+ } catch (error) {
810
+ if (isCodexAppServerOverloadError(error) && error.method === "thread/resume") throw error;
811
+ if (error instanceof CodexThreadDirectInputError) {
812
+ if (params.incognito && ownsNativeSubscription) {
813
+ if (!await unsubscribeCodexThreadBestEffort(client, {
814
+ threadId,
815
+ timeoutMs: 5e3
816
+ })) await retireUnsafeCodexTurnClientBestEffort(client, "parent-owned thread unsubscribe");
817
+ }
818
+ throw error;
819
+ }
820
+ if (error instanceof CodexConversationTurnTimeoutError && activeTurnId || turnRoute && isCodexAppServerIndeterminateRequestCancellationError(error)) {
821
+ if (!await interruptCodexTurnAndWaitBestEffort(client, {
822
+ threadId,
823
+ turnId: activeTurnId ?? ""
824
+ })) {
825
+ await retireUnsafeCodexTurnClientBestEffort(client, "turn interrupt");
826
+ isolatedSubscriptionClient = client;
827
+ }
828
+ }
829
+ if (params.incognito) {
830
+ if (await params.bindingStore.mutate(identity, {
831
+ kind: "clear",
832
+ threadId
833
+ }) && isolatedSubscriptionClient !== client) {
834
+ if (!await unsubscribeCodexThreadBestEffort(client, {
835
+ threadId,
836
+ timeoutMs: 5e3
837
+ })) await retireUnsafeCodexTurnClientBestEffort(client, "thread unsubscribe");
838
+ }
839
+ }
840
+ throw error;
841
+ } finally {
842
+ activeTurnCleanup();
843
+ turnRoute?.release();
844
+ try {
845
+ if (ownsNativeSubscription && isolatedSubscriptionClient !== client && !params.incognito && isCodexAppServerClientRuntimeLive(client)) {
846
+ const currentLiveThreadOwnership = liveThreadOwnership?.client === client && liveThreadOwnership.threadId === threadId ? liveThreadOwnership.ownership : void 0;
847
+ let retained = false;
848
+ if (turnSucceeded) retained = await params.bindingStore.withLease(identity, async () => {
849
+ const latest = params.bindingStore.read(identity);
850
+ if (latest?.threadId !== threadId || latest.clientId !== client.getInstanceId()) return false;
851
+ return await retainCodexAppServerBindingSubscription(client, threadId, currentLiveThreadOwnership);
852
+ });
853
+ if (!retained) {
854
+ if (!(currentLiveThreadOwnership ? await currentLiveThreadOwnership.release(threadId).then(() => true) : await unsubscribeCodexThreadBestEffort(client, {
855
+ threadId,
856
+ timeoutMs: 5e3
857
+ }))) await closeCodexStartupClientBestEffort(client);
858
+ }
859
+ }
860
+ } catch (error) {
861
+ embeddedAgentLog.warn("codex conversation subscription cleanup failed", {
862
+ threadId,
863
+ reason: formatErrorMessage(error)
864
+ });
865
+ await closeCodexStartupClientBestEffort(client);
866
+ } finally {
867
+ releaseCodexAppServerClientLease(clientLease);
868
+ }
869
+ }
870
+ }
871
+ });
872
+ }
873
+ async function runBoundTurnWithMissingThreadRecovery(params) {
874
+ await prepareCodexConversationBinding(params);
875
+ try {
876
+ return await runBoundTurn(params);
877
+ } catch (error) {
878
+ if (!isCodexThreadNotFoundError(error)) throw error;
879
+ await prepareCodexConversationBinding(params, { forceNew: true });
880
+ return await runBoundTurn(params);
881
+ }
882
+ }
883
+ function isCodexThreadNotFoundError(error) {
884
+ if (isCodexAppServerOverloadError(error) || isCodexAppServerUnsafeSubscriptionError(error)) return false;
885
+ const message = formatErrorMessage(error);
886
+ return /\bthread not found:/iu.test(message) || /\bbound Codex conversation has no thread binding\b/u.test(message);
887
+ }
888
+ //#endregion
889
+ export { runBoundTurnWithMissingThreadRecovery };