@openclaw/codex 2026.9.2 → 2026.9.4

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 (120) hide show
  1. package/README.md +2 -0
  2. package/dist/api.js +29 -26
  3. package/dist/apply-CvT8tW5i.js +356 -0
  4. package/dist/{attempt-client-cleanup-Bp1ZOnk2.js → attempt-client-cleanup-DKHzbwt5.js} +38 -214
  5. package/dist/{attempt-context-CSazkWns.js → attempt-context-D44yeDII.js} +9 -7
  6. package/dist/{event-projector-usage-ZqEVbAEY.js → attempt-notifications-C9XufG6l.js} +3 -106
  7. package/dist/{auth-binding-Ds_QVm-X.js → auth-binding-CWlKVccJ.js} +2 -2
  8. package/dist/auth-cache-key-7orQae_j.js +498 -0
  9. package/dist/auth-profile-CwzO4_RG.js +43 -0
  10. package/dist/auth-profile-selection-DFu9e167.js +45 -0
  11. package/dist/binding-connection-D3udKbIE.js +60 -0
  12. package/dist/{bounded-turn-DxGJRYbY.js → bounded-turn-C_Hneydj.js} +28 -125
  13. package/dist/build-state-BCmg9-5p.js +19 -0
  14. package/dist/{capabilities-B1mgvpP8.js → capabilities-8vx68WLH.js} +1 -1
  15. package/dist/cli-metadata.js +1 -1
  16. package/dist/client-B57KWPQx.js +4005 -0
  17. package/dist/command-dispatch-BpduNGVf.js +21 -0
  18. package/dist/{command-formatters-DSbGMXA0.js → command-formatters-lxlkNZgU.js} +4 -3
  19. package/dist/{command-handlers-DOJvGv6-.js → command-handlers-tiL9iWj7.js} +788 -135
  20. package/dist/command-rpc-CR5EOEZX.js +233 -0
  21. package/dist/{compact-Y_xBqrHN.js → compact-B_H3AOd7.js} +19 -14
  22. package/dist/config-oIORaQ5T.js +106 -0
  23. package/dist/{config-CyapYSji.js → config-options-CXMq1T-C.js} +205 -593
  24. package/dist/config-runtime-D0zGNIHu.js +11 -0
  25. package/dist/{config-utils-C57JiJrU.js → config-utils-DujwEnhg.js} +2 -4
  26. package/dist/conversation-binding-DsB4-O7U.js +900 -0
  27. package/dist/{plugin-marketplace-discovery-B7gHXJ3d.js → conversation-control-CtUu1b3m.js} +11 -143
  28. package/dist/doctor-contract-api.js +5 -5
  29. package/dist/{dynamic-tools-BwpvTxaX.js → dynamic-tools-CmN5RAip.js} +133 -76
  30. package/dist/{effective-mcp-catalog-CY9Jn4bA.js → effective-mcp-catalog-Bd73bqA8.js} +3 -3
  31. package/dist/{elicitation-input-DMkeki8d.js → elicitation-input-DPoK8F7w.js} +8 -22
  32. package/dist/ephemeral-turn-D78_VmzO.js +101 -0
  33. package/dist/{event-projector-assistant-message-B9kp6iGC.js → event-projector-assistant-message-BmBM4qBV.js} +12 -25
  34. package/dist/event-projector-items-5VhsECCO.js +200 -0
  35. package/dist/event-projector-usage-BfXmpOec.js +102 -0
  36. package/dist/{harness-Czh2Sdoc.js → harness-C7rRa7bm.js} +48 -24
  37. package/dist/harness.js +1 -1
  38. package/dist/history-rejection-B4nOXNXF.js +13 -0
  39. package/dist/incognito-session-uhrBF6wJ.js +15 -0
  40. package/dist/index.js +406 -2915
  41. package/dist/inference-context-Dr_mzvk4.js +94 -0
  42. package/dist/inference-proxy-HGXKUIre.js +311 -0
  43. package/dist/{isolated-completion-CRh-51AK.js → isolated-completion-DWVb5U2J.js} +7 -4
  44. package/dist/launch-args-RXQwn8zV.js +90 -0
  45. package/dist/{managed-binary-DcpnJF9x.js → managed-binary-D22rNEcR.js} +13 -103
  46. package/dist/managed-thread-store-D1t5jh3G.js +66 -0
  47. package/dist/media-understanding-provider.js +4 -1
  48. package/dist/migration-provider-api.js +2 -0
  49. package/dist/{model-catalog-DT5vo6qo.js → model-catalog-Dq2n8f_p.js} +6 -5
  50. package/dist/{models-CcrgSMQR.js → models-PCKOim-h.js} +3 -3
  51. package/dist/native-execution-policy-umd0tgh4.js +128 -0
  52. package/dist/native-subagent-history-DjtPloaU.js +140 -0
  53. package/dist/native-subagent-history-owner-Pcp7JuVF.js +34 -0
  54. package/dist/{native-subagent-monitor-B3bvQGcg.js → native-subagent-monitor-fQ7eIXEf.js} +115 -460
  55. package/dist/native-subagent-task-ids-BzTdT0Uw.js +387 -0
  56. package/dist/node-cli-sessions-DKaOmVUh.js +527 -0
  57. package/dist/{node-exec-server.runtime-nfbUOtUB.js → node-exec-server.runtime-Cl2vapEi.js} +55 -24
  58. package/dist/plan-7uG-Fp36.js +947 -0
  59. package/dist/plugin-activation-BYQculil.js +138 -0
  60. package/dist/{thread-resume-BLgqkUc1.js → plugin-app-cache-key-d0eKxy7b.js} +4 -52
  61. package/dist/{plugin-approval-roundtrip-aQVdyL5g.js → plugin-approval-roundtrip-Bz3FT8W5.js} +1 -1
  62. package/dist/plugin-inventory-oL4Na1ZG.js +328 -0
  63. package/dist/plugin-marketplace-discovery-P-Vndfqq.js +137 -0
  64. package/dist/{plugin-metadata-cache-BwCLTY_U.js → plugin-metadata-cache-ClPJpRio.js} +17 -9
  65. package/dist/protocol-5bh1G-H7.js +275 -0
  66. package/dist/provider-MQ8PVkuE.js +50 -0
  67. package/dist/{provider-capabilities-BpG9Ltc5.js → provider-capabilities-BvCUL69A.js} +2 -2
  68. package/dist/{rate-limits-Fx_l4eNa.js → rate-limits-CBMmWBr9.js} +1 -1
  69. package/dist/request-B7hEkBGq.js +212 -0
  70. package/dist/rpc-error-ttN_QjEm.js +30 -0
  71. package/dist/{run-attempt-BzthFQSM.js → run-attempt-CvsAAGa0.js} +664 -377
  72. package/dist/{runtime-artifact-Bh_gf6-t.js → runtime-artifact-BJ0rXBZ_.js} +27 -27
  73. package/dist/sandbox-guard-BxgyoM8M.js +166 -0
  74. package/dist/{session-binding-4Ki04sW-.js → session-binding-CI8UuilT.js} +30 -48
  75. package/dist/{session-binding-record-CyaXZvTe.js → session-binding-record-Bcvslnhf.js} +12 -26
  76. package/dist/session-catalog-adoption-tcZi1IfX.js +251 -0
  77. package/dist/session-catalog-archive-CwKg8Xz2.js +56 -0
  78. package/dist/session-catalog-ct-AH01I.js +1477 -0
  79. package/dist/session-catalog-home-id-CwhLdC03.js +18 -0
  80. package/dist/session-catalog-node-adoption-B1tgDW-m.js +182 -0
  81. package/dist/session-catalog-node-continue-Ci-DXyJP.js +238 -0
  82. package/dist/session-catalog-provenance-D-dIfRAH.js +96 -0
  83. package/dist/{session-cli-BjLhpmS1.js → session-cli-DNN_jhGc.js} +3 -2
  84. package/dist/{session-history-DWmMqiqW.js → session-history-Cz-tOOYj.js} +6 -3
  85. package/dist/{session-upstream-marker-DReTpGz6.js → session-history-import-DwyijFI6.js} +5 -50
  86. package/dist/{session-history-read-LHldwvyh.js → session-history-read-cxPUxyVV.js} +21 -7
  87. package/dist/session-history-worker-runtime.js +18 -6
  88. package/dist/session-history.worker-BeK1YOyT.js +378 -0
  89. package/dist/session-history.worker.js +1 -1
  90. package/dist/{session-initialization-BCFVnLL4.js → session-initialization-Ct0_Hzmg.js} +2 -2
  91. package/dist/{session-permission-policy-BaueHmSj.js → session-permission-policy-BBmXA8Eg.js} +4 -7
  92. package/dist/{session-retirement-DNdmYdFc.js → session-retirement-B2YfbAAT.js} +6 -5
  93. package/dist/session-upstream-marker-D15C9NHp.js +46 -0
  94. package/dist/{settled-turn-context-BOM3Y1pl.js → settled-turn-context-Du6gdUDd.js} +9 -9
  95. package/dist/{settled-turn-finalizer-BI-b1sry.js → settled-turn-finalizer-Dn91stmc.js} +8 -7
  96. package/dist/shared-client-CscigXXL.js +3109 -0
  97. package/dist/{side-question-B6DWAw1A.js → side-question-BGMJQf4U.js} +65 -170
  98. package/dist/source-CElXU0Oy.js +518 -0
  99. package/dist/thread-archive-guard-DfpBr-Sz.js +53 -0
  100. package/dist/thread-history-page-D0JIp48V.js +361 -0
  101. package/dist/{thread-lifecycle-DPK30jad.js → thread-lifecycle-DXl1-I6b.js} +187 -639
  102. package/dist/{thread-ownership-CbucAmmp.js → thread-ownership-BsDRTsyp.js} +13 -15
  103. package/dist/thread-policy-BuEQfE0g.js +54 -0
  104. package/dist/thread-resume-DsDOZpFg.js +52 -0
  105. package/dist/timeout-BVw8aaM1.js +33 -0
  106. package/dist/transcript-history-projection-v17wXgdK.js +238 -0
  107. package/dist/{transcript-mirror-_njEB84z.js → transcript-mirror-C5CKgNy6.js} +50 -260
  108. package/dist/{transcript-mirror-attestation-Byl34PC-.js → transcript-mirror-attestation-DibE0krH.js} +17 -4
  109. package/dist/{transport-stdio-CzTngV0j.js → transport-stdio-BGjHoYLr.js} +73 -43
  110. package/dist/{upstream-session-fork-1fwbNKzQ.js → upstream-session-fork-BZerMte_.js} +27 -19
  111. package/dist/{usage-i9fv5-4m.js → usage-tCk-mg_x.js} +4 -3
  112. package/dist/version-omm6IPI3.js +12 -0
  113. package/dist/{web-search-provider.runtime-DKQn67A7.js → web-search-provider.runtime-B7bGbGWq.js} +3 -3
  114. package/openclaw.plugin.json +23 -1
  115. package/package.json +4 -4
  116. package/dist/request-C8H22rPi.js +0 -475
  117. package/dist/session-catalog-B47uwekb.js +0 -3272
  118. package/dist/session-history.worker-zZVcx0ME.js +0 -267
  119. package/dist/shared-client-CKhX2kFt.js +0 -7505
  120. package/dist/thread-policy-CTalhWFy.js +0 -105
@@ -0,0 +1,900 @@
1
+ import { l as sessionBindingIdentity, n as assertCodexBindingMayBeReplaced } from "./session-binding-record-Bcvslnhf.js";
2
+ import { b as readCodexRequirementsToml, g as readCodexEffectiveConfig, n as codexSandboxPolicyForTurn } from "./config-options-CXMq1T-C.js";
3
+ import { o as isJsonObject, x as readCodexPluginConfig } from "./protocol-5bh1G-H7.js";
4
+ import { _ as assertCodexThreadAcceptsDirectInput, b as assertCodexThreadStartResponse, l as isCodexAppServerOverloadError, m as CodexThreadDirectInputError, s as isCodexAppServerIndeterminateRequestCancellationError } from "./client-B57KWPQx.js";
5
+ import { o as resolveCodexDefaultWorkspaceDir } from "./conversation-binding-data-BiodpIzq.js";
6
+ import { n as projectBoundedCodexVisibleSessionHistory } from "./transcript-history-projection-v17wXgdK.js";
7
+ import { n as resolveOpenClawExecPolicyForCodexAppServer, t as canUseCodexModelBackedApprovalsReviewerForModel } from "./config-oIORaQ5T.js";
8
+ import { n as resolveCodexAppServerRuntimeOptions } from "./config-runtime-D0zGNIHu.js";
9
+ import { n as normalizeCodexAppServerBindingModelProvider, t as isCodexAppServerNativeAuthProfile } from "./auth-profile-CwzO4_RG.js";
10
+ import { H as isCodexAppServerClientRuntimeLive, R as consumeCodexAppServerLiveThread, U as isCodexAppServerLiveThreadClaimed, V as hasCodexAppServerLiveThread, _ as retainSharedCodexAppServerClientByInstanceId, c as getLeasedSharedCodexAppServerClient, m as releaseCodexAppServerClientLease, q as releaseCodexAppServerLiveThread, x as withLeasedCodexAppServerClientStartSelectionRetry } from "./shared-client-CscigXXL.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-DKHzbwt5.js";
12
+ import { n as isAssistantCommentaryCompletionNotification } from "./attempt-notifications-C9XufG6l.js";
13
+ import { D as resolveCodexAppServerRequestModelSelection, Et as mergeCodexThreadConfigs, M as buildCodexProjectDocThreadConfig, T as CODEX_NATIVE_PERSONALITY_NONE, Tt as buildDisabledAppsConfigPatch } from "./thread-lifecycle-DXl1-I6b.js";
14
+ import "./session-binding-CI8UuilT.js";
15
+ import { t as resumeCodexAppServerThread } from "./thread-resume-DsDOZpFg.js";
16
+ import { a as rollbackCodexAppServerBindingSubscription, n as releaseCodexAppServerBindingSubscription, r as retainCodexAppServerBindingSubscription, s as withExclusiveCodexAppServerThread, t as isSameCodexAppServerThreadOwner } from "./thread-ownership-BsDRTsyp.js";
17
+ import { a as resolveCodexAppServerForModelProvider, i as resolveCodexSessionPermissionCwd, n as applyCodexSessionPermissionPolicy, t as CODEX_SESSION_PERMISSION_EXEC_MODES } from "./session-permission-policy-BBmXA8Eg.js";
18
+ import { u as trackCodexConversationActiveTurn } from "./conversation-control-CtUu1b3m.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, effectiveNativeConfig) {
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, effectiveNativeConfig),
170
+ ...serviceTier ? { serviceTier } : {}
171
+ };
172
+ }
173
+ async function buildConversationThreadRequestForClient(client, resolved, serviceTier, requestOptions) {
174
+ const effectiveConfig = await readCodexEffectiveConfig(client, resolved.workspaceDir, requestOptions());
175
+ requestOptions();
176
+ return buildConversationThreadRequest(resolved, serviceTier, effectiveConfig);
177
+ }
178
+ function codexConversationSandboxOrPermissions(runtime, sandbox, effectiveNativeConfig) {
179
+ const networkProxy = runtime.networkProxy;
180
+ const config = buildCodexProjectDocThreadConfig(mergeCodexThreadConfigs(networkProxy?.configPatch, buildDisabledAppsConfigPatch()), effectiveNativeConfig);
181
+ return networkProxy ? { config } : {
182
+ sandbox,
183
+ config
184
+ };
185
+ }
186
+ async function writeThreadBindingFromResponse(params, resolved, client, response, requestOptions) {
187
+ let retained = false;
188
+ let sameOwner = false;
189
+ try {
190
+ const current = params.bindingStore.read(params.identity);
191
+ assertCodexBindingMayBeReplaced(current, "storing a conversation-bound Codex thread");
192
+ const trackSubscription = !params.incognito && isCodexAppServerClientRuntimeLive(client);
193
+ sameOwner = isSameCodexAppServerThreadOwner(current, {
194
+ threadId: response.thread.id,
195
+ clientId: client.getInstanceId()
196
+ });
197
+ requestOptions();
198
+ assertCodexThreadAcceptsDirectInput(response.thread);
199
+ if (trackSubscription) {
200
+ retained = await retainCodexAppServerBindingSubscription(client, response.thread.id);
201
+ if (!retained) throw new Error("Codex conversation thread lost its native subscription owner.");
202
+ }
203
+ if (current && !sameOwner) {
204
+ const { assertCurrent } = requestOptions();
205
+ await releaseCodexAppServerBindingSubscription(current, { assertCurrent });
206
+ }
207
+ requestOptions();
208
+ if (!await params.bindingStore.mutate(params.identity, {
209
+ kind: "set",
210
+ binding: {
211
+ threadId: response.thread.id,
212
+ clientId: client.getInstanceId(),
213
+ cwd: resolved.workspaceDir,
214
+ authProfileId: params.authProfileId,
215
+ model: response.model ?? resolved.model ?? params.model,
216
+ modelProvider: normalizeCodexAppServerBindingModelProvider({
217
+ authProfileId: params.authProfileId,
218
+ modelProvider: response.modelProvider ?? resolved.modelProvider ?? params.modelProvider,
219
+ ...resolved.agentLookup
220
+ }),
221
+ serviceTier: params.serviceTier ?? resolved.runtime.serviceTier ?? void 0,
222
+ networkProxyProfileName: resolved.runtime.networkProxy?.profileName,
223
+ networkProxyConfigFingerprint: resolved.runtime.networkProxy?.configFingerprint
224
+ }
225
+ }, requestOptions)) throw new Error("Codex conversation binding changed while storing its thread.");
226
+ } catch (error) {
227
+ if (retained && !sameOwner || !hasCodexAppServerLiveThread(client, response.thread.id)) await rollbackCodexAppServerBindingSubscription(client, response.thread.id, retained);
228
+ throw error;
229
+ }
230
+ }
231
+ async function bindThread(params, threadId) {
232
+ const current = params.bindingStore.read(params.identity);
233
+ assertCodexBindingMayBeReplaced(current, "binding a conversation-bound Codex thread");
234
+ const resolved = await resolveThreadBindingRuntime(params);
235
+ const clientLease = { client: await getLeasedSharedCodexAppServerClient(resolved.clientOptions) };
236
+ try {
237
+ await withLeasedCodexAppServerClientStartSelectionRetry({
238
+ lease: clientLease,
239
+ options: resolved.clientOptions,
240
+ run: async (client, requestOptions) => {
241
+ const request = await buildConversationThreadRequestForClient(client, resolved, params.serviceTier ?? resolved.runtime.serviceTier, requestOptions);
242
+ let response;
243
+ if (threadId && !resolved.runtime.networkProxy) {
244
+ if (isCodexAppServerLiveThreadClaimed(client, threadId)) throw new Error(`Codex thread ${threadId} has an active run; stop it before binding its conversation.`);
245
+ const { thread } = await client.request("thread/read", {
246
+ threadId,
247
+ includeTurns: false
248
+ }, requestOptions());
249
+ assertCodexThreadAcceptsDirectInput(thread);
250
+ const { assertCurrent } = requestOptions();
251
+ await releaseCodexAppServerLiveThread(client, threadId, assertCurrent);
252
+ if (isCodexAppServerLiveThreadClaimed(client, threadId)) throw new Error(`Codex thread ${threadId} has an active run; stop it before binding its conversation.`);
253
+ response = await resumeCodexAppServerThread({
254
+ client,
255
+ abandonClient: () => closeCodexStartupClientBestEffort(client),
256
+ request: {
257
+ ...request,
258
+ threadId
259
+ },
260
+ requestResume: (resumeRequest) => client.request("thread/resume", resumeRequest, requestOptions())
261
+ });
262
+ } else response = await client.request("thread/start", {
263
+ ...request,
264
+ developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
265
+ experimentalRawEvents: true,
266
+ ...params.incognito ? { ephemeral: true } : {}
267
+ }, requestOptions());
268
+ await writeThreadBindingFromResponse(params, resolved, client, response, requestOptions);
269
+ }
270
+ });
271
+ } finally {
272
+ releaseCodexAppServerClientLease(clientLease);
273
+ }
274
+ }
275
+ function assertNativeConversationApprovalPolicySupported(runtime) {
276
+ if (runtime.approvalPolicy !== "never" && runtime.approvalsReviewer === "user") throw new Error(NATIVE_CONVERSATION_INTERACTIVE_APPROVALS_UNAVAILABLE);
277
+ }
278
+ async function prepareCodexConversationBinding(params, options = {}) {
279
+ const identity = {
280
+ kind: "conversation",
281
+ bindingId: params.data.bindingId
282
+ };
283
+ const snapshot = params.bindingStore.read(identity);
284
+ const run = () => params.bindingStore.withLease(identity, async () => {
285
+ const current = params.bindingStore.read(identity);
286
+ if (current?.threadId !== snapshot?.threadId || current?.clientId !== snapshot?.clientId) throw new Error("Codex conversation binding changed before preparation.");
287
+ const requested = params.data.start && current?.conversationStartId !== params.data.start.id ? params.data.start : void 0;
288
+ if (current && !requested && !options.forceNew) return;
289
+ const sourceIdentity = params.data.source ? sessionBindingIdentity({
290
+ agentId: params.data.source.agentId,
291
+ sessionId: params.data.source.sessionId,
292
+ sessionKey: params.data.source.sessionKey,
293
+ config: params.config
294
+ }) : void 0;
295
+ const sourceBinding = sourceIdentity ? params.bindingStore.read(sourceIdentity) : void 0;
296
+ assertCodexBindingMayBeReplaced(current, "initializing a conversation-bound Codex thread");
297
+ assertCodexBindingMayBeReplaced(sourceBinding, "transferring a session into a conversation-bound Codex thread");
298
+ const inherited = current ?? sourceBinding;
299
+ const agentLookup = buildCodexConversationAgentLookup({
300
+ agentDir: params.data.agentDir,
301
+ config: params.config
302
+ });
303
+ const bindingParams = {
304
+ bindingStore: params.bindingStore,
305
+ identity,
306
+ pluginConfig: params.pluginConfig,
307
+ workspaceDir: requested ? params.data.workspaceDir : inherited?.cwd ?? params.data.workspaceDir,
308
+ ...agentLookup,
309
+ model: requested?.model ?? inherited?.model,
310
+ modelProvider: requested?.modelProvider ?? inherited?.modelProvider,
311
+ authProfileId: requested?.authProfileId ?? inherited?.authProfileId,
312
+ serviceTier: inherited?.serviceTier,
313
+ config: params.config,
314
+ sessionKey: params.data.legacyBinding ? params.sessionKey : params.data.source?.sessionKey,
315
+ source: params.data.source,
316
+ incognito: params.incognito,
317
+ agentId: params.data.source?.agentId ?? params.data.agentId
318
+ };
319
+ const threadId = requested?.threadId;
320
+ await bindThread(bindingParams, options.forceNew ? void 0 : threadId);
321
+ const stored = params.bindingStore.read(identity);
322
+ if (!stored) throw new Error("Codex conversation binding disappeared while initializing its thread.");
323
+ if (sourceIdentity && params.data.source && !current?.conversationSourceTransferComplete) await params.bindingStore.withLease(sourceIdentity, async () => {
324
+ const source = params.bindingStore.read(sourceIdentity);
325
+ if (source && source.threadId === params.data.source?.threadId) {
326
+ const sourceSessionKey = sourceIdentity.sessionKey ?? resolveTranscriptSessionKeyBySessionId({
327
+ agentId: sourceIdentity.agentId,
328
+ sessionId: sourceIdentity.sessionId,
329
+ storePath: resolveStorePath(params.config?.session?.store, { agentId: sourceIdentity.agentId })
330
+ });
331
+ if (sourceSessionKey && resolveActiveEmbeddedRunSessionId(sourceSessionKey) === sourceIdentity.sessionId) throw new Error("Codex source session has an active run; stop it before binding this conversation.");
332
+ if (source.threadId !== stored.threadId) {
333
+ await releaseCodexAppServerBindingSubscription(source);
334
+ await projectConversationSourceHistory(params.data.source, stored, params.config);
335
+ }
336
+ await params.bindingStore.mutate(sourceIdentity, {
337
+ kind: "clear",
338
+ threadId: source.threadId
339
+ });
340
+ }
341
+ });
342
+ if (!await params.bindingStore.mutate(identity, {
343
+ kind: "patch",
344
+ threadId: stored.threadId,
345
+ patch: {
346
+ ...params.data.start ? { conversationStartId: params.data.start.id } : {},
347
+ ...sourceIdentity ? { conversationSourceTransferComplete: true } : {}
348
+ }
349
+ })) throw new Error("Codex conversation binding changed while initializing its thread.");
350
+ });
351
+ const threadId = params.data.start?.threadId ?? snapshot?.threadId;
352
+ if (threadId) await withExclusiveCodexAppServerThread({
353
+ bindingStore: params.bindingStore,
354
+ identity,
355
+ threadId,
356
+ run
357
+ });
358
+ else await run();
359
+ }
360
+ async function projectConversationSourceHistory(source, target, config) {
361
+ const storePath = resolveStorePath(config?.session?.store, { agentId: source.agentId });
362
+ const sessionKey = source.sessionKey ?? resolveTranscriptSessionKeyBySessionId({
363
+ agentId: source.agentId,
364
+ sessionId: source.sessionId,
365
+ storePath
366
+ });
367
+ if (!sessionKey) return;
368
+ const entries = await readVisibleSessionTranscriptMessageEntries({
369
+ agentId: source.agentId,
370
+ sessionId: source.sessionId,
371
+ sessionKey,
372
+ storePath
373
+ });
374
+ const history = projectBoundedCodexVisibleSessionHistory(entries);
375
+ if (history.length === 0) return;
376
+ const clientLease = retainSharedCodexAppServerClientByInstanceId(target.clientId);
377
+ if (!clientLease) throw new Error("Codex conversation source history lost its bound client owner.");
378
+ try {
379
+ await clientLease.client.request("thread/inject_items", {
380
+ threadId: target.threadId,
381
+ items: history
382
+ });
383
+ } finally {
384
+ clientLease.release();
385
+ }
386
+ }
387
+ function resolveThreadRequestModelProvider(params) {
388
+ const modelProvider = params.modelProvider?.trim();
389
+ if (!modelProvider || modelProvider.toLowerCase() === "codex") return;
390
+ if (isCodexAppServerNativeAuthProfile(params) && modelProvider.toLowerCase() === "openai") return;
391
+ return modelProvider.toLowerCase() === "openai" ? "openai" : modelProvider;
392
+ }
393
+ function resolveOptionalThreadRequestModelSelection(params) {
394
+ if (!params.model?.trim()) return;
395
+ return resolveCodexAppServerRequestModelSelection({
396
+ model: params.model,
397
+ modelProvider: params.modelProvider,
398
+ authProfileId: params.authProfileId,
399
+ agentDir: params.agentDir,
400
+ config: params.config
401
+ });
402
+ }
403
+ function resolveModelBackedReviewerPolicyProvider(params) {
404
+ const modelProvider = params.modelProvider?.trim();
405
+ if (modelProvider && modelProvider.toLowerCase() !== "codex") return modelProvider.toLowerCase() === "openai" ? "openai" : modelProvider;
406
+ return isCodexAppServerNativeAuthProfile(params) ? "openai" : void 0;
407
+ }
408
+ function buildCodexConversationAgentLookup(params) {
409
+ const agentDir = params.agentDir?.trim();
410
+ return {
411
+ ...agentDir ? { agentDir } : {},
412
+ ...params.config ? { config: params.config } : {}
413
+ };
414
+ }
415
+ //#endregion
416
+ //#region extensions/codex/src/conversation-turn-collector.ts
417
+ /** Identifies a timer that expired in the bound-turn collector itself. */
418
+ var CodexConversationTurnTimeoutError = class extends Error {
419
+ constructor() {
420
+ super("codex app-server bound turn timed out");
421
+ this.name = "CodexConversationTurnTimeoutError";
422
+ }
423
+ };
424
+ function createCodexConversationTurnCollector(threadId) {
425
+ let turnId;
426
+ let completed = false;
427
+ let failedError;
428
+ let timeout;
429
+ const assistantTextByItem = /* @__PURE__ */ new Map();
430
+ let resolveCompletion;
431
+ let rejectCompletion;
432
+ const collectReplyText = () => {
433
+ return [...assistantTextByItem.values()].map((text) => text.trim()).filter(Boolean).at(-1) ?? "";
434
+ };
435
+ const clearWaitState = () => {
436
+ if (timeout) {
437
+ clearTimeout(timeout);
438
+ timeout = void 0;
439
+ }
440
+ resolveCompletion = void 0;
441
+ rejectCompletion = void 0;
442
+ };
443
+ const finish = () => {
444
+ if (completed) return;
445
+ completed = true;
446
+ if (failedError) rejectCompletion?.(new Error(failedError));
447
+ else resolveCompletion?.({ replyText: collectReplyText() });
448
+ clearWaitState();
449
+ };
450
+ const handleNotification = (notification) => {
451
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
452
+ if (!params || !turnId || !isCodexNotificationForTurn(params, threadId, turnId)) return;
453
+ if (notification.method === "item/agentMessage/delta") {
454
+ const itemId = normalizeOptionalString(params.itemId) ?? "assistant";
455
+ const delta = readTextString(params, "delta");
456
+ if (!delta) return;
457
+ assistantTextByItem.set(itemId, `${assistantTextByItem.get(itemId) ?? ""}${delta}`);
458
+ return;
459
+ }
460
+ if (notification.method === "item/completed") {
461
+ const item = isJsonObject(params.item) ? params.item : void 0;
462
+ if (item?.type === "agentMessage") {
463
+ const itemId = normalizeOptionalString(item.id) ?? normalizeOptionalString(params.itemId) ?? "assistant";
464
+ assistantTextByItem.delete(itemId);
465
+ if (isAssistantCommentaryCompletionNotification(notification)) return;
466
+ const text = readTextString(item, "text");
467
+ if (text?.trim()) assistantTextByItem.set(itemId, text);
468
+ }
469
+ return;
470
+ }
471
+ if (notification.method === "turn/completed") {
472
+ const turn = isJsonObject(params.turn) ? params.turn : void 0;
473
+ const status = normalizeOptionalString(turn?.status);
474
+ if (status === "failed") failedError = normalizeOptionalString(asOptionalRecord(turn?.error)?.message) ?? "codex app-server turn failed";
475
+ else if (status === "interrupted") failedError = "codex app-server turn interrupted";
476
+ else if (status !== "completed") failedError = "codex app-server turn completed without a valid terminal status";
477
+ if (status === "completed") {
478
+ const items = Array.isArray(turn?.items) ? turn.items : [];
479
+ for (const item of items) {
480
+ if (!isJsonObject(item) || item.type !== "agentMessage") continue;
481
+ const itemId = normalizeOptionalString(item.id) ?? `assistant-${assistantTextByItem.size + 1}`;
482
+ assistantTextByItem.delete(itemId);
483
+ const text = item.phase === "commentary" ? void 0 : readTextString(item, "text");
484
+ if (text?.trim()) assistantTextByItem.set(itemId, text);
485
+ }
486
+ }
487
+ finish();
488
+ }
489
+ };
490
+ return {
491
+ setTurnId(nextTurnId) {
492
+ turnId = nextTurnId;
493
+ },
494
+ handleNotification,
495
+ wait(params) {
496
+ if (completed) return failedError ? Promise.reject(new Error(failedError)) : Promise.resolve({ replyText: collectReplyText() });
497
+ return new Promise((resolve, reject) => {
498
+ resolveCompletion = resolve;
499
+ rejectCompletion = reject;
500
+ timeout = setTimeout(() => {
501
+ completed = true;
502
+ reject(new CodexConversationTurnTimeoutError());
503
+ clearWaitState();
504
+ }, resolveTimerTimeoutMs(params.timeoutMs, 100, 100));
505
+ timeout.unref?.();
506
+ });
507
+ }
508
+ };
509
+ }
510
+ function readTextString(record, key) {
511
+ const value = record?.[key];
512
+ return typeof value === "string" && value.length > 0 ? value : void 0;
513
+ }
514
+ //#endregion
515
+ //#region extensions/codex/src/conversation-turn-input.ts
516
+ const IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
517
+ ".avif",
518
+ ".gif",
519
+ ".jpeg",
520
+ ".jpg",
521
+ ".png",
522
+ ".webp"
523
+ ]);
524
+ function buildCodexConversationTurnInput(params) {
525
+ return [{
526
+ type: "text",
527
+ text: params.prompt,
528
+ text_elements: []
529
+ }, ...extractInboundMedia(params.event).map(toCodexImageInput).filter((item) => item !== void 0)];
530
+ }
531
+ function extractInboundMedia(event) {
532
+ const metadata = event.metadata ?? {};
533
+ const paths = normalizeSingleOrTrimmedStringList(metadata.mediaPaths).concat(normalizeSingleOrTrimmedStringList(metadata.mediaPath));
534
+ const urls = normalizeSingleOrTrimmedStringList(metadata.mediaUrls).concat(normalizeSingleOrTrimmedStringList(metadata.mediaUrl));
535
+ const mimeTypes = normalizeSingleOrTrimmedStringList(metadata.mediaTypes).concat(normalizeSingleOrTrimmedStringList(metadata.mediaType));
536
+ const count = Math.max(paths.length, urls.length, mimeTypes.length);
537
+ const media = [];
538
+ for (let index = 0; index < count; index += 1) media.push({
539
+ path: paths[index],
540
+ url: urls[index],
541
+ mimeType: mimeTypes[index] ?? mimeTypes[0]
542
+ });
543
+ return media;
544
+ }
545
+ function toCodexImageInput(media) {
546
+ if (!isImageMedia(media)) return;
547
+ const localPath = media.path ?? readLocalMediaPath(media.url);
548
+ if (localPath) {
549
+ const normalized = normalizeFileUrl(localPath);
550
+ return normalized ? {
551
+ type: "localImage",
552
+ path: normalized
553
+ } : void 0;
554
+ }
555
+ return media.url ? {
556
+ type: "image",
557
+ url: media.url
558
+ } : void 0;
559
+ }
560
+ function isImageMedia(media) {
561
+ if (media.mimeType?.toLowerCase().startsWith("image/")) return true;
562
+ const candidate = media.path ?? media.url;
563
+ if (!candidate) return false;
564
+ return IMAGE_EXTENSIONS.has(path.extname(candidate.split(/[?#]/, 1)[0] ?? "").toLowerCase());
565
+ }
566
+ function normalizeFileUrl(value) {
567
+ if (!/^file:\/\//iu.test(value)) return value;
568
+ try {
569
+ const fileUrl = new URL(value);
570
+ decodeURIComponent(fileUrl.pathname);
571
+ return fileURLToPath(fileUrl);
572
+ } catch {
573
+ return;
574
+ }
575
+ }
576
+ function readLocalMediaPath(value) {
577
+ if (!value) return;
578
+ if (/^file:\/\//iu.test(value)) return value;
579
+ if (value.startsWith("//")) return;
580
+ if (path.isAbsolute(value) || path.win32.isAbsolute(value)) return value;
581
+ return /^[a-z][a-z0-9+.-]*:/i.test(value) ? void 0 : value;
582
+ }
583
+ //#endregion
584
+ //#region extensions/codex/src/conversation-binding.ts
585
+ const DEFAULT_BOUND_TURN_TIMEOUT_MS = 12e5;
586
+ async function runBoundTurn(params) {
587
+ const agentLookup = buildCodexConversationAgentLookup({
588
+ agentDir: params.data.agentDir,
589
+ config: params.config
590
+ });
591
+ const identity = {
592
+ kind: "conversation",
593
+ bindingId: params.data.bindingId
594
+ };
595
+ const binding = params.bindingStore.read(identity);
596
+ if (!binding?.threadId) throw new Error("bound Codex conversation has no thread binding");
597
+ return await withExclusiveCodexAppServerThread({
598
+ bindingStore: params.bindingStore,
599
+ identity,
600
+ threadId: binding.threadId,
601
+ run: async () => {
602
+ const current = params.bindingStore.read(identity);
603
+ if (!isSameCodexAppServerThreadOwner(current, binding)) throw new Error("Codex conversation binding changed before its turn.");
604
+ assertCodexBindingMayBeReplaced(binding, "running a conversation-bound Codex thread");
605
+ let threadId = binding.threadId;
606
+ const requestedWorkspaceDir = binding.cwd || params.data.workspaceDir;
607
+ const reviewerModelProvider = resolveModelBackedReviewerPolicyProvider({
608
+ authProfileId: binding.authProfileId,
609
+ modelProvider: binding.modelProvider,
610
+ ...agentLookup
611
+ });
612
+ const { runtime, workspaceDir } = await resolveConversationAppServerRuntime({
613
+ pluginConfig: params.pluginConfig,
614
+ config: params.config,
615
+ agentId: params.data.source?.agentId ?? params.data.agentId,
616
+ sessionKey: params.data.legacyBinding ? params.sessionKey : params.data.source?.sessionKey,
617
+ source: params.data.source,
618
+ workspaceDir: requestedWorkspaceDir,
619
+ modelProvider: reviewerModelProvider,
620
+ model: binding.model,
621
+ agentDir: params.data.agentDir
622
+ });
623
+ const modelScopedRuntime = resolveCodexAppServerForModelProvider({
624
+ appServer: runtime,
625
+ provider: reviewerModelProvider,
626
+ model: binding.model,
627
+ config: params.config,
628
+ env: process.env,
629
+ agentDir: params.data.agentDir
630
+ });
631
+ const sessionRoot = modelScopedRuntime.sessionRoot;
632
+ const approvalPolicy = modelScopedRuntime.approvalPolicy;
633
+ const sandbox = modelScopedRuntime.sandbox;
634
+ const permissionProfile = modelScopedRuntime.networkProxy?.profileName;
635
+ const networkProxyConfigFingerprint = modelScopedRuntime.networkProxy?.configFingerprint;
636
+ const networkProxyBindingChanged = binding.networkProxyProfileName !== permissionProfile || binding.networkProxyConfigFingerprint !== networkProxyConfigFingerprint;
637
+ const serviceTier = binding.serviceTier ?? runtime.serviceTier;
638
+ let useStickyNetworkProfile = permissionProfile !== void 0 && binding.networkProxyProfileName === permissionProfile && binding.networkProxyConfigFingerprint === networkProxyConfigFingerprint;
639
+ assertNativeConversationApprovalPolicySupported(modelScopedRuntime);
640
+ const modelSelection = binding.model ? resolveCodexAppServerRequestModelSelection({
641
+ model: binding.model,
642
+ modelProvider: binding.modelProvider,
643
+ authProfileId: binding.authProfileId,
644
+ ...agentLookup
645
+ }) : void 0;
646
+ const threadRequestRuntime = {
647
+ runtime: modelScopedRuntime,
648
+ workspaceDir,
649
+ ...modelSelection
650
+ };
651
+ const clientOptions = {
652
+ startOptions: runtime.start,
653
+ timeoutMs: runtime.requestTimeoutMs,
654
+ authProfileId: binding.authProfileId,
655
+ ...agentLookup
656
+ };
657
+ let client = await getLeasedSharedCodexAppServerClient(clientOptions);
658
+ const clientLease = { client };
659
+ let activeTurnId;
660
+ let activeTurnCleanup = () => void 0;
661
+ let isolatedSubscriptionClient;
662
+ let turnRoute;
663
+ let liveThreadOwnership;
664
+ let ownsNativeSubscription = false;
665
+ let turnSucceeded = false;
666
+ const assertResumeInputAllowed = async () => {
667
+ const { thread } = await client.request("thread/read", {
668
+ threadId,
669
+ includeTurns: false
670
+ }, { timeoutMs: runtime.requestTimeoutMs });
671
+ assertCodexThreadAcceptsDirectInput(thread);
672
+ };
673
+ try {
674
+ if (!networkProxyBindingChanged && binding.clientId !== client.getInstanceId()) await assertResumeInputAllowed();
675
+ if (!params.incognito && isCodexAppServerClientRuntimeLive(client)) {
676
+ const ownership = await consumeCodexAppServerLiveThread(client, threadId);
677
+ if (ownership) {
678
+ liveThreadOwnership = {
679
+ client,
680
+ threadId,
681
+ ownership
682
+ };
683
+ ownsNativeSubscription = true;
684
+ }
685
+ }
686
+ if (networkProxyBindingChanged) {
687
+ const response = assertCodexThreadStartResponse(await withLeasedCodexAppServerClientStartSelectionRetry({
688
+ lease: clientLease,
689
+ options: clientOptions,
690
+ run: async (requestClient, requestOptions) => {
691
+ const threadRequest = await buildConversationThreadRequestForClient(requestClient, threadRequestRuntime, serviceTier, requestOptions);
692
+ return await requestClient.request("thread/start", {
693
+ ...threadRequest,
694
+ developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
695
+ experimentalRawEvents: true,
696
+ ...params.incognito ? { ephemeral: true } : {}
697
+ }, requestOptions());
698
+ },
699
+ onClientChange: (nextClient) => {
700
+ client = nextClient;
701
+ }
702
+ }));
703
+ threadId = response.thread.id;
704
+ ownsNativeSubscription = true;
705
+ assertCodexThreadAcceptsDirectInput(response.thread);
706
+ if (liveThreadOwnership && (liveThreadOwnership.threadId !== threadId || liveThreadOwnership.client !== client)) {
707
+ const previousOwnership = liveThreadOwnership;
708
+ try {
709
+ await previousOwnership.ownership.release(previousOwnership.threadId);
710
+ } catch (error) {
711
+ if (!(isCodexAppServerClientRuntimeLive(previousOwnership.client) && await retainCodexAppServerBindingSubscription(previousOwnership.client, previousOwnership.threadId, previousOwnership.ownership).catch(() => false))) await closeCodexStartupClientBestEffort(previousOwnership.client);
712
+ liveThreadOwnership = void 0;
713
+ throw error;
714
+ }
715
+ liveThreadOwnership = void 0;
716
+ } else if (binding.threadId !== threadId) await releaseCodexAppServerBindingSubscription(binding);
717
+ if (!await params.bindingStore.mutate(identity, {
718
+ kind: "set",
719
+ binding: {
720
+ threadId,
721
+ clientId: client.getInstanceId(),
722
+ cwd: response.thread.cwd ?? workspaceDir,
723
+ authProfileId: binding.authProfileId,
724
+ model: response.model ?? modelSelection?.model ?? binding.model,
725
+ modelProvider: normalizeCodexAppServerBindingModelProvider({
726
+ authProfileId: binding.authProfileId,
727
+ modelProvider: response.modelProvider ?? modelSelection?.modelProvider ?? binding.modelProvider,
728
+ ...agentLookup
729
+ }),
730
+ serviceTier: serviceTier ?? void 0,
731
+ networkProxyProfileName: modelScopedRuntime.networkProxy?.profileName,
732
+ networkProxyConfigFingerprint: modelScopedRuntime.networkProxy?.configFingerprint,
733
+ conversationStartId: binding.conversationStartId,
734
+ conversationSourceTransferComplete: binding.conversationSourceTransferComplete,
735
+ historyCoveredThrough: binding.historyCoveredThrough
736
+ }
737
+ })) throw new Error("Codex conversation binding changed while rotating its thread.");
738
+ useStickyNetworkProfile = modelScopedRuntime.networkProxy !== void 0;
739
+ } else if (binding.clientId !== client.getInstanceId() || isCodexAppServerClientRuntimeLive(client) && !params.incognito && !liveThreadOwnership) {
740
+ if (binding.clientId === client.getInstanceId()) await assertResumeInputAllowed();
741
+ const response = await withLeasedCodexAppServerClientStartSelectionRetry({
742
+ lease: clientLease,
743
+ options: clientOptions,
744
+ run: async (requestClient, requestOptions) => {
745
+ const threadRequest = await buildConversationThreadRequestForClient(requestClient, threadRequestRuntime, serviceTier, requestOptions);
746
+ return await resumeCodexAppServerThread({
747
+ client: requestClient,
748
+ onSubscriptionReleased: () => {
749
+ isolatedSubscriptionClient = requestClient;
750
+ },
751
+ abandonClient: async () => {
752
+ await closeCodexStartupClientBestEffort(requestClient);
753
+ isolatedSubscriptionClient = requestClient;
754
+ },
755
+ request: {
756
+ threadId,
757
+ ...threadRequest
758
+ },
759
+ requestResume: (request) => requestClient.request("thread/resume", request, requestOptions())
760
+ });
761
+ },
762
+ onClientChange: (nextClient) => {
763
+ client = nextClient;
764
+ }
765
+ });
766
+ threadId = response.thread.id;
767
+ ownsNativeSubscription = true;
768
+ assertCodexThreadAcceptsDirectInput(response.thread);
769
+ if (!isSameCodexAppServerThreadOwner(binding, {
770
+ threadId,
771
+ clientId: client.getInstanceId()
772
+ })) await releaseCodexAppServerBindingSubscription(binding);
773
+ if (!await params.bindingStore.mutate(identity, {
774
+ kind: "patch",
775
+ threadId: binding.threadId,
776
+ patch: {
777
+ clientId: client.getInstanceId(),
778
+ cwd: response.thread.cwd ?? binding.cwd,
779
+ model: response.model ?? modelSelection?.model ?? binding.model,
780
+ modelProvider: normalizeCodexAppServerBindingModelProvider({
781
+ authProfileId: binding.authProfileId,
782
+ modelProvider: response.modelProvider ?? modelSelection?.modelProvider ?? binding.modelProvider,
783
+ ...agentLookup
784
+ })
785
+ }
786
+ })) throw new Error("Codex conversation binding changed while resuming on a new client.");
787
+ }
788
+ const turnCollector = createCodexConversationTurnCollector(threadId);
789
+ turnRoute = getCodexAppServerTurnRouter(client).reserveThread({
790
+ threadId,
791
+ onNotification: turnCollector.handleNotification
792
+ });
793
+ turnRoute.armTurn();
794
+ activeTurnId = (await client.request("turn/start", {
795
+ threadId,
796
+ input: buildCodexConversationTurnInput({
797
+ prompt: params.prompt,
798
+ event: params.event
799
+ }),
800
+ cwd: workspaceDir,
801
+ ...sessionRoot ? { runtimeWorkspaceRoots: [sessionRoot] } : {},
802
+ approvalPolicy,
803
+ approvalsReviewer: modelScopedRuntime.approvalsReviewer,
804
+ ...useStickyNetworkProfile ? {} : { sandboxPolicy: codexSandboxPolicyForTurn(sandbox, sessionRoot ?? workspaceDir) },
805
+ ...modelSelection?.model ? { model: modelSelection.model } : {},
806
+ personality: CODEX_NATIVE_PERSONALITY_NONE,
807
+ ...serviceTier ? { serviceTier } : {}
808
+ }, { timeoutMs: runtime.requestTimeoutMs })).turn.id;
809
+ activeTurnCleanup = trackCodexConversationActiveTurn({
810
+ identity,
811
+ client,
812
+ threadId,
813
+ turnId: activeTurnId
814
+ });
815
+ turnCollector.setTurnId(activeTurnId);
816
+ await turnRoute.bindTurn(activeTurnId);
817
+ const replyText = (await turnCollector.wait({ timeoutMs: params.timeoutMs ?? DEFAULT_BOUND_TURN_TIMEOUT_MS })).replyText.trim();
818
+ turnSucceeded = true;
819
+ return { reply: { text: replyText || "Codex completed without a text reply." } };
820
+ } catch (error) {
821
+ if (isCodexAppServerOverloadError(error) && error.method === "thread/resume") throw error;
822
+ if (error instanceof CodexThreadDirectInputError) {
823
+ if (params.incognito && ownsNativeSubscription) {
824
+ if (!await unsubscribeCodexThreadBestEffort(client, {
825
+ threadId,
826
+ timeoutMs: 5e3
827
+ })) await retireUnsafeCodexTurnClientBestEffort(client, "parent-owned thread unsubscribe");
828
+ }
829
+ throw error;
830
+ }
831
+ if (error instanceof CodexConversationTurnTimeoutError && activeTurnId || turnRoute && isCodexAppServerIndeterminateRequestCancellationError(error)) {
832
+ if (!await interruptCodexTurnAndWaitBestEffort(client, {
833
+ threadId,
834
+ turnId: activeTurnId ?? ""
835
+ })) {
836
+ await retireUnsafeCodexTurnClientBestEffort(client, "turn interrupt");
837
+ isolatedSubscriptionClient = client;
838
+ }
839
+ }
840
+ if (params.incognito) {
841
+ if (await params.bindingStore.mutate(identity, {
842
+ kind: "clear",
843
+ threadId
844
+ }) && isolatedSubscriptionClient !== client) {
845
+ if (!await unsubscribeCodexThreadBestEffort(client, {
846
+ threadId,
847
+ timeoutMs: 5e3
848
+ })) await retireUnsafeCodexTurnClientBestEffort(client, "thread unsubscribe");
849
+ }
850
+ }
851
+ throw error;
852
+ } finally {
853
+ activeTurnCleanup();
854
+ turnRoute?.release();
855
+ try {
856
+ if (ownsNativeSubscription && isolatedSubscriptionClient !== client && !params.incognito && isCodexAppServerClientRuntimeLive(client)) {
857
+ const currentLiveThreadOwnership = liveThreadOwnership?.client === client && liveThreadOwnership.threadId === threadId ? liveThreadOwnership.ownership : void 0;
858
+ let retained = false;
859
+ if (turnSucceeded) retained = await params.bindingStore.withLease(identity, async () => {
860
+ const latest = params.bindingStore.read(identity);
861
+ if (latest?.threadId !== threadId || latest.clientId !== client.getInstanceId()) return false;
862
+ return await retainCodexAppServerBindingSubscription(client, threadId, currentLiveThreadOwnership);
863
+ });
864
+ if (!retained) {
865
+ if (!(currentLiveThreadOwnership ? await currentLiveThreadOwnership.release(threadId).then(() => true) : await unsubscribeCodexThreadBestEffort(client, {
866
+ threadId,
867
+ timeoutMs: 5e3
868
+ }))) await closeCodexStartupClientBestEffort(client);
869
+ }
870
+ }
871
+ } catch (error) {
872
+ embeddedAgentLog.warn("codex conversation subscription cleanup failed", {
873
+ threadId,
874
+ reason: formatErrorMessage(error)
875
+ });
876
+ await closeCodexStartupClientBestEffort(client);
877
+ } finally {
878
+ releaseCodexAppServerClientLease(clientLease);
879
+ }
880
+ }
881
+ }
882
+ });
883
+ }
884
+ async function runBoundTurnWithMissingThreadRecovery(params) {
885
+ await prepareCodexConversationBinding(params);
886
+ try {
887
+ return await runBoundTurn(params);
888
+ } catch (error) {
889
+ if (!isCodexThreadNotFoundError(error)) throw error;
890
+ await prepareCodexConversationBinding(params, { forceNew: true });
891
+ return await runBoundTurn(params);
892
+ }
893
+ }
894
+ function isCodexThreadNotFoundError(error) {
895
+ if (isCodexAppServerOverloadError(error) || isCodexAppServerUnsafeSubscriptionError(error)) return false;
896
+ const message = formatErrorMessage(error);
897
+ return /\bthread not found:/iu.test(message) || /\bbound Codex conversation has no thread binding\b/u.test(message);
898
+ }
899
+ //#endregion
900
+ export { runBoundTurnWithMissingThreadRecovery };