@openclaw/codex 2026.5.24-beta.2 → 2026.5.25-beta.1
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/dist/{client-1sSy4p3z.js → client-SrtuTTVa.js} +10 -3
- package/dist/{client-factory-Bk6i4FnW.js → client-factory-A7DQkSLq.js} +1 -1
- package/dist/{command-handlers-CcTABuem.js → command-handlers-CLtPT37L.js} +10 -23
- package/dist/compact-acCua7zr.js +155 -0
- package/dist/{computer-use-Hdq1WgTA.js → computer-use-CVfE2Vfb.js} +2 -2
- package/dist/{config-DDMrwfJl.js → config-D6TUuyo2.js} +6 -3
- package/dist/{node-cli-sessions-9CAqnIaA.js → conversation-binding-B-GevkEb.js} +426 -434
- package/dist/{dynamic-tools-Bq717oJR.js → dynamic-tools-BXAE1U1-.js} +20 -11
- package/dist/harness.js +5 -5
- package/dist/index.js +451 -17
- package/dist/media-understanding-provider.js +7 -5
- package/dist/{models-DtGLkqMP.js → models-CC2IusQs.js} +3 -3
- package/dist/{notification-correlation-qKY_sgga.js → notification-correlation-D7tJW3FW.js} +26 -11
- package/dist/provider.js +3 -3
- package/dist/{request-D93E78SA.js → request-BO0z3vA8.js} +3 -3
- package/dist/{run-attempt-CT1N__qp.js → run-attempt-BjoI3hUJ.js} +279 -230
- package/dist/{session-binding-Bw_mfIW2.js → session-binding-CwNOIDP2.js} +1 -1
- package/dist/{shared-client-CFCUGEVs.js → shared-client-CZP1DjaN.js} +3 -4
- package/dist/{side-question-CYFMTA1O.js → side-question-Cb4COOQ9.js} +16 -11
- package/dist/test-api.js +3 -3
- package/dist/{thread-lifecycle-DGoaguJh.js → thread-lifecycle-CXMd9UgJ.js} +10 -12
- package/dist/{vision-tools-DOnxzH2y.js → vision-tools-CETeQgze.js} +16 -4
- package/npm-shrinkwrap.json +225 -238
- package/package.json +7 -7
- package/dist/compact-DnTgL6UT.js +0 -475
- package/dist/protocol-oeJQu4rs.js +0 -9
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { a as isCodexFastServiceTier, l as resolveCodexAppServerRuntimeOptions, r as codexSandboxPolicyForTurn } from "./config-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { r as
|
|
5
|
-
import {
|
|
6
|
-
import { i as
|
|
1
|
+
import { a as isCodexFastServiceTier, l as resolveCodexAppServerRuntimeOptions, r as codexSandboxPolicyForTurn } from "./config-D6TUuyo2.js";
|
|
2
|
+
import { l as isJsonObject } from "./client-SrtuTTVa.js";
|
|
3
|
+
import { t as CODEX_NATIVE_PERSONALITY_NONE } from "./thread-lifecycle-CXMd9UgJ.js";
|
|
4
|
+
import { i as readCodexAppServerBinding, n as isCodexAppServerNativeAuthProfile, o as writeCodexAppServerBinding, r as normalizeCodexAppServerBindingModelProvider, t as clearCodexAppServerBinding } from "./session-binding-CwNOIDP2.js";
|
|
5
|
+
import { r as CODEX_CONTROL_METHODS } from "./request-BO0z3vA8.js";
|
|
6
|
+
import { i as readCodexNotificationTurnId, r as readCodexNotificationThreadId, s as formatCodexDisplayText } from "./notification-correlation-D7tJW3FW.js";
|
|
7
|
+
import { i as getSharedCodexAppServerClient, u as resolveCodexAppServerAuthProfileIdForAgent } from "./shared-client-CZP1DjaN.js";
|
|
7
8
|
import { n as resolveCodexNativeExecutionBlock, r as resolveCodexNativeSandboxBlock } from "./sandbox-guard-CTnEWuor.js";
|
|
8
9
|
import os from "node:os";
|
|
10
|
+
import { asOptionalRecord, isRecord, normalizeSingleOrTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
9
11
|
import { materializeWindowsSpawnProgram, resolveWindowsSpawnProgram } from "openclaw/plugin-sdk/windows-spawn";
|
|
10
12
|
import { formatErrorMessage } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
11
13
|
import { spawn } from "node:child_process";
|
|
@@ -63,10 +65,7 @@ function readCodexConversationBindingDataRecord(data) {
|
|
|
63
65
|
};
|
|
64
66
|
}
|
|
65
67
|
function resolveCodexDefaultWorkspaceDir(pluginConfig) {
|
|
66
|
-
return readString$1(
|
|
67
|
-
}
|
|
68
|
-
function readRecord$1(value) {
|
|
69
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
68
|
+
return readString$1(asOptionalRecord(asOptionalRecord(pluginConfig)?.appServer), "defaultWorkspaceDir") ?? process.cwd();
|
|
70
69
|
}
|
|
71
70
|
function readString$1(record, key) {
|
|
72
71
|
const value = record?.[key];
|
|
@@ -317,7 +316,7 @@ function createCodexConversationTurnCollector(threadId) {
|
|
|
317
316
|
}
|
|
318
317
|
if (notification.method === "turn/completed") {
|
|
319
318
|
const turn = isJsonObject(params.turn) ? params.turn : void 0;
|
|
320
|
-
if (readString(turn, "status") === "failed") failedError = readString(
|
|
319
|
+
if (readString(turn, "status") === "failed") failedError = readString(asOptionalRecord(turn?.error), "message") ?? "codex app-server turn failed";
|
|
321
320
|
const items = Array.isArray(turn?.items) ? turn.items : [];
|
|
322
321
|
for (const item of items) {
|
|
323
322
|
if (!isJsonObject(item) || item.type !== "agentMessage") continue;
|
|
@@ -364,9 +363,6 @@ function isNotificationForTurn(params, threadId, turnId) {
|
|
|
364
363
|
function readNotificationTurnId(params) {
|
|
365
364
|
return readCodexNotificationTurnId(params);
|
|
366
365
|
}
|
|
367
|
-
function readRecord(value) {
|
|
368
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
369
|
-
}
|
|
370
366
|
function readString(record, key) {
|
|
371
367
|
const value = record?.[key];
|
|
372
368
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
@@ -394,9 +390,9 @@ function buildCodexConversationTurnInput(params) {
|
|
|
394
390
|
}
|
|
395
391
|
function extractInboundMedia(event) {
|
|
396
392
|
const metadata = event.metadata ?? {};
|
|
397
|
-
const paths =
|
|
398
|
-
const urls =
|
|
399
|
-
const mimeTypes =
|
|
393
|
+
const paths = normalizeSingleOrTrimmedStringList(metadata.mediaPaths).concat(normalizeSingleOrTrimmedStringList(metadata.mediaPath));
|
|
394
|
+
const urls = normalizeSingleOrTrimmedStringList(metadata.mediaUrls).concat(normalizeSingleOrTrimmedStringList(metadata.mediaUrl));
|
|
395
|
+
const mimeTypes = normalizeSingleOrTrimmedStringList(metadata.mediaTypes).concat(normalizeSingleOrTrimmedStringList(metadata.mediaType));
|
|
400
396
|
const count = Math.max(paths.length, urls.length, mimeTypes.length);
|
|
401
397
|
const media = [];
|
|
402
398
|
for (let index = 0; index < count; index += 1) media.push({
|
|
@@ -442,428 +438,104 @@ function readLocalMediaPath(value) {
|
|
|
442
438
|
if (path.isAbsolute(value) || path.win32.isAbsolute(value)) return value;
|
|
443
439
|
return /^[a-z][a-z0-9+.-]*:/i.test(value) ? void 0 : value;
|
|
444
440
|
}
|
|
445
|
-
function readStringArray(value) {
|
|
446
|
-
if (typeof value === "string" && value.trim()) return [value.trim()];
|
|
447
|
-
if (!Array.isArray(value)) return [];
|
|
448
|
-
return value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter(Boolean);
|
|
449
|
-
}
|
|
450
441
|
//#endregion
|
|
451
|
-
//#region extensions/codex/src/
|
|
452
|
-
const
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
442
|
+
//#region extensions/codex/src/node-cli-sessions.ts
|
|
443
|
+
const CODEX_CLI_SESSIONS_LIST_COMMAND = "codex.cli.sessions.list";
|
|
444
|
+
const CODEX_CLI_SESSION_RESUME_COMMAND = "codex.cli.session.resume";
|
|
445
|
+
const DEFAULT_SESSION_LIMIT = 10;
|
|
446
|
+
const MAX_SESSION_LIMIT = 50;
|
|
447
|
+
const DEFAULT_RESUME_TIMEOUT_MS = 20 * 6e4;
|
|
448
|
+
const SESSION_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
449
|
+
const activeResumeSessions = /* @__PURE__ */ new Set();
|
|
450
|
+
const DEFAULT_RESUME_SPAWN_RUNTIME = {
|
|
451
|
+
platform: process.platform,
|
|
452
|
+
env: process.env,
|
|
453
|
+
execPath: process.execPath
|
|
454
|
+
};
|
|
455
|
+
function createCodexCliSessionNodeHostCommands() {
|
|
456
|
+
return [{
|
|
457
|
+
command: CODEX_CLI_SESSIONS_LIST_COMMAND,
|
|
458
|
+
cap: "codex-cli-sessions",
|
|
459
|
+
handle: listLocalCodexCliSessions
|
|
460
|
+
}, {
|
|
461
|
+
command: CODEX_CLI_SESSION_RESUME_COMMAND,
|
|
462
|
+
cap: "codex-cli-sessions",
|
|
463
|
+
dangerous: true,
|
|
464
|
+
handle: resumeLocalCodexCliSession
|
|
465
|
+
}];
|
|
458
466
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
authProfileId,
|
|
480
|
-
approvalPolicy: params.approvalPolicy,
|
|
481
|
-
sandbox: params.sandbox,
|
|
482
|
-
serviceTier: params.serviceTier,
|
|
483
|
-
config: params.config
|
|
484
|
-
});
|
|
485
|
-
else await createThread({
|
|
486
|
-
pluginConfig: params.pluginConfig,
|
|
487
|
-
sessionFile: params.sessionFile,
|
|
488
|
-
workspaceDir,
|
|
489
|
-
...agentDir ? { agentDir } : {},
|
|
490
|
-
model: params.model,
|
|
491
|
-
modelProvider: params.modelProvider,
|
|
492
|
-
authProfileId,
|
|
493
|
-
approvalPolicy: params.approvalPolicy,
|
|
494
|
-
sandbox: params.sandbox,
|
|
495
|
-
serviceTier: params.serviceTier,
|
|
496
|
-
config: params.config
|
|
497
|
-
});
|
|
498
|
-
return createCodexConversationBindingData({
|
|
499
|
-
sessionFile: params.sessionFile,
|
|
500
|
-
workspaceDir,
|
|
501
|
-
...agentDir ? { agentDir } : {}
|
|
467
|
+
function createCodexCliSessionNodeInvokePolicies() {
|
|
468
|
+
return [{
|
|
469
|
+
commands: [CODEX_CLI_SESSIONS_LIST_COMMAND],
|
|
470
|
+
defaultPlatforms: [
|
|
471
|
+
"macos",
|
|
472
|
+
"linux",
|
|
473
|
+
"windows"
|
|
474
|
+
],
|
|
475
|
+
handle: (ctx) => ctx.invokeNode()
|
|
476
|
+
}, {
|
|
477
|
+
commands: [CODEX_CLI_SESSION_RESUME_COMMAND],
|
|
478
|
+
dangerous: true,
|
|
479
|
+
handle: (ctx) => ctx.invokeNode()
|
|
480
|
+
}];
|
|
481
|
+
}
|
|
482
|
+
async function listCodexCliSessionsOnNode(params) {
|
|
483
|
+
const node = await resolveCodexCliNode({
|
|
484
|
+
runtime: params.runtime,
|
|
485
|
+
requestedNode: params.requestedNode,
|
|
486
|
+
command: CODEX_CLI_SESSIONS_LIST_COMMAND
|
|
502
487
|
});
|
|
488
|
+
return {
|
|
489
|
+
node,
|
|
490
|
+
result: parseCodexCliSessionsListResult(await params.runtime.nodes.invoke({
|
|
491
|
+
nodeId: readNodeId(node),
|
|
492
|
+
command: CODEX_CLI_SESSIONS_LIST_COMMAND,
|
|
493
|
+
params: {
|
|
494
|
+
limit: params.limit,
|
|
495
|
+
filter: params.filter
|
|
496
|
+
},
|
|
497
|
+
timeoutMs: 15e3
|
|
498
|
+
}))
|
|
499
|
+
};
|
|
503
500
|
}
|
|
504
|
-
async function
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
const nativeExecutionBlock = data.kind === "codex-cli-node-session" ? resolveCodexNativeSandboxBlock({
|
|
511
|
-
config: options.config,
|
|
512
|
-
sessionKey: event.sessionKey ?? ctx.sessionKey,
|
|
513
|
-
surface: "Codex CLI node conversation binding"
|
|
514
|
-
}) : resolveCodexNativeExecutionBlock({
|
|
515
|
-
config: options.config,
|
|
516
|
-
sessionKey: event.sessionKey ?? ctx.sessionKey,
|
|
517
|
-
surface: "Codex app-server conversation binding"
|
|
501
|
+
async function resolveCodexCliSessionForBindingOnNode(params) {
|
|
502
|
+
const listing = await listCodexCliSessionsOnNode({
|
|
503
|
+
runtime: params.runtime,
|
|
504
|
+
requestedNode: params.requestedNode,
|
|
505
|
+
filter: params.sessionId,
|
|
506
|
+
limit: MAX_SESSION_LIMIT
|
|
518
507
|
});
|
|
519
|
-
if (
|
|
520
|
-
|
|
521
|
-
|
|
508
|
+
if (!listing.node.commands?.includes("codex.cli.session.resume")) throw new Error(`Node ${formatNodeLabel(listing.node)} does not expose ${CODEX_CLI_SESSION_RESUME_COMMAND}.`);
|
|
509
|
+
return {
|
|
510
|
+
node: listing.node,
|
|
511
|
+
session: listing.result.sessions.find((session) => session.sessionId === params.sessionId)
|
|
522
512
|
};
|
|
523
|
-
if (data.kind === "codex-cli-node-session") {
|
|
524
|
-
const resume = options.resumeCodexCliSessionOnNode;
|
|
525
|
-
if (!resume) return {
|
|
526
|
-
handled: true,
|
|
527
|
-
reply: { text: "Codex CLI node binding is unavailable because Gateway node runtime is not attached." }
|
|
528
|
-
};
|
|
529
|
-
try {
|
|
530
|
-
return {
|
|
531
|
-
handled: true,
|
|
532
|
-
reply: (await enqueueBoundTurn(`${data.nodeId}:${data.sessionId}`, async () => {
|
|
533
|
-
return { reply: { text: (await resume({
|
|
534
|
-
nodeId: data.nodeId,
|
|
535
|
-
sessionId: data.sessionId,
|
|
536
|
-
prompt,
|
|
537
|
-
cwd: data.cwd,
|
|
538
|
-
timeoutMs: options.timeoutMs
|
|
539
|
-
})).text.trim() || "Codex completed without a text reply." } };
|
|
540
|
-
})).reply
|
|
541
|
-
};
|
|
542
|
-
} catch (error) {
|
|
543
|
-
return {
|
|
544
|
-
handled: true,
|
|
545
|
-
reply: { text: `Codex CLI node turn failed: ${formatCodexDisplayText(formatErrorMessage(error))}` }
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
try {
|
|
550
|
-
return {
|
|
551
|
-
handled: true,
|
|
552
|
-
reply: (await enqueueBoundTurn(data.sessionFile, () => runBoundTurnWithMissingThreadRecovery({
|
|
553
|
-
data,
|
|
554
|
-
prompt,
|
|
555
|
-
event,
|
|
556
|
-
pluginConfig: options.pluginConfig,
|
|
557
|
-
timeoutMs: options.timeoutMs
|
|
558
|
-
}))).reply
|
|
559
|
-
};
|
|
560
|
-
} catch (error) {
|
|
561
|
-
return {
|
|
562
|
-
handled: true,
|
|
563
|
-
reply: { text: `Codex app-server turn failed: ${formatCodexDisplayText(formatErrorMessage(error))}` }
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
513
|
}
|
|
567
|
-
async function
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
514
|
+
async function resumeCodexCliSessionOnNode(params) {
|
|
515
|
+
const payload = unwrapNodeInvokePayload(await params.runtime.nodes.invoke({
|
|
516
|
+
nodeId: params.nodeId,
|
|
517
|
+
command: CODEX_CLI_SESSION_RESUME_COMMAND,
|
|
518
|
+
params: {
|
|
519
|
+
sessionId: params.sessionId,
|
|
520
|
+
prompt: params.prompt,
|
|
521
|
+
cwd: params.cwd,
|
|
522
|
+
timeoutMs: params.timeoutMs
|
|
523
|
+
},
|
|
524
|
+
timeoutMs: (params.timeoutMs ?? DEFAULT_RESUME_TIMEOUT_MS) + 5e3
|
|
525
|
+
}));
|
|
526
|
+
if (!isRecord(payload) || payload.ok !== true || typeof payload.text !== "string") throw new Error("Codex CLI resume returned an invalid payload.");
|
|
527
|
+
return {
|
|
528
|
+
ok: true,
|
|
529
|
+
sessionId: typeof payload.sessionId === "string" ? payload.sessionId : params.sessionId,
|
|
530
|
+
text: payload.text
|
|
531
|
+
};
|
|
572
532
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
});
|
|
579
|
-
const modelProvider = resolveThreadRequestModelProvider({
|
|
580
|
-
authProfileId: params.authProfileId,
|
|
581
|
-
modelProvider: params.modelProvider,
|
|
582
|
-
...agentLookup
|
|
583
|
-
});
|
|
584
|
-
const response = await (await getSharedCodexAppServerClient({
|
|
585
|
-
startOptions: runtime.start,
|
|
586
|
-
timeoutMs: runtime.requestTimeoutMs,
|
|
587
|
-
authProfileId: params.authProfileId,
|
|
588
|
-
...agentLookup
|
|
589
|
-
})).request(CODEX_CONTROL_METHODS.resumeThread, {
|
|
590
|
-
threadId: params.threadId,
|
|
591
|
-
...params.model ? { model: params.model } : {},
|
|
592
|
-
...modelProvider ? { modelProvider } : {},
|
|
593
|
-
approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
|
|
594
|
-
approvalsReviewer: runtime.approvalsReviewer,
|
|
595
|
-
sandbox: params.sandbox ?? runtime.sandbox,
|
|
596
|
-
...params.serviceTier ?? runtime.serviceTier ? { serviceTier: params.serviceTier ?? runtime.serviceTier } : {},
|
|
597
|
-
persistExtendedHistory: true
|
|
598
|
-
}, { timeoutMs: runtime.requestTimeoutMs });
|
|
599
|
-
const thread = response.thread;
|
|
600
|
-
const runtimeApprovalPolicy = typeof runtime.approvalPolicy === "string" ? runtime.approvalPolicy : void 0;
|
|
601
|
-
await writeCodexAppServerBinding(params.sessionFile, {
|
|
602
|
-
threadId: thread.id,
|
|
603
|
-
cwd: thread.cwd ?? params.workspaceDir,
|
|
604
|
-
authProfileId: params.authProfileId,
|
|
605
|
-
model: response.model ?? params.model,
|
|
606
|
-
modelProvider: normalizeCodexAppServerBindingModelProvider({
|
|
607
|
-
authProfileId: params.authProfileId,
|
|
608
|
-
modelProvider: response.modelProvider ?? params.modelProvider,
|
|
609
|
-
...agentLookup
|
|
610
|
-
}),
|
|
611
|
-
approvalPolicy: params.approvalPolicy ?? runtimeApprovalPolicy,
|
|
612
|
-
sandbox: params.sandbox ?? runtime.sandbox,
|
|
613
|
-
serviceTier: params.serviceTier ?? runtime.serviceTier
|
|
614
|
-
}, { ...agentLookup });
|
|
615
|
-
}
|
|
616
|
-
async function createThread(params) {
|
|
617
|
-
const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
|
|
618
|
-
const agentLookup = buildAgentLookup({
|
|
619
|
-
agentDir: params.agentDir,
|
|
620
|
-
config: params.config
|
|
621
|
-
});
|
|
622
|
-
const modelProvider = resolveThreadRequestModelProvider({
|
|
623
|
-
authProfileId: params.authProfileId,
|
|
624
|
-
modelProvider: params.modelProvider,
|
|
625
|
-
...agentLookup
|
|
626
|
-
});
|
|
627
|
-
const response = await (await getSharedCodexAppServerClient({
|
|
628
|
-
startOptions: runtime.start,
|
|
629
|
-
timeoutMs: runtime.requestTimeoutMs,
|
|
630
|
-
authProfileId: params.authProfileId,
|
|
631
|
-
...agentLookup
|
|
632
|
-
})).request("thread/start", {
|
|
633
|
-
cwd: params.workspaceDir,
|
|
634
|
-
...params.model ? { model: params.model } : {},
|
|
635
|
-
...modelProvider ? { modelProvider } : {},
|
|
636
|
-
approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
|
|
637
|
-
approvalsReviewer: runtime.approvalsReviewer,
|
|
638
|
-
sandbox: params.sandbox ?? runtime.sandbox,
|
|
639
|
-
...params.serviceTier ?? runtime.serviceTier ? { serviceTier: params.serviceTier ?? runtime.serviceTier } : {},
|
|
640
|
-
developerInstructions: "This Codex thread is bound to an OpenClaw conversation. Answer normally; OpenClaw will deliver your final response back to the conversation.",
|
|
641
|
-
experimentalRawEvents: true,
|
|
642
|
-
persistExtendedHistory: true
|
|
643
|
-
}, { timeoutMs: runtime.requestTimeoutMs });
|
|
644
|
-
const runtimeApprovalPolicy = typeof runtime.approvalPolicy === "string" ? runtime.approvalPolicy : void 0;
|
|
645
|
-
await writeCodexAppServerBinding(params.sessionFile, {
|
|
646
|
-
threadId: response.thread.id,
|
|
647
|
-
cwd: response.thread.cwd ?? params.workspaceDir,
|
|
648
|
-
authProfileId: params.authProfileId,
|
|
649
|
-
model: response.model ?? params.model,
|
|
650
|
-
modelProvider: normalizeCodexAppServerBindingModelProvider({
|
|
651
|
-
authProfileId: params.authProfileId,
|
|
652
|
-
modelProvider: response.modelProvider ?? params.modelProvider,
|
|
653
|
-
...agentLookup
|
|
654
|
-
}),
|
|
655
|
-
approvalPolicy: params.approvalPolicy ?? runtimeApprovalPolicy,
|
|
656
|
-
sandbox: params.sandbox ?? runtime.sandbox,
|
|
657
|
-
serviceTier: params.serviceTier ?? runtime.serviceTier
|
|
658
|
-
}, { ...agentLookup });
|
|
659
|
-
}
|
|
660
|
-
async function runBoundTurn(params) {
|
|
661
|
-
const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
|
|
662
|
-
const agentLookup = buildAgentLookup({ agentDir: params.data.agentDir });
|
|
663
|
-
const binding = await readCodexAppServerBinding(params.data.sessionFile, agentLookup);
|
|
664
|
-
const threadId = binding?.threadId;
|
|
665
|
-
if (!threadId) throw new Error("bound Codex conversation has no thread binding");
|
|
666
|
-
const client = await getSharedCodexAppServerClient({
|
|
667
|
-
startOptions: runtime.start,
|
|
668
|
-
timeoutMs: runtime.requestTimeoutMs,
|
|
669
|
-
authProfileId: binding.authProfileId,
|
|
670
|
-
...agentLookup
|
|
671
|
-
});
|
|
672
|
-
const collector = createCodexConversationTurnCollector(threadId);
|
|
673
|
-
const notificationCleanup = client.addNotificationHandler((notification) => collector.handleNotification(notification));
|
|
674
|
-
const requestCleanup = client.addRequestHandler(async (request) => {
|
|
675
|
-
if (request.method === "item/tool/call") return {
|
|
676
|
-
contentItems: [{
|
|
677
|
-
type: "inputText",
|
|
678
|
-
text: "OpenClaw native Codex conversation binding does not expose dynamic OpenClaw tools yet."
|
|
679
|
-
}],
|
|
680
|
-
success: false
|
|
681
|
-
};
|
|
682
|
-
if (request.method === "item/commandExecution/requestApproval" || request.method === "item/fileChange/requestApproval") return {
|
|
683
|
-
decision: "decline",
|
|
684
|
-
reason: "OpenClaw native Codex conversation binding cannot route interactive approvals yet; use the Codex harness or explicit /acp spawn codex for that workflow."
|
|
685
|
-
};
|
|
686
|
-
if (request.method === "item/permissions/requestApproval") return {
|
|
687
|
-
permissions: {},
|
|
688
|
-
scope: "turn"
|
|
689
|
-
};
|
|
690
|
-
if (request.method.includes("requestApproval")) return {
|
|
691
|
-
decision: "decline",
|
|
692
|
-
reason: "OpenClaw native Codex conversation binding cannot route interactive approvals yet; use the Codex harness or explicit /acp spawn codex for that workflow."
|
|
693
|
-
};
|
|
694
|
-
});
|
|
695
|
-
try {
|
|
696
|
-
const turnId = (await client.request("turn/start", {
|
|
697
|
-
threadId,
|
|
698
|
-
input: buildCodexConversationTurnInput({
|
|
699
|
-
prompt: params.prompt,
|
|
700
|
-
event: params.event
|
|
701
|
-
}),
|
|
702
|
-
cwd: binding.cwd || params.data.workspaceDir,
|
|
703
|
-
approvalPolicy: binding.approvalPolicy ?? runtime.approvalPolicy,
|
|
704
|
-
approvalsReviewer: runtime.approvalsReviewer,
|
|
705
|
-
sandboxPolicy: codexSandboxPolicyForTurn(binding.sandbox ?? runtime.sandbox, binding.cwd || params.data.workspaceDir),
|
|
706
|
-
...binding.model ? { model: binding.model } : {},
|
|
707
|
-
...binding.serviceTier ?? runtime.serviceTier ? { serviceTier: binding.serviceTier ?? runtime.serviceTier } : {}
|
|
708
|
-
}, { timeoutMs: runtime.requestTimeoutMs })).turn.id;
|
|
709
|
-
const activeCleanup = trackCodexConversationActiveTurn({
|
|
710
|
-
sessionFile: params.data.sessionFile,
|
|
711
|
-
threadId,
|
|
712
|
-
turnId
|
|
713
|
-
});
|
|
714
|
-
collector.setTurnId(turnId);
|
|
715
|
-
return { reply: { text: (await collector.wait({ timeoutMs: params.timeoutMs ?? DEFAULT_BOUND_TURN_TIMEOUT_MS }).finally(activeCleanup)).replyText.trim() || "Codex completed without a text reply." } };
|
|
716
|
-
} finally {
|
|
717
|
-
notificationCleanup();
|
|
718
|
-
requestCleanup();
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
async function runBoundTurnWithMissingThreadRecovery(params) {
|
|
722
|
-
try {
|
|
723
|
-
return await runBoundTurn(params);
|
|
724
|
-
} catch (error) {
|
|
725
|
-
if (!isCodexThreadNotFoundError(error)) throw error;
|
|
726
|
-
const agentLookup = buildAgentLookup({ agentDir: params.data.agentDir });
|
|
727
|
-
const binding = await readCodexAppServerBinding(params.data.sessionFile, agentLookup);
|
|
728
|
-
await startCodexConversationThread({
|
|
729
|
-
pluginConfig: params.pluginConfig,
|
|
730
|
-
sessionFile: params.data.sessionFile,
|
|
731
|
-
workspaceDir: binding?.cwd || params.data.workspaceDir,
|
|
732
|
-
...agentLookup,
|
|
733
|
-
model: binding?.model,
|
|
734
|
-
modelProvider: binding?.modelProvider,
|
|
735
|
-
authProfileId: binding?.authProfileId,
|
|
736
|
-
approvalPolicy: binding?.approvalPolicy,
|
|
737
|
-
sandbox: binding?.sandbox,
|
|
738
|
-
serviceTier: binding?.serviceTier
|
|
739
|
-
});
|
|
740
|
-
return await runBoundTurn(params);
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
function isCodexThreadNotFoundError(error) {
|
|
744
|
-
return /\bthread not found:/iu.test(formatErrorMessage(error));
|
|
745
|
-
}
|
|
746
|
-
function enqueueBoundTurn(key, run) {
|
|
747
|
-
const state = getGlobalState();
|
|
748
|
-
const next = (state.queues.get(key) ?? Promise.resolve()).then(run, run);
|
|
749
|
-
const queued = next.then(() => void 0, () => void 0);
|
|
750
|
-
state.queues.set(key, queued);
|
|
751
|
-
next.finally(() => {
|
|
752
|
-
if (state.queues.get(key) === queued) state.queues.delete(key);
|
|
753
|
-
}).catch(() => void 0);
|
|
754
|
-
return next;
|
|
755
|
-
}
|
|
756
|
-
function resolveThreadRequestModelProvider(params) {
|
|
757
|
-
const modelProvider = params.modelProvider?.trim();
|
|
758
|
-
if (!modelProvider || modelProvider.toLowerCase() === "codex") return;
|
|
759
|
-
if (isCodexAppServerNativeAuthProfile(params) && (modelProvider.toLowerCase() === "openai" || modelProvider.toLowerCase() === "openai-codex")) return;
|
|
760
|
-
return modelProvider.toLowerCase() === "openai-codex" ? "openai" : modelProvider;
|
|
761
|
-
}
|
|
762
|
-
function buildAgentLookup(params) {
|
|
763
|
-
const agentDir = params.agentDir?.trim();
|
|
764
|
-
return {
|
|
765
|
-
...agentDir ? { agentDir } : {},
|
|
766
|
-
...params.config ? { config: params.config } : {}
|
|
767
|
-
};
|
|
768
|
-
}
|
|
769
|
-
//#endregion
|
|
770
|
-
//#region extensions/codex/src/node-cli-sessions.ts
|
|
771
|
-
const CODEX_CLI_SESSIONS_LIST_COMMAND = "codex.cli.sessions.list";
|
|
772
|
-
const CODEX_CLI_SESSION_RESUME_COMMAND = "codex.cli.session.resume";
|
|
773
|
-
const DEFAULT_SESSION_LIMIT = 10;
|
|
774
|
-
const MAX_SESSION_LIMIT = 50;
|
|
775
|
-
const DEFAULT_RESUME_TIMEOUT_MS = 20 * 6e4;
|
|
776
|
-
const SESSION_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
777
|
-
const activeResumeSessions = /* @__PURE__ */ new Set();
|
|
778
|
-
const DEFAULT_RESUME_SPAWN_RUNTIME = {
|
|
779
|
-
platform: process.platform,
|
|
780
|
-
env: process.env,
|
|
781
|
-
execPath: process.execPath
|
|
782
|
-
};
|
|
783
|
-
function createCodexCliSessionNodeHostCommands() {
|
|
784
|
-
return [{
|
|
785
|
-
command: CODEX_CLI_SESSIONS_LIST_COMMAND,
|
|
786
|
-
cap: "codex-cli-sessions",
|
|
787
|
-
handle: listLocalCodexCliSessions
|
|
788
|
-
}, {
|
|
789
|
-
command: CODEX_CLI_SESSION_RESUME_COMMAND,
|
|
790
|
-
cap: "codex-cli-sessions",
|
|
791
|
-
dangerous: true,
|
|
792
|
-
handle: resumeLocalCodexCliSession
|
|
793
|
-
}];
|
|
794
|
-
}
|
|
795
|
-
function createCodexCliSessionNodeInvokePolicies() {
|
|
796
|
-
return [{
|
|
797
|
-
commands: [CODEX_CLI_SESSIONS_LIST_COMMAND],
|
|
798
|
-
defaultPlatforms: [
|
|
799
|
-
"macos",
|
|
800
|
-
"linux",
|
|
801
|
-
"windows"
|
|
802
|
-
],
|
|
803
|
-
handle: (ctx) => ctx.invokeNode()
|
|
804
|
-
}, {
|
|
805
|
-
commands: [CODEX_CLI_SESSION_RESUME_COMMAND],
|
|
806
|
-
dangerous: true,
|
|
807
|
-
handle: (ctx) => ctx.invokeNode()
|
|
808
|
-
}];
|
|
809
|
-
}
|
|
810
|
-
async function listCodexCliSessionsOnNode(params) {
|
|
811
|
-
const node = await resolveCodexCliNode({
|
|
812
|
-
runtime: params.runtime,
|
|
813
|
-
requestedNode: params.requestedNode,
|
|
814
|
-
command: CODEX_CLI_SESSIONS_LIST_COMMAND
|
|
815
|
-
});
|
|
816
|
-
return {
|
|
817
|
-
node,
|
|
818
|
-
result: parseCodexCliSessionsListResult(await params.runtime.nodes.invoke({
|
|
819
|
-
nodeId: readNodeId(node),
|
|
820
|
-
command: CODEX_CLI_SESSIONS_LIST_COMMAND,
|
|
821
|
-
params: {
|
|
822
|
-
limit: params.limit,
|
|
823
|
-
filter: params.filter
|
|
824
|
-
},
|
|
825
|
-
timeoutMs: 15e3
|
|
826
|
-
}))
|
|
827
|
-
};
|
|
828
|
-
}
|
|
829
|
-
async function resolveCodexCliSessionForBindingOnNode(params) {
|
|
830
|
-
const listing = await listCodexCliSessionsOnNode({
|
|
831
|
-
runtime: params.runtime,
|
|
832
|
-
requestedNode: params.requestedNode,
|
|
833
|
-
filter: params.sessionId,
|
|
834
|
-
limit: MAX_SESSION_LIMIT
|
|
835
|
-
});
|
|
836
|
-
if (!listing.node.commands?.includes("codex.cli.session.resume")) throw new Error(`Node ${formatNodeLabel(listing.node)} does not expose ${CODEX_CLI_SESSION_RESUME_COMMAND}.`);
|
|
837
|
-
return {
|
|
838
|
-
node: listing.node,
|
|
839
|
-
session: listing.result.sessions.find((session) => session.sessionId === params.sessionId)
|
|
840
|
-
};
|
|
841
|
-
}
|
|
842
|
-
async function resumeCodexCliSessionOnNode(params) {
|
|
843
|
-
const payload = unwrapNodeInvokePayload(await params.runtime.nodes.invoke({
|
|
844
|
-
nodeId: params.nodeId,
|
|
845
|
-
command: CODEX_CLI_SESSION_RESUME_COMMAND,
|
|
846
|
-
params: {
|
|
847
|
-
sessionId: params.sessionId,
|
|
848
|
-
prompt: params.prompt,
|
|
849
|
-
cwd: params.cwd,
|
|
850
|
-
timeoutMs: params.timeoutMs
|
|
851
|
-
},
|
|
852
|
-
timeoutMs: (params.timeoutMs ?? DEFAULT_RESUME_TIMEOUT_MS) + 5e3
|
|
853
|
-
}));
|
|
854
|
-
if (!isRecord(payload) || payload.ok !== true || typeof payload.text !== "string") throw new Error("Codex CLI resume returned an invalid payload.");
|
|
855
|
-
return {
|
|
856
|
-
ok: true,
|
|
857
|
-
sessionId: typeof payload.sessionId === "string" ? payload.sessionId : params.sessionId,
|
|
858
|
-
text: payload.text
|
|
859
|
-
};
|
|
860
|
-
}
|
|
861
|
-
function formatCodexCliSessions(params) {
|
|
862
|
-
if (params.result.sessions.length === 0) return `No Codex CLI sessions returned from ${formatCodexDisplayText(formatNodeLabel(params.node))}.`;
|
|
863
|
-
return [`Codex CLI sessions on ${formatCodexDisplayText(formatNodeLabel(params.node))}:`, ...params.result.sessions.map((session) => {
|
|
864
|
-
const details = [session.cwd, session.updatedAt].filter((value) => Boolean(value));
|
|
865
|
-
return `- ${formatCodexDisplayText(session.sessionId)}${session.lastMessage ? ` - ${formatCodexDisplayText(session.lastMessage)}` : ""}${details.length > 0 ? ` (${details.map(formatCodexDisplayText).join(", ")})` : ""}\n Bind: /codex resume ${formatCodexDisplayText(session.sessionId)} --host ${formatCodexDisplayText(readNodeId(params.node))} --bind here`;
|
|
866
|
-
})].join("\n");
|
|
533
|
+
function formatCodexCliSessions(params) {
|
|
534
|
+
if (params.result.sessions.length === 0) return `No Codex CLI sessions returned from ${formatCodexDisplayText(formatNodeLabel(params.node))}.`;
|
|
535
|
+
return [`Codex CLI sessions on ${formatCodexDisplayText(formatNodeLabel(params.node))}:`, ...params.result.sessions.map((session) => {
|
|
536
|
+
const details = [session.cwd, session.updatedAt].filter((value) => Boolean(value));
|
|
537
|
+
return `- ${formatCodexDisplayText(session.sessionId)}${session.lastMessage ? ` - ${formatCodexDisplayText(session.lastMessage)}` : ""}${details.length > 0 ? ` (${details.map(formatCodexDisplayText).join(", ")})` : ""}\n Bind: /codex resume ${formatCodexDisplayText(session.sessionId)} --host ${formatCodexDisplayText(readNodeId(params.node))} --bind here`;
|
|
538
|
+
})].join("\n");
|
|
867
539
|
}
|
|
868
540
|
async function listLocalCodexCliSessions(paramsJSON) {
|
|
869
541
|
const params = readRecordParam(paramsJSON);
|
|
@@ -1223,8 +895,328 @@ function formatNodeLabel(node) {
|
|
|
1223
895
|
node.remoteIp
|
|
1224
896
|
].filter(Boolean).join(" / ") || "node";
|
|
1225
897
|
}
|
|
1226
|
-
|
|
1227
|
-
|
|
898
|
+
//#endregion
|
|
899
|
+
//#region extensions/codex/src/conversation-binding.ts
|
|
900
|
+
const DEFAULT_BOUND_TURN_TIMEOUT_MS = 20 * 6e4;
|
|
901
|
+
const CODEX_CONVERSATION_GLOBAL_STATE = Symbol.for("openclaw.codex.conversationBinding");
|
|
902
|
+
function getGlobalState() {
|
|
903
|
+
const globalState = globalThis;
|
|
904
|
+
globalState[CODEX_CONVERSATION_GLOBAL_STATE] ??= { queues: /* @__PURE__ */ new Map() };
|
|
905
|
+
return globalState[CODEX_CONVERSATION_GLOBAL_STATE];
|
|
906
|
+
}
|
|
907
|
+
async function startCodexConversationThread(params) {
|
|
908
|
+
const workspaceDir = params.workspaceDir?.trim() || resolveCodexDefaultWorkspaceDir(params.pluginConfig);
|
|
909
|
+
const agentDir = params.agentDir?.trim();
|
|
910
|
+
const agentLookup = buildAgentLookup({
|
|
911
|
+
agentDir,
|
|
912
|
+
config: params.config
|
|
913
|
+
});
|
|
914
|
+
const existingBinding = await readCodexAppServerBinding(params.sessionFile, { ...agentLookup });
|
|
915
|
+
const authProfileId = resolveCodexAppServerAuthProfileIdForAgent({
|
|
916
|
+
authProfileId: params.authProfileId ?? existingBinding?.authProfileId,
|
|
917
|
+
...agentLookup
|
|
918
|
+
});
|
|
919
|
+
if (params.threadId?.trim()) await attachExistingThread({
|
|
920
|
+
pluginConfig: params.pluginConfig,
|
|
921
|
+
sessionFile: params.sessionFile,
|
|
922
|
+
threadId: params.threadId.trim(),
|
|
923
|
+
workspaceDir,
|
|
924
|
+
...agentDir ? { agentDir } : {},
|
|
925
|
+
model: params.model,
|
|
926
|
+
modelProvider: params.modelProvider,
|
|
927
|
+
authProfileId,
|
|
928
|
+
approvalPolicy: params.approvalPolicy,
|
|
929
|
+
sandbox: params.sandbox,
|
|
930
|
+
serviceTier: params.serviceTier,
|
|
931
|
+
config: params.config
|
|
932
|
+
});
|
|
933
|
+
else await createThread({
|
|
934
|
+
pluginConfig: params.pluginConfig,
|
|
935
|
+
sessionFile: params.sessionFile,
|
|
936
|
+
workspaceDir,
|
|
937
|
+
...agentDir ? { agentDir } : {},
|
|
938
|
+
model: params.model,
|
|
939
|
+
modelProvider: params.modelProvider,
|
|
940
|
+
authProfileId,
|
|
941
|
+
approvalPolicy: params.approvalPolicy,
|
|
942
|
+
sandbox: params.sandbox,
|
|
943
|
+
serviceTier: params.serviceTier,
|
|
944
|
+
config: params.config
|
|
945
|
+
});
|
|
946
|
+
return createCodexConversationBindingData({
|
|
947
|
+
sessionFile: params.sessionFile,
|
|
948
|
+
workspaceDir,
|
|
949
|
+
...agentDir ? { agentDir } : {}
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
async function handleCodexConversationInboundClaim(event, ctx, options = {}) {
|
|
953
|
+
const data = readCodexConversationBindingData(ctx.pluginBinding);
|
|
954
|
+
if (!data) return;
|
|
955
|
+
if (event.commandAuthorized !== true) return { handled: true };
|
|
956
|
+
const prompt = event.bodyForAgent?.trim() || event.content?.trim() || "";
|
|
957
|
+
if (!prompt) return { handled: true };
|
|
958
|
+
const nativeExecutionBlock = data.kind === "codex-cli-node-session" ? resolveCodexNativeSandboxBlock({
|
|
959
|
+
config: options.config,
|
|
960
|
+
sessionKey: event.sessionKey ?? ctx.sessionKey,
|
|
961
|
+
surface: "Codex CLI node conversation binding"
|
|
962
|
+
}) : resolveCodexNativeExecutionBlock({
|
|
963
|
+
config: options.config,
|
|
964
|
+
sessionKey: event.sessionKey ?? ctx.sessionKey,
|
|
965
|
+
surface: "Codex app-server conversation binding"
|
|
966
|
+
});
|
|
967
|
+
if (nativeExecutionBlock) return {
|
|
968
|
+
handled: true,
|
|
969
|
+
reply: { text: nativeExecutionBlock }
|
|
970
|
+
};
|
|
971
|
+
if (data.kind === "codex-cli-node-session") {
|
|
972
|
+
const resume = options.resumeCodexCliSessionOnNode;
|
|
973
|
+
if (!resume) return {
|
|
974
|
+
handled: true,
|
|
975
|
+
reply: { text: "Codex CLI node binding is unavailable because Gateway node runtime is not attached." }
|
|
976
|
+
};
|
|
977
|
+
try {
|
|
978
|
+
return {
|
|
979
|
+
handled: true,
|
|
980
|
+
reply: (await enqueueBoundTurn(`${data.nodeId}:${data.sessionId}`, async () => {
|
|
981
|
+
return { reply: { text: (await resume({
|
|
982
|
+
nodeId: data.nodeId,
|
|
983
|
+
sessionId: data.sessionId,
|
|
984
|
+
prompt,
|
|
985
|
+
cwd: data.cwd,
|
|
986
|
+
timeoutMs: options.timeoutMs
|
|
987
|
+
})).text.trim() || "Codex completed without a text reply." } };
|
|
988
|
+
})).reply
|
|
989
|
+
};
|
|
990
|
+
} catch (error) {
|
|
991
|
+
return {
|
|
992
|
+
handled: true,
|
|
993
|
+
reply: { text: `Codex CLI node turn failed: ${formatCodexDisplayText(formatErrorMessage(error))}` }
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
try {
|
|
998
|
+
return {
|
|
999
|
+
handled: true,
|
|
1000
|
+
reply: (await enqueueBoundTurn(data.sessionFile, () => runBoundTurnWithMissingThreadRecovery({
|
|
1001
|
+
data,
|
|
1002
|
+
prompt,
|
|
1003
|
+
event,
|
|
1004
|
+
pluginConfig: options.pluginConfig,
|
|
1005
|
+
timeoutMs: options.timeoutMs
|
|
1006
|
+
}))).reply
|
|
1007
|
+
};
|
|
1008
|
+
} catch (error) {
|
|
1009
|
+
return {
|
|
1010
|
+
handled: true,
|
|
1011
|
+
reply: { text: `Codex app-server turn failed: ${formatCodexDisplayText(formatErrorMessage(error))}` }
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
async function handleCodexConversationBindingResolved(event) {
|
|
1016
|
+
if (event.status !== "denied") return;
|
|
1017
|
+
const data = readCodexConversationBindingDataRecord(event.request.data ?? {});
|
|
1018
|
+
if (!data || data.kind !== "codex-app-server-session") return;
|
|
1019
|
+
await clearCodexAppServerBinding(data.sessionFile);
|
|
1020
|
+
}
|
|
1021
|
+
async function attachExistingThread(params) {
|
|
1022
|
+
const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
|
|
1023
|
+
const agentLookup = buildAgentLookup({
|
|
1024
|
+
agentDir: params.agentDir,
|
|
1025
|
+
config: params.config
|
|
1026
|
+
});
|
|
1027
|
+
const modelProvider = resolveThreadRequestModelProvider({
|
|
1028
|
+
authProfileId: params.authProfileId,
|
|
1029
|
+
modelProvider: params.modelProvider,
|
|
1030
|
+
...agentLookup
|
|
1031
|
+
});
|
|
1032
|
+
const response = await (await getSharedCodexAppServerClient({
|
|
1033
|
+
startOptions: runtime.start,
|
|
1034
|
+
timeoutMs: runtime.requestTimeoutMs,
|
|
1035
|
+
authProfileId: params.authProfileId,
|
|
1036
|
+
...agentLookup
|
|
1037
|
+
})).request(CODEX_CONTROL_METHODS.resumeThread, {
|
|
1038
|
+
threadId: params.threadId,
|
|
1039
|
+
...params.model ? { model: params.model } : {},
|
|
1040
|
+
...modelProvider ? { modelProvider } : {},
|
|
1041
|
+
personality: CODEX_NATIVE_PERSONALITY_NONE,
|
|
1042
|
+
approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
|
|
1043
|
+
approvalsReviewer: runtime.approvalsReviewer,
|
|
1044
|
+
sandbox: params.sandbox ?? runtime.sandbox,
|
|
1045
|
+
...params.serviceTier ?? runtime.serviceTier ? { serviceTier: params.serviceTier ?? runtime.serviceTier } : {},
|
|
1046
|
+
persistExtendedHistory: true
|
|
1047
|
+
}, { timeoutMs: runtime.requestTimeoutMs });
|
|
1048
|
+
const thread = response.thread;
|
|
1049
|
+
const runtimeApprovalPolicy = typeof runtime.approvalPolicy === "string" ? runtime.approvalPolicy : void 0;
|
|
1050
|
+
await writeCodexAppServerBinding(params.sessionFile, {
|
|
1051
|
+
threadId: thread.id,
|
|
1052
|
+
cwd: thread.cwd ?? params.workspaceDir,
|
|
1053
|
+
authProfileId: params.authProfileId,
|
|
1054
|
+
model: response.model ?? params.model,
|
|
1055
|
+
modelProvider: normalizeCodexAppServerBindingModelProvider({
|
|
1056
|
+
authProfileId: params.authProfileId,
|
|
1057
|
+
modelProvider: response.modelProvider ?? params.modelProvider,
|
|
1058
|
+
...agentLookup
|
|
1059
|
+
}),
|
|
1060
|
+
approvalPolicy: params.approvalPolicy ?? runtimeApprovalPolicy,
|
|
1061
|
+
sandbox: params.sandbox ?? runtime.sandbox,
|
|
1062
|
+
serviceTier: params.serviceTier ?? runtime.serviceTier
|
|
1063
|
+
}, { ...agentLookup });
|
|
1064
|
+
}
|
|
1065
|
+
async function createThread(params) {
|
|
1066
|
+
const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
|
|
1067
|
+
const agentLookup = buildAgentLookup({
|
|
1068
|
+
agentDir: params.agentDir,
|
|
1069
|
+
config: params.config
|
|
1070
|
+
});
|
|
1071
|
+
const modelProvider = resolveThreadRequestModelProvider({
|
|
1072
|
+
authProfileId: params.authProfileId,
|
|
1073
|
+
modelProvider: params.modelProvider,
|
|
1074
|
+
...agentLookup
|
|
1075
|
+
});
|
|
1076
|
+
const response = await (await getSharedCodexAppServerClient({
|
|
1077
|
+
startOptions: runtime.start,
|
|
1078
|
+
timeoutMs: runtime.requestTimeoutMs,
|
|
1079
|
+
authProfileId: params.authProfileId,
|
|
1080
|
+
...agentLookup
|
|
1081
|
+
})).request("thread/start", {
|
|
1082
|
+
cwd: params.workspaceDir,
|
|
1083
|
+
...params.model ? { model: params.model } : {},
|
|
1084
|
+
...modelProvider ? { modelProvider } : {},
|
|
1085
|
+
personality: CODEX_NATIVE_PERSONALITY_NONE,
|
|
1086
|
+
approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
|
|
1087
|
+
approvalsReviewer: runtime.approvalsReviewer,
|
|
1088
|
+
sandbox: params.sandbox ?? runtime.sandbox,
|
|
1089
|
+
...params.serviceTier ?? runtime.serviceTier ? { serviceTier: params.serviceTier ?? runtime.serviceTier } : {},
|
|
1090
|
+
developerInstructions: "This Codex thread is bound to an OpenClaw conversation. Answer normally; OpenClaw will deliver your final response back to the conversation.",
|
|
1091
|
+
experimentalRawEvents: true,
|
|
1092
|
+
persistExtendedHistory: true
|
|
1093
|
+
}, { timeoutMs: runtime.requestTimeoutMs });
|
|
1094
|
+
const runtimeApprovalPolicy = typeof runtime.approvalPolicy === "string" ? runtime.approvalPolicy : void 0;
|
|
1095
|
+
await writeCodexAppServerBinding(params.sessionFile, {
|
|
1096
|
+
threadId: response.thread.id,
|
|
1097
|
+
cwd: response.thread.cwd ?? params.workspaceDir,
|
|
1098
|
+
authProfileId: params.authProfileId,
|
|
1099
|
+
model: response.model ?? params.model,
|
|
1100
|
+
modelProvider: normalizeCodexAppServerBindingModelProvider({
|
|
1101
|
+
authProfileId: params.authProfileId,
|
|
1102
|
+
modelProvider: response.modelProvider ?? params.modelProvider,
|
|
1103
|
+
...agentLookup
|
|
1104
|
+
}),
|
|
1105
|
+
approvalPolicy: params.approvalPolicy ?? runtimeApprovalPolicy,
|
|
1106
|
+
sandbox: params.sandbox ?? runtime.sandbox,
|
|
1107
|
+
serviceTier: params.serviceTier ?? runtime.serviceTier
|
|
1108
|
+
}, { ...agentLookup });
|
|
1109
|
+
}
|
|
1110
|
+
async function runBoundTurn(params) {
|
|
1111
|
+
const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
|
|
1112
|
+
const agentLookup = buildAgentLookup({ agentDir: params.data.agentDir });
|
|
1113
|
+
const binding = await readCodexAppServerBinding(params.data.sessionFile, agentLookup);
|
|
1114
|
+
const threadId = binding?.threadId;
|
|
1115
|
+
if (!threadId) throw new Error("bound Codex conversation has no thread binding");
|
|
1116
|
+
const client = await getSharedCodexAppServerClient({
|
|
1117
|
+
startOptions: runtime.start,
|
|
1118
|
+
timeoutMs: runtime.requestTimeoutMs,
|
|
1119
|
+
authProfileId: binding.authProfileId,
|
|
1120
|
+
...agentLookup
|
|
1121
|
+
});
|
|
1122
|
+
const collector = createCodexConversationTurnCollector(threadId);
|
|
1123
|
+
const notificationCleanup = client.addNotificationHandler((notification) => collector.handleNotification(notification));
|
|
1124
|
+
const requestCleanup = client.addRequestHandler(async (request) => {
|
|
1125
|
+
if (request.method === "item/tool/call") return {
|
|
1126
|
+
contentItems: [{
|
|
1127
|
+
type: "inputText",
|
|
1128
|
+
text: "OpenClaw native Codex conversation binding does not expose dynamic OpenClaw tools yet."
|
|
1129
|
+
}],
|
|
1130
|
+
success: false
|
|
1131
|
+
};
|
|
1132
|
+
if (request.method === "item/commandExecution/requestApproval" || request.method === "item/fileChange/requestApproval") return {
|
|
1133
|
+
decision: "decline",
|
|
1134
|
+
reason: "OpenClaw native Codex conversation binding cannot route interactive approvals yet; use the Codex harness or explicit /acp spawn codex for that workflow."
|
|
1135
|
+
};
|
|
1136
|
+
if (request.method === "item/permissions/requestApproval") return {
|
|
1137
|
+
permissions: {},
|
|
1138
|
+
scope: "turn"
|
|
1139
|
+
};
|
|
1140
|
+
if (request.method.includes("requestApproval")) return {
|
|
1141
|
+
decision: "decline",
|
|
1142
|
+
reason: "OpenClaw native Codex conversation binding cannot route interactive approvals yet; use the Codex harness or explicit /acp spawn codex for that workflow."
|
|
1143
|
+
};
|
|
1144
|
+
});
|
|
1145
|
+
try {
|
|
1146
|
+
const turnId = (await client.request("turn/start", {
|
|
1147
|
+
threadId,
|
|
1148
|
+
input: buildCodexConversationTurnInput({
|
|
1149
|
+
prompt: params.prompt,
|
|
1150
|
+
event: params.event
|
|
1151
|
+
}),
|
|
1152
|
+
cwd: binding.cwd || params.data.workspaceDir,
|
|
1153
|
+
approvalPolicy: binding.approvalPolicy ?? runtime.approvalPolicy,
|
|
1154
|
+
approvalsReviewer: runtime.approvalsReviewer,
|
|
1155
|
+
sandboxPolicy: codexSandboxPolicyForTurn(binding.sandbox ?? runtime.sandbox, binding.cwd || params.data.workspaceDir),
|
|
1156
|
+
...binding.model ? { model: binding.model } : {},
|
|
1157
|
+
personality: CODEX_NATIVE_PERSONALITY_NONE,
|
|
1158
|
+
...binding.serviceTier ?? runtime.serviceTier ? { serviceTier: binding.serviceTier ?? runtime.serviceTier } : {}
|
|
1159
|
+
}, { timeoutMs: runtime.requestTimeoutMs })).turn.id;
|
|
1160
|
+
const activeCleanup = trackCodexConversationActiveTurn({
|
|
1161
|
+
sessionFile: params.data.sessionFile,
|
|
1162
|
+
threadId,
|
|
1163
|
+
turnId
|
|
1164
|
+
});
|
|
1165
|
+
collector.setTurnId(turnId);
|
|
1166
|
+
return { reply: { text: (await collector.wait({ timeoutMs: params.timeoutMs ?? DEFAULT_BOUND_TURN_TIMEOUT_MS }).finally(activeCleanup)).replyText.trim() || "Codex completed without a text reply." } };
|
|
1167
|
+
} finally {
|
|
1168
|
+
notificationCleanup();
|
|
1169
|
+
requestCleanup();
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
async function runBoundTurnWithMissingThreadRecovery(params) {
|
|
1173
|
+
try {
|
|
1174
|
+
return await runBoundTurn(params);
|
|
1175
|
+
} catch (error) {
|
|
1176
|
+
if (!isCodexThreadNotFoundError(error)) throw error;
|
|
1177
|
+
const agentLookup = buildAgentLookup({ agentDir: params.data.agentDir });
|
|
1178
|
+
const binding = await readCodexAppServerBinding(params.data.sessionFile, agentLookup);
|
|
1179
|
+
await startCodexConversationThread({
|
|
1180
|
+
pluginConfig: params.pluginConfig,
|
|
1181
|
+
sessionFile: params.data.sessionFile,
|
|
1182
|
+
workspaceDir: binding?.cwd || params.data.workspaceDir,
|
|
1183
|
+
...agentLookup,
|
|
1184
|
+
model: binding?.model,
|
|
1185
|
+
modelProvider: binding?.modelProvider,
|
|
1186
|
+
authProfileId: binding?.authProfileId,
|
|
1187
|
+
approvalPolicy: binding?.approvalPolicy,
|
|
1188
|
+
sandbox: binding?.sandbox,
|
|
1189
|
+
serviceTier: binding?.serviceTier
|
|
1190
|
+
});
|
|
1191
|
+
return await runBoundTurn(params);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
function isCodexThreadNotFoundError(error) {
|
|
1195
|
+
const message = formatErrorMessage(error);
|
|
1196
|
+
return /\bthread not found:/iu.test(message) || /\bbound Codex conversation has no thread binding\b/u.test(message);
|
|
1197
|
+
}
|
|
1198
|
+
function enqueueBoundTurn(key, run) {
|
|
1199
|
+
const state = getGlobalState();
|
|
1200
|
+
const next = (state.queues.get(key) ?? Promise.resolve()).then(run, run);
|
|
1201
|
+
const queued = next.then(() => void 0, () => void 0);
|
|
1202
|
+
state.queues.set(key, queued);
|
|
1203
|
+
next.finally(() => {
|
|
1204
|
+
if (state.queues.get(key) === queued) state.queues.delete(key);
|
|
1205
|
+
}).catch(() => void 0);
|
|
1206
|
+
return next;
|
|
1207
|
+
}
|
|
1208
|
+
function resolveThreadRequestModelProvider(params) {
|
|
1209
|
+
const modelProvider = params.modelProvider?.trim();
|
|
1210
|
+
if (!modelProvider || modelProvider.toLowerCase() === "codex") return;
|
|
1211
|
+
if (isCodexAppServerNativeAuthProfile(params) && (modelProvider.toLowerCase() === "openai" || modelProvider.toLowerCase() === "openai-codex")) return;
|
|
1212
|
+
return modelProvider.toLowerCase() === "openai-codex" ? "openai" : modelProvider;
|
|
1213
|
+
}
|
|
1214
|
+
function buildAgentLookup(params) {
|
|
1215
|
+
const agentDir = params.agentDir?.trim();
|
|
1216
|
+
return {
|
|
1217
|
+
...agentDir ? { agentDir } : {},
|
|
1218
|
+
...params.config ? { config: params.config } : {}
|
|
1219
|
+
};
|
|
1228
1220
|
}
|
|
1229
1221
|
//#endregion
|
|
1230
|
-
export { steerCodexConversationTurn as _,
|
|
1222
|
+
export { steerCodexConversationTurn as _, createCodexCliSessionNodeInvokePolicies as a, readCodexConversationBindingData as b, resolveCodexCliSessionForBindingOnNode as c, parseCodexFastModeArg as d, parseCodexPermissionsModeArg as f, setCodexConversationPermissions as g, setCodexConversationModel as h, createCodexCliSessionNodeHostCommands as i, resumeCodexCliSessionOnNode as l, setCodexConversationFastMode as m, handleCodexConversationInboundClaim as n, formatCodexCliSessions as o, readCodexConversationActiveTurn as p, startCodexConversationThread as r, listCodexCliSessionsOnNode as s, handleCodexConversationBindingResolved as t, formatPermissionsMode as u, stopCodexConversationTurn as v, resolveCodexDefaultWorkspaceDir as x, createCodexCliNodeConversationBindingData as y };
|