@openclaw/codex 2026.7.1-beta.1 → 2026.7.1-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{app-server-policy-xi0ZmBGO.js → app-server-policy-BUk0GLMy.js} +5 -5
- package/dist/{attempt-notifications-l9LElGOU.js → attempt-notifications-BGsEIIDI.js} +5 -40
- package/dist/{command-handlers-vZy8xX5X.js → command-handlers-UVniG5Hl.js} +267 -232
- package/dist/command-rpc-BjgEPh19.js +784 -0
- package/dist/compact-CZzbcygx.js +603 -0
- package/dist/{computer-use-FPGG-mtX.js → computer-use-DDeySrnb.js} +3 -3
- package/dist/{config-C_whBx3M.js → config-CYEDnLJ2.js} +36 -15
- package/dist/conversation-binding-data-BHRpYbX3.js +121 -0
- package/dist/doctor-contract-api.js +439 -22
- package/dist/harness.js +29 -10
- package/dist/index.js +1119 -28
- package/dist/{media-understanding-provider-C5yYzx_6.js → media-understanding-provider-DgqRvU2_.js} +13 -8
- package/dist/media-understanding-provider.js +1 -1
- package/dist/{models-Ccy1_7nN.js → models-DDmO1zwd.js} +2 -2
- package/dist/notification-correlation-Bo7KB3ks.js +35 -0
- package/dist/{plugin-app-cache-key-CDioyur-.js → plugin-app-cache-key-o-AHbdaf.js} +8 -4
- package/dist/{protocol-dh-ETiNd.js → protocol-2POPqAY4.js} +2 -1
- package/dist/{protocol-validators-CB4mtyrs.js → protocol-validators-dZQ-UTOa.js} +117 -62
- package/dist/{provider-capabilities-CE5k8cmO.js → provider-capabilities-gTCwjfmh.js} +2920 -114
- package/dist/{provider-DosqwRb6.js → provider-cc_62eQE.js} +19 -10
- package/dist/provider.js +2 -2
- package/dist/{request-BQuSQF29.js → request-BcJyl8KL.js} +2 -2
- package/dist/{run-attempt-O3Zs32KI.js → run-attempt-8SPLiDlT.js} +1016 -2877
- package/dist/{sandbox-guard-pTY2COoC.js → sandbox-guard-DA2TQfZW.js} +3 -3
- package/dist/session-binding-Dc03iwRF.js +595 -0
- package/dist/session-binding-meta-B7aEMU7g.js +6 -0
- package/dist/{shared-client-rR6TDJA3.js → shared-client-4ICy3U6d.js} +892 -103
- package/dist/{side-question-DDVgFmpv.js → side-question-DkjXvYv7.js} +132 -140
- package/dist/{thread-lifecycle-DeSMZSXr.js → thread-lifecycle-BskXnNP-.js} +609 -444
- package/dist/{web-search-provider.runtime-Q89rQhsL.js → web-search-provider.runtime-B5F42P7x.js} +3 -3
- package/npm-shrinkwrap.json +34 -34
- package/openclaw.plugin.json +20 -0
- package/package.json +9 -6
- package/dist/client-IcTBKEqA.js +0 -732
- package/dist/client-factory-CwjdQQ4j.js +0 -18
- package/dist/compact-BzB9AqN1.js +0 -278
- package/dist/conversation-binding-CEE2UyYb.js +0 -1570
- package/dist/rate-limit-cache-C7qmZ0Jh.js +0 -26
- package/dist/session-binding-Cm0Gg7Z1.js +0 -339
|
@@ -1,194 +1,53 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { a as
|
|
6
|
-
import {
|
|
7
|
-
import { a as
|
|
8
|
-
import {
|
|
9
|
-
import { n as buildCodexPluginAppCacheKey, r as CODEX_CONTROL_METHODS, t as buildCodexAppServerRuntimeFingerprint } from "./plugin-app-cache-key-
|
|
10
|
-
import { a as formatCodexDisplayText, n as resolveCodexAppServerForOpenClawToolPolicy, t as resolveCodexAppServerForModelProvider } from "./app-server-policy-
|
|
11
|
-
import {
|
|
12
|
-
import { t as
|
|
13
|
-
import {
|
|
14
|
-
import { t as ensureCodexComputerUse } from "./computer-use-FPGG-mtX.js";
|
|
15
|
-
import { n as rememberCodexRateLimits, t as readRecentCodexRateLimits } from "./rate-limit-cache-C7qmZ0Jh.js";
|
|
1
|
+
import { n as flattenCodexDynamicToolFunctions, r as isJsonObject } from "./protocol-2POPqAY4.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-BGsEIIDI.js";
|
|
3
|
+
import { _ as shouldAutoApproveCodexAppServerApprovals, a as isCodexAppServerApprovalPolicyAllowedByRequirements, d as resolveCodexAppServerRuntimeOptions, g as resolveOpenClawExecPolicyForCodexAppServer, h as resolveCodexPluginsPolicy, m as resolveCodexModelBackedReviewerPolicyContext, p as resolveCodexComputerUseConfig, s as isCodexSandboxExecServerEnabled, u as readCodexPluginConfig, v as withMcpElicitationsApprovalPolicy } from "./config-CYEDnLJ2.js";
|
|
4
|
+
import { a as readCodexDynamicToolCallParams, i as assertCodexTurnStartResponse } from "./protocol-validators-dZQ-UTOa.js";
|
|
5
|
+
import { A as resolveCodexContextEngineProjectionReserveTokens, B as buildCodexPluginThreadConfigInputFingerprint, D as fitCodexProjectedContextForTurnStart, E as resolveCodexDynamicToolsLoadingForRuntime, F as interruptCodexTurnBestEffort, H as shouldBuildCodexPluginThreadConfig, I as retireCodexAppServerClientAfterTimedOutTurn, K as defaultCodexAppInventoryCache, L as unsubscribeCodexThreadBestEffort, M as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, N as CodexAppServerUnsafeSubscriptionError, O as projectContextEngineAssemblyForCodex, P as closeCodexStartupClientBestEffort, V as mergeCodexThreadConfigs, _ as resolveCodexWebSearchPlan, a as buildDeveloperInstructions, c as codexDynamicToolsFingerprint, f as resolveCodexAppServerThreadModelSelection, h as startOrResumeThread, i as buildContextEngineBinding, j as CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS, k as resolveCodexContextEngineProjectionMaxChars, l as isContextEngineBindingCompatible, n as areCodexDynamicToolFingerprintsCompatible, o as buildTurnCollaborationMode, s as buildTurnStartParams, v as isCodexAppServerProfilerEnabled, z as buildCodexPluginThreadConfig } from "./thread-lifecycle-BskXnNP-.js";
|
|
6
|
+
import { c as formatCodexUsageLimitErrorMessage, d as shouldRefreshCodexRateLimitsForUsageLimitMessage, u as resolveCodexUsageLimitResetAtMs } from "./provider-cc_62eQE.js";
|
|
7
|
+
import { _ as resolveCodexAppServerAuthAccountCacheKey, a as getLeasedSharedCodexAppServerClient, b as resolveCodexAppServerFallbackApiKeyCacheKey, d as isCodexAppServerApprovalRequest, f as isCodexAppServerConnectionClosedError, g as rememberCodexRateLimitsRead, h as readRecentCodexRateLimits, m as readCodexRateLimitsRevision, o as releaseLeasedSharedCodexAppServerClient, p as ensureCodexAppServerClientRuntime, s as retireSharedCodexAppServerClientIfCurrent, t as clearSharedCodexAppServerClientIfCurrent, u as CodexAppServerRpcError, v as resolveCodexAppServerAuthProfileId, x as resolveCodexAppServerHomeDir, y as resolveCodexAppServerAuthProfileIdForAgent } from "./shared-client-4ICy3U6d.js";
|
|
8
|
+
import { o as sessionBindingIdentity, r as isCodexAppServerNativeAuthProfile } from "./session-binding-Dc03iwRF.js";
|
|
9
|
+
import { n as buildCodexPluginAppCacheKey, r as CODEX_CONTROL_METHODS, t as buildCodexAppServerRuntimeFingerprint } from "./plugin-app-cache-key-o-AHbdaf.js";
|
|
10
|
+
import { a as formatCodexDisplayText, n as resolveCodexAppServerForOpenClawToolPolicy, t as resolveCodexAppServerForModelProvider } from "./app-server-policy-BUk0GLMy.js";
|
|
11
|
+
import { n as readCodexNotificationThreadId, r as readCodexNotificationTurnId, t as isCodexNotificationForTurn } from "./notification-correlation-Bo7KB3ks.js";
|
|
12
|
+
import { $ as isDynamicToolTerminalDiagnosticEvent, A as createCodexDynamicToolBuildStageTracker, B as shouldWarnCodexDynamicToolBuildStageSummary, C as resolveCodexLocalRuntimeAttribution, D as emitDynamicToolStartedDiagnostic, E as emitDynamicToolErrorDiagnostic, F as resolveCodexExternalSandboxPolicyForOpenClawSandbox, G as resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs, H as readCodexMirroredSessionHistoryMessages, I as resolveCodexMessageToolProvider, J as resolveCodexTurnCompletionIdleTimeoutMs, K as resolveCodexStartupTimeoutMs, L as resolveCodexSandboxEnvironmentSelection, M as formatCodexDynamicToolBuildStageSummary, N as resolveCodexAppServerExecutionCwd, O as emitDynamicToolTerminalDiagnostic, P as resolveCodexAppServerHookChannelId, Q as hasPendingDynamicToolTerminalDiagnostic, R as shouldEnableCodexAppServerNativeToolSurface, S as scheduleCodexNativeHookRelayUnregister, T as createCodexDynamicToolBridge, U as handleCodexAppServerApprovalRequest, W as CODEX_POST_REASONING_REPLY_IDLE_TIMEOUT_MS, X as withCodexStartupTimeout, Y as resolveCodexTurnTerminalIdleTimeoutMs, Z as handleDynamicToolCallWithTimeout, _ as buildCodexNativeHookRelayDisabledConfig, a as shouldEmitTranscriptToolProgress, at as toCodexDynamicToolProgressResponse, b as resolveCodexNativeHookRelayEvents, c as mirrorPromptAtTurnStartBestEffort, d as resolveCodexToolProgressDetailMode, et as isMatchingDynamicToolTerminalDiagnostic, f as sanitizeCodexToolArguments, g as buildCodexNativeHookRelayConfig, h as CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS, it as shouldReleaseTurnAfterTerminalDynamicTool, j as disableCodexPluginThreadConfig, k as buildDynamicTools, l as mirrorTranscriptBestEffort, nt as resolveTerminalDynamicToolBatchAction, o as buildCodexUserPromptMessage, ot as toCodexDynamicToolProtocolResponse, p as sanitizeCodexToolResponse, q as resolveCodexTurnAssistantCompletionIdleTimeoutMs, r as CodexAppServerEventProjector, rt as shouldBlockTerminalReleaseForNonTerminalDynamicToolResult, s as createCodexAppServerUserMessagePersistenceNotifier, st as resolveCodexToolAbortTerminalReason, t as resolveCodexProviderWebSearchSupport, tt as resolveDynamicToolCallTimeoutMs, u as inferCodexDynamicToolMeta, v as createCodexNativeHookRelay, w as handleCodexAppServerElicitationRequest, x as resolveCodexNativeHookRelayTtlMs, y as emitCodexNativePreToolUseFailureDiagnostic, z as shouldRequireCodexSandboxExecServerEnvironment } from "./provider-capabilities-gTCwjfmh.js";
|
|
13
|
+
import { t as ensureCodexComputerUse } from "./computer-use-DDeySrnb.js";
|
|
16
14
|
import fs from "node:fs/promises";
|
|
17
15
|
import path, { posix } from "node:path";
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
16
|
+
import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
|
|
17
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
20
18
|
import { createHash, randomUUID } from "node:crypto";
|
|
21
19
|
import fsSync from "node:fs";
|
|
22
20
|
import { markAuthProfileBlockedUntil, resolveAgentDir, resolveAgentWorkspaceDir } from "openclaw/plugin-sdk/agent-runtime";
|
|
23
21
|
import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
|
|
24
|
-
import {
|
|
22
|
+
import { asFiniteNumber, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
23
|
+
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, loadCodexBundleMcpThreadConfig, 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";
|
|
25
24
|
import { buildMemorySystemPromptAddition } from "openclaw/plugin-sdk/core";
|
|
26
25
|
import { spawn } from "node:child_process";
|
|
27
26
|
import { once } from "node:events";
|
|
28
27
|
import { WebSocketServer } from "ws";
|
|
29
|
-
import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
|
|
30
|
-
import { createDiagnosticTraceContextFromActiveScope, emitTrustedDiagnosticEvent, emitTrustedDiagnosticEventWithPrivateData, freezeDiagnosticTraceContext, hasPendingInternalDiagnosticEvent, onInternalDiagnosticEvent, resolveDiagnosticModelContentCapturePolicy } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
31
28
|
import { appendRegularFile, pathExists } from "openclaw/plugin-sdk/security-runtime";
|
|
29
|
+
import { createDiagnosticTraceContextFromActiveScope, emitTrustedDiagnosticEvent, emitTrustedDiagnosticEventWithPrivateData, freezeDiagnosticTraceContext, onInternalDiagnosticEvent, resolveDiagnosticModelContentCapturePolicy } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
32
30
|
import { MESSAGE_TOOL_DELIVERY_HINTS } from "openclaw/plugin-sdk/message-tool-delivery-hints";
|
|
33
|
-
import { buildSessionContext, migrateSessionEntries, parseSessionEntries } from "openclaw/plugin-sdk/agent-sessions";
|
|
34
|
-
import { publishSessionTranscriptUpdateByIdentity, resolveSessionTranscriptTarget, withSessionTranscriptWriteLock } from "openclaw/plugin-sdk/session-transcript-runtime";
|
|
35
31
|
import { isIP } from "node:net";
|
|
36
32
|
import { SsrFBlockedError, isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
37
|
-
import { generatedImageAssetFromBase64 } from "openclaw/plugin-sdk/image-generation";
|
|
38
|
-
import { saveMediaBuffer } from "openclaw/plugin-sdk/media-store";
|
|
39
33
|
import { createAgentHarnessTaskRuntime, deliverAgentHarnessTaskCompletion, isDurableAgentHarnessCompletionDelivery } from "openclaw/plugin-sdk/agent-harness-task-runtime";
|
|
40
|
-
//#region extensions/codex/src/app-server/attempt-client-cleanup.ts
|
|
41
|
-
/**
|
|
42
|
-
* Best-effort cleanup helpers for Codex app-server startup attempts and turns.
|
|
43
|
-
*/
|
|
44
|
-
/** Timeout for best-effort app-server turn interruption during cleanup. */
|
|
45
|
-
const CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS = 5e3;
|
|
46
|
-
/** Timeout for best-effort thread unsubscribe during cleanup. */
|
|
47
|
-
const CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS = 5e3;
|
|
48
|
-
async function closeClientAndWaitIfAvailable(client) {
|
|
49
|
-
const closeable = client;
|
|
50
|
-
if (typeof closeable.closeAndWait === "function") {
|
|
51
|
-
await closeable.closeAndWait();
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
closeable.close?.();
|
|
55
|
-
}
|
|
56
|
-
async function closeCodexStartupClientBestEffort(client) {
|
|
57
|
-
if (!client) return;
|
|
58
|
-
const unclaimedSharedClient = clearSharedCodexAppServerClientIfCurrentAndUnclaimed(client);
|
|
59
|
-
if (unclaimedSharedClient.closed) {
|
|
60
|
-
await closeClientAndWaitIfAvailable(client);
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
if (unclaimedSharedClient.found) {
|
|
64
|
-
if (retireSharedCodexAppServerClientIfCurrent(client)?.closed) await closeClientAndWaitIfAvailable(client);
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client);
|
|
68
|
-
if (retiredSharedClient) {
|
|
69
|
-
if (retiredSharedClient.closed) await closeClientAndWaitIfAvailable(client);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
if (clearSharedCodexAppServerClientIfCurrent(client)) {
|
|
73
|
-
await closeClientAndWaitIfAvailable(client);
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
await closeClientAndWaitIfAvailable(client);
|
|
77
|
-
}
|
|
78
|
-
/** Sends a turn interrupt without blocking abort cleanup on app-server errors. */
|
|
79
|
-
function interruptCodexTurnBestEffort(client, params) {
|
|
80
|
-
const requestOptions = params.timeoutMs && Number.isFinite(params.timeoutMs) && params.timeoutMs > 0 ? { timeoutMs: params.timeoutMs } : void 0;
|
|
81
|
-
const requestParams = {
|
|
82
|
-
threadId: params.threadId,
|
|
83
|
-
turnId: params.turnId
|
|
84
|
-
};
|
|
85
|
-
try {
|
|
86
|
-
const interrupt = requestOptions ? client.request("turn/interrupt", requestParams, requestOptions) : client.request("turn/interrupt", requestParams);
|
|
87
|
-
Promise.resolve(interrupt).catch((error) => {
|
|
88
|
-
embeddedAgentLog.debug("codex app-server turn interrupt failed during abort", { error });
|
|
89
|
-
});
|
|
90
|
-
} catch (error) {
|
|
91
|
-
embeddedAgentLog.debug("codex app-server turn interrupt failed during abort", { error });
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
/** Unsubscribes from a thread while swallowing cleanup-only failures. */
|
|
95
|
-
async function unsubscribeCodexThreadBestEffort(client, params) {
|
|
96
|
-
try {
|
|
97
|
-
await client.request("thread/unsubscribe", { threadId: params.threadId }, { timeoutMs: params.timeoutMs });
|
|
98
|
-
} catch (error) {
|
|
99
|
-
embeddedAgentLog.debug("codex app-server thread unsubscribe cleanup failed", {
|
|
100
|
-
threadId: params.threadId,
|
|
101
|
-
error
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Retires the shared client after a timed-out turn so later runs do not reuse a
|
|
107
|
-
* potentially wedged app-server connection.
|
|
108
|
-
*/
|
|
109
|
-
async function retireCodexAppServerClientAfterTimedOutTurn(client, params) {
|
|
110
|
-
const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client);
|
|
111
|
-
const detachedSharedClient = Boolean(retiredSharedClient);
|
|
112
|
-
interruptCodexTurnBestEffort(client, {
|
|
113
|
-
threadId: params.threadId,
|
|
114
|
-
turnId: params.turnId,
|
|
115
|
-
timeoutMs: CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS
|
|
116
|
-
});
|
|
117
|
-
await unsubscribeCodexThreadBestEffort(client, {
|
|
118
|
-
threadId: params.threadId,
|
|
119
|
-
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
|
|
120
|
-
});
|
|
121
|
-
let closedClient = retiredSharedClient?.closed ?? false;
|
|
122
|
-
if (!detachedSharedClient) {
|
|
123
|
-
const close = client.close;
|
|
124
|
-
if (typeof close === "function") try {
|
|
125
|
-
close.call(client);
|
|
126
|
-
closedClient = true;
|
|
127
|
-
} catch (error) {
|
|
128
|
-
embeddedAgentLog.debug("codex app-server client close failed during timeout cleanup", {
|
|
129
|
-
threadId: params.threadId,
|
|
130
|
-
turnId: params.turnId,
|
|
131
|
-
error
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
embeddedAgentLog.warn("codex app-server client retired after timed-out turn", {
|
|
136
|
-
threadId: params.threadId,
|
|
137
|
-
turnId: params.turnId,
|
|
138
|
-
reason: params.reason,
|
|
139
|
-
detachedSharedClient,
|
|
140
|
-
closedClient,
|
|
141
|
-
activeSharedClientLeases: retiredSharedClient?.activeLeases ?? 0
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
//#endregion
|
|
145
|
-
//#region extensions/codex/src/app-server/session-history.ts
|
|
146
|
-
/**
|
|
147
|
-
* Reads OpenClaw session history for Codex transcript mirroring and sanitizes
|
|
148
|
-
* image payloads before replaying messages into the app-server projector.
|
|
149
|
-
*/
|
|
150
|
-
/** Returns sanitized session-context messages for a Codex mirrored session file. */
|
|
151
|
-
async function readCodexMirroredSessionHistoryMessages(target) {
|
|
152
|
-
try {
|
|
153
|
-
await resolveSessionTranscriptTarget(resolveCodexHistoryTranscriptTarget(target));
|
|
154
|
-
const entries = parseSessionEntries(await fs.readFile(target.sessionFile, "utf-8"));
|
|
155
|
-
if (entries.length === 0) return [];
|
|
156
|
-
const firstEntry = entries[0];
|
|
157
|
-
if (firstEntry?.type !== "session" || typeof firstEntry.id !== "string") return;
|
|
158
|
-
migrateSessionEntries(entries);
|
|
159
|
-
return sanitizeCodexHistoryImagePayloads(buildSessionContext(entries.filter((entry) => {
|
|
160
|
-
return entry !== null && typeof entry === "object" && !Array.isArray(entry) && entry.type !== "session";
|
|
161
|
-
})).messages, "codex mirrored history");
|
|
162
|
-
} catch {
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
function resolveCodexHistoryTranscriptTarget(target) {
|
|
167
|
-
return {
|
|
168
|
-
...target.agentId ? { agentId: target.agentId } : {},
|
|
169
|
-
sessionFile: target.sessionFile,
|
|
170
|
-
sessionId: target.sessionId,
|
|
171
|
-
sessionKey: target.sessionKey ?? ""
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
//#endregion
|
|
175
34
|
//#region extensions/codex/src/app-server/attempt-context.ts
|
|
176
35
|
/**
|
|
177
36
|
* Builds Codex app-server prompt context, workspace bootstrap injections,
|
|
178
37
|
* system-prompt reports, and context-engine projection decisions.
|
|
179
38
|
*/
|
|
180
|
-
const CODEX_NATIVE_PROJECT_DOC_BASENAMES = new Set(["agents.md"]);
|
|
181
|
-
const CODEX_INHERITED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES = new Set(["tools.md"]);
|
|
182
|
-
const CODEX_TURN_SCOPED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES = new Set([
|
|
39
|
+
const CODEX_NATIVE_PROJECT_DOC_BASENAMES = /* @__PURE__ */ new Set(["agents.md"]);
|
|
40
|
+
const CODEX_INHERITED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES = /* @__PURE__ */ new Set(["tools.md"]);
|
|
41
|
+
const CODEX_TURN_SCOPED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES = /* @__PURE__ */ new Set([
|
|
183
42
|
"identity.md",
|
|
184
43
|
"soul.md",
|
|
185
44
|
"user.md"
|
|
186
45
|
]);
|
|
187
|
-
const CODEX_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES = new Set([...CODEX_INHERITED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES, ...CODEX_TURN_SCOPED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES]);
|
|
46
|
+
const CODEX_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES = /* @__PURE__ */ new Set([...CODEX_INHERITED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES, ...CODEX_TURN_SCOPED_WORKSPACE_DEVELOPER_CONTEXT_BASENAMES]);
|
|
188
47
|
const CODEX_HEARTBEAT_CONTEXT_BASENAME = "heartbeat.md";
|
|
189
48
|
const CODEX_MEMORY_CONTEXT_BASENAME = "memory.md";
|
|
190
|
-
const CODEX_MEMORY_TOOL_NAMES = new Set(["memory_search", "memory_get"]);
|
|
191
|
-
const CODEX_BOOTSTRAP_CONTEXT_ORDER = new Map([
|
|
49
|
+
const CODEX_MEMORY_TOOL_NAMES = /* @__PURE__ */ new Set(["memory_search", "memory_get"]);
|
|
50
|
+
const CODEX_BOOTSTRAP_CONTEXT_ORDER = /* @__PURE__ */ new Map([
|
|
192
51
|
["soul.md", 10],
|
|
193
52
|
["identity.md", 20],
|
|
194
53
|
["user.md", 30],
|
|
@@ -427,10 +286,10 @@ function stableJsonHash(value) {
|
|
|
427
286
|
function buildCodexBootstrapInjectionStats(params) {
|
|
428
287
|
const injectedIndex = indexCodexContextFileContent(params.injectedFiles);
|
|
429
288
|
const developerInstructionIndex = indexCodexContextFileContent(params.developerInstructionFiles ?? []);
|
|
430
|
-
const memoryToolRoutedPaths = new Set((params.memoryToolRoutedBootstrapFiles ?? []).map((file) => readNonEmptyString
|
|
289
|
+
const memoryToolRoutedPaths = new Set((params.memoryToolRoutedBootstrapFiles ?? []).map((file) => readNonEmptyString(file.path)).filter(isNonEmptyString$1).map(normalizeCodexContextFilePath));
|
|
431
290
|
return params.bootstrapFiles.map((file) => {
|
|
432
|
-
const fileName = readNonEmptyString
|
|
433
|
-
const pathValue = readNonEmptyString
|
|
291
|
+
const fileName = readNonEmptyString(file.name);
|
|
292
|
+
const pathValue = readNonEmptyString(file.path) ?? fileName ?? "";
|
|
434
293
|
const displayName = (fileName ?? getCodexContextFileDisplayBasename(pathValue)) || pathValue;
|
|
435
294
|
const baseName = getCodexContextFileBasename(pathValue || fileName || "");
|
|
436
295
|
const rawChars = file.missing ? 0 : (file.content ?? "").trimEnd().length;
|
|
@@ -461,7 +320,7 @@ function indexCodexContextFileContent(files) {
|
|
|
461
320
|
const byPath = /* @__PURE__ */ new Map();
|
|
462
321
|
const byBaseName = /* @__PURE__ */ new Map();
|
|
463
322
|
for (const file of files) {
|
|
464
|
-
const pathValue = readNonEmptyString
|
|
323
|
+
const pathValue = readNonEmptyString(file.path);
|
|
465
324
|
if (!pathValue) continue;
|
|
466
325
|
if (!byPath.has(pathValue)) byPath.set(pathValue, file.content);
|
|
467
326
|
const baseName = getCodexContextFileBasename(pathValue);
|
|
@@ -485,7 +344,7 @@ function readCodexIndexedContextFileContent(index, pathValue, fileName) {
|
|
|
485
344
|
function readPositiveNumber(value) {
|
|
486
345
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
|
|
487
346
|
}
|
|
488
|
-
function readNonEmptyString
|
|
347
|
+
function readNonEmptyString(value) {
|
|
489
348
|
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
490
349
|
}
|
|
491
350
|
/**
|
|
@@ -706,13 +565,13 @@ function isMissingCodexBootstrapContextFile(file) {
|
|
|
706
565
|
}
|
|
707
566
|
function toCodexEmbeddedContextFile(file) {
|
|
708
567
|
return {
|
|
709
|
-
path: readNonEmptyString
|
|
568
|
+
path: readNonEmptyString(file.path) ?? readNonEmptyString(file.name) ?? "",
|
|
710
569
|
content: file.content ?? ""
|
|
711
570
|
};
|
|
712
571
|
}
|
|
713
572
|
function isCodexWorkspaceRootMemoryBootstrapFile(params) {
|
|
714
573
|
return isCodexWorkspaceRootMemoryPath({
|
|
715
|
-
filePath: readNonEmptyString
|
|
574
|
+
filePath: readNonEmptyString(params.file.path) ?? readNonEmptyString(params.file.name) ?? "",
|
|
716
575
|
workspaceDir: params.workspaceDir
|
|
717
576
|
});
|
|
718
577
|
}
|
|
@@ -819,6 +678,7 @@ function buildCodexPluginThreadConfigEligibilityLogData(params) {
|
|
|
819
678
|
enabled: params.pluginThreadConfigRequired,
|
|
820
679
|
policyConfigured: params.resolvedPluginPolicy?.configured === true,
|
|
821
680
|
policyEnabled: params.resolvedPluginPolicy?.enabled === true,
|
|
681
|
+
allowAllPlugins: params.resolvedPluginPolicy?.allowAllPlugins === true,
|
|
822
682
|
pluginConfigKeys: params.resolvedPluginPolicy?.pluginPolicies.map((plugin) => plugin.configKey).toSorted(),
|
|
823
683
|
enabledPluginConfigKeys: params.enabledPluginConfigKeys,
|
|
824
684
|
appCacheKeyFingerprint: fingerprintCodexLogValue("openclaw:codex:plugin-app-cache-key:v1", params.pluginAppCacheKey),
|
|
@@ -927,7 +787,7 @@ function reportCodexExecutionNotification(params) {
|
|
|
927
787
|
}
|
|
928
788
|
/** Returns true when a notification ends the current app-server turn. */
|
|
929
789
|
function isTerminalCodexTurnNotificationForTurn(params) {
|
|
930
|
-
if (!
|
|
790
|
+
if (!isCodexNotificationForTurn(params.notification.params, params.threadId, params.turnId)) return false;
|
|
931
791
|
return params.notification.method === "turn/completed" || isCodexTurnAbortMarkerNotification(params.notification, { currentPromptTexts: params.currentPromptTexts });
|
|
932
792
|
}
|
|
933
793
|
/**
|
|
@@ -936,11 +796,10 @@ function isTerminalCodexTurnNotificationForTurn(params) {
|
|
|
936
796
|
*/
|
|
937
797
|
function applyCodexTurnNotificationState(params) {
|
|
938
798
|
const { notification, turnWatches } = params;
|
|
939
|
-
const isCurrentTurnNotification =
|
|
799
|
+
const isCurrentTurnNotification = isCodexNotificationForTurn(notification.params, params.threadId, params.turnId);
|
|
940
800
|
const isTurnCompletion = notification.method === "turn/completed" && isCurrentTurnNotification;
|
|
941
|
-
const isNativeResponseStreamDelta = isNativeResponseStreamDeltaNotification(notification);
|
|
942
801
|
let turnCrossedToolHandoff = params.turnCrossedToolHandoff;
|
|
943
|
-
if (isCurrentTurnNotification
|
|
802
|
+
if (isCurrentTurnNotification) {
|
|
944
803
|
turnWatches.touchActivity(`notification:${notification.method}`, {
|
|
945
804
|
details: describeNotificationActivity(notification),
|
|
946
805
|
attemptProgress: true
|
|
@@ -985,7 +844,7 @@ function applyCodexTurnNotificationState(params) {
|
|
|
985
844
|
else if (rawResponseItemCompletedWithNoActiveItems) turnWatches.armCompletionIdleWatch();
|
|
986
845
|
else if (isCurrentTurnNotification && rawToolOutputCompletion) armPostToolContinuationWatch();
|
|
987
846
|
else if (isCurrentTurnNotification && shouldDisarmAssistantCompletionIdleWatch(notification)) turnWatches.disarmAssistantCompletionIdleWatch();
|
|
988
|
-
if (turnWatches.isCompletionIdleWatchArmed() && !turnWatches.isCompletionIdleWatchPinnedByTerminalError() && notification.method !== "turn/completed" && isCurrentTurnNotification && !
|
|
847
|
+
if (turnWatches.isCompletionIdleWatchArmed() && !turnWatches.isCompletionIdleWatchPinnedByTerminalError() && notification.method !== "turn/completed" && isCurrentTurnNotification && !trackedDynamicToolCompletion && !rawToolOutputCompletion && !postToolProgressNeedsTerminalGuard && !postToolPatchUpdateNeedsTerminalGuard && !rawResponseItemCompletedWithNoActiveItems && !shouldArmNoToolPostProgressReplyWatch && !shouldArmNoToolPostRawProgressReplyWatch && !shouldRearmCompletionIdleWatchAfterLastCurrentTurnItem) turnWatches.disarmCompletionIdleWatch();
|
|
989
848
|
if (trackedDynamicToolCompletion) {
|
|
990
849
|
const itemId = readNotificationItemId(notification);
|
|
991
850
|
if (itemId) {
|
|
@@ -2258,7 +2117,6 @@ const CODEX_SANDBOX_EXEC_SERVER_MAX_INBOUND_MESSAGE_BYTES = 100 * 1024 * 1024;
|
|
|
2258
2117
|
async function ensureCodexSandboxExecServerEnvironment(params) {
|
|
2259
2118
|
if (!params.sandbox?.enabled || !params.sandbox.backend) return;
|
|
2260
2119
|
if (!canExposeLocalExecServerToAppServer(params.appServerStartOptions)) throw new Error("OpenClaw Codex exec-server uses a local loopback URL and cannot be registered with a remote Codex app-server.");
|
|
2261
|
-
assertCodexSandboxExecServerSupported(params.client);
|
|
2262
2120
|
const execServer = await acquireOpenClawExecServer(params.sandbox);
|
|
2263
2121
|
try {
|
|
2264
2122
|
await params.client.request("environment/add", {
|
|
@@ -2287,10 +2145,6 @@ async function releaseCodexSandboxExecServerEnvironment(sandbox) {
|
|
|
2287
2145
|
const server = await SANDBOX_EXEC_SERVERS.get(sandbox.runtimeId)?.catch(() => void 0);
|
|
2288
2146
|
if (server) await releaseOpenClawExecServer(server);
|
|
2289
2147
|
}
|
|
2290
|
-
function assertCodexSandboxExecServerSupported(client) {
|
|
2291
|
-
const detectedVersion = client.getServerVersion();
|
|
2292
|
-
if (!detectedVersion || compareCodexAppServerVersions(detectedVersion, "0.132.0") < 0) throw new Error(`Codex app-server ${MIN_CODEX_SANDBOX_EXEC_SERVER_APP_SERVER_VERSION} or newer is required for OpenClaw sandbox exec-server environments, but detected ${detectedVersion ?? "an unknown version"}. Disable appServer.experimental.sandboxExecServer or configure a newer Codex app-server binary.`);
|
|
2293
|
-
}
|
|
2294
2148
|
function isEnvironmentAddUnsupported(error) {
|
|
2295
2149
|
if (!(error instanceof Error)) return false;
|
|
2296
2150
|
return error.message.includes("environment/add") && (error.message.includes("unknown variant") || error.message.includes("Method not found"));
|
|
@@ -2447,6 +2301,335 @@ async function dispatchRequest(execServer, processes, socket, request) {
|
|
|
2447
2301
|
}
|
|
2448
2302
|
}
|
|
2449
2303
|
//#endregion
|
|
2304
|
+
//#region extensions/codex/src/app-server/turn-router.ts
|
|
2305
|
+
/** Keyed routing for all turn traffic on one shared Codex app-server client. */
|
|
2306
|
+
const DEFAULT_PREBIND_NOTIFICATION_LIMIT = 256;
|
|
2307
|
+
const CODEX_APP_SERVER_NATIVE_TURN_WAIT_TIMEOUT_MS = 3e4;
|
|
2308
|
+
const routers = /* @__PURE__ */ new WeakMap();
|
|
2309
|
+
/** Returns the sole router installed on a physical app-server client. */
|
|
2310
|
+
function getCodexAppServerTurnRouter(client) {
|
|
2311
|
+
const existing = routers.get(client);
|
|
2312
|
+
if (existing) return existing;
|
|
2313
|
+
const router = new ClientTurnRouter(client);
|
|
2314
|
+
routers.set(client, router);
|
|
2315
|
+
return router;
|
|
2316
|
+
}
|
|
2317
|
+
var ClientTurnRouter = class {
|
|
2318
|
+
constructor(client) {
|
|
2319
|
+
this.routes = /* @__PURE__ */ new Map();
|
|
2320
|
+
this.nativeTurnCompletionWatchers = /* @__PURE__ */ new Map();
|
|
2321
|
+
this.disposed = false;
|
|
2322
|
+
client.addNotificationHandler((notification) => this.routeNotification(notification));
|
|
2323
|
+
client.addRequestHandler((request) => this.routeRequest(request));
|
|
2324
|
+
client.addCloseHandler(() => this.dispose());
|
|
2325
|
+
}
|
|
2326
|
+
reserveThread(options) {
|
|
2327
|
+
this.assertActive();
|
|
2328
|
+
const threadId = requireId(options.threadId, "thread id");
|
|
2329
|
+
if (this.routes.has(threadId)) throw new Error(`codex app-server thread route already reserved: ${threadId}`);
|
|
2330
|
+
const route = {
|
|
2331
|
+
threadId,
|
|
2332
|
+
controller: new AbortController(),
|
|
2333
|
+
ended: deferred(),
|
|
2334
|
+
activated: deferred(),
|
|
2335
|
+
gate: "open",
|
|
2336
|
+
pending: [],
|
|
2337
|
+
notificationTail: Promise.resolve(),
|
|
2338
|
+
nativeTurnCompleted: false
|
|
2339
|
+
};
|
|
2340
|
+
this.routes.set(threadId, route);
|
|
2341
|
+
if (options.onNotification || options.onRequest) this.activateNow(route, options);
|
|
2342
|
+
const releaseOn = options.releaseOn;
|
|
2343
|
+
if (releaseOn) {
|
|
2344
|
+
const release = () => this.release(route, abortReason(releaseOn));
|
|
2345
|
+
releaseOn.addEventListener("abort", release, { once: true });
|
|
2346
|
+
route.detachReleaseOn = () => releaseOn.removeEventListener("abort", release);
|
|
2347
|
+
if (releaseOn.aborted) release();
|
|
2348
|
+
}
|
|
2349
|
+
return {
|
|
2350
|
+
threadId,
|
|
2351
|
+
signal: route.controller.signal,
|
|
2352
|
+
activate: (handlers) => this.activate(route, handlers),
|
|
2353
|
+
armTurn: () => this.armTurn(route),
|
|
2354
|
+
bindTurn: (turnId) => this.bindTurn(route, turnId),
|
|
2355
|
+
cancelTurn: () => this.cancelTurn(route),
|
|
2356
|
+
waitForTurnCompletion: (waitOptions) => this.waitForTurnCompletion(route, waitOptions),
|
|
2357
|
+
drain: () => this.drainNotifications(route),
|
|
2358
|
+
release: () => this.release(route)
|
|
2359
|
+
};
|
|
2360
|
+
}
|
|
2361
|
+
watchNativeTurnCompletion(options) {
|
|
2362
|
+
this.assertActive();
|
|
2363
|
+
const threadId = requireId(options.threadId, "thread id");
|
|
2364
|
+
const turnId = requireId(options.turnId, "turn id");
|
|
2365
|
+
let settle;
|
|
2366
|
+
const completion = new Promise((resolve) => {
|
|
2367
|
+
settle = resolve;
|
|
2368
|
+
});
|
|
2369
|
+
const watchers = this.nativeTurnCompletionWatchers.get(threadId) ?? /* @__PURE__ */ new Set();
|
|
2370
|
+
this.nativeTurnCompletionWatchers.set(threadId, watchers);
|
|
2371
|
+
let settled = false;
|
|
2372
|
+
const finish = (completed) => {
|
|
2373
|
+
if (settled) return;
|
|
2374
|
+
settled = true;
|
|
2375
|
+
watchers.delete(watcher);
|
|
2376
|
+
if (watchers.size === 0) this.nativeTurnCompletionWatchers.delete(threadId);
|
|
2377
|
+
clearTimeout(timeout);
|
|
2378
|
+
settle(completed);
|
|
2379
|
+
};
|
|
2380
|
+
const touch = () => {
|
|
2381
|
+
timeout.refresh();
|
|
2382
|
+
};
|
|
2383
|
+
const watcher = {
|
|
2384
|
+
turnId,
|
|
2385
|
+
finish,
|
|
2386
|
+
touch
|
|
2387
|
+
};
|
|
2388
|
+
watchers.add(watcher);
|
|
2389
|
+
const timeout = setTimeout(() => finish(false), Math.max(1, options.timeoutMs));
|
|
2390
|
+
timeout.unref?.();
|
|
2391
|
+
return {
|
|
2392
|
+
completion,
|
|
2393
|
+
cancel: () => finish(false)
|
|
2394
|
+
};
|
|
2395
|
+
}
|
|
2396
|
+
dispose() {
|
|
2397
|
+
if (this.disposed) return;
|
|
2398
|
+
this.disposed = true;
|
|
2399
|
+
for (const route of this.routes.values()) this.release(route, /* @__PURE__ */ new Error("codex app-server turn router closed"));
|
|
2400
|
+
for (const watchers of this.nativeTurnCompletionWatchers.values()) for (const watcher of watchers) watcher.finish(false);
|
|
2401
|
+
}
|
|
2402
|
+
async activate(route, handlers) {
|
|
2403
|
+
this.assertRoute(route);
|
|
2404
|
+
this.activateNow(route, handlers);
|
|
2405
|
+
await this.waitForNotifications(route);
|
|
2406
|
+
this.assertRoute(route);
|
|
2407
|
+
}
|
|
2408
|
+
activateNow(route, handlers) {
|
|
2409
|
+
if (route.handlers) throw new Error(`codex app-server thread route already activated: ${route.threadId}`);
|
|
2410
|
+
this.assertRoute(route);
|
|
2411
|
+
if (!handlers.onNotification && !handlers.onRequest) throw new Error("codex app-server thread route requires a notification or request handler");
|
|
2412
|
+
route.handlers = handlers;
|
|
2413
|
+
if (!handlers.onNotification) route.pending.length = 0;
|
|
2414
|
+
else if (route.gate !== "armed") this.flushNotifications(route);
|
|
2415
|
+
route.activated.resolve();
|
|
2416
|
+
}
|
|
2417
|
+
armTurn(route) {
|
|
2418
|
+
this.assertRoute(route);
|
|
2419
|
+
if (route.gate !== "open") throw new Error(`codex app-server thread route cannot arm from ${route.gate}`);
|
|
2420
|
+
route.gate = "armed";
|
|
2421
|
+
route.nativeTurnCompleted = false;
|
|
2422
|
+
route.binding = deferred();
|
|
2423
|
+
}
|
|
2424
|
+
async cancelTurn(route) {
|
|
2425
|
+
if (route.released || route.gate !== "armed") return;
|
|
2426
|
+
route.gate = "open";
|
|
2427
|
+
route.binding?.resolve();
|
|
2428
|
+
route.binding = void 0;
|
|
2429
|
+
this.flushNotifications(route);
|
|
2430
|
+
await this.waitForNotifications(route);
|
|
2431
|
+
this.assertRoute(route);
|
|
2432
|
+
}
|
|
2433
|
+
async bindTurn(route, turnIdInput) {
|
|
2434
|
+
this.assertRoute(route);
|
|
2435
|
+
if (!route.handlers) throw new Error("codex app-server thread route must be activated before binding a turn");
|
|
2436
|
+
if (route.gate !== "armed") throw new Error(`codex app-server thread route cannot bind from ${route.gate}`);
|
|
2437
|
+
const turnId = requireId(turnIdInput, "turn id");
|
|
2438
|
+
route.gate = "bound";
|
|
2439
|
+
route.turnId = turnId;
|
|
2440
|
+
this.flushNotifications(route);
|
|
2441
|
+
route.binding?.resolve();
|
|
2442
|
+
await this.waitForNotifications(route);
|
|
2443
|
+
this.assertRoute(route);
|
|
2444
|
+
}
|
|
2445
|
+
routeNotification(notification) {
|
|
2446
|
+
if (this.disposed) return;
|
|
2447
|
+
const scope = readScope(notification.params);
|
|
2448
|
+
const watchers = scope.threadId ? this.nativeTurnCompletionWatchers.get(scope.threadId) : void 0;
|
|
2449
|
+
const route = scope.threadId ? this.routes.get(scope.threadId) : void 0;
|
|
2450
|
+
if (!watchers && !route) return;
|
|
2451
|
+
const terminal = isCodexTerminalTurnNotification(notification);
|
|
2452
|
+
if (scope.turnId && watchers) {
|
|
2453
|
+
for (const watcher of watchers) if (watcher.turnId === scope.turnId) if (terminal) watcher.finish(true);
|
|
2454
|
+
else watcher.touch();
|
|
2455
|
+
}
|
|
2456
|
+
if (!route) return;
|
|
2457
|
+
const routeScope = {
|
|
2458
|
+
threadId: route.threadId,
|
|
2459
|
+
...scope.turnId ? { turnId: scope.turnId } : {}
|
|
2460
|
+
};
|
|
2461
|
+
const receivedAtMs = Date.now();
|
|
2462
|
+
if (route.gate !== "bound" && terminal) if (route.nativeTurnCompletion) route.nativeTurnCompletion.resolve();
|
|
2463
|
+
else route.nativeTurnCompleted = true;
|
|
2464
|
+
if (!route.handlers) {
|
|
2465
|
+
this.bufferNotification(route, notification, routeScope, receivedAtMs);
|
|
2466
|
+
return;
|
|
2467
|
+
}
|
|
2468
|
+
const handler = route.handlers.onNotification;
|
|
2469
|
+
if (!handler) return;
|
|
2470
|
+
if (route.gate === "bound" && scope.turnId && scope.turnId !== route.turnId) return;
|
|
2471
|
+
if (route.gate === "armed") {
|
|
2472
|
+
this.bufferNotification(route, notification, routeScope, receivedAtMs);
|
|
2473
|
+
return;
|
|
2474
|
+
}
|
|
2475
|
+
route.handlers.onNotificationReceived?.(notification, routeScope, receivedAtMs);
|
|
2476
|
+
this.enqueueNotification(route, handler, notification, routeScope);
|
|
2477
|
+
return route.notificationTail;
|
|
2478
|
+
}
|
|
2479
|
+
async routeRequest(request) {
|
|
2480
|
+
if (this.disposed) return;
|
|
2481
|
+
const scope = readScope(request.params);
|
|
2482
|
+
if (!scope.threadId) return;
|
|
2483
|
+
const route = this.routes.get(scope.threadId);
|
|
2484
|
+
if (!route || route.released) return;
|
|
2485
|
+
if (!route.handlers) await route.activated.promise;
|
|
2486
|
+
if (route.released || !route.handlers) return;
|
|
2487
|
+
const handler = route.handlers.onRequest;
|
|
2488
|
+
if (!handler) return;
|
|
2489
|
+
while (route.gate === "armed") {
|
|
2490
|
+
await route.binding?.promise;
|
|
2491
|
+
if (route.released) return;
|
|
2492
|
+
}
|
|
2493
|
+
if (route.gate === "bound") {
|
|
2494
|
+
if (scope.turnId && scope.turnId !== route.turnId) return;
|
|
2495
|
+
if (route.released) return;
|
|
2496
|
+
}
|
|
2497
|
+
await this.waitForNotifications(route);
|
|
2498
|
+
if (route.released) return;
|
|
2499
|
+
try {
|
|
2500
|
+
const result = await handler(request, {
|
|
2501
|
+
threadId: scope.threadId,
|
|
2502
|
+
...scope.turnId ? { turnId: scope.turnId } : {}
|
|
2503
|
+
});
|
|
2504
|
+
return route.released ? void 0 : result;
|
|
2505
|
+
} catch (error) {
|
|
2506
|
+
if (route.released) return;
|
|
2507
|
+
throw error;
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
flushNotifications(route) {
|
|
2511
|
+
const handler = route.handlers?.onNotification;
|
|
2512
|
+
if (!handler) return;
|
|
2513
|
+
for (const pending of route.pending.splice(0)) if (!pending.scope.turnId || route.gate !== "bound" || pending.scope.turnId === route.turnId) {
|
|
2514
|
+
route.handlers?.onNotificationReceived?.(pending.notification, pending.scope, pending.receivedAtMs);
|
|
2515
|
+
this.enqueueNotification(route, handler, pending.notification, pending.scope);
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
bufferNotification(route, notification, scope, receivedAtMs) {
|
|
2519
|
+
if (route.pending.length < DEFAULT_PREBIND_NOTIFICATION_LIMIT) {
|
|
2520
|
+
route.pending.push({
|
|
2521
|
+
notification,
|
|
2522
|
+
receivedAtMs,
|
|
2523
|
+
scope
|
|
2524
|
+
});
|
|
2525
|
+
return;
|
|
2526
|
+
}
|
|
2527
|
+
const error = /* @__PURE__ */ new Error(`codex app-server pre-bind notification buffer exceeded ${DEFAULT_PREBIND_NOTIFICATION_LIMIT} entries for thread ${route.threadId}`);
|
|
2528
|
+
embeddedAgentLog.warn(error.message);
|
|
2529
|
+
this.release(route, error);
|
|
2530
|
+
}
|
|
2531
|
+
enqueueNotification(route, handler, notification, scope) {
|
|
2532
|
+
if (route.released) return;
|
|
2533
|
+
route.notificationTail = route.notificationTail.then(() => handler(notification, scope)).catch((error) => {
|
|
2534
|
+
if (!route.released) embeddedAgentLog.warn("codex app-server keyed notification handler failed", {
|
|
2535
|
+
method: notification.method,
|
|
2536
|
+
threadId: route.threadId,
|
|
2537
|
+
turnId: route.turnId,
|
|
2538
|
+
error
|
|
2539
|
+
});
|
|
2540
|
+
});
|
|
2541
|
+
}
|
|
2542
|
+
async waitForNotifications(route) {
|
|
2543
|
+
await Promise.race([route.notificationTail, route.ended.promise]);
|
|
2544
|
+
}
|
|
2545
|
+
async drainNotifications(route) {
|
|
2546
|
+
await route.notificationTail;
|
|
2547
|
+
}
|
|
2548
|
+
async waitForTurnCompletion(route, options) {
|
|
2549
|
+
this.assertRoute(route);
|
|
2550
|
+
if (route.nativeTurnCompleted) {
|
|
2551
|
+
route.nativeTurnCompleted = false;
|
|
2552
|
+
return true;
|
|
2553
|
+
}
|
|
2554
|
+
if (route.nativeTurnCompletion) throw new Error(`codex app-server turn completion wait already active: ${route.threadId}`);
|
|
2555
|
+
const completion = deferred();
|
|
2556
|
+
route.nativeTurnCompletion = completion;
|
|
2557
|
+
let timeout;
|
|
2558
|
+
let removeAbort;
|
|
2559
|
+
const timedOut = new Promise((resolve) => {
|
|
2560
|
+
timeout = setTimeout(() => resolve(false), Math.max(1, options.timeoutMs));
|
|
2561
|
+
});
|
|
2562
|
+
const aborted = new Promise((resolve) => {
|
|
2563
|
+
const signal = options.signal;
|
|
2564
|
+
if (!signal) return;
|
|
2565
|
+
const onAbort = () => resolve(false);
|
|
2566
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2567
|
+
removeAbort = () => signal.removeEventListener("abort", onAbort);
|
|
2568
|
+
if (signal.aborted) onAbort();
|
|
2569
|
+
});
|
|
2570
|
+
try {
|
|
2571
|
+
return await Promise.race([
|
|
2572
|
+
completion.promise.then(() => true),
|
|
2573
|
+
route.ended.promise.then(() => false),
|
|
2574
|
+
timedOut,
|
|
2575
|
+
aborted
|
|
2576
|
+
]);
|
|
2577
|
+
} finally {
|
|
2578
|
+
if (route.nativeTurnCompletion === completion) route.nativeTurnCompletion = void 0;
|
|
2579
|
+
if (timeout) clearTimeout(timeout);
|
|
2580
|
+
removeAbort?.();
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
release(route, error = /* @__PURE__ */ new Error("codex app-server thread route is released")) {
|
|
2584
|
+
if (route.released) return;
|
|
2585
|
+
route.released = error;
|
|
2586
|
+
route.pending.length = 0;
|
|
2587
|
+
route.ended.resolve();
|
|
2588
|
+
route.activated.resolve();
|
|
2589
|
+
route.binding?.resolve();
|
|
2590
|
+
route.detachReleaseOn?.();
|
|
2591
|
+
route.controller.abort(error);
|
|
2592
|
+
if (this.routes.get(route.threadId) === route) this.routes.delete(route.threadId);
|
|
2593
|
+
}
|
|
2594
|
+
assertActive() {
|
|
2595
|
+
if (this.disposed) throw new Error("codex app-server turn router is closed");
|
|
2596
|
+
}
|
|
2597
|
+
assertRoute(route) {
|
|
2598
|
+
if (route.released) throw route.released;
|
|
2599
|
+
}
|
|
2600
|
+
};
|
|
2601
|
+
/** True after Codex will not continue the exact turn. */
|
|
2602
|
+
function isCodexTerminalTurnNotification(notification) {
|
|
2603
|
+
if (notification.method === "turn/completed") return true;
|
|
2604
|
+
return notification.method === "error" && isJsonObject(notification.params) && notification.params.willRetry === false;
|
|
2605
|
+
}
|
|
2606
|
+
function deferred() {
|
|
2607
|
+
let resolve;
|
|
2608
|
+
return {
|
|
2609
|
+
promise: new Promise((resolvePromise) => {
|
|
2610
|
+
resolve = resolvePromise;
|
|
2611
|
+
}),
|
|
2612
|
+
resolve
|
|
2613
|
+
};
|
|
2614
|
+
}
|
|
2615
|
+
function abortReason(signal) {
|
|
2616
|
+
return signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason ?? "codex app-server thread route aborted"));
|
|
2617
|
+
}
|
|
2618
|
+
function readScope(value) {
|
|
2619
|
+
if (!isJsonObject(value)) return {};
|
|
2620
|
+
const threadId = readCodexNotificationThreadId(value);
|
|
2621
|
+
const turnId = readCodexNotificationTurnId(value);
|
|
2622
|
+
return {
|
|
2623
|
+
...threadId ? { threadId } : {},
|
|
2624
|
+
...turnId ? { turnId } : {}
|
|
2625
|
+
};
|
|
2626
|
+
}
|
|
2627
|
+
function requireId(value, label) {
|
|
2628
|
+
const normalized = value.trim();
|
|
2629
|
+
if (!normalized) throw new Error(`codex app-server ${label} must not be empty`);
|
|
2630
|
+
return normalized;
|
|
2631
|
+
}
|
|
2632
|
+
//#endregion
|
|
2450
2633
|
//#region extensions/codex/src/app-server/attempt-startup.ts
|
|
2451
2634
|
/**
|
|
2452
2635
|
* Startup orchestration for Codex app-server attempts, including shared-client
|
|
@@ -2500,7 +2683,11 @@ async function startCodexAttemptThread(params) {
|
|
|
2500
2683
|
let startupAttemptError;
|
|
2501
2684
|
let startupAttemptSucceeded = false;
|
|
2502
2685
|
try {
|
|
2503
|
-
startupClient = await params.attemptClientFactory(
|
|
2686
|
+
startupClient = await params.attemptClientFactory({
|
|
2687
|
+
startOptions: params.appServer.start,
|
|
2688
|
+
authProfileId: params.startupAuthProfileId,
|
|
2689
|
+
agentDir: params.agentDir,
|
|
2690
|
+
config: params.config,
|
|
2504
2691
|
onStartedClient: (client) => {
|
|
2505
2692
|
startupClientForAbandonedRequestCleanup = client;
|
|
2506
2693
|
if (startupAbandoned || startupAbandonController.signal.aborted) closeCodexStartupClientBestEffort(client);
|
|
@@ -2519,6 +2706,12 @@ async function startCodexAttemptThread(params) {
|
|
|
2519
2706
|
startupClientForAbandonedRequestCleanup = activeStartupClient;
|
|
2520
2707
|
if (startupAbandoned) throw new Error("codex app-server startup timed out");
|
|
2521
2708
|
if (startupAbandonController.signal.aborted) throw new Error("codex app-server startup aborted");
|
|
2709
|
+
ensureCodexAppServerClientRuntime(activeStartupClient, {
|
|
2710
|
+
agentDir: params.agentDir,
|
|
2711
|
+
authProfileId: params.startupAuthProfileId,
|
|
2712
|
+
config: params.config
|
|
2713
|
+
});
|
|
2714
|
+
const turnRouter = getCodexAppServerTurnRouter(activeStartupClient);
|
|
2522
2715
|
await ensureCodexComputerUse({
|
|
2523
2716
|
client: activeStartupClient,
|
|
2524
2717
|
pluginConfig: params.pluginConfig,
|
|
@@ -2595,8 +2788,31 @@ async function startCodexAttemptThread(params) {
|
|
|
2595
2788
|
remoteWorkspaceRoot: params.appServer.remoteWorkspaceRoot
|
|
2596
2789
|
});
|
|
2597
2790
|
const startupSandboxPolicy = startupSandboxEnvironment ? resolveCodexExternalSandboxPolicyForOpenClawSandbox(params.sandbox) : void 0;
|
|
2598
|
-
|
|
2791
|
+
let startupReservation;
|
|
2792
|
+
const releaseStartupReservation = () => {
|
|
2793
|
+
startupReservation?.release();
|
|
2794
|
+
startupReservation = void 0;
|
|
2795
|
+
};
|
|
2796
|
+
const reserveStartupThread = (threadId) => {
|
|
2797
|
+
if (startupReservation) {
|
|
2798
|
+
if (startupReservation.threadId !== threadId) throw new Error(`codex app-server reserved ${startupReservation.threadId} but started ${threadId}`);
|
|
2799
|
+
return { release: releaseStartupReservation };
|
|
2800
|
+
}
|
|
2801
|
+
startupReservation = turnRouter.reserveThread({
|
|
2802
|
+
threadId,
|
|
2803
|
+
releaseOn: params.signal
|
|
2804
|
+
});
|
|
2805
|
+
return { release: releaseStartupReservation };
|
|
2806
|
+
};
|
|
2807
|
+
const releaseStartupResources = async () => {
|
|
2808
|
+
releaseStartupReservation();
|
|
2809
|
+
await releaseStartupSandboxEnvironment();
|
|
2810
|
+
};
|
|
2811
|
+
releaseStartupResourcesOnTimeout = releaseStartupResources;
|
|
2812
|
+
const buildThreadLifecycleParams = (signal, reserveResumeThread) => ({
|
|
2599
2813
|
client: activeStartupClient,
|
|
2814
|
+
reserveResumeThread,
|
|
2815
|
+
bindingStore: params.bindingStore,
|
|
2600
2816
|
params: params.buildAttemptParams(),
|
|
2601
2817
|
agentId: params.sessionAgentId,
|
|
2602
2818
|
cwd: startupExecutionCwd,
|
|
@@ -2636,15 +2852,25 @@ async function startCodexAttemptThread(params) {
|
|
|
2636
2852
|
} : void 0
|
|
2637
2853
|
});
|
|
2638
2854
|
try {
|
|
2639
|
-
const startupThread = await startOrResumeThread(buildThreadLifecycleParams(startupAbandonController.signal));
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2855
|
+
const startupThread = await startOrResumeThread(buildThreadLifecycleParams(startupAbandonController.signal, reserveStartupThread));
|
|
2856
|
+
try {
|
|
2857
|
+
reserveStartupThread(startupThread.threadId);
|
|
2858
|
+
} catch (error) {
|
|
2859
|
+
if (!await unsubscribeCodexThreadBestEffort(activeStartupClient, {
|
|
2860
|
+
threadId: startupThread.threadId,
|
|
2861
|
+
timeoutMs: 5e3
|
|
2862
|
+
})) throw new CodexAppServerUnsafeSubscriptionError("Codex startup subscription cleanup failed", { cause: error });
|
|
2863
|
+
throw error;
|
|
2643
2864
|
}
|
|
2865
|
+
if (startupAbandonController.signal.aborted) throw new Error("codex app-server startup aborted");
|
|
2866
|
+
const startupRoute = startupReservation;
|
|
2867
|
+
if (!startupRoute) throw new Error("codex app-server startup did not reserve its thread route");
|
|
2644
2868
|
startupSandboxEnvironmentAcquired = false;
|
|
2645
2869
|
startupAttemptSucceeded = true;
|
|
2646
2870
|
return {
|
|
2647
2871
|
client: activeStartupClient,
|
|
2872
|
+
turnRouter,
|
|
2873
|
+
turnRoute: startupRoute,
|
|
2648
2874
|
thread: startupThread,
|
|
2649
2875
|
sandboxEnvironment: startupSandboxEnvironment,
|
|
2650
2876
|
environmentSelection: startupEnvironmentSelection,
|
|
@@ -2653,10 +2879,10 @@ async function startCodexAttemptThread(params) {
|
|
|
2653
2879
|
restartContextEngineCodexThread: () => startOrResumeThread(buildThreadLifecycleParams(params.signal))
|
|
2654
2880
|
};
|
|
2655
2881
|
} catch (error) {
|
|
2656
|
-
await
|
|
2882
|
+
await releaseStartupResources();
|
|
2657
2883
|
throw error;
|
|
2658
2884
|
} finally {
|
|
2659
|
-
if (releaseStartupResourcesOnTimeout ===
|
|
2885
|
+
if (releaseStartupResourcesOnTimeout === releaseStartupResources) releaseStartupResourcesOnTimeout = void 0;
|
|
2660
2886
|
}
|
|
2661
2887
|
} catch (error) {
|
|
2662
2888
|
startupAttemptError = error;
|
|
@@ -2912,7 +3138,7 @@ function createCodexAttemptTurnWatchController(params) {
|
|
|
2912
3138
|
}
|
|
2913
3139
|
function scheduleAssistantCompletionIdleWatch() {
|
|
2914
3140
|
clearAssistantCompletionIdleTimer();
|
|
2915
|
-
if (params.isCompleted() || params.signal.aborted || !assistantCompletionIdleWatchArmed) return;
|
|
3141
|
+
if (params.isCompleted() || params.signal.aborted || !assistantCompletionIdleWatchArmed || params.getActiveFinalizationHookCount() > 0) return;
|
|
2916
3142
|
const elapsedMs = Math.max(0, Date.now() - assistantCompletionLastActivityAt);
|
|
2917
3143
|
const delayMs = Math.max(1, turnAssistantCompletionIdleTimeoutMs - elapsedMs);
|
|
2918
3144
|
assistantCompletionIdleTimer = setTimeout(fireAssistantCompletionIdleRelease, delayMs);
|
|
@@ -2955,10 +3181,16 @@ function createCodexAttemptTurnWatchController(params) {
|
|
|
2955
3181
|
}
|
|
2956
3182
|
function fireAssistantCompletionIdleRelease() {
|
|
2957
3183
|
if (params.isCompleted() || params.signal.aborted || !assistantCompletionIdleWatchArmed) return;
|
|
2958
|
-
if (params.getActiveAppServerTurnRequests() > 0 || params.getActiveTurnItemCount() > 0) {
|
|
3184
|
+
if (params.getActiveAppServerTurnRequests() > 0 || params.getActiveTurnItemCount() > 0 || params.getActiveFinalizationHookCount() > 0) {
|
|
2959
3185
|
scheduleAssistantCompletionIdleWatch();
|
|
2960
3186
|
return;
|
|
2961
3187
|
}
|
|
3188
|
+
if (!params.canReleaseAssistantCompletionIdle()) {
|
|
3189
|
+
assistantCompletionIdleWatchArmed = false;
|
|
3190
|
+
assistantCompletionLastActivityDetails = void 0;
|
|
3191
|
+
clearAssistantCompletionIdleTimer();
|
|
3192
|
+
return;
|
|
3193
|
+
}
|
|
2962
3194
|
const idleMs = Math.max(0, Date.now() - assistantCompletionLastActivityAt);
|
|
2963
3195
|
if (idleMs < turnAssistantCompletionIdleTimeoutMs) {
|
|
2964
3196
|
scheduleAssistantCompletionIdleWatch();
|
|
@@ -3156,2406 +3388,23 @@ function createCodexAttemptTurnWatchController(params) {
|
|
|
3156
3388
|
scheduleProgressWatches();
|
|
3157
3389
|
},
|
|
3158
3390
|
noteNotificationReceived: (method, options) => {
|
|
3159
|
-
|
|
3391
|
+
const now = Date.now();
|
|
3392
|
+
completionLastActivityAt = Math.max(completionLastActivityAt, Math.min(now, options?.receivedAtMs ?? now));
|
|
3160
3393
|
completionLastActivityReason = `notification:${method}`;
|
|
3161
3394
|
if (options?.details !== void 0) completionLastActivityDetails = options.details;
|
|
3162
|
-
if (options?.attemptProgress) recordAttemptProgress(completionLastActivityReason, options);
|
|
3163
|
-
},
|
|
3164
|
-
extendAttemptIdleWatch: (timeoutMs) => {
|
|
3165
|
-
attemptIdleTimeoutOverrideMs = resolveWatchTimeoutMs(timeoutMs);
|
|
3166
|
-
scheduleAttemptIdleWatch();
|
|
3167
|
-
},
|
|
3168
|
-
scheduleProgressWatches,
|
|
3169
|
-
clearCompletionIdleTimer,
|
|
3170
|
-
clearAssistantCompletionIdleTimer,
|
|
3171
|
-
clearTerminalIdleTimer,
|
|
3172
|
-
clearAttemptIdleTimer,
|
|
3173
|
-
clearAllTimers
|
|
3174
|
-
};
|
|
3175
|
-
}
|
|
3176
|
-
/** Hard cap for per-call Codex dynamic tool timeout overrides. */
|
|
3177
|
-
const CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS = 6e5;
|
|
3178
|
-
const CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS = 12e4;
|
|
3179
|
-
/** Timeout for message-delivery dynamic tool calls. */
|
|
3180
|
-
const CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS = 12e4;
|
|
3181
|
-
const LOG_FIELD_MAX_LENGTH = 160;
|
|
3182
|
-
function normalizeLogField(value) {
|
|
3183
|
-
if (typeof value !== "string") return;
|
|
3184
|
-
const normalized = value.replaceAll(String.fromCharCode(27), " ").replaceAll("\r", " ").replaceAll("\n", " ").replaceAll(" ", " ").trim();
|
|
3185
|
-
if (!normalized) return;
|
|
3186
|
-
return normalized.length > LOG_FIELD_MAX_LENGTH ? `${normalized.slice(0, LOG_FIELD_MAX_LENGTH - 3)}...` : normalized;
|
|
3187
|
-
}
|
|
3188
|
-
function readNumericTimeoutMs(value) {
|
|
3189
|
-
if (typeof value === "number" && Number.isFinite(value)) return Math.max(0, Math.floor(value));
|
|
3190
|
-
if (typeof value === "string") {
|
|
3191
|
-
const parsed = parseStrictNonNegativeInteger(value);
|
|
3192
|
-
if (parsed !== void 0) return Math.max(0, Math.floor(parsed));
|
|
3193
|
-
}
|
|
3194
|
-
}
|
|
3195
|
-
function formatDynamicToolTimeoutDetails(params) {
|
|
3196
|
-
const tool = normalizeLogField(params.call.tool) ?? "unknown";
|
|
3197
|
-
const baseMeta = {
|
|
3198
|
-
tool: params.call.tool,
|
|
3199
|
-
toolCallId: params.call.callId,
|
|
3200
|
-
threadId: params.call.threadId,
|
|
3201
|
-
turnId: params.call.turnId,
|
|
3202
|
-
timeoutMs: params.timeoutMs,
|
|
3203
|
-
timeoutKind: "codex_dynamic_tool_rpc"
|
|
3204
|
-
};
|
|
3205
|
-
if (tool !== "process" || !isJsonObject(params.call.arguments)) return {
|
|
3206
|
-
responseMessage: `OpenClaw dynamic tool call timed out after ${params.timeoutMs}ms while running tool ${tool}.`,
|
|
3207
|
-
consoleMessage: `codex dynamic tool timeout: tool=${tool} toolTimeoutMs=${params.timeoutMs}; per-tool-call watchdog, not session idle`,
|
|
3208
|
-
meta: baseMeta
|
|
3209
|
-
};
|
|
3210
|
-
const action = normalizeLogField(params.call.arguments.action);
|
|
3211
|
-
const sessionId = normalizeLogField(params.call.arguments.sessionId);
|
|
3212
|
-
const requestedTimeoutMs = readNumericTimeoutMs(params.call.arguments.timeout);
|
|
3213
|
-
const actionPart = action ? ` action=${action}` : "";
|
|
3214
|
-
const sessionPart = sessionId ? ` sessionId=${sessionId}` : "";
|
|
3215
|
-
const requestedPart = requestedTimeoutMs === void 0 ? "" : ` requestedWaitMs=${requestedTimeoutMs}`;
|
|
3216
|
-
const retryHint = action === "poll" ? "; repeated lines usually mean process-poll retry churn, not model progress" : "";
|
|
3217
|
-
const responseTarget = action || sessionId ? ` while waiting for process${actionPart}${sessionPart}` : " while waiting for the process tool";
|
|
3218
|
-
return {
|
|
3219
|
-
responseMessage: `OpenClaw dynamic tool call timed out after ${params.timeoutMs}ms${responseTarget}. This is a tool RPC timeout, not a session idle timeout.`,
|
|
3220
|
-
consoleMessage: `codex process tool timeout:${actionPart}${sessionPart} toolTimeoutMs=${params.timeoutMs}${requestedPart}; per-tool-call watchdog, not session idle${retryHint}`,
|
|
3221
|
-
meta: {
|
|
3222
|
-
...baseMeta,
|
|
3223
|
-
processAction: action,
|
|
3224
|
-
processSessionId: sessionId,
|
|
3225
|
-
processRequestedTimeoutMs: requestedTimeoutMs
|
|
3226
|
-
}
|
|
3227
|
-
};
|
|
3228
|
-
}
|
|
3229
|
-
/**
|
|
3230
|
-
* Runs a dynamic tool call with run-abort and per-call timeout handling,
|
|
3231
|
-
* returning a Codex protocol response instead of throwing.
|
|
3232
|
-
*/
|
|
3233
|
-
async function handleDynamicToolCallWithTimeout(params) {
|
|
3234
|
-
let didNotifyAgentToolResult = false;
|
|
3235
|
-
const notifyAgentToolResult = (event) => {
|
|
3236
|
-
if (didNotifyAgentToolResult) return;
|
|
3237
|
-
didNotifyAgentToolResult = true;
|
|
3238
|
-
try {
|
|
3239
|
-
params.onAgentToolResult?.(event);
|
|
3240
|
-
} catch (error) {
|
|
3241
|
-
embeddedAgentLog.warn(`onAgentToolResult handler failed: tool=${params.call.tool} error=${String(error)}`);
|
|
3242
|
-
}
|
|
3243
|
-
};
|
|
3244
|
-
const notifyFailedToolResult = (message) => {
|
|
3245
|
-
notifyAgentToolResult({
|
|
3246
|
-
toolName: params.call.tool,
|
|
3247
|
-
result: {
|
|
3248
|
-
content: [{
|
|
3249
|
-
type: "text",
|
|
3250
|
-
text: message
|
|
3251
|
-
}],
|
|
3252
|
-
details: {
|
|
3253
|
-
status: "failed",
|
|
3254
|
-
error: message
|
|
3255
|
-
}
|
|
3256
|
-
},
|
|
3257
|
-
isError: true
|
|
3258
|
-
});
|
|
3259
|
-
};
|
|
3260
|
-
if (params.signal.aborted) {
|
|
3261
|
-
const message = "OpenClaw dynamic tool call aborted before execution.";
|
|
3262
|
-
params.onFallbackSelected?.();
|
|
3263
|
-
notifyFailedToolResult(message);
|
|
3264
|
-
return failedDynamicToolResponse(message);
|
|
3265
|
-
}
|
|
3266
|
-
const controller = new AbortController();
|
|
3267
|
-
let timeout;
|
|
3268
|
-
let timedOut = false;
|
|
3269
|
-
let resolveAbort;
|
|
3270
|
-
const abortFromRun = () => {
|
|
3271
|
-
const message = "OpenClaw dynamic tool call aborted.";
|
|
3272
|
-
params.onFallbackSelected?.();
|
|
3273
|
-
controller.abort(params.signal.reason ?? /* @__PURE__ */ new Error(message));
|
|
3274
|
-
notifyFailedToolResult(message);
|
|
3275
|
-
resolveAbort?.(failedDynamicToolResponse(message, { sideEffectEvidence: true }));
|
|
3276
|
-
};
|
|
3277
|
-
const abortPromise = new Promise((resolve) => {
|
|
3278
|
-
resolveAbort = resolve;
|
|
3279
|
-
});
|
|
3280
|
-
const timeoutPromise = new Promise((resolve) => {
|
|
3281
|
-
const timeoutMs = clampDynamicToolTimeoutMs(params.timeoutMs);
|
|
3282
|
-
timeout = setTimeout(() => {
|
|
3283
|
-
timedOut = true;
|
|
3284
|
-
const timeoutDetails = formatDynamicToolTimeoutDetails({
|
|
3285
|
-
call: params.call,
|
|
3286
|
-
timeoutMs
|
|
3287
|
-
});
|
|
3288
|
-
params.onFallbackSelected?.();
|
|
3289
|
-
controller.abort(new Error(timeoutDetails.responseMessage));
|
|
3290
|
-
params.onTimeout?.();
|
|
3291
|
-
embeddedAgentLog.warn("codex dynamic tool call timed out", {
|
|
3292
|
-
...timeoutDetails.meta,
|
|
3293
|
-
consoleMessage: timeoutDetails.consoleMessage
|
|
3294
|
-
});
|
|
3295
|
-
notifyFailedToolResult(timeoutDetails.responseMessage);
|
|
3296
|
-
resolve(failedDynamicToolResponse(timeoutDetails.responseMessage, { sideEffectEvidence: true }));
|
|
3297
|
-
}, timeoutMs);
|
|
3298
|
-
timeout.unref?.();
|
|
3299
|
-
});
|
|
3300
|
-
try {
|
|
3301
|
-
params.signal.addEventListener("abort", abortFromRun, { once: true });
|
|
3302
|
-
if (params.signal.aborted) abortFromRun();
|
|
3303
|
-
const response = await Promise.race([
|
|
3304
|
-
params.toolBridge.handleToolCall(params.call, {
|
|
3305
|
-
signal: controller.signal,
|
|
3306
|
-
onAgentToolResult: notifyAgentToolResult,
|
|
3307
|
-
toolCallOrdinal: params.toolCallOrdinal
|
|
3308
|
-
}),
|
|
3309
|
-
abortPromise,
|
|
3310
|
-
timeoutPromise
|
|
3311
|
-
]);
|
|
3312
|
-
if (!response.success && !didNotifyAgentToolResult) notifyFailedToolResult(readDynamicToolResponseText(response));
|
|
3313
|
-
return response;
|
|
3314
|
-
} catch (error) {
|
|
3315
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
3316
|
-
notifyFailedToolResult(message);
|
|
3317
|
-
return failedDynamicToolResponse(message, { sideEffectEvidence: true });
|
|
3318
|
-
} finally {
|
|
3319
|
-
if (timeout) clearTimeout(timeout);
|
|
3320
|
-
params.signal.removeEventListener("abort", abortFromRun);
|
|
3321
|
-
resolveAbort = void 0;
|
|
3322
|
-
if (!timedOut && !controller.signal.aborted) controller.abort(/* @__PURE__ */ new Error("OpenClaw dynamic tool call finished."));
|
|
3323
|
-
}
|
|
3324
|
-
}
|
|
3325
|
-
function readDynamicToolResponseText(response) {
|
|
3326
|
-
return response.contentItems.flatMap((item) => item.type === "inputText" && typeof item.text === "string" ? [item.text] : []).join("\n").trim() || "OpenClaw dynamic tool call failed.";
|
|
3327
|
-
}
|
|
3328
|
-
function failedDynamicToolResponse(message, options) {
|
|
3329
|
-
const response = {
|
|
3330
|
-
contentItems: [{
|
|
3331
|
-
type: "inputText",
|
|
3332
|
-
text: message
|
|
3333
|
-
}],
|
|
3334
|
-
success: false
|
|
3335
|
-
};
|
|
3336
|
-
Object.defineProperty(response, "diagnosticTerminalType", {
|
|
3337
|
-
configurable: true,
|
|
3338
|
-
enumerable: false,
|
|
3339
|
-
value: "error"
|
|
3340
|
-
});
|
|
3341
|
-
if (options?.sideEffectEvidence === true) Object.defineProperty(response, "sideEffectEvidence", {
|
|
3342
|
-
configurable: true,
|
|
3343
|
-
enumerable: false,
|
|
3344
|
-
value: true
|
|
3345
|
-
});
|
|
3346
|
-
return response;
|
|
3347
|
-
}
|
|
3348
|
-
/** Strips OpenClaw-only metadata before sending a dynamic tool response to Codex. */
|
|
3349
|
-
function toCodexDynamicToolProtocolResponse(response) {
|
|
3350
|
-
return {
|
|
3351
|
-
contentItems: response.contentItems,
|
|
3352
|
-
success: response.success
|
|
3353
|
-
};
|
|
3354
|
-
}
|
|
3355
|
-
/** Adds async-started progress details when a tool result continues out of band. */
|
|
3356
|
-
function toCodexDynamicToolProgressResponse(response, protocolResponse) {
|
|
3357
|
-
if (response.asyncStarted !== true) return protocolResponse;
|
|
3358
|
-
return {
|
|
3359
|
-
...protocolResponse,
|
|
3360
|
-
details: {
|
|
3361
|
-
async: true,
|
|
3362
|
-
status: "started"
|
|
3363
|
-
}
|
|
3364
|
-
};
|
|
3365
|
-
}
|
|
3366
|
-
/** Decides whether a terminal dynamic tool response can release the Codex turn. */
|
|
3367
|
-
function shouldReleaseTurnAfterTerminalDynamicTool(state) {
|
|
3368
|
-
return !state.completed && !state.aborted && state.responseSuccess && !state.currentTurnHadNonTerminalDynamicToolResult && state.activeAppServerTurnRequests === 0 && state.activeTurnItemIdsCount === 0 && state.pendingOpenClawDynamicToolCompletionIdsCount === 0;
|
|
3369
|
-
}
|
|
3370
|
-
/** Returns true when a non-async result should block terminal-release shortcuts. */
|
|
3371
|
-
function shouldBlockTerminalReleaseForNonTerminalDynamicToolResult(response) {
|
|
3372
|
-
return response.asyncStarted !== true;
|
|
3373
|
-
}
|
|
3374
|
-
/** Resolves whether terminal diagnostic state should release, wait, or stay idle. */
|
|
3375
|
-
function resolveTerminalDynamicToolBatchAction(state) {
|
|
3376
|
-
if (state.activeAppServerTurnRequests > 0 || state.activeTurnItemIdsCount > 0 || state.pendingOpenClawDynamicToolCompletionIdsCount > 0) return "wait";
|
|
3377
|
-
if (state.currentTurnHadNonTerminalDynamicToolResult) return "clear-nonterminal-batch";
|
|
3378
|
-
if (state.hasPendingTerminalDynamicToolRelease) return "release-pending-terminal";
|
|
3379
|
-
return "idle";
|
|
3380
|
-
}
|
|
3381
|
-
/** Returns true for diagnostic events that terminate a dynamic tool call. */
|
|
3382
|
-
function isDynamicToolTerminalDiagnosticEvent(event) {
|
|
3383
|
-
return event.type === "tool.execution.completed" || event.type === "tool.execution.error" || event.type === "tool.execution.blocked";
|
|
3384
|
-
}
|
|
3385
|
-
/** Matches terminal diagnostics to a specific dynamic tool call id/name. */
|
|
3386
|
-
function isMatchingDynamicToolTerminalDiagnostic(params) {
|
|
3387
|
-
if (params.event.toolCallId !== params.call.callId || params.event.toolName !== params.call.tool) return false;
|
|
3388
|
-
if (params.runId !== void 0) return params.event.runId === params.runId;
|
|
3389
|
-
if (params.sessionId !== void 0) return params.event.sessionId === params.sessionId;
|
|
3390
|
-
if (params.sessionKey !== void 0) return params.event.sessionKey === params.sessionKey;
|
|
3391
|
-
return params.event.runId === void 0 && params.event.sessionId === void 0 && params.event.sessionKey === void 0;
|
|
3392
|
-
}
|
|
3393
|
-
/** Checks pending diagnostics for a terminal event matching a tool call. */
|
|
3394
|
-
function hasPendingDynamicToolTerminalDiagnostic(params) {
|
|
3395
|
-
return hasPendingInternalDiagnosticEvent((event) => {
|
|
3396
|
-
if (!isDynamicToolTerminalDiagnosticEvent(event)) return false;
|
|
3397
|
-
return isMatchingDynamicToolTerminalDiagnostic({
|
|
3398
|
-
event,
|
|
3399
|
-
call: params.call,
|
|
3400
|
-
runId: params.runId,
|
|
3401
|
-
sessionId: params.sessionId,
|
|
3402
|
-
sessionKey: params.sessionKey
|
|
3403
|
-
});
|
|
3404
|
-
});
|
|
3405
|
-
}
|
|
3406
|
-
/** Resolves per-tool timeout, applying media/message defaults and hard caps. */
|
|
3407
|
-
function resolveDynamicToolCallTimeoutMs(params) {
|
|
3408
|
-
return clampDynamicToolTimeoutMs(readDynamicToolCallTimeoutMs(params.call.arguments) ?? readConfiguredDynamicToolTimeoutMs(params.call.tool, params.config) ?? 9e4);
|
|
3409
|
-
}
|
|
3410
|
-
function readDynamicToolCallTimeoutMs(value) {
|
|
3411
|
-
if (!isJsonObject(value)) return;
|
|
3412
|
-
return readPositiveFiniteTimeoutMs(value.timeoutMs);
|
|
3413
|
-
}
|
|
3414
|
-
function readConfiguredDynamicToolTimeoutMs(toolName, config) {
|
|
3415
|
-
if (toolName === "image_generate") {
|
|
3416
|
-
const imageGenerationModel = config?.agents?.defaults?.imageGenerationModel;
|
|
3417
|
-
if (!imageGenerationModel || typeof imageGenerationModel !== "object") return CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
|
|
3418
|
-
return readPositiveFiniteTimeoutMs(imageGenerationModel.timeoutMs) ?? CODEX_DYNAMIC_IMAGE_GENERATION_TOOL_TIMEOUT_MS;
|
|
3419
|
-
}
|
|
3420
|
-
if (toolName === "image") return readTimeoutSecondsAsMs(config?.tools?.media?.image?.timeoutSeconds) ?? 6e4;
|
|
3421
|
-
if (toolName === "message") return CODEX_DYNAMIC_MESSAGE_TOOL_TIMEOUT_MS;
|
|
3422
|
-
}
|
|
3423
|
-
function readTimeoutSecondsAsMs(value) {
|
|
3424
|
-
const seconds = readPositiveFiniteTimeoutMs(value);
|
|
3425
|
-
return seconds === void 0 ? void 0 : seconds * 1e3;
|
|
3426
|
-
}
|
|
3427
|
-
function readPositiveFiniteTimeoutMs(value) {
|
|
3428
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : void 0;
|
|
3429
|
-
}
|
|
3430
|
-
function clampDynamicToolTimeoutMs(timeoutMs) {
|
|
3431
|
-
return Math.max(1, Math.min(CODEX_DYNAMIC_TOOL_MAX_TIMEOUT_MS, Math.floor(timeoutMs)));
|
|
3432
|
-
}
|
|
3433
|
-
//#endregion
|
|
3434
|
-
//#region extensions/codex/src/app-server/local-runtime-attribution.ts
|
|
3435
|
-
const OPENAI_PROVIDER_ID = "openai";
|
|
3436
|
-
const OPENAI_RESPONSES_API = "openai-responses";
|
|
3437
|
-
const OPENAI_CODEX_RESPONSES_API = "openai-chatgpt-responses";
|
|
3438
|
-
function normalizeRuntimeId(value) {
|
|
3439
|
-
return value?.trim().toLowerCase() ?? "";
|
|
3440
|
-
}
|
|
3441
|
-
/** Maps local Codex runtime plans onto the provider/api pair exposed to event projection. */
|
|
3442
|
-
function resolveCodexLocalRuntimeAttribution(params) {
|
|
3443
|
-
const authProfileProvider = normalizeRuntimeId(params.runtimePlan?.auth?.authProfileProviderForAuth);
|
|
3444
|
-
if (normalizeRuntimeId(params.runtimePlan?.observability.harnessId) === "codex" && authProfileProvider !== OPENAI_PROVIDER_ID && normalizeRuntimeId(params.model.provider) === OPENAI_PROVIDER_ID && normalizeRuntimeId(params.model.api) === OPENAI_RESPONSES_API) return {
|
|
3445
|
-
provider: OPENAI_PROVIDER_ID,
|
|
3446
|
-
api: OPENAI_CODEX_RESPONSES_API
|
|
3447
|
-
};
|
|
3448
|
-
return {
|
|
3449
|
-
provider: params.provider,
|
|
3450
|
-
api: params.model.api
|
|
3451
|
-
};
|
|
3452
|
-
}
|
|
3453
|
-
//#endregion
|
|
3454
|
-
//#region extensions/codex/src/app-server/tool-progress-normalization.ts
|
|
3455
|
-
/**
|
|
3456
|
-
* Normalizes and sanitizes Codex dynamic-tool progress payloads before they are
|
|
3457
|
-
* emitted into OpenClaw events or logs.
|
|
3458
|
-
*/
|
|
3459
|
-
/** Maps OpenClaw tool-progress config to the mode used by Codex progress metadata. */
|
|
3460
|
-
function resolveCodexToolProgressDetailMode(value) {
|
|
3461
|
-
return value === "raw" ? "raw" : "explain";
|
|
3462
|
-
}
|
|
3463
|
-
/** Recursively redacts sensitive strings and handles circular values in event payloads. */
|
|
3464
|
-
function sanitizeCodexAgentEventValue(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
3465
|
-
if (typeof value === "string") return redactToolPayloadText(value);
|
|
3466
|
-
if (Array.isArray(value)) {
|
|
3467
|
-
if (seen.has(value)) return "[Circular]";
|
|
3468
|
-
seen.add(value);
|
|
3469
|
-
return value.map((entry) => sanitizeCodexAgentEventValue(entry, seen));
|
|
3470
|
-
}
|
|
3471
|
-
if (value && typeof value === "object") {
|
|
3472
|
-
if (seen.has(value)) return "[Circular]";
|
|
3473
|
-
seen.add(value);
|
|
3474
|
-
const out = {};
|
|
3475
|
-
for (const [key, child] of Object.entries(value)) out[key] = typeof child === "string" ? redactSensitiveFieldValue(key, child) : sanitizeCodexAgentEventValue(child, seen);
|
|
3476
|
-
return out;
|
|
3477
|
-
}
|
|
3478
|
-
return value;
|
|
3479
|
-
}
|
|
3480
|
-
/** Sanitizes a record-shaped Codex agent event payload. */
|
|
3481
|
-
function sanitizeCodexAgentEventRecord(value) {
|
|
3482
|
-
return sanitizeCodexAgentEventValue(value);
|
|
3483
|
-
}
|
|
3484
|
-
/** Sanitizes dynamic-tool arguments before diagnostic/event emission. */
|
|
3485
|
-
function sanitizeCodexToolArguments(value) {
|
|
3486
|
-
if (!isJsonObject(value)) return;
|
|
3487
|
-
return sanitizeCodexAgentEventRecord(value);
|
|
3488
|
-
}
|
|
3489
|
-
/** Sanitizes a Codex dynamic-tool response before diagnostic/event emission. */
|
|
3490
|
-
function sanitizeCodexToolResponse(response) {
|
|
3491
|
-
return sanitizeCodexAgentEventRecord(response);
|
|
3492
|
-
}
|
|
3493
|
-
/** Infers compact human-readable tool metadata from Codex dynamic-tool arguments. */
|
|
3494
|
-
function inferCodexDynamicToolMeta(call, detailMode) {
|
|
3495
|
-
return inferToolMetaFromArgs(call.tool, call.arguments, { detailMode });
|
|
3496
|
-
}
|
|
3497
|
-
//#endregion
|
|
3498
|
-
//#region extensions/codex/src/app-server/transcript-mirror.ts
|
|
3499
|
-
const MIRROR_IDENTITY_META_KEY = "mirrorIdentity";
|
|
3500
|
-
function buildSenderLabel(params) {
|
|
3501
|
-
const label = params.senderName ?? params.senderUsername ?? params.senderE164 ?? params.senderId;
|
|
3502
|
-
if (!label) return;
|
|
3503
|
-
if (!params.senderId || label.includes(params.senderId)) return label;
|
|
3504
|
-
return `${label} (${params.senderId})`;
|
|
3505
|
-
}
|
|
3506
|
-
function buildCodexUserPromptMessageFromPrepared(params, preparedUserMessage) {
|
|
3507
|
-
const senderId = normalizeOptionalString(params.senderId);
|
|
3508
|
-
const senderName = normalizeOptionalString(params.senderName);
|
|
3509
|
-
const senderUsername = normalizeOptionalString(params.senderUsername);
|
|
3510
|
-
const senderE164 = normalizeOptionalString(params.senderE164);
|
|
3511
|
-
const senderLabel = buildSenderLabel({
|
|
3512
|
-
senderId,
|
|
3513
|
-
senderName,
|
|
3514
|
-
senderUsername,
|
|
3515
|
-
senderE164
|
|
3516
|
-
});
|
|
3517
|
-
const sourceChannel = normalizeOptionalString(params.inputProvenance?.sourceChannel ?? params.messageChannel ?? params.messageProvider);
|
|
3518
|
-
if (preparedUserMessage) return {
|
|
3519
|
-
role: "user",
|
|
3520
|
-
timestamp: Date.now(),
|
|
3521
|
-
...params.inputProvenance ? { provenance: params.inputProvenance } : {},
|
|
3522
|
-
...sourceChannel ? { sourceChannel } : {},
|
|
3523
|
-
...senderId ? { senderId } : {},
|
|
3524
|
-
...senderName ? { senderName } : {},
|
|
3525
|
-
...senderUsername ? { senderUsername } : {},
|
|
3526
|
-
...senderE164 ? { senderE164 } : {},
|
|
3527
|
-
...senderLabel ? { senderLabel } : {},
|
|
3528
|
-
...preparedUserMessage
|
|
3529
|
-
};
|
|
3530
|
-
return {
|
|
3531
|
-
role: "user",
|
|
3532
|
-
content: params.prompt,
|
|
3533
|
-
timestamp: Date.now(),
|
|
3534
|
-
...params.inputProvenance ? { provenance: params.inputProvenance } : {},
|
|
3535
|
-
...sourceChannel ? { sourceChannel } : {},
|
|
3536
|
-
...senderId ? { senderId } : {},
|
|
3537
|
-
...senderName ? { senderName } : {},
|
|
3538
|
-
...senderUsername ? { senderUsername } : {},
|
|
3539
|
-
...senderE164 ? { senderE164 } : {},
|
|
3540
|
-
...senderLabel ? { senderLabel } : {}
|
|
3541
|
-
};
|
|
3542
|
-
}
|
|
3543
|
-
function buildCodexUserPromptMessage(params) {
|
|
3544
|
-
return buildCodexUserPromptMessageFromPrepared(params, params.userTurnTranscriptRecorder?.message);
|
|
3545
|
-
}
|
|
3546
|
-
async function buildResolvedCodexUserPromptMessage(params) {
|
|
3547
|
-
return buildCodexUserPromptMessageFromPrepared(params, await params.userTurnTranscriptRecorder?.resolveMessage() ?? params.userTurnTranscriptRecorder?.message);
|
|
3548
|
-
}
|
|
3549
|
-
async function mirrorTranscriptBestEffort(params) {
|
|
3550
|
-
try {
|
|
3551
|
-
const messages = await resolveFinalCodexMirrorMessages({
|
|
3552
|
-
params: params.params,
|
|
3553
|
-
messagesSnapshot: params.result.messagesSnapshot,
|
|
3554
|
-
turnId: params.turnId
|
|
3555
|
-
});
|
|
3556
|
-
const mirrorResult = await mirrorCodexAppServerTranscript({
|
|
3557
|
-
sessionFile: params.params.sessionFile,
|
|
3558
|
-
agentId: params.agentId,
|
|
3559
|
-
sessionKey: params.sessionKey,
|
|
3560
|
-
sessionId: params.params.sessionId,
|
|
3561
|
-
cwd: params.cwd,
|
|
3562
|
-
messages,
|
|
3563
|
-
idempotencyScope: `codex-app-server:${params.threadId}`,
|
|
3564
|
-
config: params.params.config
|
|
3565
|
-
});
|
|
3566
|
-
for (const message of mirrorResult.userMessagesPresent) params.notifyUserMessagePersisted(message);
|
|
3567
|
-
} catch (error) {
|
|
3568
|
-
embeddedAgentLog.warn("failed to mirror codex app-server transcript", { error });
|
|
3569
|
-
}
|
|
3570
|
-
}
|
|
3571
|
-
async function resolveFinalCodexMirrorMessages(params) {
|
|
3572
|
-
if (params.params.suppressNextUserMessagePersistence || !params.params.userTurnTranscriptRecorder) return params.messagesSnapshot;
|
|
3573
|
-
const resolvedPrompt = attachCodexMirrorIdentity(await buildResolvedCodexUserPromptMessage(params.params), `${params.turnId}:prompt`);
|
|
3574
|
-
const firstUserIndex = params.messagesSnapshot.findIndex((message) => message.role === "user");
|
|
3575
|
-
if (firstUserIndex === -1) return [resolvedPrompt, ...params.messagesSnapshot];
|
|
3576
|
-
const messages = params.messagesSnapshot.slice();
|
|
3577
|
-
messages[firstUserIndex] = resolvedPrompt;
|
|
3578
|
-
return messages;
|
|
3579
|
-
}
|
|
3580
|
-
function createCodexAppServerUserMessagePersistenceNotifier(runParams) {
|
|
3581
|
-
let notified = false;
|
|
3582
|
-
return (message) => {
|
|
3583
|
-
if (notified) return;
|
|
3584
|
-
notified = true;
|
|
3585
|
-
runParams.userTurnTranscriptRecorder?.markRuntimePersisted(message);
|
|
3586
|
-
try {
|
|
3587
|
-
runParams.onUserMessagePersisted?.(message);
|
|
3588
|
-
} catch (error) {
|
|
3589
|
-
embeddedAgentLog.warn("codex app-server user persistence notification failed", { error: formatErrorMessage(error) });
|
|
3590
|
-
}
|
|
3591
|
-
};
|
|
3592
|
-
}
|
|
3593
|
-
async function mirrorPromptAtTurnStartBestEffort(params) {
|
|
3594
|
-
if (params.params.suppressNextUserMessagePersistence) return;
|
|
3595
|
-
try {
|
|
3596
|
-
const mirrorPromise = (async () => {
|
|
3597
|
-
const userPromptMessage = attachCodexMirrorIdentity(await buildResolvedCodexUserPromptMessage(params.params), `${params.turnId}:prompt`);
|
|
3598
|
-
const mirrorResult = await mirrorCodexAppServerTranscript({
|
|
3599
|
-
sessionFile: params.params.sessionFile,
|
|
3600
|
-
agentId: params.agentId,
|
|
3601
|
-
sessionKey: params.sessionKey,
|
|
3602
|
-
sessionId: params.params.sessionId,
|
|
3603
|
-
cwd: params.cwd,
|
|
3604
|
-
messages: [userPromptMessage],
|
|
3605
|
-
idempotencyScope: `codex-app-server:${params.threadId}`,
|
|
3606
|
-
config: params.params.config
|
|
3607
|
-
});
|
|
3608
|
-
for (const message of mirrorResult.userMessagesPresent) params.notifyUserMessagePersisted(message);
|
|
3609
|
-
})();
|
|
3610
|
-
params.params.userTurnTranscriptRecorder?.markRuntimePersistencePending(mirrorPromise);
|
|
3611
|
-
await mirrorPromise;
|
|
3612
|
-
} catch (error) {
|
|
3613
|
-
embeddedAgentLog.warn("failed to mirror codex app-server prompt at turn start", { error });
|
|
3614
|
-
}
|
|
3615
|
-
}
|
|
3616
|
-
/**
|
|
3617
|
-
* Tag a message with a stable logical identity for mirror dedupe. Callers
|
|
3618
|
-
* should use a value that is invariant for the same logical message across
|
|
3619
|
-
* re-emits (e.g. `${turnId}:prompt`, `${turnId}:assistant`) but distinct
|
|
3620
|
-
* for genuinely-distinct messages (different turns, different kinds). When
|
|
3621
|
-
* present this identity replaces the role/content fingerprint in the
|
|
3622
|
-
* idempotency key, so the dedupe survives caller-scope rotation without
|
|
3623
|
-
* collapsing distinct same-content turns.
|
|
3624
|
-
*/
|
|
3625
|
-
function attachCodexMirrorIdentity(message, identity) {
|
|
3626
|
-
const record = message;
|
|
3627
|
-
const existing = record["__openclaw"];
|
|
3628
|
-
const baseMeta = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
|
|
3629
|
-
return {
|
|
3630
|
-
...record,
|
|
3631
|
-
__openclaw: {
|
|
3632
|
-
...baseMeta,
|
|
3633
|
-
[MIRROR_IDENTITY_META_KEY]: identity
|
|
3634
|
-
}
|
|
3635
|
-
};
|
|
3636
|
-
}
|
|
3637
|
-
function readMirrorIdentity(message) {
|
|
3638
|
-
const meta = message["__openclaw"];
|
|
3639
|
-
if (!meta || typeof meta !== "object" || Array.isArray(meta)) return;
|
|
3640
|
-
const id = meta[MIRROR_IDENTITY_META_KEY];
|
|
3641
|
-
return typeof id === "string" && id.length > 0 ? id : void 0;
|
|
3642
|
-
}
|
|
3643
|
-
function fingerprintMirrorMessageContent(message) {
|
|
3644
|
-
const payload = JSON.stringify({
|
|
3645
|
-
role: message.role,
|
|
3646
|
-
content: message.content
|
|
3647
|
-
});
|
|
3648
|
-
return createHash("sha256").update(payload).digest("hex").slice(0, 16);
|
|
3649
|
-
}
|
|
3650
|
-
function buildMirrorDedupeIdentity(message) {
|
|
3651
|
-
const explicit = readMirrorIdentity(message);
|
|
3652
|
-
if (explicit) return explicit;
|
|
3653
|
-
return `${message.role}:${fingerprintMirrorMessageContent(message)}`;
|
|
3654
|
-
}
|
|
3655
|
-
async function mirrorCodexAppServerTranscript(params) {
|
|
3656
|
-
const messages = params.messages.filter((message) => message.role === "user" || message.role === "assistant" || message.role === "toolResult");
|
|
3657
|
-
if (messages.length === 0) return { userMessagesPresent: [] };
|
|
3658
|
-
const transcriptTarget = resolveCodexMirrorTranscriptTarget(params);
|
|
3659
|
-
const { appendedUpdates, userMessagesPresent } = await withSessionTranscriptWriteLock({
|
|
3660
|
-
...transcriptTarget,
|
|
3661
|
-
config: params.config
|
|
3662
|
-
}, async (transcript) => {
|
|
3663
|
-
const nextAppendedUpdates = [];
|
|
3664
|
-
const nextUserMessagesPresent = [];
|
|
3665
|
-
const mirrorState = readTranscriptMirrorState(await transcript.readEvents());
|
|
3666
|
-
let nextMessageSeq = mirrorState.messageCount;
|
|
3667
|
-
for (const message of messages) {
|
|
3668
|
-
const dedupeIdentity = buildMirrorDedupeIdentity(message);
|
|
3669
|
-
const idempotencyKey = params.idempotencyScope ? `${params.idempotencyScope}:${dedupeIdentity}` : void 0;
|
|
3670
|
-
const transcriptMessage = {
|
|
3671
|
-
...message,
|
|
3672
|
-
...idempotencyKey ? { idempotencyKey } : {}
|
|
3673
|
-
};
|
|
3674
|
-
if (idempotencyKey && mirrorState.idempotencyKeys.has(idempotencyKey)) {
|
|
3675
|
-
const persistedUserMessage = mirrorState.userMessagesByIdempotencyKey.get(idempotencyKey);
|
|
3676
|
-
if (persistedUserMessage) nextUserMessagesPresent.push(persistedUserMessage);
|
|
3677
|
-
continue;
|
|
3678
|
-
}
|
|
3679
|
-
const nextMessage = runAgentHarnessBeforeMessageWriteHook({
|
|
3680
|
-
message: transcriptMessage,
|
|
3681
|
-
agentId: params.agentId,
|
|
3682
|
-
sessionKey: params.sessionKey
|
|
3683
|
-
});
|
|
3684
|
-
if (!nextMessage) continue;
|
|
3685
|
-
const messageToAppend = idempotencyKey ? {
|
|
3686
|
-
...nextMessage,
|
|
3687
|
-
idempotencyKey
|
|
3688
|
-
} : nextMessage;
|
|
3689
|
-
const appended = await transcript.appendMessage({
|
|
3690
|
-
message: messageToAppend,
|
|
3691
|
-
idempotencyLookup: idempotencyKey ? "caller-checked" : "scan",
|
|
3692
|
-
cwd: params.cwd
|
|
3693
|
-
});
|
|
3694
|
-
if (!appended) continue;
|
|
3695
|
-
const { messageId, message: appendedMessage } = appended;
|
|
3696
|
-
if (appendedMessage.role === "user") {
|
|
3697
|
-
nextUserMessagesPresent.push(appendedMessage);
|
|
3698
|
-
if (idempotencyKey) mirrorState.userMessagesByIdempotencyKey.set(idempotencyKey, appendedMessage);
|
|
3699
|
-
}
|
|
3700
|
-
nextMessageSeq += 1;
|
|
3701
|
-
nextAppendedUpdates.push({
|
|
3702
|
-
messageId,
|
|
3703
|
-
message: appendedMessage,
|
|
3704
|
-
messageSeq: nextMessageSeq
|
|
3705
|
-
});
|
|
3706
|
-
if (idempotencyKey) mirrorState.idempotencyKeys.add(idempotencyKey);
|
|
3707
|
-
}
|
|
3708
|
-
return {
|
|
3709
|
-
appendedUpdates: nextAppendedUpdates,
|
|
3710
|
-
userMessagesPresent: nextUserMessagesPresent
|
|
3711
|
-
};
|
|
3712
|
-
});
|
|
3713
|
-
for (const update of appendedUpdates) await publishSessionTranscriptUpdateByIdentity({
|
|
3714
|
-
...transcriptTarget,
|
|
3715
|
-
update: {
|
|
3716
|
-
...params.sessionKey ? { sessionKey: params.sessionKey } : {},
|
|
3717
|
-
...params.agentId ? { agentId: params.agentId } : {},
|
|
3718
|
-
message: update.message,
|
|
3719
|
-
messageId: update.messageId,
|
|
3720
|
-
messageSeq: update.messageSeq
|
|
3721
|
-
}
|
|
3722
|
-
});
|
|
3723
|
-
return { userMessagesPresent };
|
|
3724
|
-
}
|
|
3725
|
-
function resolveCodexMirrorTranscriptTarget(params) {
|
|
3726
|
-
return {
|
|
3727
|
-
...params.agentId ? { agentId: params.agentId } : {},
|
|
3728
|
-
sessionFile: params.sessionFile,
|
|
3729
|
-
sessionId: params.sessionId,
|
|
3730
|
-
sessionKey: params.sessionKey ?? ""
|
|
3731
|
-
};
|
|
3732
|
-
}
|
|
3733
|
-
function readTranscriptMirrorState(events) {
|
|
3734
|
-
const idempotencyKeys = /* @__PURE__ */ new Set();
|
|
3735
|
-
const userMessagesByIdempotencyKey = /* @__PURE__ */ new Map();
|
|
3736
|
-
let messageCount = 0;
|
|
3737
|
-
for (const event of events) {
|
|
3738
|
-
if (!event || typeof event !== "object" || Array.isArray(event)) continue;
|
|
3739
|
-
const parsed = event;
|
|
3740
|
-
if (parsed.type === "message") messageCount += 1;
|
|
3741
|
-
if (typeof parsed.message?.idempotencyKey === "string") {
|
|
3742
|
-
idempotencyKeys.add(parsed.message.idempotencyKey);
|
|
3743
|
-
if (parsed.message.role === "user") userMessagesByIdempotencyKey.set(parsed.message.idempotencyKey, parsed.message);
|
|
3744
|
-
}
|
|
3745
|
-
}
|
|
3746
|
-
return {
|
|
3747
|
-
idempotencyKeys,
|
|
3748
|
-
messageCount,
|
|
3749
|
-
userMessagesByIdempotencyKey
|
|
3750
|
-
};
|
|
3751
|
-
}
|
|
3752
|
-
//#endregion
|
|
3753
|
-
//#region extensions/codex/src/app-server/event-projector.ts
|
|
3754
|
-
const ZERO_USAGE = {
|
|
3755
|
-
input: 0,
|
|
3756
|
-
output: 0,
|
|
3757
|
-
cacheRead: 0,
|
|
3758
|
-
cacheWrite: 0,
|
|
3759
|
-
totalTokens: 0,
|
|
3760
|
-
cost: {
|
|
3761
|
-
input: 0,
|
|
3762
|
-
output: 0,
|
|
3763
|
-
cacheRead: 0,
|
|
3764
|
-
cacheWrite: 0,
|
|
3765
|
-
total: 0
|
|
3766
|
-
}
|
|
3767
|
-
};
|
|
3768
|
-
const CURRENT_TOKEN_USAGE_KEYS = [
|
|
3769
|
-
"last",
|
|
3770
|
-
"current",
|
|
3771
|
-
"lastCall",
|
|
3772
|
-
"lastCallUsage",
|
|
3773
|
-
"lastTokenUsage",
|
|
3774
|
-
"last_token_usage"
|
|
3775
|
-
];
|
|
3776
|
-
const CODEX_PROMPT_TOTAL_INPUT_KEYS = [
|
|
3777
|
-
"inputTokens",
|
|
3778
|
-
"input_tokens",
|
|
3779
|
-
"promptTokens",
|
|
3780
|
-
"prompt_tokens"
|
|
3781
|
-
];
|
|
3782
|
-
const MAX_TOOL_OUTPUT_DELTA_MESSAGES_PER_ITEM = 20;
|
|
3783
|
-
const TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS = 12e3;
|
|
3784
|
-
const MISSING_TOOL_RESULT_ERROR = "OpenClaw recorded a native Codex tool.call without a matching tool.result before the turn completed.";
|
|
3785
|
-
const GENERATED_IMAGE_MEDIA_SUBDIR = "tool-image-generation";
|
|
3786
|
-
const BYTES_PER_MB = 1024 * 1024;
|
|
3787
|
-
const DEFAULT_GENERATED_IMAGE_MAX_BYTES = 6 * BYTES_PER_MB;
|
|
3788
|
-
const TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES = new Set([
|
|
3789
|
-
"message",
|
|
3790
|
-
"messages",
|
|
3791
|
-
"reply",
|
|
3792
|
-
"send",
|
|
3793
|
-
"reaction",
|
|
3794
|
-
"react",
|
|
3795
|
-
"typing"
|
|
3796
|
-
]);
|
|
3797
|
-
function shouldEmitTranscriptToolProgress(toolName, _args) {
|
|
3798
|
-
const normalized = typeof toolName === "string" ? toolName.trim().toLowerCase() : "";
|
|
3799
|
-
return Boolean(normalized && !TRANSCRIPT_PROGRESS_SUPPRESSED_TOOL_NAMES.has(normalized));
|
|
3800
|
-
}
|
|
3801
|
-
var CodexAppServerEventProjector = class {
|
|
3802
|
-
constructor(params, threadId, turnId, options = {}) {
|
|
3803
|
-
this.params = params;
|
|
3804
|
-
this.threadId = threadId;
|
|
3805
|
-
this.turnId = turnId;
|
|
3806
|
-
this.options = options;
|
|
3807
|
-
this.assistantTextByItem = /* @__PURE__ */ new Map();
|
|
3808
|
-
this.assistantItemOrder = [];
|
|
3809
|
-
this.assistantPhaseByItem = /* @__PURE__ */ new Map();
|
|
3810
|
-
this.lastCommentaryProgressTextByItem = /* @__PURE__ */ new Map();
|
|
3811
|
-
this.pendingRawCommentaryEchoes = 0;
|
|
3812
|
-
this.reasoningTextByGroup = /* @__PURE__ */ new Map();
|
|
3813
|
-
this.reasoningItemOrder = /* @__PURE__ */ new Map();
|
|
3814
|
-
this.planTextByItem = /* @__PURE__ */ new Map();
|
|
3815
|
-
this.activeItemIds = /* @__PURE__ */ new Set();
|
|
3816
|
-
this.completedItemIds = /* @__PURE__ */ new Set();
|
|
3817
|
-
this.activeCompactionItemIds = /* @__PURE__ */ new Set();
|
|
3818
|
-
this.toolProgressTexts = /* @__PURE__ */ new Set();
|
|
3819
|
-
this.toolResultSummaryItemIds = /* @__PURE__ */ new Set();
|
|
3820
|
-
this.toolResultOutputItemIds = /* @__PURE__ */ new Set();
|
|
3821
|
-
this.toolResultOutputStreamedItemIds = /* @__PURE__ */ new Set();
|
|
3822
|
-
this.transcriptToolProgressSuppressedIds = /* @__PURE__ */ new Set();
|
|
3823
|
-
this.toolTranscriptArgumentsById = /* @__PURE__ */ new Map();
|
|
3824
|
-
this.toolResultOutputDeltaState = /* @__PURE__ */ new Map();
|
|
3825
|
-
this.toolResultOutputTextByItem = /* @__PURE__ */ new Map();
|
|
3826
|
-
this.toolMetas = /* @__PURE__ */ new Map();
|
|
3827
|
-
this.terminalPresentationClearedItemIds = /* @__PURE__ */ new Set();
|
|
3828
|
-
this.nativeToolOutcomeOrdinals = /* @__PURE__ */ new Map();
|
|
3829
|
-
this.sideEffectingToolItemIds = /* @__PURE__ */ new Set();
|
|
3830
|
-
this.sideEffectingDynamicToolCallIds = /* @__PURE__ */ new Set();
|
|
3831
|
-
this.toolTranscriptMessages = [];
|
|
3832
|
-
this.toolTranscriptCallIds = /* @__PURE__ */ new Set();
|
|
3833
|
-
this.toolTranscriptResultIds = /* @__PURE__ */ new Set();
|
|
3834
|
-
this.toolTranscriptNamesById = /* @__PURE__ */ new Map();
|
|
3835
|
-
this.toolTrajectoryCallIds = /* @__PURE__ */ new Set();
|
|
3836
|
-
this.toolTrajectoryResultIds = /* @__PURE__ */ new Set();
|
|
3837
|
-
this.toolTrajectoryNamesById = /* @__PURE__ */ new Map();
|
|
3838
|
-
this.toolTrajectoryItemsById = /* @__PURE__ */ new Map();
|
|
3839
|
-
this.transcriptToolProgressCallIds = /* @__PURE__ */ new Set();
|
|
3840
|
-
this.nativeGeneratedMediaItemIds = /* @__PURE__ */ new Set();
|
|
3841
|
-
this.nativeGeneratedMediaUrlsByItemId = /* @__PURE__ */ new Map();
|
|
3842
|
-
this.diagnosticToolStartedAtByItem = /* @__PURE__ */ new Map();
|
|
3843
|
-
this.afterToolCallObservedItemIds = /* @__PURE__ */ new Set();
|
|
3844
|
-
this.assistantStarted = false;
|
|
3845
|
-
this.reasoningStarted = false;
|
|
3846
|
-
this.reasoningEnded = false;
|
|
3847
|
-
this.streamedPartialAssistantItemReplaceable = false;
|
|
3848
|
-
this.promptErrorSource = null;
|
|
3849
|
-
this.synthesizedMissingToolResultError = null;
|
|
3850
|
-
this.aborted = false;
|
|
3851
|
-
this.guardianReviewCount = 0;
|
|
3852
|
-
this.completedCompactionCount = 0;
|
|
3853
|
-
}
|
|
3854
|
-
getCompletedTurnStatus() {
|
|
3855
|
-
return this.completedTurn?.status;
|
|
3856
|
-
}
|
|
3857
|
-
hasCompletedTerminalAssistantText() {
|
|
3858
|
-
const finalItem = this.resolveFinalAssistantTextItem();
|
|
3859
|
-
return finalItem !== void 0 && this.completedItemIds.has(finalItem.itemId);
|
|
3860
|
-
}
|
|
3861
|
-
/** Resolves the shared model-order position for a native tool item. */
|
|
3862
|
-
recordNativeToolOutcome(item) {
|
|
3863
|
-
if (!item || this.nativeToolOutcomeOrdinals.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
|
|
3864
|
-
const ordinal = this.params.allocateToolOutcomeOrdinal?.(item.id);
|
|
3865
|
-
if (ordinal !== void 0) this.nativeToolOutcomeOrdinals.set(item.id, ordinal);
|
|
3866
|
-
}
|
|
3867
|
-
async handleNotification(notification) {
|
|
3868
|
-
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
3869
|
-
if (!params) return;
|
|
3870
|
-
if (notification.method === "account/rateLimits/updated") {
|
|
3871
|
-
this.latestRateLimits = params;
|
|
3872
|
-
rememberCodexRateLimits(params);
|
|
3873
|
-
return;
|
|
3874
|
-
}
|
|
3875
|
-
if (isHookNotificationMethod(notification.method)) {
|
|
3876
|
-
if (!this.isHookNotificationForCurrentThread(params)) return;
|
|
3877
|
-
} else if (!this.isNotificationForTurn(params)) return;
|
|
3878
|
-
switch (notification.method) {
|
|
3879
|
-
case "item/agentMessage/delta":
|
|
3880
|
-
await this.handleAssistantDelta(params);
|
|
3881
|
-
break;
|
|
3882
|
-
case "item/reasoning/summaryTextDelta":
|
|
3883
|
-
case "item/reasoning/textDelta":
|
|
3884
|
-
await this.handleReasoningDelta(notification.method, params);
|
|
3885
|
-
break;
|
|
3886
|
-
case "item/plan/delta":
|
|
3887
|
-
this.handlePlanDelta(params);
|
|
3888
|
-
break;
|
|
3889
|
-
case "turn/plan/updated":
|
|
3890
|
-
this.handleTurnPlanUpdated(params);
|
|
3891
|
-
break;
|
|
3892
|
-
case "item/started":
|
|
3893
|
-
await this.handleItemStarted(params);
|
|
3894
|
-
break;
|
|
3895
|
-
case "item/completed":
|
|
3896
|
-
await this.handleItemCompleted(params);
|
|
3897
|
-
break;
|
|
3898
|
-
case "item/commandExecution/outputDelta":
|
|
3899
|
-
this.handleOutputDelta(params, "bash");
|
|
3900
|
-
break;
|
|
3901
|
-
case "item/fileChange/outputDelta":
|
|
3902
|
-
this.handleOutputDelta(params, "apply_patch");
|
|
3903
|
-
break;
|
|
3904
|
-
case "item/autoApprovalReview/started":
|
|
3905
|
-
case "item/autoApprovalReview/completed":
|
|
3906
|
-
this.handleGuardianReviewNotification(notification.method, params);
|
|
3907
|
-
break;
|
|
3908
|
-
case "hook/started":
|
|
3909
|
-
case "hook/completed":
|
|
3910
|
-
this.handleHookNotification(notification.method, params);
|
|
3911
|
-
break;
|
|
3912
|
-
case "thread/tokenUsage/updated":
|
|
3913
|
-
this.handleTokenUsage(params);
|
|
3914
|
-
break;
|
|
3915
|
-
case "turn/completed":
|
|
3916
|
-
await this.handleTurnCompleted(params);
|
|
3917
|
-
break;
|
|
3918
|
-
case "rawResponseItem/completed":
|
|
3919
|
-
await this.handleRawResponseItemCompleted(params);
|
|
3920
|
-
break;
|
|
3921
|
-
case "error":
|
|
3922
|
-
if (readBooleanAlias(params, ["willRetry", "will_retry"]) === true) break;
|
|
3923
|
-
this.promptError = this.formatCodexErrorMessage(params) ?? "codex app-server error";
|
|
3924
|
-
this.promptErrorSource = "prompt";
|
|
3925
|
-
break;
|
|
3926
|
-
default: break;
|
|
3927
|
-
}
|
|
3928
|
-
}
|
|
3929
|
-
buildResult(toolTelemetry, options) {
|
|
3930
|
-
const assistantTexts = this.collectAssistantTexts();
|
|
3931
|
-
const reasoningText = collectReasoningTextValues(this.reasoningTextByGroup, this.reasoningItemOrder).join("\n\n");
|
|
3932
|
-
const planText = collectTextValues(this.planTextByItem).join("\n\n");
|
|
3933
|
-
const hasAssistantItemText = this.hasAssistantItemTextForSynthesis();
|
|
3934
|
-
const legacyFailClosed = !this.completedTurn || this.completedTurn.status !== "completed" || hasAssistantItemText;
|
|
3935
|
-
const hasDeliverableAssistantOnCompletedTurn = this.completedTurn?.status === "completed" && assistantTexts.some((text) => text.trim().length > 0);
|
|
3936
|
-
this.synthesizeMissingToolResults({
|
|
3937
|
-
synthesize: legacyFailClosed,
|
|
3938
|
-
recordPromptError: legacyFailClosed && !hasDeliverableAssistantOnCompletedTurn
|
|
3939
|
-
});
|
|
3940
|
-
const lastAssistant = assistantTexts.length > 0 ? this.createAssistantMessage(assistantTexts.join("\n\n")) : void 0;
|
|
3941
|
-
const currentAttemptAssistant = this.createCurrentAttemptAssistantMessage();
|
|
3942
|
-
const turnId = this.turnId;
|
|
3943
|
-
const messagesSnapshot = this.params.suppressNextUserMessagePersistence ? [] : [attachCodexMirrorIdentity(buildCodexUserPromptMessage(this.params), `${turnId}:prompt`)];
|
|
3944
|
-
if (reasoningText) messagesSnapshot.push(attachCodexMirrorIdentity(this.createAssistantMirrorMessage("Codex reasoning", reasoningText), `${turnId}:reasoning`));
|
|
3945
|
-
if (planText) messagesSnapshot.push(attachCodexMirrorIdentity(this.createAssistantMirrorMessage("Codex plan", planText), `${turnId}:plan`));
|
|
3946
|
-
messagesSnapshot.push(...this.toolTranscriptMessages);
|
|
3947
|
-
if (lastAssistant) messagesSnapshot.push(attachCodexMirrorIdentity(lastAssistant, `${turnId}:assistant`));
|
|
3948
|
-
const turnFailed = this.completedTurn?.status === "failed";
|
|
3949
|
-
const promptError = this.promptError ?? this.synthesizedMissingToolResultError ?? (turnFailed ? this.completedTurn?.error?.message ?? "codex app-server turn failed" : null);
|
|
3950
|
-
const agentHarnessResultClassification = classifyAgentHarnessTerminalOutcome({
|
|
3951
|
-
assistantTexts,
|
|
3952
|
-
reasoningText,
|
|
3953
|
-
planText,
|
|
3954
|
-
promptError,
|
|
3955
|
-
turnCompleted: Boolean(this.completedTurn)
|
|
3956
|
-
});
|
|
3957
|
-
const toolMetas = [...this.toolMetas.values()];
|
|
3958
|
-
const hadPotentialSideEffects = toolTelemetry.didSendViaMessagingTool || (toolTelemetry.successfulCronAdds ?? 0) > 0 || this.nativeGeneratedMediaItemIds.size > 0 || this.sideEffectingToolItemIds.size > 0 || this.sideEffectingDynamicToolCallIds.size > 0;
|
|
3959
|
-
return {
|
|
3960
|
-
aborted: this.aborted,
|
|
3961
|
-
externalAbort: false,
|
|
3962
|
-
timedOut: false,
|
|
3963
|
-
idleTimedOut: false,
|
|
3964
|
-
timedOutDuringCompaction: false,
|
|
3965
|
-
timedOutDuringToolExecution: false,
|
|
3966
|
-
promptError,
|
|
3967
|
-
promptErrorSource: promptError ? this.promptErrorSource || "prompt" : null,
|
|
3968
|
-
sessionIdUsed: this.params.sessionId,
|
|
3969
|
-
...agentHarnessResultClassification ? { agentHarnessResultClassification } : {},
|
|
3970
|
-
bootstrapPromptWarningSignaturesSeen: this.params.bootstrapPromptWarningSignaturesSeen,
|
|
3971
|
-
bootstrapPromptWarningSignature: this.params.bootstrapPromptWarningSignature,
|
|
3972
|
-
messagesSnapshot,
|
|
3973
|
-
assistantTexts,
|
|
3974
|
-
toolMetas,
|
|
3975
|
-
lastAssistant,
|
|
3976
|
-
currentAttemptAssistant,
|
|
3977
|
-
...this.lastNativeToolError ? { lastToolError: this.lastNativeToolError } : {},
|
|
3978
|
-
didSendViaMessagingTool: toolTelemetry.didSendViaMessagingTool,
|
|
3979
|
-
didDeliverSourceReplyViaMessageTool: toolTelemetry.didDeliverSourceReplyViaMessageTool === true,
|
|
3980
|
-
messagingToolSentTexts: toolTelemetry.messagingToolSentTexts,
|
|
3981
|
-
messagingToolSentMediaUrls: toolTelemetry.messagingToolSentMediaUrls,
|
|
3982
|
-
messagingToolSentTargets: toolTelemetry.messagingToolSentTargets,
|
|
3983
|
-
messagingToolSourceReplyPayloads: toolTelemetry.messagingToolSourceReplyPayloads ?? [],
|
|
3984
|
-
heartbeatToolResponse: toolTelemetry.heartbeatToolResponse,
|
|
3985
|
-
toolMediaUrls: this.buildToolMediaUrls(toolTelemetry),
|
|
3986
|
-
toolAudioAsVoice: toolTelemetry.toolAudioAsVoice,
|
|
3987
|
-
successfulCronAdds: toolTelemetry.successfulCronAdds,
|
|
3988
|
-
cloudCodeAssistFormatError: false,
|
|
3989
|
-
attemptUsage: this.tokenUsage,
|
|
3990
|
-
replayMetadata: {
|
|
3991
|
-
hadPotentialSideEffects,
|
|
3992
|
-
replaySafe: !hadPotentialSideEffects
|
|
3993
|
-
},
|
|
3994
|
-
itemLifecycle: {
|
|
3995
|
-
startedCount: this.activeItemIds.size + this.completedItemIds.size,
|
|
3996
|
-
completedCount: this.completedItemIds.size,
|
|
3997
|
-
activeCount: this.activeItemIds.size,
|
|
3998
|
-
...this.completedCompactionCount > 0 ? { compactionCount: this.completedCompactionCount } : {}
|
|
3999
|
-
},
|
|
4000
|
-
yieldDetected: options?.yieldDetected || false,
|
|
4001
|
-
didSendDeterministicApprovalPrompt: this.guardianReviewCount > 0 ? false : void 0
|
|
4002
|
-
};
|
|
4003
|
-
}
|
|
4004
|
-
recordDynamicToolCall(params) {
|
|
4005
|
-
const args = sanitizeCodexToolArguments(params.arguments);
|
|
4006
|
-
this.recordToolTranscriptCall({
|
|
4007
|
-
id: params.callId,
|
|
4008
|
-
name: params.tool,
|
|
4009
|
-
arguments: args
|
|
4010
|
-
});
|
|
4011
|
-
}
|
|
4012
|
-
recordDynamicToolResult(params) {
|
|
4013
|
-
const resultText = collectDynamicToolContentText(params.contentItems);
|
|
4014
|
-
if (params.asyncStarted === true) {
|
|
4015
|
-
const existing = this.toolMetas.get(params.callId);
|
|
4016
|
-
this.toolMetas.set(params.callId, {
|
|
4017
|
-
toolName: existing?.toolName ?? params.tool,
|
|
4018
|
-
...existing?.meta ? { meta: existing.meta } : {},
|
|
4019
|
-
asyncStarted: true
|
|
4020
|
-
});
|
|
4021
|
-
}
|
|
4022
|
-
this.recordToolTranscriptResult({
|
|
4023
|
-
id: params.callId,
|
|
4024
|
-
name: params.tool,
|
|
4025
|
-
text: resultText,
|
|
4026
|
-
isError: !params.success
|
|
4027
|
-
});
|
|
4028
|
-
if (!params.success && params.terminalType === "blocked") this.lastNativeToolError = {
|
|
4029
|
-
toolName: params.tool,
|
|
4030
|
-
error: resultText || "codex dynamic tool blocked"
|
|
4031
|
-
};
|
|
4032
|
-
else if (params.success && this.lastNativeToolError && !this.lastNativeToolError.mutatingAction) this.lastNativeToolError = void 0;
|
|
4033
|
-
if (params.sideEffectEvidence === true) this.sideEffectingDynamicToolCallIds.add(params.callId);
|
|
4034
|
-
}
|
|
4035
|
-
markTimedOut() {
|
|
4036
|
-
this.aborted = true;
|
|
4037
|
-
this.promptError = "codex app-server attempt timed out";
|
|
4038
|
-
this.promptErrorSource = "prompt";
|
|
4039
|
-
}
|
|
4040
|
-
markAborted() {
|
|
4041
|
-
this.aborted = true;
|
|
4042
|
-
}
|
|
4043
|
-
isCompacting() {
|
|
4044
|
-
return this.activeCompactionItemIds.size > 0;
|
|
4045
|
-
}
|
|
4046
|
-
async handleAssistantDelta(params) {
|
|
4047
|
-
const itemId = readString$5(params, "itemId") ?? readString$5(params, "id") ?? "assistant";
|
|
4048
|
-
const delta = readString$5(params, "delta") ?? "";
|
|
4049
|
-
if (!delta) return;
|
|
4050
|
-
this.rememberAssistantPhase(readItem(params.item));
|
|
4051
|
-
const phase = readString$5(params, "phase");
|
|
4052
|
-
if (phase) this.assistantPhaseByItem.set(itemId, phase);
|
|
4053
|
-
if (!this.assistantStarted) {
|
|
4054
|
-
this.assistantStarted = true;
|
|
4055
|
-
await this.params.onAssistantMessageStart?.();
|
|
4056
|
-
}
|
|
4057
|
-
this.rememberAssistantItem(itemId);
|
|
4058
|
-
const text = `${this.assistantTextByItem.get(itemId) ?? ""}${delta}`;
|
|
4059
|
-
this.assistantTextByItem.set(itemId, text);
|
|
4060
|
-
if (this.isCommentaryAssistantItem(itemId)) this.emitCommentaryProgress({
|
|
4061
|
-
itemId,
|
|
4062
|
-
text
|
|
4063
|
-
});
|
|
4064
|
-
else {
|
|
4065
|
-
const knownFinalAnswer = this.shouldStreamAssistantPartial(itemId);
|
|
4066
|
-
const replace = this.streamedPartialAssistantItemId !== void 0 && this.streamedPartialAssistantItemId !== itemId;
|
|
4067
|
-
if (replace && (!knownFinalAnswer || this.streamedPartialAssistantItemReplaceable)) this.streamedPartialAssistantItemReplaceable = true;
|
|
4068
|
-
else if (this.streamedPartialAssistantItemId === void 0) this.streamedPartialAssistantItemReplaceable = !knownFinalAnswer;
|
|
4069
|
-
this.streamedPartialAssistantItemId = itemId;
|
|
4070
|
-
const replaceable = this.streamedPartialAssistantItemReplaceable;
|
|
4071
|
-
const replacement = replace && replaceable;
|
|
4072
|
-
const streamPayload = {
|
|
4073
|
-
text,
|
|
4074
|
-
delta: replacement ? "" : delta,
|
|
4075
|
-
...replacement ? { replace: true } : {}
|
|
4076
|
-
};
|
|
4077
|
-
this.emitAgentEvent({
|
|
4078
|
-
stream: "assistant",
|
|
4079
|
-
data: {
|
|
4080
|
-
...streamPayload,
|
|
4081
|
-
...replaceable ? { replaceable: true } : {}
|
|
4082
|
-
}
|
|
4083
|
-
});
|
|
4084
|
-
if (knownFinalAnswer && !replaceable) await this.params.onPartialReply?.(streamPayload);
|
|
4085
|
-
}
|
|
4086
|
-
}
|
|
4087
|
-
async handleReasoningDelta(method, params) {
|
|
4088
|
-
const itemId = readString$5(params, "itemId") ?? readString$5(params, "id") ?? "reasoning";
|
|
4089
|
-
const delta = readString$5(params, "delta") ?? "";
|
|
4090
|
-
if (!delta) return;
|
|
4091
|
-
this.reasoningStarted = true;
|
|
4092
|
-
if (!this.reasoningItemOrder.has(itemId)) this.reasoningItemOrder.set(itemId, this.reasoningItemOrder.size);
|
|
4093
|
-
const groupIndex = method === "item/reasoning/textDelta" ? readNonNegativeInteger(params, "contentIndex") ?? 0 : readNonNegativeInteger(params, "summaryIndex") ?? 0;
|
|
4094
|
-
const groupKey = `${method}\0${itemId}\0${groupIndex}`;
|
|
4095
|
-
const current = this.reasoningTextByGroup.get(groupKey);
|
|
4096
|
-
this.reasoningTextByGroup.set(groupKey, {
|
|
4097
|
-
itemId,
|
|
4098
|
-
method,
|
|
4099
|
-
index: groupIndex,
|
|
4100
|
-
text: `${current?.text ?? ""}${delta}`
|
|
4101
|
-
});
|
|
4102
|
-
await this.params.onReasoningStream?.({
|
|
4103
|
-
text: collectReasoningTextValues(this.reasoningTextByGroup, this.reasoningItemOrder).join("\n\n"),
|
|
4104
|
-
isReasoningSnapshot: true
|
|
4105
|
-
});
|
|
4106
|
-
}
|
|
4107
|
-
handlePlanDelta(params) {
|
|
4108
|
-
const itemId = readString$5(params, "itemId") ?? readString$5(params, "id") ?? "plan";
|
|
4109
|
-
const delta = readString$5(params, "delta") ?? "";
|
|
4110
|
-
if (!delta) return;
|
|
4111
|
-
const text = `${this.planTextByItem.get(itemId) ?? ""}${delta}`;
|
|
4112
|
-
this.planTextByItem.set(itemId, text);
|
|
4113
|
-
this.emitPlanUpdate({
|
|
4114
|
-
explanation: void 0,
|
|
4115
|
-
steps: splitPlanText(text)
|
|
4116
|
-
});
|
|
4117
|
-
}
|
|
4118
|
-
handleTurnPlanUpdated(params) {
|
|
4119
|
-
const plan = Array.isArray(params.plan) ? params.plan.flatMap((entry) => {
|
|
4120
|
-
if (!isJsonObject(entry)) return [];
|
|
4121
|
-
const step = readString$5(entry, "step");
|
|
4122
|
-
const status = readString$5(entry, "status");
|
|
4123
|
-
if (!step) return [];
|
|
4124
|
-
return status ? [`${step} (${status})`] : [step];
|
|
4125
|
-
}) : void 0;
|
|
4126
|
-
this.emitPlanUpdate({
|
|
4127
|
-
explanation: readNullableString$1(params, "explanation"),
|
|
4128
|
-
steps: plan
|
|
4129
|
-
});
|
|
4130
|
-
}
|
|
4131
|
-
async handleItemStarted(params) {
|
|
4132
|
-
const item = readItem(params.item);
|
|
4133
|
-
const itemId = item?.id ?? readString$5(params, "itemId") ?? readString$5(params, "id");
|
|
4134
|
-
this.rememberAssistantPhase(item);
|
|
4135
|
-
if (itemId) this.activeItemIds.add(itemId);
|
|
4136
|
-
this.recordNativeToolOutcome(item);
|
|
4137
|
-
if (item?.type === "contextCompaction" && itemId) {
|
|
4138
|
-
this.activeCompactionItemIds.add(itemId);
|
|
4139
|
-
await runAgentHarnessBeforeCompactionHook({
|
|
4140
|
-
sessionFile: this.params.sessionFile,
|
|
4141
|
-
messages: await this.readMirroredSessionMessages(),
|
|
4142
|
-
ctx: {
|
|
4143
|
-
runId: this.params.runId,
|
|
4144
|
-
agentId: this.params.agentId,
|
|
4145
|
-
sessionKey: this.params.sessionKey,
|
|
4146
|
-
sessionId: this.params.sessionId,
|
|
4147
|
-
workspaceDir: this.params.workspaceDir,
|
|
4148
|
-
messageProvider: this.params.messageProvider ?? void 0,
|
|
4149
|
-
trigger: this.params.trigger,
|
|
4150
|
-
channelId: this.params.messageChannel ?? this.params.messageProvider ?? void 0
|
|
4151
|
-
}
|
|
4152
|
-
});
|
|
4153
|
-
this.emitAgentEvent({
|
|
4154
|
-
stream: "compaction",
|
|
4155
|
-
data: {
|
|
4156
|
-
phase: "start",
|
|
4157
|
-
backend: "codex-app-server",
|
|
4158
|
-
threadId: this.threadId,
|
|
4159
|
-
turnId: this.turnId,
|
|
4160
|
-
itemId
|
|
4161
|
-
}
|
|
4162
|
-
});
|
|
4163
|
-
}
|
|
4164
|
-
this.recordToolMeta(item);
|
|
4165
|
-
this.emitStandardItemEvent({
|
|
4166
|
-
phase: "start",
|
|
4167
|
-
item
|
|
4168
|
-
});
|
|
4169
|
-
await this.emitNormalizedToolItemEvent({
|
|
4170
|
-
phase: "start",
|
|
4171
|
-
item
|
|
4172
|
-
});
|
|
4173
|
-
this.recordNativeToolTranscriptCall(item);
|
|
4174
|
-
this.emitToolResultSummary(item);
|
|
4175
|
-
this.emitAgentEvent({
|
|
4176
|
-
stream: "codex_app_server.item",
|
|
4177
|
-
data: {
|
|
4178
|
-
phase: "started",
|
|
4179
|
-
itemId,
|
|
4180
|
-
type: item?.type
|
|
4181
|
-
}
|
|
4182
|
-
});
|
|
4183
|
-
}
|
|
4184
|
-
async handleItemCompleted(params) {
|
|
4185
|
-
const item = readItem(params.item);
|
|
4186
|
-
this.recordNativeToolOutcome(item);
|
|
4187
|
-
this.clearTerminalPresentationForNativeItem(item);
|
|
4188
|
-
const itemId = item?.id ?? readString$5(params, "itemId") ?? readString$5(params, "id");
|
|
4189
|
-
if (itemId) {
|
|
4190
|
-
this.activeItemIds.delete(itemId);
|
|
4191
|
-
this.completedItemIds.add(itemId);
|
|
4192
|
-
}
|
|
4193
|
-
this.rememberAssistantPhase(item);
|
|
4194
|
-
if (item?.type === "agentMessage" && typeof item.text === "string") {
|
|
4195
|
-
this.rememberAssistantItem(item.id);
|
|
4196
|
-
this.assistantTextByItem.set(item.id, item.text);
|
|
4197
|
-
if (item.text && this.isCommentaryAssistantItem(item.id)) {
|
|
4198
|
-
this.emitCommentaryProgress({
|
|
4199
|
-
itemId: item.id,
|
|
4200
|
-
text: item.text
|
|
4201
|
-
});
|
|
4202
|
-
this.pendingRawCommentaryEchoes += 1;
|
|
4203
|
-
}
|
|
4204
|
-
}
|
|
4205
|
-
this.recordNativeGeneratedMedia(item);
|
|
4206
|
-
if (item?.type === "plan" && typeof item.text === "string" && item.text) {
|
|
4207
|
-
this.planTextByItem.set(item.id, item.text);
|
|
4208
|
-
this.emitPlanUpdate({
|
|
4209
|
-
explanation: void 0,
|
|
4210
|
-
steps: splitPlanText(item.text)
|
|
4211
|
-
});
|
|
4212
|
-
}
|
|
4213
|
-
if (item?.type === "contextCompaction" && itemId) {
|
|
4214
|
-
this.activeCompactionItemIds.delete(itemId);
|
|
4215
|
-
this.completedCompactionCount += 1;
|
|
4216
|
-
await runAgentHarnessAfterCompactionHook({
|
|
4217
|
-
sessionFile: this.params.sessionFile,
|
|
4218
|
-
messages: await this.readMirroredSessionMessages(),
|
|
4219
|
-
compactedCount: -1,
|
|
4220
|
-
ctx: {
|
|
4221
|
-
runId: this.params.runId,
|
|
4222
|
-
agentId: this.params.agentId,
|
|
4223
|
-
sessionKey: this.params.sessionKey,
|
|
4224
|
-
sessionId: this.params.sessionId,
|
|
4225
|
-
workspaceDir: this.params.workspaceDir,
|
|
4226
|
-
messageProvider: this.params.messageProvider ?? void 0,
|
|
4227
|
-
trigger: this.params.trigger,
|
|
4228
|
-
channelId: this.params.messageChannel ?? this.params.messageProvider ?? void 0
|
|
4229
|
-
}
|
|
4230
|
-
});
|
|
4231
|
-
this.emitAgentEvent({
|
|
4232
|
-
stream: "compaction",
|
|
4233
|
-
data: {
|
|
4234
|
-
phase: "end",
|
|
4235
|
-
backend: "codex-app-server",
|
|
4236
|
-
completed: true,
|
|
4237
|
-
threadId: this.threadId,
|
|
4238
|
-
turnId: this.turnId,
|
|
4239
|
-
itemId
|
|
4240
|
-
}
|
|
4241
|
-
});
|
|
4242
|
-
}
|
|
4243
|
-
this.recordToolMeta(item);
|
|
4244
|
-
this.emitStandardItemEvent({
|
|
4245
|
-
phase: "end",
|
|
4246
|
-
item
|
|
4247
|
-
});
|
|
4248
|
-
await this.emitNormalizedToolItemEvent({
|
|
4249
|
-
phase: "result",
|
|
4250
|
-
item
|
|
4251
|
-
});
|
|
4252
|
-
this.recordNativeToolTranscriptCall(item);
|
|
4253
|
-
this.recordNativeToolTranscriptResult(item);
|
|
4254
|
-
this.emitToolResultSummary(item);
|
|
4255
|
-
this.emitToolResultOutput(item);
|
|
4256
|
-
this.emitAgentEvent({
|
|
4257
|
-
stream: "codex_app_server.item",
|
|
4258
|
-
data: {
|
|
4259
|
-
phase: "completed",
|
|
4260
|
-
itemId,
|
|
4261
|
-
type: item?.type
|
|
4262
|
-
}
|
|
4263
|
-
});
|
|
4264
|
-
}
|
|
4265
|
-
handleTokenUsage(params) {
|
|
4266
|
-
const tokenUsage = isJsonObject(params.tokenUsage) ? params.tokenUsage : void 0;
|
|
4267
|
-
const current = (tokenUsage ? readFirstJsonObject(tokenUsage, CURRENT_TOKEN_USAGE_KEYS) : void 0) ?? readFirstJsonObject(params, CURRENT_TOKEN_USAGE_KEYS);
|
|
4268
|
-
if (!current) return;
|
|
4269
|
-
const usage = normalizeCodexTokenUsage(current);
|
|
4270
|
-
if (usage) this.tokenUsage = usage;
|
|
4271
|
-
}
|
|
4272
|
-
handleGuardianReviewNotification(method, params) {
|
|
4273
|
-
this.guardianReviewCount += 1;
|
|
4274
|
-
const review = isJsonObject(params.review) ? params.review : void 0;
|
|
4275
|
-
const action = isJsonObject(params.action) ? params.action : void 0;
|
|
4276
|
-
this.emitAgentEvent({
|
|
4277
|
-
stream: "codex_app_server.guardian",
|
|
4278
|
-
data: {
|
|
4279
|
-
method,
|
|
4280
|
-
phase: method.endsWith("/started") ? "started" : "completed",
|
|
4281
|
-
reviewId: readString$5(params, "reviewId"),
|
|
4282
|
-
targetItemId: readNullableString$1(params, "targetItemId"),
|
|
4283
|
-
decisionSource: readString$5(params, "decisionSource"),
|
|
4284
|
-
status: review ? readString$5(review, "status") : void 0,
|
|
4285
|
-
riskLevel: review ? readString$5(review, "riskLevel") : void 0,
|
|
4286
|
-
userAuthorization: review ? readString$5(review, "userAuthorization") : void 0,
|
|
4287
|
-
rationale: review ? readNullableString$1(review, "rationale") : void 0,
|
|
4288
|
-
actionType: action ? readString$5(action, "type") : void 0
|
|
4289
|
-
}
|
|
4290
|
-
});
|
|
4291
|
-
}
|
|
4292
|
-
handleHookNotification(method, params) {
|
|
4293
|
-
const run = isJsonObject(params.run) ? params.run : void 0;
|
|
4294
|
-
if (!run) return;
|
|
4295
|
-
const durationMs = readNumber$1(run, "durationMs");
|
|
4296
|
-
const entries = readHookOutputEntries(run.entries);
|
|
4297
|
-
const hookTurnId = readNullableString$1(params, "turnId");
|
|
4298
|
-
this.emitAgentEvent({
|
|
4299
|
-
stream: "codex_app_server.hook",
|
|
4300
|
-
data: {
|
|
4301
|
-
phase: method === "hook/started" ? "started" : "completed",
|
|
4302
|
-
threadId: this.threadId,
|
|
4303
|
-
turnId: hookTurnId === void 0 ? this.turnId : hookTurnId,
|
|
4304
|
-
hookRunId: readString$5(run, "id"),
|
|
4305
|
-
eventName: readString$5(run, "eventName"),
|
|
4306
|
-
handlerType: readString$5(run, "handlerType"),
|
|
4307
|
-
executionMode: readString$5(run, "executionMode"),
|
|
4308
|
-
scope: readString$5(run, "scope"),
|
|
4309
|
-
source: readString$5(run, "source"),
|
|
4310
|
-
sourcePath: readString$5(run, "sourcePath"),
|
|
4311
|
-
status: readString$5(run, "status"),
|
|
4312
|
-
statusMessage: readNullableString$1(run, "statusMessage"),
|
|
4313
|
-
...durationMs !== void 0 ? { durationMs } : {},
|
|
4314
|
-
...entries.length > 0 ? { entries } : {}
|
|
4315
|
-
}
|
|
4316
|
-
});
|
|
4317
|
-
}
|
|
4318
|
-
async handleTurnCompleted(params) {
|
|
4319
|
-
const turn = readTurn(params.turn);
|
|
4320
|
-
if (!turn || turn.id !== this.turnId) return;
|
|
4321
|
-
this.completedTurn = turn;
|
|
4322
|
-
if (turn.status === "failed") {
|
|
4323
|
-
this.promptError = formatCodexUsageLimitErrorMessage({
|
|
4324
|
-
message: turn.error?.message,
|
|
4325
|
-
codexErrorInfo: turn.error?.codexErrorInfo,
|
|
4326
|
-
rateLimits: this.latestRateLimits ?? readRecentCodexRateLimits()
|
|
4327
|
-
}) ?? turn.error?.message ?? "codex app-server turn failed";
|
|
4328
|
-
this.promptErrorSource = "prompt";
|
|
4329
|
-
}
|
|
4330
|
-
const turnItems = turn.items ?? [];
|
|
4331
|
-
for (let index = turnItems.length - 1; index >= 0; index -= 1) {
|
|
4332
|
-
const item = turnItems[index];
|
|
4333
|
-
if (!item || !this.isCurrentTurnSnapshotItem(item)) continue;
|
|
4334
|
-
if (item?.type === "dynamicToolCall") break;
|
|
4335
|
-
if (shouldClearTerminalPresentationForNativeItem(item)) {
|
|
4336
|
-
this.clearTerminalPresentationForNativeItem(item);
|
|
4337
|
-
break;
|
|
4338
|
-
}
|
|
4339
|
-
}
|
|
4340
|
-
for (const item of turnItems) {
|
|
4341
|
-
this.rememberAssistantPhase(item);
|
|
4342
|
-
if (item.type === "agentMessage" && typeof item.text === "string") {
|
|
4343
|
-
this.rememberAssistantItem(item.id);
|
|
4344
|
-
this.assistantTextByItem.set(item.id, item.text);
|
|
4345
|
-
}
|
|
4346
|
-
this.recordNativeGeneratedMedia(item);
|
|
4347
|
-
if (item.type === "plan" && typeof item.text === "string" && item.text) {
|
|
4348
|
-
this.planTextByItem.set(item.id, item.text);
|
|
4349
|
-
this.emitPlanUpdate({
|
|
4350
|
-
explanation: void 0,
|
|
4351
|
-
steps: splitPlanText(item.text)
|
|
4352
|
-
});
|
|
4353
|
-
}
|
|
4354
|
-
this.recordToolMeta(item);
|
|
4355
|
-
await this.emitSnapshotOnlyNativeToolProgress(item);
|
|
4356
|
-
this.recordNativeToolTranscriptCall(item);
|
|
4357
|
-
this.recordNativeToolTranscriptResult(item);
|
|
4358
|
-
this.emitAfterToolCallObservation(item);
|
|
4359
|
-
this.emitToolResultSummary(item);
|
|
4360
|
-
this.emitToolResultOutput(item);
|
|
4361
|
-
}
|
|
4362
|
-
this.activeCompactionItemIds.clear();
|
|
4363
|
-
await this.maybeEndReasoning();
|
|
4364
|
-
}
|
|
4365
|
-
async emitSnapshotOnlyNativeToolProgress(item) {
|
|
4366
|
-
if (!shouldSynthesizeToolProgressForItem(item) || !this.isCurrentTurnSnapshotItem(item) || this.completedItemIds.has(item.id) || itemStatus(item) === "running") return;
|
|
4367
|
-
if (!this.activeItemIds.has(item.id)) {
|
|
4368
|
-
this.emitStandardItemEvent({
|
|
4369
|
-
phase: "start",
|
|
4370
|
-
item
|
|
4371
|
-
});
|
|
4372
|
-
await this.emitNormalizedToolItemEvent({
|
|
4373
|
-
phase: "start",
|
|
4374
|
-
item
|
|
4375
|
-
});
|
|
4376
|
-
}
|
|
4377
|
-
this.activeItemIds.delete(item.id);
|
|
4378
|
-
this.emitStandardItemEvent({
|
|
4379
|
-
phase: "end",
|
|
4380
|
-
item
|
|
4381
|
-
});
|
|
4382
|
-
await this.emitNormalizedToolItemEvent({
|
|
4383
|
-
phase: "result",
|
|
4384
|
-
item
|
|
4385
|
-
});
|
|
4386
|
-
this.completedItemIds.add(item.id);
|
|
4387
|
-
}
|
|
4388
|
-
isCurrentTurnSnapshotItem(item) {
|
|
4389
|
-
const itemTurnId = readItemString(item, "turnId") ?? readItemString(item, "turn_id");
|
|
4390
|
-
return itemTurnId === void 0 || itemTurnId === this.turnId;
|
|
4391
|
-
}
|
|
4392
|
-
handleOutputDelta(params, toolName) {
|
|
4393
|
-
const itemId = readString$5(params, "itemId");
|
|
4394
|
-
const delta = readString$5(params, "delta");
|
|
4395
|
-
if (!itemId || !delta) return;
|
|
4396
|
-
appendToolOutputDeltaText(this.toolResultOutputTextByItem, itemId, delta);
|
|
4397
|
-
if (!this.shouldEmitToolOutput()) return;
|
|
4398
|
-
if (this.transcriptToolProgressSuppressedIds.has(itemId) || !shouldEmitTranscriptToolProgress(toolName, this.toolTranscriptArgumentsById.get(itemId))) return;
|
|
4399
|
-
const state = this.toolResultOutputDeltaState.get(itemId) ?? {
|
|
4400
|
-
chars: 0,
|
|
4401
|
-
messages: 0,
|
|
4402
|
-
truncated: false
|
|
4403
|
-
};
|
|
4404
|
-
if (state.truncated) return;
|
|
4405
|
-
const remainingChars = Math.max(0, TOOL_PROGRESS_OUTPUT_MAX_CHARS - state.chars);
|
|
4406
|
-
const remainingMessages = Math.max(0, MAX_TOOL_OUTPUT_DELTA_MESSAGES_PER_ITEM - state.messages);
|
|
4407
|
-
if (remainingChars === 0 || remainingMessages === 0) {
|
|
4408
|
-
state.truncated = true;
|
|
4409
|
-
this.toolResultOutputDeltaState.set(itemId, state);
|
|
4410
|
-
this.emitToolResultMessage({
|
|
4411
|
-
itemId,
|
|
4412
|
-
text: formatToolOutput(toolName, void 0, "(output truncated)")
|
|
4413
|
-
});
|
|
4414
|
-
return;
|
|
4415
|
-
}
|
|
4416
|
-
const chunk = delta.length > remainingChars ? delta.slice(0, remainingChars) : delta;
|
|
4417
|
-
state.chars += chunk.length;
|
|
4418
|
-
state.messages += 1;
|
|
4419
|
-
const reachedLimit = delta.length > remainingChars || state.chars >= TOOL_PROGRESS_OUTPUT_MAX_CHARS || state.messages >= MAX_TOOL_OUTPUT_DELTA_MESSAGES_PER_ITEM;
|
|
4420
|
-
if (reachedLimit) state.truncated = true;
|
|
4421
|
-
this.toolResultOutputDeltaState.set(itemId, state);
|
|
4422
|
-
this.toolResultOutputStreamedItemIds.add(itemId);
|
|
4423
|
-
this.emitToolResultMessage({
|
|
4424
|
-
itemId,
|
|
4425
|
-
text: formatToolOutput(toolName, void 0, reachedLimit ? `${chunk}\n...(truncated)...` : chunk)
|
|
4426
|
-
});
|
|
4427
|
-
}
|
|
4428
|
-
async handleRawResponseItemCompleted(params) {
|
|
4429
|
-
const item = isJsonObject(params.item) ? params.item : void 0;
|
|
4430
|
-
if (!item) return;
|
|
4431
|
-
await this.recordRawGeneratedImageMedia(item);
|
|
4432
|
-
if (readString$5(item, "role") !== "assistant") return;
|
|
4433
|
-
const phase = readString$5(item, "phase");
|
|
4434
|
-
if (phase === "commentary" && this.pendingRawCommentaryEchoes > 0) {
|
|
4435
|
-
this.pendingRawCommentaryEchoes -= 1;
|
|
4436
|
-
return;
|
|
4437
|
-
}
|
|
4438
|
-
const text = extractRawAssistantText(item);
|
|
4439
|
-
if (!text) return;
|
|
4440
|
-
const itemId = readString$5(item, "id") ?? `raw-assistant-${this.assistantItemOrder.length + 1}`;
|
|
4441
|
-
if (phase) this.assistantPhaseByItem.set(itemId, phase);
|
|
4442
|
-
this.rememberAssistantItem(itemId);
|
|
4443
|
-
this.assistantTextByItem.set(itemId, text);
|
|
4444
|
-
if (phase === "commentary") this.emitCommentaryProgress({
|
|
4445
|
-
itemId,
|
|
4446
|
-
text
|
|
4447
|
-
});
|
|
4448
|
-
}
|
|
4449
|
-
recordNativeGeneratedMedia(item) {
|
|
4450
|
-
if (item?.type !== "imageGeneration") return;
|
|
4451
|
-
const savedPath = readItemString(item, "savedPath")?.trim();
|
|
4452
|
-
if (savedPath) this.recordNativeGeneratedMediaUrl({
|
|
4453
|
-
itemId: item.id,
|
|
4454
|
-
mediaUrl: savedPath
|
|
4455
|
-
});
|
|
4456
|
-
}
|
|
4457
|
-
async recordRawGeneratedImageMedia(item) {
|
|
4458
|
-
if (readString$5(item, "type") !== "image_generation_call") return;
|
|
4459
|
-
const result = readString$5(item, "result");
|
|
4460
|
-
if (!result) return;
|
|
4461
|
-
const itemId = readString$5(item, "id") ?? `raw-image-${this.nativeGeneratedMediaItemIds.size}`;
|
|
4462
|
-
this.nativeGeneratedMediaItemIds.add(itemId);
|
|
4463
|
-
const maxBytes = resolveGeneratedImageMaxBytes(this.params.config);
|
|
4464
|
-
const estimatedDecodedBytes = estimateBase64DecodedBytes(result);
|
|
4465
|
-
if (estimatedDecodedBytes !== void 0 && estimatedDecodedBytes > maxBytes) {
|
|
4466
|
-
embeddedAgentLog.warn("codex app-server raw image generation result exceeds media limit", {
|
|
4467
|
-
itemId,
|
|
4468
|
-
estimatedDecodedBytes,
|
|
4469
|
-
maxBytes
|
|
4470
|
-
});
|
|
4471
|
-
return;
|
|
4472
|
-
}
|
|
4473
|
-
const asset = generatedImageAssetFromBase64({
|
|
4474
|
-
base64: result,
|
|
4475
|
-
index: this.nativeGeneratedMediaItemIds.size,
|
|
4476
|
-
revisedPrompt: readString$5(item, "revised_prompt") ?? readString$5(item, "revisedPrompt"),
|
|
4477
|
-
fileNamePrefix: "codex-image-generation",
|
|
4478
|
-
sniffMimeType: true
|
|
4479
|
-
});
|
|
4480
|
-
if (!asset) return;
|
|
4481
|
-
try {
|
|
4482
|
-
const saved = await saveMediaBuffer(asset.buffer, asset.mimeType, GENERATED_IMAGE_MEDIA_SUBDIR, maxBytes, asset.fileName);
|
|
4483
|
-
this.recordNativeGeneratedMediaUrl({
|
|
4484
|
-
itemId,
|
|
4485
|
-
mediaUrl: saved.path,
|
|
4486
|
-
replaceExisting: true
|
|
4487
|
-
});
|
|
4488
|
-
} catch (error) {
|
|
4489
|
-
embeddedAgentLog.warn("codex app-server raw image generation result save failed", {
|
|
4490
|
-
itemId,
|
|
4491
|
-
error
|
|
4492
|
-
});
|
|
4493
|
-
}
|
|
4494
|
-
}
|
|
4495
|
-
recordNativeGeneratedMediaUrl(params) {
|
|
4496
|
-
if (this.nativeGeneratedMediaUrlsByItemId.has(params.itemId) && params.replaceExisting !== true) {
|
|
4497
|
-
this.nativeGeneratedMediaItemIds.add(params.itemId);
|
|
4498
|
-
return;
|
|
4499
|
-
}
|
|
4500
|
-
this.nativeGeneratedMediaUrlsByItemId.set(params.itemId, params.mediaUrl);
|
|
4501
|
-
this.nativeGeneratedMediaItemIds.add(params.itemId);
|
|
4502
|
-
}
|
|
4503
|
-
buildToolMediaUrls(toolTelemetry) {
|
|
4504
|
-
const mediaUrls = new Set(toolTelemetry.toolMediaUrls?.map((url) => url.trim()).filter(Boolean) ?? []);
|
|
4505
|
-
if ((toolTelemetry.messagingToolSentMediaUrls?.length ?? 0) === 0) for (const mediaUrl of this.nativeGeneratedMediaUrlsByItemId.values()) mediaUrls.add(mediaUrl);
|
|
4506
|
-
return mediaUrls.size > 0 ? [...mediaUrls] : toolTelemetry.toolMediaUrls;
|
|
4507
|
-
}
|
|
4508
|
-
async maybeEndReasoning() {
|
|
4509
|
-
if (!this.reasoningStarted || this.reasoningEnded) return;
|
|
4510
|
-
this.reasoningEnded = true;
|
|
4511
|
-
await this.params.onReasoningEnd?.();
|
|
4512
|
-
}
|
|
4513
|
-
emitPlanUpdate(params) {
|
|
4514
|
-
if (!params.explanation && (!params.steps || params.steps.length === 0)) return;
|
|
4515
|
-
this.emitAgentEvent({
|
|
4516
|
-
stream: "plan",
|
|
4517
|
-
data: {
|
|
4518
|
-
phase: "update",
|
|
4519
|
-
title: "Plan updated",
|
|
4520
|
-
source: "codex-app-server",
|
|
4521
|
-
...params.explanation ? { explanation: params.explanation } : {},
|
|
4522
|
-
...params.steps && params.steps.length > 0 ? { steps: params.steps } : {}
|
|
4523
|
-
}
|
|
4524
|
-
});
|
|
4525
|
-
}
|
|
4526
|
-
rememberAssistantPhase(item) {
|
|
4527
|
-
if (item?.type !== "agentMessage") return;
|
|
4528
|
-
const phase = readItemString(item, "phase");
|
|
4529
|
-
if (phase) this.assistantPhaseByItem.set(item.id, phase);
|
|
4530
|
-
}
|
|
4531
|
-
isCommentaryAssistantItem(itemId) {
|
|
4532
|
-
return this.assistantPhaseByItem.get(itemId) === "commentary";
|
|
4533
|
-
}
|
|
4534
|
-
shouldStreamAssistantPartial(itemId) {
|
|
4535
|
-
return this.assistantPhaseByItem.get(itemId) === "final_answer";
|
|
4536
|
-
}
|
|
4537
|
-
emitCommentaryProgress(params) {
|
|
4538
|
-
const progressText = params.text.replace(/\s+/g, " ").trim();
|
|
4539
|
-
if (!progressText || this.lastCommentaryProgressTextByItem.get(params.itemId) === progressText) return;
|
|
4540
|
-
this.lastCommentaryProgressTextByItem.set(params.itemId, progressText);
|
|
4541
|
-
this.emitAgentEvent({
|
|
4542
|
-
stream: "item",
|
|
4543
|
-
data: {
|
|
4544
|
-
itemId: params.itemId,
|
|
4545
|
-
kind: "preamble",
|
|
4546
|
-
title: "Preamble",
|
|
4547
|
-
phase: "update",
|
|
4548
|
-
progressText,
|
|
4549
|
-
source: "codex-app-server"
|
|
4550
|
-
}
|
|
4551
|
-
});
|
|
4552
|
-
}
|
|
4553
|
-
emitStandardItemEvent(params) {
|
|
4554
|
-
const { item } = params;
|
|
4555
|
-
if (!item) return;
|
|
4556
|
-
const kind = itemKind(item);
|
|
4557
|
-
if (!kind) return;
|
|
4558
|
-
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
4559
|
-
const suppressChannelProgress = shouldSuppressChannelProgressForItem(item);
|
|
4560
|
-
this.emitAgentEvent({
|
|
4561
|
-
stream: "item",
|
|
4562
|
-
data: {
|
|
4563
|
-
itemId: item.id,
|
|
4564
|
-
phase: params.phase,
|
|
4565
|
-
kind,
|
|
4566
|
-
title: itemTitle(item),
|
|
4567
|
-
status: params.phase === "start" ? "running" : itemStatus(item),
|
|
4568
|
-
...itemName(item) ? { name: itemName(item) } : {},
|
|
4569
|
-
...meta ? { meta } : {},
|
|
4570
|
-
...suppressChannelProgress ? { suppressChannelProgress: true } : {}
|
|
4571
|
-
}
|
|
4572
|
-
});
|
|
4573
|
-
}
|
|
4574
|
-
async emitNormalizedToolItemEvent(params) {
|
|
4575
|
-
const { item } = params;
|
|
4576
|
-
if (!item || !shouldSynthesizeToolProgressForItem(item)) return;
|
|
4577
|
-
const name = itemName(item);
|
|
4578
|
-
if (!name) return;
|
|
4579
|
-
const status = params.phase === "result" ? itemStatus(item) : "running";
|
|
4580
|
-
const args = itemToolArgs(item);
|
|
4581
|
-
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
4582
|
-
this.recordToolTrajectoryEvent({
|
|
4583
|
-
phase: params.phase,
|
|
4584
|
-
item,
|
|
4585
|
-
name,
|
|
4586
|
-
args,
|
|
4587
|
-
status
|
|
4588
|
-
});
|
|
4589
|
-
this.emitDiagnosticToolExecutionEvent({
|
|
4590
|
-
phase: params.phase,
|
|
4591
|
-
item,
|
|
4592
|
-
name,
|
|
4593
|
-
status
|
|
4594
|
-
});
|
|
4595
|
-
if (params.phase === "result") this.recordNativeToolError({
|
|
4596
|
-
item,
|
|
4597
|
-
name,
|
|
4598
|
-
meta,
|
|
4599
|
-
status
|
|
4600
|
-
});
|
|
4601
|
-
if (!shouldEmitTranscriptToolProgress(name, args)) {
|
|
4602
|
-
if (params.phase === "result") {
|
|
4603
|
-
this.emitAfterToolCallObservation(item);
|
|
4604
|
-
await this.options.onNativeToolResultRecorded?.();
|
|
4605
|
-
}
|
|
4606
|
-
return;
|
|
4607
|
-
}
|
|
4608
|
-
this.emitAgentEvent({
|
|
4609
|
-
stream: "tool",
|
|
4610
|
-
data: {
|
|
4611
|
-
phase: params.phase,
|
|
4612
|
-
name,
|
|
4613
|
-
itemId: item.id,
|
|
4614
|
-
toolCallId: item.id,
|
|
4615
|
-
...meta ? { meta } : {},
|
|
4616
|
-
...params.phase === "start" && args ? { args } : {},
|
|
4617
|
-
...params.phase === "result" ? {
|
|
4618
|
-
status,
|
|
4619
|
-
isError: isNonSuccessItemStatus(status),
|
|
4620
|
-
...itemToolResult(item)
|
|
4621
|
-
} : {}
|
|
4622
|
-
}
|
|
4623
|
-
});
|
|
4624
|
-
if (params.phase === "result") {
|
|
4625
|
-
this.emitAfterToolCallObservation(item);
|
|
4626
|
-
await this.options.onNativeToolResultRecorded?.();
|
|
4627
|
-
}
|
|
4628
|
-
}
|
|
4629
|
-
clearTerminalPresentationForNativeItem(item) {
|
|
4630
|
-
if (!item || this.terminalPresentationClearedItemIds.has(item.id) || !shouldClearTerminalPresentationForNativeItem(item)) return;
|
|
4631
|
-
const toolCallOrdinal = this.nativeToolOutcomeOrdinals.get(item.id);
|
|
4632
|
-
this.terminalPresentationClearedItemIds.add(item.id);
|
|
4633
|
-
this.params.onToolOutcome?.({
|
|
4634
|
-
toolName: itemName(item) ?? item.type,
|
|
4635
|
-
argsHash: "",
|
|
4636
|
-
resultHash: "",
|
|
4637
|
-
...toolCallOrdinal !== void 0 ? { toolCallOrdinal } : {},
|
|
4638
|
-
terminalPresentation: void 0,
|
|
4639
|
-
presentationOnly: true
|
|
4640
|
-
});
|
|
4641
|
-
}
|
|
4642
|
-
recordNativeToolError(params) {
|
|
4643
|
-
if (!isNonSuccessItemStatus(params.status)) {
|
|
4644
|
-
if (!this.lastNativeToolError) return;
|
|
4645
|
-
if (!this.lastNativeToolError.mutatingAction) {
|
|
4646
|
-
this.lastNativeToolError = void 0;
|
|
4647
|
-
return;
|
|
4648
|
-
}
|
|
4649
|
-
const actionFingerprint = nativeToolActionFingerprint(params.item);
|
|
4650
|
-
if (this.lastNativeToolError.actionFingerprint && actionFingerprint && this.lastNativeToolError.actionFingerprint === actionFingerprint) this.lastNativeToolError = void 0;
|
|
4651
|
-
return;
|
|
4652
|
-
}
|
|
4653
|
-
const error = itemToolError(params.item, params.status, this.toolResultOutputTextByItem);
|
|
4654
|
-
const actionFingerprint = nativeToolActionFingerprint(params.item);
|
|
4655
|
-
this.lastNativeToolError = {
|
|
4656
|
-
toolName: params.name,
|
|
4657
|
-
...params.meta ? { meta: params.meta } : {},
|
|
4658
|
-
...error ? { error } : {},
|
|
4659
|
-
...isMutatingNativeToolItem(params.item) ? { mutatingAction: true } : {},
|
|
4660
|
-
...actionFingerprint ? { actionFingerprint } : {}
|
|
4661
|
-
};
|
|
4662
|
-
}
|
|
4663
|
-
recordToolTrajectoryEvent(params) {
|
|
4664
|
-
if (params.phase === "start") {
|
|
4665
|
-
this.toolTrajectoryCallIds.add(params.item.id);
|
|
4666
|
-
this.toolTrajectoryNamesById.set(params.item.id, params.name);
|
|
4667
|
-
this.toolTrajectoryItemsById.set(params.item.id, params.item);
|
|
4668
|
-
this.options.trajectoryRecorder?.recordEvent("tool.call", {
|
|
4669
|
-
threadId: this.threadId,
|
|
4670
|
-
turnId: this.turnId,
|
|
4671
|
-
itemId: params.item.id,
|
|
4672
|
-
toolCallId: params.item.id,
|
|
4673
|
-
name: params.name,
|
|
4674
|
-
arguments: params.args
|
|
4675
|
-
});
|
|
4676
|
-
return;
|
|
4677
|
-
}
|
|
4678
|
-
this.toolTrajectoryResultIds.add(params.item.id);
|
|
4679
|
-
const toolResult = itemToolResult(params.item).result;
|
|
4680
|
-
const output = itemOutputText(params.item, this.toolResultOutputTextByItem);
|
|
4681
|
-
this.options.trajectoryRecorder?.recordEvent("tool.result", {
|
|
4682
|
-
threadId: this.threadId,
|
|
4683
|
-
turnId: this.turnId,
|
|
4684
|
-
itemId: params.item.id,
|
|
4685
|
-
toolCallId: params.item.id,
|
|
4686
|
-
name: params.name,
|
|
4687
|
-
status: params.status,
|
|
4688
|
-
isError: isNonSuccessItemStatus(params.status),
|
|
4689
|
-
...toolResult ? { result: toolResult } : {},
|
|
4690
|
-
...output ? { output } : {}
|
|
4691
|
-
});
|
|
4692
|
-
}
|
|
4693
|
-
emitDiagnosticToolExecutionEvent(params) {
|
|
4694
|
-
const base = {
|
|
4695
|
-
runId: this.params.runId,
|
|
4696
|
-
sessionId: this.params.sessionId,
|
|
4697
|
-
sessionKey: this.params.sessionKey,
|
|
4698
|
-
toolName: params.name,
|
|
4699
|
-
toolCallId: params.item.id
|
|
4700
|
-
};
|
|
4701
|
-
if (params.phase === "start") {
|
|
4702
|
-
this.diagnosticToolStartedAtByItem.set(params.item.id, Date.now());
|
|
4703
|
-
emitTrustedDiagnosticEvent({
|
|
4704
|
-
type: "tool.execution.started",
|
|
4705
|
-
...base
|
|
4706
|
-
});
|
|
4707
|
-
return;
|
|
4708
|
-
}
|
|
4709
|
-
const startedAt = this.diagnosticToolStartedAtByItem.get(params.item.id);
|
|
4710
|
-
this.diagnosticToolStartedAtByItem.delete(params.item.id);
|
|
4711
|
-
const durationMs = (typeof params.item.durationMs === "number" ? params.item.durationMs : void 0) ?? (startedAt === void 0 ? 0 : Math.max(0, Date.now() - startedAt));
|
|
4712
|
-
const terminalEvent = params.status === "blocked" ? {
|
|
4713
|
-
type: "tool.execution.blocked",
|
|
4714
|
-
reason: "codex_native_tool_blocked",
|
|
4715
|
-
deniedReason: "codex_native_tool_blocked"
|
|
4716
|
-
} : params.status === "failed" ? {
|
|
4717
|
-
type: "tool.execution.error",
|
|
4718
|
-
durationMs,
|
|
4719
|
-
errorCategory: "codex_native_tool_error"
|
|
4720
|
-
} : {
|
|
4721
|
-
type: "tool.execution.completed",
|
|
4722
|
-
durationMs
|
|
4723
|
-
};
|
|
4724
|
-
emitTrustedDiagnosticEvent({
|
|
4725
|
-
...base,
|
|
4726
|
-
...terminalEvent
|
|
4727
|
-
});
|
|
4728
|
-
}
|
|
4729
|
-
emitAfterToolCallObservation(item) {
|
|
4730
|
-
if (!this.shouldEmitAfterToolCallObservation(item)) return;
|
|
4731
|
-
const name = itemName(item);
|
|
4732
|
-
if (!name) return;
|
|
4733
|
-
const status = itemStatus(item);
|
|
4734
|
-
if (status === "running") return;
|
|
4735
|
-
this.afterToolCallObservedItemIds.add(item.id);
|
|
4736
|
-
const result = itemToolResult(item).result;
|
|
4737
|
-
const error = itemToolError(item, status, this.toolResultOutputTextByItem);
|
|
4738
|
-
const startedAt = resolveStartedAtFromDurationMs(item.durationMs);
|
|
4739
|
-
const hookParams = {
|
|
4740
|
-
toolName: name,
|
|
4741
|
-
toolCallId: item.id,
|
|
4742
|
-
runId: this.params.runId,
|
|
4743
|
-
agentId: this.params.agentId,
|
|
4744
|
-
sessionId: this.params.sessionId,
|
|
4745
|
-
sessionKey: this.params.sessionKey,
|
|
4746
|
-
startArgs: itemToolArgs(item) ?? {},
|
|
4747
|
-
...result !== void 0 ? { result } : {},
|
|
4748
|
-
...error ? { error } : {},
|
|
4749
|
-
...startedAt !== void 0 ? { startedAt } : {}
|
|
4750
|
-
};
|
|
4751
|
-
setImmediate(() => {
|
|
4752
|
-
runAgentHarnessAfterToolCallHook(hookParams);
|
|
4753
|
-
});
|
|
4754
|
-
}
|
|
4755
|
-
shouldEmitAfterToolCallObservation(item) {
|
|
4756
|
-
if (!shouldSynthesizeToolProgressForItem(item) || this.afterToolCallObservedItemIds.has(item.id)) return false;
|
|
4757
|
-
if (this.options.nativePostToolUseRelayEnabled && isNativePostToolUseRelayItem(item)) return false;
|
|
4758
|
-
return true;
|
|
4759
|
-
}
|
|
4760
|
-
emitToolResultSummary(item) {
|
|
4761
|
-
if (!item || !this.params.onToolResult || !this.shouldEmitToolResult()) return;
|
|
4762
|
-
const itemId = item.id;
|
|
4763
|
-
if (this.toolResultSummaryItemIds.has(itemId)) return;
|
|
4764
|
-
const toolName = itemName(item);
|
|
4765
|
-
if (!toolName) return;
|
|
4766
|
-
if (!shouldEmitTranscriptToolProgress(toolName, itemToolArgs(item))) return;
|
|
4767
|
-
this.toolResultSummaryItemIds.add(itemId);
|
|
4768
|
-
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
4769
|
-
this.emitToolResultMessage({
|
|
4770
|
-
itemId,
|
|
4771
|
-
text: formatToolSummary(toolName, meta)
|
|
4772
|
-
});
|
|
4773
|
-
}
|
|
4774
|
-
emitToolResultOutput(item) {
|
|
4775
|
-
if (!item || !this.params.onToolResult || !this.shouldEmitToolOutput()) return;
|
|
4776
|
-
const itemId = item.id;
|
|
4777
|
-
if (this.toolResultOutputItemIds.has(itemId)) return;
|
|
4778
|
-
if (this.toolResultOutputStreamedItemIds.has(itemId)) return;
|
|
4779
|
-
const toolName = itemName(item);
|
|
4780
|
-
const output = itemOutputText(item, this.toolResultOutputTextByItem);
|
|
4781
|
-
if (!toolName || !output) return;
|
|
4782
|
-
if (!shouldEmitTranscriptToolProgress(toolName, itemToolArgs(item))) return;
|
|
4783
|
-
this.emitToolResultMessage({
|
|
4784
|
-
itemId,
|
|
4785
|
-
text: formatToolOutput(toolName, itemMeta(item, this.toolProgressDetailMode()), output),
|
|
4786
|
-
finalOutput: true,
|
|
4787
|
-
isError: isNonSuccessItemStatus(itemStatus(item))
|
|
4788
|
-
});
|
|
4789
|
-
}
|
|
4790
|
-
emitToolResultMessage(params) {
|
|
4791
|
-
const text = params.text.trim();
|
|
4792
|
-
if (!text) return;
|
|
4793
|
-
this.toolProgressTexts.add(text);
|
|
4794
|
-
if (params.finalOutput) this.toolResultOutputItemIds.add(params.itemId);
|
|
4795
|
-
try {
|
|
4796
|
-
Promise.resolve(this.params.onToolResult?.({
|
|
4797
|
-
text,
|
|
4798
|
-
...params.isError === true ? { isError: true } : {}
|
|
4799
|
-
})).catch(() => {});
|
|
4800
|
-
} catch {}
|
|
4801
|
-
}
|
|
4802
|
-
shouldEmitToolResult() {
|
|
4803
|
-
return typeof this.params.shouldEmitToolResult === "function" ? this.params.shouldEmitToolResult() : this.params.verboseLevel === "on" || this.params.verboseLevel === "full";
|
|
4804
|
-
}
|
|
4805
|
-
shouldEmitToolOutput() {
|
|
4806
|
-
return typeof this.params.shouldEmitToolOutput === "function" ? this.params.shouldEmitToolOutput() : this.params.verboseLevel === "full";
|
|
4807
|
-
}
|
|
4808
|
-
toolProgressDetailMode() {
|
|
4809
|
-
return resolveCodexToolProgressDetailMode(this.params.toolProgressDetail);
|
|
4810
|
-
}
|
|
4811
|
-
recordToolMeta(item) {
|
|
4812
|
-
if (!item) return;
|
|
4813
|
-
if (isSideEffectingNativeToolItem(item)) this.sideEffectingToolItemIds.add(item.id);
|
|
4814
|
-
else this.sideEffectingToolItemIds.delete(item.id);
|
|
4815
|
-
const toolName = itemName(item);
|
|
4816
|
-
if (!toolName) return;
|
|
4817
|
-
const meta = itemMeta(item, this.toolProgressDetailMode());
|
|
4818
|
-
const existing = this.toolMetas.get(item.id);
|
|
4819
|
-
this.toolMetas.set(item.id, {
|
|
4820
|
-
toolName,
|
|
4821
|
-
...meta ? { meta } : {},
|
|
4822
|
-
...existing?.asyncStarted ? { asyncStarted: true } : {}
|
|
4823
|
-
});
|
|
4824
|
-
}
|
|
4825
|
-
recordNativeToolTranscriptCall(item) {
|
|
4826
|
-
if (!item || !shouldRecordNativeToolTranscript(item)) return;
|
|
4827
|
-
const name = itemName(item);
|
|
4828
|
-
if (!name) return;
|
|
4829
|
-
this.recordToolTranscriptCall({
|
|
4830
|
-
id: item.id,
|
|
4831
|
-
name,
|
|
4832
|
-
arguments: itemToolArgs(item)
|
|
4833
|
-
});
|
|
4834
|
-
}
|
|
4835
|
-
recordNativeToolTranscriptResult(item) {
|
|
4836
|
-
if (!item || !shouldRecordNativeToolTranscript(item)) return;
|
|
4837
|
-
const name = itemName(item);
|
|
4838
|
-
if (!name) return;
|
|
4839
|
-
this.recordToolTranscriptResult({
|
|
4840
|
-
id: item.id,
|
|
4841
|
-
name,
|
|
4842
|
-
text: itemTranscriptResultText(item, this.toolResultOutputTextByItem),
|
|
4843
|
-
isError: isNonSuccessItemStatus(itemStatus(item))
|
|
4844
|
-
});
|
|
4845
|
-
}
|
|
4846
|
-
recordToolTranscriptCall(params) {
|
|
4847
|
-
if (!params.id || !params.name || this.toolTranscriptCallIds.has(params.id)) return;
|
|
4848
|
-
this.toolTranscriptCallIds.add(params.id);
|
|
4849
|
-
this.toolTranscriptNamesById.set(params.id, params.name);
|
|
4850
|
-
this.toolTranscriptArgumentsById.set(params.id, params.arguments);
|
|
4851
|
-
if (!shouldEmitTranscriptToolProgress(params.name, params.arguments)) this.transcriptToolProgressSuppressedIds.add(params.id);
|
|
4852
|
-
else this.transcriptToolProgressSuppressedIds.delete(params.id);
|
|
4853
|
-
this.emitTranscriptToolCallProgress(params);
|
|
4854
|
-
this.toolTranscriptMessages.push(attachCodexMirrorIdentity(this.createToolCallMessage(params), `${this.turnId}:tool:${params.id}:call`));
|
|
4855
|
-
}
|
|
4856
|
-
recordToolTranscriptResult(params) {
|
|
4857
|
-
if (!params.id || !params.name || this.toolTranscriptResultIds.has(params.id)) return;
|
|
4858
|
-
this.toolTranscriptResultIds.add(params.id);
|
|
4859
|
-
this.emitTranscriptToolResultProgress(params);
|
|
4860
|
-
this.toolTranscriptMessages.push(attachCodexMirrorIdentity(this.createToolResultMessage(params), `${this.turnId}:tool:${params.id}:result`));
|
|
4861
|
-
}
|
|
4862
|
-
synthesizeMissingToolResults(params) {
|
|
4863
|
-
if (!params.synthesize) return;
|
|
4864
|
-
const missingTranscriptIds = [...this.toolTranscriptCallIds].filter((id) => !this.toolTranscriptResultIds.has(id));
|
|
4865
|
-
const missingTrajectoryIds = [...this.toolTrajectoryCallIds].filter((id) => !this.toolTrajectoryResultIds.has(id));
|
|
4866
|
-
if (missingTranscriptIds.length === 0 && missingTrajectoryIds.length === 0) return;
|
|
4867
|
-
for (const id of missingTranscriptIds) {
|
|
4868
|
-
const name = this.toolTranscriptNamesById.get(id) ?? this.toolTrajectoryNamesById.get(id);
|
|
4869
|
-
if (!name) continue;
|
|
4870
|
-
this.recordToolTranscriptResult({
|
|
4871
|
-
id,
|
|
4872
|
-
name,
|
|
4873
|
-
text: formatMissingToolResultError({
|
|
4874
|
-
id,
|
|
4875
|
-
name
|
|
4876
|
-
}),
|
|
4877
|
-
isError: true
|
|
4878
|
-
});
|
|
4879
|
-
}
|
|
4880
|
-
for (const id of missingTrajectoryIds) {
|
|
4881
|
-
const name = this.toolTrajectoryNamesById.get(id) ?? this.toolTranscriptNamesById.get(id);
|
|
4882
|
-
if (!name) continue;
|
|
4883
|
-
this.toolTrajectoryResultIds.add(id);
|
|
4884
|
-
const text = formatMissingToolResultError({
|
|
4885
|
-
id,
|
|
4886
|
-
name
|
|
4887
|
-
});
|
|
4888
|
-
this.options.trajectoryRecorder?.recordEvent("tool.result", {
|
|
4889
|
-
threadId: this.threadId,
|
|
4890
|
-
turnId: this.turnId,
|
|
4891
|
-
itemId: id,
|
|
4892
|
-
toolCallId: id,
|
|
4893
|
-
name,
|
|
4894
|
-
status: "failed",
|
|
4895
|
-
isError: true,
|
|
4896
|
-
result: {
|
|
4897
|
-
status: "failed",
|
|
4898
|
-
reason: "missing_tool_result"
|
|
4899
|
-
},
|
|
4900
|
-
output: text
|
|
4901
|
-
});
|
|
4902
|
-
}
|
|
4903
|
-
if (!params.recordPromptError) {
|
|
4904
|
-
const firstMissingId = missingTranscriptIds.find((id) => {
|
|
4905
|
-
const name = this.toolTranscriptNamesById.get(id) ?? this.toolTrajectoryNamesById.get(id);
|
|
4906
|
-
return Boolean(name);
|
|
4907
|
-
}) ?? missingTrajectoryIds.find((id) => {
|
|
4908
|
-
const name = this.toolTrajectoryNamesById.get(id) ?? this.toolTranscriptNamesById.get(id);
|
|
4909
|
-
return Boolean(name);
|
|
4910
|
-
});
|
|
4911
|
-
if (firstMissingId) {
|
|
4912
|
-
const name = this.toolTranscriptNamesById.get(firstMissingId) ?? this.toolTrajectoryNamesById.get(firstMissingId);
|
|
4913
|
-
if (name) {
|
|
4914
|
-
const item = this.toolTrajectoryItemsById.get(firstMissingId);
|
|
4915
|
-
const meta = item ? itemMeta(item, this.toolProgressDetailMode()) : this.toolMetas.get(firstMissingId)?.meta;
|
|
4916
|
-
const actionFingerprint = item ? nativeToolActionFingerprint(item) : void 0;
|
|
4917
|
-
this.lastNativeToolError = {
|
|
4918
|
-
toolName: name,
|
|
4919
|
-
...meta ? { meta } : {},
|
|
4920
|
-
error: formatMissingToolResultError({
|
|
4921
|
-
id: firstMissingId,
|
|
4922
|
-
name
|
|
4923
|
-
}),
|
|
4924
|
-
...item && isMutatingNativeToolItem(item) ? { mutatingAction: true } : {},
|
|
4925
|
-
...actionFingerprint ? { actionFingerprint } : {}
|
|
4926
|
-
};
|
|
4927
|
-
}
|
|
4928
|
-
}
|
|
4929
|
-
return;
|
|
4930
|
-
}
|
|
4931
|
-
const missingCount = new Set([...missingTranscriptIds, ...missingTrajectoryIds]).size;
|
|
4932
|
-
this.synthesizedMissingToolResultError = missingCount === 1 ? MISSING_TOOL_RESULT_ERROR : `${MISSING_TOOL_RESULT_ERROR} missingToolResultCount=${missingCount}`;
|
|
4933
|
-
this.promptErrorSource = this.promptErrorSource ?? "prompt";
|
|
4934
|
-
}
|
|
4935
|
-
emitTranscriptToolCallProgress(params) {
|
|
4936
|
-
if (!shouldEmitTranscriptToolProgress(params.name, params.arguments)) return;
|
|
4937
|
-
this.transcriptToolProgressCallIds.add(params.id);
|
|
4938
|
-
const args = normalizeToolTranscriptArguments(params.arguments);
|
|
4939
|
-
const meta = inferToolMetaFromArgs(params.name, args, { detailMode: this.toolProgressDetailMode() });
|
|
4940
|
-
if (!this.params.onToolResult || !this.shouldEmitToolResult() || this.toolResultSummaryItemIds.has(params.id) || this.toolResultOutputStreamedItemIds.has(params.id)) return;
|
|
4941
|
-
this.toolResultSummaryItemIds.add(params.id);
|
|
4942
|
-
this.emitToolResultMessage({
|
|
4943
|
-
itemId: params.id,
|
|
4944
|
-
text: formatToolSummary(params.name, meta)
|
|
4945
|
-
});
|
|
4946
|
-
}
|
|
4947
|
-
emitTranscriptToolResultProgress(params) {
|
|
4948
|
-
if (this.transcriptToolProgressSuppressedIds.has(params.id) || !shouldEmitTranscriptToolProgress(params.name, this.toolTranscriptArgumentsById.get(params.id))) return;
|
|
4949
|
-
if (!this.transcriptToolProgressCallIds.has(params.id)) this.emitTranscriptToolCallProgress({
|
|
4950
|
-
id: params.id,
|
|
4951
|
-
name: params.name,
|
|
4952
|
-
arguments: {}
|
|
4953
|
-
});
|
|
4954
|
-
if (!this.params.onToolResult || !this.shouldEmitToolOutput() || this.toolResultOutputItemIds.has(params.id) || this.toolResultOutputStreamedItemIds.has(params.id)) return;
|
|
4955
|
-
const text = params.text?.trim();
|
|
4956
|
-
if (!text) return;
|
|
4957
|
-
this.emitToolResultMessage({
|
|
4958
|
-
itemId: params.id,
|
|
4959
|
-
text: formatToolOutput(params.name, void 0, text),
|
|
4960
|
-
finalOutput: true,
|
|
4961
|
-
isError: params.isError
|
|
4962
|
-
});
|
|
4963
|
-
}
|
|
4964
|
-
formatCodexErrorMessage(params) {
|
|
4965
|
-
const error = isJsonObject(params.error) ? params.error : void 0;
|
|
4966
|
-
return formatCodexUsageLimitErrorMessage({
|
|
4967
|
-
message: error ? readString$5(error, "message") : void 0,
|
|
4968
|
-
codexErrorInfo: error?.codexErrorInfo,
|
|
4969
|
-
rateLimits: this.latestRateLimits ?? readRecentCodexRateLimits()
|
|
4970
|
-
}) ?? readCodexErrorNotificationMessage(params);
|
|
4971
|
-
}
|
|
4972
|
-
emitAgentEvent(event) {
|
|
4973
|
-
try {
|
|
4974
|
-
emitAgentEvent({
|
|
4975
|
-
runId: this.params.runId,
|
|
4976
|
-
stream: event.stream,
|
|
4977
|
-
data: event.data,
|
|
4978
|
-
...this.params.sessionKey ? { sessionKey: this.params.sessionKey } : {}
|
|
4979
|
-
});
|
|
4980
|
-
} catch (error) {
|
|
4981
|
-
embeddedAgentLog.debug("codex app-server global agent event emit failed", { error });
|
|
4982
|
-
}
|
|
4983
|
-
try {
|
|
4984
|
-
const maybePromise = this.params.onAgentEvent?.(event);
|
|
4985
|
-
Promise.resolve(maybePromise).catch((error) => {
|
|
4986
|
-
embeddedAgentLog.debug("codex app-server agent event handler rejected", { error });
|
|
4987
|
-
});
|
|
4988
|
-
} catch (error) {
|
|
4989
|
-
embeddedAgentLog.debug("codex app-server agent event handler threw", { error });
|
|
4990
|
-
}
|
|
4991
|
-
}
|
|
4992
|
-
collectAssistantTexts() {
|
|
4993
|
-
const finalText = this.resolveFinalAssistantText();
|
|
4994
|
-
return finalText ? [finalText] : [];
|
|
4995
|
-
}
|
|
4996
|
-
hasAssistantItemTextForSynthesis() {
|
|
4997
|
-
for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
|
|
4998
|
-
const itemId = this.assistantItemOrder[i];
|
|
4999
|
-
if (!itemId) continue;
|
|
5000
|
-
if (this.assistantPhaseByItem.get(itemId) === "commentary") continue;
|
|
5001
|
-
const text = this.assistantTextByItem.get(itemId);
|
|
5002
|
-
if (text && text.length > 0) return true;
|
|
5003
|
-
}
|
|
5004
|
-
return false;
|
|
5005
|
-
}
|
|
5006
|
-
resolveFinalAssistantText() {
|
|
5007
|
-
return this.resolveFinalAssistantTextItem()?.text;
|
|
5008
|
-
}
|
|
5009
|
-
resolveFinalAssistantTextItem() {
|
|
5010
|
-
for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
|
|
5011
|
-
const itemId = this.assistantItemOrder[i];
|
|
5012
|
-
if (!itemId) continue;
|
|
5013
|
-
const text = this.assistantTextByItem.get(itemId)?.trim();
|
|
5014
|
-
if (this.assistantPhaseByItem.get(itemId) === "commentary") continue;
|
|
5015
|
-
if (text && !this.toolProgressTexts.has(text)) return {
|
|
5016
|
-
itemId,
|
|
5017
|
-
text
|
|
5018
|
-
};
|
|
5019
|
-
}
|
|
5020
|
-
}
|
|
5021
|
-
rememberAssistantItem(itemId) {
|
|
5022
|
-
if (!itemId || this.assistantItemOrder.includes(itemId)) return;
|
|
5023
|
-
this.assistantItemOrder.push(itemId);
|
|
5024
|
-
}
|
|
5025
|
-
createCurrentAttemptAssistantMessage() {
|
|
5026
|
-
for (let i = this.assistantItemOrder.length - 1; i >= 0; i -= 1) {
|
|
5027
|
-
const itemId = this.assistantItemOrder[i];
|
|
5028
|
-
if (!itemId || this.isCommentaryAssistantItem(itemId) || !this.assistantTextByItem.has(itemId)) continue;
|
|
5029
|
-
const text = this.assistantTextByItem.get(itemId) ?? "";
|
|
5030
|
-
const normalizedText = text.trim();
|
|
5031
|
-
if (normalizedText && this.toolProgressTexts.has(normalizedText)) continue;
|
|
5032
|
-
return this.createAssistantMessage(text);
|
|
5033
|
-
}
|
|
5034
|
-
}
|
|
5035
|
-
async readMirroredSessionMessages() {
|
|
5036
|
-
return await readCodexMirroredSessionHistoryMessages({
|
|
5037
|
-
agentId: this.params.agentId,
|
|
5038
|
-
sessionFile: this.params.sessionFile,
|
|
5039
|
-
sessionId: this.params.sessionId,
|
|
5040
|
-
sessionKey: this.params.sessionKey
|
|
5041
|
-
}) ?? [];
|
|
5042
|
-
}
|
|
5043
|
-
createAssistantMessage(text) {
|
|
5044
|
-
const attribution = resolveCodexLocalRuntimeAttribution(this.params);
|
|
5045
|
-
const usage = this.tokenUsage ? {
|
|
5046
|
-
input: this.tokenUsage.input ?? 0,
|
|
5047
|
-
output: this.tokenUsage.output ?? 0,
|
|
5048
|
-
cacheRead: this.tokenUsage.cacheRead ?? 0,
|
|
5049
|
-
cacheWrite: this.tokenUsage.cacheWrite ?? 0,
|
|
5050
|
-
totalTokens: this.tokenUsage.total ?? (this.tokenUsage.input ?? 0) + (this.tokenUsage.output ?? 0) + (this.tokenUsage.cacheRead ?? 0) + (this.tokenUsage.cacheWrite ?? 0),
|
|
5051
|
-
cost: ZERO_USAGE.cost
|
|
5052
|
-
} : ZERO_USAGE;
|
|
5053
|
-
return {
|
|
5054
|
-
role: "assistant",
|
|
5055
|
-
content: [{
|
|
5056
|
-
type: "text",
|
|
5057
|
-
text
|
|
5058
|
-
}],
|
|
5059
|
-
api: attribution.api ?? "openai-chatgpt-responses",
|
|
5060
|
-
provider: attribution.provider,
|
|
5061
|
-
model: this.params.modelId,
|
|
5062
|
-
usage,
|
|
5063
|
-
stopReason: this.aborted ? "aborted" : this.promptError ? "error" : "stop",
|
|
5064
|
-
errorMessage: this.promptError ? formatErrorMessage(this.promptError) : void 0,
|
|
5065
|
-
timestamp: Date.now()
|
|
5066
|
-
};
|
|
5067
|
-
}
|
|
5068
|
-
createAssistantMirrorMessage(title, text) {
|
|
5069
|
-
const attribution = resolveCodexLocalRuntimeAttribution(this.params);
|
|
5070
|
-
return {
|
|
5071
|
-
role: "assistant",
|
|
5072
|
-
content: [{
|
|
5073
|
-
type: "text",
|
|
5074
|
-
text: `${title}:\n${text}`
|
|
5075
|
-
}],
|
|
5076
|
-
api: attribution.api ?? "openai-chatgpt-responses",
|
|
5077
|
-
provider: attribution.provider,
|
|
5078
|
-
model: this.params.modelId,
|
|
5079
|
-
usage: ZERO_USAGE,
|
|
5080
|
-
stopReason: "stop",
|
|
5081
|
-
timestamp: Date.now()
|
|
5082
|
-
};
|
|
5083
|
-
}
|
|
5084
|
-
createToolCallMessage(params) {
|
|
5085
|
-
const args = normalizeToolTranscriptArguments(params.arguments);
|
|
5086
|
-
const attribution = resolveCodexLocalRuntimeAttribution(this.params);
|
|
5087
|
-
return {
|
|
5088
|
-
role: "assistant",
|
|
5089
|
-
content: [{
|
|
5090
|
-
type: "toolCall",
|
|
5091
|
-
id: params.id,
|
|
5092
|
-
name: params.name,
|
|
5093
|
-
arguments: args,
|
|
5094
|
-
input: args
|
|
5095
|
-
}],
|
|
5096
|
-
api: attribution.api ?? "openai-chatgpt-responses",
|
|
5097
|
-
provider: attribution.provider,
|
|
5098
|
-
model: this.params.modelId,
|
|
5099
|
-
usage: ZERO_USAGE,
|
|
5100
|
-
stopReason: "toolUse",
|
|
5101
|
-
timestamp: Date.now()
|
|
5102
|
-
};
|
|
5103
|
-
}
|
|
5104
|
-
createToolResultMessage(params) {
|
|
5105
|
-
const text = truncateToolTranscriptText(params.text?.trim() || toolResultStatusText(params));
|
|
5106
|
-
return {
|
|
5107
|
-
role: "toolResult",
|
|
5108
|
-
toolCallId: params.id,
|
|
5109
|
-
toolName: params.name,
|
|
5110
|
-
isError: params.isError,
|
|
5111
|
-
content: [{
|
|
5112
|
-
type: "toolResult",
|
|
5113
|
-
id: params.id,
|
|
5114
|
-
name: params.name,
|
|
5115
|
-
toolName: params.name,
|
|
5116
|
-
toolCallId: params.id,
|
|
5117
|
-
toolUseId: params.id,
|
|
5118
|
-
tool_use_id: params.id,
|
|
5119
|
-
content: text,
|
|
5120
|
-
text
|
|
5121
|
-
}],
|
|
5122
|
-
timestamp: Date.now()
|
|
5123
|
-
};
|
|
5124
|
-
}
|
|
5125
|
-
isNotificationForTurn(params) {
|
|
5126
|
-
const threadId = readCodexNotificationThreadId(params);
|
|
5127
|
-
const turnId = readNotificationTurnId(params);
|
|
5128
|
-
return threadId === this.threadId && turnId === this.turnId;
|
|
5129
|
-
}
|
|
5130
|
-
isHookNotificationForCurrentThread(params) {
|
|
5131
|
-
const threadId = readString$5(params, "threadId");
|
|
5132
|
-
const turnId = params.turnId;
|
|
5133
|
-
return threadId === this.threadId && (turnId === this.turnId || turnId === null);
|
|
5134
|
-
}
|
|
5135
|
-
};
|
|
5136
|
-
function isHookNotificationMethod(method) {
|
|
5137
|
-
return method === "hook/started" || method === "hook/completed";
|
|
5138
|
-
}
|
|
5139
|
-
function readNotificationTurnId(record) {
|
|
5140
|
-
return readCodexNotificationTurnId(record);
|
|
5141
|
-
}
|
|
5142
|
-
function readString$5(record, key) {
|
|
5143
|
-
const value = record[key];
|
|
5144
|
-
return typeof value === "string" ? value : void 0;
|
|
5145
|
-
}
|
|
5146
|
-
function estimateBase64DecodedBytes(base64) {
|
|
5147
|
-
let nonWhitespaceLength = 0;
|
|
5148
|
-
let previousCode = -1;
|
|
5149
|
-
let lastCode = -1;
|
|
5150
|
-
for (let i = 0; i < base64.length; i += 1) {
|
|
5151
|
-
const code = base64.charCodeAt(i);
|
|
5152
|
-
if (isBase64WhitespaceCode(code)) continue;
|
|
5153
|
-
nonWhitespaceLength += 1;
|
|
5154
|
-
previousCode = lastCode;
|
|
5155
|
-
lastCode = code;
|
|
5156
|
-
}
|
|
5157
|
-
if (nonWhitespaceLength === 0) return;
|
|
5158
|
-
const equalsCode = "=".charCodeAt(0);
|
|
5159
|
-
const padding = lastCode === equalsCode ? previousCode === equalsCode ? 2 : 1 : 0;
|
|
5160
|
-
return Math.max(0, Math.floor(nonWhitespaceLength * 3 / 4) - padding);
|
|
5161
|
-
}
|
|
5162
|
-
function isBase64WhitespaceCode(code) {
|
|
5163
|
-
return code === 32 || code === 9 || code === 10 || code === 13;
|
|
5164
|
-
}
|
|
5165
|
-
function resolveGeneratedImageMaxBytes(config) {
|
|
5166
|
-
const configured = config?.agents?.defaults?.mediaMaxMb;
|
|
5167
|
-
if (typeof configured === "number" && Number.isFinite(configured) && configured > 0) return Math.floor(configured * BYTES_PER_MB);
|
|
5168
|
-
return DEFAULT_GENERATED_IMAGE_MAX_BYTES;
|
|
5169
|
-
}
|
|
5170
|
-
function normalizeNonEmptyString(value) {
|
|
5171
|
-
if (typeof value !== "string") return;
|
|
5172
|
-
return value.trim() || void 0;
|
|
5173
|
-
}
|
|
5174
|
-
function readNonEmptyString(record, key) {
|
|
5175
|
-
return normalizeNonEmptyString(record[key]);
|
|
5176
|
-
}
|
|
5177
|
-
function readNonEmptyStringArray(record, key) {
|
|
5178
|
-
const value = record[key];
|
|
5179
|
-
if (!Array.isArray(value)) return [];
|
|
5180
|
-
const entries = [];
|
|
5181
|
-
for (const entry of value) {
|
|
5182
|
-
const normalized = normalizeNonEmptyString(entry);
|
|
5183
|
-
if (normalized) entries.push(normalized);
|
|
5184
|
-
}
|
|
5185
|
-
return entries;
|
|
5186
|
-
}
|
|
5187
|
-
function readNullableString$1(record, key) {
|
|
5188
|
-
const value = record[key];
|
|
5189
|
-
if (value === null) return null;
|
|
5190
|
-
return typeof value === "string" ? value : void 0;
|
|
5191
|
-
}
|
|
5192
|
-
function readNumber$1(record, key) {
|
|
5193
|
-
const value = record[key];
|
|
5194
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
5195
|
-
}
|
|
5196
|
-
function resolveStartedAtFromDurationMs(durationMs) {
|
|
5197
|
-
if (typeof durationMs !== "number" || !Number.isFinite(durationMs)) return;
|
|
5198
|
-
return asDateTimestampMs(Date.now() - Math.max(0, durationMs));
|
|
5199
|
-
}
|
|
5200
|
-
function readNonNegativeInteger(record, key) {
|
|
5201
|
-
const value = readNumber$1(record, key);
|
|
5202
|
-
return value !== void 0 && Number.isInteger(value) && value >= 0 ? value : void 0;
|
|
5203
|
-
}
|
|
5204
|
-
function readBoolean(record, key) {
|
|
5205
|
-
const value = record[key];
|
|
5206
|
-
return typeof value === "boolean" ? value : void 0;
|
|
5207
|
-
}
|
|
5208
|
-
function readBooleanAlias(record, keys) {
|
|
5209
|
-
for (const key of keys) {
|
|
5210
|
-
const value = readBoolean(record, key);
|
|
5211
|
-
if (value !== void 0) return value;
|
|
5212
|
-
}
|
|
5213
|
-
}
|
|
5214
|
-
function readCodexErrorNotificationMessage(record) {
|
|
5215
|
-
const error = record.error;
|
|
5216
|
-
if (isJsonObject(error)) return readString$5(error, "message") ?? readString$5(error, "error");
|
|
5217
|
-
return readString$5(record, "message");
|
|
5218
|
-
}
|
|
5219
|
-
function readHookOutputEntries(value) {
|
|
5220
|
-
if (!Array.isArray(value)) return [];
|
|
5221
|
-
return value.flatMap((entry) => {
|
|
5222
|
-
if (!isJsonObject(entry)) return [];
|
|
5223
|
-
const text = readString$5(entry, "text");
|
|
5224
|
-
if (!text) return [];
|
|
5225
|
-
const kind = readString$5(entry, "kind");
|
|
5226
|
-
return [{
|
|
5227
|
-
...kind ? { kind } : {},
|
|
5228
|
-
text
|
|
5229
|
-
}];
|
|
5230
|
-
});
|
|
5231
|
-
}
|
|
5232
|
-
function readFirstJsonObject(record, keys) {
|
|
5233
|
-
for (const key of keys) {
|
|
5234
|
-
const value = record[key];
|
|
5235
|
-
if (isJsonObject(value)) return value;
|
|
5236
|
-
}
|
|
5237
|
-
}
|
|
5238
|
-
function readNumberAlias(record, keys) {
|
|
5239
|
-
for (const key of keys) {
|
|
5240
|
-
const value = readNumber$1(record, key);
|
|
5241
|
-
if (value !== void 0) return value;
|
|
5242
|
-
}
|
|
5243
|
-
}
|
|
5244
|
-
function normalizeCodexTokenUsage(record) {
|
|
5245
|
-
const promptTotalInput = readNumberAlias(record, CODEX_PROMPT_TOTAL_INPUT_KEYS);
|
|
5246
|
-
const cacheRead = readNumberAlias(record, [
|
|
5247
|
-
"cachedInputTokens",
|
|
5248
|
-
"cached_input_tokens",
|
|
5249
|
-
"cacheRead",
|
|
5250
|
-
"cache_read",
|
|
5251
|
-
"cache_read_input_tokens",
|
|
5252
|
-
"cached_tokens"
|
|
5253
|
-
]);
|
|
5254
|
-
return normalizeUsage({
|
|
5255
|
-
input: promptTotalInput !== void 0 && cacheRead !== void 0 ? Math.max(0, promptTotalInput - cacheRead) : promptTotalInput ?? readNumber$1(record, "input"),
|
|
5256
|
-
output: readNumberAlias(record, [
|
|
5257
|
-
"outputTokens",
|
|
5258
|
-
"output_tokens",
|
|
5259
|
-
"output"
|
|
5260
|
-
]),
|
|
5261
|
-
cacheRead,
|
|
5262
|
-
cacheWrite: readNumberAlias(record, [
|
|
5263
|
-
"cacheWrite",
|
|
5264
|
-
"cache_write",
|
|
5265
|
-
"cacheCreationInputTokens",
|
|
5266
|
-
"cache_creation_input_tokens"
|
|
5267
|
-
]),
|
|
5268
|
-
total: readNumberAlias(record, [
|
|
5269
|
-
"totalTokens",
|
|
5270
|
-
"total_tokens",
|
|
5271
|
-
"total"
|
|
5272
|
-
])
|
|
5273
|
-
});
|
|
5274
|
-
}
|
|
5275
|
-
function splitPlanText(text) {
|
|
5276
|
-
return text.split(/\r?\n/).map((line) => line.trim().replace(/^[-*]\s+/, "")).filter((line) => line.length > 0);
|
|
5277
|
-
}
|
|
5278
|
-
function collectTextValues(map) {
|
|
5279
|
-
return [...map.values()].filter((text) => text.trim().length > 0);
|
|
5280
|
-
}
|
|
5281
|
-
function collectReasoningTextValues(groups, itemOrder) {
|
|
5282
|
-
return [...groups.values()].toSorted((left, right) => {
|
|
5283
|
-
const itemDelta = (itemOrder.get(left.itemId) ?? Number.MAX_SAFE_INTEGER) - (itemOrder.get(right.itemId) ?? Number.MAX_SAFE_INTEGER);
|
|
5284
|
-
if (itemDelta !== 0) return itemDelta;
|
|
5285
|
-
const methodDelta = reasoningMethodOrder(left.method) - reasoningMethodOrder(right.method);
|
|
5286
|
-
return methodDelta !== 0 ? methodDelta : left.index - right.index;
|
|
5287
|
-
}).map((group) => group.text).filter((text) => text.trim().length > 0);
|
|
5288
|
-
}
|
|
5289
|
-
function reasoningMethodOrder(method) {
|
|
5290
|
-
return method === "item/reasoning/summaryTextDelta" ? 0 : 1;
|
|
5291
|
-
}
|
|
5292
|
-
function extractRawAssistantText(item) {
|
|
5293
|
-
return (Array.isArray(item.content) ? item.content : []).flatMap((entry) => {
|
|
5294
|
-
if (!isJsonObject(entry)) return [];
|
|
5295
|
-
const type = readString$5(entry, "type");
|
|
5296
|
-
if (type !== "output_text" && type !== "text") return [];
|
|
5297
|
-
const value = readString$5(entry, "text");
|
|
5298
|
-
return value ? [value] : [];
|
|
5299
|
-
}).join("").trim() || void 0;
|
|
5300
|
-
}
|
|
5301
|
-
function itemKind(item) {
|
|
5302
|
-
switch (item.type) {
|
|
5303
|
-
case "dynamicToolCall":
|
|
5304
|
-
case "mcpToolCall": return "tool";
|
|
5305
|
-
case "commandExecution": return "command";
|
|
5306
|
-
case "fileChange": return "patch";
|
|
5307
|
-
case "webSearch": return "search";
|
|
5308
|
-
case "reasoning":
|
|
5309
|
-
case "contextCompaction": return "analysis";
|
|
5310
|
-
default: return;
|
|
5311
|
-
}
|
|
5312
|
-
}
|
|
5313
|
-
function itemTitle(item) {
|
|
5314
|
-
switch (item.type) {
|
|
5315
|
-
case "commandExecution": return "Command";
|
|
5316
|
-
case "fileChange": return "File change";
|
|
5317
|
-
case "mcpToolCall": return "MCP tool";
|
|
5318
|
-
case "dynamicToolCall": return "Tool";
|
|
5319
|
-
case "webSearch": return "Web search";
|
|
5320
|
-
case "contextCompaction": return "Context compaction";
|
|
5321
|
-
case "reasoning": return "Reasoning";
|
|
5322
|
-
default: return item.type;
|
|
5323
|
-
}
|
|
5324
|
-
}
|
|
5325
|
-
function itemStatus(item) {
|
|
5326
|
-
const status = readItemString(item, "status");
|
|
5327
|
-
if (status === "failed") return "failed";
|
|
5328
|
-
if (status === "declined") return "blocked";
|
|
5329
|
-
if (status === "inProgress" || status === "running") return "running";
|
|
5330
|
-
return "completed";
|
|
5331
|
-
}
|
|
5332
|
-
function formatMissingToolResultError(params) {
|
|
5333
|
-
return `${MISSING_TOOL_RESULT_ERROR} toolCallId=${params.id}; toolName=${params.name}`;
|
|
5334
|
-
}
|
|
5335
|
-
function isNonSuccessItemStatus(status) {
|
|
5336
|
-
return status === "failed" || status === "blocked";
|
|
5337
|
-
}
|
|
5338
|
-
function itemName(item) {
|
|
5339
|
-
if (item.type === "dynamicToolCall" && typeof item.tool === "string") return item.tool;
|
|
5340
|
-
if (item.type === "mcpToolCall" && typeof item.tool === "string") {
|
|
5341
|
-
const server = typeof item.server === "string" ? item.server : void 0;
|
|
5342
|
-
return server ? `${server}.${item.tool}` : item.tool;
|
|
5343
|
-
}
|
|
5344
|
-
if (item.type === "commandExecution") return "bash";
|
|
5345
|
-
if (item.type === "fileChange") return "apply_patch";
|
|
5346
|
-
if (item.type === "webSearch") return "web_search";
|
|
5347
|
-
}
|
|
5348
|
-
function isSideEffectingNativeToolItem(item) {
|
|
5349
|
-
return itemStatus(item) !== "blocked" && (isMutatingNativeToolItem(item) || item.type === "mcpToolCall");
|
|
5350
|
-
}
|
|
5351
|
-
function shouldSynthesizeToolProgressForItem(item) {
|
|
5352
|
-
switch (item.type) {
|
|
5353
|
-
case "commandExecution":
|
|
5354
|
-
case "fileChange":
|
|
5355
|
-
case "webSearch":
|
|
5356
|
-
case "mcpToolCall": return true;
|
|
5357
|
-
default: return false;
|
|
5358
|
-
}
|
|
5359
|
-
}
|
|
5360
|
-
function shouldRecordNativeToolTranscript(item) {
|
|
5361
|
-
return shouldSynthesizeToolProgressForItem(item) && item.type !== "webSearch";
|
|
5362
|
-
}
|
|
5363
|
-
function isMutatingNativeToolItem(item) {
|
|
5364
|
-
if (item.type === "commandExecution") return true;
|
|
5365
|
-
return item.type === "fileChange" || item.type === "collabAgentToolCall" || item.type === "imageGeneration";
|
|
5366
|
-
}
|
|
5367
|
-
function shouldClearTerminalPresentationForNativeItem(item) {
|
|
5368
|
-
switch (item.type) {
|
|
5369
|
-
case "collabAgentToolCall":
|
|
5370
|
-
case "commandExecution":
|
|
5371
|
-
case "fileChange":
|
|
5372
|
-
case "imageGeneration":
|
|
5373
|
-
case "imageView":
|
|
5374
|
-
case "mcpToolCall":
|
|
5375
|
-
case "webSearch": return true;
|
|
5376
|
-
default: return false;
|
|
5377
|
-
}
|
|
5378
|
-
}
|
|
5379
|
-
function nativeToolActionFingerprint(item) {
|
|
5380
|
-
if (item.type === "commandExecution" && typeof item.command === "string") return JSON.stringify({
|
|
5381
|
-
type: item.type,
|
|
5382
|
-
command: item.command,
|
|
5383
|
-
cwd: typeof item.cwd === "string" ? item.cwd : ""
|
|
5384
|
-
});
|
|
5385
|
-
if (item.type === "fileChange") return JSON.stringify({
|
|
5386
|
-
type: item.type,
|
|
5387
|
-
changes: itemFileChanges(item)
|
|
5388
|
-
});
|
|
5389
|
-
}
|
|
5390
|
-
function isNativePostToolUseRelayItem(item) {
|
|
5391
|
-
switch (item.type) {
|
|
5392
|
-
case "commandExecution":
|
|
5393
|
-
case "fileChange":
|
|
5394
|
-
case "mcpToolCall": return true;
|
|
5395
|
-
default: return false;
|
|
5396
|
-
}
|
|
5397
|
-
}
|
|
5398
|
-
function shouldSuppressChannelProgressForItem(item) {
|
|
5399
|
-
if (shouldSynthesizeToolProgressForItem(item)) return true;
|
|
5400
|
-
return item.type === "dynamicToolCall";
|
|
5401
|
-
}
|
|
5402
|
-
function itemToolArgs(item) {
|
|
5403
|
-
if (item.type === "commandExecution") return sanitizeCodexAgentEventRecord({
|
|
5404
|
-
command: item.command,
|
|
5405
|
-
...typeof item.cwd === "string" ? { cwd: item.cwd } : {}
|
|
5406
|
-
});
|
|
5407
|
-
if (item.type === "fileChange") return sanitizeCodexAgentEventRecord({ changes: itemFileChanges(item) });
|
|
5408
|
-
if (item.type === "webSearch") return webSearchToolArgs(item);
|
|
5409
|
-
if (item.type === "dynamicToolCall" || item.type === "mcpToolCall") return sanitizeCodexToolArguments(item.arguments);
|
|
5410
|
-
}
|
|
5411
|
-
function webSearchToolArgs(item) {
|
|
5412
|
-
const action = isJsonObject(item.action) ? item.action : void 0;
|
|
5413
|
-
const actionType = action ? readNonEmptyString(action, "type") : void 0;
|
|
5414
|
-
const queries = action && actionType === "search" ? readNonEmptyStringArray(action, "queries") : [];
|
|
5415
|
-
const query = normalizeNonEmptyString(item.query) ?? (action && actionType === "search" ? readNonEmptyString(action, "query") : void 0) ?? queries[0];
|
|
5416
|
-
const url = action ? readNonEmptyString(action, "url") : void 0;
|
|
5417
|
-
const pattern = action ? readNonEmptyString(action, "pattern") : void 0;
|
|
5418
|
-
const args = {};
|
|
5419
|
-
if (query) args.query = query;
|
|
5420
|
-
if (queries.length > 0) args.queries = queries;
|
|
5421
|
-
if (actionType && actionType !== "search") args.action = actionType;
|
|
5422
|
-
if (url) args.url = url;
|
|
5423
|
-
if (pattern) args.pattern = pattern;
|
|
5424
|
-
if (!query && !url && !pattern) args.queryUnavailable = true;
|
|
5425
|
-
return sanitizeCodexAgentEventRecord(args);
|
|
5426
|
-
}
|
|
5427
|
-
function itemToolResult(item) {
|
|
5428
|
-
if (item.type === "commandExecution") return { result: sanitizeCodexAgentEventRecord({
|
|
5429
|
-
status: item.status,
|
|
5430
|
-
exitCode: item.exitCode,
|
|
5431
|
-
durationMs: item.durationMs
|
|
5432
|
-
}) };
|
|
5433
|
-
if (item.type === "fileChange") return { result: sanitizeCodexAgentEventRecord({
|
|
5434
|
-
status: item.status,
|
|
5435
|
-
changes: itemFileChanges(item)
|
|
5436
|
-
}) };
|
|
5437
|
-
if (item.type === "mcpToolCall") return { result: sanitizeCodexAgentEventRecord({
|
|
5438
|
-
status: item.status,
|
|
5439
|
-
durationMs: item.durationMs,
|
|
5440
|
-
...item.error ? { error: item.error } : {},
|
|
5441
|
-
...item.result ? { result: item.result } : {}
|
|
5442
|
-
}) };
|
|
5443
|
-
if (item.type === "webSearch") return { result: webSearchToolResult(item) };
|
|
5444
|
-
return {};
|
|
5445
|
-
}
|
|
5446
|
-
function webSearchToolResult(item) {
|
|
5447
|
-
return sanitizeCodexAgentEventRecord({
|
|
5448
|
-
status: itemStatus(item),
|
|
5449
|
-
...typeof item.durationMs === "number" ? { durationMs: item.durationMs } : {},
|
|
5450
|
-
...webSearchToolArgs(item)
|
|
5451
|
-
});
|
|
5452
|
-
}
|
|
5453
|
-
function itemFileChanges(item) {
|
|
5454
|
-
return Array.isArray(item.changes) ? item.changes.map((change) => ({
|
|
5455
|
-
path: change.path,
|
|
5456
|
-
kind: change.kind
|
|
5457
|
-
})) : [];
|
|
5458
|
-
}
|
|
5459
|
-
function itemToolError(item, status, outputTextByItem) {
|
|
5460
|
-
if (status === "blocked") return "codex native tool blocked";
|
|
5461
|
-
if (status !== "failed") return;
|
|
5462
|
-
return itemOutputText(item, outputTextByItem) ?? "codex native tool failed";
|
|
5463
|
-
}
|
|
5464
|
-
function itemMeta(item, detailMode = "explain") {
|
|
5465
|
-
if (item.type === "commandExecution" && typeof item.command === "string") return inferToolMetaFromArgs("exec", {
|
|
5466
|
-
command: item.command,
|
|
5467
|
-
cwd: typeof item.cwd === "string" ? item.cwd : void 0
|
|
5468
|
-
}, { detailMode });
|
|
5469
|
-
if (item.type === "webSearch") return inferToolMetaFromArgs("web_search", webSearchToolArgs(item), { detailMode });
|
|
5470
|
-
const toolName = itemName(item);
|
|
5471
|
-
if ((item.type === "dynamicToolCall" || item.type === "mcpToolCall") && toolName) return inferToolMetaFromArgs(toolName, item.arguments, { detailMode });
|
|
5472
|
-
}
|
|
5473
|
-
function itemOutputText(item, outputTextByItem) {
|
|
5474
|
-
if (item.type === "commandExecution") return item.aggregatedOutput?.trim() || outputTextByItem?.get(item.id)?.trim() || void 0;
|
|
5475
|
-
if (item.type === "dynamicToolCall") return collectDynamicToolContentText(item.contentItems).trim() || void 0;
|
|
5476
|
-
if (item.type === "mcpToolCall") {
|
|
5477
|
-
if (item.error) return stringifyJsonValue(item.error);
|
|
5478
|
-
return item.result ? stringifyJsonValue(item.result) : void 0;
|
|
5479
|
-
}
|
|
5480
|
-
}
|
|
5481
|
-
function itemTranscriptResultText(item, outputTextByItem) {
|
|
5482
|
-
const output = itemOutputText(item, outputTextByItem);
|
|
5483
|
-
if (output) return output;
|
|
5484
|
-
const result = itemToolResult(item).result;
|
|
5485
|
-
return result ? stringifyJsonValue(result) : itemStatus(item);
|
|
5486
|
-
}
|
|
5487
|
-
function appendToolOutputDeltaText(outputTextByItem, itemId, delta) {
|
|
5488
|
-
const current = outputTextByItem.get(itemId) ?? "";
|
|
5489
|
-
if (current.length >= TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS) return;
|
|
5490
|
-
const remaining = TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS - current.length;
|
|
5491
|
-
const next = current + (delta.length > remaining ? delta.slice(0, remaining) : delta);
|
|
5492
|
-
outputTextByItem.set(itemId, next);
|
|
5493
|
-
}
|
|
5494
|
-
function normalizeToolTranscriptArguments(value) {
|
|
5495
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
5496
|
-
return value;
|
|
5497
|
-
}
|
|
5498
|
-
function collectDynamicToolContentText(contentItems) {
|
|
5499
|
-
if (!Array.isArray(contentItems)) return "";
|
|
5500
|
-
return contentItems.flatMap((entry) => {
|
|
5501
|
-
if (!isJsonObject(entry)) return [];
|
|
5502
|
-
const text = readString$5(entry, "text");
|
|
5503
|
-
return text ? [text] : [];
|
|
5504
|
-
}).join("\n");
|
|
5505
|
-
}
|
|
5506
|
-
function truncateToolTranscriptText(text) {
|
|
5507
|
-
if (text.length <= TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS) return text;
|
|
5508
|
-
return `${text.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS)}\n...(truncated)...`;
|
|
5509
|
-
}
|
|
5510
|
-
function toolResultStatusText(params) {
|
|
5511
|
-
return params.isError ? `${params.name} failed` : `${params.name} completed`;
|
|
5512
|
-
}
|
|
5513
|
-
function stringifyJsonValue(value) {
|
|
5514
|
-
try {
|
|
5515
|
-
return JSON.stringify(value, null, 2);
|
|
5516
|
-
} catch {
|
|
5517
|
-
return;
|
|
5518
|
-
}
|
|
5519
|
-
}
|
|
5520
|
-
function formatToolSummary(toolName, meta) {
|
|
5521
|
-
const trimmedMeta = meta?.trim();
|
|
5522
|
-
return formatToolAggregate(toolName, trimmedMeta ? [trimmedMeta] : void 0, { markdown: true });
|
|
5523
|
-
}
|
|
5524
|
-
function formatToolOutput(toolName, meta, output) {
|
|
5525
|
-
const formattedOutput = formatToolProgressOutput(output);
|
|
5526
|
-
if (!formattedOutput) return formatToolSummary(toolName, meta);
|
|
5527
|
-
const fence = markdownFenceForText(formattedOutput);
|
|
5528
|
-
return `${formatToolSummary(toolName, meta)}\n${fence}txt\n${formattedOutput}\n${fence}`;
|
|
5529
|
-
}
|
|
5530
|
-
function markdownFenceForText(text) {
|
|
5531
|
-
return "`".repeat(Math.max(3, longestBacktickRun(text) + 1));
|
|
5532
|
-
}
|
|
5533
|
-
function longestBacktickRun(value) {
|
|
5534
|
-
let longest = 0;
|
|
5535
|
-
let current = 0;
|
|
5536
|
-
for (const char of value) {
|
|
5537
|
-
if (char === "`") {
|
|
5538
|
-
current += 1;
|
|
5539
|
-
longest = Math.max(longest, current);
|
|
5540
|
-
continue;
|
|
5541
|
-
}
|
|
5542
|
-
current = 0;
|
|
5543
|
-
}
|
|
5544
|
-
return longest;
|
|
5545
|
-
}
|
|
5546
|
-
function readItemString(item, key) {
|
|
5547
|
-
const value = item[key];
|
|
5548
|
-
return typeof value === "string" ? value : void 0;
|
|
5549
|
-
}
|
|
5550
|
-
function readItem(value) {
|
|
5551
|
-
if (!isJsonObject(value)) return;
|
|
5552
|
-
const type = typeof value.type === "string" ? value.type : void 0;
|
|
5553
|
-
const id = typeof value.id === "string" ? value.id : void 0;
|
|
5554
|
-
if (!type || !id) return;
|
|
5555
|
-
return value;
|
|
5556
|
-
}
|
|
5557
|
-
function readTurn(value) {
|
|
5558
|
-
return readCodexTurn(value);
|
|
3395
|
+
if (options?.attemptProgress) recordAttemptProgress(completionLastActivityReason, options);
|
|
3396
|
+
},
|
|
3397
|
+
extendAttemptIdleWatch: (timeoutMs) => {
|
|
3398
|
+
attemptIdleTimeoutOverrideMs = resolveWatchTimeoutMs(timeoutMs);
|
|
3399
|
+
scheduleAttemptIdleWatch();
|
|
3400
|
+
},
|
|
3401
|
+
scheduleProgressWatches,
|
|
3402
|
+
clearCompletionIdleTimer,
|
|
3403
|
+
clearAssistantCompletionIdleTimer,
|
|
3404
|
+
clearTerminalIdleTimer,
|
|
3405
|
+
clearAttemptIdleTimer,
|
|
3406
|
+
clearAllTimers
|
|
3407
|
+
};
|
|
5559
3408
|
}
|
|
5560
3409
|
//#endregion
|
|
5561
3410
|
//#region extensions/codex/src/app-server/native-subagent-notification.ts
|
|
@@ -5701,8 +3550,7 @@ var CodexNativeSubagentTaskMirror = class {
|
|
|
5701
3550
|
constructor(params, runtime) {
|
|
5702
3551
|
this.params = params;
|
|
5703
3552
|
this.runtime = runtime;
|
|
5704
|
-
this.
|
|
5705
|
-
this.failedMirrorThreadIds = /* @__PURE__ */ new Set();
|
|
3553
|
+
this.mirrorStateByThreadId = /* @__PURE__ */ new Map();
|
|
5706
3554
|
this.terminalRunIds = /* @__PURE__ */ new Set();
|
|
5707
3555
|
this.authoritativeRunIds = /* @__PURE__ */ new Set();
|
|
5708
3556
|
this.expectedAuthoritativeRunIds = /* @__PURE__ */ new Set();
|
|
@@ -5736,8 +3584,8 @@ var CodexNativeSubagentTaskMirror = class {
|
|
|
5736
3584
|
const spawn = readSubagentThreadSpawnSource(thread.source, this.params.parentThreadId);
|
|
5737
3585
|
if (!spawn) return;
|
|
5738
3586
|
const threadId = thread.id.trim();
|
|
5739
|
-
if (!threadId || this.
|
|
5740
|
-
this.
|
|
3587
|
+
if (!threadId || this.mirrorStateByThreadId.get(threadId) === "mirrored") return;
|
|
3588
|
+
this.mirrorStateByThreadId.set(threadId, "mirrored");
|
|
5741
3589
|
const runId = codexNativeSubagentRunId(threadId);
|
|
5742
3590
|
const label = trimOptional(spawn.agent_nickname) ?? trimOptional(thread.agentNickname) ?? trimOptional(spawn.agent_role) ?? trimOptional(thread.agentRole) ?? "Codex subagent";
|
|
5743
3591
|
const task = trimOptional(thread.preview) ?? `Codex native subagent${label === "Codex subagent" ? "" : ` ${label}`}`;
|
|
@@ -5755,11 +3603,9 @@ var CodexNativeSubagentTaskMirror = class {
|
|
|
5755
3603
|
lastEventAt: this.now(),
|
|
5756
3604
|
progressSummary: "Codex native subagent started."
|
|
5757
3605
|
})) {
|
|
5758
|
-
this.
|
|
5759
|
-
this.failedMirrorThreadIds.add(threadId);
|
|
3606
|
+
this.mirrorStateByThreadId.set(threadId, "failed");
|
|
5760
3607
|
return;
|
|
5761
3608
|
}
|
|
5762
|
-
this.failedMirrorThreadIds.delete(threadId);
|
|
5763
3609
|
this.terminalRunIds.delete(runId);
|
|
5764
3610
|
this.authoritativeRunIds.delete(runId);
|
|
5765
3611
|
this.applyStatus(threadId, thread.status);
|
|
@@ -5770,7 +3616,7 @@ var CodexNativeSubagentTaskMirror = class {
|
|
|
5770
3616
|
this.applyStatus(notification.threadId, notification.status);
|
|
5771
3617
|
}
|
|
5772
3618
|
applyStatus(threadId, status) {
|
|
5773
|
-
if (
|
|
3619
|
+
if (this.mirrorStateByThreadId.get(threadId) === "failed") return;
|
|
5774
3620
|
const statusType = status?.type;
|
|
5775
3621
|
if (!statusType) return;
|
|
5776
3622
|
const runId = codexNativeSubagentRunId(threadId);
|
|
@@ -5820,7 +3666,7 @@ var CodexNativeSubagentTaskMirror = class {
|
|
|
5820
3666
|
const isSpawnAgentTool = normalizeToolName$1(readString$3(item, "tool")) === "spawnagent";
|
|
5821
3667
|
const receiverThreadIds = readStringArray$1(item.receiverThreadIds);
|
|
5822
3668
|
const agentsStates = readAgentsStates(item.agentsStates);
|
|
5823
|
-
const spawnChildThreadIds = new Set([...receiverThreadIds, ...agentsStates.keys()]);
|
|
3669
|
+
const spawnChildThreadIds = /* @__PURE__ */ new Set([...receiverThreadIds, ...agentsStates.keys()]);
|
|
5824
3670
|
if (isSpawnAgentTool) for (const childThreadId of spawnChildThreadIds) this.createTaskFromCollabSpawnItem(childThreadId, item);
|
|
5825
3671
|
const toolCallStatus = normalizeCollabToolCallStatus(readString$3(item, "status"));
|
|
5826
3672
|
const terminalToolCallThreadIds = /* @__PURE__ */ new Set();
|
|
@@ -5843,8 +3689,8 @@ var CodexNativeSubagentTaskMirror = class {
|
|
|
5843
3689
|
}
|
|
5844
3690
|
createTaskFromCollabSpawnItem(threadId, item) {
|
|
5845
3691
|
const normalizedThreadId = threadId.trim();
|
|
5846
|
-
if (!normalizedThreadId || this.
|
|
5847
|
-
this.
|
|
3692
|
+
if (!normalizedThreadId || this.mirrorStateByThreadId.get(normalizedThreadId) === "mirrored") return;
|
|
3693
|
+
this.mirrorStateByThreadId.set(normalizedThreadId, "mirrored");
|
|
5848
3694
|
const prompt = trimOptional(readString$3(item, "prompt"));
|
|
5849
3695
|
const runId = codexNativeSubagentRunId(normalizedThreadId);
|
|
5850
3696
|
const createdAt = this.now();
|
|
@@ -5861,16 +3707,14 @@ var CodexNativeSubagentTaskMirror = class {
|
|
|
5861
3707
|
lastEventAt: createdAt,
|
|
5862
3708
|
progressSummary: "Codex native subagent spawned."
|
|
5863
3709
|
})) {
|
|
5864
|
-
this.
|
|
5865
|
-
this.failedMirrorThreadIds.add(normalizedThreadId);
|
|
3710
|
+
this.mirrorStateByThreadId.set(normalizedThreadId, "failed");
|
|
5866
3711
|
return;
|
|
5867
3712
|
}
|
|
5868
|
-
this.failedMirrorThreadIds.delete(normalizedThreadId);
|
|
5869
3713
|
this.terminalRunIds.delete(runId);
|
|
5870
3714
|
this.authoritativeRunIds.delete(runId);
|
|
5871
3715
|
}
|
|
5872
3716
|
applyCollabAgentStatus(threadId, status, message) {
|
|
5873
|
-
if (
|
|
3717
|
+
if (this.mirrorStateByThreadId.get(threadId) === "failed") return;
|
|
5874
3718
|
const normalizedStatus = normalizeAgentStateStatus(status);
|
|
5875
3719
|
if (!normalizedStatus) return;
|
|
5876
3720
|
const runId = codexNativeSubagentRunId(threadId);
|
|
@@ -6068,6 +3912,7 @@ function registerCodexNativeSubagentMonitor(params) {
|
|
|
6068
3912
|
taskRuntimeScope: params.taskRuntimeScope,
|
|
6069
3913
|
agentId: params.agentId
|
|
6070
3914
|
});
|
|
3915
|
+
return monitor;
|
|
6071
3916
|
}
|
|
6072
3917
|
/** Tracks native subagent thread notifications, transcript completions, and task delivery. */
|
|
6073
3918
|
var CodexNativeSubagentMonitor = class {
|
|
@@ -6094,6 +3939,13 @@ var CodexNativeSubagentMonitor = class {
|
|
|
6094
3939
|
this.childThreadIdsByAgentPath.clear();
|
|
6095
3940
|
this.transcriptPathsByChildThreadId.clear();
|
|
6096
3941
|
}
|
|
3942
|
+
deferUntilParentSettles(parentThreadId, callback) {
|
|
3943
|
+
const normalizedParentThreadId = parentThreadId.trim();
|
|
3944
|
+
const state = this.parentStates.get(normalizedParentThreadId);
|
|
3945
|
+
if (!state || !this.hasUnsettledChildren(normalizedParentThreadId)) return false;
|
|
3946
|
+
state.deferredSettlement = callback;
|
|
3947
|
+
return true;
|
|
3948
|
+
}
|
|
6097
3949
|
configure(options) {
|
|
6098
3950
|
const codexHome = normalizeOptionalString(options.codexHome);
|
|
6099
3951
|
if (codexHome) this.codexHome = codexHome;
|
|
@@ -6131,10 +3983,29 @@ var CodexNativeSubagentMonitor = class {
|
|
|
6131
3983
|
error: formatErrorMessage(error)
|
|
6132
3984
|
});
|
|
6133
3985
|
}
|
|
3986
|
+
this.markChildTurnStarted(notification);
|
|
3987
|
+
await this.handleChildSystemError(notification);
|
|
6134
3988
|
this.captureChildAssistantMessage(notification);
|
|
6135
3989
|
await this.handleChildTurnCompletion(notification);
|
|
6136
3990
|
await this.handleCompletionNotification(notification);
|
|
6137
3991
|
}
|
|
3992
|
+
markChildTurnStarted(notification) {
|
|
3993
|
+
if (notification.method !== "turn/started") return;
|
|
3994
|
+
const childThreadId = readString$2(isJsonObject(notification.params) ? notification.params : void 0, "threadId")?.trim();
|
|
3995
|
+
const childState = childThreadId ? this.childStates.get(childThreadId) : void 0;
|
|
3996
|
+
if (childState) childState.settledWithoutCompletion = false;
|
|
3997
|
+
}
|
|
3998
|
+
async handleChildSystemError(notification) {
|
|
3999
|
+
if (notification.method !== "thread/status/changed") return;
|
|
4000
|
+
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
4001
|
+
if (readString$2(isJsonObject(params?.status) ? params.status : void 0, "type") !== "systemError") return;
|
|
4002
|
+
const childThreadId = readString$2(params, "threadId")?.trim();
|
|
4003
|
+
const childState = childThreadId ? this.childStates.get(childThreadId) : void 0;
|
|
4004
|
+
if (childState) {
|
|
4005
|
+
childState.settledWithoutCompletion = true;
|
|
4006
|
+
await this.flushDeferredParentSettlements(childState.parentThreadId);
|
|
4007
|
+
}
|
|
4008
|
+
}
|
|
6138
4009
|
ensureParentTaskRuntime(state) {
|
|
6139
4010
|
if (state.taskRuntime || !state.requesterSessionKey || !state.taskRuntimeScope) return;
|
|
6140
4011
|
state.taskRuntime = this.runtime.createAgentHarnessTaskRuntime({
|
|
@@ -6171,7 +4042,7 @@ var CodexNativeSubagentMonitor = class {
|
|
|
6171
4042
|
const parentThreadId = item ? (readString$2(item, "senderThreadId") ?? readString$2(params, "threadId"))?.trim() : void 0;
|
|
6172
4043
|
const state = parentThreadId ? this.parentStates.get(parentThreadId) : void 0;
|
|
6173
4044
|
if (state && parentThreadId) {
|
|
6174
|
-
const childThreadIds = normalizeToolName(readString$2(item, "tool")) === "spawnagent" ? new Set([...readStringArray(item?.receiverThreadIds), ...readObjectStringKeys(item?.agentsStates)]) : new Set(readStringArray(item?.receiverThreadIds));
|
|
4045
|
+
const childThreadIds = normalizeToolName(readString$2(item, "tool")) === "spawnagent" ? /* @__PURE__ */ new Set([...readStringArray(item?.receiverThreadIds), ...readObjectStringKeys(item?.agentsStates)]) : new Set(readStringArray(item?.receiverThreadIds));
|
|
6175
4046
|
for (const childThreadId of childThreadIds) this.registerChildThread(parentThreadId, childThreadId);
|
|
6176
4047
|
}
|
|
6177
4048
|
return state;
|
|
@@ -6257,6 +4128,8 @@ var CodexNativeSubagentMonitor = class {
|
|
|
6257
4128
|
if (childState && turn && readString$2(turn, "status") === "interrupted") {
|
|
6258
4129
|
const turnId = readString$2(turn, "id");
|
|
6259
4130
|
if (turnId) childState.assistantMessagesByTurn.delete(turnId);
|
|
4131
|
+
childState.settledWithoutCompletion = true;
|
|
4132
|
+
await this.flushDeferredParentSettlements(childState.parentThreadId);
|
|
6260
4133
|
return;
|
|
6261
4134
|
}
|
|
6262
4135
|
if (childState && turn) this.captureChildTurnAssistantMessages(childState, turn);
|
|
@@ -6310,7 +4183,10 @@ var CodexNativeSubagentMonitor = class {
|
|
|
6310
4183
|
childState.noFinalCompletionFallbackTimer = void 0;
|
|
6311
4184
|
}
|
|
6312
4185
|
}
|
|
6313
|
-
if (!state.requesterSessionKey)
|
|
4186
|
+
if (!state.requesterSessionKey) {
|
|
4187
|
+
await this.flushDeferredParentSettlements(state.parentThreadId);
|
|
4188
|
+
return;
|
|
4189
|
+
}
|
|
6314
4190
|
const completionKey = buildCompletionDedupeKey(state.parentThreadId, completion);
|
|
6315
4191
|
if (state.deliveredCompletionKeys.has(completionKey)) return;
|
|
6316
4192
|
const deliveryState = childState ?? this.ensureChildState(state.parentThreadId, completion.childThreadId);
|
|
@@ -6363,6 +4239,7 @@ var CodexNativeSubagentMonitor = class {
|
|
|
6363
4239
|
});
|
|
6364
4240
|
} finally {
|
|
6365
4241
|
childState.deliveringCompletionKey = void 0;
|
|
4242
|
+
await this.flushDeferredParentSettlements(state.parentThreadId);
|
|
6366
4243
|
}
|
|
6367
4244
|
}
|
|
6368
4245
|
markCompletionDeliveryPending(completion, error) {
|
|
@@ -6395,6 +4272,28 @@ var CodexNativeSubagentMonitor = class {
|
|
|
6395
4272
|
}, delayMs);
|
|
6396
4273
|
unrefTimer(childState.completionDeliveryTimer);
|
|
6397
4274
|
}
|
|
4275
|
+
hasUnsettledChildren(parentThreadId) {
|
|
4276
|
+
for (const childState of this.childStates.values()) if (childState.parentThreadId === parentThreadId && (childState.pendingCompletion !== void 0 || childState.deliveringCompletionKey !== void 0 || !childState.transcriptTerminal && !childState.settledWithoutCompletion)) return true;
|
|
4277
|
+
return false;
|
|
4278
|
+
}
|
|
4279
|
+
async flushDeferredParentSettlements(parentThreadId) {
|
|
4280
|
+
if (this.hasUnsettledChildren(parentThreadId)) return;
|
|
4281
|
+
const state = this.parentStates.get(parentThreadId);
|
|
4282
|
+
const callback = state?.deferredSettlement;
|
|
4283
|
+
if (!state || !callback) return;
|
|
4284
|
+
state.deferredSettlement = void 0;
|
|
4285
|
+
await this.runDeferredParentSettlement(parentThreadId, callback);
|
|
4286
|
+
}
|
|
4287
|
+
async runDeferredParentSettlement(parentThreadId, callback) {
|
|
4288
|
+
try {
|
|
4289
|
+
await callback();
|
|
4290
|
+
} catch (error) {
|
|
4291
|
+
embeddedAgentLog.warn("Failed to finish deferred Codex app-server cleanup", {
|
|
4292
|
+
parentThreadId,
|
|
4293
|
+
error: formatErrorMessage(error)
|
|
4294
|
+
});
|
|
4295
|
+
}
|
|
4296
|
+
}
|
|
6398
4297
|
finalizeCompletionTask(completion, eventAt) {
|
|
6399
4298
|
const taskRuntime = this.getTaskRuntimeForChild(completion.childThreadId);
|
|
6400
4299
|
if (!taskRuntime) return;
|
|
@@ -6435,7 +4334,8 @@ var CodexNativeSubagentMonitor = class {
|
|
|
6435
4334
|
assistantMessagesByTurn: /* @__PURE__ */ new Map(),
|
|
6436
4335
|
transcriptPollAttempt: 0,
|
|
6437
4336
|
transcriptTerminal: false,
|
|
6438
|
-
completionDeliveryAttempt: 0
|
|
4337
|
+
completionDeliveryAttempt: 0,
|
|
4338
|
+
settledWithoutCompletion: false
|
|
6439
4339
|
};
|
|
6440
4340
|
this.childStates.set(normalizedChildThreadId, childState);
|
|
6441
4341
|
}
|
|
@@ -6874,7 +4774,8 @@ function parseCodexAppServerByteLimit(value) {
|
|
|
6874
4774
|
if (!match) return;
|
|
6875
4775
|
const amount = Number(match[1]);
|
|
6876
4776
|
if (!Number.isFinite(amount) || amount <= 0) return;
|
|
6877
|
-
const
|
|
4777
|
+
const unit = (match[2] ?? "b").toLowerCase();
|
|
4778
|
+
const multiplier = CODEX_APP_SERVER_BYTE_UNITS[unit];
|
|
6878
4779
|
if (multiplier === void 0) return;
|
|
6879
4780
|
return Math.max(1, Math.round(amount * multiplier));
|
|
6880
4781
|
}
|
|
@@ -7058,7 +4959,10 @@ async function rotateOversizedCodexAppServerStartupBinding(params) {
|
|
|
7058
4959
|
bytes: file.bytes
|
|
7059
4960
|
}))
|
|
7060
4961
|
});
|
|
7061
|
-
await
|
|
4962
|
+
await params.bindingStore.mutate(params.identity, {
|
|
4963
|
+
kind: "clear",
|
|
4964
|
+
threadId: binding.threadId
|
|
4965
|
+
});
|
|
7062
4966
|
return;
|
|
7063
4967
|
}
|
|
7064
4968
|
}
|
|
@@ -7087,7 +4991,10 @@ async function rotateOversizedCodexAppServerStartupBinding(params) {
|
|
|
7087
4991
|
reserveTokens,
|
|
7088
4992
|
projectedTurnTokens: params.projectedTurnTokens
|
|
7089
4993
|
});
|
|
7090
|
-
await
|
|
4994
|
+
await params.bindingStore.mutate(params.identity, {
|
|
4995
|
+
kind: "clear",
|
|
4996
|
+
threadId: binding.threadId
|
|
4997
|
+
});
|
|
7091
4998
|
return;
|
|
7092
4999
|
}
|
|
7093
5000
|
if (compaction?.truncateAfterCompaction !== true) return binding;
|
|
@@ -7371,7 +5278,7 @@ async function markCodexAuthProfileBlockedFromRateLimits(params) {
|
|
|
7371
5278
|
async function formatCodexTurnStartUsageLimitError(params) {
|
|
7372
5279
|
return refreshCodexUsageLimitError({
|
|
7373
5280
|
client: params.client,
|
|
7374
|
-
source: readCodexTurnStartUsageLimitErrorSource(params.error, params.
|
|
5281
|
+
source: readCodexTurnStartUsageLimitErrorSource(params.client, params.error, params.errorNotification, params.rateLimitsRevisionBeforeTurnStart),
|
|
7375
5282
|
timeoutMs: params.timeoutMs,
|
|
7376
5283
|
signal: params.signal
|
|
7377
5284
|
});
|
|
@@ -7384,7 +5291,7 @@ async function refreshCodexUsageLimitPromptError(params) {
|
|
|
7384
5291
|
source: {
|
|
7385
5292
|
message: params.message,
|
|
7386
5293
|
codexErrorInfo: "usageLimitExceeded",
|
|
7387
|
-
rateLimits: readRecentCodexRateLimits()
|
|
5294
|
+
rateLimits: readRecentCodexRateLimits(params.client)
|
|
7388
5295
|
},
|
|
7389
5296
|
timeoutMs: params.timeoutMs,
|
|
7390
5297
|
signal: params.signal
|
|
@@ -7422,7 +5329,7 @@ async function readCodexRateLimitsFromAppServerForUsageLimitError(params) {
|
|
|
7422
5329
|
timeoutMs: resolveCodexUsageLimitRateLimitRefreshTimeoutMs(params.timeoutMs),
|
|
7423
5330
|
signal: params.signal
|
|
7424
5331
|
});
|
|
7425
|
-
|
|
5332
|
+
rememberCodexRateLimitsRead(params.client, rateLimits);
|
|
7426
5333
|
return rateLimits;
|
|
7427
5334
|
} catch (error) {
|
|
7428
5335
|
embeddedAgentLog.debug("codex app-server rate-limit refresh failed after usage-limit error", { error: formatErrorMessage(error) });
|
|
@@ -7433,38 +5340,25 @@ function resolveCodexUsageLimitRateLimitRefreshTimeoutMs(timeoutMs) {
|
|
|
7433
5340
|
if (timeoutMs === void 0 || !Number.isFinite(timeoutMs) || timeoutMs <= 0) return CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS;
|
|
7434
5341
|
return Math.max(100, Math.min(timeoutMs, CODEX_USAGE_LIMIT_RATE_LIMIT_REFRESH_TIMEOUT_MS));
|
|
7435
5342
|
}
|
|
7436
|
-
function readCodexTurnStartUsageLimitErrorSource(error,
|
|
7437
|
-
const notificationError =
|
|
7438
|
-
const notificationRateLimits = readLatestRateLimitNotificationPayload(pendingNotifications);
|
|
5343
|
+
function readCodexTurnStartUsageLimitErrorSource(client, error, errorNotification, rateLimitsRevisionBeforeTurnStart) {
|
|
5344
|
+
const notificationError = readCodexErrorNotification(errorNotification);
|
|
7439
5345
|
const errorPayload = readCodexErrorPayload(error);
|
|
7440
|
-
const rateLimits =
|
|
5346
|
+
const rateLimits = errorPayload.rateLimits ?? readRecentCodexRateLimits(client);
|
|
5347
|
+
const cacheUpdatedDuringTurnStart = rateLimitsRevisionBeforeTurnStart !== void 0 && readCodexRateLimitsRevision(client) > rateLimitsRevisionBeforeTurnStart;
|
|
7441
5348
|
return {
|
|
7442
5349
|
message: notificationError?.message ?? errorPayload.message ?? formatErrorMessage(error),
|
|
7443
5350
|
codexErrorInfo: notificationError?.codexErrorInfo ?? errorPayload.codexErrorInfo,
|
|
7444
5351
|
rateLimits,
|
|
7445
|
-
rateLimitsTrustedForProfile:
|
|
5352
|
+
rateLimitsTrustedForProfile: errorPayload.rateLimits !== void 0 || cacheUpdatedDuringTurnStart
|
|
7446
5353
|
};
|
|
7447
5354
|
}
|
|
7448
|
-
function
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
}
|
|
7456
|
-
}
|
|
7457
|
-
function readLatestCodexErrorNotification(notifications) {
|
|
7458
|
-
for (let index = notifications.length - 1; index >= 0; index -= 1) {
|
|
7459
|
-
const notification = notifications[index];
|
|
7460
|
-
if (notification?.method !== "error" || !isJsonObject(notification.params)) continue;
|
|
7461
|
-
const error = notification.params.error;
|
|
7462
|
-
if (!isJsonObject(error)) continue;
|
|
7463
|
-
return {
|
|
7464
|
-
message: readString$1(error, "message"),
|
|
7465
|
-
codexErrorInfo: error.codexErrorInfo
|
|
7466
|
-
};
|
|
7467
|
-
}
|
|
5355
|
+
function readCodexErrorNotification(notification) {
|
|
5356
|
+
if (notification?.method !== "error" || !isJsonObject(notification.params)) return;
|
|
5357
|
+
const error = notification.params.error;
|
|
5358
|
+
return isJsonObject(error) ? {
|
|
5359
|
+
message: readString$1(error, "message"),
|
|
5360
|
+
codexErrorInfo: error.codexErrorInfo
|
|
5361
|
+
} : void 0;
|
|
7468
5362
|
}
|
|
7469
5363
|
function readCodexErrorPayload(error) {
|
|
7470
5364
|
const message = error instanceof Error ? error.message : void 0;
|
|
@@ -7473,7 +5367,6 @@ function readCodexErrorPayload(error) {
|
|
|
7473
5367
|
if (!isJsonObject(data)) return { message };
|
|
7474
5368
|
const nestedError = isJsonObject(data.error) ? data.error : data;
|
|
7475
5369
|
const rateLimits = nestedError.rateLimits ?? data.rateLimits;
|
|
7476
|
-
if (rateLimits !== void 0) rememberCodexRateLimits(rateLimits);
|
|
7477
5370
|
return {
|
|
7478
5371
|
message: readString$1(nestedError, "message") ?? message,
|
|
7479
5372
|
codexErrorInfo: nestedError.codexErrorInfo,
|
|
@@ -7613,7 +5506,10 @@ function readRequestId(record) {
|
|
|
7613
5506
|
//#region extensions/codex/src/app-server/run-attempt.ts
|
|
7614
5507
|
const CODEX_NATIVE_HOOK_RELAY_RENEW_INTERVAL_MS = 6e4;
|
|
7615
5508
|
const CODEX_APP_SERVER_PROJECTED_CHARS_PER_TOKEN = 4;
|
|
7616
|
-
|
|
5509
|
+
function shouldKeepCodexSharedAbortOpen(params) {
|
|
5510
|
+
if (params.explicitCancellationObserved || params.result.aborted || params.result.externalAbort) return false;
|
|
5511
|
+
return params.trigger === "memory" || !params.attemptSucceeded;
|
|
5512
|
+
}
|
|
7617
5513
|
const ensuredCodexWorkspaceDirs = /* @__PURE__ */ new Set();
|
|
7618
5514
|
function withCodexAppServerFastModeServiceTier(appServer, params) {
|
|
7619
5515
|
const fastMode = typeof params.fastMode === "function" ? params.fastMode() : params.fastMode;
|
|
@@ -7697,7 +5593,7 @@ function toTranscriptToolResultContentItem(item) {
|
|
|
7697
5593
|
}
|
|
7698
5594
|
function formatUnsupportedCodexDynamicToolOutput(type) {
|
|
7699
5595
|
const rawType = typeof type === "string" ? type.replace(/\s+/g, " ").trim() : "";
|
|
7700
|
-
return `[Unsupported Codex dynamic tool output: ${rawType ? rawType
|
|
5596
|
+
return `[Unsupported Codex dynamic tool output: ${rawType ? truncateUtf16Safe(rawType, 80) : "unknown"}${rawType.length > 80 ? "..." : ""}]`;
|
|
7701
5597
|
}
|
|
7702
5598
|
function shouldAwaitCodexAgentEndHook(params) {
|
|
7703
5599
|
return !params.messageChannel && !params.messageProvider;
|
|
@@ -7716,7 +5612,7 @@ async function runCodexAgentEndHook(params, hookParams) {
|
|
|
7716
5612
|
}
|
|
7717
5613
|
runAgentEndSideEffects(sideEffectParams);
|
|
7718
5614
|
}
|
|
7719
|
-
async function runCodexAppServerAttempt(params, options
|
|
5615
|
+
async function runCodexAppServerAttempt(params, options) {
|
|
7720
5616
|
const attemptStartedAt = Date.now();
|
|
7721
5617
|
const profilerEnabled = isCodexAppServerProfilerEnabled(params.config);
|
|
7722
5618
|
const codexModelCallTrace = freezeDiagnosticTraceContext(createDiagnosticTraceContextFromActiveScope());
|
|
@@ -7728,7 +5624,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
7728
5624
|
resetAnnounced: false
|
|
7729
5625
|
};
|
|
7730
5626
|
const preDynamicStartupStages = createCodexDynamicToolBuildStageTracker({ enabled: profilerEnabled });
|
|
7731
|
-
const attemptClientFactory = options.clientFactory ??
|
|
5627
|
+
const attemptClientFactory = options.clientFactory ?? getLeasedSharedCodexAppServerClient;
|
|
7732
5628
|
const pluginConfig = readCodexPluginConfig(options.pluginConfig);
|
|
7733
5629
|
const computerUseConfig = resolveCodexComputerUseConfig({ pluginConfig });
|
|
7734
5630
|
const { sessionAgentId } = resolveSessionAgentIds$1({
|
|
@@ -7756,10 +5652,17 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
7756
5652
|
agentId: sessionAgentId
|
|
7757
5653
|
});
|
|
7758
5654
|
const agentDir = params.agentDir ?? resolveAgentDir(params.config ?? {}, sessionAgentId);
|
|
5655
|
+
const bindingIdentity = sessionBindingIdentity({
|
|
5656
|
+
sessionId: params.sessionId,
|
|
5657
|
+
sessionKey: params.sessionKey,
|
|
5658
|
+
agentId: params.agentId,
|
|
5659
|
+
config: params.config
|
|
5660
|
+
});
|
|
5661
|
+
const bindingStore = options.bindingStore;
|
|
7759
5662
|
preDynamicStartupStages.mark("session-agent");
|
|
7760
5663
|
const activeContextEngine = isActiveHarnessContextEngine(params.contextEngine) ? params.contextEngine : void 0;
|
|
7761
5664
|
const isInactiveThreadBootstrapBinding = (binding) => !activeContextEngine && binding?.contextEngine?.projection?.mode === "thread_bootstrap";
|
|
7762
|
-
let startupBinding = await
|
|
5665
|
+
let startupBinding = await bindingStore.read(bindingIdentity);
|
|
7763
5666
|
preDynamicStartupStages.mark("read-binding");
|
|
7764
5667
|
const startupBindingAuthProfileId = startupBinding?.authProfileId;
|
|
7765
5668
|
const initialStartupBindingHadInactiveThreadBootstrap = isInactiveThreadBootstrapBinding(startupBinding);
|
|
@@ -7831,13 +5734,35 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
7831
5734
|
});
|
|
7832
5735
|
preDynamicStartupStages.mark("native-hook-relay");
|
|
7833
5736
|
const runAbortController = new AbortController();
|
|
5737
|
+
let explicitCancellationObserved = false;
|
|
5738
|
+
let explicitCancellationReason;
|
|
5739
|
+
let terminalOutcomeFrozen = false;
|
|
5740
|
+
let sharedAbortAllowedAfterTerminalOutcome = false;
|
|
5741
|
+
let attemptAbortNotified = false;
|
|
5742
|
+
const notifyAttemptAbort = () => {
|
|
5743
|
+
if (attemptAbortNotified) return;
|
|
5744
|
+
attemptAbortNotified = true;
|
|
5745
|
+
params.onAttemptAbort?.();
|
|
5746
|
+
};
|
|
5747
|
+
const abortExplicitly = (reason) => {
|
|
5748
|
+
if (terminalOutcomeFrozen) {
|
|
5749
|
+
if (sharedAbortAllowedAfterTerminalOutcome) notifyAttemptAbort();
|
|
5750
|
+
return;
|
|
5751
|
+
}
|
|
5752
|
+
notifyAttemptAbort();
|
|
5753
|
+
explicitCancellationObserved = true;
|
|
5754
|
+
explicitCancellationReason ??= reason;
|
|
5755
|
+
runAbortController.abort(reason);
|
|
5756
|
+
};
|
|
7834
5757
|
const abortFromUpstream = () => {
|
|
7835
|
-
|
|
5758
|
+
abortExplicitly(params.abortSignal?.reason ?? "upstream_abort");
|
|
7836
5759
|
};
|
|
7837
5760
|
if (params.abortSignal?.aborted) abortFromUpstream();
|
|
7838
5761
|
else params.abortSignal?.addEventListener("abort", abortFromUpstream, { once: true });
|
|
7839
5762
|
startupBinding = await rotateOversizedCodexAppServerStartupBinding({
|
|
7840
5763
|
binding: startupBinding,
|
|
5764
|
+
bindingStore,
|
|
5765
|
+
identity: bindingIdentity,
|
|
7841
5766
|
sessionFile: params.sessionFile,
|
|
7842
5767
|
agentDir,
|
|
7843
5768
|
codexHome: appServer.start.env?.CODEX_HOME,
|
|
@@ -8043,6 +5968,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8043
5968
|
hookContext: {
|
|
8044
5969
|
agentId: sessionAgentId,
|
|
8045
5970
|
config: params.config,
|
|
5971
|
+
workspaceDir: effectiveWorkspace,
|
|
8046
5972
|
sessionId: params.sessionId,
|
|
8047
5973
|
sessionKey: sandboxSessionKey,
|
|
8048
5974
|
runId: params.runId,
|
|
@@ -8291,8 +6217,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8291
6217
|
codexTurnPromptText = decorateCodexTurnPromptText(nextPromptBuild);
|
|
8292
6218
|
};
|
|
8293
6219
|
const selectNewerVisibleHistoryAfterBinding = (binding) => {
|
|
8294
|
-
const
|
|
8295
|
-
|
|
6220
|
+
const historyCoveredThrough = Date.parse(binding.historyCoveredThrough ?? "");
|
|
6221
|
+
const cutoff = Number.isFinite(historyCoveredThrough) ? historyCoveredThrough : 0;
|
|
8296
6222
|
return historyMessages.filter((message) => {
|
|
8297
6223
|
if (message.role !== "user" && message.role !== "assistant") return false;
|
|
8298
6224
|
const record = message;
|
|
@@ -8300,9 +6226,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8300
6226
|
if (typeof idempotencyKey === "string" && idempotencyKey.startsWith("codex-app-server:")) return false;
|
|
8301
6227
|
const meta = record["__openclaw"];
|
|
8302
6228
|
const mirrorIdentity = meta && typeof meta === "object" && !Array.isArray(meta) ? meta.mirrorIdentity : void 0;
|
|
6229
|
+
if ((meta && typeof meta === "object" && !Array.isArray(meta) ? meta.mirrorOrigin : void 0) === "codex-app-server") return false;
|
|
8303
6230
|
if (typeof mirrorIdentity === "string" && mirrorIdentity.startsWith("codex-app-server:")) return false;
|
|
8304
6231
|
const timestamp = typeof message.timestamp === "number" ? message.timestamp : typeof message.timestamp === "string" ? Date.parse(message.timestamp) : NaN;
|
|
8305
|
-
return Number.isFinite(timestamp) && timestamp >
|
|
6232
|
+
return Number.isFinite(timestamp) && timestamp > cutoff;
|
|
8306
6233
|
});
|
|
8307
6234
|
};
|
|
8308
6235
|
const applyResumeStaleBindingContinuityProjection = (binding) => {
|
|
@@ -8354,6 +6281,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8354
6281
|
});
|
|
8355
6282
|
startupBinding = await rotateOversizedCodexAppServerStartupBinding({
|
|
8356
6283
|
binding: startupBinding,
|
|
6284
|
+
bindingStore,
|
|
6285
|
+
identity: bindingIdentity,
|
|
8357
6286
|
sessionFile: params.sessionFile,
|
|
8358
6287
|
agentDir,
|
|
8359
6288
|
codexHome: appServer.start.env?.CODEX_HOME,
|
|
@@ -8401,11 +6330,41 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8401
6330
|
});
|
|
8402
6331
|
let client;
|
|
8403
6332
|
let thread;
|
|
6333
|
+
let turnRouter;
|
|
6334
|
+
let turnRoute;
|
|
6335
|
+
let routeActivated = false;
|
|
6336
|
+
let detachRouteAbort = () => void 0;
|
|
8404
6337
|
let trajectoryEndRecorded = false;
|
|
8405
6338
|
const markTrajectoryEndRecorded = () => {
|
|
8406
6339
|
trajectoryEndRecorded = true;
|
|
8407
6340
|
};
|
|
8408
6341
|
let nativeHookRelay;
|
|
6342
|
+
const nativeSubagentMonitorRef = {};
|
|
6343
|
+
const pendingNativePreToolUseFailures = [];
|
|
6344
|
+
const projectorRef = {};
|
|
6345
|
+
let nativePreToolUseFailureFallbackActive = false;
|
|
6346
|
+
let nativePreToolUseFailureFallbackTerminalReason;
|
|
6347
|
+
const emitNativePreToolUseFailure = (failure) => {
|
|
6348
|
+
emitCodexNativePreToolUseFailureDiagnostic({
|
|
6349
|
+
agentId: sessionAgentId,
|
|
6350
|
+
sessionId: params.sessionId,
|
|
6351
|
+
sessionKey: sandboxSessionKey,
|
|
6352
|
+
runId: params.runId,
|
|
6353
|
+
signal: runAbortController.signal,
|
|
6354
|
+
failure,
|
|
6355
|
+
...nativePreToolUseFailureFallbackActive ? { terminalReason: nativePreToolUseFailureFallbackTerminalReason ?? failure.disposition } : {}
|
|
6356
|
+
});
|
|
6357
|
+
};
|
|
6358
|
+
const flushPendingNativePreToolUseFailures = () => {
|
|
6359
|
+
for (const failure of pendingNativePreToolUseFailures.splice(0)) emitNativePreToolUseFailure(failure);
|
|
6360
|
+
};
|
|
6361
|
+
const activateNativePreToolUseFailureFallback = () => {
|
|
6362
|
+
if (!nativePreToolUseFailureFallbackActive) {
|
|
6363
|
+
nativePreToolUseFailureFallbackTerminalReason = runAbortController.signal.aborted ? resolveCodexToolAbortTerminalReason(runAbortController.signal) : void 0;
|
|
6364
|
+
nativePreToolUseFailureFallbackActive = true;
|
|
6365
|
+
}
|
|
6366
|
+
flushPendingNativePreToolUseFailures();
|
|
6367
|
+
};
|
|
8409
6368
|
let releaseSharedClientLease;
|
|
8410
6369
|
let sharedCodexClientRetiredForOneShotCleanup = false;
|
|
8411
6370
|
const releaseSharedClientLeaseOnce = () => {
|
|
@@ -8443,6 +6402,13 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8443
6402
|
await releaseCodexSandboxExecServerEnvironment(sandbox);
|
|
8444
6403
|
}
|
|
8445
6404
|
};
|
|
6405
|
+
const releaseCurrentRoute = () => {
|
|
6406
|
+
detachRouteAbort();
|
|
6407
|
+
detachRouteAbort = () => void 0;
|
|
6408
|
+
turnRoute?.release();
|
|
6409
|
+
turnRoute = void 0;
|
|
6410
|
+
routeActivated = false;
|
|
6411
|
+
};
|
|
8446
6412
|
let codexEnvironmentSelection;
|
|
8447
6413
|
let codexExecutionCwd = effectiveCwd;
|
|
8448
6414
|
let codexSandboxPolicy;
|
|
@@ -8467,7 +6433,13 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8467
6433
|
attemptTimeoutMs: params.timeoutMs,
|
|
8468
6434
|
startupTimeoutMs,
|
|
8469
6435
|
turnStartTimeoutMs: params.timeoutMs,
|
|
8470
|
-
signal: runAbortController.signal
|
|
6436
|
+
signal: runAbortController.signal,
|
|
6437
|
+
onPreToolUseFailure: (failure) => {
|
|
6438
|
+
const projector = projectorRef.current;
|
|
6439
|
+
if (projector) projector.recordNativeToolPreToolUseFailure(failure);
|
|
6440
|
+
else if (nativePreToolUseFailureFallbackActive) emitNativePreToolUseFailure(failure);
|
|
6441
|
+
else pendingNativePreToolUseFailures.push(failure);
|
|
6442
|
+
}
|
|
8471
6443
|
});
|
|
8472
6444
|
return {
|
|
8473
6445
|
configPatch: nativeHookRelay ? buildCodexNativeHookRelayConfig({
|
|
@@ -8487,6 +6459,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8487
6459
|
pluginAppServer = attemptAppServer;
|
|
8488
6460
|
const startupResult = await startCodexAttemptThread({
|
|
8489
6461
|
attemptClientFactory,
|
|
6462
|
+
bindingStore,
|
|
8490
6463
|
appServer: attemptAppServer,
|
|
8491
6464
|
pluginConfig,
|
|
8492
6465
|
computerUseConfig,
|
|
@@ -8519,6 +6492,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8519
6492
|
});
|
|
8520
6493
|
client = startupResult.client;
|
|
8521
6494
|
thread = startupResult.thread;
|
|
6495
|
+
turnRouter = startupResult.turnRouter;
|
|
6496
|
+
turnRoute = startupResult.turnRoute;
|
|
8522
6497
|
pluginAppServer = startupResult.pluginAppServer;
|
|
8523
6498
|
if (thread.lifecycle.action === "started") {
|
|
8524
6499
|
const activeThreadReviewerPolicyContext = resolveCodexModelBackedReviewerPolicyContext({
|
|
@@ -8574,6 +6549,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8574
6549
|
}
|
|
8575
6550
|
});
|
|
8576
6551
|
} catch (error) {
|
|
6552
|
+
activateNativePreToolUseFailureFallback();
|
|
6553
|
+
releaseCurrentRoute();
|
|
8577
6554
|
nativeHookRelay?.unregister();
|
|
8578
6555
|
await releaseSandboxExecEnvironment();
|
|
8579
6556
|
params.abortSignal?.removeEventListener("abort", abortFromUpstream);
|
|
@@ -8594,7 +6571,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8594
6571
|
prompt: codexTurnPromptText,
|
|
8595
6572
|
tools: toolBridge.availableSpecs
|
|
8596
6573
|
});
|
|
8597
|
-
|
|
6574
|
+
let latestStartupErrorNotification;
|
|
6575
|
+
let rateLimitsRevisionBeforeLastTurnStart;
|
|
8598
6576
|
let completed = false;
|
|
8599
6577
|
let terminalTurnNotificationQueued = false;
|
|
8600
6578
|
let timedOut = false;
|
|
@@ -8614,7 +6592,6 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8614
6592
|
const completion = new Promise((resolve) => {
|
|
8615
6593
|
resolveCompletion = resolve;
|
|
8616
6594
|
});
|
|
8617
|
-
let notificationQueue = Promise.resolve();
|
|
8618
6595
|
const turnCompletionIdleTimeoutMs = resolveCodexTurnCompletionIdleTimeoutMs(options.turnCompletionIdleTimeoutMs ?? appServer.turnCompletionIdleTimeoutMs);
|
|
8619
6596
|
const turnAssistantCompletionIdleTimeoutMs = resolveCodexTurnAssistantCompletionIdleTimeoutMs(options.turnAssistantCompletionIdleTimeoutMs);
|
|
8620
6597
|
const postToolRawAssistantCompletionIdleTimeoutMs = resolveCodexPostToolRawAssistantCompletionIdleTimeoutMs(options.postToolRawAssistantCompletionIdleTimeoutMs ?? appServer.postToolRawAssistantCompletionIdleTimeoutMs, turnAssistantCompletionIdleTimeoutMs);
|
|
@@ -8625,12 +6602,15 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8625
6602
|
const pendingOpenClawDynamicToolCompletionIds = /* @__PURE__ */ new Set();
|
|
8626
6603
|
const activeTurnItemIds = /* @__PURE__ */ new Set();
|
|
8627
6604
|
const activeCompletionBlockerItemIds = /* @__PURE__ */ new Set();
|
|
6605
|
+
const activeFinalizationHookRunIds = /* @__PURE__ */ new Set();
|
|
6606
|
+
const finalizationHookBatchStatuses = /* @__PURE__ */ new Map();
|
|
6607
|
+
let unsettledFinalizationHookCount = 0;
|
|
6608
|
+
let rejectedFinalizationHookAssistant;
|
|
8628
6609
|
let turnCrossedToolHandoff = false;
|
|
8629
6610
|
let pendingTerminalDynamicToolRelease;
|
|
8630
6611
|
let terminalDynamicToolReleaseCheckScheduled = false;
|
|
8631
6612
|
let currentTurnHadNonTerminalDynamicToolResult = false;
|
|
8632
6613
|
const turnIdRef = {};
|
|
8633
|
-
const projectorRef = {};
|
|
8634
6614
|
const userInputBridgeRef = {};
|
|
8635
6615
|
const steeringQueueRef = {};
|
|
8636
6616
|
const renewNativeHookRelayForTurnProgress = () => {
|
|
@@ -8656,6 +6636,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8656
6636
|
getActiveAppServerTurnRequests: () => activeAppServerTurnRequests,
|
|
8657
6637
|
getActiveTurnItemCount: () => activeTurnItemIds.size,
|
|
8658
6638
|
getActiveCompletionBlockerItemCount: () => activeCompletionBlockerItemIds.size,
|
|
6639
|
+
getActiveFinalizationHookCount: () => unsettledFinalizationHookCount,
|
|
6640
|
+
canReleaseAssistantCompletionIdle: () => projectorRef.current?.hasLatestTerminalAssistantCandidateText() === true,
|
|
8659
6641
|
turnCompletionIdleTimeoutMs,
|
|
8660
6642
|
turnAssistantCompletionIdleTimeoutMs,
|
|
8661
6643
|
turnAttemptIdleTimeoutMs,
|
|
@@ -8780,6 +6762,21 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8780
6762
|
});
|
|
8781
6763
|
lifecycleTerminalEmitted = true;
|
|
8782
6764
|
};
|
|
6765
|
+
const buildLifecycleTerminalMeta = (input) => {
|
|
6766
|
+
const abortFields = input.aborted ? resolveAgentRunAbortLifecycleFields(runAbortController.signal) : void 0;
|
|
6767
|
+
if (input.timedOut || abortFields?.stopReason === "timeout") return {
|
|
6768
|
+
aborted: true,
|
|
6769
|
+
status: "timed_out",
|
|
6770
|
+
stopReason: "timeout",
|
|
6771
|
+
timeoutPhase: "provider",
|
|
6772
|
+
providerStarted: true
|
|
6773
|
+
};
|
|
6774
|
+
return input.aborted ? {
|
|
6775
|
+
aborted: true,
|
|
6776
|
+
status: "cancelled",
|
|
6777
|
+
stopReason: "stop"
|
|
6778
|
+
} : void 0;
|
|
6779
|
+
};
|
|
8783
6780
|
const executionPhaseKeys = /* @__PURE__ */ new Set();
|
|
8784
6781
|
const emitExecutionPhaseOnce = (key, info) => {
|
|
8785
6782
|
if (executionPhaseKeys.has(key)) return;
|
|
@@ -8857,7 +6854,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8857
6854
|
const steeringQueue = steeringQueueRef.current;
|
|
8858
6855
|
userInputBridge?.handleNotification(notification);
|
|
8859
6856
|
if (!projector || !turnId) {
|
|
8860
|
-
|
|
6857
|
+
if (notification.method === "error") latestStartupErrorNotification = notification;
|
|
8861
6858
|
return;
|
|
8862
6859
|
}
|
|
8863
6860
|
const notificationState = applyCodexTurnNotificationState({
|
|
@@ -8876,10 +6873,23 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8876
6873
|
onReportExecutionNotification: reportExecutionNotification
|
|
8877
6874
|
});
|
|
8878
6875
|
turnCrossedToolHandoff = notificationState.turnCrossedToolHandoff;
|
|
6876
|
+
const finalizationHookNotification = readCodexFinalizationHookNotification(notification, thread.threadId, turnId);
|
|
6877
|
+
if (finalizationHookNotification?.phase === "started") {
|
|
6878
|
+
if (activeFinalizationHookRunIds.size === 0) finalizationHookBatchStatuses.clear();
|
|
6879
|
+
activeFinalizationHookRunIds.add(finalizationHookNotification.runId);
|
|
6880
|
+
turnWatches.disarmAssistantCompletionIdleWatch();
|
|
6881
|
+
}
|
|
8879
6882
|
if (notificationState.isTurnTerminal) terminalTurnNotificationQueued = true;
|
|
8880
6883
|
try {
|
|
8881
6884
|
await waitForCodexNotificationDispatchTurn();
|
|
8882
6885
|
await projector.handleNotification(notification);
|
|
6886
|
+
const projectedAssistantCompletionCanRelease = isAssistantCompletionReleaseNotification(notification, turnCrossedToolHandoff) || notificationState.isCurrentTurnNotification && turnCrossedToolHandoff && notification.method === "rawResponseItem/completed" && projector.canReleaseLatestTerminalAssistantAfterToolHandoff();
|
|
6887
|
+
if (notificationState.isCurrentTurnNotification && projectedAssistantCompletionCanRelease) {
|
|
6888
|
+
const completedAssistantItemId = projector.getLatestTerminalAssistantCandidate()?.itemId;
|
|
6889
|
+
if (rejectedFinalizationHookAssistant !== void 0 && completedAssistantItemId !== void 0 && completedAssistantItemId !== rejectedFinalizationHookAssistant.itemId) rejectedFinalizationHookAssistant = void 0;
|
|
6890
|
+
else if (rejectedFinalizationHookAssistant !== void 0) turnWatches.disarmAssistantCompletionIdleWatch();
|
|
6891
|
+
else if (activeFinalizationHookRunIds.size === 0 && !terminalTurnNotificationQueued && activeAppServerTurnRequests === 0 && activeTurnItemIds.size === 0 && activeCompletionBlockerItemIds.size === 0 && pendingOpenClawDynamicToolCompletionIds.size === 0 && projector.hasLatestTerminalAssistantCandidateText()) turnWatches.armAssistantCompletionIdleWatch(describeNotificationActivity(notification));
|
|
6892
|
+
}
|
|
8883
6893
|
if (notificationState.isCurrentTurnNotification && activeTurnItemIds.size === 0 && isRawFunctionToolOutputCompletionNotification(notification)) await maybeAnnounceFastModeAutoOff();
|
|
8884
6894
|
} catch (error) {
|
|
8885
6895
|
embeddedAgentLog.debug("codex app-server projector notification threw", {
|
|
@@ -8887,6 +6897,24 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8887
6897
|
error
|
|
8888
6898
|
});
|
|
8889
6899
|
} finally {
|
|
6900
|
+
if (finalizationHookNotification?.phase === "completed") {
|
|
6901
|
+
unsettledFinalizationHookCount = Math.max(0, unsettledFinalizationHookCount - 1);
|
|
6902
|
+
activeFinalizationHookRunIds.delete(finalizationHookNotification.runId);
|
|
6903
|
+
finalizationHookBatchStatuses.set(finalizationHookNotification.runId, finalizationHookNotification.status);
|
|
6904
|
+
if (activeFinalizationHookRunIds.size === 0) {
|
|
6905
|
+
const statuses = new Set(finalizationHookBatchStatuses.values());
|
|
6906
|
+
if (statuses.has("blocked") && !statuses.has("stopped")) {
|
|
6907
|
+
const itemId = projector.getLatestTerminalAssistantCandidate()?.itemId;
|
|
6908
|
+
rejectedFinalizationHookAssistant = itemId ? { itemId } : {};
|
|
6909
|
+
turnWatches.disarmAssistantCompletionIdleWatch();
|
|
6910
|
+
} else rejectedFinalizationHookAssistant = void 0;
|
|
6911
|
+
}
|
|
6912
|
+
if (activeFinalizationHookRunIds.size === 0 && rejectedFinalizationHookAssistant === void 0 && !terminalTurnNotificationQueued && activeAppServerTurnRequests === 0 && activeTurnItemIds.size === 0 && activeCompletionBlockerItemIds.size === 0 && pendingOpenClawDynamicToolCompletionIds.size === 0 && projector.hasLatestTerminalAssistantCandidateText()) turnWatches.armAssistantCompletionIdleWatch({
|
|
6913
|
+
lastNotificationMethod: notification.method,
|
|
6914
|
+
hookRunId: finalizationHookNotification.runId,
|
|
6915
|
+
hookStatus: finalizationHookNotification.status
|
|
6916
|
+
});
|
|
6917
|
+
}
|
|
8890
6918
|
if (notificationState.isTurnTerminal) {
|
|
8891
6919
|
if (notificationState.isTurnAbortMarker) projector.markAborted();
|
|
8892
6920
|
if (!timedOut && !runAbortController.signal.aborted) await steeringQueue?.flushPending();
|
|
@@ -8898,72 +6926,43 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8898
6926
|
}
|
|
8899
6927
|
}
|
|
8900
6928
|
};
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
let settled = false;
|
|
8908
|
-
const timeoutRef = {};
|
|
8909
|
-
const finish = (completedNativeTurn) => {
|
|
8910
|
-
if (settled) return;
|
|
8911
|
-
settled = true;
|
|
8912
|
-
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
8913
|
-
runAbortController.signal.removeEventListener("abort", abortListener);
|
|
8914
|
-
if (activeNativeTurnCompletionWaiter?.resolve === finishComplete) activeNativeTurnCompletionWaiter = void 0;
|
|
8915
|
-
resolve(completedNativeTurn);
|
|
8916
|
-
};
|
|
8917
|
-
const finishComplete = () => finish(true);
|
|
8918
|
-
const abortListener = () => finish(false);
|
|
8919
|
-
timeoutRef.current = setTimeout(() => finish(false), Math.min(appServer.requestTimeoutMs, CODEX_APP_SERVER_ACTIVE_NATIVE_TURN_WAIT_TIMEOUT_MS));
|
|
8920
|
-
activeNativeTurnCompletionWaiter = {
|
|
8921
|
-
matches: matchesCompletion,
|
|
8922
|
-
resolve: finishComplete
|
|
8923
|
-
};
|
|
8924
|
-
runAbortController.signal.addEventListener("abort", abortListener, { once: true });
|
|
6929
|
+
const waitForActiveNativeTurnCompletion = async () => {
|
|
6930
|
+
const route = turnRoute;
|
|
6931
|
+
if (!route) return false;
|
|
6932
|
+
return await route.waitForTurnCompletion({
|
|
6933
|
+
timeoutMs: Math.min(appServer.requestTimeoutMs, CODEX_APP_SERVER_NATIVE_TURN_WAIT_TIMEOUT_MS),
|
|
6934
|
+
signal: runAbortController.signal
|
|
8925
6935
|
});
|
|
8926
6936
|
};
|
|
8927
|
-
const
|
|
6937
|
+
const noteNotificationReceived = (notification, scope, receivedAtMs) => {
|
|
8928
6938
|
const projector = projectorRef.current;
|
|
8929
6939
|
const turnId = turnIdRef.current;
|
|
8930
|
-
|
|
8931
|
-
const correlation = describeCodexNotificationCorrelation(notification, {
|
|
8932
|
-
threadId: thread.threadId,
|
|
8933
|
-
...turnId ? { turnId } : {}
|
|
8934
|
-
});
|
|
8935
|
-
embeddedAgentLog.trace("codex app-server raw notification received", correlation);
|
|
8936
|
-
if (notification.method === "turn/completed" && correlation.matchesActiveTurn === false) if (correlation.matchesActiveThread) embeddedAgentLog.warn("codex app-server turn/completed did not match active turn", correlation);
|
|
8937
|
-
else embeddedAgentLog.debug("codex app-server turn/completed ignored for other subscribed thread", correlation);
|
|
8938
|
-
if (notification.method === "turn/completed" && correlation.matchesActiveThread) {
|
|
8939
|
-
if (activeNativeTurnCompletionWaiter?.matches(notification)) activeNativeTurnCompletionWaiter.resolve();
|
|
8940
|
-
}
|
|
8941
|
-
if (isCodexNotificationOutsideActiveRun(correlation)) return Promise.resolve();
|
|
8942
|
-
if (!projector || !turnId) {
|
|
8943
|
-
userInputBridge?.handleNotification(notification);
|
|
8944
|
-
pendingNotifications.push(notification);
|
|
8945
|
-
return Promise.resolve();
|
|
8946
|
-
}
|
|
6940
|
+
if (!projector || !turnId) return;
|
|
8947
6941
|
if (isTerminalTurnNotificationForTurn(notification, turnId)) terminalTurnNotificationQueued = true;
|
|
8948
|
-
|
|
8949
|
-
const nativeResponseStreamDeltaMatchesActiveTurn = isNativeResponseStreamDelta && (correlation.matchesActiveTurn === true || isUnscopedCodexNotification(correlation) && canAttributeUnscopedNativeResponseDeltaToThisTurn(client));
|
|
8950
|
-
const notificationMatchesActiveTurn = correlation.matchesActiveTurn === true || !isNativeResponseStreamDelta && correlation.matchesActiveTurn !== false || nativeResponseStreamDeltaMatchesActiveTurn;
|
|
8951
|
-
if (correlation.matchesActiveTurn === true) {
|
|
6942
|
+
if (scope.turnId === turnId) {
|
|
8952
6943
|
const modelToolCallId = readRawResponseToolCallId(notification);
|
|
8953
6944
|
if (modelToolCallId) allocateCodexToolOutcomeOrdinal?.(modelToolCallId);
|
|
8954
6945
|
const nativeItem = readCodexNotificationItem(notification.params);
|
|
8955
6946
|
if (nativeItem?.type === "webSearch") projector.recordNativeToolOutcome(nativeItem);
|
|
8956
6947
|
}
|
|
8957
|
-
if (
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
|
|
6948
|
+
if (readCodexFinalizationHookNotification(notification, thread.threadId, turnId)?.phase === "started") {
|
|
6949
|
+
unsettledFinalizationHookCount += 1;
|
|
6950
|
+
turnWatches.disarmAssistantCompletionIdleWatch();
|
|
6951
|
+
}
|
|
6952
|
+
turnWatches.noteNotificationReceived(notification.method, { receivedAtMs });
|
|
6953
|
+
};
|
|
6954
|
+
const enqueueNotification = async (notification, scope) => {
|
|
6955
|
+
embeddedAgentLog.trace("codex app-server raw notification received", {
|
|
6956
|
+
method: notification.method,
|
|
6957
|
+
...scope
|
|
6958
|
+
});
|
|
6959
|
+
await handleNotification(notification);
|
|
6960
|
+
};
|
|
6961
|
+
const drainNotificationQueue = async () => {
|
|
6962
|
+
await turnRoute?.drain();
|
|
8964
6963
|
};
|
|
8965
6964
|
const nativeSubagentCodexHome = appServer.start.transport === "stdio" ? appServer.start.env?.CODEX_HOME ?? resolveCodexAppServerHomeDir(agentDir) : void 0;
|
|
8966
|
-
registerCodexNativeSubagentMonitor({
|
|
6965
|
+
nativeSubagentMonitorRef.current = registerCodexNativeSubagentMonitor({
|
|
8967
6966
|
client,
|
|
8968
6967
|
parentThreadId: thread.threadId,
|
|
8969
6968
|
requesterSessionKey: params.sessionKey,
|
|
@@ -8971,8 +6970,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8971
6970
|
agentId: params.agentId,
|
|
8972
6971
|
codexHome: nativeSubagentCodexHome
|
|
8973
6972
|
});
|
|
8974
|
-
const
|
|
8975
|
-
const requestCleanup = client.addRequestHandler(async (request) => {
|
|
6973
|
+
const handleServerRequest = async (request, scope) => {
|
|
8976
6974
|
const turnId = turnIdRef.current;
|
|
8977
6975
|
const userInputBridge = userInputBridgeRef.current;
|
|
8978
6976
|
const projector = projectorRef.current;
|
|
@@ -8986,14 +6984,9 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
8986
6984
|
turnWatches.touchActivity(`request:${request.method}:start`, { attemptProgress: true });
|
|
8987
6985
|
};
|
|
8988
6986
|
try {
|
|
8989
|
-
if (request.method === "account/chatgptAuthTokens/refresh") return refreshCodexAppServerAuthTokens({
|
|
8990
|
-
agentDir,
|
|
8991
|
-
authProfileId: startupAuthProfileId,
|
|
8992
|
-
config: params.config
|
|
8993
|
-
});
|
|
8994
6987
|
if (!turnId) return;
|
|
8995
6988
|
if (request.method === "mcpServer/elicitation/request") {
|
|
8996
|
-
if (
|
|
6989
|
+
if (!scope.turnId || scope.turnId === turnId) {
|
|
8997
6990
|
armCompletionWatchOnResponse = true;
|
|
8998
6991
|
markCurrentTurnRequestProgress();
|
|
8999
6992
|
}
|
|
@@ -9008,7 +7001,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9008
7001
|
});
|
|
9009
7002
|
}
|
|
9010
7003
|
if (request.method === "item/tool/requestUserInput") {
|
|
9011
|
-
if (
|
|
7004
|
+
if (scope.turnId === turnId) {
|
|
9012
7005
|
armCompletionWatchOnResponse = true;
|
|
9013
7006
|
markCurrentTurnRequestProgress();
|
|
9014
7007
|
}
|
|
@@ -9019,7 +7012,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9019
7012
|
}
|
|
9020
7013
|
if (request.method !== "item/tool/call") {
|
|
9021
7014
|
if (isCodexAppServerApprovalRequest(request.method)) {
|
|
9022
|
-
if (
|
|
7015
|
+
if (scope.turnId === turnId) {
|
|
9023
7016
|
armCompletionWatchOnResponse = true;
|
|
9024
7017
|
markCurrentTurnRequestProgress();
|
|
9025
7018
|
}
|
|
@@ -9034,7 +7027,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9034
7027
|
execReviewerAgentId: sessionAgentId,
|
|
9035
7028
|
internalExecAutoReview: appServer.approvalsReviewer === "user",
|
|
9036
7029
|
autoApprove: shouldAutoApproveCodexAppServerApprovals(appServer),
|
|
9037
|
-
signal: runAbortController.signal
|
|
7030
|
+
signal: runAbortController.signal,
|
|
7031
|
+
onNativeToolFailureDisposition: (itemId, disposition) => projector?.recordNativeToolApprovalFailure(itemId, disposition)
|
|
9038
7032
|
});
|
|
9039
7033
|
}
|
|
9040
7034
|
return;
|
|
@@ -9065,6 +7059,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9065
7059
|
});
|
|
9066
7060
|
emitDynamicToolStartedDiagnostic({
|
|
9067
7061
|
call,
|
|
7062
|
+
agentId: sessionAgentId,
|
|
9068
7063
|
runId: params.runId,
|
|
9069
7064
|
sessionId: params.sessionId,
|
|
9070
7065
|
sessionKey: params.sessionKey
|
|
@@ -9172,6 +7167,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9172
7167
|
})) emitDynamicToolTerminalDiagnostic({
|
|
9173
7168
|
response,
|
|
9174
7169
|
call,
|
|
7170
|
+
agentId: sessionAgentId,
|
|
9175
7171
|
runId: params.runId,
|
|
9176
7172
|
sessionId: params.sessionId,
|
|
9177
7173
|
sessionKey: params.sessionKey,
|
|
@@ -9198,6 +7194,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9198
7194
|
sessionKey: params.sessionKey
|
|
9199
7195
|
})) emitDynamicToolErrorDiagnostic({
|
|
9200
7196
|
call,
|
|
7197
|
+
agentId: sessionAgentId,
|
|
9201
7198
|
runId: params.runId,
|
|
9202
7199
|
sessionId: params.sessionId,
|
|
9203
7200
|
sessionKey: params.sessionKey,
|
|
@@ -9221,7 +7218,63 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9221
7218
|
scheduleTerminalDynamicToolReleaseCheck();
|
|
9222
7219
|
} else turnWatches.scheduleProgressWatches();
|
|
9223
7220
|
}
|
|
9224
|
-
}
|
|
7221
|
+
};
|
|
7222
|
+
const attachRouteAbort = (route) => {
|
|
7223
|
+
const onAbort = () => {
|
|
7224
|
+
if (completed || terminalTurnNotificationQueued || runAbortController.signal.aborted) return;
|
|
7225
|
+
const reasonText = formatErrorMessage(route.signal.reason);
|
|
7226
|
+
const closedClient = reasonText.includes("turn router closed");
|
|
7227
|
+
clientClosedPromptError = closedClient ? "codex app-server client closed before turn completed" : `codex app-server turn route closed before turn completed: ${reasonText}`;
|
|
7228
|
+
clientClosedAbort = closedClient;
|
|
7229
|
+
const activeTurnId = turnIdRef.current;
|
|
7230
|
+
if (activeTurnId) trajectoryRecorder?.recordEvent("turn.client_closed", {
|
|
7231
|
+
threadId: thread.threadId,
|
|
7232
|
+
turnId: activeTurnId
|
|
7233
|
+
});
|
|
7234
|
+
embeddedAgentLog.warn(clientClosedPromptError, {
|
|
7235
|
+
threadId: thread.threadId,
|
|
7236
|
+
turnId: activeTurnId
|
|
7237
|
+
});
|
|
7238
|
+
runAbortController.abort(closedClient ? "client_closed" : "turn_route_closed");
|
|
7239
|
+
completed = true;
|
|
7240
|
+
turnWatches.clearAllTimers();
|
|
7241
|
+
resolveCompletion?.();
|
|
7242
|
+
};
|
|
7243
|
+
route.signal.addEventListener("abort", onAbort, { once: true });
|
|
7244
|
+
if (route.signal.aborted) onAbort();
|
|
7245
|
+
return () => route.signal.removeEventListener("abort", onAbort);
|
|
7246
|
+
};
|
|
7247
|
+
const ensureCurrentThreadRoute = async () => {
|
|
7248
|
+
if (turnRoute?.threadId !== thread.threadId) {
|
|
7249
|
+
releaseCurrentRoute();
|
|
7250
|
+
turnRoute = turnRouter.reserveThread({
|
|
7251
|
+
threadId: thread.threadId,
|
|
7252
|
+
releaseOn: runAbortController.signal
|
|
7253
|
+
});
|
|
7254
|
+
}
|
|
7255
|
+
if (!turnRoute) throw new Error("codex app-server turn route was not reserved");
|
|
7256
|
+
if (!routeActivated) {
|
|
7257
|
+
detachRouteAbort = attachRouteAbort(turnRoute);
|
|
7258
|
+
await turnRoute.activate({
|
|
7259
|
+
onNotificationReceived: noteNotificationReceived,
|
|
7260
|
+
onNotification: enqueueNotification,
|
|
7261
|
+
onRequest: handleServerRequest
|
|
7262
|
+
});
|
|
7263
|
+
routeActivated = true;
|
|
7264
|
+
}
|
|
7265
|
+
return turnRoute;
|
|
7266
|
+
};
|
|
7267
|
+
try {
|
|
7268
|
+
await ensureCurrentThreadRoute();
|
|
7269
|
+
} catch (error) {
|
|
7270
|
+
activateNativePreToolUseFailureFallback();
|
|
7271
|
+
releaseCurrentRoute();
|
|
7272
|
+
nativeHookRelay?.unregister();
|
|
7273
|
+
await releaseSandboxExecEnvironment();
|
|
7274
|
+
releaseSharedClientLeaseOnce();
|
|
7275
|
+
params.abortSignal?.removeEventListener("abort", abortFromUpstream);
|
|
7276
|
+
throw error;
|
|
7277
|
+
}
|
|
9225
7278
|
const buildLlmInputEvent = () => ({
|
|
9226
7279
|
runId: params.runId,
|
|
9227
7280
|
sessionId: params.sessionId,
|
|
@@ -9268,6 +7321,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9268
7321
|
throw error;
|
|
9269
7322
|
};
|
|
9270
7323
|
const startCodexTurn = async () => {
|
|
7324
|
+
const activeTurnRoute = await ensureCurrentThreadRoute();
|
|
9271
7325
|
const turnAppServer = withCodexAppServerFastModeServiceTier(pluginAppServer, params);
|
|
9272
7326
|
pluginAppServer = turnAppServer;
|
|
9273
7327
|
const turnStartParams = buildTurnStartParams(params, {
|
|
@@ -9285,31 +7339,42 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9285
7339
|
heartbeatCollaborationInstructions: workspaceBootstrapContext.heartbeatCollaborationInstructions
|
|
9286
7340
|
});
|
|
9287
7341
|
codexModelCallDiagnostics.setRequestPayloadBytes(utf8JsonByteLength(turnStartParams));
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
|
|
7342
|
+
latestStartupErrorNotification = void 0;
|
|
7343
|
+
rateLimitsRevisionBeforeLastTurnStart = readCodexRateLimitsRevision(client);
|
|
7344
|
+
activeTurnRoute.armTurn();
|
|
7345
|
+
let acceptedTurnId;
|
|
7346
|
+
try {
|
|
7347
|
+
const startedTurn = assertCodexTurnStartResponse(await client.request("turn/start", turnStartParams, {
|
|
7348
|
+
timeoutMs: params.timeoutMs,
|
|
7349
|
+
signal: runAbortController.signal
|
|
7350
|
+
}));
|
|
7351
|
+
acceptedTurnId = startedTurn.turn.id;
|
|
7352
|
+
throwIfTurnStartAcceptedAfterAbort();
|
|
7353
|
+
return startedTurn;
|
|
7354
|
+
} catch (error) {
|
|
7355
|
+
if (acceptedTurnId) {
|
|
7356
|
+
interruptCodexTurnBestEffort(client, {
|
|
7357
|
+
threadId: thread.threadId,
|
|
7358
|
+
turnId: acceptedTurnId,
|
|
7359
|
+
timeoutMs: CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS
|
|
7360
|
+
});
|
|
7361
|
+
releaseCurrentRoute();
|
|
7362
|
+
} else await activeTurnRoute.cancelTurn();
|
|
7363
|
+
throw error;
|
|
7364
|
+
}
|
|
9294
7365
|
};
|
|
9295
|
-
|
|
9296
|
-
|
|
9297
|
-
embeddedAgentLog.info("codex app-server resumed thread has active native turn; waiting before turn/start", {
|
|
9298
|
-
threadId: thread.threadId,
|
|
9299
|
-
activeTurnIds: activeNativeTurnIds
|
|
9300
|
-
});
|
|
7366
|
+
if (thread.lifecycle.action === "resumed" && (thread.lifecycle.activeTurnIds?.length ?? 0) > 0) {
|
|
7367
|
+
embeddedAgentLog.info("codex app-server resumed thread has active native turn; waiting before turn/start", { threadId: thread.threadId });
|
|
9301
7368
|
emitCodexAppServerEvent(params, {
|
|
9302
7369
|
stream: "codex_app_server.lifecycle",
|
|
9303
7370
|
data: {
|
|
9304
7371
|
phase: "turn_start_waiting_for_native_turn",
|
|
9305
|
-
threadId: thread.threadId
|
|
9306
|
-
activeTurnIds: activeNativeTurnIds
|
|
7372
|
+
threadId: thread.threadId
|
|
9307
7373
|
}
|
|
9308
7374
|
});
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
});
|
|
7375
|
+
const nativeTurnCompleted = await waitForActiveNativeTurnCompletion();
|
|
7376
|
+
if (nativeTurnCompleted) await turnRoute?.drain();
|
|
7377
|
+
if (!nativeTurnCompleted && !runAbortController.signal.aborted) embeddedAgentLog.warn("codex app-server active native turn did not complete before turn/start wait timed out", { threadId: thread.threadId });
|
|
9313
7378
|
}
|
|
9314
7379
|
try {
|
|
9315
7380
|
codexModelCallDiagnostics.emitStarted();
|
|
@@ -9355,25 +7420,25 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9355
7420
|
error: formatErrorMessage(turnStartError)
|
|
9356
7421
|
});
|
|
9357
7422
|
try {
|
|
9358
|
-
|
|
9359
|
-
|
|
9360
|
-
|
|
9361
|
-
|
|
7423
|
+
if (!await bindingStore.mutate(bindingIdentity, {
|
|
7424
|
+
kind: "clear",
|
|
7425
|
+
threadId: thread.threadId
|
|
7426
|
+
})) embeddedAgentLog.warn("codex app-server preserved newer context-engine binding after resume overflow; skipping fresh retry", {
|
|
9362
7427
|
threadId: thread.threadId,
|
|
9363
7428
|
error: formatErrorMessage(turnStartError)
|
|
9364
7429
|
});
|
|
9365
7430
|
else {
|
|
9366
7431
|
thread = await restartContextEngineCodexThread();
|
|
9367
7432
|
{
|
|
9368
|
-
const retryBinding = await
|
|
7433
|
+
const retryBinding = await bindingStore.read(bindingIdentity);
|
|
9369
7434
|
if (retryBinding && retryBinding.threadId === thread.threadId && retryBinding.contextEngine?.projection) {
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
contextEngine:
|
|
9374
|
-
...
|
|
7435
|
+
await bindingStore.mutate(bindingIdentity, {
|
|
7436
|
+
kind: "patch",
|
|
7437
|
+
threadId: retryBinding.threadId,
|
|
7438
|
+
patch: { contextEngine: retryBinding.contextEngine ? {
|
|
7439
|
+
...retryBinding.contextEngine,
|
|
9375
7440
|
projection: void 0
|
|
9376
|
-
} : void 0
|
|
7441
|
+
} : void 0 }
|
|
9377
7442
|
});
|
|
9378
7443
|
embeddedAgentLog.info("codex app-server cleared stale context-engine projection after overflow retry", {
|
|
9379
7444
|
threadId: thread.threadId,
|
|
@@ -9402,12 +7467,13 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9402
7467
|
const usageLimitError = await formatCodexTurnStartUsageLimitError({
|
|
9403
7468
|
client,
|
|
9404
7469
|
error: turnStartError,
|
|
9405
|
-
|
|
7470
|
+
errorNotification: latestStartupErrorNotification,
|
|
7471
|
+
rateLimitsRevisionBeforeTurnStart: rateLimitsRevisionBeforeLastTurnStart,
|
|
9406
7472
|
timeoutMs: appServer.requestTimeoutMs,
|
|
9407
7473
|
signal: runAbortController.signal
|
|
9408
7474
|
});
|
|
9409
7475
|
const turnStartErrorMessage = usageLimitError?.message ?? formatErrorMessage(turnStartError);
|
|
9410
|
-
if (isInvalidCodexImagePayloadError(turnStartErrorMessage)) await clearCodexBindingAfterInvalidImagePayload(
|
|
7476
|
+
if (isInvalidCodexImagePayloadError(turnStartErrorMessage)) await clearCodexBindingAfterInvalidImagePayload(bindingStore, bindingIdentity, {
|
|
9411
7477
|
phase: "turn_start",
|
|
9412
7478
|
threadId: thread.threadId,
|
|
9413
7479
|
error: turnStartErrorMessage
|
|
@@ -9469,8 +7535,8 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9469
7535
|
threadId: thread.threadId,
|
|
9470
7536
|
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
|
|
9471
7537
|
});
|
|
9472
|
-
|
|
9473
|
-
|
|
7538
|
+
releaseCurrentRoute();
|
|
7539
|
+
activateNativePreToolUseFailureFallback();
|
|
9474
7540
|
nativeHookRelay?.unregister();
|
|
9475
7541
|
await releaseSandboxExecEnvironment();
|
|
9476
7542
|
await runAgentCleanupStep({
|
|
@@ -9501,6 +7567,7 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9501
7567
|
}
|
|
9502
7568
|
}
|
|
9503
7569
|
if (!turn) {
|
|
7570
|
+
activateNativePreToolUseFailureFallback();
|
|
9504
7571
|
await releaseSharedClientLeaseAndRetireOneShotClient();
|
|
9505
7572
|
throw new Error("codex app-server turn/start failed without an error");
|
|
9506
7573
|
}
|
|
@@ -9532,27 +7599,12 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9532
7599
|
}
|
|
9533
7600
|
}, thread.threadId, activeTurnId, {
|
|
9534
7601
|
nativePostToolUseRelayEnabled: nativeHookRelay?.allowedEvents.includes("post_tool_use") === true && nativeHookRelay.shouldRelayEvent("post_tool_use"),
|
|
7602
|
+
readRecentRateLimits: () => readRecentCodexRateLimits(client),
|
|
7603
|
+
runAbortSignal: runAbortController.signal,
|
|
9535
7604
|
trajectoryRecorder,
|
|
9536
7605
|
onNativeToolResultRecorded: maybeAnnounceFastModeAutoOff
|
|
9537
7606
|
});
|
|
9538
|
-
if (isTerminalTurnStatus(turn.turn.status)
|
|
9539
|
-
const closeCleanup = client.addCloseHandler?.(() => {
|
|
9540
|
-
if (completed || terminalTurnNotificationQueued || runAbortController.signal.aborted) return;
|
|
9541
|
-
clientClosedPromptError = "codex app-server client closed before turn completed";
|
|
9542
|
-
trajectoryRecorder?.recordEvent("turn.client_closed", {
|
|
9543
|
-
threadId: thread.threadId,
|
|
9544
|
-
turnId: activeTurnId
|
|
9545
|
-
});
|
|
9546
|
-
embeddedAgentLog.warn("codex app-server client closed before turn completed", {
|
|
9547
|
-
threadId: thread.threadId,
|
|
9548
|
-
turnId: activeTurnId
|
|
9549
|
-
});
|
|
9550
|
-
clientClosedAbort = true;
|
|
9551
|
-
runAbortController.abort("client_closed");
|
|
9552
|
-
completed = true;
|
|
9553
|
-
turnWatches.clearAllTimers();
|
|
9554
|
-
resolveCompletion?.();
|
|
9555
|
-
});
|
|
7607
|
+
if (isTerminalTurnStatus(turn.turn.status)) terminalTurnNotificationQueued = true;
|
|
9556
7608
|
emitLifecycleStart();
|
|
9557
7609
|
const activeProjector = projectorRef.current;
|
|
9558
7610
|
if (!activeProjector) throw new Error("codex app-server projector was not initialized");
|
|
@@ -9560,7 +7612,17 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9560
7612
|
turnWatches.touchActivity("turn:start", { arm: true });
|
|
9561
7613
|
turnWatches.armAttemptIdleWatch();
|
|
9562
7614
|
turnWatches.touchActivity("turn:start", { attemptProgress: true });
|
|
9563
|
-
for (const
|
|
7615
|
+
for (const failure of pendingNativePreToolUseFailures.splice(0)) activeProjector.recordNativeToolPreToolUseFailure(failure);
|
|
7616
|
+
if (turnRoute) try {
|
|
7617
|
+
await turnRoute.bindTurn(activeTurnId);
|
|
7618
|
+
} catch (error) {
|
|
7619
|
+
if (!terminalTurnNotificationQueued) throw error;
|
|
7620
|
+
await turnRoute.drain();
|
|
7621
|
+
if (!completed) {
|
|
7622
|
+
turnWatches.clearAllTimers();
|
|
7623
|
+
throw error;
|
|
7624
|
+
}
|
|
7625
|
+
}
|
|
9564
7626
|
if (!completed && isTerminalTurnStatus(turn.turn.status)) await enqueueNotification({
|
|
9565
7627
|
method: "turn/completed",
|
|
9566
7628
|
params: {
|
|
@@ -9568,6 +7630,9 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9568
7630
|
turnId: activeTurnId,
|
|
9569
7631
|
turn: turn.turn
|
|
9570
7632
|
}
|
|
7633
|
+
}, {
|
|
7634
|
+
threadId: thread.threadId,
|
|
7635
|
+
turnId: activeTurnId
|
|
9571
7636
|
});
|
|
9572
7637
|
const activeSteeringQueue = createCodexSteeringQueue({
|
|
9573
7638
|
client,
|
|
@@ -9579,15 +7644,23 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9579
7644
|
steeringQueueRef.current = activeSteeringQueue;
|
|
9580
7645
|
const handle = {
|
|
9581
7646
|
kind: "embedded",
|
|
7647
|
+
runId: params.runId,
|
|
9582
7648
|
queueMessage: async (text, optionsLocal) => activeSteeringQueue.queue(text, optionsLocal),
|
|
9583
7649
|
isStreaming: () => !completed && !runAbortController.signal.aborted,
|
|
9584
7650
|
isStopped: () => completed || timedOut || runAbortController.signal.aborted,
|
|
7651
|
+
isAbortable: () => !terminalOutcomeFrozen || sharedAbortAllowedAfterTerminalOutcome,
|
|
9585
7652
|
isCompacting: () => projectorRef.current?.isCompacting() ?? false,
|
|
9586
7653
|
sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
|
|
9587
|
-
cancel: () =>
|
|
9588
|
-
abort: () =>
|
|
7654
|
+
cancel: () => abortExplicitly("cancelled"),
|
|
7655
|
+
abort: () => abortExplicitly("aborted")
|
|
9589
7656
|
};
|
|
7657
|
+
params.replyOperation?.attachBackend(handle);
|
|
9590
7658
|
setActiveEmbeddedRun(params.sessionId, handle, params.sessionKey, params.sessionFile);
|
|
7659
|
+
const freezeRunTerminalOutcome = () => {
|
|
7660
|
+
if (terminalOutcomeFrozen) return;
|
|
7661
|
+
terminalOutcomeFrozen = true;
|
|
7662
|
+
params.abortSignal?.removeEventListener("abort", abortFromUpstream);
|
|
7663
|
+
};
|
|
9591
7664
|
const notifyUserMessagePersisted = createCodexAppServerUserMessagePersistenceNotifier(params);
|
|
9592
7665
|
mirrorPromptAtTurnStartBestEffort({
|
|
9593
7666
|
params,
|
|
@@ -9601,7 +7674,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9601
7674
|
const abortListener = () => {
|
|
9602
7675
|
if (timedOut) {
|
|
9603
7676
|
(async () => {
|
|
9604
|
-
await
|
|
7677
|
+
await bindingStore.mutate(bindingIdentity, {
|
|
7678
|
+
kind: "clear",
|
|
7679
|
+
threadId: thread.threadId
|
|
7680
|
+
});
|
|
9605
7681
|
await retireCodexAppServerClientAfterTimedOutTurn(client, {
|
|
9606
7682
|
threadId: thread.threadId,
|
|
9607
7683
|
turnId: activeTurnId,
|
|
@@ -9622,14 +7698,34 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9622
7698
|
if (runAbortController.signal.aborted) abortListener();
|
|
9623
7699
|
try {
|
|
9624
7700
|
await completion;
|
|
9625
|
-
await
|
|
7701
|
+
await drainNotificationQueue();
|
|
7702
|
+
const hasQuiescentCompletedAssistant = activeProjector.hasCompletedTerminalAssistantText() && activeAppServerTurnRequests === 0 && activeTurnItemIds.size === 0 && activeCompletionBlockerItemIds.size === 0 && pendingOpenClawDynamicToolCompletionIds.size === 0 && activeFinalizationHookRunIds.size === 0 && unsettledFinalizationHookCount === 0 && rejectedFinalizationHookAssistant === void 0;
|
|
7703
|
+
const hasRecoverableCompletedAssistant = !turnWatches.isCompletionIdleWatchPinnedByTerminalError() && turnWatches.isAssistantCompletionIdleWatchArmed() && hasQuiescentCompletedAssistant;
|
|
7704
|
+
const recoveredTurnWatchTimeout = turnCompletionIdleTimedOut && !explicitCancellationObserved && !terminalTurnNotificationQueued && hasRecoverableCompletedAssistant && activeProjector.recoverCompletedTerminalAssistantAfterTurnWatchTimeout();
|
|
7705
|
+
if (recoveredTurnWatchTimeout) {
|
|
7706
|
+
embeddedAgentLog.warn("codex app-server recovered completed assistant output after missing turn completion", {
|
|
7707
|
+
threadId: thread.threadId,
|
|
7708
|
+
turnId: activeTurnId,
|
|
7709
|
+
timeoutKind: turnWatchTimeoutKind,
|
|
7710
|
+
idleMs: turnWatchTimeoutIdleMs,
|
|
7711
|
+
timeoutMs: turnWatchTimeoutMs
|
|
7712
|
+
});
|
|
7713
|
+
trajectoryRecorder?.recordEvent("turn.watch_timeout_recovered", {
|
|
7714
|
+
threadId: thread.threadId,
|
|
7715
|
+
turnId: activeTurnId,
|
|
7716
|
+
timeoutKind: turnWatchTimeoutKind,
|
|
7717
|
+
idleMs: turnWatchTimeoutIdleMs,
|
|
7718
|
+
timeoutMs: turnWatchTimeoutMs
|
|
7719
|
+
});
|
|
7720
|
+
}
|
|
9626
7721
|
const result = activeProjector.buildResult(toolBridge.telemetry, { yieldDetected });
|
|
9627
|
-
const
|
|
9628
|
-
const
|
|
9629
|
-
const
|
|
9630
|
-
|
|
7722
|
+
const effectiveTimedOut = timedOut && !recoveredTurnWatchTimeout;
|
|
7723
|
+
const effectiveTurnCompletionIdleTimedOut = turnCompletionIdleTimedOut && !recoveredTurnWatchTimeout;
|
|
7724
|
+
const isFinalAborted = () => result.aborted || explicitCancellationObserved || runAbortController.signal.aborted && !clientClosedAbort && !recoveredTurnWatchTimeout;
|
|
7725
|
+
const clientClosedPromptErrorForFinal = clientClosedPromptError && hasRecoverableCompletedAssistant ? void 0 : clientClosedPromptError;
|
|
7726
|
+
let finalPromptError = clientClosedPromptErrorForFinal ?? (effectiveTurnCompletionIdleTimedOut ? turnCompletionIdleTimeoutMessage : effectiveTimedOut ? "codex app-server attempt timed out" : result.promptError);
|
|
9631
7727
|
const finalPromptErrorMessage = typeof finalPromptError === "string" ? finalPromptError : finalPromptError ? formatErrorMessage(finalPromptError) : void 0;
|
|
9632
|
-
if (isInvalidCodexImagePayloadError(finalPromptErrorMessage)) await clearCodexBindingAfterInvalidImagePayload(
|
|
7728
|
+
if (isInvalidCodexImagePayloadError(finalPromptErrorMessage)) await clearCodexBindingAfterInvalidImagePayload(bindingStore, bindingIdentity, {
|
|
9633
7729
|
phase: "turn_completed",
|
|
9634
7730
|
threadId: thread.threadId,
|
|
9635
7731
|
turnId: activeTurnId,
|
|
@@ -9645,9 +7741,10 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9645
7741
|
turnId: activeTurnId,
|
|
9646
7742
|
error: finalPromptErrorMessage
|
|
9647
7743
|
});
|
|
9648
|
-
|
|
9649
|
-
|
|
9650
|
-
|
|
7744
|
+
await bindingStore.mutate(bindingIdentity, {
|
|
7745
|
+
kind: "clear",
|
|
7746
|
+
threadId: thread.threadId
|
|
7747
|
+
});
|
|
9651
7748
|
}
|
|
9652
7749
|
const refreshedUsageLimitPromptError = await refreshCodexUsageLimitPromptError({
|
|
9653
7750
|
client,
|
|
@@ -9656,12 +7753,12 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9656
7753
|
signal: runAbortController.signal
|
|
9657
7754
|
});
|
|
9658
7755
|
if (refreshedUsageLimitPromptError) finalPromptError = refreshedUsageLimitPromptError;
|
|
9659
|
-
const finalPromptErrorSource =
|
|
9660
|
-
const codexAppServerFailureKind = clientClosedPromptErrorForFinal ? "client_closed_before_turn_completed" :
|
|
7756
|
+
const finalPromptErrorSource = effectiveTimedOut || clientClosedPromptErrorForFinal ? "prompt" : result.promptErrorSource;
|
|
7757
|
+
const codexAppServerFailureKind = clientClosedPromptErrorForFinal ? "client_closed_before_turn_completed" : effectiveTurnCompletionIdleTimedOut ? "turn_completion_idle_timeout" : void 0;
|
|
9661
7758
|
const codexAppServerReplayBlockedReason = codexAppServerFailureKind ? resolveCodexAppServerReplayBlockedReason(result) : void 0;
|
|
9662
7759
|
const promptTimeoutOutcome = buildCodexAppServerPromptTimeoutOutcome({
|
|
9663
7760
|
result,
|
|
9664
|
-
turnCompletionIdleTimedOut,
|
|
7761
|
+
turnCompletionIdleTimedOut: effectiveTurnCompletionIdleTimedOut,
|
|
9665
7762
|
turnWatchTimeoutKind
|
|
9666
7763
|
});
|
|
9667
7764
|
const codexAppServerFailureDiagnostics = codexAppServerFailureKind === "turn_completion_idle_timeout" && turnWatchTimeoutKind === "completion" ? buildCodexAppServerTimeoutDiagnostics({
|
|
@@ -9670,36 +7767,40 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9670
7767
|
lastActivityReason: turnWatchTimeoutLastActivityReason,
|
|
9671
7768
|
details: turnWatchTimeoutDetails
|
|
9672
7769
|
}) : void 0;
|
|
7770
|
+
const codexAppServerFailure = codexAppServerFailureKind ? {
|
|
7771
|
+
kind: codexAppServerFailureKind,
|
|
7772
|
+
...codexAppServerFailureKind === "turn_completion_idle_timeout" && turnWatchTimeoutKind ? { turnWatchTimeoutKind } : {},
|
|
7773
|
+
transport: appServer.start.transport,
|
|
7774
|
+
threadId: thread.threadId,
|
|
7775
|
+
turnId: activeTurnId,
|
|
7776
|
+
replaySafe: codexAppServerReplayBlockedReason === void 0,
|
|
7777
|
+
...codexAppServerReplayBlockedReason ? { replayBlockedReason: codexAppServerReplayBlockedReason } : {},
|
|
7778
|
+
...codexAppServerFailureDiagnostics ? { diagnostics: codexAppServerFailureDiagnostics } : {}
|
|
7779
|
+
} : void 0;
|
|
7780
|
+
const finalAborted = isFinalAborted();
|
|
7781
|
+
const completedTurnStatus = activeProjector.getCompletedTurnStatus();
|
|
7782
|
+
const completedWithoutTerminalNotification = completed && !terminalTurnNotificationQueued && !timedOut && clientClosedPromptErrorForFinal === void 0;
|
|
7783
|
+
const attemptSucceeded = !finalAborted && !effectiveTimedOut && (finalPromptError === null || finalPromptError === void 0) && result.agentHarnessResultClassification === void 0 && (completedTurnStatus === "completed" || recoveredTurnWatchTimeout || completedWithoutTerminalNotification);
|
|
7784
|
+
sharedAbortAllowedAfterTerminalOutcome = shouldKeepCodexSharedAbortOpen({
|
|
7785
|
+
trigger: params.trigger,
|
|
7786
|
+
result,
|
|
7787
|
+
attemptSucceeded,
|
|
7788
|
+
explicitCancellationObserved
|
|
7789
|
+
});
|
|
7790
|
+
freezeRunTerminalOutcome();
|
|
9673
7791
|
const modelCallFailureKind = classifyCodexModelCallFailureKind({
|
|
9674
7792
|
error: finalPromptError,
|
|
9675
|
-
timedOut,
|
|
9676
|
-
turnCompletionIdleTimedOut,
|
|
9677
|
-
runAborted:
|
|
9678
|
-
abortReason: runAbortController.signal.reason,
|
|
7793
|
+
timedOut: effectiveTimedOut,
|
|
7794
|
+
turnCompletionIdleTimedOut: effectiveTurnCompletionIdleTimedOut,
|
|
7795
|
+
runAborted: finalAborted,
|
|
7796
|
+
abortReason: explicitCancellationReason ?? runAbortController.signal.reason,
|
|
9679
7797
|
clientClosedAbort,
|
|
9680
7798
|
formatError: formatErrorMessage
|
|
9681
7799
|
}) ?? (finalAborted ? "aborted" : void 0);
|
|
9682
7800
|
if (modelCallFailureKind) codexModelCallDiagnostics.emitError(finalPromptError ?? "codex app-server attempt interrupted", { failureKind: modelCallFailureKind });
|
|
9683
7801
|
else if (finalPromptError) codexModelCallDiagnostics.emitError(finalPromptError);
|
|
9684
7802
|
else codexModelCallDiagnostics.emitCompleted(result);
|
|
9685
|
-
|
|
9686
|
-
attempt: params,
|
|
9687
|
-
result,
|
|
9688
|
-
threadId: thread.threadId,
|
|
9689
|
-
turnId: activeTurnId,
|
|
9690
|
-
timedOut,
|
|
9691
|
-
yieldDetected
|
|
9692
|
-
});
|
|
9693
|
-
trajectoryRecorder?.recordEvent("session.ended", {
|
|
9694
|
-
status: finalPromptError ? "error" : finalAborted || timedOut ? "interrupted" : "success",
|
|
9695
|
-
threadId: thread.threadId,
|
|
9696
|
-
turnId: activeTurnId,
|
|
9697
|
-
timedOut,
|
|
9698
|
-
yieldDetected,
|
|
9699
|
-
promptError: normalizeCodexTrajectoryError(finalPromptError)
|
|
9700
|
-
});
|
|
9701
|
-
markTrajectoryEndRecorded();
|
|
9702
|
-
await mirrorTranscriptBestEffort({
|
|
7803
|
+
const assistantTranscriptOwned = await mirrorTranscriptBestEffort({
|
|
9703
7804
|
params,
|
|
9704
7805
|
agentId: sessionAgentId,
|
|
9705
7806
|
notifyUserMessagePersisted,
|
|
@@ -9709,19 +7810,6 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9709
7810
|
threadId: thread.threadId,
|
|
9710
7811
|
turnId: activeTurnId
|
|
9711
7812
|
});
|
|
9712
|
-
const terminalAssistantText = collectTerminalAssistantText(result);
|
|
9713
|
-
if (terminalAssistantText && (!assistantStreamEventEmitted || assistantStreamNeedsTerminalSnapshot) && !finalAborted && !finalPromptError) emitCodexAppServerEvent(params, {
|
|
9714
|
-
stream: "assistant",
|
|
9715
|
-
data: { text: terminalAssistantText }
|
|
9716
|
-
});
|
|
9717
|
-
if (finalPromptError) emitLifecycleTerminal({
|
|
9718
|
-
phase: "error",
|
|
9719
|
-
error: formatErrorMessage(finalPromptError)
|
|
9720
|
-
});
|
|
9721
|
-
else emitLifecycleTerminal({
|
|
9722
|
-
phase: "end",
|
|
9723
|
-
...finalAborted ? { aborted: true } : {}
|
|
9724
|
-
});
|
|
9725
7813
|
if (activeContextEngine) {
|
|
9726
7814
|
const activeContextEnginePluginIdLocal = resolveContextEngineOwnerPluginId(activeContextEngine);
|
|
9727
7815
|
const isHeartbeatLifecycleRun = params.bootstrapContextRunKind === "heartbeat" || params.bootstrapContextRunKind === "commitment-only";
|
|
@@ -9786,31 +7874,70 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9786
7874
|
ctx: hookContext,
|
|
9787
7875
|
hookRunner
|
|
9788
7876
|
});
|
|
9789
|
-
shouldDelayNativeHookRelayUnregister =
|
|
9790
|
-
if (shouldDelayNativeHookRelayUnregister)
|
|
9791
|
-
|
|
7877
|
+
shouldDelayNativeHookRelayUnregister = completedTurnStatus === "completed" && !effectiveTimedOut && !runAbortController.signal.aborted && !finalAborted && !finalPromptError;
|
|
7878
|
+
if (shouldDelayNativeHookRelayUnregister) try {
|
|
7879
|
+
await markCodexAppServerBindingCoveredThroughTurn({
|
|
7880
|
+
bindingStore,
|
|
7881
|
+
identity: bindingIdentity,
|
|
7882
|
+
threadId: thread.threadId
|
|
7883
|
+
});
|
|
7884
|
+
} catch (error) {
|
|
7885
|
+
if (!await bindingStore.mutate(bindingIdentity, {
|
|
7886
|
+
kind: "clear",
|
|
7887
|
+
threadId: thread.threadId
|
|
7888
|
+
})) throw error;
|
|
7889
|
+
embeddedAgentLog.warn("codex app-server binding coverage update failed after completed turn; cleared stale binding", {
|
|
7890
|
+
threadId: thread.threadId,
|
|
7891
|
+
turnId: activeTurnId,
|
|
7892
|
+
error
|
|
7893
|
+
});
|
|
7894
|
+
}
|
|
7895
|
+
recordCodexTrajectoryCompletion(trajectoryRecorder, {
|
|
7896
|
+
attempt: params,
|
|
7897
|
+
result,
|
|
9792
7898
|
threadId: thread.threadId,
|
|
9793
|
-
|
|
9794
|
-
|
|
9795
|
-
|
|
7899
|
+
turnId: activeTurnId,
|
|
7900
|
+
timedOut: effectiveTimedOut,
|
|
7901
|
+
yieldDetected
|
|
7902
|
+
});
|
|
7903
|
+
trajectoryRecorder?.recordEvent("session.ended", {
|
|
7904
|
+
status: finalPromptError ? "error" : finalAborted || effectiveTimedOut ? "interrupted" : "success",
|
|
7905
|
+
threadId: thread.threadId,
|
|
7906
|
+
turnId: activeTurnId,
|
|
7907
|
+
timedOut: effectiveTimedOut,
|
|
7908
|
+
yieldDetected,
|
|
7909
|
+
promptError: normalizeCodexTrajectoryError(finalPromptError)
|
|
7910
|
+
});
|
|
7911
|
+
markTrajectoryEndRecorded();
|
|
7912
|
+
const terminalAssistantText = collectTerminalAssistantText(result);
|
|
7913
|
+
if (terminalAssistantText && (!assistantStreamEventEmitted || assistantStreamNeedsTerminalSnapshot) && !finalAborted && !finalPromptError) emitCodexAppServerEvent(params, {
|
|
7914
|
+
stream: "assistant",
|
|
7915
|
+
data: { text: terminalAssistantText }
|
|
7916
|
+
});
|
|
7917
|
+
if (finalPromptError) emitLifecycleTerminal({
|
|
7918
|
+
phase: "error",
|
|
7919
|
+
error: formatErrorMessage(finalPromptError),
|
|
7920
|
+
...buildLifecycleTerminalMeta({
|
|
7921
|
+
aborted: finalAborted,
|
|
7922
|
+
timedOut: effectiveTimedOut
|
|
7923
|
+
})
|
|
7924
|
+
});
|
|
7925
|
+
else emitLifecycleTerminal({
|
|
7926
|
+
phase: "end",
|
|
7927
|
+
...buildLifecycleTerminalMeta({
|
|
7928
|
+
aborted: finalAborted,
|
|
7929
|
+
timedOut: effectiveTimedOut
|
|
7930
|
+
})
|
|
9796
7931
|
});
|
|
9797
7932
|
return {
|
|
9798
7933
|
...result,
|
|
9799
|
-
timedOut,
|
|
7934
|
+
timedOut: effectiveTimedOut,
|
|
9800
7935
|
aborted: finalAborted,
|
|
9801
7936
|
promptError: finalPromptError,
|
|
9802
7937
|
promptErrorSource: finalPromptErrorSource,
|
|
9803
|
-
...
|
|
9804
|
-
kind: codexAppServerFailureKind,
|
|
9805
|
-
...codexAppServerFailureKind === "turn_completion_idle_timeout" && turnWatchTimeoutKind ? { turnWatchTimeoutKind } : {},
|
|
9806
|
-
transport: appServer.start.transport,
|
|
9807
|
-
threadId: thread.threadId,
|
|
9808
|
-
turnId: activeTurnId,
|
|
9809
|
-
replaySafe: codexAppServerReplayBlockedReason === void 0,
|
|
9810
|
-
...codexAppServerReplayBlockedReason ? { replayBlockedReason: codexAppServerReplayBlockedReason } : {},
|
|
9811
|
-
...codexAppServerFailureDiagnostics ? { diagnostics: codexAppServerFailureDiagnostics } : {}
|
|
9812
|
-
} } : {},
|
|
7938
|
+
...codexAppServerFailure ? { codexAppServerFailure } : {},
|
|
9813
7939
|
...promptTimeoutOutcome ? { promptTimeoutOutcome } : {},
|
|
7940
|
+
...assistantTranscriptOwned ? { assistantTranscriptOwned: true } : {},
|
|
9814
7941
|
systemPromptReport
|
|
9815
7942
|
};
|
|
9816
7943
|
} finally {
|
|
@@ -9818,7 +7945,11 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9818
7945
|
codexModelCallDiagnostics.emitError("codex app-server run completed without model-call terminal event");
|
|
9819
7946
|
emitLifecycleTerminal({
|
|
9820
7947
|
phase: "error",
|
|
9821
|
-
error: "codex app-server run completed without lifecycle terminal event"
|
|
7948
|
+
error: "codex app-server run completed without lifecycle terminal event",
|
|
7949
|
+
...buildLifecycleTerminalMeta({
|
|
7950
|
+
aborted: runAbortController.signal.aborted && !clientClosedAbort,
|
|
7951
|
+
timedOut
|
|
7952
|
+
})
|
|
9822
7953
|
});
|
|
9823
7954
|
if (trajectoryRecorder && !trajectoryEndRecorded) trajectoryRecorder.recordEvent("session.ended", {
|
|
9824
7955
|
status: timedOut || runAbortController.signal.aborted && !clientClosedAbort ? "interrupted" : "cleanup",
|
|
@@ -9837,16 +7968,21 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9837
7968
|
}
|
|
9838
7969
|
});
|
|
9839
7970
|
if (!timedOut && !runAbortController.signal.aborted) await steeringQueueRef.current?.flushPending();
|
|
9840
|
-
|
|
7971
|
+
const yieldedOneShotCleanupDeferred = !timedOut && params.cleanupBundleMcpOnRunEnd === true && yieldDetected && nativeSubagentMonitorRef.current?.deferUntilParentSettles(thread.threadId, async () => {
|
|
7972
|
+
await unsubscribeCodexThreadBestEffort(client, {
|
|
7973
|
+
threadId: thread.threadId,
|
|
7974
|
+
timeoutMs: 5e3
|
|
7975
|
+
});
|
|
7976
|
+
await releaseSharedClientLeaseAndRetireOneShotClient();
|
|
7977
|
+
});
|
|
7978
|
+
if (!timedOut && !yieldedOneShotCleanupDeferred) await unsubscribeCodexThreadBestEffort(client, {
|
|
9841
7979
|
threadId: thread.threadId,
|
|
9842
7980
|
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
|
|
9843
7981
|
});
|
|
9844
7982
|
userInputBridgeRef.current?.cancelPending();
|
|
9845
7983
|
turnWatches.clearAllTimers();
|
|
9846
|
-
|
|
9847
|
-
|
|
9848
|
-
closeCleanup?.();
|
|
9849
|
-
await releaseSharedClientLeaseAndRetireOneShotClient();
|
|
7984
|
+
releaseCurrentRoute();
|
|
7985
|
+
if (!yieldedOneShotCleanupDeferred) await releaseSharedClientLeaseAndRetireOneShotClient();
|
|
9850
7986
|
if (nativeHookRelay) if (shouldDelayNativeHookRelayUnregister) scheduleCodexNativeHookRelayUnregister({
|
|
9851
7987
|
relay: nativeHookRelay,
|
|
9852
7988
|
hookTimeoutSec: options.nativeHookRelay?.hookTimeoutSec
|
|
@@ -9854,17 +7990,20 @@ async function runCodexAppServerAttempt(params, options = {}) {
|
|
|
9854
7990
|
else nativeHookRelay.unregister();
|
|
9855
7991
|
await releaseSandboxExecEnvironment();
|
|
9856
7992
|
runAbortController.signal.removeEventListener("abort", abortListener);
|
|
9857
|
-
params.abortSignal?.removeEventListener("abort", abortFromUpstream);
|
|
9858
7993
|
steeringQueueRef.current?.cancel();
|
|
7994
|
+
freezeRunTerminalOutcome();
|
|
7995
|
+
params.replyOperation?.detachBackend(handle);
|
|
9859
7996
|
clearActiveEmbeddedRun(params.sessionId, handle, params.sessionKey, params.sessionFile);
|
|
9860
7997
|
}
|
|
9861
7998
|
}
|
|
9862
7999
|
function readDynamicToolCallParams(value) {
|
|
9863
8000
|
return readCodexDynamicToolCallParams(value);
|
|
9864
8001
|
}
|
|
9865
|
-
async function clearCodexBindingAfterInvalidImagePayload(
|
|
9866
|
-
const currentBinding = await
|
|
9867
|
-
|
|
8002
|
+
async function clearCodexBindingAfterInvalidImagePayload(bindingStore, identity, fields) {
|
|
8003
|
+
const currentBinding = await bindingStore.read(identity);
|
|
8004
|
+
const expectedThreadId = fields.threadId ?? currentBinding?.threadId;
|
|
8005
|
+
if (!expectedThreadId) return;
|
|
8006
|
+
if (currentBinding && currentBinding.threadId !== expectedThreadId) {
|
|
9868
8007
|
embeddedAgentLog.warn("codex app-server image payload error detected for unbound thread; preserving thread binding", {
|
|
9869
8008
|
...fields,
|
|
9870
8009
|
boundThreadId: currentBinding.threadId
|
|
@@ -9872,32 +8011,21 @@ async function clearCodexBindingAfterInvalidImagePayload(sessionFile, fields) {
|
|
|
9872
8011
|
return;
|
|
9873
8012
|
}
|
|
9874
8013
|
embeddedAgentLog.warn("codex app-server image payload error detected; clearing thread binding", fields);
|
|
9875
|
-
await
|
|
8014
|
+
await bindingStore.mutate(identity, {
|
|
8015
|
+
kind: "clear",
|
|
8016
|
+
threadId: expectedThreadId
|
|
8017
|
+
});
|
|
9876
8018
|
}
|
|
9877
8019
|
async function markCodexAppServerBindingCoveredThroughTurn(params) {
|
|
9878
|
-
|
|
9879
|
-
|
|
9880
|
-
|
|
9881
|
-
|
|
9882
|
-
});
|
|
9883
|
-
if (!currentBinding || currentBinding.threadId !== params.threadId) return;
|
|
9884
|
-
const { schemaVersion: _schemaVersion, sessionFile: _boundSessionFile, updatedAt: _updatedAt, ...bindingForWrite } = currentBinding;
|
|
9885
|
-
await writeCodexAppServerBinding(params.sessionFile, bindingForWrite, {
|
|
9886
|
-
authProfileStore: params.authProfileStore,
|
|
9887
|
-
agentDir: params.agentDir,
|
|
9888
|
-
config: params.config
|
|
8020
|
+
await params.bindingStore.mutate(params.identity, {
|
|
8021
|
+
kind: "patch",
|
|
8022
|
+
threadId: params.threadId,
|
|
8023
|
+
patch: { historyCoveredThrough: (/* @__PURE__ */ new Date()).toISOString() }
|
|
9889
8024
|
});
|
|
9890
8025
|
}
|
|
9891
8026
|
function isNonEmptyString(value) {
|
|
9892
8027
|
return typeof value === "string" && value.length > 0;
|
|
9893
8028
|
}
|
|
9894
|
-
function canAttributeUnscopedNativeResponseDeltaToThisTurn(client) {
|
|
9895
|
-
const activeLeases = client.getActiveSharedLeaseCountForUnscopedNotifications?.();
|
|
9896
|
-
return activeLeases === void 0 || activeLeases <= 1;
|
|
9897
|
-
}
|
|
9898
|
-
function isUnscopedCodexNotification(correlation) {
|
|
9899
|
-
return !correlation.threadId && !correlation.turnId && !correlation.nestedTurnThreadId && !correlation.nestedTurnId;
|
|
9900
|
-
}
|
|
9901
8029
|
function shouldUseFreshCodexThreadAfterContextEngineOverflow(params) {
|
|
9902
8030
|
if (!params.contextEngineActive || params.thread.lifecycle.action !== "resumed") return false;
|
|
9903
8031
|
return isCodexContextWindowError(params.error);
|
|
@@ -9912,12 +8040,20 @@ function isCodexActiveCompactTurnError(error) {
|
|
|
9912
8040
|
const codexErrorInfo = isJsonObject(data?.codexErrorInfo) ? data.codexErrorInfo : void 0;
|
|
9913
8041
|
return (isJsonObject(codexErrorInfo?.activeTurnNotSteerable) ? codexErrorInfo.activeTurnNotSteerable : void 0)?.turnKind === "compact";
|
|
9914
8042
|
}
|
|
9915
|
-
function
|
|
9916
|
-
if (notification.method !== "
|
|
9917
|
-
const
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
8043
|
+
function readCodexFinalizationHookNotification(notification, threadId, turnId) {
|
|
8044
|
+
if (notification.method !== "hook/started" && notification.method !== "hook/completed") return;
|
|
8045
|
+
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
8046
|
+
const run = params && isJsonObject(params.run) ? params.run : void 0;
|
|
8047
|
+
if (params?.threadId !== threadId || params.turnId !== turnId || run?.eventName !== "stop" && run?.eventName !== "subagentStop" || typeof run.id !== "string" || !run.id) return;
|
|
8048
|
+
if (notification.method === "hook/started") return {
|
|
8049
|
+
phase: "started",
|
|
8050
|
+
runId: run.id
|
|
8051
|
+
};
|
|
8052
|
+
return {
|
|
8053
|
+
phase: "completed",
|
|
8054
|
+
runId: run.id,
|
|
8055
|
+
status: typeof run.status === "string" ? run.status : void 0
|
|
8056
|
+
};
|
|
9921
8057
|
}
|
|
9922
8058
|
function joinPresentSections(...sections) {
|
|
9923
8059
|
return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
|
|
@@ -9973,12 +8109,15 @@ function handleApprovalRequest(params) {
|
|
|
9973
8109
|
execReviewerAgentId: params.execReviewerAgentId,
|
|
9974
8110
|
internalExecAutoReview: params.internalExecAutoReview,
|
|
9975
8111
|
autoApprove: params.autoApprove,
|
|
9976
|
-
signal: params.signal
|
|
8112
|
+
signal: params.signal,
|
|
8113
|
+
onNativeToolFailureDisposition: params.onNativeToolFailureDisposition
|
|
9977
8114
|
});
|
|
9978
8115
|
}
|
|
9979
8116
|
function resolveCodexDynamicToolDirectNames(params) {
|
|
9980
|
-
|
|
9981
|
-
|
|
8117
|
+
const names = [];
|
|
8118
|
+
if (params.crestodianTool) names.push("crestodian");
|
|
8119
|
+
if (params.sourceReplyDeliveryMode === "message_tool_only") names.push("message");
|
|
8120
|
+
return names;
|
|
9982
8121
|
}
|
|
9983
8122
|
//#endregion
|
|
9984
8123
|
export { runCodexAppServerAttempt };
|