@openclaw/codex 2026.7.2-beta.5 → 2026.8.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/{app-server-policy-BmDgseo_.js → app-server-policy-zcBDc1JU.js} +1 -1
- package/dist/attempt-client-cleanup-DMLXUfa8.js +735 -0
- package/dist/binding-connection-I5Bo-AsX.js +33 -0
- package/dist/{bounded-turn-FEbUXoYT.js → bounded-turn-qgZmzy_4.js} +71 -63
- package/dist/{capabilities-BHRSI6Cn.js → capabilities-CybGI1oz.js} +8 -2
- package/dist/cli-metadata.js +1 -1
- package/dist/{command-formatters-ico4ZFnk.js → command-formatters-B1AEz8ZA.js} +18 -18
- package/dist/{command-handlers-CRmnwI2N.js → command-handlers-Db8pu_ds.js} +484 -95
- package/dist/{compact-dEN49UH6.js → compact-LspQaGfl.js} +213 -199
- package/dist/{conversation-binding-data-BHRpYbX3.js → conversation-binding-data-CW2GuupV.js} +6 -10
- package/dist/doctor-contract-api.js +40 -43
- package/dist/{dynamic-tools-LIt9ojep.js → dynamic-tools-BVdyWkUD.js} +2984 -1221
- package/dist/effective-mcp-catalog-DIIhAtOZ.js +119 -0
- package/dist/{event-projector-assistant-message-DTgnPPVF.js → event-projector-assistant-message-BwxV4d6N.js} +11 -23
- package/dist/{attempt-notifications-CMR2_fmR.js → event-projector-usage-e5huIQWu.js} +113 -29
- package/dist/harness.js +83 -13
- package/dist/image-payload-sanitizer-CnfoUPCE.js +65 -0
- package/dist/index.js +703 -329
- package/dist/isolated-completion-C58NbL40.js +75 -0
- package/dist/local-runtime-attribution-B_2mnqhb.js +19 -0
- package/dist/media-understanding-provider.js +5 -5
- package/dist/{models-BA01NEez.js → models-BFlpn1Ex.js} +12 -17
- package/dist/native-subagent-monitor-DyTGmTUx.js +3111 -0
- package/dist/{plugin-app-cache-key-DSXX-rXO.js → plugin-app-cache-key-C3H2CVp6.js} +135 -42
- package/dist/plugin-marketplace-discovery-CzrGJdCR.js +385 -0
- package/dist/{computer-use-BjGylO-E.js → plugin-metadata-cache-CNJPj4_b.js} +211 -113
- package/dist/protocol-validators-B4F7L8Rf.js +2346 -0
- package/dist/{rate-limits-S_PXs0ul.js → rate-limits-DZXmOL8-.js} +17 -35
- package/dist/{request-CXpPennK.js → request-BRl5_UfX.js} +4 -4
- package/dist/{run-attempt-C5GTPJOE.js → run-attempt-P8BdxC1I.js} +1996 -5728
- package/dist/{runtime-artifact-C2ITXdhM.js → runtime-artifact-BVSPdd6g.js} +91 -40
- package/dist/{sandbox-guard-D1Wky__S.js → sandbox-guard-Cmwc9eIk.js} +10 -13
- package/dist/{session-binding-DlVQ-Pxs.js → session-binding-DPxIvMgE.js} +305 -271
- package/dist/{session-catalog-CP2oCcEb.js → session-catalog-xwNi_SDM.js} +418 -144
- package/dist/{session-cli-CqZ0Fp2a.js → session-cli-7hgXZDV7.js} +1 -1
- package/dist/session-retirement-DoMMJFKm.js +36 -0
- package/dist/{session-upstream-marker-QE-m5QFG.js → session-upstream-marker-CrIFX06J.js} +1 -1
- package/dist/{settled-turn-finalizer-CA_2QXIE.js → settled-turn-finalizer-BRWBO4MO.js} +36 -15
- package/dist/{shared-client-BVKE40c-.js → shared-client-kIBbGjk4.js} +721 -240
- package/dist/{side-question-CO12sHxo.js → side-question-9HAL26l9.js} +102 -53
- package/dist/{thread-lifecycle-DRg_FdzC.js → thread-lifecycle-Bu1BTDXr.js} +1355 -660
- package/dist/thread-ownership-6kOQHlyC.js +92 -0
- package/dist/thread-resume-CPrJNQVM.js +30 -0
- package/dist/{transcript-mirror-uKA-A4tB.js → transcript-mirror-B9gGgnJS.js} +202 -182
- package/dist/{transport-stdio-DXgGZ7Ib.js → transport-stdio-DIImwDVS.js} +11 -0
- package/dist/upstream-prompt-provenance-ClRBije0.js +41 -0
- package/dist/{upstream-session-fork-_Vrz4WJr.js → upstream-session-fork-CE-_LdnP.js} +5 -5
- package/dist/{usage-UpVbdX5G.js → usage-CHlV0e-o.js} +3 -3
- package/dist/{web-search-provider.runtime-CaQMYDbp.js → web-search-provider.runtime-DRw2Vl42.js} +8 -9
- package/openclaw.plugin.json +18 -3
- package/package.json +6 -7
- package/dist/attempt-client-cleanup-Ia52yRWc.js +0 -127
- package/dist/conversation-control-DE7d9DOj.js +0 -287
- package/dist/event-projector-usage-tvgb1NCX.js +0 -69
- package/dist/notification-correlation-Bj_Ak_jI.js +0 -67
- package/dist/protocol-validators-Dm4eXZmt.js +0 -6337
- package/dist/realtime-voice-api-BPcA1uN9.js +0 -494
- package/dist/realtime-voice-api.js +0 -2
|
@@ -1,494 +0,0 @@
|
|
|
1
|
-
import { a as getLeasedSharedCodexAppServerClient, l as releaseLeasedSharedCodexAppServerClient, o as getSharedCodexAppServerClient } from "./shared-client-BVKE40c-.js";
|
|
2
|
-
import { T as readCodexPluginConfig, p as resolveCodexAppServerRuntimeOptions } from "./session-binding-DlVQ-Pxs.js";
|
|
3
|
-
import { n as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, u as unsubscribeCodexThreadBestEffort } from "./attempt-client-cleanup-Ia52yRWc.js";
|
|
4
|
-
import { i as assertCodexThreadStartResponse } from "./protocol-validators-Dm4eXZmt.js";
|
|
5
|
-
import { randomBytes } from "node:crypto";
|
|
6
|
-
import { resolveAgentDir, resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
|
|
7
|
-
import { readRequestBodyWithLimit } from "openclaw/plugin-sdk/webhook-request-guards";
|
|
8
|
-
//#region extensions/codex/src/realtime-browser-session.ts
|
|
9
|
-
const CODEX_REALTIME_OFFER_PATH = "/plugins/codex/realtime/calls";
|
|
10
|
-
const CODEX_REALTIME_PENDING_TTL_MS = 6e4;
|
|
11
|
-
const CODEX_REALTIME_SESSION_TTL_MS = 30 * 6e4;
|
|
12
|
-
const CODEX_REALTIME_MAX_SESSIONS = 8;
|
|
13
|
-
const CODEX_REALTIME_MAX_SDP_BYTES = 256 * 1024;
|
|
14
|
-
const CODEX_REALTIME_START_TIMEOUT_MS = 6e4;
|
|
15
|
-
const CODEX_REALTIME_PROBE_COOLDOWN_MS = 1e3;
|
|
16
|
-
function createResponseDeliveryWaiter(res, onDelivered) {
|
|
17
|
-
let settle;
|
|
18
|
-
const result = new Promise((resolve) => {
|
|
19
|
-
settle = (delivered) => {
|
|
20
|
-
res.removeListener("finish", onFinish);
|
|
21
|
-
res.removeListener("close", onClose);
|
|
22
|
-
resolve(delivered);
|
|
23
|
-
};
|
|
24
|
-
});
|
|
25
|
-
const onFinish = () => {
|
|
26
|
-
onDelivered();
|
|
27
|
-
settle(true);
|
|
28
|
-
};
|
|
29
|
-
const onClose = () => settle(false);
|
|
30
|
-
res.once("finish", onFinish);
|
|
31
|
-
res.once("close", onClose);
|
|
32
|
-
return {
|
|
33
|
-
result,
|
|
34
|
-
cancel: () => settle(false)
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
function respondText(res, statusCode, body) {
|
|
38
|
-
res.statusCode = statusCode;
|
|
39
|
-
res.setHeader("cache-control", "no-store");
|
|
40
|
-
res.setHeader("content-type", "text/plain; charset=utf-8");
|
|
41
|
-
res.setHeader("x-content-type-options", "nosniff");
|
|
42
|
-
res.end(body);
|
|
43
|
-
}
|
|
44
|
-
function resolveConfiguredControlUiOrigin(req, cfg) {
|
|
45
|
-
const rawOrigin = typeof req.headers.origin === "string" ? req.headers.origin.trim() : "";
|
|
46
|
-
if (!rawOrigin) return;
|
|
47
|
-
let origin;
|
|
48
|
-
try {
|
|
49
|
-
const parsed = new URL(rawOrigin);
|
|
50
|
-
if (parsed.origin !== rawOrigin || parsed.username || parsed.password) return;
|
|
51
|
-
origin = parsed.origin;
|
|
52
|
-
} catch {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
return (cfg?.gateway?.controlUi?.allowedOrigins ?? []).some((candidate) => {
|
|
56
|
-
const normalized = candidate.trim().toLowerCase();
|
|
57
|
-
return normalized === "*" || normalized === origin;
|
|
58
|
-
}) ? origin : void 0;
|
|
59
|
-
}
|
|
60
|
-
function applyRealtimeOfferCorsHeaders(req, res, cfg) {
|
|
61
|
-
if (!req.headers.origin) return true;
|
|
62
|
-
const origin = resolveConfiguredControlUiOrigin(req, cfg);
|
|
63
|
-
if (!origin) return false;
|
|
64
|
-
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
65
|
-
res.setHeader("Vary", "Origin");
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
function readBearerToken(req) {
|
|
69
|
-
return ((req.headers.authorization?.trim())?.match(/^Bearer\s+([^\s]+)$/i))?.[1];
|
|
70
|
-
}
|
|
71
|
-
function readNotificationParams(value) {
|
|
72
|
-
return value && typeof value === "object" ? value : {};
|
|
73
|
-
}
|
|
74
|
-
function buildCodexRealtimeThreadStartParams(params) {
|
|
75
|
-
return {
|
|
76
|
-
cwd: params.cwd,
|
|
77
|
-
ephemeral: true,
|
|
78
|
-
approvalPolicy: "never",
|
|
79
|
-
sandbox: "read-only",
|
|
80
|
-
config: { "features.realtime_conversation": true }
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
function buildCodexRealtimeStartParams(params) {
|
|
84
|
-
const initialItems = [...params.developerInstructions ? [{
|
|
85
|
-
role: "developer",
|
|
86
|
-
text: params.developerInstructions
|
|
87
|
-
}] : [], ...params.initialItems ?? []];
|
|
88
|
-
return {
|
|
89
|
-
threadId: params.threadId,
|
|
90
|
-
outputModality: "audio",
|
|
91
|
-
transport: {
|
|
92
|
-
type: "webrtc",
|
|
93
|
-
sdp: params.sdp
|
|
94
|
-
},
|
|
95
|
-
version: "v3",
|
|
96
|
-
includeStartupContext: true,
|
|
97
|
-
...params.voice ? { voice: params.voice } : {},
|
|
98
|
-
...initialItems.length > 0 ? { initialItems } : {}
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
function waitForRealtimeSdpAnswer(answerPromise, signal) {
|
|
102
|
-
return new Promise((resolve, reject) => {
|
|
103
|
-
let settled = false;
|
|
104
|
-
const finish = (result) => {
|
|
105
|
-
if (settled) return;
|
|
106
|
-
settled = true;
|
|
107
|
-
clearTimeout(timeout);
|
|
108
|
-
signal.removeEventListener("abort", onAbort);
|
|
109
|
-
if ("answer" in result) resolve(result.answer);
|
|
110
|
-
else reject(result.error);
|
|
111
|
-
};
|
|
112
|
-
const onAbort = () => finish({ error: signal.reason instanceof Error ? signal.reason : /* @__PURE__ */ new Error("Codex realtime session stopped during startup") });
|
|
113
|
-
const timeout = setTimeout(() => finish({ error: /* @__PURE__ */ new Error("Codex realtime SDP answer timed out") }), CODEX_REALTIME_START_TIMEOUT_MS);
|
|
114
|
-
timeout.unref?.();
|
|
115
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
116
|
-
if (signal.aborted) {
|
|
117
|
-
onAbort();
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
answerPromise.then((answer) => finish({ answer }), (error) => finish({ error: error instanceof Error ? error : /* @__PURE__ */ new Error("Codex realtime failed") }));
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
function createCodexRealtimeBrowserSessionBroker(params) {
|
|
124
|
-
const pendingOffers = /* @__PURE__ */ new Map();
|
|
125
|
-
const reservations = /* @__PURE__ */ new Set();
|
|
126
|
-
const activeSessions = /* @__PURE__ */ new Set();
|
|
127
|
-
const inFlightHandlers = /* @__PURE__ */ new Set();
|
|
128
|
-
const readyClients = /* @__PURE__ */ new Set();
|
|
129
|
-
const shutdownController = new AbortController();
|
|
130
|
-
let cleanedUp = false;
|
|
131
|
-
let probePromise;
|
|
132
|
-
let nextProbeAt = 0;
|
|
133
|
-
const closeSession = async (session) => {
|
|
134
|
-
if (!activeSessions.delete(session)) return;
|
|
135
|
-
clearTimeout(session.timer);
|
|
136
|
-
reservations.delete(session.reservationToken);
|
|
137
|
-
session.disposeNotificationHandler();
|
|
138
|
-
try {
|
|
139
|
-
await session.client.request("thread/realtime/stop", { threadId: session.threadId }, { timeoutMs: 2e3 });
|
|
140
|
-
} catch {}
|
|
141
|
-
await unsubscribeCodexThreadBestEffort(session.client, {
|
|
142
|
-
threadId: session.threadId,
|
|
143
|
-
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
|
|
144
|
-
});
|
|
145
|
-
releaseLeasedSharedCodexAppServerClient(session.client);
|
|
146
|
-
};
|
|
147
|
-
const resolveSubscriptionClientOptions = (request) => {
|
|
148
|
-
const pluginConfig = readCodexPluginConfig(params.getPluginConfig());
|
|
149
|
-
const agentDir = request.agentId && request.cfg ? resolveAgentDir(request.cfg, request.agentId) : void 0;
|
|
150
|
-
return {
|
|
151
|
-
startOptions: resolveCodexAppServerRuntimeOptions({
|
|
152
|
-
pluginConfig,
|
|
153
|
-
config: request.cfg,
|
|
154
|
-
agentDir
|
|
155
|
-
}).start,
|
|
156
|
-
pluginConfig,
|
|
157
|
-
config: request.cfg,
|
|
158
|
-
agentDir,
|
|
159
|
-
authRequirement: "subscription",
|
|
160
|
-
timeoutMs: CODEX_REALTIME_START_TIMEOUT_MS
|
|
161
|
-
};
|
|
162
|
-
};
|
|
163
|
-
const ensureSubscriptionRuntime = async (request) => {
|
|
164
|
-
const client = await getSharedCodexAppServerClient({
|
|
165
|
-
...resolveSubscriptionClientOptions(request),
|
|
166
|
-
abandonSignal: shutdownController.signal
|
|
167
|
-
});
|
|
168
|
-
if (!readyClients.has(client)) {
|
|
169
|
-
readyClients.add(client);
|
|
170
|
-
client.addCloseHandler((closedClient) => {
|
|
171
|
-
readyClients.delete(closedClient);
|
|
172
|
-
Promise.allSettled([...activeSessions].filter((session) => session.client === closedClient).map((session) => closeSession(session)));
|
|
173
|
-
nextProbeAt = 0;
|
|
174
|
-
requestProbe();
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
const runProbe = () => {
|
|
179
|
-
if (probePromise) return probePromise;
|
|
180
|
-
const cfg = params.getConfig();
|
|
181
|
-
nextProbeAt = Date.now() + CODEX_REALTIME_PROBE_COOLDOWN_MS;
|
|
182
|
-
const running = ensureSubscriptionRuntime({
|
|
183
|
-
cfg,
|
|
184
|
-
...cfg ? { agentId: resolveDefaultAgentId(cfg) } : {}
|
|
185
|
-
}).finally(() => {
|
|
186
|
-
if (probePromise === running) probePromise = void 0;
|
|
187
|
-
});
|
|
188
|
-
probePromise = running;
|
|
189
|
-
return running;
|
|
190
|
-
};
|
|
191
|
-
function requestProbe() {
|
|
192
|
-
if (cleanedUp || shutdownController.signal.aborted || probePromise || Date.now() < nextProbeAt) return;
|
|
193
|
-
runProbe().catch(() => void 0);
|
|
194
|
-
}
|
|
195
|
-
const warmup = async () => {
|
|
196
|
-
await runProbe();
|
|
197
|
-
};
|
|
198
|
-
const prunePendingOffers = () => {
|
|
199
|
-
const now = Date.now();
|
|
200
|
-
for (const [token, offer] of pendingOffers) if (offer.expiresAt <= now) {
|
|
201
|
-
pendingOffers.delete(token);
|
|
202
|
-
reservations.delete(token);
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
const broker = {
|
|
206
|
-
capabilities: {
|
|
207
|
-
transports: ["webrtc"],
|
|
208
|
-
handlesAgentConsult: true,
|
|
209
|
-
supportsToolCalls: false,
|
|
210
|
-
supportsVideoFrames: false
|
|
211
|
-
},
|
|
212
|
-
isConfigured: () => {
|
|
213
|
-
if (cleanedUp || shutdownController.signal.aborted) return false;
|
|
214
|
-
if (readyClients.size > 0) return true;
|
|
215
|
-
requestProbe();
|
|
216
|
-
return false;
|
|
217
|
-
},
|
|
218
|
-
createBrowserSession: async (request) => {
|
|
219
|
-
if (cleanedUp || shutdownController.signal.aborted) throw new Error("Codex OAuth realtime is stopping; restart Gateway and try again");
|
|
220
|
-
await ensureSubscriptionRuntime(request);
|
|
221
|
-
if (cleanedUp || shutdownController.signal.aborted) throw new Error("Codex OAuth realtime is stopping; restart Gateway and try again");
|
|
222
|
-
prunePendingOffers();
|
|
223
|
-
if (reservations.size >= CODEX_REALTIME_MAX_SESSIONS) throw new Error("Too many concurrent Codex OAuth realtime sessions; try again in a minute");
|
|
224
|
-
const token = randomBytes(32).toString("base64url");
|
|
225
|
-
const expiresAt = Date.now() + CODEX_REALTIME_PENDING_TTL_MS;
|
|
226
|
-
reservations.add(token);
|
|
227
|
-
pendingOffers.set(token, {
|
|
228
|
-
expiresAt,
|
|
229
|
-
request
|
|
230
|
-
});
|
|
231
|
-
const voice = request.voice?.trim() || void 0;
|
|
232
|
-
return {
|
|
233
|
-
provider: "openai",
|
|
234
|
-
transport: "webrtc",
|
|
235
|
-
clientSecret: token,
|
|
236
|
-
offerUrl: CODEX_REALTIME_OFFER_PATH,
|
|
237
|
-
...voice ? { voice } : {},
|
|
238
|
-
expiresAt
|
|
239
|
-
};
|
|
240
|
-
},
|
|
241
|
-
cancelBrowserSession: (session) => {
|
|
242
|
-
if (session.transport !== "webrtc") return;
|
|
243
|
-
pendingOffers.delete(session.clientSecret);
|
|
244
|
-
reservations.delete(session.clientSecret);
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
const handleOffer = async (req, res) => {
|
|
248
|
-
const corsAllowed = applyRealtimeOfferCorsHeaders(req, res, params.getConfig());
|
|
249
|
-
if (req.method === "OPTIONS") {
|
|
250
|
-
if (!corsAllowed) {
|
|
251
|
-
respondText(res, 403, "Origin not allowed");
|
|
252
|
-
return true;
|
|
253
|
-
}
|
|
254
|
-
res.statusCode = 204;
|
|
255
|
-
res.setHeader("cache-control", "no-store");
|
|
256
|
-
res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
|
|
257
|
-
res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
|
|
258
|
-
res.setHeader("Vary", "Origin, Access-Control-Request-Method, Access-Control-Request-Headers");
|
|
259
|
-
if (req.headers["access-control-request-private-network"] === "true") res.setHeader("Access-Control-Allow-Private-Network", "true");
|
|
260
|
-
res.setHeader("Access-Control-Max-Age", "600");
|
|
261
|
-
res.end();
|
|
262
|
-
return true;
|
|
263
|
-
}
|
|
264
|
-
if (req.method !== "POST") {
|
|
265
|
-
respondText(res, 405, "Method not allowed");
|
|
266
|
-
return true;
|
|
267
|
-
}
|
|
268
|
-
if (!req.headers["content-type"]?.toLowerCase().startsWith("application/sdp")) {
|
|
269
|
-
respondText(res, 415, "Expected application/sdp");
|
|
270
|
-
return true;
|
|
271
|
-
}
|
|
272
|
-
prunePendingOffers();
|
|
273
|
-
const token = readBearerToken(req);
|
|
274
|
-
const offer = token ? pendingOffers.get(token) : void 0;
|
|
275
|
-
if (!token || !offer || offer.expiresAt <= Date.now()) {
|
|
276
|
-
respondText(res, 401, "Invalid or expired realtime session token");
|
|
277
|
-
return true;
|
|
278
|
-
}
|
|
279
|
-
pendingOffers.delete(token);
|
|
280
|
-
const requestController = new AbortController();
|
|
281
|
-
const abortFromBrowser = () => {
|
|
282
|
-
requestController.abort(/* @__PURE__ */ new Error("Browser realtime offer request closed"));
|
|
283
|
-
};
|
|
284
|
-
req.once("aborted", abortFromBrowser);
|
|
285
|
-
res.once("close", abortFromBrowser);
|
|
286
|
-
const detachBrowserAbort = () => {
|
|
287
|
-
req.removeListener("aborted", abortFromBrowser);
|
|
288
|
-
res.removeListener("close", abortFromBrowser);
|
|
289
|
-
};
|
|
290
|
-
const lifecycleSignal = AbortSignal.any([shutdownController.signal, requestController.signal]);
|
|
291
|
-
let client;
|
|
292
|
-
let session;
|
|
293
|
-
let threadId;
|
|
294
|
-
let reservationTransferred = false;
|
|
295
|
-
let responseDeliveryWaiter;
|
|
296
|
-
try {
|
|
297
|
-
const sdp = await readRequestBodyWithLimit(req, {
|
|
298
|
-
maxBytes: CODEX_REALTIME_MAX_SDP_BYTES,
|
|
299
|
-
timeoutMs: 15e3
|
|
300
|
-
});
|
|
301
|
-
if (!sdp.trim()) {
|
|
302
|
-
respondText(res, 400, "SDP offer is required");
|
|
303
|
-
return true;
|
|
304
|
-
}
|
|
305
|
-
if (lifecycleSignal.aborted) throw new Error("Codex realtime session stopped during startup");
|
|
306
|
-
client = await getLeasedSharedCodexAppServerClient({
|
|
307
|
-
...resolveSubscriptionClientOptions(offer.request),
|
|
308
|
-
abandonSignal: lifecycleSignal
|
|
309
|
-
});
|
|
310
|
-
threadId = assertCodexThreadStartResponse(await client.request("thread/start", buildCodexRealtimeThreadStartParams({ cwd: offer.request.workspaceDir ?? process.cwd() }), {
|
|
311
|
-
timeoutMs: CODEX_REALTIME_START_TIMEOUT_MS,
|
|
312
|
-
signal: lifecycleSignal
|
|
313
|
-
})).thread.id;
|
|
314
|
-
if (lifecycleSignal.aborted) throw new Error("Codex realtime session stopped during startup");
|
|
315
|
-
let resolveSdp;
|
|
316
|
-
let rejectSdp;
|
|
317
|
-
const answerPromise = new Promise((resolve, reject) => {
|
|
318
|
-
resolveSdp = resolve;
|
|
319
|
-
rejectSdp = reject;
|
|
320
|
-
});
|
|
321
|
-
const startupController = new AbortController();
|
|
322
|
-
const startupSignal = AbortSignal.any([lifecycleSignal, startupController.signal]);
|
|
323
|
-
let startupComplete = false;
|
|
324
|
-
const stopStartup = (error) => {
|
|
325
|
-
if (!startupController.signal.aborted) startupController.abort(error);
|
|
326
|
-
};
|
|
327
|
-
const closeAfterStartup = () => {
|
|
328
|
-
if (startupComplete && session) closeSession(session);
|
|
329
|
-
};
|
|
330
|
-
const disposeNotificationHandler = client.addNotificationHandler((notification) => {
|
|
331
|
-
const notificationParams = readNotificationParams(notification.params);
|
|
332
|
-
if (notificationParams.threadId !== threadId) return;
|
|
333
|
-
if (notification.method === "thread/realtime/sdp") {
|
|
334
|
-
if (typeof notificationParams.sdp === "string" && notificationParams.sdp.trim()) resolveSdp(notificationParams.sdp);
|
|
335
|
-
else rejectSdp(/* @__PURE__ */ new Error("Codex returned an empty realtime SDP answer"));
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
if (notification.method === "thread/realtime/error") {
|
|
339
|
-
const error = new Error(typeof notificationParams.message === "string" ? notificationParams.message : "Codex realtime session failed");
|
|
340
|
-
rejectSdp(error);
|
|
341
|
-
stopStartup(error);
|
|
342
|
-
closeAfterStartup();
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
if (notification.method === "thread/realtime/closed") {
|
|
346
|
-
const error = /* @__PURE__ */ new Error("Codex realtime session closed before returning an SDP answer");
|
|
347
|
-
rejectSdp(error);
|
|
348
|
-
stopStartup(error);
|
|
349
|
-
closeAfterStartup();
|
|
350
|
-
}
|
|
351
|
-
});
|
|
352
|
-
const timer = setTimeout(() => {
|
|
353
|
-
if (session) closeSession(session);
|
|
354
|
-
}, CODEX_REALTIME_SESSION_TTL_MS);
|
|
355
|
-
timer.unref?.();
|
|
356
|
-
session = {
|
|
357
|
-
client,
|
|
358
|
-
reservationToken: token,
|
|
359
|
-
threadId,
|
|
360
|
-
timer,
|
|
361
|
-
disposeNotificationHandler
|
|
362
|
-
};
|
|
363
|
-
reservationTransferred = true;
|
|
364
|
-
activeSessions.add(session);
|
|
365
|
-
const answerResultPromise = waitForRealtimeSdpAnswer(answerPromise, startupSignal).then((answer) => ({
|
|
366
|
-
ok: true,
|
|
367
|
-
answer
|
|
368
|
-
}), (error) => ({
|
|
369
|
-
ok: false,
|
|
370
|
-
error: error instanceof Error ? error : /* @__PURE__ */ new Error("Codex realtime session failed")
|
|
371
|
-
}));
|
|
372
|
-
try {
|
|
373
|
-
await client.request("thread/realtime/start", buildCodexRealtimeStartParams({
|
|
374
|
-
threadId,
|
|
375
|
-
sdp,
|
|
376
|
-
developerInstructions: offer.request.instructions?.trim() || void 0,
|
|
377
|
-
voice: offer.request.voice?.trim() || void 0,
|
|
378
|
-
initialItems: offer.request.initialItems
|
|
379
|
-
}), {
|
|
380
|
-
timeoutMs: CODEX_REALTIME_START_TIMEOUT_MS,
|
|
381
|
-
signal: startupSignal
|
|
382
|
-
});
|
|
383
|
-
} catch (error) {
|
|
384
|
-
if (startupController.signal.aborted) {
|
|
385
|
-
const answerResult = await answerResultPromise;
|
|
386
|
-
if (!answerResult.ok) throw answerResult.error;
|
|
387
|
-
throw startupController.signal.reason instanceof Error ? startupController.signal.reason : /* @__PURE__ */ new Error("Codex realtime session stopped during startup");
|
|
388
|
-
}
|
|
389
|
-
stopStartup(error instanceof Error ? error : /* @__PURE__ */ new Error("Codex realtime session failed to start"));
|
|
390
|
-
throw error;
|
|
391
|
-
}
|
|
392
|
-
const answerResult = await answerResultPromise;
|
|
393
|
-
if (!answerResult.ok) throw answerResult.error;
|
|
394
|
-
if (startupSignal.aborted) throw startupSignal.reason instanceof Error ? startupSignal.reason : /* @__PURE__ */ new Error("Codex realtime session stopped during startup");
|
|
395
|
-
startupComplete = true;
|
|
396
|
-
responseDeliveryWaiter = createResponseDeliveryWaiter(res, detachBrowserAbort);
|
|
397
|
-
res.statusCode = 200;
|
|
398
|
-
res.setHeader("cache-control", "no-store");
|
|
399
|
-
res.setHeader("content-type", "application/sdp");
|
|
400
|
-
res.setHeader("x-content-type-options", "nosniff");
|
|
401
|
-
res.end(answerResult.answer);
|
|
402
|
-
const delivered = await responseDeliveryWaiter.result;
|
|
403
|
-
responseDeliveryWaiter = void 0;
|
|
404
|
-
if (!delivered || lifecycleSignal.aborted) await closeSession(session);
|
|
405
|
-
return true;
|
|
406
|
-
} catch (error) {
|
|
407
|
-
if (session) await closeSession(session);
|
|
408
|
-
else if (client) {
|
|
409
|
-
if (threadId) await unsubscribeCodexThreadBestEffort(client, {
|
|
410
|
-
threadId,
|
|
411
|
-
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
|
|
412
|
-
});
|
|
413
|
-
releaseLeasedSharedCodexAppServerClient(client);
|
|
414
|
-
}
|
|
415
|
-
if (requestController.signal.aborted) return true;
|
|
416
|
-
respondText(res, 502, error instanceof Error ? error.message : "Codex realtime session failed");
|
|
417
|
-
return true;
|
|
418
|
-
} finally {
|
|
419
|
-
responseDeliveryWaiter?.cancel();
|
|
420
|
-
detachBrowserAbort();
|
|
421
|
-
if (!reservationTransferred) reservations.delete(token);
|
|
422
|
-
}
|
|
423
|
-
};
|
|
424
|
-
const trackedHandleOffer = (req, res) => {
|
|
425
|
-
const handling = handleOffer(req, res);
|
|
426
|
-
inFlightHandlers.add(handling);
|
|
427
|
-
return handling.finally(() => {
|
|
428
|
-
inFlightHandlers.delete(handling);
|
|
429
|
-
});
|
|
430
|
-
};
|
|
431
|
-
const handler = trackedHandleOffer;
|
|
432
|
-
const cleanup = async () => {
|
|
433
|
-
if (cleanedUp) return;
|
|
434
|
-
cleanedUp = true;
|
|
435
|
-
shutdownController.abort();
|
|
436
|
-
pendingOffers.clear();
|
|
437
|
-
await Promise.all([...activeSessions].map((session) => closeSession(session)));
|
|
438
|
-
await Promise.allSettled(inFlightHandlers);
|
|
439
|
-
reservations.clear();
|
|
440
|
-
};
|
|
441
|
-
return {
|
|
442
|
-
broker,
|
|
443
|
-
handler,
|
|
444
|
-
warmup,
|
|
445
|
-
cleanup
|
|
446
|
-
};
|
|
447
|
-
}
|
|
448
|
-
//#endregion
|
|
449
|
-
//#region extensions/codex/realtime-voice-api.ts
|
|
450
|
-
/**
|
|
451
|
-
* Bundled Codex realtime integration shared with the bundled OpenAI provider.
|
|
452
|
-
*
|
|
453
|
-
* This artifact keeps the HTTP offer route and the provider fallback on one
|
|
454
|
-
* process-owned runtime without adding a general Plugin SDK registration API.
|
|
455
|
-
*/
|
|
456
|
-
const CODEX_REALTIME_GLOBAL_STATE = Symbol.for("openclaw.codex.realtime-voice.v1");
|
|
457
|
-
function getGlobalState() {
|
|
458
|
-
const root = globalThis;
|
|
459
|
-
const state = root[CODEX_REALTIME_GLOBAL_STATE] ??= {
|
|
460
|
-
version: 1,
|
|
461
|
-
sources: /* @__PURE__ */ new Map()
|
|
462
|
-
};
|
|
463
|
-
state.sources ??= /* @__PURE__ */ new Map();
|
|
464
|
-
return state;
|
|
465
|
-
}
|
|
466
|
-
function configureCodexRealtimeBrowserSession(params) {
|
|
467
|
-
const state = getGlobalState();
|
|
468
|
-
const leaseId = Symbol("codex-realtime-registration");
|
|
469
|
-
state.sources.set(leaseId, params);
|
|
470
|
-
if (state.runtime) return createRuntimeLease(state, state.runtime, leaseId);
|
|
471
|
-
const resolveCurrentSource = () => Array.from(state.sources.values()).at(-1);
|
|
472
|
-
const created = createCodexRealtimeBrowserSessionBroker({
|
|
473
|
-
getConfig: () => resolveCurrentSource()?.getConfig(),
|
|
474
|
-
getPluginConfig: () => resolveCurrentSource()?.getPluginConfig()
|
|
475
|
-
});
|
|
476
|
-
state.runtime = created;
|
|
477
|
-
return createRuntimeLease(state, created, leaseId);
|
|
478
|
-
}
|
|
479
|
-
function createRuntimeLease(state, runtime, leaseId) {
|
|
480
|
-
let released = false;
|
|
481
|
-
return {
|
|
482
|
-
...runtime,
|
|
483
|
-
cleanup: async () => {
|
|
484
|
-
if (released) return;
|
|
485
|
-
released = true;
|
|
486
|
-
state.sources.delete(leaseId);
|
|
487
|
-
if (state.runtime !== runtime || state.sources.size > 0) return;
|
|
488
|
-
state.runtime = void 0;
|
|
489
|
-
await runtime.cleanup();
|
|
490
|
-
}
|
|
491
|
-
};
|
|
492
|
-
}
|
|
493
|
-
//#endregion
|
|
494
|
-
export { CODEX_REALTIME_OFFER_PATH as n, configureCodexRealtimeBrowserSession as t };
|