@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.
Files changed (26) hide show
  1. package/dist/{client-1sSy4p3z.js → client-SrtuTTVa.js} +10 -3
  2. package/dist/{client-factory-Bk6i4FnW.js → client-factory-A7DQkSLq.js} +1 -1
  3. package/dist/{command-handlers-CcTABuem.js → command-handlers-CLtPT37L.js} +10 -23
  4. package/dist/compact-acCua7zr.js +155 -0
  5. package/dist/{computer-use-Hdq1WgTA.js → computer-use-CVfE2Vfb.js} +2 -2
  6. package/dist/{config-DDMrwfJl.js → config-D6TUuyo2.js} +6 -3
  7. package/dist/{node-cli-sessions-9CAqnIaA.js → conversation-binding-B-GevkEb.js} +426 -434
  8. package/dist/{dynamic-tools-Bq717oJR.js → dynamic-tools-BXAE1U1-.js} +20 -11
  9. package/dist/harness.js +5 -5
  10. package/dist/index.js +451 -17
  11. package/dist/media-understanding-provider.js +7 -5
  12. package/dist/{models-DtGLkqMP.js → models-CC2IusQs.js} +3 -3
  13. package/dist/{notification-correlation-qKY_sgga.js → notification-correlation-D7tJW3FW.js} +26 -11
  14. package/dist/provider.js +3 -3
  15. package/dist/{request-D93E78SA.js → request-BO0z3vA8.js} +3 -3
  16. package/dist/{run-attempt-CT1N__qp.js → run-attempt-BjoI3hUJ.js} +279 -230
  17. package/dist/{session-binding-Bw_mfIW2.js → session-binding-CwNOIDP2.js} +1 -1
  18. package/dist/{shared-client-CFCUGEVs.js → shared-client-CZP1DjaN.js} +3 -4
  19. package/dist/{side-question-CYFMTA1O.js → side-question-Cb4COOQ9.js} +16 -11
  20. package/dist/test-api.js +3 -3
  21. package/dist/{thread-lifecycle-DGoaguJh.js → thread-lifecycle-CXMd9UgJ.js} +10 -12
  22. package/dist/{vision-tools-DOnxzH2y.js → vision-tools-CETeQgze.js} +16 -4
  23. package/npm-shrinkwrap.json +225 -238
  24. package/package.json +7 -7
  25. package/dist/compact-DnTgL6UT.js +0 -475
  26. package/dist/protocol-oeJQu4rs.js +0 -9
@@ -1,11 +1,13 @@
1
- import { a as isCodexFastServiceTier, l as resolveCodexAppServerRuntimeOptions, r as codexSandboxPolicyForTurn } from "./config-DDMrwfJl.js";
2
- import { t as isJsonObject } from "./protocol-oeJQu4rs.js";
3
- import { i as readCodexAppServerBinding, n as isCodexAppServerNativeAuthProfile, o as writeCodexAppServerBinding, r as normalizeCodexAppServerBindingModelProvider, t as clearCodexAppServerBinding } from "./session-binding-Bw_mfIW2.js";
4
- import { r as CODEX_CONTROL_METHODS } from "./request-D93E78SA.js";
5
- import { i as readCodexNotificationTurnId, r as readCodexNotificationThreadId, s as formatCodexDisplayText } from "./notification-correlation-qKY_sgga.js";
6
- import { i as getSharedCodexAppServerClient, u as resolveCodexAppServerAuthProfileIdForAgent } from "./shared-client-CFCUGEVs.js";
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(readRecord$1(readRecord$1(pluginConfig)?.appServer), "defaultWorkspaceDir") ?? process.cwd();
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(readRecord(turn?.error), "message") ?? "codex app-server turn failed";
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 = readStringArray(metadata.mediaPaths).concat(readStringArray(metadata.mediaPath));
398
- const urls = readStringArray(metadata.mediaUrls).concat(readStringArray(metadata.mediaUrl));
399
- const mimeTypes = readStringArray(metadata.mediaTypes).concat(readStringArray(metadata.mediaType));
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/conversation-binding.ts
452
- const DEFAULT_BOUND_TURN_TIMEOUT_MS = 20 * 6e4;
453
- const CODEX_CONVERSATION_GLOBAL_STATE = Symbol.for("openclaw.codex.conversationBinding");
454
- function getGlobalState() {
455
- const globalState = globalThis;
456
- globalState[CODEX_CONVERSATION_GLOBAL_STATE] ??= { queues: /* @__PURE__ */ new Map() };
457
- return globalState[CODEX_CONVERSATION_GLOBAL_STATE];
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
- async function startCodexConversationThread(params) {
460
- const workspaceDir = params.workspaceDir?.trim() || resolveCodexDefaultWorkspaceDir(params.pluginConfig);
461
- const agentDir = params.agentDir?.trim();
462
- const agentLookup = buildAgentLookup({
463
- agentDir,
464
- config: params.config
465
- });
466
- const existingBinding = await readCodexAppServerBinding(params.sessionFile, { ...agentLookup });
467
- const authProfileId = resolveCodexAppServerAuthProfileIdForAgent({
468
- authProfileId: params.authProfileId ?? existingBinding?.authProfileId,
469
- ...agentLookup
470
- });
471
- if (params.threadId?.trim()) await attachExistingThread({
472
- pluginConfig: params.pluginConfig,
473
- sessionFile: params.sessionFile,
474
- threadId: params.threadId.trim(),
475
- workspaceDir,
476
- ...agentDir ? { agentDir } : {},
477
- model: params.model,
478
- modelProvider: params.modelProvider,
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 handleCodexConversationInboundClaim(event, ctx, options = {}) {
505
- const data = readCodexConversationBindingData(ctx.pluginBinding);
506
- if (!data) return;
507
- if (event.commandAuthorized !== true) return { handled: true };
508
- const prompt = event.bodyForAgent?.trim() || event.content?.trim() || "";
509
- if (!prompt) return { handled: true };
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 (nativeExecutionBlock) return {
520
- handled: true,
521
- reply: { text: nativeExecutionBlock }
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 handleCodexConversationBindingResolved(event) {
568
- if (event.status !== "denied") return;
569
- const data = readCodexConversationBindingDataRecord(event.request.data ?? {});
570
- if (!data || data.kind !== "codex-app-server-session") return;
571
- await clearCodexAppServerBinding(data.sessionFile);
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
- async function attachExistingThread(params) {
574
- const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
575
- const agentLookup = buildAgentLookup({
576
- agentDir: params.agentDir,
577
- config: params.config
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
- function isRecord(value) {
1227
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
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 _, resolveCodexCliSessionForBindingOnNode as a, readCodexConversationBindingData as b, handleCodexConversationInboundClaim as c, parseCodexFastModeArg as d, parseCodexPermissionsModeArg as f, setCodexConversationPermissions as g, setCodexConversationModel as h, listCodexCliSessionsOnNode as i, startCodexConversationThread as l, setCodexConversationFastMode as m, createCodexCliSessionNodeInvokePolicies as n, resumeCodexCliSessionOnNode as o, readCodexConversationActiveTurn as p, formatCodexCliSessions as r, handleCodexConversationBindingResolved as s, createCodexCliSessionNodeHostCommands as t, formatPermissionsMode as u, stopCodexConversationTurn as v, resolveCodexDefaultWorkspaceDir as x, createCodexCliNodeConversationBindingData as y };
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 };