@openclaw/codex 2026.7.2-beta.1 → 2026.7.2-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{app-server-policy-DVcxdse_.js → app-server-policy-DUMF2Xyr.js} +1 -1
- package/dist/{attempt-notifications-DEv9h7iC.js → attempt-notifications-Ecjyzufe.js} +3 -6
- package/dist/cli-metadata.js +1 -1
- package/dist/{command-formatters-D6ZlGNmb.js → command-formatters-NCTL7A2q.js} +38 -3
- package/dist/{command-handlers-Cpl9fUWv.js → command-handlers-DtFbZiba.js} +70 -15
- package/dist/{compact-FnJmiPNA.js → compact-Kgy26gIM.js} +4 -5
- package/dist/{computer-use-DVzr8OP1.js → computer-use-C7x4ZaqR.js} +4 -4
- package/dist/{conversation-control-DtQ-E7mr.js → conversation-control-B0s3aIjT.js} +5 -7
- package/dist/doctor-contract-api.js +1 -1
- package/dist/{dynamic-tools-BMLoaTeG.js → dynamic-tools-D_vSVmv9.js} +851 -434
- package/dist/harness.js +11 -6
- package/dist/index.js +52 -31
- package/dist/{media-understanding-provider-D580L0P8.js → media-understanding-provider-IPNNQre6.js} +11 -12
- package/dist/media-understanding-provider.js +1 -1
- package/dist/{models-DuKzZA6G.js → models-DD5zBdX5.js} +5 -9
- package/dist/{notification-correlation-DfaCm0mx.js → notification-correlation-C8DVQ_Bq.js} +3 -3
- package/dist/{plugin-app-cache-key-BKNjHMEs.js → plugin-app-cache-key-DJbhLYSK.js} +2 -2
- package/dist/{rate-limits-Dhp04Rqo.js → rate-limits-CqhWQ2R9.js} +9 -6
- package/dist/{request-DborTWgw.js → request-9yf9pZjf.js} +54 -8
- package/dist/{run-attempt-VPVJoYDP.js → run-attempt-BIoO8ISr.js} +637 -351
- package/dist/{config-c48K5HP9.js → session-binding-Bmt20HfB.js} +788 -3
- package/dist/{session-catalog-7H112Tr_.js → session-catalog-CA7R-hX2.js} +82 -45
- package/dist/{session-cli-B28RhCyp.js → session-cli-Bxk3-Wqz.js} +2 -2
- package/dist/{shared-client-D4mFI9al.js → shared-client-CSXLtCLE.js} +304 -81
- package/dist/{side-question-CgJBz52s.js → side-question-DtzquMOb.js} +15 -13
- package/dist/{thread-lifecycle-BgLXzjvV.js → thread-lifecycle-CQ_9mAa1.js} +1821 -1582
- package/dist/{protocol-validators-dZQ-UTOa.js → transcript-mirror-D9rTxx2P.js} +485 -2
- package/dist/usage-CuPpFgTc.js +25 -0
- package/dist/{web-search-provider.runtime-Bs-eTn5U.js → web-search-provider.runtime-_x5XTeqh.js} +3 -3
- package/npm-shrinkwrap.json +30 -30
- package/openclaw.plugin.json +21 -6
- package/package.json +6 -6
- package/dist/capabilities-42Dfn2TV.js +0 -33
- package/dist/prompt-overlay.js +0 -15
- package/dist/protocol-BMifTfdW.js +0 -15
- package/dist/provider-catalog.js +0 -118
- package/dist/provider-discovery.js +0 -34
- package/dist/provider.js +0 -307
- package/dist/session-binding-BMfX1OX-.js +0 -786
- package/dist/transcript-mirror-DhLwFIL4.js +0 -485
|
@@ -1,212 +1,328 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { S as updateActiveTurnItemIds, _ as readCodexNotificationItem, a as isCodexTurnAbortMarkerNotification, b as shouldDisarmAssistantCompletionIdleWatch, c as isPendingOpenClawDynamicToolCompletionNotification, d as isRawReasoningCompletionNotification, f as isRawToolOutputCompletionNotification, g as isTerminalTurnStatus, h as isRetryableErrorNotification, i as isAssistantCompletionReleaseNotification, l as isRawAssistantProgressNotification, m as isReasoningProgressNotification, n as describeNotificationActivity, o as isFileChangePatchUpdatedNotification, p as isReasoningItemCompletionNotification, r as isAssistantCommentaryCompletionNotification, s as isNativeToolProgressNotification, t as codexExecutionToolName, u as isRawFunctionToolOutputCompletionNotification, v as readNotificationItemId, x as updateActiveCompletionBlockerItemIds, y as readRawResponseToolCallId } from "./attempt-notifications-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { i as defaultCodexAppInventoryCache, n as buildCodexAppServerRuntimeFingerprint, r as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-
|
|
6
|
-
import { A as
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { a as
|
|
10
|
-
import { i as mirrorPromptAtTurnStartBestEffort, n as createCodexAppServerUserMessagePersistenceNotifier, o as buildCodexUserPromptMessage, t as codexTranscriptMirrorRuntime } from "./transcript-mirror-DhLwFIL4.js";
|
|
11
|
-
import { t as CODEX_CONTROL_METHODS } from "./capabilities-42Dfn2TV.js";
|
|
12
|
-
import { r as formatCodexDisplayText } from "./command-formatters-D6ZlGNmb.js";
|
|
13
|
-
import { n as resolveCodexAppServerForOpenClawToolPolicy, t as resolveCodexAppServerForModelProvider } from "./app-server-policy-DVcxdse_.js";
|
|
14
|
-
import { a as resolveCodexBindingAppServerConnection, n as readCodexNotificationThreadId, r as readCodexNotificationTurnId, t as isCodexNotificationForTurn } from "./notification-correlation-DfaCm0mx.js";
|
|
1
|
+
import { A as isCodexAppServerStartupError, B as isCodexAppServerApprovalRequest, C as resolveCodexAppServerFallbackApiKeyCacheKey, D as resolveCodexAppServerHomeDir, F as resolveCodexTurnCompletionIdleTimeoutMs, H as isCodexAppServerConnectionClosedError, I as resolveCodexTurnTerminalIdleTimeoutMs, K as isCodexAppServerRequestTimeoutError, L as withCodexStartupTimeout, M as resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs, N as resolveCodexStartupTimeoutMs, O as CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS, P as resolveCodexTurnAssistantCompletionIdleTimeoutMs, R as CodexAppServerRpcError, S as resolveCodexAppServerAuthProfileIdForAgent, T as resolveCodexAppServerPreparedAuthHandoff, V as isCodexAppServerBrokenPipeError, X as flattenCodexDynamicToolFunctions, Z as isJsonObject, _ as readCodexRateLimitsRevision, a as getLeasedSharedCodexAppServerClient, b as resolveCodexAppServerAuthAccountCacheKey, c as releaseLeasedSharedCodexAppServerClient, d as retireSharedCodexAppServerClientIfCurrent, g as ensureCodexAppServerClientRuntime, k as CodexAppServerStartupError, n as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, o as isCodexAppServerStartSelectionChangedError, t as clearSharedCodexAppServerClientIfCurrent, u as retainSharedCodexAppServerClientIfCurrent, v as readRecentCodexRateLimits, w as resolveCodexAppServerPreparedApiKeyCacheKey, x as resolveCodexAppServerAuthProfileId } from "./shared-client-CSXLtCLE.js";
|
|
2
|
+
import { S as updateActiveTurnItemIds, _ as readCodexNotificationItem, a as isCodexTurnAbortMarkerNotification, b as shouldDisarmAssistantCompletionIdleWatch, c as isPendingOpenClawDynamicToolCompletionNotification, d as isRawReasoningCompletionNotification, f as isRawToolOutputCompletionNotification, g as isTerminalTurnStatus, h as isRetryableErrorNotification, i as isAssistantCompletionReleaseNotification, l as isRawAssistantProgressNotification, m as isReasoningProgressNotification, n as describeNotificationActivity, o as isFileChangePatchUpdatedNotification, p as isReasoningItemCompletionNotification, r as isAssistantCommentaryCompletionNotification, s as isNativeToolProgressNotification, t as codexExecutionToolName, u as isRawFunctionToolOutputCompletionNotification, v as readNotificationItemId, x as updateActiveCompletionBlockerItemIds, y as readRawResponseToolCallId } from "./attempt-notifications-Ecjyzufe.js";
|
|
3
|
+
import { C as resolveCodexComputerUseConfig, D as resolveOpenClawExecPolicyForCodexAppServer, O as shouldAutoApproveCodexAppServerApprovals, T as resolveCodexPluginsPolicy, c as sessionBindingIdentity, g as isCodexAppServerApprovalPolicyAllowedByRequirements, k as withMcpElicitationsApprovalPolicy, s as reclaimCurrentCodexSessionGeneration, v as isCodexSandboxExecServerEnabled, w as resolveCodexModelBackedReviewerPolicyContext, y as readCodexPluginConfig } from "./session-binding-Bmt20HfB.js";
|
|
4
|
+
import { _ as readCodexTurnCompletedNotification, f as assertCodexTurnStartResponse, i as mirrorPromptAtTurnStartBestEffort, n as createCodexAppServerUserMessagePersistenceNotifier, o as buildCodexUserPromptMessage, p as readCodexDynamicToolCallParams, t as codexTranscriptMirrorRuntime } from "./transcript-mirror-D9rTxx2P.js";
|
|
5
|
+
import { i as defaultCodexAppInventoryCache, n as buildCodexAppServerRuntimeFingerprint, r as buildCodexPluginAppCacheKey } from "./plugin-app-cache-key-DJbhLYSK.js";
|
|
6
|
+
import { A as CodexAppServerUnsafeSubscriptionError, B as fitCodexProjectedContextForTurnStart, D as resolveCodexDynamicToolsLoadingForRuntime, F as areCodexDynamicToolFingerprintsCompatible, G as buildCodexPluginThreadConfig, H as resolveCodexContextEngineProjectionMaxChars, I as codexDynamicToolsFingerprint, J as mergeCodexThreadConfigs, K as buildCodexPluginThreadConfigInputFingerprint, L as codexLegacyDynamicToolsFingerprint, M as interruptCodexTurnBestEffort, N as retireCodexAppServerClientAfterTimedOutTurn, O as CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS, P as unsubscribeCodexThreadBestEffort, Q as resolveRecoverableCodexPluginConfigKeys, R as buildContextEngineBinding, U as resolveCodexContextEngineProjectionReserveTokens, V as projectContextEngineAssemblyForCodex, Y as shouldBuildCodexPluginThreadConfig, b as isCodexAppServerProfilerEnabled, d as resolveCodexWebSearchPlan, f as buildDeveloperInstructions, g as resolveCodexAppServerThreadModelSelection, j as closeCodexStartupClientBestEffort, k as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, n as buildTurnStartParams, q as buildCodexPluginThreadConfigTimeoutFallback, r as buildCodexUserInput, s as startOrResumeThread, t as buildTurnCollaborationMode, w as isSystemAgentOnlyCodexDynamicToolAllowlist, x as filterCodexDynamicTools, z as isContextEngineBindingCompatible } from "./thread-lifecycle-CQ_9mAa1.js";
|
|
7
|
+
import { r as formatCodexDisplayText } from "./command-formatters-NCTL7A2q.js";
|
|
8
|
+
import { n as resolveCodexAppServerForOpenClawToolPolicy, t as resolveCodexAppServerForModelProvider } from "./app-server-policy-DUMF2Xyr.js";
|
|
9
|
+
import { a as resolveCodexBindingAppServerConnection, n as readCodexNotificationThreadId, r as readCodexNotificationTurnId, t as isCodexNotificationForTurn } from "./notification-correlation-C8DVQ_Bq.js";
|
|
15
10
|
import { n as prepareCodexAppServerAuthBinding } from "./auth-binding-C1czuHPA.js";
|
|
16
|
-
import { $ as
|
|
17
|
-
import { a as runCodexComputerUseLiveTest, r as killStaleComputerUseMcpChildren, t as ensureCodexComputerUse } from "./computer-use-
|
|
11
|
+
import { $ as resolveCodexToolProgressDetailMode, B as hasPendingDynamicToolTerminalDiagnostic, C as CodexAppServerEventProjector, D as markCodexAuthProfileBlockedFromRateLimits, E as isCodexUsageLimitPromptError, F as emitCodexNativePreToolUseFailureDiagnostic, G as shouldBlockTerminalReleaseForNonTerminalDynamicToolResult, H as isMatchingDynamicToolTerminalDiagnostic, I as resolveCodexNativeHookRelayEvents, J as toCodexDynamicToolProtocolResponse, K as shouldReleaseTurnAfterTerminalDynamicTool, L as resolveCodexNativeHookRelayTtlMs, M as buildCodexNativeHookRelayConfig, N as buildCodexNativeHookRelayDisabledConfig, O as refreshCodexUsageLimitPromptError, P as createCodexNativeHookRelay, Q as inferCodexDynamicToolMeta, R as scheduleCodexNativeHookRelayUnregister, T as formatCodexTurnStartUsageLimitError, U as resolveDynamicToolCallTimeoutMs, V as isDynamicToolTerminalDiagnosticEvent, W as resolveTerminalDynamicToolBatchAction, X as readCodexMirroredSessionHistoryMessages, Y as resolveCodexToolAbortTerminalReason, Z as shouldEmitTranscriptToolProgress, _ as resolveCodexMessageToolProvider, a as emitDynamicToolStartedDiagnostic, b as shouldRequireCodexSandboxExecServerEnvironment, d as createCodexDynamicToolBuildStageTracker, et as sanitizeCodexToolArguments, f as disableCodexPluginThreadConfig, g as resolveCodexExternalSandboxPolicyForOpenClawSandbox, h as resolveCodexAppServerHookChannelId, i as emitDynamicToolErrorDiagnostic, j as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, l as settleCodexSourceReplyFinality, m as resolveCodexAppServerExecutionCwd, n as handleCodexAppServerApprovalRequest, nt as redactCodexEventKind, o as emitDynamicToolTerminalDiagnostic, p as formatCodexDynamicToolBuildStageSummary, q as toCodexDynamicToolProgressResponse, r as handleCodexAppServerElicitationRequest, rt as resolveCodexLocalRuntimeAttribution, s as resolveCodexProviderWebSearchSupport, t as createCodexDynamicToolBridge, tt as sanitizeCodexToolResponse, u as buildDynamicTools, v as resolveCodexSandboxEnvironmentSelection, w as createCodexUsageLimitPromptError, x as shouldWarnCodexDynamicToolBuildStageSummary, y as shouldEnableCodexAppServerNativeToolSurface, z as handleDynamicToolCallWithTimeout } from "./dynamic-tools-D_vSVmv9.js";
|
|
12
|
+
import { a as runCodexComputerUseLiveTest, r as killStaleComputerUseMcpChildren, t as ensureCodexComputerUse } from "./computer-use-C7x4ZaqR.js";
|
|
18
13
|
import fs from "node:fs/promises";
|
|
19
14
|
import path, { posix } from "node:path";
|
|
20
15
|
import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
|
|
21
|
-
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
16
|
+
import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
22
17
|
import { createHash, randomUUID } from "node:crypto";
|
|
23
|
-
import {
|
|
18
|
+
import { resolveAgentDir, resolveAgentWorkspaceDir } from "openclaw/plugin-sdk/agent-runtime";
|
|
24
19
|
import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
|
|
25
20
|
import { asFiniteNumber, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
26
|
-
import { CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, FAST_MODE_AUTO_PROGRESS_KIND, assembleHarnessContextEngine, assertContextEngineHostSupport, awaitAgentEndSideEffects, bootstrapHarnessContextEngine, buildAgentHarnessUserInputAnswers, buildBootstrapContextForFiles, buildHarnessContextEngineRuntimeContext, buildHarnessContextEngineRuntimeContextFromUsage, clearActiveEmbeddedRun, deliverAgentHarnessUserInputPrompt, embeddedAgentLog, emitAgentEvent, emptyAgentHarnessUserInputAnswers, finalizeHarnessContextEngineTurn, formatErrorMessage, formatFastModeAutoProgressText, getAgentHarnessHookRunner, getBeforeToolCallPolicyDiagnosticState, isActiveHarnessContextEngine, isHostScopedAgentToolActive, loadCodexBundleMcpThreadConfig, materializeRequesterScopedMcpToolsForHarnessRun, resolveAgentDir as resolveAgentDir$1, resolveAgentHarnessBeforePromptBuildResult, resolveAgentRunAbortLifecycleFields, resolveBootstrapFilesForRun, resolveContextEngineOwnerPluginId, resolveFastModeForElapsed, resolveSandboxContext, resolveSessionAgentIds as resolveSessionAgentIds$1, resolveUserPath, runAgentCleanupStep, runAgentEndSideEffects, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
21
|
+
import { CODEX_APP_SERVER_CONTEXT_ENGINE_HOST, FAST_MODE_AUTO_PROGRESS_KIND, assembleHarnessContextEngine, assertContextEngineHostSupport, awaitAgentEndSideEffects, bootstrapHarnessContextEngine, buildAgentHarnessUserInputAnswers, buildBootstrapContextForFiles, buildHarnessContextEngineRuntimeContext, buildHarnessContextEngineRuntimeContextFromUsage, callGatewayTool, cancelPendingAgentQuestionForSession, claimPendingAgentQuestionAnswer, clearActiveEmbeddedRun, deliverAgentHarnessUserInputPrompt, embeddedAgentLog, emitAgentEvent, emptyAgentHarnessUserInputAnswers, finalizeHarnessContextEngineTurn, formatErrorMessage, formatFastModeAutoProgressText, getAgentHarnessHookRunner, getBeforeToolCallPolicyDiagnosticState, isActiveHarnessContextEngine, isHostScopedAgentToolActive, loadCodexBundleMcpThreadConfig, materializeRequesterScopedMcpToolsForHarnessRun, resolveAgentDir as resolveAgentDir$1, resolveAgentHarnessBeforePromptBuildResult, resolveAgentRunAbortLifecycleFields, resolveBootstrapFilesForRun, resolveContextEngineOwnerPluginId, resolveFastModeForElapsed, resolveSandboxContext, resolveSessionAgentIds as resolveSessionAgentIds$1, resolveUserPath, runAgentCleanupStep, runAgentEndSideEffects, runAgentHarnessGatewayQuestion, runAgentHarnessLlmInputHook, runAgentHarnessLlmOutputHook, runHarnessContextEngineMaintenance, setActiveEmbeddedRun, supportsModelTools } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
27
22
|
import { spawn } from "node:child_process";
|
|
28
23
|
import { once } from "node:events";
|
|
29
24
|
import { isIP } from "node:net";
|
|
30
25
|
import { WebSocketServer } from "ws";
|
|
31
26
|
import { parseSqliteSessionFileMarker } from "openclaw/plugin-sdk/session-store-runtime";
|
|
32
|
-
import {
|
|
27
|
+
import { fileURLToPath } from "node:url";
|
|
33
28
|
import { createDiagnosticTraceContextFromActiveScope, emitTrustedDiagnosticEvent, emitTrustedDiagnosticEventWithPrivateData, freezeDiagnosticTraceContext, onInternalDiagnosticEvent, resolveDiagnosticModelContentCapturePolicy } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
29
|
+
import { prepareMemorySystemPromptAddition } from "openclaw/plugin-sdk/core";
|
|
34
30
|
import { MESSAGE_TOOL_DELIVERY_HINTS } from "openclaw/plugin-sdk/message-tool-delivery-hints";
|
|
35
31
|
import { createAgentHarnessTaskRuntime, deliverAgentHarnessTaskCompletion, isDurableAgentHarnessCompletionDelivery } from "openclaw/plugin-sdk/agent-harness-task-runtime";
|
|
36
32
|
import { SsrFBlockedError, isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
37
33
|
//#region extensions/codex/src/app-server/attempt-steering.ts
|
|
38
34
|
/**
|
|
39
|
-
* Debounced steering queue for forwarding user
|
|
35
|
+
* Debounced steering queue for forwarding user messages to an active Codex
|
|
40
36
|
* app-server turn.
|
|
41
37
|
*/
|
|
42
38
|
const CODEX_STEER_ALL_DEBOUNCE_MS = 500;
|
|
43
39
|
/**
|
|
44
|
-
* Creates a queue that batches steer
|
|
45
|
-
* `turn/steer` requests.
|
|
40
|
+
* Creates a queue that batches steer messages while still serializing
|
|
41
|
+
* app-server `turn/steer` requests.
|
|
46
42
|
*/
|
|
47
43
|
function createCodexSteeringQueue(params) {
|
|
48
|
-
let
|
|
44
|
+
let batchedMessages = [];
|
|
45
|
+
const dispatchedBatches = /* @__PURE__ */ new Map();
|
|
46
|
+
const pendingMessages = /* @__PURE__ */ new Set();
|
|
49
47
|
let batchTimer;
|
|
48
|
+
let batchSequence = 0;
|
|
50
49
|
let sendChain = Promise.resolve();
|
|
50
|
+
let closedError;
|
|
51
51
|
const clearBatchTimer = () => {
|
|
52
52
|
if (batchTimer) {
|
|
53
53
|
clearTimeout(batchTimer);
|
|
54
54
|
batchTimer = void 0;
|
|
55
55
|
}
|
|
56
56
|
};
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
const resolveItem = (item) => {
|
|
58
|
+
if (item.settled) return;
|
|
59
|
+
item.settled = true;
|
|
60
|
+
pendingMessages.delete(item);
|
|
61
|
+
item.resolve();
|
|
62
|
+
};
|
|
63
|
+
const rejectItem = (item, error) => {
|
|
64
|
+
if (item.settled) return;
|
|
65
|
+
item.settled = true;
|
|
66
|
+
pendingMessages.delete(item);
|
|
67
|
+
item.reject(error);
|
|
68
|
+
};
|
|
69
|
+
const closeQueue = (error) => {
|
|
70
|
+
if (closedError) return;
|
|
71
|
+
closedError = error;
|
|
72
|
+
params.signal.removeEventListener("abort", abortQueue);
|
|
73
|
+
clearBatchTimer();
|
|
74
|
+
batchedMessages = [];
|
|
75
|
+
dispatchedBatches.clear();
|
|
76
|
+
for (const item of pendingMessages) rejectItem(item, error);
|
|
77
|
+
};
|
|
78
|
+
const abortQueue = () => {
|
|
79
|
+
closeQueue(/* @__PURE__ */ new Error("codex app-server steering queue aborted"));
|
|
80
|
+
};
|
|
81
|
+
const cancelQueue = () => {
|
|
82
|
+
closeQueue(/* @__PURE__ */ new Error("codex app-server steering queue cancelled"));
|
|
83
|
+
};
|
|
84
|
+
const sendBatch = async (items) => {
|
|
85
|
+
const liveItems = items.filter((item) => !item.settled);
|
|
86
|
+
if (liveItems.length === 0) return;
|
|
87
|
+
const unavailableError = closedError ?? (params.signal.aborted ? /* @__PURE__ */ new Error("codex app-server steering queue aborted") : void 0);
|
|
88
|
+
if (unavailableError) {
|
|
89
|
+
for (const item of liveItems) rejectItem(item, unavailableError);
|
|
90
|
+
throw unavailableError;
|
|
91
|
+
}
|
|
92
|
+
const clientUserMessageId = `openclaw:${params.turnId}:steer:${++batchSequence}`;
|
|
93
|
+
const batch = { items: liveItems };
|
|
94
|
+
dispatchedBatches.set(clientUserMessageId, batch);
|
|
95
|
+
try {
|
|
96
|
+
await params.client.request("turn/steer", {
|
|
97
|
+
threadId: params.threadId,
|
|
98
|
+
expectedTurnId: params.turnId,
|
|
99
|
+
input: liveItems.flatMap((item) => buildCodexUserInput(item.text, item.images)),
|
|
100
|
+
clientUserMessageId
|
|
101
|
+
});
|
|
102
|
+
} catch (error) {
|
|
103
|
+
dispatchedBatches.delete(clientUserMessageId);
|
|
104
|
+
for (const item of liveItems) rejectItem(item, error);
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
65
107
|
};
|
|
66
|
-
const enqueueSend = (
|
|
67
|
-
const send = sendChain.then(() =>
|
|
68
|
-
sendChain = send
|
|
108
|
+
const enqueueSend = (items) => {
|
|
109
|
+
const send = sendChain.then(() => sendBatch(items));
|
|
110
|
+
sendChain = send;
|
|
111
|
+
send.catch((error) => {
|
|
112
|
+
for (const item of items) rejectItem(item, error);
|
|
69
113
|
embeddedAgentLog.debug("codex app-server queued steer failed", { error });
|
|
70
114
|
});
|
|
71
115
|
return send;
|
|
72
116
|
};
|
|
73
117
|
const flushBatch = () => {
|
|
74
118
|
clearBatchTimer();
|
|
75
|
-
const items =
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
send
|
|
79
|
-
|
|
80
|
-
}, (error) => {
|
|
81
|
-
for (const item of items) item.reject(error);
|
|
82
|
-
});
|
|
119
|
+
const items = batchedMessages;
|
|
120
|
+
batchedMessages = [];
|
|
121
|
+
if (items.length === 0) return sendChain;
|
|
122
|
+
const send = enqueueSend(items);
|
|
123
|
+
send.catch(() => void 0);
|
|
83
124
|
return send;
|
|
84
125
|
};
|
|
126
|
+
const createPendingMessage = (text, images) => {
|
|
127
|
+
let resolveDelivery;
|
|
128
|
+
let rejectDelivery;
|
|
129
|
+
const delivery = new Promise((resolve, reject) => {
|
|
130
|
+
resolveDelivery = resolve;
|
|
131
|
+
rejectDelivery = reject;
|
|
132
|
+
});
|
|
133
|
+
const item = {
|
|
134
|
+
text,
|
|
135
|
+
images,
|
|
136
|
+
resolve: resolveDelivery,
|
|
137
|
+
reject: rejectDelivery,
|
|
138
|
+
settled: false
|
|
139
|
+
};
|
|
140
|
+
pendingMessages.add(item);
|
|
141
|
+
return {
|
|
142
|
+
item,
|
|
143
|
+
delivery
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
params.signal.addEventListener("abort", abortQueue, { once: true });
|
|
147
|
+
if (params.signal.aborted) abortQueue();
|
|
85
148
|
return {
|
|
86
149
|
async queue(text, options) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
text
|
|
91
|
-
resolve,
|
|
92
|
-
reject
|
|
93
|
-
});
|
|
94
|
-
clearBatchTimer();
|
|
95
|
-
const debounceMs = normalizeCodexSteerDebounceMs(options?.debounceMs);
|
|
96
|
-
if (debounceMs === 0) {
|
|
97
|
-
flushBatch().catch(() => void 0);
|
|
150
|
+
const pendingUserInput = params.claimPendingUserInput();
|
|
151
|
+
if (pendingUserInput) {
|
|
152
|
+
if (!options?.images?.length) {
|
|
153
|
+
pendingUserInput.answer(text);
|
|
98
154
|
return;
|
|
99
155
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
156
|
+
flushBatch().catch(() => void 0);
|
|
157
|
+
const { item, delivery } = createPendingMessage(text, options.images);
|
|
158
|
+
await Promise.all([enqueueSend([item]).finally(() => pendingUserInput.cancel()), delivery]);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (closedError) throw closedError;
|
|
162
|
+
if (params.signal.aborted) throw new Error("codex app-server steering queue aborted");
|
|
163
|
+
const { item, delivery } = createPendingMessage(text, options?.images);
|
|
164
|
+
batchedMessages.push(item);
|
|
165
|
+
clearBatchTimer();
|
|
166
|
+
const debounceMs = normalizeCodexSteerDebounceMs(options?.debounceMs);
|
|
167
|
+
if (debounceMs === 0) flushBatch();
|
|
168
|
+
else batchTimer = setTimeout(() => {
|
|
169
|
+
batchTimer = void 0;
|
|
170
|
+
flushBatch();
|
|
171
|
+
}, debounceMs);
|
|
172
|
+
return await delivery;
|
|
105
173
|
},
|
|
106
174
|
async flushPending() {
|
|
175
|
+
if (closedError) return;
|
|
107
176
|
await flushBatch().catch(() => void 0);
|
|
108
177
|
},
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
for (const item of items) item
|
|
114
|
-
|
|
178
|
+
confirmConsumed(clientUserMessageId) {
|
|
179
|
+
const batch = dispatchedBatches.get(clientUserMessageId);
|
|
180
|
+
if (!batch) return false;
|
|
181
|
+
dispatchedBatches.delete(clientUserMessageId);
|
|
182
|
+
for (const item of batch.items) resolveItem(item);
|
|
183
|
+
return true;
|
|
184
|
+
},
|
|
185
|
+
cancel: cancelQueue
|
|
115
186
|
};
|
|
116
187
|
}
|
|
117
188
|
/** Normalizes steer debounce milliseconds, preserving explicit zero. */
|
|
118
189
|
function normalizeCodexSteerDebounceMs(value) {
|
|
119
190
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : CODEX_STEER_ALL_DEBOUNCE_MS;
|
|
120
191
|
}
|
|
121
|
-
/** Converts plain text into the Codex app-server user-input shape. */
|
|
122
|
-
function toCodexTextInput(text) {
|
|
123
|
-
return {
|
|
124
|
-
type: "text",
|
|
125
|
-
text,
|
|
126
|
-
text_elements: []
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
192
|
//#endregion
|
|
130
193
|
//#region extensions/codex/src/app-server/user-input-bridge.ts
|
|
131
|
-
/**
|
|
132
|
-
|
|
133
|
-
* turns replies into app-server answer payloads.
|
|
134
|
-
*/
|
|
194
|
+
/** Bridges Codex request_user_input calls to gateway questions and secret text prompts. */
|
|
195
|
+
const DEFAULT_USER_INPUT_TIMEOUT_MS = 15 * 6e4;
|
|
135
196
|
/** Creates a per-turn bridge for pending Codex user-input requests. */
|
|
136
197
|
function createCodexUserInputBridge(params) {
|
|
137
|
-
let
|
|
138
|
-
|
|
139
|
-
|
|
198
|
+
let sensitiveInput;
|
|
199
|
+
let pendingGateway;
|
|
200
|
+
const gatewayCall = params.gatewayCall ?? callGatewayTool;
|
|
201
|
+
const resolveSecret = (value) => {
|
|
202
|
+
const current = sensitiveInput;
|
|
140
203
|
if (!current) return;
|
|
141
|
-
|
|
204
|
+
sensitiveInput = void 0;
|
|
142
205
|
current.cleanup();
|
|
143
206
|
current.resolve(value);
|
|
144
207
|
};
|
|
208
|
+
const resolveSecretIfCurrent = (current, value) => {
|
|
209
|
+
if (sensitiveInput !== current) return false;
|
|
210
|
+
resolveSecret(value);
|
|
211
|
+
return true;
|
|
212
|
+
};
|
|
213
|
+
const cancelGateway = () => {
|
|
214
|
+
pendingGateway?.abort.abort(/* @__PURE__ */ new Error("Codex user input request cancelled"));
|
|
215
|
+
};
|
|
145
216
|
return {
|
|
146
217
|
async handleRequest(request) {
|
|
147
218
|
const requestParams = readUserInputParams(request.params);
|
|
148
219
|
if (!requestParams) return;
|
|
149
220
|
if (requestParams.threadId !== params.threadId || requestParams.turnId !== params.turnId) return;
|
|
150
221
|
if (requestParams.questions.length === 0) return emptyUserInputResponse();
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
222
|
+
resolveSecret(emptyUserInputResponse());
|
|
223
|
+
cancelGateway();
|
|
224
|
+
if (requestParams.questions.some((question) => question.isSecret)) return new Promise((resolve) => {
|
|
225
|
+
const abortListener = () => resolveSecret(emptyUserInputResponse());
|
|
154
226
|
const cleanup = () => params.signal?.removeEventListener("abort", abortListener);
|
|
155
|
-
|
|
227
|
+
sensitiveInput = {
|
|
156
228
|
requestId: request.id,
|
|
157
229
|
threadId: requestParams.threadId,
|
|
158
|
-
turnId: requestParams.turnId,
|
|
159
|
-
itemId: requestParams.itemId,
|
|
160
230
|
questions: requestParams.questions,
|
|
231
|
+
claimed: false,
|
|
161
232
|
resolve,
|
|
162
233
|
cleanup
|
|
163
234
|
};
|
|
164
235
|
params.signal?.addEventListener("abort", abortListener, { once: true });
|
|
165
236
|
if (params.signal?.aborted) {
|
|
166
|
-
|
|
237
|
+
resolveSecret(emptyUserInputResponse());
|
|
167
238
|
return;
|
|
168
239
|
}
|
|
169
|
-
|
|
170
|
-
|
|
240
|
+
deliverAgentHarnessUserInputPrompt(params.paramsForRun, requestParams.questions, {
|
|
241
|
+
formatText: formatCodexDisplayText,
|
|
242
|
+
intro: "Codex needs input:"
|
|
243
|
+
}).catch((error) => {
|
|
244
|
+
embeddedAgentLog.warn("failed to deliver secret codex user input prompt", { error });
|
|
171
245
|
});
|
|
172
246
|
});
|
|
247
|
+
const abort = new AbortController();
|
|
248
|
+
const abortFromRun = () => abort.abort(params.signal?.reason);
|
|
249
|
+
params.signal?.addEventListener("abort", abortFromRun, { once: true });
|
|
250
|
+
if (params.signal?.aborted) abortFromRun();
|
|
251
|
+
pendingGateway = {
|
|
252
|
+
requestId: request.id,
|
|
253
|
+
threadId: requestParams.threadId,
|
|
254
|
+
abort
|
|
255
|
+
};
|
|
256
|
+
try {
|
|
257
|
+
const result = await runAgentHarnessGatewayQuestion({
|
|
258
|
+
questions: requestParams.questions,
|
|
259
|
+
sessionKey: params.paramsForRun.sessionKey ?? params.paramsForRun.sessionId,
|
|
260
|
+
agentId: params.paramsForRun.agentId,
|
|
261
|
+
timeoutMs: requestParams.autoResolutionMs ?? params.paramsForRun.timeoutMs ?? DEFAULT_USER_INPUT_TIMEOUT_MS,
|
|
262
|
+
gatewayCall,
|
|
263
|
+
delivery: params.paramsForRun,
|
|
264
|
+
promptOptions: {
|
|
265
|
+
formatText: formatCodexDisplayText,
|
|
266
|
+
intro: "Codex needs input:"
|
|
267
|
+
},
|
|
268
|
+
signal: abort.signal
|
|
269
|
+
});
|
|
270
|
+
return result.status === "answered" ? result.answers : emptyUserInputResponse();
|
|
271
|
+
} catch (error) {
|
|
272
|
+
embeddedAgentLog.warn("failed to bridge codex user input through gateway", { error });
|
|
273
|
+
return emptyUserInputResponse();
|
|
274
|
+
} finally {
|
|
275
|
+
params.signal?.removeEventListener("abort", abortFromRun);
|
|
276
|
+
if (pendingGateway?.abort === abort) pendingGateway = void 0;
|
|
277
|
+
}
|
|
173
278
|
},
|
|
174
|
-
|
|
175
|
-
const current =
|
|
176
|
-
if (!current) return
|
|
177
|
-
|
|
178
|
-
return
|
|
279
|
+
claimPendingRequest() {
|
|
280
|
+
const current = sensitiveInput;
|
|
281
|
+
if (!current || current.claimed) return;
|
|
282
|
+
current.claimed = true;
|
|
283
|
+
return {
|
|
284
|
+
answer: (text) => resolveSecretIfCurrent(current, buildUserInputResponse(current.questions, text)),
|
|
285
|
+
cancel: () => resolveSecretIfCurrent(current, emptyUserInputResponse())
|
|
286
|
+
};
|
|
179
287
|
},
|
|
180
288
|
handleNotification(notification) {
|
|
181
|
-
if (notification.method !== "serverRequest/resolved"
|
|
289
|
+
if (notification.method !== "serverRequest/resolved") return;
|
|
182
290
|
const notificationParams = isJsonObject(notification.params) ? notification.params : void 0;
|
|
183
291
|
const requestId = notificationParams ? readRequestId(notificationParams) : void 0;
|
|
184
|
-
if (notificationParams
|
|
292
|
+
if (!notificationParams || requestId === void 0) return;
|
|
293
|
+
if (sensitiveInput && readString$3(notificationParams, "threadId") === sensitiveInput.threadId && String(requestId) === String(sensitiveInput.requestId)) resolveSecret(emptyUserInputResponse());
|
|
294
|
+
if (pendingGateway && readString$3(notificationParams, "threadId") === pendingGateway.threadId && String(requestId) === String(pendingGateway.requestId)) pendingGateway.abort.abort(/* @__PURE__ */ new Error("Codex server request resolved"));
|
|
185
295
|
},
|
|
186
296
|
cancelPending() {
|
|
187
|
-
|
|
297
|
+
resolveSecret(emptyUserInputResponse());
|
|
298
|
+
cancelGateway();
|
|
188
299
|
}
|
|
189
300
|
};
|
|
190
301
|
}
|
|
191
302
|
function readUserInputParams(value) {
|
|
192
303
|
if (!isJsonObject(value)) return;
|
|
193
|
-
const threadId = readString$
|
|
194
|
-
const turnId = readString$
|
|
195
|
-
const itemId = readString$
|
|
304
|
+
const threadId = readString$3(value, "threadId");
|
|
305
|
+
const turnId = readString$3(value, "turnId");
|
|
306
|
+
const itemId = readString$3(value, "itemId");
|
|
196
307
|
const questionsRaw = value.questions;
|
|
197
308
|
if (!threadId || !turnId || !itemId || !Array.isArray(questionsRaw)) return;
|
|
198
309
|
return {
|
|
199
310
|
threadId,
|
|
200
311
|
turnId,
|
|
201
312
|
itemId,
|
|
202
|
-
questions: questionsRaw.map(
|
|
313
|
+
questions: questionsRaw.map((rawQuestion) => {
|
|
314
|
+
const question = readQuestion(rawQuestion);
|
|
315
|
+
if (question && isJsonObject(rawQuestion) && rawQuestion.multiSelect === true) question.multiSelect = true;
|
|
316
|
+
return question;
|
|
317
|
+
}).filter((question) => Boolean(question)),
|
|
318
|
+
autoResolutionMs: typeof value.autoResolutionMs === "number" && value.autoResolutionMs > 0 ? value.autoResolutionMs : void 0
|
|
203
319
|
};
|
|
204
320
|
}
|
|
205
321
|
function readQuestion(value) {
|
|
206
322
|
if (!isJsonObject(value)) return;
|
|
207
|
-
const id = readString$
|
|
208
|
-
const header = readString$
|
|
209
|
-
const question = readString$
|
|
323
|
+
const id = readString$3(value, "id");
|
|
324
|
+
const header = readString$3(value, "header");
|
|
325
|
+
const question = readString$3(value, "question");
|
|
210
326
|
if (!id || !header || !question) return;
|
|
211
327
|
return {
|
|
212
328
|
id,
|
|
@@ -224,26 +340,20 @@ function readOptions(value) {
|
|
|
224
340
|
}
|
|
225
341
|
function readOption(value) {
|
|
226
342
|
if (!isJsonObject(value)) return;
|
|
227
|
-
const label = readString$
|
|
228
|
-
const description = readString$
|
|
343
|
+
const label = readString$3(value, "label");
|
|
344
|
+
const description = readString$3(value, "description") ?? "";
|
|
229
345
|
return label ? {
|
|
230
346
|
label,
|
|
231
347
|
description
|
|
232
348
|
} : void 0;
|
|
233
349
|
}
|
|
234
|
-
async function deliverUserInputPrompt(params, questions) {
|
|
235
|
-
await deliverAgentHarnessUserInputPrompt(params, questions, {
|
|
236
|
-
formatText: formatCodexDisplayText,
|
|
237
|
-
intro: "Codex needs input:"
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
350
|
function buildUserInputResponse(questions, inputText) {
|
|
241
351
|
return buildAgentHarnessUserInputAnswers(questions, inputText);
|
|
242
352
|
}
|
|
243
353
|
function emptyUserInputResponse() {
|
|
244
354
|
return emptyAgentHarnessUserInputAnswers();
|
|
245
355
|
}
|
|
246
|
-
function readString$
|
|
356
|
+
function readString$3(record, key) {
|
|
247
357
|
const value = record[key];
|
|
248
358
|
return typeof value === "string" ? value : void 0;
|
|
249
359
|
}
|
|
@@ -336,18 +446,35 @@ async function activateCodexAttemptTurn(resources, turnRuntime, lifecycle, notif
|
|
|
336
446
|
client: resourceState.client,
|
|
337
447
|
threadId: resourceState.thread.threadId,
|
|
338
448
|
turnId: activeTurnId,
|
|
339
|
-
|
|
449
|
+
claimPendingUserInput: () => userInputBridgeRef.current?.claimPendingRequest(),
|
|
340
450
|
signal: runAbortController.signal
|
|
341
451
|
});
|
|
342
452
|
steeringQueueRef.current = activeSteeringQueue;
|
|
343
453
|
const handle = {
|
|
344
454
|
kind: "embedded",
|
|
345
455
|
runId: params.runId,
|
|
346
|
-
queueMessage: async (text, optionsLocal) =>
|
|
456
|
+
queueMessage: async (text, optionsLocal) => {
|
|
457
|
+
const isInboundUserMessage = optionsLocal?.isInboundUserMessage === true;
|
|
458
|
+
if (isInboundUserMessage && !optionsLocal?.images?.length) {
|
|
459
|
+
if (await claimPendingAgentQuestionAnswer({
|
|
460
|
+
sessionKey: params.sessionKey ?? params.sessionId,
|
|
461
|
+
text
|
|
462
|
+
})) return;
|
|
463
|
+
} else if (isInboundUserMessage) try {
|
|
464
|
+
await cancelPendingAgentQuestionForSession({
|
|
465
|
+
sessionKey: params.sessionKey ?? params.sessionId,
|
|
466
|
+
resolvedBy: "image-reply"
|
|
467
|
+
});
|
|
468
|
+
} catch (error) {
|
|
469
|
+
embeddedAgentLog.warn("failed to cancel codex gateway question before image steering", { error });
|
|
470
|
+
}
|
|
471
|
+
await activeSteeringQueue.queue(text, optionsLocal);
|
|
472
|
+
},
|
|
347
473
|
isStreaming: () => !state.completed && !runAbortController.signal.aborted,
|
|
348
474
|
isStopped: () => state.completed || state.timedOut || runAbortController.signal.aborted,
|
|
349
475
|
isAbortable: () => !terminalState.terminalOutcomeFrozen || terminalState.sharedAbortAllowedAfterTerminalOutcome,
|
|
350
476
|
isCompacting: () => projectorRef.current?.isCompacting() ?? false,
|
|
477
|
+
supportsQueueMessageImages: true,
|
|
351
478
|
sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
|
|
352
479
|
cancel: () => abortExplicitly("cancelled"),
|
|
353
480
|
abort: () => abortExplicitly("aborted")
|
|
@@ -1058,6 +1185,7 @@ async function prepareCodexAttemptConnection({ params, options }) {
|
|
|
1058
1185
|
isInactiveThreadBootstrapBinding,
|
|
1059
1186
|
usesSupervisionConnection,
|
|
1060
1187
|
startupAuthProfileId,
|
|
1188
|
+
startupAuthRequirement: preparedAuthRoute?.authRequirement,
|
|
1061
1189
|
startupPreparedAuth,
|
|
1062
1190
|
startupClientAuthProfileId,
|
|
1063
1191
|
effectiveWorkspace,
|
|
@@ -1215,13 +1343,14 @@ async function buildCodexWorkspaceBootstrapContext(params) {
|
|
|
1215
1343
|
promptContext: renderCodexWorkspaceBootstrapPromptContext(promptContextFiles),
|
|
1216
1344
|
developerInstructions: renderCodexWorkspaceThreadDeveloperInstructions(developerInstructionFiles),
|
|
1217
1345
|
turnScopedDeveloperInstructions: renderCodexWorkspaceCollaborationDeveloperInstructions(turnScopedDeveloperInstructionFiles),
|
|
1218
|
-
memoryCollaborationInstructions: shouldInjectCodexOpenClawPromptContext(params.params) ? renderCodexWorkspaceMemoryCollaborationInstructions({
|
|
1346
|
+
memoryCollaborationInstructions: shouldInjectCodexOpenClawPromptContext(params.params) ? await renderCodexWorkspaceMemoryCollaborationInstructions({
|
|
1219
1347
|
files: memoryReferenceFiles,
|
|
1220
1348
|
toolNames: params.memoryToolNames,
|
|
1221
1349
|
memoryToolRouted: memoryToolsAvailable,
|
|
1222
1350
|
citationsMode: params.params.config?.memory?.citations,
|
|
1223
1351
|
agentId: params.params.agentId ?? params.sessionAgentId,
|
|
1224
|
-
agentSessionKey: params.sessionKey
|
|
1352
|
+
agentSessionKey: params.sessionKey,
|
|
1353
|
+
sandboxed: params.sandboxed
|
|
1225
1354
|
}) : void 0,
|
|
1226
1355
|
heartbeatCollaborationInstructions: renderCodexWorkspaceHeartbeatReference(heartbeatReferenceFiles)
|
|
1227
1356
|
};
|
|
@@ -1576,24 +1705,26 @@ function renderCodexWorkspaceMemoryReference(params) {
|
|
|
1576
1705
|
for (const file of params.files) lines.push(`- ${file.path}`);
|
|
1577
1706
|
return lines.join("\n").trim();
|
|
1578
1707
|
}
|
|
1579
|
-
function renderCodexWorkspaceMemoryCollaborationInstructions(params) {
|
|
1580
|
-
const sections = [params.memoryToolRouted ? renderCodexMemoryRecallInstructions({
|
|
1708
|
+
async function renderCodexWorkspaceMemoryCollaborationInstructions(params) {
|
|
1709
|
+
const sections = [params.memoryToolRouted ? await renderCodexMemoryRecallInstructions({
|
|
1581
1710
|
toolNames: params.toolNames,
|
|
1582
1711
|
citationsMode: params.citationsMode,
|
|
1583
1712
|
agentId: params.agentId,
|
|
1584
|
-
agentSessionKey: params.agentSessionKey
|
|
1713
|
+
agentSessionKey: params.agentSessionKey,
|
|
1714
|
+
sandboxed: params.sandboxed
|
|
1585
1715
|
}) : void 0, renderCodexWorkspaceMemoryReference({
|
|
1586
1716
|
files: params.files,
|
|
1587
1717
|
toolNames: params.toolNames
|
|
1588
1718
|
})].filter(isNonEmptyString$1);
|
|
1589
1719
|
return sections.length > 0 ? sections.join("\n\n") : void 0;
|
|
1590
1720
|
}
|
|
1591
|
-
function renderCodexMemoryRecallInstructions(params) {
|
|
1592
|
-
const memoryPrompt =
|
|
1721
|
+
async function renderCodexMemoryRecallInstructions(params) {
|
|
1722
|
+
const memoryPrompt = await prepareMemorySystemPromptAddition({
|
|
1593
1723
|
availableTools: new Set(params.toolNames),
|
|
1594
1724
|
citationsMode: params.citationsMode,
|
|
1595
1725
|
agentId: params.agentId,
|
|
1596
|
-
agentSessionKey: params.agentSessionKey
|
|
1726
|
+
agentSessionKey: params.agentSessionKey,
|
|
1727
|
+
sandboxed: params.sandboxed
|
|
1597
1728
|
});
|
|
1598
1729
|
if (!memoryPrompt) return;
|
|
1599
1730
|
return [memoryPrompt, renderCodexMemoryToolSearchBridge(params.toolNames)].filter(isNonEmptyString$1).join("\n").trim();
|
|
@@ -1790,7 +1921,7 @@ function buildCodexAppServerTimeoutDiagnostics(params) {
|
|
|
1790
1921
|
//#region extensions/codex/src/app-server/run-attempt-context.ts
|
|
1791
1922
|
async function prepareCodexAttemptContext(runtime, attemptTools) {
|
|
1792
1923
|
const { connection, runtimeParams, activeSessionId, activeSessionFile, buildActiveRunAttemptParams, effectiveContextWindowInfo, effectiveContextTokenBudget, effectiveRuntimeProviderId, effectiveRuntimeModelId, hookChannelId } = runtime;
|
|
1793
|
-
const { params, sessionAgentId, contextSessionKey, activeContextEngine, initialStartupBindingHadInactiveThreadBootstrap, sandboxSessionKey, effectiveWorkspace, effectiveCwd, agentDir, usesSupervisionConnection, resolvedWorkspace, initialInactiveThreadBootstrapBindingForcedFreshStart } = connection;
|
|
1924
|
+
const { params, sessionAgentId, contextSessionKey, activeContextEngine, initialStartupBindingHadInactiveThreadBootstrap, sandboxSessionKey, effectiveWorkspace, effectiveCwd, agentDir, usesSupervisionConnection, resolvedWorkspace, initialInactiveThreadBootstrapBindingForcedFreshStart, sandbox } = connection;
|
|
1794
1925
|
const { toolBridge } = attemptTools;
|
|
1795
1926
|
const activeTranscriptTarget = {
|
|
1796
1927
|
agentId: sessionAgentId,
|
|
@@ -1854,7 +1985,8 @@ async function prepareCodexAttemptContext(runtime, attemptTools) {
|
|
|
1854
1985
|
effectiveWorkspace,
|
|
1855
1986
|
sessionKey: contextSessionKey,
|
|
1856
1987
|
sessionAgentId,
|
|
1857
|
-
memoryToolNames: getCodexWorkspaceMemoryToolNames(toolBridge.availableSpecs)
|
|
1988
|
+
memoryToolNames: getCodexWorkspaceMemoryToolNames(toolBridge.availableSpecs),
|
|
1989
|
+
sandboxed: sandbox?.enabled === true
|
|
1858
1990
|
});
|
|
1859
1991
|
const baseDeveloperInstructions = joinPresentSections(buildDeveloperInstructions(runtimeParams, { dynamicTools: toolBridge.availableSpecs }), workspaceBootstrapContext.developerInstructions);
|
|
1860
1992
|
return {
|
|
@@ -2070,7 +2202,7 @@ function buildCodexTurnStartFailureResult(params) {
|
|
|
2070
2202
|
idleTimedOut: false,
|
|
2071
2203
|
timedOutDuringCompaction: false,
|
|
2072
2204
|
timedOutDuringToolExecution: false,
|
|
2073
|
-
promptError: params.message,
|
|
2205
|
+
promptError: params.promptError ?? params.message,
|
|
2074
2206
|
promptErrorSource: "prompt",
|
|
2075
2207
|
sessionIdUsed: params.params.sessionId,
|
|
2076
2208
|
messagesSnapshot: params.messagesSnapshot,
|
|
@@ -2284,139 +2416,6 @@ function normalizeCodexTrajectoryError(value) {
|
|
|
2284
2416
|
}
|
|
2285
2417
|
}
|
|
2286
2418
|
//#endregion
|
|
2287
|
-
//#region extensions/codex/src/app-server/usage-limit-error.ts
|
|
2288
|
-
/**
|
|
2289
|
-
* Enriches Codex usage-limit failures with current rate-limit information and
|
|
2290
|
-
* marks blocked auth profiles when Codex exposes a reset time.
|
|
2291
|
-
*/
|
|
2292
|
-
const CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS = 5e3;
|
|
2293
|
-
/** Marks a Codex auth profile blocked until the reset time advertised by rate limits. */
|
|
2294
|
-
async function markCodexAuthProfileBlockedFromRateLimits(params) {
|
|
2295
|
-
const authProfileId = params.authProfileId?.trim();
|
|
2296
|
-
if (!authProfileId || !params.params.authProfileStore) return;
|
|
2297
|
-
const blockedUntil = resolveCodexUsageLimitResetAtMs(params.rateLimits);
|
|
2298
|
-
if (!blockedUntil) return;
|
|
2299
|
-
try {
|
|
2300
|
-
await markAuthProfileBlockedUntil({
|
|
2301
|
-
store: params.params.authProfileStore,
|
|
2302
|
-
profileId: authProfileId,
|
|
2303
|
-
blockedUntil,
|
|
2304
|
-
source: "codex_rate_limits",
|
|
2305
|
-
agentDir: params.params.agentDir,
|
|
2306
|
-
runId: params.params.runId,
|
|
2307
|
-
modelId: params.params.modelId
|
|
2308
|
-
});
|
|
2309
|
-
} catch (error) {
|
|
2310
|
-
embeddedAgentLog.debug("failed to mark Codex auth profile blocked from app-server limits", {
|
|
2311
|
-
authProfileId,
|
|
2312
|
-
error: formatErrorMessage(error)
|
|
2313
|
-
});
|
|
2314
|
-
}
|
|
2315
|
-
}
|
|
2316
|
-
/** Formats a turn-start usage-limit error, refreshing rate limits when needed. */
|
|
2317
|
-
async function formatCodexTurnStartUsageLimitError(params) {
|
|
2318
|
-
return refreshCodexUsageLimitError({
|
|
2319
|
-
client: params.client,
|
|
2320
|
-
source: readCodexTurnStartUsageLimitErrorSource(params.client, params.error, params.errorNotification, params.rateLimitsRevisionBeforeTurnStart),
|
|
2321
|
-
timeoutMs: params.timeoutMs,
|
|
2322
|
-
signal: params.signal
|
|
2323
|
-
});
|
|
2324
|
-
}
|
|
2325
|
-
/** Refreshes a generic prompt usage-limit message into a reset-aware message. */
|
|
2326
|
-
async function refreshCodexUsageLimitPromptError(params) {
|
|
2327
|
-
if (!shouldRefreshCodexRateLimitsForUsageLimitMessage(params.message)) return;
|
|
2328
|
-
return (await refreshCodexUsageLimitError({
|
|
2329
|
-
client: params.client,
|
|
2330
|
-
source: {
|
|
2331
|
-
message: params.message,
|
|
2332
|
-
codexErrorInfo: "usageLimitExceeded",
|
|
2333
|
-
rateLimits: readRecentCodexRateLimits(params.client)
|
|
2334
|
-
},
|
|
2335
|
-
timeoutMs: params.timeoutMs,
|
|
2336
|
-
signal: params.signal
|
|
2337
|
-
}))?.message;
|
|
2338
|
-
}
|
|
2339
|
-
async function refreshCodexUsageLimitError(params) {
|
|
2340
|
-
const initialMessage = formatCodexUsageLimitErrorMessage(params.source);
|
|
2341
|
-
if (!shouldRefreshCodexRateLimitsForUsageLimitMessage(initialMessage)) return initialMessage ? {
|
|
2342
|
-
message: initialMessage,
|
|
2343
|
-
...params.source.rateLimitsTrustedForProfile ? { rateLimitsForProfile: params.source.rateLimits } : {}
|
|
2344
|
-
} : void 0;
|
|
2345
|
-
const rateLimits = await readCodexRateLimitsFromAppServerForUsageLimitError({
|
|
2346
|
-
client: params.client,
|
|
2347
|
-
timeoutMs: params.timeoutMs,
|
|
2348
|
-
signal: params.signal
|
|
2349
|
-
});
|
|
2350
|
-
if (!rateLimits) return initialMessage ? {
|
|
2351
|
-
message: initialMessage,
|
|
2352
|
-
...params.source.rateLimitsTrustedForProfile ? { rateLimitsForProfile: params.source.rateLimits } : {}
|
|
2353
|
-
} : void 0;
|
|
2354
|
-
const message = formatCodexUsageLimitErrorMessage({
|
|
2355
|
-
message: params.source.message,
|
|
2356
|
-
codexErrorInfo: params.source.codexErrorInfo,
|
|
2357
|
-
rateLimits
|
|
2358
|
-
}) ?? initialMessage;
|
|
2359
|
-
return message ? {
|
|
2360
|
-
message,
|
|
2361
|
-
rateLimitsForProfile: rateLimits
|
|
2362
|
-
} : void 0;
|
|
2363
|
-
}
|
|
2364
|
-
async function readCodexRateLimitsFromAppServerForUsageLimitError(params) {
|
|
2365
|
-
if (params.signal?.aborted) return;
|
|
2366
|
-
try {
|
|
2367
|
-
const rateLimits = await params.client.request(CODEX_CONTROL_METHODS.rateLimits, void 0, {
|
|
2368
|
-
timeoutMs: resolveCodexUsageLimitRateLimitRefreshTimeoutMs(params.timeoutMs),
|
|
2369
|
-
signal: params.signal
|
|
2370
|
-
});
|
|
2371
|
-
rememberCodexRateLimitsRead(params.client, rateLimits);
|
|
2372
|
-
return rateLimits;
|
|
2373
|
-
} catch (error) {
|
|
2374
|
-
embeddedAgentLog.debug("codex app-server rate-limit refresh failed after usage-limit error", { error: formatErrorMessage(error) });
|
|
2375
|
-
return;
|
|
2376
|
-
}
|
|
2377
|
-
}
|
|
2378
|
-
function resolveCodexUsageLimitRateLimitRefreshTimeoutMs(timeoutMs) {
|
|
2379
|
-
if (timeoutMs === void 0 || !Number.isFinite(timeoutMs) || timeoutMs <= 0) return CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS;
|
|
2380
|
-
return Math.max(100, Math.min(timeoutMs, CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS));
|
|
2381
|
-
}
|
|
2382
|
-
function readCodexTurnStartUsageLimitErrorSource(client, error, errorNotification, rateLimitsRevisionBeforeTurnStart) {
|
|
2383
|
-
const notificationError = readCodexErrorNotification(errorNotification);
|
|
2384
|
-
const errorPayload = readCodexErrorPayload(error);
|
|
2385
|
-
const rateLimits = errorPayload.rateLimits ?? readRecentCodexRateLimits(client);
|
|
2386
|
-
const cacheUpdatedDuringTurnStart = rateLimitsRevisionBeforeTurnStart !== void 0 && readCodexRateLimitsRevision(client) > rateLimitsRevisionBeforeTurnStart;
|
|
2387
|
-
return {
|
|
2388
|
-
message: notificationError?.message ?? errorPayload.message ?? formatErrorMessage(error),
|
|
2389
|
-
codexErrorInfo: notificationError?.codexErrorInfo ?? errorPayload.codexErrorInfo,
|
|
2390
|
-
rateLimits,
|
|
2391
|
-
rateLimitsTrustedForProfile: errorPayload.rateLimits !== void 0 || cacheUpdatedDuringTurnStart
|
|
2392
|
-
};
|
|
2393
|
-
}
|
|
2394
|
-
function readCodexErrorNotification(notification) {
|
|
2395
|
-
if (notification?.method !== "error" || !isJsonObject(notification.params)) return;
|
|
2396
|
-
const error = notification.params.error;
|
|
2397
|
-
return isJsonObject(error) ? {
|
|
2398
|
-
message: readString$3(error, "message"),
|
|
2399
|
-
codexErrorInfo: error.codexErrorInfo
|
|
2400
|
-
} : void 0;
|
|
2401
|
-
}
|
|
2402
|
-
function readCodexErrorPayload(error) {
|
|
2403
|
-
const message = error instanceof Error ? error.message : void 0;
|
|
2404
|
-
if (!error || typeof error !== "object" || !("data" in error)) return { message };
|
|
2405
|
-
const data = error.data;
|
|
2406
|
-
if (!isJsonObject(data)) return { message };
|
|
2407
|
-
const nestedError = isJsonObject(data.error) ? data.error : data;
|
|
2408
|
-
const rateLimits = nestedError.rateLimits ?? data.rateLimits;
|
|
2409
|
-
return {
|
|
2410
|
-
message: readString$3(nestedError, "message") ?? message,
|
|
2411
|
-
codexErrorInfo: nestedError.codexErrorInfo,
|
|
2412
|
-
rateLimits
|
|
2413
|
-
};
|
|
2414
|
-
}
|
|
2415
|
-
function readString$3(record, key) {
|
|
2416
|
-
const value = record[key];
|
|
2417
|
-
return typeof value === "string" ? value : void 0;
|
|
2418
|
-
}
|
|
2419
|
-
//#endregion
|
|
2420
2419
|
//#region extensions/codex/src/app-server/run-attempt-finalize.ts
|
|
2421
2420
|
async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notifications, requestRuntime, activeTurn) {
|
|
2422
2421
|
const { prompt, state: resourceState, trajectoryRecorder, markTrajectoryEndRecorded } = resources;
|
|
@@ -2425,7 +2424,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
|
|
|
2425
2424
|
const { hookContextWindowFields, hookRunner, promptState } = context;
|
|
2426
2425
|
const { connection, preparedAuthBinding, activeSessionId, activeSessionFile } = runtime;
|
|
2427
2426
|
const { buildActiveRunAttemptParams, effectiveContextTokenBudget, effectiveRuntimeProviderId, effectiveRuntimeModelId } = runtime;
|
|
2428
|
-
const { params, terminalState, runAbortController, activeContextEngine, bindingStore, bindingIdentity, appServer, usesSupervisionConnection, sessionAgentId, contextSessionKey, effectiveCwd, effectiveWorkspace, agentDir, attemptStartedAt } = connection;
|
|
2427
|
+
const { params, terminalState, runAbortController, activeContextEngine, bindingStore, bindingIdentity, appServer, usesSupervisionConnection, sessionAgentId, contextSessionKey, effectiveCwd, effectiveWorkspace, agentDir, attemptStartedAt, startupAuthProfileId } = connection;
|
|
2429
2428
|
const { toolBridge, toolState } = attemptTools;
|
|
2430
2429
|
const { state, completion, pendingOpenClawDynamicToolCompletionIds, activeTurnItemIds, activeCompletionBlockerItemIds, activeFinalizationHookRunIds, turnWatches } = turnRuntime;
|
|
2431
2430
|
const { emitLifecycleTerminal, buildLifecycleTerminalMeta } = lifecycle;
|
|
@@ -2459,7 +2458,7 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
|
|
|
2459
2458
|
const isFinalAborted = () => result.aborted || terminalState.explicitCancellationObserved || runAbortController.signal.aborted && !state.clientClosedAbort && !recoveredTurnWatchTimeout;
|
|
2460
2459
|
const clientClosedPromptErrorForFinal = state.clientClosedPromptError && hasRecoverableCompletedAssistant ? void 0 : state.clientClosedPromptError;
|
|
2461
2460
|
let finalPromptError = clientClosedPromptErrorForFinal ?? (effectiveTurnCompletionIdleTimedOut ? state.turnCompletionIdleTimeoutMessage : effectiveTimedOut ? "codex app-server attempt timed out" : result.promptError);
|
|
2462
|
-
const finalPromptErrorMessage = typeof finalPromptError === "string" ? finalPromptError : finalPromptError ? formatErrorMessage(finalPromptError) : void 0;
|
|
2461
|
+
const finalPromptErrorMessage = typeof finalPromptError === "string" ? finalPromptError : finalPromptError instanceof Error ? finalPromptError.message : finalPromptError ? formatErrorMessage(finalPromptError) : void 0;
|
|
2463
2462
|
if (isInvalidCodexImagePayloadError(finalPromptErrorMessage)) await clearCodexBindingAfterInvalidImagePayload(bindingStore, bindingIdentity, {
|
|
2464
2463
|
phase: "turn_completed",
|
|
2465
2464
|
threadId: resourceState.thread.threadId,
|
|
@@ -2487,7 +2486,18 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
|
|
|
2487
2486
|
timeoutMs: appServer.requestTimeoutMs,
|
|
2488
2487
|
signal: runAbortController.signal
|
|
2489
2488
|
});
|
|
2490
|
-
if (refreshedUsageLimitPromptError)
|
|
2489
|
+
if (refreshedUsageLimitPromptError) {
|
|
2490
|
+
await markCodexAuthProfileBlockedFromRateLimits({
|
|
2491
|
+
params,
|
|
2492
|
+
authProfileId: startupAuthProfileId,
|
|
2493
|
+
rateLimits: refreshedUsageLimitPromptError.rateLimitsForProfile
|
|
2494
|
+
});
|
|
2495
|
+
finalPromptError = createCodexUsageLimitPromptError(refreshedUsageLimitPromptError.message);
|
|
2496
|
+
} else if (isCodexUsageLimitPromptError(finalPromptError) && state.rateLimitsRevisionBeforeLastTurnStart !== void 0 && readCodexRateLimitsRevision(resourceState.client) > state.rateLimitsRevisionBeforeLastTurnStart) await markCodexAuthProfileBlockedFromRateLimits({
|
|
2497
|
+
params,
|
|
2498
|
+
authProfileId: startupAuthProfileId,
|
|
2499
|
+
rateLimits: readRecentCodexRateLimits(resourceState.client)
|
|
2500
|
+
});
|
|
2491
2501
|
const finalPromptErrorSource = effectiveTimedOut || clientClosedPromptErrorForFinal ? "prompt" : result.promptErrorSource;
|
|
2492
2502
|
const codexAppServerFailureKind = clientClosedPromptErrorForFinal ? "client_closed_before_turn_completed" : effectiveTurnCompletionIdleTimedOut ? "turn_completion_idle_timeout" : void 0;
|
|
2493
2503
|
const replayBlockedReason = codexAppServerFailureKind ? resolveCodexAppServerReplayBlockedReason(result) : void 0;
|
|
@@ -2515,7 +2525,9 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
|
|
|
2515
2525
|
const finalAborted = isFinalAborted();
|
|
2516
2526
|
const completedTurnStatus = activeProjector.getCompletedTurnStatus();
|
|
2517
2527
|
const completedWithoutTerminalNotification = state.completed && !state.terminalTurnNotificationQueued && !state.timedOut && clientClosedPromptErrorForFinal === void 0;
|
|
2518
|
-
const
|
|
2528
|
+
const turnSucceeded = !finalAborted && !effectiveTimedOut && (finalPromptError === null || finalPromptError === void 0) && (completedTurnStatus === "completed" || recoveredTurnWatchTimeout || completedWithoutTerminalNotification);
|
|
2529
|
+
if (settleCodexSourceReplyFinality(toolBridge.telemetry, turnSucceeded)) result.agentHarnessResultClassification = void 0;
|
|
2530
|
+
const attemptSucceeded = turnSucceeded && result.agentHarnessResultClassification === void 0;
|
|
2519
2531
|
terminalState.sharedAbortAllowedAfterTerminalOutcome = shouldKeepCodexSharedAbortOpen({
|
|
2520
2532
|
trigger: params.trigger,
|
|
2521
2533
|
result,
|
|
@@ -2662,7 +2674,8 @@ async function finalizeCodexAttempt(resources, turnRuntime, lifecycle, notificat
|
|
|
2662
2674
|
phase: "end",
|
|
2663
2675
|
...buildLifecycleTerminalMeta({
|
|
2664
2676
|
aborted: finalAborted,
|
|
2665
|
-
timedOut: effectiveTimedOut
|
|
2677
|
+
timedOut: effectiveTimedOut,
|
|
2678
|
+
yielded: toolState.yieldDetected
|
|
2666
2679
|
})
|
|
2667
2680
|
});
|
|
2668
2681
|
return {
|
|
@@ -2708,7 +2721,7 @@ function reportCodexExecutionNotification(params) {
|
|
|
2708
2721
|
/** Returns true when a notification ends the current app-server turn. */
|
|
2709
2722
|
function isTerminalCodexTurnNotificationForTurn(params) {
|
|
2710
2723
|
if (!isCodexNotificationForTurn(params.notification.params, params.threadId, params.turnId)) return false;
|
|
2711
|
-
return params.notification.method === "turn/completed"
|
|
2724
|
+
return params.notification.method === "turn/completed";
|
|
2712
2725
|
}
|
|
2713
2726
|
/**
|
|
2714
2727
|
* Applies one notification to active item tracking, idle watches, and terminal
|
|
@@ -2778,13 +2791,33 @@ function applyCodexTurnNotificationState(params) {
|
|
|
2778
2791
|
isTurnTerminal: isTerminalCodexTurnNotificationForTurn({
|
|
2779
2792
|
notification,
|
|
2780
2793
|
threadId: params.threadId,
|
|
2781
|
-
turnId: params.turnId
|
|
2782
|
-
currentPromptTexts: params.currentPromptTexts
|
|
2794
|
+
turnId: params.turnId
|
|
2783
2795
|
}),
|
|
2784
2796
|
turnCrossedToolHandoff
|
|
2785
2797
|
};
|
|
2786
2798
|
}
|
|
2787
2799
|
//#endregion
|
|
2800
|
+
//#region extensions/codex/src/app-server/run-attempt-lifecycle-terminal.ts
|
|
2801
|
+
function buildCodexLifecycleTerminalMeta(input) {
|
|
2802
|
+
if (input.timedOut || input.abortStopReason === "timeout") return {
|
|
2803
|
+
aborted: true,
|
|
2804
|
+
status: "timed_out",
|
|
2805
|
+
stopReason: "timeout",
|
|
2806
|
+
timeoutPhase: "provider",
|
|
2807
|
+
providerStarted: true
|
|
2808
|
+
};
|
|
2809
|
+
if (input.yielded && !input.aborted) return {
|
|
2810
|
+
yielded: true,
|
|
2811
|
+
livenessState: "paused",
|
|
2812
|
+
stopReason: "end_turn"
|
|
2813
|
+
};
|
|
2814
|
+
return input.aborted ? {
|
|
2815
|
+
aborted: true,
|
|
2816
|
+
status: "cancelled",
|
|
2817
|
+
stopReason: "stop"
|
|
2818
|
+
} : void 0;
|
|
2819
|
+
}
|
|
2820
|
+
//#endregion
|
|
2788
2821
|
//#region extensions/codex/src/app-server/run-attempt-lifecycle-controller.ts
|
|
2789
2822
|
function createCodexAttemptLifecycleController(resources, turnRuntime) {
|
|
2790
2823
|
const { prompt, state: resourceState, trajectoryRecorder } = resources;
|
|
@@ -2816,6 +2849,7 @@ function createCodexAttemptLifecycleController(resources, turnRuntime) {
|
|
|
2816
2849
|
tool: value.call.tool,
|
|
2817
2850
|
durationMs: value.durationMs
|
|
2818
2851
|
});
|
|
2852
|
+
turnRuntime.steeringQueueRef.current?.cancel();
|
|
2819
2853
|
interruptCodexTurnBestEffort(resourceState.client, {
|
|
2820
2854
|
threadId: value.call.threadId,
|
|
2821
2855
|
turnId: value.call.turnId,
|
|
@@ -2832,6 +2866,7 @@ function createCodexAttemptLifecycleController(resources, turnRuntime) {
|
|
|
2832
2866
|
state.terminalDynamicToolReleaseCheckScheduled = true;
|
|
2833
2867
|
setImmediate(() => {
|
|
2834
2868
|
state.terminalDynamicToolReleaseCheckScheduled = false;
|
|
2869
|
+
if (state.pendingTerminalDynamicToolRelease?.response.success === true && !state.currentTurnHadNonTerminalDynamicToolResult && state.activeAppServerTurnRequests === 0 && pendingOpenClawDynamicToolCompletionIds.size === 0) turnRuntime.steeringQueueRef.current?.cancel();
|
|
2835
2870
|
const action = resolveTerminalDynamicToolBatchAction({
|
|
2836
2871
|
activeAppServerTurnRequests: state.activeAppServerTurnRequests,
|
|
2837
2872
|
activeTurnItemIdsCount: activeTurnItemIds.size,
|
|
@@ -2868,25 +2903,17 @@ function createCodexAttemptLifecycleController(resources, turnRuntime) {
|
|
|
2868
2903
|
startedAt: attemptStartedAt,
|
|
2869
2904
|
endedAt: Date.now(),
|
|
2870
2905
|
...data,
|
|
2871
|
-
...params.deferTerminalLifecycle
|
|
2906
|
+
...params.deferTerminalLifecycle ? { phase: "finishing" } : {}
|
|
2872
2907
|
}
|
|
2873
2908
|
});
|
|
2874
2909
|
state.lifecycleTerminalEmitted = true;
|
|
2875
2910
|
};
|
|
2876
2911
|
const buildLifecycleTerminalMeta = (input) => {
|
|
2877
2912
|
const abortFields = input.aborted ? resolveAgentRunAbortLifecycleFields(runAbortController.signal) : void 0;
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
timeoutPhase: "provider",
|
|
2883
|
-
providerStarted: true
|
|
2884
|
-
};
|
|
2885
|
-
return input.aborted ? {
|
|
2886
|
-
aborted: true,
|
|
2887
|
-
status: "cancelled",
|
|
2888
|
-
stopReason: "stop"
|
|
2889
|
-
} : void 0;
|
|
2913
|
+
return buildCodexLifecycleTerminalMeta({
|
|
2914
|
+
...input,
|
|
2915
|
+
abortStopReason: abortFields?.stopReason
|
|
2916
|
+
});
|
|
2890
2917
|
};
|
|
2891
2918
|
const executionPhaseKeys = /* @__PURE__ */ new Set();
|
|
2892
2919
|
const emitExecutionPhaseOnce = (key, info) => {
|
|
@@ -2999,7 +3026,8 @@ var ClientTurnRouter = class {
|
|
|
2999
3026
|
gate: "open",
|
|
3000
3027
|
pending: [],
|
|
3001
3028
|
notificationTail: Promise.resolve(),
|
|
3002
|
-
nativeTurnCompleted: false
|
|
3029
|
+
nativeTurnCompleted: false,
|
|
3030
|
+
ignoredTurnNotificationKeys: /* @__PURE__ */ new Set()
|
|
3003
3031
|
};
|
|
3004
3032
|
this.routes.set(threadId, route);
|
|
3005
3033
|
if (options.onNotification || options.onRequest) this.activateNow(route, options);
|
|
@@ -3082,6 +3110,7 @@ var ClientTurnRouter = class {
|
|
|
3082
3110
|
this.assertRoute(route);
|
|
3083
3111
|
if (route.gate !== "open") throw new Error(`codex app-server thread route cannot arm from ${route.gate}`);
|
|
3084
3112
|
route.gate = "armed";
|
|
3113
|
+
route.ignoredTurnNotificationKeys.clear();
|
|
3085
3114
|
route.nativeTurnCompleted = false;
|
|
3086
3115
|
route.binding = deferred();
|
|
3087
3116
|
}
|
|
@@ -3131,7 +3160,10 @@ var ClientTurnRouter = class {
|
|
|
3131
3160
|
}
|
|
3132
3161
|
const handler = route.handlers.onNotification;
|
|
3133
3162
|
if (!handler) return;
|
|
3134
|
-
if (route.gate === "bound" && scope.turnId && scope.turnId !== route.turnId)
|
|
3163
|
+
if (route.gate === "bound" && scope.turnId && scope.turnId !== route.turnId) {
|
|
3164
|
+
this.warnDroppedStaleTurnNotification(route, notification, routeScope);
|
|
3165
|
+
return;
|
|
3166
|
+
}
|
|
3135
3167
|
if (route.gate === "armed") {
|
|
3136
3168
|
this.bufferNotification(route, notification, routeScope, receivedAtMs);
|
|
3137
3169
|
return;
|
|
@@ -3174,11 +3206,31 @@ var ClientTurnRouter = class {
|
|
|
3174
3206
|
flushNotifications(route) {
|
|
3175
3207
|
const handler = route.handlers?.onNotification;
|
|
3176
3208
|
if (!handler) return;
|
|
3177
|
-
for (const pending of route.pending.splice(0))
|
|
3209
|
+
for (const pending of route.pending.splice(0)) {
|
|
3210
|
+
if (route.gate === "bound" && pending.scope.turnId && pending.scope.turnId !== route.turnId) {
|
|
3211
|
+
this.warnDroppedStaleTurnNotification(route, pending.notification, pending.scope);
|
|
3212
|
+
continue;
|
|
3213
|
+
}
|
|
3178
3214
|
route.handlers?.onNotificationReceived?.(pending.notification, pending.scope, pending.receivedAtMs);
|
|
3179
3215
|
this.enqueueNotification(route, handler, pending.notification, pending.scope);
|
|
3180
3216
|
}
|
|
3181
3217
|
}
|
|
3218
|
+
warnDroppedStaleTurnNotification(route, notification, scope) {
|
|
3219
|
+
if (notification.method === "turn/completed" || !scope.turnId || !route.turnId) return;
|
|
3220
|
+
const eventKind = redactCodexEventKind(notification.method);
|
|
3221
|
+
const key = JSON.stringify([notification.method, scope.turnId]);
|
|
3222
|
+
if (route.ignoredTurnNotificationKeys.has(key)) return;
|
|
3223
|
+
route.ignoredTurnNotificationKeys.add(key);
|
|
3224
|
+
embeddedAgentLog.warn("codex app-server notification ignored for inactive turn", {
|
|
3225
|
+
eventKind,
|
|
3226
|
+
activeThreadId: route.threadId,
|
|
3227
|
+
activeTurnId: route.turnId,
|
|
3228
|
+
threadId: scope.threadId,
|
|
3229
|
+
turnId: scope.turnId,
|
|
3230
|
+
matchesActiveThread: true,
|
|
3231
|
+
matchesActiveTurn: false
|
|
3232
|
+
});
|
|
3233
|
+
}
|
|
3182
3234
|
bufferNotification(route, notification, scope, receivedAtMs) {
|
|
3183
3235
|
if (route.pending.length < DEFAULT_PREBIND_NOTIFICATION_LIMIT) {
|
|
3184
3236
|
route.pending.push({
|
|
@@ -3307,8 +3359,7 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
|
|
|
3307
3359
|
const isTerminalTurnNotificationForTurn = (notification, notificationTurnId) => isTerminalCodexTurnNotificationForTurn({
|
|
3308
3360
|
notification,
|
|
3309
3361
|
threadId: resourceState.thread.threadId,
|
|
3310
|
-
turnId: notificationTurnId
|
|
3311
|
-
currentPromptTexts: [turnState.codexTurnPromptText]
|
|
3362
|
+
turnId: notificationTurnId
|
|
3312
3363
|
});
|
|
3313
3364
|
const handleNotification = async (notification) => {
|
|
3314
3365
|
const projector = projectorRef.current;
|
|
@@ -3335,6 +3386,11 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
|
|
|
3335
3386
|
onReportExecutionNotification: reportExecutionNotification
|
|
3336
3387
|
});
|
|
3337
3388
|
state.turnCrossedToolHandoff = notificationState.turnCrossedToolHandoff;
|
|
3389
|
+
if (notificationState.isCurrentTurnNotification && notification.method === "item/completed") {
|
|
3390
|
+
const item = readCodexNotificationItem(notification.params);
|
|
3391
|
+
if (item?.type === "userMessage" && typeof item.clientId === "string") steeringQueue?.confirmConsumed(item.clientId);
|
|
3392
|
+
}
|
|
3393
|
+
if (notificationState.isTurnAbortMarker) state.sawCodexInterruptMarker = true;
|
|
3338
3394
|
const hookNotification = readCodexFinalizationHookNotification(notification, resourceState.thread.threadId, turnId);
|
|
3339
3395
|
if (hookNotification?.phase === "started") {
|
|
3340
3396
|
if (activeFinalizationHookRunIds.size === 0) finalizationHookBatchStatuses.clear();
|
|
@@ -3378,7 +3434,7 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
|
|
|
3378
3434
|
});
|
|
3379
3435
|
}
|
|
3380
3436
|
if (notificationState.isTurnTerminal) {
|
|
3381
|
-
if (
|
|
3437
|
+
if ((readCodexTurnCompletedNotification(notification.params)?.turn)?.status === "interrupted" && state.sawCodexInterruptMarker) projector.markAborted();
|
|
3382
3438
|
if (!state.timedOut && !runAbortController.signal.aborted) await steeringQueue?.flushPending();
|
|
3383
3439
|
state.completed = true;
|
|
3384
3440
|
turnWatches.clearCompletionIdleTimer();
|
|
@@ -3436,7 +3492,7 @@ function createCodexAttemptNotificationController(resources, turnRuntime, lifecy
|
|
|
3436
3492
|
async function prepareCodexAttemptPrompt(context) {
|
|
3437
3493
|
const { runtime, attemptTools, historyState, hookContext, workspaceBootstrapContext, baseDeveloperInstructions, openClawPromptContext, skillsCollaborationInstructions, promptState, codexContextProjectionMaxChars } = context;
|
|
3438
3494
|
const { connection, buildActiveRunAttemptParams, effectiveContextTokenBudget, effectiveRuntimeModelId, effectiveRuntimeProviderId } = runtime;
|
|
3439
|
-
const { params, activeContextEngine, usesSupervisionConnection, mutable, isInactiveThreadBootstrapBinding, bindingStore, bindingIdentity, agentDir, appServer, contextSessionKey, effectiveWorkspace } = connection;
|
|
3495
|
+
const { params, activeContextEngine, usesSupervisionConnection, mutable, isInactiveThreadBootstrapBinding, bindingStore, bindingIdentity, agentDir, appServer, contextSessionKey, effectiveWorkspace, sandbox } = connection;
|
|
3440
3496
|
const { toolBridge } = attemptTools;
|
|
3441
3497
|
const applyFreshThreadContinuityProjection = () => {
|
|
3442
3498
|
const projection = projectContextEngineAssemblyForCodex({
|
|
@@ -3459,6 +3515,7 @@ async function prepareCodexAttemptPrompt(context) {
|
|
|
3459
3515
|
tokenBudget: effectiveContextTokenBudget,
|
|
3460
3516
|
availableTools: new Set(flattenCodexDynamicToolFunctions(toolBridge.availableSpecs).map((tool) => tool.name).filter(isNonEmptyString)),
|
|
3461
3517
|
citationsMode: params.config?.memory?.citations,
|
|
3518
|
+
sandboxed: sandbox?.enabled === true,
|
|
3462
3519
|
modelId: effectiveRuntimeModelId,
|
|
3463
3520
|
contextEngineHostSupport: CODEX_APP_SERVER_CONTEXT_ENGINE_HOST,
|
|
3464
3521
|
providerId: effectiveRuntimeProviderId,
|
|
@@ -3520,7 +3577,7 @@ async function prepareCodexAttemptPrompt(context) {
|
|
|
3520
3577
|
const buildPromptFromCurrentInputs = () => resolveAgentHarnessBeforePromptBuildResult({
|
|
3521
3578
|
prompt: prependCurrentInboundContext(promptState.promptText, params.currentInboundContext),
|
|
3522
3579
|
developerInstructions: promptState.developerInstructions,
|
|
3523
|
-
messages:
|
|
3580
|
+
messages: structuredClone(historyState.messages),
|
|
3524
3581
|
ctx: hookContext,
|
|
3525
3582
|
bootstrapContextRunKind: params.bootstrapContextRunKind,
|
|
3526
3583
|
..."beforeAgentStartResult" in params ? { beforeAgentStartResult: params.beforeAgentStartResult } : {}
|
|
@@ -5265,6 +5322,30 @@ function sendError(socket, id, code, message) {
|
|
|
5265
5322
|
}));
|
|
5266
5323
|
}
|
|
5267
5324
|
//#endregion
|
|
5325
|
+
//#region extensions/codex/src/app-server/sandbox-exec-server/path-uri.ts
|
|
5326
|
+
/** Converts Codex PathUri protocol values into sandbox-backend path strings. */
|
|
5327
|
+
const WINDOWS_DRIVE_PATH_RE = /^\/[A-Za-z]:(?:\/|$)/u;
|
|
5328
|
+
/** Resolves one Codex exec-server PathUri into a POSIX sandbox path. */
|
|
5329
|
+
function resolveExecServerPath(rawPath, label) {
|
|
5330
|
+
let pathUrl;
|
|
5331
|
+
try {
|
|
5332
|
+
pathUrl = new URL(rawPath);
|
|
5333
|
+
} catch (error) {
|
|
5334
|
+
throw new Error(`${label} must be a valid file URI: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
5335
|
+
}
|
|
5336
|
+
if (pathUrl.protocol !== "file:") throw new Error(`${label} URI must use the file scheme, received ${pathUrl.protocol.slice(0, -1)}.`);
|
|
5337
|
+
if (pathUrl.search || pathUrl.hash) throw new Error(`${label} file URI must not include a query or fragment.`);
|
|
5338
|
+
let resolved;
|
|
5339
|
+
try {
|
|
5340
|
+
resolved = fileURLToPath(pathUrl, { windows: false });
|
|
5341
|
+
} catch (error) {
|
|
5342
|
+
throw new Error(`${label} file URI is not valid for the sandbox: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
5343
|
+
}
|
|
5344
|
+
if (WINDOWS_DRIVE_PATH_RE.test(resolved)) throw new Error(`${label} Windows file URI is not supported by the sandbox.`);
|
|
5345
|
+
if (resolved.includes("\0")) throw new Error(`${label} file URI must not contain a null byte.`);
|
|
5346
|
+
return resolved;
|
|
5347
|
+
}
|
|
5348
|
+
//#endregion
|
|
5268
5349
|
//#region extensions/codex/src/app-server/sandbox-exec-server/fs-policy.ts
|
|
5269
5350
|
/**
|
|
5270
5351
|
* Resolves Codex filesystem sandbox policy payloads into OpenClaw path/glob
|
|
@@ -5304,7 +5385,7 @@ function resolveFsSandboxPolicy(execServer, record) {
|
|
|
5304
5385
|
}
|
|
5305
5386
|
function readFsSandboxCwd(execServer, sandbox) {
|
|
5306
5387
|
if (sandbox.cwd === void 0 || sandbox.cwd === null) return normalizeSandboxAbsolutePath(execServer.sandbox.containerWorkdir, "sandbox cwd");
|
|
5307
|
-
return normalizeSandboxAbsolutePath(requireString(sandbox.cwd, "sandbox cwd"), "sandbox cwd");
|
|
5388
|
+
return normalizeSandboxAbsolutePath(resolveExecServerPath(requireString(sandbox.cwd, "sandbox cwd"), "sandbox cwd"), "sandbox cwd");
|
|
5308
5389
|
}
|
|
5309
5390
|
function resolveFsSandboxEntry(entry, cwd) {
|
|
5310
5391
|
const access = readFsAccessMode(entry.access);
|
|
@@ -5312,7 +5393,7 @@ function resolveFsSandboxEntry(entry, cwd) {
|
|
|
5312
5393
|
const pathType = requireString(pathSpec.type, "fs sandbox entry path type");
|
|
5313
5394
|
if (pathType === "path") return {
|
|
5314
5395
|
kind: "path",
|
|
5315
|
-
path: normalizeSandboxAbsolutePath(requireString(pathSpec.path, "fs sandbox path"), "fs sandbox path"),
|
|
5396
|
+
path: normalizeSandboxAbsolutePath(resolveExecServerPath(requireString(pathSpec.path, "fs sandbox path"), "fs sandbox path"), "fs sandbox path"),
|
|
5316
5397
|
access
|
|
5317
5398
|
};
|
|
5318
5399
|
if (pathType === "special") {
|
|
@@ -5506,7 +5587,7 @@ const CODEX_SANDBOX_EXEC_SERVER_MAX_READ_FILE_BYTES = 512 * 1024 * 1024;
|
|
|
5506
5587
|
/** Reads a sandbox file as base64 after read-policy and size checks. */
|
|
5507
5588
|
async function readFile(execServer, params) {
|
|
5508
5589
|
const record = requireObject(params, "fs/readFile params");
|
|
5509
|
-
const filePath = requireString(record.path, "path");
|
|
5590
|
+
const filePath = resolveExecServerPath(requireString(record.path, "path"), "read path");
|
|
5510
5591
|
assertFsSandboxAccess(execServer, record, [{
|
|
5511
5592
|
path: filePath,
|
|
5512
5593
|
access: "read"
|
|
@@ -5520,7 +5601,7 @@ async function readFile(execServer, params) {
|
|
|
5520
5601
|
/** Writes base64 data to an existing sandbox directory after write-policy checks. */
|
|
5521
5602
|
async function writeFile(execServer, params) {
|
|
5522
5603
|
const record = requireObject(params, "fs/writeFile params");
|
|
5523
|
-
const filePath = requireString(record.path, "path");
|
|
5604
|
+
const filePath = resolveExecServerPath(requireString(record.path, "path"), "write path");
|
|
5524
5605
|
assertFsSandboxAccess(execServer, record, [{
|
|
5525
5606
|
path: filePath,
|
|
5526
5607
|
access: "write"
|
|
@@ -5536,7 +5617,7 @@ async function writeFile(execServer, params) {
|
|
|
5536
5617
|
/** Creates a sandbox directory, respecting recursive and parent-directory semantics. */
|
|
5537
5618
|
async function createDirectory(execServer, params) {
|
|
5538
5619
|
const record = requireObject(params, "fs/createDirectory params");
|
|
5539
|
-
const filePath = requireString(record.path, "path");
|
|
5620
|
+
const filePath = resolveExecServerPath(requireString(record.path, "path"), "create-directory path");
|
|
5540
5621
|
assertFsSandboxAccess(execServer, record, [{
|
|
5541
5622
|
path: filePath,
|
|
5542
5623
|
access: "write"
|
|
@@ -5551,7 +5632,7 @@ async function createDirectory(execServer, params) {
|
|
|
5551
5632
|
/** Returns normalized metadata for a sandbox path. */
|
|
5552
5633
|
async function getMetadata(execServer, params) {
|
|
5553
5634
|
const record = requireObject(params, "fs/getMetadata params");
|
|
5554
|
-
const filePath = requireString(record.path, "path");
|
|
5635
|
+
const filePath = resolveExecServerPath(requireString(record.path, "path"), "metadata path");
|
|
5555
5636
|
assertFsSandboxAccess(execServer, record, [{
|
|
5556
5637
|
path: filePath,
|
|
5557
5638
|
access: "read"
|
|
@@ -5563,7 +5644,7 @@ async function getMetadata(execServer, params) {
|
|
|
5563
5644
|
/** Lists sandbox directory entries visible under the resolved filesystem policy. */
|
|
5564
5645
|
async function readDirectory(execServer, params) {
|
|
5565
5646
|
const record = requireObject(params, "fs/readDirectory params");
|
|
5566
|
-
return { entries: await listDirectoryEntries(execServer, requireString(record.path, "path"), resolveFsSandboxPolicy(execServer, record)) };
|
|
5647
|
+
return { entries: await listDirectoryEntries(execServer, resolveExecServerPath(requireString(record.path, "path"), "read-directory path"), resolveFsSandboxPolicy(execServer, record)) };
|
|
5567
5648
|
}
|
|
5568
5649
|
async function listDirectoryEntries(execServer, filePath, fsSandboxPolicy) {
|
|
5569
5650
|
assertResolvedFsSandboxAccess(fsSandboxPolicy, [{
|
|
@@ -5595,7 +5676,7 @@ async function listDirectoryEntries(execServer, filePath, fsSandboxPolicy) {
|
|
|
5595
5676
|
/** Removes a sandbox path after rejecting writes outside policy or under read-only descendants. */
|
|
5596
5677
|
async function removePath(execServer, params) {
|
|
5597
5678
|
const record = requireObject(params, "fs/remove params");
|
|
5598
|
-
const filePath = requireString(record.path, "path");
|
|
5679
|
+
const filePath = resolveExecServerPath(requireString(record.path, "path"), "remove path");
|
|
5599
5680
|
const fsSandboxPolicy = resolveFsSandboxPolicy(execServer, record);
|
|
5600
5681
|
assertResolvedFsSandboxAccess(fsSandboxPolicy, [{
|
|
5601
5682
|
path: filePath,
|
|
@@ -5611,8 +5692,8 @@ async function removePath(execServer, params) {
|
|
|
5611
5692
|
/** Copies sandbox files or recursive directories while enforcing source and destination policy. */
|
|
5612
5693
|
async function copyPath(execServer, params) {
|
|
5613
5694
|
const record = requireObject(params, "fs/copy params");
|
|
5614
|
-
const sourcePath = requireString(record.sourcePath ?? record.source, "sourcePath");
|
|
5615
|
-
const destinationPath = requireString(record.destinationPath ?? record.destination, "destinationPath");
|
|
5695
|
+
const sourcePath = resolveExecServerPath(requireString(record.sourcePath ?? record.source, "sourcePath"), "copy source path");
|
|
5696
|
+
const destinationPath = resolveExecServerPath(requireString(record.destinationPath ?? record.destination, "destinationPath"), "copy destination path");
|
|
5616
5697
|
const fsSandboxPolicy = resolveFsSandboxPolicy(execServer, record);
|
|
5617
5698
|
assertResolvedFsSandboxAccess(fsSandboxPolicy, [{
|
|
5618
5699
|
path: sourcePath,
|
|
@@ -5812,8 +5893,9 @@ function readStreamingSandboxHttpResponse(params) {
|
|
|
5812
5893
|
}
|
|
5813
5894
|
reject(new Error(message));
|
|
5814
5895
|
};
|
|
5896
|
+
params.child.stdout.setEncoding("utf8");
|
|
5815
5897
|
params.child.stdout.on("data", (chunk) => {
|
|
5816
|
-
stdoutBuffer += chunk
|
|
5898
|
+
stdoutBuffer += chunk;
|
|
5817
5899
|
let newline = stdoutBuffer.indexOf("\n");
|
|
5818
5900
|
while (newline >= 0) {
|
|
5819
5901
|
const line = stdoutBuffer.slice(0, newline).trim();
|
|
@@ -5849,8 +5931,9 @@ function readStreamingSandboxHttpResponse(params) {
|
|
|
5849
5931
|
fail(`sandbox http/request produced an unterminated stdout line longer than ${SANDBOX_HTTP_STREAM_LINE_MAX_CHARS} characters`, null);
|
|
5850
5932
|
}
|
|
5851
5933
|
});
|
|
5934
|
+
params.child.stderr.setEncoding("utf8");
|
|
5852
5935
|
params.child.stderr.on("data", (chunk) => {
|
|
5853
|
-
stderr = `${stderr}${chunk
|
|
5936
|
+
stderr = sliceUtf16Safe(`${stderr}${chunk}`, -4096);
|
|
5854
5937
|
});
|
|
5855
5938
|
params.child.once("error", (error) => {
|
|
5856
5939
|
childFailure ??= error.message;
|
|
@@ -6101,7 +6184,7 @@ async function startProcess(execServer, processes, socket, params) {
|
|
|
6101
6184
|
const processId = requireString(record.processId, "processId");
|
|
6102
6185
|
if (processes.has(processId)) throw new Error(`process already exists: ${processId}`);
|
|
6103
6186
|
const argv = requireStringArray(record.argv, "argv");
|
|
6104
|
-
const cwd = requireString(record.cwd, "cwd");
|
|
6187
|
+
const cwd = resolveExecServerPath(requireString(record.cwd, "cwd"), "process cwd");
|
|
6105
6188
|
rejectUnsupportedArg0(record.arg0);
|
|
6106
6189
|
const env = readProcessEnv(record);
|
|
6107
6190
|
const managed = {
|
|
@@ -6591,7 +6674,7 @@ function prepareCodexAttemptResources(prompt) {
|
|
|
6591
6674
|
const { context, turnState, buildRenderedCodexDeveloperInstructions } = prompt;
|
|
6592
6675
|
const { runtime, attemptTools } = context;
|
|
6593
6676
|
const { connection, hookChannelId } = runtime;
|
|
6594
|
-
const { params, effectiveCwd, sessionAgentId, sandboxSessionKey, runAbortController, sandbox, options, nativeHookRelayEvents } = connection;
|
|
6677
|
+
const { appServer, params, effectiveCwd, sessionAgentId, sandboxSessionKey, runAbortController, sandbox, options, nativeHookRelayEvents } = connection;
|
|
6595
6678
|
const { toolBridge } = attemptTools;
|
|
6596
6679
|
const hostTrajectoryRecorder = params.trajectoryRecorder;
|
|
6597
6680
|
const trajectoryRecorder = createCodexTrajectoryRecorder({
|
|
@@ -6657,16 +6740,17 @@ function prepareCodexAttemptResources(prompt) {
|
|
|
6657
6740
|
const retireSharedCodexClientForOneShotCleanup = async () => {
|
|
6658
6741
|
if (params.cleanupBundleMcpOnRunEnd !== true || state.sharedCodexClientRetiredForOneShotCleanup) return;
|
|
6659
6742
|
state.sharedCodexClientRetiredForOneShotCleanup = true;
|
|
6660
|
-
const retired =
|
|
6661
|
-
embeddedAgentLog.info("codex app-server one-shot cleanup
|
|
6743
|
+
const retired = clearSharedCodexAppServerClientIfCurrentAndUnclaimed(state.client);
|
|
6744
|
+
embeddedAgentLog.info("codex app-server one-shot cleanup checked shared client retirement", {
|
|
6662
6745
|
runId: params.runId,
|
|
6663
6746
|
sessionId: params.sessionId,
|
|
6664
6747
|
sessionKey: params.sessionKey,
|
|
6665
|
-
activeLeases: retired
|
|
6666
|
-
|
|
6667
|
-
|
|
6748
|
+
activeLeases: retired.activeLeases,
|
|
6749
|
+
pendingAcquires: retired.pendingAcquires,
|
|
6750
|
+
closed: retired.closed,
|
|
6751
|
+
matchedSharedClient: retired.found
|
|
6668
6752
|
});
|
|
6669
|
-
if (retired
|
|
6753
|
+
if (retired.closed) await state.client.closeAndWait({
|
|
6670
6754
|
exitTimeoutMs: 2e3,
|
|
6671
6755
|
forceKillDelayMs: 250
|
|
6672
6756
|
});
|
|
@@ -6724,6 +6808,7 @@ function prepareCodexAttemptResources(prompt) {
|
|
|
6724
6808
|
attemptTimeoutMs: params.timeoutMs,
|
|
6725
6809
|
startupTimeoutMs,
|
|
6726
6810
|
turnStartTimeoutMs: params.timeoutMs,
|
|
6811
|
+
loopDetectionPreToolUseRelay: appServer.loopDetectionPreToolUseRelay,
|
|
6727
6812
|
signal: runAbortController.signal,
|
|
6728
6813
|
onPreToolUseFailure: (failure) => {
|
|
6729
6814
|
const projector = projectorRef.current;
|
|
@@ -6736,7 +6821,8 @@ function prepareCodexAttemptResources(prompt) {
|
|
|
6736
6821
|
configPatch: state.nativeHookRelay ? buildCodexNativeHookRelayConfig({
|
|
6737
6822
|
relay: state.nativeHookRelay,
|
|
6738
6823
|
events: nativeHookRelayEvents,
|
|
6739
|
-
hookTimeoutSec: options.nativeHookRelay?.hookTimeoutSec
|
|
6824
|
+
hookTimeoutSec: options.nativeHookRelay?.hookTimeoutSec,
|
|
6825
|
+
loopDetectionPreToolUseRelay: appServer.loopDetectionPreToolUseRelay
|
|
6740
6826
|
}) : options.nativeHookRelay?.enabled === false ? buildCodexNativeHookRelayDisabledConfig() : void 0,
|
|
6741
6827
|
nativeHookRelayGeneration: state.nativeHookRelay?.generation
|
|
6742
6828
|
};
|
|
@@ -6955,6 +7041,21 @@ async function prepareCodexAttemptRuntime(connection) {
|
|
|
6955
7041
|
};
|
|
6956
7042
|
}
|
|
6957
7043
|
//#endregion
|
|
7044
|
+
//#region extensions/codex/src/app-server/dynamic-tool-result-projection.ts
|
|
7045
|
+
/** Project one OpenClaw dynamic-tool response with its executed mutation identity. */
|
|
7046
|
+
function recordCodexDynamicToolResult(projector, call, response, protocolResponse) {
|
|
7047
|
+
projector?.recordDynamicToolResult({
|
|
7048
|
+
callId: call.callId,
|
|
7049
|
+
tool: call.tool,
|
|
7050
|
+
asyncStarted: response.asyncStarted === true,
|
|
7051
|
+
terminalResolution: response.terminalResolution,
|
|
7052
|
+
success: protocolResponse.success,
|
|
7053
|
+
terminalType: response.diagnosticTerminalType ?? (protocolResponse.success ? "completed" : "error"),
|
|
7054
|
+
sideEffectEvidence: response.sideEffectEvidence === true || response.terminalResolution?.sideEffectEvidence === true,
|
|
7055
|
+
contentItems: protocolResponse.contentItems
|
|
7056
|
+
});
|
|
7057
|
+
}
|
|
7058
|
+
//#endregion
|
|
6958
7059
|
//#region extensions/codex/src/app-server/run-attempt-tools.ts
|
|
6959
7060
|
function toTranscriptToolResult(response) {
|
|
6960
7061
|
const sanitized = sanitizeCodexToolResponse(response);
|
|
@@ -7180,8 +7281,10 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
|
|
|
7180
7281
|
toolBridge,
|
|
7181
7282
|
signal: runAbortController.signal,
|
|
7182
7283
|
timeoutMs: dynamicToolTimeoutMs,
|
|
7284
|
+
toolMeta,
|
|
7183
7285
|
toolCallOrdinal,
|
|
7184
7286
|
onAgentToolResult: params.onAgentToolResult,
|
|
7287
|
+
observeToolTerminal: params.observeToolTerminal,
|
|
7185
7288
|
onFallbackSelected: () => {
|
|
7186
7289
|
if (toolCallOrdinal !== void 0) suppressedDynamicToolOutcomeOrdinals.add(toolCallOrdinal);
|
|
7187
7290
|
},
|
|
@@ -7217,15 +7320,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
|
|
|
7217
7320
|
success: protocolResponse.success,
|
|
7218
7321
|
contentItems: protocolResponse.contentItems
|
|
7219
7322
|
});
|
|
7220
|
-
projector
|
|
7221
|
-
callId: call.callId,
|
|
7222
|
-
tool: call.tool,
|
|
7223
|
-
asyncStarted: response.asyncStarted === true,
|
|
7224
|
-
success: protocolResponse.success,
|
|
7225
|
-
terminalType: response.diagnosticTerminalType ?? (protocolResponse.success ? "completed" : "error"),
|
|
7226
|
-
sideEffectEvidence: response.sideEffectEvidence === true,
|
|
7227
|
-
contentItems: protocolResponse.contentItems
|
|
7228
|
-
});
|
|
7323
|
+
recordCodexDynamicToolResult(projector, call, response, protocolResponse);
|
|
7229
7324
|
if (shouldEmitDynamicToolProgress) {
|
|
7230
7325
|
const progressResponse = toCodexDynamicToolProgressResponse(response, protocolResponse);
|
|
7231
7326
|
emitCodexAppServerEvent(params, {
|
|
@@ -7255,7 +7350,7 @@ function createCodexAttemptServerRequestController(resources, turnRuntime, lifec
|
|
|
7255
7350
|
durationMs: toolDurationMs
|
|
7256
7351
|
});
|
|
7257
7352
|
pendingOpenClawDynamicToolCompletionIds.delete(call.callId);
|
|
7258
|
-
if (response.terminate === true) scheduleTurnReleaseAfterTerminalDynamicTool({
|
|
7353
|
+
if (response.terminate === true && response.success) scheduleTurnReleaseAfterTerminalDynamicTool({
|
|
7259
7354
|
call,
|
|
7260
7355
|
response,
|
|
7261
7356
|
durationMs: toolDurationMs
|
|
@@ -7397,11 +7492,200 @@ function clearComputerUseHealthMonitor(client, monitor) {
|
|
|
7397
7492
|
getComputerUseHealthMonitorState().monitors.delete(client);
|
|
7398
7493
|
}
|
|
7399
7494
|
//#endregion
|
|
7495
|
+
//#region extensions/codex/src/app-server/plugin-metadata-cache.ts
|
|
7496
|
+
const CODEX_PLUGIN_METADATA_CACHE_TTL_MS = 3600 * 1e3;
|
|
7497
|
+
/** Process-local plugin metadata cache with coalesced loads per query. */
|
|
7498
|
+
var CodexPluginMetadataCache = class {
|
|
7499
|
+
constructor(nowMs = Date.now) {
|
|
7500
|
+
this.nowMs = nowMs;
|
|
7501
|
+
this.entries = /* @__PURE__ */ new Map();
|
|
7502
|
+
this.inFlight = /* @__PURE__ */ new Map();
|
|
7503
|
+
this.generations = /* @__PURE__ */ new Map();
|
|
7504
|
+
this.clearGeneration = 0;
|
|
7505
|
+
}
|
|
7506
|
+
/** Returns a fresh cached snapshot without issuing a request. */
|
|
7507
|
+
read(appCacheKey, queryKind) {
|
|
7508
|
+
const entryKey = buildMetadataCacheEntryKey(appCacheKey, queryKind);
|
|
7509
|
+
const entry = this.entries.get(entryKey);
|
|
7510
|
+
if (!entry) return;
|
|
7511
|
+
if (entry.expiresAtMs <= this.nowMs()) {
|
|
7512
|
+
this.entries.delete(entryKey);
|
|
7513
|
+
return;
|
|
7514
|
+
}
|
|
7515
|
+
return entry.snapshot;
|
|
7516
|
+
}
|
|
7517
|
+
/** Returns a fresh cached snapshot or coalesces one plugin/list request. */
|
|
7518
|
+
async load(params) {
|
|
7519
|
+
const entryKey = buildMetadataCacheEntryKey(params.appCacheKey, params.queryKind);
|
|
7520
|
+
const cached = this.read(params.appCacheKey, params.queryKind);
|
|
7521
|
+
if (cached) return cached;
|
|
7522
|
+
const pending = this.inFlight.get(entryKey);
|
|
7523
|
+
if (pending) try {
|
|
7524
|
+
return await pending.promise;
|
|
7525
|
+
} catch {
|
|
7526
|
+
if (this.inFlight.get(entryKey) === pending) this.inFlight.delete(entryKey);
|
|
7527
|
+
return await this.load(params);
|
|
7528
|
+
}
|
|
7529
|
+
const generation = this.generations.get(params.appCacheKey) ?? 0;
|
|
7530
|
+
const clearGeneration = this.clearGeneration;
|
|
7531
|
+
const promise = (async () => {
|
|
7532
|
+
const response = await params.request("plugin/list", params.requestParams);
|
|
7533
|
+
const snapshot = {
|
|
7534
|
+
appCacheKey: params.appCacheKey,
|
|
7535
|
+
queryKind: params.queryKind,
|
|
7536
|
+
response
|
|
7537
|
+
};
|
|
7538
|
+
if (generation === (this.generations.get(params.appCacheKey) ?? 0) && clearGeneration === this.clearGeneration && !hasMarketplaceLoadErrors(response) && (params.cacheable?.(response) ?? true)) this.entries.set(entryKey, {
|
|
7539
|
+
snapshot,
|
|
7540
|
+
expiresAtMs: this.nowMs() + CODEX_PLUGIN_METADATA_CACHE_TTL_MS
|
|
7541
|
+
});
|
|
7542
|
+
return snapshot;
|
|
7543
|
+
})();
|
|
7544
|
+
this.inFlight.set(entryKey, {
|
|
7545
|
+
appCacheKey: params.appCacheKey,
|
|
7546
|
+
promise
|
|
7547
|
+
});
|
|
7548
|
+
try {
|
|
7549
|
+
return await promise;
|
|
7550
|
+
} finally {
|
|
7551
|
+
if (this.inFlight.get(entryKey)?.promise === promise) this.inFlight.delete(entryKey);
|
|
7552
|
+
}
|
|
7553
|
+
}
|
|
7554
|
+
/** Invalidates all plugin metadata queries for one app-server runtime. */
|
|
7555
|
+
invalidate(appCacheKey) {
|
|
7556
|
+
this.generations.set(appCacheKey, (this.generations.get(appCacheKey) ?? 0) + 1);
|
|
7557
|
+
for (const [entryKey, entry] of this.entries) if (entry.snapshot.appCacheKey === appCacheKey) this.entries.delete(entryKey);
|
|
7558
|
+
for (const [entryKey, pending] of this.inFlight) if (pending.appCacheKey === appCacheKey) this.inFlight.delete(entryKey);
|
|
7559
|
+
}
|
|
7560
|
+
/** Clears snapshots and prevents late in-flight loads from repopulating them. */
|
|
7561
|
+
clear() {
|
|
7562
|
+
this.clearGeneration += 1;
|
|
7563
|
+
this.generations.clear();
|
|
7564
|
+
this.entries.clear();
|
|
7565
|
+
this.inFlight.clear();
|
|
7566
|
+
}
|
|
7567
|
+
};
|
|
7568
|
+
/** Shared plugin metadata cache used by Codex app-server runtime paths. */
|
|
7569
|
+
const defaultCodexPluginMetadataCache = new CodexPluginMetadataCache();
|
|
7570
|
+
function hasMarketplaceLoadErrors(response) {
|
|
7571
|
+
return (response.marketplaceLoadErrors?.length ?? 0) > 0;
|
|
7572
|
+
}
|
|
7573
|
+
function buildMetadataCacheEntryKey(appCacheKey, queryKind) {
|
|
7574
|
+
return JSON.stringify([appCacheKey, queryKind]);
|
|
7575
|
+
}
|
|
7576
|
+
//#endregion
|
|
7577
|
+
//#region extensions/codex/src/app-server/plugin-thread-config-deadline.ts
|
|
7578
|
+
/** Enforces one bounded startup budget across Codex plugin config discovery. */
|
|
7579
|
+
const CODEX_PLUGIN_THREAD_CONFIG_MAX_TIMEOUT_MS = 5e3;
|
|
7580
|
+
const CODEX_PLUGIN_THREAD_CONFIG_TIMEOUT_DIVISOR = 4;
|
|
7581
|
+
const CODEX_PLUGIN_THREAD_CONFIG_MIN_TIMEOUT_MS = 100;
|
|
7582
|
+
var CodexPluginThreadConfigDeadlineError = class extends Error {
|
|
7583
|
+
constructor() {
|
|
7584
|
+
super("Codex plugin thread config deadline elapsed");
|
|
7585
|
+
this.name = "CodexPluginThreadConfigDeadlineError";
|
|
7586
|
+
}
|
|
7587
|
+
};
|
|
7588
|
+
/** Resolves the plugin policy state reused throughout app-server startup. */
|
|
7589
|
+
function resolveCodexPluginThreadConfigStartupPolicy(params) {
|
|
7590
|
+
const pluginThreadConfigRequired = !params.nativeToolSurfaceEnabled || shouldBuildCodexPluginThreadConfig(params.pluginConfig);
|
|
7591
|
+
const pluginThreadConfigPluginConfig = params.nativeToolSurfaceEnabled ? params.pluginConfig : disableCodexPluginThreadConfig(params.pluginConfig);
|
|
7592
|
+
const resolvedPluginPolicy = pluginThreadConfigRequired ? resolveCodexPluginsPolicy(pluginThreadConfigPluginConfig) : void 0;
|
|
7593
|
+
return {
|
|
7594
|
+
pluginThreadConfigRequired,
|
|
7595
|
+
pluginThreadConfigPluginConfig,
|
|
7596
|
+
resolvedPluginPolicy,
|
|
7597
|
+
enabledPluginConfigKeys: resolvedPluginPolicy ? resolvedPluginPolicy.pluginPolicies.filter((plugin) => plugin.enabled).map((plugin) => plugin.configKey).toSorted() : void 0
|
|
7598
|
+
};
|
|
7599
|
+
}
|
|
7600
|
+
/** Builds plugin config without allowing sequential RPC timeouts to consume the turn. */
|
|
7601
|
+
async function buildCodexPluginThreadConfigWithinDeadline(params) {
|
|
7602
|
+
const { requestTimeoutMs, signal, request, ...buildParams } = params;
|
|
7603
|
+
const timeoutMs = resolveCodexPluginThreadConfigTimeoutMs(requestTimeoutMs);
|
|
7604
|
+
const deadlineMs = Date.now() + timeoutMs;
|
|
7605
|
+
try {
|
|
7606
|
+
return await waitForCodexPluginThreadConfigBuild({
|
|
7607
|
+
signal,
|
|
7608
|
+
timeoutMs,
|
|
7609
|
+
build: () => buildCodexPluginThreadConfig({
|
|
7610
|
+
...buildParams,
|
|
7611
|
+
request: (method, requestParams) => {
|
|
7612
|
+
const remainingTimeoutMs = deadlineMs - Date.now();
|
|
7613
|
+
if (remainingTimeoutMs <= 0) throw new CodexPluginThreadConfigDeadlineError();
|
|
7614
|
+
return request(method, requestParams, {
|
|
7615
|
+
timeoutMs: remainingTimeoutMs,
|
|
7616
|
+
signal
|
|
7617
|
+
});
|
|
7618
|
+
}
|
|
7619
|
+
})
|
|
7620
|
+
});
|
|
7621
|
+
} catch (error) {
|
|
7622
|
+
if (signal.aborted || !isCodexPluginThreadConfigTimeoutError(error)) throw error;
|
|
7623
|
+
return buildCodexPluginThreadConfigTimeoutFallback({
|
|
7624
|
+
pluginConfig: buildParams.pluginConfig,
|
|
7625
|
+
appCacheKey: buildParams.appCacheKey,
|
|
7626
|
+
message: `Codex plugin discovery exceeded its ${timeoutMs} ms startup budget; plugin apps were disabled for this turn.`
|
|
7627
|
+
});
|
|
7628
|
+
}
|
|
7629
|
+
}
|
|
7630
|
+
function waitForCodexPluginThreadConfigBuild(params) {
|
|
7631
|
+
if (params.signal.aborted) return Promise.reject(resolveAbortReason(params.signal));
|
|
7632
|
+
return new Promise((resolve, reject) => {
|
|
7633
|
+
let settled = false;
|
|
7634
|
+
const finish = () => {
|
|
7635
|
+
if (settled) return false;
|
|
7636
|
+
settled = true;
|
|
7637
|
+
clearTimeout(timer);
|
|
7638
|
+
params.signal.removeEventListener("abort", onAbort);
|
|
7639
|
+
return true;
|
|
7640
|
+
};
|
|
7641
|
+
const resolveOnce = (config) => {
|
|
7642
|
+
if (finish()) resolve(config);
|
|
7643
|
+
};
|
|
7644
|
+
const rejectOnce = (error) => {
|
|
7645
|
+
if (finish()) reject(error instanceof Error ? error : new Error(String(error)));
|
|
7646
|
+
};
|
|
7647
|
+
const onAbort = () => rejectOnce(resolveAbortReason(params.signal));
|
|
7648
|
+
const timer = setTimeout(() => rejectOnce(new CodexPluginThreadConfigDeadlineError()), params.timeoutMs);
|
|
7649
|
+
params.signal.addEventListener("abort", onAbort, { once: true });
|
|
7650
|
+
params.build().then(resolveOnce, rejectOnce);
|
|
7651
|
+
});
|
|
7652
|
+
}
|
|
7653
|
+
function resolveAbortReason(signal) {
|
|
7654
|
+
return signal.reason instanceof Error ? signal.reason : /* @__PURE__ */ new Error("Codex plugin thread config aborted");
|
|
7655
|
+
}
|
|
7656
|
+
/** Creates the recovery metadata and bounded builder used by thread startup. */
|
|
7657
|
+
function createCodexPluginThreadConfigStartupProvider(params) {
|
|
7658
|
+
const { client, policy, inputFingerprint, enabledPluginConfigKeys, appCache, metadataCache: configuredMetadataCache, ...buildParams } = params;
|
|
7659
|
+
const metadataCache = configuredMetadataCache ?? defaultCodexPluginMetadataCache;
|
|
7660
|
+
return {
|
|
7661
|
+
enabled: true,
|
|
7662
|
+
inputFingerprint,
|
|
7663
|
+
enabledPluginConfigKeys,
|
|
7664
|
+
accountAppRecoveryEnabled: policy?.allowAllPlugins,
|
|
7665
|
+
recoverablePluginConfigKeys: policy ? resolveRecoverableCodexPluginConfigKeys({
|
|
7666
|
+
policy,
|
|
7667
|
+
metadataCache,
|
|
7668
|
+
appCacheKey: params.appCacheKey
|
|
7669
|
+
}) : void 0,
|
|
7670
|
+
build: () => buildCodexPluginThreadConfigWithinDeadline({
|
|
7671
|
+
...buildParams,
|
|
7672
|
+
appCache: appCache ?? defaultCodexAppInventoryCache,
|
|
7673
|
+
metadataCache,
|
|
7674
|
+
request: (method, requestParams, options) => client.request(method, requestParams, options)
|
|
7675
|
+
})
|
|
7676
|
+
};
|
|
7677
|
+
}
|
|
7678
|
+
function resolveCodexPluginThreadConfigTimeoutMs(requestTimeoutMs) {
|
|
7679
|
+
return Math.min(CODEX_PLUGIN_THREAD_CONFIG_MAX_TIMEOUT_MS, Math.max(CODEX_PLUGIN_THREAD_CONFIG_MIN_TIMEOUT_MS, Math.floor((Number.isFinite(requestTimeoutMs) && requestTimeoutMs > 0 ? requestTimeoutMs : CODEX_PLUGIN_THREAD_CONFIG_MAX_TIMEOUT_MS * CODEX_PLUGIN_THREAD_CONFIG_TIMEOUT_DIVISOR) / CODEX_PLUGIN_THREAD_CONFIG_TIMEOUT_DIVISOR)));
|
|
7680
|
+
}
|
|
7681
|
+
function isCodexPluginThreadConfigTimeoutError(error) {
|
|
7682
|
+
return error instanceof CodexPluginThreadConfigDeadlineError || error instanceof Error && "code" in error && error.code === "CODEX_APP_SERVER_LOCAL_REQUEST_CANCELLED" && error.message.endsWith(" timed out");
|
|
7683
|
+
}
|
|
7684
|
+
//#endregion
|
|
7400
7685
|
//#region extensions/codex/src/app-server/attempt-startup.ts
|
|
7401
7686
|
/**
|
|
7402
7687
|
* Startup orchestration for Codex app-server attempts, including shared-client
|
|
7403
|
-
* leasing, plugin thread config, sandbox
|
|
7404
|
-
* lifecycle binding.
|
|
7688
|
+
* leasing, plugin thread config, sandbox environment, and thread lifecycle binding.
|
|
7405
7689
|
*/
|
|
7406
7690
|
const CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS = 3;
|
|
7407
7691
|
const CODEX_APP_SERVER_CONTEXT_RESTART_SELECTION_CHANGED = "CODEX_APP_SERVER_CONTEXT_RESTART_SELECTION_CHANGED";
|
|
@@ -7440,13 +7724,12 @@ async function startCodexAttemptThread(params) {
|
|
|
7440
7724
|
},
|
|
7441
7725
|
operation: async () => {
|
|
7442
7726
|
const threadConfig = mergeCodexThreadConfigs(params.bundleMcpThreadConfig?.configPatch);
|
|
7443
|
-
const pluginThreadConfigRequired
|
|
7444
|
-
|
|
7445
|
-
|
|
7727
|
+
const { pluginThreadConfigRequired, pluginThreadConfigPluginConfig, resolvedPluginPolicy, enabledPluginConfigKeys } = resolveCodexPluginThreadConfigStartupPolicy({
|
|
7728
|
+
pluginConfig: params.pluginConfig,
|
|
7729
|
+
nativeToolSurfaceEnabled: params.nativeToolSurfaceEnabled
|
|
7730
|
+
});
|
|
7446
7731
|
const computerUseMcpElicitationDelegationRequired = params.computerUseConfig.enabled;
|
|
7447
|
-
|
|
7448
|
-
const enabledPluginConfigKeys = resolvedPluginPolicy ? resolvedPluginPolicy.pluginPolicies.filter((plugin) => plugin.enabled).map((plugin) => plugin.configKey).toSorted() : void 0;
|
|
7449
|
-
pluginAppServer = mcpElicitationDelegationRequired ? {
|
|
7732
|
+
pluginAppServer = resolvedPluginPolicy?.enabled === true || computerUseMcpElicitationDelegationRequired ? {
|
|
7450
7733
|
...params.appServer,
|
|
7451
7734
|
approvalPolicy: withMcpElicitationsApprovalPolicy(params.appServer.approvalPolicy)
|
|
7452
7735
|
} : params.appServer;
|
|
@@ -7461,6 +7744,7 @@ async function startCodexAttemptThread(params) {
|
|
|
7461
7744
|
startupClient = await params.attemptClientFactory({
|
|
7462
7745
|
startOptions: params.appServer.start,
|
|
7463
7746
|
...params.startupPreparedAuth ? { preparedAuth: params.startupPreparedAuth } : { authProfileId: params.startupAuthProfileId },
|
|
7747
|
+
authRequirement: params.startupAuthRequirement,
|
|
7464
7748
|
authProfileStore: attemptParams.authProfileStore,
|
|
7465
7749
|
authBindingFingerprint: params.startupAuthBindingFingerprint,
|
|
7466
7750
|
...params.runtimeArtifactRequest ? {
|
|
@@ -7486,8 +7770,8 @@ async function startCodexAttemptThread(params) {
|
|
|
7486
7770
|
releaseSharedClientLease = startupClientLease;
|
|
7487
7771
|
attemptedClient = activeStartupClient;
|
|
7488
7772
|
startupClientForAbandonedRequestCleanup = activeStartupClient;
|
|
7489
|
-
if (startupAbandoned) throw new
|
|
7490
|
-
if (startupAbandonController.signal.aborted) throw new
|
|
7773
|
+
if (startupAbandoned) throw new CodexAppServerStartupError("timed_out");
|
|
7774
|
+
if (startupAbandonController.signal.aborted) throw new CodexAppServerStartupError("aborted");
|
|
7491
7775
|
let runtimeArtifact;
|
|
7492
7776
|
if (params.runtimeArtifactRequest) {
|
|
7493
7777
|
const { readCodexAppServerClientRuntimeArtifact, validateCodexAppServerRuntimeArtifact } = await import("./runtime-artifact-C2ITXdhM.js");
|
|
@@ -7568,7 +7852,7 @@ async function startCodexAttemptThread(params) {
|
|
|
7568
7852
|
startupSandboxEnvironmentAcquired = Boolean(startupSandboxEnvironment);
|
|
7569
7853
|
if (startupAbandonController.signal.aborted) {
|
|
7570
7854
|
await releaseStartupSandboxEnvironment();
|
|
7571
|
-
throw new
|
|
7855
|
+
throw new CodexAppServerStartupError("aborted");
|
|
7572
7856
|
}
|
|
7573
7857
|
if (params.sandbox?.enabled && params.nativeToolSurfaceEnabled && params.sandboxExecServerEnabled && !startupSandboxEnvironment) throw new Error("Codex app-server did not register an OpenClaw sandbox exec-server environment.");
|
|
7574
7858
|
} catch (error) {
|
|
@@ -7631,21 +7915,17 @@ async function startCodexAttemptThread(params) {
|
|
|
7631
7915
|
appServerRuntimeFingerprint,
|
|
7632
7916
|
contextEngineProjection: params.contextEngineProjection,
|
|
7633
7917
|
signal,
|
|
7634
|
-
pluginThreadConfig: pluginThreadConfigRequired ? {
|
|
7635
|
-
enabled: true,
|
|
7918
|
+
pluginThreadConfig: pluginThreadConfigRequired ? createCodexPluginThreadConfigStartupProvider({
|
|
7636
7919
|
inputFingerprint: pluginThreadConfigInputFingerprint,
|
|
7637
7920
|
enabledPluginConfigKeys,
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
|
|
7646
|
-
appCacheKey: pluginAppCacheKey
|
|
7647
|
-
})
|
|
7648
|
-
} : void 0
|
|
7921
|
+
policy: resolvedPluginPolicy,
|
|
7922
|
+
requestTimeoutMs: params.appServer.requestTimeoutMs,
|
|
7923
|
+
signal,
|
|
7924
|
+
pluginConfig: pluginThreadConfigPluginConfig,
|
|
7925
|
+
client: activeStartupClient,
|
|
7926
|
+
configCwd: startupExecutionCwd,
|
|
7927
|
+
appCacheKey: pluginAppCacheKey
|
|
7928
|
+
}) : void 0
|
|
7649
7929
|
});
|
|
7650
7930
|
try {
|
|
7651
7931
|
const startupThread = await startOrResumeThread(buildThreadLifecycleParams(startupAbandonController.signal, reserveStartupThread));
|
|
@@ -7658,7 +7938,7 @@ async function startCodexAttemptThread(params) {
|
|
|
7658
7938
|
})) throw new CodexAppServerUnsafeSubscriptionError("Codex startup subscription cleanup failed", { cause: error });
|
|
7659
7939
|
throw error;
|
|
7660
7940
|
}
|
|
7661
|
-
if (startupAbandonController.signal.aborted) throw new
|
|
7941
|
+
if (startupAbandonController.signal.aborted) throw new CodexAppServerStartupError("aborted");
|
|
7662
7942
|
const startupRoute = startupReservation;
|
|
7663
7943
|
if (!startupRoute) throw new Error("codex app-server startup did not reserve its thread route");
|
|
7664
7944
|
startupSandboxEnvironmentAcquired = false;
|
|
@@ -7773,14 +8053,14 @@ async function startCodexAttemptThread(params) {
|
|
|
7773
8053
|
}
|
|
7774
8054
|
}
|
|
7775
8055
|
function shouldClearSharedClientAfterStartupAbandon(error) {
|
|
7776
|
-
return
|
|
8056
|
+
return isCodexAppServerStartupError(error);
|
|
7777
8057
|
}
|
|
7778
8058
|
function shouldClearSharedClientAfterStartupRace(error) {
|
|
7779
|
-
return
|
|
8059
|
+
return shouldClearSharedClientAfterStartupAbandon(error) || isCodexAppServerRequestTimeoutError(error);
|
|
7780
8060
|
}
|
|
7781
8061
|
function shouldClearSharedClientAfterStartupFailure(params) {
|
|
7782
8062
|
if (!(params.error instanceof Error)) return !params.spawnedBy;
|
|
7783
|
-
if (params.error
|
|
8063
|
+
if (isCodexAppServerBrokenPipeError(params.error)) return true;
|
|
7784
8064
|
return !params.spawnedBy;
|
|
7785
8065
|
}
|
|
7786
8066
|
//#endregion
|
|
@@ -7791,7 +8071,7 @@ async function startCodexAttemptRuntime(resources) {
|
|
|
7791
8071
|
const { runtime, attemptTools, promptState } = context;
|
|
7792
8072
|
const { connection, runtimeParams, preparedAuthBinding, buildActiveRunAttemptParams, startupAuthAccountCacheKey, startupEnvApiKeyCacheKey, bundleMcpThreadConfig, nativeToolSurfaceEnabled, nativeProviderWebSearchSupport, sandboxExecServerEnabled } = runtime;
|
|
7793
8073
|
const { toolBridge, toolState } = attemptTools;
|
|
7794
|
-
const { params, attemptClientFactory, bindingStore, appServer, pluginConfig, computerUseConfig, startupClientAuthProfileId, runtimeArtifactRequest, startupPreparedAuth, agentDir, sessionAgentId, effectiveWorkspace, effectiveCwd, sandbox, runAbortController, usesSupervisionConnection, resolveReviewerPolicyContext, resolveRuntimeOptionsForCurrentBinding, startupAuthProfileId, abortFromUpstream } = connection;
|
|
8074
|
+
const { params, attemptClientFactory, bindingStore, appServer, pluginConfig, computerUseConfig, startupClientAuthProfileId, runtimeArtifactRequest, startupPreparedAuth, agentDir, sessionAgentId, effectiveWorkspace, effectiveCwd, sandbox, runAbortController, usesSupervisionConnection, resolveReviewerPolicyContext, resolveRuntimeOptionsForCurrentBinding, startupAuthProfileId, startupAuthRequirement, abortFromUpstream } = connection;
|
|
7795
8075
|
let pluginAppServer = withCodexAppServerFastModeServiceTier(appServer, runtimeParams);
|
|
7796
8076
|
try {
|
|
7797
8077
|
emitCodexAppServerEvent(params, {
|
|
@@ -7805,6 +8085,7 @@ async function startCodexAttemptRuntime(resources) {
|
|
|
7805
8085
|
pluginConfig,
|
|
7806
8086
|
computerUseConfig,
|
|
7807
8087
|
startupAuthProfileId: startupClientAuthProfileId,
|
|
8088
|
+
startupAuthRequirement,
|
|
7808
8089
|
startupAuthBindingFingerprint: preparedAuthBinding?.fingerprint,
|
|
7809
8090
|
...runtimeArtifactRequest ? { runtimeArtifactRequest } : {},
|
|
7810
8091
|
startupPreparedAuth,
|
|
@@ -7873,7 +8154,9 @@ async function startCodexAttemptRuntime(resources) {
|
|
|
7873
8154
|
stream: "codex_app_server.lifecycle",
|
|
7874
8155
|
data: {
|
|
7875
8156
|
phase: "thread_ready",
|
|
7876
|
-
threadId: state.thread.threadId
|
|
8157
|
+
threadId: state.thread.threadId,
|
|
8158
|
+
action: state.thread.lifecycle.action,
|
|
8159
|
+
clientId: state.client.getInstanceId()
|
|
7877
8160
|
}
|
|
7878
8161
|
});
|
|
7879
8162
|
} catch (error) {
|
|
@@ -8097,6 +8380,7 @@ async function prepareCodexAttemptTurnRequest(resources, turnRuntime, ensureCurr
|
|
|
8097
8380
|
model: usesSupervisionConnection ? resourceState.thread.model ?? effectiveRuntimeModelId : params.modelId,
|
|
8098
8381
|
api: usesSupervisionConnection ? runtimeParams.model.api : params.model.api,
|
|
8099
8382
|
transport: appServer.start.transport,
|
|
8383
|
+
observationUnit: "turn",
|
|
8100
8384
|
...hookContextWindowFields,
|
|
8101
8385
|
trace: codexModelCallTrace
|
|
8102
8386
|
},
|
|
@@ -8385,6 +8669,7 @@ async function startCodexAttemptTurn(resources, turnRuntime, notifications, requ
|
|
|
8385
8669
|
return { result: buildCodexTurnStartFailureResult({
|
|
8386
8670
|
params,
|
|
8387
8671
|
message: usageLimitError.message,
|
|
8672
|
+
promptError: createCodexUsageLimitPromptError(usageLimitError.message),
|
|
8388
8673
|
messagesSnapshot,
|
|
8389
8674
|
systemPromptReport
|
|
8390
8675
|
}) };
|
|
@@ -8771,6 +9056,7 @@ function createCodexAttemptTurnState(resources) {
|
|
|
8771
9056
|
rateLimitsRevisionBeforeLastTurnStart: void 0,
|
|
8772
9057
|
completed: false,
|
|
8773
9058
|
terminalTurnNotificationQueued: false,
|
|
9059
|
+
sawCodexInterruptMarker: false,
|
|
8774
9060
|
timedOut: false,
|
|
8775
9061
|
turnCompletionIdleTimedOut: false,
|
|
8776
9062
|
turnWatchTimeoutKind: void 0,
|
|
@@ -8798,7 +9084,7 @@ function createCodexAttemptTurnState(resources) {
|
|
|
8798
9084
|
state.resolveCompletion = resolve;
|
|
8799
9085
|
});
|
|
8800
9086
|
const turnCompletionIdleTimeoutMs = resolveCodexTurnCompletionIdleTimeoutMs(options.turnCompletionIdleTimeoutMs ?? appServer.turnCompletionIdleTimeoutMs);
|
|
8801
|
-
const turnAssistantCompletionIdleTimeoutMs = resolveCodexTurnAssistantCompletionIdleTimeoutMs(options.turnAssistantCompletionIdleTimeoutMs);
|
|
9087
|
+
const turnAssistantCompletionIdleTimeoutMs = resolveCodexTurnAssistantCompletionIdleTimeoutMs(options.turnAssistantCompletionIdleTimeoutMs ?? appServer.turnAssistantCompletionIdleTimeoutMs);
|
|
8802
9088
|
const postToolRawAssistantCompletionIdleTimeoutMs = resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs(options.postToolRawAssistantCompletionIdleTimeoutMs ?? appServer.postToolRawAssistantCompletionIdleTimeoutMs, turnAssistantCompletionIdleTimeoutMs);
|
|
8803
9089
|
const turnTerminalIdleTimeoutMs = resolveCodexTurnTerminalIdleTimeoutMs(options.turnTerminalIdleTimeoutMs, params.runTimeoutOverrideMs);
|
|
8804
9090
|
const turnAttemptIdleTimeoutMs = Math.max(100, Math.floor(params.timeoutMs));
|